Move container, jasper and servletapi tags

git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc5.5.x/tags/TOMCAT_5_5_11@802198 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/container/.cvsignore b/container/.cvsignore
new file mode 100644
index 0000000..9d0b71a
--- /dev/null
+++ b/container/.cvsignore
@@ -0,0 +1,2 @@
+build
+dist
diff --git a/container/LICENSE b/container/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/container/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/container/NOTICE b/container/NOTICE
new file mode 100644
index 0000000..3f59805
--- /dev/null
+++ b/container/NOTICE
@@ -0,0 +1,2 @@
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/container/build.xml b/container/build.xml
new file mode 100644
index 0000000..b39d54d
--- /dev/null
+++ b/container/build.xml
@@ -0,0 +1,188 @@
+<project name="Catalina 2.0" default="deploy" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <!--property file="build.properties"/>
+  <property file="${user.home}/build.properties"/-->
+
+  <!-- Build Defaults -->
+  <!--property name="catalina.build"   value="${basedir}/catalina/build"/-->
+
+  <!-- Source dependencies -->
+  <property name="api.home"
+           value="${basedir}/../jakarta-servletapi-5"/>
+  <property name="jtc.home"
+           value="${basedir}/../jakarta-tomcat-connectors"/>
+
+
+  <!-- =================== DETECT: Display configuration ================== -->
+  <target name="flags.display"
+   description="Display configuration and conditional compilation flags">
+
+    <ant dir="${basedir}/catalina" target="flags.display"/>
+    <ant dir="${basedir}/webapps/admin" target="flags.display"/>
+
+  </target>
+
+
+  <!-- ===================== DEPLOY: Create Directories =================== -->
+  <target name="deploy-prepare">
+    <mkdir dir="${tomcat.build}"/>
+  </target>
+
+
+  <!-- ====================== DEPLOY: Copy Static Files =================== -->
+  <target name="deploy-static" depends="deploy-prepare"/>
+
+
+  <!-- ====================== DEPLOY: Deploy Components =================== -->
+  <target name="deploy" depends="deploy-static"
+   description="Build and deploy all components">
+    <echo>Target: Catalina - Deploy ...</echo>
+    <ant dir="${basedir}/catalina" target="deploy"/>
+    <echo>Target: Webapps - Deploy ...</echo>
+    <ant dir="${basedir}/webapps"  target="deploy"/>
+  </target>
+
+  <!-- ====================== DEPLOY: Deploy Core Components =================== -->
+  <!-- used by gump to build just catalina and not the j-t-connectors -->
+  <target name="deploy-catalina" depends="deploy-static"
+   description="Build and deploy all components">
+    <echo>Target: Catalina - Deploy ...</echo>
+    <ant dir="${basedir}/catalina" target="deploy-catalina"/>
+    <echo>Target: Webapps - Deploy ...</echo>
+    <ant dir="${basedir}/webapps"  target="deploy"/>
+  </target>
+
+
+  <!-- ====================== COMBO: Clean All Directories ================ -->
+  <target name="clean"
+   description="Clean all components">
+    <delete dir="${tomcat.build}"/>
+    <echo>Target: Catalina - Clean ...</echo>
+    <ant dir="${basedir}/catalina" target="clean"/>
+    <echo>Target: Webapps - Clean ...</echo>
+    <ant dir="${basedir}/webapps"  target="clean"/>
+    <delete dir="${tomcat.dist}"/>
+  </target>
+
+
+  <!-- ======================= COMBO: Build All Components ================ -->
+  <target name="all"
+   description="Clean, build, and deploy all components">
+    <echo>Target: Catalina - All ...</echo>
+    <ant dir="${basedir}/catalina" target="all"/>
+    <echo>Target: Webapps - All ...</echo>
+    <ant dir="${basedir}/webapps"  target="all"/>
+  </target>
+
+
+  <!-- ======================= COMBO: Test All Components ================= -->
+  <target name="test"
+   description="Unit tests on all components">
+    <echo>Target: Catalina - Test ...</echo>
+    <ant dir="${basedir}/catalina" target="test"/>
+    <echo>Target: Webapps - Test ...</echo>
+    <ant dir="${basedir}/webapps"  target="test"/>
+  </target>
+
+
+  <!-- ====================== DIST: Create Directories ==================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${tomcat.dist}"/>
+    <mkdir dir="${tomcat.dist}/bin"/>
+    <mkdir dir="${tomcat.dist}/common"/>
+    <mkdir dir="${tomcat.dist}/common/classes"/>
+    <mkdir dir="${tomcat.dist}/common/endorsed"/>
+    <mkdir dir="${tomcat.dist}/common/lib"/>
+    <mkdir dir="${tomcat.dist}/conf"/>
+    <mkdir dir="${tomcat.dist}/logs"/>
+    <mkdir dir="${tomcat.dist}/server"/>
+    <mkdir dir="${tomcat.dist}/server/classes"/>
+    <mkdir dir="${tomcat.dist}/server/lib"/>
+    <mkdir dir="${tomcat.dist}/shared/classes"/>
+    <mkdir dir="${tomcat.dist}/shared/lib"/>
+    <mkdir dir="${tomcat.dist}/webapps"/>
+    <mkdir dir="${tomcat.dist}/work"/>
+    <mkdir dir="${tomcat.dist}/temp"/>
+  </target>
+
+
+  <!-- ====================== DIST: Copy Static Files ===================== -->
+  <target name="dist-static" depends="dist-prepare">
+
+    <!-- Copy the top-level documentation files -->
+    <copy todir="${tomcat.dist}">
+      <fileset dir=".">
+        <include name="LICENSE"/>
+        <include name="INSTALLING.txt"/>
+        <include name="BUILDING.txt"/>
+        <include name="README.txt"/>
+        <include name="RELEASE*"/>
+        <include name="RUNNING.txt"/>
+      </fileset>
+    </copy>
+
+    <!-- Copy the contents of each "build" directory -->
+    <copy todir="${tomcat.dist}/bin">
+      <fileset dir="${tomcat.build}/bin" />
+    </copy>
+    <copy todir="${tomcat.dist}/common/classes">
+      <fileset dir="${tomcat.build}/common/classes" />
+    </copy>
+    <copy todir="${tomcat.dist}/common/endorsed">
+      <fileset dir="${tomcat.build}/common/endorsed" />
+    </copy>
+    <copy todir="${tomcat.dist}/common/lib">
+      <fileset dir="${tomcat.build}/common/lib" />
+    </copy>
+    <copy todir="${tomcat.dist}/conf">
+      <fileset dir="${tomcat.build}/conf" />
+    </copy>
+    <copy todir="${tomcat.dist}/server/classes">
+      <fileset dir="${tomcat.build}/server/classes" />
+    </copy>
+    <copy todir="${tomcat.dist}/server/lib">
+      <fileset dir="${tomcat.build}/server/lib" />
+    </copy>
+    <copy todir="${tomcat.dist}/server/webapps">
+      <fileset dir="${tomcat.build}/server/webapps" />
+    </copy>
+    <copy todir="${tomcat.dist}/shared/classes">
+      <fileset dir="${tomcat.build}/shared/classes" />
+    </copy>
+    <copy todir="${tomcat.dist}/shared/lib">
+      <fileset dir="${tomcat.build}/shared/lib" />
+    </copy>
+    <copy todir="${tomcat.dist}/webapps">
+      <fileset dir="${tomcat.build}/webapps" />
+    </copy>
+
+    <!-- Correct permissions and line endings on "bin" scripts -->
+    <fixcrlf srcdir="${tomcat.dist}/bin"   includes="*.sh"  eol="lf"/>
+    <fixcrlf srcdir="${tomcat.dist}/bin"   includes="*.bat" eol="crlf"/>
+    <chmod      dir="${tomcat.dist}/bin"   includes="*.sh"  perm="+x"/>
+
+  </target>
+
+
+  <!-- ====================== DIST: Create Javadoc ======================== -->
+  <target name="dist-javadoc">
+    <ant dir="${basedir}/catalina" target="javadoc"/>
+    <mkdir  dir="${tomcat.dist}/webapps/tomcat-docs/catalina/docs/api"/>
+    <copy todir="${tomcat.dist}/webapps/tomcat-docs/catalina/docs/api">
+      <fileset dir="${catalina.build}/javadoc" />
+    </copy>
+  </target>
+
+
+  <!-- ====================== DIST: Create Archives ======================= -->
+  <target name="dist" depends="deploy,dist-static,dist-javadoc"
+   description="Create binary distribution">
+  </target>
+
+
+</project>
diff --git a/container/catalina/.cvsignore b/container/catalina/.cvsignore
new file mode 100644
index 0000000..9d0b71a
--- /dev/null
+++ b/container/catalina/.cvsignore
@@ -0,0 +1,2 @@
+build
+dist
diff --git a/container/catalina/build.xml b/container/catalina/build.xml
new file mode 100644
index 0000000..fc992cf
--- /dev/null
+++ b/container/catalina/build.xml
@@ -0,0 +1,1222 @@
+<project name="Catalina" default="deploy" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <!--property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="${user.home}/build.properties"/-->
+
+  <!-- Build Defaults -->
+  <property name="catalina.home"     location=".."/>
+  <property name="catalina.build"    value="${catalina.home}/catalina/build"/>
+  <property name="classes.dir" value="${catalina.build}/server/classes" />
+  <property name="catalina.deploy"   value="${catalina.home}/build"/>
+  <property name="catalina.dist"     value="${catalina.home}/dist"/>
+  <property name="test.failonerror"  value="true"/>
+  <property name="test.runner"       value="junit.textui.TestRunner"/>
+  <property name="test.webapp"       value="../webapps/build/ROOT"/>
+  <property name="test.webapp.war"   value="${java.io.tmpdir}/webapp.war"/>
+
+  <!-- Source dependencies -->
+  <property name="tomcat-util.home"       value="${jtc.home}/util"/>
+  <property name="tomcat-coyote.home"     value="${jtc.home}/coyote"/>
+  <property name="tomcat-jk.home"         value="${jtc.home}/jk"/>
+  <property name="tomcat-http11.home"     value="${jtc.home}/http11"/>
+
+  <!-- Dependent JARs and files -->
+  <property name="ant.jar" value="${ant.home}/lib/ant.jar"/>
+  <property name="servlet-api.jar" value="${api.home}/jsr154/dist/lib/servlet-api.jar"/>
+  <property name="tomcat-util.jar"
+           value="${tomcat-util.home}/build/lib/tomcat-util.jar"/>
+  <property name="tomcat-coyote.jar"
+           value="${tomcat-coyote.home}/build/lib/tomcat-coyote.jar"/>
+  <property name="tomcat-jk2.jar"
+           value="${tomcat-jk.home}/build/lib/tomcat-jk2.jar"/>
+  <property name="tomcat-jni.jar"
+           value="${tomcat-jk.home}/build/lib/tomcat-jni.jar"/>
+  <property name="jk2.properties"
+           value="${tomcat-jk.home}/build/conf/jk2.properties"/>
+  <property name="tomcat-http11.jar"
+           value="${tomcat-http11.home}/build/lib/tomcat-http11.jar"/>
+  <property name="tomcat-dbcp.jar" 
+           value="${base.path}/tomcat-deps/naming-factory-dbcp.jar"/>
+  <!-- Construct Catalina classpath -->
+  <path id="catalina.classpath">
+    <pathelement location="${activation.jar}"/>
+    <pathelement location="${ant.jar}"/>
+    <pathelement location="${commons-beanutils.jar}"/>
+    <pathelement location="${commons-collections.jar}"/>
+    <pathelement location="${tomcat-dbcp.jar}"/>
+    <pathelement location="${commons-digester.jar}"/>
+    <pathelement location="${commons-fileupload.jar}"/>
+    <pathelement location="${commons-launcher.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${tomcat-util.jar}"/>
+    <pathelement location="${tomcat-coyote.jar}"/>
+    <pathelement location="${jaas.jar}"/>
+    <pathelement location="${javagroups.jar}"/>
+    <pathelement location="${jcert.jar}"/>
+    <pathelement location="${jdbc20ext.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${jmx-remote.jar}"/>
+    <pathelement location="${jndi.jar}"/>
+    <pathelement location="${jnet.jar}"/>
+    <pathelement location="${jsse.jar}"/>
+    <pathelement location="${java.home}/lib/jsse.jar"/>
+    <pathelement location="${jta.jar}"/>
+    <pathelement location="${ldap.jar}"/>
+    <pathelement location="${mail.jar}"/>
+    <pathelement location="${regexp.jar}"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${xercesImpl.jar}"/>
+    <pathelement location="${xml-apis.jar}"/>
+    <pathelement location="${classes.dir}"/>
+  </path>
+
+  <!-- Construct unit tests classpath -->
+  <path id="test.classpath">
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${activation.jar}"/>
+    <pathelement location="${ant.jar}"/>
+    <pathelement location="${commons-beanutils.jar}"/>
+    <pathelement location="${commons-collections.jar}"/>
+    <pathelement location="${tomcat-dbcp.jar}"/>
+    <pathelement location="${commons-digester.jar}"/>
+    <pathelement location="${commons-fileupload.jar}"/>
+    <pathelement location="${commons-launcher.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${jaas.jar}"/>
+    <pathelement location="${jcert.jar}"/>
+    <pathelement location="${jdbc20ext.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${jndi.jar}"/>
+    <pathelement location="${jnet.jar}"/>
+    <pathelement location="${jsse.jar}"/>
+    <pathelement location="${jta.jar}"/>
+    <pathelement location="${ldap.jar}"/>
+    <pathelement location="${mail.jar}"/>
+    <pathelement location="${regexp.jar}"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${xercesImpl.jar}"/>
+    <pathelement location="${xml-apis.jar}"/>
+    <pathelement location="${classes.dir}"/>
+    <pathelement location="${catalina.build}/tests"/>
+  </path>
+
+  <!-- Source path -->
+  <path id="javadoc.sourcepath">
+    <pathelement location="src/share"/>
+    <pathelement location="${tomcat-util.home}/java"/>
+    <pathelement location="${tomcat-coyote.home}/src/java"/>
+    <pathelement location="${tomcat-jk.home}/java"/>
+    <pathelement location="${tomcat-http11.home}/src/java"/>
+    <pathelement location="../modules/cluster/src/share"/>
+    <pathelement location="../modules/storeconfig/src/share"/>
+  </path>
+
+
+  <!-- =================== BUILD: Set compile flags ======================= -->
+  <target name="flags">
+
+    <!-- JDK flags -->
+    <available property="jdk.1.2.present" classname="java.util.HashMap" />
+    <available property="jdk.1.3.present" 
+     classname="java.lang.reflect.Proxy" />
+    <available property="jdk.1.4.present" classname="java.nio.Buffer" />
+    <available property="jdk.1.5.present" 
+     classname="java.util.concurrent.Semaphore" />
+
+    <!-- Ant flags -->
+    <available property="style.available"
+     classname="org.apache.tools.ant.taskdefs.optional.TraXLiaison" />
+
+    <!-- Source path -->
+    <available property="jtc.home.present" file="${jtc.home}" />
+
+    <!-- Class availability flags -->
+    <available property="beanutils.present"
+     classname="org.apache.commons.beanutils.BeanUtils"
+     classpath="${commons-beanutils.jar}"/>
+    <available property="collections.present"
+     classname="org.apache.commons.collections.FastHashMap"
+     classpath="${commons-collections.jar}"/>
+    <available property="dbcp.present"
+     classname="org.apache.tomcat.dbcp.dbcp.ConnectionFactory"
+     classpath="${tomcat-dbcp.jar}"/>
+    <available property="digester.present"
+     classname="org.apache.commons.digester.Digester"
+     classpath="${commons-digester.jar}"/>
+    <available property="fileupload.present"
+     classname="org.apache.commons.fileupload.FileUpload"
+     classpath="${commons-fileupload.jar}"/>
+    <available property="launcher.present"
+     classname="org.apache.commons.launcher.Launcher"
+     classpath="${commons-launcher.jar}"/>
+    <available property="launcher.bootstrap.present"
+     file="${commons-launcher.bootstrap.class}"/>
+    <available property="logging.present"
+     classname="org.apache.commons.logging.Log"
+     classpath="${commons-logging.jar}"/>
+    <available property="modeler.present"
+     classname="org.apache.commons.modeler.Registry"
+     classpath="${commons-modeler.jar}:${jmx.jar}"/>
+    <available property="jaas.present"
+     classname="javax.security.auth.Subject"
+     classpath="${jaas.jar}" />
+    <condition property="jaxp.present">
+      <and>
+        <available classname="javax.xml.parsers.SAXParser"
+         classpath="${xml-apis.jar}" />
+        <available classname="org.xml.sax.ContentHandler"
+         classpath="${xml-apis.jar}" />
+      </and>
+    </condition>
+    <condition property="javamail.present">
+      <and>
+        <available classname="javax.activation.DataSource"
+         classpath="${activation.jar}" />
+        <available classname="javax.mail.Service"
+         classpath="${mail.jar}" />
+      </and>
+    </condition>
+    <available property="jmx.present"
+     classname="javax.management.MBeanServer"
+     classpath="${jmx.jar}" />
+    <available property="jndi.present"
+     classname="javax.naming.Context"
+     classpath="${jndi.jar}" />
+    <condition property="jsse.present">
+      <and>
+        <available classname="javax.security.cert.Certificate"
+         classpath="${jcert.jar}" />
+        <available classname="javax.net.SocketFactory"
+         classpath="${jnet.jar}" />
+        <available classname="javax.net.ssl.SSLSocket"
+         classpath="${jsse.jar}" />
+      </and>
+    </condition>
+    <condition property="jta.present">
+      <and>
+        <available classname="javax.sql.XADataSource"
+         classpath="${jdbc20ext.jar}" />
+        <available classname="javax.transaction.UserTransaction"
+         classpath="${jta.jar}" />
+      </and>
+    </condition>
+    <available property="junit.present"
+     classname="junit.framework.TestCase"
+     classpath="${junit.jar}" />
+    <available property="ldap.present"
+     classname="com.sun.jndi.ldap.LdapClient"
+     classpath="${ldap.jar}" />
+    <available property="pool.present"
+     classname="org.apache.tomcat.dbcp.pool.ObjectPool"
+     classpath="${tomcat-dbcp.jar}"/>
+    <available property="regexp.present"
+     classname="org.apache.regexp.RE"
+     classpath="${regexp.jar}" />
+    <available property="xerces.present"
+     classname="org.apache.xerces.parsers.SAXParser"
+     classpath="${xercesImpl.jar}" />
+
+    <!-- JAR files availability flags -->
+    <available property="dbcp.jar.present" file="${tomcat-dbcp.jar}" />
+    <available property="fileupload.jar.present" file="${commons-fileupload.jar}" />
+    <available property="jaas.jar.present" file="${jaas.jar}" />
+    <condition property="javamail.jar.present">
+      <and>
+        <available file="${activation.jar}" />
+        <available file="${mail.jar}" />
+      </and>
+    </condition>
+    <available property="jdbc20ext.jar.present" file="${jdbc20ext.jar}" />
+    <available property="jmx.jar.present" file="${jmx.jar}" />
+    <available property="jcert.jar.present" file="${jcert.jar}" />
+    <available property="jndi.jar.present" file="${jndi.jar}" />
+    <available property="jnet.jar.present" file="${jnet.jar}" />
+    <available property="jsse.jar.present" file="${jsse.jar}" />
+    <available property="jta.jar.present" file="${jta.jar}" />
+    <available property="junit.jar.present" file="${junit.jar}" />
+    <condition property="launcher.jars.present">
+      <and>
+        <equals arg1="${launcher.present}" arg2="true"/>
+        <equals arg1="${launcher.bootstrap.present}" arg2="true"/>
+      </and>
+    </condition>
+    <available property="ldap.jar.present" file="${ldap.jar}" />
+    <available property="logging.jar.present" file="${commons-logging.jar}" />
+    <available property="modeler.jar.present" file="${commons-modeler.jar}" />
+    <available property="pool.jar.present" file="${tomcat-dbcp.jar}" />
+    <available property="regexp.jar.present" file="${regexp.jar}" />
+    <available property="servlet-api.jar.present" file="${servlet-api.jar}" />
+    <available property="tomcat-util.jar.present" file="${tomcat-util.jar}" />
+    <available property="xercesImpl.jar.present" file="${xercesImpl.jar}"/>
+    <available property="xml-apis.jar.present" file="${xml-apis.jar}"/>
+    <condition property="xerces2.jars.present">
+      <and>
+        <equals arg1="${xercesImpl.jar.present}" arg2="true"/>
+        <equals arg1="${xml-apis.jar.present}" arg2="true"/>
+      </and>
+    </condition>
+
+    <!-- Conditional compilation flags (determined from the flags above) -->
+    <condition property="compile.dbcp">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <and>
+          <equals arg1="${dbcp.present}" arg2="true" />
+          <equals arg1="${pool.present}" arg2="true" />
+        </and>
+      </or>
+    </condition>
+    <condition property="compile.jaas">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${jaas.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="compile.javamail">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${javamail.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="compile.jmx">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <and>
+          <equals arg1="${jmx.present}" arg2="true" />
+          <equals arg1="${modeler.present}" arg2="true" />
+        </and>
+      </or>
+    </condition>
+    <condition property="compile.jndi">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${jndi.present}" arg2="true" />
+        <equals arg1="${jdk.1.3.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="compile.jsse">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${jsse.present}" arg2="true" />
+        <equals arg1="${jdk.1.4.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="compile.jta">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${jta.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="compile.junit">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${junit.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="compile.launcher">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <and>
+          <equals arg1="${launcher.present}" arg2="true" />
+          <equals arg1="${launcher.bootstrap.present}" arg2="true" />
+        </and>
+      </or>
+    </condition>
+    <condition property="compile.ldap">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${ldap.present}" arg2="true" />
+        <equals arg1="${jdk.1.3.present}" arg2="true" />
+      </or>
+    </condition>
+    <property name="compile.ssi" value="true"/>
+
+
+    <!-- Conditional copy flags (determined from the flags above) -->
+    <condition property="copy.dbcp.jar">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${dbcp.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="copy.jmx.jar">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${jmx.jar.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="copy.lang.jar">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${lang.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="copy.launcher.jars">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${launcher.jars.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="copy.modeler.jar">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${modeler.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="copy.logging.jar">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${logging.present}" arg2="true" />
+      </or>
+    </condition>
+    <condition property="copy.pool.jar">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${pool.present}" arg2="true" />
+      </or>
+    </condition>
+
+  </target>
+
+
+  <!-- =================== BUILD: Set compile flags ======================= -->
+  <target name="flags.display" depends="flags" unless="flags.hide">
+
+    <echo message="--- Build environment for Catalina ---" />
+
+    <echo message="If ${property_name} is displayed, then the property is not set)" />
+
+    <echo message="--- Build options ---" />
+    <echo message="full.dist=${full.dist}" />
+    <echo message="build.sysclasspath=${build.sysclasspath}" />
+    <echo message="compile.debug=${compile.debug}" />
+    <echo message="compile.deprecation=${compile.deprecation}" />
+    <echo message="compile.optimize=${compile.optimize}" />
+
+    <echo message="--- Ant Flags ---" />
+    <echo message="&lt;style&gt; task available (required)=${style.available}" />
+
+    <echo message="--- JDK ---" />
+    <echo message="jdk.1.2.present=${jdk.1.2.present}" />
+    <echo message="jdk.1.3.present=${jdk.1.3.present}" />
+    <echo message="jdk.1.4.present=${jdk.1.4.present}" />
+
+    <echo message="--- Source Dependencies ---" />
+    <echo message="jtc.home.present=${jtc.home.present}" />
+
+    <echo message="--- Required Libraries ---" />
+    <echo message="beanutils.present=${beanutils.present}" />
+    <echo message="collections.present=${collections.present}" />
+    <echo message="digester.present=${digester.present}" />
+    <echo message="jaxp.present=${jaxp.present}" />
+    <echo message="jndi.present=${jndi.present}" />
+    <echo message="logging.present=${logging.present}" />
+    <echo message="regexp.present=${regexp.present}" />
+
+    <echo message="--- Optional Libraries ---" />
+    <echo message="dbcp.present=${dbcp.present}" />
+    <echo message="fileupload.present=${fileupload.present}" />
+    <echo message="jaas.present=${jaas.present}" />
+    <echo message="javamail.present=${javamail.present}" />
+    <echo message="jmx.present=${jmx.present}" />
+    <echo message="jsse.present=${jsse.present}" />
+    <echo message="jta.present=${jta.present}" />
+    <echo message="junit.present=${junit.present}" />
+    <echo message="lang.present=${lang.present}" />
+    <echo message="launcher.present=${launcher.present}" />
+    <echo message="launcher.bootstrap.present=${launcher.bootstrap.present}" />
+    <echo message="ldap.present=${ldap.present}" />
+    <echo message="modeler.present=${modeler.present}"  />
+    <echo message="pool.present=${pool.present}" />
+
+    <echo message="--- Required JARs ---" />
+    <echo message="jndi.jar.present(except JDK 1.3+)=${jndi.jar.present}" />
+    <echo message="regexp.jar.present=${regexp.jar.present}" />
+    <echo message="servlet-api.jar.present=${servlet-api.jar.present}" />
+    <echo message="xerces2.jars.present(except JDK 1.4+)=${xerces2.jars.present}" />
+
+    <echo message="--- Optional JARs ---" />
+    <echo message="dbcp.jar.present=${dbcp.jar.present}" />
+    <echo message="fileupload.jar.present=${fileupload.jar.present}" />
+    <echo message="jaas.jar.present=${jaas.jar.present}" />
+    <echo message="javamail.jar.present=${javamail.jar.present}" />
+    <echo message="jdbc20ext.jar.present=${jdbc20ext.jar.present}" />
+    <echo message="jmx.jar.present=${jmx.jar.present}" />
+    <echo message="jta.jar.present=${jta.jar.present}" />
+    <echo message="junit.jar.present=${junit.jar.present}" />
+    <echo message="modeler.jar.present=${modeler.jar.present}" />
+    <echo message="pool.jar.present=${pool.jar.present}" />
+
+    <echo message="--- Conditional compilation flags ---" />
+    <echo message="compile.dbcp=${compile.dbcp}" />
+    <echo message="compile.jaas=${compile.jaas}" />
+    <echo message="compile.javamail=${compile.javamail}" />
+    <echo message="compile.jmx=${compile.jmx}" />
+    <echo message="compile.jndi=${compile.jndi}" />
+    <echo message="compile.jsse=${compile.jsse}" />
+    <echo message="compile.jta=${compile.jta}" />
+    <echo message="compile.junit=${compile.junit}" />
+    <echo message="compile.ldap=${compile.ldap}" />
+    <echo message="compile.ssi=${compile.ssi}" />
+
+    <echo message="--- Distribution flags ---" />
+    <echo message="copy.dbcp.jar=${copy.dbcp.jar}" />
+    <echo message="copy.jmx.jar=${copy.jmx.jar}" />
+    <echo message="copy.launcher.jars=${copy.launcher.jars}" />
+    <echo message="copy.logging.jar=${copy.logging.jar}" />
+    <echo message="copy.modeler.jar=${copy.modeler.jar}" />
+    <echo message="copy.pool.jar=${copy.pool.jar}" />
+
+  </target>
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+
+    <mkdir dir="${catalina.build}"/>
+    <mkdir dir="${catalina.build}/bin"/>
+    <mkdir dir="${catalina.build}/common/classes"/>
+    <mkdir dir="${catalina.build}/common/lib"/>
+    <mkdir dir="${catalina.build}/common/endorsed"/>
+    <mkdir dir="${catalina.build}/conf"/>
+    <mkdir dir="${catalina.build}/logs"/>
+    <mkdir dir="${classes.dir}"/>
+    <mkdir dir="${catalina.build}/server/lib"/>
+    <mkdir dir="${catalina.build}/shared/classes"/>
+    <mkdir dir="${catalina.build}/shared/lib"/>
+    <mkdir dir="${catalina.build}/work"/>
+    <mkdir dir="${catalina.build}/temp"/>
+
+  </target>
+
+
+  <!-- ======================== BUILD: Copy JARs ========================== -->
+  <target name="copy-dbcp.jar" if="copy.dbcp.jar">
+    <copy todir="${catalina.build}/common/lib" file="${commons-dbcp.jar}"/>
+  </target>
+  <target name="copy-fileupload.jar" if="fileupload.jar.present">
+  </target>
+  <target name="copy-jmx.jar" if="copy.jmx.jar">
+    <copy tofile="${catalina.build}/bin/jmx.jar" file="${jmx.jar}"/>
+  </target>
+  <target name="copy-launcher.jars" if="copy.launcher.jars">
+    <!-- <copy todir="${catalina.build}/common/lib" file="${ant.jar}"/> -->
+    <copy todir="${catalina.build}/bin" file="${commons-launcher.jar}"/>
+    <copy todir="${catalina.build}/bin" file="${commons-launcher.bootstrap.class}"/>
+    <copy todir="${catalina.build}/bin">
+      <fileset dir="src/bin" includes="*-using-launcher.*,launcher.properties,catalina.xml" />
+    </copy>
+  </target>
+  <target name="copy-modeler.jar" if="copy.modeler.jar">
+    <copy todir="${catalina.build}/server/lib" file="${commons-modeler.jar}"/>
+  </target>
+  <target name="copy-pool.jar" if="copy.pool.jar">
+    <copy todir="${catalina.build}/common/lib" file="${commons-pool.jar}"/>
+  </target>
+  <target name="copy-tomcat-util.jar">
+  	<!--
+    <copy todir="${catalina.build}/server/lib" file="${tomcat-util.jar}"/>
+    -->
+	<jar jarfile="${tomcat-util.jar}"
+             index="true">
+      <fileset dir="${catalina.build}/classes">
+        <include name="org/apache/tomcat/util/**"/>
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+     </fileset>
+    </jar>
+  </target>
+  <target name="copy-xerces2.jars">
+    <copy todir="${catalina.build}/common/endorsed" file="${xercesImpl.jar}"/>
+    <copy todir="${catalina.build}/common/endorsed" file="${xml-apis.jar}"/>
+  </target>
+
+
+  <!-- =================== BUILD: Copy Static Files ======================= -->
+  <target name="build-static" depends="flags,flags.display,build-prepare,copy-fileupload.jar,copy-launcher.jars,copy-modeler.jar">
+
+    <!-- Executable Commands -->
+    <copy todir="${catalina.build}/bin">
+      <fileset dir="src/bin" excludes="*-using-launcher.*,launcher.properties,catalina.xml,*-tasks.xml" />
+    </copy>
+    <fixcrlf srcdir="${catalina.build}/bin" includes="*.sh" eol="lf"/>
+    <fixcrlf srcdir="${catalina.build}/bin" includes="*.bat" eol="crlf"/>
+    <chmod perm="+x" dir="${catalina.build}/bin" includes="*.sh"/>
+
+    <!-- Common Extensions -->
+    <copy todir="${catalina.build}/bin"
+           file="${commons-logging-api.jar}"/>
+<!--
+    <copy todir="${catalina.build}/common/lib" file="${servlet-api.jar}"/>
+-->
+    <copy todir="${catalina.build}/common/lib"
+           file="${tomcat-dbcp.jar}" />
+
+    <!-- Configuration Files -->
+    <copy todir="${catalina.build}/conf">
+      <fileset dir="src/conf">
+        <exclude name="catalina.conf.xml" />
+      </fileset>
+    </copy>
+
+    <!-- JVM Temporary Directory README -->
+    <copy todir="${catalina.build}/temp"
+          file="src/temp/README.txt"/>
+
+    <!-- Catalina Server Libraries -->
+  	<!--
+    <copy todir="${catalina.build}/server/lib" file="${regexp.jar}"/>
+    -->
+
+  </target>
+
+
+  <!-- =================== BUILD: Build tomcat-util ======================= -->
+  <target name="build-tomcat-util">
+
+    <ant dir="${tomcat-util.home}" target="build-main">
+       <property name="jmx.jar" value="${jmx.jar}" />
+       <property name="puretls.jar" value="${puretls.jar}" />
+       <property name="jsse.lib" value="${jsse.lib}" />
+    </ant>
+
+    <copy todir="${catalina.build}/server/lib"
+           file="${tomcat-util.jar}"/>
+
+  </target>
+
+
+  <!-- ================ BUILD: Compile Catalina Components ================ -->
+  <target name="build-catalina" depends="build-catalina-core,build-catalina-optional"/>
+
+  <target name="build-catalina-core">
+    <!-- Compile internal server components -->
+    <javac srcdir="src/share" destdir="${classes.dir}"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           source="${compile.source}"
+           optimize="${compile.optimize}"
+           excludes="**/CVS/**">
+      <classpath refid="catalina.classpath" />
+
+      <exclude name="org/apache/catalina/util/ProcessHelper.java" 
+               unless="jdk.1.3.present"/>
+      <exclude name="org/apache/catalina/valves/CertificatesValve.java"
+               unless="compile.jsse"/>
+
+      <exclude name="org/apache/catalina/servlets/CGIServlet.java" />
+      <exclude name="org/apache/catalina/servlets/HTMLManagerServlet.java" />
+      <exclude name="org/apache/naming/factory/DbcpDataSourceFactory.java" />
+      <exclude name="org/apache/catalina/ssi/**"/>
+      <exclude name="org/apache/catalina/cluster/**"/>
+      <exclude name="org/apache/naming/factory/MailSessionFactory.java"/>
+      <exclude name="org/apache/naming/factory/SendMailFactory.java"/>
+      <exclude name="org/apache/catalina/launcher/**"/>
+      <exclude name="org/apache/catalina/valves/SemaphoreValve.java"/>
+    </javac>
+    <tstamp>
+        <format property="TODAY" pattern="MMM d yyyy" locale="en"/>
+        <format property="TSTAMP" pattern="hh:mm:ss"/>        
+    </tstamp>    
+    <!-- Copy static resource files -->
+    <filter token="VERSION" value="${version}"/>
+    <filter token="VERSION_NUMBER" value="${version.number}"/>
+    <filter token="VERSION_BUILT" value="${TODAY} ${TSTAMP}"/>
+    <copy todir="${classes.dir}" filtering="true" encoding="ISO-8859-1">
+      <fileset dir="src/share">
+        <exclude name="**/*.java"/>
+        <exclude name="**/*.ser"/>
+      </fileset>
+    </copy>
+    <copy todir="${classes.dir}">
+      <fileset dir="src/share">
+        <include name="**/*.ser"/>
+      </fileset>
+    </copy>
+
+  </target>
+
+	
+
+	
+  <target name="build-catalina-optional" unless="build.tomcat.base">
+    <!-- Compile internal server components -->
+    <javac srcdir="src/share" destdir="${classes.dir}"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           optimize="${compile.optimize}"
+           source="${compile.source}"
+           excludes="**/CVS/**">
+      <classpath refid="catalina.classpath" />
+      <exclude name="org/apache/naming/factory/MailSessionFactory.java"
+       unless="compile.javamail"/>
+      <exclude name="org/apache/naming/factory/SendMailFactory.java"
+       unless="compile.javamail"/>
+      <exclude name="org/apache/catalina/valves/CertificatesValve.java"
+       unless="compile.jsse"/>
+      <exclude name="org/apache/catalina/valves/SemaphoreValve.java" 
+       unless="jdk.1.5.present"/>
+    </javac>
+
+    <!-- Copy static resource files -->
+    <filter token="VERSION" value="${version}"/>
+    <copy todir="${classes.dir}" filtering="true">
+      <fileset dir="src/share">
+        <exclude name="**/*.java"/>
+      </fileset>
+    </copy>
+  </target>
+
+  <!-- ================== BUILD: Build tomcat-coyote ====================== -->
+  <target name="build-tomcat-coyote">
+
+    <ant dir="${tomcat-coyote.home}" target="compile">
+      <property name="catalina.home" value="${catalina.deploy}"/>
+      <property name="servlet.jar"   value="${servlet-api.jar}"/>
+      <property name="commons-logging.jar" value="${commons-logging.jar}"/>
+      <property name="commons-modeler.jar" value="${commons-modeler.jar}"/>
+      <property name="jmx.jar" value="${jmx.jar}"/>
+    </ant>
+
+  	<!--
+    <copy todir="${catalina.deploy}/server/lib"
+           file="${tomcat-coyote.jar}"/>
+    -->
+
+  </target>
+
+
+  <!-- ==================== BUILD: Build tomcat-jk ======================== -->
+  <target name="build-tomcat-jk">
+
+    <ant dir="${tomcat-jk.home}" target="build-main">
+      <property name="tomcat5.home" value="${catalina.deploy}"/>
+      <property name="commons-logging.jar" value="${commons-logging.jar}"/>
+      <property name="commons-modeler.jar" value="${commons-modeler.jar}"/>
+      <property name="jmx.jar" value="${jmx.jar}"/>
+    </ant>
+
+  	<!--
+    <copy tofile="${catalina.deploy}/server/lib/tomcat-ajp.jar"
+            file="${tomcat-jk2.jar}"/>
+    -->
+  	<!--
+    <copy todir="${catalina.deploy}/conf"
+           file="${jk2.properties}"/>
+    -->
+
+  </target>
+
+
+  <!-- ============== BUILD: Build tomcat-coyote-http11 =================== -->
+  <target name="build-tomcat-http11">
+
+    <ant dir="${tomcat-http11.home}" target="compile">
+      <property name="commons-logging.jar" value="${commons-logging.jar}"/>
+      <property name="commons-modeler.jar" value="${commons-modeler.jar}"/>
+      <property name="jmx.jar" value="${jmx.jar}"/>
+    </ant>
+  	<!--
+    <copy todir="${catalina.deploy}/server/lib"
+           file="${tomcat-http11.jar}"/>
+    -->
+
+  </target>
+
+
+  <!-- ================= BUILD: Compile All Server Components ============= -->
+  <target name="build-main" depends="build-static,build-tomcat-util,build-catalina">
+  </target>
+
+
+  <!-- ================ BUILD: Create Catalina Javadocs =================== -->
+  <target name="javadoc" unless="docs.uptodate" >
+    <delete dir="${catalina.build}/javadoc"/>
+    <mkdir dir="${catalina.build}/javadoc"/>
+
+    <!-- To fix Bugzilla 35880, we need to ensure files that use J2SE 5.0
+         syntax are not JavaDoc'ed.  Even though no other target uses
+         javadoc.sourcepath and I could rewrite the javadoc target below,
+         that might impact users who use javadoc.sourcepath in their own
+         build scripts.  Accordingly, let's try this ugly temporary
+         fix.  When we start building Tomcat with J2SE 5.0, we can remove
+         this ugliness.
+    -->
+    <move file="${jtc.home}/util/java/org/apache/tomcat/util/net/jsse/JSSE15SocketFactory.java"
+          tofile="${jtc.home}/util/java/org/apache/tomcat/util/net/jsse/JSSE15SocketFactory.temp" />
+
+    <javadoc packagenames="org.apache.*"
+      classpathref="catalina.classpath"
+      sourcepathref="javadoc.sourcepath"
+      destdir="${catalina.build}/javadoc"
+      author="true"
+      version="true"
+      windowtitle="Tomcat API Documentation"
+      doctitle="Tomcat API"
+      bottom="Copyright &#169; 2000-2005 Apache Software Foundation.  All Rights Reserved."
+      additionalparam="-breakiterator">
+    	<excludepackage name="org.apache.ajp.*" />
+    	<excludepackage name="org.apache.coyote.tomcat3" />
+    	<excludepackage name="org.apache.coyote.tomcat4" />
+    	<excludepackage name="org.apache.jk.apr" />
+
+    </javadoc>
+
+    <move file="${jtc.home}/util/java/org/apache/tomcat/util/net/jsse/JSSE15SocketFactory.temp"
+          tofile="${jtc.home}/util/java/org/apache/tomcat/util/net/jsse/JSSE15SocketFactory.java" />
+  </target>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+
+    <delete dir="${catalina.build}"/>
+
+    <ant dir="${tomcat-util.home}" target="clean"/>
+    <ant dir="${tomcat-coyote.home}" target="clean"/>
+    <ant dir="${tomcat-jk.home}" target="clean"/>
+    <ant dir="${tomcat-http11.home}" target="clean"/>
+
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean, build, and deploy Catalina component"/>
+
+
+  <!-- ================ TEST: Compile Unit Tests ========================== -->
+  <target name="build-tests" depends="build-main" if="junit.present">
+    <mkdir      dir="${catalina.build}/tests"/>
+    <!-- Compile unit test classes -->
+    <javac srcdir="src/test" destdir="${catalina.build}/tests"
+           deprecation="${compile.deprecation}"
+           debug="${compile.debug}"
+           optimize="off"
+           source="${compile.source}"
+           excludes="**/CVS/**">
+      <classpath refid="test.classpath"/>
+    </javac>
+  </target>
+
+
+  <!-- ==================== TEST: Execute Unit Tests ====================== -->
+  <target name="test" if="junit.present"
+   description="Run all unit test cases"
+   depends="build-tests,test-dir-context,test-util">
+  </target>
+
+  <target name="test-dir-context" if="junit.present">
+
+    <echo message="Running FileDirContext tests"/>
+    <java classname="${test.runner}" fork="yes"
+        failonerror="${test.failonerror}">
+      <sysproperty key="doc.base" value="${test.webapp}"/>
+      <arg value="org.apache.naming.resources.FileDirContextTestCase"/>
+      <classpath refid="test.classpath"/>
+    </java>
+
+    <echo message="Running WARDirContext tests"/>
+    <jar jarfile="${test.webapp.war}">
+      <fileset dir="${test.webapp}"/>
+    </jar>
+    <java classname="${test.runner}" fork="yes"
+        failonerror="${test.failonerror}">
+      <sysproperty key="doc.base" value="${test.webapp.war}"/>
+      <arg value="org.apache.naming.resources.WARDirContextTestCase"/>
+      <classpath refid="test.classpath"/>
+    </java>
+    <delete file="${test.webapp.war}"/>
+
+  </target>
+
+  <target name="test-util" if="junit.present">
+
+    <echo message="Running CookieTools tests"/>
+    <java classname="${test.runner}" fork="yes"
+        failonerror="${test.failonerror}">
+      <arg value="org.apache.catalina.util.CookieToolsTestCase"/>
+      <classpath refid="test.classpath"/>
+    </java>
+
+    <echo message="Running URL tests"/>
+    <java classname="${test.runner}" fork="yes"
+        failonerror="${test.failonerror}">
+      <arg value="org.apache.catalina.util.URLTestCase"/>
+      <classpath refid="test.classpath"/>
+    </java>
+
+  </target>
+
+
+  <!-- ====================== DEPLOY: Create Directories ================== -->
+  <target name="deploy-prepare">
+    <mkdir dir="${catalina.deploy}"/>
+    <mkdir dir="${catalina.deploy}/bin"/>
+    <mkdir dir="${catalina.deploy}/conf"/>
+    <mkdir dir="${catalina.deploy}/logs"/>
+    <mkdir dir="${catalina.deploy}/common/classes"/>
+    <mkdir dir="${catalina.deploy}/common/endorsed"/>
+    <mkdir dir="${catalina.deploy}/common/i18n"/>
+    <mkdir dir="${catalina.deploy}/common/lib"/>
+    <mkdir dir="${catalina.deploy}/server/classes"/>
+    <mkdir dir="${catalina.deploy}/server/lib"/>
+    <mkdir dir="${catalina.deploy}/work"/>
+    <mkdir dir="${catalina.deploy}/temp"/>
+  </target>
+
+
+  <!-- ====================== DEPLOY: Copy Static Files =================== -->
+  <target name="deploy-static" depends="build-main,deploy-prepare,deploy-static-only,deploy-libs"/>
+
+  <target name="deploy-static-only" depends="deploy-prepare">
+
+    <!-- Executable Commands -->
+    <copy todir="${catalina.deploy}/bin">
+      <fileset dir="src/bin" excludes="*-using-launcher.*,launcher.properties,catalina.xml" />
+    </copy>
+    <fixcrlf srcdir="${catalina.deploy}/bin" includes="*.sh"  eol="lf"/>
+    <fixcrlf srcdir="${catalina.deploy}/bin" includes="*.bat" eol="crlf"/>
+    <chmod perm="+x" dir="${catalina.build}/bin" includes="*.sh"/>
+
+    <!-- Configuration Files -->
+    <copy todir="${catalina.deploy}/conf">
+      <fileset dir="${catalina.build}/conf" />
+    </copy>
+
+  </target>
+
+  <target name="deploy-libs">
+
+    <!-- Common Extensions -->
+    <copy todir="${catalina.deploy}/common/endorsed">
+      <fileset dir="${catalina.build}/common/endorsed" />
+    </copy>
+
+    <copy todir="${catalina.deploy}/common/lib">
+      <fileset dir="${catalina.build}/common/lib" />
+    </copy>
+
+    <!-- Server Components -->
+    <copy todir="${catalina.deploy}/server/lib">
+      <fileset dir="${catalina.build}/server/lib" />
+    </copy>
+
+    <!-- Shared Extensions -->
+<!--
+    <copy todir="${catalina.deploy}/shared/lib">
+      <fileset dir="${catalina.build}/shared/lib" />
+    </copy>
+-->
+  </target>
+
+
+  <!-- ====================== DEPLOY: Create Catalina JARs ================ -->
+  <target name="catalina-jars" depends="build-static,deploy-prepare,flags,flags.display,build-catalina"
+          description="Build catalina jars">
+
+    <!-- Catalina Bootstrap JAR File. It is a very wrong idea to split packages in multiple jars !!! -->
+    <jar jarfile="${catalina.deploy}/bin/bootstrap.jar" 
+         manifest="etc/bootstrap.MF">
+      <fileset dir="${classes.dir}">
+        <include name="org/apache/catalina/startup/Bootstrap.class" />
+        <include name="org/apache/catalina/startup/catalina.properties" />
+        <include name="org/apache/catalina/startup/CatalinaProperties.class" />
+        <include name="org/apache/catalina/startup/ClassLoaderFactory.class" />
+        <include name="org/apache/catalina/startup/Tool.class" />
+        <include name="org/apache/catalina/loader/StandardClassLoader*.class" />
+        <include name="org/apache/catalina/loader/Extension.class" />
+        <include name="org/apache/catalina/loader/Reloader.class" />
+        <include name="org/apache/catalina/security/SecurityClassLoad.class" />
+        <include name="org/apache/catalina/launcher/CatalinaLaunchFilter.class" />
+        <include name="org/apache/naming/JndiPermission.class" />
+        <include name="org/apache/tomcat/util/compat/*" />
+
+      	<!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      </fileset>
+    </jar>
+
+    <!-- Catalina Main JAR File -->
+    <!-- Excluding files from here is worse than duplicating them - a loader that uses the
+         package name as index and includes both bootstrap and catalina will be confused -->
+
+    <jar jarfile="${catalina.deploy}/server/lib/catalina.jar">
+      <fileset dir="${classes.dir}">
+
+        <include name="org/apache/catalina/**" />
+
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+
+        <!-- Modules -->
+        <exclude name="org/apache/catalina/ant/**" />
+        <exclude name="org/apache/catalina/cluster/**" />
+        <exclude name="org/apache/catalina/launcher/**" />
+        <exclude name="org/apache/catalina/servlets/**" />
+        <exclude name="org/apache/catalina/startup/Bootstrap.class" />
+        <exclude name="org/apache/catalina/storeconfig/**" />
+        <exclude name="org/apache/catalina/ssi/**" />
+        <exclude name="org/apache/naming/**" />
+
+        <!-- Catalina-optional classes -->
+        <exclude name="org/apache/catalina/realm/DataSourceRealm.class" />
+        <exclude name="org/apache/catalina/realm/JAAS*" />
+        <exclude name="org/apache/catalina/realm/JDBC*" />
+        <exclude name="org/apache/catalina/realm/JNDI*" />
+        <exclude name="org/apache/catalina/realm/Memory*" />
+        <exclude name="org/apache/catalina/session/StoreBase.class" />
+        <exclude name="org/apache/catalina/session/*Store.class" />
+        <exclude name="org/apache/catalina/session/PersistentManager*" />
+        <exclude name="org/apache/catalina/util/CGIProcessEnvironment.class" />
+        <exclude name="org/apache/catalina/util/CookieTools.class" />
+        <exclude name="org/apache/catalina/util/DateTool.class" />
+        <exclude name="org/apache/catalina/util/DOMWriter.class" />
+        <exclude name="org/apache/catalina/util/FastDateFormat.class" />
+        <exclude name="org/apache/catalina/util/IOTools.class" />
+        <exclude name="org/apache/catalina/util/MIME2Java.class" />
+        <exclude name="org/apache/catalina/util/Process*" />
+        <exclude name="org/apache/catalina/util/Queue.class" />
+        <exclude name="org/apache/catalina/util/Strftime.class" />
+        <exclude name="org/apache/catalina/util/XMLWriter.class" />
+        <exclude name="org/apache/catalina/valves/ExtendedAccessLogValve.class" />
+        <exclude name="org/apache/catalina/valves/FastCommonAccessLogValve.class" />
+        <exclude name="org/apache/catalina/valves/FieldInfo.class" />
+        <exclude name="org/apache/catalina/valves/JDBCAccessLogValve.class" />
+        <exclude name="org/apache/catalina/valves/PersistentValve.class" />
+        <exclude name="org/apache/catalina/valves/Remote*" />
+        <exclude name="org/apache/catalina/valves/RequestDumperValve.class" />
+        <exclude name="org/apache/catalina/valves/RequestFilterValve.class" />
+        <exclude name="org/apache/catalina/valves/SemaphoreValve.class" />
+
+      </fileset>
+    </jar>
+
+    <jar jarfile="${catalina.deploy}/server/lib/catalina-optional.jar">
+      <fileset dir="${classes.dir}">
+
+        <include name="org/apache/catalina/realm/DataSourceRealm.class" />
+        <include name="org/apache/catalina/realm/JAAS*" />
+        <include name="org/apache/catalina/realm/JDBC*" />
+        <include name="org/apache/catalina/realm/JNDI*" />
+        <include name="org/apache/catalina/realm/Memory*" />
+        <include name="org/apache/catalina/session/StoreBase.class" />
+        <include name="org/apache/catalina/session/*Store.class" />
+        <include name="org/apache/catalina/session/PersistentManager*" />
+        <include name="org/apache/catalina/util/CGIProcessEnvironment.class" />
+        <include name="org/apache/catalina/util/CookieTools.class" />
+        <include name="org/apache/catalina/util/DateTool.class" />
+        <include name="org/apache/catalina/util/DOMWriter.class" />
+        <include name="org/apache/catalina/util/FastDateFormat.class" />
+        <include name="org/apache/catalina/util/IOTools.class" />
+        <include name="org/apache/catalina/util/MIME2Java.class" />
+        <include name="org/apache/catalina/util/Process*" />
+        <include name="org/apache/catalina/util/Queue.class" />
+        <include name="org/apache/catalina/util/Strftime.class" />
+        <include name="org/apache/catalina/util/XMLWriter.class" />
+        <include name="org/apache/catalina/valves/ExtendedAccessLogValve.class" />
+        <include name="org/apache/catalina/valves/FastCommonAccessLogValve.class" />
+        <include name="org/apache/catalina/valves/FieldInfo.class" />
+        <include name="org/apache/catalina/valves/JDBCAccessLogValve.class" />
+        <include name="org/apache/catalina/valves/PersistentValve.class" />
+        <include name="org/apache/catalina/valves/Remote*" />
+        <include name="org/apache/catalina/valves/RequestDumperValve.class" />
+        <include name="org/apache/catalina/valves/RequestFilterValve.class" />
+        <include name="org/apache/catalina/valves/SemaphoreValve.class" />
+
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+
+      </fileset>
+    </jar>
+
+    <!-- Catalina Ant Tasks JAR File -->
+    <jar jarfile="${catalina.deploy}/server/lib/catalina-ant.jar">
+      <fileset dir="${classes.dir}">
+        
+      	<include name="org/apache/catalina/ant/*" />
+        <include name="org/apache/catalina/util/Base64.class" />
+
+      	<!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      	
+      </fileset>
+    </jar>
+
+  	<!-- Catalina JMX Accessor Ant Tasks JAR File -->
+    <jar jarfile="${catalina.deploy}/server/lib/catalina-ant-jmx.jar">
+      <fileset dir="${classes.dir}">
+        
+      	<include name="org/apache/catalina/ant/jmx/*" />
+
+      	<!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      	
+      </fileset>
+    </jar>
+
+  	<!-- Naming - Factory JAR File -->
+    <jar jarfile="${catalina.deploy}/common/lib/naming-factory.jar" index="true">
+      <fileset dir="${classes.dir}">
+        <include name="org/apache/naming/**" />
+        <exclude name="org/apache/naming/resources/**" />
+        <exclude name="org/apache/naming/JndiPermission.class" />
+      	<exclude name="org/apache/naming/NameParserImpl.class" />
+      	<exclude name="org/apache/naming/NamingContextBindingsEnumeration.class" />
+      	<exclude name="org/apache/naming/NamingContextEnumeration.class" />
+      	<exclude name="org/apache/naming/NamingEntry.class" />
+      	<exclude name="org/apache/naming/StringManager.class" />
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      </fileset>
+    </jar>
+
+    <!-- Naming - Resources JAR File -->
+    <jar jarfile="${catalina.deploy}/common/lib/naming-resources.jar" index="true">
+      <fileset dir="${classes.dir}">
+        <include name="org/apache/naming/JndiPermission.class" />
+      	<include name="org/apache/naming/NameParserImpl.class" />
+      	<include name="org/apache/naming/NamingContextBindingsEnumeration.class" />
+      	<include name="org/apache/naming/NamingContextEnumeration.class" />
+      	<include name="org/apache/naming/NamingEntry.class" />
+      	<include name="org/apache/naming/StringManager.class" />
+        <include name="org/apache/naming/resources/**" />
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      </fileset>
+    </jar>
+
+    <!-- Servlets - CGI Servlet -->
+    <jar jarfile="${catalina.deploy}/server/lib/servlets-cgi.renametojar" index="true">
+      <fileset dir="${classes.dir}">
+        <include name="org/apache/catalina/servlets/CGI*" />
+      </fileset>
+    </jar>
+
+    <!-- Servlets - Default File-Serving Servlet -->
+    <jar jarfile="${catalina.deploy}/server/lib/servlets-default.jar" index="true">
+      <fileset dir="${classes.dir}">
+        <include name="org/apache/catalina/servlets/Default*" />
+        <include name="org/apache/catalina/servlets/Constants.class" />
+        <include name="org/apache/catalina/servlets/LocalStrings.properties" />
+      </fileset>
+    </jar>
+
+    <!-- Servlets - Invoker Servlet -->
+    <jar jarfile="${catalina.deploy}/server/lib/servlets-invoker.jar" index="true">
+      <fileset dir="${classes.dir}">
+        <include name="org/apache/catalina/servlets/Invoker*" />
+      </fileset>
+    </jar>
+
+    <!-- Servlets - SSI Servlet -->
+    <jar jarfile="${catalina.deploy}/server/lib/servlets-ssi.renametojar" index="true">
+      <fileset dir="${classes.dir}">
+        <include name="org/apache/catalina/ssi/**" />
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      </fileset>
+    </jar>
+
+    <!-- Servlets - Webdav Servlet -->
+    <jar jarfile="${catalina.deploy}/server/lib/servlets-webdav.jar" index="true">
+      <fileset dir="${classes.dir}">
+        <include name="org/apache/catalina/servlets/Webdav*" />
+      </fileset>
+    </jar>
+
+  </target>
+
+  <target name="deploy-catalina" depends="deploy-static,build-main,catalina-jars"
+   description="Build and deploy Catalina component">
+  </target>
+
+
+  <!-- ====================== DEPLOY: Create Catalina JARs ================ -->
+  <target name="deploy" depends="deploy-catalina,build-tomcat-coyote,build-tomcat-jk,build-tomcat-http11"
+   description="Build and deploy Catalina">
+  </target>
+
+
+  <!-- ================ DIST: Create Distribution ========================= -->
+  <target name="dist" depends="build-main"
+   description="Create binary distribution">
+
+    <!-- Executable Commands -->
+    <mkdir dir="${catalina.dist}/bin"/>
+    <copy todir="${catalina.dist}/bin">
+      <fileset dir="src/bin" excludes="*-using-launcher.*,launcher.properties,catalina.xml" />
+    </copy>
+    <fixcrlf srcdir="${catalina.dist}/bin" includes="*.sh" eol="lf"/>
+    <fixcrlf srcdir="${catalina.dist}/bin" includes="*.bat" eol="crlf"/>
+    <chmod perm="+x" dir="${catalina.build}/bin" includes="*.sh"/>
+
+    <!-- Common Extensions -->    
+    <mkdir dir="${catalina.dist}/common/classes"/>
+    <copy todir="${catalina.dist}/common/classes">
+      <fileset dir="${catalina.build}/common/classes" />
+    </copy>
+    <mkdir dir="${catalina.dist}/common/lib"/>
+    <copy todir="${catalina.dist}/common/lib">
+      <fileset dir="${catalina.build}/common/lib" />
+    </copy>
+
+    <!-- Configuration Files -->
+    <mkdir dir="${catalina.dist}/conf"/>
+    <copy todir="${catalina.dist}/conf">
+      <fileset dir="${catalina.build}/conf" />
+    </copy>
+
+    <!-- Server Components -->
+    <mkdir dir="${catalina.dist}/server/classes"/>
+    <!-- Do we want to copy the classes ? 
+    <copy todir="${catalina.dist}/server/classes">
+      <fileset dir="${classes.dir}" />
+    </copy>
+    -->
+    <mkdir dir="${catalina.dist}/server/lib"/>
+    <copy todir="${catalina.dist}/server/lib">
+      <fileset dir="${catalina.build}/server/lib" />
+    </copy>
+
+    <!-- Shared Extensions -->
+    <mkdir dir="${catalina.dist}/shared/classes"/>
+    <copy todir="${catalina.dist}/shared/classes">
+      <fileset dir="${catalina.build}/shared/classes" />
+    </copy>
+    <mkdir dir="${catalina.dist}/shared/lib"/>
+    <copy todir="${catalina.dist}/shared/lib">
+      <fileset dir="${catalina.build}/shared/lib" />
+    </copy>
+
+  </target>
+
+
+  <!-- ======================== DIST: Clean Directory ===================== -->
+  <target name="dist-clean">
+    <delete dir="${catalina.dist}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean, dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
diff --git a/container/catalina/etc/bootstrap.MF b/container/catalina/etc/bootstrap.MF
new file mode 100644
index 0000000..e5d6be8
--- /dev/null
+++ b/container/catalina/etc/bootstrap.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Main-Class: org.apache.catalina.startup.Bootstrap
+Class-Path: jmx.jar commons-daemon.jar commons-logging-api.jar tomcat-juli.jar
+Specification-Title: Catalina
+Specification-Version: 1.0
\ No newline at end of file
diff --git a/container/catalina/src/bin/catalina-tasks.xml b/container/catalina/src/bin/catalina-tasks.xml
new file mode 100644
index 0000000..256948c
--- /dev/null
+++ b/container/catalina/src/bin/catalina-tasks.xml
@@ -0,0 +1,20 @@
+<!--
+  XML file for importing Catalina ant tasks.
+  <import file="${catalina.home}/bin/catalina-tasks.xml"/>
+-->
+
+<project name="catalina-tasks">
+  <description>Catalina Ant Manager and JSPC Tasks</description>
+  <!-- set catalina.home if it's not already set -->
+  <dirname property="catalina.home.bin.dir" file="${ant.file.catalina-tasks}"/>
+  <property name="catalina.home" value="${catalina.home.bin.dir}/.."/>
+  <taskdef resource="org/apache/catalina/ant/catalina.tasks">
+    <classpath>
+      <fileset file="${catalina.home}/bin/commons-logging-api.jar"/>
+      <fileset file="${catalina.home}/common/lib/jasper-compiler.jar"/>
+      <fileset file="${catalina.home}/common/lib/jasper-runtime.jar"/>
+      <fileset file="${catalina.home}/common/lib/servlet-api.jar"/>
+      <fileset file="${catalina.home}/server/lib/catalina-ant.jar"/>
+    </classpath>
+  </taskdef>
+</project>
diff --git a/container/catalina/src/bin/catalina.bat b/container/catalina/src/bin/catalina.bat
new file mode 100644
index 0000000..da1bd3b
--- /dev/null
+++ b/container/catalina/src/bin/catalina.bat
@@ -0,0 +1,196 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+rem ---------------------------------------------------------------------------
+rem Start/Stop Script for the CATALINA Server
+rem
+rem Environment Variable Prequisites
+rem
+rem   CATALINA_HOME   May point at your Catalina "build" directory.
+rem
+rem   CATALINA_BASE   (Optional) Base directory for resolving dynamic portions
+rem                   of a Catalina installation.  If not present, resolves to
+rem                   the same directory that CATALINA_HOME points to.
+rem
+rem   CATALINA_OPTS   (Optional) Java runtime options used when the "start",
+rem                   "stop", or "run" command is executed.
+rem
+rem   CATALINA_TMPDIR (Optional) Directory path location of temporary directory
+rem                   the JVM should use (java.io.tmpdir).  Defaults to
+rem                   %CATALINA_BASE%\temp.
+rem
+rem   JAVA_HOME       Must point at your Java Development Kit installation.
+rem
+rem   JAVA_OPTS       (Optional) Java runtime options used when the "start",
+rem                   "stop", or "run" command is executed.
+rem
+rem   JSSE_HOME       (Optional) May point at your Java Secure Sockets Extension
+rem                   (JSSE) installation, whose JAR files will be added to the
+rem                   system class path used to start Tomcat.
+rem
+rem   JPDA_TRANSPORT  (Optional) JPDA transport used when the "jpda start"
+rem                   command is executed. The default is "dt_shmem".
+rem
+rem   JPDA_ADDRESS    (Optional) Java runtime options used when the "jpda start"
+rem                   command is executed. The default is "jdbconn".
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Guess CATALINA_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%CATALINA_HOME%" == "" goto gotHome
+set CATALINA_HOME=%CURRENT_DIR%
+if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
+cd ..
+set CATALINA_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
+echo The CATALINA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+rem Get standard environment variables
+if exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat"
+
+rem Get standard Java environment variables
+if exist "%CATALINA_HOME%\bin\setclasspath.bat" goto okSetclasspath
+echo Cannot find %CATALINA_HOME%\bin\setclasspath.bat
+echo This file is needed to run this program
+goto end
+:okSetclasspath
+set BASEDIR=%CATALINA_HOME%
+call "%CATALINA_HOME%\bin\setclasspath.bat"
+
+rem Add on extra jar files to CLASSPATH
+if "%JSSE_HOME%" == "" goto noJsse
+set CLASSPATH=%CLASSPATH%;%JSSE_HOME%\lib\jcert.jar;%JSSE_HOME%\lib\jnet.jar;%JSSE_HOME%\lib\jsse.jar
+:noJsse
+set CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\bootstrap.jar
+
+if not "%CATALINA_BASE%" == "" goto gotBase
+set CATALINA_BASE=%CATALINA_HOME%
+:gotBase
+
+if not "%CATALINA_TMPDIR%" == "" goto gotTmpdir
+set CATALINA_TMPDIR=%CATALINA_BASE%\temp
+:gotTmpdir
+
+if not exist "%CATALINA_HOME%\bin\tomcat-juli.jar" goto noJuli
+set JAVA_OPTS=%JAVA_OPTS% -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file="${catalina.base}\conf\logging.properties"
+:noJuli
+
+rem ----- Execute The Requested Command ---------------------------------------
+
+echo Using CATALINA_BASE:   %CATALINA_BASE%
+echo Using CATALINA_HOME:   %CATALINA_HOME%
+echo Using CATALINA_TMPDIR: %CATALINA_TMPDIR%
+echo Using JAVA_HOME:       %JAVA_HOME%
+
+set _EXECJAVA=%_RUNJAVA%
+set MAINCLASS=org.apache.catalina.startup.Bootstrap
+set ACTION=start
+set SECURITY_POLICY_FILE=
+set DEBUG_OPTS=
+set JPDA=
+
+if not ""%1"" == ""jpda"" goto noJpda
+set JPDA=jpda
+if not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransport
+set JPDA_TRANSPORT=dt_shmem
+:gotJpdaTransport
+if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress
+set JPDA_ADDRESS=jdbconn
+:gotJpdaAddress
+shift
+:noJpda
+
+if ""%1"" == ""debug"" goto doDebug
+if ""%1"" == ""run"" goto doRun
+if ""%1"" == ""start"" goto doStart
+if ""%1"" == ""stop"" goto doStop
+if ""%1"" == ""version"" goto doVersion
+
+echo Usage:  catalina ( commands ... )
+echo commands:
+echo   debug             Start Catalina in a debugger
+echo   debug -security   Debug Catalina with a security manager
+echo   jpda start        Start Catalina under JPDA debugger
+echo   run               Start Catalina in the current window
+echo   run -security     Start in the current window with security manager
+echo   start             Start Catalina in a separate window
+echo   start -security   Start in a separate window with security manager
+echo   stop              Stop Catalina
+echo   version           What version of tomcat are you running?
+goto end
+
+:doDebug
+shift
+set _EXECJAVA=%_RUNJDB%
+set DEBUG_OPTS=-sourcepath "%CATALINA_HOME%\..\..\jakarta-tomcat-catalina\catalina\src\share"
+if not ""%1"" == ""-security"" goto execCmd
+shift
+echo Using Security Manager
+set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy
+goto execCmd
+
+:doRun
+shift
+if not ""%1"" == ""-security"" goto execCmd
+shift
+echo Using Security Manager
+set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy
+goto execCmd
+
+:doStart
+shift
+if not "%OS%" == "Windows_NT" goto noTitle
+set _EXECJAVA=start "Tomcat" %_RUNJAVA%
+goto gotTitle
+:noTitle
+set _EXECJAVA=start %_RUNJAVA%
+:gotTitle
+if not ""%1"" == ""-security"" goto execCmd
+shift
+echo Using Security Manager
+set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy
+goto execCmd
+
+:doStop
+shift
+set ACTION=stop
+goto execCmd
+
+:doVersion
+%_EXECJAVA% -classpath "%CATALINA_HOME%\server\lib\catalina.jar" org.apache.catalina.util.ServerInfo
+goto end
+
+
+:execCmd
+rem Get remaining unshifted command line arguments and save them in the
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+rem Execute Java with the applicable properties
+if not "%JPDA%" == "" goto doJpda
+if not "%SECURITY_POLICY_FILE%" == "" goto doSecurity
+%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+goto end
+:doSecurity
+%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+goto end
+:doJpda
+if not "%SECURITY_POLICY_FILE%" == "" goto doSecurityJpda
+%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% -Xdebug -Xrunjdwp:transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=n %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+goto end
+:doSecurityJpda
+%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% -Xdebug -Xrunjdwp:transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=n %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+goto end
+
+:end
diff --git a/container/catalina/src/bin/catalina.sh b/container/catalina/src/bin/catalina.sh
new file mode 100644
index 0000000..35e45d8
--- /dev/null
+++ b/container/catalina/src/bin/catalina.sh
@@ -0,0 +1,310 @@
+#!/bin/sh
+# -----------------------------------------------------------------------------
+# Start/Stop Script for the CATALINA Server
+#
+# Environment Variable Prequisites
+#
+#   CATALINA_HOME   May point at your Catalina "build" directory.
+#
+#   CATALINA_BASE   (Optional) Base directory for resolving dynamic portions
+#                   of a Catalina installation.  If not present, resolves to
+#                   the same directory that CATALINA_HOME points to.
+#
+#   CATALINA_OPTS   (Optional) Java runtime options used when the "start",
+#                   "stop", or "run" command is executed.
+#
+#   CATALINA_TMPDIR (Optional) Directory path location of temporary directory
+#                   the JVM should use (java.io.tmpdir).  Defaults to
+#                   $CATALINA_BASE/temp.
+#
+#   JAVA_HOME       Must point at your Java Development Kit installation.
+#                   Required to run the with the "debug" or "javac" argument.
+#
+#   JRE_HOME        Must point at your Java Development Kit installation.
+#                   Defaults to JAVA_HOME if empty.
+#
+#   JAVA_OPTS       (Optional) Java runtime options used when the "start",
+#                   "stop", or "run" command is executed.
+#
+#   JPDA_TRANSPORT  (Optional) JPDA transport used when the "jpda start"
+#                   command is executed. The default is "dt_socket".
+#
+#   JPDA_ADDRESS    (Optional) Java runtime options used when the "jpda start"
+#                   command is executed. The default is 8000.
+#
+#   JSSE_HOME       (Optional) May point at your Java Secure Sockets Extension
+#                   (JSSE) installation, whose JAR files will be added to the
+#                   system class path used to start Tomcat.
+#
+#   CATALINA_PID    (Optional) Path of the file which should contains the pid
+#                   of catalina startup java process, when start (fork) is used
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false
+os400=false
+case "`uname`" in
+CYGWIN*) cygwin=true;;
+OS400*) os400=true;;
+esac
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ]; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+
+# Get standard environment variables
+PRGDIR=`dirname "$PRG"`
+
+# Only set CATALINA_HOME if not already set
+[ -z "$CATALINA_HOME" ] && CATALINA_HOME=`cd "$PRGDIR/.." ; pwd`
+
+if [ -r "$CATALINA_HOME"/bin/setenv.sh ]; then
+  . "$CATALINA_HOME"/bin/setenv.sh
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin; then
+  [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$JRE_HOME" ] && JRE_HOME=`cygpath --unix "$JRE_HOME"`
+  [ -n "$CATALINA_HOME" ] && CATALINA_HOME=`cygpath --unix "$CATALINA_HOME"`
+  [ -n "$CATALINA_BASE" ] && CATALINA_BASE=`cygpath --unix "$CATALINA_BASE"`
+  [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+  [ -n "$JSSE_HOME" ] && JSSE_HOME=`cygpath --absolute --unix "$JSSE_HOME"`
+fi
+
+# For OS400
+if $os400; then
+  # Set job priority to standard for interactive (interactive - 6) by using
+  # the interactive priority - 6, the helper threads that respond to requests
+  # will be running at the same priority as interactive jobs.
+  COMMAND='chgjob job('$JOBNAME') runpty(6)'
+  system $COMMAND
+
+  # Enable multi threading
+  export QIBM_MULTI_THREADED=Y
+fi
+
+# Get standard Java environment variables
+if $os400; then
+  # -r will Only work on the os400 if the files are:
+  # 1. owned by the user
+  # 2. owned by the PRIMARY group of the user
+  # this will not work if the user belongs in secondary groups
+  BASEDIR="$CATALINA_HOME"
+  . "$CATALINA_HOME"/bin/setclasspath.sh 
+else
+  if [ -r "$CATALINA_HOME"/bin/setclasspath.sh ]; then
+    BASEDIR="$CATALINA_HOME"
+    . "$CATALINA_HOME"/bin/setclasspath.sh
+  else
+    echo "Cannot find $CATALINA_HOME/bin/setclasspath.sh"
+    echo "This file is needed to run this program"
+    exit 1
+  fi
+fi
+
+# Add on extra jar files to CLASSPATH
+if [ -n "$JSSE_HOME" ]; then
+  CLASSPATH="$CLASSPATH":"$JSSE_HOME"/lib/jcert.jar:"$JSSE_HOME"/lib/jnet.jar:"$JSSE_HOME"/lib/jsse.jar
+fi
+CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/bootstrap.jar:"$CATALINA_HOME"/bin/commons-logging-api.jar
+
+if [ -z "$CATALINA_BASE" ] ; then
+  CATALINA_BASE="$CATALINA_HOME"
+fi
+
+if [ -z "$CATALINA_TMPDIR" ] ; then
+  # Define the java.io.tmpdir to use for Catalina
+  CATALINA_TMPDIR="$CATALINA_BASE"/temp
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  JAVA_HOME=`cygpath --absolute --windows "$JAVA_HOME"`
+  JRE_HOME=`cygpath --absolute --windows "$JRE_HOME"`
+  CATALINA_HOME=`cygpath --absolute --windows "$CATALINA_HOME"`
+  CATALINA_BASE=`cygpath --absolute --windows "$CATALINA_BASE"`
+  CATALINA_TMPDIR=`cygpath --absolute --windows "$CATALINA_TMPDIR"`
+  CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$JSSE_HOME" ] && JSSE_HOME=`cygpath --absolute --windows "$JSSE_HOME"`
+  JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"`
+fi
+
+# Set juli LogManager if it is present
+if [ -r "$CATALINA_HOME"/bin/tomcat-juli.jar ]; then
+  JAVA_OPTS="$JAVA_OPTS "-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager" "-Djava.util.logging.config.file="$CATALINA_BASE/conf/logging.properties"
+fi
+
+# ----- Execute The Requested Command -----------------------------------------
+
+echo "Using CATALINA_BASE:   $CATALINA_BASE"
+echo "Using CATALINA_HOME:   $CATALINA_HOME"
+echo "Using CATALINA_TMPDIR: $CATALINA_TMPDIR"
+if [ "$1" = "debug" -o "$1" = "javac" ] ; then
+  echo "Using JAVA_HOME:       $JAVA_HOME"
+else
+  echo "Using JRE_HOME:       $JRE_HOME"
+fi
+
+if [ "$1" = "jpda" ] ; then
+  if [ -z "$JPDA_TRANSPORT" ]; then
+    JPDA_TRANSPORT="dt_socket"
+  fi
+  if [ -z "$JPDA_ADDRESS" ]; then
+    JPDA_ADDRESS="8000"
+  fi
+  if [ -z "$JPDA_OPTS" ]; then
+    JPDA_OPTS="-Xdebug -Xrunjdwp:transport=$JPDA_TRANSPORT,address=$JPDA_ADDRESS,server=y,suspend=n"
+  fi
+  CATALINA_OPTS="$CATALINA_OPTS $JPDA_OPTS"
+  shift
+fi
+
+if [ "$1" = "debug" ] ; then
+  if $os400; then
+    echo "Debug command not available on OS400"
+    exit 1
+  else
+    shift
+    if [ "$1" = "-security" ] ; then
+      echo "Using Security Manager"
+      shift
+      exec "$_RUNJDB" $JAVA_OPTS $CATALINA_OPTS \
+        -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+        -sourcepath "$CATALINA_HOME"/../../jakarta-tomcat-catalina/catalina/src/share \
+        -Djava.security.manager \
+        -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
+        -Dcatalina.base="$CATALINA_BASE" \
+        -Dcatalina.home="$CATALINA_HOME" \
+        -Djava.io.tmpdir="$CATALINA_TMPDIR" \
+        org.apache.catalina.startup.Bootstrap "$@" start
+    else
+      exec "$_RUNJDB" $JAVA_OPTS $CATALINA_OPTS \
+        -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+        -sourcepath "$CATALINA_HOME"/../../jakarta-tomcat-catalina/catalina/src/share \
+        -Dcatalina.base="$CATALINA_BASE" \
+        -Dcatalina.home="$CATALINA_HOME" \
+        -Djava.io.tmpdir="$CATALINA_TMPDIR" \
+        org.apache.catalina.startup.Bootstrap "$@" start
+    fi
+  fi
+
+elif [ "$1" = "run" ]; then
+
+  shift
+  if [ "$1" = "-security" ] ; then
+    echo "Using Security Manager"
+    shift
+    exec "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \
+      -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+      -Djava.security.manager \
+      -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
+      -Dcatalina.base="$CATALINA_BASE" \
+      -Dcatalina.home="$CATALINA_HOME" \
+      -Djava.io.tmpdir="$CATALINA_TMPDIR" \
+      org.apache.catalina.startup.Bootstrap "$@" start
+  else
+    exec "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \
+      -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+      -Dcatalina.base="$CATALINA_BASE" \
+      -Dcatalina.home="$CATALINA_HOME" \
+      -Djava.io.tmpdir="$CATALINA_TMPDIR" \
+      org.apache.catalina.startup.Bootstrap "$@" start
+  fi
+
+elif [ "$1" = "start" ] ; then
+
+  shift
+  touch "$CATALINA_BASE"/logs/catalina.out
+  if [ "$1" = "-security" ] ; then
+    echo "Using Security Manager"
+    shift
+    "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \
+      -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+      -Djava.security.manager \
+      -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
+      -Dcatalina.base="$CATALINA_BASE" \
+      -Dcatalina.home="$CATALINA_HOME" \
+      -Djava.io.tmpdir="$CATALINA_TMPDIR" \
+      org.apache.catalina.startup.Bootstrap "$@" start \
+      >> "$CATALINA_BASE"/logs/catalina.out 2>&1 &
+
+      if [ ! -z "$CATALINA_PID" ]; then
+        echo $! > $CATALINA_PID
+      fi
+  else
+    "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \
+      -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+      -Dcatalina.base="$CATALINA_BASE" \
+      -Dcatalina.home="$CATALINA_HOME" \
+      -Djava.io.tmpdir="$CATALINA_TMPDIR" \
+      org.apache.catalina.startup.Bootstrap "$@" start \
+      >> "$CATALINA_BASE"/logs/catalina.out 2>&1 &
+
+      if [ ! -z "$CATALINA_PID" ]; then
+        echo $! > $CATALINA_PID
+      fi
+  fi
+
+elif [ "$1" = "stop" ] ; then
+
+  shift
+  FORCE=0
+  if [ "$1" = "-force" ]; then
+    shift
+    FORCE=1
+  fi
+
+  "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \
+    -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+    -Dcatalina.base="$CATALINA_BASE" \
+    -Dcatalina.home="$CATALINA_HOME" \
+    -Djava.io.tmpdir="$CATALINA_TMPDIR" \
+    org.apache.catalina.startup.Bootstrap "$@" stop
+
+  if [ $FORCE -eq 1 ]; then
+    if [ ! -z "$CATALINA_PID" ]; then
+       echo "Killing: `cat $CATALINA_PID`"
+       kill -9 `cat $CATALINA_PID`
+    fi
+  fi
+
+elif [ "$1" = "version" ] ; then
+
+    "$_RUNJAVA"   \
+      -classpath "$CATALINA_HOME/server/lib/catalina.jar" \
+      org.apache.catalina.util.ServerInfo
+
+else
+
+  echo "Usage: catalina.sh ( commands ... )"
+  echo "commands:"
+  if $os400; then
+    echo "  debug             Start Catalina in a debugger (not available on OS400)"
+    echo "  debug -security   Debug Catalina with a security manager (not available on OS400)"
+  else
+    echo "  debug             Start Catalina in a debugger"
+    echo "  debug -security   Debug Catalina with a security manager"
+  fi
+  echo "  jpda start        Start Catalina under JPDA debugger"
+  echo "  run               Start Catalina in the current window"
+  echo "  run -security     Start in the current window with security manager"
+  echo "  start             Start Catalina in a separate window"
+  echo "  start -security   Start in a separate window with security manager"
+  echo "  stop              Stop Catalina"
+  echo "  stop -force       Stop Catalina (followed by kill -KILL)"
+  echo "  version           What version of tomcat are you running?"
+  exit 1
+
+fi
diff --git a/container/catalina/src/bin/catalina.xml b/container/catalina/src/bin/catalina.xml
new file mode 100644
index 0000000..1e78d64
--- /dev/null
+++ b/container/catalina/src/bin/catalina.xml
@@ -0,0 +1,234 @@
+<!--
+  XML file for launching Catalina applications using ant.
+-->
+
+<project name="Catalina Launcher" default="catalina" basedir=".">
+  <property file="${user.home}/.tomcat5.properties"/>
+
+  <!-- Set the application home to the parent directory of this directory -->
+  <property name="catalina.home" location="${basedir}/.."/>
+  <property name="bootstrap.jar" location="${catalina.home}/bin/bootstrap.jar"/>
+
+  <!-- Import the user's custom properties -->
+  <property file="${catalina.home}/bin/catalina.properties"/> <!-- XXX shold it be conf ?? -->
+  <property file="${catalina.home}/conf/catalina.properties"/> <!-- XXX shold it be conf ?? -->
+
+
+  <!-- Set user configurable properties -->
+  <property name="jsse.home" location="${catalina.home}"/>
+  <property name="catalina.base" location="${catalina.home}"/>
+  <property name="catalina.tmpdir" location="${catalina.base}/temp"/>
+  <property name="catalina.out" location="${catalina.base}/logs/catalina.out"/>
+  <property name="catalina.policy" location="${catalina.base}/conf/catalina.policy"/>
+  <property name="catalina.jvm.args" value="-Dsun.io.useCanonCaches=false"/>
+
+  <property name="catalina.source.path" value="${catalina.home}/../../jakarta-servletapi-5/src/share:${catalina.home}/../../jakarta-tomcat-jasper/jasper2/src/share:${catalina.home}/../../jakarta-tomcat-connectors/coyote/src/java:${catalina.home}/../../jakarta-tomcat-catalina/catalina/src/share"/>
+
+
+  <!-- Build the classpath relative to the application home -->
+  <path id="base.class.path">
+    <pathelement location="${bootstrap.jar}"/>
+    <pathelement path="${jsse.home}/lib/jsse.jar:${jsse.home}/lib/jcert.jar:${jsse.home}/lib/jnet.jar"/>
+  </path>
+
+  <property name="basedir" location="."/>
+  
+  <property name="tools.jar" location="${java.home}/../lib/tools.jar" />
+
+  <path id="tomcatcp" >
+    <pathelement location="${catalina.home}/bin/bootstrap.jar"/>
+    <!-- 
+    <fileset dir="${catalina.home}/common/lib" includes="*.jar"/>
+    <fileset dir="${catalina.home}/server/lib" includes="*.jar"/>
+    <pathelement location="${catalina.home}/common/classes"/>
+    -->
+    <!-- 
+       <pathelement location="${ant.home}/lib/xercesImpl.jar" />
+       <pathelement location="${ant.home}/lib/xml-apis.jar" />
+    -->
+    <pathelement location="${ant.home}/lib/ant.jar" />
+    <pathelement location="${tools.jar}" />
+  </path>
+ 
+
+  <!-- =================== Initialization/helpers ================== -->
+
+
+  <target name="init"
+          description="Display configuration and conditional compilation flags">
+  </target>
+
+  <target name="init-launcher" >
+    <!-- Build the sysproperties relative to the application home -->
+    <syspropertyset id="base.sys.properties">
+      <sysproperty key="java.endorsed.dirs" file="${catalina.home}/common/endorsed"/>
+      <sysproperty key="java.io.tmpdir" file="${catalina.tmpdir}"/>
+      <sysproperty key="catalina.home" file="${catalina.home}"/>
+      <sysproperty key="catalina.base" file="${catalina.base}"/>
+    </syspropertyset>
+
+    <!-- Build the standard jvmargs -->
+    <jvmargset id="base.jvm.args">
+      <jvmarg line="${catalina.jvm.args}"/>
+      <jvmarg value="-Xdebug" if="jpda.settings"/>
+      <jvmarg value="-Xrunjdwp:${jpda.settings}" if="jpda.settings"/>
+      <jvmarg value="-sourcepath" if="jdb"/>
+      <jvmarg path="${catalina.source.path}" if="jdb"/>
+    </jvmargset>
+  </target>
+
+  <target name="echo-config" >
+    <echo>TOMCAT_HOME=${catalina.home}</echo>
+    <echo>CLASSPATH=${toString:tomcatcp}</echo>
+  </target>
+
+  <target name="help" >
+    <echo>
+  To run any of the applications in the JDB debugger, execute the Launcher with
+  a "-Ddebug=true" argument.
+
+  To run any of the applications in JPDA mode, execute the Launcher with a
+  "-Djpda=true" argument.
+   </echo>
+  </target>
+
+  <!-- Target that sets JDB properties when the "debug" property is set -->
+  <target name="setjdb" description="Set JDB properties" if="debug">
+    <property name="jdb" value="true"/>
+  </target>
+
+  <!-- Target that sets JPDA properties when the "jpda" property is set -->
+  <target name="setjpda" description="Set JPDA properties" if="jpda">
+    <condition property="jpda.transport" value="dt_shmem">
+      <os family="windows"/>
+    </condition>
+    <condition property="jpda.transport" value="dt_socket">
+      <not>
+        <os family="windows"/>
+      </not>
+    </condition>
+    <condition property="jpda.address" value="jdbconn">
+      <equals arg1="${jpda.transport}" arg2="dt_shmem"/>
+    </condition>
+    <condition property="jpda.address" value="8000">
+      <not>
+        <equals arg1="${jpda.transport}" arg2="dt_shmem"/>
+      </not>
+    </condition>
+    <property name="jpda.suspend" value="y"/>
+    <property name="jpda.settings" value="transport=${jpda.transport},address=${jpda.address},server=y,suspend=${jpda.suspend}"/>
+  </target>
+
+  <!-- =================== Launcher-based ================== -->
+
+  <!-- Target that executes Catalina -->
+  <target name="catalina" description="Execute Catalina"
+    depends="init-launcher,setjdb,setjpda" >
+
+    <!-- Set default title for minimized window -->
+    <property name="catalina.window.title" value="Catalina"/>
+
+    <!--
+      Launch Catalina. Note that the "filterclassname" attribute will force
+      the "waitforchild" attribute to "true" if any invalid arguments are
+      used or if "start" is not the last argument.
+    -->
+    <launch classname="org.apache.catalina.startup.Bootstrap"
+      waitforchild="${wait}"
+      debug="${jdb}"
+      print="${print}"
+      filterclassname="org.apache.catalina.launcher.CatalinaLaunchFilter"
+      filterclasspath="${bootstrap.jar}"
+      usesystemin="false"
+      requiretools="true"
+      redirectoutput="true"
+      output="${catalina.out}"
+      displayMinimizedWindow="true"
+      minimizedWindowTitle="${catalina.window.title}">
+        <jvmargset refid="base.jvm.args"/>
+        <syspropertyset refid="base.sys.properties"/>
+        <sysproperty key="java.security.manager" value="" if="security"/>
+        <sysproperty key="java.security.policy" value="=${catalina.policy}" if="security"/>
+        <classpath refid="base.class.path"/>
+    </launch>
+
+  </target>
+
+  <!-- Target that executes the Catalina tool wrapper -->
+  <target name="tool-wrapper" description="Execute Catalina tool wrapper"
+    depends="setjdb,setjpda" >
+
+    <!-- Launch Catalina tool wrapper -->
+    <launch classname="org.apache.catalina.startup.Tool"
+      debug="${jdb}"
+      print="${print}"
+      usesystemin="false"
+      requiretools="true">
+        <jvmargset refid="base.jvm.args"/>
+        <syspropertyset refid="base.sys.properties"/>
+        <classpath refid="base.class.path"/>
+    </launch>
+
+  </target>
+
+  <!-- ======================= Ant/JMX based ================ -->
+
+  <target name="taskdef" >
+    <property name="tomcat.home" location=".." />
+
+    <path id="jmx.test.classpath">
+      <pathelement location="${commons-modeler.jar}" />
+      <fileset dir="${tomcat.home}/common/lib" includes="*.jar"/>
+      <fileset dir="${tomcat.home}/server/lib" includes="*.jar"/>
+      <fileset dir="${tomcat.home}/bin" includes="*.jar"/>
+    </path>
+
+    <!-- part of modeler -->
+    <taskdef resource="META-INF/ant.properties"
+             classpathref="jmx.test.classpath" />
+  </target>
+
+
+  <target name="run" depends="echo-config,taskdef"
+        description="Start tomcat as a task and return">
+   
+    <MLET code="org.apache.commons.modeler.BaseModelMBean"
+          name="catalina:type=server" >
+       <arg value="org.apache.catalina.startup.Catalina" />
+    </MLET>
+
+    <jmxSet name="catalina:type=server"
+            attribute="catalinaHome"
+            value="${tomcat.home}"/>
+  
+    <!-- We could also call init and set other properties - 
+         init should load the modules -->
+
+    <jmx name="catalina:type=server"
+         method="start" />
+    
+    <echo message="Tomcat5 running"/>
+
+  </target>
+
+
+  <!-- ======================= Exec/java  ================ -->
+
+  <target name="java-start" depends="echo-config" 
+        description="Run tomcat in-process and wait for it to end, using java task" >
+
+    <property name="tomcat.fork" value="false" />
+
+    <java classname="org.apache.catalina.startup.Bootstrap" fork="${tomcat.fork}">
+      <classpath refid="tomcatcp" />
+      <arg value="startd" />
+      <sysproperty key="catalina.home" value="${catalina.home}"/>
+      <sysproperty key="build.compiler" value="jikes"/>
+      <sysproperty key="java.endorsed.dirs" value="${ant.home}/lib:${java.home}/lib"/>
+    </java>
+
+    <echo message="Tomcat5 running"/>
+
+  </target>
+
+</project>
diff --git a/container/catalina/src/bin/cpappend.bat b/container/catalina/src/bin/cpappend.bat
new file mode 100644
index 0000000..3455e6f
--- /dev/null
+++ b/container/catalina/src/bin/cpappend.bat
@@ -0,0 +1,19 @@
+rem ---------------------------------------------------------------------------
+rem Append to CLASSPATH
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Process the first argument
+if ""%1"" == """" goto end
+set CLASSPATH=%CLASSPATH%;%1
+shift
+
+rem Process the remaining arguments
+:setArgs
+if ""%1"" == """" goto doneSetArgs
+set CLASSPATH=%CLASSPATH% %1
+shift
+goto setArgs
+:doneSetArgs
+:end
diff --git a/container/catalina/src/bin/digest.bat b/container/catalina/src/bin/digest.bat
new file mode 100644
index 0000000..705f711
--- /dev/null
+++ b/container/catalina/src/bin/digest.bat
@@ -0,0 +1,41 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+rem ---------------------------------------------------------------------------
+rem Script to digest password using the algorithm specified
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Guess CATALINA_HOME if not defined
+if not "%CATALINA_HOME%" == "" goto gotHome
+set CATALINA_HOME=.
+if exist "%CATALINA_HOME%\bin\tool-wrapper.bat" goto okHome
+set CATALINA_HOME=..
+:gotHome
+if exist "%CATALINA_HOME%\bin\tool-wrapper.bat" goto okHome
+echo The CATALINA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+set EXECUTABLE=%CATALINA_HOME%\bin\tool-wrapper.bat
+
+rem Check that target executable exists
+if exist "%EXECUTABLE%" goto okExec
+echo Cannot find %EXECUTABLE%
+echo This file is needed to run this program
+goto end
+:okExec
+
+rem Get remaining unshifted command line arguments and save them in the
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+call "%EXECUTABLE%" -server org.apache.catalina.realm.RealmBase %CMD_LINE_ARGS%
+
+:end
diff --git a/container/catalina/src/bin/digest.sh b/container/catalina/src/bin/digest.sh
new file mode 100644
index 0000000..2a622f2
--- /dev/null
+++ b/container/catalina/src/bin/digest.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# -----------------------------------------------------------------------------
+# Script to digest password using the algorithm specified
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+ 
+PRGDIR=`dirname "$PRG"`
+EXECUTABLE=tool-wrapper.sh
+
+# Check that target executable exists
+if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
+  echo "Cannot find $PRGDIR/$EXECUTABLE"
+  echo "This file is needed to run this program"
+  exit 1
+fi
+
+exec "$PRGDIR"/"$EXECUTABLE" -server org.apache.catalina.realm.RealmBase "$@"
diff --git a/container/catalina/src/bin/jmxaccessor-tasks.xml b/container/catalina/src/bin/jmxaccessor-tasks.xml
new file mode 100644
index 0000000..b4ab895
--- /dev/null
+++ b/container/catalina/src/bin/jmxaccessor-tasks.xml
@@ -0,0 +1,34 @@
+<!--
+  XML file for importing Catalina jmx ant tasks.
+  <import file="${catalina.home}/bin/jmxaccessor-tasks.xml"/>
+-->
+
+<project name="jmxaccessor-tasks" >
+  <description>Catalina Ant JMX Accessor Tasks</description>
+  <!-- set catalina.home if it's not already set -->
+  <dirname property="catalina.home.bin.dir" file="${ant.file.jmxaccessor-tasks}"/>
+  <property name="catalina.home" value="${catalina.home.bin.dir}/.."/>
+  <taskdef resource="org/apache/catalina/ant/jmx/jmxaccessor.tasks">
+    <classpath>
+      <fileset file="${catalina.home}/server/lib/catalina-ant.jar"/>
+      <fileset file="${catalina.home}/server/lib/catalina-ant-jmx.jar"/>
+    </classpath>
+  </taskdef>
+  <typedef
+        name="jmxEquals"
+        classname="org.apache.catalina.ant.jmx.JMXAccessorEqualsCondition">
+    <classpath>
+      <fileset file="${catalina.home}/server/lib/catalina-ant.jar"/>
+      <fileset file="${catalina.home}/server/lib/catalina-ant-jmx.jar"/>
+    </classpath>
+  </typedef>
+  <typedef
+        name="jmxCondition"
+        classname="org.apache.catalina.ant.jmx.JMXAccessorCondition">
+    <classpath>
+      <fileset file="${catalina.home}/server/lib/catalina-ant.jar"/>
+      <fileset file="${catalina.home}/server/lib/catalina-ant-jmx.jar"/>
+    </classpath>
+  </typedef>
+
+</project>
\ No newline at end of file
diff --git a/container/catalina/src/bin/launcher.properties b/container/catalina/src/bin/launcher.properties
new file mode 100644
index 0000000..95722da
--- /dev/null
+++ b/container/catalina/src/bin/launcher.properties
@@ -0,0 +1,23 @@
+#
+# Properties for the LauncherBootstrap class
+#
+# This file contains the following configurable properties:
+# - ant.class.path - This property is a ":" separated list of the URL file
+#   fragments where the Ant classes are located. The list must include a JAXP
+#   compliant XML parser if you are not using Java(TM) SE 1.4 or higher.
+#
+# Important notes:
+# - Relative URL file fragments, such as "../lib", are supported and strongly
+#   encouraged as absolute files prevent cross platform distribution.
+# - URL file fragments are the portions of a URL after the "file:" string.
+#   This means that you must use "/" characters as directory separators on
+#   all platforms. Also, ":" characters in a file fragment, such as in drive
+#   portion of absolute Windows files, must be URL encoded as "%3A". Lastly,
+#   directories must have a "/" character at the end.
+# - All relative URL file fragments in the above properties will be resolved
+#   using the URL of the directory that the LauncherBootstrap class was
+#   loaded from, not the current working directory. This ensures that the
+#   LauncherBootstrap class can properly resolve the files without regard to
+#   the current working directory.
+#
+ant.class.path=../common/lib/ant.jar:../common/lib/ant-launcher.jar:../common/endorsed/xercesImpl.jar:../common/endorsed/xml-apis.jar
diff --git a/container/catalina/src/bin/service.bat b/container/catalina/src/bin/service.bat
new file mode 100644
index 0000000..d678d58
--- /dev/null
+++ b/container/catalina/src/bin/service.bat
@@ -0,0 +1,112 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+rem ---------------------------------------------------------------------------
+rem NT Service Install/Uninstall script
+rem
+rem Options
+rem install                Install the service using Tomcat5 as service name.
+rem                        Service is installed using default settings.
+rem remove                 Remove the service from the System.
+rem
+rem name        (optional) If the second argument is present it is considered
+rem                        to be new service name                                           
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Guess CATALINA_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%CATALINA_HOME%" == "" goto gotHome
+set CATALINA_HOME=%cd%
+if exist "%CATALINA_HOME%\bin\tomcat5.exe" goto okHome
+rem CD to the upper dir
+cd ..
+set CATALINA_HOME=%cd%
+:gotHome
+if exist "%CATALINA_HOME%\bin\tomcat5.exe" goto okHome
+echo The tomcat.exe was not found...
+echo The CATALINA_HOME environment variable is not defined correctly.
+echo This environment variable is needed to run this program
+goto end
+rem Make sure prerequisite environment variables are set
+if not "%JAVA_HOME%" == "" goto okHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto end 
+:okHome
+if not "%CATALINA_BASE%" == "" goto gotBase
+set CATALINA_BASE=%CATALINA_HOME%
+:gotBase
+ 
+set EXECUTABLE=%CATALINA_HOME%\bin\tomcat5.exe
+
+rem Set default Service name
+set SERVICE_NAME=Tomcat5
+set PR_DISPLAYNAME=Apache Tomcat
+
+if "%1" == "" goto displayUsage
+if "%2" == "" goto setServiceName
+set SERVICE_NAME=%2
+set PR_DISPLAYNAME=Apache Tomcat %2
+:setServiceName
+if %1 == install goto doInstall
+if %1 == remove goto doRemove
+if %1 == uninstall goto doRemove
+echo Unknown parameter "%1"
+:displayUsage
+echo 
+echo Usage: service.bat install/remove [service_name]
+goto end
+
+:doRemove
+rem Remove the service
+"%EXECUTABLE%" //DS//%SERVICE_NAME%
+echo The service '%SERVICE_NAME%' has been removed
+goto end
+
+:doInstall
+rem Install the service
+echo Installing the service '%SERVICE_NAME%' ...
+echo Using CATALINA_HOME:    %CATALINA_HOME%
+echo Using CATALINA_BASE:    %CATALINA_BASE%
+echo Using JAVA_HOME:        %JAVA_HOME%
+
+rem Use the environment variables as an exaple
+rem Each command line option is prefixed with PR_
+
+set PR_DESCRIPTION=Apache Tomcat Server - http://jakarta.apache.org/tomcat
+set PR_INSTALL=%EXECUTABLE%
+set PR_LOGPATH=%CATALINA_BASE%\logs
+set PR_CLASSPATH=%CATALINA_HOME%\bin\bootstrap.jar
+rem Set the server jvm from JAVA_HOME
+set PR_JVM=%JAVA_HOME%\jre\bin\server\jvm.dll
+if exist "%PR_JVM%" goto foundJvm
+rem Set the client jvm from JAVA_HOME
+set PR_JVM=%JAVA_HOME%\jre\bin\client\jvm.dll
+if exist "%PR_JVM%" goto foundJvm
+set PR_JVM=auto
+:foundJvm
+echo Using JVM:              %PR_JVM%
+"%EXECUTABLE%" //IS//%SERVICE_NAME% --StartClass org.apache.catalina.startup.Bootstrap --StopClass org.apache.catalina.startup.Bootstrap --StartParams start --StopParams stop
+if not errorlevel 1 goto installed
+echo Failed installing '%SERVICE_NAME%' service
+goto end
+:installed
+rem Clear the environment variables. They are not needed any more.
+set PR_DISPLAYNAME=
+set PR_DESCRIPTION=
+set PR_INSTALL=
+set PR_LOGPATH=
+set PR_CLASSPATH=
+set PR_JVM=
+rem Set extra parameters
+"%EXECUTABLE%" //US//%SERVICE_NAME% --JvmOptions "-Dcatalina.base=%CATALINA_BASE%;-Dcatalina.home=%CATALINA_HOME%;-Djava.endorsed.dirs=%CATALINA_HOME%\common\endorsed" --StartMode jvm --StopMode jvm
+rem More extra parameters
+set PR_LOGPATH=%CATALINA_BASE%\logs
+set PR_STDOUTPUT=auto
+set PR_STDERROR=auto
+"%EXECUTABLE%" //US//%SERVICE_NAME% ++JvmOptions "-Djava.io.tmpdir=%CATALINA_BASE%\temp" --JvmMs 128 --JvmMx 256
+echo The service '%SERVICE_NAME%' has been installed.
+
+:end
+cd %CURRENT_DIR%
diff --git a/container/catalina/src/bin/setclasspath.bat b/container/catalina/src/bin/setclasspath.bat
new file mode 100644
index 0000000..db7ed15
--- /dev/null
+++ b/container/catalina/src/bin/setclasspath.bat
@@ -0,0 +1,57 @@
+rem ---------------------------------------------------------------------------
+rem Set CLASSPATH and Java options
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Make sure prerequisite environment variables are set
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\javaw.exe" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\jdb.exe" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\javac.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+echo NB: JAVA_HOME should point to a JDK not a JRE
+goto exit
+:okJavaHome
+
+if not "%BASEDIR%" == "" goto gotBasedir
+echo The BASEDIR environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotBasedir
+if exist "%BASEDIR%\bin\setclasspath.bat" goto okBasedir
+echo The BASEDIR environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto exit
+:okBasedir
+
+rem Set the default -Djava.endorsed.dirs argument
+set JAVA_ENDORSED_DIRS=%BASEDIR%\common\endorsed
+
+rem Set standard CLASSPATH
+rem Note that there are no quotes as we do not want to introduce random
+rem quotes into the CLASSPATH
+set CLASSPATH=%JAVA_HOME%\lib\tools.jar
+
+rem Set standard command for invoking Java.
+rem Note that NT requires a window name argument when using start.
+rem Also note the quoting as JAVA_HOME may contain spaces.
+set _RUNJAVA="%JAVA_HOME%\bin\java"
+set _RUNJAVAW="%JAVA_HOME%\bin\javaw"
+set _RUNJDB="%JAVA_HOME%\bin\jdb"
+set _RUNJAVAC="%JAVA_HOME%\bin\javac"
+
+goto end
+
+:exit
+exit /b 1
+
+:end
diff --git a/container/catalina/src/bin/setclasspath.sh b/container/catalina/src/bin/setclasspath.sh
new file mode 100644
index 0000000..7e66cef
--- /dev/null
+++ b/container/catalina/src/bin/setclasspath.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+# -----------------------------------------------------------------------------
+#  Set CLASSPATH and Java options
+#
+#  $Id$
+# -----------------------------------------------------------------------------
+
+# First clear out the user classpath
+CLASSPATH=
+
+# Make sure prerequisite environment variables are set
+if [ -z "$JAVA_HOME" -a -z "$JRE_HOME" ]; then
+  echo "Neither the JAVA_HOME nor the JRE_HOME environment variable is defined"
+  echo "At least one of these environment variable is needed to run this program"
+  exit 1
+fi
+if [ -z "$JAVA_HOME" -a "$1" = "debug" ]; then
+  echo "JAVA_HOME should point to a JDK in order to run in debug mode."
+  exit 1
+fi
+if [ -z "$JRE_HOME" ]; then
+  JRE_HOME="$JAVA_HOME"
+fi
+
+# If we're running under jdb, we need a full jdk.
+if [ "$1" = "debug" -o "$1" = "javac" ] ; then
+  if [ "$os400" = "true" ]; then
+    if [ ! -x "$JAVA_HOME"/bin/java -o ! -x "$JAVA_HOME"/bin/javac ]; then
+      echo "The JAVA_HOME environment variable is not defined correctly"
+      echo "This environment variable is needed to run this program"
+      echo "NB: JAVA_HOME should point to a JDK not a JRE"
+      exit 1
+    fi
+  else
+    if [ ! -x "$JAVA_HOME"/bin/java -o ! -x "$JAVA_HOME"/bin/jdb -o ! -x "$JAVA_HOME"/bin/javac ]; then
+      echo "The JAVA_HOME environment variable is not defined correctly"
+      echo "This environment variable is needed to run this program"
+      echo "NB: JAVA_HOME should point to a JDK not a JRE"
+      exit 1
+    fi
+  fi
+fi
+if [ -z "$BASEDIR" ]; then
+  echo "The BASEDIR environment variable is not defined"
+  echo "This environment variable is needed to run this program"
+  exit 1
+fi
+if [ ! -x "$BASEDIR"/bin/setclasspath.sh ]; then
+  if $os400; then
+    # -x will Only work on the os400 if the files are:
+    # 1. owned by the user
+    # 2. owned by the PRIMARY group of the user
+    # this will not work if the user belongs in secondary groups
+    eval
+  else
+    echo "The BASEDIR environment variable is not defined correctly"
+    echo "This environment variable is needed to run this program"
+    exit 1
+  fi
+fi
+
+# Set the default -Djava.endorsed.dirs argument
+JAVA_ENDORSED_DIRS="$BASEDIR"/common/endorsed
+
+# Set standard CLASSPATH
+if [ "$1" = "debug" -o "$1" = "javac" ] ; then
+  CLASSPATH="$JAVA_HOME"/lib/tools.jar
+fi
+
+# OSX hack to CLASSPATH
+JIKESPATH=
+if [ `uname -s` = "Darwin" ]; then
+  OSXHACK="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Classes"
+  if [ -d "$OSXHACK" ]; then
+    for i in "$OSXHACK"/*.jar; do
+      JIKESPATH="$JIKESPATH":"$i"
+    done
+  fi
+fi
+
+# Set standard commands for invoking Java.
+  _RUNJAVA="$JRE_HOME"/bin/java
+if [ "$os400" != "true" ]; then
+  _RUNJDB="$JAVA_HOME"/bin/jdb
+fi
+_RUNJAVAC="$JAVA_HOME"/bin/javac
diff --git a/container/catalina/src/bin/shutdown-using-launcher.bat b/container/catalina/src/bin/shutdown-using-launcher.bat
new file mode 100644
index 0000000..b034edc
--- /dev/null
+++ b/container/catalina/src/bin/shutdown-using-launcher.bat
@@ -0,0 +1,39 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+
+rem ---------------------------------------------------------------------------
+rem
+rem Script for shutting down Catalina using the Launcher
+rem
+rem ---------------------------------------------------------------------------
+
+rem Get standard environment variables
+set PRG=%0
+if exist %PRG%\..\setenv.bat goto gotCmdPath
+rem %0 must have been found by DOS using the %PATH% so we assume that
+rem setenv.bat will also be found in the %PATH%
+goto doneSetenv
+:gotCmdPath
+call %PRG%\..\setenv.bat
+:doneSetenv
+
+rem Make sure prerequisite environment variables are set
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto end
+:gotJavaHome
+
+rem Get command line arguments and save them with the proper quoting
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+rem Execute the Launcher using the "catalina" target
+"%JAVA_HOME%\bin\java.exe" -classpath %PRG%\..;"%PATH%";. LauncherBootstrap -launchfile catalina.xml -verbose catalina %CMD_LINE_ARGS% stop
+
+:end
diff --git a/container/catalina/src/bin/shutdown-using-launcher.sh b/container/catalina/src/bin/shutdown-using-launcher.sh
new file mode 100644
index 0000000..82daec2
--- /dev/null
+++ b/container/catalina/src/bin/shutdown-using-launcher.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# -----------------------------------------------------------------------------
+#
+# Script for shutting down Catalina using the Launcher
+#
+# -----------------------------------------------------------------------------
+
+# Resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ]; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+
+# Get standard environment variables
+PRGDIR=`dirname "$PRG"`
+if [ -r "$PRGDIR"/setenv.sh ]; then
+  . "$PRGDIR"/setenv.sh
+fi
+
+# Execute the Launcher using the "catalina" target
+exec "$JAVA_HOME"/bin/java -classpath "$PRGDIR" LauncherBootstrap -launchfile catalina.xml -verbose catalina "$@" stop
diff --git a/container/catalina/src/bin/shutdown.bat b/container/catalina/src/bin/shutdown.bat
new file mode 100644
index 0000000..d28c717
--- /dev/null
+++ b/container/catalina/src/bin/shutdown.bat
@@ -0,0 +1,44 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+rem ---------------------------------------------------------------------------
+rem Stop script for the CATALINA Server
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Guess CATALINA_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%CATALINA_HOME%" == "" goto gotHome
+set CATALINA_HOME=%CURRENT_DIR%
+if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
+cd ..
+set CATALINA_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
+echo The CATALINA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+set EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat
+
+rem Check that target executable exists
+if exist "%EXECUTABLE%" goto okExec
+echo Cannot find %EXECUTABLE%
+echo This file is needed to run this program
+goto end
+:okExec
+
+rem Get remaining unshifted command line arguments and save them in the
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+call "%EXECUTABLE%" stop %CMD_LINE_ARGS%
+
+:end
diff --git a/container/catalina/src/bin/shutdown.sh b/container/catalina/src/bin/shutdown.sh
new file mode 100644
index 0000000..441962f
--- /dev/null
+++ b/container/catalina/src/bin/shutdown.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# -----------------------------------------------------------------------------
+# Stop script for the CATALINA Server
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+ 
+PRGDIR=`dirname "$PRG"`
+EXECUTABLE=catalina.sh
+
+# Check that target executable exists
+if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
+  echo "Cannot find $PRGDIR/$EXECUTABLE"
+  echo "This file is needed to run this program"
+  exit 1
+fi
+
+exec "$PRGDIR"/"$EXECUTABLE" stop "$@"
diff --git a/container/catalina/src/bin/startup-using-launcher.bat b/container/catalina/src/bin/startup-using-launcher.bat
new file mode 100644
index 0000000..2a5ece9
--- /dev/null
+++ b/container/catalina/src/bin/startup-using-launcher.bat
@@ -0,0 +1,39 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+
+rem ---------------------------------------------------------------------------
+rem
+rem Script for starting Catalina using the Launcher
+rem
+rem ---------------------------------------------------------------------------
+
+rem Get standard environment variables
+set PRG=%0
+if exist %PRG%\..\setenv.bat goto gotCmdPath
+rem %0 must have been found by DOS using the %PATH% so we assume that
+rem setenv.bat will also be found in the %PATH%
+goto doneSetenv
+:gotCmdPath
+call %PRG%\..\setenv.bat
+:doneSetenv
+
+rem Make sure prerequisite environment variables are set
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto end
+:gotJavaHome
+
+rem Get command line arguments and save them with the proper quoting
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+rem Execute the Launcher using the "catalina" target
+"%JAVA_HOME%\bin\java.exe" -classpath %PRG%\..;"%PATH%";. LauncherBootstrap -launchfile catalina.xml -verbose catalina %CMD_LINE_ARGS% start
+
+:end
diff --git a/container/catalina/src/bin/startup-using-launcher.sh b/container/catalina/src/bin/startup-using-launcher.sh
new file mode 100644
index 0000000..dcd179e
--- /dev/null
+++ b/container/catalina/src/bin/startup-using-launcher.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# -----------------------------------------------------------------------------
+#
+# Script for starting Catalina using the Launcher
+#
+# -----------------------------------------------------------------------------
+
+# Resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ]; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+
+# Get standard environment variables
+PRGDIR=`dirname "$PRG"`
+if [ -r "$PRGDIR"/setenv.sh ]; then
+  . "$PRGDIR"/setenv.sh
+fi
+
+# Execute the Launcher using the "catalina" target
+exec "$JAVA_HOME"/bin/java -classpath "$PRGDIR" LauncherBootstrap -launchfile catalina.xml -verbose catalina "$@" start
diff --git a/container/catalina/src/bin/startup.bat b/container/catalina/src/bin/startup.bat
new file mode 100644
index 0000000..246e2d9
--- /dev/null
+++ b/container/catalina/src/bin/startup.bat
@@ -0,0 +1,44 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+rem ---------------------------------------------------------------------------
+rem Start script for the CATALINA Server
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Guess CATALINA_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%CATALINA_HOME%" == "" goto gotHome
+set CATALINA_HOME=%CURRENT_DIR%
+if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
+cd ..
+set CATALINA_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
+echo The CATALINA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+set EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat
+
+rem Check that target executable exists
+if exist "%EXECUTABLE%" goto okExec
+echo Cannot find %EXECUTABLE%
+echo This file is needed to run this program
+goto end
+:okExec
+
+rem Get remaining unshifted command line arguments and save them in the
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+call "%EXECUTABLE%" start %CMD_LINE_ARGS%
+
+:end
diff --git a/container/catalina/src/bin/startup.sh b/container/catalina/src/bin/startup.sh
new file mode 100644
index 0000000..b672ffb
--- /dev/null
+++ b/container/catalina/src/bin/startup.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+# -----------------------------------------------------------------------------
+# Start Script for the CATALINA Server
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+# Better OS/400 detection: see Bugzilla 31132
+os400=false
+case "`uname`" in
+CYGWIN*) cygwin=true;;
+OS400*) os400=true;;
+esac
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+ 
+PRGDIR=`dirname "$PRG"`
+EXECUTABLE=catalina.sh
+
+# Check that target executable exists
+if $os400; then
+  # -x will Only work on the os400 if the files are: 
+  # 1. owned by the user
+  # 2. owned by the PRIMARY group of the user
+  # this will not work if the user belongs in secondary groups
+  eval
+else
+  if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
+    echo "Cannot find $PRGDIR/$EXECUTABLE"
+    echo "This file is needed to run this program"
+    exit 1
+  fi
+fi 
+
+exec "$PRGDIR"/"$EXECUTABLE" start "$@"
diff --git a/container/catalina/src/bin/tool-wrapper-using-launcher.bat b/container/catalina/src/bin/tool-wrapper-using-launcher.bat
new file mode 100644
index 0000000..0334876
--- /dev/null
+++ b/container/catalina/src/bin/tool-wrapper-using-launcher.bat
@@ -0,0 +1,39 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+
+rem ---------------------------------------------------------------------------
+rem
+rem Script for running the Catalina tool wrapper using the Launcher
+rem
+rem ---------------------------------------------------------------------------
+
+rem Get standard environment variables
+set PRG=%0
+if exist %PRG%\..\setenv.bat goto gotCmdPath
+rem %0 must have been found by DOS using the %PATH% so we assume that
+rem setenv.bat will also be found in the %PATH%
+goto doneSetenv
+:gotCmdPath
+call %PRG%\..\setenv.bat
+:doneSetenv
+
+rem Make sure prerequisite environment variables are set
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto end
+:gotJavaHome
+
+rem Get command line arguments and save them with the proper quoting
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+rem Execute the Launcher using the "tool-wrapper" target
+"%JAVA_HOME%\bin\java.exe" -classpath %PRG%\..;"%PATH%";. LauncherBootstrap -launchfile catalina.xml -verbose tool-wrapper %CMD_LINE_ARGS%
+
+:end
diff --git a/container/catalina/src/bin/tool-wrapper-using-launcher.sh b/container/catalina/src/bin/tool-wrapper-using-launcher.sh
new file mode 100644
index 0000000..4b286cc
--- /dev/null
+++ b/container/catalina/src/bin/tool-wrapper-using-launcher.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# -----------------------------------------------------------------------------
+#
+# Script for running the Catalina tool wrapper using the Launcher
+#
+# -----------------------------------------------------------------------------
+
+# Resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ]; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+
+# Get standard environment variables
+PRGDIR=`dirname "$PRG"`
+if [ -r "$PRGDIR"/setenv.sh ]; then
+  . "$PRGDIR"/setenv.sh
+fi
+
+# Execute the Launcher using the "tool-wrapper" target
+exec "$JAVA_HOME"/bin/java -classpath "$PRGDIR" LauncherBootstrap -launchfile catalina.xml -verbose tool-wrapper "$@"
diff --git a/container/catalina/src/bin/tool-wrapper.bat b/container/catalina/src/bin/tool-wrapper.bat
new file mode 100644
index 0000000..7a128db
--- /dev/null
+++ b/container/catalina/src/bin/tool-wrapper.bat
@@ -0,0 +1,59 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+rem ---------------------------------------------------------------------------
+rem Wrapper script for command line tools
+rem
+rem Environment Variable Prequisites
+rem
+rem   CATALINA_HOME May point at your Catalina "build" directory.
+rem
+rem   TOOL_OPTS     (Optional) Java runtime options used when the "start",
+rem                 "stop", or "run" command is executed.
+rem
+rem   JAVA_HOME     Must point at your Java Development Kit installation.
+rem
+rem   JAVA_OPTS     (Optional) Java runtime options used when the "start",
+rem                 "stop", or "run" command is executed.
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Guess CATALINA_HOME if not defined
+if not "%CATALINA_HOME%" == "" goto gotHome
+set CATALINA_HOME=.
+if exist "%CATALINA_HOME%\bin\tool-wrapper.bat" goto okHome
+set CATALINA_HOME=..
+:gotHome
+if exist "%CATALINA_HOME%\bin\tool-wrapper.bat" goto okHome
+echo The CATALINA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+rem Get standard environment variables
+if exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat"
+
+rem Get standard Java environment variables
+if exist "%CATALINA_HOME%\bin\setclasspath.bat" goto okSetclasspath
+echo Cannot find %CATALINA_HOME%\bin\setclasspath.bat
+echo This file is needed to run this program
+goto end
+:okSetclasspath
+set BASEDIR=%CATALINA_HOME%
+call "%CATALINA_HOME%\bin\setclasspath.bat"
+
+rem Add on extra jar files to CLASSPATH
+set CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\bootstrap.jar;"%BASEDIR%"\common\lib\jmx.jar;"%BASEDIR%"\common\lib\servlet-api.jar
+
+rem Get remaining unshifted command line arguments and save them in the
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+%_RUNJAVA% %JAVA_OPTS% %TOOL_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.home="%CATALINA_HOME%" org.apache.catalina.startup.Tool %CMD_LINE_ARGS%
+
+:end
diff --git a/container/catalina/src/bin/tool-wrapper.sh b/container/catalina/src/bin/tool-wrapper.sh
new file mode 100644
index 0000000..8ce4097
--- /dev/null
+++ b/container/catalina/src/bin/tool-wrapper.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+# -----------------------------------------------------------------------------
+# Wrapper script for command line tools
+#
+# Environment Variable Prequisites
+#
+#   CATALINA_HOME May point at your Catalina "build" directory.
+#
+#   TOOL_OPTS     (Optional) Java runtime options used when the "start",
+#                 "stop", or "run" command is executed.
+#
+#   JAVA_HOME     Must point at your Java Development Kit installation.
+#
+#   JAVA_OPTS     (Optional) Java runtime options used when the "start",
+#                 "stop", or "run" command is executed.
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false
+case "`uname`" in
+CYGWIN*) cygwin=true;;
+esac
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ]; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+
+# Get standard environment variables
+PRGDIR=`dirname "$PRG"`
+CATALINA_HOME=`cd "$PRGDIR/.." ; pwd`
+if [ -r "$CATALINA_HOME"/bin/setenv.sh ]; then
+  . "$CATALINA_HOME"/bin/setenv.sh
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin; then
+  [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CATALINA_HOME" ] && CATALINA_HOME=`cygpath --unix "$CATALINA_HOME"`
+  [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# Get standard Java environment variables
+if [ -r "$CATALINA_HOME"/bin/setclasspath.sh ]; then
+  BASEDIR="$CATALINA_HOME"
+  . "$CATALINA_HOME"/bin/setclasspath.sh
+else
+  echo "Cannot find $CATALINA_HOME/bin/setclasspath.sh"
+  echo "This file is needed to run this program"
+  exit 1
+fi
+
+# Add on extra jar files to CLASSPATH
+CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/bootstrap.jar:"$BASEDIR"/common/lib/jmx.jar:"$BASEDIR"/common/lib/servlet-api.jar
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  CATALINA_HOME=`cygpath --path --windows "$CATALINA_HOME"`
+  CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+fi
+
+# ----- Execute The Requested Command -----------------------------------------
+
+exec "$_RUNJAVA" $JAVA_OPTS $TOOL_OPTS \
+  -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+  -Dcatalina.home="$CATALINA_HOME" \
+  org.apache.catalina.startup.Tool "$@"
diff --git a/container/catalina/src/bin/version.bat b/container/catalina/src/bin/version.bat
new file mode 100755
index 0000000..b14be9f
--- /dev/null
+++ b/container/catalina/src/bin/version.bat
@@ -0,0 +1,44 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+rem ---------------------------------------------------------------------------
+rem Version script for the CATALINA Server
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Guess CATALINA_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%CATALINA_HOME%" == "" goto gotHome
+set CATALINA_HOME=%CURRENT_DIR%
+if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
+cd ..
+set CATALINA_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
+echo The CATALINA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+set EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat
+
+rem Check that target executable exists
+if exist "%EXECUTABLE%" goto okExec
+echo Cannot find %EXECUTABLE%
+echo This file is needed to run this program
+goto end
+:okExec
+
+rem Get remaining unshifted command line arguments and save them in the
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+call "%EXECUTABLE%" version %CMD_LINE_ARGS%
+
+:end
diff --git a/container/catalina/src/bin/version.sh b/container/catalina/src/bin/version.sh
new file mode 100755
index 0000000..751a55e
--- /dev/null
+++ b/container/catalina/src/bin/version.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# -----------------------------------------------------------------------------
+# Version Script for the CATALINA Server
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+
+PRGDIR=`dirname "$PRG"`
+EXECUTABLE=catalina.sh
+
+# Check that target executable exists
+if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
+  echo "Cannot find $PRGDIR/$EXECUTABLE"
+  echo "This file is needed to run this program"
+  exit 1
+fi
+
+exec "$PRGDIR"/"$EXECUTABLE" version "$@"
diff --git a/container/catalina/src/conf/catalina.policy b/container/catalina/src/conf/catalina.policy
new file mode 100644
index 0000000..34d5dd7
--- /dev/null
+++ b/container/catalina/src/conf/catalina.policy
@@ -0,0 +1,172 @@
+// ============================================================================
+// catalina.corepolicy - Security Policy Permissions for Tomcat 5
+//
+// This file contains a default set of security policies to be enforced (by the
+// JVM) when Catalina is executed with the "-security" option.  In addition
+// to the permissions granted here, the following additional permissions are
+// granted to the codebase specific to each web application:
+//
+// * Read access to the document root directory
+//
+// $Id$
+// ============================================================================
+
+
+// ========== SYSTEM CODE PERMISSIONS =========================================
+
+
+// These permissions apply to javac
+grant codeBase "file:${java.home}/lib/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to all shared system extensions
+grant codeBase "file:${java.home}/jre/lib/ext/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to javac when ${java.home] points at $JAVA_HOME/jre
+grant codeBase "file:${java.home}/../lib/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to all shared system extensions when
+// ${java.home} points at $JAVA_HOME/jre
+grant codeBase "file:${java.home}/lib/ext/-" {
+        permission java.security.AllPermission;
+};
+
+
+// ========== CATALINA CODE PERMISSIONS =======================================
+
+
+// These permissions apply to the launcher code
+grant codeBase "file:${catalina.home}/bin/commons-launcher.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the daemon code
+grant codeBase "file:${catalina.home}/bin/commons-daemon.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the commons-logging API
+grant codeBase "file:${catalina.home}/bin/commons-logging-api.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the server startup code
+grant codeBase "file:${catalina.home}/bin/bootstrap.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the JMX server
+grant codeBase "file:${catalina.home}/bin/jmx.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to JULI
+grant codeBase "file:${catalina.home}/bin/tomcat-juli.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the servlet API classes
+// and those that are shared across all class loaders
+// located in the "common" directory
+grant codeBase "file:${catalina.home}/common/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the container's core code, plus any additional
+// libraries installed in the "server" directory
+grant codeBase "file:${catalina.home}/server/-" {
+        permission java.security.AllPermission;
+};
+
+// The permissions granted to the balancer WEB-INF/classes directory
+grant codeBase "file:${catalina.home}/webapps/balancer/WEB-INF/classes/-" {
+        permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat.util.digester";
+        permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat.util.digester.*";
+};
+// ========== WEB APPLICATION PERMISSIONS =====================================
+
+
+// These permissions are granted by default to all web applications
+// In addition, a web application will be given a read FilePermission
+// and JndiPermission for all files and directories in its document root.
+grant { 
+    // Required for JNDI lookup of named JDBC DataSource's and
+    // javamail named MimePart DataSource used to send mail
+    permission java.util.PropertyPermission "java.home", "read";
+    permission java.util.PropertyPermission "java.naming.*", "read";
+    permission java.util.PropertyPermission "javax.sql.*", "read";
+
+    // OS Specific properties to allow read access
+    permission java.util.PropertyPermission "os.name", "read";
+    permission java.util.PropertyPermission "os.version", "read";
+    permission java.util.PropertyPermission "os.arch", "read";
+    permission java.util.PropertyPermission "file.separator", "read";
+    permission java.util.PropertyPermission "path.separator", "read";
+    permission java.util.PropertyPermission "line.separator", "read";
+
+    // JVM properties to allow read access
+    permission java.util.PropertyPermission "java.version", "read";
+    permission java.util.PropertyPermission "java.vendor", "read";
+    permission java.util.PropertyPermission "java.vendor.url", "read";
+    permission java.util.PropertyPermission "java.class.version", "read";
+	permission java.util.PropertyPermission "java.specification.version", "read";
+	permission java.util.PropertyPermission "java.specification.vendor", "read";
+	permission java.util.PropertyPermission "java.specification.name", "read";
+
+	permission java.util.PropertyPermission "java.vm.specification.version", "read";
+	permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
+	permission java.util.PropertyPermission "java.vm.specification.name", "read";
+	permission java.util.PropertyPermission "java.vm.version", "read";
+	permission java.util.PropertyPermission "java.vm.vendor", "read";
+	permission java.util.PropertyPermission "java.vm.name", "read";
+
+    // Required for OpenJMX
+    permission java.lang.RuntimePermission "getAttribute";
+
+	// Allow read of JAXP compliant XML parser debug
+	permission java.util.PropertyPermission "jaxp.debug", "read";
+
+    // Precompiled JSPs need access to this package.
+    permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.runtime";
+    permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.runtime.*";
+    
+};
+
+
+// You can assign additional permissions to particular web applications by
+// adding additional "grant" entries here, based on the code base for that
+// application, /WEB-INF/classes/, or /WEB-INF/lib/ jar files.
+//
+// Different permissions can be granted to JSP pages, classes loaded from
+// the /WEB-INF/classes/ directory, all jar files in the /WEB-INF/lib/
+// directory, or even to individual jar files in the /WEB-INF/lib/ directory.
+//
+// For instance, assume that the standard "examples" application
+// included a JDBC driver that needed to establish a network connection to the
+// corresponding database and used the scrape taglib to get the weather from
+// the NOAA web server.  You might create a "grant" entries like this:
+//
+// The permissions granted to the context root directory apply to JSP pages.
+// grant codeBase "file:${catalina.home}/webapps/examples/-" {
+//      permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect";
+//      permission java.net.SocketPermission "*.noaa.gov:80", "connect";
+// };
+//
+// The permissions granted to the context WEB-INF/classes directory
+// grant codeBase "file:${catalina.home}/webapps/examples/WEB-INF/classes/-" {
+// };
+//
+// The permission granted to your JDBC driver
+// grant codeBase "jar:file:${catalina.home}/webapps/examples/WEB-INF/lib/driver.jar!/-" {
+//      permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect";
+// };
+// The permission granted to the scrape taglib
+// grant codeBase "jar:file:${catalina.home}/webapps/examples/WEB-INF/lib/scrape.jar!/-" {
+//      permission java.net.SocketPermission "*.noaa.gov:80", "connect";
+// };
+
diff --git a/container/catalina/src/conf/catalina.properties b/container/catalina/src/conf/catalina.properties
new file mode 100644
index 0000000..9dfbe9d
--- /dev/null
+++ b/container/catalina/src/conf/catalina.properties
@@ -0,0 +1,66 @@
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageAccess unless the
+# corresponding RuntimePermission ("accessClassInPackage."+package) has
+# been granted.
+package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.,sun.beans.
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageDefinition unless the
+# corresponding RuntimePermission ("defineClassInPackage."+package) has
+# been granted.
+#
+# by default, no packages are restricted for definition, and none of
+# the class loaders supplied with the JDK call checkPackageDefinition.
+#
+package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.
+
+#
+#
+# List of comma-separated paths defining the contents of the "common" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
+# If left as blank,the JVM system loader will be used as Catalina's "common" 
+# loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository
+common.loader=${catalina.home}/common/classes,${catalina.home}/common/i18n/*.jar,${catalina.home}/common/endorsed/*.jar,${catalina.home}/common/lib/*.jar
+
+#
+# List of comma-separated paths defining the contents of the "server" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
+# If left as blank, the "common" loader will be used as Catalina's "server" 
+# loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository
+server.loader=${catalina.home}/server/classes,${catalina.home}/server/lib/*.jar
+
+#
+# List of comma-separated paths defining the contents of the "shared" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
+# the "common" loader will be used as Catalina's "shared" loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository 
+# Please note that for individual jar files, e.g. bar.jar, you need the URL form
+# starting with file:.
+shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar
+
+#
+# String cache configuration.
+tomcat.util.buf.StringCache.byte.enabled=true
+#tomcat.util.buf.StringCache.char.enabled=true
+#tomcat.util.buf.StringCache.trainThreshold=500000
+#tomcat.util.buf.StringCache.cacheSize=5000
diff --git a/container/catalina/src/conf/context.xml b/container/catalina/src/conf/context.xml
new file mode 100644
index 0000000..5704aba
--- /dev/null
+++ b/container/catalina/src/conf/context.xml
@@ -0,0 +1,12 @@
+<!-- The contents of this file will be loaded for each web application -->
+<Context>
+
+    <!-- Default set of monitored resources -->
+    <WatchedResource>WEB-INF/web.xml</WatchedResource>
+	
+    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
+    <!--
+    <Manager pathname="" />
+    -->
+
+</Context>
\ No newline at end of file
diff --git a/container/catalina/src/conf/server-minimal.xml b/container/catalina/src/conf/server-minimal.xml
new file mode 100644
index 0000000..7b542b6
--- /dev/null
+++ b/container/catalina/src/conf/server-minimal.xml
@@ -0,0 +1,25 @@
+<Server port="8005" shutdown="SHUTDOWN">
+
+  <GlobalNamingResources>
+    <!-- Used by Manager webapp -->
+    <Resource name="UserDatabase" auth="Container"
+              type="org.apache.catalina.UserDatabase"
+       description="User database that can be updated and saved"
+           factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
+          pathname="conf/tomcat-users.xml" />
+  </GlobalNamingResources>
+
+  <Service name="Catalina">
+    <Connector port="8080" />
+
+    <!-- This is here for compatibility only, not required -->
+    <Connector port="8009" protocol="AJP/1.3" />
+
+    <Engine name="Catalina" defaultHost="localhost">
+      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
+             resourceName="UserDatabase" />
+      <Host name="localhost" appBase="webapps" />
+    </Engine>
+    
+  </Service>
+</Server>
diff --git a/container/catalina/src/conf/server.xml b/container/catalina/src/conf/server.xml
new file mode 100644
index 0000000..fdb3f3b
--- /dev/null
+++ b/container/catalina/src/conf/server.xml
@@ -0,0 +1,381 @@
+<!-- Example Server Configuration File -->
+<!-- Note that component elements are nested corresponding to their
+     parent-child relationships with each other -->
+
+<!-- A "Server" is a singleton element that represents the entire JVM,
+     which may contain one or more "Service" instances.  The Server
+     listens for a shutdown command on the indicated port.
+
+     Note:  A "Server" is not itself a "Container", so you may not
+     define subcomponents such as "Valves" or "Loggers" at this level.
+ -->
+
+<Server port="8005" shutdown="SHUTDOWN">
+
+  <!-- Comment these entries out to disable JMX MBeans support used for the 
+       administration web application -->
+  <Listener className="org.apache.catalina.core.AprLifecycleListener" />
+  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
+  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
+  <Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>
+
+  <!-- Global JNDI resources -->
+  <GlobalNamingResources>
+
+    <!-- Test entry for demonstration purposes -->
+    <Environment name="simpleValue" type="java.lang.Integer" value="30"/>
+
+    <!-- Editable user database that can also be used by
+         UserDatabaseRealm to authenticate users -->
+    <Resource name="UserDatabase" auth="Container"
+              type="org.apache.catalina.UserDatabase"
+       description="User database that can be updated and saved"
+           factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
+          pathname="conf/tomcat-users.xml" />
+
+  </GlobalNamingResources>
+
+  <!-- A "Service" is a collection of one or more "Connectors" that share
+       a single "Container" (and therefore the web applications visible
+       within that Container).  Normally, that Container is an "Engine",
+       but this is not required.
+
+       Note:  A "Service" is not itself a "Container", so you may not
+       define subcomponents such as "Valves" or "Loggers" at this level.
+   -->
+
+  <!-- Define the Tomcat Stand-Alone Service -->
+  <Service name="Catalina">
+
+    <!-- A "Connector" represents an endpoint by which requests are received
+         and responses are returned.  Each Connector passes requests on to the
+         associated "Container" (normally an Engine) for processing.
+
+         By default, a non-SSL HTTP/1.1 Connector is established on port 8080.
+         You can also enable an SSL HTTP/1.1 Connector on port 8443 by
+         following the instructions below and uncommenting the second Connector
+         entry.  SSL support requires the following steps (see the SSL Config
+         HOWTO in the Tomcat 5 documentation bundle for more detailed
+         instructions):
+         * If your JDK version 1.3 or prior, download and install JSSE 1.0.2 or
+           later, and put the JAR files into "$JAVA_HOME/jre/lib/ext".
+         * Execute:
+             %JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA (Windows)
+             $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA  (Unix)
+           with a password value of "changeit" for both the certificate and
+           the keystore itself.
+
+         By default, DNS lookups are enabled when a web application calls
+         request.getRemoteHost().  This can have an adverse impact on
+         performance, so you can disable it by setting the
+         "enableLookups" attribute to "false".  When DNS lookups are disabled,
+         request.getRemoteHost() will return the String version of the
+         IP address of the remote client.
+    -->
+
+    <!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
+    <Connector port="8080" maxHttpHeaderSize="8192"
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false" redirectPort="8443" acceptCount="100"
+               connectionTimeout="20000" disableUploadTimeout="true" />
+    <!-- Note : To disable connection timeouts, set connectionTimeout value
+     to 0 -->
+	
+	<!-- Note : To use gzip compression you could set the following properties :
+	
+			   compression="on" 
+			   compressionMinSize="2048" 
+			   noCompressionUserAgents="gozilla, traviata" 
+			   compressableMimeType="text/html,text/xml"
+	-->
+
+    <!-- Define a SSL HTTP/1.1 Connector on port 8443 -->
+    <!--
+    <Connector port="8443" maxHttpHeaderSize="8192"
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false" disableUploadTimeout="true"
+               acceptCount="100" scheme="https" secure="true"
+               clientAuth="false" sslProtocol="TLS" />
+    -->
+
+    <!-- Define an AJP 1.3 Connector on port 8009 -->
+    <Connector port="8009" 
+               enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />
+
+    <!-- Define a Proxied HTTP/1.1 Connector on port 8082 -->
+    <!-- See proxy documentation for more information about using this. -->
+    <!--
+    <Connector port="8082" 
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false" acceptCount="100" connectionTimeout="20000"
+               proxyPort="80" disableUploadTimeout="true" />
+    -->
+
+    <!-- An Engine represents the entry point (within Catalina) that processes
+         every request.  The Engine implementation for Tomcat stand alone
+         analyzes the HTTP headers included with the request, and passes them
+         on to the appropriate Host (virtual host). -->
+
+    <!-- You should set jvmRoute to support load-balancing via AJP ie :
+    <Engine name="Standalone" defaultHost="localhost" jvmRoute="jvm1">         
+    --> 
+         
+    <!-- Define the top level container in our container hierarchy -->
+    <Engine name="Catalina" defaultHost="localhost">
+
+      <!-- The request dumper valve dumps useful debugging information about
+           the request headers and cookies that were received, and the response
+           headers and cookies that were sent, for all requests received by
+           this instance of Tomcat.  If you care only about requests to a
+           particular virtual host, or a particular application, nest this
+           element inside the corresponding <Host> or <Context> entry instead.
+
+           For a similar mechanism that is portable to all Servlet 2.4
+           containers, check out the "RequestDumperFilter" Filter in the
+           example application (the source for this filter may be found in
+           "$CATALINA_HOME/webapps/examples/WEB-INF/classes/filters").
+
+           Request dumping is disabled by default.  Uncomment the following
+           element to enable it. -->
+      <!--
+      <Valve className="org.apache.catalina.valves.RequestDumperValve"/>
+      -->
+
+      <!-- Because this Realm is here, an instance will be shared globally -->
+
+      <!-- This Realm uses the UserDatabase configured in the global JNDI
+           resources under the key "UserDatabase".  Any edits
+           that are performed against this UserDatabase are immediately
+           available for use by the Realm.  -->
+      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
+             resourceName="UserDatabase"/>
+
+      <!-- Comment out the old realm but leave here for now in case we
+           need to go back quickly -->
+      <!--
+      <Realm className="org.apache.catalina.realm.MemoryRealm" />
+      -->
+
+      <!-- Replace the above Realm with one of the following to get a Realm
+           stored in a database and accessed via JDBC -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm"
+             driverName="org.gjt.mm.mysql.Driver"
+          connectionURL="jdbc:mysql://localhost/authority"
+         connectionName="test" connectionPassword="test"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm"
+             driverName="oracle.jdbc.driver.OracleDriver"
+          connectionURL="jdbc:oracle:thin:@ntserver:1521:ORCL"
+         connectionName="scott" connectionPassword="tiger"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm"
+             driverName="sun.jdbc.odbc.JdbcOdbcDriver"
+          connectionURL="jdbc:odbc:CATALINA"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!-- Define the default virtual host
+           Note: XML Schema validation will not work with Xerces 2.2.
+       -->
+      <Host name="localhost" appBase="webapps"
+       unpackWARs="true" autoDeploy="true"
+       xmlValidation="false" xmlNamespaceAware="false">
+
+        <!-- Defines a cluster for this node,
+             By defining this element, means that every manager will be changed.
+             So when running a cluster, only make sure that you have webapps in there
+             that need to be clustered and remove the other ones.
+             A cluster has the following parameters:
+
+             className = the fully qualified name of the cluster class
+
+             name = a descriptive name for your cluster, can be anything
+
+             mcastAddr = the multicast address, has to be the same for all the nodes
+
+             mcastPort = the multicast port, has to be the same for all the nodes
+             
+             mcastBindAddr = bind the multicast socket to a specific address
+             
+             mcastTTL = the multicast TTL if you want to limit your broadcast
+             
+             mcastSoTimeout = the multicast readtimeout 
+
+             mcastFrequency = the number of milliseconds in between sending a "I'm alive" heartbeat
+
+             mcastDropTime = the number a milliseconds before a node is considered "dead" if no heartbeat is received
+
+             tcpThreadCount = the number of threads to handle incoming replication requests, optimal would be the same amount of threads as nodes 
+
+             tcpListenAddress = the listen address (bind address) for TCP cluster request on this host, 
+                                in case of multiple ethernet cards.
+                                auto means that address becomes
+                                InetAddress.getLocalHost().getHostAddress()
+
+             tcpListenPort = the tcp listen port
+
+             tcpSelectorTimeout = the timeout (ms) for the Selector.select() method in case the OS
+                                  has a wakup bug in java.nio. Set to 0 for no timeout
+
+             printToScreen = true means that managers will also print to std.out
+
+             expireSessionsOnShutdown = true means that 
+
+             useDirtyFlag = true means that we only replicate a session after setAttribute,removeAttribute has been called.
+                            false means to replicate the session after each request.
+                            false means that replication would work for the following piece of code: (only for SimpleTcpReplicationManager)
+                            <%
+                            HashMap map = (HashMap)session.getAttribute("map");
+                            map.put("key","value");
+                            %>
+             replicationMode = can be either 'pooled', 'synchronous' or 'asynchronous'.
+                               * Pooled means that the replication happens using several sockets in a synchronous way. Ie, the data gets replicated, then the request return. This is the same as the 'synchronous' setting except it uses a pool of sockets, hence it is multithreaded. This is the fastest and safest configuration. To use this, also increase the nr of tcp threads that you have dealing with replication.
+                               * Synchronous means that the thread that executes the request, is also the
+                               thread the replicates the data to the other nodes, and will not return until all
+                               nodes have received the information.
+                               * Asynchronous means that there is a specific 'sender' thread for each cluster node,
+                               so the request thread will queue the replication request into a "smart" queue,
+                               and then return to the client.
+                               The "smart" queue is a queue where when a session is added to the queue, and the same session
+                               already exists in the queue from a previous request, that session will be replaced
+                               in the queue instead of replicating two requests. This almost never happens, unless there is a 
+                               large network delay.
+        -->             
+        <!--
+            When configuring for clustering, you also add in a valve to catch all the requests
+            coming in, at the end of the request, the session may or may not be replicated.
+            A session is replicated if and only if all the conditions are met:
+            1. useDirtyFlag is true or setAttribute or removeAttribute has been called AND
+            2. a session exists (has been created)
+            3. the request is not trapped by the "filter" attribute
+
+            The filter attribute is to filter out requests that could not modify the session,
+            hence we don't replicate the session after the end of this request.
+            The filter is negative, ie, anything you put in the filter, you mean to filter out,
+            ie, no replication will be done on requests that match one of the filters.
+            The filter attribute is delimited by ;, so you can't escape out ; even if you wanted to.
+
+            filter=".*\.gif;.*\.js;" means that we will not replicate the session after requests with the URI
+            ending with .gif and .js are intercepted.
+            
+            The deployer element can be used to deploy apps cluster wide.
+            Currently the deployment only deploys/undeploys to working members in the cluster
+            so no WARs are copied upons startup of a broken node.
+            The deployer watches a directory (watchDir) for WAR files when watchEnabled="true"
+            When a new war file is added the war gets deployed to the local instance,
+            and then deployed to the other instances in the cluster.
+            When a war file is deleted from the watchDir the war is undeployed locally 
+            and cluster wide
+        -->
+        
+        <!--
+        <Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
+                 managerClassName="org.apache.catalina.cluster.session.DeltaManager"
+                 expireSessionsOnShutdown="false"
+                 useDirtyFlag="true"
+                 notifyListenersOnReplication="true">
+
+            <Membership 
+                className="org.apache.catalina.cluster.mcast.McastService"
+                mcastAddr="228.0.0.4"
+                mcastPort="45564"
+                mcastFrequency="500"
+                mcastDropTime="3000"/>
+
+            <Receiver 
+                className="org.apache.catalina.cluster.tcp.ReplicationListener"
+                tcpListenAddress="auto"
+                tcpListenPort="4001"
+                tcpSelectorTimeout="100"
+                tcpThreadCount="6"/>
+
+            <Sender
+                className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+                replicationMode="pooled"
+                ackTimeout="15000"/>
+
+            <Valve className="org.apache.catalina.cluster.tcp.ReplicationValve"
+                   filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
+                   
+            <Deployer className="org.apache.catalina.cluster.deploy.FarmWarDeployer"
+                      tempDir="/tmp/war-temp/"
+                      deployDir="/tmp/war-deploy/"
+                      watchDir="/tmp/war-listen/"
+                      watchEnabled="false"/>
+                      
+            <ClusterListener className="org.apache.catalina.cluster.session.ClusterSessionListener"/>
+        </Cluster>
+        -->        
+
+
+
+        <!-- Normally, users must authenticate themselves to each web app
+             individually.  Uncomment the following entry if you would like
+             a user to be authenticated the first time they encounter a
+             resource protected by a security constraint, and then have that
+             user identity maintained across *all* web applications contained
+             in this virtual host. -->
+        <!--
+        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
+        -->
+
+        <!-- Access log processes all requests for this virtual host.  By
+             default, log files are created in the "logs" directory relative to
+             $CATALINA_HOME.  If you wish, you can specify a different
+             directory with the "directory" attribute.  Specify either a relative
+             (to $CATALINA_HOME) or absolute path to the desired directory.
+        -->
+        <!--
+        <Valve className="org.apache.catalina.valves.AccessLogValve"
+                 directory="logs"  prefix="localhost_access_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/>
+        -->
+
+        <!-- Access log processes all requests for this virtual host.  By
+             default, log files are created in the "logs" directory relative to
+             $CATALINA_HOME.  If you wish, you can specify a different
+             directory with the "directory" attribute.  Specify either a relative
+             (to $CATALINA_HOME) or absolute path to the desired directory.
+             This access log implementation is optimized for maximum performance,
+             but is hardcoded to support only the "common" and "combined" patterns.
+        -->
+        <!--
+        <Valve className="org.apache.catalina.valves.FastCommonAccessLogValve"
+                 directory="logs"  prefix="localhost_access_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/>
+        -->
+        <!-- Access log processes all requests for this virtual host.  By
+             default, log files are created in the "logs" directory relative to
+             $CATALINA_HOME.  If you wish, you can specify a different
+             directory with the "directory" attribute.  Specify either a relative
+             (to $CATALINA_HOME) or absolute path to the desired directory.
+             This access log implementation is optimized for maximum performance,
+             but is hardcoded to support only the "common" and "combined" patterns.
+
+             This valve use NIO direct Byte Buffer to asynchornously store the
+             log.
+        -->
+        <!--
+        <Valve className="org.apache.catalina.valves.ByteBufferAccessLogValve"
+                 directory="logs"  prefix="localhost_access_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/>
+        -->
+
+      </Host>
+
+    </Engine>
+
+  </Service>
+
+</Server>
diff --git a/container/catalina/src/conf/tomcat-users.xml b/container/catalina/src/conf/tomcat-users.xml
new file mode 100644
index 0000000..388cfc9
--- /dev/null
+++ b/container/catalina/src/conf/tomcat-users.xml
@@ -0,0 +1,10 @@
+<!--
+  NOTE:  By default, no user is included in the "manager" role required
+  to operate the "/manager" web application.  If you wish to use this app,
+  you must define such a user - the username and password are arbitrary.
+-->
+<tomcat-users>
+  <user name="tomcat" password="tomcat" roles="tomcat" />
+  <user name="role1"  password="tomcat" roles="role1"  />
+  <user name="both"   password="tomcat" roles="tomcat,role1" />
+</tomcat-users>
diff --git a/container/catalina/src/conf/web.xml b/container/catalina/src/conf/web.xml
new file mode 100644
index 0000000..708ea3e
--- /dev/null
+++ b/container/catalina/src/conf/web.xml
@@ -0,0 +1,1070 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+  <!-- ======================== Introduction ============================== -->
+  <!-- This document defines default values for *all* web applications      -->
+  <!-- loaded into this instance of Tomcat.  As each application is         -->
+  <!-- deployed, this file is processed, followed by the                    -->
+  <!-- "/WEB-INF/web.xml" deployment descriptor from your own               -->
+  <!-- applications.                                                        -->
+  <!--                                                                      -->
+  <!-- WARNING:  Do not configure application-specific resources here!      -->
+  <!-- They should go in the "/WEB-INF/web.xml" file in your application.   -->
+
+
+  <!-- ================== Built In Servlet Definitions ==================== -->
+
+
+  <!-- The default servlet for all web applications, that serves static     -->
+  <!-- resources.  It processes all requests that are not mapped to other   -->
+  <!-- servlets with servlet mappings (defined either here or in your own   -->
+  <!-- web.xml file.  This servlet supports the following initialization    -->
+  <!-- parameters (default values are in square brackets):                  -->
+  <!--                                                                      -->
+  <!--   debug               Debugging detail level for messages logged     -->
+  <!--                       by this servlet.  [0]                          -->
+  <!--                                                                      -->
+  <!--   fileEncoding        Encoding to be used to read static resources   -->
+  <!--                       [platform default]                             -->
+  <!--                                                                      -->
+  <!--   input               Input buffer size (in bytes) when reading      -->
+  <!--                       resources to be served.  [2048]                -->
+  <!--                                                                      -->
+  <!--   listings            Should directory listings be produced if there -->
+  <!--                       is no welcome file in this directory?  [true]  -->
+  <!--                                                                      -->
+  <!--   output              Output buffer size (in bytes) when writing     -->
+  <!--                       resources to be served.  [2048]                -->
+  <!--                                                                      -->
+  <!--   readonly            Is this context "read only", so HTTP           -->
+  <!--                       commands like PUT and DELETE are               -->
+  <!--                       rejected?  [true]                              -->
+  <!--                                                                      -->
+  <!--   readmeFile          File name to display with the directory        -->
+  <!--                       contents. [null]                               -->
+  <!--                                                                      -->
+  <!--   sendfileSize        If the connector used supports sendfile, this  -->
+  <!--                       represents the minimal file size in KB for     -->
+  <!--                       which sendfile will be used. Use a negative    -->
+  <!--                       value to always disable sendfile.  [48]        -->
+  <!--                                                                      -->
+  <!--  For directory listing customization. Checks localXsltFile, then     -->
+  <!--  globalXsltFile, then defaults to original behavior.                 -->
+  <!--                                                                      -->
+  <!--   localXsltFile       Make directory listings an XML doc and         -->
+  <!--                       pass the result to this style sheet residing   -->
+  <!--                       in that directory. This overrides              -->
+  <!--                        globalXsltFile[null]                          -->
+  <!--                                                                      -->
+  <!--   globalXsltFile      Site wide configuration version of             -->
+  <!--                       localXsltFile This argument is expected        -->
+  <!--                       to be a physical file. [null]                  -->
+  <!--                                                                      -->
+  <!--                                                                      -->
+
+    <servlet>
+        <servlet-name>default</servlet-name>
+        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
+        <init-param>
+            <param-name>debug</param-name>
+            <param-value>0</param-value>
+        </init-param>
+        <init-param>
+            <param-name>listings</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+
+  <!-- The "invoker" servlet, which executes anonymous servlet classes      -->
+  <!-- that have not been defined in a web.xml file.  Traditionally, this   -->
+  <!-- servlet is mapped to the URL pattern "/servlet/*", but you can map   -->
+  <!-- it to other patterns as well.  The extra path info portion of such a -->
+  <!-- request must be the fully qualified class name of a Java class that  -->
+  <!-- implements Servlet (or extends HttpServlet), or the servlet name     -->
+  <!-- of an existing servlet definition.     This servlet supports the     -->
+  <!-- following initialization parameters (default values are in square    -->
+  <!-- brackets):                                                           -->
+  <!--                                                                      -->
+  <!--   debug               Debugging detail level for messages logged     -->
+  <!--                       by this servlet.  [0]                          -->
+
+<!--
+    <servlet>
+        <servlet-name>invoker</servlet-name>
+        <servlet-class>
+          org.apache.catalina.servlets.InvokerServlet
+        </servlet-class>
+        <init-param>
+            <param-name>debug</param-name>
+            <param-value>0</param-value>
+        </init-param>
+        <load-on-startup>2</load-on-startup>
+    </servlet>
+-->
+
+
+  <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
+  <!-- used by Tomcat to support JSP pages.  Traditionally, this servlet    -->
+  <!-- is mapped to the URL pattern "*.jsp".  This servlet supports the     -->
+  <!-- following initialization parameters (default values are in square    -->
+  <!-- brackets):                                                           -->
+  <!--                                                                      -->
+  <!--   checkInterval       If development is false and checkInterval is   -->
+  <!--                       greater than zero, background compilations are -->
+  <!--                       enabled. checkInterval is the time in seconds  -->
+  <!--                       between checks to see if a JSP page needs to   -->
+  <!--                       be recompiled. [0]                             -->
+  <!--                                                                      -->
+  <!--   modificationTestInterval                                           -->
+  <!--                       Causes a JSP (and its dependent files) to not  -->
+  <!--                       be checked for modification during the         -->
+  <!--                       specified time interval (in seconds) from the  -->
+  <!--                       last time the JSP was checked for              -->
+  <!--                       modification. A value of 0 will cause the JSP  -->
+  <!--                       to be checked on every access.                 -->
+  <!--                       Used in development mode only. [4]             -->
+  <!--                                                                      -->
+  <!--   compiler            Which compiler Ant should use to compile JSP   -->
+  <!--                       pages.  See the Ant documentation for more     -->
+  <!--                       information. [javac]                           -->
+  <!--                                                                      -->
+  <!--   classdebuginfo      Should the class file be compiled with         -->
+  <!--                       debugging information?  [true]                 -->
+  <!--                                                                      -->
+  <!--   classpath           What class path should I use while compiling   -->
+  <!--                       generated servlets?  [Created dynamically      -->
+  <!--                       based on the current web application]          -->
+  <!--                                                                      -->
+  <!--   development         Is Jasper used in development mode? If true,   -->
+  <!--                       the frequency at which JSPs are checked for    -->
+  <!--                       modification may be specified via the          -->
+  <!--                       modificationTestInterval parameter. [true]     -->
+  <!--                                                                      -->
+  <!--   enablePooling       Determines whether tag handler pooling is      -->
+  <!--                       enabled  [true]                                -->
+  <!--                                                                      -->
+  <!--   fork                Tell Ant to fork compiles of JSP pages so that -->
+  <!--                       a separate JVM is used for JSP page compiles   -->
+  <!--                       from the one Tomcat is running in. [true]      -->
+  <!--                                                                      -->
+  <!--   ieClassId           The class-id value to be sent to Internet      -->
+  <!--                       Explorer when using <jsp:plugin> tags.         -->
+  <!--                       [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93]   -->
+  <!--                                                                      -->
+  <!--   javaEncoding        Java file encoding to use for generating java  -->
+  <!--                       source files. [UTF8]                           -->
+  <!--                                                                      -->
+  <!--   keepgenerated       Should we keep the generated Java source code  -->
+  <!--                       for each page instead of deleting it? [true]   -->
+  <!--                                                                      -->
+  <!--   mappedfile          Should we generate static content with one     -->
+  <!--                       print statement per input line, to ease        -->
+  <!--                       debugging?  [true]                             -->
+  <!--                                                                      -->
+  <!--   trimSpaces          Should white spaces in template text between   -->
+  <!--                       actions or directives be trimmed?  [false]     -->
+  <!--                                                                      -->
+  <!--   suppressSmap        Should the generation of SMAP info for JSR45   -->
+  <!--                       debugging be suppressed?  [false]              -->
+  <!--                                                                      -->
+  <!--   dumpSmap            Should the SMAP info for JSR45 debugging be    -->
+  <!--                       dumped to a file? [false]                      -->
+  <!--                       False if suppressSmap is true                  -->
+  <!--                                                                      -->
+  <!--   genStrAsCharArray   Should text strings be generated as char       -->
+  <!--                       arrays, to improve performance in some cases?  -->
+  <!--                       [false]                                        -->
+  <!--                                                                      -->
+  <!--   errorOnUseBeanInvalidClassAttribute                                -->
+  <!--                       Should Jasper issue an error when the value of -->
+  <!--                       the class attribute in an useBean action is    -->
+  <!--                       not a valid bean class?  [true]                -->
+  <!--                                                                      -->
+  <!--   scratchdir          What scratch directory should we use when      -->
+  <!--                       compiling JSP pages?  [default work directory  -->
+  <!--                       for the current web application]               -->
+  <!--                                                                      -->
+  <!--   xpoweredBy          Determines whether X-Powered-By response       -->
+  <!--                       header is added by generated servlet  [false]  -->
+  <!--                                                                      -->
+  <!-- If you wish to use Jikes to compile JSP pages:                       -->
+  <!--   Please see the "Using Jikes" section of the Jasper-HowTo           -->
+  <!--   page in the Tomcat documentation.                                  -->
+
+    <servlet>
+        <servlet-name>jsp</servlet-name>
+        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+        <init-param>
+            <param-name>fork</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>xpoweredBy</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <load-on-startup>3</load-on-startup>
+    </servlet>
+
+
+  <!-- NOTE: An SSI Filter is also available as an alternative SSI          -->
+  <!-- implementation. Use either the Servlet or the Filter but NOT both.   -->
+  <!--                                                                      -->
+  <!-- Server Side Includes processing servlet, which processes SSI         -->
+  <!-- directives in HTML pages consistent with similar support in web      -->
+  <!-- servers like Apache.  Traditionally, this servlet is mapped to the   -->
+  <!-- URL pattern "*.shtml".  This servlet supports the following          -->
+  <!-- initialization parameters (default values are in square brackets):   -->
+  <!--                                                                      -->
+  <!--   buffered            Should output from this servlet be buffered?   -->
+  <!--                       (0=false, 1=true)  [0]                         -->
+  <!--                                                                      -->
+  <!--   debug               Debugging detail level for messages logged     -->
+  <!--                       by this servlet.  [0]                          -->
+  <!--                                                                      -->
+  <!--   expires             The number of seconds before a page with SSI   -->
+  <!--                       directives will expire.  [No default]          -->
+  <!--                                                                      -->
+  <!--   isVirtualWebappRelative                                            -->
+  <!--                       Should "virtual" paths be interpreted as       -->
+  <!--                       relative to the context root, instead of       -->
+  <!--                       the server root?  (0=false, 1=true) [0]        -->
+  <!--                                                                      -->
+  <!--   inputEncoding       The encoding to assume for SSI resources if    -->
+  <!--                       one is not available from the resource.        -->
+  <!--                       [Platform default]                             -->
+  <!--                                                                      -->
+  <!--   outputEncoding      The encoding to use for the page that results  -->
+  <!--                       from the SSI processing. [UTF-8]               -->
+  <!--                                                                      -->
+  <!--                                                                      -->
+  <!-- IMPORTANT: To use the SSI servlet, you also need to rename the       -->
+  <!--            $CATALINA_HOME/server/lib/servlets-ssi.renametojar file   -->
+  <!--            to $CATALINA_HOME/server/lib/servlets-ssi.jar             -->
+
+<!--
+    <servlet>
+        <servlet-name>ssi</servlet-name>
+        <servlet-class>
+          org.apache.catalina.ssi.SSIServlet
+        </servlet-class>
+        <init-param>
+          <param-name>buffered</param-name>
+          <param-value>1</param-value>
+        </init-param>
+        <init-param>
+          <param-name>debug</param-name>
+          <param-value>0</param-value>
+        </init-param>
+        <init-param>
+          <param-name>expires</param-name>
+          <param-value>666</param-value>
+        </init-param>
+        <init-param>
+          <param-name>isVirtualWebappRelative</param-name>
+          <param-value>0</param-value>
+        </init-param>
+        <load-on-startup>4</load-on-startup>
+    </servlet>
+-->
+
+
+  <!-- Common Gateway Includes (CGI) processing servlet, which supports     -->
+  <!-- execution of external applications that conform to the CGI spec      -->
+  <!-- requirements.  Typically, this servlet is mapped to the URL pattern  -->
+  <!-- "/cgi-bin/*", which means that any CGI applications that are         -->
+  <!-- executed must be present within the web application.  This servlet   -->
+  <!-- supports the following initialization parameters (default values     -->
+  <!-- are in square brackets):                                             -->
+  <!--                                                                      -->
+  <!--   cgiPathPrefix        The CGI search path will start at             -->
+  <!--                        webAppRootDir + File.separator + this prefix. -->
+  <!--                        [WEB-INF/cgi]                                 -->
+  <!--                                                                      -->
+  <!--   debug                Debugging detail level for messages logged    -->
+  <!--                        by this servlet.  [0]                         -->
+  <!--                                                                      -->
+  <!--   executable           Name of the exectuable used to run the        -->
+  <!--                        script. [perl]                                -->
+  <!--                                                                      -->
+  <!--   parameterEncoding    Name of parameter encoding to be used with    -->
+  <!--                        CGI servlet.                                  -->
+  <!--                        [System.getProperty("file.encoding","UTF-8")] -->
+  <!--                                                                      -->
+  <!--   passShellEnvironment Should the shell environment variables (if    -->
+  <!--                        any) be passed to the CGI script? [false]     -->
+  <!--                                                                      -->
+  <!-- IMPORTANT: To use the CGI servlet, you also need to rename the       -->
+  <!--            $CATALINA_HOME/server/lib/servlets-cgi.renametojar file   -->
+  <!--            to $CATALINA_HOME/server/lib/servlets-cgi.jar             -->
+
+<!--
+    <servlet>
+        <servlet-name>cgi</servlet-name>
+        <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
+        <init-param>
+          <param-name>debug</param-name>
+          <param-value>6</param-value>
+        </init-param>
+        <init-param>
+          <param-name>cgiPathPrefix</param-name>
+          <param-value>WEB-INF/cgi</param-value>
+        </init-param>
+         <load-on-startup>5</load-on-startup>
+    </servlet>
+-->
+
+
+  <!-- ================ Built In Servlet Mappings ========================= -->
+
+
+  <!-- The servlet mappings for the built in servlets defined above.  Note  -->
+  <!-- that, by default, the CGI and SSI servlets are *not* mapped.  You    -->
+  <!-- must uncomment these mappings (or add them to your application's own -->
+  <!-- web.xml deployment descriptor) to enable these services              -->
+
+    <!-- The mapping for the default servlet -->
+    <servlet-mapping>
+        <servlet-name>default</servlet-name>
+        <url-pattern>/</url-pattern>
+    </servlet-mapping>
+
+    <!-- The mapping for the invoker servlet -->
+<!--
+    <servlet-mapping>
+        <servlet-name>invoker</servlet-name>
+        <url-pattern>/servlet/*</url-pattern>
+    </servlet-mapping>
+-->
+
+    <!-- The mapping for the JSP servlet -->
+    <servlet-mapping>
+        <servlet-name>jsp</servlet-name>
+        <url-pattern>*.jsp</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>jsp</servlet-name>
+        <url-pattern>*.jspx</url-pattern>
+    </servlet-mapping>
+
+    <!-- The mapping for the SSI servlet -->
+<!--
+    <servlet-mapping>
+        <servlet-name>ssi</servlet-name>
+        <url-pattern>*.shtml</url-pattern>
+    </servlet-mapping>
+-->
+
+    <!-- The mapping for the CGI Gateway servlet -->
+
+<!--
+    <servlet-mapping>
+        <servlet-name>cgi</servlet-name>
+        <url-pattern>/cgi-bin/*</url-pattern>
+    </servlet-mapping>
+-->
+
+
+  <!-- ================== Built In Filter Definitions ===================== -->
+
+  <!-- NOTE: An SSI Servlet is also available as an alternative SSI         -->
+  <!-- implementation. Use either the Servlet or the Filter but NOT both.   -->
+  <!--                                                                      -->
+  <!-- Server Side Includes processing filter, which processes SSI          -->
+  <!-- directives in HTML pages consistent with similar support in web      -->
+  <!-- servers like Apache.  Traditionally, this filter is mapped to the    -->
+  <!-- URL pattern "*.shtml", though it can be mapped to "*" as it will     -->
+  <!-- selectively enable/disable SSI processing based on mime types.  The  -->
+  <!-- contentType init param allows you to apply SSI processing to JSP     -->
+  <!-- pages, javascript, or any other content you wish.  This filter       -->
+  <!-- supports the following initialization parameters (default values are -->
+  <!-- in square brackets):                                                 -->
+  <!--                                                                      -->
+  <!--   contentType         A regex pattern that must be matched before    -->
+  <!--                       SSI processing is applied.                     -->
+  <!--                       [text/x-server-parsed-html(;.*)?]              -->
+  <!--                                                                      -->
+  <!--   debug               Debugging detail level for messages logged     -->
+  <!--                       by this servlet.  [0]                          -->
+  <!--                                                                      -->
+  <!--   expires             The number of seconds before a page with SSI   -->
+  <!--                       directives will expire.  [No default]          -->
+  <!--                                                                      -->
+  <!--   isVirtualWebappRelative                                            -->
+  <!--                       Should "virtual" paths be interpreted as       -->
+  <!--                       relative to the context root, instead of       -->
+  <!--                       the server root?  (0=false, 1=true) [0]        -->
+  <!--                                                                      -->
+  <!--                                                                      -->
+  <!-- IMPORTANT: To use the SSI filter, you also need to rename the        -->
+  <!--            $CATALINA_HOME/server/lib/servlets-ssi.renametojar file   -->
+  <!--            to $CATALINA_HOME/server/lib/servlets-ssi.jar             -->
+
+<!--
+    <filter>
+        <filter-name>ssi</filter-name>
+        <filter-class>
+          org.apache.catalina.ssi.SSIFilter
+        </filter-class>
+        <init-param>
+          <param-name>contentType</param-name>
+          <param-value>text/x-server-parsed-html(;.*)?</param-value>
+        </init-param>
+        <init-param>
+          <param-name>debug</param-name>
+          <param-value>0</param-value>
+        </init-param>
+        <init-param>
+          <param-name>expires</param-name>
+          <param-value>666</param-value>
+        </init-param>
+        <init-param>
+          <param-name>isVirtualWebappRelative</param-name>
+          <param-value>0</param-value>
+        </init-param>
+    </filter>
+-->
+
+
+  <!-- ==================== Built In Filter Mappings ====================== -->
+
+  <!-- The mapping for the SSI Filter -->
+<!--
+    <filter-mapping>
+        <filter-name>ssi</filter-name>
+        <url-pattern>*.shtml</url-pattern>
+    </filter-mapping>
+-->
+
+
+  <!-- ==================== Default Session Configuration ================= -->
+  <!-- You can set the default session timeout (in minutes) for all newly   -->
+  <!-- created sessions by modifying the value below.                       -->
+
+    <session-config>
+        <session-timeout>30</session-timeout>
+    </session-config>
+
+
+  <!-- ===================== Default MIME Type Mappings =================== -->
+  <!-- When serving static resources, Tomcat will automatically generate    -->
+  <!-- a "Content-Type" header based on the resource's filename extension,  -->
+  <!-- based on these mappings.  Additional mappings can be added here (to  -->
+  <!-- apply to all web applications), or in your own application's web.xml -->
+  <!-- deployment descriptor.                                               -->
+
+    <mime-mapping>
+        <extension>abs</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ai</extension>
+        <mime-type>application/postscript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>aif</extension>
+        <mime-type>audio/x-aiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>aifc</extension>
+        <mime-type>audio/x-aiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>aiff</extension>
+        <mime-type>audio/x-aiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>aim</extension>
+        <mime-type>application/x-aim</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>art</extension>
+        <mime-type>image/x-jg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>asf</extension>
+        <mime-type>video/x-ms-asf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>asx</extension>
+        <mime-type>video/x-ms-asf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>au</extension>
+        <mime-type>audio/basic</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>avi</extension>
+        <mime-type>video/x-msvideo</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>avx</extension>
+        <mime-type>video/x-rad-screenplay</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>bcpio</extension>
+        <mime-type>application/x-bcpio</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>bin</extension>
+        <mime-type>application/octet-stream</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>bmp</extension>
+        <mime-type>image/bmp</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>body</extension>
+        <mime-type>text/html</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>cdf</extension>
+        <mime-type>application/x-cdf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>cer</extension>
+        <mime-type>application/x-x509-ca-cert</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>class</extension>
+        <mime-type>application/java</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>cpio</extension>
+        <mime-type>application/x-cpio</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>csh</extension>
+        <mime-type>application/x-csh</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>css</extension>
+        <mime-type>text/css</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>dib</extension>
+        <mime-type>image/bmp</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>doc</extension>
+        <mime-type>application/msword</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>dtd</extension>
+        <mime-type>application/xml-dtd</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>dv</extension>
+        <mime-type>video/x-dv</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>dvi</extension>
+        <mime-type>application/x-dvi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>eps</extension>
+        <mime-type>application/postscript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>etx</extension>
+        <mime-type>text/x-setext</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>exe</extension>
+        <mime-type>application/octet-stream</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>gif</extension>
+        <mime-type>image/gif</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>gtar</extension>
+        <mime-type>application/x-gtar</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>gz</extension>
+        <mime-type>application/x-gzip</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>hdf</extension>
+        <mime-type>application/x-hdf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>hqx</extension>
+        <mime-type>application/mac-binhex40</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>htc</extension>
+        <mime-type>text/x-component</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>htm</extension>
+        <mime-type>text/html</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>html</extension>
+        <mime-type>text/html</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>hqx</extension>
+        <mime-type>application/mac-binhex40</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ief</extension>
+        <mime-type>image/ief</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jad</extension>
+        <mime-type>text/vnd.sun.j2me.app-descriptor</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jar</extension>
+        <mime-type>application/java-archive</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>java</extension>
+        <mime-type>text/plain</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jnlp</extension>
+        <mime-type>application/x-java-jnlp-file</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jpe</extension>
+        <mime-type>image/jpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jpeg</extension>
+        <mime-type>image/jpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jpg</extension>
+        <mime-type>image/jpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>js</extension>
+        <mime-type>text/javascript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jsf</extension>
+        <mime-type>text/plain</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jspf</extension>
+        <mime-type>text/plain</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>kar</extension>
+        <mime-type>audio/x-midi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>latex</extension>
+        <mime-type>application/x-latex</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>m3u</extension>
+        <mime-type>audio/x-mpegurl</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mac</extension>
+        <mime-type>image/x-macpaint</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>man</extension>
+        <mime-type>application/x-troff-man</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mathml</extension>
+        <mime-type>application/mathml+xml</mime-type> 
+    </mime-mapping>
+    <mime-mapping>
+        <extension>me</extension>
+        <mime-type>application/x-troff-me</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mid</extension>
+        <mime-type>audio/x-midi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>midi</extension>
+        <mime-type>audio/x-midi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mif</extension>
+        <mime-type>application/x-mif</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mov</extension>
+        <mime-type>video/quicktime</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>movie</extension>
+        <mime-type>video/x-sgi-movie</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mp1</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mp2</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mp3</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpa</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpe</extension>
+        <mime-type>video/mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpeg</extension>
+        <mime-type>video/mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpega</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpg</extension>
+        <mime-type>video/mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpv2</extension>
+        <mime-type>video/mpeg2</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ms</extension>
+        <mime-type>application/x-wais-source</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>nc</extension>
+        <mime-type>application/x-netcdf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>oda</extension>
+        <mime-type>application/oda</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ogg</extension>
+        <mime-type>application/ogg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pbm</extension>
+        <mime-type>image/x-portable-bitmap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pct</extension>
+        <mime-type>image/pict</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pdf</extension>
+        <mime-type>application/pdf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pgm</extension>
+        <mime-type>image/x-portable-graymap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pic</extension>
+        <mime-type>image/pict</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pict</extension>
+        <mime-type>image/pict</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pls</extension>
+        <mime-type>audio/x-scpls</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>png</extension>
+        <mime-type>image/png</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pnm</extension>
+        <mime-type>image/x-portable-anymap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pnt</extension>
+        <mime-type>image/x-macpaint</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ppm</extension>
+        <mime-type>image/x-portable-pixmap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ppt</extension>
+        <mime-type>application/powerpoint</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ps</extension>
+        <mime-type>application/postscript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>psd</extension>
+        <mime-type>image/x-photoshop</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>qt</extension>
+        <mime-type>video/quicktime</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>qti</extension>
+        <mime-type>image/x-quicktime</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>qtif</extension>
+        <mime-type>image/x-quicktime</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ras</extension>
+        <mime-type>image/x-cmu-raster</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rdf</extension>
+        <mime-type>application/rdf+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rgb</extension>
+        <mime-type>image/x-rgb</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rm</extension>
+        <mime-type>application/vnd.rn-realmedia</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>roff</extension>
+        <mime-type>application/x-troff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rtf</extension>
+        <mime-type>application/rtf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rtx</extension>
+        <mime-type>text/richtext</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>sh</extension>
+        <mime-type>application/x-sh</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>shar</extension>
+        <mime-type>application/x-shar</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>shtml</extension>
+        <mime-type>text/x-server-parsed-html</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>smf</extension>
+        <mime-type>audio/x-midi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>sit</extension>
+        <mime-type>application/x-stuffit</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>snd</extension>
+        <mime-type>audio/basic</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>src</extension>
+        <mime-type>application/x-wais-source</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>sv4cpio</extension>
+        <mime-type>application/x-sv4cpio</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>sv4crc</extension>
+        <mime-type>application/x-sv4crc</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>svg</extension>
+        <mime-type>image/svg+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>swf</extension>
+        <mime-type>application/x-shockwave-flash</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>t</extension>
+        <mime-type>application/x-troff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tar</extension>
+        <mime-type>application/x-tar</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tcl</extension>
+        <mime-type>application/x-tcl</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tex</extension>
+        <mime-type>application/x-tex</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>texi</extension>
+        <mime-type>application/x-texinfo</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>texinfo</extension>
+        <mime-type>application/x-texinfo</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tif</extension>
+        <mime-type>image/tiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tiff</extension>
+        <mime-type>image/tiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tr</extension>
+        <mime-type>application/x-troff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tsv</extension>
+        <mime-type>text/tab-separated-values</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>txt</extension>
+        <mime-type>text/plain</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ulw</extension>
+        <mime-type>audio/basic</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ustar</extension>
+        <mime-type>application/x-ustar</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>vxml</extension>
+        <mime-type>application/voicexml+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xbm</extension>
+        <mime-type>image/x-xbitmap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xht</extension>
+        <mime-type>application/xhtml+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xhtml</extension>
+        <mime-type>application/xhtml+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xml</extension>
+        <mime-type>application/xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xpm</extension>
+        <mime-type>image/x-xpixmap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xsl</extension>
+        <mime-type>application/xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xslt</extension>
+        <mime-type>application/xslt+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xul</extension>
+        <mime-type>application/vnd.mozilla.xul+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xwd</extension>
+        <mime-type>image/x-xwindowdump</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>wav</extension>
+        <mime-type>audio/x-wav</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>svg</extension>
+        <mime-type>image/svg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>svgz</extension>
+        <mime-type>image/svg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>vsd</extension>
+        <mime-type>application/x-visio</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- Wireless Bitmap -->
+        <extension>wbmp</extension>
+        <mime-type>image/vnd.wap.wbmp</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- WML Source -->
+        <extension>wml</extension>
+        <mime-type>text/vnd.wap.wml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- Compiled WML -->
+        <extension>wmlc</extension>
+        <mime-type>application/vnd.wap.wmlc</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- WML Script Source -->
+        <extension>wmls</extension>
+        <mime-type>text/vnd.wap.wmlscript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- Compiled WML Script -->
+        <extension>wmlscriptc</extension>
+        <mime-type>application/vnd.wap.wmlscriptc</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>wrl</extension>
+        <mime-type>x-world/x-vrml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>Z</extension>
+        <mime-type>application/x-compress</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>z</extension>
+        <mime-type>application/x-compress</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>zip</extension>
+        <mime-type>application/zip</mime-type>
+    </mime-mapping>
+
+
+  <!-- ==================== Default Welcome File List ===================== -->
+  <!-- When a request URI refers to a directory, the default servlet looks  -->
+  <!-- for a "welcome file" within that directory and, if present,          -->
+  <!-- to the corresponding resource URI for display.  If no welcome file   -->
+  <!-- is present, the default servlet either serves a directory listing,   -->
+  <!-- or returns a 404 status, depending on how it is configured.          -->
+  <!--                                                                      -->
+  <!-- If you define welcome files in your own application's web.xml        -->
+  <!-- deployment descriptor, that list *replaces* the list configured      -->
+  <!-- here, so be sure that you include any of the default values that     -->
+  <!-- you wish to include.                                                 -->
+
+    <welcome-file-list>
+        <welcome-file>index.html</welcome-file>
+        <welcome-file>index.htm</welcome-file>
+        <welcome-file>index.jsp</welcome-file>
+    </welcome-file-list>
+
+</web-app>
diff --git a/container/catalina/src/share/org/apache/catalina/Authenticator.java b/container/catalina/src/share/org/apache/catalina/Authenticator.java
new file mode 100644
index 0000000..83b56dc
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Authenticator.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+/**
+ * An <b>Authenticator</b> is a component (usually a Valve or Container) that
+ * provides some sort of authentication service.  The interface itself has no
+ * functional significance,  but is used as a tagging mechanism so that other
+ * components can detect the presence (via an "instanceof Authenticator" test)
+ * of an already configured authentication service.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Authenticator {
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Cluster.java b/container/catalina/src/share/org/apache/catalina/Cluster.java
new file mode 100644
index 0000000..eb86712
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Cluster.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import java.io.IOException;
+import java.net.URL;
+/**
+ * A <b>Cluster</b> works as a Cluster client/server for the local host
+ * Different Cluster implementations can be used to support different
+ * ways to communicate within the Cluster. A Cluster implementation is
+ * responsible for setting up a way to communicate within the Cluster
+ * and also supply "ClientApplications" with <code>ClusterSender</code>
+ * used when sending information in the Cluster and
+ * <code>ClusterInfo</code> used for receiving information in the Cluster.
+ *
+ * @author Bip Thelin
+ * @author Remy Maucherat
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+public interface Cluster {
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return descriptive information about this Cluster implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+    /**
+     * Return the name of the cluster that this Server is currently
+     * configured to operate within.
+     *
+     * @return The name of the cluster associated with this server
+     */
+    public String getClusterName();
+
+    /**
+     * Set the name of the cluster to join, if no cluster with
+     * this name is present create one.
+     *
+     * @param clusterName The clustername to join
+     */
+    public void setClusterName(String clusterName);
+
+    /**
+     * Set the Container associated with our Cluster
+     *
+     * @param container The Container to use
+     */
+    public void setContainer(Container container);
+
+    /**
+     * Get the Container associated with our Cluster
+     *
+     * @return The Container associated with our Cluster
+     */
+    public Container getContainer();
+
+    /**
+     * Set the protocol parameters.
+     *
+     * @param protocol The protocol used by the cluster
+     * @deprecated
+     */
+    public void setProtocol(String protocol);
+
+    /**
+     * Get the protocol used by the cluster.
+     *
+     * @return The protocol
+     * @deprecated
+     */
+    public String getProtocol();
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Create a new manager which will use this cluster to replicate its
+     * sessions.
+     *
+     * @param name Name (key) of the application with which the manager is
+     * associated
+     */
+    public Manager createManager(String name);
+
+    // --------------------------------------------------------- Cluster Wide Deployments
+    
+    
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess();
+
+
+    /**
+     * Start an existing web application, attached to the specified context
+     * path in all the other nodes in the cluster.
+     * Only starts a web application if it is not running.
+     *
+     * @param contextPath The context path of the application to be started
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  startup
+     * @deprecated
+     */
+    public void startContext(String contextPath) throws IOException;
+
+
+    /**
+     * Install a new web application, whose web application archive is at the
+     * specified URL, into this container with the specified context path.
+     * A context path of "" (the empty string) should be used for the root
+     * application for this container.  Otherwise, the context path must
+     * start with a slash.
+     * <p>
+     * If this application is successfully installed, a ContainerEvent of type
+     * <code>PRE_INSTALL_EVENT</code> will be sent to registered listeners
+     * before the associated Context is started, and a ContainerEvent of type
+     * <code>INSTALL_EVENT</code> will be sent to all registered listeners
+     * after the associated Context is started, with the newly created
+     * <code>Context</code> as an argument.
+     *
+     * @param contextPath The context path to which this application should
+     *  be installed (must be unique)
+     * @param war A URL of type "jar:" that points to a WAR file, or type
+     *  "file:" that points to an unpacked directory structure containing
+     *  the web application to be installed
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalStateException if the specified context path
+     *  is already attached to an existing web application
+     * @deprecated
+     */
+    public void installContext(String contextPath, URL war);
+
+    /**
+     * Stop an existing web application, attached to the specified context
+     * path.  Only stops a web application if it is running.
+     *
+     * @param contextPath The context path of the application to be stopped
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs while stopping
+     *  the web application
+     * @deprecated
+     */
+    public void stop(String contextPath) throws IOException;
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Contained.java b/container/catalina/src/share/org/apache/catalina/Contained.java
new file mode 100644
index 0000000..a676b65
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Contained.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+/**
+ * <p>Decoupling interface which specifies that an implementing class is
+ * associated with at most one <strong>Container</strong> instance.</p>
+ *
+ * @author Craig R. McClanahan
+ * @author Peter Donald
+ * @version $Revision$ $Date$
+ */
+
+public interface Contained {
+
+
+    //-------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the <code>Container</code> with which this instance is associated
+     * (if any); otherwise return <code>null</code>.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the <code>Container</code> with which this instance is associated.
+     *
+     * @param container The Container instance with which this instance is to
+     *  be associated, or <code>null</code> to disassociate this instance
+     *  from any Container
+     */
+    public void setContainer(Container container);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Container.java b/container/catalina/src/share/org/apache/catalina/Container.java
new file mode 100644
index 0000000..5f144bc
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Container.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.naming.directory.DirContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+
+/**
+ * A <b>Container</b> is an object that can execute requests received from
+ * a client, and return responses based on those requests.  A Container may
+ * optionally support a pipeline of Valves that process the request in an
+ * order configured at runtime, by implementing the <b>Pipeline</b> interface
+ * as well.
+ * <p>
+ * Containers will exist at several conceptual levels within Catalina.  The
+ * following examples represent common cases:
+ * <ul>
+ * <li><b>Engine</b> - Representation of the entire Catalina servlet engine,
+ *     most likely containing one or more subcontainers that are either Host
+ *     or Context implementations, or other custom groups.
+ * <li><b>Host</b> - Representation of a virtual host containing a number
+ *     of Contexts.
+ * <li><b>Context</b> - Representation of a single ServletContext, which will
+ *     typically contain one or more Wrappers for the supported servlets.
+ * <li><b>Wrapper</b> - Representation of an individual servlet definition
+ *     (which may support multiple servlet instances if the servlet itself
+ *     implements SingleThreadModel).
+ * </ul>
+ * A given deployment of Catalina need not include Containers at all of the
+ * levels described above.  For example, an administration application
+ * embedded within a network device (such as a router) might only contain
+ * a single Context and a few Wrappers, or even a single Wrapper if the
+ * application is relatively small.  Therefore, Container implementations
+ * need to be designed so that they will operate correctly in the absence
+ * of parent Containers in a given deployment.
+ * <p>
+ * A Container may also be associated with a number of support components
+ * that provide functionality which might be shared (by attaching it to a
+ * parent Container) or individually customized.  The following support
+ * components are currently recognized:
+ * <ul>
+ * <li><b>Loader</b> - Class loader to use for integrating new Java classes
+ *     for this Container into the JVM in which Catalina is running.
+ * <li><b>Logger</b> - Implementation of the <code>log()</code> method
+ *     signatures of the <code>ServletContext</code> interface.
+ * <li><b>Manager</b> - Manager for the pool of Sessions associated with
+ *     this Container.
+ * <li><b>Realm</b> - Read-only interface to a security domain, for
+ *     authenticating user identities and their corresponding roles.
+ * <li><b>Resources</b> - JNDI directory context enabling access to static
+ *     resources, enabling custom linkages to existing server components when
+ *     Catalina is embedded in a larger server.
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public interface Container {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The ContainerEvent event type sent when a child container is added
+     * by <code>addChild()</code>.
+     */
+    public static final String ADD_CHILD_EVENT = "addChild";
+
+
+    /**
+     * The ContainerEvent event type sent when a Mapper is added
+     * by <code>addMapper()</code>.
+     */
+    public static final String ADD_MAPPER_EVENT = "addMapper";
+
+
+    /**
+     * The ContainerEvent event type sent when a valve is added
+     * by <code>addValve()</code>, if this Container supports pipelines.
+     */
+    public static final String ADD_VALVE_EVENT = "addValve";
+
+
+    /**
+     * The ContainerEvent event type sent when a child container is removed
+     * by <code>removeChild()</code>.
+     */
+    public static final String REMOVE_CHILD_EVENT = "removeChild";
+
+
+    /**
+     * The ContainerEvent event type sent when a Mapper is removed
+     * by <code>removeMapper()</code>.
+     */
+    public static final String REMOVE_MAPPER_EVENT = "removeMapper";
+
+
+    /**
+     * The ContainerEvent event type sent when a valve is removed
+     * by <code>removeValve()</code>, if this Container supports pipelines.
+     */
+    public static final String REMOVE_VALVE_EVENT = "removeValve";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the Loader with which this Container is associated.  If there is
+     * no associated Loader, return the Loader associated with our parent
+     * Container (if any); otherwise, return <code>null</code>.
+     */
+    public Loader getLoader();
+
+
+    /**
+     * Set the Loader with which this Container is associated.
+     *
+     * @param loader The newly associated loader
+     */
+    public void setLoader(Loader loader);
+
+
+    /**
+     * Return the Logger with which this Container is associated.  If there is
+     * no associated Logger, return the Logger associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Log getLogger();
+
+
+    /**
+     * Return the Manager with which this Container is associated.  If there is
+     * no associated Manager, return the Manager associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Manager getManager();
+
+
+    /**
+     * Set the Manager with which this Container is associated.
+     *
+     * @param manager The newly associated Manager
+     */
+    public void setManager(Manager manager);
+
+
+    /**
+     * Return an object which may be utilized for mapping to this component.
+     */
+    public Object getMappingObject();
+
+    
+    /**
+     * Return the JMX name associated with this container.
+     */
+    public String getObjectName();    
+
+    /**
+     * Return the Pipeline object that manages the Valves associated with
+     * this Container.
+     */
+    public Pipeline getPipeline();
+
+
+    /**
+     * Return the Cluster with which this Container is associated.  If there is
+     * no associated Cluster, return the Cluster associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Cluster getCluster();
+
+
+    /**
+     * Set the Cluster with which this Container is associated.
+     *
+     * @param cluster the Cluster with which this Container is associated.
+     */
+    public void setCluster(Cluster cluster);
+
+
+    /**
+     * Get the delay between the invocation of the backgroundProcess method on
+     * this container and its children. Child containers will not be invoked
+     * if their delay value is not negative (which would mean they are using 
+     * their own thread). Setting this to a positive value will cause 
+     * a thread to be spawn. After waiting the specified amount of time, 
+     * the thread will invoke the executePeriodic method on this container 
+     * and all its children.
+     */
+    public int getBackgroundProcessorDelay();
+
+
+    /**
+     * Set the delay between the invocation of the execute method on this
+     * container and its children.
+     * 
+     * @param delay The delay in seconds between the invocation of 
+     *              backgroundProcess methods
+     */
+    public void setBackgroundProcessorDelay(int delay);
+
+
+    /**
+     * Return a name string (suitable for use by humans) that describes this
+     * Container.  Within the set of child containers belonging to a particular
+     * parent, Container names must be unique.
+     */
+    public String getName();
+
+
+    /**
+     * Set a name string (suitable for use by humans) that describes this
+     * Container.  Within the set of child containers belonging to a particular
+     * parent, Container names must be unique.
+     *
+     * @param name New name of this container
+     *
+     * @exception IllegalStateException if this Container has already been
+     *  added to the children of a parent Container (after which the name
+     *  may not be changed)
+     */
+    public void setName(String name);
+
+
+    /**
+     * Return the Container for which this Container is a child, if there is
+     * one.  If there is no defined parent, return <code>null</code>.
+     */
+    public Container getParent();
+
+
+    /**
+     * Set the parent Container to which this Container is being added as a
+     * child.  This Container may refuse to become attached to the specified
+     * Container by throwing an exception.
+     *
+     * @param container Container to which this Container is being added
+     *  as a child
+     *
+     * @exception IllegalArgumentException if this Container refuses to become
+     *  attached to the specified Container
+     */
+    public void setParent(Container container);
+
+
+    /**
+     * Return the parent class loader (if any) for web applications.
+     */
+    public ClassLoader getParentClassLoader();
+
+
+    /**
+     * Set the parent class loader (if any) for web applications.
+     * This call is meaningful only <strong>before</strong> a Loader has
+     * been configured, and the specified value (if non-null) should be
+     * passed as an argument to the class loader constructor.
+     *
+     * @param parent The new parent class loader
+     */
+    public void setParentClassLoader(ClassLoader parent);
+
+
+    /**
+     * Return the Realm with which this Container is associated.  If there is
+     * no associated Realm, return the Realm associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Realm getRealm();
+
+
+    /**
+     * Set the Realm with which this Container is associated.
+     *
+     * @param realm The newly associated Realm
+     */
+    public void setRealm(Realm realm);
+
+
+    /**
+     * Return the Resources with which this Container is associated.  If there
+     * is no associated Resources object, return the Resources associated with
+     * our parent Container (if any); otherwise return <code>null</code>.
+     */
+    public DirContext getResources();
+
+
+    /**
+     * Set the Resources object with which this Container is associated.
+     *
+     * @param resources The newly associated Resources
+     */
+    public void setResources(DirContext resources);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess();
+
+
+    /**
+     * Add a new child Container to those associated with this Container,
+     * if supported.  Prior to adding this Container to the set of children,
+     * the child's <code>setParent()</code> method must be called, with this
+     * Container as an argument.  This method may thrown an
+     * <code>IllegalArgumentException</code> if this Container chooses not
+     * to be attached to the specified Container, in which case it is not added
+     *
+     * @param child New child Container to be added
+     *
+     * @exception IllegalArgumentException if this exception is thrown by
+     *  the <code>setParent()</code> method of the child Container
+     * @exception IllegalArgumentException if the new child does not have
+     *  a name unique from that of existing children of this Container
+     * @exception IllegalStateException if this Container does not support
+     *  child Containers
+     */
+    public void addChild(Container child);
+
+
+    /**
+     * Add a container event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addContainerListener(ContainerListener listener);
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Return the child Container, associated with this Container, with
+     * the specified name (if any); otherwise, return <code>null</code>
+     *
+     * @param name Name of the child Container to be retrieved
+     */
+    public Container findChild(String name);
+
+
+    /**
+     * Return the set of children Containers associated with this Container.
+     * If this Container has no children, a zero-length array is returned.
+     */
+    public Container[] findChildren();
+
+
+    /**
+     * Return the set of container listeners associated with this Container.
+     * If this Container has no registered container listeners, a zero-length
+     * array is returned.
+     */
+    public ContainerListener[] findContainerListeners();
+
+
+    /**
+     * Process the specified Request, and generate the corresponding Response,
+     * according to the design of this particular Container.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     *
+     * @exception IOException if an input/output error occurred while
+     *  processing
+     * @exception ServletException if a ServletException was thrown
+     *  while processing this request
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+
+    /**
+     * Remove an existing child Container from association with this parent
+     * Container.
+     *
+     * @param child Existing child Container to be removed
+     */
+    public void removeChild(Container child);
+
+
+    /**
+     * Remove a container event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeContainerListener(ContainerListener listener);
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ContainerEvent.java b/container/catalina/src/share/org/apache/catalina/ContainerEvent.java
new file mode 100644
index 0000000..d0bf538
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ContainerEvent.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.util.EventObject;
+
+
+/**
+ * General event for notifying listeners of significant changes on a Container.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class ContainerEvent
+    extends EventObject {
+
+
+    /**
+     * The Container on which this event occurred.
+     */
+    private Container container = null;
+
+
+    /**
+     * The event data associated with this event.
+     */
+    private Object data = null;
+
+
+    /**
+     * The event type this instance represents.
+     */
+    private String type = null;
+
+
+    /**
+     * Construct a new ContainerEvent with the specified parameters.
+     *
+     * @param container Container on which this event occurred
+     * @param type Event type
+     * @param data Event data
+     */
+    public ContainerEvent(Container container, String type, Object data) {
+
+        super(container);
+        this.container = container;
+        this.type = type;
+        this.data = data;
+
+    }
+
+
+    /**
+     * Return the event data of this event.
+     */
+    public Object getData() {
+
+        return (this.data);
+
+    }
+
+
+    /**
+     * Return the Container on which this event occurred.
+     */
+    public Container getContainer() {
+
+        return (this.container);
+
+    }
+
+
+    /**
+     * Return the event type of this event.
+     */
+    public String getType() {
+
+        return (this.type);
+
+    }
+
+
+    /**
+     * Return a string representation of this event.
+     */
+    public String toString() {
+
+        return ("ContainerEvent['" + getContainer() + "','" +
+                getType() + "','" + getData() + "']");
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ContainerListener.java b/container/catalina/src/share/org/apache/catalina/ContainerListener.java
new file mode 100644
index 0000000..14669b1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ContainerListener.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+
+
+/**
+ * Interface defining a listener for significant Container generated events.
+ * Note that "container start" and "container stop" events are normally
+ * LifecycleEvents, not ContainerEvents.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface ContainerListener {
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event ContainerEvent that has occurred
+     */
+    public void containerEvent(ContainerEvent event);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ContainerServlet.java b/container/catalina/src/share/org/apache/catalina/ContainerServlet.java
new file mode 100644
index 0000000..e0d4ab9
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ContainerServlet.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+/**
+ * A <b>ContainerServlet</b> is a servlet that has access to Catalina
+ * internal functionality, and is loaded from the Catalina class loader
+ * instead of the web application class loader.  The property setter
+ * methods must be called by the container whenever a new instance of
+ * this servlet is put into service.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface ContainerServlet {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Wrapper with which this Servlet is associated.
+     */
+    public Wrapper getWrapper();
+
+
+    /**
+     * Set the Wrapper with which this Servlet is associated.
+     *
+     * @param wrapper The new associated Wrapper
+     */
+    public void setWrapper(Wrapper wrapper);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Context.java b/container/catalina/src/share/org/apache/catalina/Context.java
new file mode 100644
index 0000000..82b75dc
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Context.java
@@ -0,0 +1,1046 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import javax.servlet.ServletContext;
+
+import org.apache.tomcat.util.http.mapper.Mapper;
+
+import org.apache.catalina.deploy.ApplicationParameter;
+import org.apache.catalina.deploy.ErrorPage;
+import org.apache.catalina.deploy.FilterDef;
+import org.apache.catalina.deploy.FilterMap;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.util.CharsetMapper;
+
+
+/**
+ * A <b>Context</b> is a Container that represents a servlet context, and
+ * therefore an individual web application, in the Catalina servlet engine.
+ * It is therefore useful in almost every deployment of Catalina (even if a
+ * Connector attached to a web server (such as Apache) uses the web server's
+ * facilities to identify the appropriate Wrapper to handle this request.
+ * It also provides a convenient mechanism to use Interceptors that see
+ * every request processed by this particular web application.
+ * <p>
+ * The parent Container attached to a Context is generally a Host, but may
+ * be some other implementation, or may be omitted if it is not necessary.
+ * <p>
+ * The child containers attached to a Context are generally implementations
+ * of Wrapper (representing individual servlet definitions).
+ * <p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Context extends Container {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The LifecycleEvent type sent when a context is reloaded.
+     */
+    public static final String RELOAD_EVENT = "reload";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the set of initialized application event listener objects,
+     * in the order they were specified in the web application deployment
+     * descriptor, for this application.
+     *
+     * @exception IllegalStateException if this method is called before
+     *  this application has started, or after it has been stopped
+     */
+    public Object[] getApplicationEventListeners();
+
+
+    /**
+     * Store the set of initialized application event listener objects,
+     * in the order they were specified in the web application deployment
+     * descriptor, for this application.
+     *
+     * @param listeners The set of instantiated listener objects.
+     */
+    public void setApplicationEventListeners(Object listeners[]);
+
+
+    /**
+     * Return the set of initialized application lifecycle listener objects,
+     * in the order they were specified in the web application deployment
+     * descriptor, for this application.
+     *
+     * @exception IllegalStateException if this method is called before
+     *  this application has started, or after it has been stopped
+     */
+    public Object[] getApplicationLifecycleListeners();
+
+
+    /**
+     * Store the set of initialized application lifecycle listener objects,
+     * in the order they were specified in the web application deployment
+     * descriptor, for this application.
+     *
+     * @param listeners The set of instantiated listener objects.
+     */
+    public void setApplicationLifecycleListeners(Object listeners[]);
+
+
+    /**
+     * Return the application available flag for this Context.
+     */
+    public boolean getAvailable();
+
+
+    /**
+     * Set the application available flag for this Context.
+     *
+     * @param available The new application available flag
+     */
+    public void setAvailable(boolean available);
+
+
+    /**
+     * Return the Locale to character set mapper for this Context.
+     */
+    public CharsetMapper getCharsetMapper();
+
+
+    /**
+     * Set the Locale to character set mapper for this Context.
+     *
+     * @param mapper The new mapper
+     */
+    public void setCharsetMapper(CharsetMapper mapper);
+
+
+    /**
+     * Return the path to a file to save this Context information.
+     */
+    public String getConfigFile();
+
+
+    /**
+     * Set the path to a file to save this Context information.
+     *
+     * @param configFile The path to a file to save this Context information.
+     */
+    public void setConfigFile(String configFile);
+
+
+    /**
+     * Return the "correctly configured" flag for this Context.
+     */
+    public boolean getConfigured();
+
+
+    /**
+     * Set the "correctly configured" flag for this Context.  This can be
+     * set to false by startup listeners that detect a fatal configuration
+     * error to avoid the application from being made available.
+     *
+     * @param configured The new correctly configured flag
+     */
+    public void setConfigured(boolean configured);
+
+
+    /**
+     * Return the "use cookies for session ids" flag.
+     */
+    public boolean getCookies();
+
+
+    /**
+     * Set the "use cookies for session ids" flag.
+     *
+     * @param cookies The new flag
+     */
+    public void setCookies(boolean cookies);
+
+
+    /**
+     * Return the "allow crossing servlet contexts" flag.
+     */
+    public boolean getCrossContext();
+
+
+    
+    /**
+     * Return the alternate Deployment Descriptor name.
+     */
+    public String getAltDDName();
+    
+    
+    /**
+     * Set an alternate Deployment Descriptor name.
+     */
+    public void setAltDDName(String altDDName) ;
+    
+    
+    /**
+     * Set the "allow crossing servlet contexts" flag.
+     *
+     * @param crossContext The new cross contexts flag
+     */
+    public void setCrossContext(boolean crossContext);
+
+
+    /**
+     * Return the display name of this web application.
+     */
+    public String getDisplayName();
+
+
+    /**
+     * Set the display name of this web application.
+     *
+     * @param displayName The new display name
+     */
+    public void setDisplayName(String displayName);
+
+
+    /**
+     * Return the distributable flag for this web application.
+     */
+    public boolean getDistributable();
+
+
+    /**
+     * Set the distributable flag for this web application.
+     *
+     * @param distributable The new distributable flag
+     */
+    public void setDistributable(boolean distributable);
+
+
+    /**
+     * Return the document root for this Context.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     */
+    public String getDocBase();
+
+
+    /**
+     * Set the document root for this Context.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     *
+     * @param docBase The new document root
+     */
+    public void setDocBase(String docBase);
+
+
+    /**
+     * Return the URL encoded context path, using UTF-8.
+     */
+    public String getEncodedPath();
+
+
+    /**
+     * Return the login configuration descriptor for this web application.
+     */
+    public LoginConfig getLoginConfig();
+
+
+    /**
+     * Set the login configuration descriptor for this web application.
+     *
+     * @param config The new login configuration
+     */
+    public void setLoginConfig(LoginConfig config);
+
+
+    /**
+     * Get the request dispatcher mapper.
+     */
+    public Mapper getMapper();
+
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    public NamingResources getNamingResources();
+
+
+    /**
+     * Set the naming resources for this web application.
+     *
+     * @param namingResources The new naming resources
+     */
+    public void setNamingResources(NamingResources namingResources);
+
+
+    /**
+     * Return the context path for this web application.
+     */
+    public String getPath();
+
+
+    /**
+     * Set the context path for this web application.
+     *
+     * @param path The new context path
+     */
+    public void setPath(String path);
+
+
+    /**
+     * Return the public identifier of the deployment descriptor DTD that is
+     * currently being parsed.
+     */
+    public String getPublicId();
+
+
+    /**
+     * Set the public identifier of the deployment descriptor DTD that is
+     * currently being parsed.
+     *
+     * @param publicId The public identifier
+     */
+    public void setPublicId(String publicId);
+
+
+    /**
+     * Return the reloadable flag for this web application.
+     */
+    public boolean getReloadable();
+
+
+    /**
+     * Set the reloadable flag for this web application.
+     *
+     * @param reloadable The new reloadable flag
+     */
+    public void setReloadable(boolean reloadable);
+
+
+    /**
+     * Return the override flag for this web application.
+     */
+    public boolean getOverride();
+
+
+    /**
+     * Set the override flag for this web application.
+     *
+     * @param override The new override flag
+     */
+    public void setOverride(boolean override);
+
+
+    /**
+     * Return the privileged flag for this web application.
+     */
+    public boolean getPrivileged();
+
+
+    /**
+     * Set the privileged flag for this web application.
+     *
+     * @param privileged The new privileged flag
+     */
+    public void setPrivileged(boolean privileged);
+
+
+    /**
+     * Return the servlet context for which this Context is a facade.
+     */
+    public ServletContext getServletContext();
+
+
+    /**
+     * Return the default session timeout (in minutes) for this
+     * web application.
+     */
+    public int getSessionTimeout();
+
+
+    /**
+     * Set the default session timeout (in minutes) for this
+     * web application.
+     *
+     * @param timeout The new default session timeout
+     */
+    public void setSessionTimeout(int timeout);
+
+
+    /**
+     * Return the value of the swallowOutput flag.
+     */
+    public boolean getSwallowOutput();
+
+
+    /**
+     * Set the value of the swallowOutput flag. If set to true, the system.out
+     * and system.err will be redirected to the logger during a servlet
+     * execution.
+     *
+     * @param swallowOutput The new value
+     */
+    public void setSwallowOutput(boolean swallowOutput);
+
+
+    /**
+     * Return the Java class name of the Wrapper implementation used
+     * for servlets registered in this Context.
+     */
+    public String getWrapperClass();
+
+
+    /**
+     * Set the Java class name of the Wrapper implementation used
+     * for servlets registered in this Context.
+     *
+     * @param wrapperClass The new wrapper class
+     */
+    public void setWrapperClass(String wrapperClass);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new Listener class name to the set of Listeners
+     * configured for this application.
+     *
+     * @param listener Java class name of a listener class
+     */
+    public void addApplicationListener(String listener);
+
+
+    /**
+     * Add a new application parameter for this application.
+     *
+     * @param parameter The new application parameter
+     */
+    public void addApplicationParameter(ApplicationParameter parameter);
+
+
+    /**
+     * Add a security constraint to the set for this web application.
+     */
+    public void addConstraint(SecurityConstraint constraint);
+
+
+    /**
+     * Add an error page for the specified error or Java exception.
+     *
+     * @param errorPage The error page definition to be added
+     */
+    public void addErrorPage(ErrorPage errorPage);
+
+
+    /**
+     * Add a filter definition to this Context.
+     *
+     * @param filterDef The filter definition to be added
+     */
+    public void addFilterDef(FilterDef filterDef);
+
+
+    /**
+     * Add a filter mapping to this Context.
+     *
+     * @param filterMap The filter mapping to be added
+     */
+    public void addFilterMap(FilterMap filterMap);
+
+
+    /**
+     * Add the classname of an InstanceListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of an InstanceListener class
+     */
+    public void addInstanceListener(String listener);
+
+
+    /**
+     * Add the given URL pattern as a jsp-property-group.  This maps
+     * resources that match the given pattern so they will be passed
+     * to the JSP container.  Though there are other elements in the
+     * property group, we only care about the URL pattern here.  The
+     * JSP container will parse the rest.
+     *
+     * @param pattern URL pattern to be mapped 
+     */
+    public void addJspMapping(String pattern);
+
+
+    /**
+     * Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4)
+     *
+     * @param locale locale to map an encoding for
+     * @param encoding encoding to be used for a give locale
+     */
+    public void addLocaleEncodingMappingParameter(String locale, String encoding);
+
+
+    /**
+     * Add a new MIME mapping, replacing any existing mapping for
+     * the specified extension.
+     *
+     * @param extension Filename extension being mapped
+     * @param mimeType Corresponding MIME type
+     */
+    public void addMimeMapping(String extension, String mimeType);
+
+
+    /**
+     * Add a new context initialization parameter, replacing any existing
+     * value for the specified name.
+     *
+     * @param name Name of the new parameter
+     * @param value Value of the new  parameter
+     */
+    public void addParameter(String name, String value);
+
+
+    /**
+     * Add a security role reference for this web application.
+     *
+     * @param role Security role used in the application
+     * @param link Actual security role to check for
+     */
+    public void addRoleMapping(String role, String link);
+
+
+    /**
+     * Add a new security role for this web application.
+     *
+     * @param role New security role
+     */
+    public void addSecurityRole(String role);
+
+
+    /**
+     * Add a new servlet mapping, replacing any existing mapping for
+     * the specified pattern.
+     *
+     * @param pattern URL pattern to be mapped
+     * @param name Name of the corresponding servlet to execute
+     */
+    public void addServletMapping(String pattern, String name);
+
+
+    /**
+     * Add a JSP tag library for the specified URI.
+     *
+     * @param uri URI, relative to the web.xml file, of this tag library
+     * @param location Location of the tag library descriptor
+     */
+    public void addTaglib(String uri, String location);
+
+    
+    /**
+     * Add a resource which will be watched for reloading by the host auto
+     * deployer. Note: this will not be used in embedded mode.
+     * 
+     * @param name Path to the resource, relative to docBase
+     */
+    public void addWatchedResource(String name);
+    
+
+    /**
+     * Add a new welcome file to the set recognized by this Context.
+     *
+     * @param name New welcome file name
+     */
+    public void addWelcomeFile(String name);
+
+
+    /**
+     * Add the classname of a LifecycleListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of a LifecycleListener class
+     */
+    public void addWrapperLifecycle(String listener);
+
+
+    /**
+     * Add the classname of a ContainerListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of a ContainerListener class
+     */
+    public void addWrapperListener(String listener);
+
+
+    /**
+     * Factory method to create and return a new Wrapper instance, of
+     * the Java implementation class appropriate for this Context
+     * implementation.  The constructor of the instantiated Wrapper
+     * will have been called, but no properties will have been set.
+     */
+    public Wrapper createWrapper();
+
+
+    /**
+     * Return the set of application listener class names configured
+     * for this application.
+     */
+    public String[] findApplicationListeners();
+
+
+    /**
+     * Return the set of application parameters for this application.
+     */
+    public ApplicationParameter[] findApplicationParameters();
+
+
+    /**
+     * Return the set of security constraints for this web application.
+     * If there are none, a zero-length array is returned.
+     */
+    public SecurityConstraint[] findConstraints();
+
+
+    /**
+     * Return the error page entry for the specified HTTP error code,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param errorCode Error code to look up
+     */
+    public ErrorPage findErrorPage(int errorCode);
+
+
+    /**
+     * Return the error page entry for the specified Java exception type,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param exceptionType Exception type to look up
+     */
+    public ErrorPage findErrorPage(String exceptionType);
+
+
+
+    /**
+     * Return the set of defined error pages for all specified error codes
+     * and exception types.
+     */
+    public ErrorPage[] findErrorPages();
+
+
+    /**
+     * Return the filter definition for the specified filter name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param filterName Filter name to look up
+     */
+    public FilterDef findFilterDef(String filterName);
+
+
+    /**
+     * Return the set of defined filters for this Context.
+     */
+    public FilterDef[] findFilterDefs();
+
+
+    /**
+     * Return the set of filter mappings for this Context.
+     */
+    public FilterMap[] findFilterMaps();
+
+
+    /**
+     * Return the set of InstanceListener classes that will be added to
+     * newly created Wrappers automatically.
+     */
+    public String[] findInstanceListeners();
+
+
+    /**
+     * Return the MIME type to which the specified extension is mapped,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param extension Extension to map to a MIME type
+     */
+    public String findMimeMapping(String extension);
+
+
+    /**
+     * Return the extensions for which MIME mappings are defined.  If there
+     * are none, a zero-length array is returned.
+     */
+    public String[] findMimeMappings();
+
+
+    /**
+     * Return the value for the specified context initialization
+     * parameter name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the parameter to return
+     */
+    public String findParameter(String name);
+
+
+    /**
+     * Return the names of all defined context initialization parameters
+     * for this Context.  If no parameters are defined, a zero-length
+     * array is returned.
+     */
+    public String[] findParameters();
+
+
+    /**
+     * For the given security role (as used by an application), return the
+     * corresponding role name (as defined by the underlying Realm) if there
+     * is one.  Otherwise, return the specified role unchanged.
+     *
+     * @param role Security role to map
+     */
+    public String findRoleMapping(String role);
+
+
+    /**
+     * Return <code>true</code> if the specified security role is defined
+     * for this application; otherwise return <code>false</code>.
+     *
+     * @param role Security role to verify
+     */
+    public boolean findSecurityRole(String role);
+
+
+    /**
+     * Return the security roles defined for this application.  If none
+     * have been defined, a zero-length array is returned.
+     */
+    public String[] findSecurityRoles();
+
+
+    /**
+     * Return the servlet name mapped by the specified pattern (if any);
+     * otherwise return <code>null</code>.
+     *
+     * @param pattern Pattern for which a mapping is requested
+     */
+    public String findServletMapping(String pattern);
+
+
+    /**
+     * Return the patterns of all defined servlet mappings for this
+     * Context.  If no mappings are defined, a zero-length array is returned.
+     */
+    public String[] findServletMappings();
+
+
+    /**
+     * Return the context-relative URI of the error page for the specified
+     * HTTP status code, if any; otherwise return <code>null</code>.
+     *
+     * @param status HTTP status code to look up
+     */
+    public String findStatusPage(int status);
+
+
+    /**
+     * Return the set of HTTP status codes for which error pages have
+     * been specified.  If none are specified, a zero-length array
+     * is returned.
+     */
+    public int[] findStatusPages();
+
+
+    /**
+     * Return the tag library descriptor location for the specified taglib
+     * URI, if any; otherwise, return <code>null</code>.
+     *
+     * @param uri URI, relative to the web.xml file
+     */
+    public String findTaglib(String uri);
+
+
+    /**
+     * Return the URIs of all tag libraries for which a tag library
+     * descriptor location has been specified.  If none are specified,
+     * a zero-length array is returned.
+     */
+    public String[] findTaglibs();
+
+
+    /**
+     * Return the set of watched resources for this Context. If none are 
+     * defined, a zero length array will be returned.
+     */
+    public String[] findWatchedResources();
+    
+
+    /**
+     * Return <code>true</code> if the specified welcome file is defined
+     * for this Context; otherwise return <code>false</code>.
+     *
+     * @param name Welcome file to verify
+     */
+    public boolean findWelcomeFile(String name);
+
+    
+    /**
+     * Return the set of welcome files defined for this Context.  If none are
+     * defined, a zero-length array is returned.
+     */
+    public String[] findWelcomeFiles();
+
+
+    /**
+     * Return the set of LifecycleListener classes that will be added to
+     * newly created Wrappers automatically.
+     */
+    public String[] findWrapperLifecycles();
+
+
+    /**
+     * Return the set of ContainerListener classes that will be added to
+     * newly created Wrappers automatically.
+     */
+    public String[] findWrapperListeners();
+
+
+    /**
+     * Reload this web application, if reloading is supported.
+     *
+     * @exception IllegalStateException if the <code>reloadable</code>
+     *  property is set to <code>false</code>.
+     */
+    public void reload();
+
+
+    /**
+     * Remove the specified application listener class from the set of
+     * listeners for this application.
+     *
+     * @param listener Java class name of the listener to be removed
+     */
+    public void removeApplicationListener(String listener);
+
+
+    /**
+     * Remove the application parameter with the specified name from
+     * the set for this application.
+     *
+     * @param name Name of the application parameter to remove
+     */
+    public void removeApplicationParameter(String name);
+
+
+    /**
+     * Remove the specified security constraint from this web application.
+     *
+     * @param constraint Constraint to be removed
+     */
+    public void removeConstraint(SecurityConstraint constraint);
+
+
+    /**
+     * Remove the error page for the specified error code or
+     * Java language exception, if it exists; otherwise, no action is taken.
+     *
+     * @param errorPage The error page definition to be removed
+     */
+    public void removeErrorPage(ErrorPage errorPage);
+
+
+    /**
+     * Remove the specified filter definition from this Context, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param filterDef Filter definition to be removed
+     */
+    public void removeFilterDef(FilterDef filterDef);
+
+
+    /**
+     * Remove a filter mapping from this Context.
+     *
+     * @param filterMap The filter mapping to be removed
+     */
+    public void removeFilterMap(FilterMap filterMap);
+
+
+    /**
+     * Remove a class name from the set of InstanceListener classes that
+     * will be added to newly created Wrappers.
+     *
+     * @param listener Class name of an InstanceListener class to be removed
+     */
+    public void removeInstanceListener(String listener);
+
+
+    /**
+     * Remove the MIME mapping for the specified extension, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param extension Extension to remove the mapping for
+     */
+    public void removeMimeMapping(String extension);
+
+
+    /**
+     * Remove the context initialization parameter with the specified
+     * name, if it exists; otherwise, no action is taken.
+     *
+     * @param name Name of the parameter to remove
+     */
+    public void removeParameter(String name);
+
+
+    /**
+     * Remove any security role reference for the specified name
+     *
+     * @param role Security role (as used in the application) to remove
+     */
+    public void removeRoleMapping(String role);
+
+
+    /**
+     * Remove any security role with the specified name.
+     *
+     * @param role Security role to remove
+     */
+    public void removeSecurityRole(String role);
+
+
+    /**
+     * Remove any servlet mapping for the specified pattern, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param pattern URL pattern of the mapping to remove
+     */
+    public void removeServletMapping(String pattern);
+
+
+    /**
+     * Remove the tag library location forthe specified tag library URI.
+     *
+     * @param uri URI, relative to the web.xml file
+     */
+    public void removeTaglib(String uri);
+
+    
+    /**
+     * Remove the specified watched resource name from the list associated
+     * with this Context.
+     * 
+     * @param name Name of the watched resource to be removed
+     */
+    public void removeWatchedResource(String name);
+    
+
+    /**
+     * Remove the specified welcome file name from the list recognized
+     * by this Context.
+     *
+     * @param name Name of the welcome file to be removed
+     */
+    public void removeWelcomeFile(String name);
+
+
+    /**
+     * Remove a class name from the set of LifecycleListener classes that
+     * will be added to newly created Wrappers.
+     *
+     * @param listener Class name of a LifecycleListener class to be removed
+     */
+    public void removeWrapperLifecycle(String listener);
+
+
+    /**
+     * Remove a class name from the set of ContainerListener classes that
+     * will be added to newly created Wrappers.
+     *
+     * @param listener Class name of a ContainerListener class to be removed
+     */
+    public void removeWrapperListener(String listener);
+
+
+    /**
+     * Get the server.xml <context> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     *
+     */
+    public boolean getXmlNamespaceAware();
+
+
+    /**
+     * Get the server.xml <context> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getXmlValidation();
+
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlValidation true to enable xml instance validation
+     */
+    public void setXmlValidation(boolean xmlValidation);
+
+
+   /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean xmlNamespaceAware);
+    /**
+     * Get the server.xml <context> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     */
+     
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing tlds files. 
+     * @param tldValidation true to enable xml instance validation
+     */
+    public void setTldValidation(boolean tldValidation);
+
+
+    /**
+     * Get the server.xml <context> attribute's webXmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getTldValidation();
+
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     */
+    public boolean getTldNamespaceAware();
+
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param tldNamespaceAware true to enable namespace awareness
+     */
+    public void setTldNamespaceAware(boolean tldNamespaceAware);
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/Engine.java b/container/catalina/src/share/org/apache/catalina/Engine.java
new file mode 100644
index 0000000..a7d49eb
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Engine.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+/**
+ * An <b>Engine</b> is a Container that represents the entire Catalina servlet
+ * engine.  It is useful in the following types of scenarios:
+ * <ul>
+ * <li>You wish to use Interceptors that see every single request processed
+ *     by the entire engine.
+ * <li>You wish to run Catalina in with a standalone HTTP connector, but still
+ *     want support for multiple virtual hosts.
+ * </ul>
+ * In general, you would not use an Engine when deploying Catalina connected
+ * to a web server (such as Apache), because the Connector will have
+ * utilized the web server's facilities to determine which Context (or
+ * perhaps even which Wrapper) should be utilized to process this request.
+ * <p>
+ * The child containers attached to an Engine are generally implementations
+ * of Host (representing a virtual host) or Context (representing individual
+ * an individual servlet context), depending upon the Engine implementation.
+ * <p>
+ * If used, an Engine is always the top level Container in a Catalina
+ * hierarchy. Therefore, the implementation's <code>setParent()</code> method
+ * should throw <code>IllegalArgumentException</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Engine extends Container {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the default hostname for this Engine.
+     */
+    public String getDefaultHost();
+
+
+    /**
+     * Set the default hostname for this Engine.
+     *
+     * @param defaultHost The new default host
+     */
+    public void setDefaultHost(String defaultHost);
+
+
+    /**
+     * Retrieve the JvmRouteId for this engine.
+     */
+    public String getJvmRoute();
+
+
+    /**
+     * Set the JvmRouteId for this engine.
+     *
+     * @param jvmRouteId the (new) JVM Route ID. Each Engine within a cluster
+     *        must have a unique JVM Route ID.
+     */
+    public void setJvmRoute(String jvmRouteId);
+
+
+    /**
+     * Return the <code>Service</code> with which we are associated (if any).
+     */
+    public Service getService();
+
+
+    /**
+     * Set the <code>Service</code> with which we are associated (if any).
+     *
+     * @param service The service that owns this Engine
+     */
+    public void setService(Service service);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Globals.java b/container/catalina/src/share/org/apache/catalina/Globals.java
new file mode 100644
index 0000000..424406b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Globals.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+/**
+ * Global constants that are applicable to multiple packages within Catalina.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class Globals {
+
+    /**
+     * The servlet context attribute under which we store the alternate
+     * deployment descriptor for this web application 
+     */
+    public static final String ALT_DD_ATTR = 
+        "org.apache.catalina.deploy.alt_dd";
+
+    /**
+     * The request attribute under which we store the array of X509Certificate
+     * objects representing the certificate chain presented by our client,
+     * if any.
+     */
+    public static final String CERTIFICATES_ATTR =
+        "javax.servlet.request.X509Certificate";
+
+    /**
+     * The request attribute under which we store the name of the cipher suite
+     * being used on an SSL connection (as an object of type
+     * java.lang.String).
+     */
+    public static final String CIPHER_SUITE_ATTR =
+        "javax.servlet.request.cipher_suite";
+
+
+    /**
+     * The servlet context attribute under which we store the class loader
+     * used for loading servlets (as an object of type java.lang.ClassLoader).
+     */
+    public static final String CLASS_LOADER_ATTR =
+        "org.apache.catalina.classloader";
+
+    /**
+     * Request dispatcher state.
+     */
+    public static final String DISPATCHER_TYPE_ATTR = 
+        "org.apache.catalina.core.DISPATCHER_TYPE";
+
+    /**
+     * Request dispatcher path.
+     */
+    public static final String DISPATCHER_REQUEST_PATH_ATTR = 
+        "org.apache.catalina.core.DISPATCHER_REQUEST_PATH";
+
+    /**
+     * The JNDI directory context which is associated with the context. This
+     * context can be used to manipulate static files.
+     */
+    public static final String RESOURCES_ATTR =
+        "org.apache.catalina.resources";
+
+
+    /**
+     * The servlet context attribute under which we store the class path
+     * for our application class loader (as an object of type String),
+     * delimited with the appropriate path delimiter for this platform.
+     */
+    public static final String CLASS_PATH_ATTR =
+        "org.apache.catalina.jsp_classpath";
+
+
+    /**
+     * The request attribute under which we forward a Java exception
+     * (as an object of type Throwable) to an error page.
+     */
+    public static final String EXCEPTION_ATTR =
+        "javax.servlet.error.exception";
+
+
+    /**
+     * The request attribute under which we forward the request URI
+     * (as an object of type String) of the page on which an error occurred.
+     */
+    public static final String EXCEPTION_PAGE_ATTR =
+        "javax.servlet.error.request_uri";
+
+
+    /**
+     * The request attribute under which we forward a Java exception type
+     * (as an object of type Class) to an error page.
+     */
+    public static final String EXCEPTION_TYPE_ATTR =
+        "javax.servlet.error.exception_type";
+
+
+    /**
+     * The request attribute under which we forward an HTTP status message
+     * (as an object of type STring) to an error page.
+     */
+    public static final String ERROR_MESSAGE_ATTR =
+        "javax.servlet.error.message";
+
+
+    /**
+     * The request attribute under which the Invoker servlet will store
+     * the invoking servlet path, if it was used to execute a servlet
+     * indirectly instead of through a servlet mapping.
+     */
+    public static final String INVOKED_ATTR =
+        "org.apache.catalina.INVOKED";
+
+
+    /**
+     * The request attribute under which we expose the value of the
+     * <code>&lt;jsp-file&gt;</code> value associated with this servlet,
+     * if any.
+     */
+    public static final String JSP_FILE_ATTR =
+        "org.apache.catalina.jsp_file";
+
+
+    /**
+     * The request attribute under which we store the key size being used for
+     * this SSL connection (as an object of type java.lang.Integer).
+     */
+    public static final String KEY_SIZE_ATTR =
+        "javax.servlet.request.key_size";
+
+    /**
+     * The request attribute under which we store the session id being used
+     * for this SSL connection (as an object of type java.lang.String).
+     */
+    public static final String SSL_SESSION_ID_ATTR =
+        "javax.servlet.request.ssl_session";
+
+
+    /**
+     * The servlet context attribute under which the managed bean Registry
+     * will be stored for privileged contexts (if enabled).
+     */
+    public static final String MBEAN_REGISTRY_ATTR =
+        "org.apache.catalina.Registry";
+
+
+    /**
+     * The servlet context attribute under which the MBeanServer will be stored
+     * for privileged contexts (if enabled).
+     */
+    public static final String MBEAN_SERVER_ATTR =
+        "org.apache.catalina.MBeanServer";
+
+
+    /**
+     * The request attribute under which we store the servlet name on a
+     * named dispatcher request.
+     */
+    public static final String NAMED_DISPATCHER_ATTR =
+        "org.apache.catalina.NAMED";
+
+
+    /**
+     * The request attribute under which the request URI of the included
+     * servlet is stored on an included dispatcher request.
+     */
+    public static final String INCLUDE_REQUEST_URI_ATTR =
+        "javax.servlet.include.request_uri";
+
+
+    /**
+     * The request attribute under which the context path of the included
+     * servlet is stored on an included dispatcher request.
+     */
+    public static final String INCLUDE_CONTEXT_PATH_ATTR =
+        "javax.servlet.include.context_path";
+
+
+    /**
+     * The request attribute under which the path info of the included
+     * servlet is stored on an included dispatcher request.
+     */
+    public static final String INCLUDE_PATH_INFO_ATTR =
+        "javax.servlet.include.path_info";
+
+
+    /**
+     * The request attribute under which the servlet path of the included
+     * servlet is stored on an included dispatcher request.
+     */
+    public static final String INCLUDE_SERVLET_PATH_ATTR =
+        "javax.servlet.include.servlet_path";
+
+
+    /**
+     * The request attribute under which the query string of the included
+     * servlet is stored on an included dispatcher request.
+     */
+    public static final String INCLUDE_QUERY_STRING_ATTR =
+        "javax.servlet.include.query_string";
+
+
+    /**
+     * The request attribute under which the original request URI is stored
+     * on an forwarded dispatcher request.
+     */
+    public static final String FORWARD_REQUEST_URI_ATTR =
+        "javax.servlet.forward.request_uri";
+    
+    
+    /**
+     * The request attribute under which the original context path is stored
+     * on an forwarded dispatcher request.
+     */
+    public static final String FORWARD_CONTEXT_PATH_ATTR =
+        "javax.servlet.forward.context_path";
+
+
+    /**
+     * The request attribute under which the original path info is stored
+     * on an forwarded dispatcher request.
+     */
+    public static final String FORWARD_PATH_INFO_ATTR =
+        "javax.servlet.forward.path_info";
+
+
+    /**
+     * The request attribute under which the original servlet path is stored
+     * on an forwarded dispatcher request.
+     */
+    public static final String FORWARD_SERVLET_PATH_ATTR =
+        "javax.servlet.forward.servlet_path";
+
+
+    /**
+     * The request attribute under which the original query string is stored
+     * on an forwarded dispatcher request.
+     */
+    public static final String FORWARD_QUERY_STRING_ATTR =
+        "javax.servlet.forward.query_string";
+
+
+    /**
+     * The request attribute under which we forward a servlet name to
+     * an error page.
+     */
+    public static final String SERVLET_NAME_ATTR =
+        "javax.servlet.error.servlet_name";
+
+    
+    /**
+     * The name of the cookie used to pass the session identifier back
+     * and forth with the client.
+     */
+    public static final String SESSION_COOKIE_NAME = "JSESSIONID";
+
+
+    /**
+     * The name of the path parameter used to pass the session identifier
+     * back and forth with the client.
+     */
+    public static final String SESSION_PARAMETER_NAME = "jsessionid";
+
+
+    /**
+     * The servlet context attribute under which we store a flag used
+     * to mark this request as having been processed by the SSIServlet.
+     * We do this because of the pathInfo mangling happening when using
+     * the CGIServlet in conjunction with the SSI servlet. (value stored
+     * as an object of type String)
+     */
+     public static final String SSI_FLAG_ATTR =
+         "org.apache.catalina.ssi.SSIServlet";
+
+
+    /**
+     * The request attribute under which we forward an HTTP status code
+     * (as an object of type Integer) to an error page.
+     */
+    public static final String STATUS_CODE_ATTR =
+        "javax.servlet.error.status_code";
+
+
+    /**
+     * The subject under which the AccessControlContext is running.
+     */
+    public static final String SUBJECT_ATTR =
+        "javax.security.auth.subject";
+
+    
+    /**
+     * The servlet context attribute under which we record the set of
+     * welcome files (as an object of type String[]) for this application.
+     */
+    public static final String WELCOME_FILES_ATTR =
+        "org.apache.catalina.WELCOME_FILES";
+
+
+    /**
+     * The servlet context attribute under which we store a temporary
+     * working directory (as an object of type File) for use by servlets
+     * within this web application.
+     */
+    public static final String WORK_DIR_ATTR =
+        "javax.servlet.context.tempdir";
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Group.java b/container/catalina/src/share/org/apache/catalina/Group.java
new file mode 100644
index 0000000..cdb13b2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Group.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.security.Principal;
+import java.util.Iterator;
+
+
+/**
+ * <p>Abstract representation of a group of {@link User}s in a
+ * {@link UserDatabase}.  Each user that is a member of this group
+ * inherits the {@link Role}s assigned to the group.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public interface Group extends Principal {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the description of this group.
+     */
+    public String getDescription();
+
+
+    /**
+     * Set the description of this group.
+     *
+     * @param description The new description
+     */
+    public void setDescription(String description);
+
+
+    /**
+     * Return the group name of this group, which must be unique
+     * within the scope of a {@link UserDatabase}.
+     */
+    public String getGroupname();
+
+
+    /**
+     * Set the group name of this group, which must be unique
+     * within the scope of a {@link UserDatabase}.
+     *
+     * @param groupname The new group name
+     */
+    public void setGroupname(String groupname);
+
+
+    /**
+     * Return the set of {@link Role}s assigned specifically to this group.
+     */
+    public Iterator getRoles();
+
+
+    /**
+     * Return the {@link UserDatabase} within which this Group is defined.
+     */
+    public UserDatabase getUserDatabase();
+
+
+    /**
+     * Return the set of {@link User}s that are members of this group.
+     */
+    public Iterator getUsers();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new {@link Role} to those assigned specifically to this group.
+     *
+     * @param role The new role
+     */
+    public void addRole(Role role);
+
+
+    /**
+     * Is this group specifically assigned the specified {@link Role}?
+     *
+     * @param role The role to check
+     */
+    public boolean isInRole(Role role);
+
+
+    /**
+     * Remove a {@link Role} from those assigned to this group.
+     *
+     * @param role The old role
+     */
+    public void removeRole(Role role);
+
+
+    /**
+     * Remove all {@link Role}s from those assigned to this group.
+     */
+    public void removeRoles();
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Host.java b/container/catalina/src/share/org/apache/catalina/Host.java
new file mode 100644
index 0000000..db5ce2b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Host.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+
+/**
+ * A <b>Host</b> is a Container that represents a virtual host in the
+ * Catalina servlet engine.  It is useful in the following types of scenarios:
+ * <ul>
+ * <li>You wish to use Interceptors that see every single request processed
+ *     by this particular virtual host.
+ * <li>You wish to run Catalina in with a standalone HTTP connector, but still
+ *     want support for multiple virtual hosts.
+ * </ul>
+ * In general, you would not use a Host when deploying Catalina connected
+ * to a web server (such as Apache), because the Connector will have
+ * utilized the web server's facilities to determine which Context (or
+ * perhaps even which Wrapper) should be utilized to process this request.
+ * <p>
+ * The parent Container attached to a Host is generally an Engine, but may
+ * be some other implementation, or may be omitted if it is not necessary.
+ * <p>
+ * The child containers attached to a Host are generally implementations
+ * of Context (representing an individual servlet context).
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Host extends Container {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The ContainerEvent event type sent when a new alias is added
+     * by <code>addAlias()</code>.
+     */
+    public static final String ADD_ALIAS_EVENT = "addAlias";
+
+
+    /**
+     * The ContainerEvent event type sent when an old alias is removed
+     * by <code>removeAlias()</code>.
+     */
+    public static final String REMOVE_ALIAS_EVENT = "removeAlias";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the application root for this Host.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     */
+    public String getAppBase();
+
+
+    /**
+     * Set the application root for this Host.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     *
+     * @param appBase The new application root
+     */
+    public void setAppBase(String appBase);
+
+
+    /**
+     * Return the value of the auto deploy flag.  If true, it indicates that 
+     * this host's child webapps should be discovred and automatically 
+     * deployed dynamically.
+     */
+    public boolean getAutoDeploy();
+
+
+    /**
+     * Set the auto deploy flag value for this host.
+     * 
+     * @param autoDeploy The new auto deploy flag
+     */
+    public void setAutoDeploy(boolean autoDeploy);
+
+
+    /**
+     * Return the Java class name of the context configuration class
+     * for new web applications.
+     */
+    public String getConfigClass();
+
+    
+    /**
+     * Set the Java class name of the context configuration class
+     * for new web applications.
+     *
+     * @param configClass The new context configuration class
+     */
+    public void setConfigClass(String configClass);
+
+        
+    /**
+     * Return the value of the deploy on startup flag.  If true, it indicates 
+     * that this host's child webapps should be discovred and automatically 
+     * deployed.
+     */
+    public boolean getDeployOnStartup();
+
+
+    /**
+     * Set the deploy on startup flag value for this host.
+     * 
+     * @param deployOnStartup The new deploy on startup flag
+     */
+    public void setDeployOnStartup(boolean deployOnStartup);
+
+
+    /**
+     * Return the canonical, fully qualified, name of the virtual host
+     * this Container represents.
+     */
+    public String getName();
+
+
+    /**
+     * Set the canonical, fully qualified, name of the virtual host
+     * this Container represents.
+     *
+     * @param name Virtual host name
+     *
+     * @exception IllegalArgumentException if name is null
+     */
+    public void setName(String name);
+
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     *
+     */
+    public boolean getXmlNamespaceAware();
+
+
+    /**
+     * Get the server.xml <host> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getXmlValidation();
+
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlValidation true to enable xml instance validation
+     */
+    public void setXmlValidation(boolean xmlValidation);
+
+
+   /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean xmlNamespaceAware);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an alias name that should be mapped to this same Host.
+     *
+     * @param alias The alias to be added
+     */
+    public void addAlias(String alias);
+
+
+    /**
+     * Return the set of alias names for this Host.  If none are defined,
+     * a zero length array is returned.
+     */
+    public String[] findAliases();
+
+
+    /**
+     * Return the Context that would be used to process the specified
+     * host-relative request URI, if any; otherwise return <code>null</code>.
+     *
+     * @param uri Request URI to be mapped
+     */
+    public Context map(String uri);
+
+
+    /**
+     * Remove the specified alias name from the aliases for this Host.
+     *
+     * @param alias Alias name to be removed
+     */
+    public void removeAlias(String alias);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/InstanceEvent.java b/container/catalina/src/share/org/apache/catalina/InstanceEvent.java
new file mode 100644
index 0000000..f7d9480
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/InstanceEvent.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.util.EventObject;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * General event for notifying listeners of significant events related to
+ * a specific instance of a Servlet, or a specific instance of a Filter,
+ * as opposed to the Wrapper component that manages it.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class InstanceEvent
+    extends EventObject {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The event indicating that the <code>init()</code> method is about
+     * to be called for this instance.
+     */
+    public static final String BEFORE_INIT_EVENT = "beforeInit";
+
+
+    /**
+     * The event indicating that the <code>init()</code> method has returned.
+     */
+    public static final String AFTER_INIT_EVENT = "afterInit";
+
+
+    /**
+     * The event indicating that the <code>service()</code> method is about
+     * to be called on a servlet.  The <code>servlet</code> property contains
+     * the servlet being called, and the <code>request</code> and
+     * <code>response</code> properties contain the current request and
+     * response being processed.
+     */
+    public static final String BEFORE_SERVICE_EVENT = "beforeService";
+
+
+    /**
+     * The event indicating that the <code>service()</code> method has
+     * returned.  The <code>servlet</code> property contains the servlet
+     * that was called, and the <code>request</code> and
+     * <code>response</code> properties contain the current request and
+     * response being processed.
+     */
+    public static final String AFTER_SERVICE_EVENT = "afterService";
+
+
+    /**
+     * The event indicating that the <code>destroy</code> method is about
+     * to be called for this instance.
+     */
+    public static final String BEFORE_DESTROY_EVENT = "beforeDestroy";
+
+
+    /**
+     * The event indicating that the <code>destroy()</code> method has
+     * returned.
+     */
+    public static final String AFTER_DESTROY_EVENT = "afterDestroy";
+
+
+    /**
+     * The event indicating that the <code>service()</code> method of a
+     * servlet accessed via a request dispatcher is about to be called.
+     * The <code>servlet</code> property contains a reference to the
+     * dispatched-to servlet instance, and the <code>request</code> and
+     * <code>response</code> properties contain the current request and
+     * response being processed.  The <code>wrapper</code> property will
+     * contain a reference to the dispatched-to Wrapper.
+     */
+    public static final String BEFORE_DISPATCH_EVENT = "beforeDispatch";
+
+
+    /**
+     * The event indicating that the <code>service()</code> method of a
+     * servlet accessed via a request dispatcher has returned.  The
+     * <code>servlet</code> property contains a reference to the
+     * dispatched-to servlet instance, and the <code>request</code> and
+     * <code>response</code> properties contain the current request and
+     * response being processed.  The <code>wrapper</code> property will
+     * contain a reference to the dispatched-to Wrapper.
+     */
+    public static final String AFTER_DISPATCH_EVENT = "afterDispatch";
+
+
+    /**
+     * The event indicating that the <code>doFilter()</code> method of a
+     * Filter is about to be called.  The <code>filter</code> property
+     * contains a reference to the relevant filter instance, and the
+     * <code>request</code> and <code>response</code> properties contain
+     * the current request and response being processed.
+     */
+    public static final String BEFORE_FILTER_EVENT = "beforeFilter";
+
+
+    /**
+     * The event indicating that the <code>doFilter()</code> method of a
+     * Filter has returned.  The <code>filter</code> property contains
+     * a reference to the relevant filter instance, and the
+     * <code>request</code> and <code>response</code> properties contain
+     * the current request and response being processed.
+     */
+    public static final String AFTER_FILTER_EVENT = "afterFilter";
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for filter lifecycle events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param filter Filter instance for which this event occurred
+     * @param type Event type (required)
+     */
+    public InstanceEvent(Wrapper wrapper, Filter filter, String type) {
+
+      super(wrapper);
+      this.wrapper = wrapper;
+      this.filter = filter;
+      this.servlet = null;
+      this.type = type;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for filter lifecycle events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param filter Filter instance for which this event occurred
+     * @param type Event type (required)
+     * @param exception Exception that occurred
+     */
+    public InstanceEvent(Wrapper wrapper, Filter filter, String type,
+                         Throwable exception) {
+
+      super(wrapper);
+      this.wrapper = wrapper;
+      this.filter = filter;
+      this.servlet = null;
+      this.type = type;
+      this.exception = exception;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for filter processing events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param filter Filter instance for which this event occurred
+     * @param type Event type (required)
+     * @param request Servlet request we are processing
+     * @param response Servlet response we are processing
+     */
+    public InstanceEvent(Wrapper wrapper, Filter filter, String type,
+                         ServletRequest request, ServletResponse response) {
+
+      super(wrapper);
+      this.wrapper = wrapper;
+      this.filter = filter;
+      this.servlet = null;
+      this.type = type;
+      this.request = request;
+      this.response = response;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for filter processing events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param filter Filter instance for which this event occurred
+     * @param type Event type (required)
+     * @param request Servlet request we are processing
+     * @param response Servlet response we are processing
+     * @param exception Exception that occurred
+     */
+    public InstanceEvent(Wrapper wrapper, Filter filter, String type,
+                         ServletRequest request, ServletResponse response,
+                         Throwable exception) {
+
+      super(wrapper);
+      this.wrapper = wrapper;
+      this.filter = filter;
+      this.servlet = null;
+      this.type = type;
+      this.request = request;
+      this.response = response;
+      this.exception = exception;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for processing servlet lifecycle events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param servlet Servlet instance for which this event occurred
+     * @param type Event type (required)
+     */
+    public InstanceEvent(Wrapper wrapper, Servlet servlet, String type) {
+
+      super(wrapper);
+      this.wrapper = wrapper;
+      this.filter = null;
+      this.servlet = servlet;
+      this.type = type;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for processing servlet lifecycle events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param servlet Servlet instance for which this event occurred
+     * @param type Event type (required)
+     * @param exception Exception that occurred
+     */
+    public InstanceEvent(Wrapper wrapper, Servlet servlet, String type,
+                         Throwable exception) {
+
+      super(wrapper);
+      this.wrapper = wrapper;
+      this.filter = null;
+      this.servlet = servlet;
+      this.type = type;
+      this.exception = exception;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for processing servlet processing events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param servlet Servlet instance for which this event occurred
+     * @param type Event type (required)
+     * @param request Servlet request we are processing
+     * @param response Servlet response we are processing
+     */
+    public InstanceEvent(Wrapper wrapper, Servlet servlet, String type,
+                         ServletRequest request, ServletResponse response) {
+
+      super(wrapper);
+      this.wrapper = wrapper;
+      this.filter = null;
+      this.servlet = servlet;
+      this.type = type;
+      this.request = request;
+      this.response = response;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for processing servlet processing events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param servlet Servlet instance for which this event occurred
+     * @param type Event type (required)
+     * @param request Servlet request we are processing
+     * @param response Servlet response we are processing
+     * @param exception Exception that occurred
+     */
+    public InstanceEvent(Wrapper wrapper, Servlet servlet, String type,
+                         ServletRequest request, ServletResponse response,
+                         Throwable exception) {
+
+      super(wrapper);
+      this.wrapper = wrapper;
+      this.filter = null;
+      this.servlet = servlet;
+      this.type = type;
+      this.request = request;
+      this.response = response;
+      this.exception = exception;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The exception that was thrown during the processing being reported
+     * by this event (AFTER_INIT_EVENT, AFTER_SERVICE_EVENT, 
+     * AFTER_DESTROY_EVENT, AFTER_DISPATCH_EVENT, and AFTER_FILTER_EVENT only).
+     */
+    private Throwable exception = null;
+
+
+    /**
+     * The Filter instance for which this event occurred (BEFORE_FILTER_EVENT
+     * and AFTER_FILTER_EVENT only).
+     */
+    private Filter filter = null;
+
+
+    /**
+     * The servlet request being processed (BEFORE_FILTER_EVENT,
+     * AFTER_FILTER_EVENT, BEFORE_SERVICE_EVENT, and AFTER_SERVICE_EVENT).
+     */
+    private ServletRequest request = null;
+
+
+    /**
+     * The servlet response being processed (BEFORE_FILTER_EVENT,
+     * AFTER_FILTER_EVENT, BEFORE_SERVICE_EVENT, and AFTER_SERVICE_EVENT).
+     */
+    private ServletResponse response = null;
+
+
+    /**
+     * The Servlet instance for which this event occurred (not present on
+     * BEFORE_FILTER_EVENT or AFTER_FILTER_EVENT events).
+     */
+    private Servlet servlet = null;
+
+
+    /**
+     * The event type this instance represents.
+     */
+    private String type = null;
+
+
+    /**
+     * The Wrapper managing the servlet instance for which this event occurred.
+     */
+    private Wrapper wrapper = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the exception that occurred during the processing
+     * that was reported by this event.
+     */
+    public Throwable getException() {
+
+        return (this.exception);
+
+    }
+
+
+    /**
+     * Return the filter instance for which this event occurred.
+     */
+    public Filter getFilter() {
+
+        return (this.filter);
+
+    }
+
+
+    /**
+     * Return the servlet request for which this event occurred.
+     */
+    public ServletRequest getRequest() {
+
+        return (this.request);
+
+    }
+
+
+    /**
+     * Return the servlet response for which this event occurred.
+     */
+    public ServletResponse getResponse() {
+
+        return (this.response);
+
+    }
+
+
+    /**
+     * Return the servlet instance for which this event occurred.
+     */
+    public Servlet getServlet() {
+
+        return (this.servlet);
+
+    }
+
+
+    /**
+     * Return the event type of this event.
+     */
+    public String getType() {
+
+        return (this.type);
+
+    }
+
+
+    /**
+     * Return the Wrapper managing the servlet instance for which this
+     * event occurred.
+     */
+    public Wrapper getWrapper() {
+
+        return (this.wrapper);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/InstanceListener.java b/container/catalina/src/share/org/apache/catalina/InstanceListener.java
new file mode 100644
index 0000000..4eebf36
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/InstanceListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+/**
+ * Interface defining a listener for significant events related to a
+ * specific servlet instance, rather than to the Wrapper component that
+ * is managing that instance.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface InstanceListener {
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event InstanceEvent that has occurred
+     */
+    public void instanceEvent(InstanceEvent event);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Lifecycle.java b/container/catalina/src/share/org/apache/catalina/Lifecycle.java
new file mode 100644
index 0000000..2c98225
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Lifecycle.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+/**
+ * Common interface for component life cycle methods.  Catalina components
+ * may, but are not required to, implement this interface (as well as the
+ * appropriate interface(s) for the functionality they support) in order to
+ * provide a consistent mechanism to start and stop the component.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Lifecycle {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The LifecycleEvent type for the "component init" event.
+     */
+    public static final String INIT_EVENT = "init";
+
+
+    /**
+     * The LifecycleEvent type for the "component start" event.
+     */
+    public static final String START_EVENT = "start";
+
+
+    /**
+     * The LifecycleEvent type for the "component before start" event.
+     */
+    public static final String BEFORE_START_EVENT = "before_start";
+
+
+    /**
+     * The LifecycleEvent type for the "component after start" event.
+     */
+    public static final String AFTER_START_EVENT = "after_start";
+
+
+    /**
+     * The LifecycleEvent type for the "component stop" event.
+     */
+    public static final String STOP_EVENT = "stop";
+
+
+    /**
+     * The LifecycleEvent type for the "component before stop" event.
+     */
+    public static final String BEFORE_STOP_EVENT = "before_stop";
+
+
+    /**
+     * The LifecycleEvent type for the "component after stop" event.
+     */
+    public static final String AFTER_STOP_EVENT = "after_stop";
+
+
+    /**
+     * The LifecycleEvent type for the "component destroy" event.
+     */
+    public static final String DESTROY_EVENT = "destroy";
+
+
+    /**
+     * The LifecycleEvent type for the "periodic" event.
+     */
+    public static final String PERIODIC_EVENT = "periodic";
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a LifecycleEvent listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener);
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners();
+
+
+    /**
+     * Remove a LifecycleEvent listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener);
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called before any of the public
+     * methods of this component are utilized.  It should also send a
+     * LifecycleEvent of type START_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException;
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.  It should also send a LifecycleEvent
+     * of type STOP_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException;
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/LifecycleEvent.java b/container/catalina/src/share/org/apache/catalina/LifecycleEvent.java
new file mode 100644
index 0000000..ce345b4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/LifecycleEvent.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.util.EventObject;
+
+
+/**
+ * General event for notifying listeners of significant changes on a component
+ * that implements the Lifecycle interface.  In particular, this will be useful
+ * on Containers, where these events replace the ContextInterceptor concept in
+ * Tomcat 3.x.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class LifecycleEvent
+    extends EventObject {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new LifecycleEvent with the specified parameters.
+     *
+     * @param lifecycle Component on which this event occurred
+     * @param type Event type (required)
+     */
+    public LifecycleEvent(Lifecycle lifecycle, String type) {
+
+        this(lifecycle, type, null);
+
+    }
+
+
+    /**
+     * Construct a new LifecycleEvent with the specified parameters.
+     *
+     * @param lifecycle Component on which this event occurred
+     * @param type Event type (required)
+     * @param data Event data (if any)
+     */
+    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
+
+        super(lifecycle);
+        this.lifecycle = lifecycle;
+        this.type = type;
+        this.data = data;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The event data associated with this event.
+     */
+    private Object data = null;
+
+
+    /**
+     * The Lifecycle on which this event occurred.
+     */
+    private Lifecycle lifecycle = null;
+
+
+    /**
+     * The event type this instance represents.
+     */
+    private String type = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the event data of this event.
+     */
+    public Object getData() {
+
+        return (this.data);
+
+    }
+
+
+    /**
+     * Return the Lifecycle on which this event occurred.
+     */
+    public Lifecycle getLifecycle() {
+
+        return (this.lifecycle);
+
+    }
+
+
+    /**
+     * Return the event type of this event.
+     */
+    public String getType() {
+
+        return (this.type);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/LifecycleException.java b/container/catalina/src/share/org/apache/catalina/LifecycleException.java
new file mode 100644
index 0000000..00ea305
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/LifecycleException.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+/**
+ * General purpose exception that is thrown to indicate a lifecycle related
+ * problem.  Such exceptions should generally be considered fatal to the
+ * operation of the application containing this component.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class LifecycleException extends Exception {
+
+
+    //------------------------------------------------------------ Constructors
+
+
+    /**
+     * Construct a new LifecycleException with no other information.
+     */
+    public LifecycleException() {
+
+        this(null, null);
+
+    }
+
+
+    /**
+     * Construct a new LifecycleException for the specified message.
+     *
+     * @param message Message describing this exception
+     */
+    public LifecycleException(String message) {
+
+        this(message, null);
+
+    }
+
+
+    /**
+     * Construct a new LifecycleException for the specified throwable.
+     *
+     * @param throwable Throwable that caused this exception
+     */
+    public LifecycleException(Throwable throwable) {
+
+        this(null, throwable);
+
+    }
+
+
+    /**
+     * Construct a new LifecycleException for the specified message
+     * and throwable.
+     *
+     * @param message Message describing this exception
+     * @param throwable Throwable that caused this exception
+     */
+    public LifecycleException(String message, Throwable throwable) {
+
+        super();
+        this.message = message;
+        this.throwable = throwable;
+
+    }
+
+
+    //------------------------------------------------------ Instance Variables
+
+
+    /**
+     * The error message passed to our constructor (if any)
+     */
+    protected String message = null;
+
+
+    /**
+     * The underlying exception or error passed to our constructor (if any)
+     */
+    protected Throwable throwable = null;
+
+
+    //---------------------------------------------------------- Public Methods
+
+
+    /**
+     * Returns the message associated with this exception, if any.
+     */
+    public String getMessage() {
+
+        return (message);
+
+    }
+
+
+    /**
+     * Returns the throwable that caused this exception, if any.
+     */
+    public Throwable getThrowable() {
+
+        return (throwable);
+
+    }
+
+
+    /**
+     * Return a formatted string that describes this exception.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("LifecycleException:  ");
+        if (message != null) {
+            sb.append(message);
+            if (throwable != null) {
+                sb.append(":  ");
+            }
+        }
+        if (throwable != null) {
+            sb.append(throwable.toString());
+        }
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/LifecycleListener.java b/container/catalina/src/share/org/apache/catalina/LifecycleListener.java
new file mode 100644
index 0000000..bde3ac8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/LifecycleListener.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+
+
+/**
+ * Interface defining a listener for significant events (including "component
+ * start" and "component stop" generated by a component that implements the
+ * Lifecycle interface.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface LifecycleListener {
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event LifecycleEvent that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Loader.java b/container/catalina/src/share/org/apache/catalina/Loader.java
new file mode 100644
index 0000000..610195a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Loader.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.beans.PropertyChangeListener;
+
+
+/**
+ * A <b>Loader</b> represents a Java ClassLoader implementation that can
+ * be used by a Container to load class files (within a repository associated
+ * with the Loader) that are designed to be reloaded upon request, as well as
+ * a mechanism to detect whether changes have occurred in the underlying
+ * repository.
+ * <p>
+ * In order for a <code>Loader</code> implementation to successfully operate
+ * with a <code>Context</code> implementation that implements reloading, it
+ * must obey the following constraints:
+ * <ul>
+ * <li>Must implement <code>Lifecycle</code> so that the Context can indicate
+ *     that a new class loader is required.
+ * <li>The <code>start()</code> method must unconditionally create a new
+ *     <code>ClassLoader</code> implementation.
+ * <li>The <code>stop()</code> method must throw away its reference to the
+ *     <code>ClassLoader</code> previously utilized, so that the class loader,
+ *     all classes loaded by it, and all objects of those classes, can be
+ *     garbage collected.
+ * <li>Must allow a call to <code>stop()</code> to be followed by a call to
+ *     <code>start()</code> on the same <code>Loader</code> instance.
+ * <li>Based on a policy chosen by the implementation, must call the
+ *     <code>Context.reload()</code> method on the owning <code>Context</code>
+ *     when a change to one or more of the class files loaded by this class
+ *     loader is detected.
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Loader {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess();
+
+
+    /**
+     * Return the Java class loader to be used by this Container.
+     */
+    public ClassLoader getClassLoader();
+
+
+    /**
+     * Return the Container with which this Loader has been associated.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the Container with which this Loader has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container);
+
+
+    /**
+     * Return the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     */
+    public boolean getDelegate();
+
+
+    /**
+     * Set the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     *
+     * @param delegate The new flag
+     */
+    public void setDelegate(boolean delegate);
+
+
+    /**
+     * Return descriptive information about this Loader implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the reloadable flag for this Loader.
+     */
+    public boolean getReloadable();
+
+
+    /**
+     * Set the reloadable flag for this Loader.
+     *
+     * @param reloadable The new reloadable flag
+     */
+    public void setReloadable(boolean reloadable);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Add a new repository to the set of repositories for this class loader.
+     *
+     * @param repository Repository to be added
+     */
+    public void addRepository(String repository);
+
+
+    /**
+     * Return the set of repositories defined for this class loader.
+     * If none are defined, a zero-length array is returned.
+     */
+    public String[] findRepositories();
+
+
+    /**
+     * Has the internal repository associated with this Loader been modified,
+     * such that the loaded classes should be reloaded?
+     */
+    public boolean modified();
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Manager.java b/container/catalina/src/share/org/apache/catalina/Manager.java
new file mode 100644
index 0000000..2635980
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Manager.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+
+
+/**
+ * A <b>Manager</b> manages the pool of Sessions that are associated with a
+ * particular Container.  Different Manager implementations may support
+ * value-added features such as the persistent storage of session data,
+ * as well as migrating sessions for distributable web applications.
+ * <p>
+ * In order for a <code>Manager</code> implementation to successfully operate
+ * with a <code>Context</code> implementation that implements reloading, it
+ * must obey the following constraints:
+ * <ul>
+ * <li>Must implement <code>Lifecycle</code> so that the Context can indicate
+ *     that a restart is required.
+ * <li>Must allow a call to <code>stop()</code> to be followed by a call to
+ *     <code>start()</code> on the same <code>Manager</code> instance.
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Manager {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Container with which this Manager is associated.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the Container with which this Manager is associated.
+     *
+     * @param container The newly associated Container
+     */
+    public void setContainer(Container container);
+
+
+    /**
+     * Return the distributable flag for the sessions supported by
+     * this Manager.
+     */
+    public boolean getDistributable();
+
+
+    /**
+     * Set the distributable flag for the sessions supported by this
+     * Manager.  If this flag is set, all user data objects added to
+     * sessions associated with this manager must implement Serializable.
+     *
+     * @param distributable The new distributable flag
+     */
+    public void setDistributable(boolean distributable);
+
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the default maximum inactive interval (in seconds)
+     * for Sessions created by this Manager.
+     */
+    public int getMaxInactiveInterval();
+
+
+    /**
+     * Set the default maximum inactive interval (in seconds)
+     * for Sessions created by this Manager.
+     *
+     * @param interval The new default value
+     */
+    public void setMaxInactiveInterval(int interval);
+
+
+    /**
+     * Gets the session id length (in bytes) of Sessions created by
+     * this Manager.
+     *
+     * @return The session id length
+     */
+    public int getSessionIdLength();
+
+
+    /**
+     * Sets the session id length (in bytes) for Sessions created by this
+     * Manager.
+     *
+     * @param idLength The session id length
+     */
+    public void setSessionIdLength(int idLength);
+
+
+    /** 
+     * Returns the total number of sessions created by this manager.
+     *
+     * @return Total number of sessions created by this manager.
+     */
+    public int getSessionCounter();
+
+
+    /** 
+     * Sets the total number of sessions created by this manager.
+     *
+     * @param sessionCounter Total number of sessions created by this manager.
+     */
+    public void setSessionCounter(int sessionCounter);
+
+
+    /**
+     * Gets the maximum number of sessions that have been active at the same
+     * time.
+     *
+     * @return Maximum number of sessions that have been active at the same
+     * time
+     */
+    public int getMaxActive();
+
+
+    /**
+     * (Re)sets the maximum number of sessions that have been active at the
+     * same time.
+     *
+     * @param maxActive Maximum number of sessions that have been active at
+     * the same time.
+     */
+    public void setMaxActive(int maxActive);
+
+
+    /** 
+     * Gets the number of currently active sessions.
+     *
+     * @return Number of currently active sessions
+     */
+    public int getActiveSessions();
+
+
+    /**
+     * Gets the number of sessions that have expired.
+     *
+     * @return Number of sessions that have expired
+     */
+    public int getExpiredSessions();
+
+
+    /**
+     * Sets the number of sessions that have expired.
+     *
+     * @param expiredSessions Number of sessions that have expired
+     */
+    public void setExpiredSessions(int expiredSessions);
+
+
+    /**
+     * Gets the number of sessions that were not created because the maximum
+     * number of active sessions was reached.
+     *
+     * @return Number of rejected sessions
+     */
+    public int getRejectedSessions();
+
+
+    /**
+     * Sets the number of sessions that were not created because the maximum
+     * number of active sessions was reached.
+     *
+     * @param rejectedSessions Number of rejected sessions
+     */
+    public void setRejectedSessions(int rejectedSessions);
+
+
+    /**
+     * Gets the longest time (in seconds) that an expired session had been
+     * alive.
+     *
+     * @return Longest time (in seconds) that an expired session had been
+     * alive.
+     */
+    public int getSessionMaxAliveTime();
+
+
+    /**
+     * Sets the longest time (in seconds) that an expired session had been
+     * alive.
+     *
+     * @param sessionMaxAliveTime Longest time (in seconds) that an expired
+     * session had been alive.
+     */
+    public void setSessionMaxAliveTime(int sessionMaxAliveTime);
+
+
+    /**
+     * Gets the average time (in seconds) that expired sessions had been
+     * alive.
+     *
+     * @return Average time (in seconds) that expired sessions had been
+     * alive.
+     */
+    public int getSessionAverageAliveTime();
+
+
+    /**
+     * Sets the average time (in seconds) that expired sessions had been
+     * alive.
+     *
+     * @param sessionAverageAliveTime Average time (in seconds) that expired
+     * sessions had been alive.
+     */
+    public void setSessionAverageAliveTime(int sessionAverageAliveTime);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add this Session to the set of active Sessions for this Manager.
+     *
+     * @param session Session to be added
+     */
+    public void add(Session session);
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Get a session from the recycled ones or create a new empty one.
+     * The PersistentManager manager does not need to create session data
+     * because it reads it from the Store.
+     */                                                                         
+    public Session createEmptySession();
+
+
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id will be assigned by this method, and available via the getId()
+     * method of the returned session.  If a new session cannot be created
+     * for any reason, return <code>null</code>.
+     * 
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @deprecated
+     */
+    public Session createSession();
+
+
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id specified will be used as the session id.
+     * If a new session cannot be created for any reason, return 
+     * <code>null</code>.
+     * 
+     * @param sessionId The session id which should be used to create the
+     *  new session; if <code>null</code>, the session
+     *  id will be assigned by this method, and available via the getId()
+     *  method of the returned session.
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     */
+    public Session createSession(String sessionId);
+
+
+    /**
+     * Return the active Session, associated with this Manager, with the
+     * specified session id (if any); otherwise return <code>null</code>.
+     *
+     * @param id The session id for the session to be returned
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @exception IOException if an input/output error occurs while
+     *  processing this request
+     */
+    public Session findSession(String id) throws IOException;
+
+
+    /**
+     * Return the set of active Sessions associated with this Manager.
+     * If this Manager has no active Sessions, a zero-length array is returned.
+     */
+    public Session[] findSessions();
+
+
+    /**
+     * Load any currently active sessions that were previously unloaded
+     * to the appropriate persistence mechanism, if any.  If persistence is not
+     * supported, this method returns without doing anything.
+     *
+     * @exception ClassNotFoundException if a serialized class cannot be
+     *  found during the reload
+     * @exception IOException if an input/output error occurs
+     */
+    public void load() throws ClassNotFoundException, IOException;
+
+
+    /**
+     * Remove this Session from the active Sessions for this Manager.
+     *
+     * @param session Session to be removed
+     */
+    public void remove(Session session);
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Save any currently active sessions in the appropriate persistence
+     * mechanism, if any.  If persistence is not supported, this method
+     * returns without doing anything.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void unload() throws IOException;
+    
+     /**
+      * This method will be invoked by the context/container on a periodic
+      * basis and allows the manager to implement
+      * a method that executes periodic tasks, such as expiring sessions etc.
+      */
+     public void backgroundProcess();
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Pipeline.java b/container/catalina/src/share/org/apache/catalina/Pipeline.java
new file mode 100644
index 0000000..8634aeb
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Pipeline.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+/**
+ * <p>Interface describing a collection of Valves that should be executed
+ * in sequence when the <code>invoke()</code> method is invoked.  It is
+ * required that a Valve somewhere in the pipeline (usually the last one)
+ * must process the request and create the corresponding response, rather
+ * than trying to pass the request on.</p>
+ *
+ * <p>There is generally a single Pipeline instance associated with each
+ * Container.  The container's normal request processing functionality is
+ * generally encapsulated in a container-specific Valve, which should always
+ * be executed at the end of a pipeline.  To facilitate this, the
+ * <code>setBasic()</code> method is provided to set the Valve instance that
+ * will always be executed last.  Other Valves will be executed in the order
+ * that they were added, before the basic Valve is executed.</p>
+ *
+ * @author Craig R. McClanahan
+ * @author Peter Donald
+ * @version $Revision$ $Date$
+ */
+
+public interface Pipeline {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * <p>Return the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).
+     */
+    public Valve getBasic();
+
+
+    /**
+     * <p>Set the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).  Prioer to setting the basic Valve,
+     * the Valve's <code>setContainer()</code> will be called, if it
+     * implements <code>Contained</code>, with the owning Container as an
+     * argument.  The method may throw an <code>IllegalArgumentException</code>
+     * if this Valve chooses not to be associated with this Container, or
+     * <code>IllegalStateException</code> if it is already associated with
+     * a different Container.</p>
+     *
+     * @param valve Valve to be distinguished as the basic Valve
+     */
+    public void setBasic(Valve valve);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add a new Valve to the end of the pipeline associated with this
+     * Container.  Prior to adding the Valve, the Valve's
+     * <code>setContainer()</code> method will be called, if it implements
+     * <code>Contained</code>, with the owning Container as an argument.
+     * The method may throw an
+     * <code>IllegalArgumentException</code> if this Valve chooses not to
+     * be associated with this Container, or <code>IllegalStateException</code>
+     * if it is already associated with a different Container.</p>
+     *
+     * @param valve Valve to be added
+     *
+     * @exception IllegalArgumentException if this Container refused to
+     *  accept the specified Valve
+     * @exception IllegalArgumentException if the specifie Valve refuses to be
+     *  associated with this Container
+     * @exception IllegalStateException if the specified Valve is already
+     *  associated with a different Container
+     */
+    public void addValve(Valve valve);
+
+
+    /**
+     * Return the set of Valves in the pipeline associated with this
+     * Container, including the basic Valve (if any).  If there are no
+     * such Valves, a zero-length array is returned.
+     */
+    public Valve[] getValves();
+
+
+    /**
+     * Remove the specified Valve from the pipeline associated with this
+     * Container, if it is found; otherwise, do nothing.  If the Valve is
+     * found and removed, the Valve's <code>setContainer(null)</code> method
+     * will be called if it implements <code>Contained</code>.
+     *
+     * @param valve Valve to be removed
+     */
+    public void removeValve(Valve valve);
+
+
+    /**
+     * <p>Return the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).
+     */
+    public Valve getFirst();
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Realm.java b/container/catalina/src/share/org/apache/catalina/Realm.java
new file mode 100644
index 0000000..ee719a4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Realm.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.SecurityConstraint;
+/**
+ * A <b>Realm</b> is a read-only facade for an underlying security realm
+ * used to authenticate individual users, and identify the security roles
+ * associated with those users.  Realms can be attached at any Container
+ * level, but will typically only be attached to a Context, or higher level,
+ * Container.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Realm {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Container with which this Realm has been associated.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the Container with which this Realm has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container);
+
+
+    /**
+     * Return descriptive information about this Realm implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    // --------------------------------------------------------- Public Methods
+
+    
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, String credentials);
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, byte[] credentials);
+
+
+    /**
+     * Return the Principal associated with the specified username, which
+     * matches the digest calculated using the given parameters using the
+     * method described in RFC 2069; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param digest Digest which has been submitted by the client
+     * @param nonce Unique (or supposedly unique) token which has been used
+     * for this request
+     * @param realm Realm name
+     * @param md5a2 Second MD5 digest used to calculate the digest :
+     * MD5(Method + ":" + uri)
+     */
+    public Principal authenticate(String username, String digest,
+                                  String nonce, String nc, String cnonce,
+                                  String qop, String realm,
+                                  String md5a2);
+
+
+    /**
+     * Return the Principal associated with the specified chain of X509
+     * client certificates.  If there is none, return <code>null</code>.
+     *
+     * @param certs Array of client certificates, with the first one in
+     *  the array being the certificate of the client itself.
+     */
+    public Principal authenticate(X509Certificate certs[]);
+    
+    
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess();
+
+
+    /**
+     * Return the SecurityConstraints configured to guard the request URI for
+     * this request, or <code>null</code> if there is no such constraint.
+     *
+     * @param request Request we are processing
+     */
+    public SecurityConstraint [] findSecurityConstraints(Request request,
+                                                     Context context);
+    
+    
+    /**
+     * Perform access control based on the specified authorization constraint.
+     * Return <code>true</code> if this constraint is satisfied and processing
+     * should continue, or <code>false</code> otherwise.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraint Security constraint we are enforcing
+     * @param context The Context to which client of this class is attached.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean hasResourcePermission(Request request,
+                                         Response response,
+                                         SecurityConstraint [] constraint,
+                                         Context context)
+        throws IOException;
+    
+    
+    /**
+     * Return <code>true</code> if the specified Principal has the specified
+     * security role, within the context of this Realm; otherwise return
+     * <code>false</code>.
+     *
+     * @param principal Principal for whom the role is to be checked
+     * @param role Security role to be checked
+     */
+    public boolean hasRole(Principal principal, String role);
+
+        /**
+     * Enforce any user data constraint required by the security constraint
+     * guarding this request URI.  Return <code>true</code> if this constraint
+     * was not violated and processing should continue, or <code>false</code>
+     * if we have created a response already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraint Security constraint being checked
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean hasUserDataPermission(Request request,
+                                         Response response,
+                                         SecurityConstraint []constraint)
+        throws IOException;
+    
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Role.java b/container/catalina/src/share/org/apache/catalina/Role.java
new file mode 100644
index 0000000..3a197a8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Role.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.security.Principal;
+
+
+/**
+ * <p>Abstract representation of a security role, suitable for use in
+ * environments like JAAS that want to deal with <code>Principals</code>.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public interface Role extends Principal {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the description of this role.
+     */
+    public String getDescription();
+
+
+    /**
+     * Set the description of this role.
+     *
+     * @param description The new description
+     */
+    public void setDescription(String description);
+
+
+    /**
+     * Return the role name of this role, which must be unique
+     * within the scope of a {@link UserDatabase}.
+     */
+    public String getRolename();
+
+
+    /**
+     * Set the role name of this role, which must be unique
+     * within the scope of a {@link UserDatabase}.
+     *
+     * @param rolename The new role name
+     */
+    public void setRolename(String rolename);
+
+
+    /**
+     * Return the {@link UserDatabase} within which this Role is defined.
+     */
+    public UserDatabase getUserDatabase();
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Server.java b/container/catalina/src/share/org/apache/catalina/Server.java
new file mode 100644
index 0000000..ae8f7bf
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Server.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+import org.apache.catalina.deploy.NamingResources;
+
+/**
+ * A <code>Server</code> element represents the entire Catalina
+ * servlet container.  Its attributes represent the characteristics of
+ * the servlet container as a whole.  A <code>Server</code> may contain
+ * one or more <code>Services</code>, and the top level set of naming
+ * resources.
+ * <p>
+ * Normally, an implementation of this interface will also implement
+ * <code>Lifecycle</code>, such that when the <code>start()</code> and
+ * <code>stop()</code> methods are called, all of the defined
+ * <code>Services</code> are also started or stopped.
+ * <p>
+ * In between, the implementation must open a server socket on the port number
+ * specified by the <code>port</code> property.  When a connection is accepted,
+ * the first line is read and compared with the specified shutdown command.
+ * If the command matches, shutdown of the server is initiated.
+ * <p>
+ * <strong>NOTE</strong> - The concrete implementation of this class should
+ * register the (singleton) instance with the <code>ServerFactory</code>
+ * class in its constructor(s).
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Server {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Server implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the global naming resources.
+     */
+    public NamingResources getGlobalNamingResources();
+
+
+    /**
+     * Set the global naming resources.
+     * 
+     * @param globalNamingResources The new global naming resources
+     */
+    public void setGlobalNamingResources
+        (NamingResources globalNamingResources);
+
+
+    /**
+     * Return the port number we listen to for shutdown commands.
+     */
+    public int getPort();
+
+
+    /**
+     * Set the port number we listen to for shutdown commands.
+     *
+     * @param port The new port number
+     */
+    public void setPort(int port);
+
+
+    /**
+     * Return the shutdown command string we are waiting for.
+     */
+    public String getShutdown();
+
+
+    /**
+     * Set the shutdown command we are waiting for.
+     *
+     * @param shutdown The new shutdown command
+     */
+    public void setShutdown(String shutdown);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new Service to the set of defined Services.
+     *
+     * @param service The Service to be added
+     */
+    public void addService(Service service);
+
+
+    /**
+     * Wait until a proper shutdown command is received, then return.
+     */
+    public void await();
+
+
+    /**
+     * Return the specified Service (if it exists); otherwise return
+     * <code>null</code>.
+     *
+     * @param name Name of the Service to be returned
+     */
+    public Service findService(String name);
+
+
+    /**
+     * Return the set of Services defined within this Server.
+     */
+    public Service[] findServices();
+
+
+    /**
+     * Remove the specified Service from the set associated from this
+     * Server.
+     *
+     * @param service The Service to be removed
+     */
+    public void removeService(Service service);
+
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     *
+     * @exception LifecycleException If this server was already initialized.
+     */
+    public void initialize()
+    throws LifecycleException;
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ServerFactory.java b/container/catalina/src/share/org/apache/catalina/ServerFactory.java
new file mode 100644
index 0000000..35745ae
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ServerFactory.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+import org.apache.catalina.core.StandardServer;
+
+
+/**
+ * <p><strong>ServerFactory</strong> allows the registration of the
+ * (singleton) <code>Server</code> instance for this JVM, so that it
+ * can be accessed independently of any existing reference to the
+ * component hierarchy.  This is important for administration tools
+ * that are built around the internal component implementation classes.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ServerFactory {
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The singleton <code>Server</code> instance for this JVM.
+     */
+    private static Server server = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the singleton <code>Server</code> instance for this JVM.
+     */
+    public static Server getServer() {
+        if( server==null )
+            server=new StandardServer();
+        return (server);
+
+    }
+
+
+    /**
+     * Set the singleton <code>Server</code> instance for this JVM.  This
+     * method must <strong>only</strong> be called from a constructor of
+     * the (singleton) <code>Server</code> instance that is created for
+     * this execution of Catalina.
+     *
+     * @param theServer The new singleton instance
+     */
+    public static void setServer(Server theServer) {
+
+        if (server == null)
+            server = theServer;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Service.java b/container/catalina/src/share/org/apache/catalina/Service.java
new file mode 100644
index 0000000..99e9a8f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Service.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+import org.apache.catalina.connector.Connector;
+
+
+/**
+ * A <strong>Service</strong> is a group of one or more
+ * <strong>Connectors</strong> that share a single <strong>Container</strong>
+ * to process their incoming requests.  This arrangement allows, for example,
+ * a non-SSL and SSL connector to share the same population of web apps.
+ * <p>
+ * A given JVM can contain any number of Service instances; however, they are
+ * completely independent of each other and share only the basic JVM facilities
+ * and classes on the system class path.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Service {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the <code>Container</code> that handles requests for all
+     * <code>Connectors</code> associated with this Service.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the <code>Container</code> that handles requests for all
+     * <code>Connectors</code> associated with this Service.
+     *
+     * @param container The new Container
+     */
+    public void setContainer(Container container);
+
+
+    /**
+     * Return descriptive information about this Service implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the name of this Service.
+     */
+    public String getName();
+
+
+    /**
+     * Set the name of this Service.
+     *
+     * @param name The new service name
+     */
+    public void setName(String name);
+
+
+    /**
+     * Return the <code>Server</code> with which we are associated (if any).
+     */
+    public Server getServer();
+
+
+    /**
+     * Set the <code>Server</code> with which we are associated (if any).
+     *
+     * @param server The server that owns this Service
+     */
+    public void setServer(Server server);
+
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new Connector to the set of defined Connectors, and associate it
+     * with this Service's Container.
+     *
+     * @param connector The Connector to be added
+     */
+    public void addConnector(Connector connector);
+
+
+    /**
+     * Find and return the set of Connectors associated with this Service.
+     */
+    public Connector[] findConnectors();
+
+
+    /**
+     * Remove the specified Connector from the set associated from this
+     * Service.  The removed Connector will also be disassociated from our
+     * Container.
+     *
+     * @param connector The Connector to be removed
+     */
+    public void removeConnector(Connector connector);
+
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     *
+     * @exception LifecycleException If this server was already initialized.
+     */
+    public void initialize()
+    throws LifecycleException;
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Session.java b/container/catalina/src/share/org/apache/catalina/Session.java
new file mode 100644
index 0000000..1127fc8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Session.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.security.Principal;
+import java.util.Iterator;
+
+import javax.servlet.http.HttpSession;
+
+
+/**
+ * A <b>Session</b> is the Catalina-internal facade for an
+ * <code>HttpSession</code> that is used to maintain state information
+ * between requests for a particular user of a web application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Session {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The SessionEvent event type when a session is created.
+     */
+    public static final String SESSION_CREATED_EVENT = "createSession";
+
+
+    /**
+     * The SessionEvent event type when a session is destroyed.
+     */
+    public static final String SESSION_DESTROYED_EVENT = "destroySession";
+
+
+    /**
+     * The SessionEvent event type when a session is activated.
+     */
+    public static final String SESSION_ACTIVATED_EVENT = "activateSession";
+
+
+    /**
+     * The SessionEvent event type when a session is passivated.
+     */
+    public static final String SESSION_PASSIVATED_EVENT = "passivateSession";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the authentication type used to authenticate our cached
+     * Principal, if any.
+     */
+    public String getAuthType();
+
+
+    /**
+     * Set the authentication type used to authenticate our cached
+     * Principal, if any.
+     *
+     * @param authType The new cached authentication type
+     */
+    public void setAuthType(String authType);
+
+
+    /**
+     * Return the creation time for this session.
+     */
+    public long getCreationTime();
+
+
+    /**
+     * Set the creation time for this session.  This method is called by the
+     * Manager when an existing Session instance is reused.
+     *
+     * @param time The new creation time
+     */
+    public void setCreationTime(long time);
+
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getId();
+
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getIdInternal();
+
+
+    /**
+     * Set the session identifier for this session.
+     *
+     * @param id The new session identifier
+     */
+    public void setId(String id);
+
+
+    /**
+     * Return descriptive information about this Session implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the last time the client sent a request associated with this
+     * session, as the number of milliseconds since midnight, January 1, 1970
+     * GMT.  Actions that your application takes, such as getting or setting
+     * a value associated with the session, do not affect the access time.
+     */
+    public long getLastAccessedTime();
+
+
+    /**
+     * Return the Manager within which this Session is valid.
+     */
+    public Manager getManager();
+
+
+    /**
+     * Set the Manager within which this Session is valid.
+     *
+     * @param manager The new Manager
+     */
+    public void setManager(Manager manager);
+
+
+    /**
+     * Return the maximum time interval, in seconds, between client requests
+     * before the servlet container will invalidate the session.  A negative
+     * time indicates that the session should never time out.
+     */
+    public int getMaxInactiveInterval();
+
+
+    /**
+     * Set the maximum time interval, in seconds, between client requests
+     * before the servlet container will invalidate the session.  A negative
+     * time indicates that the session should never time out.
+     *
+     * @param interval The new maximum interval
+     */
+    public void setMaxInactiveInterval(int interval);
+
+
+    /**
+     * Set the <code>isNew</code> flag for this session.
+     *
+     * @param isNew The new value for the <code>isNew</code> flag
+     */
+    public void setNew(boolean isNew);
+
+
+    /**
+     * Return the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.  If there
+     * is no current associated Principal, return <code>null</code>.
+     */
+    public Principal getPrincipal();
+
+
+    /**
+     * Set the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.
+     *
+     * @param principal The new Principal, or <code>null</code> if none
+     */
+    public void setPrincipal(Principal principal);
+
+
+    /**
+     * Return the <code>HttpSession</code> for which this object
+     * is the facade.
+     */
+    public HttpSession getSession();
+
+
+    /**
+     * Set the <code>isValid</code> flag for this session.
+     *
+     * @param isValid The new value for the <code>isValid</code> flag
+     */
+    public void setValid(boolean isValid);
+
+
+    /**
+     * Return the <code>isValid</code> flag for this session.
+     */
+    public boolean isValid();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Update the accessed time information for this session.  This method
+     * should be called by the context when a request comes in for a particular
+     * session, even if the application does not reference it.
+     */
+    public void access();
+
+
+    /**
+     * Add a session event listener to this component.
+     */
+    public void addSessionListener(SessionListener listener);
+
+
+    /**
+     * End access to the session.
+     */
+    public void endAccess();
+
+
+    /**
+     * Perform the internal processing required to invalidate this session,
+     * without triggering an exception if the session has already expired.
+     */
+    public void expire();
+
+
+    /**
+     * Return the object bound with the specified name to the internal notes
+     * for this session, or <code>null</code> if no such binding exists.
+     *
+     * @param name Name of the note to be returned
+     */
+    public Object getNote(String name);
+
+
+    /**
+     * Return an Iterator containing the String names of all notes bindings
+     * that exist for this session.
+     */
+    public Iterator getNoteNames();
+
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle();
+
+
+    /**
+     * Remove any object bound to the specified name in the internal notes
+     * for this session.
+     *
+     * @param name Name of the note to be removed
+     */
+    public void removeNote(String name);
+
+
+    /**
+     * Remove a session event listener from this component.
+     */
+    public void removeSessionListener(SessionListener listener);
+
+
+    /**
+     * Bind an object to a specified name in the internal notes associated
+     * with this session, replacing any existing binding for this name.
+     *
+     * @param name Name to which the object should be bound
+     * @param value Object to be bound to the specified name
+     */
+    public void setNote(String name, Object value);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/SessionEvent.java b/container/catalina/src/share/org/apache/catalina/SessionEvent.java
new file mode 100644
index 0000000..a113801
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/SessionEvent.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.util.EventObject;
+
+
+/**
+ * General event for notifying listeners of significant changes on a Session.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class SessionEvent
+    extends EventObject {
+
+
+    /**
+     * The event data associated with this event.
+     */
+    private Object data = null;
+
+
+    /**
+     * The Session on which this event occurred.
+     */
+    private Session session = null;
+
+
+    /**
+     * The event type this instance represents.
+     */
+    private String type = null;
+
+
+    /**
+     * Construct a new SessionEvent with the specified parameters.
+     *
+     * @param session Session on which this event occurred
+     * @param type Event type
+     * @param data Event data
+     */
+    public SessionEvent(Session session, String type, Object data) {
+
+        super(session);
+        this.session = session;
+        this.type = type;
+        this.data = data;
+
+    }
+
+
+    /**
+     * Return the event data of this event.
+     */
+    public Object getData() {
+
+        return (this.data);
+
+    }
+
+
+    /**
+     * Return the Session on which this event occurred.
+     */
+    public Session getSession() {
+
+        return (this.session);
+
+    }
+
+
+    /**
+     * Return the event type of this event.
+     */
+    public String getType() {
+
+        return (this.type);
+
+    }
+
+
+    /**
+     * Return a string representation of this event.
+     */
+    public String toString() {
+
+        return ("SessionEvent['" + getSession() + "','" +
+                getType() + "']");
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/SessionListener.java b/container/catalina/src/share/org/apache/catalina/SessionListener.java
new file mode 100644
index 0000000..1313cb3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/SessionListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+
+
+/**
+ * Interface defining a listener for significant Session generated events.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface SessionListener {
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event SessionEvent that has occurred
+     */
+    public void sessionEvent(SessionEvent event);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Store.java b/container/catalina/src/share/org/apache/catalina/Store.java
new file mode 100644
index 0000000..69a0217
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Store.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+
+
+/**
+ * A <b>Store</b> is the abstraction of a Catalina component that provides
+ * persistent storage and loading of Sessions and their associated user data.
+ * Implementations are free to save and load the Sessions to any media they
+ * wish, but it is assumed that saved Sessions are persistent across
+ * server or context restarts.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Store {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Store implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the Manager instance associated with this Store.
+     */
+    public Manager getManager();
+
+
+    /**
+     * Set the Manager associated with this Store.
+     *
+     * @param manager The Manager which will use this Store.
+     */
+    public void setManager(Manager manager);
+
+
+    /**
+     * Return the number of Sessions present in this Store.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public int getSize() throws IOException;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Return an array containing the session identifiers of all Sessions
+     * currently saved in this Store.  If there are no such Sessions, a
+     * zero-length array is returned.
+     *
+     * @exception IOException if an input/output error occurred
+     */
+    public String[] keys() throws IOException;
+
+
+    /**
+     * Load and return the Session associated with the specified session
+     * identifier from this Store, without removing it.  If there is no
+     * such stored Session, return <code>null</code>.
+     *
+     * @param id Session identifier of the session to load
+     *
+     * @exception ClassNotFoundException if a deserialization error occurs
+     * @exception IOException if an input/output error occurs
+     */
+    public Session load(String id)
+        throws ClassNotFoundException, IOException;
+
+
+    /**
+     * Remove the Session with the specified session identifier from
+     * this Store, if present.  If no such Session is present, this method
+     * takes no action.
+     *
+     * @param id Session identifier of the Session to be removed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void remove(String id) throws IOException;
+
+
+    /**
+     * Remove all Sessions from this Store.
+     */
+    public void clear() throws IOException;
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Save the specified Session into this Store.  Any previously saved
+     * information for the associated session identifier is replaced.
+     *
+     * @param session Session to be saved
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void save(Session session) throws IOException;
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/User.java b/container/catalina/src/share/org/apache/catalina/User.java
new file mode 100644
index 0000000..2a42a79
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/User.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.security.Principal;
+import java.util.Iterator;
+
+
+/**
+ * <p>Abstract representation of a user in a {@link UserDatabase}.  Each user
+ * is optionally associated with a set of {@link Group}s through which he or
+ * she inherits additional security roles, and is optionally assigned a set
+ * of specific {@link Role}s.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public interface User extends Principal {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the full name of this user.
+     */
+    public String getFullName();
+
+
+    /**
+     * Set the full name of this user.
+     *
+     * @param fullName The new full name
+     */
+    public void setFullName(String fullName);
+
+
+    /**
+     * Return the set of {@link Group}s to which this user belongs.
+     */
+    public Iterator getGroups();
+
+
+    /**
+     * Return the logon password of this user, optionally prefixed with the
+     * identifier of an encoding scheme surrounded by curly braces, such as
+     * <code>{md5}xxxxx</code>.
+     */
+    public String getPassword();
+
+
+    /**
+     * Set the logon password of this user, optionally prefixed with the
+     * identifier of an encoding scheme surrounded by curly braces, such as
+     * <code>{md5}xxxxx</code>.
+     *
+     * @param password The new logon password
+     */
+    public void setPassword(String password);
+
+
+    /**
+     * Return the set of {@link Role}s assigned specifically to this user.
+     */
+    public Iterator getRoles();
+
+
+    /**
+     * Return the {@link UserDatabase} within which this User is defined.
+     */
+    public UserDatabase getUserDatabase();
+
+
+    /**
+     * Return the logon username of this user, which must be unique
+     * within the scope of a {@link UserDatabase}.
+     */
+    public String getUsername();
+
+
+    /**
+     * Set the logon username of this user, which must be unique within
+     * the scope of a {@link UserDatabase}.
+     *
+     * @param username The new logon username
+     */
+    public void setUsername(String username);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new {@link Group} to those this user belongs to.
+     *
+     * @param group The new group
+     */
+    public void addGroup(Group group);
+
+
+    /**
+     * Add a {@link Role} to those assigned specifically to this user.
+     *
+     * @param role The new role
+     */
+    public void addRole(Role role);
+
+
+    /**
+     * Is this user in the specified {@link Group}?
+     *
+     * @param group The group to check
+     */
+    public boolean isInGroup(Group group);
+
+
+    /**
+     * Is this user specifically assigned the specified {@link Role}?  This
+     * method does <strong>NOT</strong> check for roles inherited based on
+     * {@link Group} membership.
+     *
+     * @param role The role to check
+     */
+    public boolean isInRole(Role role);
+
+
+    /**
+     * Remove a {@link Group} from those this user belongs to.
+     *
+     * @param group The old group
+     */
+    public void removeGroup(Group group);
+
+
+    /**
+     * Remove all {@link Group}s from those this user belongs to.
+     */
+    public void removeGroups();
+
+
+    /**
+     * Remove a {@link Role} from those assigned to this user.
+     *
+     * @param role The old role
+     */
+    public void removeRole(Role role);
+
+
+    /**
+     * Remove all {@link Role}s from those assigned to this user.
+     */
+    public void removeRoles();
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/UserDatabase.java b/container/catalina/src/share/org/apache/catalina/UserDatabase.java
new file mode 100644
index 0000000..88af63b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/UserDatabase.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.util.Iterator;
+
+
+/**
+ * <p>Abstract representation of a database of {@link User}s and
+ * {@link Group}s that can be maintained by an application,
+ * along with definitions of corresponding {@link Role}s, and
+ * referenced by a {@link Realm} for authentication and access control.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public interface UserDatabase {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the set of {@link Group}s defined in this user database.
+     */
+    public Iterator getGroups();
+
+
+    /**
+     * Return the unique global identifier of this user database.
+     */
+    public String getId();
+
+
+    /**
+     * Return the set of {@link Role}s defined in this user database.
+     */
+    public Iterator getRoles();
+
+
+    /**
+     * Return the set of {@link User}s defined in this user database.
+     */
+    public Iterator getUsers();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Finalize access to this user database.
+     *
+     * @exception Exception if any exception is thrown during closing
+     */
+    public void close() throws Exception;
+
+
+    /**
+     * Create and return a new {@link Group} defined in this user database.
+     *
+     * @param groupname The group name of the new group (must be unique)
+     * @param description The description of this group
+     */
+    public Group createGroup(String groupname, String description);
+
+
+    /**
+     * Create and return a new {@link Role} defined in this user database.
+     *
+     * @param rolename The role name of the new role (must be unique)
+     * @param description The description of this role
+     */
+    public Role createRole(String rolename, String description);
+
+
+    /**
+     * Create and return a new {@link User} defined in this user database.
+     *
+     * @param username The logon username of the new user (must be unique)
+     * @param password The logon password of the new user
+     * @param fullName The full name of the new user
+     */
+    public User createUser(String username, String password,
+                           String fullName);
+
+
+    /**
+     * Return the {@link Group} with the specified group name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param groupname Name of the group to return
+     */
+    public Group findGroup(String groupname);
+
+
+    /**
+     * Return the {@link Role} with the specified role name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param rolename Name of the role to return
+     */
+    public Role findRole(String rolename);
+
+
+    /**
+     * Return the {@link User} with the specified user name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param username Name of the user to return
+     */
+    public User findUser(String username);
+
+
+    /**
+     * Initialize access to this user database.
+     *
+     * @exception Exception if any exception is thrown during opening
+     */
+    public void open() throws Exception;
+
+
+    /**
+     * Remove the specified {@link Group} from this user database.
+     *
+     * @param group The group to be removed
+     */
+    public void removeGroup(Group group);
+
+
+    /**
+     * Remove the specified {@link Role} from this user database.
+     *
+     * @param role The role to be removed
+     */
+    public void removeRole(Role role);
+
+
+    /**
+     * Remove the specified {@link User} from this user database.
+     *
+     * @param user The user to be removed
+     */
+    public void removeUser(User user);
+
+
+    /**
+     * Save any updated information to the persistent storage location for
+     * this user database.
+     *
+     * @exception Exception if any exception is thrown during saving
+     */
+    public void save() throws Exception;
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Valve.java b/container/catalina/src/share/org/apache/catalina/Valve.java
new file mode 100644
index 0000000..6bced6d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Valve.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+
+/**
+ * <p>A <b>Valve</b> is a request processing component associated with a
+ * particular Container.  A series of Valves are generally associated with
+ * each other into a Pipeline.  The detailed contract for a Valve is included
+ * in the description of the <code>invoke()</code> method below.</p>
+ *
+ * <b>HISTORICAL NOTE</b>:  The "Valve" name was assigned to this concept
+ * because a valve is what you use in a real world pipeline to control and/or
+ * modify flows through it.
+ *
+ * @author Craig R. McClanahan
+ * @author Gunnar Rjnning
+ * @author Peter Donald
+ * @version $Revision$ $Date$
+ */
+
+public interface Valve {
+
+
+    //-------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the next Valve in the pipeline containing this Valve, if any.
+     */
+    public Valve getNext();
+
+
+    /**
+     * Set the next Valve in the pipeline containing this Valve.
+     *
+     * @param valve The new next valve, or <code>null</code> if none
+     */
+    public void setNext(Valve valve);
+
+
+    //---------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess();
+
+
+    /**
+     * <p>Perform request processing as required by this Valve.</p>
+     *
+     * <p>An individual Valve <b>MAY</b> perform the following actions, in
+     * the specified order:</p>
+     * <ul>
+     * <li>Examine and/or modify the properties of the specified Request and
+     *     Response.
+     * <li>Examine the properties of the specified Request, completely generate
+     *     the corresponding Response, and return control to the caller.
+     * <li>Examine the properties of the specified Request and Response, wrap
+     *     either or both of these objects to supplement their functionality,
+     *     and pass them on.
+     * <li>If the corresponding Response was not generated (and control was not
+     *     returned, call the next Valve in the pipeline (if there is one) by
+     *     executing <code>context.invokeNext()</code>.
+     * <li>Examine, but not modify, the properties of the resulting Response
+     *     (which was created by a subsequently invoked Valve or Container).
+     * </ul>
+     *
+     * <p>A Valve <b>MUST NOT</b> do any of the following things:</p>
+     * <ul>
+     * <li>Change request properties that have already been used to direct
+     *     the flow of processing control for this request (for instance,
+     *     trying to change the virtual host to which a Request should be
+     *     sent from a pipeline attached to a Host or Context in the
+     *     standard implementation).
+     * <li>Create a completed Response <strong>AND</strong> pass this
+     *     Request and Response on to the next Valve in the pipeline.
+     * <li>Consume bytes from the input stream associated with the Request,
+     *     unless it is completely generating the response, or wrapping the
+     *     request before passing it on.
+     * <li>Modify the HTTP headers included with the Response after the
+     *     <code>invokeNext()</code> method has returned.
+     * <li>Perform any actions on the output stream associated with the
+     *     specified Response after the <code>invokeNext()</code> method has
+     *     returned.
+     * </ul>
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     * @exception ServletException if a servlet error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/Wrapper.java b/container/catalina/src/share/org/apache/catalina/Wrapper.java
new file mode 100644
index 0000000..5b3f14f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/Wrapper.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina;
+
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+
+
+/**
+ * A <b>Wrapper</b> is a Container that represents an individual servlet
+ * definition from the deployment descriptor of the web application.  It
+ * provides a convenient mechanism to use Interceptors that see every single
+ * request to the servlet represented by this definition.
+ * <p>
+ * Implementations of Wrapper are responsible for managing the servlet life
+ * cycle for their underlying servlet class, including calling init() and
+ * destroy() at appropriate times, as well as respecting the existence of
+ * the SingleThreadModel declaration on the servlet class itself.
+ * <p>
+ * The parent Container attached to a Wrapper will generally be an
+ * implementation of Context, representing the servlet context (and
+ * therefore the web application) within which this servlet executes.
+ * <p>
+ * Child Containers are not allowed on Wrapper implementations, so the
+ * <code>addChild()</code> method should throw an
+ * <code>IllegalArgumentException</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Wrapper extends Container {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the available date/time for this servlet, in milliseconds since
+     * the epoch.  If this date/time is in the future, any request for this
+     * servlet will return an SC_SERVICE_UNAVAILABLE error.  If it is zero,
+     * the servlet is currently available.  A value equal to Long.MAX_VALUE
+     * is considered to mean that unavailability is permanent.
+     */
+    public long getAvailable();
+
+
+    /**
+     * Set the available date/time for this servlet, in milliseconds since the
+     * epoch.  If this date/time is in the future, any request for this servlet
+     * will return an SC_SERVICE_UNAVAILABLE error.  A value equal to
+     * Long.MAX_VALUE is considered to mean that unavailability is permanent.
+     *
+     * @param available The new available date/time
+     */
+    public void setAvailable(long available);
+
+
+    /**
+     * Return the context-relative URI of the JSP file for this servlet.
+     */
+    public String getJspFile();
+
+
+    /**
+     * Set the context-relative URI of the JSP file for this servlet.
+     *
+     * @param jspFile JSP file URI
+     */
+    public void setJspFile(String jspFile);
+
+
+    /**
+     * Return the load-on-startup order value (negative value means
+     * load on first call).
+     */
+    public int getLoadOnStartup();
+
+
+    /**
+     * Set the load-on-startup order value (negative value means
+     * load on first call).
+     *
+     * @param value New load-on-startup value
+     */
+    public void setLoadOnStartup(int value);
+
+
+    /**
+     * Return the run-as identity for this servlet.
+     */
+    public String getRunAs();
+
+
+    /**
+     * Set the run-as identity for this servlet.
+     *
+     * @param runAs New run-as identity value
+     */
+    public void setRunAs(String runAs);
+
+
+    /**
+     * Return the fully qualified servlet class name for this servlet.
+     */
+    public String getServletClass();
+
+
+    /**
+     * Set the fully qualified servlet class name for this servlet.
+     *
+     * @param servletClass Servlet class name
+     */
+    public void setServletClass(String servletClass);
+
+
+    /**
+     * Gets the names of the methods supported by the underlying servlet.
+     *
+     * This is the same set of methods included in the Allow response header
+     * in response to an OPTIONS request method processed by the underlying
+     * servlet.
+     *
+     * @return Array of names of the methods supported by the underlying
+     * servlet
+     */
+    public String[] getServletMethods() throws ServletException;
+
+
+    /**
+     * Is this servlet currently unavailable?
+     */
+    public boolean isUnavailable();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new servlet initialization parameter for this servlet.
+     *
+     * @param name Name of this initialization parameter to add
+     * @param value Value of this initialization parameter to add
+     */
+    public void addInitParameter(String name, String value);
+
+
+    /**
+     * Add a new listener interested in InstanceEvents.
+     *
+     * @param listener The new listener
+     */
+    public void addInstanceListener(InstanceListener listener);
+
+
+    /**
+     * Add a mapping associated with the Wrapper.
+     * 
+     * @param mapping The new wrapper mapping
+     */
+    public void addMapping(String mapping);
+
+
+    /**
+     * Add a new security role reference record to the set of records for
+     * this servlet.
+     *
+     * @param name Role name used within this servlet
+     * @param link Role name used within the web application
+     */
+    public void addSecurityReference(String name, String link);
+
+
+    /**
+     * Allocate an initialized instance of this Servlet that is ready to have
+     * its <code>service()</code> method called.  If the servlet class does
+     * not implement <code>SingleThreadModel</code>, the (only) initialized
+     * instance may be returned immediately.  If the servlet class implements
+     * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
+     * that this instance is not allocated again until it is deallocated by a
+     * call to <code>deallocate()</code>.
+     *
+     * @exception ServletException if the servlet init() method threw
+     *  an exception
+     * @exception ServletException if a loading error occurs
+     */
+    public Servlet allocate() throws ServletException;
+
+
+    /**
+     * Return this previously allocated servlet to the pool of available
+     * instances.  If this servlet class does not implement SingleThreadModel,
+     * no action is actually required.
+     *
+     * @param servlet The servlet to be returned
+     *
+     * @exception ServletException if a deallocation error occurs
+     */
+    public void deallocate(Servlet servlet) throws ServletException;
+
+
+    /**
+     * Return the value for the specified initialization parameter name,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the requested initialization parameter
+     */
+    public String findInitParameter(String name);
+
+
+    /**
+     * Return the names of all defined initialization parameters for this
+     * servlet.
+     */
+    public String[] findInitParameters();
+
+
+    /**
+     * Return the mappings associated with this wrapper.
+     */
+    public String[] findMappings();
+
+
+    /**
+     * Return the security role link for the specified security role
+     * reference name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Security role reference used within this servlet
+     */
+    public String findSecurityReference(String name);
+
+
+    /**
+     * Return the set of security role reference names associated with
+     * this servlet, if any; otherwise return a zero-length array.
+     */
+    public String[] findSecurityReferences();
+
+
+    /**
+     * Increment the error count value used when monitoring.
+     */
+    public void incrementErrorCount();
+
+
+    /**
+     * Load and initialize an instance of this servlet, if there is not already
+     * at least one initialized instance.  This can be used, for example, to
+     * load servlets that are marked in the deployment descriptor to be loaded
+     * at server startup time.
+     *
+     * @exception ServletException if the servlet init() method threw
+     *  an exception
+     * @exception ServletException if some other loading problem occurs
+     */
+    public void load() throws ServletException;
+
+
+    /**
+     * Remove the specified initialization parameter from this servlet.
+     *
+     * @param name Name of the initialization parameter to remove
+     */
+    public void removeInitParameter(String name);
+
+
+    /**
+     * Remove a listener no longer interested in InstanceEvents.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeInstanceListener(InstanceListener listener);
+
+
+    /**
+     * Remove a mapping associated with the wrapper.
+     *
+     * @param mapping The pattern to remove
+     */
+    public void removeMapping(String mapping);
+
+
+    /**
+     * Remove any security role reference for the specified role name.
+     *
+     * @param name Security role used within this servlet to be removed
+     */
+    public void removeSecurityReference(String name);
+
+
+    /**
+     * Process an UnavailableException, marking this servlet as unavailable
+     * for the specified amount of time.
+     *
+     * @param unavailable The exception that occurred, or <code>null</code>
+     *  to mark this servlet as permanently unavailable
+     */
+    public void unavailable(UnavailableException unavailable);
+
+
+    /**
+     * Unload all initialized instances of this servlet, after calling the
+     * <code>destroy()</code> method for each instance.  This can be used,
+     * for example, prior to shutting down the entire servlet engine, or
+     * prior to reloading all of the classes from the Loader associated with
+     * our Loader's repository.
+     *
+     * @exception ServletException if an unload error occurs
+     */
+    public void unload() throws ServletException;
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/AbstractCatalinaTask.java b/container/catalina/src/share/org/apache/catalina/ant/AbstractCatalinaTask.java
new file mode 100644
index 0000000..a623bbb
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/AbstractCatalinaTask.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.io.BufferedOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import org.apache.catalina.util.Base64;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+
+/**
+ * Abstract base class for Ant tasks that interact with the
+ * <em>Manager</em> web application for dynamically deploying and
+ * undeploying applications.  These tasks require Ant 1.4 or later.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public abstract class AbstractCatalinaTask extends BaseRedirectorHelperTask {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * manager webapp's encoding.
+     */ 
+    private static String CHARSET = "utf-8";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The charset used during URL encoding.
+     */
+    protected String charset = "ISO-8859-1";
+
+    public String getCharset() {
+        return (this.charset);
+    }
+
+    public void setCharset(String charset) {
+        this.charset = charset;
+    }
+
+
+    /**
+     * The login password for the <code>Manager</code> application.
+     */
+    protected String password = null;
+
+    public String getPassword() {
+        return (this.password);
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+
+    /**
+     * The URL of the <code>Manager</code> application to be used.
+     */
+    protected String url = "http://localhost:8080/manager";
+
+    public String getUrl() {
+        return (this.url);
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+
+    /**
+     * The login username for the <code>Manager</code> application.
+     */
+    protected String username = null;
+
+    public String getUsername() {
+        return (this.username);
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the specified command.  This logic only performs the common
+     * attribute validation required by all subclasses; it does not perform
+     * any functional logic directly.
+     *
+     * @exception BuildException if a validation error occurs
+     */
+    public void execute() throws BuildException {
+
+        if ((username == null) || (password == null) || (url == null)) {
+            throw new BuildException
+                ("Must specify all of 'username', 'password', and 'url'");
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Execute the specified command, based on the configured properties.
+     *
+     * @param command Command to be executed
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute(String command) throws BuildException {
+
+        execute(command, null, null, -1);
+
+    }
+
+
+    /**
+     * Execute the specified command, based on the configured properties.
+     * The input stream will be closed upon completion of this task, whether
+     * it was executed successfully or not.
+     *
+     * @param command Command to be executed
+     * @param istream InputStream to include in an HTTP PUT, if any
+     * @param contentType Content type to specify for the input, if any
+     * @param contentLength Content length to specify for the input, if any
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute(String command, InputStream istream,
+                        String contentType, int contentLength)
+        throws BuildException {
+
+        URLConnection conn = null;
+        InputStreamReader reader = null;
+        try {
+
+            // Create a connection for this command
+            conn = (new URL(url + command)).openConnection();
+            HttpURLConnection hconn = (HttpURLConnection) conn;
+
+            // Set up standard connection characteristics
+            hconn.setAllowUserInteraction(false);
+            hconn.setDoInput(true);
+            hconn.setUseCaches(false);
+            if (istream != null) {
+                hconn.setDoOutput(true);
+                hconn.setRequestMethod("PUT");
+                if (contentType != null) {
+                    hconn.setRequestProperty("Content-Type", contentType);
+                }
+                if (contentLength >= 0) {
+                    hconn.setRequestProperty("Content-Length",
+                                             "" + contentLength);
+                }
+            } else {
+                hconn.setDoOutput(false);
+                hconn.setRequestMethod("GET");
+            }
+            hconn.setRequestProperty("User-Agent",
+                                     "Catalina-Ant-Task/1.0");
+
+            // Set up an authorization header with our credentials
+            String input = username + ":" + password;
+            String output = new String(Base64.encode(input.getBytes()));
+            hconn.setRequestProperty("Authorization",
+                                     "Basic " + output);
+
+            // Establish the connection with the server
+            hconn.connect();
+
+            // Send the request data (if any)
+            if (istream != null) {
+                BufferedOutputStream ostream =
+                    new BufferedOutputStream(hconn.getOutputStream(), 1024);
+                byte buffer[] = new byte[1024];
+                while (true) {
+                    int n = istream.read(buffer);
+                    if (n < 0) {
+                        break;
+                    }
+                    ostream.write(buffer, 0, n);
+                }
+                ostream.flush();
+                ostream.close();
+                istream.close();
+            }
+
+            // Process the response message
+            reader = new InputStreamReader(hconn.getInputStream(), CHARSET);
+            StringBuffer buff = new StringBuffer();
+            String error = null;
+            int msgPriority = Project.MSG_INFO;
+            boolean first = true;
+            while (true) {
+                int ch = reader.read();
+                if (ch < 0) {
+                    break;
+                } else if ((ch == '\r') || (ch == '\n')) {
+                    // in Win \r\n would cause handleOutput() to be called
+                    // twice, the second time with an empty string,
+                    // producing blank lines
+                    if (buff.length() > 0) {
+                        String line = buff.toString();
+                        buff.setLength(0);
+                        if (first) {
+                            if (!line.startsWith("OK -")) {
+                                error = line;
+                                msgPriority = Project.MSG_ERR;
+                            }
+                            first = false;
+                        }
+                        handleOutput(line, msgPriority);
+                    }
+                } else {
+                    buff.append((char) ch);
+                }
+            }
+            if (buff.length() > 0) {
+                handleOutput(buff.toString(), msgPriority);
+            }
+            if (error != null && isFailOnError()) {
+                // exception should be thrown only if failOnError == true
+                // or error line will be logged twice
+                throw new BuildException(error);
+            }
+        } catch (Throwable t) {
+            if (isFailOnError()) {
+                throw new BuildException(t);
+            } else {
+                handleErrorOutput(t.getMessage());
+            }
+        } finally {
+            closeRedirector();
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (Throwable u) {
+                    ;
+                }
+                reader = null;
+            }
+            if (istream != null) {
+                try {
+                    istream.close();
+                } catch (Throwable u) {
+                    ;
+                }
+                istream = null;
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/BaseRedirectorHelperTask.java b/container/catalina/src/share/org/apache/catalina/ant/BaseRedirectorHelperTask.java
new file mode 100644
index 0000000..a1468c7
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/BaseRedirectorHelperTask.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Redirector;
+import org.apache.tools.ant.types.RedirectorElement;
+
+
+/**
+ * Abstract base class to add output redirection support for Catalina
+ * Ant tasks. These tasks require Ant 1.5 or later.
+ * <br>
+ * <strong>WARNING:</strong> due to depends chain, Ant could call a Task 
+ * more than once and this can affect the output redirection when configured.
+ * If you are collecting the output in a property, it will collect the output
+ * of only the first run, since Ant properties are immutable and once created
+ * they cannot be changed.
+ * <br>
+ * If you are collecting output in a file the file will be overwritten with the
+ * output of the last run, unless you set append="true", in which case each run 
+ * will append it's output to the file.
+ * 
+ *
+ * @author Gabriele Garuglieri
+ * @version $Revision$ $Date$
+ * @since 5.5
+ */
+
+public abstract class BaseRedirectorHelperTask extends Task {
+
+    // ------------------------------------------------------------- Properties
+
+    /** Redirector helper */
+    protected Redirector redirector = new Redirector(this);
+    //protected Redirector redirector = null;
+    /** Redirector element for this task */
+    protected RedirectorElement redirectorElement = null;
+    /** The stream for info output */
+    protected OutputStream redirectOutStream = null;
+    /** The stream for error output */
+    protected OutputStream redirectErrStream = null;
+    /** The print stream for info output */
+    PrintStream redirectOutPrintStream = null;
+    /** The print stream for error output */
+    PrintStream redirectErrPrintStream = null;
+        
+   /**
+     * Whether to fail (with a BuildException) if
+     * ManagerServlet returns an error. The default behavior is
+     * to do so.
+     * <b>
+     * This flag does not control parameters checking. If the task is called
+     * with wrong or invalid parameters, it will throw BuildException
+     * independently from the setting of this flag.
+     */    
+    protected boolean failOnError = true;
+    
+    /** 
+      * <code>true</code> true when output redirection is requested for this task .
+      * Default is to log on Ant log.
+      */    
+    protected boolean redirectOutput = false;
+ 
+    /** 
+      * will be set to <code>true</code> when the configuration of the Redirector is
+      * complete.
+      */    
+    protected boolean redirectorConfigured = false;
+
+    /** 
+     * Flag which indicates that, if redirected, output should also be 
+     * always sent to the log. Default is that otput is sent only to
+     * redirected streams.
+     */
+    protected boolean alwaysLog = false;
+
+    /**
+     * Whether to fail (with a BuildException) if
+     * ManagerServlet returns an error.  The default behavior is
+     * to do so.
+     */
+    public void setFailonerror(boolean fail) {
+        failOnError = fail;
+    }
+
+    /**
+     * Returns the value of the failOnError
+     * property.
+     */
+    public boolean isFailOnError() {
+      return failOnError;
+    }
+        
+
+    /**
+     * File the output of the task is redirected to.
+     *
+     * @param out name of the output file
+     */
+    public void setOutput(File out) {
+        redirector.setOutput(out);
+        redirectOutput = true;
+    }
+
+    /**
+     * File the error output of the task is redirected to.
+     *
+     * @param error name of the error file
+     *
+     */
+    public void setError(File error) {
+        redirector.setError(error);
+        redirectOutput = true;
+    }
+
+    /**
+     * Controls whether error output is logged. This is only useful
+     * when output is being redirected and error output is desired in the
+     * Ant log
+     *
+     * @param logError if true the standard error is sent to the Ant log system
+     *        and not sent to output stream.
+     */
+    public void setLogError(boolean logError) {
+        redirector.setLogError(logError);
+        redirectOutput = true;
+    }
+
+    /**
+     * Property name whose value should be set to the output of
+     * the task.
+     *
+     * @param outputProperty property name
+     *
+     */
+    public void setOutputproperty(String outputProperty) {
+        redirector.setOutputProperty(outputProperty);
+        redirectOutput = true;
+    }
+
+    /**
+     * Property name whose value should be set to the error of
+     * the task..
+     *
+     * @param errorProperty property name
+     *
+     */
+    public void setErrorProperty(String errorProperty) {
+        redirector.setErrorProperty(errorProperty);
+        redirectOutput = true;
+    }
+
+    /**
+     * If true, append output to existing file.
+     *
+     * @param append if true, append output to existing file
+     *
+     */
+    public void setAppend(boolean append) {
+        redirector.setAppend(append);
+        redirectOutput = true;
+    }
+
+    /**
+     * If true, (error and non-error) output will be redirected
+     * as specified while being sent to Ant's logging mechanism as if no
+     * redirection had taken place.  Defaults to false.
+     * <br>
+     * Actually handled internally, with Ant 1.6.3 it will be handled by
+     * the <code>Redirector</code> itself.
+     * @param alwaysLog <code>boolean</code>
+     */
+    public void setAlwaysLog(boolean alwaysLog) {
+        this.alwaysLog = alwaysLog;
+        //redirector.setAlwaysLog(alwaysLog);
+        redirectOutput = true;
+    }
+
+    /**
+     * Whether output and error files should be created even when empty.
+     * Defaults to true.
+     * @param createEmptyFiles <CODE>boolean</CODE>.
+     */
+    public void setCreateEmptyFiles(boolean createEmptyFiles) {
+        redirector.setCreateEmptyFiles(createEmptyFiles);
+        redirectOutput = true;
+    }
+
+    /**
+     * Add a <CODE>RedirectorElement</CODE> to this task.
+     * @param redirectorElement   <CODE>RedirectorElement</CODE>.
+     */
+    public void addConfiguredRedirector(RedirectorElement redirectorElement) {
+        if (this.redirectorElement != null) {
+            throw new BuildException("Cannot have > 1 nested <redirector>s");
+        } else {
+            this.redirectorElement = redirectorElement;
+        }
+    }
+
+    /**
+     * Set up properties on the Redirector from RedirectorElement if present.
+     */
+    private void configureRedirector() {
+        if (redirectorElement != null) {
+            redirectorElement.configure(redirector);
+            redirectOutput = true;
+        }
+        /*
+         * Due to depends chain, Ant could call the Task more than once,
+         * this is to prevent that we attempt to configure uselessly
+         * more than once the Redirector.
+         */
+        redirectorConfigured = true;
+    }
+
+    /**
+     * Set up properties on the Redirector and create output streams.
+     */
+    protected void openRedirector() {
+        if (! redirectorConfigured) {
+            configureRedirector();
+        }
+        if (redirectOutput) {
+            redirector.createStreams();
+            redirectOutStream = redirector.getOutputStream();
+            redirectOutPrintStream = new PrintStream(redirectOutStream);
+            redirectErrStream = redirector.getErrorStream();
+            redirectErrPrintStream = new PrintStream(redirectErrStream);
+        }
+   }
+
+    /**
+     * Ask redirector to close all the streams. It is necessary to call this method
+     * before leaving the Task to have the Streams flush their contents. If you are 
+     * collecting output in a property, it will be created only if this method is
+     * called, otherwise you'll find it unset.
+     */
+    protected void closeRedirector() {
+        try {
+            if (redirectOutput) {
+                redirector.complete();
+            }
+        } catch (IOException ioe) {
+            log("Error closing redirector: "
+                + ioe.getMessage(), Project.MSG_ERR);
+        }
+        /*
+         * Due to depends chain, Ant could call the Task more than once,
+         * this is to prevent that we attempt to reuse the previuosly 
+         * closed Streams.
+         */
+        redirectOutStream = null;
+        redirectOutPrintStream = null;
+        redirectErrStream = null;
+        redirectErrPrintStream = null;
+    }
+    
+    /**
+     * Handles output with the INFO priority.
+     *
+     * @param output The output to log. Should not be <code>null</code>.
+     */
+    protected void handleOutput(String output) {
+        if (redirectOutput) {
+            if (redirectOutPrintStream == null) {
+                openRedirector();
+            }
+            redirectOutPrintStream.println(output);
+            if (alwaysLog) {
+                log(output, Project.MSG_INFO);
+            }
+        } else { 
+            log(output, Project.MSG_INFO);
+        }
+    }
+
+    /**
+     * Handles output with the INFO priority and flushes the stream.
+     *
+     * @param output The output to log. Should not be <code>null</code>.
+     *
+     */
+    protected void handleFlush(String output) {
+        handleOutput(output);
+        redirectOutPrintStream.flush();
+    }
+
+    /**
+     * Handles error output with the ERR priority.
+     *
+     * @param output The error output to log. Should not be <code>null</code>.
+     */
+    protected void handleErrorOutput(String output) {
+        if (redirectOutput) {
+            if (redirectErrPrintStream == null) {
+                openRedirector();
+            }
+            redirectErrPrintStream.println(output);
+            if (alwaysLog) {
+                log(output, Project.MSG_ERR);
+            }
+        } else { 
+            log(output, Project.MSG_ERR);
+        }
+    }
+
+    /**
+     * Handles error output with the ERR priority and flushes the stream.
+     *
+     * @param output The error output to log. Should not be <code>null</code>.
+     *
+     */
+    protected void handleErrorFlush(String output) {
+        handleErrorOutput(output);
+        redirectErrPrintStream.flush();
+    }
+  
+    /**
+     * Handles output with ERR priority to error stream and all other
+     * pritorities to output stream.
+     *
+     * @param output The output to log. Should not be <code>null</code>.
+     */
+    protected void handleOutput(String output, int priority) {
+        if (priority == Project.MSG_ERR) {
+            handleErrorOutput(output);
+        } else {
+            handleOutput(output);
+        }
+    }
+  
+    /**
+     * Handles output with ERR priority to error stream and all other
+     * pritorities to output stream, then flushes the stream.
+     *
+     * @param output The output to log. Should not be <code>null</code>.
+     */
+    protected void handleFlush(String output, int priority) {
+        if (priority == Project.MSG_ERR) {
+            handleErrorFlush(output);
+        } else {
+            handleFlush(output);
+        }
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/DeployTask.java b/container/catalina/src/share/org/apache/catalina/ant/DeployTask.java
new file mode 100644
index 0000000..443c251
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/DeployTask.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/deploy</code> command, supported by
+ * the Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+public class DeployTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * URL of the context configuration file for this application, if any.
+     */
+    protected String config = null;
+
+    public String getConfig() {
+        return (this.config);
+    }
+
+    public void setConfig(String config) {
+        this.config = config;
+    }
+
+
+    /**
+     * URL of the server local web application archive (WAR) file 
+     * to be deployed.
+     */
+    protected String localWar = null;
+
+    public String getLocalWar() {
+        return (this.localWar);
+    }
+
+    public void setLocalWar(String localWar) {
+        this.localWar = localWar;
+    }
+
+
+    /**
+     * The context path of the web application we are managing.
+     */
+    protected String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+
+    /**
+     * Tag to associate with this to be deployed webapp.
+     */
+    protected String tag = null;
+
+    public String getTag() {
+        return (this.tag);
+    }
+
+    public void setTag(String tag) {
+        this.tag = tag;
+    }
+
+
+    /**
+     * Update existing webapps.
+     */
+    protected boolean update = false;
+
+    public boolean getUpdate() {
+        return (this.update);
+    }
+
+    public void setUpdate(boolean update) {
+        this.update = update;
+    }
+
+
+    /**
+     * URL of the web application archive (WAR) file to be deployed.
+     */
+    protected String war = null;
+
+    public String getWar() {
+        return (this.war);
+    }
+
+    public void setWar(String war) {
+        this.war = war;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        if (path == null) {
+            throw new BuildException
+                ("Must specify 'path' attribute");
+        }
+        if ((war == null) && (localWar == null) && (config == null) && (tag == null)) {
+            throw new BuildException
+                ("Must specify either 'war', 'localWar', 'config', or 'tag' attribute");
+        }
+
+        // Building an input stream on the WAR to upload, if any
+        BufferedInputStream stream = null;
+        String contentType = null;
+        int contentLength = -1;
+        if (war != null) {
+            if (war.startsWith("file:")) {
+                try {
+                    URL url = new URL(war);
+                    URLConnection conn = url.openConnection();
+                    contentLength = conn.getContentLength();
+                    stream = new BufferedInputStream
+                        (conn.getInputStream(), 1024);
+                } catch (IOException e) {
+                    throw new BuildException(e);
+                }
+            } else {
+                try {
+                    stream = new BufferedInputStream
+                        (new FileInputStream(war), 1024);
+                } catch (IOException e) {
+                    throw new BuildException(e);
+                }
+            }
+            contentType = "application/octet-stream";
+        }
+
+        // Building URL
+        StringBuffer sb = new StringBuffer("/deploy?path=");
+        try {
+            sb.append(URLEncoder.encode(this.path, getCharset()));
+            if ((war == null) && (config != null)) {
+                sb.append("&config=");
+                sb.append(URLEncoder.encode(config, getCharset()));
+            }
+            if ((war == null) && (localWar != null)) {
+                sb.append("&war=");
+                sb.append(URLEncoder.encode(localWar, getCharset()));
+            }
+            if (update) {
+                sb.append("&update=true");
+            }
+            if (tag != null) {
+                sb.append("&tag=");
+                sb.append(URLEncoder.encode(tag, getCharset()));
+            }
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException("Invalid 'charset' attribute: " + getCharset());
+        }
+
+        execute(sb.toString(), stream, contentType, contentLength);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/InstallTask.java b/container/catalina/src/share/org/apache/catalina/ant/InstallTask.java
new file mode 100644
index 0000000..c62a24e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/InstallTask.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/install</code> command, supported by the
+ * Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ * @deprecated Replaced by DeployTask
+ */
+public class InstallTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * URL of the context configuration file for this application, if any.
+     */
+    protected String config = null;
+
+    public String getConfig() {
+        return (this.config);
+    }
+
+    public void setConfig(String config) {
+        this.config = config;
+    }
+
+
+    /**
+     * The context path of the web application we are managing.
+     */
+    protected String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+
+    /**
+     * URL of the web application archive (WAR) file, or the unpacked directory
+     * containing this application, if any.
+     */
+    protected String war = null;
+
+    public String getWar() {
+        return (this.war);
+    }
+
+    public void setWar(String war) {
+        this.war = war;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        if (path == null) {
+            throw new BuildException
+                ("Must specify 'path' attribute");
+        }
+        if ((config == null) && (war == null)) {
+            throw new BuildException
+                ("Must specify at least one of 'config' and 'war'");
+        }
+        StringBuffer sb = new StringBuffer("/install?path=");
+        sb.append(URLEncoder.encode(this.path));
+        if (config != null) {
+            sb.append("&config=");
+            sb.append(URLEncoder.encode(config));
+        }
+        if (war != null) {
+            sb.append("&war=");
+            sb.append(URLEncoder.encode(war));
+        }
+        execute(sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/JKStatusUpdateTask.java b/container/catalina/src/share/org/apache/catalina/ant/JKStatusUpdateTask.java
new file mode 100644
index 0000000..38be397
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/JKStatusUpdateTask.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ant;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Ant task that implements the <code>/status</code> command, supported by the
+ * mod_jk status (1.2.9) application.
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$
+ * @since 5.5.9
+ */
+public class JKStatusUpdateTask extends AbstractCatalinaTask {
+
+    private String worker = "lb";
+
+    private String workerType = "lb";
+
+    private int internalid = 0;
+
+    private Integer lbRetries;
+
+    private Integer lbRecovertime;
+
+    private Boolean lbStickySession = Boolean.TRUE;
+
+    private Boolean lbForceSession = Boolean.FALSE;
+
+    private Integer workerLoadFactor;
+
+    private String workerRedirect;
+
+    private String workerClusterDomain;
+
+    private Boolean workerDisabled = Boolean.FALSE;
+
+    private Boolean workerStopped = Boolean.FALSE;
+    
+    private boolean isLBMode = true;
+
+    private String workerLb;
+
+    /**
+     *  
+     */
+    public JKStatusUpdateTask() {
+        super();
+        setUrl("http://localhost/status");
+    }
+
+    /**
+     * @return Returns the internalid.
+     */
+    public int getInternalid() {
+        return internalid;
+    }
+
+    /**
+     * @param internalid
+     *            The internalid to set.
+     */
+    public void setInternalid(int internalid) {
+        this.internalid = internalid;
+    }
+
+    /**
+     * @return Returns the lbForceSession.
+     */
+    public Boolean getLbForceSession() {
+        return lbForceSession;
+    }
+
+    /**
+     * @param lbForceSession
+     *            The lbForceSession to set.
+     */
+    public void setLbForceSession(Boolean lbForceSession) {
+        this.lbForceSession = lbForceSession;
+    }
+
+    /**
+     * @return Returns the lbRecovertime.
+     */
+    public Integer getLbRecovertime() {
+        return lbRecovertime;
+    }
+
+    /**
+     * @param lbRecovertime
+     *            The lbRecovertime to set.
+     */
+    public void setLbRecovertime(Integer lbRecovertime) {
+        this.lbRecovertime = lbRecovertime;
+    }
+
+    /**
+     * @return Returns the lbRetries.
+     */
+    public Integer getLbRetries() {
+        return lbRetries;
+    }
+
+    /**
+     * @param lbRetries
+     *            The lbRetries to set.
+     */
+    public void setLbRetries(Integer lbRetries) {
+        this.lbRetries = lbRetries;
+    }
+
+    /**
+     * @return Returns the lbStickySession.
+     */
+    public Boolean getLbStickySession() {
+        return lbStickySession;
+    }
+
+    /**
+     * @param lbStickySession
+     *            The lbStickySession to set.
+     */
+    public void setLbStickySession(Boolean lbStickySession) {
+        this.lbStickySession = lbStickySession;
+    }
+
+    /**
+     * @return Returns the worker.
+     */
+    public String getWorker() {
+        return worker;
+    }
+
+    /**
+     * @param worker
+     *            The worker to set.
+     */
+    public void setWorker(String worker) {
+        this.worker = worker;
+    }
+
+    /**
+     * @return Returns the workerType.
+     */
+    public String getWorkerType() {
+        return workerType;
+    }
+
+    /**
+     * @param workerType
+     *            The workerType to set.
+     */
+    public void setWorkerType(String workerType) {
+        this.workerType = workerType;
+    }
+
+    /**
+     * @return Returns the workerLb.
+     */
+    public String getWorkerLb() {
+        return workerLb;
+    }
+
+    /**
+     * @param workerLb
+     *            The workerLb to set.
+     */
+    public void setWorkerLb(String workerLb) {
+        this.workerLb = workerLb;
+    }
+
+    /**
+     * @return Returns the workerClusterDomain.
+     */
+    public String getWorkerClusterDomain() {
+        return workerClusterDomain;
+    }
+
+    /**
+     * @param workerClusterDomain
+     *            The workerClusterDomain to set.
+     */
+    public void setWorkerClusterDomain(String workerClusterDomain) {
+        this.workerClusterDomain = workerClusterDomain;
+    }
+
+    /**
+     * @return Returns the workerDisabled.
+     */
+    public Boolean getWorkerDisabled() {
+        return workerDisabled;
+    }
+
+    /**
+     * @param workerDisabled
+     *            The workerDisabled to set.
+     */
+    public void setWorkerDisabled(Boolean workerDisabled) {
+        this.workerDisabled = workerDisabled;
+    }
+
+    /**
+     * @return Returns the workerStopped.
+     */
+    public Boolean getWorkerStopped() {
+        return workerStopped;
+    }
+    
+    /**
+     * @param workerStopped The workerStopped to set.
+     */
+    public void setWorkerStopped(Boolean workerStopped) {
+        this.workerStopped = workerStopped;
+    }
+    
+    /**
+     * @return Returns the workerLoadFactor.
+     */
+    public Integer getWorkerLoadFactor() {
+        return workerLoadFactor;
+    }
+
+    /**
+     * @param workerLoadFactor
+     *            The workerLoadFactor to set.
+     */
+    public void setWorkerLoadFactor(Integer workerLoadFactor) {
+        this.workerLoadFactor = workerLoadFactor;
+    }
+
+    /**
+     * @return Returns the workerRedirect.
+     */
+    public String getWorkerRedirect() {
+        return workerRedirect;
+    }
+
+    /**
+     * @param workerRedirect
+     *            The workerRedirect to set.
+     */
+    public void setWorkerRedirect(String workerRedirect) {
+        this.workerRedirect = workerRedirect;
+    }
+
+    /**
+     * Execute the requested operation.
+     * 
+     * @exception BuildException
+     *                if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        checkParameter();
+        StringBuffer sb = createLink();
+        execute(sb.toString(), null, null, -1);
+
+    }
+
+    /**
+     * Create JkStatus link
+     * <ul>
+     * <li><b>load balance example:
+     * </b>http://localhost/status?cmd=update&mime=txt&w=lb&lf=false&ls=true</li>
+     * <li><b>worker example:
+     * </b>http://localhost/status?cmd=update&mime=txt&w=node1&l=lb&wf=1&wd=false&ws=false
+     * </li>
+     * </ul>
+     * 
+     * @return create jkstatus link
+     */
+    private StringBuffer createLink() {
+        // Building URL
+        StringBuffer sb = new StringBuffer();
+        try {
+            sb.append("?cmd=update&mime=txt");
+            sb.append("&w=");
+            sb.append(URLEncoder.encode(worker, getCharset()));
+
+            if (isLBMode) {
+                //http://localhost/status?cmd=update&mime=txt&w=lb&lf=false&ls=true
+                if ((lbRetries != null)) { // > 0
+                    sb.append("&lr=");
+                    sb.append(lbRetries);
+                }
+                if ((lbRecovertime != null)) { // > 59
+                    sb.append("&lt=");
+                    sb.append(lbRecovertime);
+                }
+                if ((lbStickySession != null)) {
+                    sb.append("&ls=");
+                    sb.append(lbStickySession);
+                }
+                if ((lbForceSession != null)) {
+                    sb.append("&lf=");
+                    sb.append(lbForceSession);
+                }
+            } else {
+                //http://localhost/status?cmd=update&mime=txt&w=node1&l=lb&wf=1&wd=false&ws=false
+                if ((workerLb != null)) { // must be configured
+                    sb.append("&l=");
+                    sb.append(URLEncoder.encode(workerLb, getCharset()));
+                }
+                if ((workerLoadFactor != null)) { // >= 1
+                    sb.append("&wf=");
+                    sb.append(workerLoadFactor);
+                }
+                if ((workerDisabled != null)) {
+                    sb.append("&wd=");
+                    sb.append(workerDisabled);
+                }
+                if ((workerStopped != null)) {
+                    sb.append("&ws=");
+                    sb.append(workerStopped);
+                }
+                if ((workerRedirect != null)) { // other worker conrecte lb's
+                    sb.append("&wr=");
+                }
+                if ((workerClusterDomain != null)) {
+                    sb.append("&wc=");
+                    sb.append(URLEncoder.encode(workerClusterDomain,
+                            getCharset()));
+                }
+            }
+
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException("Invalid 'charset' attribute: "
+                    + getCharset());
+        }
+        return sb;
+    }
+
+    /**
+     * check correct lb and worker pararmeter
+     */
+    protected void checkParameter() {
+        if (worker == null) {
+            throw new BuildException("Must specify 'worker' attribute");
+        }
+        if (workerType == null) {
+            throw new BuildException("Must specify 'workerType' attribute");
+        }
+        if ("lb".equals(workerType)) {
+            if (lbRecovertime == null && lbRetries == null) {
+                throw new BuildException(
+                        "Must specify at a lb worker either 'lbRecovertime' or"
+                                + "'lbRetries' attribute");
+            }
+            if (lbStickySession == null || lbForceSession == null) {
+                throw new BuildException("Must specify at a lb worker either"
+                        + "'lbStickySession' and 'lbForceSession' attribute");
+            }
+            if (null != lbRecovertime && 60 < lbRecovertime.intValue()) {
+                throw new BuildException(
+                        "The 'lbRecovertime' must be greater than 59");
+            }
+            if (null != lbRetries && 1 < lbRetries.intValue()) {
+                throw new BuildException(
+                        "The 'lbRetries' must be greater than 1");
+            }
+            isLBMode = true;
+        } else if ("worker".equals(workerType)) {
+            if (workerDisabled == null) {
+                throw new BuildException(
+                        "Must specify at a node worker 'workerDisabled' attribute");
+            }
+            if (workerStopped == null) {
+                throw new BuildException(
+                        "Must specify at a node worker 'workerStopped' attribute");
+            }
+            if (workerLoadFactor == null ) {
+                throw new BuildException(
+                        "Must specify at a node worker 'workerLoadFactor' attribute");
+            }
+            if (workerClusterDomain == null) {
+                throw new BuildException(
+                        "Must specify at a node worker 'workerClusterDomain' attribute");
+            }
+            if (workerRedirect == null) {
+                throw new BuildException(
+                        "Must specify at a node worker 'workerRedirect' attribute");
+            }
+            if (workerLb == null) {
+                throw new BuildException("Must specify 'workerLb' attribute");
+            }
+            if (workerLoadFactor.intValue() < 1) {
+                throw new BuildException(
+                        "The 'workerLoadFactor' must be greater or equal 1");
+            }
+            isLBMode = false;
+        } else {
+            throw new BuildException(
+                    "Only 'lb' and 'worker' supported as workerType attribute");
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ant/JMXGetTask.java b/container/catalina/src/share/org/apache/catalina/ant/JMXGetTask.java
new file mode 100644
index 0000000..376a1c7
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/JMXGetTask.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the JMX Get command (<code>/jmxproxy/?get</code>)
+ * supported by the Tomcat manager application.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$
+ */
+public class JMXGetTask extends AbstractCatalinaTask {
+
+    // Properties
+
+    /**
+     * The full bean name
+     */
+    protected String bean      = null;
+
+    /**
+     * The attribute you wish to alter
+     */
+    protected String attribute = null;
+
+    // Public Methods
+    
+    /**
+     * Get method for the bean name
+     * @return Bean name
+     */
+    public String getBean () {
+        return this.bean;
+    }
+
+    /**
+     * Set method for the bean name
+     * @param bean Bean name
+     */
+    public void setBean (String bean) {
+        this.bean = bean;
+    }
+
+    /**
+     * Get method for the attribute name
+     * @return Attribute name
+     */
+    public String getAttribute () {
+        return this.attribute;
+    }
+
+    /**
+     * Set method for the attribute name
+     * @param attribute Attribute name
+     */
+    public void setAttribute (String attribute) {
+        this.attribute = attribute;
+    }
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        super.execute();
+        if (bean == null || attribute == null) {
+            throw new BuildException
+                ("Must specify 'bean' and 'attribute' attributes");
+        }
+        log("Getting attribute " + attribute +
+                " in bean " + bean ); 
+        execute("/jmxproxy/?get=" + bean 
+                + "&att=" + attribute );
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/JMXQueryTask.java b/container/catalina/src/share/org/apache/catalina/ant/JMXQueryTask.java
new file mode 100644
index 0000000..69214c1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/JMXQueryTask.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the JMX Query command 
+ * (<code>/jmxproxy/?qry</code>) supported by the Tomcat manager application.
+ *
+ * @author Vivek Chopra
+ * @version $Revision$
+ */
+public class JMXQueryTask extends AbstractCatalinaTask {
+
+    // Properties
+
+    /**
+     * The JMX query string 
+     * @see #setQuery(String)
+     */
+    protected String query      = null;
+
+    // Public Methods
+    
+    /**
+     * Get method for the JMX query string
+     * @return Query string
+     */
+    public String getQuery () {
+        return this.query;
+    }
+
+    /**
+     * Set method for the JMX query string.
+    * <P>Examples of query format:
+     * <UL>
+     * <LI>*:*</LI>
+     * <LI>*:type=RequestProcessor,*</LI>
+     * <LI>*:j2eeType=Servlet,*</LI>
+     * <LI>Catalina:type=Environment,resourcetype=Global,name=simpleValue</LI>
+     * </UL>
+     * </P> 
+     * @param query JMX Query string
+     */
+    public void setQuery (String query) {
+        this.query = query;
+    }
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        super.execute();
+        String queryString = (query == null) ? "":("?qry="+query);
+        log("Query string is " + queryString); 
+        execute ("/jmxproxy/" + queryString);
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/JMXSetTask.java b/container/catalina/src/share/org/apache/catalina/ant/JMXSetTask.java
new file mode 100644
index 0000000..a0c3ffe
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/JMXSetTask.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the JMX Set command (<code>/jmxproxy/?set</code>)
+ * supported by the Tomcat manager application.
+ *
+ * @author Vivek Chopra
+ * @version $Revision$
+ */
+public class JMXSetTask extends AbstractCatalinaTask {
+
+    // Properties
+
+    /**
+     * The full bean name
+     */
+    protected String bean      = null;
+
+    /**
+     * The attribute you wish to alter
+     */
+    protected String attribute = null;
+
+    /**
+     * The new value for the attribute
+     */
+    protected String value     = null;
+
+    // Public Methods
+    
+    /**
+     * Get method for the bean name
+     * @return Bean name
+     */
+    public String getBean () {
+        return this.bean;
+    }
+
+    /**
+     * Set method for the bean name
+     * @param bean Bean name
+     */
+    public void setBean (String bean) {
+        this.bean = bean;
+    }
+
+    /**
+     * Get method for the attribute name
+     * @return Attribute name
+     */
+    public String getAttribute () {
+        return this.attribute;
+    }
+
+    /**
+     * Set method for the attribute name
+     * @param attribute Attribute name
+     */
+    public void setAttribute (String attribute) {
+        this.attribute = attribute;
+    }
+
+    /**
+     * Get method for the attribute value
+     * @return Attribute value
+     */
+    public String getValue () {
+        return this.value;
+    }
+
+    /**
+     * Set method for the attribute value.
+     * @param value Attribute value
+     */
+    public void setValue (String value) {
+        this.value = value;
+    }
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        super.execute();
+        if (bean == null || attribute == null || value == null) {
+            throw new BuildException
+                ("Must specify 'bean', 'attribute' and 'value' attributes");
+        }
+        log("Setting attribute " + attribute +
+                            " in bean " + bean +
+                            " to " + value); 
+        execute("/jmxproxy/?set=" + bean 
+                + "&att=" + attribute 
+                + "&val=" + value);
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/ListTask.java b/container/catalina/src/share/org/apache/catalina/ant/ListTask.java
new file mode 100644
index 0000000..6f4c1dc
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/ListTask.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/list</code> command, supported by the
+ * Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+public class ListTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        execute("/list");
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/ReloadTask.java b/container/catalina/src/share/org/apache/catalina/ant/ReloadTask.java
new file mode 100644
index 0000000..8cf0721
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/ReloadTask.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/reload</code> command, supported by the
+ * Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+public class ReloadTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The context path of the web application we are managing.
+     */
+    protected String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        if (path == null) {
+            throw new BuildException
+                ("Must specify 'path' attribute");
+        }
+        try {
+            execute("/reload?path=" + URLEncoder.encode(this.path, getCharset()));
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException
+                ("Invalid 'charset' attribute: " + getCharset());
+        }
+
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/RemoveTask.java b/container/catalina/src/share/org/apache/catalina/ant/RemoveTask.java
new file mode 100644
index 0000000..3ba0987
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/RemoveTask.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/remove</code> command, supported by the
+ * Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @deprecated Replaced by UndeployTask
+ */
+public class RemoveTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The context path of the web application we are managing.
+     */
+    protected String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        if (path == null) {
+            throw new BuildException
+                ("Must specify 'path' attribute");
+        }
+        execute("/remove?path=" + URLEncoder.encode(this.path));
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/ResourcesTask.java b/container/catalina/src/share/org/apache/catalina/ant/ResourcesTask.java
new file mode 100644
index 0000000..11522ef
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/ResourcesTask.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/resources</code> command, supported by
+ * the Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+public class ResourcesTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The fully qualified class name of the resource type being requested
+     * (if any).
+     */
+    protected String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        if (type != null) {
+            execute("/resources?type=" + type);
+        } else {
+            execute("/resources");
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/RolesTask.java b/container/catalina/src/share/org/apache/catalina/ant/RolesTask.java
new file mode 100644
index 0000000..07e340c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/RolesTask.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/roles</code> command, supported by the
+ * Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+public class RolesTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        execute("/roles");
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/ServerinfoTask.java b/container/catalina/src/share/org/apache/catalina/ant/ServerinfoTask.java
new file mode 100644
index 0000000..6d41c8c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/ServerinfoTask.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/serverinfo</code> command
+ * supported by the Tomcat manager application.
+ *
+ * @author Vivek Chopra
+ * @version $Revision$ $Date$
+ */
+public class ServerinfoTask extends AbstractCatalinaTask {
+
+    // Public Methods
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        execute("/serverinfo");
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/SessionsTask.java b/container/catalina/src/share/org/apache/catalina/ant/SessionsTask.java
new file mode 100644
index 0000000..55f6637
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/SessionsTask.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/sessions</code> command
+ * supported by the Tomcat manager application.
+ *
+ * @author Vivek Chopra
+ * @version $Revision$
+ */
+public class SessionsTask extends AbstractCatalinaTask {
+
+    // Properties
+
+    /**
+     * The context path of the web application we are managing.
+     */
+    protected String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    // Public Methods
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        if (path == null) {
+            throw new BuildException
+                ("Must specify 'path' attribute");
+        }
+        
+        try {
+            execute("/sessions?path=" + URLEncoder.encode(this.path, getCharset()));
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException
+                ("Invalid 'charset' attribute: " + getCharset());
+        }
+        
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/StartTask.java b/container/catalina/src/share/org/apache/catalina/ant/StartTask.java
new file mode 100644
index 0000000..5599089
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/StartTask.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/start</code> command, supported by the
+ * Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+public class StartTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The context path of the web application we are managing.
+     */
+    protected String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        if (path == null) {
+            throw new BuildException
+                ("Must specify 'path' attribute");
+        }
+        try {
+            execute("/start?path=" + URLEncoder.encode(this.path, getCharset()));
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException
+                ("Invalid 'charset' attribute: " + getCharset());
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/StopTask.java b/container/catalina/src/share/org/apache/catalina/ant/StopTask.java
new file mode 100644
index 0000000..45d40e4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/StopTask.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/stop</code> command, supported by the
+ * Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+public class StopTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The context path of the web application we are managing.
+     */
+    protected String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        if (path == null) {
+            throw new BuildException
+                ("Must specify 'path' attribute");
+        }
+        try {
+            execute("/stop?path=" + URLEncoder.encode(this.path, getCharset()));
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException
+                ("Invalid 'charset' attribute: " + getCharset());
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/UndeployTask.java b/container/catalina/src/share/org/apache/catalina/ant/UndeployTask.java
new file mode 100644
index 0000000..f57b818
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/UndeployTask.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Ant task that implements the <code>/undeploy</code> command, supported by
+ * the Tomcat manager application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+public class UndeployTask extends AbstractCatalinaTask {
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * The context path of the web application we are managing.
+     */
+    protected String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested operation.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        if (path == null) {
+            throw new BuildException
+                ("Must specify 'path' attribute");
+        }
+
+        execute("/undeploy?path=" + this.path);
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/ValidatorTask.java b/container/catalina/src/share/org/apache/catalina/ant/ValidatorTask.java
new file mode 100644
index 0000000..8e5f4ca
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/ValidatorTask.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant;
+
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+import org.apache.catalina.startup.Constants;
+import org.apache.catalina.startup.DigesterFactory;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tools.ant.BuildException;
+import org.xml.sax.InputSource;
+
+
+/**
+ * Task for validating a web application deployment descriptor, using XML 
+ * schema validation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ * @since 5.0
+ */
+
+public class ValidatorTask extends BaseRedirectorHelperTask {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The path to the webapp directory.
+     */
+    protected String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the specified command.  This logic only performs the common
+     * attribute validation required by all subclasses; it does not perform
+     * any functional logic directly.
+     *
+     * @exception BuildException if a validation error occurs
+     */
+    public void execute() throws BuildException {
+
+        if (path == null) {
+            throw new BuildException("Must specify 'path'");
+        }
+
+        File file = new File(path, Constants.ApplicationWebXml);
+        if ((!file.exists()) || (!file.canRead())) {
+            throw new BuildException("Cannot find web.xml");
+        }
+
+        // Commons-logging likes having the context classloader set
+        ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader
+            (ValidatorTask.class.getClassLoader());
+
+        Digester digester = DigesterFactory.newDigester(true, true, null);
+        try {
+            file = file.getCanonicalFile();
+            InputStream stream = 
+                new BufferedInputStream(new FileInputStream(file));
+            InputSource is = new InputSource(file.toURL().toExternalForm());
+            is.setByteStream(stream);
+            digester.parse(is);
+            handleOutput("web.xml validated");
+        } catch (Throwable t) {
+            if (isFailOnError()) {
+                throw new BuildException("Validation failure", t);
+            } else {
+                handleErrorOutput("Validation failure: " + t);
+            }
+        } finally {
+            Thread.currentThread().setContextClassLoader(oldCL);
+            closeRedirector();
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/antlib.xml b/container/catalina/src/share/org/apache/catalina/ant/antlib.xml
new file mode 100644
index 0000000..942cd52
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/antlib.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<antlib>
+  <typedef
+        name="list"
+        classname="org.apache.catalina.ant.ListTask" />
+  <typedef
+        name="deploy"
+        classname="org.apache.catalina.ant.DeployTask" />
+  <typedef
+        name="start"
+        classname="org.apache.catalina.ant.StartTask" />
+  <typedef
+        name="reload"
+        classname="org.apache.catalina.ant.ReloadTask" />
+  <typedef
+        name="stop"
+        classname="org.apache.catalina.ant.StopTask" />        
+  <typedef
+        name="undeploy"
+        classname="org.apache.catalina.ant.UndeployTask" />
+  <typedef
+        name="roles"
+        classname="org.apache.catalina.ant.RolesTask" />
+  <typedef
+        name="resources"
+        classname="org.apache.catalina.ant.ResourcesTask" />
+  <typedef
+        name="sessions"
+        classname="org.apache.catalina.ant.SessionsTask" />
+  <typedef
+        name="jkupdate"
+        classname="org.apache.catalina.ant.JkStatusUpdateTask" />
+  <typedef
+        name="jmxManagerSet"
+        classname="org.apache.catalina.ant.JMXSetTask" />
+  <typedef
+        name="jmxManagerGet"
+        classname="org.apache.catalina.ant.JMXGetTask" />
+  <typedef
+        name="jmxManagerQuery"
+        classname="org.apache.catalina.ant.JMXQueryTask" />
+</antlib>
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ant/catalina.tasks b/container/catalina/src/share/org/apache/catalina/ant/catalina.tasks
new file mode 100644
index 0000000..e46f153
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/catalina.tasks
@@ -0,0 +1,22 @@
+# Pure catalina tasks
+deploy=org.apache.catalina.ant.DeployTask
+list=org.apache.catalina.ant.ListTask
+reload=org.apache.catalina.ant.ReloadTask
+sessions=org.apache.catalina.ant.SessionsTask
+resources=org.apache.catalina.ant.ResourcesTask
+roles=org.apache.catalina.ant.RolesTask
+start=org.apache.catalina.ant.StartTask
+stop=org.apache.catalina.ant.StopTask
+undeploy=org.apache.catalina.ant.UndeployTask
+validator=org.apache.catalina.ant.ValidatorTask
+
+#Jk Task
+jkstatus=org.apache.catalina.ant.JKStatusUpdateTask
+
+# Manager JMX
+jmxManagerSet=org.apache.catalina.ant.JMXSetTask
+jmxManagerGet=org.apache.catalina.ant.JMXGetTask
+jmxManagerQuery=org.apache.catalina.ant.JMXQueryTask
+
+# Jasper tasks
+jasper2=org.apache.jasper.JspC
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/Arg.java b/container/catalina/src/share/org/apache/catalina/ant/jmx/Arg.java
new file mode 100644
index 0000000..825dd9a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/Arg.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ant.jmx;
+
+/**
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ */
+public class Arg {
+    String type;
+    String value;
+
+    public void setType( String type) {
+        this.type=type;
+    }
+    public void setValue( String value ) {
+        this.value=value;
+    }
+    public void addText( String text ) {
+        this.value=text;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String getType() {
+        return type;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorCondition.java b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorCondition.java
new file mode 100644
index 0000000..3aef909
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorCondition.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ant.jmx;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+
+/**
+ *
+ * <b>Definition</b>:
+ * <pre> 
+ *   &lt;path id="catalina_ant">
+ *       &lt;fileset dir="${catalina.home}/server/lib">
+ *           &lt;include name="catalina-ant.jar"/>
+ *           &lt;include name="catalina-ant-jmx.jar"/>
+ *       &lt;/fileset>
+ *   &lt;/path>
+ *
+ *   &lt;typedef
+ *       name="jmxCondition"
+ *       classname="org.apache.catalina.ant.jmx.JMXAccessorCondition"
+ *       classpathref="catalina_ant"/>
+ *   &lt;taskdef
+ *       name="jmxOpen"
+ *       classname="org.apache.catalina.ant.jmx.JMXAccessorTask"
+ *       classpathref="catalina_ant"/>
+ * </pre>
+ * 
+ * <b>Usage</b>: Wait for start backup node
+ * <pre>
+ *     &lt;target name="wait"&gt;
+ *       &lt;jmxOpen
+ *               host="${jmx.host}" port="${jmx.port}" username="${jmx.username}" password="${jmx.password}" /&gt;
+ *        &lt;waitfor maxwait="${maxwait}" maxwaitunit="second" timeoutproperty="server.timeout" &gt;
+ *           &lt;and&gt;
+ *               &lt;socket server="${server.name}" port="${server.port}"/&gt;
+ *               &lt;http url="${url}"/&gt;
+ *               &lt;jmxCondition
+ *                   name="Catalina:type=IDataSender,host=localhost,senderAddress=192.168.111.1,senderPort=9025"
+ *                   operation="==" 
+ *                   attribute="connected" value="true"
+ *               /&gt;
+ *               &lt;jmxCondition
+ *                   operation="&amp;lt;"
+ *                   name="Catalina:j2eeType=WebModule,name=//${tomcat.application.host}${tomcat.application.path},J2EEApplication=none,J2EEServer=none"
+ *                   attribute="startupTime" value="250"
+ *               /&gt;
+ *           &lt;/and&gt;
+ *       &lt;/waitfor&gt;
+ *       &lt;fail if="server.timeout" message="Server ${url} don't answer inside ${maxwait} sec" /&gt;
+ *       &lt;echo message="Server ${url} alive" /&gt;
+ *   &lt;/target&gt;
+ *
+ * </pre>
+ * Allowed operation between jmx attribute and reference value:
+ * <ul>
+ * <li>==  equals</li>
+ * <li>!=  not equals</li>
+ * <li>&gt; greater than (&amp;gt;)</li>
+ * <li>&gt;= greater than or equals (&amp;gt;=)</li>
+ * <li>&lt; lesser than (&amp;lt;)</li>
+ * <li>&lt;= lesser than or equals (&amp;lt;=)</li>
+ * </ul> 
+ * <b>NOTE</b>:  For numeric expressions the type must be set and use xml entities as operations.<br/>
+ * As type we currently support <em>long</em> and <em>double</em>.
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ *
+ */
+public class JMXAccessorCondition extends ProjectComponent implements Condition {
+
+    // ----------------------------------------------------- Instance Variables
+
+    private String url = null;
+    private String host = "localhost";
+    private String port = "8050";
+    private String password = null;
+    private String username = null;
+    private String name = null;
+    private String attribute;
+    private String value;
+    private String operation = "==" ;
+    private String type = "long" ;
+    private String ref = "jmx.server";
+    private String unlessCondition;
+    private String ifCondition;
+     
+    // ----------------------------------------------------- Instance Info
+
+    /**
+     * Descriptive information describing this implementation.
+     */
+    private static final String info = "org.apache.catalina.ant.JMXAccessorCondition/1.1";
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+    // ----------------------------------------------------- Properties
+
+    /**
+     * @return Returns the operation.
+     */
+    public String getOperation() {
+        return operation;
+    }
+    /**
+     * @param operation The operation to set.
+     */
+    public void setOperation(String operation) {
+        this.operation = operation;
+    }
+
+    /**
+     * @return Returns the type.
+     */
+    public String getType() {
+        return type;
+    }
+    /**
+     * @param type The type to set.
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+    /**
+     * @return Returns the attribute.
+     */
+    public String getAttribute() {
+        return attribute;
+    }
+    /**
+     * @param attribute The attribute to set.
+     */
+    public void setAttribute(String attribute) {
+        this.attribute = attribute;
+    }
+    /**
+     * @return Returns the host.
+     */
+    public String getHost() {
+        return host;
+    }
+    /**
+     * @param host The host to set.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }
+    /**
+     * @return Returns the name.
+     */
+    public String getName() {
+        return name;
+    }
+    /**
+     * @param objectName The name to set.
+     */
+    public void setName(String objectName) {
+        this.name = objectName;
+    }
+    /**
+     * @return Returns the password.
+     */
+    public String getPassword() {
+        return password;
+    }
+    /**
+     * @param password The password to set.
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+    /**
+     * @return Returns the port.
+     */
+    public String getPort() {
+        return port;
+    }
+    /**
+     * @param port The port to set.
+     */
+    public void setPort(String port) {
+        this.port = port;
+    }
+    /**
+     * @return Returns the url.
+     */
+    public String getUrl() {
+        return url;
+    }
+    /**
+     * @param url The url to set.
+     */
+    public void setUrl(String url) {
+        this.url = url;
+    }
+    /**
+     * @return Returns the username.
+     */
+    public String getUsername() {
+        return username;
+    }
+    /**
+     * @param username The username to set.
+     */
+    public void setUsername(String username) {
+        this.username = username;
+    }
+    /**
+     * @return Returns the value.
+     */
+    public String getValue() {
+        return value;
+    }
+    // The setter for the "value" attribute
+    public void setValue(String value) {
+        this.value = value;
+    }
+ 
+    /**
+     * @return Returns the ref.
+     */
+    public String getRef() {
+        return ref;
+    }
+    /**
+     * @param refId The ref to set.
+     */
+    public void setRef(String refId) {
+        this.ref = refId;
+    }
+    /**
+     * @return Returns the ifCondition.
+     */
+    public String getIf() {
+        return ifCondition;
+    }
+    /**
+     * Only execute if a property of the given name exists in the current project.
+     * @param c property name
+     */
+    public void setIf(String c) {
+        ifCondition = c;
+    }
+   /**
+     * @return Returns the unlessCondition.
+     */
+    public String getUnless() {
+        return unlessCondition;
+    }
+ 
+    /**
+     * Only execute if a property of the given name does not
+     * exist in the current project.
+     * @param c property name
+     */
+    public void setUnless(String c) {
+        unlessCondition = c;
+    }
+
+    /**
+     * Get JMXConnection (default look at <em>jmx.server</em> project reference from jmxOpen Task)
+     * @return active JMXConnection
+     * @throws MalformedURLException
+     * @throws IOException
+     */
+    protected MBeanServerConnection getJMXConnection()
+            throws MalformedURLException, IOException {
+        return JMXAccessorTask.accessJMXConnection(
+                getProject(),
+                getUrl(), getHost(),
+                getPort(), getUsername(), getPassword(), ref);
+    }
+
+    /**
+     * Get value from MBeans attribute 
+     * @return The value
+     */
+    protected String accessJMXValue() {
+        try {
+            Object result = getJMXConnection().getAttribute(
+                    new ObjectName(name), attribute);
+            if(result != null)
+                return result.toString();
+        } catch (Exception e) {
+            // ignore access or connection open errors
+        }
+        return null;
+    }
+
+    /**
+     * test the if condition
+     * @return true if there is no if condition, or the named property exists
+     */
+    protected boolean testIfCondition() {
+        if (ifCondition == null || "".equals(ifCondition)) {
+            return true;
+        }
+        return getProject().getProperty(ifCondition) != null;
+    }
+
+    /**
+     * test the unless condition
+     * @return true if there is no unless condition,
+     *  or there is a named property but it doesn't exist
+     */
+    protected boolean testUnlessCondition() {
+        if (unlessCondition == null || "".equals(unlessCondition)) {
+            return true;
+        }
+        return getProject().getProperty(unlessCondition) == null;
+    }
+
+    /**
+     * This method evaluates the condition
+     * It support for operation ">,>=,<,<=" the types <code>long</code> and <code>double</code>.
+     * @return expression <em>jmxValue</em> <em>operation</em> <em>value</em>
+     */
+    public boolean eval() {
+        if (operation == null) {
+            throw new BuildException("operation attribute is not set");
+        }
+        if (value == null) {
+            throw new BuildException("value attribute is not set");
+        }
+        if ((name == null || attribute == null)) {
+            throw new BuildException(
+                    "Must specify a 'attribute', name for equals condition");
+        }
+        if (testIfCondition() && testUnlessCondition()) {
+            String jmxValue = accessJMXValue();
+            if (jmxValue != null) {
+                String op = getOperation();
+                if ("==".equals(op)) {
+                    return jmxValue.equals(value);
+                } else if ("!=".equals(op)) {
+                    return !jmxValue.equals(value);
+                } else {
+                    if ("long".equals(type)) {
+                        long jvalue = Long.parseLong(jmxValue);
+                        long lvalue = Long.parseLong(value);
+                        if (">".equals(op)) {
+                            return jvalue > lvalue;
+                        } else if (">=".equals(op)) {
+                            return jvalue >= lvalue;
+                        } else if ("<".equals(op)) {
+                            return jvalue < lvalue;
+                        } else if ("<=".equals(op)) {
+                            return jvalue <= lvalue;
+                        }
+                    } else if ("double".equals(type)) {
+                        double jvalue = Double.parseDouble(jmxValue);
+                        double dvalue = Double.parseDouble(value);
+                        if (">".equals(op)) {
+                            return jvalue > dvalue;
+                        } else if (">=".equals(op)) {
+                            return jvalue >= dvalue;
+                        } else if ("<".equals(op)) {
+                            return jvalue < dvalue;
+                        } else if ("<=".equals(op)) {
+                            return jvalue <= dvalue;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+        return true;
+    }
+ }
+
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorEqualsCondition.java b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorEqualsCondition.java
new file mode 100644
index 0000000..2fa049d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorEqualsCondition.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ant.jmx;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+
+/**
+ *
+ * Definition
+ * <pre> 
+ *   &lt;path id="catalina_ant">
+ *       &lt;fileset dir="${catalina.home}/server/lib">
+ *           &lt;include name="catalina-ant.jar"/>
+ *           &lt;include name="catalina-ant-jmx.jar"/>
+ *       &lt;/fileset>
+ *   &lt;/path>
+ *
+ *   &lt;typedef
+ *       name="jmxEquals"
+ *       classname="org.apache.catalina.ant.jmx.JMXAccessorEqualsCondition"
+ *       classpathref="catalina_ant"/>
+ * </pre>
+ * 
+ * usage: Wait for start backup node
+ * <pre>
+ *     &lt;target name="wait"&gt;
+ *        &lt;waitfor maxwait="${maxwait}" maxwaitunit="second" timeoutproperty="server.timeout" &gt;
+ *           &lt;and&gt;
+ *               &lt;socket server="${server.name}" port="${server.port}"/&gt;
+ *               &lt;http url="${url}"/&gt;
+ *               &lt;jmxEquals 
+ *                   host="localhost" port="9014" username="controlRole" password="tomcat"
+ *                   name="Catalina:type=IDataSender,host=localhost,senderAddress=192.168.111.1,senderPort=9025"
+ *                   attribute="connected" value="true"
+ *               /&gt;
+ *           &lt;/and&gt;
+ *       &lt;/waitfor&gt;
+ *       &lt;fail if="server.timeout" message="Server ${url} don't answer inside ${maxwait} sec" /&gt;
+ *       &lt;echo message="Server ${url} alive" /&gt;
+ *   &lt;/target&gt;
+ *
+ * </pre>
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ *
+ */
+public class JMXAccessorEqualsCondition  extends ProjectComponent  implements Condition {
+
+    // ----------------------------------------------------- Instance Variables
+
+    private String url = null;
+    private String host = "localhost";
+    private String port = "8050";
+    private String password = null;
+    private String username = null;
+    private String name = null;
+    private String attribute;
+    private String value;
+    private String ref = "jmx.server" ;
+    // ----------------------------------------------------- Instance Info
+
+    /**
+     * Descriptive information describing this implementation.
+     */
+    private static final String info = "org.apache.catalina.ant.JMXAccessorEqualsCondition/1.1";
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+    // ----------------------------------------------------- Properties
+
+    /**
+     * @return Returns the attribute.
+     */
+    public String getAttribute() {
+        return attribute;
+    }
+    /**
+     * @param attribute The attribute to set.
+     */
+    public void setAttribute(String attribute) {
+        this.attribute = attribute;
+    }
+    /**
+     * @return Returns the host.
+     */
+    public String getHost() {
+        return host;
+    }
+    /**
+     * @param host The host to set.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }
+    /**
+     * @return Returns the name.
+     */
+    public String getName() {
+        return name;
+    }
+    /**
+     * @param objectName The name to set.
+     */
+    public void setName(String objectName) {
+        this.name = objectName;
+    }
+    /**
+     * @return Returns the password.
+     */
+    public String getPassword() {
+        return password;
+    }
+    /**
+     * @param password The password to set.
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+    /**
+     * @return Returns the port.
+     */
+    public String getPort() {
+        return port;
+    }
+    /**
+     * @param port The port to set.
+     */
+    public void setPort(String port) {
+        this.port = port;
+    }
+    /**
+     * @return Returns the url.
+     */
+    public String getUrl() {
+        return url;
+    }
+    /**
+     * @param url The url to set.
+     */
+    public void setUrl(String url) {
+        this.url = url;
+    }
+    /**
+     * @return Returns the username.
+     */
+    public String getUsername() {
+        return username;
+    }
+    /**
+     * @param username The username to set.
+     */
+    public void setUsername(String username) {
+        this.username = username;
+    }
+    /**
+     * @return Returns the value.
+     */
+    public String getValue() {
+        return value;
+    }
+    // The setter for the "value" attribute
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    /**
+     * @return Returns the ref.
+     */
+    public String getRef() {
+        return ref;
+    }
+    /**
+     * @param refId The ref to set.
+     */
+    public void setRef(String refId) {
+        this.ref = refId;
+    }
+    
+    protected MBeanServerConnection getJMXConnection()
+            throws MalformedURLException, IOException {
+        return JMXAccessorTask.accessJMXConnection(
+                getProject(),
+                getUrl(), getHost(),
+                getPort(), getUsername(), getPassword(), ref);
+    }
+
+    /**
+     * @return The value
+     */
+    protected String accessJMXValue() {
+        try {
+            Object result = getJMXConnection().getAttribute(
+                    new ObjectName(name), attribute);
+            if(result != null)
+                return result.toString();
+        } catch (Exception e) {
+            // ignore access or connection open errors
+        }
+        return null;
+    }
+
+    // This method evaluates the condition
+    public boolean eval() {
+        if (value == null) {
+            throw new BuildException("value attribute is not set");
+        }
+        if ((name == null || attribute == null)) {
+            throw new BuildException(
+                    "Must specify a 'attribute', name for equals condition");
+        }
+        //FIXME check url or host/parameter
+        String jmxValue = accessJMXValue();
+        if(jmxValue != null)
+            return jmxValue.equals(value);
+        return false;
+    }
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java
new file mode 100644
index 0000000..3feb46b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant.jmx;
+
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Access <em>JMX</em> JSR 160 MBeans Server. 
+ * <ul>
+ * <li>Get Mbeans attributes</li>
+ * <li>Show Get result as Ant console log</li>
+ * <li>Bind Get result as Ant properties</li>
+ * </ul>
+ * <p>
+ * Examples:
+ * <br/>
+ * Get a Mbean IDataSender attribute nrOfRequests and create a new ant property <em>IDataSender.9025.nrOfRequests</em> 
+ * <pre>
+ *   &lt;jmx:get
+ *           ref="jmx.server"
+ *           name="Catalina:type=IDataSender,host=localhost,senderAddress=192.168.1.2,senderPort=9025" 
+ *           attribute="nrOfRequests"
+ *           resultproperty="IDataSender.9025.nrOfRequests"
+ *           echo="false"&gt;
+ *       /&gt;
+ * </pre>
+ * </p>
+ * <p>
+ * First call to a remote MBeanserver save the JMXConnection a referenz <em>jmx.server</em>
+ * </p>
+ * These tasks require Ant 1.6 or later interface.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ */
+
+public class JMXAccessorGetTask extends JMXAccessorTask {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    private String attribute;
+
+    // ----------------------------------------------------- Instance Info
+
+    /**
+     * Descriptive information describing this implementation.
+     */
+    private static final String info = "org.apache.catalina.ant.JMXAccessorGetTask/1.0";
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    // ------------------------------------------------------------- Properties
+    
+    /**
+     * @return Returns the attribute.
+     */
+    public String getAttribute() {
+        return attribute;
+    }
+    
+    /**
+     * @param attribute The attribute to set.
+     */
+    public void setAttribute(String attribute) {
+        this.attribute = attribute;
+    }
+    
+  
+    // ------------------------------------------------------ protected Methods
+    
+    /**
+     * Execute the specified command, based on the configured properties. The
+     * input stream will be closed upon completion of this task, whether it was
+     * executed successfully or not.
+     * 
+     * @exception BuildException
+     *                if an error occurs
+     */
+    public String jmxExecute(MBeanServerConnection jmxServerConnection)
+        throws Exception {
+
+        if (getName() == null) {
+            throw new BuildException("Must specify a 'name'");
+        }
+        if ((attribute == null)) {
+            throw new BuildException(
+                    "Must specify a 'attribute' for get");
+        }
+        return  jmxGet(jmxServerConnection, getName());
+     }
+
+
+    /**
+     * @param jmxServerConnection
+     * @param name
+     * @return The value of the given named attribute
+     * @throws Exception
+     */
+    protected String jmxGet(MBeanServerConnection jmxServerConnection,String name) throws Exception {
+        String error = null;
+        if(isEcho()) {
+            handleOutput("MBean " + name + " get attribute " + attribute );
+        }
+        Object result = jmxServerConnection.getAttribute(
+                new ObjectName(name), attribute);
+        if (result != null) {
+            echoResult(attribute,result);
+            createProperty(result);
+        } else
+            error = "Attribute " + attribute + " is empty";
+        return error;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java
new file mode 100644
index 0000000..bfaae8a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant.jmx;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Access <em>JMX</em> JSR 160 MBeans Server. 
+ * <ul>
+ * <li>open more then one JSR 160 rmi connection</li>
+ * <li>Get/Set Mbeans attributes</li>
+ * <li>Call Mbean Operation with arguments</li>
+ * <li>Argument values can be converted from string to int,long,float,double,boolean,ObjectName or InetAddress </li>
+ * <li>Query Mbeans</li>
+ * <li>Show Get, Call, Query result at Ant console log</li>
+ * <li>Bind Get, Call, Query result at Ant properties</li>
+ * </ul>
+ *
+ * Examples:
+ * <ul>
+ * <li>
+ * Get a session attribute hello from session with ref <em>${sessionid.0}</em> form 
+ * app <em>Catalina:type=Manager,path=/ClusterTest,host=localhost</em> 
+ * <pre>
+ *   &lt;jmx:invoke
+ *           name="Catalina:type=Manager,path=/ClusterTest,host=localhost" 
+ *           operation="getSessionAttribute"
+ *           resultproperty="hello"&gt;
+ *         &lt;arg value="${sessionid.0}"/&gt;
+ *         &lt;arg value="Hello"/&gt;
+ *   &lt;/jmx:invoke&gt;
+ * </pre>
+ * </li>
+ * <li>
+ * Create new AccessLogger at localhost 
+ * <code>
+ *   &lt;jmx:invoke
+ *           name="Catalina:type=MBeanFactory" 
+ *           operation="createAcccesLoggerValve"
+ *           resultproperty="acccesLoggerObjectName"
+ *       &gt;
+ *         &lt;arg value="Catalina:type=Host,host=localhost"/&gt;
+ *   &lt;/jmx:invoke&gt;
+ *
+ * </code>
+ * </li>
+ * <li>
+ * Remove existing AccessLogger at localhost 
+ * <code>
+ *   &lt;jmx:invoke
+ *           name="Catalina:type=MBeanFactory" 
+ *           operation="removeValve"
+ *       &gt;
+ *         &lt;arg value="Catalina:type=Valve,name=AccessLogValve,host=localhost"/&gt;
+ *   &lt;/jmx:invoke&gt;
+ *
+ * </code>
+ * </li>
+ * </ul>
+ * <p>
+ * First call to a remote MBeanserver save the JMXConnection a referenz <em>jmx.server</em>
+ * </p>
+ * These tasks require Ant 1.6 or later interface.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ */
+
+public class JMXAccessorInvokeTask extends JMXAccessorTask {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    private String operation ;
+    private List args=new ArrayList();
+
+    // ----------------------------------------------------- Instance Info
+
+    /**
+     * Descriptive information describing this implementation.
+     */
+    private static final String info = "org.apache.catalina.ant.JMXAccessorInvokeTask/1.0";
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    // ------------------------------------------------------------- Properties
+    
+    /**
+     * @return Returns the operation.
+     */
+    public String getOperation() {
+        return operation;
+    }
+    /**
+     * @param operation The operation to set.
+     */
+    public void setOperation(String operation) {
+        this.operation = operation;
+    }
+
+    public void addArg(Arg arg ) {
+        args.add(arg);
+    }
+
+    /**
+     * @return Returns the args.
+     */
+    public List getArgs() {
+        return args;
+    }
+    /**
+     * @param args The args to set.
+     */
+    public void setArgs(List args) {
+        this.args = args;
+    }
+    
+    // ------------------------------------------------------ protected Methods
+    
+    /**
+     * Execute the specified command, based on the configured properties. The
+     * input stream will be closed upon completion of this task, whether it was
+     * executed successfully or not.
+     * 
+     * @exception BuildException
+     *                if an error occurs
+     */
+    public String jmxExecute(MBeanServerConnection jmxServerConnection)
+        throws Exception {
+
+        if (getName() == null) {
+            throw new BuildException("Must specify a 'name'");
+        }
+        if ((operation == null)) {
+            throw new BuildException(
+                    "Must specify a 'operation' for call");
+        }
+        return  jmxInvoke(jmxServerConnection, getName());
+     }
+
+    /**
+     * @param jmxServerConnection
+     * @throws Exception
+     */
+    protected String jmxInvoke(MBeanServerConnection jmxServerConnection, String name) throws Exception {
+        Object result ;
+        if (args == null) {
+             result = jmxServerConnection.invoke(new ObjectName(name),
+                    operation, null, null);
+        } else {
+            Object argsA[]=new Object[ args.size()];
+            String sigA[]=new String[args.size()];
+            for( int i=0; i<args.size(); i++ ) {
+                Arg arg=(Arg)args.get(i);
+                if( arg.type==null) {
+                    arg.type="java.lang.String";
+                    sigA[i]=arg.getType();
+                    argsA[i]=arg.getValue();
+                } else {
+                    sigA[i]=arg.getType();
+                    argsA[i]=convertStringToType(arg.getValue(),arg.getType());
+                }                
+            }
+            result = jmxServerConnection.invoke(new ObjectName(name), operation, argsA, sigA);
+        }
+        if(result != null) {
+            echoResult(operation,result);
+            createProperty(result);
+        }
+        return null;
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java
new file mode 100644
index 0000000..ea16b37
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant.jmx;
+
+
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Query for Mbeans. 
+ * <ul>
+ * <li>open no existing JSR 160 rmi jmx connection</li>
+ * <li>Get all Mbeans attributes</li>
+ * <li>Get only the Query Mbeans ObjectNames</li>
+ * <li>Show query result as Ant console log</li>
+ * <li>Bind query result as Ant properties</li>
+ * </ul>
+ * <br/>
+ * Query a list of Mbeans.
+ * <pre>
+ *   &lt;jmxQuery
+ *           host="127.0.0.1"
+ *           port="9014"
+ *           name="Catalina:type=Manager,* 
+ *           resultproperty="manager" /&gt;
+ * </pre>
+ * with attribute <em>attributebinding="true"</em> you can get 
+ * all attributes also from result objects.<br/>
+ * The poperty manager.lenght show the size of the result 
+ * and with manager.[0..lenght].name the 
+ * resulted ObjectNames are saved. 
+ * These tasks require Ant 1.6 or later interface.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ */
+
+public class JMXAccessorQueryTask extends JMXAccessorTask {
+
+    // ----------------------------------------------------- Instance Variables
+
+    private boolean attributebinding = false;
+
+    // ----------------------------------------------------- Instance Info
+
+    /**
+     * Descriptive information describing this implementation.
+     */
+    private static final String info = "org.apache.catalina.ant.JMXAccessorQueryTask/1.0";
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    // ------------------------------------------------------------- Properties
+    
+    /**
+     * @return Returns the attributebinding.
+     */
+    public boolean isAttributebinding() {
+        return attributebinding;
+    }
+    /**
+     * @param attributeBinding The attributebinding to set.
+     */
+    public void setAttributebinding(boolean attributeBinding) {
+        this.attributebinding = attributeBinding;
+    }
+  
+    // ------------------------------------------------------ protected Methods
+
+    
+    /**
+     * Execute the specified command, based on the configured properties. The
+     * input stream will be closed upon completion of this task, whether it was
+     * executed successfully or not.
+     * 
+     * @exception BuildException
+     *                if an error occurs
+     */
+    public String jmxExecute(MBeanServerConnection jmxServerConnection)
+        throws Exception {
+
+        if (getName() == null) {
+            throw new BuildException("Must specify a 'name'");
+        }
+        return jmxQuery(jmxServerConnection, getName());
+
+    }
+
+       
+    /**
+     * Call Mbean server for some mbeans with same domain, attributes.
+     *  with <em>attributebindung=true</em> you can save all attributes from all found objects
+     * as your ant properties
+     * @param jmxServerConnection
+     * @param qry
+     * @return The query result
+     */
+    protected String jmxQuery(MBeanServerConnection jmxServerConnection,
+            String qry) {
+        String isError = null;
+        Set names = null;
+        String resultproperty = getResultproperty();
+        try {
+            names = jmxServerConnection.queryNames(new ObjectName(qry), null);
+            if (resultproperty != null) {
+                setProperty(resultproperty + ".Length",Integer.toString(names.size()));
+            }
+        } catch (Exception e) {
+            if (isEcho())
+                handleErrorOutput(e.getMessage());
+            return "Can't query mbeans " + qry;
+        }
+
+        if (resultproperty != null) {
+            Iterator it = names.iterator();
+            int oindex = 0;
+            String pname = null;
+            while (it.hasNext()) {
+                ObjectName oname = (ObjectName) it.next();
+                pname = resultproperty + "." + Integer.toString(oindex) + ".";
+                oindex++;
+                    setProperty(pname + "Name", oname.toString());
+                    if (isAttributebinding()) {
+                        bindAttributes(jmxServerConnection, resultproperty, pname, oname);
+                
+                    }
+                }
+        }
+        return isError;
+    }
+
+    /**
+     * @param jmxServerConnection
+     * @param resultproperty
+     * @param pname
+     * @param oname
+     */
+    protected void bindAttributes(MBeanServerConnection jmxServerConnection, String resultproperty, String pname, ObjectName oname) {
+        if (jmxServerConnection != null  && resultproperty != null 
+            && pname != null && oname != null ) {
+            try {
+                MBeanInfo minfo = jmxServerConnection.getMBeanInfo(oname);
+                String code = minfo.getClassName();
+                if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) {
+                    code = (String) jmxServerConnection.getAttribute(oname,
+                            "modelerType");
+                }
+                MBeanAttributeInfo attrs[] = minfo.getAttributes();
+                Object value = null;
+
+                for (int i = 0; i < attrs.length; i++) {
+                    if (!attrs[i].isReadable())
+                        continue;
+                    String attName = attrs[i].getName();
+                    if (attName.indexOf("=") >= 0 || attName.indexOf(":") >= 0
+                            || attName.indexOf(" ") >= 0) {
+                        continue;
+                    }
+
+                    try {
+                        value = jmxServerConnection
+                                .getAttribute(oname, attName);
+                    } catch (Throwable t) {
+                        if (isEcho())
+                            handleErrorOutput("Error getting attribute "
+                                    + oname + " " + pname + attName + " "
+                                    + t.toString());
+                        continue;
+                    }
+                    if (value == null)
+                        continue;
+                    if ("modelerType".equals(attName))
+                        continue;
+                    createProperty(pname + attName, value);
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java
new file mode 100644
index 0000000..ed4f1fe
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.ant.jmx;
+
+
+import javax.management.Attribute;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Access <em>JMX</em> JSR 160 MBeans Server. 
+ * <ul>
+ * <li>Get Mbeans attributes</li>
+ * <li>Show Get result as Ant console log</li>
+ * <li>Bind Get result as Ant properties</li>
+ * </ul>
+ * <p>
+ * Examples:
+ * Set a Mbean Manager attribute maxActiveSessions.
+ * Set this attribute with fresh jmx connection without save reference 
+ * <pre>
+ *   &lt;jmx:set
+ *           host="127.0.0.1"
+ *           port="9014"
+ *           ref=""
+ *           name="Catalina:type=Manager,path="/ClusterTest",host=localhost" 
+ *           attribute="maxActiveSessions"
+ *           value="100"
+ *           type="int"
+ *           echo="false"&gt;
+ *       /&gt;
+ * </pre>
+ * </p>
+ * <p>
+ * First call to a remote MBeanserver save the JMXConnection a referenz <em>jmx.server</em>
+ * </p>
+ * These tasks require Ant 1.6 or later interface.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ */
+
+public class JMXAccessorSetTask extends JMXAccessorTask {
+
+    // ----------------------------------------------------- Instance Variables
+
+    private String attribute;
+    private String value;
+    private String type;
+    private boolean convert = false ;
+    
+    // ----------------------------------------------------- Instance Info
+
+    /**
+     * Descriptive information describing this implementation.
+     */
+    private static final String info = "org.apache.catalina.ant.JMXAccessorSetTask/1.0";
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    // ------------------------------------------------------------- Properties
+    
+    /**
+     * @return Returns the attribute.
+     */
+    public String getAttribute() {
+        return attribute;
+    }
+    
+    /**
+     * @param attribute The attribute to set.
+     */
+    public void setAttribute(String attribute) {
+        this.attribute = attribute;
+    }
+    
+    /**
+     * @return Returns the value.
+     */
+    public String getValue() {
+        return value;
+    }
+    /**
+     * @param value The value to set.
+     */
+    public void setValue(String value) {
+        this.value = value;
+    }
+    
+    
+    /**
+     * @return Returns the type.
+     */
+    public String getType() {
+        return type;
+    }
+    
+    /**
+     * @param valueType The type to set.
+     */
+    public void setType(String valueType) {
+        this.type = valueType;
+    }
+ 
+ 
+    /**
+     * @return Returns the convert.
+     */
+    public boolean isConvert() {
+        return convert;
+    }
+    /**
+     * @param convert The convert to set.
+     */
+    public void setConvert(boolean convert) {
+        this.convert = convert;
+    }
+    // ------------------------------------------------------ protected Methods
+    
+    /**
+     * Execute the specified command, based on the configured properties. The
+     * input stream will be closed upon completion of this task, whether it was
+     * executed successfully or not.
+     * 
+     * @exception BuildException
+     *                if an error occurs
+     */
+    public String jmxExecute(MBeanServerConnection jmxServerConnection)
+        throws Exception {
+
+        if (getName() == null) {
+            throw new BuildException("Must specify a 'name'");
+        }
+        if ((attribute == null || value == null)) {
+            throw new BuildException(
+                    "Must specify a 'attribute' and 'value' for set");
+        }
+        return  jmxSet(jmxServerConnection, getName());
+     }
+
+    /**
+     * @param jmxServerConnection
+     * @param name
+     * @throws Exception
+     */
+    protected String jmxSet(MBeanServerConnection jmxServerConnection,
+            String name) throws Exception {
+        Object realValue;
+        if (type != null) {
+            realValue = convertStringToType(value, type);
+        } else {
+            if (isConvert()) {
+                String mType = getMBeanAttributeType(jmxServerConnection, name,
+                        attribute);
+                realValue = convertStringToType(value, mType);
+            } else
+                realValue = value;
+        }
+        jmxServerConnection.setAttribute(new ObjectName(name), new Attribute(
+                attribute, realValue));
+        return null;
+    }
+    
+
+
+    /**
+     * Get MBean Attriute from Mbean Server
+     * @param jmxServerConnection
+     * @param name
+     * @param attribute
+     * @return The type
+     * @throws Exception
+     */
+    protected String getMBeanAttributeType(
+            MBeanServerConnection jmxServerConnection,
+            String name,
+            String attribute) throws Exception {
+        ObjectName oname = new ObjectName(name);
+        String mattrType = null;
+        MBeanInfo minfo = jmxServerConnection.getMBeanInfo(oname);
+        MBeanAttributeInfo attrs[] = minfo.getAttributes();
+        if (attrs != null) {
+            for (int i = 0; mattrType == null && i < attrs.length; i++) {
+                if (attribute.equals(attrs[i].getName()))
+                    mattrType = attrs[i].getType();
+            }
+        }
+        return mattrType;
+    }
+ }
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorTask.java b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorTask.java
new file mode 100644
index 0000000..d59fe3e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/JMXAccessorTask.java
@@ -0,0 +1,731 @@
+/*
+ * Copyright 2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ant.jmx;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+import org.apache.catalina.ant.BaseRedirectorHelperTask;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Access <em>JMX</em> JSR 160 MBeans Server.
+ * <ul>
+ * <li>open more then one JSR 160 rmi connection</li>
+ * <li>Get/Set Mbeans attributes</li>
+ * <li>Call Mbean Operation with arguments</li>
+ * <li>Argument values can be converted from string to
+ * int,long,float,double,boolean,ObjectName or InetAddress</li>
+ * <li>Query Mbeans</li>
+ * <li>Show Get, Call, Query result at Ant console log</li>
+ * <li>Bind Get, Call, Query result at Ant properties</li>
+ * </ul>
+ * 
+ * Examples: open server with reference and autorisation
+ * 
+ * <pre>
+ * 
+ *    &lt;jmxOpen
+ *            host=&quot;127.0.0.1&quot;
+ *            port=&quot;9014&quot;
+ *            username=&quot;monitorRole&quot;
+ *            password=&quot;mysecret&quot;
+ *            ref=&quot;jmx.myserver&quot; 
+ *        /&gt;
+ *  
+ * </pre>
+ * 
+ * All calls after opening with same refid reuse the connection.
+ * <p>
+ * First call to a remote MBeanserver save the JMXConnection a referenz
+ * <em>jmx.server</em>
+ * </p>
+ * All JMXAccessorXXXTask support the attribute <em>if</em> and
+ * <em>unless</em>. With <em>if</em> the task is only execute when property
+ * exist and with <em>unless</em> when property not exists. <br/><b>NOTE
+ * </b>: These tasks require Ant 1.6 or later interface.
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ */
+
+public class JMXAccessorTask extends BaseRedirectorHelperTask {
+
+    // ----------------------------------------------------- Instance Variables
+
+    public static String JMX_SERVICE_PREFIX = "service:jmx:rmi:///jndi/rmi://";
+
+    public static String JMX_SERVICE_SUFFIX = "/jmxrmi";
+
+    private String name = null;
+
+    private String resultproperty;
+
+    private String url = null;
+
+    private String host = "localhost";
+
+    private String port = "8050";
+
+    private String password = null;
+
+    private String username = null;
+
+    private String ref = "jmx.server";
+
+    private boolean echo = false;
+
+    private boolean separatearrayresults = true;
+
+    private String delimiter;
+
+    private String unlessCondition;
+
+    private String ifCondition;
+
+    private Properties properties = new Properties();
+
+    // ----------------------------------------------------- Instance Info
+
+    /**
+     * Descriptive information describing this implementation.
+     */
+    private static final String info = "org.apache.catalina.ant.JMXAccessorTask/1.1";
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * The name used at remote MbeanServer
+     */
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String objectName) {
+        this.name = objectName;
+    }
+
+    /**
+     * @return Returns the resultproperty.
+     */
+    public String getResultproperty() {
+        return resultproperty;
+    }
+
+    /**
+     * @param propertyName The resultproperty to set.
+     */
+    public void setResultproperty(String propertyName) {
+        this.resultproperty = propertyName;
+    }
+
+    /**
+     * @return Returns the delimiter.
+     */
+    public String getDelimiter() {
+        return delimiter;
+    }
+
+    /**
+     * @param separator The delimiter to set.
+     */
+    public void setDelimiter(String separator) {
+        this.delimiter = separator;
+    }
+
+    /**
+     * @return Returns the echo.
+     */
+    public boolean isEcho() {
+        return echo;
+    }
+
+    /**
+     * @param echo
+     *            The echo to set.
+     */
+    public void setEcho(boolean echo) {
+        this.echo = echo;
+    }
+
+    /**
+     * @return Returns the separatearrayresults.
+     */
+    public boolean isSeparatearrayresults() {
+        return separatearrayresults;
+    }
+
+    /**
+     * @param separateArrayResults
+     *            The separatearrayresults to set.
+     */
+    public void setSeparatearrayresults(boolean separateArrayResults) {
+        this.separatearrayresults = separateArrayResults;
+    }
+
+    /**
+     * The login password for the <code>Manager</code> application.
+     */
+    public String getPassword() {
+        return (this.password);
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * The login username for the <code>JMX</code> MBeanServer.
+     */
+    public String getUsername() {
+        return (this.username);
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    /**
+     * The URL of the <code>JMX JSR 160</code> MBeanServer to be used.
+     */
+
+    public String getUrl() {
+        return (this.url);
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    /**
+     * The Host of the <code>JMX JSR 160</code> MBeanServer to be used.
+     */
+
+    public String getHost() {
+        return (this.host);
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    /**
+     * The Port of the <code>JMX JSR 160</code> MBeanServer to be used.
+     */
+
+    public String getPort() {
+        return (this.port);
+    }
+
+    public void setPort(String port) {
+        this.port = port;
+    }
+
+    /**
+     * @return Returns the useRef.
+     */
+    public boolean isUseRef() {
+        return ref != null && !"".equals(ref);
+    }
+
+    /**
+     * @return Returns the ref.
+     */
+    public String getRef() {
+        return ref;
+    }
+
+    /**
+     * @param refId The ref to set.
+     */
+    public void setRef(String refId) {
+        this.ref = refId;
+    }
+
+    /**
+     * @return Returns the ifCondition.
+     */
+    public String getIf() {
+        return ifCondition;
+    }
+
+    /**
+     * Only execute if a property of the given name exists in the current
+     * project.
+     * 
+     * @param c property name
+     */
+    public void setIf(String c) {
+        ifCondition = c;
+    }
+
+    /**
+     * @return Returns the unlessCondition.
+     */
+    public String getUnless() {
+        return unlessCondition;
+    }
+
+    /**
+     * Only execute if a property of the given name does not exist in the
+     * current project.
+     * 
+     * @param c property name
+     */
+    public void setUnless(String c) {
+        unlessCondition = c;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Execute the specified command. This logic only performs the common
+     * attribute validation required by all subclasses; it does not perform any
+     * functional logic directly.
+     * 
+     * @exception BuildException
+     *                if a validation error occurs
+     */
+    public void execute() throws BuildException {
+        if (testIfCondition() && testUnlessCondition()) {
+            try {
+                String error = null;
+
+                MBeanServerConnection jmxServerConnection = getJMXConnection();
+                error = jmxExecute(jmxServerConnection);
+                if (error != null && isFailOnError()) {
+                    // exception should be thrown only if failOnError == true
+                    // or error line will be logged twice
+                    throw new BuildException(error);
+                }
+            } catch (Throwable t) {
+                if (isFailOnError()) {
+                    throw new BuildException(t);
+                } else {
+                    handleErrorOutput(t.getMessage());
+                }
+            } finally {
+                closeRedirector();
+            }
+        }
+    }
+
+    /**
+     * create a new JMX Connection with auth when username and password is set.
+     */
+    public static MBeanServerConnection createJMXConnection(String url,
+            String host, String port, String username, String password)
+            throws MalformedURLException, IOException {
+        String urlForJMX;
+        if (url != null)
+            urlForJMX = url;
+        else
+            urlForJMX = JMX_SERVICE_PREFIX + host + ":" + port
+                    + JMX_SERVICE_SUFFIX;
+        Map environment = null;
+        if (username != null && password != null) {
+            String[] credentials = new String[2];
+            credentials[0] = username;
+            credentials[1] = password;
+            environment = new HashMap();
+            environment.put(JMXConnector.CREDENTIALS, credentials);
+        }
+        return JMXConnectorFactory.connect(new JMXServiceURL(urlForJMX),
+                environment).getMBeanServerConnection();
+
+    }
+
+    /**
+     * test the if condition
+     * 
+     * @return true if there is no if condition, or the named property exists
+     */
+    protected boolean testIfCondition() {
+        if (ifCondition == null || "".equals(ifCondition)) {
+            return true;
+        }
+        return getProperty(ifCondition) != null;
+    }
+
+    /**
+     * test the unless condition
+     * 
+     * @return true if there is no unless condition, or there is a named
+     *         property but it doesn't exist
+     */
+    protected boolean testUnlessCondition() {
+        if (unlessCondition == null || "".equals(unlessCondition)) {
+            return true;
+        }
+        return getProperty(unlessCondition) == null;
+    }
+
+    /**
+     * Get Current Connection from <em>ref</em> parameter or create a new one!
+     * 
+     * @return The server connection
+     * @throws MalformedURLException
+     * @throws IOException
+     */
+    public static MBeanServerConnection accessJMXConnection(Project project,
+            String url, String host, String port, String username,
+            String password, String refId) throws MalformedURLException,
+            IOException {
+        MBeanServerConnection jmxServerConnection = null;
+        boolean isRef = project != null && refId != null && refId.length() > 0;
+        if (isRef) {
+            Object pref = project.getReference(refId);
+            try {
+                jmxServerConnection = (MBeanServerConnection) pref;
+            } catch (ClassCastException cce) {
+                if (project != null) {
+                    project.log("wrong object reference " + refId + " - "
+                            + pref.getClass());
+                }
+                return null;
+            }
+        }
+        if (jmxServerConnection == null) {
+            jmxServerConnection = createJMXConnection(url, host, port,
+                    username, password);
+        }
+        if (isRef && jmxServerConnection != null) {
+            project.addReference(refId, jmxServerConnection);
+        }
+        return jmxServerConnection;
+    }
+
+    // ------------------------------------------------------ protected Methods
+
+    /**
+     * get JMXConnection
+     * 
+     * @return The connection
+     * @throws MalformedURLException
+     * @throws IOException
+     */
+    protected MBeanServerConnection getJMXConnection()
+            throws MalformedURLException, IOException {
+
+        MBeanServerConnection jmxServerConnection = null;
+        if (isUseRef()) {
+            Object pref = null ;
+            if(getProject() != null) {
+                pref = getProject().getReference(getRef());
+                if (pref != null) {
+                    try {
+                        jmxServerConnection = (MBeanServerConnection) pref;
+                    } catch (ClassCastException cce) {
+                        getProject().log(
+                            "Wrong object reference " + getRef() + " - "
+                                    + pref.getClass());
+                        return null;
+                    }
+                }
+            }
+            if (jmxServerConnection == null) {
+                jmxServerConnection = accessJMXConnection(getProject(),
+                        getUrl(), getHost(), getPort(), getUsername(),
+                        getPassword(), getRef());
+            }
+        } else {
+            jmxServerConnection = accessJMXConnection(getProject(), getUrl(),
+                    getHost(), getPort(), getUsername(), getPassword(), null);
+        }
+        return jmxServerConnection;
+    }
+
+    /**
+     * Execute the specified command, based on the configured properties. The
+     * input stream will be closed upon completion of this task, whether it was
+     * executed successfully or not.
+     * 
+     * @exception BuildException
+     *                if an error occurs
+     */
+    public String jmxExecute(MBeanServerConnection jmxServerConnection)
+            throws Exception {
+
+        if ((jmxServerConnection == null)) {
+            throw new BuildException("Must open a connection!");
+        } else if (isEcho()) {
+            handleOutput("JMX Connection ref=" + ref + " is open!");
+        }
+        return null;
+    }
+
+    /**
+     * Convert string to datatype FIXME How we can transfer values from ant
+     * project reference store (ref)?
+     * 
+     * @param value The value
+     * @param valueType The type
+     * @return The converted object
+     */
+    protected Object convertStringToType(String value, String valueType) {
+        if ("java.lang.String".equals(valueType))
+            return value;
+
+        Object convertValue = value;
+        if ("java.lang.Integer".equals(valueType) || "int".equals(valueType)) {
+            try {
+                convertValue = new Integer(value);
+            } catch (NumberFormatException ex) {
+                if (isEcho())
+                    handleErrorOutput("Unable to convert to integer:" + value);
+            }
+        } else if ("java.lang.Long".equals(valueType)
+                || "long".equals(valueType)) {
+            try {
+                convertValue = new Long(value);
+            } catch (NumberFormatException ex) {
+                if (isEcho())
+                    handleErrorOutput("Unable to convert to long:" + value);
+            }
+        } else if ("java.lang.Boolean".equals(valueType)
+                || "boolean".equals(valueType)) {
+            convertValue = new Boolean(value);
+        } else if ("java.lang.Float".equals(valueType)
+                || "float".equals(valueType)) {
+            try {
+                convertValue = new Float(value);
+            } catch (NumberFormatException ex) {
+                if (isEcho())
+                    handleErrorOutput("Unable to convert to float:" + value);
+            }
+        } else if ("java.lang.Double".equals(valueType)
+                || "double".equals(valueType)) {
+            try {
+                convertValue = new Double(value);
+            } catch (NumberFormatException ex) {
+                if (isEcho())
+                    handleErrorOutput("Unable to convert to double:" + value);
+            }
+        } else if ("javax.management.ObjectName".equals(valueType)
+                || "name".equals(valueType)) {
+            try {
+                convertValue = new ObjectName(value);
+            } catch (MalformedObjectNameException e) {
+                if (isEcho())
+                    handleErrorOutput("Unable to convert to ObjectName:"
+                            + value);
+            }
+        } else if ("java.net.InetAddress".equals(valueType)) {
+            try {
+                convertValue = InetAddress.getByName(value);
+            } catch (UnknownHostException exc) {
+                if (isEcho())
+                    handleErrorOutput("Unable to resolve host name:" + value);
+            }
+        }
+        return convertValue;
+    }
+
+    /**
+     * @param name context of result
+     * @param result
+     */
+    protected void echoResult(String name, Object result) {
+        if (isEcho()) {
+            if (result.getClass().isArray()) {
+                for (int i = 0; i < Array.getLength(result); i++) {
+                    handleOutput(name + "." + i + "=" + Array.get(result, i));
+                }
+            } else
+                handleOutput(name + "=" + result);
+        }
+    }
+
+    /**
+     * create result as property with name from attribute resultproperty
+     * 
+     * @param result The result
+     * @see #createProperty(String, Object)
+     */
+    protected void createProperty(Object result) {
+        if (resultproperty != null) {
+            createProperty(resultproperty, result);
+        }
+    }
+
+    /**
+     * create result as property with name from property prefix When result is
+     * an array and isSeparateArrayResults is true, resultproperty used as
+     * prefix (<code>resultproperty.0-array.length</code> and store the
+     * result array length at <code>resultproperty.length</code>. Other
+     * option is that you delemit your result with a delimiter
+     * (java.util.StringTokenizer is used).
+     * 
+     * @param propertyPrefix
+     * @param result
+     */
+    protected void createProperty(String propertyPrefix, Object result) {
+        if (propertyPrefix == null)
+            propertyPrefix = "";
+        if (result instanceof CompositeDataSupport) {
+            CompositeDataSupport data = (CompositeDataSupport) result;
+            CompositeType compositeType = data.getCompositeType();
+            Set keys = compositeType.keySet();
+            for (Iterator iter = keys.iterator(); iter.hasNext();) {
+                String key = (String) iter.next();
+                Object value = data.get(key);
+                OpenType type = compositeType.getType(key);
+                if (type instanceof SimpleType) {
+                    setProperty(propertyPrefix + "." + key, value);
+                } else {
+                    createProperty(propertyPrefix + "." + key, value);
+                }
+            }
+        } else if (result instanceof TabularDataSupport) {
+            TabularDataSupport data = (TabularDataSupport) result;
+            for (Iterator iter = data.keySet().iterator(); iter.hasNext();) {
+                Object key = iter.next();
+                for (Iterator iter1 = ((List) key).iterator(); iter1.hasNext();) {
+                    Object key1 = iter1.next();
+                    CompositeData valuedata = data.get(new Object[] { key1 });
+                    Object value = valuedata.get("value");
+                    OpenType type = valuedata.getCompositeType().getType(
+                            "value");
+                    if (type instanceof SimpleType) {
+                        setProperty(propertyPrefix + "." + key1, value);
+                    } else {
+                        createProperty(propertyPrefix + "." + key1, value);
+                    }
+                }
+            }
+        } else if (result.getClass().isArray()) {
+            if (isSeparatearrayresults()) {
+                int size = 0;
+                for (int i = 0; i < Array.getLength(result); i++) {
+                    if (setProperty(propertyPrefix + "." + size, Array.get(
+                            result, i))) {
+                        size++;
+                    }
+                }
+                if (size > 0) {
+                    setProperty(propertyPrefix + ".Length", Integer
+                            .toString(size));
+                }
+            }
+        } else {
+            String delim = getDelimiter();
+            if (delim != null) {
+                StringTokenizer tokenizer = new StringTokenizer(result
+                        .toString(), delim);
+                int size = 0;
+                for (; tokenizer.hasMoreTokens();) {
+                    String token = tokenizer.nextToken();
+                    if (setProperty(propertyPrefix + "." + size, token)) {
+                        size++;
+                    }
+                }
+                if (size > 0)
+                    setProperty(propertyPrefix + ".Length", Integer
+                            .toString(size));
+            } else {
+                setProperty(propertyPrefix, result.toString());
+            }
+        }
+    }
+
+    /**
+     * get all properties, when project is there got all project Properties
+     * @return properties
+     */
+    public Map getProperties() {
+        Project currentProject = getProject();
+        if (currentProject != null) {
+            return currentProject.getProperties();
+        } else {
+            return properties;
+        }        
+    }
+    
+    /**
+     * get all Properties
+     * @param property
+     * @return The property
+     */
+    public String getProperty(String property) {
+        Project currentProject = getProject();
+        if (currentProject != null) {
+            return currentProject.getProperty(property);
+        } else {
+            return properties.getProperty(property);
+        }
+    }
+
+    /**
+     * @param property The property
+     * @param value The value
+     * @return True if successful
+     */
+    public boolean setProperty(String property, Object value) {
+        if (property != null) {
+            if (value == null)
+                value = "";
+            if (isEcho()) {
+                handleOutput(property + "=" + value.toString());
+            }
+            Project currentProject = getProject();
+            if (currentProject != null) {
+                currentProject.setNewProperty(property, value.toString());
+            } else {
+                properties.setProperty(property, value.toString());
+            }
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/antlib.xml b/container/catalina/src/share/org/apache/catalina/ant/jmx/antlib.xml
new file mode 100644
index 0000000..811a403
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/antlib.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<antlib>
+  <typedef
+        name="open"
+        classname="org.apache.catalina.ant.jmx.JMXAccessorTask" />
+  <typedef
+        name="set"
+        classname="org.apache.catalina.ant.jmx.JMXAccessorSetTask" />
+  <typedef
+        name="get"
+        classname="org.apache.catalina.ant.jmx.JMXAccessorGetTask" />
+  <typedef
+        name="invoke"
+        classname="org.apache.catalina.ant.jmx.JMXAccessorInvokeTask" />
+  <typedef
+        name="query"
+        classname="org.apache.catalina.ant.jmx.JMXAccessorQueryTask" />
+  <typedef
+        name="equals"
+        classname="org.apache.catalina.ant.jmx.JMXAccessorEqualsCondition" />
+  <typedef
+        name="condition"
+        classname="org.apache.catalina.ant.jmx.JMXAccessorCondition" />
+</antlib>
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/jmxaccessor.tasks b/container/catalina/src/share/org/apache/catalina/ant/jmx/jmxaccessor.tasks
new file mode 100644
index 0000000..259e3a1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/jmxaccessor.tasks
@@ -0,0 +1,6 @@
+# JMX
+jmxOpen=org.apache.catalina.ant.jmx.JMXAccessorTask
+jmxSet=org.apache.catalina.ant.jmx.JMXAccessorSetTask
+jmxGet=org.apache.catalina.ant.jmx.JMXAccessorGetTask
+jmxInvoke=org.apache.catalina.ant.jmx.JMXAccessorInvokeTask
+jmxQuery=org.apache.catalina.ant.jmx.JMXAccessorQueryTask
diff --git a/container/catalina/src/share/org/apache/catalina/ant/jmx/package.html b/container/catalina/src/share/org/apache/catalina/ant/jmx/package.html
new file mode 100644
index 0000000..299dc89
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/jmx/package.html
@@ -0,0 +1,61 @@
+<body>
+
+<p>This package contains a set of <code>JMX Task</code> implementations for
+<em>Ant (version 1.6 or later)</em> that can be used to interact with the
+Remote JMX JSR 160 RMI Adaptor to get/set attributes, invoke MBean operations
+and query for Mbeans inside a running instance of Tomcat.  For more information, see
+<a href="http://jakarta.apache.org/tomcat/tomcat-5.5-doc/monitoring.html">
+http://jakarta.apache.org/tomcat/tomcat-5.5-doc/monitoring.html</a>.</p>
+
+<p>Each task element can open a new jmx connection or reference an
+	existing one. The following attribute are exists in every tasks:
+</p>
+
+<table>
+  <tr>
+    <th align="center" width="15%">Attribute</th>
+    <th align="center" width="85%">Description</th>
+  </tr>
+  <tr>
+    <td align="center">url</td>
+    <td>
+      The JMX Connection URL of the remote Tomcat MBeansServer.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">username</td>
+    <td>
+      The username of a MBeanServer auth, when configured.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">password</td>
+    <td>
+      The password of a MBeanServer auth, when configured.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">host</td>
+    <td>
+      The JMX Connection host.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">port</td>
+    <td>
+      The JMX Connection port.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">ref</td>
+    <td>
+      The name of the ant internal reference for a jmx connection.
+    </td>
+  </tr>
+
+</table>
+
+<p><strong>NOTE</strong> - This Tasks only work, 
+	when JSR 160 MBean Adaptor as remote jvm is configured.</p>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/catalina/ant/package.html b/container/catalina/src/share/org/apache/catalina/ant/package.html
new file mode 100644
index 0000000..eaaa40d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ant/package.html
@@ -0,0 +1,86 @@
+<body>
+
+<p>This package contains a set of <code>Task</code> implementations for
+<em>Ant (version 1.6.x or later)</em> that can be used to interact with the
+Manager application to deploy, undeploy, list, reload, start and stop web applications 
+from a running instance of Tomcat.  For more information, see
+<a href="http://jakarta.apache.org/tomcat/tomcat-5.5-doc/manager-howto.html">
+http://jakarta.apache.org/tomcat/tomcat-5.5-doc/manager-howto.html</a>.</p>
+
+<p>The attributes of each task element correspond
+exactly to the request parameters that are included with an HTTP request
+sent directly to the Manager application.  They are summarized as follows:
+</p>
+
+<table>
+  <tr>
+    <th align="center" width="15%">Attribute</th>
+    <th align="center" width="85%">Description</th>
+  </tr>
+  <tr>
+    <td align="center">url</td>
+    <td>
+      The URL of the Manager web application you will use to
+      perform the requested operations.  If not specified, defaults to
+      <code>http://localhost:8080/manager</code> (which corresponds
+      to a standard installation of Tomcat 5).
+    </td>
+  </tr>
+  <tr>
+    <td align="center">username</td>
+    <td>
+      The username of a Tomcat user that has been configured with the
+      <code>manager</code> role, as required to execute Manager
+      application commands.  This attribute is required.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">password</td>
+    <td>
+      The password of a Tomcat user that has been configured with the
+      <code>manager</code> role, as required to execute Manager
+      application commands.  This attribute is required.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">config</td>
+    <td>
+      A URL pointing at the context configuration file (i.e. a file
+      containing only the <code>&lt;Context&gt;</code> element, and
+      its nested elements, from <code>server.xml</code> for a particular
+      web application).  This attribute is supported only on the
+      <code>install</code> target, and is required only if you wish to
+      install an application with non-default configuration characteristics.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">path</td>
+    <td>
+      The context path (including the leading slash) of the web application
+      this command is intended to manage, or a zero-length string for the
+      ROOT web application.  This attribute is valid for the
+      <code>install</code>, <code>reload</code>, <code>remove</code>,
+      <code>start</code>, and <code>stop</code> tasks only, and is
+      required in all of those cases.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">war</td>
+    <td>
+      A <code>jar:</code> URL that points at a web application archive (WAR)
+      file, or a <code>file:</code> URL that points at an unpacked directory
+      containing the web application.  This attribute is supported only on
+      the <code>install</code> target.  You must specify at least one of the
+      <code>config</code> and <code>war</code> attributes; if you specify
+      both, the <code>war</code> attribute overrides the <code>docBase</code>
+      attribute in the context configuration file.
+    </td>
+  </tr>
+</table>
+
+<p><strong>NOTE</strong> - Commands executed through the <em>Manager</em>
+application are <strong>NOT</strong> reflected in updates to the Tomcat
+<code>server.xml</code> configuration file, so they do not persist past the
+next time you restart the entire Tomcat container.</p>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/AuthenticatorBase.java b/container/catalina/src/share/org/apache/catalina/authenticator/AuthenticatorBase.java
new file mode 100644
index 0000000..115cb0d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/AuthenticatorBase.java
@@ -0,0 +1,863 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.authenticator;
+
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Random;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import org.apache.catalina.Authenticator;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.Valve;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.util.DateTool;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Basic implementation of the <b>Valve</b> interface that enforces the
+ * <code>&lt;security-constraint&gt;</code> elements in the web application
+ * deployment descriptor.  This functionality is implemented as a Valve
+ * so that it can be ommitted in environments that do not require these
+ * features.  Individual implementations of each supported authentication
+ * method can subclass this base class as required.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  When this class is utilized, the Context to
+ * which it is attached (or a parent Container in a hierarchy) must have an
+ * associated Realm that can be used for authenticating users and enumerating
+ * the roles to which they have been assigned.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  This Valve is only useful when processing HTTP
+ * requests.  Requests of any other type will simply be passed through.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+
+public abstract class AuthenticatorBase
+    extends ValveBase
+    implements Authenticator, Lifecycle {
+    private static Log log = LogFactory.getLog(AuthenticatorBase.class);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The default message digest algorithm to use if we cannot use
+     * the requested one.
+     */
+    protected static final String DEFAULT_ALGORITHM = "MD5";
+
+
+    /**
+     * The number of random bytes to include when generating a
+     * session identifier.
+     */
+    protected static final int SESSION_ID_BYTES = 16;
+
+
+    /**
+     * The message digest algorithm to be used when generating session
+     * identifiers.  This must be an algorithm supported by the
+     * <code>java.security.MessageDigest</code> class on your platform.
+     */
+    protected String algorithm = DEFAULT_ALGORITHM;
+
+
+    /**
+     * Should we cache authenticated Principals if the request is part of
+     * an HTTP session?
+     */
+    protected boolean cache = true;
+
+
+    /**
+     * The Context to which this Valve is attached.
+     */
+    protected Context context = null;
+
+
+    /**
+     * Return the MessageDigest implementation to be used when
+     * creating session identifiers.
+     */
+    protected MessageDigest digest = null;
+
+
+    /**
+     * A String initialization parameter used to increase the entropy of
+     * the initialization of our random number generator.
+     */
+    protected String entropy = null;
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.AuthenticatorBase/1.0";
+
+    /**
+     * Flag to determine if we disable proxy caching, or leave the issue
+     * up to the webapp developer.
+     */
+    protected boolean disableProxyCaching = true;
+
+    /**
+     * Flag to determine if we disable proxy caching with headers incompatible
+     * with IE 
+     */
+    protected boolean securePagesWithPragma = true;
+    
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * A random number generator to use when generating session identifiers.
+     */
+    protected Random random = null;
+
+
+    /**
+     * The Java class name of the random number generator class to be used
+     * when generating session identifiers.
+     */
+    protected String randomClass = "java.security.SecureRandom";
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The SingleSignOn implementation in our request processing chain,
+     * if there is one.
+     */
+    protected SingleSignOn sso = null;
+
+
+    /**
+     * Has this component been started?
+     */
+    protected boolean started = false;
+
+
+    /**
+     * "Expires" header always set to Date(1), so generate once only
+     */
+    private static final String DATE_ONE =
+        (new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER,
+                              Locale.US)).format(new Date(1));
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the message digest algorithm for this Manager.
+     */
+    public String getAlgorithm() {
+
+        return (this.algorithm);
+
+    }
+
+
+    /**
+     * Set the message digest algorithm for this Manager.
+     *
+     * @param algorithm The new message digest algorithm
+     */
+    public void setAlgorithm(String algorithm) {
+
+        this.algorithm = algorithm;
+
+    }
+
+
+    /**
+     * Return the cache authenticated Principals flag.
+     */
+    public boolean getCache() {
+
+        return (this.cache);
+
+    }
+
+
+    /**
+     * Set the cache authenticated Principals flag.
+     *
+     * @param cache The new cache flag
+     */
+    public void setCache(boolean cache) {
+
+        this.cache = cache;
+
+    }
+
+
+    /**
+     * Return the Container to which this Valve is attached.
+     */
+    public Container getContainer() {
+
+        return (this.context);
+
+    }
+
+
+    /**
+     * Set the Container to which this Valve is attached.
+     *
+     * @param container The container to which we are attached
+     */
+    public void setContainer(Container container) {
+
+        if (!(container instanceof Context))
+            throw new IllegalArgumentException
+                (sm.getString("authenticator.notContext"));
+
+        super.setContainer(container);
+        this.context = (Context) container;
+
+    }
+
+
+    /**
+     * Return the entropy increaser value, or compute a semi-useful value
+     * if this String has not yet been set.
+     */
+    public String getEntropy() {
+
+        // Calculate a semi-useful value if this has not been set
+        if (this.entropy == null)
+            setEntropy(this.toString());
+
+        return (this.entropy);
+
+    }
+
+
+    /**
+     * Set the entropy increaser value.
+     *
+     * @param entropy The new entropy increaser value
+     */
+    public void setEntropy(String entropy) {
+
+        this.entropy = entropy;
+
+    }
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the random number generator class name.
+     */
+    public String getRandomClass() {
+
+        return (this.randomClass);
+
+    }
+
+
+    /**
+     * Set the random number generator class name.
+     *
+     * @param randomClass The new random number generator class name
+     */
+    public void setRandomClass(String randomClass) {
+
+        this.randomClass = randomClass;
+
+    }
+
+    /**
+     * Return the flag that states if we add headers to disable caching by
+     * proxies.
+     */
+    public boolean getDisableProxyCaching() {
+        return disableProxyCaching;
+    }
+
+    /**
+     * Set the value of the flag that states if we add headers to disable
+     * caching by proxies.
+     * @param nocache <code>true</code> if we add headers to disable proxy 
+     *              caching, <code>false</code> if we leave the headers alone.
+     */
+    public void setDisableProxyCaching(boolean nocache) {
+        disableProxyCaching = nocache;
+    }
+    
+    /**
+     * Return the flag that states, if proxy caching is disabled, what headers
+     * we add to disable the caching.
+     */
+    public boolean getSecurePagesWithPragma() {
+        return securePagesWithPragma;
+    }
+
+    /**
+     * Set the value of the flag that states what headers we add to disable
+     * proxy caching.
+     * @param securePagesWithPragma <code>true</code> if we add headers which 
+     * are incompatible with downloading office documents in IE under SSL but
+     * which fix a caching problem in Mozilla.
+     */
+    public void setSecurePagesWithPragma(boolean securePagesWithPragma) {
+        this.securePagesWithPragma = securePagesWithPragma;
+    }    
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Enforce the security restrictions in the web application deployment
+     * descriptor of our associated Context.
+     *
+     * @param request Request to be processed
+     * @param response Response to be processed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if thrown by a processing element
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        if (log.isDebugEnabled())
+            log.debug("Security checking request " +
+                request.getMethod() + " " + request.getRequestURI());
+        LoginConfig config = this.context.getLoginConfig();
+
+        // Have we got a cached authenticated Principal to record?
+        if (cache) {
+            Principal principal = request.getUserPrincipal();
+            if (principal == null) {
+                Session session = request.getSessionInternal(false);
+                if (session != null) {
+                    principal = session.getPrincipal();
+                    if (principal != null) {
+                        if (log.isDebugEnabled())
+                            log.debug("We have cached auth type " +
+                                session.getAuthType() +
+                                " for principal " +
+                                session.getPrincipal());
+                        request.setAuthType(session.getAuthType());
+                        request.setUserPrincipal(principal);
+                    }
+                }
+            }
+        }
+
+        // Special handling for form-based logins to deal with the case
+        // where the login form (and therefore the "j_security_check" URI
+        // to which it submits) might be outside the secured area
+        String contextPath = this.context.getPath();
+        String requestURI = request.getDecodedRequestURI();
+        if (requestURI.startsWith(contextPath) &&
+            requestURI.endsWith(Constants.FORM_ACTION)) {
+            if (!authenticate(request, response, config)) {
+                if (log.isDebugEnabled())
+                    log.debug(" Failed authenticate() test ??" + requestURI );
+                return;
+            }
+        }
+
+        Realm realm = this.context.getRealm();
+        // Is this request URI subject to a security constraint?
+        SecurityConstraint [] constraints
+            = realm.findSecurityConstraints(request, this.context);
+       
+        if ((constraints == null) /* &&
+            (!Constants.FORM_METHOD.equals(config.getAuthMethod())) */ ) {
+            if (log.isDebugEnabled())
+                log.debug(" Not subject to any constraint");
+            getNext().invoke(request, response);
+            return;
+        }
+
+        // Make sure that constrained resources are not cached by web proxies
+        // or browsers as caching can provide a security hole
+        if (disableProxyCaching && 
+            // FIXME: Disabled for Mozilla FORM support over SSL 
+            // (improper caching issue)
+            //!request.isSecure() &&
+            !"POST".equalsIgnoreCase(request.getMethod())) {
+            if (securePagesWithPragma) {
+                // FIXME: These cause problems with downloading office docs
+                // from IE under SSL and may not be needed for newer Mozilla
+                // clients.
+                response.setHeader("Pragma", "No-cache");
+                response.setHeader("Cache-Control", "no-cache");
+            } else {
+                response.setHeader("Cache-Control", "private");
+            }
+            response.setHeader("Expires", DATE_ONE);
+        }
+
+        int i;
+        // Enforce any user data constraint for this security constraint
+        if (log.isDebugEnabled()) {
+            log.debug(" Calling hasUserDataPermission()");
+        }
+        if (!realm.hasUserDataPermission(request, response,
+                                         constraints)) {
+            if (log.isDebugEnabled()) {
+                log.debug(" Failed hasUserDataPermission() test");
+            }
+            /*
+             * ASSERT: Authenticator already set the appropriate
+             * HTTP status code, so we do not have to do anything special
+             */
+            return;
+        }
+       
+        for(i=0; i < constraints.length; i++) {
+            // Authenticate based upon the specified login configuration
+            if (constraints[i].getAuthConstraint()) {
+                if (log.isDebugEnabled()) {
+                    log.debug(" Calling authenticate()");
+                }
+                if (!authenticate(request, response, config)) {
+                    if (log.isDebugEnabled()) {
+                        log.debug(" Failed authenticate() test");
+                    }
+                    /*
+                     * ASSERT: Authenticator already set the appropriate
+                     * HTTP status code, so we do not have to do anything
+                     * special
+                     */
+                    return;
+                } else {
+                    break;
+                }
+            }
+        }
+        if (log.isDebugEnabled()) {
+            log.debug(" Calling accessControl()");
+        }
+        if (!realm.hasResourcePermission(request, response,
+                                         constraints,
+                                         this.context)) {
+            if (log.isDebugEnabled()) {
+                log.debug(" Failed accessControl() test");
+            }
+            /*
+             * ASSERT: AccessControl method has already set the
+             * appropriate HTTP status code, so we do not have to do
+             * anything special
+             */
+            return;
+        }
+    
+        // Any and all specified constraints have been satisfied
+        if (log.isDebugEnabled()) {
+            log.debug(" Successfully passed all security constraints");
+        }
+        getNext().invoke(request, response);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+
+
+    /**
+     * Associate the specified single sign on identifier with the
+     * specified Session.
+     *
+     * @param ssoId Single sign on identifier
+     * @param session Session to be associated
+     */
+    protected void associate(String ssoId, Session session) {
+
+        if (sso == null)
+            return;
+        sso.associate(ssoId, session);
+
+    }
+
+
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config    Login configuration describing how authentication
+     *              should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected abstract boolean authenticate(Request request,
+                                            Response response,
+                                            LoginConfig config)
+        throws IOException;
+
+
+    /**
+     * Generate and return a new session identifier for the cookie that
+     * identifies an SSO principal.
+     */
+    protected synchronized String generateSessionId() {
+
+        // Generate a byte array containing a session identifier
+        byte bytes[] = new byte[SESSION_ID_BYTES];
+        getRandom().nextBytes(bytes);
+        bytes = getDigest().digest(bytes);
+
+        // Render the result as a String of hexadecimal digits
+        StringBuffer result = new StringBuffer();
+        for (int i = 0; i < bytes.length; i++) {
+            byte b1 = (byte) ((bytes[i] & 0xf0) >> 4);
+            byte b2 = (byte) (bytes[i] & 0x0f);
+            if (b1 < 10)
+                result.append((char) ('0' + b1));
+            else
+                result.append((char) ('A' + (b1 - 10)));
+            if (b2 < 10)
+                result.append((char) ('0' + b2));
+            else
+                result.append((char) ('A' + (b2 - 10)));
+        }
+        return (result.toString());
+
+    }
+
+
+    /**
+     * Return the MessageDigest object to be used for calculating
+     * session identifiers.  If none has been created yet, initialize
+     * one the first time this method is called.
+     */
+    protected synchronized MessageDigest getDigest() {
+
+        if (this.digest == null) {
+            try {
+                this.digest = MessageDigest.getInstance(algorithm);
+            } catch (NoSuchAlgorithmException e) {
+                try {
+                    this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
+                } catch (NoSuchAlgorithmException f) {
+                    this.digest = null;
+                }
+            }
+        }
+
+        return (this.digest);
+
+    }
+
+
+    /**
+     * Return the random number generator instance we should use for
+     * generating session identifiers.  If there is no such generator
+     * currently defined, construct and seed a new one.
+     */
+    protected synchronized Random getRandom() {
+
+        if (this.random == null) {
+            try {
+                Class clazz = Class.forName(randomClass);
+                this.random = (Random) clazz.newInstance();
+                long seed = System.currentTimeMillis();
+                char entropy[] = getEntropy().toCharArray();
+                for (int i = 0; i < entropy.length; i++) {
+                    long update = ((byte) entropy[i]) << ((i % 8) * 8);
+                    seed ^= update;
+                }
+                this.random.setSeed(seed);
+            } catch (Exception e) {
+                this.random = new java.util.Random();
+            }
+        }
+
+        return (this.random);
+
+    }
+
+
+    /**
+     * Attempts reauthentication to the <code>Realm</code> using
+     * the credentials included in argument <code>entry</code>.
+     *
+     * @param ssoId identifier of SingleSignOn session with which the
+     *              caller is associated
+     * @param request   the request that needs to be authenticated
+     */
+    protected boolean reauthenticateFromSSO(String ssoId, Request request) {
+
+        if (sso == null || ssoId == null)
+            return false;
+
+        boolean reauthenticated = false;
+
+        Container parent = getContainer();
+        if (parent != null) {
+            Realm realm = parent.getRealm();
+            if (realm != null) {
+                reauthenticated = sso.reauthenticate(ssoId, realm, request);
+            }
+        }
+
+        if (reauthenticated) {
+            associate(ssoId, request.getSessionInternal(true));
+
+            if (log.isDebugEnabled()) {
+                log.debug(" Reauthenticated cached principal '" +
+                          request.getUserPrincipal().getName() +
+                          "' with auth type '" +  request.getAuthType() + "'");
+            }
+        }
+
+        return reauthenticated;
+    }
+
+
+    /**
+     * Register an authenticated Principal and authentication type in our
+     * request, in the current session (if there is one), and with our
+     * SingleSignOn valve, if there is one.  Set the appropriate cookie
+     * to be returned.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are generating
+     * @param principal The authenticated Principal to be registered
+     * @param authType The authentication type to be registered
+     * @param username Username used to authenticate (if any)
+     * @param password Password used to authenticate (if any)
+     */
+    protected void register(Request request, Response response,
+                            Principal principal, String authType,
+                            String username, String password) {
+
+        if (log.isDebugEnabled())
+            log.debug("Authenticated '" + principal.getName() + "' with type '"
+                + authType + "'");
+
+        // Cache the authentication information in our request
+        request.setAuthType(authType);
+        request.setUserPrincipal(principal);
+
+        Session session = request.getSessionInternal(false);
+        // Cache the authentication information in our session, if any
+        if (cache) {
+            if (session != null) {
+                session.setAuthType(authType);
+                session.setPrincipal(principal);
+                if (username != null)
+                    session.setNote(Constants.SESS_USERNAME_NOTE, username);
+                else
+                    session.removeNote(Constants.SESS_USERNAME_NOTE);
+                if (password != null)
+                    session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+                else
+                    session.removeNote(Constants.SESS_PASSWORD_NOTE);
+            }
+        }
+
+        // Construct a cookie to be returned to the client
+        if (sso == null)
+            return;
+
+        // Only create a new SSO entry if the SSO did not already set a note
+        // for an existing entry (as it would do with subsequent requests
+        // for DIGEST and SSL authenticated contexts)
+        String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+        if (ssoId == null) {
+            // Construct a cookie to be returned to the client
+            ssoId = generateSessionId();
+            Cookie cookie = new Cookie(Constants.SINGLE_SIGN_ON_COOKIE, ssoId);
+            cookie.setMaxAge(-1);
+            cookie.setPath("/");
+            response.addCookie(cookie);
+
+            // Register this principal with our SSO valve
+            sso.register(ssoId, principal, authType, username, password);
+            request.setNote(Constants.REQ_SSOID_NOTE, ssoId);
+
+        } else {
+            // Update the SSO session with the latest authentication data
+            sso.update(ssoId, principal, authType, username, password);
+        }
+
+        // Fix for Bug 10040
+        // Always associate a session with a new SSO reqistration.
+        // SSO entries are only removed from the SSO registry map when
+        // associated sessions are destroyed; if a new SSO entry is created
+        // above for this request and the user never revisits the context, the
+        // SSO entry will never be cleared if we don't associate the session
+        if (session == null)
+            session = request.getSessionInternal(true);
+        sso.associate(ssoId, session);
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (sm.getString("authenticator.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Look up the SingleSignOn implementation in our request processing
+        // path, if there is one
+        Container parent = context.getParent();
+        while ((sso == null) && (parent != null)) {
+            if (!(parent instanceof Pipeline)) {
+                parent = parent.getParent();
+                continue;
+            }
+            Valve valves[] = ((Pipeline) parent).getValves();
+            for (int i = 0; i < valves.length; i++) {
+                if (valves[i] instanceof SingleSignOn) {
+                    sso = (SingleSignOn) valves[i];
+                    break;
+                }
+            }
+            if (sso == null)
+                parent = parent.getParent();
+        }
+        if (log.isDebugEnabled()) {
+            if (sso != null)
+                log.debug("Found SingleSignOn Valve at " + sso);
+            else
+                log.debug("No SingleSignOn Valve is present");
+        }
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("authenticator.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        sso = null;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/BasicAuthenticator.java b/container/catalina/src/share/org/apache/catalina/authenticator/BasicAuthenticator.java
new file mode 100644
index 0000000..0679e1c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/BasicAuthenticator.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.authenticator;
+
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.util.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation of HTTP BASIC
+ * Authentication, as outlined in RFC 2617:  "HTTP Authentication: Basic
+ * and Digest Access Authentication."
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class BasicAuthenticator
+    extends AuthenticatorBase {
+    private static Log log = LogFactory.getLog(BasicAuthenticator.class);
+
+
+
+    /**
+     * Authenticate bytes.
+     */
+    public static final byte[] AUTHENTICATE_BYTES = {
+        (byte) 'W',
+        (byte) 'W',
+        (byte) 'W',
+        (byte) '-',
+        (byte) 'A',
+        (byte) 'u',
+        (byte) 't',
+        (byte) 'h',
+        (byte) 'e',
+        (byte) 'n',
+        (byte) 't',
+        (byte) 'i',
+        (byte) 'c',
+        (byte) 'a',
+        (byte) 't',
+        (byte) 'e'
+    };
+
+
+   // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.BasicAuthenticator/1.0";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config    Login configuration describing how authentication
+     *              should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean authenticate(Request request,
+                                Response response,
+                                LoginConfig config)
+        throws IOException {
+
+        // Have we already authenticated someone?
+        Principal principal = request.getUserPrincipal();
+        String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+        if (principal != null) {
+            if (log.isDebugEnabled())
+                log.debug("Already authenticated '" + principal.getName() + "'");
+            // Associate the session with any existing SSO session
+            if (ssoId != null)
+                associate(ssoId, request.getSessionInternal(true));
+            return (true);
+        }
+
+        // Is there an SSO session against which we can try to reauthenticate?
+        if (ssoId != null) {
+            if (log.isDebugEnabled())
+                log.debug("SSO Id " + ssoId + " set; attempting " +
+                          "reauthentication");
+            /* Try to reauthenticate using data cached by SSO.  If this fails,
+               either the original SSO logon was of DIGEST or SSL (which
+               we can't reauthenticate ourselves because there is no
+               cached username and password), or the realm denied
+               the user's reauthentication for some reason.
+               In either case we have to prompt the user for a logon */
+            if (reauthenticateFromSSO(ssoId, request))
+                return true;
+        }
+
+        // Validate any credentials already included with this request
+        String username = null;
+        String password = null;
+
+        MessageBytes authorization = 
+            request.getCoyoteRequest().getMimeHeaders()
+            .getValue("authorization");
+        
+        if (authorization != null) {
+            authorization.toBytes();
+            ByteChunk authorizationBC = authorization.getByteChunk();
+            if (authorizationBC.startsWithIgnoreCase("basic ", 0)) {
+                authorizationBC.setOffset(authorizationBC.getOffset() + 6);
+                // FIXME: Add trimming
+                // authorizationBC.trim();
+                
+                CharChunk authorizationCC = authorization.getCharChunk();
+                Base64.decode(authorizationBC, authorizationCC);
+                
+                // Get username and password
+                int colon = authorizationCC.indexOf(':');
+                if (colon < 0) {
+                    username = authorizationCC.toString();
+                } else {
+                    char[] buf = authorizationCC.getBuffer();
+                    username = new String(buf, 0, colon);
+                    password = new String(buf, colon + 1, 
+                            authorizationCC.getEnd() - colon - 1);
+                }
+                
+                authorizationBC.setOffset(authorizationBC.getOffset() - 6);
+            }
+
+            principal = context.getRealm().authenticate(username, password);
+            if (principal != null) {
+                register(request, response, principal, Constants.BASIC_METHOD,
+                         username, password);
+                return (true);
+            }
+        }
+        
+
+        // Send an "unauthorized" response and an appropriate challenge
+        MessageBytes authenticate = 
+            response.getCoyoteResponse().getMimeHeaders()
+            .addValue(AUTHENTICATE_BYTES, 0, AUTHENTICATE_BYTES.length);
+        CharChunk authenticateCC = authenticate.getCharChunk();
+        authenticateCC.append("Basic realm=\"");
+        if (config.getRealmName() == null) {
+            authenticateCC.append(request.getServerName());
+            authenticateCC.append(':');
+            authenticateCC.append(Integer.toString(request.getServerPort()));
+        } else {
+            authenticateCC.append(config.getRealmName());
+        }
+        authenticateCC.append('\"');        
+        authenticate.toChars();
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        //response.flushBuffer();
+        return (false);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/Constants.java b/container/catalina/src/share/org/apache/catalina/authenticator/Constants.java
new file mode 100644
index 0000000..7725997
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/Constants.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.authenticator;
+
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.authenticator";
+
+    // Authentication methods for login configuration
+    public static final String BASIC_METHOD = "BASIC";
+    public static final String CERT_METHOD = "CLIENT-CERT";
+    public static final String DIGEST_METHOD = "DIGEST";
+    public static final String FORM_METHOD = "FORM";
+
+    // User data constraints for transport guarantee
+    public static final String NONE_TRANSPORT = "NONE";
+    public static final String INTEGRAL_TRANSPORT = "INTEGRAL";
+    public static final String CONFIDENTIAL_TRANSPORT = "CONFIDENTIAL";
+
+    // Form based authentication constants
+    public static final String FORM_ACTION = "/j_security_check";
+    public static final String FORM_PASSWORD = "j_password";
+    public static final String FORM_USERNAME = "j_username";
+
+    // Cookie name for single sign on support
+    public static final String SINGLE_SIGN_ON_COOKIE = "JSESSIONIDSSO";
+
+
+    // --------------------------------------------------------- Request Notes
+
+
+    /**
+     * <p>If a user has been authenticated by the web layer, by means of a
+     * login method other than CLIENT_CERT, the username and password
+     * used to authenticate the user will be attached to the request as
+     * Notes for use by other server components.  A server component can
+     * also call several existing methods on Request to determine whether
+     * or not any user has been authenticated:</p>
+     * <ul>
+     * <li><strong>request.getAuthType()</strong>
+     *     will return BASIC, CLIENT-CERT, DIGEST, FORM, or <code>null</code>
+     *     if there is no authenticated user.</li>
+     * <li><strong>request.getUserPrincipal()</strong>
+     *     will return the authenticated <code>Principal</code> returned by the
+     *     <code>Realm</code> that authenticated this user.</li>
+     * </ul>
+     * <p>If CLIENT_CERT authentication was performed, the certificate chain
+     * will be available as a request attribute, as defined in the
+     * servlet specification.</p>
+     */
+
+
+    /**
+     * The notes key for the password used to authenticate this user.
+     */
+    public static final String REQ_PASSWORD_NOTE =
+      "org.apache.catalina.request.PASSWORD";
+
+
+    /**
+     * The notes key for the username used to authenticate this user.
+     */
+    public static final String REQ_USERNAME_NOTE =
+      "org.apache.catalina.request.USERNAME";
+
+
+    /**
+     * The notes key to track the single-sign-on identity with which this
+     * request is associated.
+     */
+    public static final String REQ_SSOID_NOTE =
+      "org.apache.catalina.request.SSOID";
+
+
+    // ---------------------------------------------------------- Session Notes
+
+
+    /**
+     * If the <code>cache</code> property of our authenticator is set, and
+     * the current request is part of a session, authentication information
+     * will be cached to avoid the need for repeated calls to
+     * <code>Realm.authenticate()</code>, under the following keys:
+     */
+
+
+    /**
+     * The notes key for the password used to authenticate this user.
+     */
+    public static final String SESS_PASSWORD_NOTE =
+      "org.apache.catalina.session.PASSWORD";
+
+
+    /**
+     * The notes key for the username used to authenticate this user.
+     */
+    public static final String SESS_USERNAME_NOTE =
+      "org.apache.catalina.session.USERNAME";
+
+
+    /**
+     * The following note keys are used during form login processing to
+     * cache required information prior to the completion of authentication.
+     */
+
+
+    /**
+     * The previously authenticated principal (if caching is disabled).
+     */
+    public static final String FORM_PRINCIPAL_NOTE =
+        "org.apache.catalina.authenticator.PRINCIPAL";
+
+
+    /**
+     * The original request information, to which the user will be
+     * redirected if authentication succeeds.
+     */
+    public static final String FORM_REQUEST_NOTE =
+        "org.apache.catalina.authenticator.REQUEST";
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/DigestAuthenticator.java b/container/catalina/src/share/org/apache/catalina/authenticator/DigestAuthenticator.java
new file mode 100644
index 0000000..1b256c3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/DigestAuthenticator.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.authenticator;
+
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.util.StringTokenizer;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.catalina.Realm;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.util.MD5Encoder;
+
+
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation of HTTP DIGEST
+ * Authentication (see RFC 2069).
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class DigestAuthenticator
+    extends AuthenticatorBase {
+    private static Log log = LogFactory.getLog(DigestAuthenticator.class);
+
+
+    // -------------------------------------------------------------- Constants
+
+    /**
+     * The MD5 helper object for this class.
+     */
+    protected static final MD5Encoder md5Encoder = new MD5Encoder();
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.DigestAuthenticator/1.0";
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public DigestAuthenticator() {
+        super();
+        try {
+            if (md5Helper == null)
+                md5Helper = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            throw new IllegalStateException();
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * MD5 message digest provider.
+     */
+    protected static MessageDigest md5Helper;
+
+
+    /**
+     * Private key.
+     */
+    protected String key = "Catalina";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config    Login configuration describing how authentication
+     *              should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean authenticate(Request request,
+                                Response response,
+                                LoginConfig config)
+        throws IOException {
+
+        // Have we already authenticated someone?
+        Principal principal = request.getUserPrincipal();
+        //String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+        if (principal != null) {
+            if (log.isDebugEnabled())
+                log.debug("Already authenticated '" + principal.getName() + "'");
+            // Associate the session with any existing SSO session in order
+            // to get coordinated session invalidation at logout
+            String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+            if (ssoId != null)
+                associate(ssoId, request.getSessionInternal(true));
+            return (true);
+        }
+
+        // NOTE: We don't try to reauthenticate using any existing SSO session,
+        // because that will only work if the original authentication was
+        // BASIC or FORM, which are less secure than the DIGEST auth-type
+        // specified for this webapp
+        //
+        // Uncomment below to allow previous FORM or BASIC authentications
+        // to authenticate users for this webapp
+        // TODO make this a configurable attribute (in SingleSignOn??)
+        /*
+        // Is there an SSO session against which we can try to reauthenticate?
+        if (ssoId != null) {
+            if (log.isDebugEnabled())
+                log.debug("SSO Id " + ssoId + " set; attempting " +
+                          "reauthentication");
+            // Try to reauthenticate using data cached by SSO.  If this fails,
+            // either the original SSO logon was of DIGEST or SSL (which
+            // we can't reauthenticate ourselves because there is no
+            // cached username and password), or the realm denied
+            // the user's reauthentication for some reason.
+            // In either case we have to prompt the user for a logon
+            if (reauthenticateFromSSO(ssoId, request))
+                return true;
+        }
+        */
+
+        // Validate any credentials already included with this request
+        String authorization = request.getHeader("authorization");
+        if (authorization != null) {
+            principal = findPrincipal(request, authorization, context.getRealm());
+            if (principal != null) {
+                String username = parseUsername(authorization);
+                register(request, response, principal,
+                         Constants.DIGEST_METHOD,
+                         username, null);
+                return (true);
+            }
+        }
+
+        // Send an "unauthorized" response and an appropriate challenge
+
+        // Next, generate a nOnce token (that is a token which is supposed
+        // to be unique).
+        String nOnce = generateNOnce(request);
+
+        setAuthenticateHeader(request, response, config, nOnce);
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        //      hres.flushBuffer();
+        return (false);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Parse the specified authorization credentials, and return the
+     * associated Principal that these credentials authenticate (if any)
+     * from the specified Realm.  If there is no such Principal, return
+     * <code>null</code>.
+     *
+     * @param request HTTP servlet request
+     * @param authorization Authorization credentials from this request
+     * @param realm Realm used to authenticate Principals
+     */
+    protected static Principal findPrincipal(Request request,
+                                             String authorization,
+                                             Realm realm) {
+
+        //System.out.println("Authorization token : " + authorization);
+        // Validate the authorization credentials format
+        if (authorization == null)
+            return (null);
+        if (!authorization.startsWith("Digest "))
+            return (null);
+        authorization = authorization.substring(7).trim();
+
+
+        StringTokenizer commaTokenizer =
+            new StringTokenizer(authorization, ",");
+
+        String userName = null;
+        String realmName = null;
+        String nOnce = null;
+        String nc = null;
+        String cnonce = null;
+        String qop = null;
+        String uri = null;
+        String response = null;
+        String method = request.getMethod();
+
+        while (commaTokenizer.hasMoreTokens()) {
+            String currentToken = commaTokenizer.nextToken();
+            int equalSign = currentToken.indexOf('=');
+            if (equalSign < 0)
+                return null;
+            String currentTokenName =
+                currentToken.substring(0, equalSign).trim();
+            String currentTokenValue =
+                currentToken.substring(equalSign + 1).trim();
+            if ("username".equals(currentTokenName))
+                userName = removeQuotes(currentTokenValue);
+            if ("realm".equals(currentTokenName))
+                realmName = removeQuotes(currentTokenValue, true);
+            if ("nonce".equals(currentTokenName))
+                nOnce = removeQuotes(currentTokenValue);
+            if ("nc".equals(currentTokenName))
+                nc = removeQuotes(currentTokenValue);
+            if ("cnonce".equals(currentTokenName))
+                cnonce = removeQuotes(currentTokenValue);
+            if ("qop".equals(currentTokenName))
+                qop = removeQuotes(currentTokenValue);
+            if ("uri".equals(currentTokenName))
+                uri = removeQuotes(currentTokenValue);
+            if ("response".equals(currentTokenName))
+                response = removeQuotes(currentTokenValue);
+        }
+
+        if ( (userName == null) || (realmName == null) || (nOnce == null)
+             || (uri == null) || (response == null) )
+            return null;
+
+        // Second MD5 digest used to calculate the digest :
+        // MD5(Method + ":" + uri)
+        String a2 = method + ":" + uri;
+        //System.out.println("A2:" + a2);
+
+        byte[] buffer = null;
+        synchronized (md5Helper) {
+            buffer = md5Helper.digest(a2.getBytes());
+        }
+        String md5a2 = md5Encoder.encode(buffer);
+
+        return (realm.authenticate(userName, response, nOnce, nc, cnonce, qop,
+                                   realmName, md5a2));
+
+    }
+
+
+    /**
+     * Parse the username from the specified authorization string.  If none
+     * can be identified, return <code>null</code>
+     *
+     * @param authorization Authorization string to be parsed
+     */
+    protected String parseUsername(String authorization) {
+
+        //System.out.println("Authorization token : " + authorization);
+        // Validate the authorization credentials format
+        if (authorization == null)
+            return (null);
+        if (!authorization.startsWith("Digest "))
+            return (null);
+        authorization = authorization.substring(7).trim();
+
+        StringTokenizer commaTokenizer =
+            new StringTokenizer(authorization, ",");
+
+        while (commaTokenizer.hasMoreTokens()) {
+            String currentToken = commaTokenizer.nextToken();
+            int equalSign = currentToken.indexOf('=');
+            if (equalSign < 0)
+                return null;
+            String currentTokenName =
+                currentToken.substring(0, equalSign).trim();
+            String currentTokenValue =
+                currentToken.substring(equalSign + 1).trim();
+            if ("username".equals(currentTokenName))
+                return (removeQuotes(currentTokenValue));
+        }
+
+        return (null);
+
+    }
+
+
+    /**
+     * Removes the quotes on a string. RFC2617 states quotes are optional for
+     * all parameters except realm.
+     */
+    protected static String removeQuotes(String quotedString,
+                                         boolean quotesRequired) {
+        //support both quoted and non-quoted
+        if (quotedString.length() > 0 && quotedString.charAt(0) != '"' &&
+                !quotesRequired) {
+            return quotedString;
+        } else if (quotedString.length() > 2) {
+            return quotedString.substring(1, quotedString.length() - 1);
+        } else {
+            return new String();
+        }
+    }
+
+    /**
+     * Removes the quotes on a string.
+     */
+    protected static String removeQuotes(String quotedString) {
+        return removeQuotes(quotedString, false);
+    }
+
+    /**
+     * Generate a unique token. The token is generated according to the
+     * following pattern. NOnceToken = Base64 ( MD5 ( client-IP ":"
+     * time-stamp ":" private-key ) ).
+     *
+     * @param request HTTP Servlet request
+     */
+    protected String generateNOnce(Request request) {
+
+        long currentTime = System.currentTimeMillis();
+
+        String nOnceValue = request.getRemoteAddr() + ":" +
+            currentTime + ":" + key;
+
+        byte[] buffer = null;
+        synchronized (md5Helper) {
+            buffer = md5Helper.digest(nOnceValue.getBytes());
+        }
+        nOnceValue = md5Encoder.encode(buffer);
+
+        return nOnceValue;
+    }
+
+
+    /**
+     * Generates the WWW-Authenticate header.
+     * <p>
+     * The header MUST follow this template :
+     * <pre>
+     *      WWW-Authenticate    = "WWW-Authenticate" ":" "Digest"
+     *                            digest-challenge
+     *
+     *      digest-challenge    = 1#( realm | [ domain ] | nOnce |
+     *                  [ digest-opaque ] |[ stale ] | [ algorithm ] )
+     *
+     *      realm               = "realm" "=" realm-value
+     *      realm-value         = quoted-string
+     *      domain              = "domain" "=" <"> 1#URI <">
+     *      nonce               = "nonce" "=" nonce-value
+     *      nonce-value         = quoted-string
+     *      opaque              = "opaque" "=" quoted-string
+     *      stale               = "stale" "=" ( "true" | "false" )
+     *      algorithm           = "algorithm" "=" ( "MD5" | token )
+     * </pre>
+     *
+     * @param request HTTP Servlet request
+     * @param response HTTP Servlet response
+     * @param config    Login configuration describing how authentication
+     *              should be performed
+     * @param nOnce nonce token
+     */
+    protected void setAuthenticateHeader(Request request,
+                                         Response response,
+                                         LoginConfig config,
+                                         String nOnce) {
+
+        // Get the realm name
+        String realmName = config.getRealmName();
+        if (realmName == null)
+            realmName = request.getServerName() + ":"
+                + request.getServerPort();
+
+        byte[] buffer = null;
+        synchronized (md5Helper) {
+            buffer = md5Helper.digest(nOnce.getBytes());
+        }
+
+        String authenticateHeader = "Digest realm=\"" + realmName + "\", "
+            +  "qop=\"auth\", nonce=\"" + nOnce + "\", " + "opaque=\""
+            + md5Encoder.encode(buffer) + "\"";
+        response.setHeader("WWW-Authenticate", authenticateHeader);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/FormAuthenticator.java b/container/catalina/src/share/org/apache/catalina/authenticator/FormAuthenticator.java
new file mode 100644
index 0000000..07fa401
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/FormAuthenticator.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.authenticator;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Locale;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.coyote.ActionCode;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation of FORM BASED
+ * Authentication, as described in the Servlet API Specification, Version 2.2.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class FormAuthenticator
+    extends AuthenticatorBase {
+    private static Log log = LogFactory.getLog(FormAuthenticator.class);
+
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.FormAuthenticator/1.0";
+
+    /**
+     * Character encoding to use to read the username and password parameters
+     * from the request. If not set, the encoding of the request body will be
+     * used.
+     */
+    protected String characterEncoding = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the character encoding to use to read the username and password.
+     */
+    public String getCharacterEncoding() {
+        return characterEncoding;
+    }
+
+    
+    /**
+     * Set the character encoding to be used to read the username and password. 
+     */
+    public void setCharacterEncoding(String encoding) {
+        characterEncoding = encoding;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config    Login configuration describing how authentication
+     *              should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean authenticate(Request request,
+                                Response response,
+                                LoginConfig config)
+        throws IOException {
+
+        // References to objects we will need later
+        Session session = null;
+
+        // Have we already authenticated someone?
+        Principal principal = request.getUserPrincipal();
+        String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+        if (principal != null) {
+            if (log.isDebugEnabled())
+                log.debug("Already authenticated '" +
+                    principal.getName() + "'");
+            // Associate the session with any existing SSO session
+            if (ssoId != null)
+                associate(ssoId, request.getSessionInternal(true));
+            return (true);
+        }
+
+        // Is there an SSO session against which we can try to reauthenticate?
+        if (ssoId != null) {
+            if (log.isDebugEnabled())
+                log.debug("SSO Id " + ssoId + " set; attempting " +
+                          "reauthentication");
+            // Try to reauthenticate using data cached by SSO.  If this fails,
+            // either the original SSO logon was of DIGEST or SSL (which
+            // we can't reauthenticate ourselves because there is no
+            // cached username and password), or the realm denied
+            // the user's reauthentication for some reason.
+            // In either case we have to prompt the user for a logon */
+            if (reauthenticateFromSSO(ssoId, request))
+                return true;
+        }
+
+        // Have we authenticated this user before but have caching disabled?
+        if (!cache) {
+            session = request.getSessionInternal(true);
+            if (log.isDebugEnabled())
+                log.debug("Checking for reauthenticate in session " + session);
+            String username =
+                (String) session.getNote(Constants.SESS_USERNAME_NOTE);
+            String password =
+                (String) session.getNote(Constants.SESS_PASSWORD_NOTE);
+            if ((username != null) && (password != null)) {
+                if (log.isDebugEnabled())
+                    log.debug("Reauthenticating username '" + username + "'");
+                principal =
+                    context.getRealm().authenticate(username, password);
+                if (principal != null) {
+                    session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
+                    if (!matchRequest(request)) {
+                        register(request, response, principal,
+                                 Constants.FORM_METHOD,
+                                 username, password);
+                        return (true);
+                    }
+                }
+                if (log.isDebugEnabled())
+                    log.debug("Reauthentication failed, proceed normally");
+            }
+        }
+
+        // Is this the re-submit of the original request URI after successful
+        // authentication?  If so, forward the *original* request instead.
+        if (matchRequest(request)) {
+            session = request.getSessionInternal(true);
+            if (log.isDebugEnabled())
+                log.debug("Restore request from session '"
+                          + session.getIdInternal() 
+                          + "'");
+            principal = (Principal)
+                session.getNote(Constants.FORM_PRINCIPAL_NOTE);
+            register(request, response, principal, Constants.FORM_METHOD,
+                     (String) session.getNote(Constants.SESS_USERNAME_NOTE),
+                     (String) session.getNote(Constants.SESS_PASSWORD_NOTE));
+            // If we're caching principals we no longer need the username
+            // and password in the session, so remove them
+            if (cache) {
+                session.removeNote(Constants.SESS_USERNAME_NOTE);
+                session.removeNote(Constants.SESS_PASSWORD_NOTE);
+            }
+            if (restoreRequest(request, session)) {
+                if (log.isDebugEnabled())
+                    log.debug("Proceed to restored request");
+                return (true);
+            } else {
+                if (log.isDebugEnabled())
+                    log.debug("Restore of original request failed");
+                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+                return (false);
+            }
+        }
+
+        // Acquire references to objects we will need to evaluate
+        MessageBytes uriMB = MessageBytes.newInstance();
+        CharChunk uriCC = uriMB.getCharChunk();
+        uriCC.setLimit(-1);
+        String contextPath = request.getContextPath();
+        String requestURI = request.getDecodedRequestURI();
+        response.setContext(request.getContext());
+
+        // Is this the action request from the login page?
+        boolean loginAction =
+            requestURI.startsWith(contextPath) &&
+            requestURI.endsWith(Constants.FORM_ACTION);
+
+        // No -- Save this request and redirect to the form login page
+        if (!loginAction) {
+            session = request.getSessionInternal(true);
+            if (log.isDebugEnabled())
+                log.debug("Save request in session '" + session.getIdInternal() + "'");
+            try {
+                saveRequest(request, session);
+            } catch (IOException ioe) {
+                log.debug("Request body too big to save during authentication");
+                response.sendError(HttpServletResponse.SC_FORBIDDEN,
+                        sm.getString("authenticator.requestBodyTooBig"));
+                return (false);
+            }
+            RequestDispatcher disp =
+                context.getServletContext().getRequestDispatcher
+                (config.getLoginPage());
+            try {
+                disp.forward(request.getRequest(), response.getResponse());
+                response.finishResponse();
+            } catch (Throwable t) {
+                log.warn("Unexpected error forwarding to login page", t);
+            }
+            return (false);
+        }
+
+        // Yes -- Validate the specified credentials and redirect
+        // to the error page if they are not correct
+        Realm realm = context.getRealm();
+        if (characterEncoding != null) {
+            request.setCharacterEncoding(characterEncoding);
+        }
+        String username = request.getParameter(Constants.FORM_USERNAME);
+        String password = request.getParameter(Constants.FORM_PASSWORD);
+        if (log.isDebugEnabled())
+            log.debug("Authenticating username '" + username + "'");
+        principal = realm.authenticate(username, password);
+        if (principal == null) {
+            RequestDispatcher disp =
+                context.getServletContext().getRequestDispatcher
+                (config.getErrorPage());
+            try {
+                disp.forward(request.getRequest(), response.getResponse());
+            } catch (Throwable t) {
+                log.warn("Unexpected error forwarding to error page", t);
+            }
+            return (false);
+        }
+
+        if (log.isDebugEnabled())
+            log.debug("Authentication of '" + username + "' was successful");
+
+        if (session == null)
+            session = request.getSessionInternal(false);
+        if (session == null) {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug
+                    ("User took so long to log on the session expired");
+            response.sendError(HttpServletResponse.SC_REQUEST_TIMEOUT,
+                               sm.getString("authenticator.sessionExpired"));
+            return (false);
+        }
+
+        // Save the authenticated Principal in our session
+        session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
+
+        // Save the username and password as well
+        session.setNote(Constants.SESS_USERNAME_NOTE, username);
+        session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+
+        // Redirect the user to the original request URI (which will cause
+        // the original request to be restored)
+        requestURI = savedRequestURL(session);
+        if (log.isDebugEnabled())
+            log.debug("Redirecting to original '" + requestURI + "'");
+        if (requestURI == null)
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+                               sm.getString("authenticator.formlogin"));
+        else
+            response.sendRedirect(response.encodeRedirectURL(requestURI));
+        return (false);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Does this request match the saved one (so that it must be the redirect
+     * we signalled after successful authentication?
+     *
+     * @param request The request to be verified
+     */
+    protected boolean matchRequest(Request request) {
+
+      // Has a session been created?
+      Session session = request.getSessionInternal(false);
+      if (session == null)
+          return (false);
+
+      // Is there a saved request?
+      SavedRequest sreq = (SavedRequest)
+          session.getNote(Constants.FORM_REQUEST_NOTE);
+      if (sreq == null)
+          return (false);
+
+      // Is there a saved principal?
+      if (session.getNote(Constants.FORM_PRINCIPAL_NOTE) == null)
+          return (false);
+
+      // Does the request URI match?
+      String requestURI = request.getRequestURI();
+      if (requestURI == null)
+          return (false);
+      return (requestURI.equals(request.getRequestURI()));
+
+    }
+
+
+    /**
+     * Restore the original request from information stored in our session.
+     * If the original request is no longer present (because the session
+     * timed out), return <code>false</code>; otherwise, return
+     * <code>true</code>.
+     *
+     * @param request The request to be restored
+     * @param session The session containing the saved information
+     */
+    protected boolean restoreRequest(Request request, Session session)
+        throws IOException {
+
+        // Retrieve and remove the SavedRequest object from our session
+        SavedRequest saved = (SavedRequest)
+            session.getNote(Constants.FORM_REQUEST_NOTE);
+        session.removeNote(Constants.FORM_REQUEST_NOTE);
+        session.removeNote(Constants.FORM_PRINCIPAL_NOTE);
+        if (saved == null)
+            return (false);
+
+        // Modify our current request to reflect the original one
+        request.clearCookies();
+        Iterator cookies = saved.getCookies();
+        while (cookies.hasNext()) {
+            request.addCookie((Cookie) cookies.next());
+        }
+
+        request.getCoyoteRequest().getMimeHeaders().recycle();
+        Iterator names = saved.getHeaderNames();
+        while (names.hasNext()) {
+            String name = (String) names.next();
+            Iterator values = saved.getHeaderValues(name);
+            while (values.hasNext()) {
+                request.addHeader(name, (String) values.next());
+            }
+        }
+        
+        request.clearLocales();
+        Iterator locales = saved.getLocales();
+        while (locales.hasNext()) {
+            request.addLocale((Locale) locales.next());
+        }
+        
+        request.getCoyoteRequest().getParameters().recycle();
+        
+        if ("POST".equalsIgnoreCase(saved.getMethod())) {
+            ByteChunk body = saved.getBody();
+            
+            if (body != null) {
+                request.getCoyoteRequest().action
+                    (ActionCode.ACTION_REQ_SET_BODY_REPLAY, body);
+    
+                // Set content type
+                MessageBytes contentType = MessageBytes.newInstance();
+                contentType.setString("application/x-www-form-urlencoded");
+                request.getCoyoteRequest().setContentType(contentType);
+            }
+        }
+        request.getCoyoteRequest().method().setString(saved.getMethod());
+
+        request.getCoyoteRequest().queryString().setString
+            (saved.getQueryString());
+
+        request.getCoyoteRequest().requestURI().setString
+            (saved.getRequestURI());
+        return (true);
+
+    }
+
+
+    /**
+     * Save the original request information into our session.
+     *
+     * @param request The request to be saved
+     * @param session The session to contain the saved information
+     * @throws IOException
+     */
+    private void saveRequest(Request request, Session session)
+        throws IOException {
+
+        // Create and populate a SavedRequest object for this request
+        SavedRequest saved = new SavedRequest();
+        Cookie cookies[] = request.getCookies();
+        if (cookies != null) {
+            for (int i = 0; i < cookies.length; i++)
+                saved.addCookie(cookies[i]);
+        }
+        Enumeration names = request.getHeaderNames();
+        while (names.hasMoreElements()) {
+            String name = (String) names.nextElement();
+            Enumeration values = request.getHeaders(name);
+            while (values.hasMoreElements()) {
+                String value = (String) values.nextElement();
+                saved.addHeader(name, value);
+            }
+        }
+        Enumeration locales = request.getLocales();
+        while (locales.hasMoreElements()) {
+            Locale locale = (Locale) locales.nextElement();
+            saved.addLocale(locale);
+        }
+
+        if ("POST".equalsIgnoreCase(request.getMethod())) {
+            ByteChunk body = new ByteChunk();
+            body.setLimit(request.getConnector().getMaxSavePostSize());
+
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+            InputStream is = request.getInputStream();
+        
+            while ( (bytesRead = is.read(buffer) ) >= 0) {
+                body.append(buffer, 0, bytesRead);
+            }
+            saved.setBody(body);
+        }
+
+        saved.setMethod(request.getMethod());
+        saved.setQueryString(request.getQueryString());
+        saved.setRequestURI(request.getRequestURI());
+
+        // Stash the SavedRequest in our session for later use
+        session.setNote(Constants.FORM_REQUEST_NOTE, saved);
+
+    }
+
+
+    /**
+     * Return the request URI (with the corresponding query string, if any)
+     * from the saved request so that we can redirect to it.
+     *
+     * @param session Our current session
+     */
+    private String savedRequestURL(Session session) {
+
+        SavedRequest saved =
+            (SavedRequest) session.getNote(Constants.FORM_REQUEST_NOTE);
+        if (saved == null)
+            return (null);
+        StringBuffer sb = new StringBuffer(saved.getRequestURI());
+        if (saved.getQueryString() != null) {
+            sb.append('?');
+            sb.append(saved.getQueryString());
+        }
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings.properties
new file mode 100644
index 0000000..e606f6b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings.properties
@@ -0,0 +1,14 @@
+authenticator.alreadyStarted=Security Interceptor has already been started
+authenticator.certificates=No client certificate chain in this request
+authenticator.forbidden=Access to the requested resource has been denied
+authenticator.formlogin=Invalid direct reference to form login page
+authenticator.invalid=Invalid client certificate chain in this request
+authenticator.keystore=Exception loading key store
+authenticator.manager=Exception initializing trust managers
+authenticator.notAuthenticated=Configuration error:  Cannot perform access control without an authenticated principal
+authenticator.notContext=Configuration error:  Must be attached to a Context
+authenticator.notStarted=Security Interceptor has not yet been started
+authenticator.requestBodyTooBig=The request body was too large to be cached during the authentication process
+authenticator.sessionExpired=The time allowed for the login process has been exceeded. If you wish to continue you must either click back twice and re-click the link you requested or close and re-open your browser
+authenticator.unauthorized=Cannot authenticate with the provided credentials
+authenticator.userDataConstraint=This request violates a User Data constraint for this application
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings_es.properties
new file mode 100644
index 0000000..37da93c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings_es.properties
@@ -0,0 +1,12 @@
+authenticator.alreadyStarted=El interceptor de seguridad ya ha sido arrancado
+authenticator.certificates=No hay cadena de certificados del cliente en esta petición
+authenticator.forbidden=El acceso al recurso pedido ha sido denegado
+authenticator.formlogin=Referencia directa al formulario de conexión (página de formulario de login) inválida
+authenticator.invalid=No es válida la cadena de certificados del cliente en esta petición
+authenticator.keystore=Excepción cargando el almacén de claves
+authenticator.manager=Excepción inicializando administradores de confianza
+authenticator.notAuthenticated=Error de Configuración: No se pueden realizar funciones de control de acceso sin un principal autenticado
+authenticator.notContext=Error de Configuración: Debe de estar unido a un Contexto
+authenticator.notStarted=El Interceptor de seguridad no sido aún iniciado
+authenticator.unauthorized=Imposible autenticar mediante las credenciales suministradas
+authenticator.userDataConstraint=Esta petición viola una Restrición de Datos de usuario para esta aplicación
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings_fr.properties
new file mode 100644
index 0000000..04e5d6b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings_fr.properties
@@ -0,0 +1,12 @@
+authenticator.alreadyStarted=L''intercepteur de sécurité (security interceptor) a déjà été démarré
+authenticator.certificates=Aucune chaîne de certificat client (client certificate chain) dans cette requête
+authenticator.forbidden=L''accès à la ressource demandée a été interdit
+authenticator.formlogin=Référence directe à la form de connexion (form login page) invalide
+authenticator.invalid=Chaîne de certificat client invalide dans cette requête
+authenticator.keystore=Exception lors du chargement du référentiel de clefs (key store)
+authenticator.manager=Exception lors de l''initialisation des gestionnaires d''authentification (trust managers)
+authenticator.notAuthenticated=Erreur de configuration:  Impossible de procéder à un contrôle d''accès sans un principal authentifié (authenticated principal)
+authenticator.notContext=Erreur de configuration:  Doit être attaché à un contexte
+authenticator.notStarted=L''intercepteur de sécurité (security interceptor) n''a pas encore été démarré
+authenticator.unauthorized=Impossible d''authentifier avec les crédits fournis (provided credentials)
+authenticator.userDataConstraint=Cette requête viole une contrainte donnée utilisateur (user data constraint) pour cette application
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings_ja.properties
new file mode 100644
index 0000000..5d7fd58
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/LocalStrings_ja.properties
@@ -0,0 +1,13 @@
+authenticator.alreadyStarted=\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30a4\u30f3\u30bf\u30fc\u30bb\u30d7\u30bf\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+authenticator.certificates=\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u306f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u8a8d\u8a3c\u30c1\u30a7\u30fc\u30f3\u304c\u3042\u308a\u307e\u305b\u3093
+authenticator.forbidden=\u30ea\u30af\u30a8\u30b9\u30c8\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9\u3078\u306e\u30a2\u30af\u30bb\u30b9\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f
+authenticator.formlogin=\u30d5\u30a9\u30fc\u30e0\u30ed\u30b0\u30a4\u30f3\u30da\u30fc\u30b8\u3078\u306e\u7121\u52b9\u306a\u76f4\u63a5\u53c2\u7167\u3067\u3059
+authenticator.invalid=\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u7121\u52b9\u306a\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u8a8d\u8a3c\u30c1\u30a7\u30fc\u30f3\u304c\u3042\u308a\u307e\u3059
+authenticator.keystore=\u30ad\u30fc\u30b9\u30c8\u30a2\u3092\u30ed\u30fc\u30c9\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+authenticator.manager=\u30c8\u30e9\u30b9\u30c8\u30de\u30cd\u30fc\u30b8\u30e3\u3092\u521d\u671f\u5316\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+authenticator.notAuthenticated=\u8a2d\u5b9a\u30a8\u30e9\u30fc: \u8a8d\u8a3c\u3055\u308c\u305f\u4e3b\u4f53\u306a\u3057\u306b\u30a2\u30af\u30bb\u30b9\u5236\u5fa1\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093
+authenticator.notContext=\u8a2d\u5b9a\u30a8\u30e9\u30fc: \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u6307\u5b9a\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+authenticator.notStarted=\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30a4\u30f3\u30bf\u30fc\u30bb\u30d7\u30bf\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+authenticator.sessionExpired=\u30ed\u30b0\u30a4\u30f3\u30d7\u30ed\u30bb\u30b9\u306b\u8a8d\u3081\u3089\u308c\u3066\u3044\u305f\u6642\u9593\u304c\u904e\u304e\u307e\u3057\u305f\u3002\u7d99\u7d9a\u3057\u305f\u3044\u306a\u3089\u3070\uff0c\u30d0\u30c3\u30af\u30dc\u30bf\u30f3\u30922\u5ea6\u62bc\u3057\u3066\u304b\u3089\u518d\u5ea6\u30ea\u30f3\u30af\u3092\u62bc\u3059\u304b\uff0c\u30d6\u30e9\u30a6\u30b6\u3092\u7acb\u3061\u4e0a\u3052\u76f4\u3057\u3066\u304f\u3060\u3055\u3044
+authenticator.unauthorized=\u7528\u610f\u3055\u308c\u305f\u8a3c\u660e\u66f8\u3067\u8a8d\u8a3c\u3067\u304d\u307e\u305b\u3093
+authenticator.userDataConstraint=\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306f\u3001\u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u306e\u5236\u9650\u306b\u9055\u53cd\u3057\u3066\u3044\u307e\u3059
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/NonLoginAuthenticator.java b/container/catalina/src/share/org/apache/catalina/authenticator/NonLoginAuthenticator.java
new file mode 100644
index 0000000..83fb4b2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/NonLoginAuthenticator.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.authenticator;
+
+
+import java.io.IOException;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.LoginConfig;
+
+
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation that checks
+ * only security constraints not involving user authentication.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class NonLoginAuthenticator
+    extends AuthenticatorBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.authenticator.NonLoginAuthenticator/1.0";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config    Login configuration describing how authentication
+     *              should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean authenticate(Request request,
+                                Response response,
+                                LoginConfig config)
+        throws IOException {
+
+        /*  Associating this request's session with an SSO would allow
+            coordinated session invalidation, but should the session for
+            a webapp that the user didn't log into be invalidated when
+            another session is logged out?
+        String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+        if (ssoId != null)
+            associate(ssoId, getSession(request, true));
+        */
+        
+        if (containerLog.isDebugEnabled())
+            containerLog.debug("User authentication is not required");
+        return (true);
+
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/SSLAuthenticator.java b/container/catalina/src/share/org/apache/catalina/authenticator/SSLAuthenticator.java
new file mode 100644
index 0000000..cb12129
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/SSLAuthenticator.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.authenticator;
+
+
+import java.io.IOException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.coyote.ActionCode;
+import org.apache.catalina.Globals;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.LoginConfig;
+
+
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation of authentication
+ * that utilizes SSL certificates to identify client users.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SSLAuthenticator
+    extends AuthenticatorBase {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.SSLAuthenticator/1.0";
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Authenticate the user by checking for the existence of a certificate
+     * chain, and optionally asking a trust manager to validate that we trust
+     * this user.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config    Login configuration describing how authentication
+     *              should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean authenticate(Request request,
+                                Response response,
+                                LoginConfig config)
+        throws IOException {
+
+        // Have we already authenticated someone?
+        Principal principal = request.getUserPrincipal();
+        //String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+        if (principal != null) {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug("Already authenticated '" + principal.getName() + "'");
+            // Associate the session with any existing SSO session in order
+            // to get coordinated session invalidation at logout
+            String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+            if (ssoId != null)
+                associate(ssoId, request.getSessionInternal(true));
+            return (true);
+        }
+
+        // NOTE: We don't try to reauthenticate using any existing SSO session,
+        // because that will only work if the original authentication was
+        // BASIC or FORM, which are less secure than the CLIENT-CERT auth-type
+        // specified for this webapp
+        //
+        // Uncomment below to allow previous FORM or BASIC authentications
+        // to authenticate users for this webapp
+        // TODO make this a configurable attribute (in SingleSignOn??)
+        /*
+        // Is there an SSO session against which we can try to reauthenticate?
+        if (ssoId != null) {
+            if (log.isDebugEnabled())
+                log.debug("SSO Id " + ssoId + " set; attempting " +
+                          "reauthentication");
+            // Try to reauthenticate using data cached by SSO.  If this fails,
+            // either the original SSO logon was of DIGEST or SSL (which
+            // we can't reauthenticate ourselves because there is no
+            // cached username and password), or the realm denied
+            // the user's reauthentication for some reason.
+            // In either case we have to prompt the user for a logon
+            if (reauthenticateFromSSO(ssoId, request))
+                return true;
+        }
+        */
+
+        // Retrieve the certificate chain for this client
+        if (containerLog.isDebugEnabled())
+            containerLog.debug(" Looking up certificates");
+
+        X509Certificate certs[] = (X509Certificate[])
+            request.getAttribute(Globals.CERTIFICATES_ATTR);
+        if ((certs == null) || (certs.length < 1)) {
+            request.getCoyoteRequest().action
+                              (ActionCode.ACTION_REQ_SSL_CERTIFICATE, null);
+            certs = (X509Certificate[])
+                request.getAttribute(Globals.CERTIFICATES_ATTR);
+        }
+        if ((certs == null) || (certs.length < 1)) {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug("  No certificates included with this request");
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+                               sm.getString("authenticator.certificates"));
+            return (false);
+        }
+
+        // Authenticate the specified certificate chain
+        principal = context.getRealm().authenticate(certs);
+        if (principal == null) {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug("  Realm.authenticate() returned false");
+            response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
+                               sm.getString("authenticator.unauthorized"));
+            return (false);
+        }
+
+        // Cache the principal (if requested) and record this authentication
+        register(request, response, principal, Constants.CERT_METHOD,
+                 null, null);
+        return (true);
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Initialize the database we will be using for client verification
+     * and certificate validation (if any).
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        super.start();
+
+    }
+
+
+    /**
+     * Finalize the database we used for client verification and
+     * certificate validation (if any).
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void stop() throws LifecycleException {
+
+        super.stop();
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/SavedRequest.java b/container/catalina/src/share/org/apache/catalina/authenticator/SavedRequest.java
new file mode 100644
index 0000000..2503fd0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/SavedRequest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.authenticator;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+
+import javax.servlet.http.Cookie;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+
+/**
+ * Object that saves the critical information from a request so that
+ * form-based authentication can reproduce it once the user has been
+ * authenticated.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b> - It is assumed that this object is accessed
+ * only from the context of a single thread, so no synchronization around
+ * internal collection classes is performed.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class SavedRequest {
+
+
+    /**
+     * The set of Cookies associated with this Request.
+     */
+    private ArrayList cookies = new ArrayList();
+
+    public void addCookie(Cookie cookie) {
+        cookies.add(cookie);
+    }
+
+    public Iterator getCookies() {
+        return (cookies.iterator());
+    }
+
+
+    /**
+     * The set of Headers associated with this Request.  Each key is a header
+     * name, while the value is a ArrayList containing one or more actual
+     * values for this header.  The values are returned as an Iterator when
+     * you ask for them.
+     */
+    private HashMap headers = new HashMap();
+
+    public void addHeader(String name, String value) {
+        ArrayList values = (ArrayList) headers.get(name);
+        if (values == null) {
+            values = new ArrayList();
+            headers.put(name, values);
+        }
+        values.add(value);
+    }
+
+    public Iterator getHeaderNames() {
+        return (headers.keySet().iterator());
+    }
+
+    public Iterator getHeaderValues(String name) {
+        ArrayList values = (ArrayList) headers.get(name);
+        if (values == null)
+            return ((new ArrayList()).iterator());
+        else
+            return (values.iterator());
+    }
+
+
+    /**
+     * The set of Locales associated with this Request.
+     */
+    private ArrayList locales = new ArrayList();
+
+    public void addLocale(Locale locale) {
+        locales.add(locale);
+    }
+
+    public Iterator getLocales() {
+        return (locales.iterator());
+    }
+
+
+    /**
+     * The request method used on this Request.
+     */
+    private String method = null;
+
+    public String getMethod() {
+        return (this.method);
+    }
+
+    public void setMethod(String method) {
+        this.method = method;
+    }
+
+
+
+    /**
+     * The set of request parameters associated with this Request.  Each
+     * entry is keyed by the parameter name, pointing at a String array of
+     * the corresponding values.
+     */
+    private HashMap parameters = new HashMap();
+
+    public void addParameter(String name, String values[]) {
+        parameters.put(name, values);
+    }
+
+    public Iterator getParameterNames() {
+        return (parameters.keySet().iterator());
+    }
+
+    public String[] getParameterValues(String name) {
+        return ((String[]) parameters.get(name));
+    }
+
+
+    /**
+     * The query string associated with this Request.
+     */
+    private String queryString = null;
+
+    public String getQueryString() {
+        return (this.queryString);
+    }
+
+    public void setQueryString(String queryString) {
+        this.queryString = queryString;
+    }
+
+
+    /**
+     * The request URI associated with this Request.
+     */
+    private String requestURI = null;
+
+    public String getRequestURI() {
+        return (this.requestURI);
+    }
+
+    public void setRequestURI(String requestURI) {
+        this.requestURI = requestURI;
+    }
+
+    
+    /**
+     * The body of this request.
+     */
+    private ByteChunk body = null;
+    
+    public ByteChunk getBody() {
+        return (this.body);
+    }
+
+    public void setBody(ByteChunk body) {
+        this.body = body;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/SingleSignOn.java b/container/catalina/src/share/org/apache/catalina/authenticator/SingleSignOn.java
new file mode 100644
index 0000000..4923b28
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/SingleSignOn.java
@@ -0,0 +1,676 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.authenticator;
+
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.SessionListener;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+
+
+/**
+ * A <strong>Valve</strong> that supports a "single sign on" user experience,
+ * where the security identity of a user who successfully authenticates to one
+ * web application is propogated to other web applications in the same
+ * security domain.  For successful use, the following requirements must
+ * be met:
+ * <ul>
+ * <li>This Valve must be configured on the Container that represents a
+ *     virtual host (typically an implementation of <code>Host</code>).</li>
+ * <li>The <code>Realm</code> that contains the shared user and role
+ *     information must be configured on the same Container (or a higher
+ *     one), and not overridden at the web application level.</li>
+ * <li>The web applications themselves must use one of the standard
+ *     Authenticators found in the
+ *     <code>org.apache.catalina.authenticator</code> package.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SingleSignOn
+    extends ValveBase
+    implements Lifecycle, SessionListener {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The cache of SingleSignOnEntry instances for authenticated Principals,
+     * keyed by the cookie value that is used to select them.
+     */
+    protected HashMap cache = new HashMap();
+
+
+    /**
+     * Descriptive information about this Valve implementation.
+     */
+    protected static String info =
+        "org.apache.catalina.authenticator.SingleSignOn";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+    /**
+     * Indicates whether this valve should require a downstream Authenticator to
+     * reauthenticate each request, or if it itself can bind a UserPrincipal
+     * and AuthType object to the request.
+     */
+    private boolean requireReauthentication = false;
+
+    /**
+     * The cache of single sign on identifiers, keyed by the Session that is
+     * associated with them.
+     */
+    protected HashMap reverse = new HashMap();
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected final static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Component started flag.
+     */
+    protected boolean started = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Gets whether each request needs to be reauthenticated (by an
+     * Authenticator downstream in the pipeline) to the security
+     * <code>Realm</code>, or if this Valve can itself bind security info
+     * to the request based on the presence of a valid SSO entry without
+     * rechecking with the <code>Realm</code..
+     *
+     * @return  <code>true</code> if it is required that a downstream
+     *          Authenticator reauthenticate each request before calls to
+     *          <code>HttpServletRequest.setUserPrincipal()</code>
+     *          and <code>HttpServletRequest.setAuthType()</code> are made;
+     *          <code>false</code> if the <code>Valve</code> can itself make
+     *          those calls relying on the presence of a valid SingleSignOn
+     *          entry associated with the request.
+     *
+     * @see #setRequireReauthentication
+     */
+    public boolean getRequireReauthentication()
+    {
+        return requireReauthentication;
+    }
+
+
+    /**
+     * Sets whether each request needs to be reauthenticated (by an
+     * Authenticator downstream in the pipeline) to the security
+     * <code>Realm</code>, or if this Valve can itself bind security info
+     * to the request, based on the presence of a valid SSO entry, without
+     * rechecking with the <code>Realm</code.
+     * <p>
+     * If this property is <code>false</code> (the default), this
+     * <code>Valve</code> will bind a UserPrincipal and AuthType to the request
+     * if a valid SSO entry is associated with the request.  It will not notify
+     * the security <code>Realm</code> of the incoming request.
+     * <p>
+     * This property should be set to <code>true</code> if the overall server
+     * configuration requires that the <code>Realm</code> reauthenticate each
+     * request thread.  An example of such a configuration would be one where
+     * the <code>Realm</code> implementation provides security for both a
+     * web tier and an associated EJB tier, and needs to set security
+     * credentials on each request thread in order to support EJB access.
+     * <p>
+     * If this property is set to <code>true</code>, this Valve will set flags
+     * on the request notifying the downstream Authenticator that the request
+     * is associated with an SSO session.  The Authenticator will then call its
+     * {@link AuthenticatorBase#reauthenticateFromSSO reauthenticateFromSSO}
+     * method to attempt to reauthenticate the request to the
+     * <code>Realm</code>, using any credentials that were cached with this
+     * Valve.
+     * <p>
+     * The default value of this property is <code>false</code>, in order
+     * to maintain backward compatibility with previous versions of Tomcat.
+     *
+     * @param required  <code>true</code> if it is required that a downstream
+     *                  Authenticator reauthenticate each request before calls
+     *                  to  <code>HttpServletRequest.setUserPrincipal()</code>
+     *                  and <code>HttpServletRequest.setAuthType()</code> are
+     *                  made; <code>false</code> if the <code>Valve</code> can
+     *                  itself make those calls relying on the presence of a
+     *                  valid SingleSignOn entry associated with the request.
+     *
+     * @see AuthenticatorBase#reauthenticateFromSSO
+     */
+    public void setRequireReauthentication(boolean required)
+    {
+        this.requireReauthentication = required;
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (sm.getString("authenticator.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("authenticator.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+    }
+
+
+    // ------------------------------------------------ SessionListener Methods
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event SessionEvent that has occurred
+     */
+    public void sessionEvent(SessionEvent event) {
+
+        // We only care about session destroyed events
+        if (!Session.SESSION_DESTROYED_EVENT.equals(event.getType())
+                && (!Session.SESSION_PASSIVATED_EVENT.equals(event.getType())))
+            return;
+
+        // Look up the single session id associated with this session (if any)
+        Session session = event.getSession();
+        if (containerLog.isDebugEnabled())
+            containerLog.debug("Process session destroyed on " + session);
+
+        String ssoId = null;
+        synchronized (reverse) {
+            ssoId = (String) reverse.get(session);
+        }
+        if (ssoId == null)
+            return;
+
+        // Was the session destroyed as the result of a timeout?
+        // If so, we'll just remove the expired session from the
+        // SSO.  If the session was logged out, we'll log out
+        // of all session associated with the SSO.
+        if (((session.getMaxInactiveInterval() > 0)
+            && (System.currentTimeMillis() - session.getLastAccessedTime() >=
+                session.getMaxInactiveInterval() * 1000)) 
+            || (Session.SESSION_PASSIVATED_EVENT.equals(event.getType()))) {
+            removeSession(ssoId, session);
+        } else {
+            // The session was logged out.
+            // Deregister this single session id, invalidating 
+            // associated sessions
+            deregister(ssoId);
+        }
+
+    }
+
+
+    // ---------------------------------------------------------- Valve Methods
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Perform single-sign-on support processing for this request.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        request.removeNote(Constants.REQ_SSOID_NOTE);
+
+        // Has a valid user already been authenticated?
+        if (containerLog.isDebugEnabled())
+            containerLog.debug("Process request for '" + request.getRequestURI() + "'");
+        if (request.getUserPrincipal() != null) {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug(" Principal '" + request.getUserPrincipal().getName() +
+                    "' has already been authenticated");
+            getNext().invoke(request, response);
+            return;
+        }
+
+        // Check for the single sign on cookie
+        if (containerLog.isDebugEnabled())
+            containerLog.debug(" Checking for SSO cookie");
+        Cookie cookie = null;
+        Cookie cookies[] = request.getCookies();
+        if (cookies == null)
+            cookies = new Cookie[0];
+        for (int i = 0; i < cookies.length; i++) {
+            if (Constants.SINGLE_SIGN_ON_COOKIE.equals(cookies[i].getName())) {
+                cookie = cookies[i];
+                break;
+            }
+        }
+        if (cookie == null) {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug(" SSO cookie is not present");
+            getNext().invoke(request, response);
+            return;
+        }
+
+        // Look up the cached Principal associated with this cookie value
+        if (containerLog.isDebugEnabled())
+            containerLog.debug(" Checking for cached principal for " + cookie.getValue());
+        SingleSignOnEntry entry = lookup(cookie.getValue());
+        if (entry != null) {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug(" Found cached principal '" +
+                    entry.getPrincipal().getName() + "' with auth type '" +
+                    entry.getAuthType() + "'");
+            request.setNote(Constants.REQ_SSOID_NOTE, cookie.getValue());
+            // Only set security elements if reauthentication is not required
+            if (!getRequireReauthentication()) {
+                request.setAuthType(entry.getAuthType());
+                request.setUserPrincipal(entry.getPrincipal());
+            }
+        } else {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug(" No cached principal found, erasing SSO cookie");
+            cookie.setMaxAge(0);
+            response.addCookie(cookie);
+        }
+
+        // Invoke the next Valve in our pipeline
+        getNext().invoke(request, response);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String rendering of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SingleSignOn[");
+        if (container == null )
+            sb.append("Container is null");
+        else
+            sb.append(container.getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Associate the specified single sign on identifier with the
+     * specified Session.
+     *
+     * @param ssoId Single sign on identifier
+     * @param session Session to be associated
+     */
+    protected void associate(String ssoId, Session session) {
+
+        if (containerLog.isDebugEnabled())
+            containerLog.debug("Associate sso id " + ssoId + " with session " + session);
+
+        SingleSignOnEntry sso = lookup(ssoId);
+        if (sso != null)
+            sso.addSession(this, session);
+        synchronized (reverse) {
+            reverse.put(session, ssoId);
+        }
+
+    }
+
+    /**
+     * Deregister the specified session.  If it is the last session,
+     * then also get rid of the single sign on identifier
+     *
+     * @param ssoId Single sign on identifier
+     * @param session Session to be deregistered
+     */
+    protected void deregister(String ssoId, Session session) {
+
+        synchronized (reverse) {
+            reverse.remove(session);
+        }
+
+        SingleSignOnEntry sso = lookup(ssoId);
+        if ( sso == null )
+            return;
+
+        sso.removeSession( session );
+
+        // see if we are the last session, if so blow away ssoId
+        Session sessions[] = sso.findSessions();
+        if ( sessions == null || sessions.length == 0 ) {
+            synchronized (cache) {
+                sso = (SingleSignOnEntry) cache.remove(ssoId);
+            }
+        }
+
+    }
+
+
+    /**
+     * Deregister the specified single sign on identifier, and invalidate
+     * any associated sessions.
+     *
+     * @param ssoId Single sign on identifier to deregister
+     */
+    protected void deregister(String ssoId) {
+
+        if (containerLog.isDebugEnabled())
+            containerLog.debug("Deregistering sso id '" + ssoId + "'");
+
+        // Look up and remove the corresponding SingleSignOnEntry
+        SingleSignOnEntry sso = null;
+        synchronized (cache) {
+            sso = (SingleSignOnEntry) cache.remove(ssoId);
+        }
+
+        if (sso == null)
+            return;
+
+        // Expire any associated sessions
+        Session sessions[] = sso.findSessions();
+        for (int i = 0; i < sessions.length; i++) {
+            if (containerLog.isTraceEnabled())
+                containerLog.trace(" Invalidating session " + sessions[i]);
+            // Remove from reverse cache first to avoid recursion
+            synchronized (reverse) {
+                reverse.remove(sessions[i]);
+            }
+            // Invalidate this session
+            sessions[i].expire();
+        }
+
+        // NOTE:  Clients may still possess the old single sign on cookie,
+        // but it will be removed on the next request since it is no longer
+        // in the cache
+
+    }
+
+
+    /**
+     * Attempts reauthentication to the given <code>Realm</code> using
+     * the credentials associated with the single sign-on session
+     * identified by argument <code>ssoId</code>.
+     * <p>
+     * If reauthentication is successful, the <code>Principal</code> and
+     * authorization type associated with the SSO session will be bound
+     * to the given <code>Request</code> object via calls to 
+     * {@link Request#setAuthType Request.setAuthType()} and 
+     * {@link Request#setUserPrincipal Request.setUserPrincipal()}
+     * </p>
+     *
+     * @param ssoId     identifier of SingleSignOn session with which the
+     *                  caller is associated
+     * @param realm     Realm implementation against which the caller is to
+     *                  be authenticated
+     * @param request   the request that needs to be authenticated
+     * 
+     * @return  <code>true</code> if reauthentication was successful,
+     *          <code>false</code> otherwise.
+     */
+    protected boolean reauthenticate(String ssoId, Realm realm,
+                                     Request request) {
+
+        if (ssoId == null || realm == null)
+            return false;
+
+        boolean reauthenticated = false;
+
+        SingleSignOnEntry entry = lookup(ssoId);
+        if (entry != null && entry.getCanReauthenticate()) {
+            
+            String username = entry.getUsername();
+            if (username != null) {
+                Principal reauthPrincipal =
+                        realm.authenticate(username, entry.getPassword());                
+                if (reauthPrincipal != null) {                    
+                    reauthenticated = true;                    
+                    // Bind the authorization credentials to the request
+                    request.setAuthType(entry.getAuthType());
+                    request.setUserPrincipal(reauthPrincipal);
+                }
+            }
+        }
+
+        return reauthenticated;
+    }
+
+
+    /**
+     * Register the specified Principal as being associated with the specified
+     * value for the single sign on identifier.
+     *
+     * @param ssoId Single sign on identifier to register
+     * @param principal Associated user principal that is identified
+     * @param authType Authentication type used to authenticate this
+     *  user principal
+     * @param username Username used to authenticate this user
+     * @param password Password used to authenticate this user
+     */
+    protected void register(String ssoId, Principal principal, String authType,
+                  String username, String password) {
+
+        if (containerLog.isDebugEnabled())
+            containerLog.debug("Registering sso id '" + ssoId + "' for user '" +
+                principal.getName() + "' with auth type '" + authType + "'");
+
+        synchronized (cache) {
+            cache.put(ssoId, new SingleSignOnEntry(principal, authType,
+                                                   username, password));
+        }
+
+    }
+
+
+    /**
+     * Updates any <code>SingleSignOnEntry</code> found under key
+     * <code>ssoId</code> with the given authentication data.
+     * <p>
+     * The purpose of this method is to allow an SSO entry that was
+     * established without a username/password combination (i.e. established
+     * following DIGEST or CLIENT-CERT authentication) to be updated with
+     * a username and password if one becomes available through a subsequent
+     * BASIC or FORM authentication.  The SSO entry will then be usable for
+     * reauthentication.
+     * <p>
+     * <b>NOTE:</b> Only updates the SSO entry if a call to
+     * <code>SingleSignOnEntry.getCanReauthenticate()</code> returns
+     * <code>false</code>; otherwise, it is assumed that the SSO entry already
+     * has sufficient information to allow reauthentication and that no update
+     * is needed.
+     *
+     * @param ssoId     identifier of Single sign to be updated
+     * @param principal the <code>Principal</code> returned by the latest
+     *                  call to <code>Realm.authenticate</code>.
+     * @param authType  the type of authenticator used (BASIC, CLIENT-CERT,
+     *                  DIGEST or FORM)
+     * @param username  the username (if any) used for the authentication
+     * @param password  the password (if any) used for the authentication
+     */
+    protected void update(String ssoId, Principal principal, String authType,
+                          String username, String password) {
+
+        SingleSignOnEntry sso = lookup(ssoId);
+        if (sso != null && !sso.getCanReauthenticate()) {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug("Update sso id " + ssoId + " to auth type " + authType);
+
+            synchronized(sso) {
+                sso.updateCredentials(principal, authType, username, password);
+            }
+
+        }
+    }
+
+
+    /**
+     * Look up and return the cached SingleSignOn entry associated with this
+     * sso id value, if there is one; otherwise return <code>null</code>.
+     *
+     * @param ssoId Single sign on identifier to look up
+     */
+    protected SingleSignOnEntry lookup(String ssoId) {
+
+        synchronized (cache) {
+            return ((SingleSignOnEntry) cache.get(ssoId));
+        }
+
+    }
+
+    
+    /**
+     * Remove a single Session from a SingleSignOn.  Called when
+     * a session is timed out and no longer active.
+     *
+     * @param ssoId Single sign on identifier from which to remove the session.
+     * @param session the session to be removed.
+     */
+    protected void removeSession(String ssoId, Session session) {
+
+        if (containerLog.isDebugEnabled())
+            containerLog.debug("Removing session " + session.toString() + " from sso id " + 
+                ssoId );
+
+        // Get a reference to the SingleSignOn
+        SingleSignOnEntry entry = lookup(ssoId);
+        if (entry == null)
+            return;
+
+        // Remove the inactive session from SingleSignOnEntry
+        entry.removeSession(session);
+
+        // Remove the inactive session from the 'reverse' Map.
+        synchronized(reverse) {
+            reverse.remove(session);
+        }
+
+        // If there are not sessions left in the SingleSignOnEntry,
+        // deregister the entry.
+        if (entry.findSessions().length == 0) {
+            deregister(ssoId);
+        }
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/SingleSignOnEntry.java b/container/catalina/src/share/org/apache/catalina/authenticator/SingleSignOnEntry.java
new file mode 100644
index 0000000..dce9196
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/SingleSignOnEntry.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.authenticator;
+
+import java.security.Principal;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+
+/**
+ * A class that represents entries in the cache of authenticated users.
+ * This is necessary to make it available to
+ * <code>AuthenticatorBase</code> subclasses that need it in order to perform
+ * reauthentications when SingleSignOn is in use.
+ *
+ * @author  B Stansberry, based on work by Craig R. McClanahan
+ * @version $Revision$
+ *
+ * @see SingleSignOn
+ * @see AuthenticatorBase#reauthenticateFromSSO
+ */
+public class SingleSignOnEntry
+{
+    // ------------------------------------------------------  Instance Fields
+
+    protected String authType = null;
+
+    protected String password = null;
+
+    protected Principal principal = null;
+
+    protected Session sessions[] = new Session[0];
+
+    protected String username = null;
+
+    protected boolean canReauthenticate = false;
+
+    // ---------------------------------------------------------  Constructors
+
+    /**
+     * Creates a new SingleSignOnEntry
+     *
+     * @param principal the <code>Principal</code> returned by the latest
+     *                  call to <code>Realm.authenticate</code>.
+     * @param authType  the type of authenticator used (BASIC, CLIENT-CERT,
+     *                  DIGEST or FORM)
+     * @param username  the username (if any) used for the authentication
+     * @param password  the password (if any) used for the authentication
+     */
+    public SingleSignOnEntry(Principal principal, String authType,
+                             String username, String password) {
+        super();
+        updateCredentials(principal, authType, username, password);
+    }
+
+    public SingleSignOnEntry() {
+    }
+
+    // ------------------------------------------------------- Package Methods
+
+    /**
+     * Adds a <code>Session</code> to the list of those associated with
+     * this SSO.
+     *
+     * @param sso       The <code>SingleSignOn</code> valve that is managing
+     *                  the SSO session.
+     * @param session   The <code>Session</code> being associated with the SSO.
+     */
+    public synchronized void addSession(SingleSignOn sso, Session session) {
+        for (int i = 0; i < sessions.length; i++) {
+            if (session == sessions[i])
+                return;
+        }
+        Session results[] = new Session[sessions.length + 1];
+        System.arraycopy(sessions, 0, results, 0, sessions.length);
+        results[sessions.length] = session;
+        sessions = results;
+        session.addSessionListener(sso);
+    }
+
+    /**
+     * Removes the given <code>Session</code> from the list of those
+     * associated with this SSO.
+     *
+     * @param session  the <code>Session</code> to remove.
+     */
+    public synchronized void removeSession(Session session) {
+        Session[] nsessions = new Session[sessions.length - 1];
+        for (int i = 0, j = 0; i < sessions.length; i++) {
+            if (session == sessions[i])
+                continue;
+            nsessions[j++] = sessions[i];
+        }
+        sessions = nsessions;
+    }
+
+    /**
+     * Returns the <code>Session</code>s associated with this SSO.
+     */
+    public synchronized Session[] findSessions() {
+        return (this.sessions);
+    }
+
+    /**
+     * Gets the name of the authentication type originally used to authenticate
+     * the user associated with the SSO.
+     *
+     * @return "BASIC", "CLIENT-CERT", "DIGEST", "FORM" or "NONE"
+     */
+    public String getAuthType() {
+        return (this.authType);
+    }
+
+    /**
+     * Gets whether the authentication type associated with the original
+     * authentication supports reauthentication.
+     *
+     * @return  <code>true</code> if <code>getAuthType</code> returns
+     *          "BASIC" or "FORM", <code>false</code> otherwise.
+     */
+    public boolean getCanReauthenticate() {
+        return (this.canReauthenticate);
+    }
+
+    /**
+     * Gets the password credential (if any) associated with the SSO.
+     *
+     * @return  the password credential associated with the SSO, or
+     *          <code>null</code> if the original authentication type
+     *          does not involve a password.
+     */
+    public String getPassword() {
+        return (this.password);
+    }
+
+    /**
+     * Gets the <code>Principal</code> that has been authenticated by
+     * the SSO.
+     */
+    public Principal getPrincipal() {
+        return (this.principal);
+    }
+
+    /**
+     * Gets the username provided by the user as part of the authentication
+     * process.
+     */
+    public String getUsername() {
+        return (this.username);
+    }
+
+
+    /**
+     * Updates the SingleSignOnEntry to reflect the latest security
+     * information associated with the caller.
+     *
+     * @param principal the <code>Principal</code> returned by the latest
+     *                  call to <code>Realm.authenticate</code>.
+     * @param authType  the type of authenticator used (BASIC, CLIENT-CERT,
+     *                  DIGEST or FORM)
+     * @param username  the username (if any) used for the authentication
+     * @param password  the password (if any) used for the authentication
+     */
+    public void updateCredentials(Principal principal, String authType,
+                                  String username, String password) {
+
+        this.principal = principal;
+        this.authType = authType;
+        this.username = username;
+        this.password = password;
+        this.canReauthenticate =
+            (Constants.BASIC_METHOD.equals(authType)
+                || Constants.FORM_METHOD.equals(authType));
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/authenticator/mbeans-descriptors.xml
new file mode 100644
index 0000000..49d0894
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/mbeans-descriptors.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean name="BasicAuthenticator"
+         description="An Authenticator and Valve implementation of HTTP BASIC Authentication"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.BasicAuthenticator">
+    
+    <attribute   name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+      
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="entropy"
+               description="A String initialization parameter used to increase the  entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+  
+  <mbean name="DigestAuthenticator"
+         description="An Authenticator and Valve implementation of HTTP DIGEST Authentication"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.DigestAuthenticator">
+    
+    <attribute name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute   name="entropy"
+               description="A String initialization parameter used to increase the  entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+  <mbean name="FormAuthenticator"
+         description="An Authenticator and Valve implementation of FORM BASED Authentication"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.FormAuthenticator">
+    
+    <attribute   name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute   name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute   name="entropy"
+               description="A String initialization parameter used to increase the entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+  <mbean name="NonLoginAuthenticator"
+         description="An Authenticator and Valve implementation that checks only security constraints not involving user authentication"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.NonLoginAuthenticator">
+    
+    <attribute name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+      
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="entropy"
+               description="A String initialization parameter used to increase the entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+  
+  <mbean name="SingleSignOn"
+         description="A Valve that supports a 'single signon' user experience"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.SingleSignOn">
+    
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="requireReauthentication"
+               description="Should we attempt to reauthenticate each request against the security Realm?"
+               type="boolean"/>
+      
+  </mbean>
+
+
+  <mbean  name="SSLAuthenticator"
+         description="An Authenticator and Valve implementation of authentication that utilizes SSL certificates to identify client users"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.SSLAuthenticator">
+
+    <attribute name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="entropy"
+               description="A String initialization parameter used to increase the entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/authenticator/package.html b/container/catalina/src/share/org/apache/catalina/authenticator/package.html
new file mode 100644
index 0000000..65814f2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/authenticator/package.html
@@ -0,0 +1,38 @@
+<body>
+
+<p>This package contains <code>Authenticator</code> implementations for the
+various supported authentication methods (BASIC, DIGEST, and FORM).  In
+addition, there is a convenience base class,
+<code>AuthenticatorBase</code>, for customized <code>Authenticator</code>
+implementations.</p>
+
+<p>If you are using the standard context configuration class
+(<code>org.apache.catalina.startup.ContextConfig</code>) to configure the
+Authenticator associated with a particular context, you can register the Java
+class to be used for each possible authentication method by modifying the
+following Properties file:</p>
+<pre>
+    src/share/org/apache/catalina/startup/Authenticators.properties
+</pre>
+
+<p>Each of the standard implementations extends a common base class
+(<code>AuthenticatorBase</code>), which is configured by setting the
+following JavaBeans properties (with default values in square brackets):</p>
+<ul>
+<li><b>cache</b> - Should we cache authenticated Principals (thus avoiding
+    per-request lookups in our underyling <code>Realm</code>) if this request
+    is part of an HTTP session?  [true]</li>
+<li><b>debug</b> - Debugging detail level for this component.  [0]</li>
+</ul>
+
+<p>The standard authentication methods that are currently provided include:</p>
+<ul>
+<li><b>BasicAuthenticator</b> - Implements HTTP BASIC authentication, as
+    described in RFC 2617.</li>
+<li><b>DigestAuthenticator</b> - Implements HTTP DIGEST authentication, as
+    described in RFC 2617.</li>
+<li><b>FormAuthenticator</b> - Implements FORM-BASED authentication, as
+    described in the Servlet API Specification, version 2.2.</li>
+</ul>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/catalina/connector/ClientAbortException.java b/container/catalina/src/share/org/apache/catalina/connector/ClientAbortException.java
new file mode 100644
index 0000000..3f6861e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/ClientAbortException.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+
+/**
+ * Wrap an IOException identifying it as being caused by an abort
+ * of a request by a remote client.
+ *
+ * @author Glenn L. Nielsen
+ * @version $Revision$ $Date$
+ */
+
+public final class ClientAbortException extends IOException {
+
+
+    //------------------------------------------------------------ Constructors
+
+
+    /**
+     * Construct a new ClientAbortException with no other information.
+     */
+    public ClientAbortException() {
+
+        this(null, null);
+
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified message.
+     *
+     * @param message Message describing this exception
+     */
+    public ClientAbortException(String message) {
+
+        this(message, null);
+
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified throwable.
+     *
+     * @param throwable Throwable that caused this exception
+     */
+    public ClientAbortException(Throwable throwable) {
+
+        this(null, throwable);
+
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified message
+     * and throwable.
+     *
+     * @param message Message describing this exception
+     * @param throwable Throwable that caused this exception
+     */
+    public ClientAbortException(String message, Throwable throwable) {
+
+        super();
+        this.message = message;
+        this.throwable = throwable;
+
+    }
+
+
+    //------------------------------------------------------ Instance Variables
+
+
+    /**
+     * The error message passed to our constructor (if any)
+     */
+    protected String message = null;
+
+
+    /**
+     * The underlying exception or error passed to our constructor (if any)
+     */
+    protected Throwable throwable = null;
+
+
+    //---------------------------------------------------------- Public Methods
+
+
+    /**
+     * Returns the message associated with this exception, if any.
+     */
+    public String getMessage() {
+
+        return (message);
+
+    }
+
+
+    /**
+     * Returns the cause that caused this exception, if any.
+     */
+    public Throwable getCause() {
+        
+        return (throwable);
+        
+    }
+
+    
+    /**
+     * Return a formatted string that describes this exception.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ClientAbortException:  ");
+        if (message != null) {
+            sb.append(message);
+            if (throwable != null) {
+                sb.append(":  ");
+            }
+        }
+        if (throwable != null) {
+            sb.append(throwable.toString());
+        }
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/Connector.java b/container/catalina/src/share/org/apache/catalina/connector/Connector.java
new file mode 100644
index 0000000..5d582bc
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/Connector.java
@@ -0,0 +1,1259 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.connector;
+
+import java.lang.reflect.Method;
+import java.net.URLEncoder;
+import java.util.HashMap;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Service;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.http.mapper.Mapper;
+
+
+/**
+ * Implementation of a Coyote connector for Tomcat 5.x.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+
+public class Connector
+    implements Lifecycle, MBeanRegistration
+{
+    private static Log log = LogFactory.getLog(Connector.class);
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    public Connector()
+        throws Exception {
+        this(null);
+    }
+    
+    public Connector(String protocol) 
+        throws Exception {
+        setProtocol(protocol);
+        // Instantiate protocol handler
+        try {
+            Class clazz = Class.forName(protocolHandlerClassName);
+            this.protocolHandler = (ProtocolHandler) clazz.newInstance();
+        } catch (Exception e) {
+            log.error
+                (sm.getString
+                 ("coyoteConnector.protocolHandlerInstantiationFailed", e));
+        }
+    }
+    
+    
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>Service</code> we are associated with (if any).
+     */
+    protected Service service = null;
+
+
+    /**
+     * Do we allow TRACE ?
+     */
+    protected boolean allowTrace = false;
+
+
+    /**
+     * The Container used for processing requests received by this Connector.
+     */
+    protected Container container = null;
+
+
+    /**
+     * Use "/" as path for session cookies ?
+     */
+    protected boolean emptySessionPath = false;
+
+
+    /**
+     * The "enable DNS lookups" flag for this Connector.
+     */
+    protected boolean enableLookups = false;
+
+
+    /*
+     * Is generation of X-Powered-By response header enabled/disabled?
+     */
+    protected boolean xpoweredBy = false;
+
+
+    /**
+     * Descriptive information about this Connector implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.connector.Connector/2.1";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The port number on which we listen for requests.
+     */
+    protected int port = 0;
+
+
+    /**
+     * The server name to which we should pretend requests to this Connector
+     * were directed.  This is useful when operating Tomcat behind a proxy
+     * server, so that redirects get constructed accurately.  If not specified,
+     * the server name included in the <code>Host</code> header is used.
+     */
+    protected String proxyName = null;
+
+
+    /**
+     * The server port to which we should pretent requests to this Connector
+     * were directed.  This is useful when operating Tomcat behind a proxy
+     * server, so that redirects get constructed accurately.  If not specified,
+     * the port number specified by the <code>port</code> property is used.
+     */
+    protected int proxyPort = 0;
+
+
+    /**
+     * The redirect port for non-SSL to SSL redirects.
+     */
+    protected int redirectPort = 443;
+
+
+    /**
+     * The request scheme that will be set on all requests received
+     * through this connector.
+     */
+    protected String scheme = "http";
+
+
+    /**
+     * The secure connection flag that will be set on all requests received
+     * through this connector.
+     */
+    protected boolean secure = false;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Maximum size of a POST which will be automatically parsed by the 
+     * container. 2MB by default.
+     */
+    protected int maxPostSize = 2 * 1024 * 1024;
+
+
+    /**
+     * Maximum size of a POST which will be saved by the container
+     * during authentication. 4kB by default
+     */
+    protected int maxSavePostSize = 4 * 1024;
+
+
+    /**
+     * Has this component been initialized yet?
+     */
+    protected boolean initialized = false;
+
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+
+    /**
+     * The shutdown signal to our background thread
+     */
+    protected boolean stopped = false;
+
+    /**
+     * Flag to use IP-based virtual hosting.
+     */
+    protected boolean useIPVHosts = false;
+
+    /**
+     * The background thread.
+     */
+    protected Thread thread = null;
+
+
+    /**
+     * Coyote Protocol handler class name.
+     * Defaults to the Coyote HTTP/1.1 protocolHandler.
+     */
+    protected String protocolHandlerClassName =
+        "org.apache.coyote.http11.Http11Protocol";
+
+
+    /**
+     * Coyote protocol handler.
+     */
+    protected ProtocolHandler protocolHandler = null;
+
+
+    /**
+     * Coyote adapter.
+     */
+    protected Adapter adapter = null;
+
+
+     /**
+      * Mapper.
+      */
+     protected Mapper mapper = new Mapper();
+
+
+     /**
+      * Mapper listener.
+      */
+     protected MapperListener mapperListener = new MapperListener(mapper);
+
+
+     /**
+      * URI encoding.
+      */
+     protected String URIEncoding = null;
+
+
+     /**
+      * URI encoding as body.
+      */
+     protected boolean useBodyEncodingForURI = false;
+
+
+     protected static HashMap replacements = new HashMap();
+     static {
+         replacements.put("acceptCount", "backlog");
+         replacements.put("connectionLinger", "soLinger");
+         replacements.put("connectionTimeout", "soTimeout");
+         replacements.put("connectionUploadTimeout", "timeout");
+         replacements.put("clientAuth", "clientauth");
+         replacements.put("keystoreFile", "keystore");
+         replacements.put("randomFile", "randomfile");
+         replacements.put("rootFile", "rootfile");
+         replacements.put("keystorePass", "keypass");
+         replacements.put("keystoreType", "keytype");
+         replacements.put("sslProtocol", "protocol");
+         replacements.put("sslProtocols", "protocols");
+     }
+     
+     
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return a configured property.
+     */
+    public Object getProperty(String name) {
+        String repl = name;
+        if (replacements.get(name) != null) {
+            repl = (String) replacements.get(name);
+        }
+        return IntrospectionUtils.getProperty(protocolHandler, repl);
+    }
+
+    
+    /**
+     * Set a configured property.
+     */
+    public void setProperty(String name, String value) {
+        String repl = name;
+        if (replacements.get(name) != null) {
+            repl = (String) replacements.get(name);
+        }
+        IntrospectionUtils.setProperty(protocolHandler, repl, value);
+    }
+
+    
+    /**
+     * Return a configured property.
+     */
+    public Object getAttribute(String name) {
+        return getProperty(name);
+    }
+
+    
+    /**
+     * Set a configured property.
+     */
+    public void setAttribute(String name, Object value) {
+        setProperty(name, String.valueOf(value));
+    }
+
+    
+    /** 
+     * remove a configured property.
+     */
+    public void removeProperty(String name) {
+        // FIXME !
+        //protocolHandler.removeAttribute(name);
+    }
+
+    
+    /**
+     * Return the <code>Service</code> with which we are associated (if any).
+     */
+    public Service getService() {
+
+        return (this.service);
+
+    }
+
+
+    /**
+     * Set the <code>Service</code> with which we are associated (if any).
+     *
+     * @param service The service that owns this Engine
+     */
+    public void setService(Service service) {
+
+        this.service = service;
+        // FIXME: setProperty("service", service);
+
+    }
+
+
+    /**
+     * True if the TRACE method is allowed.  Default value is "false".
+     */
+    public boolean getAllowTrace() {
+
+        return (this.allowTrace);
+
+    }
+
+
+    /**
+     * Set the allowTrace flag, to disable or enable the TRACE HTTP method.
+     *
+     * @param allowTrace The new allowTrace flag
+     */
+    public void setAllowTrace(boolean allowTrace) {
+
+        this.allowTrace = allowTrace;
+        setProperty("allowTrace", String.valueOf(allowTrace));
+
+    }
+
+    /**
+     * Is this connector available for processing requests?
+     */
+    public boolean isAvailable() {
+
+        return (started);
+
+    }
+
+
+    /**
+     * Return the input buffer size for this Connector.
+     * 
+     * @deprecated
+     */
+    public int getBufferSize() {
+        return 2048;
+    }
+
+    /**
+     * Set the input buffer size for this Connector.
+     *
+     * @param bufferSize The new input buffer size.
+     * @deprecated
+     */
+    public void setBufferSize(int bufferSize) {
+    }
+
+    
+    /**
+     * Return the Container used for processing requests received by this
+     * Connector.
+     */
+    public Container getContainer() {
+        if( container==null ) {
+            // Lazy - maybe it was added later
+            findContainer();     
+        }
+        return (container);
+
+    }
+
+
+    /**
+     * Set the Container used for processing requests received by this
+     * Connector.
+     *
+     * @param container The new Container to use
+     */
+    public void setContainer(Container container) {
+
+        this.container = container;
+
+    }
+
+
+    /**
+     * Return the "empty session path" flag.
+     */
+    public boolean getEmptySessionPath() {
+
+        return (this.emptySessionPath);
+
+    }
+
+
+    /**
+     * Set the "empty session path" flag.
+     *
+     * @param emptySessionPath The new "empty session path" flag value
+     */
+    public void setEmptySessionPath(boolean emptySessionPath) {
+
+        this.emptySessionPath = emptySessionPath;
+        setProperty("emptySessionPath", String.valueOf(emptySessionPath));
+
+    }
+
+
+    /**
+     * Return the "enable DNS lookups" flag.
+     */
+    public boolean getEnableLookups() {
+
+        return (this.enableLookups);
+
+    }
+
+
+    /**
+     * Set the "enable DNS lookups" flag.
+     *
+     * @param enableLookups The new "enable DNS lookups" flag value
+     */
+    public void setEnableLookups(boolean enableLookups) {
+
+        this.enableLookups = enableLookups;
+        setProperty("enableLookups", String.valueOf(enableLookups));
+
+    }
+
+
+    /**
+     * Return descriptive information about this Connector implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+     /**
+      * Return the mapper.
+      */
+     public Mapper getMapper() {
+
+         return (mapper);
+
+     }
+
+
+    /**
+     * Return the maximum size of a POST which will be automatically
+     * parsed by the container.
+     */
+    public int getMaxPostSize() {
+
+        return (maxPostSize);
+
+    }
+
+
+    /**
+     * Set the maximum size of a POST which will be automatically
+     * parsed by the container.
+     *
+     * @param maxPostSize The new maximum size in bytes of a POST which will 
+     * be automatically parsed by the container
+     */
+    public void setMaxPostSize(int maxPostSize) {
+
+        this.maxPostSize = maxPostSize;
+    }
+
+
+    /**
+     * Return the maximum size of a POST which will be saved by the container
+     * during authentication.
+     */
+    public int getMaxSavePostSize() {
+
+        return (maxSavePostSize);
+
+    }
+
+
+    /**
+     * Set the maximum size of a POST which will be saved by the container
+     * during authentication.
+     *
+     * @param maxSavePostSize The new maximum size in bytes of a POST which will
+     * be saved by the container during authentication.
+     */
+    public void setMaxSavePostSize(int maxSavePostSize) {
+
+        this.maxSavePostSize = maxSavePostSize;
+        setProperty("maxSavePostSize", String.valueOf(maxSavePostSize));
+    }
+
+
+    /**
+     * Return the port number on which we listen for requests.
+     */
+    public int getPort() {
+
+        return (this.port);
+
+    }
+
+
+    /**
+     * Set the port number on which we listen for requests.
+     *
+     * @param port The new port number
+     */
+    public void setPort(int port) {
+
+        this.port = port;
+        setProperty("port", String.valueOf(port));
+
+    }
+
+
+    /**
+     * Return the Coyote protocol handler in use.
+     */
+    public String getProtocol() {
+
+        if ("org.apache.coyote.http11.Http11Protocol".equals
+            (getProtocolHandlerClassName())
+            || "org.apache.coyote.http11.Http11AprProtocol".equals
+            (getProtocolHandlerClassName())) {
+            return "HTTP/1.1";
+        } else if ("org.apache.jk.server.JkCoyoteHandler".equals
+                   (getProtocolHandlerClassName())
+                   || "org.apache.coyote.ajp.AjpAprProtocol".equals
+                   (getProtocolHandlerClassName())) {
+            return "AJP/1.3";
+        }
+        return getProtocolHandlerClassName();
+
+    }
+
+
+    /**
+     * Set the Coyote protocol which will be used by the connector.
+     *
+     * @param protocol The Coyote protocol name
+     */
+    public void setProtocol(String protocol) {
+
+        // Test APR support
+        boolean apr = false;
+        try {
+            String methodName = "initialize";
+            Class paramTypes[] = new Class[1];
+            paramTypes[0] = String.class;
+            Object paramValues[] = new Object[1];
+            paramValues[0] = null;
+            Method method = Class.forName("org.apache.tomcat.jni.Library")
+                .getMethod(methodName, paramTypes);
+            method.invoke(null, paramValues);
+            apr = true;
+        } catch (Throwable t) {
+            // Ignore
+        }
+
+        if (apr) {
+            if ("HTTP/1.1".equals(protocol)) {
+                setProtocolHandlerClassName
+                    ("org.apache.coyote.http11.Http11AprProtocol");
+            } else if ("AJP/1.3".equals(protocol)) {
+                setProtocolHandlerClassName
+                    ("org.apache.coyote.ajp.AjpAprProtocol");
+            } else if (protocol != null) {
+                setProtocolHandlerClassName(protocol);
+            } else {
+                setProtocolHandlerClassName
+                    ("org.apache.coyote.http11.Http11AprProtocol");
+            }
+        } else {
+            if ("HTTP/1.1".equals(protocol)) {
+                setProtocolHandlerClassName
+                    ("org.apache.coyote.http11.Http11Protocol");
+            } else if ("AJP/1.3".equals(protocol)) {
+                setProtocolHandlerClassName
+                    ("org.apache.jk.server.JkCoyoteHandler");
+            } else if (protocol != null) {
+                setProtocolHandlerClassName(protocol);
+            }
+        }
+
+    }
+
+
+    /**
+     * Return the class name of the Coyote protocol handler in use.
+     */
+    public String getProtocolHandlerClassName() {
+
+        return (this.protocolHandlerClassName);
+
+    }
+
+
+    /**
+     * Set the class name of the Coyote protocol handler which will be used
+     * by the connector.
+     *
+     * @param protocolHandlerClassName The new class name
+     */
+    public void setProtocolHandlerClassName(String protocolHandlerClassName) {
+
+        this.protocolHandlerClassName = protocolHandlerClassName;
+
+    }
+
+
+    /**
+     * Return the protocol handler associated with the connector.
+     */
+    public ProtocolHandler getProtocolHandler() {
+
+        return (this.protocolHandler);
+
+    }
+
+
+    /**
+     * Return the proxy server name for this Connector.
+     */
+    public String getProxyName() {
+
+        return (this.proxyName);
+
+    }
+
+
+    /**
+     * Set the proxy server name for this Connector.
+     *
+     * @param proxyName The new proxy server name
+     */
+    public void setProxyName(String proxyName) {
+
+        if(proxyName != null && proxyName.length() > 0) {
+            this.proxyName = proxyName;
+            setProperty("proxyName", proxyName);
+        } else {
+            this.proxyName = null;
+            removeProperty("proxyName");
+        }
+
+    }
+
+
+    /**
+     * Return the proxy server port for this Connector.
+     */
+    public int getProxyPort() {
+
+        return (this.proxyPort);
+
+    }
+
+
+    /**
+     * Set the proxy server port for this Connector.
+     *
+     * @param proxyPort The new proxy server port
+     */
+    public void setProxyPort(int proxyPort) {
+
+        this.proxyPort = proxyPort;
+        setProperty("proxyPort", String.valueOf(proxyPort));
+
+    }
+
+
+    /**
+     * Return the port number to which a request should be redirected if
+     * it comes in on a non-SSL port and is subject to a security constraint
+     * with a transport guarantee that requires SSL.
+     */
+    public int getRedirectPort() {
+
+        return (this.redirectPort);
+
+    }
+
+
+    /**
+     * Set the redirect port number.
+     *
+     * @param redirectPort The redirect port number (non-SSL to SSL)
+     */
+    public void setRedirectPort(int redirectPort) {
+
+        this.redirectPort = redirectPort;
+        setProperty("redirectPort", String.valueOf(redirectPort));
+
+    }
+
+    
+    /**
+     * Return the scheme that will be assigned to requests received
+     * through this connector.  Default value is "http".
+     */
+    public String getScheme() {
+
+        return (this.scheme);
+
+    }
+
+
+    /**
+     * Set the scheme that will be assigned to requests received through
+     * this connector.
+     *
+     * @param scheme The new scheme
+     */
+    public void setScheme(String scheme) {
+
+        this.scheme = scheme;
+
+    }
+
+
+    /**
+     * Return the secure connection flag that will be assigned to requests
+     * received through this connector.  Default value is "false".
+     */
+    public boolean getSecure() {
+
+        return (this.secure);
+
+    }
+
+
+    /**
+     * Set the secure connection flag that will be assigned to requests
+     * received through this connector.
+     *
+     * @param secure The new secure connection flag
+     */
+    public void setSecure(boolean secure) {
+
+        this.secure = secure;
+        setProperty("secure", Boolean.toString(secure));
+    }
+
+     /**
+      * Return the character encoding to be used for the URI.
+      */
+     public String getURIEncoding() {
+
+         return (this.URIEncoding);
+
+     }
+
+
+     /**
+      * Set the URI encoding to be used for the URI.
+      *
+      * @param URIEncoding The new URI character encoding.
+      */
+     public void setURIEncoding(String URIEncoding) {
+
+         this.URIEncoding = URIEncoding;
+         setProperty("uRIEncoding", URIEncoding);
+
+     }
+
+
+     /**
+      * Return the true if the entity body encoding should be used for the URI.
+      */
+     public boolean getUseBodyEncodingForURI() {
+
+         return (this.useBodyEncodingForURI);
+
+     }
+
+
+     /**
+      * Set if the entity body encoding should be used for the URI.
+      *
+      * @param useBodyEncodingForURI The new value for the flag.
+      */
+     public void setUseBodyEncodingForURI(boolean useBodyEncodingForURI) {
+
+         this.useBodyEncodingForURI = useBodyEncodingForURI;
+         setProperty
+             ("useBodyEncodingForURI", String.valueOf(useBodyEncodingForURI));
+
+     }
+
+
+    /**
+     * Indicates whether the generation of an X-Powered-By response header for
+     * servlet-generated responses is enabled or disabled for this Connector.
+     *
+     * @return true if generation of X-Powered-By response header is enabled,
+     * false otherwise
+     */
+    public boolean getXpoweredBy() {
+        return xpoweredBy;
+    }
+
+
+    /**
+     * Enables or disables the generation of an X-Powered-By header (with value
+     * Servlet/2.4) for all servlet-generated responses returned by this
+     * Connector.
+     *
+     * @param xpoweredBy true if generation of X-Powered-By response header is
+     * to be enabled, false otherwise
+     */
+    public void setXpoweredBy(boolean xpoweredBy) {
+        this.xpoweredBy = xpoweredBy;
+        setProperty("xpoweredBy", String.valueOf(xpoweredBy));
+    }
+
+    /**
+     * Enable the use of IP-based virtual hosting.
+     *
+     * @param useIPVHosts <code>true</code> if Hosts are identified by IP,
+     *                    <code>false/code> if Hosts are identified by name.
+     */
+    public void setUseIPVHosts(boolean useIPVHosts) {
+        this.useIPVHosts = useIPVHosts;
+        setProperty("useIPVHosts", String.valueOf(useIPVHosts));
+    }
+
+    /**
+     * Test if IP-based virtual hosting is enabled.
+     */
+    public boolean getUseIPVHosts() {
+        return useIPVHosts;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Create (or allocate) and return a Request object suitable for
+     * specifying the contents of a Request to the responsible Container.
+     */
+    public Request createRequest() {
+
+        Request request = new Request();
+        request.setConnector(this);
+        return (request);
+
+    }
+
+
+    /**
+     * Create (or allocate) and return a Response object suitable for
+     * receiving the contents of a Response from the responsible Container.
+     */
+    public Response createResponse() {
+
+        Response response = new Response();
+        response.setConnector(this);
+        return (response);
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+    
+    protected ObjectName createObjectName(String domain, String type)
+            throws MalformedObjectNameException {
+        String encodedAddr = null;
+        if (getProperty("address") != null) {
+            encodedAddr = URLEncoder.encode(getProperty("address").toString());
+        }
+        String addSuffix = (getProperty("address") == null) ? "" : ",address="
+                + encodedAddr;
+        ObjectName _oname = new ObjectName(domain + ":type=" + type + ",port="
+                + getPort() + addSuffix);
+        return _oname;
+    }
+    
+    /**
+     * Initialize this connector (create ServerSocket here!)
+     */
+    public void initialize()
+        throws LifecycleException
+    {
+        if (initialized) {
+            if(log.isInfoEnabled())
+                log.info(sm.getString("coyoteConnector.alreadyInitialized"));
+           return;
+        }
+
+        this.initialized = true;
+
+        if( oname == null && (container instanceof StandardEngine)) {
+            try {
+                // we are loaded directly, via API - and no name was given to us
+                StandardEngine cb=(StandardEngine)container;
+                oname = createObjectName(cb.getName(), "Connector");
+                Registry.getRegistry(null, null)
+                    .registerComponent(this, oname, null);
+                controller=oname;
+            } catch (Exception e) {
+                log.error( "Error registering connector ", e);
+            }
+            if(log.isDebugEnabled())
+                log.debug("Creating name for connector " + oname);
+        }
+
+        // Initializa adapter
+        adapter = new CoyoteAdapter(this);
+        protocolHandler.setAdapter(adapter);
+
+        IntrospectionUtils.setProperty(protocolHandler, "jkHome",
+                                       System.getProperty("catalina.base"));
+
+        try {
+            protocolHandler.init();
+        } catch (Exception e) {
+            throw new LifecycleException
+                (sm.getString
+                 ("coyoteConnector.protocolHandlerInitializationFailed", e));
+        }
+    }
+
+
+    /**
+     * Pause the connector.
+     */
+    public void pause()
+        throws LifecycleException {
+        try {
+            protocolHandler.pause();
+        } catch (Exception e) {
+            log.error(sm.getString
+                      ("coyoteConnector.protocolHandlerPauseFailed"), e);
+        }
+    }
+
+
+    /**
+     * Pause the connector.
+     */
+    public void resume()
+        throws LifecycleException {
+        try {
+            protocolHandler.resume();
+        } catch (Exception e) {
+            log.error(sm.getString
+                      ("coyoteConnector.protocolHandlerResumeFailed"), e);
+        }
+    }
+
+
+    /**
+     * Begin processing requests via this Connector.
+     *
+     * @exception LifecycleException if a fatal startup error occurs
+     */
+    public void start() throws LifecycleException {
+        if( !initialized )
+            initialize();
+
+        // Validate and update our current state
+        if (started ) {
+            if(log.isInfoEnabled())
+                log.info(sm.getString("coyoteConnector.alreadyStarted"));
+            return;
+        }
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // We can't register earlier - the JMX registration of this happens
+        // in Server.start callback
+        if ( this.oname != null ) {
+            // We are registred - register the adapter as well.
+            try {
+                Registry.getRegistry(null, null).registerComponent
+                    (protocolHandler, createObjectName(this.domain,"ProtocolHandler"), null);
+            } catch (Exception ex) {
+                log.error(sm.getString
+                          ("coyoteConnector.protocolRegistrationFailed"), ex);
+            }
+        } else {
+            if(log.isInfoEnabled())
+                log.info(sm.getString
+                     ("coyoteConnector.cannotRegisterProtocol"));
+        }
+
+        try {
+            protocolHandler.start();
+        } catch (Exception e) {
+            throw new LifecycleException
+                (sm.getString
+                 ("coyoteConnector.protocolHandlerStartFailed", e));
+        }
+
+        if( this.domain != null ) {
+            mapperListener.setDomain( domain );
+            //mapperListener.setEngine( service.getContainer().getName() );
+            mapperListener.init();
+            try {
+                ObjectName mapperOname = createObjectName(this.domain,"Mapper");
+                if (log.isDebugEnabled())
+                    log.debug(sm.getString(
+                            "coyoteConnector.MapperRegistration", mapperOname));
+                Registry.getRegistry(null, null).registerComponent
+                    (mapper, mapperOname, "Mapper");
+            } catch (Exception ex) {
+                log.error(sm.getString
+                        ("coyoteConnector.protocolRegistrationFailed"), ex);
+            }
+        }
+    }
+
+
+    /**
+     * Terminate processing requests via this Connector.
+     *
+     * @exception LifecycleException if a fatal shutdown error occurs
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current state
+        if (!started) {
+            log.error(sm.getString("coyoteConnector.notStarted"));
+            return;
+
+        }
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        try {
+            mapperListener.destroy();
+            Registry.getRegistry(null, null).unregisterComponent
+                (createObjectName(this.domain,"Mapper"));
+            Registry.getRegistry(null, null).unregisterComponent
+                (createObjectName(this.domain,"ProtocolHandler"));
+        } catch (MalformedObjectNameException e) {
+            log.error( sm.getString
+                    ("coyoteConnector.protocolUnregistrationFailed"), e);
+        }
+        try {
+            protocolHandler.destroy();
+        } catch (Exception e) {
+            throw new LifecycleException
+                (sm.getString
+                 ("coyoteConnector.protocolHandlerDestroyFailed", e));
+        }
+
+    }
+
+
+    // -------------------- JMX registration  --------------------
+    protected String domain;
+    protected ObjectName oname;
+    protected MBeanServer mserver;
+    ObjectName controller;
+
+    public ObjectName getController() {
+        return controller;
+    }
+
+    public void setController(ObjectName controller) {
+        this.controller = controller;
+    }
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+        try {
+            if( started ) {
+                stop();
+            }
+        } catch( Throwable t ) {
+            log.error( "Unregistering - can't stop", t);
+        }
+    }
+    
+    protected void findContainer() {
+        try {
+            // Register to the service
+            ObjectName parentName=new ObjectName( domain + ":" +
+                    "type=Service");
+            
+            if(log.isDebugEnabled())
+                log.debug("Adding to " + parentName );
+            if( mserver.isRegistered(parentName )) {
+                mserver.invoke(parentName, "addConnector", new Object[] { this },
+                        new String[] {"org.apache.catalina.connector.Connector"});
+                // As a side effect we'll get the container field set
+                // Also initialize will be called
+                //return;
+            }
+            // XXX Go directly to the Engine
+            // initialize(); - is called by addConnector
+            ObjectName engName=new ObjectName( domain + ":" + "type=Engine");
+            if( mserver.isRegistered(engName )) {
+                Object obj=mserver.getAttribute(engName, "managedResource");
+                if(log.isDebugEnabled())
+                      log.debug("Found engine " + obj + " " + obj.getClass());
+                container=(Container)obj;
+                
+                // Internal initialize - we now have the Engine
+                initialize();
+                
+                if(log.isDebugEnabled())
+                    log.debug("Initialized");
+                // As a side effect we'll get the container field set
+                // Also initialize will be called
+                return;
+            }
+        } catch( Exception ex ) {
+            log.error( "Error finding container " + ex);
+        }
+    }
+
+    public void init() throws Exception {
+
+        if( this.getService() != null ) {
+            if(log.isDebugEnabled())
+                 log.debug( "Already configured" );
+            return;
+        }
+        if( container==null ) {
+            findContainer();
+        }
+    }
+
+    public void destroy() throws Exception {
+        if( oname!=null && controller==oname ) {
+            if(log.isDebugEnabled())
+                 log.debug("Unregister itself " + oname );
+            Registry.getRegistry(null, null).unregisterComponent(oname);
+        }
+        if( getService() == null)
+            return;
+        getService().removeConnector(this);
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/Constants.java b/container/catalina/src/share/org/apache/catalina/connector/Constants.java
new file mode 100644
index 0000000..7f49bb9
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/Constants.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.connector;
+
+
+/**
+ * Static constants for this package.
+ */
+
+public final class Constants {
+
+	
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String Package = "org.apache.catalina.connector";
+
+    public static final int DEFAULT_CONNECTION_LINGER = -1;
+    public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
+    public static final int DEFAULT_CONNECTION_UPLOAD_TIMEOUT = 300000;
+    public static final int DEFAULT_SERVER_SOCKET_TIMEOUT = 0;
+
+    public static final int PROCESSOR_IDLE = 0;
+    public static final int PROCESSOR_ACTIVE = 1;
+
+    /**
+     * Security flag.
+     */
+    public static final boolean SECURITY = 
+        (System.getSecurityManager() != null);
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/CoyoteAdapter.java b/container/catalina/src/share/org/apache/catalina/connector/CoyoteAdapter.java
new file mode 100644
index 0000000..9d6c464
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/CoyoteAdapter.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Adapter;
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.Cookies;
+import org.apache.tomcat.util.http.ServerCookie;
+
+
+/**
+ * Implementation of a request processor which delegates the processing to a
+ * Coyote processor.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class CoyoteAdapter
+    implements Adapter 
+ {
+    private static Log log = LogFactory.getLog(CoyoteAdapter.class);
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final int ADAPTER_NOTES = 1;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new CoyoteProcessor associated with the specified connector.
+     *
+     * @param connector CoyoteConnector that owns this processor
+     */
+    public CoyoteAdapter(Connector connector) {
+
+        super();
+        this.connector = connector;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The CoyoteConnector with which this processor is associated.
+     */
+    private Connector connector = null;
+
+
+    /**
+     * The match string for identifying a session ID parameter.
+     */
+    private static final String match =
+        ";" + Globals.SESSION_PARAMETER_NAME + "=";
+
+
+    /**
+     * The match string for identifying a session ID parameter.
+     */
+    private static final char[] SESSION_ID = match.toCharArray();
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // -------------------------------------------------------- Adapter Methods
+
+
+    /**
+     * Service method.
+     */
+    public void service(org.apache.coyote.Request req, 
+    	                org.apache.coyote.Response res)
+        throws Exception {
+
+        Request request = (Request) req.getNote(ADAPTER_NOTES);
+        Response response = (Response) res.getNote(ADAPTER_NOTES);
+
+        if (request == null) {
+
+            // Create objects
+            request = (Request) connector.createRequest();
+            request.setCoyoteRequest(req);
+            response = (Response) connector.createResponse();
+            response.setCoyoteResponse(res);
+
+            // Link objects
+            request.setResponse(response);
+            response.setRequest(request);
+
+            // Set as notes
+            req.setNote(ADAPTER_NOTES, request);
+            res.setNote(ADAPTER_NOTES, response);
+
+            // Set query string encoding
+            req.getParameters().setQueryStringEncoding
+                (connector.getURIEncoding());
+
+        }
+
+        if (connector.getXpoweredBy()) {
+            response.addHeader("X-Powered-By", "Servlet/2.4");
+        }
+
+        try {
+
+            // Parse and set Catalina and configuration specific 
+            // request parameters
+            if ( postParseRequest(req, request, res, response) ) {
+                // Calling the container
+                connector.getContainer().getPipeline().getFirst().invoke(request, response);
+            }
+
+            response.finishResponse();
+            req.action( ActionCode.ACTION_POST_REQUEST , null);
+
+        } catch (IOException e) {
+            ;
+        } catch (Throwable t) {
+            log.error(sm.getString("coyoteAdapter.service"), t);
+        } finally {
+            // Recycle the wrapper request and response
+            request.recycle();
+            response.recycle();
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Parse additional request parameters.
+     */
+    protected boolean postParseRequest(org.apache.coyote.Request req, 
+                                       Request request,
+    		                       org.apache.coyote.Response res, 
+                                       Response response)
+            throws Exception {
+
+        // XXX the processor needs to set a correct scheme and port prior to this point, 
+        // in ajp13 protocols dont make sense to get the port from the connector..
+        // XXX the processor may have set a correct scheme and port prior to this point, 
+        // in ajp13 protocols dont make sense to get the port from the connector...
+        // otherwise, use connector configuration
+        if (! req.scheme().isNull()) {
+            // use processor specified scheme to determine secure state
+            request.setSecure(req.scheme().equals("https"));
+        } else {
+            // use connector scheme and secure configuration, (defaults to
+            // "http" and false respectively)
+            req.scheme().setString(connector.getScheme());
+            request.setSecure(connector.getSecure());
+        }
+
+        // FIXME: the code below doesnt belongs to here, 
+        // this is only have sense 
+        // in Http11, not in ajp13..
+        // At this point the Host header has been processed.
+        // Override if the proxyPort/proxyHost are set 
+        String proxyName = connector.getProxyName();
+        int proxyPort = connector.getProxyPort();
+        if (proxyPort != 0) {
+            req.setServerPort(proxyPort);
+        }
+        if (proxyName != null) {
+            req.serverName().setString(proxyName);
+        }
+
+        // URI decoding
+        MessageBytes decodedURI = req.decodedURI();
+        decodedURI.duplicate(req.requestURI());
+
+        if (decodedURI.getType() == MessageBytes.T_BYTES) {
+            // %xx decoding of the URL
+            try {
+                req.getURLDecoder().convert(decodedURI, false);
+            } catch (IOException ioe) {
+                res.setStatus(400);
+                res.setMessage("Invalid URI");
+                throw ioe;
+            }
+            // Normalization
+            if (!normalize(req.decodedURI())) {
+                res.setStatus(400);
+                res.setMessage("Invalid URI");
+                return false;
+            }
+            // Character decoding
+            convertURI(decodedURI, request);
+        } else {
+            // The URL is chars or String, and has been sent using an in-memory
+            // protocol handler, we have to assume the URL has been properly
+            // decoded already
+            decodedURI.toChars();
+        }
+
+        // Set the remote principal
+        String principal = req.getRemoteUser().toString();
+        if (principal != null) {
+            request.setUserPrincipal(new CoyotePrincipal(principal));
+        }
+
+        // Set the authorization type
+        String authtype = req.getAuthType().toString();
+        if (authtype != null) {
+            request.setAuthType(authtype);
+        }
+
+        // Parse session Id
+        parseSessionId(req, request);
+
+        // Remove any remaining parameters (other than session id, which has
+        // already been removed in parseSessionId()) from the URI, so they
+        // won't be considered by the mapping algorithm.
+        CharChunk uriCC = decodedURI.getCharChunk();
+        int semicolon = uriCC.indexOf(';');
+        if (semicolon > 0) {
+            decodedURI.setChars
+                (uriCC.getBuffer(), uriCC.getStart(), semicolon);
+        }
+
+        // Request mapping.
+        MessageBytes serverName;
+        if(connector.getUseIPVHosts()) {
+            serverName = req.localName();
+            if(serverName.isNull()) {
+                // well, they did ask for it
+                res.action(ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE, null);
+            }
+        } else {
+            serverName = req.serverName();
+        }
+        connector.getMapper().map(serverName, decodedURI, 
+                                  request.getMappingData());
+        request.setContext((Context) request.getMappingData().context);
+        request.setWrapper((Wrapper) request.getMappingData().wrapper);
+
+        // Filter trace method
+        if (!connector.getAllowTrace() 
+                && req.method().equalsIgnoreCase("TRACE")) {
+            Wrapper wrapper = request.getWrapper();
+            String header = null;
+            if (wrapper != null) {
+                String[] methods = wrapper.getServletMethods();
+                if (methods != null) {
+                    for (int i=0; i<methods.length; i++) {
+                        if ("TRACE".equals(methods[i])) {
+                            continue;
+                        }
+                        if (header == null) {
+                            header = methods[i];
+                        } else {
+                            header += ", " + methods[i];
+                        }
+                    }
+                }
+            }                               
+            res.setStatus(405);
+            res.addHeader("Allow", header);
+            res.setMessage("TRACE method is not allowed");
+            return false;
+        }
+
+        // Possible redirect
+        MessageBytes redirectPathMB = request.getMappingData().redirectPath;
+        if (!redirectPathMB.isNull()) {
+            String redirectPath = redirectPathMB.toString();
+            String query = request.getQueryString();
+            if (query != null) {
+                // This is not optimal, but as this is not very common, it
+                // shouldn't matter
+                redirectPath = redirectPath + "?" + query;
+            }
+            response.sendRedirect(redirectPath);
+            return false;
+        }
+
+        // Parse session Id
+        parseSessionCookiesId(req, request);
+
+        return true;
+    }
+
+
+    /**
+     * Parse session id in URL.
+     */
+    protected void parseSessionId(org.apache.coyote.Request req, Request request) {
+
+        CharChunk uriCC = req.decodedURI().getCharChunk();
+        int semicolon = uriCC.indexOf(match, 0, match.length(), 0);
+
+        if (semicolon > 0) {
+
+            // Parse session ID, and extract it from the decoded request URI
+            int start = uriCC.getStart();
+            int end = uriCC.getEnd();
+
+            int sessionIdStart = start + semicolon + match.length();
+            int semicolon2 = uriCC.indexOf(';', sessionIdStart);
+            if (semicolon2 >= 0) {
+                request.setRequestedSessionId
+                    (new String(uriCC.getBuffer(), sessionIdStart, 
+                                semicolon2 - semicolon - match.length()));
+            } else {
+                request.setRequestedSessionId
+                    (new String(uriCC.getBuffer(), sessionIdStart, 
+                                end - sessionIdStart));
+            }
+            request.setRequestedSessionURL(true);
+
+            // Extract session ID from request URI
+            ByteChunk uriBC = req.requestURI().getByteChunk();
+            start = uriBC.getStart();
+            end = uriBC.getEnd();
+            semicolon = uriBC.indexOf(match, 0, match.length(), 0);
+
+            if (semicolon > 0) {
+                sessionIdStart = start + semicolon;
+                semicolon2 = uriCC.indexOf
+                    (';', start + semicolon + match.length());
+                uriBC.setEnd(start + semicolon);
+                byte[] buf = uriBC.getBuffer();
+                if (semicolon2 >= 0) {
+                    for (int i = 0; i < end - start - semicolon2; i++) {
+                        buf[start + semicolon + i] 
+                            = buf[start + i + semicolon2];
+                    }
+                    uriBC.setBytes(buf, start, semicolon 
+                                   + (end - start - semicolon2));
+                }
+            }
+
+        } else {
+            request.setRequestedSessionId(null);
+            request.setRequestedSessionURL(false);
+        }
+
+    }
+
+
+    /**
+     * Parse session id in URL.
+     */
+    protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {
+
+        // Parse session id from cookies
+        Cookies serverCookies = req.getCookies();
+        int count = serverCookies.getCookieCount();
+        if (count <= 0)
+            return;
+
+        for (int i = 0; i < count; i++) {
+            ServerCookie scookie = serverCookies.getCookie(i);
+            if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) {
+                // Override anything requested in the URL
+                if (!request.isRequestedSessionIdFromCookie()) {
+                    // Accept only the first session id cookie
+                    convertMB(scookie.getValue());
+                    request.setRequestedSessionId
+                        (scookie.getValue().toString());
+                    request.setRequestedSessionCookie(true);
+                    request.setRequestedSessionURL(false);
+                    if (log.isDebugEnabled())
+                        log.debug(" Requested cookie session id is " +
+                            request.getRequestedSessionId());
+                } else {
+                    if (!request.isRequestedSessionIdValid()) {
+                        // Replace the session id until one is valid
+                        convertMB(scookie.getValue());
+                        request.setRequestedSessionId
+                            (scookie.getValue().toString());
+                    }
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Character conversion of the URI.
+     */
+    protected void convertURI(MessageBytes uri, Request request) 
+        throws Exception {
+
+        ByteChunk bc = uri.getByteChunk();
+        CharChunk cc = uri.getCharChunk();
+        cc.allocate(bc.getLength(), -1);
+
+        String enc = connector.getURIEncoding();
+        if (enc != null) {
+            B2CConverter conv = request.getURIConverter();
+            try {
+                if (conv == null) {
+                    conv = new B2CConverter(enc);
+                    request.setURIConverter(conv);
+                } else {
+                    conv.recycle();
+                }
+            } catch (IOException e) {
+                // Ignore
+                log.error("Invalid URI encoding; using HTTP default");
+                connector.setURIEncoding(null);
+            }
+            if (conv != null) {
+                try {
+                    conv.convert(bc, cc);
+                    uri.setChars(cc.getBuffer(), cc.getStart(), 
+                                 cc.getLength());
+                    return;
+                } catch (IOException e) {
+                    log.error("Invalid URI character encoding; trying ascii");
+                    cc.recycle();
+                }
+            }
+        }
+
+        // Default encoding: fast conversion
+        byte[] bbuf = bc.getBuffer();
+        char[] cbuf = cc.getBuffer();
+        int start = bc.getStart();
+        for (int i = 0; i < bc.getLength(); i++) {
+            cbuf[i] = (char) (bbuf[i + start] & 0xff);
+        }
+        uri.setChars(cbuf, 0, bc.getLength());
+
+    }
+
+
+    /**
+     * Character conversion of the a US-ASCII MessageBytes.
+     */
+    protected void convertMB(MessageBytes mb) {
+
+        // This is of course only meaningful for bytes
+        if (mb.getType() != MessageBytes.T_BYTES)
+            return;
+        
+        ByteChunk bc = mb.getByteChunk();
+        CharChunk cc = mb.getCharChunk();
+        cc.allocate(bc.getLength(), -1);
+
+        // Default encoding: fast conversion
+        byte[] bbuf = bc.getBuffer();
+        char[] cbuf = cc.getBuffer();
+        int start = bc.getStart();
+        for (int i = 0; i < bc.getLength(); i++) {
+            cbuf[i] = (char) (bbuf[i + start] & 0xff);
+        }
+        mb.setChars(cbuf, 0, bc.getLength());
+
+    }
+
+
+    /**
+     * Normalize URI.
+     * <p>
+     * This method normalizes "\", "//", "/./" and "/../". This method will
+     * return false when trying to go above the root, or if the URI contains
+     * a null byte.
+     * 
+     * @param uriMB URI to be normalized
+     */
+    public static boolean normalize(MessageBytes uriMB) {
+
+        ByteChunk uriBC = uriMB.getByteChunk();
+        byte[] b = uriBC.getBytes();
+        int start = uriBC.getStart();
+        int end = uriBC.getEnd();
+
+        // URL * is acceptable
+        if ((end - start == 1) && b[start] == (byte) '*')
+          return true;
+
+        int pos = 0;
+        int index = 0;
+
+        // Replace '\' with '/'
+        // Check for null byte
+        for (pos = start; pos < end; pos++) {
+            if (b[pos] == (byte) '\\')
+                b[pos] = (byte) '/';
+            if (b[pos] == (byte) 0)
+                return false;
+        }
+
+        // The URL must start with '/'
+        if (b[start] != (byte) '/') {
+            return false;
+        }
+
+        // Replace "//" with "/"
+        for (pos = start; pos < (end - 1); pos++) {
+            if (b[pos] == (byte) '/') {
+                while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) {
+                    copyBytes(b, pos, pos + 1, end - pos - 1);
+                    end--;
+                }
+            }
+        }
+
+        // If the URI ends with "/." or "/..", then we append an extra "/"
+        // Note: It is possible to extend the URI by 1 without any side effect
+        // as the next character is a non-significant WS.
+        if (((end - start) >= 2) && (b[end - 1] == (byte) '.')) {
+            if ((b[end - 2] == (byte) '/') 
+                || ((b[end - 2] == (byte) '.') 
+                    && (b[end - 3] == (byte) '/'))) {
+                b[end] = (byte) '/';
+                end++;
+            }
+        }
+
+        uriBC.setEnd(end);
+
+        index = 0;
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            index = uriBC.indexOf("/./", 0, 3, index);
+            if (index < 0)
+                break;
+            copyBytes(b, start + index, start + index + 2, 
+                      end - start - index - 2);
+            end = end - 2;
+            uriBC.setEnd(end);
+        }
+
+        index = 0;
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            index = uriBC.indexOf("/../", 0, 4, index);
+            if (index < 0)
+                break;
+            // Prevent from going outside our context
+            if (index == 0)
+                return false;
+            int index2 = -1;
+            for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
+                if (b[pos] == (byte) '/') {
+                    index2 = pos;
+                }
+            }
+            copyBytes(b, start + index2, start + index + 3,
+                      end - start - index - 3);
+            end = end + index2 - index - 3;
+            uriBC.setEnd(end);
+            index = index2;
+        }
+
+        uriBC.setBytes(b, start, end);
+
+        return true;
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Copy an array of bytes to a different position. Used during 
+     * normalization.
+     */
+    protected static void copyBytes(byte[] b, int dest, int src, int len) {
+        for (int pos = 0; pos < len; pos++) {
+            b[pos + dest] = b[pos + src];
+        }
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/CoyoteInputStream.java b/container/catalina/src/share/org/apache/catalina/connector/CoyoteInputStream.java
new file mode 100644
index 0000000..6d8ad7c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/CoyoteInputStream.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import javax.servlet.ServletInputStream;
+
+import org.apache.catalina.security.SecurityUtil;
+
+/**
+ * This class handles reading bytes.
+ * 
+ * @author Remy Maucherat
+ * @author Jean-Francois Arcand
+ */
+public class CoyoteInputStream
+    extends ServletInputStream {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected InputBuffer ib;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    protected CoyoteInputStream(InputBuffer ib) {
+        this.ib = ib;
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Clear facade.
+     */
+    void clear() {
+        ib = null;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Prevent cloning the facade.
+     */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+
+    // --------------------------------------------- ServletInputStream Methods
+
+
+    public int read()
+        throws IOException {    
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            
+            try{
+                Integer result = 
+                    (Integer)AccessController.doPrivileged(
+                        new PrivilegedExceptionAction(){
+
+                            public Object run() throws IOException{
+                                Integer integer = new Integer(ib.readByte());
+                                return integer;
+                            }
+
+                });
+                return result.intValue();
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+            return ib.readByte();
+        }
+    }
+
+    public int available() throws IOException {
+        
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                Integer result = 
+                    (Integer)AccessController.doPrivileged(
+                        new PrivilegedExceptionAction(){
+
+                            public Object run() throws IOException{
+                                Integer integer = new Integer(ib.available());
+                                return integer;
+                            }
+
+                });
+                return result.intValue();
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+           return ib.available();
+        }    
+    }
+
+    public int read(final byte[] b) throws IOException {
+        
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                Integer result = 
+                    (Integer)AccessController.doPrivileged(
+                        new PrivilegedExceptionAction(){
+
+                            public Object run() throws IOException{
+                                Integer integer = 
+                                    new Integer(ib.read(b, 0, b.length));
+                                return integer;
+                            }
+
+                });
+                return result.intValue();
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+            return ib.read(b, 0, b.length);
+         }        
+    }
+
+
+    public int read(final byte[] b, final int off, final int len)
+        throws IOException {
+            
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                Integer result = 
+                    (Integer)AccessController.doPrivileged(
+                        new PrivilegedExceptionAction(){
+
+                            public Object run() throws IOException{
+                                Integer integer = 
+                                    new Integer(ib.read(b, off, len));
+                                return integer;
+                            }
+
+                });
+                return result.intValue();
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+            return ib.read(b, off, len);
+        }            
+    }
+
+
+    public int readLine(byte[] b, int off, int len) throws IOException {
+        return super.readLine(b, off, len);
+    }
+
+
+    /** 
+     * Close the stream
+     * Since we re-cycle, we can't allow the call to super.close()
+     * which would permantely disable us.
+     */
+    public void close() throws IOException {
+        
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                AccessController.doPrivileged(
+                    new PrivilegedExceptionAction(){
+
+                        public Object run() throws IOException{
+                            ib.close();
+                            return null;
+                        }
+
+                });
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+             ib.close();
+        }            
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/CoyoteOutputStream.java b/container/catalina/src/share/org/apache/catalina/connector/CoyoteOutputStream.java
new file mode 100644
index 0000000..0dc4c31
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/CoyoteOutputStream.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * Coyote implementation of the servlet output stream.
+ * 
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public class CoyoteOutputStream 
+    extends ServletOutputStream {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected OutputBuffer ob;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    protected CoyoteOutputStream(OutputBuffer ob) {
+        this.ob = ob;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Prevent cloning the facade.
+     */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Clear facade.
+     */
+    void clear() {
+        ob = null;
+    }
+
+
+    // --------------------------------------------------- OutputStream Methods
+
+
+    public void write(int i)
+        throws IOException {
+        ob.writeByte(i);
+    }
+
+
+    public void write(byte[] b)
+        throws IOException {
+        write(b, 0, b.length);
+    }
+
+
+    public void write(byte[] b, int off, int len)
+        throws IOException {
+        ob.write(b, off, len);
+    }
+
+
+    /**
+     * Will send the buffer to the client.
+     */
+    public void flush()
+        throws IOException {
+        ob.flush();
+    }
+
+
+    public void close()
+        throws IOException {
+        ob.close();
+    }
+
+
+    // -------------------------------------------- ServletOutputStream Methods
+
+
+    public void print(String s)
+        throws IOException {
+        ob.write(s);
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/connector/CoyotePrincipal.java b/container/catalina/src/share/org/apache/catalina/connector/CoyotePrincipal.java
new file mode 100644
index 0000000..dc0a26b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/CoyotePrincipal.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.connector;
+
+import java.security.Principal;
+
+/**
+ * Generic implementation of <strong>java.security.Principal</strong> that
+ * is used to represent principals authenticated at the protocol handler level.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class CoyotePrincipal 
+    implements Principal {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public CoyotePrincipal(String name) {
+
+        this.name = name;
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The username of the user represented by this Principal.
+     */
+    protected String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object, which exposes only
+     * information that should be public.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("CoyotePrincipal[");
+        sb.append(this.name);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/CoyoteReader.java b/container/catalina/src/share/org/apache/catalina/connector/CoyoteReader.java
new file mode 100644
index 0000000..120ddce
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/CoyoteReader.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.catalina.connector;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+
+/**
+ * Coyote implementation of the buffred reader.
+ * 
+ * @author Remy Maucherat
+ */
+public class CoyoteReader
+    extends BufferedReader {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    private static final char[] LINE_SEP = { '\r', '\n' };
+    private static final int MAX_LINE_LENGTH = 4096;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected InputBuffer ib;
+
+
+    protected char[] lineBuffer = null;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public CoyoteReader(InputBuffer ib) {
+        super(ib, 1);
+        this.ib = ib;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Prevent cloning the facade.
+     */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Clear facade.
+     */
+    void clear() {
+        ib = null;
+    }
+
+
+    // --------------------------------------------------------- Reader Methods
+
+
+    public void close()
+        throws IOException {
+        ib.close();
+    }
+
+
+    public int read()
+        throws IOException {
+        return ib.read();
+    }
+
+
+    public int read(char[] cbuf)
+        throws IOException {
+        return ib.read(cbuf, 0, cbuf.length);
+    }
+
+
+    public int read(char[] cbuf, int off, int len)
+        throws IOException {
+        return ib.read(cbuf, off, len);
+    }
+
+
+    public long skip(long n)
+        throws IOException {
+        return ib.skip(n);
+    }
+
+
+    public boolean ready()
+        throws IOException {
+        return ib.ready();
+    }
+
+
+    public boolean markSupported() {
+        return true;
+    }
+
+
+    public void mark(int readAheadLimit)
+        throws IOException {
+        ib.mark(readAheadLimit);
+    }
+
+
+    public void reset()
+        throws IOException {
+        ib.reset();
+    }
+
+
+    public String readLine()
+        throws IOException {
+
+        if (lineBuffer == null) {
+            lineBuffer = new char[MAX_LINE_LENGTH];
+       }
+
+        String result = null;
+
+        int pos = 0;
+        int end = -1;
+        int skip = -1;
+        StringBuffer aggregator = null;
+        while (end < 0) {
+            mark(MAX_LINE_LENGTH);
+            while ((pos < MAX_LINE_LENGTH) && (end < 0)) {
+                int nRead = read(lineBuffer, pos, MAX_LINE_LENGTH - pos);
+                if (nRead < 0) {
+                    if (pos == 0) {
+                        return null;
+                    }
+                    end = pos;
+                    skip = pos;
+                }
+                for (int i = pos; (i < (pos + nRead)) && (end < 0); i++) {
+                    if (lineBuffer[i] == LINE_SEP[0]) {
+                        end = i;
+                        skip = i + 1;
+                        char nextchar;
+                        if (i == (pos + nRead - 1)) {
+                            nextchar = (char) read();
+                        } else {
+                            nextchar = lineBuffer[i+1];
+                        }
+                        if (nextchar == LINE_SEP[1]) {
+                            skip++;
+                        }
+                    } else if (lineBuffer[i] == LINE_SEP[1]) {
+                        end = i;
+                        skip = i + 1;
+                    }
+                }
+                if (nRead > 0) {
+                    pos += nRead;
+                }
+            }
+            if (end < 0) {
+                if (aggregator == null) {
+                    aggregator = new StringBuffer();
+                }
+                aggregator.append(lineBuffer);
+                pos = 0;
+            } else {
+                reset();
+                skip(skip);
+            }
+        }
+
+        if (aggregator == null) {
+            result = new String(lineBuffer, 0, end);
+        } else {
+            aggregator.append(lineBuffer, 0, end);
+            result = aggregator.toString();
+        }
+
+        return result;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/CoyoteWriter.java b/container/catalina/src/share/org/apache/catalina/connector/CoyoteWriter.java
new file mode 100644
index 0000000..edd6603
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/CoyoteWriter.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Coyote implementation of the servlet writer.
+ * 
+ * @author Remy Maucherat
+ */
+public class CoyoteWriter
+    extends PrintWriter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    private static final char[] LINE_SEP = { '\r', '\n' };
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected OutputBuffer ob;
+    protected boolean error = false;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public CoyoteWriter(OutputBuffer ob) {
+        super(ob);
+        this.ob = ob;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Prevent cloning the facade.
+     */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Clear facade.
+     */
+    void clear() {
+        ob = null;
+    }
+
+
+    /**
+     * Recycle.
+     */
+    void recycle() {
+        error = false;
+    }
+
+
+    // --------------------------------------------------------- Writer Methods
+
+
+    public void flush() {
+
+        if (error)
+            return;
+
+        try {
+            ob.flush();
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void close() {
+
+        // We don't close the PrintWriter - super() is not called,
+        // so the stream can be reused. We close ob.
+        try {
+            ob.close();
+        } catch (IOException ex ) {
+            ;
+        }
+        error = false;
+
+    }
+
+
+    public boolean checkError() {
+        flush();
+        return error;
+    }
+
+
+    public void write(int c) {
+
+        if (error)
+            return;
+
+        try {
+            ob.write(c);
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void write(char buf[], int off, int len) {
+
+        if (error)
+            return;
+
+        try {
+            ob.write(buf, off, len);
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void write(char buf[]) {
+	write(buf, 0, buf.length);
+    }
+
+
+    public void write(String s, int off, int len) {
+
+        if (error)
+            return;
+
+        try {
+            ob.write(s, off, len);
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void write(String s) {
+        write(s, 0, s.length());
+    }
+
+
+    // ---------------------------------------------------- PrintWriter Methods
+
+
+    public void print(boolean b) {
+        if (b) {
+            write("true");
+        } else {
+            write("false");
+        }
+    }
+
+
+    public void print(char c) {
+        write(c);
+    }
+
+
+    public void print(int i) {
+        write(String.valueOf(i));
+    }
+
+
+    public void print(long l) {
+        write(String.valueOf(l));
+    }
+
+
+    public void print(float f) {
+        write(String.valueOf(f));
+    }
+
+
+    public void print(double d) {
+        write(String.valueOf(d));
+    }
+
+
+    public void print(char s[]) {
+        write(s);
+    }
+
+
+    public void print(String s) {
+        if (s == null) {
+            s = "null";
+        }
+        write(s);
+    }
+
+
+    public void print(Object obj) {
+        write(String.valueOf(obj));
+    }
+
+
+    public void println() {
+        write(LINE_SEP);
+    }
+
+
+    public void println(boolean b) {
+        print(b);
+        println();
+    }
+
+
+    public void println(char c) {
+        print(c);
+        println();
+    }
+
+
+    public void println(int i) {
+        print(i);
+        println();
+    }
+
+
+    public void println(long l) {
+        print(l);
+        println();
+    }
+
+
+    public void println(float f) {
+        print(f);
+        println();
+    }
+
+
+    public void println(double d) {
+        print(d);
+        println();
+    }
+
+
+    public void println(char c[]) {
+        print(c);
+        println();
+    }
+
+
+    public void println(String s) {
+        print(s);
+        println();
+    }
+
+
+    public void println(Object o) {
+        print(o);
+        println();
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/InputBuffer.java b/container/catalina/src/share/org/apache/catalina/connector/InputBuffer.java
new file mode 100644
index 0000000..66e8614
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/InputBuffer.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+
+
+/**
+ * The buffer used by Tomcat request. This is a derivative of the Tomcat 3.3
+ * OutputBuffer, adapted to handle input instead of output. This allows 
+ * complete recycling of the facade objects (the ServletInputStream and the
+ * BufferedReader).
+ *
+ * @author Remy Maucherat
+ */
+public class InputBuffer extends Reader
+    implements ByteChunk.ByteInputChannel, CharChunk.CharInputChannel,
+               CharChunk.CharOutputChannel {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String DEFAULT_ENCODING = 
+        org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+    public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+
+    // The buffer can be used for byte[] and char[] reading
+    // ( this is needed to support ServletInputStream and BufferedReader )
+    public final int INITIAL_STATE = 0;
+    public final int CHAR_STATE = 1;
+    public final int BYTE_STATE = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The byte buffer.
+     */
+    private ByteChunk bb;
+
+
+    /**
+     * The chunk buffer.
+     */
+    private CharChunk cb;
+
+
+    /**
+     * State of the output buffer.
+     */
+    private int state = 0;
+
+
+    /**
+     * Number of bytes read.
+     */
+    private int bytesRead = 0;
+
+
+    /**
+     * Number of chars read.
+     */
+    private int charsRead = 0;
+
+
+    /**
+     * Flag which indicates if the input buffer is closed.
+     */
+    private boolean closed = false;
+
+
+    /**
+     * Byte chunk used to input bytes.
+     */
+    private ByteChunk inputChunk = new ByteChunk();
+
+
+    /**
+     * Encoding to use.
+     */
+    private String enc;
+
+
+    /**
+     * Encoder is set.
+     */
+    private boolean gotEnc = false;
+
+
+    /**
+     * List of encoders.
+     */
+    protected HashMap encoders = new HashMap();
+
+
+    /**
+     * Current byte to char converter.
+     */
+    protected B2CConverter conv;
+
+
+    /**
+     * Associated Coyote request.
+     */
+    private Request coyoteRequest;
+
+
+    /**
+     * Buffer position.
+     */
+    private int markPos = -1;
+
+
+    /**
+     * Buffer size.
+     */
+    private int size = -1;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor. Allocate the buffer with the default buffer size.
+     */
+    public InputBuffer() {
+
+        this(DEFAULT_BUFFER_SIZE);
+
+    }
+
+
+    /**
+     * Alternate constructor which allows specifying the initial buffer size.
+     * 
+     * @param size Buffer size to use
+     */
+    public InputBuffer(int size) {
+
+        this.size = size;
+        bb = new ByteChunk(size);
+        bb.setLimit(size);
+        bb.setByteInputChannel(this);
+        cb = new CharChunk(size);
+        cb.setLimit(size);
+        cb.setOptimizedWrite(false);
+        cb.setCharInputChannel(this);
+        cb.setCharOutputChannel(this);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Coyote request.
+     * 
+     * @param coyoteRequest Associated Coyote request
+     */
+    public void setRequest(Request coyoteRequest) {
+	this.coyoteRequest = coyoteRequest;
+    }
+
+
+    /**
+     * Get associated Coyote request.
+     * 
+     * @return the associated Coyote request
+     */
+    public Request getRequest() {
+        return this.coyoteRequest;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the output buffer.
+     */
+    public void recycle() {
+        
+        state = INITIAL_STATE;
+        bytesRead = 0;
+        charsRead = 0;
+        
+        // If usage of mark made the buffer too big, reallocate it
+        if (cb.getChars().length > size) {
+            cb = new CharChunk(size);
+            cb.setLimit(size);
+            cb.setCharInputChannel(this);
+            cb.setCharOutputChannel(this);
+        } else {
+            cb.recycle();
+        }
+        markPos = -1;
+        bb.recycle(); 
+        closed = false;
+        
+        if (conv != null) {
+            conv.recycle();
+        }
+        
+        gotEnc = false;
+        enc = null;
+        
+    }
+
+
+    /**
+     * Close the input buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void close()
+        throws IOException {
+        closed = true;
+    }
+
+
+    public int available()
+        throws IOException {
+        if (state == BYTE_STATE) {
+            return bb.getLength();
+        } else if (state == CHAR_STATE) {
+            return cb.getLength();
+        } else {
+            return 0;
+        }
+    }
+
+
+    // ------------------------------------------------- Bytes Handling Methods
+
+
+    /** 
+     * Reads new bytes in the byte chunk.
+     * 
+     * @param cbuf Byte buffer to be written to the response
+     * @param off Offset
+     * @param len Length
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public int realReadBytes(byte cbuf[], int off, int len)
+	throws IOException {
+
+        if (closed)
+            return -1;
+        if (coyoteRequest == null)
+            return -1;
+
+        state = BYTE_STATE;
+
+        int result = coyoteRequest.doRead(bb);
+
+        return result;
+
+    }
+
+
+    public int readByte()
+        throws IOException {
+        return bb.substract();
+    }
+
+
+    public int read(byte[] b, int off, int len)
+        throws IOException {
+        return bb.substract(b, off, len);
+    }
+
+
+    // ------------------------------------------------- Chars Handling Methods
+
+
+    /**
+     * Since the converter will use append, it is possible to get chars to
+     * be removed from the buffer for "writing". Since the chars have already
+     * been read before, they are ignored. If a mark was set, then the
+     * mark is lost.
+     */
+    public void realWriteChars(char c[], int off, int len) 
+        throws IOException {
+        markPos = -1;
+    }
+
+
+    public void setEncoding(String s) {
+        enc = s;
+    }
+
+
+    public int realReadChars(char cbuf[], int off, int len)
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+        if (bb.getLength() <= 0) {
+            int nRead = realReadBytes(bb.getBytes(), 0, bb.getBytes().length);
+            if (nRead < 0) {
+                return -1;
+            }
+        }
+
+        if (markPos == -1) {
+            cb.setOffset(0);
+            cb.setEnd(0);
+        }
+
+        conv.convert(bb, cb);
+        bb.setOffset(bb.getEnd());
+        state = CHAR_STATE;
+
+        return cb.getLength();
+
+    }
+
+
+    public int read()
+        throws IOException {
+        return cb.substract();
+    }
+
+
+    public int read(char[] cbuf)
+        throws IOException {
+        return read(cbuf, 0, cbuf.length);
+    }
+
+
+    public int read(char[] cbuf, int off, int len)
+        throws IOException {
+        return cb.substract(cbuf, off, len);
+    }
+
+
+    public long skip(long n)
+        throws IOException {
+
+        if (n < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        long nRead = 0;
+        while (nRead < n) {
+            if (cb.getLength() >= n) {
+                cb.setOffset(cb.getStart() + (int) n);
+                nRead = n;
+            } else {
+                nRead += cb.getLength();
+                cb.setOffset(cb.getEnd());
+                int toRead = 0;
+                if (cb.getChars().length < (n - nRead)) {
+                    toRead = cb.getChars().length;
+                } else {
+                    toRead = (int) (n - nRead);
+                }
+                int nb = realReadChars(cb.getChars(), 0, toRead);
+                if (nb < 0)
+                    break;
+            }
+        }
+
+        return nRead;
+
+    }
+
+
+    public boolean ready()
+        throws IOException {
+        return (cb.getLength() > 0);
+    }
+
+
+    public boolean markSupported() {
+        return true;
+    }
+
+
+    public void mark(int readAheadLimit)
+        throws IOException {
+        if (cb.getLength() <= 0) {
+            cb.setOffset(0);
+            cb.setEnd(0);
+        } else {
+            if ((cb.getBuffer().length > (2 * size)) 
+                && (cb.getLength()) < (cb.getStart())) {
+                System.arraycopy(cb.getBuffer(), cb.getStart(), 
+                                 cb.getBuffer(), 0, cb.getLength());
+                cb.setEnd(cb.getLength());
+                cb.setOffset(0);
+            }
+        }
+        int offset = readAheadLimit;
+        if (offset < size) {
+            offset = size;
+        }
+        cb.setLimit(cb.getStart() + offset);
+        markPos = cb.getStart();
+    }
+
+
+    public void reset()
+        throws IOException {
+        if (state == CHAR_STATE) {
+            if (markPos < 0) {
+                cb.recycle();
+                markPos = -1;
+                throw new IOException();
+            } else {
+                cb.setOffset(markPos);
+            }
+        } else {
+            bb.recycle();
+        }
+    }
+
+
+    public void checkConverter() 
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+    }
+
+
+    protected void setConverter()
+        throws IOException {
+
+        if (coyoteRequest != null)
+            enc = coyoteRequest.getCharacterEncoding();
+
+        gotEnc = true;
+        if (enc == null)
+            enc = DEFAULT_ENCODING;
+        conv = (B2CConverter) encoders.get(enc);
+        if (conv == null) {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    conv = (B2CConverter)AccessController.doPrivileged(
+                            new PrivilegedExceptionAction(){
+
+                                public Object run() throws IOException{
+                                    return new B2CConverter(enc);
+                                }
+
+                            }
+                    );              
+                }catch(PrivilegedActionException ex){
+                    Exception e = ex.getException();
+                    if (e instanceof IOException)
+                        throw (IOException)e; 
+                }
+            } else {
+                conv = new B2CConverter(enc);
+            }
+            encoders.put(enc, conv);
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/connector/LocalStrings.properties
new file mode 100644
index 0000000..a85f688
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/LocalStrings.properties
@@ -0,0 +1,64 @@
+
+#
+# CoyoteConnector
+#
+
+coyoteConnector.alreadyInitialized=The connector has already been initialized
+coyoteConnector.alreadyStarted=The connector has already been started
+coyoteConnector.cannotRegisterProtocol=Cannot register MBean for the Protocol
+coyoteConnector.notStarted=Coyote connector has not been started
+coyoteConnector.protocolHandlerDestroyFailed=Protocol handler destroy failed: {0}
+coyoteConnector.protocolHandlerInitializationFailed=Protocol handler initialization failed: {0}
+coyoteConnector.protocolHandlerInstantiationFailed=Protocol handler instantiation failed: {0}
+coyoteConnector.protocolHandlerStartFailed=Protocol handler start failed: {0}
+coyoteConnector.protocolRegistrationFailed=Protocol JMX registration failed
+coyoteConnector.protocolHandlerPauseFailed=Protocol handler pause failed
+coyoteConnector.protocolHandlerResumeFailed=Protocol handler resume failed
+coyoteConnector.MapperRegistration=register Mapper: {0}
+coyoteConnector.protocolUnregistrationFailed=Protocol handler stop failed
+
+
+#
+# CoyoteAdapter
+#
+
+coyoteAdapter.service=An exception or error occurred in the container during the request processing
+
+#
+# CoyoteResponse
+#
+
+coyoteResponse.getOutputStream.ise=getWriter() has already been called for this response
+coyoteResponse.getWriter.ise=getOutputStream() has already been called for this response
+coyoteResponse.resetBuffer.ise=Cannot reset buffer after response has been committed
+coyoteResponse.sendError.ise=Cannot call sendError() after the response has been committed
+coyoteResponse.sendRedirect.ise=Cannot call sendRedirect() after the response has been committed
+coyoteResponse.setBufferSize.ise=Cannot change buffer size after data has been written
+
+#
+# CoyoteRequest
+#
+
+coyoteRequest.getInputStream.ise=getReader() has already been called for this request
+coyoteRequest.getReader.ise=getInputStream() has already been called for this request
+coyoteRequest.sessionCreateCommitted=Cannot create a session after the response has been committed
+coyoteRequest.setAttribute.namenull=Cannot call setAttribute with a null name
+coyoteRequest.listenerStart=Exception sending context initialized event to listener instance of class {0}
+coyoteRequest.listenerStop=Exception sending context destroyed event to listener instance of class {0}
+coyoteRequest.attributeEvent=Exception thrown by attributes event listener
+coyoteRequest.postTooLarge=Parameters were not parsed because the size of the posted data was too big. Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs.
+requestFacade.nullRequest=Null request object
+responseFacade.nullResponse=Null response object
+
+#
+# MapperListener
+#
+mapperListener.unknownDefaultHost=Unknown default host: {0}
+mapperListener.registerHost=Register host {0} at domain {1}
+mapperListener.unregisterHost=Unregister host {0} at domain {1}
+mapperListener.registerContext=Register Context {0} 
+mapperListener.unregisterContext=Unregister Context {0}
+mapperListener.registerWrapper=Register Wrapper {0} in Context {1}
+
+
+
diff --git a/container/catalina/src/share/org/apache/catalina/connector/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/connector/LocalStrings_es.properties
new file mode 100644
index 0000000..6bca4bf
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/LocalStrings_es.properties
@@ -0,0 +1,56 @@
+
+#
+# CoyoteConnector
+#
+
+coyoteConnector.alreadyInitialized=Ya ha sido inicializado el conector
+coyoteConnector.alreadyStarted=Ya ha sido arrancado el conector
+coyoteConnector.cannotRegisterProtocol=No puedo registrar MBean para el Protocolo
+coyoteConnector.notStarted=El conector Coyote no ha sido arrancado
+coyoteConnector.protocolHandlerDestroyFailed=Falló la destrucción del manejador de protocolo: {0}
+coyoteConnector.protocolHandlerInitializationFailed=Falló la inicialización del manejador de protocolo: {0}
+coyoteConnector.protocolHandlerInstantiationFailed=Falló la instanciación del manejador de protocolo: {0}
+coyoteConnector.protocolHandlerStartFailed=Falló el arranque del manejador de protocolo: {0}
+coyoteConnector.protocolRegistrationFailed=Falló el registro de JMX
+
+#
+# CoyoteAdapter
+#
+
+coyoteAdapter.service=Ha tenido lugar una excepción o error en el contenedor durante el procesamiento del requerimiento
+
+#
+# CoyoteResponse
+#
+
+coyoteResponse.getOutputStream.ise=getWriter() ya ha sido llamado para esta respuesta
+coyoteResponse.getWriter.ise=getOutputStream() ya ha sido llamado para esta respuesta
+coyoteResponse.resetBuffer.ise=No puedo limpiar el búfer después de que la repuesta ha sido llevada a cabo
+coyoteResponse.sendError.ise=No puedo llamar a sendError() tras llevar a cabo la respuesta
+coyoteResponse.sendRedirect.ise=No puedo llamar a sendRedirect() tras llevar a cabo la respuesta
+coyoteResponse.setBufferSize.ise=No puedo cambiar la medida del búfer tras escribir los datos
+
+#
+# CoyoteRequest
+#
+
+coyoteRequest.getInputStream.ise=getReader() ya ha sido llamado para este requerimiento
+coyoteRequest.getReader.ise=getInputStream() ya ha sido llamado para este requerimiento
+coyoteRequest.sessionCreateCommitted=No puedo crear una sesión después de llevar a cabo la respueta
+coyoteRequest.setAttribute.namenull=No pudeo llamar a setAttribute con un nombre nulo
+coyoteRequest.listenerStart=Excepción enviando evento inicializado de contexto a instancia de escuchador de clase {0}
+coyoteRequest.listenerStop=Excepción enviando evento destruído de contexto a instancia de escuchador de clase {0}
+coyoteRequest.attributeEvent=Excepción lanzada mediante el escuchador de eventos de atributos
+coyoteRequest.postTooLarge=No se analizaron los parámetros porque la medida de los datos enviados era demasiado grande. Usa el atributo maxPostSize del conector para resolver esto en caso de que la aplicación debiera de aceptar POSTs más grandes.
+
+
+#
+# MapperListener
+#
+
+mapperListener.registerContext=Registrar Contexto {0}
+mapperListener.unregisterContext=Desregistrar Contexto {0}
+mapperListener.registerWrapper=Registrar Arropador (Wrapper) {0} en Contexto {1}
+
+
+
diff --git a/container/catalina/src/share/org/apache/catalina/connector/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/connector/LocalStrings_fr.properties
new file mode 100644
index 0000000..069058d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/LocalStrings_fr.properties
@@ -0,0 +1,58 @@
+
+#
+# CoyoteConnector
+#
+
+coyoteConnector.alreadyInitialized=Le connecteur a déjà été initialisé
+coyoteConnector.alreadyStarted=Le connecteur a déjà été démarré
+coyoteConnector.cannotRegisterProtocol=Impossible d''enregistrer le MBean pour le Protocol
+coyoteConnector.notStarted=Le connecteur Coyote n''a pas été démarré
+coyoteConnector.protocolHandlerDestroyFailed=La destruction du gestionnaire de protocole a échouée: {0}
+coyoteConnector.protocolHandlerInitializationFailed=L''initialisation du gestionnaire de protocole a échoué: {0}
+coyoteConnector.protocolHandlerInstantiationFailed=L''instantiation du gestionnaire de protocole a échoué: {0}
+coyoteConnector.protocolHandlerStartFailed=Le démarrage du gestionnaire de protocole a échoué: {0}
+coyoteConnector.protocolRegistrationFailed=L''enregistrement du protocol JMX a échoué
+coyoteConnector.protocolHandlerPauseFailed=La suspension du gestionnaire de protocole a échouée
+coyoteConnector.protocolHandlerResumeFailed=Le redémarrage du gestionnaire de protocole a échoué
+
+#
+# CoyoteAdapter
+#
+
+coyoteAdapter.service=Une exception ou une erreur s''est produite dans le conteneur durant le traitement de la requête
+
+#
+# CoyoteResponse
+#
+
+coyoteResponse.getOutputStream.ise="getWriter()" a déjà été appelé pour cette réponse
+coyoteResponse.getWriter.ise="getOutputStream()" a déjà été appelé pour cette réponse
+coyoteResponse.resetBuffer.ise=Impossible de remettre à zéro le tampon après que la réponse ait été envoyée
+coyoteResponse.sendError.ise=Impossible d''appeler "sendError()" après que la réponse ait été envoyée
+coyoteResponse.sendRedirect.ise=Impossible d''appeler "sendRedirect()" après que la réponse ait été envoyée
+coyoteResponse.setBufferSize.ise=Impossible de changer la taille du tampon après que les données aient été écrites
+
+#
+# CoyoteRequest
+#
+
+coyoteRequest.getInputStream.ise="getReader()" a déjà été appelé pour cette requête
+coyoteRequest.getReader.ise="getInputStream()" a déjà été appelé pour cette requête
+coyoteRequest.sessionCreateCommitted=Impossible de créer une sessionaprès que la réponse ait été envoyée
+coyoteRequest.setAttribute.namenull=Impossible d''appeler "setAttribute" avec un nom nul
+coyoteRequest.listenerStart=Une exception s''est produite lors de l''envoi de l''évènement contexte initialisé à l''instance de classe d''écoute {0}
+coyoteRequest.listenerStop=Une exception s''est produite lors de l''envoi de l''évènement contexte détruit à l''instance de classe d''écoute {0}
+coyoteRequest.attributeEvent=Une exception a été lancée par l''instance d''écoute pour l''évènement attributs (attributes)
+coyoteRequest.postTooLarge=Les paramètres n''ont pas été évalué car la taille des données postées est trop important. Utilisez l''attribut maxPostSize du connecteur pour corriger ce problème si votre application doit accepter des POSTs importants.
+
+
+#
+# MapperListener
+#
+
+mapperListener.registerContext=Enregistrement du contexte {0}
+mapperListener.unregisterContext=Désenregistrement du contexte {0}
+mapperListener.registerWrapper=Enregistrement de l''enrobeur (wrapper) {0} dans le contexte {1}
+
+
+
diff --git a/container/catalina/src/share/org/apache/catalina/connector/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/connector/LocalStrings_ja.properties
new file mode 100644
index 0000000..1b3bd81
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/LocalStrings_ja.properties
@@ -0,0 +1,58 @@
+
+#
+# CoyoteConnector
+#
+
+coyoteConnector.alreadyInitialized=\u30b3\u30cd\u30af\u30bf\u306f\u65e2\u306b\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteConnector.alreadyStarted=\u30b3\u30cd\u30af\u30bf\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteConnector.cannotRegisterProtocol=\u305d\u306e\u30d7\u30ed\u30c8\u30b3\u30eb\u306bMBean\u3092\u767b\u9332\u3067\u304d\u307e\u305b\u3093
+coyoteConnector.notStarted=Coyote\u30b3\u30cd\u30af\u30bf\u306f\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+coyoteConnector.protocolHandlerDestroyFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u5ec3\u68c4\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+coyoteConnector.protocolHandlerInitializationFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u521d\u671f\u5316\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+coyoteConnector.protocolHandlerInstantiationFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5316\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+coyoteConnector.protocolHandlerStartFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u8d77\u52d5\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+coyoteConnector.protocolRegistrationFailed=\u30d7\u30ed\u30c8\u30b3\u30ebJMX\u306e\u767b\u9332\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+coyoteConnector.protocolHandlerPauseFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u4e00\u6642\u505c\u6b62\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+coyoteConnector.protocolHandlerResumeFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u518d\u958b\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+
+#
+# CoyoteAdapter
+#
+
+coyoteAdapter.service=\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u51e6\u7406\u4e2d\u306b\u30b3\u30cd\u30af\u30bf\u3067\u4f8b\u5916\u307e\u305f\u306f\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+
+#
+# CoyoteResponse
+#
+
+coyoteResponse.getOutputStream.ise=getWriter()\u306f\u3053\u306e\u30ec\u30b9\u30dd\u30f3\u30b9\u306b\u5bfe\u3057\u3066\u65e2\u306b\u547c\u3073\u51fa\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteResponse.getWriter.ise=getOutputStream()\u306f\u3053\u306e\u30ec\u30b9\u30dd\u30f3\u30b9\u306b\u5bfe\u3057\u3066\u65e2\u306b\u547c\u3073\u51fa\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteResponse.resetBuffer.ise=\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u30b3\u30df\u30c3\u30c8\u3055\u308c\u305f\u5f8c\u3067\u30d0\u30c3\u30d5\u30a1\u3092\u30ea\u30bb\u30c3\u30c8\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+coyoteResponse.sendError.ise=\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u30b3\u30df\u30c3\u30c8\u3055\u308c\u305f\u5f8c\u3067sendError()\u3092\u547c\u3073\u51fa\u3059\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+coyoteResponse.sendRedirect.ise=\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u30b3\u30df\u30c3\u30c8\u3055\u308c\u305f\u5f8c\u3067sendRedirect()\u3092\u547c\u3073\u51fa\u3059\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+coyoteResponse.setBufferSize.ise=\u30c7\u30fc\u30bf\u304c\u65e2\u306b\u66f8\u304d\u8fbc\u307e\u308c\u305f\u5f8c\u3067\u30d0\u30c3\u30d5\u30a1\u30b5\u30a4\u30ba\u3092\u5909\u66f4\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+
+#
+# CoyoteRequest
+#
+
+coyoteRequest.getInputStream.ise=getReader()\u306f\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5bfe\u3057\u3066\u65e2\u306b\u547c\u3073\u51fa\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteRequest.getReader.ise=getInputStream()\u306f\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5bfe\u3057\u3066\u65e2\u306b\u547c\u3073\u51fa\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteRequest.sessionCreateCommitted=\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u30b3\u30df\u30c3\u30c8\u3057\u305f\u5f8c\u3067\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093
+coyoteRequest.setAttribute.namenull=setAttribute\u3092\u540d\u524d\u3092\u6307\u5b9a\u305b\u305a\u306b\u547c\u3073\u51fa\u3059\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+coyoteRequest.listenerStart=\u30af\u30e9\u30b9 {0} \u306e\u30ea\u30b9\u30ca\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u521d\u671f\u5316\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u4e2d\u306b\u4f8b\u5916\u304c\u6295\u3052\u3089\u308c\u307e\u3057\u305f
+coyoteRequest.listenerStop=\u30af\u30e9\u30b9 {0} \u306e\u30ea\u30b9\u30ca\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u5ec3\u68c4\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u4e2d\u306b\u4f8b\u5916\u304c\u6295\u3052\u3089\u308c\u307e\u3057\u305f
+coyoteRequest.attributeEvent=\u5c5e\u6027\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u306b\u3088\u3063\u3066\u4f8b\u5916\u304c\u6295\u3052\u3089\u308c\u307e\u3057\u305f
+coyoteRequest.postTooLarge=POST\u3055\u308c\u305f\u30c7\u30fc\u30bf\u304c\u5927\u304d\u3059\u304e\u305f\u306e\u3067\u3001\u30d1\u30e9\u30e1\u30fc\u30bf\u304c\u69cb\u6587\u89e3\u6790\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u305d\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u5de8\u5927\u306aPOST\u3092\u53d7\u3051\u4ed8\u3051\u306d\u3070\u306a\u3089\u306a\u3044\u5834\u5408\u306b\u306f\u3001\u3053\u308c\u3092\u89e3\u6c7a\u3059\u308b\u305f\u3081\u306b\u30b3\u30cd\u30af\u30bf\u306emaxPostSize\u5c5e\u6027\u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+
+
+#
+# MapperListener
+#
+
+mapperListener.registerContext=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8 {0}\u3000\u3092\u767b\u9332\u3057\u307e\u3059
+mapperListener.unregisterContext=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8 {0} \u306e\u767b\u9332\u3092\u62b9\u6d88\u3057\u307e\u3059
+mapperListener.registerWrapper=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8 {1} \u306b\u30e9\u30c3\u30d1 {0} \u3092\u767b\u9332\u3057\u307e\u3059
+
+
+
diff --git a/container/catalina/src/share/org/apache/catalina/connector/MapperListener.java b/container/catalina/src/share/org/apache/catalina/connector/MapperListener.java
new file mode 100644
index 0000000..588c604
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/MapperListener.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+package org.apache.catalina.connector;
+
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.commons.modeler.Registry;
+
+import org.apache.tomcat.util.http.mapper.Mapper;
+
+import org.apache.tomcat.util.res.StringManager;
+
+
+/**
+ * Mapper listener.
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class MapperListener
+    implements NotificationListener 
+ {
+    private static Log log = LogFactory.getLog(MapperListener.class);
+
+
+    // ----------------------------------------------------- Instance Variables
+    /**
+     * Associated mapper.
+     */
+    protected Mapper mapper = null;
+
+    /**
+     * MBean server.
+     */
+    protected MBeanServer mBeanServer = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    private StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    // It should be null - and fail if not set
+    private String domain="*";
+    private String engine="*";
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create mapper listener.
+     */
+    public MapperListener(Mapper mapper) {
+        this.mapper = mapper;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+
+    public String getEngine() {
+        return engine;
+    }
+
+    public void setEngine(String engine) {
+        this.engine = engine;
+    }
+
+    /**
+     * Initialize associated mapper.
+     */
+    public void init() {
+
+        try {
+
+            mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+
+            registerEngine();
+
+            // Query hosts
+            String onStr = domain + ":type=Host,*";
+            ObjectName objectName = new ObjectName(onStr);
+            Set set = mBeanServer.queryMBeans(objectName, null);
+            Iterator iterator = set.iterator();
+            while (iterator.hasNext()) {
+                ObjectInstance oi = (ObjectInstance) iterator.next();
+                registerHost(oi.getObjectName());
+            }
+
+
+            // Query contexts
+            onStr = "*:j2eeType=WebModule,*";
+            objectName = new ObjectName(onStr);
+            set = mBeanServer.queryMBeans(objectName, null);
+            iterator = set.iterator();
+            while (iterator.hasNext()) {
+                ObjectInstance oi = (ObjectInstance) iterator.next();
+                registerContext(oi.getObjectName());
+            }
+
+            // Query wrappers
+            onStr = "*:j2eeType=Servlet,*";
+            objectName = new ObjectName(onStr);
+            set = mBeanServer.queryMBeans(objectName, null);
+            iterator = set.iterator();
+            while (iterator.hasNext()) {
+                ObjectInstance oi = (ObjectInstance) iterator.next();
+                registerWrapper(oi.getObjectName());
+            }
+
+            onStr = "JMImplementation:type=MBeanServerDelegate";
+            objectName = new ObjectName(onStr);
+            mBeanServer.addNotificationListener(objectName, this, null, null);
+
+        } catch (Exception e) {
+            log.warn("Error registering contexts",e);
+        }
+
+    }
+
+    /**
+     * unregister this from JMImplementation:type=MBeanServerDelegate
+     */
+    public void destroy() {
+        try {
+
+            ObjectName objectName = new ObjectName(
+                    "JMImplementation:type=MBeanServerDelegate");
+            mBeanServer.removeNotificationListener(objectName, this);
+        } catch (Exception e) {
+            log.warn("Error unregistering MBeanServerDelegate", e);
+        }
+    }
+
+    // ------------------------------------------- NotificationListener Methods
+
+
+    public void handleNotification(Notification notification,
+                                   java.lang.Object handback) {
+
+        if (notification instanceof MBeanServerNotification) {
+            ObjectName objectName = 
+                ((MBeanServerNotification) notification).getMBeanName();
+            String j2eeType = objectName.getKeyProperty("j2eeType");
+            String engineName = null;
+            if (j2eeType != null) {
+                if ((j2eeType.equals("WebModule")) || 
+                    (j2eeType.equals("Servlet"))) {
+                    if (mBeanServer.isRegistered(objectName)) {
+                        try {
+                            engineName = (String)
+                                mBeanServer.getAttribute(objectName, "engineName");
+                        } catch (Exception e) {
+                            // Ignore  
+                        }
+                    }
+                }
+            }
+
+            // At deployment time, engineName is always = null.
+            if ( (!"*".equals(domain)) &&
+                 ( !domain.equals(objectName.getDomain()) ) &&
+                 ( (!domain.equals(engineName) ) &&
+                   (engineName != null) ) )  {
+                return;
+            }
+            if(log.isDebugEnabled())
+                log.debug( "Handle " + objectName  + " type : " + notification.getType());    
+            if (notification.getType().equals
+                (MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
+                String type=objectName.getKeyProperty("type");
+                if( "Host".equals( type ) && domain.equals(objectName.getDomain())) {
+                    try {
+                        registerHost(objectName);
+                    } catch (Exception e) {
+                        log.warn("Error registering Host " + objectName, e);  
+                    }
+                }
+    
+                if (j2eeType != null) {
+                    if (j2eeType.equals("WebModule")) {
+                        try {
+                            registerContext(objectName);
+                        } catch (Throwable t) {
+                            log.warn("Error registering Context " + objectName,t);
+                        }
+                    } else if (j2eeType.equals("Servlet")) {
+                        try {
+                            registerWrapper(objectName);
+                        } catch (Throwable t) {
+                            log.warn("Error registering Wrapper " + objectName,t);
+                        }
+                    }
+                }
+            } else if (notification.getType().equals
+                       (MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
+                String type=objectName.getKeyProperty("type");
+                if( "Host".equals( type )&& domain.equals(objectName.getDomain())) {
+                    try {
+                        unregisterHost(objectName);
+                    } catch (Exception e) {
+                        log.warn("Error unregistering Host " + objectName,e);  
+                    }
+                }
+ 
+                if (j2eeType != null) {
+                    if (j2eeType.equals("WebModule")) {
+                        try {
+                            unregisterContext(objectName);
+                        } catch (Throwable t) {
+                            log.warn("Error unregistering webapp " + objectName,t);
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+    private void registerEngine()
+        throws Exception
+    {
+        ObjectName engineName = new ObjectName
+            (domain + ":type=Engine");
+        if ( ! mBeanServer.isRegistered(engineName)) return;
+        String defaultHost = 
+            (String) mBeanServer.getAttribute(engineName, "defaultHost");
+        ObjectName hostName = new ObjectName
+            (domain + ":type=Host," + "host=" + defaultHost);
+        if (!mBeanServer.isRegistered(hostName)) {
+
+            // Get the hosts' list
+            String onStr = domain + ":type=Host,*";
+            ObjectName objectName = new ObjectName(onStr);
+            Set set = mBeanServer.queryMBeans(objectName, null);
+            Iterator iterator = set.iterator();
+            String[] aliases;
+            boolean isRegisteredWithAlias = false;
+            
+            while (iterator.hasNext()) {
+
+                if (isRegisteredWithAlias) break;
+            
+                ObjectInstance oi = (ObjectInstance) iterator.next();
+                hostName = oi.getObjectName();
+                aliases = (String[])
+                    mBeanServer.invoke(hostName, "findAliases", null, null);
+
+                for (int i=0; i < aliases.length; i++){
+                    if (aliases[i].equalsIgnoreCase(defaultHost)){
+                        isRegisteredWithAlias = true;
+                        break;
+                    }
+                }
+            }
+            
+            if (!isRegisteredWithAlias && log.isWarnEnabled())
+                log.warn(sm.getString("mapperListener.unknownDefaultHost", defaultHost));
+        }
+        // This should probablt be called later 
+        if( defaultHost != null ) {
+            mapper.setDefaultHostName(defaultHost);
+        }
+    }
+
+    /**
+     * Register host.
+     */
+    private void registerHost(ObjectName objectName)
+        throws Exception {
+        String name=objectName.getKeyProperty("host");
+        if( name != null ) {        
+            String[] aliases = (String[])
+                mBeanServer.invoke(objectName, "findAliases", null, null);
+            mapper.addHost(name, aliases, objectName);
+            if(log.isDebugEnabled())
+                log.debug(sm.getString
+                     ("mapperListener.registerHost", name, domain));
+
+        }
+    }
+
+
+    /**
+     * Unregister host.
+     */
+    private void unregisterHost(ObjectName objectName)
+        throws Exception {
+        String name=objectName.getKeyProperty("host");
+        mapper.removeHost(name);
+        if(log.isDebugEnabled())
+            log.debug(sm.getString
+                 ("mapperListener.unregisterHost", name, domain));
+    }
+
+
+    /**
+     * Register context.
+     */
+    private void registerContext(ObjectName objectName)
+        throws Exception {
+
+        String name = objectName.getKeyProperty("name");
+        
+        // If the domain is the same with ours or the engine 
+        // name attribute is the same... - then it's ours
+        String targetDomain=objectName.getDomain();
+        if( ! domain.equals( targetDomain )) {
+            try {
+                targetDomain = (String) mBeanServer.getAttribute
+                    (objectName, "engineName");
+            } catch (Exception e) {
+                // Ignore
+            }
+            if( ! domain.equals( targetDomain )) {
+                // not ours
+                return;
+            }
+        }
+
+        String hostName = null;
+        String contextName = null;
+        if (name.startsWith("//")) {
+            name = name.substring(2);
+        }
+        int slash = name.indexOf("/");
+        if (slash != -1) {
+            hostName = name.substring(0, slash);
+            contextName = name.substring(slash);
+        } else {
+            return;
+        }
+        // Special case for the root context
+        if (contextName.equals("/")) {
+            contextName = "";
+        }
+
+        if(log.isDebugEnabled())
+             log.debug(sm.getString
+                  ("mapperListener.registerContext", contextName));
+
+        Object context = 
+            mBeanServer.invoke(objectName, "findMappingObject", null, null);
+            //mBeanServer.getAttribute(objectName, "mappingObject");
+        javax.naming.Context resources = (javax.naming.Context)
+            mBeanServer.invoke(objectName, "findStaticResources", null, null);
+            //mBeanServer.getAttribute(objectName, "staticResources");
+        String[] welcomeFiles = (String[])
+            mBeanServer.getAttribute(objectName, "welcomeFiles");
+
+        mapper.addContext(hostName, contextName, context, 
+                          welcomeFiles, resources);
+
+    }
+
+
+    /**
+     * Unregister context.
+     */
+    private void unregisterContext(ObjectName objectName)
+        throws Exception {
+
+        String name = objectName.getKeyProperty("name");
+
+        // If the domain is the same with ours or the engine 
+        // name attribute is the same... - then it's ours
+        String targetDomain=objectName.getDomain();
+        if( ! domain.equals( targetDomain )) {
+            try {
+                targetDomain = (String) mBeanServer.getAttribute
+                    (objectName, "engineName");
+            } catch (Exception e) {
+                // Ignore
+            }
+            if( ! domain.equals( targetDomain )) {
+                // not ours
+                return;
+            }
+        }
+
+        String hostName = null;
+        String contextName = null;
+        if (name.startsWith("//")) {
+            name = name.substring(2);
+        }
+        int slash = name.indexOf("/");
+        if (slash != -1) {
+            hostName = name.substring(0, slash);
+            contextName = name.substring(slash);
+        } else {
+            return;
+        }
+        // Special case for the root context
+        if (contextName.equals("/")) {
+            contextName = "";
+        }
+        if(log.isDebugEnabled())
+            log.debug(sm.getString
+                  ("mapperListener.unregisterContext", contextName));
+
+        mapper.removeContext(hostName, contextName);
+
+    }
+
+
+    /**
+     * Register wrapper.
+     */
+    private void registerWrapper(ObjectName objectName)
+        throws Exception {
+    
+        // If the domain is the same with ours or the engine 
+        // name attribute is the same... - then it's ours
+        String targetDomain=objectName.getDomain();
+        if( ! domain.equals( targetDomain )) {
+            try {
+                targetDomain=(String) mBeanServer.getAttribute(objectName, "engineName");
+            } catch (Exception e) {
+                // Ignore
+            }
+            if( ! domain.equals( targetDomain )) {
+                // not ours
+                return;
+            }
+            
+        }
+
+        String wrapperName = objectName.getKeyProperty("name");
+        String name = objectName.getKeyProperty("WebModule");
+
+        String hostName = null;
+        String contextName = null;
+        if (name.startsWith("//")) {
+            name = name.substring(2);
+        }
+        int slash = name.indexOf("/");
+        if (slash != -1) {
+            hostName = name.substring(0, slash);
+            contextName = name.substring(slash);
+        } else {
+            return;
+        }
+        // Special case for the root context
+        if (contextName.equals("/")) {
+            contextName = "";
+        }
+        if(log.isDebugEnabled())
+            log.debug(sm.getString
+                  ("mapperListener.registerWrapper", 
+                   wrapperName, contextName));
+
+        String[] mappings = (String[])
+            mBeanServer.invoke(objectName, "findMappings", null, null);
+        Object wrapper = 
+            mBeanServer.invoke(objectName, "findMappingObject", null, null);
+
+        for (int i = 0; i < mappings.length; i++) {
+            boolean jspWildCard = (wrapperName.equals("jsp")
+                                   && mappings[i].endsWith("/*"));
+            mapper.addWrapper(hostName, contextName, mappings[i], wrapper,
+                              jspWildCard);
+        }
+
+    }
+
+
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/OutputBuffer.java b/container/catalina/src/share/org/apache/catalina/connector/OutputBuffer.java
new file mode 100644
index 0000000..75fbdad
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/OutputBuffer.java
@@ -0,0 +1,651 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.catalina.connector;
+
+
+import java.io.IOException;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.buf.CharChunk;
+
+
+/**
+ * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3
+ * OutputBuffer, with the removal of some of the state handling (which in 
+ * Coyote is mostly the Processor's responsability).
+ *
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public class OutputBuffer extends Writer
+    implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String DEFAULT_ENCODING = 
+        org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+    public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+
+
+    // The buffer can be used for byte[] and char[] writing
+    // ( this is needed to support ServletOutputStream and for
+    // efficient implementations of templating systems )
+    public final int INITIAL_STATE = 0;
+    public final int CHAR_STATE = 1;
+    public final int BYTE_STATE = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The byte buffer.
+     */
+    private ByteChunk bb;
+
+
+    /**
+     * The chunk buffer.
+     */
+    private CharChunk cb;
+
+
+    /**
+     * State of the output buffer.
+     */
+    private int state = 0;
+
+
+    /**
+     * Number of bytes written.
+     */
+    private int bytesWritten = 0;
+
+
+    /**
+     * Number of chars written.
+     */
+    private int charsWritten = 0;
+
+
+    /**
+     * Flag which indicates if the output buffer is closed.
+     */
+    private boolean closed = false;
+
+
+    /**
+     * Do a flush on the next operation.
+     */
+    private boolean doFlush = false;
+
+
+    /**
+     * Byte chunk used to output bytes.
+     */
+    private ByteChunk outputChunk = new ByteChunk();
+
+
+    /**
+     * Encoding to use.
+     */
+    private String enc;
+
+
+    /**
+     * Encoder is set.
+     */
+    private boolean gotEnc = false;
+
+
+    /**
+     * List of encoders.
+     */
+    protected HashMap encoders = new HashMap();
+
+
+    /**
+     * Current char to byte converter.
+     */
+    protected C2BConverter conv;
+
+
+    /**
+     * Associated Coyote response.
+     */
+    private Response coyoteResponse;
+
+
+    /**
+     * Suspended flag. All output bytes will be swallowed if this is true.
+     */
+    private boolean suspended = false;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor. Allocate the buffer with the default buffer size.
+     */
+    public OutputBuffer() {
+
+        this(DEFAULT_BUFFER_SIZE);
+
+    }
+
+
+    /**
+     * Alternate constructor which allows specifying the initial buffer size.
+     * 
+     * @param size Buffer size to use
+     */
+    public OutputBuffer(int size) {
+
+        bb = new ByteChunk(size);
+        bb.setLimit(size);
+        bb.setByteOutputChannel(this);
+        cb = new CharChunk(size);
+        cb.setCharOutputChannel(this);
+        cb.setLimit(size);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Coyote response.
+     * 
+     * @param coyoteResponse Associated Coyote response
+     */
+    public void setResponse(Response coyoteResponse) {
+	this.coyoteResponse = coyoteResponse;
+    }
+
+
+    /**
+     * Get associated Coyote response.
+     * 
+     * @return the associated Coyote response
+     */
+    public Response getResponse() {
+        return this.coyoteResponse;
+    }
+
+
+    /**
+     * Is the response output suspended ?
+     * 
+     * @return suspended flag value
+     */
+    public boolean isSuspended() {
+        return this.suspended;
+    }
+
+
+    /**
+     * Set the suspended flag.
+     * 
+     * @param suspended New suspended flag value
+     */
+    public void setSuspended(boolean suspended) {
+        this.suspended = suspended;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the output buffer.
+     */
+    public void recycle() {
+        
+        state = INITIAL_STATE;
+        bytesWritten = 0;
+        charsWritten = 0;
+        
+        cb.recycle();
+        bb.recycle(); 
+        closed = false;
+        suspended = false;
+        
+        if (conv!= null) {
+            conv.recycle();
+        }
+        
+        gotEnc = false;
+        enc = null;
+        
+    }
+
+
+    /**
+     * Close the output buffer. This tries to calculate the response size if 
+     * the response has not been committed yet.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void close()
+        throws IOException {
+
+        if (closed)
+            return;
+        if (suspended)
+            return;
+
+        if ((!coyoteResponse.isCommitted()) 
+            && (coyoteResponse.getContentLengthLong() == -1)) {
+            // Flushing the char buffer
+            if (state == CHAR_STATE) {
+                cb.flushBuffer();
+                state = BYTE_STATE;
+            }
+            // If this didn't cause a commit of the response, the final content
+            // length can be calculated
+            if (!coyoteResponse.isCommitted()) {
+                coyoteResponse.setContentLength(bb.getLength());
+            }
+        }
+
+        doFlush(false);
+        closed = true;
+
+        coyoteResponse.finish();
+
+    }
+
+
+    /**
+     * Flush bytes or chars contained in the buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void flush()
+        throws IOException {
+        doFlush(true);
+    }
+
+
+    /**
+     * Flush bytes or chars contained in the buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    protected void doFlush(boolean realFlush)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        doFlush = true;
+        if (state == CHAR_STATE) {
+            cb.flushBuffer();
+            bb.flushBuffer();
+            state = BYTE_STATE;
+        } else if (state == BYTE_STATE) {
+            bb.flushBuffer();
+        } else if (state == INITIAL_STATE) {
+            // If the buffers are empty, commit the response header
+            coyoteResponse.sendHeaders();
+        }
+        doFlush = false;
+
+        if (realFlush) {
+            coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH, 
+                                  coyoteResponse);
+            // If some exception occurred earlier, or if some IOE occurred
+            // here, notify the servlet with an IOE
+            if (coyoteResponse.isExceptionPresent()) {
+                throw new ClientAbortException
+                    (coyoteResponse.getErrorException());
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------- Bytes Handling Methods
+
+
+    /** 
+     * Sends the buffer data to the client output, checking the
+     * state of Response and calling the right interceptors.
+     * 
+     * @param buf Byte buffer to be written to the response
+     * @param off Offset
+     * @param cnt Length
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void realWriteBytes(byte buf[], int off, int cnt)
+	throws IOException {
+
+        if (closed)
+            return;
+        if (coyoteResponse == null)
+            return;
+
+        // If we really have something to write
+        if (cnt > 0) {
+            // real write to the adapter
+            outputChunk.setBytes(buf, off, cnt);
+            try {
+                coyoteResponse.doWrite(outputChunk);
+            } catch (IOException e) {
+                // An IOException on a write is almost always due to
+                // the remote client aborting the request.  Wrap this
+                // so that it can be handled better by the error dispatcher.
+                throw new ClientAbortException(e);
+            }
+        }
+
+    }
+
+
+    public void write(byte b[], int off, int len) throws IOException {
+
+        if (suspended)
+            return;
+
+        if (state == CHAR_STATE)
+            cb.flushBuffer();
+        state = BYTE_STATE;
+        writeBytes(b, off, len);
+
+    }
+
+
+    private void writeBytes(byte b[], int off, int len) 
+        throws IOException {
+
+        if (closed)
+            return;
+
+        bb.append(b, off, len);
+        bytesWritten += len;
+
+        // if called from within flush(), then immediately flush
+        // remaining bytes
+        if (doFlush) {
+            bb.flushBuffer();
+        }
+
+    }
+
+
+    public void writeByte(int b)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        if (state == CHAR_STATE)
+            cb.flushBuffer();
+        state = BYTE_STATE;
+
+        bb.append( (byte)b );
+        bytesWritten++;
+
+    }
+
+
+    // ------------------------------------------------- Chars Handling Methods
+
+
+    public void write(int c)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        cb.append((char) c);
+        charsWritten++;
+
+    }
+
+
+    public void write(char c[])
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        write(c, 0, c.length);
+
+    }
+
+
+    public void write(char c[], int off, int len)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        cb.append(c, off, len);
+        charsWritten += len;
+
+    }
+
+
+    public void write(StringBuffer sb)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        int len = sb.length();
+        charsWritten += len;
+        cb.append(sb);
+
+    }
+
+
+    /**
+     * Append a string to the buffer
+     */
+    public void write(String s, int off, int len)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state=CHAR_STATE;
+
+        charsWritten += len;
+        if (s==null)
+            s="null";
+        cb.append( s, off, len );
+
+    }
+
+
+    public void write(String s)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+        if (s==null)
+            s="null";
+        write(s, 0, s.length());
+
+    } 
+
+
+    public void flushChars()
+        throws IOException {
+
+        cb.flushBuffer();
+        state = BYTE_STATE;
+
+    }
+
+
+    public boolean flushCharsNeeded() {
+        return state == CHAR_STATE;
+    }
+
+
+    public void setEncoding(String s) {
+        enc = s;
+    }
+
+
+    public void realWriteChars(char c[], int off, int len) 
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+        conv.convert(c, off, len);
+        conv.flushBuffer();	// ???
+
+    }
+
+
+    public void checkConverter() 
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+    }
+
+
+    protected void setConverter() 
+        throws IOException {
+
+        if (coyoteResponse != null)
+            enc = coyoteResponse.getCharacterEncoding();
+
+        gotEnc = true;
+        if (enc == null)
+            enc = DEFAULT_ENCODING;
+        conv = (C2BConverter) encoders.get(enc);
+        if (conv == null) {
+            
+            if (System.getSecurityManager() != null){
+                try{
+                    conv = (C2BConverter)AccessController.doPrivileged(
+                            new PrivilegedExceptionAction(){
+
+                                public Object run() throws IOException{
+                                    return new C2BConverter(bb, enc);
+                                }
+
+                            }
+                    );              
+                }catch(PrivilegedActionException ex){
+                    Exception e = ex.getException();
+                    if (e instanceof IOException)
+                        throw (IOException)e; 
+                }
+            } else {
+                conv = new C2BConverter(bb, enc);
+            }
+            
+            encoders.put(enc, conv);
+
+        }
+    }
+
+    
+    // --------------------  BufferedOutputStream compatibility
+
+
+    /**
+     * Real write - this buffer will be sent to the client
+     */
+    public void flushBytes()
+        throws IOException {
+
+        bb.flushBuffer();
+
+    }
+
+
+    public int getBytesWritten() {
+        return bytesWritten;
+    }
+
+
+    public int getCharsWritten() {
+        return charsWritten;
+    }
+
+
+    public int getContentWritten() {
+        return bytesWritten + charsWritten;
+    }
+
+
+    /** 
+     * True if this buffer hasn't been used ( since recycle() ) -
+     * i.e. no chars or bytes have been added to the buffer.  
+     */
+    public boolean isNew() {
+        return (bytesWritten == 0) && (charsWritten == 0);
+    }
+
+
+    public void setBufferSize(int size) {
+        if (size > bb.getLimit()) {// ??????
+	    bb.setLimit(size);
+	}
+    }
+
+
+    public void reset() {
+
+        //count=0;
+        bb.recycle();
+        bytesWritten = 0;
+        cb.recycle();
+        charsWritten = 0;
+        gotEnc = false;
+        enc = null;
+        state = INITIAL_STATE;
+    }
+
+
+    public int getBufferSize() {
+	return bb.getLimit();
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/Request.java b/container/catalina/src/share/org/apache/catalina/connector/Request.java
new file mode 100644
index 0000000..5a568de
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/Request.java
@@ -0,0 +1,2519 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.connector;
+
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.TreeMap;
+
+import javax.security.auth.Subject;
+import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.StringCache;
+import org.apache.tomcat.util.http.Cookies;
+import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.apache.tomcat.util.http.Parameters;
+import org.apache.tomcat.util.http.ServerCookie;
+import org.apache.tomcat.util.http.mapper.MappingData;
+
+import org.apache.coyote.ActionCode;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.ApplicationFilterFactory;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.ParameterMap;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.util.StringParser;
+
+
+/**
+ * Wrapper object for the Coyote request.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Request
+    implements HttpServletRequest {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    static {
+        // Ensure that classes are loaded for SM
+        new StringCache.ByteEntry();
+        new StringCache.CharEntry();
+    }
+
+    public Request() {
+
+        formats[0].setTimeZone(GMT_ZONE);
+        formats[1].setTimeZone(GMT_ZONE);
+        formats[2].setTimeZone(GMT_ZONE);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Coyote request.
+     */
+    protected org.apache.coyote.Request coyoteRequest;
+
+    /**
+     * Set the Coyote request.
+     * 
+     * @param coyoteRequest The Coyote request
+     */
+    public void setCoyoteRequest(org.apache.coyote.Request coyoteRequest) {
+        this.coyoteRequest = coyoteRequest;
+        inputBuffer.setRequest(coyoteRequest);
+    }
+
+    /**
+     * Get the Coyote request.
+     */
+    public org.apache.coyote.Request getCoyoteRequest() {
+        return (this.coyoteRequest);
+    }
+
+
+    // ----------------------------------------------------- Variables
+
+
+    protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The set of cookies associated with this Request.
+     */
+    protected Cookie[] cookies = null;
+
+
+    /**
+     * The set of SimpleDateFormat formats to use in getDateHeader().
+     *
+     * Notice that because SimpleDateFormat is not thread-safe, we can't
+     * declare formats[] as a static variable.
+     */
+    protected SimpleDateFormat formats[] = {
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+    };
+
+
+    /**
+     * The default Locale if none are specified.
+     */
+    protected static Locale defaultLocale = Locale.getDefault();
+
+
+    /**
+     * The attributes associated with this Request, keyed by attribute name.
+     */
+    protected HashMap attributes = new HashMap();
+
+
+    /**
+     * List of read only attributes for this Request.
+     */
+    private HashMap readOnlyAttributes = new HashMap();
+
+
+    /**
+     * The preferred Locales assocaited with this Request.
+     */
+    protected ArrayList locales = new ArrayList();
+
+
+    /**
+     * Internal notes associated with this request by Catalina components
+     * and event listeners.
+     */
+    private transient HashMap notes = new HashMap();
+
+
+    /**
+     * Authentication type.
+     */
+    protected String authType = null;
+
+
+    /**
+     * The current dispatcher type.
+     */
+    protected Object dispatcherType = null;
+
+
+    /**
+     * The associated input buffer.
+     */
+    protected InputBuffer inputBuffer = new InputBuffer();
+
+
+    /**
+     * ServletInputStream.
+     */
+    protected CoyoteInputStream inputStream = 
+        new CoyoteInputStream(inputBuffer);
+
+
+    /**
+     * Reader.
+     */
+    protected CoyoteReader reader = new CoyoteReader(inputBuffer);
+
+
+    /**
+     * Using stream flag.
+     */
+    protected boolean usingInputStream = false;
+
+
+    /**
+     * Using writer flag.
+     */
+    protected boolean usingReader = false;
+
+
+    /**
+     * User principal.
+     */
+    protected Principal userPrincipal = null;
+
+
+    /**
+     * Session parsed flag.
+     */
+    protected boolean sessionParsed = false;
+
+
+    /**
+     * Request parameters parsed flag.
+     */
+    protected boolean parametersParsed = false;
+
+
+    /**
+     * Cookies parsed flag.
+     */
+    protected boolean cookiesParsed = false;
+
+
+    /**
+     * Secure flag.
+     */
+    protected boolean secure = false;
+
+    
+    /**
+     * The Subject associated with the current AccessControllerContext
+     */
+    protected transient Subject subject = null;
+
+
+    /**
+     * Post data buffer.
+     */
+    protected static int CACHED_POST_LEN = 8192;
+    protected byte[] postData = null;
+
+
+    /**
+     * Hash map used in the getParametersMap method.
+     */
+    protected ParameterMap parameterMap = new ParameterMap();
+
+
+    /**
+     * The currently active session for this request.
+     */
+    protected Session session = null;
+
+
+    /**
+     * The current request dispatcher path.
+     */
+    protected Object requestDispatcherPath = null;
+
+
+    /**
+     * Was the requested session ID received in a cookie?
+     */
+    protected boolean requestedSessionCookie = false;
+
+
+    /**
+     * The requested session ID (if any) for this request.
+     */
+    protected String requestedSessionId = null;
+
+
+    /**
+     * Was the requested session ID received in a URL?
+     */
+    protected boolean requestedSessionURL = false;
+
+
+    /**
+     * Parse locales.
+     */
+    protected boolean localesParsed = false;
+
+
+    /**
+     * The string parser we will use for parsing request lines.
+     */
+    private StringParser parser = new StringParser();
+
+
+    /**
+     * Local port
+     */
+    protected int localPort = -1;
+
+    /**
+     * Remote address.
+     */
+    protected String remoteAddr = null;
+
+
+    /**
+     * Remote host.
+     */
+    protected String remoteHost = null;
+
+    
+    /**
+     * Remote port
+     */
+    protected int remotePort = -1;
+    
+    /**
+     * Local address
+     */
+    protected String localAddr = null;
+
+    
+    /**
+     * Local address
+     */
+    protected String localName = null;
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle() {
+
+        context = null;
+        wrapper = null;
+
+        dispatcherType = null;
+        requestDispatcherPath = null;
+
+        authType = null;
+        inputBuffer.recycle();
+        usingInputStream = false;
+        usingReader = false;
+        userPrincipal = null;
+        subject = null;
+        sessionParsed = false;
+        parametersParsed = false;
+        cookiesParsed = false;
+        locales.clear();
+        localesParsed = false;
+        secure = false;
+        remoteAddr = null;
+        remoteHost = null;
+        remotePort = -1;
+        localPort = -1;
+        localAddr = null;
+        localName = null;
+
+        attributes.clear();
+        notes.clear();
+        cookies = null;
+
+        if (session != null) {
+            session.endAccess();
+        }
+        session = null;
+        requestedSessionCookie = false;
+        requestedSessionId = null;
+        requestedSessionURL = false;
+
+        parameterMap.setLocked(false);
+        parameterMap.clear();
+
+        mappingData.recycle();
+
+        if (Constants.SECURITY) {
+            if (facade != null) {
+                facade.clear();
+                facade = null;
+            }
+            if (inputStream != null) {
+                inputStream.clear();
+                inputStream = null;
+            }
+            if (reader != null) {
+                reader.clear();
+                reader = null;
+            }
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Request Methods
+
+
+    /**
+     * Associated Catalina connector.
+     */
+    protected Connector connector;
+
+    /**
+     * Return the Connector through which this Request was received.
+     */
+    public Connector getConnector() {
+        return (this.connector);
+    }
+
+    /**
+     * Set the Connector through which this Request was received.
+     *
+     * @param connector The new connector
+     */
+    public void setConnector(Connector connector) {
+        this.connector = connector;
+    }
+
+
+    /**
+     * Associated context.
+     */
+    protected Context context = null;
+
+    /**
+     * Return the Context within which this Request is being processed.
+     */
+    public Context getContext() {
+        return (this.context);
+    }
+
+
+    /**
+     * Set the Context within which this Request is being processed.  This
+     * must be called as soon as the appropriate Context is identified, because
+     * it identifies the value to be returned by <code>getContextPath()</code>,
+     * and thus enables parsing of the request URI.
+     *
+     * @param context The newly associated Context
+     */
+    public void setContext(Context context) {
+        this.context = context;
+    }
+
+
+    /**
+     * Filter chain associated with the request.
+     */
+    protected FilterChain filterChain = null;
+
+    /**
+     * Get filter chain associated with the request.
+     */
+    public FilterChain getFilterChain() {
+        return (this.filterChain);
+    }
+
+    /**
+     * Set filter chain associated with the request.
+     * 
+     * @param filterChain new filter chain
+     */
+    public void setFilterChain(FilterChain filterChain) {
+        this.filterChain = filterChain;
+    }
+
+
+    /**
+     * Return the Host within which this Request is being processed.
+     */
+    public Host getHost() {
+        if (getContext() == null)
+            return null;
+        return (Host) getContext().getParent();
+        //return ((Host) mappingData.host);
+    }
+
+
+    /**
+     * Set the Host within which this Request is being processed.  This
+     * must be called as soon as the appropriate Host is identified, and
+     * before the Request is passed to a context.
+     *
+     * @param host The newly associated Host
+     */
+    public void setHost(Host host) {
+        mappingData.host = host;
+    }
+
+
+    /**
+     * Descriptive information about this Request implementation.
+     */
+    protected static final String info =
+        "org.apache.coyote.catalina.CoyoteRequest/1.0";
+
+    /**
+     * Return descriptive information about this Request implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+
+    /**
+     * Mapping data.
+     */
+    protected MappingData mappingData = new MappingData();
+
+    /**
+     * Return mapping data.
+     */
+    public MappingData getMappingData() {
+        return (mappingData);
+    }
+
+
+    /**
+     * The facade associated with this request.
+     */
+    protected RequestFacade facade = null;
+
+    /**
+     * Return the <code>ServletRequest</code> for which this object
+     * is the facade.  This method must be implemented by a subclass.
+     */
+    public HttpServletRequest getRequest() {
+        if (facade == null) {
+            facade = new RequestFacade(this);
+        } 
+        return (facade);
+    }
+
+
+    /**
+     * The response with which this request is associated.
+     */
+    protected org.apache.catalina.connector.Response response = null;
+
+    /**
+     * Return the Response with which this Request is associated.
+     */
+    public org.apache.catalina.connector.Response getResponse() {
+        return (this.response);
+    }
+
+    /**
+     * Set the Response with which this Request is associated.
+     *
+     * @param response The new associated response
+     */
+    public void setResponse(org.apache.catalina.connector.Response response) {
+        this.response = response;
+    }
+
+    /**
+     * Return the input stream associated with this Request.
+     */
+    public InputStream getStream() {
+        if (inputStream == null) {
+            inputStream = new CoyoteInputStream(inputBuffer);
+        }
+        return inputStream;
+    }
+
+    /**
+     * Set the input stream associated with this Request.
+     *
+     * @param stream The new input stream
+     */
+    public void setStream(InputStream stream) {
+        // Ignore
+    }
+
+
+    /**
+     * URI byte to char converter (not recycled).
+     */
+    protected B2CConverter URIConverter = null;
+
+    /**
+     * Return the URI converter.
+     */
+    protected B2CConverter getURIConverter() {
+        return URIConverter;
+    }
+
+    /**
+     * Set the URI converter.
+     * 
+     * @param URIConverter the new URI connverter
+     */
+    protected void setURIConverter(B2CConverter URIConverter) {
+        this.URIConverter = URIConverter;
+    }
+
+
+    /**
+     * Associated wrapper.
+     */
+    protected Wrapper wrapper = null;
+
+    /**
+     * Return the Wrapper within which this Request is being processed.
+     */
+    public Wrapper getWrapper() {
+        return (this.wrapper);
+    }
+
+
+    /**
+     * Set the Wrapper within which this Request is being processed.  This
+     * must be called as soon as the appropriate Wrapper is identified, and
+     * before the Request is ultimately passed to an application servlet.
+     * @param wrapper The newly associated Wrapper
+     */
+    public void setWrapper(Wrapper wrapper) {
+        this.wrapper = wrapper;
+    }
+
+
+    // ------------------------------------------------- Request Public Methods
+
+
+    /**
+     * Create and return a ServletInputStream to read the content
+     * associated with this Request.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletInputStream createInputStream() 
+        throws IOException {
+        if (inputStream == null) {
+            inputStream = new CoyoteInputStream(inputBuffer);
+        }
+        return inputStream;
+    }
+
+
+    /**
+     * Perform whatever actions are required to flush and close the input
+     * stream or reader, in a single operation.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void finishRequest() throws IOException {
+        // The reader and input stream don't need to be closed
+    }
+
+
+    /**
+     * Return the object bound with the specified name to the internal notes
+     * for this request, or <code>null</code> if no such binding exists.
+     *
+     * @param name Name of the note to be returned
+     */
+    public Object getNote(String name) {
+        return (notes.get(name));
+    }
+
+
+    /**
+     * Return an Iterator containing the String names of all notes bindings
+     * that exist for this request.
+     */
+    public Iterator getNoteNames() {
+        return (notes.keySet().iterator());
+    }
+
+
+    /**
+     * Remove any object bound to the specified name in the internal notes
+     * for this request.
+     *
+     * @param name Name of the note to be removed
+     */
+    public void removeNote(String name) {
+        notes.remove(name);
+    }
+
+
+    /**
+     * Bind an object to a specified name in the internal notes associated
+     * with this request, replacing any existing binding for this name.
+     *
+     * @param name Name to which the object should be bound
+     * @param value Object to be bound to the specified name
+     */
+    public void setNote(String name, Object value) {
+        notes.put(name, value);
+    }
+
+
+    /**
+     * Set the content length associated with this Request.
+     *
+     * @param length The new content length
+     */
+    public void setContentLength(int length) {
+        // Not used
+    }
+
+
+    /**
+     * Set the content type (and optionally the character encoding)
+     * associated with this Request.  For example,
+     * <code>text/html; charset=ISO-8859-4</code>.
+     *
+     * @param type The new content type
+     */
+    public void setContentType(String type) {
+        // Not used
+    }
+
+
+    /**
+     * Set the protocol name and version associated with this Request.
+     *
+     * @param protocol Protocol name and version
+     */
+    public void setProtocol(String protocol) {
+        // Not used
+    }
+
+
+    /**
+     * Set the IP address of the remote client associated with this Request.
+     *
+     * @param remoteAddr The remote IP address
+     */
+    public void setRemoteAddr(String remoteAddr) {
+        // Not used
+    }
+
+
+    /**
+     * Set the fully qualified name of the remote client associated with this
+     * Request.
+     *
+     * @param remoteHost The remote host name
+     */
+    public void setRemoteHost(String remoteHost) {
+        // Not used
+    }
+
+
+    /**
+     * Set the name of the scheme associated with this request.  Typical values
+     * are <code>http</code>, <code>https</code>, and <code>ftp</code>.
+     *
+     * @param scheme The scheme
+     */
+    public void setScheme(String scheme) {
+        // Not used
+    }
+
+
+    /**
+     * Set the value to be returned by <code>isSecure()</code>
+     * for this Request.
+     *
+     * @param secure The new isSecure value
+     */
+    public void setSecure(boolean secure) {
+        this.secure = secure;
+    }
+
+
+    /**
+     * Set the name of the server (virtual host) to process this request.
+     *
+     * @param name The server name
+     */
+    public void setServerName(String name) {
+        coyoteRequest.serverName().setString(name);
+    }
+
+
+    /**
+     * Set the port number of the server to process this request.
+     *
+     * @param port The server port
+     */
+    public void setServerPort(int port) {
+        coyoteRequest.setServerPort(port);
+    }
+
+
+    // ------------------------------------------------- ServletRequest Methods
+
+
+    /**
+     * Return the specified request attribute if it exists; otherwise, return
+     * <code>null</code>.
+     *
+     * @param name Name of the request attribute to return
+     */
+    public Object getAttribute(String name) {
+
+        if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
+            return (dispatcherType == null) 
+                ? ApplicationFilterFactory.REQUEST_INTEGER
+                : dispatcherType;
+        } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
+            return (requestDispatcherPath == null) 
+                ? getRequestPathMB().toString()
+                : requestDispatcherPath.toString();
+        }
+
+        Object attr=attributes.get(name);
+
+        if(attr!=null)
+            return(attr);
+
+        attr =  coyoteRequest.getAttribute(name);
+        if(attr != null)
+            return attr;
+        if( isSSLAttribute(name) ) {
+            coyoteRequest.action(ActionCode.ACTION_REQ_SSL_ATTRIBUTE, 
+                                 coyoteRequest);
+            attr = coyoteRequest.getAttribute(Globals.CERTIFICATES_ATTR);
+            if( attr != null) {
+                attributes.put(Globals.CERTIFICATES_ATTR, attr);
+            }
+            attr = coyoteRequest.getAttribute(Globals.CIPHER_SUITE_ATTR);
+            if(attr != null) {
+                attributes.put(Globals.CIPHER_SUITE_ATTR, attr);
+            }
+            attr = coyoteRequest.getAttribute(Globals.KEY_SIZE_ATTR);
+            if(attr != null) {
+                attributes.put(Globals.KEY_SIZE_ATTR, attr);
+            }
+            attr = coyoteRequest.getAttribute(Globals.SSL_SESSION_ID_ATTR);
+            if(attr != null) {
+                attributes.put(Globals.SSL_SESSION_ID_ATTR, attr);
+            }
+            attr = attributes.get(name);
+        }
+        return attr;
+    }
+
+
+    /**
+     * Test if a given name is one of the special Servlet-spec SSL attributes.
+     */
+    static boolean isSSLAttribute(String name) {
+        return Globals.CERTIFICATES_ATTR.equals(name) ||
+            Globals.CIPHER_SUITE_ATTR.equals(name) ||
+            Globals.KEY_SIZE_ATTR.equals(name)  ||
+            Globals.SSL_SESSION_ID_ATTR.equals(name);
+    }
+
+    /**
+     * Return the names of all request attributes for this Request, or an
+     * empty <code>Enumeration</code> if there are none.
+     */
+    public Enumeration getAttributeNames() {
+        if (isSecure()) {
+            getAttribute(Globals.CERTIFICATES_ATTR);
+        }
+        return new Enumerator(attributes.keySet(), true);
+    }
+
+
+    /**
+     * Return the character encoding for this Request.
+     */
+    public String getCharacterEncoding() {
+      return (coyoteRequest.getCharacterEncoding());
+    }
+
+
+    /**
+     * Return the content length for this Request.
+     */
+    public int getContentLength() {
+        return (coyoteRequest.getContentLength());
+    }
+
+
+    /**
+     * Return the content type for this Request.
+     */
+    public String getContentType() {
+        return (coyoteRequest.getContentType());
+    }
+
+
+    /**
+     * Return the servlet input stream for this Request.  The default
+     * implementation returns a servlet input stream created by
+     * <code>createInputStream()</code>.
+     *
+     * @exception IllegalStateException if <code>getReader()</code> has
+     *  already been called for this request
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletInputStream getInputStream() throws IOException {
+
+        if (usingReader)
+            throw new IllegalStateException
+                (sm.getString("coyoteRequest.getInputStream.ise"));
+
+        usingInputStream = true;
+        if (inputStream == null) {
+            inputStream = new CoyoteInputStream(inputBuffer);
+        }
+        return inputStream;
+
+    }
+
+
+    /**
+     * Return the preferred Locale that the client will accept content in,
+     * based on the value for the first <code>Accept-Language</code> header
+     * that was encountered.  If the request did not specify a preferred
+     * language, the server's default Locale is returned.
+     */
+    public Locale getLocale() {
+
+        if (!localesParsed)
+            parseLocales();
+
+        if (locales.size() > 0) {
+            return ((Locale) locales.get(0));
+        } else {
+            return (defaultLocale);
+        }
+
+    }
+
+
+    /**
+     * Return the set of preferred Locales that the client will accept
+     * content in, based on the values for any <code>Accept-Language</code>
+     * headers that were encountered.  If the request did not specify a
+     * preferred language, the server's default Locale is returned.
+     */
+    public Enumeration getLocales() {
+
+        if (!localesParsed)
+            parseLocales();
+
+        if (locales.size() > 0)
+            return (new Enumerator(locales));
+        ArrayList results = new ArrayList();
+        results.add(defaultLocale);
+        return (new Enumerator(results));
+
+    }
+
+
+    /**
+     * Return the value of the specified request parameter, if any; otherwise,
+     * return <code>null</code>.  If there is more than one value defined,
+     * return only the first one.
+     *
+     * @param name Name of the desired request parameter
+     */
+    public String getParameter(String name) {
+
+        if (!parametersParsed)
+            parseParameters();
+
+        return coyoteRequest.getParameters().getParameter(name);
+
+    }
+
+
+
+    /**
+     * Returns a <code>Map</code> of the parameters of this request.
+     * Request parameters are extra information sent with the request.
+     * For HTTP servlets, parameters are contained in the query string
+     * or posted form data.
+     *
+     * @return A <code>Map</code> containing parameter names as keys
+     *  and parameter values as map values.
+     */
+    public Map getParameterMap() {
+
+        if (parameterMap.isLocked())
+            return parameterMap;
+
+        Enumeration enumeration = getParameterNames();
+        while (enumeration.hasMoreElements()) {
+            String name = enumeration.nextElement().toString();
+            String[] values = getParameterValues(name);
+            parameterMap.put(name, values);
+        }
+
+        parameterMap.setLocked(true);
+
+        return parameterMap;
+
+    }
+
+
+    /**
+     * Return the names of all defined request parameters for this request.
+     */
+    public Enumeration getParameterNames() {
+
+        if (!parametersParsed)
+            parseParameters();
+
+        return coyoteRequest.getParameters().getParameterNames();
+
+    }
+
+
+    /**
+     * Return the defined values for the specified request parameter, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired request parameter
+     */
+    public String[] getParameterValues(String name) {
+
+        if (!parametersParsed)
+            parseParameters();
+
+        return coyoteRequest.getParameters().getParameterValues(name);
+
+    }
+
+
+    /**
+     * Return the protocol and version used to make this Request.
+     */
+    public String getProtocol() {
+        return coyoteRequest.protocol().toString();
+    }
+
+
+    /**
+     * Read the Reader wrapping the input stream for this Request.  The
+     * default implementation wraps a <code>BufferedReader</code> around the
+     * servlet input stream returned by <code>createInputStream()</code>.
+     *
+     * @exception IllegalStateException if <code>getInputStream()</code>
+     *  has already been called for this request
+     * @exception IOException if an input/output error occurs
+     */
+    public BufferedReader getReader() throws IOException {
+
+        if (usingInputStream)
+            throw new IllegalStateException
+                (sm.getString("coyoteRequest.getReader.ise"));
+
+        usingReader = true;
+        inputBuffer.checkConverter();
+        if (reader == null) {
+            reader = new CoyoteReader(inputBuffer);
+        }
+        return reader;
+
+    }
+
+
+    /**
+     * Return the real path of the specified virtual path.
+     *
+     * @param path Path to be translated
+     *
+     * @deprecated As of version 2.1 of the Java Servlet API, use
+     *  <code>ServletContext.getRealPath()</code>.
+     */
+    public String getRealPath(String path) {
+
+        if (context == null)
+            return (null);
+        ServletContext servletContext = context.getServletContext();
+        if (servletContext == null)
+            return (null);
+        else {
+            try {
+                return (servletContext.getRealPath(path));
+            } catch (IllegalArgumentException e) {
+                return (null);
+            }
+        }
+
+    }
+
+
+    /**
+     * Return the remote IP address making this Request.
+     */
+    public String getRemoteAddr() {
+        if (remoteAddr == null) {
+            coyoteRequest.action
+                (ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE, coyoteRequest);
+            remoteAddr = coyoteRequest.remoteAddr().toString();
+        }
+        return remoteAddr;
+    }
+
+
+    /**
+     * Return the remote host name making this Request.
+     */
+    public String getRemoteHost() {
+        if (remoteHost == null) {
+            if (!connector.getEnableLookups()) {
+                remoteHost = getRemoteAddr();
+            } else {
+                coyoteRequest.action
+                    (ActionCode.ACTION_REQ_HOST_ATTRIBUTE, coyoteRequest);
+                remoteHost = coyoteRequest.remoteHost().toString();
+            }
+        }
+        return remoteHost;
+    }
+
+    /**
+     * Returns the Internet Protocol (IP) source port of the client
+     * or last proxy that sent the request.
+     */    
+    public int getRemotePort(){
+        if (remotePort == -1) {
+            coyoteRequest.action
+                (ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE, coyoteRequest);
+            remotePort = coyoteRequest.getRemotePort();
+        }
+        return remotePort;    
+    }
+
+    /**
+     * Returns the host name of the Internet Protocol (IP) interface on
+     * which the request was received.
+     */
+    public String getLocalName(){
+        if (localName == null) {
+            coyoteRequest.action
+                (ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE, coyoteRequest);
+            localName = coyoteRequest.localName().toString();
+        }
+        return localName;
+    }
+
+    /**
+     * Returns the Internet Protocol (IP) address of the interface on
+     * which the request  was received.
+     */       
+    public String getLocalAddr(){
+        if (localAddr == null) {
+            coyoteRequest.action
+                (ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE, coyoteRequest);
+            localAddr = coyoteRequest.localAddr().toString();
+        }
+        return localAddr;    
+    }
+
+
+    /**
+     * Returns the Internet Protocol (IP) port number of the interface
+     * on which the request was received.
+     */
+    public int getLocalPort(){
+        if (localPort == -1){
+            coyoteRequest.action
+                (ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE, coyoteRequest);
+            localPort = coyoteRequest.getLocalPort();
+        }
+        return localPort;
+    }
+    
+    /**
+     * Return a RequestDispatcher that wraps the resource at the specified
+     * path, which may be interpreted as relative to the current request path.
+     *
+     * @param path Path of the resource to be wrapped
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        if (context == null)
+            return (null);
+
+        // If the path is already context-relative, just pass it through
+        if (path == null)
+            return (null);
+        else if (path.startsWith("/"))
+            return (context.getServletContext().getRequestDispatcher(path));
+
+        // Convert a request-relative path to a context-relative one
+        String servletPath = (String) getAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR);
+        if (servletPath == null)
+            servletPath = getServletPath();
+
+        // Add the path info, if there is any
+        String pathInfo = getPathInfo();
+        String requestPath = null;
+
+        if (pathInfo == null) {
+            requestPath = servletPath;
+        } else {
+            requestPath = servletPath + pathInfo;
+        }
+
+        int pos = requestPath.lastIndexOf('/');
+        String relative = null;
+        if (pos >= 0) {
+            relative = RequestUtil.normalize
+                (requestPath.substring(0, pos + 1) + path);
+        } else {
+            relative = RequestUtil.normalize(requestPath + path);
+        }
+
+        return (context.getServletContext().getRequestDispatcher(relative));
+
+    }
+
+
+    /**
+     * Return the scheme used to make this Request.
+     */
+    public String getScheme() {
+        return (coyoteRequest.scheme().toString());
+    }
+
+
+    /**
+     * Return the server name responding to this Request.
+     */
+    public String getServerName() {
+        return (coyoteRequest.serverName().toString());
+    }
+
+
+    /**
+     * Return the server port responding to this Request.
+     */
+    public int getServerPort() {
+        return (coyoteRequest.getServerPort());
+    }
+
+
+    /**
+     * Was this request received on a secure connection?
+     */
+    public boolean isSecure() {
+        return (secure);
+    }
+
+
+    /**
+     * Remove the specified request attribute if it exists.
+     *
+     * @param name Name of the request attribute to remove
+     */
+    public void removeAttribute(String name) {
+        Object value = null;
+        boolean found = false;
+
+        // Remove the specified attribute
+        // Check for read only attribute
+        // requests are per thread so synchronization unnecessary
+        if (readOnlyAttributes.containsKey(name)) {
+            return;
+        }
+        found = attributes.containsKey(name);
+        if (found) {
+            value = attributes.get(name);
+            attributes.remove(name);
+        } else {
+            return;
+        }
+
+        // Notify interested application event listeners
+        Object listeners[] = context.getApplicationEventListeners();
+        if ((listeners == null) || (listeners.length == 0))
+            return;
+        ServletRequestAttributeEvent event =
+          new ServletRequestAttributeEvent(context.getServletContext(),
+                                           getRequest(), name, value);
+        for (int i = 0; i < listeners.length; i++) {
+            if (!(listeners[i] instanceof ServletRequestAttributeListener))
+                continue;
+            ServletRequestAttributeListener listener =
+                (ServletRequestAttributeListener) listeners[i];
+            try {
+                listener.attributeRemoved(event);
+            } catch (Throwable t) {
+                context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t);
+                // Error valve will pick this execption up and display it to user
+                attributes.put( Globals.EXCEPTION_ATTR, t );
+            }
+        }
+    }
+
+
+    /**
+     * Set the specified request attribute to the specified value.
+     *
+     * @param name Name of the request attribute to set
+     * @param value The associated value
+     */
+    public void setAttribute(String name, Object value) {
+	
+        // Name cannot be null
+        if (name == null)
+            throw new IllegalArgumentException
+                (sm.getString("coyoteRequest.setAttribute.namenull"));
+
+        // Null value is the same as removeAttribute()
+        if (value == null) {
+            removeAttribute(name);
+            return;
+        }
+
+        if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
+            dispatcherType = value;
+            return;
+        } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
+            requestDispatcherPath = value;
+            return;
+        }
+
+        Object oldValue = null;
+        boolean replaced = false;
+
+        // Add or replace the specified attribute
+        // Check for read only attribute
+        // requests are per thread so synchronization unnecessary
+        if (readOnlyAttributes.containsKey(name)) {
+            return;
+        }
+
+        oldValue = attributes.put(name, value);
+        if (oldValue != null) {
+            replaced = true;
+        }
+
+        // Pass special attributes to the native layer
+        if (name.startsWith("org.apache.tomcat.")) {
+            coyoteRequest.setAttribute(name, value);
+        }
+        
+        // Notify interested application event listeners
+        Object listeners[] = context.getApplicationEventListeners();
+        if ((listeners == null) || (listeners.length == 0))
+            return;
+        ServletRequestAttributeEvent event = null;
+        if (replaced)
+            event =
+                new ServletRequestAttributeEvent(context.getServletContext(),
+                                                 getRequest(), name, oldValue);
+        else
+            event =
+                new ServletRequestAttributeEvent(context.getServletContext(),
+                                                 getRequest(), name, value);
+
+        for (int i = 0; i < listeners.length; i++) {
+            if (!(listeners[i] instanceof ServletRequestAttributeListener))
+                continue;
+            ServletRequestAttributeListener listener =
+                (ServletRequestAttributeListener) listeners[i];
+            try {
+                if (replaced) {
+                    listener.attributeReplaced(event);
+                } else {
+                    listener.attributeAdded(event);
+                }
+            } catch (Throwable t) {
+                context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t);
+                // Error valve will pick this execption up and display it to user
+                attributes.put( Globals.EXCEPTION_ATTR, t );
+            }
+        }
+    }
+
+
+    /**
+     * Overrides the name of the character encoding used in the body of
+     * this request.  This method must be called prior to reading request
+     * parameters or reading input using <code>getReader()</code>.
+     *
+     * @param enc The character encoding to be used
+     *
+     * @exception UnsupportedEncodingException if the specified encoding
+     *  is not supported
+     *
+     * @since Servlet 2.3
+     */
+    public void setCharacterEncoding(String enc)
+        throws UnsupportedEncodingException {
+
+        // Ensure that the specified encoding is valid
+        byte buffer[] = new byte[1];
+        buffer[0] = (byte) 'a';
+        String dummy = new String(buffer, enc);
+
+        // Save the validated encoding
+        coyoteRequest.setCharacterEncoding(enc);
+
+    }
+
+
+    // ---------------------------------------------------- HttpRequest Methods
+
+
+    /**
+     * Add a Cookie to the set of Cookies associated with this Request.
+     *
+     * @param cookie The new cookie
+     */
+    public void addCookie(Cookie cookie) {
+
+        if (!cookiesParsed)
+            parseCookies();
+
+        int size = 0;
+        if (cookies != null) {
+            size = cookies.length;
+        }
+
+        Cookie[] newCookies = new Cookie[size + 1];
+        for (int i = 0; i < size; i++) {
+            newCookies[i] = cookies[i];
+        }
+        newCookies[size] = cookie;
+
+        cookies = newCookies;
+
+    }
+
+
+    /**
+     * Add a Header to the set of Headers associated with this Request.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void addHeader(String name, String value) {
+        // Not used
+    }
+
+
+    /**
+     * Add a Locale to the set of preferred Locales for this Request.  The
+     * first added Locale will be the first one returned by getLocales().
+     *
+     * @param locale The new preferred Locale
+     */
+    public void addLocale(Locale locale) {
+        locales.add(locale);
+    }
+
+
+    /**
+     * Add a parameter name and corresponding set of values to this Request.
+     * (This is used when restoring the original request on a form based
+     * login).
+     *
+     * @param name Name of this request parameter
+     * @param values Corresponding values for this request parameter
+     */
+    public void addParameter(String name, String values[]) {
+        coyoteRequest.getParameters().addParameterValues(name, values);
+    }
+
+
+    /**
+     * Clear the collection of Cookies associated with this Request.
+     */
+    public void clearCookies() {
+        cookiesParsed = true;
+        cookies = null;
+    }
+
+
+    /**
+     * Clear the collection of Headers associated with this Request.
+     */
+    public void clearHeaders() {
+        // Not used
+    }
+
+
+    /**
+     * Clear the collection of Locales associated with this Request.
+     */
+    public void clearLocales() {
+        locales.clear();
+    }
+
+
+    /**
+     * Clear the collection of parameters associated with this Request.
+     */
+    public void clearParameters() {
+        // Not used
+    }
+
+
+    /**
+     * Set the authentication type used for this request, if any; otherwise
+     * set the type to <code>null</code>.  Typical values are "BASIC",
+     * "DIGEST", or "SSL".
+     *
+     * @param type The authentication type used
+     */
+    public void setAuthType(String type) {
+        this.authType = type;
+    }
+
+
+    /**
+     * Set the context path for this Request.  This will normally be called
+     * when the associated Context is mapping the Request to a particular
+     * Wrapper.
+     *
+     * @param path The context path
+     */
+    public void setContextPath(String path) {
+
+        if (path == null) {
+            mappingData.contextPath.setString("");
+        } else {
+            mappingData.contextPath.setString(path);
+        }
+
+    }
+
+
+    /**
+     * Set the HTTP request method used for this Request.
+     *
+     * @param method The request method
+     */
+    public void setMethod(String method) {
+        // Not used
+    }
+
+
+    /**
+     * Set the query string for this Request.  This will normally be called
+     * by the HTTP Connector, when it parses the request headers.
+     *
+     * @param query The query string
+     */
+    public void setQueryString(String query) {
+        // Not used
+    }
+
+
+    /**
+     * Set the path information for this Request.  This will normally be called
+     * when the associated Context is mapping the Request to a particular
+     * Wrapper.
+     *
+     * @param path The path information
+     */
+    public void setPathInfo(String path) {
+        mappingData.pathInfo.setString(path);
+    }
+
+
+    /**
+     * Set a flag indicating whether or not the requested session ID for this
+     * request came in through a cookie.  This is normally called by the
+     * HTTP Connector, when it parses the request headers.
+     *
+     * @param flag The new flag
+     */
+    public void setRequestedSessionCookie(boolean flag) {
+
+        this.requestedSessionCookie = flag;
+
+    }
+
+
+    /**
+     * Set the requested session ID for this request.  This is normally called
+     * by the HTTP Connector, when it parses the request headers.
+     *
+     * @param id The new session id
+     */
+    public void setRequestedSessionId(String id) {
+
+        this.requestedSessionId = id;
+
+    }
+
+
+    /**
+     * Set a flag indicating whether or not the requested session ID for this
+     * request came in through a URL.  This is normally called by the
+     * HTTP Connector, when it parses the request headers.
+     *
+     * @param flag The new flag
+     */
+    public void setRequestedSessionURL(boolean flag) {
+
+        this.requestedSessionURL = flag;
+
+    }
+
+
+    /**
+     * Set the unparsed request URI for this Request.  This will normally be
+     * called by the HTTP Connector, when it parses the request headers.
+     *
+     * @param uri The request URI
+     */
+    public void setRequestURI(String uri) {
+        // Not used
+    }
+
+
+    /**
+     * Set the decoded request URI.
+     * 
+     * @param uri The decoded request URI
+     */
+    public void setDecodedRequestURI(String uri) {
+        // Not used
+    }
+
+
+    /**
+     * Get the decoded request URI.
+     * 
+     * @return the URL decoded request URI
+     */
+    public String getDecodedRequestURI() {
+        return (coyoteRequest.decodedURI().toString());
+    }
+
+
+    /**
+     * Get the decoded request URI.
+     * 
+     * @return the URL decoded request URI
+     */
+    public MessageBytes getDecodedRequestURIMB() {
+        return (coyoteRequest.decodedURI());
+    }
+
+
+    /**
+     * Set the servlet path for this Request.  This will normally be called
+     * when the associated Context is mapping the Request to a particular
+     * Wrapper.
+     *
+     * @param path The servlet path
+     */
+    public void setServletPath(String path) {
+        if (path != null)
+            mappingData.wrapperPath.setString(path);
+    }
+
+
+    /**
+     * Set the Principal who has been authenticated for this Request.  This
+     * value is also used to calculate the value to be returned by the
+     * <code>getRemoteUser()</code> method.
+     *
+     * @param principal The user Principal
+     */
+    public void setUserPrincipal(Principal principal) {
+
+        if (System.getSecurityManager() != null){
+            HttpSession session = getSession(false);
+            if ( (subject != null) && 
+                 (!subject.getPrincipals().contains(principal)) ){
+                subject.getPrincipals().add(principal);         
+            } else if (session != null &&
+                        session.getAttribute(Globals.SUBJECT_ATTR) == null) {
+                subject = new Subject();
+                subject.getPrincipals().add(principal);         
+            }
+            if (session != null){
+                session.setAttribute(Globals.SUBJECT_ATTR, subject);
+            }
+        } 
+
+        this.userPrincipal = principal;
+    }
+
+
+    // --------------------------------------------- HttpServletRequest Methods
+
+
+    /**
+     * Return the authentication type used for this Request.
+     */
+    public String getAuthType() {
+        return (authType);
+    }
+
+
+    /**
+     * Return the portion of the request URI used to select the Context
+     * of the Request.
+     */
+    public String getContextPath() {
+        return (mappingData.contextPath.toString());
+    }
+
+
+    /**
+     * Get the context path.
+     * 
+     * @return the context path
+     */
+    public MessageBytes getContextPathMB() {
+        return (mappingData.contextPath);
+    }
+
+
+    /**
+     * Return the set of Cookies received with this Request.
+     */
+    public Cookie[] getCookies() {
+
+        if (!cookiesParsed)
+            parseCookies();
+
+        return cookies;
+
+    }
+
+
+    /**
+     * Set the set of cookies recieved with this Request.
+     */
+    public void setCookies(Cookie[] cookies) {
+
+        this.cookies = cookies;
+
+    }
+
+
+    /**
+     * Return the value of the specified date header, if any; otherwise
+     * return -1.
+     *
+     * @param name Name of the requested date header
+     *
+     * @exception IllegalArgumentException if the specified header value
+     *  cannot be converted to a date
+     */
+    public long getDateHeader(String name) {
+
+        String value = getHeader(name);
+        if (value == null)
+            return (-1L);
+
+        // Attempt to convert the date header in a variety of formats
+        long result = FastHttpDateFormat.parseDate(value, formats);
+        if (result != (-1L)) {
+            return result;
+        }
+        throw new IllegalArgumentException(value);
+
+    }
+
+
+    /**
+     * Return the first value of the specified header, if any; otherwise,
+     * return <code>null</code>
+     *
+     * @param name Name of the requested header
+     */
+    public String getHeader(String name) {
+        return coyoteRequest.getHeader(name);
+    }
+
+
+    /**
+     * Return all of the values of the specified header, if any; otherwise,
+     * return an empty enumeration.
+     *
+     * @param name Name of the requested header
+     */
+    public Enumeration getHeaders(String name) {
+        return coyoteRequest.getMimeHeaders().values(name);
+    }
+
+
+    /**
+     * Return the names of all headers received with this request.
+     */
+    public Enumeration getHeaderNames() {
+        return coyoteRequest.getMimeHeaders().names();
+    }
+
+
+    /**
+     * Return the value of the specified header as an integer, or -1 if there
+     * is no such header for this request.
+     *
+     * @param name Name of the requested header
+     *
+     * @exception IllegalArgumentException if the specified header value
+     *  cannot be converted to an integer
+     */
+    public int getIntHeader(String name) {
+
+        String value = getHeader(name);
+        if (value == null) {
+            return (-1);
+        } else {
+            return (Integer.parseInt(value));
+        }
+
+    }
+
+
+    /**
+     * Return the HTTP request method used in this Request.
+     */
+    public String getMethod() {
+        return coyoteRequest.method().toString();
+    }
+
+
+    /**
+     * Return the path information associated with this Request.
+     */
+    public String getPathInfo() {
+        return (mappingData.pathInfo.toString());
+    }
+
+
+    /**
+     * Get the path info.
+     * 
+     * @return the path info
+     */
+    public MessageBytes getPathInfoMB() {
+        return (mappingData.pathInfo);
+    }
+
+
+    /**
+     * Return the extra path information for this request, translated
+     * to a real path.
+     */
+    public String getPathTranslated() {
+
+        if (context == null)
+            return (null);
+
+        if (getPathInfo() == null) {
+            return (null);
+        } else {
+            return (context.getServletContext().getRealPath(getPathInfo()));
+        }
+
+    }
+
+
+    /**
+     * Return the query string associated with this request.
+     */
+    public String getQueryString() {
+        String queryString = coyoteRequest.queryString().toString();
+        if (queryString == null || queryString.equals("")) {
+            return (null);
+        } else {
+            return queryString;
+        }
+    }
+
+
+    /**
+     * Return the name of the remote user that has been authenticated
+     * for this Request.
+     */
+    public String getRemoteUser() {
+
+        if (userPrincipal != null) {
+            return (userPrincipal.getName());
+        } else {
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Get the request path.
+     * 
+     * @return the request path
+     */
+    public MessageBytes getRequestPathMB() {
+        return (mappingData.requestPath);
+    }
+
+
+    /**
+     * Return the session identifier included in this request, if any.
+     */
+    public String getRequestedSessionId() {
+        return (requestedSessionId);
+    }
+
+
+    /**
+     * Return the request URI for this request.
+     */
+    public String getRequestURI() {
+        return coyoteRequest.requestURI().toString();
+    }
+
+
+    /**
+     * Reconstructs the URL the client used to make the request.
+     * The returned URL contains a protocol, server name, port
+     * number, and server path, but it does not include query
+     * string parameters.
+     * <p>
+     * Because this method returns a <code>StringBuffer</code>,
+     * not a <code>String</code>, you can modify the URL easily,
+     * for example, to append query parameters.
+     * <p>
+     * This method is useful for creating redirect messages and
+     * for reporting errors.
+     *
+     * @return A <code>StringBuffer</code> object containing the
+     *  reconstructed URL
+     */
+    public StringBuffer getRequestURL() {
+
+        StringBuffer url = new StringBuffer();
+        String scheme = getScheme();
+        int port = getServerPort();
+        if (port < 0)
+            port = 80; // Work around java.net.URL bug
+
+        url.append(scheme);
+        url.append("://");
+        url.append(getServerName());
+        if ((scheme.equals("http") && (port != 80))
+            || (scheme.equals("https") && (port != 443))) {
+            url.append(':');
+            url.append(port);
+        }
+        url.append(getRequestURI());
+
+        return (url);
+
+    }
+
+
+    /**
+     * Return the portion of the request URI used to select the servlet
+     * that will process this request.
+     */
+    public String getServletPath() {
+        return (mappingData.wrapperPath.toString());
+    }
+
+
+    /**
+     * Get the servlet path.
+     * 
+     * @return the servlet path
+     */
+    public MessageBytes getServletPathMB() {
+        return (mappingData.wrapperPath);
+    }
+
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary.
+     */
+    public HttpSession getSession() {
+        Session session = doGetSession(true);
+        if (session != null) {
+            return session.getSession();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary and requested.
+     *
+     * @param create Create a new session if one does not exist
+     */
+    public HttpSession getSession(boolean create) {
+        Session session = doGetSession(create);
+        if (session != null) {
+            return session.getSession();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request came from a cookie.
+     */
+    public boolean isRequestedSessionIdFromCookie() {
+
+        if (requestedSessionId != null)
+            return (requestedSessionCookie);
+        else
+            return (false);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request came from the request URI.
+     */
+    public boolean isRequestedSessionIdFromURL() {
+
+        if (requestedSessionId != null)
+            return (requestedSessionURL);
+        else
+            return (false);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request came from the request URI.
+     *
+     * @deprecated As of Version 2.1 of the Java Servlet API, use
+     *  <code>isRequestedSessionIdFromURL()</code> instead.
+     */
+    public boolean isRequestedSessionIdFromUrl() {
+        return (isRequestedSessionIdFromURL());
+    }
+
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request identifies a valid session.
+     */
+    public boolean isRequestedSessionIdValid() {
+
+        if (requestedSessionId == null)
+            return (false);
+        if (context == null)
+            return (false);
+        Manager manager = context.getManager();
+        if (manager == null)
+            return (false);
+        Session session = null;
+        try {
+            session = manager.findSession(requestedSessionId);
+        } catch (IOException e) {
+            session = null;
+        }
+        if ((session != null) && session.isValid())
+            return (true);
+        else
+            return (false);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the authenticated user principal
+     * possesses the specified role name.
+     *
+     * @param role Role name to be validated
+     */
+    public boolean isUserInRole(String role) {
+
+        // Have we got an authenticated principal at all?
+        if (userPrincipal == null)
+            return (false);
+
+        // Identify the Realm we will use for checking role assignmenets
+        if (context == null)
+            return (false);
+        Realm realm = context.getRealm();
+        if (realm == null)
+            return (false);
+
+        // Check for a role alias defined in a <security-role-ref> element
+        if (wrapper != null) {
+            String realRole = wrapper.findSecurityReference(role);
+            if ((realRole != null) &&
+                realm.hasRole(userPrincipal, realRole))
+                return (true);
+        }
+
+        // Check for a role defined directly as a <security-role>
+        return (realm.hasRole(userPrincipal, role));
+
+    }
+
+
+    /**
+     * Return the principal that has been authenticated for this Request.
+     */
+    public Principal getUserPrincipal() {
+        if (userPrincipal instanceof GenericPrincipal) {
+            return ((GenericPrincipal) userPrincipal).getUserPrincipal();
+        } else {
+            return (userPrincipal);
+        }
+    }
+
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary.
+     */
+    public Session getSessionInternal() {
+        return doGetSession(true);
+    }
+
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary and requested.
+     *
+     * @param create Create a new session if one does not exist
+     */
+    public Session getSessionInternal(boolean create) {
+        return doGetSession(create);
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    protected Session doGetSession(boolean create) {
+
+        // There cannot be a session if no context has been assigned yet
+        if (context == null)
+            return (null);
+
+        // Return the current session if it exists and is valid
+        if ((session != null) && !session.isValid())
+            session = null;
+        if (session != null)
+            return (session);
+
+        // Return the requested session if it exists and is valid
+        Manager manager = null;
+        if (context != null)
+            manager = context.getManager();
+        if (manager == null)
+            return (null);      // Sessions are not supported
+        if (requestedSessionId != null) {
+            try {
+                session = manager.findSession(requestedSessionId);
+            } catch (IOException e) {
+                session = null;
+            }
+            if ((session != null) && !session.isValid())
+                session = null;
+            if (session != null) {
+                session.access();
+                return (session);
+            }
+        }
+
+        // Create a new session if requested and the response is not committed
+        if (!create)
+            return (null);
+        if ((context != null) && (response != null) &&
+            context.getCookies() &&
+            response.getResponse().isCommitted()) {
+            throw new IllegalStateException
+              (sm.getString("coyoteRequest.sessionCreateCommitted"));
+        }
+
+        // Attempt to reuse session id if one was submitted in a cookie
+        // Do not reuse the session id if it is from a URL, to prevent possible
+        // phishing attacks
+        if (connector.getEmptySessionPath() 
+                && isRequestedSessionIdFromCookie()) {
+            session = manager.createSession(getRequestedSessionId());
+        } else {
+            session = manager.createSession(null);
+        }
+
+        // Creating a new session cookie based on that session
+        if ((session != null) && (getContext() != null)
+               && getContext().getCookies()) {
+            Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,
+                                       session.getIdInternal());
+            configureSessionCookie(cookie);
+            response.addCookie(cookie);
+        }
+
+        if (session != null) {
+            session.access();
+            return (session);
+        } else {
+            return (null);
+        }
+
+    }
+
+    /**
+     * Configures the given JSESSIONID cookie.
+     *
+     * @param cookie The JSESSIONID cookie to be configured
+     */
+    protected void configureSessionCookie(Cookie cookie) {
+        cookie.setMaxAge(-1);
+        String contextPath = null;
+        if (!connector.getEmptySessionPath() && (getContext() != null)) {
+            contextPath = getContext().getEncodedPath();
+        }
+        if ((contextPath != null) && (contextPath.length() > 0)) {
+            cookie.setPath(contextPath);
+        } else {
+            cookie.setPath("/");
+        }
+        if (isSecure()) {
+            cookie.setSecure(true);
+        }
+    }
+
+    /**
+     * Parse cookies.
+     */
+    protected void parseCookies() {
+
+        cookiesParsed = true;
+
+        Cookies serverCookies = coyoteRequest.getCookies();
+        int count = serverCookies.getCookieCount();
+        if (count <= 0)
+            return;
+
+        cookies = new Cookie[count];
+
+        int idx=0;
+        for (int i = 0; i < count; i++) {
+            ServerCookie scookie = serverCookies.getCookie(i);
+            try {
+                Cookie cookie = new Cookie(scookie.getName().toString(),
+                                           scookie.getValue().toString());
+                cookie.setPath(scookie.getPath().toString());
+                cookie.setVersion(scookie.getVersion());
+                String domain = scookie.getDomain().toString();
+                if (domain != null) {
+                    cookie.setDomain(scookie.getDomain().toString());
+                }
+                cookies[idx++] = cookie;
+            } catch(IllegalArgumentException e) {
+                // Ignore bad cookie
+            }
+        }
+        if( idx < count ) {
+            Cookie [] ncookies = new Cookie[idx];
+            System.arraycopy(cookies, 0, ncookies, 0, idx);
+            cookies = ncookies;
+        }
+
+    }
+
+    /**
+     * Parse request parameters.
+     */
+    protected void parseParameters() {
+
+        parametersParsed = true;
+
+        Parameters parameters = coyoteRequest.getParameters();
+
+        // getCharacterEncoding() may have been overridden to search for
+        // hidden form field containing request encoding
+        String enc = getCharacterEncoding();
+
+        boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
+        if (enc != null) {
+            parameters.setEncoding(enc);
+            if (useBodyEncodingForURI) {
+                parameters.setQueryStringEncoding(enc);
+            }
+        } else {
+            parameters.setEncoding
+                (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
+            if (useBodyEncodingForURI) {
+                parameters.setQueryStringEncoding
+                    (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
+            }
+        }
+
+        parameters.handleQueryParameters();
+
+        if (usingInputStream || usingReader)
+            return;
+
+        if (!getMethod().equalsIgnoreCase("POST"))
+            return;
+
+        String contentType = getContentType();
+        if (contentType == null)
+            contentType = "";
+        int semicolon = contentType.indexOf(';');
+        if (semicolon >= 0) {
+            contentType = contentType.substring(0, semicolon).trim();
+        } else {
+            contentType = contentType.trim();
+        }
+        if (!("application/x-www-form-urlencoded".equals(contentType)))
+            return;
+
+        int len = getContentLength();
+
+        if (len > 0) {
+            int maxPostSize = connector.getMaxPostSize();
+            if ((maxPostSize > 0) && (len > maxPostSize)) {
+                context.getLogger().info
+                    (sm.getString("coyoteRequest.postTooLarge"));
+                throw new IllegalStateException("Post too large");
+            }
+            try {
+                byte[] formData = null;
+                if (len < CACHED_POST_LEN) {
+                    if (postData == null)
+                        postData = new byte[CACHED_POST_LEN];
+                    formData = postData;
+                } else {
+                    formData = new byte[len];
+                }
+                int actualLen = readPostBody(formData, len);
+                if (actualLen == len) {
+                    parameters.processParameters(formData, 0, len);
+                }
+            } catch (Throwable t) {
+                ; // Ignore
+            }
+        }
+
+    }
+
+
+    /**
+     * Read post body in an array.
+     */
+    protected int readPostBody(byte body[], int len)
+        throws IOException {
+
+        int offset = 0;
+        do {
+            int inputLen = getStream().read(body, offset, len - offset);
+            if (inputLen <= 0) {
+                return offset;
+            }
+            offset += inputLen;
+        } while ((len - offset) > 0);
+        return len;
+
+    }
+
+
+    /**
+     * Parse request locales.
+     */
+    protected void parseLocales() {
+
+        localesParsed = true;
+
+        Enumeration values = getHeaders("accept-language");
+
+        while (values.hasMoreElements()) {
+            String value = values.nextElement().toString();
+            parseLocalesHeader(value);
+        }
+
+    }
+
+
+    /**
+     * Parse accept-language header value.
+     */
+    protected void parseLocalesHeader(String value) {
+
+        // Store the accumulated languages that have been requested in
+        // a local collection, sorted by the quality value (so we can
+        // add Locales in descending order).  The values will be ArrayLists
+        // containing the corresponding Locales to be added
+        TreeMap locales = new TreeMap();
+
+        // Preprocess the value to remove all whitespace
+        int white = value.indexOf(' ');
+        if (white < 0)
+            white = value.indexOf('\t');
+        if (white >= 0) {
+            StringBuffer sb = new StringBuffer();
+            int len = value.length();
+            for (int i = 0; i < len; i++) {
+                char ch = value.charAt(i);
+                if ((ch != ' ') && (ch != '\t'))
+                    sb.append(ch);
+            }
+            value = sb.toString();
+        }
+
+        // Process each comma-delimited language specification
+        parser.setString(value);        // ASSERT: parser is available to us
+        int length = parser.getLength();
+        while (true) {
+
+            // Extract the next comma-delimited entry
+            int start = parser.getIndex();
+            if (start >= length)
+                break;
+            int end = parser.findChar(',');
+            String entry = parser.extract(start, end).trim();
+            parser.advance();   // For the following entry
+
+            // Extract the quality factor for this entry
+            double quality = 1.0;
+            int semi = entry.indexOf(";q=");
+            if (semi >= 0) {
+                try {
+                    quality = Double.parseDouble(entry.substring(semi + 3));
+                } catch (NumberFormatException e) {
+                    quality = 0.0;
+                }
+                entry = entry.substring(0, semi);
+            }
+
+            // Skip entries we are not going to keep track of
+            if (quality < 0.00005)
+                continue;       // Zero (or effectively zero) quality factors
+            if ("*".equals(entry))
+                continue;       // FIXME - "*" entries are not handled
+
+            // Extract the language and country for this entry
+            String language = null;
+            String country = null;
+            String variant = null;
+            int dash = entry.indexOf('-');
+            if (dash < 0) {
+                language = entry;
+                country = "";
+                variant = "";
+            } else {
+                language = entry.substring(0, dash);
+                country = entry.substring(dash + 1);
+                int vDash = country.indexOf('-');
+                if (vDash > 0) {
+                    String cTemp = country.substring(0, vDash);
+                    variant = country.substring(vDash + 1);
+                    country = cTemp;
+                } else {
+                    variant = "";
+                }
+            }
+
+            // Add a new Locale to the list of Locales for this quality level
+            Locale locale = new Locale(language, country, variant);
+            Double key = new Double(-quality);  // Reverse the order
+            ArrayList values = (ArrayList) locales.get(key);
+            if (values == null) {
+                values = new ArrayList();
+                locales.put(key, values);
+            }
+            values.add(locale);
+
+        }
+
+        // Process the quality values in highest->lowest order (due to
+        // negating the Double value when creating the key)
+        Iterator keys = locales.keySet().iterator();
+        while (keys.hasNext()) {
+            Double key = (Double) keys.next();
+            ArrayList list = (ArrayList) locales.get(key);
+            Iterator values = list.iterator();
+            while (values.hasNext()) {
+                Locale locale = (Locale) values.next();
+                addLocale(locale);
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/RequestFacade.java b/container/catalina/src/share/org/apache/catalina/connector/RequestFacade.java
new file mode 100644
index 0000000..d950b09
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/RequestFacade.java
@@ -0,0 +1,933 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.connector;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.util.StringManager;
+
+
+import org.apache.catalina.security.SecurityUtil;
+
+/**
+ * Facade class that wraps a Coyote request object.  
+ * All methods are delegated to the wrapped request.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @author Jean-Francois Arcand
+ * @version $Revision$ $Date$
+ */
+
+public class RequestFacade implements HttpServletRequest {
+        
+        
+    // ----------------------------------------------------------- DoPrivileged
+    
+    private final class GetAttributePrivilegedAction
+            implements PrivilegedAction {
+        
+        public Object run() {
+            return request.getAttributeNames();
+        }            
+    }
+     
+    
+    private final class GetParameterMapPrivilegedAction
+            implements PrivilegedAction {
+        
+        public Object run() {
+            return request.getParameterMap();
+        }        
+    }    
+    
+    
+    private final class GetRequestDispatcherPrivilegedAction
+            implements PrivilegedAction {
+
+        private String path;
+
+        public GetRequestDispatcherPrivilegedAction(String path){
+            this.path = path;
+        }
+        
+        public Object run() {   
+            return request.getRequestDispatcher(path);
+        }           
+    }    
+    
+    
+    private final class GetParameterPrivilegedAction
+            implements PrivilegedAction {
+
+        public String name;
+
+        public GetParameterPrivilegedAction(String name){
+            this.name = name;
+        }
+
+        public Object run() {       
+            return request.getParameter(name);
+        }           
+    }    
+    
+     
+    private final class GetParameterNamesPrivilegedAction
+            implements PrivilegedAction {
+        
+        public Object run() {          
+            return request.getParameterNames();
+        }           
+    } 
+    
+    
+    private final class GetParameterValuePrivilegedAction
+            implements PrivilegedAction {
+
+        public String name;
+
+        public GetParameterValuePrivilegedAction(String name){
+            this.name = name;
+        }
+
+        public Object run() {       
+            return request.getParameterValues(name);
+        }           
+    }    
+  
+    
+    private final class GetCookiesPrivilegedAction
+            implements PrivilegedAction {
+        
+        public Object run() {       
+            return request.getCookies();
+        }           
+    }      
+    
+    
+    private final class GetCharacterEncodingPrivilegedAction
+            implements PrivilegedAction {
+        
+        public Object run() {       
+            return request.getCharacterEncoding();
+        }           
+    }   
+        
+    
+    private final class GetHeadersPrivilegedAction
+            implements PrivilegedAction {
+
+        private String name;
+
+        public GetHeadersPrivilegedAction(String name){
+            this.name = name;
+        }
+        
+        public Object run() {       
+            return request.getHeaders(name);
+        }           
+    }    
+        
+    
+    private final class GetHeaderNamesPrivilegedAction
+            implements PrivilegedAction {
+
+        public Object run() {       
+            return request.getHeaderNames();
+        }           
+    }  
+            
+    
+    private final class GetLocalePrivilegedAction
+            implements PrivilegedAction {
+
+        public Object run() {       
+            return request.getLocale();
+        }           
+    }    
+            
+    
+    private final class GetLocalesPrivilegedAction
+            implements PrivilegedAction {
+
+        public Object run() {       
+            return request.getLocales();
+        }           
+    }    
+    
+    private final class GetSessionPrivilegedAction
+            implements PrivilegedAction {
+
+        private boolean create;
+        
+        public GetSessionPrivilegedAction(boolean create){
+            this.create = create;
+        }
+                
+        public Object run() {  
+            return request.getSession(create);
+        }           
+    }
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a wrapper for the specified request.
+     *
+     * @param request The request to be wrapped
+     */
+    public RequestFacade(Request request) {
+
+        this.request = request;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The wrapped request.
+     */
+    protected Request request = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Clear facade.
+     */
+    public void clear() {
+        request = null;
+    }
+
+    
+    /**
+     * Prevent cloning the facade.
+     */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+
+    // ------------------------------------------------- ServletRequest Methods
+
+
+    public Object getAttribute(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getAttribute(name);
+    }
+
+
+    public Enumeration getAttributeNames() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (Enumeration)AccessController.doPrivileged(
+                new GetAttributePrivilegedAction());        
+        } else {
+            return request.getAttributeNames();
+        }
+    }
+
+
+    public String getCharacterEncoding() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (String)AccessController.doPrivileged(
+                new GetCharacterEncodingPrivilegedAction());
+        } else {
+            return request.getCharacterEncoding();
+        }         
+    }
+
+
+    public void setCharacterEncoding(String env)
+            throws java.io.UnsupportedEncodingException {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        request.setCharacterEncoding(env);
+    }
+
+
+    public int getContentLength() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getContentLength();
+    }
+
+
+    public String getContentType() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getContentType();
+    }
+
+
+    public ServletInputStream getInputStream() throws IOException {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getInputStream();
+    }
+
+
+    public String getParameter(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (String)AccessController.doPrivileged(
+                new GetParameterPrivilegedAction(name));
+        } else {
+            return request.getParameter(name);
+        }
+    }
+
+
+    public Enumeration getParameterNames() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (Enumeration)AccessController.doPrivileged(
+                new GetParameterNamesPrivilegedAction());
+        } else {
+            return request.getParameterNames();
+        }
+    }
+
+
+    public String[] getParameterValues(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        String[] ret = null;
+
+        /*
+         * Clone the returned array only if there is a security manager
+         * in place, so that performance won't suffer in the nonsecure case
+         */
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            ret = (String[]) AccessController.doPrivileged(
+                new GetParameterValuePrivilegedAction(name));
+            if (ret != null) {
+                ret = (String[]) ret.clone();
+            }
+        } else {
+            ret = request.getParameterValues(name);
+        }
+
+        return ret;
+    }
+
+
+    public Map getParameterMap() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (Map)AccessController.doPrivileged(
+                new GetParameterMapPrivilegedAction());        
+        } else {
+            return request.getParameterMap();
+        }
+    }
+
+
+    public String getProtocol() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getProtocol();
+    }
+
+
+    public String getScheme() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getScheme();
+    }
+
+
+    public String getServerName() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getServerName();
+    }
+
+
+    public int getServerPort() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getServerPort();
+    }
+
+
+    public BufferedReader getReader() throws IOException {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getReader();
+    }
+
+
+    public String getRemoteAddr() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getRemoteAddr();
+    }
+
+
+    public String getRemoteHost() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getRemoteHost();
+    }
+
+
+    public void setAttribute(String name, Object o) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        request.setAttribute(name, o);
+    }
+
+
+    public void removeAttribute(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        request.removeAttribute(name);
+    }
+
+
+    public Locale getLocale() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (Locale)AccessController.doPrivileged(
+                new GetLocalePrivilegedAction());
+        } else {
+            return request.getLocale();
+        }        
+    }
+
+
+    public Enumeration getLocales() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (Enumeration)AccessController.doPrivileged(
+                new GetLocalesPrivilegedAction());
+        } else {
+            return request.getLocales();
+        }        
+    }
+
+
+    public boolean isSecure() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.isSecure();
+    }
+
+
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (RequestDispatcher)AccessController.doPrivileged(
+                new GetRequestDispatcherPrivilegedAction(path));
+        } else {
+            return request.getRequestDispatcher(path);
+        }
+    }
+
+
+    public String getRealPath(String path) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getRealPath(path);
+    }
+
+
+    public String getAuthType() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getAuthType();
+    }
+
+
+    public Cookie[] getCookies() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        Cookie[] ret = null;
+
+        /*
+         * Clone the returned array only if there is a security manager
+         * in place, so that performance won't suffer in the nonsecure case
+         */
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            ret = (Cookie[])AccessController.doPrivileged(
+                new GetCookiesPrivilegedAction());
+            if (ret != null) {
+                ret = (Cookie[]) ret.clone();
+            }
+        } else {
+            ret = request.getCookies();
+        }
+
+        return ret;
+    }
+
+
+    public long getDateHeader(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getDateHeader(name);
+    }
+
+
+    public String getHeader(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getHeader(name);
+    }
+
+
+    public Enumeration getHeaders(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (Enumeration)AccessController.doPrivileged(
+                new GetHeadersPrivilegedAction(name));
+        } else {
+            return request.getHeaders(name);
+        }         
+    }
+
+
+    public Enumeration getHeaderNames() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (System.getSecurityManager() != null){
+            return (Enumeration)AccessController.doPrivileged(
+                new GetHeaderNamesPrivilegedAction());
+        } else {
+            return request.getHeaderNames();
+        }             
+    }
+
+
+    public int getIntHeader(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getIntHeader(name);
+    }
+
+
+    public String getMethod() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getMethod();
+    }
+
+
+    public String getPathInfo() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getPathInfo();
+    }
+
+
+    public String getPathTranslated() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getPathTranslated();
+    }
+
+
+    public String getContextPath() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getContextPath();
+    }
+
+
+    public String getQueryString() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getQueryString();
+    }
+
+
+    public String getRemoteUser() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getRemoteUser();
+    }
+
+
+    public boolean isUserInRole(String role) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.isUserInRole(role);
+    }
+
+
+    public java.security.Principal getUserPrincipal() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getUserPrincipal();
+    }
+
+
+    public String getRequestedSessionId() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getRequestedSessionId();
+    }
+
+
+    public String getRequestURI() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getRequestURI();
+    }
+
+
+    public StringBuffer getRequestURL() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getRequestURL();
+    }
+
+
+    public String getServletPath() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getServletPath();
+    }
+
+
+    public HttpSession getSession(boolean create) {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return (HttpSession)AccessController.
+                doPrivileged(new GetSessionPrivilegedAction(create));
+        } else {
+            return request.getSession(create);
+        }
+    }
+
+    public HttpSession getSession() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return getSession(true);
+    }
+
+
+    public boolean isRequestedSessionIdValid() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.isRequestedSessionIdValid();
+    }
+
+
+    public boolean isRequestedSessionIdFromCookie() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.isRequestedSessionIdFromCookie();
+    }
+
+
+    public boolean isRequestedSessionIdFromURL() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.isRequestedSessionIdFromURL();
+    }
+
+
+    public boolean isRequestedSessionIdFromUrl() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.isRequestedSessionIdFromURL();
+    }
+
+
+    public String getLocalAddr() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getLocalAddr();
+    }
+
+
+    public String getLocalName() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getLocalName();
+    }
+
+
+    public int getLocalPort() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getLocalPort();
+    }
+
+
+    public int getRemotePort() {
+
+        if (request == null) {
+            throw new IllegalStateException(
+                            sm.getString("requestFacade.nullRequest"));
+        }
+
+        return request.getRemotePort();
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/Response.java b/container/catalina/src/share/org/apache/catalina/connector/Response.java
new file mode 100644
index 0000000..06f766d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/Response.java
@@ -0,0 +1,1558 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.connector;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.Vector;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Session;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.util.CharsetMapper;
+import org.apache.catalina.util.DateTool;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.UEncoder;
+import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.http.ServerCookie;
+import org.apache.tomcat.util.net.URL;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * Wrapper object for the Coyote response.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Response
+    implements HttpServletResponse {
+
+
+    // ----------------------------------------------------------- Constructors
+
+    static {
+        // Ensure that URL is loaded for SM
+        URL.isSchemeChar('c');
+    }
+
+    public Response() {
+        urlEncoder.addSafeCharacter('/');
+    }
+
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * JDK compatibility support
+     */
+    private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+
+    /**
+     * Descriptive information about this Response implementation.
+     */
+    protected static final String info =
+        "org.apache.coyote.tomcat5.CoyoteResponse/1.0";
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The date format we will use for creating date headers.
+     */
+    protected SimpleDateFormat format = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Catalina connector.
+     */
+    protected Connector connector;
+
+    /**
+     * Return the Connector through which this Request was received.
+     */
+    public Connector getConnector() {
+        return (this.connector);
+    }
+
+    /**
+     * Set the Connector through which this Request was received.
+     *
+     * @param connector The new connector
+     */
+    public void setConnector(Connector connector) {
+        this.connector = connector;
+        if("AJP/1.3".equals(connector.getProtocol())) {
+            // default size to size of one ajp-packet
+            outputBuffer = new OutputBuffer(8184);
+        } else {
+            outputBuffer = new OutputBuffer();
+        }
+        outputStream = new CoyoteOutputStream(outputBuffer);
+        writer = new CoyoteWriter(outputBuffer);
+    }
+
+
+    /**
+     * Coyote response.
+     */
+    protected org.apache.coyote.Response coyoteResponse;
+
+    /**
+     * Set the Coyote response.
+     * 
+     * @param coyoteResponse The Coyote response
+     */
+    public void setCoyoteResponse(org.apache.coyote.Response coyoteResponse) {
+        this.coyoteResponse = coyoteResponse;
+        outputBuffer.setResponse(coyoteResponse);
+    }
+
+    /**
+     * Get the Coyote response.
+     */
+    public org.apache.coyote.Response getCoyoteResponse() {
+        return (coyoteResponse);
+    }
+
+
+    /**
+     * Return the Context within which this Request is being processed.
+     */
+    public Context getContext() {
+        return (request.getContext());
+    }
+
+    /**
+     * Set the Context within which this Request is being processed.  This
+     * must be called as soon as the appropriate Context is identified, because
+     * it identifies the value to be returned by <code>getContextPath()</code>,
+     * and thus enables parsing of the request URI.
+     *
+     * @param context The newly associated Context
+     */
+    public void setContext(Context context) {
+        request.setContext(context);
+    }
+
+
+    /**
+     * The associated output buffer.
+     */
+    protected OutputBuffer outputBuffer;
+
+
+    /**
+     * The associated output stream.
+     */
+    protected CoyoteOutputStream outputStream;
+
+
+    /**
+     * The associated writer.
+     */
+    protected CoyoteWriter writer;
+
+
+    /**
+     * The application commit flag.
+     */
+    protected boolean appCommitted = false;
+
+
+    /**
+     * The included flag.
+     */
+    protected boolean included = false;
+
+    
+    /**
+     * The characterEncoding flag
+     */
+    private boolean isCharacterEncodingSet = false;
+    
+    /**
+     * The contextType flag
+     */    
+    private boolean isContentTypeSet = false;
+
+    
+    /**
+     * The error flag.
+     */
+    protected boolean error = false;
+
+
+    /**
+     * The set of Cookies associated with this Response.
+     */
+    protected ArrayList cookies = new ArrayList();
+
+
+    /**
+     * Using output stream flag.
+     */
+    protected boolean usingOutputStream = false;
+
+
+    /**
+     * Using writer flag.
+     */
+    protected boolean usingWriter = false;
+
+
+    /**
+     * URL encoder.
+     */
+    protected UEncoder urlEncoder = new UEncoder();
+
+
+    /**
+     * Recyclable buffer to hold the redirect URL.
+     */
+    protected CharChunk redirectURLCC = new CharChunk();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle() {
+
+        outputBuffer.recycle();
+        usingOutputStream = false;
+        usingWriter = false;
+        appCommitted = false;
+        included = false;
+        error = false;
+        isContentTypeSet = false;
+        isCharacterEncodingSet = false;
+        
+        cookies.clear();
+
+        if (Constants.SECURITY) {
+            if (facade != null) {
+                facade.clear();
+                facade = null;
+            }
+            if (outputStream != null) {
+                outputStream.clear();
+                outputStream = null;
+            }
+            if (writer != null) {
+                writer.clear();
+                writer = null;
+            }
+        } else {
+            writer.recycle();
+        }
+
+    }
+
+
+    // ------------------------------------------------------- Response Methods
+
+
+    /**
+     * Return the number of bytes actually written to the output stream.
+     */
+    public int getContentCount() {
+        return outputBuffer.getContentWritten();
+    }
+
+
+    /**
+     * Set the application commit flag.
+     * 
+     * @param appCommitted The new application committed flag value
+     */
+    public void setAppCommitted(boolean appCommitted) {
+        this.appCommitted = appCommitted;
+    }
+
+
+    /**
+     * Application commit flag accessor.
+     */
+    public boolean isAppCommitted() {
+        return (this.appCommitted || isCommitted() || isSuspended()
+                || ((getContentLength() > 0) 
+                    && (getContentCount() >= getContentLength())));
+    }
+
+
+    /**
+     * Return the "processing inside an include" flag.
+     */
+    public boolean getIncluded() {
+        return included;
+    }
+
+
+    /**
+     * Set the "processing inside an include" flag.
+     *
+     * @param included <code>true</code> if we are currently inside a
+     *  RequestDispatcher.include(), else <code>false</code>
+     */
+    public void setIncluded(boolean included) {
+        this.included = included;
+    }
+
+
+    /**
+     * Return descriptive information about this Response implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+
+    /**
+     * The request with which this response is associated.
+     */
+    protected Request request = null;
+
+    /**
+     * Return the Request with which this Response is associated.
+     */
+    public org.apache.catalina.connector.Request getRequest() {
+        return (this.request);
+    }
+
+    /**
+     * Set the Request with which this Response is associated.
+     *
+     * @param request The new associated request
+     */
+    public void setRequest(org.apache.catalina.connector.Request request) {
+        this.request = (Request) request;
+    }
+
+
+    /**
+     * The facade associated with this response.
+     */
+    protected ResponseFacade facade = null;
+
+    /**
+     * Return the <code>ServletResponse</code> for which this object
+     * is the facade.
+     */
+    public HttpServletResponse getResponse() {
+        if (facade == null) {
+            facade = new ResponseFacade(this);
+        }
+        return (facade);
+    }
+
+
+    /**
+     * Return the output stream associated with this Response.
+     */
+    public OutputStream getStream() {
+        if (outputStream == null) {
+            outputStream = new CoyoteOutputStream(outputBuffer);
+        }
+        return outputStream;
+    }
+
+
+    /**
+     * Set the output stream associated with this Response.
+     *
+     * @param stream The new output stream
+     */
+    public void setStream(OutputStream stream) {
+        // This method is evil
+    }
+
+
+    /**
+     * Set the suspended flag.
+     * 
+     * @param suspended The new suspended flag value
+     */
+    public void setSuspended(boolean suspended) {
+        outputBuffer.setSuspended(suspended);
+    }
+
+
+    /**
+     * Suspended flag accessor.
+     */
+    public boolean isSuspended() {
+        return outputBuffer.isSuspended();
+    }
+
+
+    /**
+     * Set the error flag.
+     */
+    public void setError() {
+        error = true;
+    }
+
+
+    /**
+     * Error flag accessor.
+     */
+    public boolean isError() {
+        return error;
+    }
+
+
+    /**
+     * Create and return a ServletOutputStream to write the content
+     * associated with this Response.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletOutputStream createOutputStream() 
+        throws IOException {
+        // Probably useless
+        if (outputStream == null) {
+            outputStream = new CoyoteOutputStream(outputBuffer);
+        }
+        return outputStream;
+    }
+
+
+    /**
+     * Perform whatever actions are required to flush and close the output
+     * stream or writer, in a single operation.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void finishResponse() 
+        throws IOException {
+        // Writing leftover bytes
+        try {
+            outputBuffer.close();
+        } catch(IOException e) {
+	    ;
+        } catch(Throwable t) {
+	    t.printStackTrace();
+        }
+    }
+
+
+    /**
+     * Return the content length that was set or calculated for this Response.
+     */
+    public int getContentLength() {
+        return (coyoteResponse.getContentLength());
+    }
+
+
+    /**
+     * Return the content type that was set or calculated for this response,
+     * or <code>null</code> if no content type was set.
+     */
+    public String getContentType() {
+        return (coyoteResponse.getContentType());
+    }
+
+
+    /**
+     * Return a PrintWriter that can be used to render error messages,
+     * regardless of whether a stream or writer has already been acquired.
+     *
+     * @return Writer which can be used for error reports. If the response is
+     * not an error report returned using sendError or triggered by an
+     * unexpected exception thrown during the servlet processing
+     * (and only in that case), null will be returned if the response stream
+     * has already been used.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public PrintWriter getReporter() throws IOException {
+        if (outputBuffer.isNew()) {
+            outputBuffer.checkConverter();
+            if (writer == null) {
+                writer = new CoyoteWriter(outputBuffer);
+            }
+            return writer;
+        } else {
+            return null;
+        }
+    }
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    /**
+     * Flush the buffer and commit this response.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void flushBuffer() 
+        throws IOException {
+        outputBuffer.flush();
+    }
+
+
+    /**
+     * Return the actual buffer size used for this Response.
+     */
+    public int getBufferSize() {
+        return outputBuffer.getBufferSize();
+    }
+
+
+    /**
+     * Return the character encoding used for this Response.
+     */
+    public String getCharacterEncoding() {
+        return (coyoteResponse.getCharacterEncoding());
+    }
+
+
+    /**
+     * Return the servlet output stream associated with this Response.
+     *
+     * @exception IllegalStateException if <code>getWriter</code> has
+     *  already been called for this response
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletOutputStream getOutputStream() 
+        throws IOException {
+
+        if (usingWriter)
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.getOutputStream.ise"));
+
+        usingOutputStream = true;
+        if (outputStream == null) {
+            outputStream = new CoyoteOutputStream(outputBuffer);
+        }
+        return outputStream;
+
+    }
+
+
+    /**
+     * Return the Locale assigned to this response.
+     */
+    public Locale getLocale() {
+        return (coyoteResponse.getLocale());
+    }
+
+
+    /**
+     * Return the writer associated with this Response.
+     *
+     * @exception IllegalStateException if <code>getOutputStream</code> has
+     *  already been called for this response
+     * @exception IOException if an input/output error occurs
+     */
+    public PrintWriter getWriter() 
+        throws IOException {
+
+        if (usingOutputStream)
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.getWriter.ise"));
+
+        /*
+         * If the response's character encoding has not been specified as
+         * described in <code>getCharacterEncoding</code> (i.e., the method
+         * just returns the default value <code>ISO-8859-1</code>),
+         * <code>getWriter</code> updates it to <code>ISO-8859-1</code>
+         * (with the effect that a subsequent call to getContentType() will
+         * include a charset=ISO-8859-1 component which will also be
+         * reflected in the Content-Type response header, thereby satisfying
+         * the Servlet spec requirement that containers must communicate the
+         * character encoding used for the servlet response's writer to the
+         * client).
+         */
+        setCharacterEncoding(getCharacterEncoding());
+
+        usingWriter = true;
+        outputBuffer.checkConverter();
+        if (writer == null) {
+            writer = new CoyoteWriter(outputBuffer);
+        }
+        return writer;
+
+    }
+
+
+    /**
+     * Has the output of this response already been committed?
+     */
+    public boolean isCommitted() {
+        return (coyoteResponse.isCommitted());
+    }
+
+
+    /**
+     * Clear any content written to the buffer.
+     *
+     * @exception IllegalStateException if this response has already
+     *  been committed
+     */
+    public void reset() {
+
+        if (included)
+            return;     // Ignore any call from an included servlet
+
+        coyoteResponse.reset();
+        outputBuffer.reset();
+    }
+
+
+    /**
+     * Reset the data buffer but not any status or header information.
+     *
+     * @exception IllegalStateException if the response has already
+     *  been committed
+     */
+    public void resetBuffer() {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.resetBuffer.ise"));
+
+        outputBuffer.reset();
+
+    }
+
+
+    /**
+     * Set the buffer size to be used for this Response.
+     *
+     * @param size The new buffer size
+     *
+     * @exception IllegalStateException if this method is called after
+     *  output has been committed for this response
+     */
+    public void setBufferSize(int size) {
+
+        if (isCommitted() || !outputBuffer.isNew())
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.setBufferSize.ise"));
+
+        outputBuffer.setBufferSize(size);
+
+    }
+
+
+    /**
+     * Set the content length (in bytes) for this Response.
+     *
+     * @param length The new content length
+     */
+    public void setContentLength(int length) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+        
+        if (usingWriter)
+            return;
+        
+        coyoteResponse.setContentLength(length);
+
+    }
+
+
+    /**
+     * Set the content type for this Response.
+     *
+     * @param type The new content type
+     */
+    public void setContentType(String type) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        // Ignore charset if getWriter() has already been called
+        if (usingWriter) {
+            if (type != null) {
+                int index = type.indexOf(";");
+                if (index != -1) {
+                    type = type.substring(0, index);
+                }
+            }
+        }
+
+        coyoteResponse.setContentType(type);
+
+        // Check to see if content type contains charset
+        if (type != null) {
+            int index = type.indexOf(";");
+            if (index != -1) {
+                int len = type.length();
+                index++;
+                while (index < len && Character.isSpace(type.charAt(index))) {
+                    index++;
+                }
+                if (index+7 < len
+                        && type.charAt(index) == 'c'
+                        && type.charAt(index+1) == 'h'
+                        && type.charAt(index+2) == 'a'
+                        && type.charAt(index+3) == 'r'
+                        && type.charAt(index+4) == 's'
+                        && type.charAt(index+5) == 'e'
+                        && type.charAt(index+6) == 't'
+                        && type.charAt(index+7) == '=') {
+                    isCharacterEncodingSet = true;
+                }
+            }
+        }
+
+        isContentTypeSet = true;    
+    }
+
+
+    /*
+     * Overrides the name of the character encoding used in the body
+     * of the request. This method must be called prior to reading
+     * request parameters or reading input using getReader().
+     *
+     * @param charset String containing the name of the chararacter encoding.
+     */
+    public void setCharacterEncoding(String charset) {
+
+        if (isCommitted())
+            return;
+        
+        // Ignore any call from an included servlet
+        if (included)
+            return;     
+        
+        // Ignore any call made after the getWriter has been invoked
+        // The default should be used
+        if (usingWriter)
+            return;
+
+        coyoteResponse.setCharacterEncoding(charset);
+        isCharacterEncodingSet = true;
+    }
+
+    
+    
+    /**
+     * Set the Locale that is appropriate for this response, including
+     * setting the appropriate character encoding.
+     *
+     * @param locale The new locale
+     */
+    public void setLocale(Locale locale) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.setLocale(locale);
+
+        // Ignore any call made after the getWriter has been invoked.
+        // The default should be used
+        if (usingWriter)
+            return;
+
+        if (isCharacterEncodingSet) {
+            return;
+        }
+
+        CharsetMapper cm = getContext().getCharsetMapper();
+        String charset = cm.getCharset( locale );
+        if ( charset != null ){
+            coyoteResponse.setCharacterEncoding(charset);
+        }
+
+    }
+
+
+    // --------------------------------------------------- HttpResponse Methods
+
+
+    /**
+     * Return an array of all cookies set for this response, or
+     * a zero-length array if no cookies have been set.
+     */
+    public Cookie[] getCookies() {
+        return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
+    }
+
+
+    /**
+     * Return the value for the specified header, or <code>null</code> if this
+     * header has not been set.  If more than one value was added for this
+     * name, only the first is returned; use getHeaderValues() to retrieve all
+     * of them.
+     *
+     * @param name Header name to look up
+     */
+    public String getHeader(String name) {
+        return coyoteResponse.getMimeHeaders().getHeader(name);
+    }
+
+
+    /**
+     * Return an array of all the header names set for this response, or
+     * a zero-length array if no headers have been set.
+     */
+    public String[] getHeaderNames() {
+
+        MimeHeaders headers = coyoteResponse.getMimeHeaders();
+        int n = headers.size();
+        String[] result = new String[n];
+        for (int i = 0; i < n; i++) {
+            result[i] = headers.getName(i).toString();
+        }
+        return result;
+
+    }
+
+
+    /**
+     * Return an array of all the header values associated with the
+     * specified header name, or an zero-length array if there are no such
+     * header values.
+     *
+     * @param name Header name to look up
+     */
+    public String[] getHeaderValues(String name) {
+
+        Enumeration enumeration = coyoteResponse.getMimeHeaders().values(name);
+        Vector result = new Vector();
+        while (enumeration.hasMoreElements()) {
+            result.addElement(enumeration.nextElement());
+        }
+        String[] resultArray = new String[result.size()];
+        result.copyInto(resultArray);
+        return resultArray;
+
+    }
+
+
+    /**
+     * Return the error message that was set with <code>sendError()</code>
+     * for this Response.
+     */
+    public String getMessage() {
+        return coyoteResponse.getMessage();
+    }
+
+
+    /**
+     * Return the HTTP status code associated with this Response.
+     */
+    public int getStatus() {
+        return coyoteResponse.getStatus();
+    }
+
+
+    /**
+     * Reset this response, and specify the values for the HTTP status code
+     * and corresponding message.
+     *
+     * @exception IllegalStateException if this response has already been
+     *  committed
+     */
+    public void reset(int status, String message) {
+        reset();
+        setStatus(status, message);
+    }
+
+
+    // -------------------------------------------- HttpServletResponse Methods
+
+
+    /**
+     * Add the specified Cookie to those that will be included with
+     * this Response.
+     *
+     * @param cookie Cookie to be added
+     */
+    public void addCookie(final Cookie cookie) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        cookies.add(cookie);
+
+        final StringBuffer sb = new StringBuffer();
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run(){
+                    ServerCookie.appendCookieValue
+                        (sb, cookie.getVersion(), cookie.getName(), 
+                         cookie.getValue(), cookie.getPath(), 
+                         cookie.getDomain(), cookie.getComment(), 
+                         cookie.getMaxAge(), cookie.getSecure());
+                    return null;
+                }
+            });
+        } else {
+            ServerCookie.appendCookieValue
+                (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(),
+                     cookie.getPath(), cookie.getDomain(), cookie.getComment(), 
+                     cookie.getMaxAge(), cookie.getSecure());
+        }
+
+        // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
+        // RFC2965 is not supported by browsers and the Servlet spec
+        // asks for 2109.
+        addHeader("Set-Cookie", sb.toString());
+
+    }
+
+
+    /**
+     * Add the specified date header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Date value to be set
+     */
+    public void addDateHeader(String name, long value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included) {
+            return;
+        }
+
+        if (format == null) {
+            format = new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER,
+                                          Locale.US);
+            format.setTimeZone(TimeZone.getTimeZone("GMT"));
+        }
+
+        addHeader(name, FastHttpDateFormat.formatDate(value, format));
+
+    }
+
+
+    /**
+     * Add the specified header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Value to be set
+     */
+    public void addHeader(String name, String value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.addHeader(name, value);
+
+    }
+
+
+    /**
+     * Add the specified integer header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Integer value to be set
+     */
+    public void addIntHeader(String name, int value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        addHeader(name, "" + value);
+
+    }
+
+
+    /**
+     * Has the specified header been set already in this response?
+     *
+     * @param name Name of the header to check
+     */
+    public boolean containsHeader(String name) {
+        return coyoteResponse.containsHeader(name);
+    }
+
+
+    /**
+     * Encode the session identifier associated with this response
+     * into the specified redirect URL, if necessary.
+     *
+     * @param url URL to be encoded
+     */
+    public String encodeRedirectURL(String url) {
+
+        if (isEncodeable(toAbsolute(url))) {
+            return (toEncoded(url, request.getSessionInternal().getIdInternal()));
+        } else {
+            return (url);
+        }
+
+    }
+
+
+    /**
+     * Encode the session identifier associated with this response
+     * into the specified redirect URL, if necessary.
+     *
+     * @param url URL to be encoded
+     *
+     * @deprecated As of Version 2.1 of the Java Servlet API, use
+     *  <code>encodeRedirectURL()</code> instead.
+     */
+    public String encodeRedirectUrl(String url) {
+        return (encodeRedirectURL(url));
+    }
+
+
+    /**
+     * Encode the session identifier associated with this response
+     * into the specified URL, if necessary.
+     *
+     * @param url URL to be encoded
+     */
+    public String encodeURL(String url) {
+        
+        String absolute = toAbsolute(url);
+        if (isEncodeable(absolute)) {
+            // W3c spec clearly said 
+            if (url.equalsIgnoreCase("")){
+                url = absolute;
+            }
+            return (toEncoded(url, request.getSessionInternal().getIdInternal()));
+        } else {
+            return (url);
+        }
+
+    }
+
+
+    /**
+     * Encode the session identifier associated with this response
+     * into the specified URL, if necessary.
+     *
+     * @param url URL to be encoded
+     *
+     * @deprecated As of Version 2.1 of the Java Servlet API, use
+     *  <code>encodeURL()</code> instead.
+     */
+    public String encodeUrl(String url) {
+        return (encodeURL(url));
+    }
+
+
+    /**
+     * Send an acknowledgment of a request.
+     * 
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendAcknowledgement()
+        throws IOException {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return; 
+
+        coyoteResponse.acknowledge();
+
+    }
+
+
+    /**
+     * Send an error response with the specified status and a
+     * default message.
+     *
+     * @param status HTTP status code to send
+     *
+     * @exception IllegalStateException if this response has
+     *  already been committed
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendError(int status) 
+        throws IOException {
+        sendError(status, null);
+    }
+
+
+    /**
+     * Send an error response with the specified status and message.
+     *
+     * @param status HTTP status code to send
+     * @param message Corresponding message to send
+     *
+     * @exception IllegalStateException if this response has
+     *  already been committed
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendError(int status, String message) 
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.sendError.ise"));
+
+        // Ignore any call from an included servlet
+        if (included)
+            return; 
+
+        Wrapper wrapper = getRequest().getWrapper();
+        if (wrapper != null) {
+            wrapper.incrementErrorCount();
+        } 
+
+        setError();
+
+        coyoteResponse.setStatus(status);
+        coyoteResponse.setMessage(message);
+
+        // Clear any data content that has been buffered
+        resetBuffer();
+
+        // Cause the response to be finished (from the application perspective)
+        setSuspended(true);
+
+    }
+
+
+    /**
+     * Send a temporary redirect to the specified redirect location URL.
+     *
+     * @param location Location URL to redirect to
+     *
+     * @exception IllegalStateException if this response has
+     *  already been committed
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendRedirect(String location) 
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.sendRedirect.ise"));
+
+        // Ignore any call from an included servlet
+        if (included)
+            return; 
+
+        // Clear any data content that has been buffered
+        resetBuffer();
+
+        // Generate a temporary redirect to the specified location
+        try {
+            String absolute = toAbsolute(location);
+            setStatus(SC_FOUND);
+            setHeader("Location", absolute);
+        } catch (IllegalArgumentException e) {
+            setStatus(SC_NOT_FOUND);
+        }
+
+        // Cause the response to be finished (from the application perspective)
+        setSuspended(true);
+
+    }
+
+
+    /**
+     * Set the specified date header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Date value to be set
+     */
+    public void setDateHeader(String name, long value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included) {
+            return;
+        }
+
+        if (format == null) {
+            format = new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER,
+                                          Locale.US);
+            format.setTimeZone(TimeZone.getTimeZone("GMT"));
+        }
+
+        setHeader(name, FastHttpDateFormat.formatDate(value, format));
+
+    }
+
+
+    /**
+     * Set the specified header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Value to be set
+     */
+    public void setHeader(String name, String value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.setHeader(name, value);
+
+    }
+
+
+    /**
+     * Set the specified integer header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Integer value to be set
+     */
+    public void setIntHeader(String name, int value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        setHeader(name, "" + value);
+
+    }
+
+
+    /**
+     * Set the HTTP status to be returned with this response.
+     *
+     * @param status The new HTTP status
+     */
+    public void setStatus(int status) {
+        setStatus(status, null);
+    }
+
+
+    /**
+     * Set the HTTP status and message to be returned with this response.
+     *
+     * @param status The new HTTP status
+     * @param message The associated text message
+     *
+     * @deprecated As of Version 2.1 of the Java Servlet API, this method
+     *  has been deprecated due to the ambiguous meaning of the message
+     *  parameter.
+     */
+    public void setStatus(int status, String message) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.setStatus(status);
+        coyoteResponse.setMessage(message);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return <code>true</code> if the specified URL should be encoded with
+     * a session identifier.  This will be true if all of the following
+     * conditions are met:
+     * <ul>
+     * <li>The request we are responding to asked for a valid session
+     * <li>The requested session ID was not received via a cookie
+     * <li>The specified URL points back to somewhere within the web
+     *     application that is responding to this request
+     * </ul>
+     *
+     * @param location Absolute URL to be validated
+     */
+    protected boolean isEncodeable(final String location) {
+
+        if (location == null)
+            return (false);
+
+        // Is this an intra-document reference?
+        if (location.startsWith("#"))
+            return (false);
+
+        // Are we in a valid session that is not using cookies?
+        final Request hreq = request;
+        final Session session = hreq.getSessionInternal(false);
+        if (session == null)
+            return (false);
+        if (hreq.isRequestedSessionIdFromCookie())
+            return (false);
+        
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return ((Boolean)
+                AccessController.doPrivileged(new PrivilegedAction() {
+
+                public Object run(){
+                    return new Boolean(doIsEncodeable(hreq, session, location));
+                }
+            })).booleanValue();
+        } else {
+            return doIsEncodeable(hreq, session, location);
+        }
+    }
+
+    private boolean doIsEncodeable(Request hreq, Session session, 
+                                   String location) {
+        // Is this a valid absolute URL?
+        URL url = null;
+        try {
+            url = new URL(location);
+        } catch (MalformedURLException e) {
+            return (false);
+        }
+
+        // Does this URL match down to (and including) the context path?
+        if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol()))
+            return (false);
+        if (!hreq.getServerName().equalsIgnoreCase(url.getHost()))
+            return (false);
+        int serverPort = hreq.getServerPort();
+        if (serverPort == -1) {
+            if ("https".equals(hreq.getScheme()))
+                serverPort = 443;
+            else
+                serverPort = 80;
+        }
+        int urlPort = url.getPort();
+        if (urlPort == -1) {
+            if ("https".equals(url.getProtocol()))
+                urlPort = 443;
+            else
+                urlPort = 80;
+        }
+        if (serverPort != urlPort)
+            return (false);
+
+        String contextPath = getContext().getPath();
+        if (contextPath != null) {
+            String file = url.getFile();
+            if ((file == null) || !file.startsWith(contextPath))
+                return (false);
+            if( file.indexOf(";jsessionid=" + session.getIdInternal()) >= 0 )
+                return (false);
+        }
+
+        // This URL belongs to our web application, so it is encodeable
+        return (true);
+
+    }
+
+
+    /**
+     * Convert (if necessary) and return the absolute URL that represents the
+     * resource referenced by this possibly relative URL.  If this URL is
+     * already absolute, return it unchanged.
+     *
+     * @param location URL to be (possibly) converted and then returned
+     *
+     * @exception IllegalArgumentException if a MalformedURLException is
+     *  thrown when converting the relative URL to an absolute one
+     */
+    private String toAbsolute(String location) {
+
+        if (location == null)
+            return (location);
+
+        boolean leadingSlash = location.startsWith("/");
+
+        if (leadingSlash || !hasScheme(location)) {
+
+            redirectURLCC.recycle();
+
+            String scheme = request.getScheme();
+            String name = request.getServerName();
+            int port = request.getServerPort();
+
+            try {
+                redirectURLCC.append(scheme, 0, scheme.length());
+                redirectURLCC.append("://", 0, 3);
+                redirectURLCC.append(name, 0, name.length());
+                if ((scheme.equals("http") && port != 80)
+                    || (scheme.equals("https") && port != 443)) {
+                    redirectURLCC.append(':');
+                    String portS = port + "";
+                    redirectURLCC.append(portS, 0, portS.length());
+                }
+                if (!leadingSlash) {
+                    String relativePath = request.getDecodedRequestURI();
+                    int pos = relativePath.lastIndexOf('/');
+                    relativePath = relativePath.substring(0, pos);
+                    
+                    String encodedURI = null;
+                    final String frelativePath = relativePath;
+                    if (SecurityUtil.isPackageProtectionEnabled() ){
+                        try{
+                            encodedURI = (String)AccessController.doPrivileged( 
+                                new PrivilegedExceptionAction(){                                
+                                    public Object run() throws IOException{
+                                        return urlEncoder.encodeURL(frelativePath);
+                                    }
+                           });   
+                        } catch (PrivilegedActionException pae){
+                            IllegalArgumentException iae =
+                                new IllegalArgumentException(location);
+                            jdkCompat.chainException(iae, pae.getException());
+                            throw iae;
+                        }
+                    } else {
+                        encodedURI = urlEncoder.encodeURL(relativePath);
+                    }
+                    redirectURLCC.append(encodedURI, 0, encodedURI.length());
+                    redirectURLCC.append('/');
+                }
+                redirectURLCC.append(location, 0, location.length());
+            } catch (IOException e) {
+                IllegalArgumentException iae =
+                    new IllegalArgumentException(location);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+
+            return redirectURLCC.toString();
+
+        } else {
+
+            return (location);
+
+        }
+
+    }
+
+
+    /**
+     * Determine if a URI string has a <code>scheme</code> component.
+     */
+    private boolean hasScheme(String uri) {
+        int len = uri.length();
+        for(int i=0; i < len ; i++) {
+            char c = uri.charAt(i);
+            if(c == ':') {
+                return i > 0;
+            } else if(!URL.isSchemeChar(c)) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return the specified URL with the specified session identifier
+     * suitably encoded.
+     *
+     * @param url URL to be encoded with the session id
+     * @param sessionId Session id to be included in the encoded URL
+     */
+    protected String toEncoded(String url, String sessionId) {
+
+        if ((url == null) || (sessionId == null))
+            return (url);
+
+        String path = url;
+        String query = "";
+        String anchor = "";
+        int question = url.indexOf('?');
+        if (question >= 0) {
+            path = url.substring(0, question);
+            query = url.substring(question);
+        }
+        int pound = path.indexOf('#');
+        if (pound >= 0) {
+            anchor = path.substring(pound);
+            path = path.substring(0, pound);
+        }
+        StringBuffer sb = new StringBuffer(path);
+        if( sb.length() > 0 ) { // jsessionid can't be first.
+            sb.append(";jsessionid=");
+            sb.append(sessionId);
+        }
+        sb.append(anchor);
+        sb.append(query);
+        return (sb.toString());
+
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/connector/ResponseFacade.java b/container/catalina/src/share/org/apache/catalina/connector/ResponseFacade.java
new file mode 100644
index 0000000..d512c6c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/ResponseFacade.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.security.SecurityUtil;
+
+/**
+ * Facade class that wraps a Coyote response object. 
+ * All methods are delegated to the wrapped response.
+ *
+ * @author Remy Maucherat
+ * @author Jean-Francois Arcand
+ * @version $Revision$ $Date$
+ */
+public class ResponseFacade 
+    implements HttpServletResponse {
+
+
+    // ----------------------------------------------------------- DoPrivileged
+    
+    private final class SetContentTypePrivilegedAction
+            implements PrivilegedAction {
+
+        private String contentType;
+
+        public SetContentTypePrivilegedAction(String contentType){
+            this.contentType = contentType;
+        }
+        
+        public Object run() {
+            response.setContentType(contentType);
+            return null;
+        }            
+    }
+
+    private final class DateHeaderPrivilegedAction
+            implements PrivilegedAction {
+
+        private String name;
+        private long value;
+        private boolean add;
+
+        DateHeaderPrivilegedAction(String name, long value, boolean add) {
+            this.name = name;
+            this.value = value;
+            this.add = add;
+        }
+
+        public Object run() {
+            if(add) {
+                response.addDateHeader(name, value);
+            } else {
+                response.setDateHeader(name, value);
+            }
+            return null;
+        }
+    }
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a wrapper for the specified response.
+     *
+     * @param response The response to be wrapped
+     */
+    public ResponseFacade(Response response) {
+
+         this.response = response;
+    }
+
+
+    // ----------------------------------------------- Class/Instance Variables
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The wrapped response.
+     */
+    protected Response response = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Clear facade.
+     */
+    public void clear() {
+        response = null;
+    }
+
+
+    /**
+     * Prevent cloning the facade.
+     */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+
+    public void finish() {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        response.setSuspended(true);
+    }
+
+
+    public boolean isFinished() {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.isSuspended();
+    }
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    public String getCharacterEncoding() {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.getCharacterEncoding();
+    }
+
+
+    public ServletOutputStream getOutputStream()
+        throws IOException {
+
+        //        if (isFinished())
+        //            throw new IllegalStateException
+        //                (/*sm.getString("responseFacade.finished")*/);
+
+        ServletOutputStream sos = response.getOutputStream();
+        if (isFinished())
+            response.setSuspended(true);
+        return (sos);
+
+    }
+
+
+    public PrintWriter getWriter()
+        throws IOException {
+
+        //        if (isFinished())
+        //            throw new IllegalStateException
+        //                (/*sm.getString("responseFacade.finished")*/);
+
+        PrintWriter writer = response.getWriter();
+        if (isFinished())
+            response.setSuspended(true);
+        return (writer);
+
+    }
+
+
+    public void setContentLength(int len) {
+
+        if (isCommitted())
+            return;
+
+        response.setContentLength(len);
+
+    }
+
+
+    public void setContentType(String type) {
+
+        if (isCommitted())
+            return;
+        
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            AccessController.doPrivileged(new SetContentTypePrivilegedAction(type));
+        } else {
+            response.setContentType(type);            
+        }
+    }
+
+
+    public void setBufferSize(int size) {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setBufferSize(size);
+
+    }
+
+
+    public int getBufferSize() {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.getBufferSize();
+    }
+
+
+    public void flushBuffer()
+        throws IOException {
+
+        if (isFinished())
+            //            throw new IllegalStateException
+            //                (/*sm.getString("responseFacade.finished")*/);
+            return;
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                AccessController.doPrivileged(new PrivilegedExceptionAction(){
+
+                    public Object run() throws IOException{
+                        response.setAppCommitted(true);
+
+                        response.flushBuffer();
+                        return null;
+                    }
+                });
+            } catch(PrivilegedActionException e){
+                Exception ex = e.getException();
+                if (ex instanceof IOException){
+                    throw (IOException)ex;
+                }
+            }
+        } else {
+            response.setAppCommitted(true);
+
+            response.flushBuffer();            
+        }
+
+    }
+
+
+    public void resetBuffer() {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.resetBuffer();
+
+    }
+
+
+    public boolean isCommitted() {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return (response.isAppCommitted());
+    }
+
+
+    public void reset() {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.reset();
+
+    }
+
+
+    public void setLocale(Locale loc) {
+
+        if (isCommitted())
+            return;
+
+        response.setLocale(loc);
+    }
+
+
+    public Locale getLocale() {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.getLocale();
+    }
+
+
+    public void addCookie(Cookie cookie) {
+
+        if (isCommitted())
+            return;
+
+        response.addCookie(cookie);
+
+    }
+
+
+    public boolean containsHeader(String name) {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.containsHeader(name);
+    }
+
+
+    public String encodeURL(String url) {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.encodeURL(url);
+    }
+
+
+    public String encodeRedirectURL(String url) {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.encodeRedirectURL(url);
+    }
+
+
+    public String encodeUrl(String url) {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.encodeURL(url);
+    }
+
+
+    public String encodeRedirectUrl(String url) {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.encodeRedirectURL(url);
+    }
+
+
+    public void sendError(int sc, String msg)
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setAppCommitted(true);
+
+        response.sendError(sc, msg);
+
+    }
+
+
+    public void sendError(int sc)
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setAppCommitted(true);
+
+        response.sendError(sc);
+
+    }
+
+
+    public void sendRedirect(String location)
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setAppCommitted(true);
+
+        response.sendRedirect(location);
+
+    }
+
+
+    public void setDateHeader(String name, long date) {
+
+        if (isCommitted())
+            return;
+
+        if(System.getSecurityManager() != null) {
+            AccessController.doPrivileged(new DateHeaderPrivilegedAction
+                                             (name, date, false));
+        } else {
+            response.setDateHeader(name, date);
+        }
+
+    }
+
+
+    public void addDateHeader(String name, long date) {
+
+        if (isCommitted())
+            return;
+
+        if(System.getSecurityManager() != null) {
+            AccessController.doPrivileged(new DateHeaderPrivilegedAction
+                                             (name, date, true));
+        } else {
+            response.addDateHeader(name, date);
+        }
+
+    }
+
+
+    public void setHeader(String name, String value) {
+
+        if (isCommitted())
+            return;
+
+        response.setHeader(name, value);
+
+    }
+
+
+    public void addHeader(String name, String value) {
+
+        if (isCommitted())
+            return;
+
+        response.addHeader(name, value);
+
+    }
+
+
+    public void setIntHeader(String name, int value) {
+
+        if (isCommitted())
+            return;
+
+        response.setIntHeader(name, value);
+
+    }
+
+
+    public void addIntHeader(String name, int value) {
+
+        if (isCommitted())
+            return;
+
+        response.addIntHeader(name, value);
+
+    }
+
+
+    public void setStatus(int sc) {
+
+        if (isCommitted())
+            return;
+
+        response.setStatus(sc);
+
+    }
+
+
+    public void setStatus(int sc, String sm) {
+
+        if (isCommitted())
+            return;
+
+        response.setStatus(sc, sm);
+    }
+
+
+    public String getContentType() {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        return response.getContentType();
+    }
+
+
+    public void setCharacterEncoding(String arg0) {
+
+        if (response == null) {
+            throw new IllegalStateException(
+                            sm.getString("responseFacade.nullResponse"));
+        }
+
+        response.setCharacterEncoding(arg0);
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/connector/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/connector/mbeans-descriptors.xml
new file mode 100644
index 0000000..5651d53
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/connector/mbeans-descriptors.xml
@@ -0,0 +1,198 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean         name="CoyoteConnector"
+            className="org.apache.catalina.mbeans.ConnectorMBean"
+          description="Implementation of a Coyote connector"
+               domain="Catalina"
+                group="Connector"
+                 type="org.apache.catalina.connector.Connector">
+
+    <attribute   name="acceptCount"
+          description="The accept count for this Connector"
+                 type="int"/>
+
+    <attribute   name="address"
+          description="The IP address on which to bind"
+                 type="java.lang.String"/>
+
+    <attribute   name="algorithm"
+          description="The certificate encoding algorithm to be used"
+                 type="java.lang.String"/>
+
+    <attribute   name="allowTrace"
+          description="Allow disabling TRACE method"
+                 type="boolean"/>
+
+    <attribute   name="bufferSize"
+          description="The input buffer size we should create on input streams"
+                 type="int"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="clientAuth"
+          description="Should we require client authentication?"
+                 type="java.lang.String"/>
+
+    <attribute   name="ciphers"
+          description="Comma-separated list of SSL cipher suites to be enabled"
+                 type="java.lang.String"/>
+
+    <attribute   name="compression"
+          description="Compression value"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionLinger"
+          description="Linger value on the incoming connection"
+                 type="int"/>
+
+    <attribute   name="connectionTimeout"
+          description="Timeout value on the incoming connection"
+                 type="int"/>
+
+    <attribute   name="connectionUploadTimeout"
+          description="Timeout value on the incoming connection during request processing"
+                 type="int"/>
+
+    <attribute    name="disableUploadTimeout"
+           description="Should Tomcat ignore setting a timeout for uploads?" 
+                  type="boolean"/>
+
+    <attribute   name="emptySessionPath"
+          description="The 'empty session path' flag for this Connector"
+                 type="boolean"/>
+
+    <attribute   name="enableLookups"
+          description="The 'enable DNS lookups' flag for this Connector"
+                 type="boolean"/>
+
+    <attribute   name="keystoreFile"
+          description="Pathname to the key store file to be used"
+                 type="java.lang.String"/>
+
+    <attribute   name="keystorePass"
+          description="Password for accessing the key store file"
+                 type="java.lang.String"/>
+
+    <attribute   name="keystoreType"
+          description="Type of keystore file to be used for the server certificate"
+                 type="java.lang.String"/>
+
+    <attribute   name="keyAlias"
+          description="Alias name of this connector's keypair and supporting certificate chain"
+                 type="java.lang.String"/>
+
+    <attribute   name="maxHttpHeaderSize"
+          description="Maximum size in bytes of the HTTP header"
+                 type="int"/>
+
+    <attribute   name="maxKeepAliveRequests"
+          description="Maximum number of Keep-Alive requests to honor per connection"
+                 type="int"/>
+
+    <attribute   name="maxPostSize"
+          description="Maximum size in bytes of a POST which will be handled by the servlet API provided features"
+                 type="int"/>
+
+    <attribute   name="maxSpareThreads"
+          description="The maximum number of unused request processing threads"
+                 type="int"/>
+
+    <attribute   name="maxThreads"
+          description="The maximum number of request processing threads to be created"
+                 type="int"/>
+
+    <attribute   name="minSpareThreads"
+          description="The number of request processing threads that will be created"
+                 type="int"/>
+
+    <attribute   name="port"
+          description="The port number on which we listen for ajp13 requests"
+                type="int"/>
+
+    <attribute   name="protocol"
+          description="Coyote protocol handler in use"
+                 type="java.lang.String"/>
+
+    <attribute   name="protocolHandlerClassName"
+          description="Coyote Protocol handler class name"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="proxyName"
+          description="Ther Server name to which we should pretend requests to this Connector"
+                 type="java.lang.String"/>
+
+    <attribute   name="proxyPort"
+          description="Ther Server port to which we should pretend requests to this Connector"
+                 type="int"/>
+
+    <attribute   name="redirectPort"
+          description="The redirect port for non-SSL to SSL redirects"
+                 type="int"/>
+
+    <attribute   name="scheme"
+          description="Protocol name for this Connector (http, https)"
+                 type="java.lang.String"/>
+
+    <attribute   name="secret"
+          description="Authentication secret (I guess ... not in Javadocs)"
+            readable = "false" 
+                 type="java.lang.String"/>
+
+    <attribute   name="secure"
+          description="Is this a secure (SSL) Connector?"
+                 type="boolean"/>
+
+    <attribute   name="sslProtocol"
+          description="SSL protocol variant to be used"
+                 type="java.lang.String"/>
+
+    <attribute   name="sslProtocols"
+          description="Comma-separated list of SSL protocol variants to be enabled"
+                 type="java.lang.String"/>
+
+    <attribute   name="strategy"
+          description="Thread pool strategy"
+                 type="java.lang.String"/>
+
+    <attribute   name="tcpNoDelay"
+          description="Should we use TCP no delay?"
+                 type="boolean"/>
+
+    <attribute    name="tomcatAuthentication"
+           description="Should Tomcat perform all authentications?"
+                  type="boolean"/>
+
+    <attribute    name="threadPriority"
+           description="The thread priority for processors"
+                  type="int"/>
+
+    <attribute   name="URIEncoding"
+          description="Character encoding used to decode the URI"
+                 type="java.lang.String"/>
+
+    <attribute   name="useBodyEncodingForURI"
+          description="Should the body encoding be used for URI query parameters"
+                 type="boolean"/>
+
+    <attribute    name="xpoweredBy"
+           description="Is generation of X-Powered-By response header enabled/disabled?"
+                  type="boolean"/>
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="pause" description="Start" impact="ACTION" returnType="void" />
+    <operation name="resume" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+
+  </mbean>
+
+
+</mbeans-descriptors>
+
+
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationContext.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationContext.java
new file mode 100644
index 0000000..7f581b7
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationContext.java
@@ -0,0 +1,999 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.Binding;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.deploy.ApplicationParameter;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.ResourceSet;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.apache.naming.resources.Resource;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.mapper.MappingData;
+
+
+/**
+ * Standard implementation of <code>ServletContext</code> that represents
+ * a web application's execution environment.  An instance of this class is
+ * associated with each instance of <code>StandardContext</code>.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ApplicationContext
+    implements ServletContext {
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class, associated with the specified
+     * Context instance.
+     *
+     * @param context The associated Context instance
+     */
+    public ApplicationContext(String basePath, StandardContext context) {
+        super();
+        this.context = context;
+        this.basePath = basePath;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The context attributes for this context.
+     */
+    private HashMap attributes = new HashMap();
+
+
+    /**
+     * List of read only attributes for this context.
+     */
+    private HashMap readOnlyAttributes = new HashMap();
+
+
+    /**
+     * The Context instance with which we are associated.
+     */
+    private StandardContext context = null;
+
+
+    /**
+     * Empty collection to serve as the basis for empty enumerations.
+     * <strong>DO NOT ADD ANY ELEMENTS TO THIS COLLECTION!</strong>
+     */
+    private static final ArrayList empty = new ArrayList();
+
+
+    /**
+     * The facade around this object.
+     */
+    private ServletContext facade = new ApplicationContextFacade(this);
+
+
+    /**
+     * The merged context initialization parameters for this Context.
+     */
+    private HashMap parameters = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+      StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Base path.
+     */
+    private String basePath = null;
+
+
+    /**
+     * Thread local mapping data.
+     */
+    private ThreadLocal localMappingData = new ThreadLocal();
+
+
+    /**
+     * Thread local URI message bytes.
+     */
+    private ThreadLocal localUriMB = new ThreadLocal();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the resources object that is mapped to a specified path.
+     * The path must begin with a "/" and is interpreted as relative to the
+     * current context root.
+     */
+    public DirContext getResources() {
+
+        return context.getResources();
+
+    }
+
+
+    // ------------------------------------------------- ServletContext Methods
+
+
+    /**
+     * Return the value of the specified context attribute, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the context attribute to return
+     */
+    public Object getAttribute(String name) {
+
+        synchronized (attributes) {
+            return (attributes.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return an enumeration of the names of the context attributes
+     * associated with this context.
+     */
+    public Enumeration getAttributeNames() {
+
+        synchronized (attributes) {
+            return new Enumerator(attributes.keySet(), true);
+        }
+
+    }
+
+
+    /**
+     * Return a <code>ServletContext</code> object that corresponds to a
+     * specified URI on the server.  This method allows servlets to gain
+     * access to the context for various parts of the server, and as needed
+     * obtain <code>RequestDispatcher</code> objects or resources from the
+     * context.  The given path must be absolute (beginning with a "/"),
+     * and is interpreted based on our virtual host's document root.
+     *
+     * @param uri Absolute URI of a resource on the server
+     */
+    public ServletContext getContext(String uri) {
+
+        // Validate the format of the specified argument
+        if ((uri == null) || (!uri.startsWith("/")))
+            return (null);
+
+        // Return the current context if requested
+        String contextPath = context.getPath();
+        if (!contextPath.endsWith("/"))
+            contextPath = contextPath + "/";
+
+        if (((contextPath.length() > 1) && (uri.startsWith(contextPath))) ||
+            ((contextPath.equals("/")) && (uri.equals("/")))) {
+            return (this);
+        }
+
+        // Return other contexts only if allowed
+        if (!context.getCrossContext())
+            return (null);
+        try {
+            Host host = (Host) context.getParent();
+            Context child = null;
+            String mapuri = uri;
+            while (true) {
+                child = (Context) host.findChild(mapuri);
+                if (child != null)
+                    break;
+                int slash = mapuri.lastIndexOf('/');
+                if (slash < 0)
+                    break;
+                mapuri = mapuri.substring(0, slash);
+            }
+            if (child == null) {
+                child = (Context) host.findChild("");
+            }
+            if (child != null)
+                return (child.getServletContext());
+            else
+                return (null);
+        } catch (Throwable t) {
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Return the value of the specified initialization parameter, or
+     * <code>null</code> if this parameter does not exist.
+     *
+     * @param name Name of the initialization parameter to retrieve
+     */
+    public String getInitParameter(final String name) {
+
+        mergeParameters();
+        synchronized (parameters) {
+            return ((String) parameters.get(name));
+        }
+    }
+
+
+    /**
+     * Return the names of the context's initialization parameters, or an
+     * empty enumeration if the context has no initialization parameters.
+     */
+    public Enumeration getInitParameterNames() {
+
+        mergeParameters();
+        synchronized (parameters) {
+           return (new Enumerator(parameters.keySet()));
+        }
+
+    }
+
+
+    /**
+     * Return the major version of the Java Servlet API that we implement.
+     */
+    public int getMajorVersion() {
+
+        return (Constants.MAJOR_VERSION);
+
+    }
+
+
+    /**
+     * Return the minor version of the Java Servlet API that we implement.
+     */
+    public int getMinorVersion() {
+
+        return (Constants.MINOR_VERSION);
+
+    }
+
+
+    /**
+     * Return the MIME type of the specified file, or <code>null</code> if
+     * the MIME type cannot be determined.
+     *
+     * @param file Filename for which to identify a MIME type
+     */
+    public String getMimeType(String file) {
+
+        if (file == null)
+            return (null);
+        int period = file.lastIndexOf(".");
+        if (period < 0)
+            return (null);
+        String extension = file.substring(period + 1);
+        if (extension.length() < 1)
+            return (null);
+        return (context.findMimeMapping(extension));
+
+    }
+
+
+    /**
+     * Return a <code>RequestDispatcher</code> object that acts as a
+     * wrapper for the named servlet.
+     *
+     * @param name Name of the servlet for which a dispatcher is requested
+     */
+    public RequestDispatcher getNamedDispatcher(String name) {
+
+        // Validate the name argument
+        if (name == null)
+            return (null);
+
+        // Create and return a corresponding request dispatcher
+        Wrapper wrapper = (Wrapper) context.findChild(name);
+        if (wrapper == null)
+            return (null);
+        
+        ApplicationDispatcher dispatcher;
+        dispatcher =
+              new ApplicationDispatcher(wrapper, null, null, null, null, name);
+        
+        return ((RequestDispatcher) dispatcher);
+
+    }
+
+
+    /**
+     * Return the real path for a given virtual path, if possible; otherwise
+     * return <code>null</code>.
+     *
+     * @param path The path to the desired resource
+     */
+    public String getRealPath(String path) {
+
+        if (!context.isFilesystemBased())
+            return null;
+
+        if (path == null) {
+            return null;
+        }
+
+        File file = new File(basePath, path);
+        return (file.getAbsolutePath());
+
+    }
+
+
+    /**
+     * Return a <code>RequestDispatcher</code> instance that acts as a
+     * wrapper for the resource at the given path.  The path must begin
+     * with a "/" and is interpreted as relative to the current context root.
+     *
+     * @param path The path to the desired resource.
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        // Validate the path argument
+        if (path == null)
+            return (null);
+        if (!path.startsWith("/"))
+            throw new IllegalArgumentException
+                (sm.getString
+                 ("applicationContext.requestDispatcher.iae", path));
+        path = normalize(path);
+        if (path == null)
+            return (null);
+
+        // Retrieve the thread local URI
+        MessageBytes uriMB = (MessageBytes) localUriMB.get();
+        if (uriMB == null) {
+            uriMB = MessageBytes.newInstance();
+            CharChunk uriCC = uriMB.getCharChunk();
+            uriCC.setLimit(-1);
+            localUriMB.set(uriMB);
+        } else {
+            uriMB.recycle();
+        }
+
+        // Get query string
+        String queryString = null;
+        int pos = path.indexOf('?');
+        if (pos >= 0) {
+            queryString = path.substring(pos + 1);
+        } else {
+            pos = path.length();
+        }
+ 
+        // Retrieve the thread local mapping data
+        MappingData mappingData = (MappingData) localMappingData.get();
+        if (mappingData == null) {
+            mappingData = new MappingData();
+            localMappingData.set(mappingData);
+        }
+
+        // Map the URI
+        CharChunk uriCC = uriMB.getCharChunk();
+        try {
+            uriCC.append(context.getPath(), 0, context.getPath().length());
+            /*
+             * Ignore any trailing path params (separated by ';') for mapping
+             * purposes
+             */
+            int semicolon = path.indexOf(';');
+            if (pos >= 0 && semicolon > pos) {
+                semicolon = -1;
+            }
+            uriCC.append(path, 0, semicolon > 0 ? semicolon : pos);
+            context.getMapper().map(uriMB, mappingData);
+            if (mappingData.wrapper == null) {
+                return (null);
+            }
+            /*
+             * Append any trailing path params (separated by ';') that were
+             * ignored for mapping purposes, so that they're reflected in the
+             * RequestDispatcher's requestURI
+             */
+            if (semicolon > 0) {
+                uriCC.append(path, semicolon, pos - semicolon);
+            }
+        } catch (Exception e) {
+            // Should never happen
+            log(sm.getString("applicationContext.mapping.error"), e);
+            return (null);
+        }
+
+        Wrapper wrapper = (Wrapper) mappingData.wrapper;
+        String wrapperPath = mappingData.wrapperPath.toString();
+        String pathInfo = mappingData.pathInfo.toString();
+
+        mappingData.recycle();
+        
+        // Construct a RequestDispatcher to process this request
+        return (RequestDispatcher) new ApplicationDispatcher
+            (wrapper, uriCC.toString(), wrapperPath, pathInfo, 
+             queryString, null);
+
+    }
+
+
+
+    /**
+     * Return the URL to the resource that is mapped to a specified path.
+     * The path must begin with a "/" and is interpreted as relative to the
+     * current context root.
+     *
+     * @param path The path to the desired resource
+     *
+     * @exception MalformedURLException if the path is not given
+     *  in the correct form
+     */
+    public URL getResource(String path)
+        throws MalformedURLException {
+
+        if (path == null || !path.startsWith("/")) {
+            throw new MalformedURLException(sm.getString("applicationContext.requestDispatcher.iae", path));
+        }
+        
+        path = normalize(path);
+        if (path == null)
+            return (null);
+
+        String libPath = "/WEB-INF/lib/";
+        if ((path.startsWith(libPath)) && (path.endsWith(".jar"))) {
+            File jarFile = null;
+            if (context.isFilesystemBased()) {
+                jarFile = new File(basePath, path);
+            } else {
+                jarFile = new File(context.getWorkPath(), path);
+            }
+            if (jarFile.exists()) {
+                return jarFile.toURL();
+            } else {
+                return null;
+            }
+        } else {
+
+            DirContext resources = context.getResources();
+            if (resources != null) {
+                String fullPath = context.getName() + path;
+                String hostName = context.getParent().getName();
+                try {
+                    resources.lookup(path);
+                    return new URL
+                        ("jndi", null, 0, getJNDIUri(hostName, fullPath),
+                         new DirContextURLStreamHandler(resources));
+                } catch (Exception e) {
+                    // Ignore
+                }
+            }
+        }
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the requested resource as an <code>InputStream</code>.  The
+     * path must be specified according to the rules described under
+     * <code>getResource</code>.  If no such resource can be identified,
+     * return <code>null</code>.
+     *
+     * @param path The path to the desired resource.
+     */
+    public InputStream getResourceAsStream(String path) {
+
+        path = normalize(path);
+        if (path == null)
+            return (null);
+
+        DirContext resources = context.getResources();
+        if (resources != null) {
+            try {
+                Object resource = resources.lookup(path);
+                if (resource instanceof Resource)
+                    return (((Resource) resource).streamContent());
+            } catch (Exception e) {
+            }
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Return a Set containing the resource paths of resources member of the
+     * specified collection. Each path will be a String starting with
+     * a "/" character. The returned set is immutable.
+     *
+     * @param path Collection path
+     */
+    public Set getResourcePaths(String path) {
+
+        // Validate the path argument
+        if (path == null) {
+            return null;
+        }
+        if (!path.startsWith("/")) {
+            throw new IllegalArgumentException
+                (sm.getString("applicationContext.resourcePaths.iae", path));
+        }
+
+        path = normalize(path);
+        if (path == null)
+            return (null);
+
+        DirContext resources = context.getResources();
+        if (resources != null) {
+            return (getResourcePathsInternal(resources, path));
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Internal implementation of getResourcesPath() logic.
+     *
+     * @param resources Directory context to search
+     * @param path Collection path
+     */
+    private Set getResourcePathsInternal(DirContext resources, String path) {
+
+        ResourceSet set = new ResourceSet();
+        try {
+            listCollectionPaths(set, resources, path);
+        } catch (NamingException e) {
+            return (null);
+        }
+        set.setLocked(true);
+        return (set);
+
+    }
+
+
+    /**
+     * Return the name and version of the servlet container.
+     */
+    public String getServerInfo() {
+
+        return (ServerInfo.getServerInfo());
+
+    }
+
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     */
+    public Servlet getServlet(String name) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the display name of this web application.
+     */
+    public String getServletContextName() {
+
+        return (context.getDisplayName());
+
+    }
+
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     */
+    public Enumeration getServletNames() {
+        return (new Enumerator(empty));
+    }
+
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     */
+    public Enumeration getServlets() {
+        return (new Enumerator(empty));
+    }
+
+
+    /**
+     * Writes the specified message to a servlet log file.
+     *
+     * @param message Message to be written
+     */
+    public void log(String message) {
+
+        context.getLogger().info(message);
+
+    }
+
+
+    /**
+     * Writes the specified exception and message to a servlet log file.
+     *
+     * @param exception Exception to be reported
+     * @param message Message to be written
+     *
+     * @deprecated As of Java Servlet API 2.1, use
+     *  <code>log(String, Throwable)</code> instead
+     */
+    public void log(Exception exception, String message) {
+        
+        context.getLogger().error(message, exception);
+
+    }
+
+
+    /**
+     * Writes the specified message and exception to a servlet log file.
+     *
+     * @param message Message to be written
+     * @param throwable Exception to be reported
+     */
+    public void log(String message, Throwable throwable) {
+        
+        context.getLogger().error(message, throwable);
+
+    }
+
+
+    /**
+     * Remove the context attribute with the specified name, if any.
+     *
+     * @param name Name of the context attribute to be removed
+     */
+    public void removeAttribute(String name) {
+
+        Object value = null;
+        boolean found = false;
+
+        // Remove the specified attribute
+        synchronized (attributes) {
+            // Check for read only attribute
+           if (readOnlyAttributes.containsKey(name))
+                return;
+            found = attributes.containsKey(name);
+            if (found) {
+                value = attributes.get(name);
+                attributes.remove(name);
+            } else {
+                return;
+            }
+        }
+
+        // Notify interested application event listeners
+        Object listeners[] = context.getApplicationEventListeners();
+        if ((listeners == null) || (listeners.length == 0))
+            return;
+        ServletContextAttributeEvent event =
+          new ServletContextAttributeEvent(context.getServletContext(),
+                                            name, value);
+        for (int i = 0; i < listeners.length; i++) {
+            if (!(listeners[i] instanceof ServletContextAttributeListener))
+                continue;
+            ServletContextAttributeListener listener =
+                (ServletContextAttributeListener) listeners[i];
+            try {
+                context.fireContainerEvent("beforeContextAttributeRemoved",
+                                           listener);
+                listener.attributeRemoved(event);
+                context.fireContainerEvent("afterContextAttributeRemoved",
+                                           listener);
+            } catch (Throwable t) {
+                context.fireContainerEvent("afterContextAttributeRemoved",
+                                           listener);
+                // FIXME - should we do anything besides log these?
+                log(sm.getString("applicationContext.attributeEvent"), t);
+            }
+        }
+
+    }
+
+
+    /**
+     * Bind the specified value with the specified context attribute name,
+     * replacing any existing value for that name.
+     *
+     * @param name Attribute name to be bound
+     * @param value New attribute value to be bound
+     */
+    public void setAttribute(String name, Object value) {
+
+        // Name cannot be null
+        if (name == null)
+            throw new IllegalArgumentException
+                (sm.getString("applicationContext.setAttribute.namenull"));
+
+        // Null value is the same as removeAttribute()
+        if (value == null) {
+            removeAttribute(name);
+            return;
+        }
+
+        Object oldValue = null;
+        boolean replaced = false;
+
+        // Add or replace the specified attribute
+        synchronized (attributes) {
+            // Check for read only attribute
+            if (readOnlyAttributes.containsKey(name))
+                return;
+            oldValue = attributes.get(name);
+            if (oldValue != null)
+                replaced = true;
+            attributes.put(name, value);
+        }
+
+        // Notify interested application event listeners
+        Object listeners[] = context.getApplicationEventListeners();
+        if ((listeners == null) || (listeners.length == 0))
+            return;
+        ServletContextAttributeEvent event = null;
+        if (replaced)
+            event =
+                new ServletContextAttributeEvent(context.getServletContext(),
+                                                 name, oldValue);
+        else
+            event =
+                new ServletContextAttributeEvent(context.getServletContext(),
+                                                 name, value);
+
+        for (int i = 0; i < listeners.length; i++) {
+            if (!(listeners[i] instanceof ServletContextAttributeListener))
+                continue;
+            ServletContextAttributeListener listener =
+                (ServletContextAttributeListener) listeners[i];
+            try {
+                if (replaced) {
+                    context.fireContainerEvent
+                        ("beforeContextAttributeReplaced", listener);
+                    listener.attributeReplaced(event);
+                    context.fireContainerEvent("afterContextAttributeReplaced",
+                                               listener);
+                } else {
+                    context.fireContainerEvent("beforeContextAttributeAdded",
+                                               listener);
+                    listener.attributeAdded(event);
+                    context.fireContainerEvent("afterContextAttributeAdded",
+                                               listener);
+                }
+            } catch (Throwable t) {
+                if (replaced)
+                    context.fireContainerEvent("afterContextAttributeReplaced",
+                                               listener);
+                else
+                    context.fireContainerEvent("afterContextAttributeAdded",
+                                               listener);
+                // FIXME - should we do anything besides log these?
+                log(sm.getString("applicationContext.attributeEvent"), t);
+            }
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Clear all application-created attributes.
+     */
+    void clearAttributes() {
+
+        // Create list of attributes to be removed
+        ArrayList list = new ArrayList();
+        synchronized (attributes) {
+            Iterator iter = attributes.keySet().iterator();
+            while (iter.hasNext()) {
+                list.add(iter.next());
+            }
+        }
+
+        // Remove application originated attributes
+        // (read only attributes will be left in place)
+        Iterator keys = list.iterator();
+        while (keys.hasNext()) {
+            String key = (String) keys.next();
+            removeAttribute(key);
+        }
+        
+    }
+    
+    
+    /**
+     * Return the facade associated with this ApplicationContext.
+     */
+    protected ServletContext getFacade() {
+
+        return (this.facade);
+
+    }
+
+
+    /**
+     * Set an attribute as read only.
+     */
+    void setAttributeReadOnly(String name) {
+
+        synchronized (attributes) {
+            if (attributes.containsKey(name))
+                readOnlyAttributes.put(name, name);
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Return a context-relative path, beginning with a "/", that represents
+     * the canonical version of the specified path after ".." and "." elements
+     * are resolved out.  If the specified path attempts to go outside the
+     * boundaries of the current context (i.e. too many ".." path elements
+     * are present), return <code>null</code> instead.
+     *
+     * @param path Path to be normalized
+     */
+    private String normalize(String path) {
+
+        if (path == null) {
+            return null;
+        }
+
+        String normalized = path;
+
+        // Normalize the slashes and add leading slash if necessary
+        if (normalized.indexOf('\\') >= 0)
+            normalized = normalized.replace('\\', '/');
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/../");
+            if (index < 0)
+                break;
+            if (index == 0)
+                return (null);  // Trying to go outside our context
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            normalized = normalized.substring(0, index2) +
+                normalized.substring(index + 3);
+        }
+
+        // Return the normalized path that we have completed
+        return (normalized);
+
+    }
+
+
+    /**
+     * Merge the context initialization parameters specified in the application
+     * deployment descriptor with the application parameters described in the
+     * server configuration, respecting the <code>override</code> property of
+     * the application parameters appropriately.
+     */
+    private void mergeParameters() {
+
+        if (parameters != null)
+            return;
+        HashMap results = new HashMap();
+        String names[] = context.findParameters();
+        for (int i = 0; i < names.length; i++)
+            results.put(names[i], context.findParameter(names[i]));
+        ApplicationParameter params[] =
+            context.findApplicationParameters();
+        for (int i = 0; i < params.length; i++) {
+            if (params[i].getOverride()) {
+                if (results.get(params[i].getName()) == null)
+                    results.put(params[i].getName(), params[i].getValue());
+            } else {
+                results.put(params[i].getName(), params[i].getValue());
+            }
+        }
+        parameters = results;
+
+    }
+
+
+    /**
+     * List resource paths (recursively), and store all of them in the given
+     * Set.
+     */
+    private static void listPaths(Set set, DirContext resources, String path)
+        throws NamingException {
+
+        Enumeration childPaths = resources.listBindings(path);
+        while (childPaths.hasMoreElements()) {
+            Binding binding = (Binding) childPaths.nextElement();
+            String name = binding.getName();
+            String childPath = path + "/" + name;
+            set.add(childPath);
+            Object object = binding.getObject();
+            if (object instanceof DirContext) {
+                listPaths(set, resources, childPath);
+            }
+        }
+
+    }
+
+
+    /**
+     * List resource paths (recursively), and store all of them in the given
+     * Set.
+     */
+    private static void listCollectionPaths
+        (Set set, DirContext resources, String path)
+        throws NamingException {
+
+        Enumeration childPaths = resources.listBindings(path);
+        while (childPaths.hasMoreElements()) {
+            Binding binding = (Binding) childPaths.nextElement();
+            String name = binding.getName();
+            StringBuffer childPath = new StringBuffer(path);
+            if (!"/".equals(path) && !path.endsWith("/"))
+                childPath.append("/");
+            childPath.append(name);
+            Object object = binding.getObject();
+            if (object instanceof DirContext) {
+                childPath.append("/");
+            }
+            set.add(childPath.toString());
+        }
+
+    }
+
+
+    /**
+     * Get full path, based on the host name and the context path.
+     */
+    private static String getJNDIUri(String hostName, String path) {
+        if (!path.startsWith("/"))
+            return "/" + hostName + "/" + path;
+        else
+            return "/" + hostName + path;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationContextFacade.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationContextFacade.java
new file mode 100644
index 0000000..f00b436
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationContextFacade.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Set;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.security.SecurityUtil;
+
+
+/**
+ * Facade object which masks the internal <code>ApplicationContext</code>
+ * object from the web application.
+ *
+ * @author Remy Maucherat
+ * @author Jean-Francois Arcand
+ * @version $Revision$ $Date$
+ */
+
+public final class ApplicationContextFacade
+    implements ServletContext {
+        
+    // ---------------------------------------------------------- Attributes
+    /**
+     * Cache Class object used for reflection.
+     */
+    private HashMap classCache;
+    
+    
+    /**
+     * Cache method object.
+     */
+    private HashMap objectCache;
+    
+    
+    private static org.apache.commons.logging.Log sysLog=
+        org.apache.commons.logging.LogFactory.getLog( ApplicationContextFacade.class );
+
+        
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class, associated with the specified
+     * Context instance.
+     *
+     * @param context The associated Context instance
+     */
+    public ApplicationContextFacade(ApplicationContext context) {
+        super();
+        this.context = context;
+        
+        classCache = new HashMap();
+        objectCache = new HashMap();
+        initClassCache();
+    }
+    
+    
+    private void initClassCache(){
+        Class[] clazz = new Class[]{String.class};
+        classCache.put("getContext", clazz);
+        classCache.put("getMimeType", clazz);
+        classCache.put("getResourcePaths", clazz);
+        classCache.put("getResource", clazz);
+        classCache.put("getResourceAsStream", clazz);
+        classCache.put("getRequestDispatcher", clazz);
+        classCache.put("getNamedDispatcher", clazz);
+        classCache.put("getServlet", clazz);
+        classCache.put("getInitParameter", clazz);
+        classCache.put("setAttribute", new Class[]{String.class, Object.class});
+        classCache.put("removeAttribute", clazz);
+        classCache.put("getRealPath", clazz);
+        classCache.put("getAttribute", clazz);
+        classCache.put("log", clazz);
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Wrapped application context.
+     */
+    private ApplicationContext context = null;
+    
+
+
+    // ------------------------------------------------- ServletContext Methods
+
+
+    public ServletContext getContext(String uripath) {
+        ServletContext theContext = null;
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            theContext = (ServletContext)
+                doPrivileged("getContext", new Object[]{uripath});
+        } else {
+            theContext = context.getContext(uripath);
+        }
+        if ((theContext != null) &&
+            (theContext instanceof ApplicationContext)){
+            theContext = ((ApplicationContext)theContext).getFacade();
+        }
+        return (theContext);
+    }
+
+
+    public int getMajorVersion() {
+        return context.getMajorVersion();
+    }
+
+
+    public int getMinorVersion() {
+        return context.getMinorVersion();
+    }
+
+
+    public String getMimeType(String file) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String)doPrivileged("getMimeType", new Object[]{file});
+        } else {
+            return context.getMimeType(file);
+        }
+    }
+
+
+    public Set getResourcePaths(String path) {
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return (Set)doPrivileged("getResourcePaths", new Object[]{path});
+        } else {
+            return context.getResourcePaths(path);
+        }
+    }
+
+
+    public URL getResource(String path)
+        throws MalformedURLException {
+        if (System.getSecurityManager() != null) {
+            try {
+                return (URL) invokeMethod(context, "getResource", 
+                                          new Object[]{path});
+            } catch(Throwable t) {
+                if (t instanceof MalformedURLException){
+                    throw (MalformedURLException)t;
+                }
+                return null;
+            }
+        } else {
+            return context.getResource(path);
+        }
+    }
+
+
+    public InputStream getResourceAsStream(String path) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (InputStream) doPrivileged("getResourceAsStream", 
+                                              new Object[]{path});
+        } else {
+            return context.getResourceAsStream(path);
+        }
+    }
+
+
+    public RequestDispatcher getRequestDispatcher(final String path) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (RequestDispatcher) doPrivileged("getRequestDispatcher", 
+                                                    new Object[]{path});
+        } else {
+            return context.getRequestDispatcher(path);
+        }
+    }
+
+
+    public RequestDispatcher getNamedDispatcher(String name) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (RequestDispatcher) doPrivileged("getNamedDispatcher", 
+                                                    new Object[]{name});
+        } else {
+            return context.getNamedDispatcher(name);
+        }
+    }
+
+
+    public Servlet getServlet(String name)
+        throws ServletException {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            try {
+                return (Servlet) invokeMethod(context, "getServlet", 
+                                              new Object[]{name});
+            } catch (Throwable t) {
+                if (t instanceof ServletException) {
+                    throw (ServletException) t;
+                }
+                return null;
+            }
+        } else {
+            return context.getServlet(name);
+        }
+    }
+
+
+    public Enumeration getServlets() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Enumeration) doPrivileged("getServlets", null);
+        } else {
+            return context.getServlets();
+        }
+    }
+
+
+    public Enumeration getServletNames() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Enumeration) doPrivileged("getServletNames", null);
+        } else {
+            return context.getServletNames();
+        }
+   }
+
+
+    public void log(String msg) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("log", new Object[]{msg} );
+        } else {
+            context.log(msg);
+        }
+    }
+
+
+    public void log(Exception exception, String msg) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("log", new Class[]{Exception.class, String.class}, 
+                         new Object[]{exception,msg});
+        } else {
+            context.log(exception, msg);
+        }
+    }
+
+
+    public void log(String message, Throwable throwable) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("log", new Class[]{String.class, Throwable.class}, 
+                         new Object[]{message, throwable});
+        } else {
+            context.log(message, throwable);
+        }
+    }
+
+
+    public String getRealPath(String path) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getRealPath", new Object[]{path});
+        } else {
+            return context.getRealPath(path);
+        }
+    }
+
+
+    public String getServerInfo() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getServerInfo", null);
+        } else {
+            return context.getServerInfo();
+        }
+    }
+
+
+    public String getInitParameter(String name) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getInitParameter", 
+                                         new Object[]{name});
+        } else {
+            return context.getInitParameter(name);
+        }
+    }
+
+
+    public Enumeration getInitParameterNames() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Enumeration) doPrivileged("getInitParameterNames", null);
+        } else {
+            return context.getInitParameterNames();
+        }
+    }
+
+
+    public Object getAttribute(String name) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return doPrivileged("getAttribute", new Object[]{name});
+        } else {
+            return context.getAttribute(name);
+        }
+     }
+
+
+    public Enumeration getAttributeNames() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Enumeration) doPrivileged("getAttributeNames", null);
+        } else {
+            return context.getAttributeNames();
+        }
+    }
+
+
+    public void setAttribute(String name, Object object) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("setAttribute", new Object[]{name,object});
+        } else {
+            context.setAttribute(name, object);
+        }
+    }
+
+
+    public void removeAttribute(String name) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("removeAttribute", new Object[]{name});
+        } else {
+            context.removeAttribute(name);
+        }
+    }
+
+
+    public String getServletContextName() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getServletContextName", null);
+        } else {
+            return context.getServletContextName();
+        }
+    }
+
+       
+    /**
+     * Use reflection to invoke the requested method. Cache the method object 
+     * to speed up the process
+     * @param appContext The AppliationContext object on which the method
+     *                   will be invoked
+     * @param methodName The method to call.
+     * @param params The arguments passed to the called method.
+     */
+    private Object doPrivileged(ApplicationContext appContext,
+                                final String methodName, 
+                                final Object[] params) {
+        try{
+            return invokeMethod(appContext, methodName, params );
+        } catch (Throwable t){
+            throw new RuntimeException(t.getMessage());
+        }
+
+    }
+
+
+    /**
+     * Use reflection to invoke the requested method. Cache the method object 
+     * to speed up the process
+     *                   will be invoked
+     * @param methodName The method to call.
+     * @param params The arguments passed to the called method.
+     */
+    private Object doPrivileged(final String methodName, final Object[] params){
+        try{
+            return invokeMethod(context, methodName, params);
+        }catch(Throwable t){
+            throw new RuntimeException(t.getMessage());
+        }
+    }
+
+    
+    /**
+     * Use reflection to invoke the requested method. Cache the method object 
+     * to speed up the process
+     * @param appContext The AppliationContext object on which the method
+     *                   will be invoked
+     * @param methodName The method to call.
+     * @param params The arguments passed to the called method.
+     */
+    private Object invokeMethod(ApplicationContext appContext,
+                                final String methodName, 
+                                Object[] params) 
+        throws Throwable{
+
+        try{
+            Method method = (Method)objectCache.get(methodName);
+            if (method == null){
+                method = appContext.getClass()
+                    .getMethod(methodName, (Class[])classCache.get(methodName));
+                objectCache.put(methodName, method);
+            }
+            
+            return executeMethod(method,appContext,params);
+        } catch (Exception ex){
+            handleException(ex, methodName);
+            return null;
+        } finally {
+            params = null;
+        }
+    }
+    
+    /**
+     * Use reflection to invoke the requested method. Cache the method object 
+     * to speed up the process
+     * @param methodName The method to invoke.
+     * @param clazz The class where the method is.
+     * @param params The arguments passed to the called method.
+     */    
+    private Object doPrivileged(final String methodName, 
+                                final Class[] clazz,
+                                Object[] params){
+
+        try{
+            Method method = context.getClass()
+                    .getMethod(methodName, (Class[])clazz);
+            return executeMethod(method,context,params);
+        } catch (Exception ex){
+            try{
+                handleException(ex, methodName);
+            }catch (Throwable t){
+                throw new RuntimeException(t.getMessage());
+            }
+            return null;
+        } finally {
+            params = null;
+        }
+    }
+    
+    
+    /**
+     * Executes the method of the specified <code>ApplicationContext</code>
+     * @param method The method object to be invoked.
+     * @param context The AppliationContext object on which the method
+     *                   will be invoked
+     * @param params The arguments passed to the called method.
+     */
+    private Object executeMethod(final Method method, 
+                                 final ApplicationContext context,
+                                 final Object[] params) 
+            throws PrivilegedActionException, 
+                   IllegalAccessException,
+                   InvocationTargetException {
+                                     
+        if (SecurityUtil.isPackageProtectionEnabled()){
+           return AccessController.doPrivileged(new PrivilegedExceptionAction(){
+                public Object run() throws IllegalAccessException, InvocationTargetException{
+                    return method.invoke(context,  params);
+                }
+            });
+        } else {
+            return method.invoke(context, params);
+        }        
+    }
+
+    
+    /**
+     *
+     * Throw the real exception.
+     * @param ex The current exception
+     */
+    private void handleException(Exception ex, String methodName)
+	    throws Throwable {
+
+        Throwable realException;
+
+        if (sysLog.isDebugEnabled()) {   
+            sysLog.debug("ApplicationContextFacade." + methodName, ex);
+        }
+
+	if (ex instanceof PrivilegedActionException) {
+            ex = ((PrivilegedActionException) ex).getException();
+	}
+
+        if (ex instanceof InvocationTargetException) {
+            realException =
+		((InvocationTargetException) ex).getTargetException();
+        } else {
+            realException = ex;
+        }   
+
+        throw realException;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationDispatcher.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationDispatcher.java
new file mode 100644
index 0000000..14854a7
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationDispatcher.java
@@ -0,0 +1,961 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.InstanceEvent;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.ClientAbortException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.RequestFacade;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.connector.ResponseFacade;
+import org.apache.catalina.util.InstanceSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Standard implementation of <code>RequestDispatcher</code> that allows a
+ * request to be forwarded to a different resource to create the ultimate
+ * response, or to include the output of another resource in the response
+ * from this resource.  This implementation allows application level servlets
+ * to wrap the request and/or response objects that are passed on to the
+ * called resource, as long as the wrapping classes extend
+ * <code>javax.servlet.ServletRequestWrapper</code> and
+ * <code>javax.servlet.ServletResponseWrapper</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+final class ApplicationDispatcher
+    implements RequestDispatcher {
+
+
+    protected class PrivilegedForward implements PrivilegedExceptionAction {
+        private ServletRequest request;
+        private ServletResponse response;
+
+        PrivilegedForward(ServletRequest request, ServletResponse response)
+        {
+            this.request = request;
+            this.response = response;
+        }
+
+        public Object run() throws java.lang.Exception {
+            doForward(request,response);
+            return null;
+        }
+    }
+
+    protected class PrivilegedInclude implements PrivilegedExceptionAction {
+        private ServletRequest request;
+        private ServletResponse response;
+
+        PrivilegedInclude(ServletRequest request, ServletResponse response)
+        {
+            this.request = request;
+            this.response = response;
+        }
+
+        public Object run() throws ServletException, IOException {
+            doInclude(request,response);
+            return null;
+        }
+    }
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class, configured according to the
+     * specified parameters.  If both servletPath and pathInfo are
+     * <code>null</code>, it will be assumed that this RequestDispatcher
+     * was acquired by name, rather than by path.
+     *
+     * @param wrapper The Wrapper associated with the resource that will
+     *  be forwarded to or included (required)
+     * @param requestURI The request URI to this resource (if any)
+     * @param servletPath The revised servlet path to this resource (if any)
+     * @param pathInfo The revised extra path information to this resource
+     *  (if any)
+     * @param queryString Query string parameters included with this request
+     *  (if any)
+     * @param name Servlet name (if a named dispatcher was created)
+     *  else <code>null</code>
+     */
+    public ApplicationDispatcher
+        (Wrapper wrapper, String requestURI, String servletPath,
+         String pathInfo, String queryString, String name) {
+
+        super();
+
+        // Save all of our configuration parameters
+        this.wrapper = wrapper;
+        this.context = (Context) wrapper.getParent();
+        this.requestURI = requestURI;
+        this.servletPath = servletPath;
+        this.origServletPath = servletPath;
+        this.pathInfo = pathInfo;
+        this.queryString = queryString;
+        this.name = name;
+        if (wrapper instanceof StandardWrapper)
+            this.support = ((StandardWrapper) wrapper).getInstanceSupport();
+        else
+            this.support = new InstanceSupport(wrapper);
+
+        if ( log.isDebugEnabled() )
+            log.debug("servletPath=" + this.servletPath + ", pathInfo=" +
+                this.pathInfo + ", queryString=" + queryString +
+                ", name=" + this.name);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    private static Log log = LogFactory.getLog(ApplicationDispatcher.class);
+
+    /**
+     * The request specified by the dispatching application.
+     */
+    private ServletRequest appRequest = null;
+
+
+    /**
+     * The response specified by the dispatching application.
+     */
+    private ServletResponse appResponse = null;
+
+
+    /**
+     * The Context this RequestDispatcher is associated with.
+     */
+    private Context context = null;
+
+
+    /**
+     * Are we performing an include() instead of a forward()?
+     */
+    private boolean including = false;
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.ApplicationDispatcher/1.0";
+
+
+    /**
+     * The servlet name for a named dispatcher.
+     */
+    private String name = null;
+
+
+    /**
+     * The outermost request that will be passed on to the invoked servlet.
+     */
+    private ServletRequest outerRequest = null;
+
+
+    /**
+     * The outermost response that will be passed on to the invoked servlet.
+     */
+    private ServletResponse outerResponse = null;
+
+
+    /**
+     * The extra path information for this RequestDispatcher.
+     */
+    private String pathInfo = null;
+
+
+    /**
+     * The query string parameters for this RequestDispatcher.
+     */
+    private String queryString = null;
+
+
+    /**
+     * The request URI for this RequestDispatcher.
+     */
+    private String requestURI = null;
+
+    /**
+     * The servlet path for this RequestDispatcher.
+     */
+    private String servletPath = null;
+
+    private String origServletPath = null;
+    
+    /**
+     * The StringManager for this package.
+     */
+    private static final StringManager sm =
+      StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The InstanceSupport instance associated with our Wrapper (used to
+     * send "before dispatch" and "after dispatch" events.
+     */
+    private InstanceSupport support = null;
+
+
+    /**
+     * The Wrapper associated with the resource that will be forwarded to
+     * or included.
+     */
+    private Wrapper wrapper = null;
+
+
+    /**
+     * The request wrapper we have created and installed (if any).
+     */
+    private ServletRequest wrapRequest = null;
+
+
+    /**
+     * The response wrapper we have created and installed (if any).
+     */
+    private ServletResponse wrapResponse = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Forward this request and response to another resource for processing.
+     * Any runtime exception, IOException, or ServletException thrown by the
+     * called servlet will be propogated to the caller.
+     *
+     * @param request The servlet request to be forwarded
+     * @param response The servlet response to be forwarded
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public void forward(ServletRequest request, ServletResponse response)
+        throws ServletException, IOException
+    {
+        if (System.getSecurityManager() != null) {
+            try {
+                PrivilegedForward dp = new PrivilegedForward(request,response);
+                AccessController.doPrivileged(dp);
+            } catch (PrivilegedActionException pe) {
+                Exception e = pe.getException();
+                if (e instanceof ServletException)
+                    throw (ServletException) e;
+                throw (IOException) e;
+            }
+        } else {
+            doForward(request,response);
+        }
+    }
+
+    private void doForward(ServletRequest request, ServletResponse response)
+        throws ServletException, IOException
+    {
+        
+        // Reset any output that has been buffered, but keep headers/cookies
+        if (response.isCommitted()) {
+            if ( log.isDebugEnabled() )
+                log.debug("  Forward on committed response --> ISE");
+            throw new IllegalStateException
+                (sm.getString("applicationDispatcher.forward.ise"));
+        }
+        try {
+            response.resetBuffer();
+        } catch (IllegalStateException e) {
+            if ( log.isDebugEnabled() )
+                log.debug("  Forward resetBuffer() returned ISE: " + e);
+            throw e;
+        }
+
+        // Set up to handle the specified request and response
+        setup(request, response, false);
+
+        // Identify the HTTP-specific request and response objects (if any)
+        HttpServletRequest hrequest = null;
+        if (request instanceof HttpServletRequest)
+            hrequest = (HttpServletRequest) request;
+        HttpServletResponse hresponse = null;
+        if (response instanceof HttpServletResponse)
+            hresponse = (HttpServletResponse) response;
+
+        // Handle a non-HTTP forward by passing the existing request/response
+        if ((hrequest == null) || (hresponse == null)) {
+
+            if ( log.isDebugEnabled() )
+                log.debug(" Non-HTTP Forward");
+            
+            processRequest(hrequest,hresponse);
+
+        }
+
+        // Handle an HTTP named dispatcher forward
+        else if ((servletPath == null) && (pathInfo == null)) {
+
+            if ( log.isDebugEnabled() )
+                log.debug(" Named Dispatcher Forward");
+            
+            ApplicationHttpRequest wrequest =
+                (ApplicationHttpRequest) wrapRequest();
+            wrequest.setRequestURI(hrequest.getRequestURI());
+            wrequest.setContextPath(hrequest.getContextPath());
+            wrequest.setServletPath(hrequest.getServletPath());
+            wrequest.setPathInfo(hrequest.getPathInfo());
+            wrequest.setQueryString(hrequest.getQueryString());
+
+            processRequest(request,response);
+
+            wrequest.recycle();
+            unwrapRequest();
+
+        }
+
+        // Handle an HTTP path-based forward
+        else {
+
+            if ( log.isDebugEnabled() )
+                log.debug(" Path Based Forward");
+
+            ApplicationHttpRequest wrequest =
+                (ApplicationHttpRequest) wrapRequest();
+            String contextPath = context.getPath();
+
+            if (hrequest.getAttribute(Globals.FORWARD_REQUEST_URI_ATTR) == null) {
+                wrequest.setAttribute(Globals.FORWARD_REQUEST_URI_ATTR,
+                                      hrequest.getRequestURI());
+                wrequest.setAttribute(Globals.FORWARD_CONTEXT_PATH_ATTR,
+                                      hrequest.getContextPath());
+                wrequest.setAttribute(Globals.FORWARD_SERVLET_PATH_ATTR,
+                                      hrequest.getServletPath());
+                wrequest.setAttribute(Globals.FORWARD_PATH_INFO_ATTR,
+                                      hrequest.getPathInfo());
+                wrequest.setAttribute(Globals.FORWARD_QUERY_STRING_ATTR,
+                                      hrequest.getQueryString());
+            }
+ 
+            wrequest.setContextPath(contextPath);
+            wrequest.setRequestURI(requestURI);
+            wrequest.setServletPath(servletPath);
+            wrequest.setPathInfo(pathInfo);
+            if (queryString != null) {
+                wrequest.setQueryString(queryString);
+                wrequest.setQueryParams(queryString);
+            }
+
+            processRequest(request,response);
+
+            wrequest.recycle();
+            unwrapRequest();
+
+        }
+
+        // This is not a real close in order to support error processing
+        if ( log.isDebugEnabled() )
+            log.debug(" Disabling the response for futher output");
+
+        if  (response instanceof ResponseFacade) {
+            ((ResponseFacade) response).finish();
+        } else {
+            // Servlet SRV.6.2.2. The Resquest/Response may have been wrapped
+            // and may no longer be instance of RequestFacade 
+            if (log.isDebugEnabled()){
+                log.debug( " The Response is vehiculed using a wrapper: " 
+                           + response.getClass().getName() );
+            }
+
+            // Close anyway
+            try {
+                PrintWriter writer = response.getWriter();
+                writer.close();
+            } catch (IllegalStateException e) {
+                try {
+                    ServletOutputStream stream = response.getOutputStream();
+                    stream.close();
+                } catch (IllegalStateException f) {
+                    ;
+                } catch (IOException f) {
+                    ;
+                }
+            } catch (IOException e) {
+                ;
+            }
+        }
+
+    }
+
+    
+
+    /**
+     * Prepare the request based on the filter configuration.
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    private void processRequest(ServletRequest request, 
+                                ServletResponse response)
+        throws IOException, ServletException {
+                
+        Integer disInt = (Integer) request.getAttribute
+            (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR);
+        if (disInt != null) {
+            if (disInt.intValue() != ApplicationFilterFactory.ERROR) {
+                outerRequest.setAttribute
+                    (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
+                     origServletPath);
+                outerRequest.setAttribute
+                    (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+                     new Integer(ApplicationFilterFactory.FORWARD));
+                invoke(outerRequest, response);
+            } else {
+                invoke(outerRequest, response);
+            }
+        }
+
+    }
+    
+    
+    
+    /**
+     * Include the response from another resource in the current response.
+     * Any runtime exception, IOException, or ServletException thrown by the
+     * called servlet will be propogated to the caller.
+     *
+     * @param request The servlet request that is including this one
+     * @param response The servlet response to be appended to
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public void include(ServletRequest request, ServletResponse response)
+        throws ServletException, IOException
+    {
+        if (System.getSecurityManager() != null) {
+            try {
+                PrivilegedInclude dp = new PrivilegedInclude(request,response);
+                AccessController.doPrivileged(dp);
+            } catch (PrivilegedActionException pe) {
+                Exception e = pe.getException();
+
+                if (e instanceof ServletException)
+                    throw (ServletException) e;
+                throw (IOException) e;
+            }
+        } else {
+            doInclude(request,response);
+        }
+    }
+
+    private void doInclude(ServletRequest request, ServletResponse response)
+        throws ServletException, IOException
+    {
+        // Set up to handle the specified request and response
+        setup(request, response, true);
+
+        // Create a wrapped response to use for this request
+        // ServletResponse wresponse = null;
+        ServletResponse wresponse = wrapResponse();
+
+        // Handle a non-HTTP include
+        if (!(request instanceof HttpServletRequest) ||
+            !(response instanceof HttpServletResponse)) {
+
+            if ( log.isDebugEnabled() )
+                log.debug(" Non-HTTP Include");
+            request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+                                             new Integer(ApplicationFilterFactory.INCLUDE));
+            request.setAttribute(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR, origServletPath);
+            invoke(request, outerResponse);
+        }
+
+        // Handle an HTTP named dispatcher include
+        else if (name != null) {
+
+            if ( log.isDebugEnabled() )
+                log.debug(" Named Dispatcher Include");
+
+            ApplicationHttpRequest wrequest =
+                (ApplicationHttpRequest) wrapRequest();
+            wrequest.setAttribute(Globals.NAMED_DISPATCHER_ATTR, name);
+            if (servletPath != null)
+                wrequest.setServletPath(servletPath);
+            wrequest.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+                                             new Integer(ApplicationFilterFactory.INCLUDE));
+            wrequest.setAttribute(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR, origServletPath);
+            invoke(outerRequest, outerResponse);
+
+            wrequest.recycle();
+        }
+
+        // Handle an HTTP path based include
+        else {
+
+            if ( log.isDebugEnabled() )
+                log.debug(" Path Based Include");
+
+            ApplicationHttpRequest wrequest =
+                (ApplicationHttpRequest) wrapRequest();
+            String contextPath = context.getPath();
+            if (requestURI != null)
+                wrequest.setAttribute(Globals.INCLUDE_REQUEST_URI_ATTR,
+                                      requestURI);
+            if (contextPath != null)
+                wrequest.setAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR,
+                                      contextPath);
+            if (servletPath != null)
+                wrequest.setAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR,
+                                      servletPath);
+            if (pathInfo != null)
+                wrequest.setAttribute(Globals.INCLUDE_PATH_INFO_ATTR,
+                                      pathInfo);
+            if (queryString != null) {
+                wrequest.setAttribute(Globals.INCLUDE_QUERY_STRING_ATTR,
+                                      queryString);
+                wrequest.setQueryParams(queryString);
+            }
+            
+            wrequest.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+                                             new Integer(ApplicationFilterFactory.INCLUDE));
+            wrequest.setAttribute(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR, origServletPath);
+            invoke(outerRequest, outerResponse);
+
+            wrequest.recycle();
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Ask the resource represented by this RequestDispatcher to process
+     * the associated request, and create (or append to) the associated
+     * response.
+     * <p>
+     * <strong>IMPLEMENTATION NOTE</strong>: This implementation assumes
+     * that no filters are applied to a forwarded or included resource,
+     * because they were already done for the original request.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    private void invoke(ServletRequest request, ServletResponse response)
+            throws IOException, ServletException {
+
+        // Checking to see if the context classloader is the current context
+        // classloader. If it's not, we're saving it, and setting the context
+        // classloader to the Context classloader
+        ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
+        ClassLoader contextClassLoader = context.getLoader().getClassLoader();
+
+        if (oldCCL != contextClassLoader) {
+            Thread.currentThread().setContextClassLoader(contextClassLoader);
+        } else {
+            oldCCL = null;
+        }
+
+        // Initialize local variables we may need
+        HttpServletRequest hrequest = (HttpServletRequest) request;
+        HttpServletResponse hresponse = (HttpServletResponse) response;
+        Servlet servlet = null;
+        IOException ioException = null;
+        ServletException servletException = null;
+        RuntimeException runtimeException = null;
+        boolean unavailable = false;
+
+        // Check for the servlet being marked unavailable
+        if (wrapper.isUnavailable()) {
+            wrapper.getLogger().warn(
+                    sm.getString("applicationDispatcher.isUnavailable", 
+                    wrapper.getName()));
+            long available = wrapper.getAvailable();
+            if ((available > 0L) && (available < Long.MAX_VALUE))
+                hresponse.setDateHeader("Retry-After", available);
+            hresponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm
+                    .getString("applicationDispatcher.isUnavailable", wrapper
+                            .getName()));
+            unavailable = true;
+        }
+
+        // Allocate a servlet instance to process this request
+        try {
+            if (!unavailable) {
+                servlet = wrapper.allocate();
+            }
+        } catch (ServletException e) {
+            wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException",
+                             wrapper.getName()), StandardWrapper.getRootCause(e));
+            servletException = e;
+            servlet = null;
+        } catch (Throwable e) {
+            wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException",
+                             wrapper.getName()), e);
+            servletException = new ServletException
+                (sm.getString("applicationDispatcher.allocateException",
+                              wrapper.getName()), e);
+            servlet = null;
+        }
+                
+        // Get the FilterChain Here
+        ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
+        ApplicationFilterChain filterChain = factory.createFilterChain(request,
+                                                                wrapper,servlet);
+        // Call the service() method for the allocated servlet instance
+        try {
+            String jspFile = wrapper.getJspFile();
+            if (jspFile != null)
+                request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
+            else
+                request.removeAttribute(Globals.JSP_FILE_ATTR);
+            support.fireInstanceEvent(InstanceEvent.BEFORE_DISPATCH_EVENT,
+                                      servlet, request, response);
+            // for includes/forwards
+            if ((servlet != null) && (filterChain != null)) {
+               filterChain.doFilter(request, response);
+             }
+            // Servlet Service Method is called by the FilterChain
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+        } catch (ClientAbortException e) {
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            ioException = e;
+        } catch (IOException e) {
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
+                             wrapper.getName()), e);
+            ioException = e;
+        } catch (UnavailableException e) {
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
+                             wrapper.getName()), e);
+            servletException = e;
+            wrapper.unavailable(e);
+        } catch (ServletException e) {
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            Throwable rootCause = StandardWrapper.getRootCause(e);
+            if (!(rootCause instanceof ClientAbortException)) {
+                wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
+                        wrapper.getName()), rootCause);
+            }
+            servletException = e;
+        } catch (RuntimeException e) {
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
+                             wrapper.getName()), e);
+            runtimeException = e;
+        }
+
+        // Release the filter chain (if any) for this request
+        try {
+            if (filterChain != null)
+                filterChain.release();
+        } catch (Throwable e) {
+            log.error(sm.getString("standardWrapper.releaseFilters",
+                             wrapper.getName()), e);
+          //FIXME Exception handling needs to be simpiler to what is in the StandardWrapperValue
+        }
+
+        // Deallocate the allocated servlet instance
+        try {
+            if (servlet != null) {
+                wrapper.deallocate(servlet);
+            }
+        } catch (ServletException e) {
+            wrapper.getLogger().error(sm.getString("applicationDispatcher.deallocateException",
+                             wrapper.getName()), e);
+            servletException = e;
+        } catch (Throwable e) {
+            wrapper.getLogger().error(sm.getString("applicationDispatcher.deallocateException",
+                             wrapper.getName()), e);
+            servletException = new ServletException
+                (sm.getString("applicationDispatcher.deallocateException",
+                              wrapper.getName()), e);
+        }
+
+        // Reset the old context class loader
+        if (oldCCL != null)
+            Thread.currentThread().setContextClassLoader(oldCCL);
+        
+        // Unwrap request/response if needed
+        // See Bugzilla 30949
+        unwrapRequest();
+        unwrapResponse();
+
+        // Rethrow an exception if one was thrown by the invoked servlet
+        if (ioException != null)
+            throw ioException;
+        if (servletException != null)
+            throw servletException;
+        if (runtimeException != null)
+            throw runtimeException;
+
+    }
+
+
+    /**
+     * Set up to handle the specified request and response
+     *
+     * @param request The servlet request specified by the caller
+     * @param response The servlet response specified by the caller
+     * @param including Are we performing an include() as opposed to
+     *  a forward()?
+     */
+    private void setup(ServletRequest request, ServletResponse response,
+                       boolean including) {
+
+        this.appRequest = request;
+        this.appResponse = response;
+        this.outerRequest = request;
+        this.outerResponse = response;
+        this.including = including;
+
+    }
+
+
+    /**
+     * Unwrap the request if we have wrapped it.
+     */
+    private void unwrapRequest() {
+
+        if (wrapRequest == null)
+            return;
+
+        ServletRequest previous = null;
+        ServletRequest current = outerRequest;
+        while (current != null) {
+
+            // If we run into the container request we are done
+            if ((current instanceof Request)
+                || (current instanceof RequestFacade))
+                break;
+
+            // Remove the current request if it is our wrapper
+            if (current == wrapRequest) {
+                ServletRequest next =
+                  ((ServletRequestWrapper) current).getRequest();
+                if (previous == null)
+                    outerRequest = next;
+                else
+                    ((ServletRequestWrapper) previous).setRequest(next);
+                break;
+            }
+
+            // Advance to the next request in the chain
+            previous = current;
+            current = ((ServletRequestWrapper) current).getRequest();
+
+        }
+
+    }
+
+
+    /**
+     * Unwrap the response if we have wrapped it.
+     */
+    private void unwrapResponse() {
+
+        if (wrapResponse == null)
+            return;
+
+        ServletResponse previous = null;
+        ServletResponse current = outerResponse;
+        while (current != null) {
+
+            // If we run into the container response we are done
+            if ((current instanceof Response)
+                || (current instanceof ResponseFacade))
+                break;
+
+            // Remove the current response if it is our wrapper
+            if (current == wrapResponse) {
+                ServletResponse next =
+                  ((ServletResponseWrapper) current).getResponse();
+                if (previous == null)
+                    outerResponse = next;
+                else
+                    ((ServletResponseWrapper) previous).setResponse(next);
+                break;
+            }
+
+            // Advance to the next response in the chain
+            previous = current;
+            current = ((ServletResponseWrapper) current).getResponse();
+
+        }
+
+    }
+
+
+    /**
+     * Create and return a request wrapper that has been inserted in the
+     * appropriate spot in the request chain.
+     */
+    private ServletRequest wrapRequest() {
+
+        // Locate the request we should insert in front of
+        ServletRequest previous = null;
+        ServletRequest current = outerRequest;
+        while (current != null) {
+            if ("org.apache.catalina.servlets.InvokerHttpRequest".
+                equals(current.getClass().getName()))
+                break; // KLUDGE - Make nested RD.forward() using invoker work
+            if (!(current instanceof ServletRequestWrapper))
+                break;
+            if (current instanceof ApplicationHttpRequest)
+                break;
+            if (current instanceof ApplicationRequest)
+                break;
+            if (current instanceof Request)
+                break;
+            previous = current;
+            current = ((ServletRequestWrapper) current).getRequest();
+        }
+
+        // Instantiate a new wrapper at this point and insert it in the chain
+        ServletRequest wrapper = null;
+        if ((current instanceof ApplicationHttpRequest) ||
+            (current instanceof Request) ||
+            (current instanceof HttpServletRequest)) {
+            // Compute a crossContext flag
+            HttpServletRequest hcurrent = (HttpServletRequest) current;
+            boolean crossContext = false;
+            if ((outerRequest instanceof ApplicationHttpRequest) ||
+                (outerRequest instanceof Request) ||
+                (outerRequest instanceof HttpServletRequest)) {
+                HttpServletRequest houterRequest = 
+                    (HttpServletRequest) outerRequest;
+                Object contextPath = houterRequest.getAttribute
+                    (Globals.INCLUDE_CONTEXT_PATH_ATTR);
+                if (contextPath == null) {
+                    // Forward
+                    contextPath = houterRequest.getContextPath();
+                }
+                crossContext = !(context.getPath().equals(contextPath));
+            }
+            wrapper = new ApplicationHttpRequest
+                (hcurrent, context, crossContext);
+        } else {
+            wrapper = new ApplicationRequest(current);
+        }
+        if (previous == null)
+            outerRequest = wrapper;
+        else
+            ((ServletRequestWrapper) previous).setRequest(wrapper);
+        wrapRequest = wrapper;
+        return (wrapper);
+
+    }
+
+
+    /**
+     * Create and return a response wrapper that has been inserted in the
+     * appropriate spot in the response chain.
+     */
+    private ServletResponse wrapResponse() {
+
+        // Locate the response we should insert in front of
+        ServletResponse previous = null;
+        ServletResponse current = outerResponse;
+        while (current != null) {
+            if (!(current instanceof ServletResponseWrapper))
+                break;
+            if (current instanceof ApplicationHttpResponse)
+                break;
+            if (current instanceof ApplicationResponse)
+                break;
+            if (current instanceof Response)
+                break;
+            previous = current;
+            current = ((ServletResponseWrapper) current).getResponse();
+        }
+
+        // Instantiate a new wrapper at this point and insert it in the chain
+        ServletResponse wrapper = null;
+        if ((current instanceof ApplicationHttpResponse) ||
+            (current instanceof Response) ||
+            (current instanceof HttpServletResponse))
+            wrapper =
+                new ApplicationHttpResponse((HttpServletResponse) current,
+                                            including);
+        else
+            wrapper = new ApplicationResponse(current, including);
+        if (previous == null)
+            outerResponse = wrapper;
+        else
+            ((ServletResponseWrapper) previous).setResponse(wrapper);
+        wrapResponse = wrapper;
+        return (wrapper);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationFilterChain.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationFilterChain.java
new file mode 100644
index 0000000..655ee19
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationFilterChain.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.IOException;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.InstanceEvent;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.util.InstanceSupport;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * Implementation of <code>javax.servlet.FilterChain</code> used to manage
+ * the execution of a set of filters for a particular request.  When the
+ * set of defined filters has all been executed, the next call to
+ * <code>doFilter()</code> will execute the servlet's <code>service()</code>
+ * method itself.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+final class ApplicationFilterChain implements FilterChain {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final int INCREMENT = 10;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new chain instance with no defined filters.
+     */
+    public ApplicationFilterChain() {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Filters.
+     */
+    private ApplicationFilterConfig[] filters = 
+        new ApplicationFilterConfig[0];
+
+
+    /**
+     * The int which is used to maintain the current position 
+     * in the filter chain.
+     */
+    private int pos = 0;
+
+
+    /**
+     * The int which gives the current number of filters in the chain.
+     */
+    private int n = 0;
+
+
+    /**
+     * The servlet instance to be executed by this chain.
+     */
+    private Servlet servlet = null;
+
+
+    /**
+     * The string manager for our package.
+     */
+    private static final StringManager sm =
+      StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The InstanceSupport instance associated with our Wrapper (used to
+     * send "before filter" and "after filter" events.
+     */
+    private InstanceSupport support = null;
+
+    
+    /**
+     * Static class array used when the SecurityManager is turned on and 
+     * <code>doFilter</code is invoked.
+     */
+    private static Class[] classType = new Class[]{ServletRequest.class, 
+                                                   ServletResponse.class,
+                                                   FilterChain.class};
+                                                   
+    /**
+     * Static class array used when the SecurityManager is turned on and 
+     * <code>service</code is invoked.
+     */                                                 
+    private static Class[] classTypeUsedInService = new Class[]{
+                                                         ServletRequest.class,
+                                                         ServletResponse.class};
+
+    // ---------------------------------------------------- FilterChain Methods
+
+
+    /**
+     * Invoke the next filter in this chain, passing the specified request
+     * and response.  If there are no more filters in this chain, invoke
+     * the <code>service()</code> method of the servlet itself.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response)
+        throws IOException, ServletException {
+
+        if( System.getSecurityManager() != null ) {
+            final ServletRequest req = request;
+            final ServletResponse res = response;
+            try {
+                java.security.AccessController.doPrivileged(
+                    new java.security.PrivilegedExceptionAction() {
+                        public Object run() 
+                            throws ServletException, IOException {
+                            internalDoFilter(req,res);
+                            return null;
+                        }
+                    }
+                );
+            } catch( PrivilegedActionException pe) {
+                Exception e = pe.getException();
+                if (e instanceof ServletException)
+                    throw (ServletException) e;
+                else if (e instanceof IOException)
+                    throw (IOException) e;
+                else if (e instanceof RuntimeException)
+                    throw (RuntimeException) e;
+                else
+                    throw new ServletException(e.getMessage(), e);
+            }
+        } else {
+            internalDoFilter(request,response);
+        }
+    }
+
+    private void internalDoFilter(ServletRequest request, 
+                                  ServletResponse response)
+        throws IOException, ServletException {
+
+        // Call the next filter if there is one
+        if (pos < n) {
+            ApplicationFilterConfig filterConfig = filters[pos++];
+            Filter filter = null;
+            try {
+                filter = filterConfig.getFilter();
+                support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
+                                          filter, request, response);
+                
+                if( System.getSecurityManager() != null ) {
+                    final ServletRequest req = request;
+                    final ServletResponse res = response;
+                    Principal principal = 
+                        ((HttpServletRequest) req).getUserPrincipal();
+
+                    Object[] args = new Object[]{req, res, this};
+                    SecurityUtil.doAsPrivilege
+                        ("doFilter", filter, classType, args);
+                    
+                    args = null;
+                } else {  
+                    filter.doFilter(request, response, this);
+                }
+
+                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
+                                          filter, request, response);
+            } catch (IOException e) {
+                if (filter != null)
+                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
+                                              filter, request, response, e);
+                throw e;
+            } catch (ServletException e) {
+                if (filter != null)
+                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
+                                              filter, request, response, e);
+                throw e;
+            } catch (RuntimeException e) {
+                if (filter != null)
+                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
+                                              filter, request, response, e);
+                throw e;
+            } catch (Throwable e) {
+                if (filter != null)
+                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
+                                              filter, request, response, e);
+                throw new ServletException
+                  (sm.getString("filterChain.filter"), e);
+            }
+            return;
+        }
+
+        // We fell off the end of the chain -- call the servlet instance
+        try {
+            support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
+                                      servlet, request, response);
+            if ((request instanceof HttpServletRequest) &&
+                (response instanceof HttpServletResponse)) {
+                    
+                if( System.getSecurityManager() != null ) {
+                    final ServletRequest req = request;
+                    final ServletResponse res = response;
+                    Principal principal = 
+                        ((HttpServletRequest) req).getUserPrincipal();
+                    Object[] args = new Object[]{req, res};
+                    SecurityUtil.doAsPrivilege("service",
+                                               servlet,
+                                               classTypeUsedInService, 
+                                               args,
+                                               principal);   
+                    args = null;
+                } else {  
+                    servlet.service((HttpServletRequest) request,
+                                    (HttpServletResponse) response);
+                }
+            } else {
+                servlet.service(request, response);
+            }
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response);
+        } catch (IOException e) {
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response, e);
+            throw e;
+        } catch (ServletException e) {
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response, e);
+            throw e;
+        } catch (RuntimeException e) {
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response, e);
+            throw e;
+        } catch (Throwable e) {
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response, e);
+            throw new ServletException
+              (sm.getString("filterChain.servlet"), e);
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+
+    /**
+     * Add a filter to the set of filters that will be executed in this chain.
+     *
+     * @param filterConfig The FilterConfig for the servlet to be executed
+     */
+    void addFilter(ApplicationFilterConfig filterConfig) {
+
+        if (n == filters.length) {
+            ApplicationFilterConfig[] newFilters =
+                new ApplicationFilterConfig[n + INCREMENT];
+            System.arraycopy(filters, 0, newFilters, 0, n);
+            filters = newFilters;
+        }
+        filters[n++] = filterConfig;
+
+    }
+
+
+    /**
+     * Release references to the filters and wrapper executed by this chain.
+     */
+    void release() {
+
+        n = 0;
+        pos = 0;
+        servlet = null;
+        support = null;
+
+    }
+
+
+    /**
+     * Set the servlet that will be executed at the end of this chain.
+     *
+     * @param servlet The Wrapper for the servlet to be executed
+     */
+    void setServlet(Servlet servlet) {
+
+        this.servlet = servlet;
+
+    }
+
+
+    /**
+     * Set the InstanceSupport object used for event notifications
+     * for this filter chain.
+     *
+     * @param support The InstanceSupport object for our Wrapper
+     */
+    void setSupport(InstanceSupport support) {
+
+        this.support = support;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationFilterConfig.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationFilterConfig.java
new file mode 100644
index 0000000..bf6cded
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationFilterConfig.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.deploy.FilterDef;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.util.Enumerator;
+import org.apache.tomcat.util.log.SystemLogHandler;
+
+
+/**
+ * Implementation of a <code>javax.servlet.FilterConfig</code> useful in
+ * managing the filter instances instantiated when a web application
+ * is first started.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+final class ApplicationFilterConfig implements FilterConfig, Serializable {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( ApplicationFilterConfig.class );
+ 
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new ApplicationFilterConfig for the specified filter
+     * definition.
+     *
+     * @param context The context with which we are associated
+     * @param filterDef Filter definition for which a FilterConfig is to be
+     *  constructed
+     *
+     * @exception ClassCastException if the specified class does not implement
+     *  the <code>javax.servlet.Filter</code> interface
+     * @exception ClassNotFoundException if the filter class cannot be found
+     * @exception IllegalAccessException if the filter class cannot be
+     *  publicly instantiated
+     * @exception InstantiationException if an exception occurs while
+     *  instantiating the filter object
+     * @exception ServletException if thrown by the filter's init() method
+     */
+    public ApplicationFilterConfig(Context context, FilterDef filterDef)
+        throws ClassCastException, ClassNotFoundException,
+               IllegalAccessException, InstantiationException,
+               ServletException {
+
+        super();
+        this.context = context;
+        setFilterDef(filterDef);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Context with which we are associated.
+     */
+    private Context context = null;
+
+
+    /**
+     * The application Filter we are configured for.
+     */
+    private transient Filter filter = null;
+
+
+    /**
+     * The <code>FilterDef</code> that defines our associated Filter.
+     */
+    private FilterDef filterDef = null;
+
+
+    // --------------------------------------------------- FilterConfig Methods
+
+
+    /**
+     * Return the name of the filter we are configuring.
+     */
+    public String getFilterName() {
+
+        return (filterDef.getFilterName());
+
+    }
+
+
+    /**
+     * Return a <code>String</code> containing the value of the named
+     * initialization parameter, or <code>null</code> if the parameter
+     * does not exist.
+     *
+     * @param name Name of the requested initialization parameter
+     */
+    public String getInitParameter(String name) {
+
+        Map map = filterDef.getParameterMap();
+        if (map == null)
+            return (null);
+        else
+            return ((String) map.get(name));
+
+    }
+
+
+    /**
+     * Return an <code>Enumeration</code> of the names of the initialization
+     * parameters for this Filter.
+     */
+    public Enumeration getInitParameterNames() {
+
+        Map map = filterDef.getParameterMap();
+        if (map == null)
+            return (new Enumerator(new ArrayList()));
+        else
+            return (new Enumerator(map.keySet()));
+
+    }
+
+
+    /**
+     * Return the ServletContext of our associated web application.
+     */
+    public ServletContext getServletContext() {
+
+        return (this.context.getServletContext());
+
+    }
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ApplicationFilterConfig[");
+        sb.append("name=");
+        sb.append(filterDef.getFilterName());
+        sb.append(", filterClass=");
+        sb.append(filterDef.getFilterClass());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Return the application Filter we are configured for.
+     *
+     * @exception ClassCastException if the specified class does not implement
+     *  the <code>javax.servlet.Filter</code> interface
+     * @exception ClassNotFoundException if the filter class cannot be found
+     * @exception IllegalAccessException if the filter class cannot be
+     *  publicly instantiated
+     * @exception InstantiationException if an exception occurs while
+     *  instantiating the filter object
+     * @exception ServletException if thrown by the filter's init() method
+     */
+    Filter getFilter() throws ClassCastException, ClassNotFoundException,
+        IllegalAccessException, InstantiationException, ServletException {
+
+        // Return the existing filter instance, if any
+        if (this.filter != null)
+            return (this.filter);
+
+        // Identify the class loader we will be using
+        String filterClass = filterDef.getFilterClass();
+        ClassLoader classLoader = null;
+        if (filterClass.startsWith("org.apache.catalina."))
+            classLoader = this.getClass().getClassLoader();
+        else
+            classLoader = context.getLoader().getClassLoader();
+
+        ClassLoader oldCtxClassLoader =
+            Thread.currentThread().getContextClassLoader();
+
+        // Instantiate a new instance of this filter and return it
+        Class clazz = classLoader.loadClass(filterClass);
+        this.filter = (Filter) clazz.newInstance();
+        if (context instanceof StandardContext &&
+            ((StandardContext)context).getSwallowOutput()) {
+            try {
+                SystemLogHandler.startCapture();
+                filter.init(this);
+            } finally {
+                String log = SystemLogHandler.stopCapture();
+                if (log != null && log.length() > 0) {
+                    getServletContext().log(log);
+                }
+            }
+        } else {
+            filter.init(this);
+        }
+        return (this.filter);
+
+    }
+
+
+    /**
+     * Return the filter definition we are configured for.
+     */
+    FilterDef getFilterDef() {
+
+        return (this.filterDef);
+
+    }
+
+
+    /**
+     * Release the Filter instance associated with this FilterConfig,
+     * if there is one.
+     */
+    void release() {
+
+        if (this.filter != null){
+             if( System.getSecurityManager() != null) {
+                try{
+                    SecurityUtil.doAsPrivilege("destroy",
+                                               filter); 
+                    SecurityUtil.remove(filter);
+                } catch(java.lang.Exception ex){                    
+                    log.error("ApplicationFilterConfig.doAsPrivilege", ex);
+                }
+            } else { 
+                filter.destroy();
+            }
+        }
+        this.filter = null;
+
+     }
+
+
+    /**
+     * Set the filter definition we are configured for.  This has the side
+     * effect of instantiating an instance of the corresponding filter class.
+     *
+     * @param filterDef The new filter definition
+     *
+     * @exception ClassCastException if the specified class does not implement
+     *  the <code>javax.servlet.Filter</code> interface
+     * @exception ClassNotFoundException if the filter class cannot be found
+     * @exception IllegalAccessException if the filter class cannot be
+     *  publicly instantiated
+     * @exception InstantiationException if an exception occurs while
+     *  instantiating the filter object
+     * @exception ServletException if thrown by the filter's init() method
+     */
+    void setFilterDef(FilterDef filterDef)
+        throws ClassCastException, ClassNotFoundException,
+               IllegalAccessException, InstantiationException,
+               ServletException {
+
+        this.filterDef = filterDef;
+        if (filterDef == null) {
+
+            // Release any previously allocated filter instance
+            if (this.filter != null){
+                 if( System.getSecurityManager() != null) {
+                    try{
+                        SecurityUtil.doAsPrivilege("destroy",
+                                                   filter);  
+                        SecurityUtil.remove(filter);
+                    } catch(java.lang.Exception ex){    
+                        log.error("ApplicationFilterConfig.doAsPrivilege", ex);
+                    }
+                } else { 
+                    filter.destroy();
+                }
+            }
+            this.filter = null;
+
+        } else {
+
+            // Allocate a new filter instance
+            Filter filter = getFilter();
+
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationFilterFactory.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationFilterFactory.java
new file mode 100644
index 0000000..e8ace84
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationFilterFactory.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.deploy.FilterMap;
+
+/**
+ * Factory for the creation and caching of Filters and creationg 
+ * of Filter Chains.
+ *
+ * @author Greg Murray
+ * @author Remy Maucherat
+ * @version $Revision: 1.0
+ */
+
+public final class ApplicationFilterFactory {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final int ERROR = 1;
+    public static final Integer ERROR_INTEGER = new Integer(ERROR);
+    public static final int FORWARD = 2;
+    public static final Integer FORWARD_INTEGER = new Integer(FORWARD);
+    public static final int INCLUDE = 4;
+    public static final Integer INCLUDE_INTEGER = new Integer(INCLUDE);
+    public static final int REQUEST = 8;
+    public static final Integer REQUEST_INTEGER = new Integer(REQUEST);
+
+    public static final String DISPATCHER_TYPE_ATTR = 
+        Globals.DISPATCHER_TYPE_ATTR;
+    public static final String DISPATCHER_REQUEST_PATH_ATTR = 
+        Globals.DISPATCHER_REQUEST_PATH_ATTR;
+
+    private static final SecurityManager securityManager = 
+        System.getSecurityManager();
+
+    private static ApplicationFilterFactory factory = null;;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /*
+     * Prevent instanciation outside of the getInstanceMethod().
+     */
+    private ApplicationFilterFactory() {
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the fqctory instance.
+     */
+    public static ApplicationFilterFactory getInstance() {
+        if (factory == null) {
+            factory = new ApplicationFilterFactory();
+        }
+        return factory;
+    }
+
+
+    /**
+     * Construct and return a FilterChain implementation that will wrap the
+     * execution of the specified servlet instance.  If we should not execute
+     * a filter chain at all, return <code>null</code>.
+     *
+     * @param request The servlet request we are processing
+     * @param servlet The servlet instance to be wrapped
+     */
+    public ApplicationFilterChain createFilterChain
+        (ServletRequest request, Wrapper wrapper, Servlet servlet) {
+
+        // get the dispatcher type
+        int dispatcher = -1; 
+        if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) {
+            Integer dispatcherInt = 
+                (Integer) request.getAttribute(DISPATCHER_TYPE_ATTR);
+            dispatcher = dispatcherInt.intValue();
+        }
+        String requestPath = null;
+        Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR);
+        
+        if (attribute != null){
+            requestPath = attribute.toString();
+        }
+        
+        HttpServletRequest hreq = null;
+        if (request instanceof HttpServletRequest) 
+            hreq = (HttpServletRequest)request;
+        // If there is no servlet to execute, return null
+        if (servlet == null)
+            return (null);
+
+        // Create and initialize a filter chain object
+        ApplicationFilterChain filterChain = null;
+        if ((securityManager == null) && (request instanceof Request)) {
+            Request req = (Request) request;
+            filterChain = (ApplicationFilterChain) req.getFilterChain();
+            if (filterChain == null) {
+                filterChain = new ApplicationFilterChain();
+                req.setFilterChain(filterChain);
+            }
+        } else {
+            // Security: Do not recycle
+            filterChain = new ApplicationFilterChain();
+        }
+
+        filterChain.setServlet(servlet);
+
+        filterChain.setSupport
+            (((StandardWrapper)wrapper).getInstanceSupport());
+
+        // Acquire the filter mappings for this Context
+        StandardContext context = (StandardContext) wrapper.getParent();
+        FilterMap filterMaps[] = context.findFilterMaps();
+
+        // If there are no filter mappings, we are done
+        if ((filterMaps == null) || (filterMaps.length == 0))
+            return (filterChain);
+
+        // Acquire the information we will need to match filter mappings
+        String servletName = wrapper.getName();
+
+        int n = 0;
+
+        // Add the relevant path-mapped filters to this filter chain
+        for (int i = 0; i < filterMaps.length; i++) {
+            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
+                continue;
+            }
+            if (!matchFiltersURL(filterMaps[i], requestPath))
+                continue;
+            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
+                context.findFilterConfig(filterMaps[i].getFilterName());
+            if (filterConfig == null) {
+                ;       // FIXME - log configuration problem
+                continue;
+            }
+            filterChain.addFilter(filterConfig);
+            n++;
+        }
+
+        // Add filters that match on servlet name second
+        for (int i = 0; i < filterMaps.length; i++) {
+            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
+                continue;
+            }
+            if (!matchFiltersServlet(filterMaps[i], servletName))
+                continue;
+            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
+                context.findFilterConfig(filterMaps[i].getFilterName());
+            if (filterConfig == null) {
+                ;       // FIXME - log configuration problem
+                continue;
+            }
+            filterChain.addFilter(filterConfig);
+            n++;
+        }
+
+        // Return the completed filter chain
+        return (filterChain);
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Return <code>true</code> if the context-relative request path
+     * matches the requirements of the specified filter mapping;
+     * otherwise, return <code>null</code>.
+     *
+     * @param filterMap Filter mapping being checked
+     * @param requestPath Context-relative request path of this request
+     */
+    private boolean matchFiltersURL(FilterMap filterMap, String requestPath) {
+
+        if (requestPath == null)
+            return (false);
+
+        // Match on context relative request path
+        String testPath = filterMap.getURLPattern();
+        if (testPath == null)
+            return (false);
+
+        // Case 1 - Exact Match
+        if (testPath.equals(requestPath))
+            return (true);
+
+        // Case 2 - Path Match ("/.../*")
+        if (testPath.equals("/*"))
+            return (true);
+        if (testPath.endsWith("/*")) {
+            if (testPath.regionMatches(0, requestPath, 0, 
+                                       testPath.length() - 2)) {
+                if (requestPath.length() == (testPath.length() - 2)) {
+                    return (true);
+                } else if ('/' == requestPath.charAt(testPath.length() - 2)) {
+                    return (true);
+                }
+            }
+            return (false);
+        }
+
+        // Case 3 - Extension Match
+        if (testPath.startsWith("*.")) {
+            int slash = requestPath.lastIndexOf('/');
+            int period = requestPath.lastIndexOf('.');
+            if ((slash >= 0) && (period > slash) 
+                && (period != requestPath.length() - 1)
+                && ((requestPath.length() - period) 
+                    == (testPath.length() - 1))) {
+                return (testPath.regionMatches(2, requestPath, period + 1,
+                                               testPath.length() - 2));
+            }
+        }
+
+        // Case 4 - "Default" Match
+        return (false); // NOTE - Not relevant for selecting filters
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified servlet name matches
+     * the requirements of the specified filter mapping; otherwise
+     * return <code>false</code>.
+     *
+     * @param filterMap Filter mapping being checked
+     * @param servletName Servlet name being checked
+     */
+    private boolean matchFiltersServlet(FilterMap filterMap, 
+                                        String servletName) {
+
+        if (servletName == null) {
+            return (false);
+        } else {
+            if (servletName.equals(filterMap.getServletName())) {
+                return (true);
+            } else {
+                return false;
+            }
+        }
+
+    }
+
+
+    /**
+     * Convienience method which returns true if  the dispatcher type
+     * matches the dispatcher types specified in the FilterMap
+     */
+    private boolean matchDispatcher(FilterMap filterMap, int dispatcher) {
+        switch (dispatcher) {
+            case FORWARD : {
+                if (filterMap.getDispatcherMapping() == FilterMap.FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR ||
+                    filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) {
+                        return true;
+                }
+                break;
+            }
+            case INCLUDE : {
+                if (filterMap.getDispatcherMapping() == FilterMap.INCLUDE ||
+                    filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR ||
+                    filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) {
+                        return true;
+                }
+                break;
+            }
+            case REQUEST : {
+                if (filterMap.getDispatcherMapping() == FilterMap.REQUEST ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE) {
+                        return true;
+                }
+                break;
+            }
+            case ERROR : {
+                if (filterMap.getDispatcherMapping() == FilterMap.ERROR ||
+                    filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR || 
+                    filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR || 
+                    filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD || 
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE ||
+                    filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE) {
+                        return true;
+                }
+                break;
+            }
+        }
+        return false;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationHttpRequest.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationHttpRequest.java
new file mode 100644
index 0000000..13515cf
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationHttpRequest.java
@@ -0,0 +1,950 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Session;
+import org.apache.catalina.Manager;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.http.HttpServletRequest</code>
+ * that transforms an application request object (which might be the original
+ * one passed to a servlet, or might be based on the 2.3
+ * <code>javax.servlet.http.HttpServletRequestWrapper</code> class)
+ * back into an internal <code>org.apache.catalina.HttpRequest</code>.
+ * <p>
+ * <strong>WARNING</strong>:  Due to Java's lack of support for multiple
+ * inheritance, all of the logic in <code>ApplicationRequest</code> is
+ * duplicated in <code>ApplicationHttpRequest</code>.  Make sure that you
+ * keep these two classes in synchronization when making changes!
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+class ApplicationHttpRequest extends HttpServletRequestWrapper {
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The set of attribute names that are special for request dispatchers.
+     */
+    protected static final String specials[] =
+    { Globals.INCLUDE_REQUEST_URI_ATTR, Globals.INCLUDE_CONTEXT_PATH_ATTR,
+      Globals.INCLUDE_SERVLET_PATH_ATTR, Globals.INCLUDE_PATH_INFO_ATTR,
+      Globals.INCLUDE_QUERY_STRING_ATTR, Globals.FORWARD_REQUEST_URI_ATTR, 
+      Globals.FORWARD_CONTEXT_PATH_ATTR, Globals.FORWARD_SERVLET_PATH_ATTR, 
+      Globals.FORWARD_PATH_INFO_ATTR, Globals.FORWARD_QUERY_STRING_ATTR };
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new wrapped request around the specified servlet request.
+     *
+     * @param request The servlet request being wrapped
+     */
+    public ApplicationHttpRequest(HttpServletRequest request, Context context,
+                                  boolean crossContext) {
+
+        super(request);
+        this.context = context;
+        this.crossContext = crossContext;
+        setRequest(request);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The context for this request.
+     */
+    protected Context context = null;
+
+
+    /**
+     * The context path for this request.
+     */
+    protected String contextPath = null;
+
+
+    /**
+     * If this request is cross context, since this changes session accesss
+     * behavior.
+     */
+    protected boolean crossContext = false;
+
+
+    /**
+     * The current dispatcher type.
+     */
+    protected Object dispatcherType = null;
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.core.ApplicationHttpRequest/1.0";
+
+
+    /**
+     * The request parameters for this request.  This is initialized from the
+     * wrapped request, but updates are allowed.
+     */
+    protected Map parameters = null;
+
+
+    /**
+     * Have the parameters for this request already been parsed?
+     */
+    private boolean parsedParams = false;
+
+
+    /**
+     * The path information for this request.
+     */
+    protected String pathInfo = null;
+
+
+    /**
+     * The query parameters for the current request.
+     */
+    private String queryParamString = null;
+
+
+    /**
+     * The query string for this request.
+     */
+    protected String queryString = null;
+
+
+    /**
+     * The current request dispatcher path.
+     */
+    protected Object requestDispatcherPath = null;
+
+
+    /**
+     * The request URI for this request.
+     */
+    protected String requestURI = null;
+
+
+    /**
+     * The servlet path for this request.
+     */
+    protected String servletPath = null;
+
+
+    /**
+     * The currently active session for this request.
+     */
+    protected Session session = null;
+
+
+    /**
+     * Special attributes.
+     */
+    protected Object[] specialAttributes = new Object[specials.length];
+
+
+    // ------------------------------------------------- ServletRequest Methods
+
+
+    /**
+     * Override the <code>getAttribute()</code> method of the wrapped request.
+     *
+     * @param name Name of the attribute to retrieve
+     */
+    public Object getAttribute(String name) {
+
+        if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
+            return dispatcherType;
+        } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
+            if ( requestDispatcherPath != null ){
+                return requestDispatcherPath.toString();
+            } else {
+                return null;   
+            }
+        }
+
+        int pos = getSpecial(name);
+        if (pos == -1) {
+            return getRequest().getAttribute(name);
+        } else {
+            if ((specialAttributes[pos] == null) 
+                && (specialAttributes[5] == null) && (pos >= 5)) {
+                // If it's a forward special attribute, and null, it means this
+                // is an include, so we check the wrapped request since 
+                // the request could have been forwarded before the include
+                return getRequest().getAttribute(name);
+            } else {
+                return specialAttributes[pos];
+            }
+        }
+
+    }
+
+
+    /**
+     * Override the <code>getAttributeNames()</code> method of the wrapped
+     * request.
+     */
+    public Enumeration getAttributeNames() {
+        return (new AttributeNamesEnumerator());
+    }
+
+
+    /**
+     * Override the <code>removeAttribute()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the attribute to remove
+     */
+    public void removeAttribute(String name) {
+
+        if (!removeSpecial(name))
+            getRequest().removeAttribute(name);
+
+    }
+
+
+    /**
+     * Override the <code>setAttribute()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the attribute to set
+     * @param value Value of the attribute to set
+     */
+    public void setAttribute(String name, Object value) {
+
+        if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
+            dispatcherType = value;
+            return;
+        } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
+            requestDispatcherPath = value;
+            return;
+        }
+
+        if (!setSpecial(name, value)) {
+            getRequest().setAttribute(name, value);
+        }
+
+    }
+
+
+    /**
+     * Return a RequestDispatcher that wraps the resource at the specified
+     * path, which may be interpreted as relative to the current request path.
+     *
+     * @param path Path of the resource to be wrapped
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        if (context == null)
+            return (null);
+
+        // If the path is already context-relative, just pass it through
+        if (path == null)
+            return (null);
+        else if (path.startsWith("/"))
+            return (context.getServletContext().getRequestDispatcher(path));
+
+        // Convert a request-relative path to a context-relative one
+        String servletPath = 
+            (String) getAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR);
+        if (servletPath == null)
+            servletPath = getServletPath();
+
+        // Add the path info, if there is any
+        String pathInfo = getPathInfo();
+        String requestPath = null;
+
+        if (pathInfo == null) {
+            requestPath = servletPath;
+        } else {
+            requestPath = servletPath + pathInfo;
+        }
+
+        int pos = requestPath.lastIndexOf('/');
+        String relative = null;
+        if (pos >= 0) {
+            relative = RequestUtil.normalize
+                (requestPath.substring(0, pos + 1) + path);
+        } else {
+            relative = RequestUtil.normalize(requestPath + path);
+        }
+
+        return (context.getServletContext().getRequestDispatcher(relative));
+
+    }
+
+
+    // --------------------------------------------- HttpServletRequest Methods
+
+
+    /**
+     * Override the <code>getContextPath()</code> method of the wrapped
+     * request.
+     */
+    public String getContextPath() {
+
+        return (this.contextPath);
+
+    }
+
+
+    /**
+     * Override the <code>getParameter()</code> method of the wrapped request.
+     *
+     * @param name Name of the requested parameter
+     */
+    public String getParameter(String name) {
+
+	parseParameters();
+
+        Object value = parameters.get(name);
+        if (value == null)
+            return (null);
+        else if (value instanceof String[])
+            return (((String[]) value)[0]);
+        else if (value instanceof String)
+            return ((String) value);
+        else
+            return (value.toString());
+
+    }
+
+
+    /**
+     * Override the <code>getParameterMap()</code> method of the
+     * wrapped request.
+     */
+    public Map getParameterMap() {
+
+	parseParameters();
+        return (parameters);
+
+    }
+
+
+    /**
+     * Override the <code>getParameterNames()</code> method of the
+     * wrapped request.
+     */
+    public Enumeration getParameterNames() {
+
+	parseParameters();
+        return (new Enumerator(parameters.keySet()));
+
+    }
+
+
+    /**
+     * Override the <code>getParameterValues()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the requested parameter
+     */
+    public String[] getParameterValues(String name) {
+
+	parseParameters();
+        Object value = parameters.get(name);
+        if (value == null)
+            return ((String[]) null);
+        else if (value instanceof String[])
+            return ((String[]) value);
+        else if (value instanceof String) {
+            String values[] = new String[1];
+            values[0] = (String) value;
+            return (values);
+        } else {
+            String values[] = new String[1];
+            values[0] = value.toString();
+            return (values);
+        }
+
+    }
+
+
+    /**
+     * Override the <code>getPathInfo()</code> method of the wrapped request.
+     */
+    public String getPathInfo() {
+
+        return (this.pathInfo);
+
+    }
+
+
+    /**
+     * Override the <code>getQueryString()</code> method of the wrapped
+     * request.
+     */
+    public String getQueryString() {
+
+        return (this.queryString);
+
+    }
+
+
+    /**
+     * Override the <code>getRequestURI()</code> method of the wrapped
+     * request.
+     */
+    public String getRequestURI() {
+
+        return (this.requestURI);
+
+    }
+
+
+    /**
+     * Override the <code>getRequestURL()</code> method of the wrapped
+     * request.
+     */
+    public StringBuffer getRequestURL() {
+
+        StringBuffer url = new StringBuffer();
+        String scheme = getScheme();
+        int port = getServerPort();
+        if (port < 0)
+            port = 80; // Work around java.net.URL bug
+
+        url.append(scheme);
+        url.append("://");
+        url.append(getServerName());
+        if ((scheme.equals("http") && (port != 80))
+            || (scheme.equals("https") && (port != 443))) {
+            url.append(':');
+            url.append(port);
+        }
+        url.append(getRequestURI());
+
+        return (url);
+
+    }
+
+
+    /**
+     * Override the <code>getServletPath()</code> method of the wrapped
+     * request.
+     */
+    public String getServletPath() {
+
+        return (this.servletPath);
+
+    }
+
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary.
+     */
+    public HttpSession getSession() {
+        return (getSession(true));
+    }
+
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary and requested.
+     *
+     * @param create Create a new session if one does not exist
+     */
+    public HttpSession getSession(boolean create) {
+
+        if (crossContext) {
+            
+            // There cannot be a session if no context has been assigned yet
+            if (context == null)
+                return (null);
+
+            // Return the current session if it exists and is valid
+            if (session != null && session.isValid()) {
+                return (session.getSession());
+            }
+
+            HttpSession other = super.getSession(false);
+            if (create && (other == null)) {
+                // First create a session in the first context: the problem is
+                // that the top level request is the only one which can 
+                // create the cookie safely
+                other = super.getSession(true);
+            }
+            if (other != null) {
+                Session localSession = null;
+                try {
+                    localSession =
+                        context.getManager().findSession(other.getId());
+                } catch (IOException e) {
+                    // Ignore
+                }
+                if (localSession == null && create) {
+                    localSession = 
+                        context.getManager().createSession(other.getId());
+                }
+                if (localSession != null) {
+                    localSession.access();
+                    session = localSession;
+                    return session.getSession();
+                }
+            }
+            return null;
+
+        } else {
+            return super.getSession(create);
+        }
+
+    }
+
+
+    /**
+     * Returns true if the request specifies a JSESSIONID that is valid within
+     * the context of this ApplicationHttpRequest, false otherwise.
+     *
+     * @return true if the request specifies a JSESSIONID that is valid within
+     * the context of this ApplicationHttpRequest, false otherwise.
+     */
+    public boolean isRequestedSessionIdValid() {
+
+        if (crossContext) {
+
+            String requestedSessionId = getRequestedSessionId();
+            if (requestedSessionId == null)
+                return (false);
+            if (context == null)
+                return (false);
+            Manager manager = context.getManager();
+            if (manager == null)
+                return (false);
+            Session session = null;
+            try {
+                session = manager.findSession(requestedSessionId);
+            } catch (IOException e) {
+                session = null;
+            }
+            if ((session != null) && session.isValid()) {
+                return (true);
+            } else {
+                return (false);
+            }
+
+        } else {
+            return super.isRequestedSessionIdValid();
+        }
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Recycle this request
+     */
+    public void recycle() {
+        if (session != null) {
+            session.endAccess();
+        }
+    }
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Perform a shallow copy of the specified Map, and return the result.
+     *
+     * @param orig Origin Map to be copied
+     */
+    Map copyMap(Map orig) {
+
+        if (orig == null)
+            return (new HashMap());
+        HashMap dest = new HashMap();
+        Iterator keys = orig.keySet().iterator();
+        while (keys.hasNext()) {
+            String key = (String) keys.next();
+            dest.put(key, orig.get(key));
+        }
+        return (dest);
+
+    }
+
+
+    /**
+     * Set the context path for this request.
+     *
+     * @param contextPath The new context path
+     */
+    void setContextPath(String contextPath) {
+
+        this.contextPath = contextPath;
+
+    }
+
+
+    /**
+     * Set the path information for this request.
+     *
+     * @param pathInfo The new path info
+     */
+    void setPathInfo(String pathInfo) {
+
+        this.pathInfo = pathInfo;
+
+    }
+
+
+    /**
+     * Set the query string for this request.
+     *
+     * @param queryString The new query string
+     */
+    void setQueryString(String queryString) {
+
+        this.queryString = queryString;
+
+    }
+
+
+    /**
+     * Set the request that we are wrapping.
+     *
+     * @param request The new wrapped request
+     */
+    void setRequest(HttpServletRequest request) {
+
+        super.setRequest(request);
+
+        // Initialize the attributes for this request
+        dispatcherType = request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
+        requestDispatcherPath = 
+            request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
+
+        // Initialize the path elements for this request
+        contextPath = request.getContextPath();
+        pathInfo = request.getPathInfo();
+        queryString = request.getQueryString();
+        requestURI = request.getRequestURI();
+        servletPath = request.getServletPath();
+
+    }
+
+
+    /**
+     * Set the request URI for this request.
+     *
+     * @param requestURI The new request URI
+     */
+    void setRequestURI(String requestURI) {
+
+        this.requestURI = requestURI;
+
+    }
+
+
+    /**
+     * Set the servlet path for this request.
+     *
+     * @param servletPath The new servlet path
+     */
+    void setServletPath(String servletPath) {
+
+        this.servletPath = servletPath;
+
+    }
+
+
+    /**
+     * Parses the parameters of this request.
+     *
+     * If parameters are present in both the query string and the request
+     * content, they are merged.
+     */
+    void parseParameters() {
+
+	if (parsedParams) {
+	    return;
+	}
+
+        parameters = new HashMap();
+        parameters = copyMap(getRequest().getParameterMap());
+        mergeParameters();
+        parsedParams = true;
+    }
+
+
+    /**
+     * Save query parameters for this request.
+     *
+     * @param queryString The query string containing parameters for this
+     *                    request
+     */
+    void setQueryParams(String queryString) {
+        this.queryParamString = queryString;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Is this attribute name one of the special ones that is added only for
+     * included servlets?
+     *
+     * @param name Attribute name to be tested
+     */
+    protected boolean isSpecial(String name) {
+
+        for (int i = 0; i < specials.length; i++) {
+            if (specials[i].equals(name))
+                return (true);
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Get a special attribute.
+     *
+     * @return the special attribute pos, or -1 if it is not a special 
+     *         attribute
+     */
+    protected int getSpecial(String name) {
+        for (int i = 0; i < specials.length; i++) {
+            if (specials[i].equals(name)) {
+                return (i);
+            }
+        }
+        return (-1);
+    }
+
+
+    /**
+     * Set a special attribute.
+     * 
+     * @return true if the attribute was a special attribute, false otherwise
+     */
+    protected boolean setSpecial(String name, Object value) {
+        for (int i = 0; i < specials.length; i++) {
+            if (specials[i].equals(name)) {
+                specialAttributes[i] = value;
+                return (true);
+            }
+        }
+        return (false);
+    }
+
+
+    /**
+     * Remove a special attribute.
+     * 
+     * @return true if the attribute was a special attribute, false otherwise
+     */
+    protected boolean removeSpecial(String name) {
+        for (int i = 0; i < specials.length; i++) {
+            if (specials[i].equals(name)) {
+                specialAttributes[i] = null;
+                return (true);
+            }
+        }
+        return (false);
+    }
+
+
+    /**
+     * Merge the two sets of parameter values into a single String array.
+     *
+     * @param values1 First set of values
+     * @param values2 Second set of values
+     */
+    protected String[] mergeValues(Object values1, Object values2) {
+
+        ArrayList results = new ArrayList();
+
+        if (values1 == null)
+            ;
+        else if (values1 instanceof String)
+            results.add(values1);
+        else if (values1 instanceof String[]) {
+            String values[] = (String[]) values1;
+            for (int i = 0; i < values.length; i++)
+                results.add(values[i]);
+        } else
+            results.add(values1.toString());
+
+        if (values2 == null)
+            ;
+        else if (values2 instanceof String)
+            results.add(values2);
+        else if (values2 instanceof String[]) {
+            String values[] = (String[]) values2;
+            for (int i = 0; i < values.length; i++)
+                results.add(values[i]);
+        } else
+            results.add(values2.toString());
+
+        String values[] = new String[results.size()];
+        return ((String[]) results.toArray(values));
+
+    }
+
+
+    // ------------------------------------------------------ Private Methods
+
+
+    /**
+     * Merge the parameters from the saved query parameter string (if any), and
+     * the parameters already present on this request (if any), such that the
+     * parameter values from the query string show up first if there are
+     * duplicate parameter names.
+     */
+    private void mergeParameters() {
+
+        if ((queryParamString == null) || (queryParamString.length() < 1))
+            return;
+
+        HashMap queryParameters = new HashMap();
+        String encoding = getCharacterEncoding();
+        if (encoding == null)
+            encoding = "ISO-8859-1";
+        try {
+            RequestUtil.parseParameters
+                (queryParameters, queryParamString, encoding);
+        } catch (Exception e) {
+            ;
+        }
+        Iterator keys = parameters.keySet().iterator();
+        while (keys.hasNext()) {
+            String key = (String) keys.next();
+            Object value = queryParameters.get(key);
+            if (value == null) {
+                queryParameters.put(key, parameters.get(key));
+                continue;
+            }
+            queryParameters.put
+                (key, mergeValues(value, parameters.get(key)));
+        }
+        parameters = queryParameters;
+
+    }
+
+
+    // ----------------------------------- AttributeNamesEnumerator Inner Class
+
+
+    /**
+     * Utility class used to expose the special attributes as being available
+     * as request attributes.
+     */
+    protected class AttributeNamesEnumerator implements Enumeration {
+
+        protected int pos = -1;
+        protected int last = -1;
+        protected Enumeration parentEnumeration = null;
+        protected String next = null;
+
+        public AttributeNamesEnumerator() {
+            parentEnumeration = getRequest().getAttributeNames();
+            for (int i = 0; i < specialAttributes.length; i++) {
+                if (getAttribute(specials[i]) != null) {
+                    last = i;
+                }
+            }
+        }
+
+        public boolean hasMoreElements() {
+            return ((pos != last) || (next != null) 
+                    || ((next = findNext()) != null));
+        }
+
+        public Object nextElement() {
+            if (pos != last) {
+                for (int i = pos + 1; i <= last; i++) {
+                    if (getAttribute(specials[i]) != null) {
+                        pos = i;
+                        return (specials[i]);
+                    }
+                }
+            }
+            String result = next;
+            if (next != null) {
+                next = findNext();
+            } else {
+                throw new NoSuchElementException();
+            }
+            return result;
+        }
+
+        protected String findNext() {
+            String result = null;
+            while ((result == null) && (parentEnumeration.hasMoreElements())) {
+                String current = (String) parentEnumeration.nextElement();
+                if (!isSpecial(current)) {
+                    result = current;
+                }
+            }
+            return result;
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationHttpResponse.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationHttpResponse.java
new file mode 100644
index 0000000..18da9b3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationHttpResponse.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.IOException;
+import java.util.Locale;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.http.HttpServletResponse</code>
+ * that transforms an application response object (which might be the original
+ * one passed to a servlet, or might be based on the 2.3
+ * <code>javax.servlet.http.HttpServletResponseWrapper</code> class)
+ * back into an internal <code>org.apache.catalina.HttpResponse</code>.
+ * <p>
+ * <strong>WARNING</strong>:  Due to Java's lack of support for multiple
+ * inheritance, all of the logic in <code>ApplicationResponse</code> is
+ * duplicated in <code>ApplicationHttpResponse</code>.  Make sure that you
+ * keep these two classes in synchronization when making changes!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+class ApplicationHttpResponse extends HttpServletResponseWrapper {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new wrapped response around the specified servlet response.
+     *
+     * @param response The servlet response being wrapped
+     */
+    public ApplicationHttpResponse(HttpServletResponse response) {
+
+        this(response, false);
+
+    }
+
+
+    /**
+     * Construct a new wrapped response around the specified servlet response.
+     *
+     * @param response The servlet response being wrapped
+     * @param included <code>true</code> if this response is being processed
+     *  by a <code>RequestDispatcher.include()</code> call
+     */
+    public ApplicationHttpResponse(HttpServletResponse response,
+                                   boolean included) {
+
+        super(response);
+        setIncluded(included);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Is this wrapped response the subject of an <code>include()</code>
+     * call?
+     */
+    protected boolean included = false;
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.core.ApplicationHttpResponse/1.0";
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    /**
+     * Disallow <code>reset()</code> calls on a included response.
+     *
+     * @exception IllegalStateException if the response has already
+     *  been committed
+     */
+    public void reset() {
+
+        // If already committed, the wrapped response will throw ISE
+        if (!included || getResponse().isCommitted())
+            getResponse().reset();
+
+    }
+
+
+    /**
+     * Disallow <code>setContentLength()</code> calls on an included response.
+     *
+     * @param len The new content length
+     */
+    public void setContentLength(int len) {
+
+        if (!included)
+            getResponse().setContentLength(len);
+
+    }
+
+
+    /**
+     * Disallow <code>setContentType()</code> calls on an included response.
+     *
+     * @param type The new content type
+     */
+    public void setContentType(String type) {
+
+        if (!included)
+            getResponse().setContentType(type);
+
+    }
+
+
+    /**
+     * Disallow <code>setLocale()</code> calls on an included response.
+     *
+     * @param loc The new locale
+     */
+    public void setLocale(Locale loc) {
+
+        if (!included)
+            getResponse().setLocale(loc);
+
+    }
+
+
+    /**
+     * Ignore <code>setBufferSize()</code> calls on an included response.
+     *
+     * @param size The buffer size
+     */
+    public void setBufferSize(int size) {
+        if (!included)
+            getResponse().setBufferSize(size);
+    }
+
+
+    // -------------------------------------------- HttpServletResponse Methods
+
+
+    /**
+     * Disallow <code>addCookie()</code> calls on an included response.
+     *
+     * @param cookie The new cookie
+     */
+    public void addCookie(Cookie cookie) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).addCookie(cookie);
+
+    }
+
+
+    /**
+     * Disallow <code>addDateHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void addDateHeader(String name, long value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).addDateHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>addHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void addHeader(String name, String value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).addHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>addIntHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void addIntHeader(String name, int value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).addIntHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>sendError()</code> calls on an included response.
+     *
+     * @param sc The new status code
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendError(int sc) throws IOException {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).sendError(sc);
+
+    }
+
+
+    /**
+     * Disallow <code>sendError()</code> calls on an included response.
+     *
+     * @param sc The new status code
+     * @param msg The new message
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendError(int sc, String msg) throws IOException {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).sendError(sc, msg);
+
+    }
+
+
+    /**
+     * Disallow <code>sendRedirect()</code> calls on an included response.
+     *
+     * @param location The new location
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendRedirect(String location) throws IOException {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).sendRedirect(location);
+
+    }
+
+
+    /**
+     * Disallow <code>setDateHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void setDateHeader(String name, long value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setDateHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>setHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void setHeader(String name, String value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>setIntHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void setIntHeader(String name, int value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setIntHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>setStatus()</code> calls on an included response.
+     *
+     * @param sc The new status code
+     */
+    public void setStatus(int sc) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setStatus(sc);
+
+    }
+
+
+    /**
+     * Disallow <code>setStatus()</code> calls on an included response.
+     *
+     * @param sc The new status code
+     * @param msg The new message
+     */
+    public void setStatus(int sc, String msg) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setStatus(sc, msg);
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the included flag for this response.
+     */
+    boolean isIncluded() {
+
+        return (this.included);
+
+    }
+
+
+    /**
+     * Set the included flag for this response.
+     *
+     * @param included The new included flag
+     */
+    void setIncluded(boolean included) {
+
+        this.included = included;
+
+    }
+
+
+    /**
+     * Set the response that we are wrapping.
+     *
+     * @param response The new wrapped response
+     */
+    void setResponse(HttpServletResponse response) {
+
+        super.setResponse(response);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationRequest.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationRequest.java
new file mode 100644
index 0000000..1db1bde
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationRequest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.util.Enumeration;
+import java.util.HashMap;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.ServletRequest</code>
+ * that transforms an application request object (which might be the original
+ * one passed to a servlet, or might be based on the 2.3
+ * <code>javax.servlet.ServletRequestWrapper</code> class)
+ * back into an internal <code>org.apache.catalina.Request</code>.
+ * <p>
+ * <strong>WARNING</strong>:  Due to Java's lack of support for multiple
+ * inheritance, all of the logic in <code>ApplicationRequest</code> is
+ * duplicated in <code>ApplicationHttpRequest</code>.  Make sure that you
+ * keep these two classes in synchronization when making changes!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+class ApplicationRequest extends ServletRequestWrapper {
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The set of attribute names that are special for request dispatchers.
+     */
+    protected static final String specials[] =
+    { Globals.INCLUDE_REQUEST_URI_ATTR, Globals.INCLUDE_CONTEXT_PATH_ATTR,
+      Globals.INCLUDE_SERVLET_PATH_ATTR, Globals.INCLUDE_PATH_INFO_ATTR,
+      Globals.INCLUDE_QUERY_STRING_ATTR, Globals.FORWARD_REQUEST_URI_ATTR, 
+      Globals.FORWARD_CONTEXT_PATH_ATTR, Globals.FORWARD_SERVLET_PATH_ATTR, 
+      Globals.FORWARD_PATH_INFO_ATTR, Globals.FORWARD_QUERY_STRING_ATTR };
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new wrapped request around the specified servlet request.
+     *
+     * @param request The servlet request being wrapped
+     */
+    public ApplicationRequest(ServletRequest request) {
+
+        super(request);
+        setRequest(request);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The request attributes for this request.  This is initialized from the
+     * wrapped request, but updates are allowed.
+     */
+    protected HashMap attributes = new HashMap();
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------- ServletRequest Methods
+
+
+    /**
+     * Override the <code>getAttribute()</code> method of the wrapped request.
+     *
+     * @param name Name of the attribute to retrieve
+     */
+    public Object getAttribute(String name) {
+
+        synchronized (attributes) {
+            return (attributes.get(name));
+        }
+
+    }
+
+
+    /**
+     * Override the <code>getAttributeNames()</code> method of the wrapped
+     * request.
+     */
+    public Enumeration getAttributeNames() {
+
+        synchronized (attributes) {
+            return (new Enumerator(attributes.keySet()));
+        }
+
+    }
+
+
+    /**
+     * Override the <code>removeAttribute()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the attribute to remove
+     */
+    public void removeAttribute(String name) {
+
+        synchronized (attributes) {
+            attributes.remove(name);
+            if (!isSpecial(name))
+                getRequest().removeAttribute(name);
+        }
+
+    }
+
+
+    /**
+     * Override the <code>setAttribute()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the attribute to set
+     * @param value Value of the attribute to set
+     */
+    public void setAttribute(String name, Object value) {
+
+        synchronized (attributes) {
+            attributes.put(name, value);
+            if (!isSpecial(name))
+                getRequest().setAttribute(name, value);
+        }
+
+    }
+
+
+    // ------------------------------------------ ServletRequestWrapper Methods
+
+
+    /**
+     * Set the request that we are wrapping.
+     *
+     * @param request The new wrapped request
+     */
+    public void setRequest(ServletRequest request) {
+
+        super.setRequest(request);
+
+        // Initialize the attributes for this request
+        synchronized (attributes) {
+            attributes.clear();
+            Enumeration names = request.getAttributeNames();
+            while (names.hasMoreElements()) {
+                String name = (String) names.nextElement();
+                Object value = request.getAttribute(name);
+                attributes.put(name, value);
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Is this attribute name one of the special ones that is added only for
+     * included servlets?
+     *
+     * @param name Attribute name to be tested
+     */
+    protected boolean isSpecial(String name) {
+
+        for (int i = 0; i < specials.length; i++) {
+            if (specials[i].equals(name))
+                return (true);
+        }
+        return (false);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ApplicationResponse.java b/container/catalina/src/share/org/apache/catalina/core/ApplicationResponse.java
new file mode 100644
index 0000000..ad56a15
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ApplicationResponse.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.util.Locale;
+
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.ServletResponse</code>
+ * that transforms an application response object (which might be the original
+ * one passed to a servlet, or might be based on the 2.3
+ * <code>javax.servlet.ServletResponseWrapper</code> class)
+ * back into an internal <code>org.apache.catalina.Response</code>.
+ * <p>
+ * <strong>WARNING</strong>:  Due to Java's lack of support for multiple
+ * inheritance, all of the logic in <code>ApplicationResponse</code> is
+ * duplicated in <code>ApplicationHttpResponse</code>.  Make sure that you
+ * keep these two classes in synchronization when making changes!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+class ApplicationResponse extends ServletResponseWrapper {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new wrapped response around the specified servlet response.
+     *
+     * @param response The servlet response being wrapped
+     */
+    public ApplicationResponse(ServletResponse response) {
+
+        this(response, false);
+
+    }
+
+
+    /**
+     * Construct a new wrapped response around the specified servlet response.
+     *
+     * @param response The servlet response being wrapped
+     * @param included <code>true</code> if this response is being processed
+     *  by a <code>RequestDispatcher.include()</code> call
+     */
+    public ApplicationResponse(ServletResponse response, boolean included) {
+
+        super(response);
+        setIncluded(included);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Is this wrapped response the subject of an <code>include()</code>
+     * call?
+     */
+    protected boolean included = false;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    /**
+     * Disallow <code>reset()</code> calls on a included response.
+     *
+     * @exception IllegalStateException if the response has already
+     *  been committed
+     */
+    public void reset() {
+
+        // If already committed, the wrapped response will throw ISE
+        if (!included || getResponse().isCommitted())
+            getResponse().reset();
+
+    }
+
+
+    /**
+     * Disallow <code>setContentLength()</code> calls on an included response.
+     *
+     * @param len The new content length
+     */
+    public void setContentLength(int len) {
+
+        if (!included)
+            getResponse().setContentLength(len);
+
+    }
+
+
+    /**
+     * Disallow <code>setContentType()</code> calls on an included response.
+     *
+     * @param type The new content type
+     */
+    public void setContentType(String type) {
+
+        if (!included)
+            getResponse().setContentType(type);
+
+    }
+
+
+    /**
+     * Ignore <code>setLocale()</code> calls on an included response.
+     *
+     * @param loc The new locale
+     */
+    public void setLocale(Locale loc) {
+        if (!included)
+            getResponse().setLocale(loc);
+    }
+
+
+    /**
+     * Ignore <code>setBufferSize()</code> calls on an included response.
+     *
+     * @param size The buffer size
+     */
+    public void setBufferSize(int size) {
+        if (!included)
+            getResponse().setBufferSize(size);
+    }
+
+
+    // ----------------------------------------- ServletResponseWrapper Methods
+
+
+    /**
+     * Set the response that we are wrapping.
+     *
+     * @param response The new wrapped response
+     */
+    public void setResponse(ServletResponse response) {
+
+        super.setResponse(response);
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Return the included flag for this response.
+     */
+    boolean isIncluded() {
+
+        return (this.included);
+
+    }
+
+
+    /**
+     * Set the included flag for this response.
+     *
+     * @param included The new included flag
+     */
+    void setIncluded(boolean included) {
+
+        this.included = included;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/AprLifecycleListener.java b/container/catalina/src/share/org/apache/catalina/core/AprLifecycleListener.java
new file mode 100644
index 0000000..0ae6b6a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/AprLifecycleListener.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import java.lang.reflect.Method;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Implementation of <code>LifecycleListener</code> that will init and
+ * and destroy APR.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class AprLifecycleListener
+    implements LifecycleListener {
+
+    private static Log log = LogFactory.getLog(AprLifecycleListener.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final int REQUIRED_MAJOR = 1;
+    protected static final int REQUIRED_MINOR = 1;
+    protected static final int REQUIRED_PATCH = 0;
+
+
+    // ---------------------------------------------- LifecycleListener Methods
+
+
+    /**
+     * Primary entry point for startup and shutdown events.
+     *
+     * @param event The event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        if (Lifecycle.INIT_EVENT.equals(event.getType())) {
+            int major = 0;
+            int minor = 0;
+            int patch = 0;
+            try {
+                String methodName = "initialize";
+                Class paramTypes[] = new Class[1];
+                paramTypes[0] = String.class;
+                Object paramValues[] = new Object[1];
+                paramValues[0] = null;
+                Class clazz = Class.forName("org.apache.tomcat.jni.Library");
+                Method method = clazz.getMethod(methodName, paramTypes);
+                method.invoke(null, paramValues);
+                major = clazz.getField("TCN_MAJOR_VERSION").getInt(null);
+                minor = clazz.getField("TCN_MINOR_VERSION").getInt(null);
+                patch = clazz.getField("TCN_PATCH_VERSION").getInt(null);
+            } catch (Throwable t) {
+                if (!log.isDebugEnabled()) {
+                    log.info(sm.getString("aprListener.aprInit", 
+                            System.getProperty("java.library.path")));
+                } else {
+                    log.debug(sm.getString("aprListener.aprInit", 
+                            System.getProperty("java.library.path")), t);
+                }
+                return;
+            }
+            if ((major != REQUIRED_MAJOR) || (minor != REQUIRED_MINOR)
+                    || (patch < REQUIRED_PATCH)) {
+                log.error(sm.getString("aprListener.tcnInvalid", major + "." 
+                        + minor + "." + patch, REQUIRED_MAJOR + "." 
+                        + REQUIRED_MINOR + "." + REQUIRED_PATCH));
+            }
+        } else if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())) {
+            try {
+                String methodName = "terminate";
+                Method method = Class.forName("org.apache.tomcat.jni.Library")
+                    .getMethod(methodName, null);
+                method.invoke(null, null);
+            } catch (Throwable t) {
+                if (!log.isDebugEnabled()) {
+                    log.info(sm.getString("aprListener.aprDestroy"));
+                } else {
+                    log.debug(sm.getString("aprListener.aprDestroy"), t);
+                }
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/Constants.java b/container/catalina/src/share/org/apache/catalina/core/Constants.java
new file mode 100644
index 0000000..c75986a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/Constants.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.core";
+    public static final int MAJOR_VERSION = 2;
+    public static final int MINOR_VERSION = 4;
+
+    public static final String JSP_SERVLET_CLASS =
+        "org.apache.jasper.servlet.JspServlet";
+    public static final String JSP_SERVLET_NAME = "jsp";
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/ContainerBase.java b/container/catalina/src/share/org/apache/catalina/core/ContainerBase.java
new file mode 100644
index 0000000..4fff2e1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/ContainerBase.java
@@ -0,0 +1,1585 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.io.Serializable;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.ContainerListener;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Valve;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+import org.apache.naming.resources.ProxyDirContext;
+
+
+/**
+ * Abstract implementation of the <b>Container</b> interface, providing common
+ * functionality required by nearly every implementation.  Classes extending
+ * this base class must implement <code>getInfo()</code>, and may implement
+ * a replacement for <code>invoke()</code>.
+ * <p>
+ * All subclasses of this abstract base class will include support for a
+ * Pipeline object that defines the processing to be performed for each request
+ * received by the <code>invoke()</code> method of this class, utilizing the
+ * "Chain of Responsibility" design pattern.  A subclass should encapsulate its
+ * own processing functionality as a <code>Valve</code>, and configure this
+ * Valve into the pipeline by calling <code>setBasic()</code>.
+ * <p>
+ * This implementation fires property change events, per the JavaBeans design
+ * pattern, for changes in singleton properties.  In addition, it fires the
+ * following <code>ContainerEvent</code> events to listeners who register
+ * themselves with <code>addContainerListener()</code>:
+ * <table border=1>
+ *   <tr>
+ *     <th>Type</th>
+ *     <th>Data</th>
+ *     <th>Description</th>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>addChild</code></td>
+ *     <td align=center><code>Container</code></td>
+ *     <td>Child container added to this Container.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>addValve</code></td>
+ *     <td align=center><code>Valve</code></td>
+ *     <td>Valve added to this Container.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>removeChild</code></td>
+ *     <td align=center><code>Container</code></td>
+ *     <td>Child container removed from this Container.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>removeValve</code></td>
+ *     <td align=center><code>Valve</code></td>
+ *     <td>Valve removed from this Container.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>start</code></td>
+ *     <td align=center><code>null</code></td>
+ *     <td>Container was started.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>stop</code></td>
+ *     <td align=center><code>null</code></td>
+ *     <td>Container was stopped.</td>
+ *   </tr>
+ * </table>
+ * Subclasses that fire additional events should document them in the
+ * class comments of the implementation class.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public abstract class ContainerBase
+    implements Container, Lifecycle, Pipeline, MBeanRegistration, Serializable {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( ContainerBase.class );
+
+    /**
+     * Perform addChild with the permissions of this class.
+     * addChild can be called with the XML parser on the stack,
+     * this allows the XML parser to have fewer privileges than
+     * Tomcat.
+     */
+    protected class PrivilegedAddChild
+        implements PrivilegedAction {
+
+        private Container child;
+
+        PrivilegedAddChild(Container child) {
+            this.child = child;
+        }
+
+        public Object run() {
+            addChildInternal(child);
+            return null;
+        }
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The child Containers belonging to this Container, keyed by name.
+     */
+    protected HashMap children = new HashMap();
+
+
+    /**
+     * The processor delay for this component.
+     */
+    protected int backgroundProcessorDelay = -1;
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The container event listeners for this Container.
+     */
+    protected ArrayList listeners = new ArrayList();
+
+
+    /**
+     * The Loader implementation with which this Container is associated.
+     */
+    protected Loader loader = null;
+
+
+    /**
+     * The Logger implementation with which this Container is associated.
+     */
+    protected Log logger = null;
+
+
+    /**
+     * Associated logger name.
+     */
+    protected String logName = null;
+    
+
+    /**
+     * The Manager implementation with which this Container is associated.
+     */
+    protected Manager manager = null;
+
+
+    /**
+     * The cluster with which this Container is associated.
+     */
+    protected Cluster cluster = null;
+
+    
+    /**
+     * The human-readable name of this Container.
+     */
+    protected String name = null;
+
+
+    /**
+     * The parent Container to which this Container is a child.
+     */
+    protected Container parent = null;
+
+
+    /**
+     * The parent class loader to be configured when we install a Loader.
+     */
+    protected ClassLoader parentClassLoader = null;
+
+
+    /**
+     * The Pipeline object with which this Container is associated.
+     */
+    protected Pipeline pipeline = new StandardPipeline(this);
+
+
+    /**
+     * The Realm with which this Container is associated.
+     */
+    protected Realm realm = null;
+
+
+    /**
+     * The resources DirContext object with which this Container is associated.
+     */
+    protected DirContext resources = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started?
+     */
+    protected boolean started = false;
+
+    protected boolean initialized=false;
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    /**
+     * The background thread.
+     */
+    private Thread thread = null;
+
+
+    /**
+     * The background thread completion semaphore.
+     */
+    private boolean threadDone = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Get the delay between the invocation of the backgroundProcess method on
+     * this container and its children. Child containers will not be invoked
+     * if their delay value is not negative (which would mean they are using 
+     * their own thread). Setting this to a positive value will cause 
+     * a thread to be spawn. After waiting the specified amount of time, 
+     * the thread will invoke the executePeriodic method on this container 
+     * and all its children.
+     */
+    public int getBackgroundProcessorDelay() {
+        return backgroundProcessorDelay;
+    }
+
+
+    /**
+     * Set the delay between the invocation of the execute method on this
+     * container and its children.
+     * 
+     * @param delay The delay in seconds between the invocation of 
+     *              backgroundProcess methods
+     */
+    public void setBackgroundProcessorDelay(int delay) {
+        backgroundProcessorDelay = delay;
+    }
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return this.getClass().getName();
+    }
+
+
+    /**
+     * Return the Loader with which this Container is associated.  If there is
+     * no associated Loader, return the Loader associated with our parent
+     * Container (if any); otherwise, return <code>null</code>.
+     */
+    public Loader getLoader() {
+
+        if (loader != null)
+            return (loader);
+        if (parent != null)
+            return (parent.getLoader());
+        return (null);
+
+    }
+
+
+    /**
+     * Set the Loader with which this Container is associated.
+     *
+     * @param loader The newly associated loader
+     */
+    public synchronized void setLoader(Loader loader) {
+
+        // Change components if necessary
+        Loader oldLoader = this.loader;
+        if (oldLoader == loader)
+            return;
+        this.loader = loader;
+
+        // Stop the old component if necessary
+        if (started && (oldLoader != null) &&
+            (oldLoader instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) oldLoader).stop();
+            } catch (LifecycleException e) {
+                log.error("ContainerBase.setLoader: stop: ", e);
+            }
+        }
+
+        // Start the new component if necessary
+        if (loader != null)
+            loader.setContainer(this);
+        if (started && (loader != null) &&
+            (loader instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) loader).start();
+            } catch (LifecycleException e) {
+                log.error("ContainerBase.setLoader: start: ", e);
+            }
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("loader", oldLoader, this.loader);
+
+    }
+
+
+    /**
+     * Return the Logger with which this Container is associated.  If there is
+     * no associated Logger, return the Logger associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Log getLogger() {
+
+        if (logger != null)
+            return (logger);
+        logger = LogFactory.getLog(logName());
+        return (logger);
+
+    }
+
+
+    /**
+     * Return the Manager with which this Container is associated.  If there is
+     * no associated Manager, return the Manager associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Manager getManager() {
+
+        if (manager != null)
+            return (manager);
+        if (parent != null)
+            return (parent.getManager());
+        return (null);
+
+    }
+
+
+    /**
+     * Set the Manager with which this Container is associated.
+     *
+     * @param manager The newly associated Manager
+     */
+    public synchronized void setManager(Manager manager) {
+
+        // Change components if necessary
+        Manager oldManager = this.manager;
+        if (oldManager == manager)
+            return;
+        this.manager = manager;
+
+        // Stop the old component if necessary
+        if (started && (oldManager != null) &&
+            (oldManager instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) oldManager).stop();
+            } catch (LifecycleException e) {
+                log.error("ContainerBase.setManager: stop: ", e);
+            }
+        }
+
+        // Start the new component if necessary
+        if (manager != null)
+            manager.setContainer(this);
+        if (started && (manager != null) &&
+            (manager instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) manager).start();
+            } catch (LifecycleException e) {
+                log.error("ContainerBase.setManager: start: ", e);
+            }
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("manager", oldManager, this.manager);
+
+    }
+
+
+    /**
+     * Return an object which may be utilized for mapping to this component.
+     */
+    public Object getMappingObject() {
+        return this;
+    }
+
+
+    /**
+     * Return the Cluster with which this Container is associated.  If there is
+     * no associated Cluster, return the Cluster associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Cluster getCluster() {
+        if (cluster != null)
+            return (cluster);
+
+        if (parent != null)
+            return (parent.getCluster());
+
+        return (null);
+    }
+
+
+    /**
+     * Set the Cluster with which this Container is associated.
+     *
+     * @param cluster The newly associated Cluster
+     */
+    public synchronized void setCluster(Cluster cluster) {
+        // Change components if necessary
+        Cluster oldCluster = this.cluster;
+        if (oldCluster == cluster)
+            return;
+        this.cluster = cluster;
+
+        // Stop the old component if necessary
+        if (started && (oldCluster != null) &&
+            (oldCluster instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) oldCluster).stop();
+            } catch (LifecycleException e) {
+                log.error("ContainerBase.setCluster: stop: ", e);
+            }
+        }
+
+        // Start the new component if necessary
+        if (cluster != null)
+            cluster.setContainer(this);
+
+        if (started && (cluster != null) &&
+            (cluster instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) cluster).start();
+            } catch (LifecycleException e) {
+                log.error("ContainerBase.setCluster: start: ", e);
+            }
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("cluster", oldCluster, this.cluster);
+    }
+
+
+    /**
+     * Return a name string (suitable for use by humans) that describes this
+     * Container.  Within the set of child containers belonging to a particular
+     * parent, Container names must be unique.
+     */
+    public String getName() {
+
+        return (name);
+
+    }
+
+
+    /**
+     * Set a name string (suitable for use by humans) that describes this
+     * Container.  Within the set of child containers belonging to a particular
+     * parent, Container names must be unique.
+     *
+     * @param name New name of this container
+     *
+     * @exception IllegalStateException if this Container has already been
+     *  added to the children of a parent Container (after which the name
+     *  may not be changed)
+     */
+    public void setName(String name) {
+
+        String oldName = this.name;
+        this.name = name;
+        support.firePropertyChange("name", oldName, this.name);
+    }
+
+
+    /**
+     * Return the Container for which this Container is a child, if there is
+     * one.  If there is no defined parent, return <code>null</code>.
+     */
+    public Container getParent() {
+
+        return (parent);
+
+    }
+
+
+    /**
+     * Set the parent Container to which this Container is being added as a
+     * child.  This Container may refuse to become attached to the specified
+     * Container by throwing an exception.
+     *
+     * @param container Container to which this Container is being added
+     *  as a child
+     *
+     * @exception IllegalArgumentException if this Container refuses to become
+     *  attached to the specified Container
+     */
+    public void setParent(Container container) {
+
+        Container oldParent = this.parent;
+        this.parent = container;
+        support.firePropertyChange("parent", oldParent, this.parent);
+
+    }
+
+
+    /**
+     * Return the parent class loader (if any) for this web application.
+     * This call is meaningful only <strong>after</strong> a Loader has
+     * been configured.
+     */
+    public ClassLoader getParentClassLoader() {
+        if (parentClassLoader != null)
+            return (parentClassLoader);
+        if (parent != null) {
+            return (parent.getParentClassLoader());
+        }
+        return (ClassLoader.getSystemClassLoader());
+
+    }
+
+
+    /**
+     * Set the parent class loader (if any) for this web application.
+     * This call is meaningful only <strong>before</strong> a Loader has
+     * been configured, and the specified value (if non-null) should be
+     * passed as an argument to the class loader constructor.
+     *
+     *
+     * @param parent The new parent class loader
+     */
+    public void setParentClassLoader(ClassLoader parent) {
+        ClassLoader oldParentClassLoader = this.parentClassLoader;
+        this.parentClassLoader = parent;
+        support.firePropertyChange("parentClassLoader", oldParentClassLoader,
+                                   this.parentClassLoader);
+
+    }
+
+
+    /**
+     * Return the Pipeline object that manages the Valves associated with
+     * this Container.
+     */
+    public Pipeline getPipeline() {
+
+        return (this.pipeline);
+
+    }
+
+
+    /**
+     * Return the Realm with which this Container is associated.  If there is
+     * no associated Realm, return the Realm associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Realm getRealm() {
+
+        if (realm != null)
+            return (realm);
+        if (parent != null)
+            return (parent.getRealm());
+        return (null);
+
+    }
+
+
+    /**
+     * Set the Realm with which this Container is associated.
+     *
+     * @param realm The newly associated Realm
+     */
+    public synchronized void setRealm(Realm realm) {
+
+        // Change components if necessary
+        Realm oldRealm = this.realm;
+        if (oldRealm == realm)
+            return;
+        this.realm = realm;
+
+        // Stop the old component if necessary
+        if (started && (oldRealm != null) &&
+            (oldRealm instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) oldRealm).stop();
+            } catch (LifecycleException e) {
+                log.error("ContainerBase.setRealm: stop: ", e);
+            }
+        }
+
+        // Start the new component if necessary
+        if (realm != null)
+            realm.setContainer(this);
+        if (started && (realm != null) &&
+            (realm instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) realm).start();
+            } catch (LifecycleException e) {
+                log.error("ContainerBase.setRealm: start: ", e);
+            }
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("realm", oldRealm, this.realm);
+
+    }
+
+
+    /**
+      * Return the resources DirContext object with which this Container is
+      * associated.  If there is no associated resources object, return the
+      * resources associated with our parent Container (if any); otherwise
+      * return <code>null</code>.
+     */
+    public DirContext getResources() {
+        if (resources != null)
+            return (resources);
+        if (parent != null)
+            return (parent.getResources());
+        return (null);
+
+    }
+
+
+    /**
+     * Set the resources DirContext object with which this Container is
+     * associated.
+     *
+     * @param resources The newly associated DirContext
+     */
+    public synchronized void setResources(DirContext resources) {
+        // Called from StandardContext.setResources()
+        //              <- StandardContext.start() 
+        //              <- ContainerBase.addChildInternal() 
+
+        // Change components if necessary
+        DirContext oldResources = this.resources;
+        if (oldResources == resources)
+            return;
+        Hashtable env = new Hashtable();
+        if (getParent() != null)
+            env.put(ProxyDirContext.HOST, getParent().getName());
+        env.put(ProxyDirContext.CONTEXT, getName());
+        this.resources = new ProxyDirContext(env, resources);
+        // Report this property change to interested listeners
+        support.firePropertyChange("resources", oldResources, this.resources);
+
+    }
+
+
+    // ------------------------------------------------------ Container Methods
+
+
+    /**
+     * Add a new child Container to those associated with this Container,
+     * if supported.  Prior to adding this Container to the set of children,
+     * the child's <code>setParent()</code> method must be called, with this
+     * Container as an argument.  This method may thrown an
+     * <code>IllegalArgumentException</code> if this Container chooses not
+     * to be attached to the specified Container, in which case it is not added
+     *
+     * @param child New child Container to be added
+     *
+     * @exception IllegalArgumentException if this exception is thrown by
+     *  the <code>setParent()</code> method of the child Container
+     * @exception IllegalArgumentException if the new child does not have
+     *  a name unique from that of existing children of this Container
+     * @exception IllegalStateException if this Container does not support
+     *  child Containers
+     */
+    public void addChild(Container child) {
+        if (System.getSecurityManager() != null) {
+            PrivilegedAction dp =
+                new PrivilegedAddChild(child);
+            AccessController.doPrivileged(dp);
+        } else {
+            addChildInternal(child);
+        }
+    }
+
+    private void addChildInternal(Container child) {
+
+        if( log.isDebugEnabled() )
+            log.debug("Add child " + child + " " + this);
+        synchronized(children) {
+            if (children.get(child.getName()) != null)
+                throw new IllegalArgumentException("addChild:  Child name '" +
+                                                   child.getName() +
+                                                   "' is not unique");
+            child.setParent(this);  // May throw IAE
+            children.put(child.getName(), child);
+
+            // Start child
+            if (started && (child instanceof Lifecycle)) {
+                boolean success = false;
+                try {
+                    ((Lifecycle) child).start();
+                    success = true;
+                } catch (LifecycleException e) {
+                    log.error("ContainerBase.addChild: start: ", e);
+                    throw new IllegalStateException
+                        ("ContainerBase.addChild: start: " + e);
+                } finally {
+                    if (!success) {
+                        children.remove(child.getName());
+                    }
+                }
+            }
+
+            fireContainerEvent(ADD_CHILD_EVENT, child);
+        }
+
+    }
+
+
+    /**
+     * Add a container event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addContainerListener(ContainerListener listener) {
+
+        synchronized (listeners) {
+            listeners.add(listener);
+        }
+
+    }
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Return the child Container, associated with this Container, with
+     * the specified name (if any); otherwise, return <code>null</code>
+     *
+     * @param name Name of the child Container to be retrieved
+     */
+    public Container findChild(String name) {
+
+        if (name == null)
+            return (null);
+        synchronized (children) {       // Required by post-start changes
+            return ((Container) children.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the set of children Containers associated with this Container.
+     * If this Container has no children, a zero-length array is returned.
+     */
+    public Container[] findChildren() {
+
+        synchronized (children) {
+            Container results[] = new Container[children.size()];
+            return ((Container[]) children.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the set of container listeners associated with this Container.
+     * If this Container has no registered container listeners, a zero-length
+     * array is returned.
+     */
+    public ContainerListener[] findContainerListeners() {
+
+        synchronized (listeners) {
+            ContainerListener[] results = 
+                new ContainerListener[listeners.size()];
+            return ((ContainerListener[]) listeners.toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Process the specified Request, to produce the corresponding Response,
+     * by invoking the first Valve in our pipeline (if any), or the basic
+     * Valve otherwise.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     *
+     * @exception IllegalStateException if neither a pipeline or a basic
+     *  Valve have been configured for this Container
+     * @exception IOException if an input/output error occurred while
+     *  processing
+     * @exception ServletException if a ServletException was thrown
+     *  while processing this request
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        pipeline.getFirst().invoke(request, response);
+
+    }
+
+
+    /**
+     * Remove an existing child Container from association with this parent
+     * Container.
+     *
+     * @param child Existing child Container to be removed
+     */
+    public void removeChild(Container child) {
+
+        synchronized(children) {
+            if (children.get(child.getName()) == null)
+                return;
+            children.remove(child.getName());
+        }
+        
+        if (started && (child instanceof Lifecycle)) {
+            try {
+                if( child instanceof ContainerBase ) {
+                    if( ((ContainerBase)child).started ) {
+                        ((Lifecycle) child).stop();
+                    }
+                } else {
+                    ((Lifecycle) child).stop();
+                }
+            } catch (LifecycleException e) {
+                log.error("ContainerBase.removeChild: stop: ", e);
+            }
+        }
+        
+        fireContainerEvent(REMOVE_CHILD_EVENT, child);
+        
+        // child.setParent(null);
+
+    }
+
+
+    /**
+     * Remove a container event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeContainerListener(ContainerListener listener) {
+
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
+
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public synchronized void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started) {
+            if(log.isInfoEnabled())
+                log.info(sm.getString("containerBase.alreadyStarted", logName()));
+            return;
+        }
+        
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+        started = true;
+
+        // Start our subordinate components, if any
+        if ((loader != null) && (loader instanceof Lifecycle))
+            ((Lifecycle) loader).start();
+        getLogger();
+        if ((logger != null) && (logger instanceof Lifecycle))
+            ((Lifecycle) logger).start();
+        if ((manager != null) && (manager instanceof Lifecycle))
+            ((Lifecycle) manager).start();
+        if ((cluster != null) && (cluster instanceof Lifecycle))
+            ((Lifecycle) cluster).start();
+        if ((realm != null) && (realm instanceof Lifecycle))
+            ((Lifecycle) realm).start();
+        if ((resources != null) && (resources instanceof Lifecycle))
+            ((Lifecycle) resources).start();
+
+        // Start our child containers, if any
+        Container children[] = findChildren();
+        for (int i = 0; i < children.length; i++) {
+            if (children[i] instanceof Lifecycle)
+                ((Lifecycle) children[i]).start();
+        }
+
+        // Start the Valves in our pipeline (including the basic), if any
+        if (pipeline instanceof Lifecycle)
+            ((Lifecycle) pipeline).start();
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+
+        // Start our thread
+        threadStart();
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public synchronized void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started) {
+            if(log.isInfoEnabled())
+                log.info(sm.getString("containerBase.notStarted", logName()));
+            return;
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+        // Stop our thread
+        threadStop();
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Stop the Valves in our pipeline (including the basic), if any
+        if (pipeline instanceof Lifecycle) {
+            ((Lifecycle) pipeline).stop();
+        }
+
+        // Stop our child containers, if any
+        Container children[] = findChildren();
+        for (int i = 0; i < children.length; i++) {
+            if (children[i] instanceof Lifecycle)
+                ((Lifecycle) children[i]).stop();
+        }
+        // Remove children - so next start can work
+        children = findChildren();
+        for (int i = 0; i < children.length; i++) {
+            removeChild(children[i]);
+        }
+
+        // Stop our subordinate components, if any
+        if ((resources != null) && (resources instanceof Lifecycle)) {
+            ((Lifecycle) resources).stop();
+        }
+        if ((realm != null) && (realm instanceof Lifecycle)) {
+            ((Lifecycle) realm).stop();
+        }
+        if ((cluster != null) && (cluster instanceof Lifecycle)) {
+            ((Lifecycle) cluster).stop();
+        }
+        if ((manager != null) && (manager instanceof Lifecycle)) {
+            ((Lifecycle) manager).stop();
+        }
+        if ((logger != null) && (logger instanceof Lifecycle)) {
+            ((Lifecycle) logger).stop();
+        }
+        if ((loader != null) && (loader instanceof Lifecycle)) {
+            ((Lifecycle) loader).stop();
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+
+    }
+
+    /** Init method, part of the MBean lifecycle.
+     *  If the container was added via JMX, it'll register itself with the 
+     * parent, using the ObjectName conventions to locate the parent.
+     * 
+     *  If the container was added directly and it doesn't have an ObjectName,
+     * it'll create a name and register itself with the JMX console. On destroy(), 
+     * the object will unregister.
+     * 
+     * @throws Exception
+     */ 
+    public void init() throws Exception {
+
+        if( this.getParent() == null ) {
+            // "Life" update
+            ObjectName parentName=getParentName();
+
+            //log.info("Register " + parentName );
+            if( parentName != null && 
+                    mserver.isRegistered(parentName)) 
+            {
+                mserver.invoke(parentName, "addChild", new Object[] { this },
+                        new String[] {"org.apache.catalina.Container"});
+            }
+        }
+        initialized=true;
+    }
+    
+    public ObjectName getParentName() throws MalformedObjectNameException {
+        return null;
+    }
+    
+    public void destroy() throws Exception {
+        if( started ) {
+            stop();
+        }
+        initialized=false;
+
+        // unregister this component
+        if ( oname != null ) {
+            try {
+                if( controller == oname ) {
+                    Registry.getRegistry(null, null)
+                        .unregisterComponent(oname);
+                    if(log.isDebugEnabled())
+                        log.debug("unregistering " + oname);
+                }
+            } catch( Throwable t ) {
+                log.error("Error unregistering ", t );
+            }
+        }
+
+        if (parent != null) {
+            parent.removeChild(this);
+        }
+
+        // Stop our child containers, if any
+        Container children[] = findChildren();
+        for (int i = 0; i < children.length; i++) {
+            removeChild(children[i]);
+        }
+                
+    }
+
+    // ------------------------------------------------------- Pipeline Methods
+
+
+    /**
+     * Add a new Valve to the end of the pipeline associated with this
+     * Container.  Prior to adding the Valve, the Valve's
+     * <code>setContainer</code> method must be called, with this Container
+     * as an argument.  The method may throw an
+     * <code>IllegalArgumentException</code> if this Valve chooses not to
+     * be associated with this Container, or <code>IllegalStateException</code>
+     * if it is already associated with a different Container.
+     *
+     * @param valve Valve to be added
+     *
+     * @exception IllegalArgumentException if this Container refused to
+     *  accept the specified Valve
+     * @exception IllegalArgumentException if the specifie Valve refuses to be
+     *  associated with this Container
+     * @exception IllegalStateException if the specified Valve is already
+     *  associated with a different Container
+     */
+    public synchronized void addValve(Valve valve) {
+
+        pipeline.addValve(valve);
+        fireContainerEvent(ADD_VALVE_EVENT, valve);
+    }
+
+    public ObjectName[] getValveObjectNames() {
+        return ((StandardPipeline)pipeline).getValveObjectNames();
+    }
+    
+    /**
+     * <p>Return the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).
+     */
+    public Valve getBasic() {
+
+        return (pipeline.getBasic());
+
+    }
+
+
+    /**
+     * Return the first valve in the pipeline.
+     */
+    public Valve getFirst() {
+
+        return (pipeline.getFirst());
+
+    }
+
+
+    /**
+     * Return the set of Valves in the pipeline associated with this
+     * Container, including the basic Valve (if any).  If there are no
+     * such Valves, a zero-length array is returned.
+     */
+    public Valve[] getValves() {
+
+        return (pipeline.getValves());
+
+    }
+
+
+    /**
+     * Remove the specified Valve from the pipeline associated with this
+     * Container, if it is found; otherwise, do nothing.
+     *
+     * @param valve Valve to be removed
+     */
+    public synchronized void removeValve(Valve valve) {
+
+        pipeline.removeValve(valve);
+        fireContainerEvent(REMOVE_VALVE_EVENT, valve);
+    }
+
+
+    /**
+     * <p>Set the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).  Prioer to setting the basic Valve,
+     * the Valve's <code>setContainer()</code> will be called, if it
+     * implements <code>Contained</code>, with the owning Container as an
+     * argument.  The method may throw an <code>IllegalArgumentException</code>
+     * if this Valve chooses not to be associated with this Container, or
+     * <code>IllegalStateException</code> if it is already associated with
+     * a different Container.</p>
+     *
+     * @param valve Valve to be distinguished as the basic Valve
+     */
+    public void setBasic(Valve valve) {
+
+        pipeline.setBasic(valve);
+
+    }
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess() {
+        
+        if (!started)
+            return;
+
+        if (cluster != null) {
+            try {
+                cluster.backgroundProcess();
+            } catch (Exception e) {
+                log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e);                
+            }
+        }
+        if (loader != null) {
+            try {
+                loader.backgroundProcess();
+            } catch (Exception e) {
+                log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e);                
+            }
+        }
+        if (manager != null) {
+            try {
+                manager.backgroundProcess();
+            } catch (Exception e) {
+                log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e);                
+            }
+        }
+        if (realm != null) {
+            try {
+                realm.backgroundProcess();
+            } catch (Exception e) {
+                log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e);                
+            }
+        }
+        Valve current = pipeline.getFirst();
+        while (current != null) {
+            try {
+                current.backgroundProcess();
+            } catch (Exception e) {
+                log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e);                
+            }
+            current = current.getNext();
+        }
+        lifecycle.fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Notify all container event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param data Event data
+     */
+    public void fireContainerEvent(String type, Object data) {
+
+        if (listeners.size() < 1)
+            return;
+        ContainerEvent event = new ContainerEvent(this, type, data);
+        ContainerListener list[] = new ContainerListener[0];
+        synchronized (listeners) {
+            list = (ContainerListener[]) listeners.toArray(list);
+        }
+        for (int i = 0; i < list.length; i++)
+            ((ContainerListener) list[i]).containerEvent(event);
+
+    }
+
+
+    /**
+     * Return the abbreviated name of this container for logging messsages
+     */
+    protected String logName() {
+
+        if (logName != null) {
+            return logName;
+        }
+        String loggerName = null;
+        Container current = this;
+        while (current != null) {
+            String name = current.getName();
+            if ((name == null) || (name.equals(""))) {
+                name = "/";
+            }
+            loggerName = "[" + name + "]" 
+                + ((loggerName != null) ? ("." + loggerName) : "");
+            current = current.getParent();
+        }
+        logName = ContainerBase.class.getName() + "." + loggerName;
+        return logName;
+        
+    }
+
+    
+    // -------------------- JMX and Registration  --------------------
+    protected String type;
+    protected String domain;
+    protected String suffix;
+    protected ObjectName oname;
+    protected ObjectName controller;
+    protected transient MBeanServer mserver;
+
+    public ObjectName getJmxName() {
+        return oname;
+    }
+    
+    public String getObjectName() {
+        if (oname != null) {
+            return oname.toString();
+        } else return null;
+    }
+
+    public String getDomain() {
+        if( domain==null ) {
+            Container parent=this;
+            while( parent != null &&
+                    !( parent instanceof StandardEngine) ) {
+                parent=parent.getParent();
+            }
+            if( parent instanceof StandardEngine ) {
+                domain=((StandardEngine)parent).getDomain();
+            } 
+        }
+        return domain;
+    }
+
+    public void setDomain(String domain) {
+        this.domain=domain;
+    }
+    
+    public String getType() {
+        return type;
+    }
+
+    protected String getJSR77Suffix() {
+        return suffix;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        if (name == null ){
+            return null;
+        }
+
+        domain=name.getDomain();
+
+        type=name.getKeyProperty("type");
+        if( type==null ) {
+            type=name.getKeyProperty("j2eeType");
+        }
+
+        String j2eeApp=name.getKeyProperty("J2EEApplication");
+        String j2eeServer=name.getKeyProperty("J2EEServer");
+        if( j2eeApp==null ) {
+            j2eeApp="none";
+        }
+        if( j2eeServer==null ) {
+            j2eeServer="none";
+        }
+        suffix=",J2EEApplication=" + j2eeApp + ",J2EEServer=" + j2eeServer;
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+
+    public ObjectName[] getChildren() {
+        ObjectName result[]=new ObjectName[children.size()];
+        Iterator it=children.values().iterator();
+        int i=0;
+        while( it.hasNext() ) {
+            Object next=it.next();
+            if( next instanceof ContainerBase ) {
+                result[i++]=((ContainerBase)next).getJmxName();
+            }
+        }
+        return result;
+    }
+
+    public ObjectName createObjectName(String domain, ObjectName parent)
+        throws Exception
+    {
+        if( log.isDebugEnabled())
+            log.debug("Create ObjectName " + domain + " " + parent );
+        return null;
+    }
+
+    public String getContainerSuffix() {
+        Container container=this;
+        Container context=null;
+        Container host=null;
+        Container servlet=null;
+        
+        StringBuffer suffix=new StringBuffer();
+        
+        if( container instanceof StandardHost ) {
+            host=container;
+        } else if( container instanceof StandardContext ) {
+            host=container.getParent();
+            context=container;
+        } else if( container instanceof StandardWrapper ) {
+            context=container.getParent();
+            host=context.getParent();
+            servlet=container;
+        }
+        if( context!=null ) {
+            String path=((StandardContext)context).getPath();
+            suffix.append(",path=").append((path.equals("")) ? "/" : path);
+        } 
+        if( host!=null ) suffix.append(",host=").append( host.getName() );
+        if( servlet != null ) {
+            String name=container.getName();
+            suffix.append(",servlet=");
+            suffix.append((name=="") ? "/" : name);
+        }
+        return suffix.toString();
+    }
+
+
+    /**
+     * Start the background thread that will periodically check for
+     * session timeouts.
+     */
+    protected void threadStart() {
+
+        if (thread != null)
+            return;
+        if (backgroundProcessorDelay <= 0)
+            return;
+
+        threadDone = false;
+        String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
+        thread = new Thread(new ContainerBackgroundProcessor(), threadName);
+        thread.setDaemon(true);
+        thread.start();
+
+    }
+
+
+    /**
+     * Stop the background thread that is periodically checking for
+     * session timeouts.
+     */
+    protected void threadStop() {
+
+        if (thread == null)
+            return;
+
+        threadDone = true;
+        thread.interrupt();
+        try {
+            thread.join();
+        } catch (InterruptedException e) {
+            ;
+        }
+
+        thread = null;
+
+    }
+
+
+    // -------------------------------------- ContainerExecuteDelay Inner Class
+
+
+    /**
+     * Private thread class to invoke the backgroundProcess method 
+     * of this container and its children after a fixed delay.
+     */
+    protected class ContainerBackgroundProcessor implements Runnable {
+
+        public void run() {
+            while (!threadDone) {
+                try {
+                    Thread.sleep(backgroundProcessorDelay * 1000L);
+                } catch (InterruptedException e) {
+                    ;
+                }
+                if (!threadDone) {
+                    Container parent = (Container) getMappingObject();
+                    ClassLoader cl = 
+                        Thread.currentThread().getContextClassLoader();
+                    if (parent.getLoader() != null) {
+                        cl = parent.getLoader().getClassLoader();
+                    }
+                    processChildren(parent, cl);
+                }
+            }
+        }
+
+        protected void processChildren(Container container, ClassLoader cl) {
+            try {
+                if (container.getLoader() != null) {
+                    Thread.currentThread().setContextClassLoader
+                        (container.getLoader().getClassLoader());
+                }
+                container.backgroundProcess();
+            } catch (Throwable t) {
+                log.error("Exception invoking periodic operation: ", t);
+            } finally {
+                Thread.currentThread().setContextClassLoader(cl);
+            }
+            Container[] children = container.findChildren();
+            for (int i = 0; i < children.length; i++) {
+                if (children[i].getBackgroundProcessorDelay() <= 0) {
+                    processChildren(children[i], cl);
+                }
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/DummyRequest.java b/container/catalina/src/share/org/apache/catalina/core/DummyRequest.java
new file mode 100644
index 0000000..566ece3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/DummyRequest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.Socket;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.connector.Response;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+
+/**
+ * Dummy request object, used for request dispatcher mapping, as well as
+ * JSP precompilation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class DummyRequest
+    implements HttpServletRequest {
+
+    public DummyRequest() {
+    }
+
+    public DummyRequest(String contextPath, String decodedURI,
+                        String queryString) {
+        this.contextPath = contextPath;
+        this.decodedURI = decodedURI;
+        this.queryString = queryString;
+    }
+
+    protected String contextPath = null;
+    protected String decodedURI = null;
+    protected String queryString = null;
+
+    protected String pathInfo = null;
+    protected String servletPath = null;
+    protected Wrapper wrapper = null;
+
+    protected FilterChain filterChain = null;
+    
+    private static Enumeration dummyEnum = new Enumeration(){
+        public boolean hasMoreElements(){
+            return false;
+        }
+        public Object nextElement(){
+            return null;
+        }
+    };
+
+    public String getContextPath() {
+        return (contextPath);
+    }
+
+    public MessageBytes getContextPathMB() {
+        return null;
+    }
+
+    public ServletRequest getRequest() {
+        return (this);
+    }
+
+    public String getDecodedRequestURI() {
+        return decodedURI;
+    }
+
+    public MessageBytes getDecodedRequestURIMB() {
+        return null;
+    }
+
+    public FilterChain getFilterChain() {
+        return (this.filterChain);
+    }
+
+    public void setFilterChain(FilterChain filterChain) {
+        this.filterChain = filterChain;
+    }
+
+    public String getQueryString() {
+        return queryString;
+    }
+
+    public void setQueryString(String query) {
+        queryString = query;
+    }
+
+    public String getPathInfo() {
+        return pathInfo;
+    }
+
+    public void setPathInfo(String path) {
+        pathInfo = path;
+    }
+
+    public MessageBytes getPathInfoMB() {
+        return null;
+    }
+
+    public MessageBytes getRequestPathMB() {
+        return null;
+    }
+
+    public String getServletPath() {
+        return servletPath;
+    }
+
+    public void setServletPath(String path) {
+        servletPath = path;
+    }
+
+    public MessageBytes getServletPathMB() {
+        return null;
+    }
+
+    public Wrapper getWrapper() {
+        return (this.wrapper);
+    }
+
+    public void setWrapper(Wrapper wrapper) {
+        this.wrapper = wrapper;
+    }
+
+    public String getAuthorization() { return null; }
+    public void setAuthorization(String authorization) {}
+    public Connector getConnector() { return null; }
+    public void setConnector(Connector connector) {}
+    public Context getContext() { return null; }
+    public void setContext(Context context) {}
+    public Host getHost() { return null; }
+    public void setHost(Host host) {}
+    public String getInfo() { return null; }
+    public Response getResponse() { return null; }
+    public void setResponse(Response response) {}
+    public Socket getSocket() { return null; }
+    public void setSocket(Socket socket) {}
+    public InputStream getStream() { return null; }
+    public void setStream(InputStream input) {}
+    public void addLocale(Locale locale) {}
+    public ServletInputStream createInputStream() throws IOException {
+        return null;
+    }
+    public void finishRequest() throws IOException {}
+    public Object getNote(String name) { return null; }
+    public Iterator getNoteNames() { return null; }
+    public void removeNote(String name) {}
+    public void setContentType(String type) {}
+    public void setNote(String name, Object value) {}
+    public void setProtocol(String protocol) {}
+    public void setRemoteAddr(String remoteAddr) {}
+    public void setRemoteHost(String remoteHost) {}
+    public void setScheme(String scheme) {}
+    public void setServerName(String name) {}
+    public void setServerPort(int port) {}
+    public Object getAttribute(String name) { return null; }
+    public Enumeration getAttributeNames() { return null; }
+    public String getCharacterEncoding() { return null; }
+    public int getContentLength() { return -1; }
+    public void setContentLength(int length) {}
+    public String getContentType() { return null; }
+    public ServletInputStream getInputStream() throws IOException {
+        return null;
+    }
+    public Locale getLocale() { return null; }
+    public Enumeration getLocales() { return null; }
+    public String getProtocol() { return null; }
+    public BufferedReader getReader() throws IOException { return null; }
+    public String getRealPath(String path) { return null; }
+    public String getRemoteAddr() { return null; }
+    public String getRemoteHost() { return null; }
+    public String getScheme() { return null; }
+    public String getServerName() { return null; }
+    public int getServerPort() { return -1; }
+    public boolean isSecure() { return false; }
+    public void removeAttribute(String name) {}
+    public void setAttribute(String name, Object value) {}
+    public void setCharacterEncoding(String enc)
+        throws UnsupportedEncodingException {}
+    public void addCookie(Cookie cookie) {}
+    public void addHeader(String name, String value) {}
+    public void addParameter(String name, String values[]) {}
+    public void clearCookies() {}
+    public void clearHeaders() {}
+    public void clearLocales() {}
+    public void clearParameters() {}
+    public void recycle() {}
+    public void setAuthType(String authType) {}
+    public void setContextPath(String path) {}
+    public void setMethod(String method) {}
+    public void setRequestedSessionCookie(boolean flag) {}
+    public void setRequestedSessionId(String id) {}
+    public void setRequestedSessionURL(boolean flag) {}
+    public void setRequestURI(String uri) {}
+    public void setSecure(boolean secure) {}
+    public void setUserPrincipal(Principal principal) {}
+    public String getParameter(String name) { return null; }
+    public Map getParameterMap() { return null; }
+    public Enumeration getParameterNames() { return dummyEnum; }
+    public String[] getParameterValues(String name) { return null; }
+    public RequestDispatcher getRequestDispatcher(String path) {
+        return null;
+    }
+    public String getAuthType() { return null; }
+    public Cookie[] getCookies() { return null; }
+    public long getDateHeader(String name) { return -1; }
+    public String getHeader(String name) { return null; }
+    public Enumeration getHeaders(String name) { return null; }
+    public Enumeration getHeaderNames() { return null; }
+    public int getIntHeader(String name) { return -1; }
+    public String getMethod() { return null; }
+    public String getPathTranslated() { return null; }
+    public String getRemoteUser() { return null; }
+    public String getRequestedSessionId() { return null; }
+    public String getRequestURI() { return null; }
+    public void setDecodedRequestURI(String uri) {}
+    public StringBuffer getRequestURL() { return null; }
+    public HttpSession getSession() { return null; }
+    public HttpSession getSession(boolean create) { return null; }
+    public boolean isRequestedSessionIdFromCookie() { return false; }
+    public boolean isRequestedSessionIdFromURL() { return false; }
+    public boolean isRequestedSessionIdFromUrl() { return false; }
+    public boolean isRequestedSessionIdValid() { return false; }
+    public boolean isUserInRole(String role) { return false; }
+    public Principal getUserPrincipal() { return null; }
+    public String getLocalAddr() { return null; }    
+    public String getLocalName() { return null; }
+    public int getLocalPort() { return -1; }
+    public int getRemotePort() { return -1; }
+    
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/core/DummyResponse.java b/container/catalina/src/share/org/apache/catalina/core/DummyResponse.java
new file mode 100644
index 0000000..66bf84f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/DummyResponse.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.connector.Request;
+
+
+/**
+ * Dummy response object, used for JSP precompilation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class DummyResponse
+    implements HttpServletResponse {
+
+    public DummyResponse() {
+    }
+
+
+    public void setAppCommitted(boolean appCommitted) {}
+    public boolean isAppCommitted() { return false; }
+    public Connector getConnector() { return null; }
+    public void setConnector(Connector connector) {}
+    public int getContentCount() { return -1; }
+    public Context getContext() { return null; }
+    public void setContext(Context context) {}
+    public boolean getIncluded() { return false; }
+    public void setIncluded(boolean included) {}
+    public String getInfo() { return null; }
+    public Request getRequest() { return null; }
+    public void setRequest(Request request) {}
+    public ServletResponse getResponse() { return null; }
+    public OutputStream getStream() { return null; }
+    public void setStream(OutputStream stream) {}
+    public void setSuspended(boolean suspended) {}
+    public boolean isSuspended() { return false; }
+    public void setError() {}
+    public boolean isError() { return false; }
+    public ServletOutputStream createOutputStream() throws IOException {
+        return null;
+    }
+    public void finishResponse() throws IOException {}
+    public int getContentLength() { return -1; }
+    public String getContentType() { return null; }
+    public PrintWriter getReporter() { return null; }
+    public void recycle() {}
+    public void write(int b) throws IOException {}
+    public void write(byte b[]) throws IOException {}
+    public void write(byte b[], int off, int len) throws IOException {}
+    public void flushBuffer() throws IOException {}
+    public int getBufferSize() { return -1; }
+    public String getCharacterEncoding() { return null; }
+    public void setCharacterEncoding(String charEncoding) {}
+    public ServletOutputStream getOutputStream() throws IOException {
+        return null;
+    }
+    public Locale getLocale() { return null; }
+    public PrintWriter getWriter() throws IOException { return null; }
+    public boolean isCommitted() { return false; }
+    public void reset() {}
+    public void resetBuffer() {}
+    public void setBufferSize(int size) {}
+    public void setContentLength(int length) {}
+    public void setContentType(String type) {}
+    public void setLocale(Locale locale) {}
+
+    public Cookie[] getCookies() { return null; }
+    public String getHeader(String name) { return null; }
+    public String[] getHeaderNames() { return null; }
+    public String[] getHeaderValues(String name) { return null; }
+    public String getMessage() { return null; }
+    public int getStatus() { return -1; }
+    public void reset(int status, String message) {}
+    public void addCookie(Cookie cookie) {}
+    public void addDateHeader(String name, long value) {}
+    public void addHeader(String name, String value) {}
+    public void addIntHeader(String name, int value) {}
+    public boolean containsHeader(String name) { return false; }
+    public String encodeRedirectURL(String url) { return null; }
+    public String encodeRedirectUrl(String url) { return null; }
+    public String encodeURL(String url) { return null; }
+    public String encodeUrl(String url) { return null; }
+    public void sendAcknowledgement() throws IOException {}
+    public void sendError(int status) throws IOException {}
+    public void sendError(int status, String message) throws IOException {}
+    public void sendRedirect(String location) throws IOException {}
+    public void setDateHeader(String name, long value) {}
+    public void setHeader(String name, String value) {}
+    public void setIntHeader(String name, int value) {}
+    public void setStatus(int status) {}
+    public void setStatus(int status, String message) {}
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/core/LocalStrings.properties
new file mode 100644
index 0000000..fc3478a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/LocalStrings.properties
@@ -0,0 +1,186 @@
+applicationContext.attributeEvent=Exception thrown by attributes event listener
+applicationContext.mapping.error=Error during mapping
+applicationContext.requestDispatcher.iae=Path {0} does not start with a "/" character
+applicationContext.resourcePaths.iae=Path {0} does not start with a "/" character
+applicationContext.setAttribute.namenull=Name cannot be null
+applicationDispatcher.allocateException=Allocate exception for servlet {0}
+applicationDispatcher.deallocateException=Deallocate exception for servlet {0}
+applicationDispatcher.forward.ise=Cannot forward after response has been committed
+applicationDispatcher.forward.throw=Forwarded resource threw an exception
+applicationDispatcher.include.throw=Included resource threw an exception
+applicationDispatcher.isUnavailable=Servlet {0} is currently unavailable
+applicationDispatcher.serviceException=Servlet.service() for servlet {0} threw exception
+applicationRequest.badParent=Cannot locate parent Request implementation
+applicationRequest.badRequest=Request is not a javax.servlet.ServletRequestWrapper
+applicationResponse.badParent=Cannot locate parent Response implementation
+applicationResponse.badResponse=Response is not a javax.servlet.ServletResponseWrapper
+aprListener.aprInit=The Apache Portable Runtime which allows optimal performance in production environments was not found on the java.library.path: {0}
+aprListener.tcnInvalid=An incompatible version {0} of the Tomcat Native library is installed, while Tomcat requires version {1} 
+aprListener.aprDestroy=Failed shutdown of Apache Portable Runtime
+containerBase.addDefaultMapper=Exception configuring default mapper of class {0}
+containerBase.alreadyStarted=Container {0} has already been started
+containerBase.notConfigured=No basic Valve has been configured
+containerBase.notStarted=Container {0} has not been started
+containerBase.backgroundProcess.cluster=Exception processing cluster {0} background process
+containerBase.backgroundProcess.loader=Exception processing loader {0} background process
+containerBase.backgroundProcess.manager=Exception processing manager {0} background process
+containerBase.backgroundProcess.realm=Exception processing realm {0} background process
+containerBase.backgroundProcess.valve=Exception processing valve {0} background process
+fastEngineMapper.alreadyStarted=FastEngineMapper {0} has already been started
+fastEngineMapper.notStarted=FastEngineMapper {0} has not yet been started
+filterChain.filter=Filter execution threw an exception
+filterChain.servlet=Servlet execution threw an exception
+httpContextMapper.container=This container is not a StandardContext
+httpEngineMapper.container=This container is not a StandardEngine
+httpHostMapper.container=This container is not a StandardHost
+interceptorValve.alreadyStarted=InterceptorValve has already been started
+interceptorValve.notStarted=InterceptorValve has not yet been started
+naming.bindFailed=Failed to bind object: {0}
+naming.jmxRegistrationFailed=Failed to register in JMX: {0}
+naming.unbindFailed=Failed to unbind object: {0}
+naming.invalidEnvEntryType=Environment entry {0} has an invalid type
+naming.invalidEnvEntryValue=Environment entry {0} has an invalid value
+naming.namingContextCreationFailed=Creation of the naming context failed: {0}
+standardContext.invalidWrapperClass={0} is not a subclass of StandardWrapper
+standardContext.alreadyStarted=Context has already been started
+standardContext.applicationListener=Error configuring application listener of class {0}
+standardContext.applicationSkipped=Skipped installing application listeners due to previous error(s)
+standardContext.badRequest=Invalid request path ({0}).
+standardContext.errorPage.error=Error page location {0} must start with a ''/''
+standardContext.errorPage.required=ErrorPage cannot be null
+standardContext.errorPage.warning=WARNING: Error page location {0} must start with a ''/'' in Servlet 2.4
+standardContext.filterMap.either=Filter mapping must specify either a <url-pattern> or a <servlet-name>
+standardContext.filterMap.name=Filter mapping specifies an unknown filter name {0}
+standardContext.filterMap.pattern=Invalid <url-pattern> {0} in filter mapping
+standardContext.filterStart=Exception starting filter {0}
+standardContext.filterStartFailed=Failed to start application Filters successfully
+standardContext.requestListenerStartFailed=Failed to start request listener valve successfully
+standardContext.requestListenerConfig.added=Added request listener Valve
+standardContext.requestListenerConfig.error=Exception adding request listener Valve: {0}
+standardContext.isUnavailable=This application is not currently available
+standardContext.listenerStart=Exception sending context initialized event to listener instance of class {0}
+standardContext.listenerStartFailed=Failed to start application Listeners successfully
+standardContext.listenerStop=Exception sending context destroyed event to listener instance of class {0}
+standardContext.loginConfig.errorPage=Form error page {0} must start with a ''/'
+standardContext.loginConfig.errorWarning=WARNING: Form error page {0} must start with a ''/'' in Servlet 2.4
+standardContext.loginConfig.loginPage=Form login page {0} must start with a ''/'
+standardContext.loginConfig.loginWarning=WARNING: Form login page {0} must start with a ''/'' in Servlet 2.4
+standardContext.loginConfig.required=LoginConfig cannot be null
+standardContext.mappingError=MAPPING configuration error for relative URI {0}
+standardContext.notFound=The requested resource ({0}) is not available.
+standardContext.notReloadable=Reloading is disabled on this Context
+standardContext.notStarted=Context has not yet been started
+standardContext.notWrapper=Child of a Context must be a Wrapper
+standardContext.parameter.duplicate=Duplicate context initialization parameter {0}
+standardContext.parameter.required=Both parameter name and parameter value are required
+standardContext.reloadingCompleted=Reloading this Context is completed
+standardContext.reloadingFailed=Reloading this Context failed due to previous errors
+standardContext.reloadingStarted=Reloading this Context has started
+standardContext.resourcesStart=Error starting static Resources
+standardContext.securityConstraint.pattern=Invalid <url-pattern> {0} in security constraint
+standardContext.servletMap.name=Servlet mapping specifies an unknown servlet name {0}
+standardContext.servletMap.pattern=Invalid <url-pattern> {0} in servlet mapping
+standardContext.startCleanup=Exception during cleanup after start failed
+standardContext.startFailed=Context [{0}] startup failed due to previous errors
+standardContext.startingLoader=Exception starting Loader
+standardContext.startingManager=Exception starting Manager
+standardContext.startingWrapper=Exception starting Wrapper for servlet {0}
+standardContext.stoppingContext=Exception stopping Context
+standardContext.stoppingLoader=Exception stopping Loader
+standardContext.stoppingManager=Exception stopping Manager
+standardContext.stoppingWrapper=Exception stopping Wrapper for servlet {0}
+standardContext.urlDecode=Cannot URL decode request path {0}
+standardContext.urlPattern.patternWarning=WARNING: URL pattern {0} must start with a ''/'' in Servlet 2.4
+standardContext.urlValidate=Cannot validate URL decoded request path {0}
+standardContext.wrapper.error=JSP file {0} must start with a ''/'
+standardContext.wrapper.warning=WARNING: JSP file {0} must start with a ''/'' in Servlet 2.4
+standardEngine.alreadyStarted=Engine has already been started
+standardEngine.mappingError=MAPPING configuration error for server name {0}
+standardEngine.noHost=No Host matches server name {0}
+standardEngine.noHostHeader=HTTP/1.1 request with no Host: header
+standardEngine.notHost=Child of an Engine must be a Host
+standardEngine.notParent=Engine cannot have a parent Container
+standardEngine.notStarted=Engine has not yet been started
+standardEngine.unfoundHost=Virtual host {0} not found
+standardEngine.unknownHost=No server host specified in this request
+standardEngine.unregister.mbeans.failed=Error in destroy() for mbean file {0}
+standardHost.accessBase=Cannot access document base directory {0}
+standardHost.alreadyStarted=Host has already been started
+standardHost.appBase=Application base directory {0} does not exist
+standardHost.clientAbort=Remote Client Aborted Request, IOException: {0}
+standardHost.configRequired=URL to configuration file is required
+standardHost.configNotAllowed=Use of configuration file is not allowed
+standardHost.installBase=Only web applications in the Host web application directory can be installed
+standardHost.installing=Installing web application at context path {0} from URL {1}
+standardHost.installingWAR=Installing web application from URL {0}
+standardHost.installingXML=Processing Context configuration file URL {0}
+standardHost.installError=Error deploying application at context path {0}
+standardHost.invalidErrorReportValveClass=Couldn''t load specified error report valve class: {0}
+standardHost.docBase=Document base directory {0} already exists
+standardHost.mappingError=MAPPING configuration error for request URI {0}
+standardHost.noContext=No Context configured to process this request
+standardHost.noHost=No Host configured to process this request
+standardHost.notContext=Child of a Host must be a Context
+standardHost.notStarted=Host has not yet been started
+standardHost.nullName=Host name is required
+standardHost.pathFormat=Invalid context path: {0}
+standardHost.pathMatch=Context path {0} must match the directory or WAR file name: {1}
+standardHost.pathMissing=Context path {0} is not currently in use
+standardHost.pathRequired=Context path is required
+standardHost.pathUsed=Context path {0} is already in use
+standardHost.removing=Removing web application at context path {0}
+standardHost.removeError=Error removing application at context path {0}
+standardHost.start=Starting web application at context path {0}
+standardHost.stop=Stopping web application at context path {0}
+standardHost.unfoundContext=Cannot find context for request URI {0}
+standardHost.warRequired=URL to web application archive is required
+standardHost.warURL=Invalid URL for web application archive: {0}
+standardHost.validationEnabled=XML validation enabled
+standardHost.validationDisabled=XML validation disabled
+standardPipeline.alreadyStarted=Pipeline has already been started
+standardPipeline.notStarted=Pipeline has not been started
+standardPipeline.noValve=No more Valves in the Pipeline processing this request
+standardServer.addContainer.ise=No connectors available to associate this container with
+standardServer.initialize.initialized=This server has already been initialized
+standardServer.start.connectors=At least one connector is not associated with any container
+standardServer.start.started=This server has already been started
+standardServer.stop.notStarted=This server has not yet been started
+standardService.initialize.initialized=This service has already been initialized
+standardService.initialize.failed=Service initializing at {0} failed
+standardService.register.failed=Error registering Service at domain {0}
+standardService.start.name=Starting service {0}
+standardService.start.started=This service has already been started
+standardService.stop.name=Stopping service {0}
+standardService.stop.notStarted=This service has not yet been started
+standardWrapper.allocate=Error allocating a servlet instance
+standardWrapper.allocateException=Allocate exception for servlet {0}
+standardWrapper.containerServlet=Loading container servlet {0}
+standardWrapper.createFilters=Create filters exception for servlet {0}
+standardWrapper.deallocateException=Deallocate exception for servlet {0}
+standardWrapper.destroyException=Servlet.destroy() for servlet {0} threw exception
+standardWrapper.exception0=Tomcat Exception Report
+standardWrapper.exception1=A Servlet Exception Has Occurred
+standardWrapper.exception2=Exception Report:
+standardWrapper.exception3=Root Cause:
+standardWrapper.initException=Servlet.init() for servlet {0} threw exception
+standardWrapper.instantiate=Error instantiating servlet class {0}
+standardWrapper.isUnavailable=Servlet {0} is currently unavailable
+standardWrapper.jasperLoader=Using Jasper classloader for servlet {0}
+standardWrapper.jspFile.format=JSP file {0} does not start with a ''/'' character
+standardWrapper.loadException=Servlet {0} threw load() exception
+standardWrapper.missingClass=Wrapper cannot find servlet class {0} or a class it depends on
+standardWrapper.missingLoader=Wrapper cannot find Loader for servlet {0}
+standardWrapper.notChild=Wrapper container may not have child containers
+standardWrapper.notClass=No servlet class has been specified for servlet {0}
+standardWrapper.notContext=Parent container of a Wrapper must be a Context
+standardWrapper.notFound=Servlet {0} is not available
+standardWrapper.notServlet=Class {0} is not a Servlet
+standardWrapper.privilegedServlet=Servlet of class {0} is privileged and cannot be loaded by this web application
+standardWrapper.releaseFilters=Release filters exception for servlet {0}
+standardWrapper.serviceException=Servlet.service() for servlet {0} threw exception
+standardWrapper.statusHeader=HTTP Status {0} - {1}
+standardWrapper.statusTitle=Tomcat Error Report
+standardWrapper.unavailable=Marking servlet {0} as unavailable
+standardWrapper.unloadException=Servlet {0} threw unload() exception
+standardWrapper.unloading=Cannot allocate servlet {0} because it is being unloaded
+standardWrapper.waiting=Waiting for {0} instance(s) to be deallocated
diff --git a/container/catalina/src/share/org/apache/catalina/core/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/core/LocalStrings_es.properties
new file mode 100644
index 0000000..2281b6c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/LocalStrings_es.properties
@@ -0,0 +1,172 @@
+applicationContext.attributeEvent=Excepción lanzada por escuchador de eventos de atributos
+applicationContext.mapping.error=Error durante mapeo
+applicationContext.requestDispatcher.iae=La Trayectoria {0} no comienza con carácter "/"
+applicationContext.setAttribute.namenull=El nombre no puede ser nulo
+applicationDispatcher.allocateException=Excepción de reserva de espacio para servlet {0}
+applicationDispatcher.deallocateException=Excepción de recuperación de espacio para servlet {0}
+applicationDispatcher.forward.ise=No puedo reenviar después de que la respuesta se haya llevado a cabo.
+applicationDispatcher.forward.throw=El recurso reenviado lanzó un excepción
+applicationDispatcher.include.throw=El recurso incluído lanzó una excepción
+applicationDispatcher.isUnavailable=El Servlet {0} no está disponible en este momento
+applicationDispatcher.serviceException=El Servlet.service() para servlet {0} lanzó una excepción
+applicationRequest.badParent=No puedo localizar la implementación de Requerimiento padre
+applicationRequest.badRequest=El requerimiento no es un javax.servlet.ServletRequestWrapper
+applicationResponse.badParent=No puedo localizar implementación de Respuesta padre
+applicationResponse.badResponse=La Respuesta no es un javax.servlet.ServletResponseWrapper
+containerBase.addDefaultMapper=Excepción configurando mapeador por defecto de clase {0}
+containerBase.alreadyStarted=Ya ha sido arrancado el Contenedor {0}
+containerBase.notConfigured=No se ha configurado Válvula básica
+containerBase.notStarted=No se ha arrancado el Contenedor {0}
+fastEngineMapper.alreadyStarted=Ya se ha arrancado el FastEngineMapper {0}
+fastEngineMapper.notStarted=No se ha arrancado aún el FastEngineMapper {0}
+filterChain.filter=La ejecución del Filtro lanzó una excepción
+filterChain.servlet=La ejecución del Servlet lanzó una excepción
+httpContextMapper.container=Este Contenedor no es un StandardContext
+httpEngineMapper.container=Este Contenedor no es un StandardEngine
+httpHostMapper.container=Esta Contenedor no es una StandardHost
+interceptorValve.alreadyStarted=Ya ha sido arrancada la InterceptorValve
+interceptorValve.notStarted=Aún no ha sido arrancada la InterceptorValve
+naming.bindFailed=No pude cambiar (bind) objeto: {0}
+naming.unbindFailed=No pude descambiar (unbind) objecto: {0}
+naming.invalidEnvEntryType=La entrada de Entorno {0} tiene un tipo inválido
+naming.invalidEnvEntryValue=La entrada de Entorno {0} tiene un valor inválido
+naming.namingContextCreationFailed=Falló la creación del contexto de nombres (naming): {0}
+standardContext.alreadyStarted=Ya se ha arrancado el Contexto
+standardContext.applicationListener=Error configurando escuchador de aplicación de clase {0}
+standardContext.applicationSkipped=Se ha saltado la instalación de escuchadores de aplicación debido a error(es) previo(s)
+standardContext.badRequest=Trayectoria de requerimiento inválida ({0}).
+standardContext.errorPage.error=La localización de la página de error 0} debe de comenzar con ''/''
+standardContext.errorPage.required=ErrorPage no puede ser nulo
+standardContext.errorPage.warning=AVISO: La localización de la página de error {0} debe de comenzar con ''/'' en Servlet 2.4
+standardContext.filterMap.either=El mapeo de filtro debe de especificar o un <url-pattern> o un <servlet-name>
+standardContext.filterMap.name=El mapeo de filtro especifica un nombre desconocido de filtro {0}
+standardContext.filterMap.pattern=<url-pattern> {0} inválido en mapeo de filtro
+standardContext.filterStart=Excepción arrancando filtro {0}
+standardContext.filterStartFailed=No pude arrancar Filtros de aplicación con éxito
+standardContext.requestListenerStartFailed=No pude arrancar válvula de escuchador de requerimiento con exito
+standardContext.requestListenerConfig.added=Añadida Válvula de escuchador de requerimiento
+standardContext.requestListenerConfig.error=Excepción añadiendo Válvula de escuchador de requerimiento: {0}
+standardContext.isUnavailable=Esta aplicación no está disponible en este momento
+standardContext.listenerStart=Excepción enviando evento inicializado de contexto a instancia de escuchador de clase {0}
+standardContext.listenerStartFailed=No pude arrancar Escuchadores de aplicación con éxito
+standardContext.listenerStop=Excepción enviando evento de contexto destruído a instancia de escuchador de clase {0}
+standardContext.loginConfig.errorPage=La Página de error de Formulario {0} debe de comenzar con ''/'
+standardContext.loginConfig.errorWarning=AVISO: La página de error de Formulario {0} debe de comenzar con ''/'' en Servlet 2.4
+standardContext.loginConfig.loginPage=La página de login de Formulario {0} debe de comenzar con ''/'
+standardContext.loginConfig.loginWarning=AVISO: La página de login de Formulario {0} debe de comenzar con ''/'' en Servlet 2.4
+standardContext.loginConfig.required=LoginConfig no puede ser nula
+standardContext.mappingError=Error de configuración de MAPEO para URI relativa {0}
+standardContext.notFound=El recurso requerido ({0}) no se encuentra disponible
+standardContext.notReloadable=Está desactivada la recarga en este Contexto
+standardContext.notStarted=Aún no se ha arrancado el Contexto
+standardContext.notWrapper=El Hijo de un Contexto debe de ser un Arropador (Wrapper)
+standardContext.parameter.duplicate=Duplicado parámetro de inicialización de contexto {0}
+standardContext.parameter.required=Es necesario poner nombre de parámetro y valor de parámetro
+standardContext.reloadingCompleted=Se ha completado la Regarga de este Contexto
+standardContext.reloadingFailed=Falló la recarga de este Contexto debido a errores previos
+standardContext.reloadingStarted=Ha comenzado la recarga de este Contexto
+standardContext.resourcesStart=Error arrancando Recursos estáticos
+standardContext.securityConstraint.pattern=<url-pattern> {0} inválida en restricción de seguridad
+standardContext.servletMap.name=El mapeo de Servlet especifica un nombre de servlet desconocido {0}
+standardContext.servletMap.pattern=<url-pattern> {0} inválida en mapeo de servlet
+standardContext.startCleanup=Excepción durante la limpieza tras no poder arrancar
+standardContext.startFailed=Falló en arranque del Contexto [{0}] debido a errores previos
+standardContext.startingLoader=Excepción arrancando Cargador
+standardContext.startingManager=Excepción arrancando Gestor
+standardContext.startingWrapper=Excepción arrancando Arropador (Wrapper) para servlet {0}
+standardContext.stoppingContext=Excepci?n parando Context
+standardContext.stoppingLoader=Excepción parando Cargador
+standardContext.stoppingManager=Excepción parando Gestor
+standardContext.stoppingWrapper=Excepción parando Arropador (Wrapper) para servlet {0}
+standardContext.urlDecode=No puedo decodificar URL de trayectoria de requerimiento {0}
+standardContext.urlPattern.patternWarning=AVISO: el patrón URL {0} debe de comenzar con ''/'' en Servlet 2.4
+standardContext.urlValidate=No puedo validar trayectoria de requerimiento de URL decodificada {0}
+standardContext.wrapper.error=El archivo JSP {0} debe de comenzar con ''/'
+standardContext.wrapper.warning=AVISO: El archivo JSP {0} debe de comenzar con ''/'' en Servlet 2.4
+standardEngine.alreadyStarted=Ya ha sido arrancado el Motor
+standardEngine.mappingError=Error de configuración de MAPEO para nombre de servidor {0}
+standardEngine.noHost=No hay Máquina que coincida con nombre de servidor {0}
+standardEngine.noHostHeader=Requerimiento HTTP/1.1 sin Máquina: cabecera
+standardEngine.notHost=El Hijo de un Motor debe de ser un Máquina
+standardEngine.notParent=El Motor no puede tener un Contenedor padre
+standardEngine.notStarted=Aún no se ha arrancado el Motor
+standardEngine.unfoundHost=Máquina virtual {0} no hallada
+standardEngine.unknownHost=No se ha especificado máquina servidora en este requerimiento
+standardHost.accessBase=No puedo acceder a directorio base de documento {0}
+standardHost.alreadyStarted=Ya ha sido arrancada la Máquina
+standardHost.appBase=No existe el directorio base de aplicación {0}
+standardHost.clientAbort=El Cliente Remoto Abortó el Requerimiento, IOException: {0}
+standardHost.configRequired=Es necesario poner la URL a archivo de configuración
+standardHost.configNotAllowed=No se permite el uso del archivo de configuración
+standardHost.installBase=Sólo se pueden instalar aplicaciones web en el directorio de aplicaciones web de Máquina
+standardHost.installing=Instalando aplicaciones web en trayectoria de contexto {0} desde URL {1}
+standardHost.installingWAR=Instalando aplicación web desde URL {0}
+standardHost.installingXML=Procesando URL de archivo de configuración de Contexto {0}
+standardHost.installError=Error desplegando aplicación en trayectoria de contexto {0}
+standardHost.invalidErrorReportValveClass=No pude cargar clase especifiada de válvula de informe de error: {0}
+standardHost.docBase=Ya existe el directorio base de documento {0}
+standardHost.mappingError=Error de configuración de MAPEO para URI de requerimiento {0}
+standardHost.noContext=No se ha configurado Contexto para procesar este requerimiento
+standardHost.noHost=No se ha configurado Máquina para procesar este requerimiento
+standardHost.notContext=El Hijo de una Máquina debe de ser un Contexto
+standardHost.notStarted=Aún no se ha arrancado la Máquina
+standardHost.nullName=Es necesario poner el nombre de Máquina
+standardHost.pathFormat=Trayectoria de contexto inválida: {0}
+standardHost.pathMatch=La trayectoria de Contexto {0} debe de coincidir con el nombre de directorio o de archivo WAR: {1}
+standardHost.pathMissing=La trayectoria de Contexto {0} no está en uso en este momento
+standardHost.pathRequired=Es necesario poner la trayectoria de Contexto
+standardHost.pathUsed=Ya está en uso la trayectoria de Contexto {0}
+standardHost.removing=Quitando aplicación web en trayectoria de contexto {0}
+standardHost.removeError=Error quitando aplicación en trayectoria de contexto {0}
+standardHost.start=Arrancando aplicación web en trayectoria de contexto {0}
+standardHost.stop=Parando aplicación web en trayectoria de contexto {0}
+standardHost.unfoundContext=No puedo hallar contexto para URI de requerimiento {0}
+standardHost.warRequired=Es necesario poner la URL a archivo de aplicación web
+standardHost.warURL=URL inválida para archivo de aplicación web: {0}
+standardHost.validationEnabled=Activada la validación XML
+standardHost.validationDisabled=Desactivada la validación XML
+standardPipeline.alreadyStarted=Ya se ha arrancado la Tubería (Pipeline)
+standardPipeline.notStarted=No se ha arrancado la Tubería (Pipeline)
+standardPipeline.noValve=No hay más Válvulas en la Tubería (Pipeline) procesando este requerimiento
+standardServer.addContainer.ise=No hay conectores disponibles para ser asociados con este contenedor
+standardServer.initialize.initialized=Ya se ha inicializado este servidor
+standardServer.start.connectors=Al menos un conector no está asociado con cualquier contenedor
+standardServer.start.started=Ya ha sido arrancado este servidor
+standardServer.stop.notStarted=Aún no ha sido arrancado este servidor
+standardService.initialize.initialized=Ya ha sido inicializado este servicio
+standardService.start.name=Arrancando servicio {0}
+standardService.start.started=Ya ha sido arrancado este sercicio
+standardService.stop.name=Parando servicio {0}
+standardService.stop.notStarted=Aún no se ha arrancado este servicio
+standardWrapper.allocate=Error reservando espacio para una instancia de servlet
+standardWrapper.allocateException=Excepción de reserva de espacio para servlet {0}
+standardWrapper.containerServlet=Cargando servlet de contenedor {0}
+standardWrapper.createFilters=Excepción de creación de filtros para servlet {0}
+standardWrapper.deallocateException=Excepción de recuperación de espacio para servlet {0}
+standardWrapper.destroyException=Servlet.destroy() para servlet {0} lanzó excepción
+standardWrapper.exception0=Informe de Excepción de Tomcat
+standardWrapper.exception1=Ha tenido lugar una Excepción de Servlet
+standardWrapper.exception2=Informe de Excepción:
+standardWrapper.exception3=Causa Raíz:
+standardWrapper.initException=Servlet.init() para servlet {0} lanzó excepción
+standardWrapper.instantiate=Error instanciando clase de servlet {0}
+standardWrapper.isUnavailable=El Servlet {0} no está disponible en este momento
+standardWrapper.jasperLoader=Usando cargador de clases (classloader) de Jasper para servlet {0}
+standardWrapper.jspFile.format=El archivo JSP {0} no comienza con carácter ''/''
+standardWrapper.loadException=El Servlet {0} lanzó excepción de load()
+standardWrapper.missingClass=El Arropador (Wrapper) no puede hallar clase de servlet {0} o una clase de la que depende
+standardWrapper.missingLoader=El Arropador (Wrapper) no puede hallar Cargador para servlet {0}
+standardWrapper.notChild=El contenedor de Arropador (Wrapper) no puede tener contenedores hijo
+standardWrapper.notClass=No se ha especificado clase de servlet para servlet {0}
+standardWrapper.notContext=El contenedor padre para un Arropador (Wrapper) debe de ser un Contexto
+standardWrapper.notFound=No está disponible el Servlet {0}
+standardWrapper.notServlet=La Clase {0} no es un Servlet
+standardWrapper.privilegedServlet=El Servlet de clase {0} es privilegiado y no puede ser cargado mediante esta aplicación web
+standardWrapper.releaseFilters=Excepción de Liberación de filtros para servlet {0}
+standardWrapper.serviceException=Servlet.service() para servlet {0} lanzó excepción
+standardWrapper.statusHeader=HTTP Estado {0} - {1}
+standardWrapper.statusTitle=Informe de Error de Tomcat
+standardWrapper.unavailable=Marcando el servlet {0} como no disponible
+standardWrapper.unloadException=El Servlet {0} lanzó excepción unload()
+standardWrapper.unloading=No puedo reservar espacio para servlet {0} porque está siendo descargado
+standardWrapper.waiting=Esperando por {0} instancia(s) para recuperar su espacio reservado
diff --git a/container/catalina/src/share/org/apache/catalina/core/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/core/LocalStrings_fr.properties
new file mode 100644
index 0000000..633f2c3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/LocalStrings_fr.properties
@@ -0,0 +1,164 @@
+applicationContext.attributeEvent=Exception lancée par l''écouteur (listener) d''évènement attributs
+applicationContext.requestDispatcher.iae=Le chemin {0} ne commence pas par le caractère "/"
+applicationContext.setAttribute.namenull=le nom ne peut être nul
+applicationDispatcher.allocateException=Exception d''allocation pour la servlet {0}
+applicationDispatcher.deallocateException=Exception de désallocation pour la servlet {0}
+applicationDispatcher.forward.ise=Impossible d''utiliser faire-suivre (forward) après que la réponse ait été envoyée
+applicationDispatcher.forward.throw=La ressource faire-suivre (forwarded) a lancé une exception
+applicationDispatcher.include.throw=La ressource incluse (included) a lancé une exception
+applicationDispatcher.isUnavailable=La servlet {0} est actuellement indisponible
+applicationDispatcher.serviceException="Servlet.service()" pour la servlet {0} a lancé une exception
+applicationRequest.badParent=Impossible de trouver l''implementation requête apparentée (parent request)
+applicationRequest.badRequest=La requête n''est pas une "javax.servlet.ServletRequestWrapper"
+applicationResponse.badParent=Impossible de trouver une implémentation réponse apparentée (parent response)
+applicationResponse.badResponse=La réponse n''est pas une "javax.servlet.ServletResponseWrapper"
+containerBase.addDefaultMapper=Exception lors de la configuration du routeur par défaut (default mapper) pour la classe {0}
+containerBase.alreadyStarted=Le conteneur {0} a déjà été démarré
+containerBase.notConfigured=Aucune Valve basique (basic valve) n''a été configurée
+containerBase.notStarted=Le conteneur {0} n''a pas été démarré
+fastEngineMapper.alreadyStarted=le "FastEngineMapper" {0} a déjà été démarré
+fastEngineMapper.notStarted=Le "FastEngineMapper" {0} n''a pas encore été démarré
+filterChain.filter=L''exécution du filtre (Filter) a lancé une exception
+filterChain.servlet=L''exécution de la servlet a lancé une exception
+httpContextMapper.container=Ce conteneur n''est pas un "StandardContext"
+httpEngineMapper.container=Ce conteneur n''est pas un "StandardEngine"
+httpHostMapper.container=Ce conteneur n''est pas un "StandardHost"
+interceptorValve.alreadyStarted=La valve d''interception (InterceptorValve) a déjà été démarrée
+interceptorValve.notStarted=La valve d''interception (InterceptorValve) n''a pas encore été démarrée
+naming.bindFailed=Echec lors du liage à l''objet: {0}
+naming.unbindFailed=Echec lors du déliage à l''objet : {0}
+naming.invalidEnvEntryType=L''entrée environnement {0} a un type invalide
+naming.invalidEnvEntryValue=L''entrée environnement {0} a une valeur invalide
+naming.namingContextCreationFailed=La création du context de nommage (naming context) a échoué : {0}
+standardContext.alreadyStarted=Le contexte a déjà été démarré
+standardContext.applicationListener=Erreur lors de la configuration de la classe d''écoute de l''application (application listener) {0}
+standardContext.applicationSkipped=L''installation des écouteurs (listeners) de l''application a été sautée suite aux erreurs précédentes
+standardContext.badRequest=Chemin de requête invalide ({0}).
+standardContext.errorPage.error=La position de la page d''erreur (ErrorPage) {0} doit commencer par un ''/'
+standardContext.errorPage.required=La page d''erreur (ErrorPage) ne peut être nulle
+standardContext.errorPage.warning=ATTENTION: La position de la page d''erreur (ErrorPage) {0} doit commencer par un ''/'' dans l''API Servlet 2.4
+standardContext.filterMap.either=L''association de filtre (filter mapping) doit indiqué soit une <url-pattern> ou une <servlet-name>
+standardContext.filterMap.name=L''association de filtre (filter mapping) indique un nom de filtre inconnu {0}
+standardContext.filterMap.pattern=<url-pattern> {0} invalide dans l''association de filtre (filter mapping)
+standardContext.filterStart=Exception au démarrage du filtre {0}
+standardContext.filterStartFailed=Echec du démarrage des filtres d''application
+standardContext.requestListenerStartFailed=Echec démarrage des Valves d''écoute
+standardContext.requestListenerConfig.added=Ajout de la valve d''écoute
+standardContext.requestListenerConfig.error=Exception lors de l''ajout de la valve d''écoute de requête: {0}
+standardContext.isUnavailable=Cette application n''est pas disponible actuellement
+standardContext.listenerStart=Exception lors de l''envoi de l''évènement contexte initialisé (context initialized) à l''instance de classe d''écoute (listener) {0}
+standardContext.listenerStartFailed=Echec du démarrage des écouteurs (listeners) d''application
+standardContext.listenerStop=Exception lors de l''envoi de l''évènement contexte détruit (context destroyed) à l''instance de classe d''écoute {0}
+standardContext.loginConfig.errorPage=La forme de page d''erreur (form error page) {0} doit commencer par un ''/''
+standardContext.loginConfig.errorWarning=ATTENTION: La forme de page d''erreur (form error page) {0} doit commencer par un ''/'' dans l''API Servlet 2.4
+standardContext.loginConfig.loginPage=La forme de page de connexion (form login page) {0} doit commencer par un ''/''
+standardContext.loginConfig.loginWarning=ATTENTION: La forme de page de connexion (form login page) {0} doit commencer par un ''/'' dans l''API Servlet 2.4
+standardContext.loginConfig.required="LoginConfig" ne peut être nul
+standardContext.mappingError=Erreur dans la configuration d''association (mapping configuration) pour l''URI relative {0}
+standardContext.notFound=La ressource demandée ({0}) n''est pas disponible.
+standardContext.notReloadable=Le rechargement est désactivé pour ce contexte
+standardContext.notStarted=Le contexte n''a pas encore été démarré
+standardContext.notWrapper=Le fils du contexte (child of context) doit être un enrobeur (wrapper)
+standardContext.parameter.duplicate=Paramètre d''initialisation de contexte dupliqué {0}
+standardContext.parameter.required=Le nom de paramètre ainsi que la valeur du paramètre sont requis
+standardContext.reloadingCompleted=Le rechargement de ce contexte est terminé
+standardContext.reloadingFailed=Le rechargement de ce contexte a échoué suite à une erreur précédente
+standardContext.reloadingStarted=Le rechargement de ce contexte a démarré
+standardContext.securityConstraint.pattern=<url-pattern> {0} invalide d''après les contraintes de sécurité (security constraint)
+standardContext.servletMap.name=L''association de servlet (servlet mapping) indique un nom de servlet inconnu {0}
+standardContext.servletMap.pattern=<url-pattern> {0} invalide dans l''association de servlet (servlet mapping)
+standardContext.startCleanup=Exception lors du nettoyage après que le démarrage ait échoué
+standardContext.startFailed=Erreur de démarrage du contexte [{0}] suite aux erreurs précédentes
+standardContext.startingLoader=Exception an démarrage du "Loader"
+standardContext.startingManager=Exception an démarrage du "Manager"
+standardContext.startingWrapper=Exception an démarrage de l''enrobeur (wrapper) de la servlet {0}
+standardContext.stoppingContext=Exception ? l''arr?t du "Context"
+standardContext.stoppingLoader=Exception à l''arrêt du "Loader"
+standardContext.stoppingManager=Exception à l''arrêt du "Manager"
+standardContext.stoppingWrapper=Exception à l''arrêt de l''enrobeur (wrapper) de la servlet {0}
+standardContext.resourcesStart=Erreur lors du démarrage des Resources statiques
+standardContext.urlDecode=Impossible de décoder le chemin de requête encodé dans l''URL {0}
+standardContext.urlPattern.patternWarning=ATTENTION: Le modèle (pattern) URL {0} doit commencer par un ''/'' dans l''API Servlet 2.4
+standardContext.urlValidate=Impossible de valider le chemin de requête encodé dans l''URL {0}
+standardContext.wrapper.error=Le fichier JSP {0} doit commencer par un ''/''
+standardContext.wrapper.warning=ATTENTION: Le fichier JSP {0} doit commencer par un  ''/'' dans l''API Servlet 2.4
+standardEngine.alreadyStarted=Le moteur a déjà été démarré
+standardEngine.mappingError=Erreur de configuration d''association (mapping configuration) pour le serveur {0}
+standardEngine.noHost=Aucune hôte (host) ne correspond au nom de serveur {0}
+standardEngine.noHostHeader=requête HTTP/1.1 sans entête Host: 
+standardEngine.notHost=Le fils d''un moteur (child of an Engine) doit être un hôte
+standardEngine.notParent=Les moteurs (engine) ne peuvent avoir de parent conteneur (container)
+standardEngine.notStarted=Le moteur n''a pas encore été démarré
+standardEngine.unfoundHost=L''hôte virtuel (virtual host) {0} est introuvable
+standardEngine.unknownHost=Aucun serveur hôte n''est indiqué pour cette requête
+standardHost.accessBase=Impossible d''accéder le répertoire "document base" {0}
+standardHost.alreadyStarted=L''hôte a déjà été démarré
+standardHost.appBase=Le répertoire de base de l''application {0} n''existe pas
+standardHost.configRequired=Une URL vers le fichier de configuration est obligatoire
+standardHost.configNotAllowed=L''utilisation d''un fichier de configuration file n''est pas autorisé
+standardHost.installing=Installation d''une application pour le chemin de contexte {0} depuis l''URL {1}
+standardHost.installingWAR=Installation d''une application depuis l''URL {0}
+standardHost.installError=Erreur lors du déploiement de l''application pour le chemin de contexte {0}
+standardHost.invalidErrorReportValveClass=Impossible de charger la classe valve de rapport d''erreur: {0}
+standardHost.docBase=Le répertoire "document base" {0} existe déjà
+standardHost.mappingError=Erreur d''association de configuration (mapping configuration) pour l''URI demandée {0}
+standardHost.noContext=Aucune contexte n''est configuré pour traiter cette requête
+standardHost.noHost=Aucun hôte n''est configuré pour traiter cette requête
+standardHost.notContext=Le fils d''un hôte (child of a Host) doit être un contexte
+standardHost.notStarted=l''hôte n''a pas encore été démarré
+standardHost.nullName=Le nom d''hôte est requis
+standardHost.pathFormat=Chemin de contexte invalide: {0}
+standardHost.pathMissing=Le chemin de contexte {0} n''est pas utilisé actuellement
+standardHost.pathRequired=Le chemin de contexte est requis
+standardHost.pathUsed=Le chemin de contexte {0} est déjà utilisé
+standardHost.removing=Retrait de l''application web pour le chemin de contexte {0}
+standardHost.removeError=Erreur lors du retrait de l''application web pour le chemin de contexte {0}
+standardHost.start=Démarrage de l''application web application pour le chemin de contexte {0}
+standardHost.stop=Arrét de l''application web application pour le chemin de contexte {0}
+standardHost.unfoundContext=Impossible de trouver un contexte pour l''URI {0} demandée
+standardHost.warRequired=Une URL vers l''archive d''application web (war) est nécessaire 
+standardHost.warURL=URL vers l''archive d''application web (war) invalide: {0}
+standardPipeline.alreadyStarted=Le "Pipeline" a déjà été démarré
+standardPipeline.notStarted=le "Pipeline" n''a pas été démarré
+standardPipeline.noValve=Plus aucune Valves dans le "Pipeline" traitant cette requête
+standardServer.addContainer.ise=Aucun connecteur disponible à associer avec ce conteneur (container)
+standardServer.initialize.initialized=Ce serveur a déjà été initialisé
+standardServer.start.connectors=Au moins un connecteur n''est pas associé à aucun conteneur (container)
+standardServer.start.started=Ce serveur a déjà été démarré
+standardServer.stop.notStarted=Ce serveur n''a pas encore été démarré
+standardService.initialize.initialized=Ce service a déjà été initialisé
+standardService.start.name=Démarrage du service {0}
+standardService.start.started=Ce service a déjà été démarré
+standardService.stop.name=Arrêt du service {0}
+standardService.stop.notStarted=Ce service n''a pas encore été démarré
+standardWrapper.allocate=Erreur d''allocation à une instance de servlet
+standardWrapper.allocateException=Exception lors de l''allocation pour la servlet {0}
+standardWrapper.containerServlet=Chargement du conteneur (container) de servlet {0}
+standardWrapper.createFilters=Exception à la création de filtres pour la servlet {0}
+standardWrapper.deallocateException=Exception à la désallocation pour la servlet {0}
+standardWrapper.destroyException="Servlet.destroy()" de la servlet {0} a généré une exception
+standardWrapper.exception0=Rapport d''exception Tomcat
+standardWrapper.exception1=Une exception Servlet s''est produite
+standardWrapper.exception2=Rapport d''exception:
+standardWrapper.exception3=Cause mère:
+standardWrapper.initException="Servlet.init()" pour la servlet {0} a généré une exception
+standardWrapper.instantiate=Erreur à l''instantiation de la classe servlet {0}
+standardWrapper.isUnavailable=La servlet {0} est actuellement indisponible
+standardWrapper.jasperLoader=Utilisation du chargeur de classe Jasper (classloader) pour la servlet {0}
+standardWrapper.jspFile.format=Le fichier JSP {0} ne commence par par un caractère ''/''
+standardWrapper.loadException=La servlet {0} a généré une exception "load()"
+standardWrapper.missingClass=L''enrobeur (wrapper) ne peut trouver la classe servlet {0} ou une classe dont elle dépend
+standardWrapper.missingLoader=L''enrobeur (wrapper) ne peut trouver de chargeur (loader) pour la servlet {0}
+standardWrapper.notChild=L''enrobeur de conteneur (wrapper container) peut ne pas avoir de conteneurs fils
+standardWrapper.notClass=Aucune classe servlet n''a été spécifiée pour la servlet {0}
+standardWrapper.notContext=Le conteneur parent d''un enrobeur (wrapper) doit être un contexte
+standardWrapper.notFound=Servlet {0} n''est pas disponible.
+standardWrapper.notServlet=La classe {0} n''est pas une servlet
+standardWrapper.privilegedServlet=La servlet de classe {0} est privilégiée (privileged) et ne peut être chargé par cette application web
+standardWrapper.releaseFilters=Exception des filtres de sortie (release filters) pour la servlet {0}
+standardWrapper.serviceException="Servlet.service()" pour la servlet {0} a généré une exception
+standardWrapper.statusHeader=Etat HTTP {0} - {1}
+standardWrapper.statusTitle=Rapport d''erreur Tomcat
+standardWrapper.unavailable=La servlet {0} est marqué comme indisponible
+standardWrapper.unloadException=La servlet {0} a généré une exception "unload()"
+standardWrapper.unloading=Impossible d''allouer la servlet {0} car elle a été déchargée
diff --git a/container/catalina/src/share/org/apache/catalina/core/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/core/LocalStrings_ja.properties
new file mode 100644
index 0000000..c7a0bf0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/LocalStrings_ja.properties
@@ -0,0 +1,172 @@
+applicationContext.attributeEvent=\u5c5e\u6027\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u306b\u3088\u3063\u3066\u4f8b\u5916\u304c\u6295\u3052\u3089\u308c\u307e\u3057\u305f
+applicationContext.mapping.error=\u30de\u30c3\u30d4\u30f3\u30b0\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+applicationContext.requestDispatcher.iae=\u30d1\u30b9 {0} \u304c"/"\u6587\u5b57\u3067\u59cb\u307e\u308a\u307e\u305b\u3093
+applicationContext.setAttribute.namenull=name\u304cnull\u3067\u306f\u3044\u3051\u307e\u305b\u3093
+applicationDispatcher.allocateException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306b\u4f8b\u5916\u3092\u5272\u308a\u5f53\u3066\u307e\u3059
+applicationDispatcher.deallocateException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306e\u4f8b\u5916\u3092\u89e3\u9664\u3057\u307e\u3059
+applicationDispatcher.forward.ise=\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u30b3\u30df\u30c3\u30c8\u3057\u305f\u5f8c\u3067\u30d5\u30a9\u30ef\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093
+applicationDispatcher.forward.throw=\u30d5\u30a9\u30ef\u30fc\u30c9\u3057\u305f\u30ea\u30bd\u30fc\u30b9\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+applicationDispatcher.include.throw=\u30a4\u30f3\u30af\u30eb\u30fc\u30c9\u3057\u305f\u30ea\u30bd\u30fc\u30b9\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+applicationDispatcher.isUnavailable=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306f\u73fe\u5728\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+applicationDispatcher.serviceException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306eServlet.service()\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+applicationRequest.badParent=\u89aa\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u5b9f\u88c5\u3092\u914d\u7f6e\u3067\u304d\u307e\u305b\u3093
+applicationRequest.badRequest=\u30ea\u30af\u30a8\u30b9\u30c8\u304cjavax.servlet.ServletRequestWrapper\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+applicationResponse.badParent=\u89aa\u306e\u30ec\u30b9\u30dd\u30f3\u30b9\u5b9f\u88c5\u3092\u914d\u7f6e\u3067\u304d\u307e\u305b\u3093
+applicationResponse.badResponse=\u30ec\u30b9\u30dd\u30f3\u30b9\u304cjavax.servlet.ServletResponseWrapper\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+containerBase.addDefaultMapper=\u30af\u30e9\u30b9 {0} \u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30de\u30c3\u30d1\u3092\u8a2d\u5b9a\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+containerBase.alreadyStarted=\u30b3\u30f3\u30c6\u30ca {0} \u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+containerBase.notConfigured=\u57fa\u672c\u30d0\u30eb\u30d6\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+containerBase.notStarted=\u30b3\u30f3\u30c6\u30ca {0} \u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+fastEngineMapper.alreadyStarted=FastEngineMapper {0} \u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+fastEngineMapper.notStarted=FastEngineMapper {0} \u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+filterChain.filter=\u30d5\u30a3\u30eb\u30bf\u306e\u5b9f\u884c\u306b\u3088\u308a\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+filterChain.servlet=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u306e\u5b9f\u884c\u306b\u3088\u308a\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+httpContextMapper.container=\u3053\u306e\u30b3\u30f3\u30c6\u30ca\u306fStandardContext\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+httpEngineMapper.container=\u3053\u306e\u30b3\u30f3\u30c6\u30ca\u306fStandardEngine\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+httpHostMapper.container=\u3053\u306e\u30b3\u30f3\u30c6\u30ca\u306fStandardHost\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+interceptorValve.alreadyStarted=InterceptorValve\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+interceptorValve.notStarted=InterceptorValve\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+naming.bindFailed=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u30d0\u30a4\u30f3\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+naming.unbindFailed=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u30a2\u30f3\u30d0\u30a4\u30f3\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+naming.invalidEnvEntryType=\u74b0\u5883\u30a8\u30f3\u30c8\u30ea {0} \u306f\u7121\u52b9\u306a\u578b\u3092\u6301\u3063\u3066\u3044\u307e\u3059
+naming.invalidEnvEntryValue=\u74b0\u5883\u30a8\u30f3\u30c8\u30ea {0} \u306f\u7121\u52b9\u306a\u5024\u3092\u6301\u3063\u3066\u3044\u307e\u3059
+naming.namingContextCreationFailed=\u540d\u524d\u4ed8\u304d\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u751f\u6210\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+standardContext.alreadyStarted=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardContext.applicationListener=\u30af\u30e9\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30ea\u30b9\u30ca\u306e\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+standardContext.applicationSkipped=\u524d\u306e\u30a8\u30e9\u30fc\u306e\u305f\u3081\u306b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30ea\u30b9\u30ca\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059
+standardContext.badRequest=\u7121\u52b9\u306a\u30ea\u30af\u30a8\u30b9\u30c8\u30d1\u30b9\u3067\u3059 ({0})\u3002
+standardContext.errorPage.error=\u30a8\u30e9\u30fc\u30da\u30fc\u30b8\u306e\u4f4d\u7f6e {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.errorPage.required=ErrorPage\u304cnull\u3067\u306f\u3044\u3051\u307e\u305b\u3093
+standardContext.errorPage.warning=\u8b66\u544a: Servlet 2.4\u3067\u306f\u30a8\u30e9\u30fc\u30da\u30fc\u30b8\u306e\u4f4d\u7f6e {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.filterMap.either=\u30d5\u30a3\u30eb\u30bf\u30de\u30c3\u30d4\u30f3\u30b0\u306f<url-pattern>\u53c8\u306f<servlet-name>\u306e\u3069\u3061\u3089\u304b\u3092\u6307\u5b9a\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.filterMap.name=\u30d5\u30a3\u30eb\u30bf\u30de\u30c3\u30d4\u30f3\u30b0\u306f\u672a\u77e5\u306e\u30d5\u30a3\u30eb\u30bf\u540d {0} \u3092\u6307\u5b9a\u3057\u307e\u3057\u305f
+standardContext.filterMap.pattern=\u30d5\u30a3\u30eb\u30bf\u30de\u30c3\u30d4\u30f3\u30b0\u4e2d\u306b\u7121\u52b9\u306a <url-pattern> {0} \u304c\u3042\u308a\u307e\u3059
+standardContext.filterStart=\u30d5\u30a3\u30eb\u30bf {0} \u306e\u8d77\u52d5\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.filterStartFailed=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d5\u30a3\u30eb\u30bf\u306e\u8d77\u52d5\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+standardContext.requestListenerStartFailed=\u30ea\u30af\u30a8\u30b9\u30c8\u30ea\u30b9\u30ca\u30d0\u30eb\u30d6\u306e\u8d77\u52d5\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+standardContext.requestListenerConfig.added=\u30ea\u30af\u30a8\u30b9\u30c8\u30ea\u30b9\u30ca\u30d0\u30eb\u30d6\u3092\u8ffd\u52a0\u3057\u307e\u3057\u305f
+standardContext.requestListenerConfig.error=\u30ea\u30af\u30a8\u30b9\u30c8\u30ea\u30b9\u30ca\u30d0\u30eb\u30d6\u8ffd\u52a0\u4e2d\u306e\u4f8b\u5916\u3067\u3059: {0}
+standardContext.isUnavailable=\u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u73fe\u5728\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+standardContext.listenerStart=\u30af\u30e9\u30b9 {0} \u306e\u30ea\u30b9\u30ca\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u521d\u671f\u5316\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.listenerStartFailed=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30ea\u30b9\u30ca\u306e\u8d77\u52d5\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+standardContext.listenerStop=\u30af\u30e9\u30b9 {0} \u306e\u30ea\u30b9\u30ca\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u7834\u68c4\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.loginConfig.errorPage=\u30d5\u30a9\u30fc\u30e0\u306e\u30a8\u30e9\u30fc\u30da\u30fc\u30b8 {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.loginConfig.errorWarning=\u8b66\u544a: Servlet 2.4\u3067\u306f\u30d5\u30a9\u30fc\u30e0\u306e\u30a8\u30e9\u30fc\u30da\u30fc\u30b8 {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.loginConfig.loginPage=\u30d5\u30a9\u30fc\u30e0\u306e\u30ed\u30b0\u30a4\u30f3\u30da\u30fc\u30b8 {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.loginConfig.loginWarning=\u8b66\u544a: Servlet 2.4\u3067\u306f\u30d5\u30a9\u30fc\u30e0\u306e\u30ed\u30b0\u30a4\u30f3\u30da\u30fc\u30b8 {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.loginConfig.required=LoginConfig\u306fnull\u3067\u306f\u3044\u3051\u307e\u305b\u3093
+standardContext.mappingError=\u76f8\u5bfeURI {0} \u306e\u30de\u30c3\u30d4\u30f3\u30b0\u8a2d\u5b9a\u30a8\u30e9\u30fc\u3067\u3059
+standardContext.notFound=\u30ea\u30af\u30a8\u30b9\u30c8\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9 ({0}) \u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002
+standardContext.notReloadable=\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3067\u306f\u518d\u30ed\u30fc\u30c9\u306f\u7121\u52b9\u3067\u3059
+standardContext.notStarted=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardContext.notWrapper=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u5b50\u4f9b\u306f\u30e9\u30c3\u30d1\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.parameter.duplicate=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u521d\u671f\u5316\u30d1\u30e9\u30e1\u30bf {0} \u304c\u91cd\u8907\u3057\u3066\u3044\u307e\u3059
+standardContext.parameter.required=\u30d1\u30e9\u30e1\u30bf\u540d\u3068\u30d1\u30e9\u30e1\u30bf\u5024\u306e\u4e21\u65b9\u304c\u5fc5\u8981\u3067\u3059
+standardContext.reloadingCompleted=\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u518d\u30ed\u30fc\u30c9\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f
+standardContext.reloadingFailed=\u4ee5\u524d\u306e\u30a8\u30e9\u30fc\u306e\u305f\u3081\u306b\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u518d\u30ed\u30fc\u30c9\u304c\u5931\u6557\u3057\u307e\u3057\u305f
+standardContext.reloadingStarted=\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u518d\u30ed\u30fc\u30c9\u3092\u958b\u59cb\u3057\u307e\u3057\u305f
+standardContext.resourcesStart=\u9759\u7684\u30ea\u30bd\u30fc\u30b9\u306e\u8d77\u52d5\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+standardContext.securityConstraint.pattern=\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u306e\u5236\u7d04\u306e\u4e2d\u306b\u7121\u52b9\u306a <url-pattern> {0} \u304c\u3042\u308a\u307e\u3059
+standardContext.servletMap.name=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30de\u30c3\u30d4\u30f3\u30b0\u306f\u672a\u77e5\u306e\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u540d {0} \u3092\u6307\u5b9a\u3057\u3066\u3044\u307e\u3059
+standardContext.servletMap.pattern=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30de\u30c3\u30d4\u30f3\u30b0\u4e2d\u306b\u7121\u52b9\u306a <url-pattern> {0} \u304c\u3042\u308a\u307e\u3059
+standardContext.startCleanup=\u8d77\u52d5\u304c\u5931\u6557\u3057\u305f\u5f8c\u306e\u30af\u30ea\u30fc\u30f3\u30ca\u30c3\u30d7\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+standardContext.startFailed=\u4ee5\u524d\u306e\u30a8\u30e9\u30fc\u306e\u305f\u3081\u306b\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u8d77\u52d5\u304c\u5931\u6557\u3057\u307e\u3057\u305f [{0}]
+standardContext.startingLoader=\u30ed\u30fc\u30c0\u3092\u8d77\u52d5\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.startingManager=\u30de\u30cd\u30fc\u30b8\u30e3\u3092\u8d77\u52d5\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.startingWrapper=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306e\u30e9\u30c3\u30d1\u3092\u8d77\u52d5\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.stoppingContext=\u30ed\u30fc\u30c0\u3092\u505c\u6b62\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.stoppingLoader=\u30ed\u30fc\u30c0\u3092\u505c\u6b62\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.stoppingManager=\u30de\u30cd\u30fc\u30b8\u30e3\u3092\u505c\u6b62\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.stoppingWrapper=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306e\u30e9\u30c3\u30d1\u3092\u505c\u6b62\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardContext.urlDecode=\u30ea\u30af\u30a8\u30b9\u30c8\u30d1\u30b9 {0} \u306eURL\u30c7\u30b3\u30fc\u30c9\u304c\u3067\u304d\u307e\u305b\u3093
+standardContext.urlPattern.patternWarning=\u8b66\u544a: Servlet 2.4\u3067\u306fURL\u30d1\u30bf\u30fc\u30f3 {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.urlValidate=URL\u30c7\u30b3\u30fc\u30c9\u3055\u308c\u305f\u30ea\u30af\u30a8\u30b9\u30c8\u30d1\u30b9 {0} \u3092\u691c\u8a3c\u3067\u304d\u307e\u305b\u3093
+standardContext.wrapper.error=JSP\u30d5\u30a1\u30a4\u30eb {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardContext.wrapper.warning=\u8b66\u544a: Servlet 2.4\u3067\u306fJSP\u30d5\u30a1\u30a4\u30eb {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardEngine.alreadyStarted=\u30a8\u30f3\u30b8\u30f3\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardEngine.mappingError=\u30b5\u30fc\u30d0\u540d {0} \u306e\u30de\u30c3\u30d4\u30f3\u30b0\u8a2d\u5b9a\u30a8\u30e9\u30fc\u3067\u3059
+standardEngine.noHost=\u30b5\u30fc\u30d0\u540d {0} \u306b\u4e00\u81f4\u3059\u308b\u30db\u30b9\u30c8\u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+standardEngine.noHostHeader=Host:\u30d8\u30c3\u30c0\u3092\u6301\u305f\u306a\u3044 HTTP/1.1 \u30ea\u30af\u30a8\u30b9\u30c8\u3067\u3059
+standardEngine.notHost=\u30a8\u30f3\u30b8\u30f3\u306e\u5b50\u4f9b\u306f\u30db\u30b9\u30c8\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardEngine.notParent=\u30a8\u30f3\u30b8\u30f3\u306f\u89aa\u306e\u30b3\u30f3\u30c6\u30ca\u3092\u6301\u3064\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+standardEngine.notStarted=\u30a8\u30f3\u30b8\u30f3\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardEngine.unfoundHost=\u30d0\u30fc\u30c1\u30e3\u30eb\u30db\u30b9\u30c8 {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+standardEngine.unknownHost=\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u4e2d\u306b\u30b5\u30fc\u30d0\u30db\u30b9\u30c8\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardHost.accessBase=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u305b\u3093
+standardHost.alreadyStarted=\u30db\u30b9\u30c8\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardHost.appBase=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+standardHost.clientAbort=\u30ea\u30e2\u30fc\u30c8\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u4e2d\u6b62\u3057\u307e\u3057\u305f, IOException: {0}
+standardHost.configRequired=\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3078\u306eURL\u304c\u5fc5\u8981\u3067\u3059
+standardHost.configNotAllowed=\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u304c\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
+standardHost.installBase=\u30db\u30b9\u30c8Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u4e2d\u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3060\u3051\u304c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3067\u304d\u307e\u3059
+standardHost.installing=URL {1} \u304b\u3089\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306bWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u307e\u3059
+standardHost.installingWAR=URL {0} \u304b\u3089Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u307e\u3059
+standardHost.installingXML=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306eURL {0} \u3092\u51e6\u7406\u3057\u3066\u3044\u307e\u3059
+standardHost.installError=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+standardHost.invalidErrorReportValveClass=\u6307\u5b9a\u3055\u308c\u305f\u30a8\u30e9\u30fc\u30ea\u30dd\u30fc\u30c8\u30d0\u30eb\u30d6\u30af\u30e9\u30b9\u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093: {0}
+standardHost.docBase=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059
+standardHost.mappingError=\u30ea\u30af\u30a8\u30b9\u30c8URI {0} \u306e\u30de\u30c3\u30d4\u30f3\u30b0\u8a2d\u5b9a\u30a8\u30e9\u30fc\u3067\u3059
+standardHost.noContext=\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u8a2d\u5b9a\u3055\u308c\u305f\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u304c\u3042\u308a\u307e\u305b\u3093
+standardHost.noHost=\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u8a2d\u5b9a\u3055\u308c\u305f\u30db\u30b9\u30c8\u304c\u3042\u308a\u307e\u305b\u3093
+standardHost.notContext=\u30db\u30b9\u30c8\u306e\u5b50\u4f9b\u306f\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardHost.notStarted=\u30db\u30b9\u30c8\u304c\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardHost.nullName=\u30db\u30b9\u30c8\u540d\u304c\u5fc5\u8981\u3067\u3059
+standardHost.pathFormat=\u7121\u52b9\u306a\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9\u3067\u3059: {0}
+standardHost.pathMatch=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u53c8\u306fWAR\u30d5\u30a1\u30a4\u30eb\u540d\u306b\u4e00\u81f4\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093: {1}
+standardHost.pathMissing=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306f\u73fe\u5728\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardHost.pathRequired=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9\u304c\u5fc5\u8981\u3067\u3059
+standardHost.pathUsed=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306f\u65e2\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059
+standardHost.removing=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u524a\u9664\u3057\u307e\u3059
+standardHost.removeError=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u524a\u9664\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+standardHost.start=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u307e\u3059
+standardHost.stop=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u505c\u6b62\u3057\u307e\u3059
+standardHost.unfoundContext=\u30ea\u30af\u30a8\u30b9\u30c8URI {0} \u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u898b\u3064\u3051\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093
+standardHost.warRequired=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30a2\u30fc\u30ab\u30a4\u30d6\u306eURL\u304c\u5fc5\u8981\u3067\u3059
+standardHost.warURL=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30a2\u30fc\u30ab\u30a4\u30d6\u306b\u5bfe\u3059\u308b\u7121\u52b9\u306aURL\u3067\u3059: {0}
+standardHost.validationEnabled=XML\u691c\u8a3c\u306f\u6709\u52b9\u3067\u3059
+standardHost.validationDisabled=XML\u691c\u8a3c\u306f\u7121\u52b9\u3067\u3059
+standardPipeline.alreadyStarted=\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardPipeline.notStarted=\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardPipeline.noValve=\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u51e6\u7406\u3059\u308b\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u4e2d\u306b\u3053\u308c\u4ee5\u4e0a\u306e\u30d0\u30eb\u30d6\u306f\u3042\u308a\u307e\u305b\u3093
+standardServer.addContainer.ise=\u3053\u306e\u30b3\u30f3\u30c6\u30ca\u3092\u95a2\u9023\u3065\u3051\u308b\u305f\u3081\u306e\u30b3\u30cd\u30af\u30bf\u304c\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+standardServer.initialize.initialized=\u3053\u306e\u30b5\u30fc\u30d0\u306f\u65e2\u306b\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardServer.start.connectors=\u30b3\u30f3\u30c6\u30ca\u306b\u4e00\u3064\u3082\u30b3\u30cd\u30af\u30bf\u304c\u95a2\u9023\u3065\u3051\u3089\u308c\u3066\u3044\u307e\u305b\u3093
+standardServer.start.started=\u3053\u306e\u30b5\u30fc\u30d0\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardServer.stop.notStarted=\u3053\u306e\u30b5\u30fc\u30d0\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardService.initialize.initialized=\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u306f\u65e2\u306b\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardService.start.name=\u30b5\u30fc\u30d3\u30b9 {0} \u3092\u8d77\u52d5\u3057\u307e\u3059
+standardService.start.started=\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardService.stop.name=\u30b5\u30fc\u30d3\u30b9 {0} \u3092\u505c\u6b62\u3057\u307e\u3059
+standardService.stop.notStarted=\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardWrapper.allocate=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u5272\u308a\u5f53\u3066\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+standardWrapper.allocateException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306b\u4f8b\u5916\u3092\u5272\u308a\u5f53\u3066\u307e\u3059
+standardWrapper.containerServlet=\u30b3\u30f3\u30c6\u30ca\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u3092\u30ed\u30fc\u30c9\u3057\u307e\u3059
+standardWrapper.createFilters=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0}\u306b\u5bfe\u3059\u308b\u30d5\u30a3\u30eb\u30bf\u4f8b\u5916\u3092\u4f5c\u6210\u3057\u307e\u3059
+standardWrapper.deallocateException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306b\u5bfe\u3059\u308b\u4f8b\u5916\u306e\u5272\u308a\u5f53\u3066\u3092\u89e3\u9664\u3057\u307e\u3059
+standardWrapper.destroyException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306eServlet.destroy()\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+standardWrapper.exception0=Tomcat\u306e\u4f8b\u5916\u306e\u5831\u544a
+standardWrapper.exception1=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+standardWrapper.exception2=\u4f8b\u5916\u306e\u5831\u544a:
+standardWrapper.exception3=\u6839\u672c\u306e\u539f\u56e0:
+standardWrapper.initException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306eServlet.init()\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+standardWrapper.instantiate=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30af\u30e9\u30b9 {0} \u3092\u521d\u671f\u5316\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+standardWrapper.isUnavailable=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306f\u73fe\u5728\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+standardWrapper.jasperLoader=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306bJasper\u30af\u30e9\u30b9\u30ed\u30fc\u30c0\u3092\u4f7f\u7528\u3057\u307e\u3059
+standardWrapper.jspFile.format=JSP\u30d5\u30a1\u30a4\u30eb {0} \u304c''/''\u6587\u5b57\u3067\u59cb\u307e\u3063\u3066\u3044\u307e\u305b\u3093
+standardWrapper.loadException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u304cload()\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+standardWrapper.missingClass=\u30e9\u30c3\u30d1\u304c\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30af\u30e9\u30b9 {0} \u53c8\u306f\u305d\u308c\u304c\u4f9d\u5b58\u3059\u308b\u30af\u30e9\u30b9\u3092\u898b\u3064\u3051\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093
+standardWrapper.missingLoader=\u30e9\u30c3\u30d1\u304c\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306e\u30ed\u30fc\u30c0\u3092\u898b\u3064\u3051\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093
+standardWrapper.notChild=\u30e9\u30c3\u30d1\u30b3\u30f3\u30c6\u30ca\u306f\u5b50\u4f9b\u306e\u30b3\u30f3\u30c6\u30ca\u3092\u6301\u3064\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+standardWrapper.notClass=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306b\u6307\u5b9a\u3055\u308c\u305f\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30af\u30e9\u30b9\u304c\u3042\u308a\u307e\u305b\u3093
+standardWrapper.notContext=\u30e9\u30c3\u30d1\u306e\u89aa\u306e\u30b3\u30f3\u30c6\u30ca\u306f\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+standardWrapper.notFound=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u304c\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+standardWrapper.notServlet=\u30af\u30e9\u30b9 {0} \u306f\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+standardWrapper.privilegedServlet=\u30af\u30e9\u30b9 {0} \u306e\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u306f\u7279\u6a29\u3092\u4e0e\u3048\u3089\u308c\u3066\u3044\u308b\u306e\u3067\u3001\u3053\u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u3088\u3063\u3066\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093
+standardWrapper.releaseFilters=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306e\u30d5\u30a3\u30eb\u30bf\u4f8b\u5916\u3092\u89e3\u9664\u3057\u307e\u3059
+standardWrapper.serviceException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u306eServlet.service()\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+standardWrapper.statusHeader=HTTP\u30b9\u30c6\u30fc\u30bf\u30b9 {0} - {1}
+standardWrapper.statusTitle=Tomcat\u306e\u30a8\u30e9\u30fc\u306e\u5831\u544a
+standardWrapper.unavailable=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u3092\u5229\u7528\u4e0d\u53ef\u80fd\u306b\u30de\u30fc\u30af\u3057\u307e\u3059
+standardWrapper.unloadException=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u304cunload()\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+standardWrapper.unloading=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8 {0} \u304c\u30ed\u30fc\u30c9\u3055\u308c\u3066\u3044\u306a\u3044\u306e\u3067\u3001\u5272\u308a\u5f53\u3066\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093
+standardWrapper.waiting={0} \u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u304c\u5272\u308a\u5f53\u3066\u89e3\u9664\u3055\u308c\u308b\u306e\u3092\u5f85\u3063\u3066\u3044\u307e\u3059
diff --git a/container/catalina/src/share/org/apache/catalina/core/NamingContextListener.java b/container/catalina/src/share/org/apache/catalina/core/NamingContextListener.java
new file mode 100644
index 0000000..1f38308
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/NamingContextListener.java
@@ -0,0 +1,1016 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.ContainerListener;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.deploy.ContextEjb;
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.ContextLocalEjb;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.ContextResourceEnvRef;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.ContextTransaction;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+import org.apache.naming.ContextAccessController;
+import org.apache.naming.ContextBindings;
+import org.apache.naming.EjbRef;
+import org.apache.naming.NamingContext;
+import org.apache.naming.ResourceEnvRef;
+import org.apache.naming.ResourceLinkRef;
+import org.apache.naming.ResourceRef;
+import org.apache.naming.TransactionRef;
+
+
+/**
+ * Helper class used to initialize and populate the JNDI context associated
+ * with each context and server.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class NamingContextListener
+    implements LifecycleListener, ContainerListener, PropertyChangeListener {
+
+    private static Log log = LogFactory.getLog(NamingContextListener.class);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected Log logger = log;
+    
+    
+    /**
+     * Name of the associated naming context.
+     */
+    protected String name = "/";
+
+
+    /**
+     * Associated container.
+     */
+    protected Object container = null;
+
+
+    /**
+     * Initialized flag.
+     */
+    protected boolean initialized = false;
+
+
+    /**
+     * Associated naming resources.
+     */
+    protected NamingResources namingResources = null;
+
+
+    /**
+     * Associated JNDI context.
+     */
+    protected NamingContext namingContext = null;
+
+
+    /**
+     * Comp context.
+     */
+    protected javax.naming.Context compCtx = null;
+
+
+    /**
+     * Env context.
+     */
+    protected javax.naming.Context envCtx = null;
+
+    
+    /**
+     * Objectnames hashtable.
+     */
+    protected HashMap objectNames = new HashMap();
+    
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the "name" property.
+     */
+    public String getName() {
+
+        return (this.name);
+
+    }
+
+
+    /**
+     * Set the "name" property.
+     *
+     * @param name The new name
+     */
+    public void setName(String name) {
+
+        this.name = name;
+        if( log.isDebugEnabled() )
+            log.debug( "setName " + name);
+    }
+
+
+    /**
+     * Return the associated naming context.
+     */
+    public NamingContext getNamingContext() {
+
+        return (this.namingContext);
+
+    }
+
+
+    // ---------------------------------------------- LifecycleListener Methods
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event LifecycleEvent that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        container = event.getLifecycle();
+
+        if (container instanceof Context) {
+            namingResources = ((Context) container).getNamingResources();
+            logger = log;
+        } else if (container instanceof Server) {
+            namingResources = ((Server) container).getGlobalNamingResources();
+        } else {
+            return;
+        }
+
+        if (event.getType() == Lifecycle.START_EVENT) {
+
+            if (initialized)
+                return;
+
+            Hashtable contextEnv = new Hashtable();
+            try {
+                namingContext = new NamingContext(contextEnv, getName());
+            } catch (NamingException e) {
+                // Never happens
+            }
+            ContextAccessController.setSecurityToken(getName(), container);
+            ContextBindings.bindContext(container, namingContext, container);
+            if( log.isDebugEnabled() ) {
+                log.debug("Bound " + container );
+            }
+
+            // Setting the context in read/write mode
+            ContextAccessController.setWritable(getName(), container);
+
+            try {
+                createNamingContext();
+            } catch (NamingException e) {
+                logger.error
+                    (sm.getString("naming.namingContextCreationFailed", e));
+            }
+
+            // Binding the naming context to the class loader
+            if (container instanceof Context) {
+                // Setting the context in read only mode
+                ContextAccessController.setReadOnly(getName());
+                try {
+                    ContextBindings.bindClassLoader
+                        (container, container, 
+                         ((Container) container).getLoader().getClassLoader());
+                } catch (NamingException e) {
+                    logger.error(sm.getString("naming.bindFailed", e));
+                }
+            }
+
+            if (container instanceof Server) {
+                namingResources.addPropertyChangeListener(this);
+                org.apache.naming.factory.ResourceLinkFactory.setGlobalContext
+                    (namingContext);
+                try {
+                    ContextBindings.bindClassLoader
+                        (container, container, 
+                         this.getClass().getClassLoader());
+                } catch (NamingException e) {
+                    logger.error(sm.getString("naming.bindFailed", e));
+                }
+                if (container instanceof StandardServer) {
+                    ((StandardServer) container).setGlobalNamingContext
+                        (namingContext);
+                }
+            }
+
+            initialized = true;
+
+        } else if (event.getType() == Lifecycle.STOP_EVENT) {
+
+            if (!initialized)
+                return;
+
+            // Setting the context in read/write mode
+            ContextAccessController.setWritable(getName(), container);
+            ContextBindings.unbindContext(container, container);
+
+            if (container instanceof Context) {
+                ContextBindings.unbindClassLoader
+                    (container, container, 
+                     ((Container) container).getLoader().getClassLoader());
+            }
+
+            if (container instanceof Server) {
+                namingResources.removePropertyChangeListener(this);
+                ContextBindings.unbindClassLoader
+                    (container, container, 
+                     this.getClass().getClassLoader());
+            }
+
+            ContextAccessController.unsetSecurityToken(getName(), container);
+
+            namingContext = null;
+            envCtx = null;
+            compCtx = null;
+            initialized = false;
+
+        }
+
+    }
+
+
+    // ---------------------------------------------- ContainerListener Methods
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     * Note: Will never be called when the listener is associated to a Server,
+     * since it is not a Container.
+     *
+     * @param event ContainerEvent that has occurred
+     */
+    public void containerEvent(ContainerEvent event) {
+
+        if (!initialized)
+            return;
+
+        // Setting the context in read/write mode
+        ContextAccessController.setWritable(getName(), container);
+
+        String type = event.getType();
+
+        if (type.equals("addEjb")) {
+
+            String ejbName = (String) event.getData();
+            if (ejbName != null) {
+                ContextEjb ejb = namingResources.findEjb(ejbName);
+                addEjb(ejb);
+            }
+
+        } else if (type.equals("addEnvironment")) {
+
+            String environmentName = (String) event.getData();
+            if (environmentName != null) {
+                ContextEnvironment env = 
+                    namingResources.findEnvironment(environmentName);
+                addEnvironment(env);
+            }
+
+        } else if (type.equals("addLocalEjb")) {
+
+            String localEjbName = (String) event.getData();
+            if (localEjbName != null) {
+                ContextLocalEjb localEjb = 
+                    namingResources.findLocalEjb(localEjbName);
+                addLocalEjb(localEjb);
+            }
+
+        } else if (type.equals("addResource")) {
+
+            String resourceName = (String) event.getData();
+            if (resourceName != null) {
+                ContextResource resource = 
+                    namingResources.findResource(resourceName);
+                addResource(resource);
+            }
+
+        } else if (type.equals("addResourceLink")) {
+
+            String resourceLinkName = (String) event.getData();
+            if (resourceLinkName != null) {
+                ContextResourceLink resourceLink = 
+                    namingResources.findResourceLink(resourceLinkName);
+                addResourceLink(resourceLink);
+            }
+
+        } else if (type.equals("addResourceEnvRef")) {
+
+            String resourceEnvRefName = (String) event.getData();
+            if (resourceEnvRefName != null) {
+                ContextResourceEnvRef resourceEnvRef = 
+                    namingResources.findResourceEnvRef(resourceEnvRefName);
+                addResourceEnvRef(resourceEnvRef);
+            }
+
+        } else if (type.equals("removeEjb")) {
+
+            String ejbName = (String) event.getData();
+            if (ejbName != null) {
+                removeEjb(ejbName);
+            }
+
+        } else if (type.equals("removeEnvironment")) {
+
+            String environmentName = (String) event.getData();
+            if (environmentName != null) {
+                removeEnvironment(environmentName);
+            }
+
+        } else if (type.equals("removeLocalEjb")) {
+
+            String localEjbName = (String) event.getData();
+            if (localEjbName != null) {
+                removeLocalEjb(localEjbName);
+            }
+
+        } else if (type.equals("removeResource")) {
+
+            String resourceName = (String) event.getData();
+            if (resourceName != null) {
+                removeResource(resourceName);
+            }
+
+        } else if (type.equals("removeResourceLink")) {
+
+            String resourceLinkName = (String) event.getData();
+            if (resourceLinkName != null) {
+                removeResourceLink(resourceLinkName);
+            }
+
+        } else if (type.equals("removeResourceEnvRef")) {
+
+            String resourceEnvRefName = (String) event.getData();
+            if (resourceEnvRefName != null) {
+                removeResourceEnvRef(resourceEnvRefName);
+            }
+
+        }
+
+        // Setting the context in read only mode
+        ContextAccessController.setReadOnly(getName());
+
+    }
+
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+
+    /**
+     * Process property change events.  Currently, only listens to such events
+     * on the <code>NamingResources</code> instance for the global naming
+     * resources.
+     *
+     * @param event The property change event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        if (!initialized)
+            return;
+
+        Object source = event.getSource();
+        if (source == namingResources) {
+
+            // Setting the context in read/write mode
+            ContextAccessController.setWritable(getName(), container);
+
+            processGlobalResourcesChange(event.getPropertyName(),
+                                         event.getOldValue(),
+                                         event.getNewValue());
+
+            // Setting the context in read only mode
+            ContextAccessController.setReadOnly(getName());
+
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Process a property change on the global naming resources, by making the
+     * corresponding addition or removal to the associated JNDI context.
+     *
+     * @param name Property name of the change to be processed
+     * @param oldValue The old value (or <code>null</code> if adding)
+     * @param newValue The new value (or <code>null</code> if removing)
+     */
+    private void processGlobalResourcesChange(String name,
+                                              Object oldValue,
+                                              Object newValue) {
+
+        // NOTE - It seems that the Context for global JNDI resources
+        // is left in read-write mode, so we do not have to change it here
+
+        if (name.equals("ejb")) {
+            if (oldValue != null) {
+                ContextEjb ejb = (ContextEjb) oldValue;
+                if (ejb.getName() != null) {
+                    removeEjb(ejb.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextEjb ejb = (ContextEjb) newValue;
+                if (ejb.getName() != null) {
+                    addEjb(ejb);
+                }
+            }
+        } else if (name.equals("environment")) {
+            if (oldValue != null) {
+                ContextEnvironment env = (ContextEnvironment) oldValue;
+                if (env.getName() != null) {
+                    removeEnvironment(env.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextEnvironment env = (ContextEnvironment) newValue;
+                if (env.getName() != null) {
+                    addEnvironment(env);
+                }
+            }
+        } else if (name.equals("localEjb")) {
+            if (oldValue != null) {
+                ContextLocalEjb ejb = (ContextLocalEjb) oldValue;
+                if (ejb.getName() != null) {
+                    removeLocalEjb(ejb.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextLocalEjb ejb = (ContextLocalEjb) newValue;
+                if (ejb.getName() != null) {
+                    addLocalEjb(ejb);
+                }
+            }
+        } else if (name.equals("resource")) {
+            if (oldValue != null) {
+                ContextResource resource = (ContextResource) oldValue;
+                if (resource.getName() != null) {
+                    removeResource(resource.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextResource resource = (ContextResource) newValue;
+                if (resource.getName() != null) {
+                    addResource(resource);
+                }
+            }
+        } else if (name.equals("resourceEnvRef")) {
+            if (oldValue != null) {
+                ContextResourceEnvRef resourceEnvRef = 
+                    (ContextResourceEnvRef) oldValue;
+                if (resourceEnvRef.getName() != null) {
+                    removeResourceEnvRef(resourceEnvRef.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextResourceEnvRef resourceEnvRef = 
+                    (ContextResourceEnvRef) newValue;
+                if (resourceEnvRef.getName() != null) {
+                    addResourceEnvRef(resourceEnvRef);
+                }
+            }
+        } else if (name.equals("resourceLink")) {
+            if (oldValue != null) {
+                ContextResourceLink rl = (ContextResourceLink) oldValue;
+                if (rl.getName() != null) {
+                    removeResourceLink(rl.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextResourceLink rl = (ContextResourceLink) newValue;
+                if (rl.getName() != null) {
+                    addResourceLink(rl);
+                }
+            }
+        }
+
+
+    }
+
+
+    /**
+     * Create and initialize the JNDI naming context.
+     */
+    private void createNamingContext()
+        throws NamingException {
+
+        // Creating the comp subcontext
+        if (container instanceof Server) {
+            compCtx = namingContext;
+            envCtx = namingContext;
+        } else {
+            compCtx = namingContext.createSubcontext("comp");
+            envCtx = compCtx.createSubcontext("env");
+        }
+
+        int i;
+
+        if (log.isDebugEnabled())
+            log.debug("Creating JNDI naming context");
+
+        if (namingResources == null) {
+            namingResources = new NamingResources();
+            namingResources.setContainer(container);
+        }
+
+        // Resource links
+        ContextResourceLink[] resourceLinks = 
+            namingResources.findResourceLinks();
+        for (i = 0; i < resourceLinks.length; i++) {
+            addResourceLink(resourceLinks[i]);
+        }
+
+        // Resources
+        ContextResource[] resources = namingResources.findResources();
+        for (i = 0; i < resources.length; i++) {
+            addResource(resources[i]);
+        }
+
+        // Resources Env
+        ContextResourceEnvRef[] resourceEnvRefs = namingResources.findResourceEnvRefs();
+        for (i = 0; i < resourceEnvRefs.length; i++) {
+            addResourceEnvRef(resourceEnvRefs[i]);
+        }
+
+        // Environment entries
+        ContextEnvironment[] contextEnvironments = 
+            namingResources.findEnvironments();
+        for (i = 0; i < contextEnvironments.length; i++) {
+            addEnvironment(contextEnvironments[i]);
+        }
+
+        // EJB references
+        ContextEjb[] ejbs = namingResources.findEjbs();
+        for (i = 0; i < ejbs.length; i++) {
+            addEjb(ejbs[i]);
+        }
+
+        // Binding a User Transaction reference
+        if (container instanceof Context) {
+            try {
+                Reference ref = new TransactionRef();
+                compCtx.bind("UserTransaction", ref);
+                ContextTransaction transaction = namingResources.getTransaction();
+                if (transaction != null) {
+                    Iterator params = transaction.listProperties();
+                    while (params.hasNext()) {
+                        String paramName = (String) params.next();
+                        String paramValue = (String) transaction.getProperty(paramName);
+                        StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
+                        ref.add(refAddr);
+                    }
+                }
+            } catch (NameAlreadyBoundException e) {
+                // Ignore because UserTransaction was obviously 
+                // added via ResourceLink
+            } catch (NamingException e) {
+                logger.error(sm.getString("naming.bindFailed", e));
+            }
+        }
+
+        // Binding the resources directory context
+        if (container instanceof Context) {
+            try {
+                compCtx.bind("Resources", 
+                             ((Container) container).getResources());
+            } catch (NamingException e) {
+                logger.error(sm.getString("naming.bindFailed", e));
+            }
+        }
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>ContextResource</code> object.
+     *
+     * @param resource The resource
+     * @return ObjectName The object name
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    protected ObjectName createObjectName(ContextResource resource)
+        throws MalformedObjectNameException {
+
+        String domain = null;
+        if (container instanceof StandardServer) {
+            domain = ((StandardServer) container).getDomain();
+        } else if (container instanceof ContainerBase) {
+            domain = ((ContainerBase) container).getDomain();
+        }
+        if (domain == null) {
+            domain = "Catalina";
+        }
+        
+        ObjectName name = null;
+        String quotedResourceName = ObjectName.quote(resource.getName());
+        if (container instanceof Server) {        
+            name = new ObjectName(domain + ":type=DataSource" +
+                        ",class=" + resource.getType() + 
+                        ",name=" + quotedResourceName);
+        } else if (container instanceof Context) {                    
+            String path = ((Context)container).getPath();
+            if (path.length() < 1)
+                path = "/";
+            Host host = (Host) ((Context)container).getParent();
+            Engine engine = (Engine) host.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=DataSource" +
+                        ",path=" + path + 
+                        ",host=" + host.getName() +
+                        ",class=" + resource.getType() +
+                        ",name=" + quotedResourceName);
+        }
+        
+        return (name);
+
+    }
+
+    
+    /**
+     * Set the specified EJBs in the naming context.
+     */
+    public void addEjb(ContextEjb ejb) {
+
+        // Create a reference to the EJB.
+        Reference ref = new EjbRef
+            (ejb.getType(), ejb.getHome(), ejb.getRemote(), ejb.getLink());
+        // Adding the additional parameters, if any
+        Iterator params = ejb.listProperties();
+        while (params.hasNext()) {
+            String paramName = (String) params.next();
+            String paramValue = (String) ejb.getProperty(paramName);
+            StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
+            ref.add(refAddr);
+        }
+        try {
+            createSubcontexts(envCtx, ejb.getName());
+            envCtx.bind(ejb.getName(), ref);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.bindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Set the specified environment entries in the naming context.
+     */
+    public void addEnvironment(ContextEnvironment env) {
+
+        Object value = null;
+        // Instantiating a new instance of the correct object type, and
+        // initializing it.
+        String type = env.getType();
+        try {
+            if (type.equals("java.lang.String")) {
+                value = env.getValue();
+            } else if (type.equals("java.lang.Byte")) {
+                if (env.getValue() == null) {
+                    value = new Byte((byte) 0);
+                } else {
+                    value = Byte.decode(env.getValue());
+                }
+            } else if (type.equals("java.lang.Short")) {
+                if (env.getValue() == null) {
+                    value = new Short((short) 0);
+                } else {
+                    value = Short.decode(env.getValue());
+                }
+            } else if (type.equals("java.lang.Integer")) {
+                if (env.getValue() == null) {
+                    value = new Integer(0);
+                } else {
+                    value = Integer.decode(env.getValue());
+                }
+            } else if (type.equals("java.lang.Long")) {
+                if (env.getValue() == null) {
+                    value = new Long(0);
+                } else {
+                    value = Long.decode(env.getValue());
+                }
+            } else if (type.equals("java.lang.Boolean")) {
+                value = Boolean.valueOf(env.getValue());
+            } else if (type.equals("java.lang.Double")) {
+                if (env.getValue() == null) {
+                    value = new Double(0);
+                } else {
+                    value = Double.valueOf(env.getValue());
+                }
+            } else if (type.equals("java.lang.Float")) {
+                if (env.getValue() == null) {
+                    value = new Float(0);
+                } else {
+                    value = Float.valueOf(env.getValue());
+                }
+            } else if (type.equals("java.lang.Character")) {
+                if (env.getValue() == null) {
+                    value = new Character((char) 0);
+                } else {
+                    if (env.getValue().length() == 1) {
+                        value = new Character(env.getValue().charAt(0));
+                    } else {
+                        throw new IllegalArgumentException();
+                    }
+                }
+            } else {
+                logger.error(sm.getString("naming.invalidEnvEntryType", env.getName()));
+            }
+        } catch (NumberFormatException e) {
+            logger.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
+        } catch (IllegalArgumentException e) {
+            logger.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
+        }
+
+        // Binding the object to the appropriate name
+        if (value != null) {
+            try {
+                if (logger.isDebugEnabled())
+                    logger.debug("  Adding environment entry " + env.getName());
+                createSubcontexts(envCtx, env.getName());
+                envCtx.bind(env.getName(), value);
+            } catch (NamingException e) {
+                logger.error(sm.getString("naming.invalidEnvEntryValue", e));
+            }
+        }
+
+    }
+
+
+    /**
+     * Set the specified local EJBs in the naming context.
+     */
+    public void addLocalEjb(ContextLocalEjb localEjb) {
+
+
+
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void addResource(ContextResource resource) {
+
+        // Create a reference to the resource.
+        Reference ref = new ResourceRef
+            (resource.getType(), resource.getDescription(),
+             resource.getScope(), resource.getAuth());
+        // Adding the additional parameters, if any
+        Iterator params = resource.listProperties();
+        while (params.hasNext()) {
+            String paramName = (String) params.next();
+            String paramValue = (String) resource.getProperty(paramName);
+            StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
+            ref.add(refAddr);
+        }
+        try {
+            if (logger.isDebugEnabled()) {
+                logger.debug("  Adding resource ref " 
+                             + resource.getName() + "  " + ref);
+            }
+            createSubcontexts(envCtx, resource.getName());
+            envCtx.bind(resource.getName(), ref);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.bindFailed", e));
+        }
+
+        if ("javax.sql.DataSource".equals(ref.getClassName())) {
+            try {
+                ObjectName on = createObjectName(resource);
+                Object actualResource = envCtx.lookup(resource.getName());
+                Registry.getRegistry(null, null).registerComponent(actualResource, on, null);
+                objectNames.put(resource.getName(), on);
+            } catch (Exception e) {
+                logger.warn(sm.getString("naming.jmxRegistrationFailed", e));
+            }
+        }
+        
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void addResourceEnvRef(ContextResourceEnvRef resourceEnvRef) {
+
+        // Create a reference to the resource env.
+        Reference ref = new ResourceEnvRef(resourceEnvRef.getType());
+        // Adding the additional parameters, if any
+        Iterator params = resourceEnvRef.listProperties();
+        while (params.hasNext()) {
+            String paramName = (String) params.next();
+            String paramValue = (String) resourceEnvRef.getProperty(paramName);
+            StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
+            ref.add(refAddr);
+        }
+        try {
+            if (logger.isDebugEnabled())
+                log.debug("  Adding resource env ref " + resourceEnvRef.getName());
+            createSubcontexts(envCtx, resourceEnvRef.getName());
+            envCtx.bind(resourceEnvRef.getName(), ref);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.bindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Set the specified resource link in the naming context.
+     */
+    public void addResourceLink(ContextResourceLink resourceLink) {
+
+        // Create a reference to the resource.
+        Reference ref = new ResourceLinkRef
+            (resourceLink.getType(), resourceLink.getGlobal());
+        javax.naming.Context ctx = 
+            "UserTransaction".equals(resourceLink.getName()) 
+            ? compCtx : envCtx;
+        try {
+            if (logger.isDebugEnabled())
+                log.debug("  Adding resource link " + resourceLink.getName());
+            createSubcontexts(envCtx, resourceLink.getName());
+            ctx.bind(resourceLink.getName(), ref);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.bindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Set the specified EJBs in the naming context.
+     */
+    public void removeEjb(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.unbindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Set the specified environment entries in the naming context.
+     */
+    public void removeEnvironment(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.unbindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Set the specified local EJBs in the naming context.
+     */
+    public void removeLocalEjb(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.unbindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void removeResource(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.unbindFailed", e));
+        }
+
+        ObjectName on = (ObjectName) objectNames.get(name);
+        if (on != null) {
+            Registry.getRegistry(null, null).unregisterComponent(on);
+        }
+
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void removeResourceEnvRef(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.unbindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void removeResourceLink(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            logger.error(sm.getString("naming.unbindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Create all intermediate subcontexts.
+     */
+    private void createSubcontexts(javax.naming.Context ctx, String name)
+        throws NamingException {
+        javax.naming.Context currentContext = ctx;
+        StringTokenizer tokenizer = new StringTokenizer(name, "/");
+        while (tokenizer.hasMoreTokens()) {
+            String token = tokenizer.nextToken();
+            if ((!token.equals("")) && (tokenizer.hasMoreTokens())) {
+                try {
+                    currentContext = currentContext.createSubcontext(token);
+                } catch (NamingException e) {
+                    // Silent catch. Probably an object is already bound in
+                    // the context.
+                    currentContext =
+                        (javax.naming.Context) currentContext.lookup(token);
+                }
+            }
+        }
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardContext.java b/container/catalina/src/share/org/apache/catalina/core/StandardContext.java
new file mode 100644
index 0000000..e2af0b5
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardContext.java
@@ -0,0 +1,5365 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Stack;
+import java.util.TreeMap;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerListener;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.InstanceListener;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.deploy.ApplicationParameter;
+import org.apache.catalina.deploy.ErrorPage;
+import org.apache.catalina.deploy.FilterDef;
+import org.apache.catalina.deploy.FilterMap;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.deploy.MessageDestination;
+import org.apache.catalina.deploy.MessageDestinationRef;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.deploy.SecurityCollection;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.startup.ContextConfig;
+import org.apache.catalina.startup.TldConfig;
+import org.apache.catalina.util.CharsetMapper;
+import org.apache.catalina.util.ExtensionValidator;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.URLEncoder;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+import org.apache.naming.ContextBindings;
+import org.apache.naming.resources.BaseDirContext;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.apache.naming.resources.FileDirContext;
+import org.apache.naming.resources.ProxyDirContext;
+import org.apache.naming.resources.WARDirContext;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * Standard implementation of the <b>Context</b> interface.  Each
+ * child container must be a Wrapper implementation to process the
+ * requests directed to a particular servlet.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class StandardContext
+    extends ContainerBase
+    implements Context, Serializable, NotificationEmitter
+{
+    private static transient Log log = LogFactory.getLog(StandardContext.class);
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new StandardContext component with the default basic Valve.
+     */
+    public StandardContext() {
+
+        super();
+        pipeline.setBasic(new StandardContextValve());
+        broadcaster = new NotificationBroadcasterSupport();
+
+    }
+
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardContext/1.0";
+
+
+    /**
+     * JDK compatibility support
+     */
+    private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+
+    /**
+     * Array containing the safe characters set.
+     */
+    protected static URLEncoder urlEncoder;
+
+
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    static {
+        urlEncoder = new URLEncoder();
+        urlEncoder.addSafeCharacter('~');
+        urlEncoder.addSafeCharacter('-');
+        urlEncoder.addSafeCharacter('_');
+        urlEncoder.addSafeCharacter('.');
+        urlEncoder.addSafeCharacter('*');
+        urlEncoder.addSafeCharacter('/');
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The alternate deployment descriptor name.
+     */
+    private String altDDName = null;
+
+
+    /**
+     * Associated host name.
+     */
+    private String hostName;
+
+
+    /**
+     * The antiJARLocking flag for this Context.
+     */
+    private boolean antiJARLocking = false;
+
+    
+    /**
+     * The antiResourceLocking flag for this Context.
+     */
+    private boolean antiResourceLocking = false;
+
+    
+    /**
+     * The set of application listener class names configured for this
+     * application, in the order they were encountered in the web.xml file.
+     */
+    private String applicationListeners[] = new String[0];
+
+
+    /**
+     * The set of instantiated application event listener objects</code>.
+     */
+    private transient Object applicationEventListenersObjects[] = 
+        new Object[0];
+
+
+    /**
+     * The set of instantiated application lifecycle listener objects</code>.
+     */
+    private transient Object applicationLifecycleListenersObjects[] = 
+        new Object[0];
+
+
+    /**
+     * The set of application parameters defined for this application.
+     */
+    private ApplicationParameter applicationParameters[] =
+        new ApplicationParameter[0];
+
+
+    /**
+     * The application available flag for this Context.
+     */
+    private boolean available = false;
+    
+    /**
+     * The broadcaster that sends j2ee notifications. 
+     */
+    private NotificationBroadcasterSupport broadcaster = null;
+    
+    /**
+     * The Locale to character set mapper for this application.
+     */
+    private transient CharsetMapper charsetMapper = null;
+
+
+    /**
+     * The Java class name of the CharsetMapper class to be created.
+     */
+    private String charsetMapperClass =
+      "org.apache.catalina.util.CharsetMapper";
+
+
+    /**
+     * The path to a file to save this Context information.
+     */
+    private String configFile = null;
+
+
+    /**
+     * The "correctly configured" flag for this Context.
+     */
+    private boolean configured = false;
+
+
+    /**
+     * The security constraints for this web application.
+     */
+    private SecurityConstraint constraints[] = new SecurityConstraint[0];
+
+
+    /**
+     * The ServletContext implementation associated with this Context.
+     */
+    private transient ApplicationContext context = null;
+
+
+    /**
+     * Compiler classpath to use.
+     */
+    private String compilerClasspath = null;
+
+
+    /**
+     * Should we attempt to use cookies for session id communication?
+     */
+    private boolean cookies = true;
+
+
+    /**
+     * Should we allow the <code>ServletContext.getContext()</code> method
+     * to access the context of other web applications in this server?
+     */
+    private boolean crossContext = false;
+
+    
+    /**
+     * Encoded path.
+     */
+    private String encodedPath = null;
+    
+
+    /**
+     * The "follow standard delegation model" flag that will be used to
+     * configure our ClassLoader.
+     */
+    private boolean delegate = false;
+
+
+     /**
+     * The display name of this web application.
+     */
+    private String displayName = null;
+
+
+    /** 
+     * Override the default context xml location.
+     */
+    private String defaultContextXml;
+
+
+    /** 
+     * Override the default web xml location.
+     */
+    private String defaultWebXml;
+
+
+    /**
+     * The distributable flag for this web application.
+     */
+    private boolean distributable = false;
+
+
+    /**
+     * The document root for this web application.
+     */
+    private String docBase = null;
+
+
+    /**
+     * The exception pages for this web application, keyed by fully qualified
+     * class name of the Java exception.
+     */
+    private HashMap exceptionPages = new HashMap();
+
+
+    /**
+     * The set of filter configurations (and associated filter instances) we
+     * have initialized, keyed by filter name.
+     */
+    private HashMap filterConfigs = new HashMap();
+
+
+    /**
+     * The set of filter definitions for this application, keyed by
+     * filter name.
+     */
+    private HashMap filterDefs = new HashMap();
+
+
+    /**
+     * The set of filter mappings for this application, in the order
+     * they were defined in the deployment descriptor.
+     */
+    private FilterMap filterMaps[] = new FilterMap[0];
+
+
+    /**
+     * The set of classnames of InstanceListeners that will be added
+     * to each newly created Wrapper by <code>createWrapper()</code>.
+     */
+    private String instanceListeners[] = new String[0];
+
+
+    /**
+     * The login configuration descriptor for this web application.
+     */
+    private LoginConfig loginConfig = null;
+
+
+    /**
+     * The mapper associated with this context.
+     */
+    private org.apache.tomcat.util.http.mapper.Mapper mapper = 
+        new org.apache.tomcat.util.http.mapper.Mapper();
+
+
+    /**
+     * The naming context listener for this web application.
+     */
+    private transient NamingContextListener namingContextListener = null;
+
+
+    /**
+     * The naming resources for this web application.
+     */
+    private NamingResources namingResources = null;
+
+
+    /**
+     * The message destinations for this web application.
+     */
+    private HashMap messageDestinations = new HashMap();
+
+
+    /**
+     * The MIME mappings for this web application, keyed by extension.
+     */
+    private HashMap mimeMappings = new HashMap();
+
+
+     /**
+      * Special case: error page for status 200.
+      */
+     private ErrorPage okErrorPage = null;
+
+
+    /**
+     * The context initialization parameters for this web application,
+     * keyed by name.
+     */
+    private HashMap parameters = new HashMap();
+
+
+    /**
+     * The request processing pause flag (while reloading occurs)
+     */
+    private boolean paused = false;
+
+
+    /**
+     * The public identifier of the DTD for the web application deployment
+     * descriptor version we are currently parsing.  This is used to support
+     * relaxed validation rules when processing version 2.2 web.xml files.
+     */
+    private String publicId = null;
+
+
+    /**
+     * The reloadable flag for this web application.
+     */
+    private boolean reloadable = false;
+
+
+    /**
+     * Unpack WAR property.
+     */
+    private boolean unpackWAR = true;
+
+
+    /**
+     * The DefaultContext override flag for this web application.
+     */
+    private boolean override = false;
+
+
+    /**
+     * The privileged flag for this web application.
+     */
+    private boolean privileged = false;
+
+
+    /**
+     * Should the next call to <code>addWelcomeFile()</code> cause replacement
+     * of any existing welcome files?  This will be set before processing the
+     * web application's deployment descriptor, so that application specified
+     * choices <strong>replace</strong>, rather than append to, those defined
+     * in the global descriptor.
+     */
+    private boolean replaceWelcomeFiles = false;
+
+
+    /**
+     * The security role mappings for this application, keyed by role
+     * name (as used within the application).
+     */
+    private HashMap roleMappings = new HashMap();
+
+
+    /**
+     * The security roles for this application, keyed by role name.
+     */
+    private String securityRoles[] = new String[0];
+
+
+    /**
+     * The servlet mappings for this web application, keyed by
+     * matching pattern.
+     */
+    private HashMap servletMappings = new HashMap();
+
+
+    /**
+     * The session timeout (in minutes) for this web application.
+     */
+    private int sessionTimeout = 30;
+
+    /**
+     * The notification sequence number.
+     */
+    private long sequenceNumber = 0;
+    
+    /**
+     * The status code error pages for this web application, keyed by
+     * HTTP status code (as an Integer).
+     */
+    private HashMap statusPages = new HashMap();
+
+
+    /**
+     * Set flag to true to cause the system.out and system.err to be redirected
+     * to the logger when executing a servlet.
+     */
+    private boolean swallowOutput = false;
+
+
+    /**
+     * The JSP tag libraries for this web application, keyed by URI
+     */
+    private HashMap taglibs = new HashMap();
+
+
+    /**
+     * The watched resources for this application.
+     */
+    private String watchedResources[] = new String[0];
+
+
+    /**
+     * The welcome files for this application.
+     */
+    private String welcomeFiles[] = new String[0];
+
+
+    /**
+     * The set of classnames of LifecycleListeners that will be added
+     * to each newly created Wrapper by <code>createWrapper()</code>.
+     */
+    private String wrapperLifecycles[] = new String[0];
+
+
+    /**
+     * The set of classnames of ContainerListeners that will be added
+     * to each newly created Wrapper by <code>createWrapper()</code>.
+     */
+    private String wrapperListeners[] = new String[0];
+
+
+    /**
+     * The pathname to the work directory for this context (relative to
+     * the server's home if not absolute).
+     */
+    private String workDir = null;
+
+
+    /**
+     * Java class name of the Wrapper class implementation we use.
+     */
+    private String wrapperClassName = StandardWrapper.class.getName();
+    private Class wrapperClass = null;
+
+
+    /**
+     * JNDI use flag.
+     */
+    private boolean useNaming = true;
+
+
+    /**
+     * Filesystem based flag.
+     */
+    private boolean filesystemBased = false;
+
+
+    /**
+     * Name of the associated naming context.
+     */
+    private String namingContextName = null;
+
+
+    /**
+     * Caching allowed flag.
+     */
+    private boolean cachingAllowed = true;
+
+
+    /**
+     * Case sensitivity.
+     */
+    protected boolean caseSensitive = true;
+
+
+    /**
+     * Allow linking.
+     */
+    protected boolean allowLinking = false;
+
+
+    /**
+     * Cache max size in KB.
+     */
+    protected int cacheMaxSize = 10240; // 10 MB
+
+
+    /**
+     * Cache TTL in ms.
+     */
+    protected int cacheTTL = 5000;
+
+
+    private boolean lazy=true;
+
+    /**
+     * Non proxied resources.
+     */
+    private transient DirContext webappResources = null;
+
+    private long startupTime;
+    private long startTime;
+    private long tldScanTime;
+
+    /** 
+     * Name of the engine. If null, the domain is used.
+     */ 
+    private String engineName = null;
+    private String j2EEApplication="none";
+    private String j2EEServer="none";
+
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+     private boolean webXmlValidation = false;
+
+
+    /**
+     * Attribute value used to turn on/off XML namespace validation
+     */
+     private boolean webXmlNamespaceAware = false;
+
+    /**
+     * Attribute value used to turn on/off TLD processing
+     */
+    private boolean processTlds = true;
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+     private boolean tldValidation = false;
+
+
+    /**
+     * Attribute value used to turn on/off TLD XML namespace validation
+     */
+     private boolean tldNamespaceAware = false;
+
+
+    /**
+     * Should we save the configuration.
+     */
+    private boolean saveConfig = true;
+
+
+    // ----------------------------------------------------- Context Properties
+
+
+    public String getEncodedPath() {
+        return encodedPath;
+    }
+
+
+    public void setName( String name ) {
+        super.setName( name );
+        encodedPath = urlEncoder.encode(name);
+    }
+
+
+    /**
+     * Is caching allowed ?
+     */
+    public boolean isCachingAllowed() {
+        return cachingAllowed;
+    }
+
+
+    /**
+     * Set caching allowed flag.
+     */
+    public void setCachingAllowed(boolean cachingAllowed) {
+        this.cachingAllowed = cachingAllowed;
+    }
+
+
+    /**
+     * Set case sensitivity.
+     */
+    public void setCaseSensitive(boolean caseSensitive) {
+        this.caseSensitive = caseSensitive;
+    }
+
+
+    /**
+     * Is case sensitive ?
+     */
+    public boolean isCaseSensitive() {
+        return caseSensitive;
+    }
+
+
+    /**
+     * Set allow linking.
+     */
+    public void setAllowLinking(boolean allowLinking) {
+        this.allowLinking = allowLinking;
+    }
+
+
+    /**
+     * Is linking allowed.
+     */
+    public boolean isAllowLinking() {
+        return allowLinking;
+    }
+
+
+    /**
+     * Set cache TTL.
+     */
+    public void setCacheTTL(int cacheTTL) {
+        this.cacheTTL = cacheTTL;
+    }
+
+
+    /**
+     * Get cache TTL.
+     */
+    public int getCacheTTL() {
+        return cacheTTL;
+    }
+
+
+    /**
+     * Return the maximum size of the cache in KB.
+     */
+    public int getCacheMaxSize() {
+        return cacheMaxSize;
+    }
+
+
+    /**
+     * Set the maximum size of the cache in KB.
+     */
+    public void setCacheMaxSize(int cacheMaxSize) {
+        this.cacheMaxSize = cacheMaxSize;
+    }
+
+
+    /**
+     * Return the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     */
+    public boolean getDelegate() {
+
+        return (this.delegate);
+
+    }
+
+
+    /**
+     * Set the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     *
+     * @param delegate The new flag
+     */
+    public void setDelegate(boolean delegate) {
+
+        boolean oldDelegate = this.delegate;
+        this.delegate = delegate;
+        support.firePropertyChange("delegate", new Boolean(oldDelegate),
+                                   new Boolean(this.delegate));
+
+    }
+
+
+    /**
+     * Returns true if the internal naming support is used.
+     */
+    public boolean isUseNaming() {
+
+        return (useNaming);
+
+    }
+
+
+    /**
+     * Enables or disables naming.
+     */
+    public void setUseNaming(boolean useNaming) {
+        this.useNaming = useNaming;
+    }
+
+
+    /**
+     * Returns true if the resources associated with this context are
+     * filesystem based.
+     */
+    public boolean isFilesystemBased() {
+
+        return (filesystemBased);
+
+    }
+
+
+    /**
+     * Return the set of initialized application event listener objects,
+     * in the order they were specified in the web application deployment
+     * descriptor, for this application.
+     *
+     * @exception IllegalStateException if this method is called before
+     *  this application has started, or after it has been stopped
+     */
+    public Object[] getApplicationEventListeners() {
+        return (applicationEventListenersObjects);
+    }
+
+
+    /**
+     * Store the set of initialized application event listener objects,
+     * in the order they were specified in the web application deployment
+     * descriptor, for this application.
+     *
+     * @param listeners The set of instantiated listener objects.
+     */
+    public void setApplicationEventListeners(Object listeners[]) {
+        applicationEventListenersObjects = listeners;
+    }
+
+
+    /**
+     * Return the set of initialized application lifecycle listener objects,
+     * in the order they were specified in the web application deployment
+     * descriptor, for this application.
+     *
+     * @exception IllegalStateException if this method is called before
+     *  this application has started, or after it has been stopped
+     */
+    public Object[] getApplicationLifecycleListeners() {
+        return (applicationLifecycleListenersObjects);
+    }
+
+
+    /**
+     * Store the set of initialized application lifecycle listener objects,
+     * in the order they were specified in the web application deployment
+     * descriptor, for this application.
+     *
+     * @param listeners The set of instantiated listener objects.
+     */
+    public void setApplicationLifecycleListeners(Object listeners[]) {
+        applicationLifecycleListenersObjects = listeners;
+    }
+
+
+    /**
+     * Return the antiJARLocking flag for this Context.
+     */
+    public boolean getAntiJARLocking() {
+
+        return (this.antiJARLocking);
+
+    }
+
+
+    /**
+     * Return the antiResourceLocking flag for this Context.
+     */
+    public boolean getAntiResourceLocking() {
+
+        return (this.antiResourceLocking);
+
+    }
+
+
+    /**
+     * Set the antiJARLocking feature for this Context.
+     *
+     * @param antiJARLocking The new flag value
+     */
+    public void setAntiJARLocking(boolean antiJARLocking) {
+
+        boolean oldAntiJARLocking = this.antiJARLocking;
+        this.antiJARLocking = antiJARLocking;
+        support.firePropertyChange("antiJARLocking",
+                                   new Boolean(oldAntiJARLocking),
+                                   new Boolean(this.antiJARLocking));
+
+    }
+
+
+    /**
+     * Set the antiResourceLocking feature for this Context.
+     *
+     * @param antiResourceLocking The new flag value
+     */
+    public void setAntiResourceLocking(boolean antiResourceLocking) {
+
+        boolean oldAntiResourceLocking = this.antiResourceLocking;
+        this.antiResourceLocking = antiResourceLocking;
+        support.firePropertyChange("antiResourceLocking",
+                                   new Boolean(oldAntiResourceLocking),
+                                   new Boolean(this.antiResourceLocking));
+
+    }
+
+
+    /**
+     * Return the application available flag for this Context.
+     */
+    public boolean getAvailable() {
+
+        return (this.available);
+
+    }
+
+
+    /**
+     * Set the application available flag for this Context.
+     *
+     * @param available The new application available flag
+     */
+    public void setAvailable(boolean available) {
+
+        boolean oldAvailable = this.available;
+        this.available = available;
+        support.firePropertyChange("available",
+                                   new Boolean(oldAvailable),
+                                   new Boolean(this.available));
+
+    }
+
+
+    /**
+     * Return the Locale to character set mapper for this Context.
+     */
+    public CharsetMapper getCharsetMapper() {
+
+        // Create a mapper the first time it is requested
+        if (this.charsetMapper == null) {
+            try {
+                Class clazz = Class.forName(charsetMapperClass);
+                this.charsetMapper =
+                  (CharsetMapper) clazz.newInstance();
+            } catch (Throwable t) {
+                this.charsetMapper = new CharsetMapper();
+            }
+        }
+
+        return (this.charsetMapper);
+
+    }
+
+
+    /**
+     * Set the Locale to character set mapper for this Context.
+     *
+     * @param mapper The new mapper
+     */
+    public void setCharsetMapper(CharsetMapper mapper) {
+
+        CharsetMapper oldCharsetMapper = this.charsetMapper;
+        this.charsetMapper = mapper;
+        if( mapper != null )
+            this.charsetMapperClass= mapper.getClass().getName();
+        support.firePropertyChange("charsetMapper", oldCharsetMapper,
+                                   this.charsetMapper);
+
+    }
+
+    /**
+     * Return the path to a file to save this Context information.
+     */
+    public String getConfigFile() {
+
+        return (this.configFile);
+
+    }
+
+
+    /**
+     * Set the path to a file to save this Context information.
+     *
+     * @param configFile The path to a file to save this Context information.
+     */
+    public void setConfigFile(String configFile) {
+
+        this.configFile = configFile;
+    }
+
+
+    /**
+     * Return the "correctly configured" flag for this Context.
+     */
+    public boolean getConfigured() {
+
+        return (this.configured);
+
+    }
+
+
+    /**
+     * Set the "correctly configured" flag for this Context.  This can be
+     * set to false by startup listeners that detect a fatal configuration
+     * error to avoid the application from being made available.
+     *
+     * @param configured The new correctly configured flag
+     */
+    public void setConfigured(boolean configured) {
+
+        boolean oldConfigured = this.configured;
+        this.configured = configured;
+        support.firePropertyChange("configured",
+                                   new Boolean(oldConfigured),
+                                   new Boolean(this.configured));
+
+    }
+
+
+    /**
+     * Return the "use cookies for session ids" flag.
+     */
+    public boolean getCookies() {
+
+        return (this.cookies);
+
+    }
+
+
+    /**
+     * Set the "use cookies for session ids" flag.
+     *
+     * @param cookies The new flag
+     */
+    public void setCookies(boolean cookies) {
+
+        boolean oldCookies = this.cookies;
+        this.cookies = cookies;
+        support.firePropertyChange("cookies",
+                                   new Boolean(oldCookies),
+                                   new Boolean(this.cookies));
+
+    }
+
+
+    /**
+     * Return the "allow crossing servlet contexts" flag.
+     */
+    public boolean getCrossContext() {
+
+        return (this.crossContext);
+
+    }
+
+
+    /**
+     * Set the "allow crossing servlet contexts" flag.
+     *
+     * @param crossContext The new cross contexts flag
+     */
+    public void setCrossContext(boolean crossContext) {
+
+        boolean oldCrossContext = this.crossContext;
+        this.crossContext = crossContext;
+        support.firePropertyChange("crossContext",
+                                   new Boolean(oldCrossContext),
+                                   new Boolean(this.crossContext));
+
+    }
+
+    public String getDefaultContextXml() {
+        return defaultContextXml;
+    }
+
+    /** 
+     * Set the location of the default context xml that will be used.
+     * If not absolute, it'll be made relative to the engine's base dir
+     * ( which defaults to catalina.base system property ).
+     *
+     * @param defaultContextXml The default web xml 
+     */
+    public void setDefaultContextXml(String defaultContextXml) {
+        this.defaultContextXml = defaultContextXml;
+    }
+
+    public String getDefaultWebXml() {
+        return defaultWebXml;
+    }
+
+    /** 
+     * Set the location of the default web xml that will be used.
+     * If not absolute, it'll be made relative to the engine's base dir
+     * ( which defaults to catalina.base system property ).
+     *
+     * @param defaultWebXml The default web xml 
+     */
+    public void setDefaultWebXml(String defaultWebXml) {
+        this.defaultWebXml = defaultWebXml;
+    }
+
+    /**
+     * Gets the time (in milliseconds) it took to start this context.
+     *
+     * @return Time (in milliseconds) it took to start this context.
+     */
+    public long getStartupTime() {
+        return startupTime;
+    }
+
+    public void setStartupTime(long startupTime) {
+        this.startupTime = startupTime;
+    }
+
+    public long getTldScanTime() {
+        return tldScanTime;
+    }
+
+    public void setTldScanTime(long tldScanTime) {
+        this.tldScanTime = tldScanTime;
+    }
+
+    /**
+     * Return the display name of this web application.
+     */
+    public String getDisplayName() {
+
+        return (this.displayName);
+
+    }
+
+
+    /**
+     * Return the alternate Deployment Descriptor name.
+     */
+    public String getAltDDName(){
+        return altDDName;
+    }
+
+
+    /**
+     * Set an alternate Deployment Descriptor name.
+     */
+    public void setAltDDName(String altDDName) {
+        this.altDDName = altDDName;
+        if (context != null) {
+            context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
+        }
+    }
+
+
+    /**
+     * Return the compiler classpath.
+     */
+    public String getCompilerClasspath(){
+        return compilerClasspath;
+    }
+
+
+    /**
+     * Set the compiler classpath.
+     */
+    public void setCompilerClasspath(String compilerClasspath) {
+        this.compilerClasspath = compilerClasspath;
+    }
+
+
+    /**
+     * Set the display name of this web application.
+     *
+     * @param displayName The new display name
+     */
+    public void setDisplayName(String displayName) {
+
+        String oldDisplayName = this.displayName;
+        this.displayName = displayName;
+        support.firePropertyChange("displayName", oldDisplayName,
+                                   this.displayName);
+    }
+
+
+    /**
+     * Return the distributable flag for this web application.
+     */
+    public boolean getDistributable() {
+
+        return (this.distributable);
+
+    }
+
+    /**
+     * Set the distributable flag for this web application.
+     *
+     * @param distributable The new distributable flag
+     */
+    public void setDistributable(boolean distributable) {
+        boolean oldDistributable = this.distributable;
+        this.distributable = distributable;
+        support.firePropertyChange("distributable",
+                                   new Boolean(oldDistributable),
+                                   new Boolean(this.distributable));
+
+        // Bugzilla 32866
+        if(getManager() != null) {
+            if(log.isDebugEnabled()) {
+                log.debug("Propagating distributable=" + distributable
+                          + " to manager");
+            }
+            getManager().setDistributable(distributable);
+        }
+    }
+
+
+    /**
+     * Return the document root for this Context.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     */
+    public String getDocBase() {
+
+        return (this.docBase);
+
+    }
+
+
+    /**
+     * Set the document root for this Context.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     *
+     * @param docBase The new document root
+     */
+    public void setDocBase(String docBase) {
+
+        this.docBase = docBase;
+
+    }
+
+    // experimental
+    public boolean isLazy() {
+        return lazy;
+    }
+
+    public void setLazy(boolean lazy) {
+        this.lazy = lazy;
+    }
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    public String getEngineName() {
+        if( engineName != null ) return engineName;
+        return domain;
+    }
+
+    public void setEngineName(String engineName) {
+        this.engineName = engineName;
+    }
+
+    public String getJ2EEApplication() {
+        return j2EEApplication;
+    }
+
+    public void setJ2EEApplication(String j2EEApplication) {
+        this.j2EEApplication = j2EEApplication;
+    }
+
+    public String getJ2EEServer() {
+        return j2EEServer;
+    }
+
+    public void setJ2EEServer(String j2EEServer) {
+        this.j2EEServer = j2EEServer;
+    }
+
+
+    /**
+     * Set the Loader with which this Context is associated.
+     *
+     * @param loader The newly associated loader
+     */
+    public synchronized void setLoader(Loader loader) {
+
+        super.setLoader(loader);
+
+    }
+
+
+    /**
+     * Return the login configuration descriptor for this web application.
+     */
+    public LoginConfig getLoginConfig() {
+
+        return (this.loginConfig);
+
+    }
+
+
+    /**
+     * Set the login configuration descriptor for this web application.
+     *
+     * @param config The new login configuration
+     */
+    public void setLoginConfig(LoginConfig config) {
+
+        // Validate the incoming property value
+        if (config == null)
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.loginConfig.required"));
+        String loginPage = config.getLoginPage();
+        if ((loginPage != null) && !loginPage.startsWith("/")) {
+            if (isServlet22()) {
+                if(log.isDebugEnabled())
+                    log.debug(sm.getString("standardContext.loginConfig.loginWarning",
+                                 loginPage));
+                config.setLoginPage("/" + loginPage);
+            } else {
+                throw new IllegalArgumentException
+                    (sm.getString("standardContext.loginConfig.loginPage",
+                                  loginPage));
+            }
+        }
+        String errorPage = config.getErrorPage();
+        if ((errorPage != null) && !errorPage.startsWith("/")) {
+            if (isServlet22()) {
+                if(log.isDebugEnabled())
+                    log.debug(sm.getString("standardContext.loginConfig.errorWarning",
+                                 errorPage));
+                config.setErrorPage("/" + errorPage);
+            } else {
+                throw new IllegalArgumentException
+                    (sm.getString("standardContext.loginConfig.errorPage",
+                                  errorPage));
+            }
+        }
+
+        // Process the property setting change
+        LoginConfig oldLoginConfig = this.loginConfig;
+        this.loginConfig = config;
+        support.firePropertyChange("loginConfig",
+                                   oldLoginConfig, this.loginConfig);
+
+    }
+
+
+    /**
+     * Get the mapper associated with the context.
+     */
+    public org.apache.tomcat.util.http.mapper.Mapper getMapper() {
+        return (mapper);
+    }
+
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    public NamingResources getNamingResources() {
+
+        if (namingResources == null) {
+            setNamingResources(new NamingResources());
+        }
+        return (namingResources);
+
+    }
+
+
+    /**
+     * Set the naming resources for this web application.
+     *
+     * @param namingResources The new naming resources
+     */
+    public void setNamingResources(NamingResources namingResources) {
+
+        // Process the property setting change
+        NamingResources oldNamingResources = this.namingResources;
+        this.namingResources = namingResources;
+        namingResources.setContainer(this);
+        support.firePropertyChange("namingResources",
+                                   oldNamingResources, this.namingResources);
+
+    }
+
+
+    /**
+     * Return the context path for this Context.
+     */
+    public String getPath() {
+
+        return (getName());
+
+    }
+
+    
+    /**
+     * Set the context path for this Context.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  The context path is used as the "name" of
+     * a Context, because it must be unique.
+     *
+     * @param path The new context path
+     */
+    public void setPath(String path) {
+        // XXX Use host in name
+        setName(RequestUtil.URLDecode(path));
+
+    }
+
+
+    /**
+     * Return the public identifier of the deployment descriptor DTD that is
+     * currently being parsed.
+     */
+    public String getPublicId() {
+
+        return (this.publicId);
+
+    }
+
+
+    /**
+     * Set the public identifier of the deployment descriptor DTD that is
+     * currently being parsed.
+     *
+     * @param publicId The public identifier
+     */
+    public void setPublicId(String publicId) {
+
+        if (log.isDebugEnabled())
+            log.debug("Setting deployment descriptor public ID to '" +
+                publicId + "'");
+
+        String oldPublicId = this.publicId;
+        this.publicId = publicId;
+        support.firePropertyChange("publicId", oldPublicId, publicId);
+
+    }
+
+
+    /**
+     * Return the reloadable flag for this web application.
+     */
+    public boolean getReloadable() {
+
+        return (this.reloadable);
+
+    }
+
+
+    /**
+     * Return the DefaultContext override flag for this web application.
+     */
+    public boolean getOverride() {
+
+        return (this.override);
+
+    }
+
+
+    /**
+     * Return the privileged flag for this web application.
+     */
+    public boolean getPrivileged() {
+
+        return (this.privileged);
+
+    }
+
+
+    /**
+     * Set the privileged flag for this web application.
+     *
+     * @param privileged The new privileged flag
+     */
+    public void setPrivileged(boolean privileged) {
+
+        boolean oldPrivileged = this.privileged;
+        this.privileged = privileged;
+        support.firePropertyChange("privileged",
+                                   new Boolean(oldPrivileged),
+                                   new Boolean(this.privileged));
+
+    }
+
+
+    /**
+     * Set the reloadable flag for this web application.
+     *
+     * @param reloadable The new reloadable flag
+     */
+    public void setReloadable(boolean reloadable) {
+
+        boolean oldReloadable = this.reloadable;
+        this.reloadable = reloadable;
+        support.firePropertyChange("reloadable",
+                                   new Boolean(oldReloadable),
+                                   new Boolean(this.reloadable));
+
+    }
+
+
+    /**
+     * Set the DefaultContext override flag for this web application.
+     *
+     * @param override The new override flag
+     */
+    public void setOverride(boolean override) {
+
+        boolean oldOverride = this.override;
+        this.override = override;
+        support.firePropertyChange("override",
+                                   new Boolean(oldOverride),
+                                   new Boolean(this.override));
+
+    }
+
+
+    /**
+     * Return the "replace welcome files" property.
+     */
+    public boolean isReplaceWelcomeFiles() {
+
+        return (this.replaceWelcomeFiles);
+
+    }
+
+
+    /**
+     * Set the "replace welcome files" property.
+     *
+     * @param replaceWelcomeFiles The new property value
+     */
+    public void setReplaceWelcomeFiles(boolean replaceWelcomeFiles) {
+
+        boolean oldReplaceWelcomeFiles = this.replaceWelcomeFiles;
+        this.replaceWelcomeFiles = replaceWelcomeFiles;
+        support.firePropertyChange("replaceWelcomeFiles",
+                                   new Boolean(oldReplaceWelcomeFiles),
+                                   new Boolean(this.replaceWelcomeFiles));
+
+    }
+
+
+    /**
+     * Return the servlet context for which this Context is a facade.
+     */
+    public ServletContext getServletContext() {
+
+        if (context == null) {
+            context = new ApplicationContext(getBasePath(), this);
+            if (altDDName != null)
+                context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
+        }
+        return (context.getFacade());
+
+    }
+
+
+    /**
+     * Return the default session timeout (in minutes) for this
+     * web application.
+     */
+    public int getSessionTimeout() {
+
+        return (this.sessionTimeout);
+
+    }
+
+
+    /**
+     * Set the default session timeout (in minutes) for this
+     * web application.
+     *
+     * @param timeout The new default session timeout
+     */
+    public void setSessionTimeout(int timeout) {
+
+        int oldSessionTimeout = this.sessionTimeout;
+        /*
+         * SRV.13.4 ("Deployment Descriptor"):
+         * If the timeout is 0 or less, the container ensures the default
+         * behaviour of sessions is never to time out.
+         */
+        this.sessionTimeout = (timeout == 0) ? -1 : timeout;
+        support.firePropertyChange("sessionTimeout",
+                                   new Integer(oldSessionTimeout),
+                                   new Integer(this.sessionTimeout));
+
+    }
+
+
+    /**
+     * Return the value of the swallowOutput flag.
+     */
+    public boolean getSwallowOutput() {
+
+        return (this.swallowOutput);
+
+    }
+
+
+    /**
+     * Set the value of the swallowOutput flag. If set to true, the system.out
+     * and system.err will be redirected to the logger during a servlet
+     * execution.
+     *
+     * @param swallowOutput The new value
+     */
+    public void setSwallowOutput(boolean swallowOutput) {
+
+        boolean oldSwallowOutput = this.swallowOutput;
+        this.swallowOutput = swallowOutput;
+        support.firePropertyChange("swallowOutput",
+                                   new Boolean(oldSwallowOutput),
+                                   new Boolean(this.swallowOutput));
+
+    }
+
+
+    /**
+     * Unpack WAR flag accessor.
+     */
+    public boolean getUnpackWAR() {
+
+        return (unpackWAR);
+
+    }
+
+
+    /**
+     * Unpack WAR flag mutator.
+     */
+    public void setUnpackWAR(boolean unpackWAR) {
+
+        this.unpackWAR = unpackWAR;
+
+    }
+
+    /**
+     * Return the Java class name of the Wrapper implementation used
+     * for servlets registered in this Context.
+     */
+    public String getWrapperClass() {
+
+        return (this.wrapperClassName);
+
+    }
+
+
+    /**
+     * Set the Java class name of the Wrapper implementation used
+     * for servlets registered in this Context.
+     *
+     * @param wrapperClassName The new wrapper class name
+     *
+     * @throws IllegalArgumentException if the specified wrapper class
+     * cannot be found or is not a subclass of StandardWrapper
+     */
+    public void setWrapperClass(String wrapperClassName) {
+
+        this.wrapperClassName = wrapperClassName;
+
+        try {
+            wrapperClass = Class.forName(wrapperClassName);         
+            if (!StandardWrapper.class.isAssignableFrom(wrapperClass)) {
+                throw new IllegalArgumentException(
+                    sm.getString("standardContext.invalidWrapperClass",
+                                 wrapperClassName));
+            }
+        } catch (ClassNotFoundException cnfe) {
+            throw new IllegalArgumentException(cnfe.getMessage());
+        }
+    }
+
+
+    /**
+     * Set the resources DirContext object with which this Container is
+     * associated.
+     *
+     * @param resources The newly associated DirContext
+     */
+    public synchronized void setResources(DirContext resources) {
+
+        if (started) {
+            throw new IllegalStateException
+                (sm.getString("standardContext.resources.started"));
+        }
+
+        DirContext oldResources = this.webappResources;
+        if (oldResources == resources)
+            return;
+
+        if (resources instanceof BaseDirContext) {
+            ((BaseDirContext) resources).setCached(isCachingAllowed());
+            ((BaseDirContext) resources).setCacheTTL(getCacheTTL());
+            ((BaseDirContext) resources).setCacheMaxSize(getCacheMaxSize());
+        }
+        if (resources instanceof FileDirContext) {
+            filesystemBased = true;
+            ((FileDirContext) resources).setCaseSensitive(isCaseSensitive());
+            ((FileDirContext) resources).setAllowLinking(isAllowLinking());
+        }
+        this.webappResources = resources;
+
+        // The proxied resources will be refreshed on start
+        this.resources = null;
+
+        support.firePropertyChange("resources", oldResources,
+                                   this.webappResources);
+
+    }
+
+
+    // ------------------------------------------------------ Public Properties
+
+
+    /**
+     * Return the Locale to character set mapper class for this Context.
+     */
+    public String getCharsetMapperClass() {
+
+        return (this.charsetMapperClass);
+
+    }
+
+
+    /**
+     * Set the Locale to character set mapper class for this Context.
+     *
+     * @param mapper The new mapper class
+     */
+    public void setCharsetMapperClass(String mapper) {
+
+        String oldCharsetMapperClass = this.charsetMapperClass;
+        this.charsetMapperClass = mapper;
+        support.firePropertyChange("charsetMapperClass",
+                                   oldCharsetMapperClass,
+                                   this.charsetMapperClass);
+
+    }
+
+
+    /** Get the absolute path to the work dir.
+     *  To avoid duplication.
+     * 
+     * @return The work path
+     */ 
+    public String getWorkPath() {
+        File workDir = new File(getWorkDir());
+        if (!workDir.isAbsolute()) {
+            File catalinaHome = engineBase();
+            String catalinaHomePath = null;
+            try {
+                catalinaHomePath = catalinaHome.getCanonicalPath();
+                workDir = new File(catalinaHomePath,
+                        getWorkDir());
+            } catch (IOException e) {
+                log.warn("Exception obtaining work path for " + getPath());
+            }
+        }
+        return workDir.getAbsolutePath();
+    }
+    
+    /**
+     * Return the work directory for this Context.
+     */
+    public String getWorkDir() {
+
+        return (this.workDir);
+
+    }
+
+
+    /**
+     * Set the work directory for this Context.
+     *
+     * @param workDir The new work directory
+     */
+    public void setWorkDir(String workDir) {
+
+        this.workDir = workDir;
+
+        if (started) {
+            postWorkDirectory();
+        }
+    }
+
+
+    /**
+     * Save config ?
+     */
+    public boolean isSaveConfig() {
+        return saveConfig;
+    }
+
+
+    /**
+     * Set save config flag.
+     */
+    public void setSaveConfig(boolean saveConfig) {
+        this.saveConfig = saveConfig;
+    }
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Add a new Listener class name to the set of Listeners
+     * configured for this application.
+     *
+     * @param listener Java class name of a listener class
+     */
+    public void addApplicationListener(String listener) {
+
+        synchronized (applicationListeners) {
+            String results[] =new String[applicationListeners.length + 1];
+            for (int i = 0; i < applicationListeners.length; i++) {
+                if (listener.equals(applicationListeners[i]))
+                    return;
+                results[i] = applicationListeners[i];
+            }
+            results[applicationListeners.length] = listener;
+            applicationListeners = results;
+        }
+        fireContainerEvent("addApplicationListener", listener);
+
+        // FIXME - add instance if already started?
+
+    }
+
+
+    /**
+     * Add a new application parameter for this application.
+     *
+     * @param parameter The new application parameter
+     */
+    public void addApplicationParameter(ApplicationParameter parameter) {
+
+        synchronized (applicationParameters) {
+            String newName = parameter.getName();
+            for (int i = 0; i < applicationParameters.length; i++) {
+                if (newName.equals(applicationParameters[i].getName()) &&
+                    !applicationParameters[i].getOverride())
+                    return;
+            }
+            ApplicationParameter results[] =
+                new ApplicationParameter[applicationParameters.length + 1];
+            System.arraycopy(applicationParameters, 0, results, 0,
+                             applicationParameters.length);
+            results[applicationParameters.length] = parameter;
+            applicationParameters = results;
+        }
+        fireContainerEvent("addApplicationParameter", parameter);
+
+    }
+
+
+    /**
+     * Add a child Container, only if the proposed child is an implementation
+     * of Wrapper.
+     *
+     * @param child Child container to be added
+     *
+     * @exception IllegalArgumentException if the proposed container is
+     *  not an implementation of Wrapper
+     */
+    public void addChild(Container child) {
+
+        // Global JspServlet
+        Wrapper oldJspServlet = null;
+
+        if (!(child instanceof Wrapper)) {
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.notWrapper"));
+        }
+
+        Wrapper wrapper = (Wrapper) child;
+        boolean isJspServlet = "jsp".equals(child.getName());
+
+        // Allow webapp to override JspServlet inherited from global web.xml.
+        if (isJspServlet) {
+            oldJspServlet = (Wrapper) findChild("jsp");
+            if (oldJspServlet != null) {
+                removeChild(oldJspServlet);
+            }
+        }
+
+        String jspFile = wrapper.getJspFile();
+        if ((jspFile != null) && !jspFile.startsWith("/")) {
+            if (isServlet22()) {
+                if(log.isDebugEnabled())
+                    log.debug(sm.getString("standardContext.wrapper.warning", 
+                                       jspFile));
+                wrapper.setJspFile("/" + jspFile);
+            } else {
+                throw new IllegalArgumentException
+                    (sm.getString("standardContext.wrapper.error", jspFile));
+            }
+        }
+
+        super.addChild(child);
+
+        if (isJspServlet && oldJspServlet != null) {
+            /*
+             * The webapp-specific JspServlet inherits all the mappings
+             * specified in the global web.xml, and may add additional ones.
+             */
+            String[] jspMappings = oldJspServlet.findMappings();
+            for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
+                addServletMapping(jspMappings[i], child.getName());
+            }
+        }
+    }
+
+
+    /**
+     * Add a security constraint to the set for this web application.
+     */
+    public void addConstraint(SecurityConstraint constraint) {
+
+        // Validate the proposed constraint
+        SecurityCollection collections[] = constraint.findCollections();
+        for (int i = 0; i < collections.length; i++) {
+            String patterns[] = collections[i].findPatterns();
+            for (int j = 0; j < patterns.length; j++) {
+                patterns[j] = adjustURLPattern(patterns[j]);
+                if (!validateURLPattern(patterns[j]))
+                    throw new IllegalArgumentException
+                        (sm.getString
+                         ("standardContext.securityConstraint.pattern",
+                          patterns[j]));
+            }
+        }
+
+        // Add this constraint to the set for our web application
+        synchronized (constraints) {
+            SecurityConstraint results[] =
+                new SecurityConstraint[constraints.length + 1];
+            for (int i = 0; i < constraints.length; i++)
+                results[i] = constraints[i];
+            results[constraints.length] = constraint;
+            constraints = results;
+        }
+
+    }
+
+
+
+    /**
+     * Add an error page for the specified error or Java exception.
+     *
+     * @param errorPage The error page definition to be added
+     */
+    public void addErrorPage(ErrorPage errorPage) {
+        // Validate the input parameters
+        if (errorPage == null)
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.errorPage.required"));
+        String location = errorPage.getLocation();
+        if ((location != null) && !location.startsWith("/")) {
+            if (isServlet22()) {
+                if(log.isDebugEnabled())
+                    log.debug(sm.getString("standardContext.errorPage.warning",
+                                 location));
+                errorPage.setLocation("/" + location);
+            } else {
+                throw new IllegalArgumentException
+                    (sm.getString("standardContext.errorPage.error",
+                                  location));
+            }
+        }
+
+        // Add the specified error page to our internal collections
+        String exceptionType = errorPage.getExceptionType();
+        if (exceptionType != null) {
+            synchronized (exceptionPages) {
+                exceptionPages.put(exceptionType, errorPage);
+            }
+        } else {
+            synchronized (statusPages) {
+                if (errorPage.getErrorCode() == 200) {
+                    this.okErrorPage = errorPage;
+                }
+                statusPages.put(new Integer(errorPage.getErrorCode()),
+                                errorPage);
+            }
+        }
+        fireContainerEvent("addErrorPage", errorPage);
+
+    }
+
+
+    /**
+     * Add a filter definition to this Context.
+     *
+     * @param filterDef The filter definition to be added
+     */
+    public void addFilterDef(FilterDef filterDef) {
+
+        synchronized (filterDefs) {
+            filterDefs.put(filterDef.getFilterName(), filterDef);
+        }
+        fireContainerEvent("addFilterDef", filterDef);
+
+    }
+
+
+    /**
+     * Add a filter mapping to this Context.
+     *
+     * @param filterMap The filter mapping to be added
+     *
+     * @exception IllegalArgumentException if the specified filter name
+     *  does not match an existing filter definition, or the filter mapping
+     *  is malformed
+     */
+    public void addFilterMap(FilterMap filterMap) {
+
+        // Validate the proposed filter mapping
+        String filterName = filterMap.getFilterName();
+        String servletName = filterMap.getServletName();
+        String urlPattern = filterMap.getURLPattern();
+        if (findFilterDef(filterName) == null)
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.filterMap.name", filterName));
+        if ((servletName == null) && (urlPattern == null))
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.filterMap.either"));
+        if ((servletName != null) && (urlPattern != null))
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.filterMap.either"));
+        // Because filter-pattern is new in 2.3, no need to adjust
+        // for 2.2 backwards compatibility
+        if ((urlPattern != null) && !validateURLPattern(urlPattern))
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.filterMap.pattern",
+                              urlPattern));
+
+        // Add this filter mapping to our registered set
+        synchronized (filterMaps) {
+            FilterMap results[] =new FilterMap[filterMaps.length + 1];
+            System.arraycopy(filterMaps, 0, results, 0, filterMaps.length);
+            results[filterMaps.length] = filterMap;
+            filterMaps = results;
+        }
+        fireContainerEvent("addFilterMap", filterMap);
+
+    }
+
+
+    /**
+     * Add the classname of an InstanceListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of an InstanceListener class
+     */
+    public void addInstanceListener(String listener) {
+
+        synchronized (instanceListeners) {
+            String results[] =new String[instanceListeners.length + 1];
+            for (int i = 0; i < instanceListeners.length; i++)
+                results[i] = instanceListeners[i];
+            results[instanceListeners.length] = listener;
+            instanceListeners = results;
+        }
+        fireContainerEvent("addInstanceListener", listener);
+
+    }
+
+    /**
+     * Add the given URL pattern as a jsp-property-group.  This maps
+     * resources that match the given pattern so they will be passed
+     * to the JSP container.  Though there are other elements in the
+     * property group, we only care about the URL pattern here.  The
+     * JSP container will parse the rest.
+     *
+     * @param pattern URL pattern to be mapped
+     */
+    public void addJspMapping(String pattern) {
+        String servletName = findServletMapping("*.jsp");
+        if (servletName == null) {
+            servletName = "jsp";
+        }
+
+        if( findChild(servletName) != null) {
+            addServletMapping(pattern, servletName, true);
+        } else {
+            if(log.isDebugEnabled())
+                log.debug("Skiping " + pattern + " , no servlet " + servletName);
+        }
+    }
+
+
+    /**
+     * Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4)
+     *
+     * @param locale locale to map an encoding for
+     * @param encoding encoding to be used for a give locale
+     */
+    public void addLocaleEncodingMappingParameter(String locale, String encoding){
+        getCharsetMapper().addCharsetMappingFromDeploymentDescriptor(locale, encoding);
+    }
+
+
+    /**
+     * Add a message destination for this web application.
+     *
+     * @param md New message destination
+     */
+    public void addMessageDestination(MessageDestination md) {
+
+        synchronized (messageDestinations) {
+            messageDestinations.put(md.getName(), md);
+        }
+        fireContainerEvent("addMessageDestination", md.getName());
+
+    }
+
+
+    /**
+     * Add a message destination reference for this web application.
+     *
+     * @param mdr New message destination reference
+     */
+    public void addMessageDestinationRef
+        (MessageDestinationRef mdr) {
+
+        namingResources.addMessageDestinationRef(mdr);
+        fireContainerEvent("addMessageDestinationRef", mdr.getName());
+
+    }
+
+
+    /**
+     * Add a new MIME mapping, replacing any existing mapping for
+     * the specified extension.
+     *
+     * @param extension Filename extension being mapped
+     * @param mimeType Corresponding MIME type
+     */
+    public void addMimeMapping(String extension, String mimeType) {
+
+        synchronized (mimeMappings) {
+            mimeMappings.put(extension, mimeType);
+        }
+        fireContainerEvent("addMimeMapping", extension);
+
+    }
+
+
+    /**
+     * Add a new context initialization parameter.
+     *
+     * @param name Name of the new parameter
+     * @param value Value of the new  parameter
+     *
+     * @exception IllegalArgumentException if the name or value is missing,
+     *  or if this context initialization parameter has already been
+     *  registered
+     */
+    public void addParameter(String name, String value) {
+        // Validate the proposed context initialization parameter
+        if ((name == null) || (value == null))
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.parameter.required"));
+        if (parameters.get(name) != null)
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.parameter.duplicate", name));
+
+        // Add this parameter to our defined set
+        synchronized (parameters) {
+            parameters.put(name, value);
+        }
+        fireContainerEvent("addParameter", name);
+
+    }
+
+
+    /**
+     * Add a security role reference for this web application.
+     *
+     * @param role Security role used in the application
+     * @param link Actual security role to check for
+     */
+    public void addRoleMapping(String role, String link) {
+
+        synchronized (roleMappings) {
+            roleMappings.put(role, link);
+        }
+        fireContainerEvent("addRoleMapping", role);
+
+    }
+
+
+    /**
+     * Add a new security role for this web application.
+     *
+     * @param role New security role
+     */
+    public void addSecurityRole(String role) {
+
+        synchronized (securityRoles) {
+            String results[] =new String[securityRoles.length + 1];
+            for (int i = 0; i < securityRoles.length; i++)
+                results[i] = securityRoles[i];
+            results[securityRoles.length] = role;
+            securityRoles = results;
+        }
+        fireContainerEvent("addSecurityRole", role);
+
+    }
+
+
+    /**
+     * Add a new servlet mapping, replacing any existing mapping for
+     * the specified pattern.
+     *
+     * @param pattern URL pattern to be mapped
+     * @param name Name of the corresponding servlet to execute
+     *
+     * @exception IllegalArgumentException if the specified servlet name
+     *  is not known to this Context
+     */
+    public void addServletMapping(String pattern, String name) {
+        addServletMapping(pattern, name, false);
+    }
+
+
+    /**
+     * Add a new servlet mapping, replacing any existing mapping for
+     * the specified pattern.
+     *
+     * @param pattern URL pattern to be mapped
+     * @param name Name of the corresponding servlet to execute
+     * @param jspWildCard true if name identifies the JspServlet
+     * and pattern contains a wildcard; false otherwise
+     *
+     * @exception IllegalArgumentException if the specified servlet name
+     *  is not known to this Context
+     */
+    public void addServletMapping(String pattern, String name,
+                                  boolean jspWildCard) {
+        // Validate the proposed mapping
+        if (findChild(name) == null)
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.servletMap.name", name));
+        pattern = adjustURLPattern(RequestUtil.URLDecode(pattern));
+        if (!validateURLPattern(pattern))
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.servletMap.pattern", pattern));
+
+        // Add this mapping to our registered set
+        synchronized (servletMappings) {
+            String name2 = (String) servletMappings.get(pattern);
+            if (name2 != null) {
+                // Don't allow more than one servlet on the same pattern
+                Wrapper wrapper = (Wrapper) findChild(name2);
+                wrapper.removeMapping(pattern);
+                mapper.removeWrapper(pattern);
+            }
+            servletMappings.put(pattern, name);
+        }
+        Wrapper wrapper = (Wrapper) findChild(name);
+        wrapper.addMapping(pattern);
+
+        // Update context mapper
+        mapper.addWrapper(pattern, wrapper, jspWildCard);
+
+        fireContainerEvent("addServletMapping", pattern);
+
+    }
+
+
+    /**
+     * Add a JSP tag library for the specified URI.
+     *
+     * @param uri URI, relative to the web.xml file, of this tag library
+     * @param location Location of the tag library descriptor
+     */
+    public void addTaglib(String uri, String location) {
+
+        synchronized (taglibs) {
+            taglibs.put(uri, location);
+        }
+        fireContainerEvent("addTaglib", uri);
+
+    }
+
+
+    /**
+     * Add a new watched resource to the set recognized by this Context.
+     *
+     * @param name New watched resource file name
+     */
+    public void addWatchedResource(String name) {
+
+        synchronized (watchedResources) {
+            String results[] = new String[watchedResources.length + 1];
+            for (int i = 0; i < watchedResources.length; i++)
+                results[i] = watchedResources[i];
+            results[watchedResources.length] = name;
+            watchedResources = results;
+        }
+        fireContainerEvent("addWatchedResource", name);
+
+    }
+
+
+    /**
+     * Add a new welcome file to the set recognized by this Context.
+     *
+     * @param name New welcome file name
+     */
+    public void addWelcomeFile(String name) {
+
+        synchronized (welcomeFiles) {
+            // Welcome files from the application deployment descriptor
+            // completely replace those from the default conf/web.xml file
+            if (replaceWelcomeFiles) {
+                welcomeFiles = new String[0];
+                setReplaceWelcomeFiles(false);
+            }
+            String results[] =new String[welcomeFiles.length + 1];
+            for (int i = 0; i < welcomeFiles.length; i++)
+                results[i] = welcomeFiles[i];
+            results[welcomeFiles.length] = name;
+            welcomeFiles = results;
+        }
+        postWelcomeFiles();
+        fireContainerEvent("addWelcomeFile", name);
+
+    }
+
+
+    /**
+     * Add the classname of a LifecycleListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of a LifecycleListener class
+     */
+    public void addWrapperLifecycle(String listener) {
+
+        synchronized (wrapperLifecycles) {
+            String results[] =new String[wrapperLifecycles.length + 1];
+            for (int i = 0; i < wrapperLifecycles.length; i++)
+                results[i] = wrapperLifecycles[i];
+            results[wrapperLifecycles.length] = listener;
+            wrapperLifecycles = results;
+        }
+        fireContainerEvent("addWrapperLifecycle", listener);
+
+    }
+
+
+    /**
+     * Add the classname of a ContainerListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of a ContainerListener class
+     */
+    public void addWrapperListener(String listener) {
+
+        synchronized (wrapperListeners) {
+            String results[] =new String[wrapperListeners.length + 1];
+            for (int i = 0; i < wrapperListeners.length; i++)
+                results[i] = wrapperListeners[i];
+            results[wrapperListeners.length] = listener;
+            wrapperListeners = results;
+        }
+        fireContainerEvent("addWrapperListener", listener);
+
+    }
+
+
+    /**
+     * Factory method to create and return a new Wrapper instance, of
+     * the Java implementation class appropriate for this Context
+     * implementation.  The constructor of the instantiated Wrapper
+     * will have been called, but no properties will have been set.
+     */
+    public Wrapper createWrapper() {
+
+        Wrapper wrapper = null;
+        if (wrapperClass != null) {
+            try {
+                wrapper = (Wrapper) wrapperClass.newInstance();
+            } catch (Throwable t) {
+                log.error("createWrapper", t);
+                return (null);
+            }
+        } else {
+            wrapper = new StandardWrapper();
+        }
+
+        synchronized (instanceListeners) {
+            for (int i = 0; i < instanceListeners.length; i++) {
+                try {
+                    Class clazz = Class.forName(instanceListeners[i]);
+                    InstanceListener listener =
+                      (InstanceListener) clazz.newInstance();
+                    wrapper.addInstanceListener(listener);
+                } catch (Throwable t) {
+                    log.error("createWrapper", t);
+                    return (null);
+                }
+            }
+        }
+
+        synchronized (wrapperLifecycles) {
+            for (int i = 0; i < wrapperLifecycles.length; i++) {
+                try {
+                    Class clazz = Class.forName(wrapperLifecycles[i]);
+                    LifecycleListener listener =
+                      (LifecycleListener) clazz.newInstance();
+                    if (wrapper instanceof Lifecycle)
+                        ((Lifecycle) wrapper).addLifecycleListener(listener);
+                } catch (Throwable t) {
+                    log.error("createWrapper", t);
+                    return (null);
+                }
+            }
+        }
+
+        synchronized (wrapperListeners) {
+            for (int i = 0; i < wrapperListeners.length; i++) {
+                try {
+                    Class clazz = Class.forName(wrapperListeners[i]);
+                    ContainerListener listener =
+                      (ContainerListener) clazz.newInstance();
+                    wrapper.addContainerListener(listener);
+                } catch (Throwable t) {
+                    log.error("createWrapper", t);
+                    return (null);
+                }
+            }
+        }
+
+        return (wrapper);
+
+    }
+
+
+    /**
+     * Return the set of application listener class names configured
+     * for this application.
+     */
+    public String[] findApplicationListeners() {
+
+        return (applicationListeners);
+
+    }
+
+
+    /**
+     * Return the set of application parameters for this application.
+     */
+    public ApplicationParameter[] findApplicationParameters() {
+
+        return (applicationParameters);
+
+    }
+
+
+    /**
+     * Return the security constraints for this web application.
+     * If there are none, a zero-length array is returned.
+     */
+    public SecurityConstraint[] findConstraints() {
+
+        return (constraints);
+
+    }
+
+
+    /**
+     * Return the error page entry for the specified HTTP error code,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param errorCode Error code to look up
+     */
+    public ErrorPage findErrorPage(int errorCode) {
+        if (errorCode == 200) {
+            return (okErrorPage);
+        } else {
+            return ((ErrorPage) statusPages.get(new Integer(errorCode)));
+        }
+
+    }
+
+
+    /**
+     * Return the error page entry for the specified Java exception type,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param exceptionType Exception type to look up
+     */
+    public ErrorPage findErrorPage(String exceptionType) {
+
+        synchronized (exceptionPages) {
+            return ((ErrorPage) exceptionPages.get(exceptionType));
+        }
+
+    }
+
+
+    /**
+     * Return the set of defined error pages for all specified error codes
+     * and exception types.
+     */
+    public ErrorPage[] findErrorPages() {
+
+        synchronized(exceptionPages) {
+            synchronized(statusPages) {
+                ErrorPage results1[] = new ErrorPage[exceptionPages.size()];
+                results1 =
+                    (ErrorPage[]) exceptionPages.values().toArray(results1);
+                ErrorPage results2[] = new ErrorPage[statusPages.size()];
+                results2 =
+                    (ErrorPage[]) statusPages.values().toArray(results2);
+                ErrorPage results[] =
+                    new ErrorPage[results1.length + results2.length];
+                for (int i = 0; i < results1.length; i++)
+                    results[i] = results1[i];
+                for (int i = results1.length; i < results.length; i++)
+                    results[i] = results2[i - results1.length];
+                return (results);
+            }
+        }
+
+    }
+
+
+    /**
+     * Return the filter definition for the specified filter name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param filterName Filter name to look up
+     */
+    public FilterDef findFilterDef(String filterName) {
+
+        synchronized (filterDefs) {
+            return ((FilterDef) filterDefs.get(filterName));
+        }
+
+    }
+
+
+    /**
+     * Return the set of defined filters for this Context.
+     */
+    public FilterDef[] findFilterDefs() {
+
+        synchronized (filterDefs) {
+            FilterDef results[] = new FilterDef[filterDefs.size()];
+            return ((FilterDef[]) filterDefs.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the set of filter mappings for this Context.
+     */
+    public FilterMap[] findFilterMaps() {
+
+        return (filterMaps);
+
+    }
+
+
+    /**
+     * Return the set of InstanceListener classes that will be added to
+     * newly created Wrappers automatically.
+     */
+    public String[] findInstanceListeners() {
+
+        return (instanceListeners);
+
+    }
+
+
+    /**
+     * FIXME: Fooling introspection ...
+     */
+    public Context findMappingObject() {
+        return (Context) getMappingObject();
+    }
+    
+    
+    /**
+     * Return the message destination with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired message destination
+     */
+    public MessageDestination findMessageDestination(String name) {
+
+        synchronized (messageDestinations) {
+            return ((MessageDestination) messageDestinations.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the set of defined message destinations for this web
+     * application.  If none have been defined, a zero-length array
+     * is returned.
+     */
+    public MessageDestination[] findMessageDestinations() {
+
+        synchronized (messageDestinations) {
+            MessageDestination results[] =
+                new MessageDestination[messageDestinations.size()];
+            return ((MessageDestination[])
+                    messageDestinations.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the message destination ref with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired message destination ref
+     */
+    public MessageDestinationRef
+        findMessageDestinationRef(String name) {
+
+        return namingResources.findMessageDestinationRef(name);
+
+    }
+
+
+    /**
+     * Return the set of defined message destination refs for this web
+     * application.  If none have been defined, a zero-length array
+     * is returned.
+     */
+    public MessageDestinationRef[]
+        findMessageDestinationRefs() {
+
+        return namingResources.findMessageDestinationRefs();
+
+    }
+
+
+    /**
+     * Return the MIME type to which the specified extension is mapped,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param extension Extension to map to a MIME type
+     */
+    public String findMimeMapping(String extension) {
+
+        return ((String) mimeMappings.get(extension));
+
+    }
+
+
+    /**
+     * Return the extensions for which MIME mappings are defined.  If there
+     * are none, a zero-length array is returned.
+     */
+    public String[] findMimeMappings() {
+
+        synchronized (mimeMappings) {
+            String results[] = new String[mimeMappings.size()];
+            return
+                ((String[]) mimeMappings.keySet().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the value for the specified context initialization
+     * parameter name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the parameter to return
+     */
+    public String findParameter(String name) {
+
+        synchronized (parameters) {
+            return ((String) parameters.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the names of all defined context initialization parameters
+     * for this Context.  If no parameters are defined, a zero-length
+     * array is returned.
+     */
+    public String[] findParameters() {
+
+        synchronized (parameters) {
+            String results[] = new String[parameters.size()];
+            return ((String[]) parameters.keySet().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * For the given security role (as used by an application), return the
+     * corresponding role name (as defined by the underlying Realm) if there
+     * is one.  Otherwise, return the specified role unchanged.
+     *
+     * @param role Security role to map
+     */
+    public String findRoleMapping(String role) {
+
+        String realRole = null;
+        synchronized (roleMappings) {
+            realRole = (String) roleMappings.get(role);
+        }
+        if (realRole != null)
+            return (realRole);
+        else
+            return (role);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified security role is defined
+     * for this application; otherwise return <code>false</code>.
+     *
+     * @param role Security role to verify
+     */
+    public boolean findSecurityRole(String role) {
+
+        synchronized (securityRoles) {
+            for (int i = 0; i < securityRoles.length; i++) {
+                if (role.equals(securityRoles[i]))
+                    return (true);
+            }
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Return the security roles defined for this application.  If none
+     * have been defined, a zero-length array is returned.
+     */
+    public String[] findSecurityRoles() {
+
+        return (securityRoles);
+
+    }
+
+
+    /**
+     * Return the servlet name mapped by the specified pattern (if any);
+     * otherwise return <code>null</code>.
+     *
+     * @param pattern Pattern for which a mapping is requested
+     */
+    public String findServletMapping(String pattern) {
+
+        synchronized (servletMappings) {
+            return ((String) servletMappings.get(pattern));
+        }
+
+    }
+
+
+    /**
+     * Return the patterns of all defined servlet mappings for this
+     * Context.  If no mappings are defined, a zero-length array is returned.
+     */
+    public String[] findServletMappings() {
+
+        synchronized (servletMappings) {
+            String results[] = new String[servletMappings.size()];
+            return
+               ((String[]) servletMappings.keySet().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the context-relative URI of the error page for the specified
+     * HTTP status code, if any; otherwise return <code>null</code>.
+     *
+     * @param status HTTP status code to look up
+     */
+    public String findStatusPage(int status) {
+
+        return ((String) statusPages.get(new Integer(status)));
+
+    }
+
+
+    /**
+     * Return the set of HTTP status codes for which error pages have
+     * been specified.  If none are specified, a zero-length array
+     * is returned.
+     */
+    public int[] findStatusPages() {
+
+        synchronized (statusPages) {
+            int results[] = new int[statusPages.size()];
+            Iterator elements = statusPages.keySet().iterator();
+            int i = 0;
+            while (elements.hasNext())
+                results[i++] = ((Integer) elements.next()).intValue();
+            return (results);
+        }
+
+    }
+
+
+    /**
+     * Return the tag library descriptor location for the specified taglib
+     * URI, if any; otherwise, return <code>null</code>.
+     *
+     * @param uri URI, relative to the web.xml file
+     */
+    public String findTaglib(String uri) {
+
+        synchronized (taglibs) {
+            return ((String) taglibs.get(uri));
+        }
+
+    }
+
+
+    /**
+     * Return the URIs of all tag libraries for which a tag library
+     * descriptor location has been specified.  If none are specified,
+     * a zero-length array is returned.
+     */
+    public String[] findTaglibs() {
+
+        synchronized (taglibs) {
+            String results[] = new String[taglibs.size()];
+            return ((String[]) taglibs.keySet().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified welcome file is defined
+     * for this Context; otherwise return <code>false</code>.
+     *
+     * @param name Welcome file to verify
+     */
+    public boolean findWelcomeFile(String name) {
+
+        synchronized (welcomeFiles) {
+            for (int i = 0; i < welcomeFiles.length; i++) {
+                if (name.equals(welcomeFiles[i]))
+                    return (true);
+            }
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Return the set of watched resources for this Context. If none are 
+     * defined, a zero length array will be returned.
+     */
+    public String[] findWatchedResources() {
+        return watchedResources;
+    }
+    
+    
+    /**
+     * Return the set of welcome files defined for this Context.  If none are
+     * defined, a zero-length array is returned.
+     */
+    public String[] findWelcomeFiles() {
+
+        return (welcomeFiles);
+
+    }
+
+
+    /**
+     * Return the set of LifecycleListener classes that will be added to
+     * newly created Wrappers automatically.
+     */
+    public String[] findWrapperLifecycles() {
+
+        return (wrapperLifecycles);
+
+    }
+
+
+    /**
+     * Return the set of ContainerListener classes that will be added to
+     * newly created Wrappers automatically.
+     */
+    public String[] findWrapperListeners() {
+
+        return (wrapperListeners);
+
+    }
+
+
+    /**
+     * Reload this web application, if reloading is supported.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  This method is designed to deal with
+     * reloads required by changes to classes in the underlying repositories
+     * of our class loader.  It does not handle changes to the web application
+     * deployment descriptor.  If that has occurred, you should stop this
+     * Context and create (and start) a new Context instance instead.
+     *
+     * @exception IllegalStateException if the <code>reloadable</code>
+     *  property is set to <code>false</code>.
+     */
+    public synchronized void reload() {
+
+        // Validate our current component state
+        if (!started)
+            throw new IllegalStateException
+                (sm.getString("containerBase.notStarted", logName()));
+
+        // Make sure reloading is enabled
+        //      if (!reloadable)
+        //          throw new IllegalStateException
+        //              (sm.getString("standardContext.notReloadable"));
+        if(log.isInfoEnabled())
+            log.info(sm.getString("standardContext.reloadingStarted"));
+
+        // Stop accepting requests temporarily
+        setPaused(true);
+
+        try {
+            stop();
+        } catch (LifecycleException e) {
+            log.error(sm.getString("standardContext.stoppingContext"), e);
+        }
+
+        try {
+            start();
+        } catch (LifecycleException e) {
+            log.error(sm.getString("standardContext.startingContext"), e);
+        }
+
+        setPaused(false);
+
+    }
+
+
+    /**
+     * Remove the specified application listener class from the set of
+     * listeners for this application.
+     *
+     * @param listener Java class name of the listener to be removed
+     */
+    public void removeApplicationListener(String listener) {
+
+        synchronized (applicationListeners) {
+
+            // Make sure this welcome file is currently present
+            int n = -1;
+            for (int i = 0; i < applicationListeners.length; i++) {
+                if (applicationListeners[i].equals(listener)) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified constraint
+            int j = 0;
+            String results[] = new String[applicationListeners.length - 1];
+            for (int i = 0; i < applicationListeners.length; i++) {
+                if (i != n)
+                    results[j++] = applicationListeners[i];
+            }
+            applicationListeners = results;
+
+        }
+
+        // Inform interested listeners
+        fireContainerEvent("removeApplicationListener", listener);
+
+        // FIXME - behavior if already started?
+
+    }
+
+
+    /**
+     * Remove the application parameter with the specified name from
+     * the set for this application.
+     *
+     * @param name Name of the application parameter to remove
+     */
+    public void removeApplicationParameter(String name) {
+
+        synchronized (applicationParameters) {
+
+            // Make sure this parameter is currently present
+            int n = -1;
+            for (int i = 0; i < applicationParameters.length; i++) {
+                if (name.equals(applicationParameters[i].getName())) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified parameter
+            int j = 0;
+            ApplicationParameter results[] =
+                new ApplicationParameter[applicationParameters.length - 1];
+            for (int i = 0; i < applicationParameters.length; i++) {
+                if (i != n)
+                    results[j++] = applicationParameters[i];
+            }
+            applicationParameters = results;
+
+        }
+
+        // Inform interested listeners
+        fireContainerEvent("removeApplicationParameter", name);
+
+    }
+
+
+    /**
+     * Add a child Container, only if the proposed child is an implementation
+     * of Wrapper.
+     *
+     * @param child Child container to be added
+     *
+     * @exception IllegalArgumentException if the proposed container is
+     *  not an implementation of Wrapper
+     */
+    public void removeChild(Container child) {
+
+        if (!(child instanceof Wrapper)) {
+            throw new IllegalArgumentException
+                (sm.getString("standardContext.notWrapper"));
+        }
+
+        super.removeChild(child);
+
+    }
+
+
+    /**
+     * Remove the specified security constraint from this web application.
+     *
+     * @param constraint Constraint to be removed
+     */
+    public void removeConstraint(SecurityConstraint constraint) {
+
+        synchronized (constraints) {
+
+            // Make sure this constraint is currently present
+            int n = -1;
+            for (int i = 0; i < constraints.length; i++) {
+                if (constraints[i].equals(constraint)) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified constraint
+            int j = 0;
+            SecurityConstraint results[] =
+                new SecurityConstraint[constraints.length - 1];
+            for (int i = 0; i < constraints.length; i++) {
+                if (i != n)
+                    results[j++] = constraints[i];
+            }
+            constraints = results;
+
+        }
+
+        // Inform interested listeners
+        fireContainerEvent("removeConstraint", constraint);
+
+    }
+
+
+    /**
+     * Remove the error page for the specified error code or
+     * Java language exception, if it exists; otherwise, no action is taken.
+     *
+     * @param errorPage The error page definition to be removed
+     */
+    public void removeErrorPage(ErrorPage errorPage) {
+
+        String exceptionType = errorPage.getExceptionType();
+        if (exceptionType != null) {
+            synchronized (exceptionPages) {
+                exceptionPages.remove(exceptionType);
+            }
+        } else {
+            synchronized (statusPages) {
+                if (errorPage.getErrorCode() == 200) {
+                    this.okErrorPage = null;
+                }
+                statusPages.remove(new Integer(errorPage.getErrorCode()));
+            }
+        }
+        fireContainerEvent("removeErrorPage", errorPage);
+
+    }
+
+
+    /**
+     * Remove the specified filter definition from this Context, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param filterDef Filter definition to be removed
+     */
+    public void removeFilterDef(FilterDef filterDef) {
+
+        synchronized (filterDefs) {
+            filterDefs.remove(filterDef.getFilterName());
+        }
+        fireContainerEvent("removeFilterDef", filterDef);
+
+    }
+
+
+    /**
+     * Remove a filter mapping from this Context.
+     *
+     * @param filterMap The filter mapping to be removed
+     */
+    public void removeFilterMap(FilterMap filterMap) {
+
+        synchronized (filterMaps) {
+
+            // Make sure this filter mapping is currently present
+            int n = -1;
+            for (int i = 0; i < filterMaps.length; i++) {
+                if (filterMaps[i] == filterMap) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified filter mapping
+            FilterMap results[] = new FilterMap[filterMaps.length - 1];
+            System.arraycopy(filterMaps, 0, results, 0, n);
+            System.arraycopy(filterMaps, n + 1, results, n,
+                             (filterMaps.length - 1) - n);
+            filterMaps = results;
+
+        }
+
+        // Inform interested listeners
+        fireContainerEvent("removeFilterMap", filterMap);
+
+    }
+
+
+    /**
+     * Remove a class name from the set of InstanceListener classes that
+     * will be added to newly created Wrappers.
+     *
+     * @param listener Class name of an InstanceListener class to be removed
+     */
+    public void removeInstanceListener(String listener) {
+
+        synchronized (instanceListeners) {
+
+            // Make sure this welcome file is currently present
+            int n = -1;
+            for (int i = 0; i < instanceListeners.length; i++) {
+                if (instanceListeners[i].equals(listener)) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified constraint
+            int j = 0;
+            String results[] = new String[instanceListeners.length - 1];
+            for (int i = 0; i < instanceListeners.length; i++) {
+                if (i != n)
+                    results[j++] = instanceListeners[i];
+            }
+            instanceListeners = results;
+
+        }
+
+        // Inform interested listeners
+        fireContainerEvent("removeInstanceListener", listener);
+
+    }
+
+
+    /**
+     * Remove any message destination with the specified name.
+     *
+     * @param name Name of the message destination to remove
+     */
+    public void removeMessageDestination(String name) {
+
+        synchronized (messageDestinations) {
+            messageDestinations.remove(name);
+        }
+        fireContainerEvent("removeMessageDestination", name);
+
+    }
+
+
+    /**
+     * Remove any message destination ref with the specified name.
+     *
+     * @param name Name of the message destination ref to remove
+     */
+    public void removeMessageDestinationRef(String name) {
+
+        namingResources.removeMessageDestinationRef(name);
+        fireContainerEvent("removeMessageDestinationRef", name);
+
+    }
+
+
+    /**
+     * Remove the MIME mapping for the specified extension, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param extension Extension to remove the mapping for
+     */
+    public void removeMimeMapping(String extension) {
+
+        synchronized (mimeMappings) {
+            mimeMappings.remove(extension);
+        }
+        fireContainerEvent("removeMimeMapping", extension);
+
+    }
+
+
+    /**
+     * Remove the context initialization parameter with the specified
+     * name, if it exists; otherwise, no action is taken.
+     *
+     * @param name Name of the parameter to remove
+     */
+    public void removeParameter(String name) {
+
+        synchronized (parameters) {
+            parameters.remove(name);
+        }
+        fireContainerEvent("removeParameter", name);
+
+    }
+
+
+    /**
+     * Remove any security role reference for the specified name
+     *
+     * @param role Security role (as used in the application) to remove
+     */
+    public void removeRoleMapping(String role) {
+
+        synchronized (roleMappings) {
+            roleMappings.remove(role);
+        }
+        fireContainerEvent("removeRoleMapping", role);
+
+    }
+
+
+    /**
+     * Remove any security role with the specified name.
+     *
+     * @param role Security role to remove
+     */
+    public void removeSecurityRole(String role) {
+
+        synchronized (securityRoles) {
+
+            // Make sure this security role is currently present
+            int n = -1;
+            for (int i = 0; i < securityRoles.length; i++) {
+                if (role.equals(securityRoles[i])) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified security role
+            int j = 0;
+            String results[] = new String[securityRoles.length - 1];
+            for (int i = 0; i < securityRoles.length; i++) {
+                if (i != n)
+                    results[j++] = securityRoles[i];
+            }
+            securityRoles = results;
+
+        }
+
+        // Inform interested listeners
+        fireContainerEvent("removeSecurityRole", role);
+
+    }
+
+
+    /**
+     * Remove any servlet mapping for the specified pattern, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param pattern URL pattern of the mapping to remove
+     */
+    public void removeServletMapping(String pattern) {
+
+        String name = null;
+        synchronized (servletMappings) {
+            name = (String) servletMappings.remove(pattern);
+        }
+        Wrapper wrapper = (Wrapper) findChild(name);
+        if( wrapper != null ) {
+            wrapper.removeMapping(pattern);
+        }
+        mapper.removeWrapper(pattern);
+        fireContainerEvent("removeServletMapping", pattern);
+
+    }
+
+
+    /**
+     * Remove the tag library location forthe specified tag library URI.
+     *
+     * @param uri URI, relative to the web.xml file
+     */
+    public void removeTaglib(String uri) {
+
+        synchronized (taglibs) {
+            taglibs.remove(uri);
+        }
+        fireContainerEvent("removeTaglib", uri);
+    }
+
+
+    /**
+     * Remove the specified watched resource name from the list associated
+     * with this Context.
+     * 
+     * @param name Name of the watched resource to be removed
+     */
+    public void removeWatchedResource(String name) {
+        
+        synchronized (watchedResources) {
+
+            // Make sure this watched resource is currently present
+            int n = -1;
+            for (int i = 0; i < watchedResources.length; i++) {
+                if (watchedResources[i].equals(name)) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified watched resource
+            int j = 0;
+            String results[] = new String[watchedResources.length - 1];
+            for (int i = 0; i < watchedResources.length; i++) {
+                if (i != n)
+                    results[j++] = watchedResources[i];
+            }
+            watchedResources = results;
+
+        }
+
+        fireContainerEvent("removeWatchedResource", name);
+
+    }
+    
+    
+    /**
+     * Remove the specified welcome file name from the list recognized
+     * by this Context.
+     *
+     * @param name Name of the welcome file to be removed
+     */
+    public void removeWelcomeFile(String name) {
+
+        synchronized (welcomeFiles) {
+
+            // Make sure this welcome file is currently present
+            int n = -1;
+            for (int i = 0; i < welcomeFiles.length; i++) {
+                if (welcomeFiles[i].equals(name)) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified constraint
+            int j = 0;
+            String results[] = new String[welcomeFiles.length - 1];
+            for (int i = 0; i < welcomeFiles.length; i++) {
+                if (i != n)
+                    results[j++] = welcomeFiles[i];
+            }
+            welcomeFiles = results;
+
+        }
+
+        // Inform interested listeners
+        postWelcomeFiles();
+        fireContainerEvent("removeWelcomeFile", name);
+
+    }
+
+
+    /**
+     * Remove a class name from the set of LifecycleListener classes that
+     * will be added to newly created Wrappers.
+     *
+     * @param listener Class name of a LifecycleListener class to be removed
+     */
+    public void removeWrapperLifecycle(String listener) {
+
+
+        synchronized (wrapperLifecycles) {
+
+            // Make sure this welcome file is currently present
+            int n = -1;
+            for (int i = 0; i < wrapperLifecycles.length; i++) {
+                if (wrapperLifecycles[i].equals(listener)) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified constraint
+            int j = 0;
+            String results[] = new String[wrapperLifecycles.length - 1];
+            for (int i = 0; i < wrapperLifecycles.length; i++) {
+                if (i != n)
+                    results[j++] = wrapperLifecycles[i];
+            }
+            wrapperLifecycles = results;
+
+        }
+
+        // Inform interested listeners
+        fireContainerEvent("removeWrapperLifecycle", listener);
+
+    }
+
+
+    /**
+     * Remove a class name from the set of ContainerListener classes that
+     * will be added to newly created Wrappers.
+     *
+     * @param listener Class name of a ContainerListener class to be removed
+     */
+    public void removeWrapperListener(String listener) {
+
+
+        synchronized (wrapperListeners) {
+
+            // Make sure this welcome file is currently present
+            int n = -1;
+            for (int i = 0; i < wrapperListeners.length; i++) {
+                if (wrapperListeners[i].equals(listener)) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified constraint
+            int j = 0;
+            String results[] = new String[wrapperListeners.length - 1];
+            for (int i = 0; i < wrapperListeners.length; i++) {
+                if (i != n)
+                    results[j++] = wrapperListeners[i];
+            }
+            wrapperListeners = results;
+
+        }
+
+        // Inform interested listeners
+        fireContainerEvent("removeWrapperListener", listener);
+
+    }
+
+
+    /**
+     * Gets the cumulative processing times of all servlets in this
+     * StandardContext.
+     *
+     * @return Cumulative processing times of all servlets in this
+     * StandardContext
+     */
+    public long getProcessingTime() {
+        
+        long result = 0;
+
+        Container[] children = findChildren();
+        if (children != null) {
+            for( int i=0; i< children.length; i++ ) {
+                result += ((StandardWrapper)children[i]).getProcessingTime();
+            }
+        }
+
+        return result;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Configure and initialize the set of filters for this Context.
+     * Return <code>true</code> if all filter initialization completed
+     * successfully, or <code>false</code> otherwise.
+     */
+    public boolean filterStart() {
+
+        if (getLogger().isDebugEnabled())
+            getLogger().debug("Starting filters");
+        // Instantiate and record a FilterConfig for each defined filter
+        boolean ok = true;
+        synchronized (filterConfigs) {
+            filterConfigs.clear();
+            Iterator names = filterDefs.keySet().iterator();
+            while (names.hasNext()) {
+                String name = (String) names.next();
+                if (getLogger().isDebugEnabled())
+                    getLogger().debug(" Starting filter '" + name + "'");
+                ApplicationFilterConfig filterConfig = null;
+                try {
+                    filterConfig = new ApplicationFilterConfig
+                      (this, (FilterDef) filterDefs.get(name));
+                    filterConfigs.put(name, filterConfig);
+                } catch (Throwable t) {
+                    getLogger().error
+                        (sm.getString("standardContext.filterStart", name), t);
+                    ok = false;
+                }
+            }
+        }
+
+        return (ok);
+
+    }
+
+
+    /**
+     * Finalize and release the set of filters for this Context.
+     * Return <code>true</code> if all filter finalization completed
+     * successfully, or <code>false</code> otherwise.
+     */
+    public boolean filterStop() {
+
+        if (getLogger().isDebugEnabled())
+            getLogger().debug("Stopping filters");
+
+        // Release all Filter and FilterConfig instances
+        synchronized (filterConfigs) {
+            Iterator names = filterConfigs.keySet().iterator();
+            while (names.hasNext()) {
+                String name = (String) names.next();
+                if (getLogger().isDebugEnabled())
+                    getLogger().debug(" Stopping filter '" + name + "'");
+                ApplicationFilterConfig filterConfig =
+                  (ApplicationFilterConfig) filterConfigs.get(name);
+                filterConfig.release();
+            }
+            filterConfigs.clear();
+        }
+        return (true);
+
+    }
+
+
+    /**
+     * Find and return the initialized <code>FilterConfig</code> for the
+     * specified filter name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired filter
+     */
+    public FilterConfig findFilterConfig(String name) {
+
+        return ((FilterConfig) filterConfigs.get(name));
+
+    }
+
+
+    /**
+     * Configure the set of instantiated application event listeners
+     * for this Context.  Return <code>true</code> if all listeners wre
+     * initialized successfully, or <code>false</code> otherwise.
+     */
+    public boolean listenerStart() {
+
+        if (log.isDebugEnabled())
+            log.debug("Configuring application event listeners");
+
+        // Instantiate the required listeners
+        ClassLoader loader = getLoader().getClassLoader();
+        String listeners[] = findApplicationListeners();
+        Object results[] = new Object[listeners.length];
+        boolean ok = true;
+        for (int i = 0; i < results.length; i++) {
+            if (getLogger().isDebugEnabled())
+                getLogger().debug(" Configuring event listener class '" +
+                    listeners[i] + "'");
+            try {
+                Class clazz = loader.loadClass(listeners[i]);
+                results[i] = clazz.newInstance();
+            } catch (Throwable t) {
+                getLogger().error
+                    (sm.getString("standardContext.applicationListener",
+                                  listeners[i]), t);
+                ok = false;
+            }
+        }
+        if (!ok) {
+            getLogger().error(sm.getString("standardContext.applicationSkipped"));
+            return (false);
+        }
+
+        // Sort listeners in two arrays
+        ArrayList eventListeners = new ArrayList();
+        ArrayList lifecycleListeners = new ArrayList();
+        for (int i = 0; i < results.length; i++) {
+            if ((results[i] instanceof ServletContextAttributeListener)
+                || (results[i] instanceof ServletRequestAttributeListener)
+                || (results[i] instanceof ServletRequestListener)
+                || (results[i] instanceof HttpSessionAttributeListener)) {
+                eventListeners.add(results[i]);
+            }
+            if ((results[i] instanceof ServletContextListener)
+                || (results[i] instanceof HttpSessionListener)) {
+                lifecycleListeners.add(results[i]);
+            }
+        }
+
+        setApplicationEventListeners(eventListeners.toArray());
+        setApplicationLifecycleListeners(lifecycleListeners.toArray());
+
+        // Send application start events
+
+        if (getLogger().isDebugEnabled())
+            getLogger().debug("Sending application start events");
+
+        Object instances[] = getApplicationLifecycleListeners();
+        if (instances == null)
+            return (ok);
+        ServletContextEvent event =
+          new ServletContextEvent(getServletContext());
+        for (int i = 0; i < instances.length; i++) {
+            if (instances[i] == null)
+                continue;
+            if (!(instances[i] instanceof ServletContextListener))
+                continue;
+            ServletContextListener listener =
+                (ServletContextListener) instances[i];
+            try {
+                fireContainerEvent("beforeContextInitialized", listener);
+                listener.contextInitialized(event);
+                fireContainerEvent("afterContextInitialized", listener);
+            } catch (Throwable t) {
+                fireContainerEvent("afterContextInitialized", listener);
+                getLogger().error
+                    (sm.getString("standardContext.listenerStart",
+                                  instances[i].getClass().getName()), t);
+                ok = false;
+            }
+        }
+        return (ok);
+
+    }
+
+
+    /**
+     * Send an application stop event to all interested listeners.
+     * Return <code>true</code> if all events were sent successfully,
+     * or <code>false</code> otherwise.
+     */
+    public boolean listenerStop() {
+
+        if (log.isDebugEnabled())
+            log.debug("Sending application stop events");
+
+        boolean ok = true;
+        Object listeners[] = getApplicationLifecycleListeners();
+        if (listeners == null)
+            return (ok);
+        ServletContextEvent event =
+          new ServletContextEvent(getServletContext());
+        for (int i = 0; i < listeners.length; i++) {
+            int j = (listeners.length - 1) - i;
+            if (listeners[j] == null)
+                continue;
+            if (!(listeners[j] instanceof ServletContextListener))
+                continue;
+            ServletContextListener listener =
+                (ServletContextListener) listeners[j];
+            try {
+                fireContainerEvent("beforeContextDestroyed", listener);
+                listener.contextDestroyed(event);
+                fireContainerEvent("afterContextDestroyed", listener);
+            } catch (Throwable t) {
+                fireContainerEvent("afterContextDestroyed", listener);
+                getLogger().error
+                    (sm.getString("standardContext.listenerStop",
+                                  listeners[j].getClass().getName()), t);
+                ok = false;
+            }
+        }
+
+        setApplicationEventListeners(null);
+        setApplicationLifecycleListeners(null);
+
+        return (ok);
+
+    }
+
+
+    /**
+     * Allocate resources, including proxy.
+     * Return <code>true</code> if initialization was successfull,
+     * or <code>false</code> otherwise.
+     */
+    public boolean resourcesStart() {
+
+        boolean ok = true;
+
+        Hashtable env = new Hashtable();
+        if (getParent() != null)
+            env.put(ProxyDirContext.HOST, getParent().getName());
+        env.put(ProxyDirContext.CONTEXT, getName());
+
+        try {
+            ProxyDirContext proxyDirContext =
+                new ProxyDirContext(env, webappResources);
+            if (webappResources instanceof FileDirContext) {
+                filesystemBased = true;
+                ((FileDirContext) webappResources).setCaseSensitive
+                    (isCaseSensitive());
+                ((FileDirContext) webappResources).setAllowLinking
+                    (isAllowLinking());
+            }
+            if (webappResources instanceof BaseDirContext) {
+                ((BaseDirContext) webappResources).setDocBase(getBasePath());
+                ((BaseDirContext) webappResources).setCached
+                    (isCachingAllowed());
+                ((BaseDirContext) webappResources).setCacheTTL(getCacheTTL());
+                ((BaseDirContext) webappResources).setCacheMaxSize
+                    (getCacheMaxSize());
+                ((BaseDirContext) webappResources).allocate();
+            }
+            // Register the cache in JMX
+            if (isCachingAllowed()) {
+                ObjectName resourcesName = 
+                    new ObjectName(this.getDomain() + ":type=Cache,host=" 
+                                   + getHostname() + ",path=" 
+                                   + (("".equals(getPath()))?"/":getPath()));
+                Registry.getRegistry(null, null).registerComponent
+                    (proxyDirContext.getCache(), resourcesName, null);
+            }
+            this.resources = proxyDirContext;
+        } catch (Throwable t) {
+            log.error(sm.getString("standardContext.resourcesStart"), t);
+            ok = false;
+        }
+
+        return (ok);
+
+    }
+
+
+    /**
+     * Deallocate resources and destroy proxy.
+     */
+    public boolean resourcesStop() {
+
+        boolean ok = true;
+
+        try {
+            if (resources != null) {
+                if (resources instanceof Lifecycle) {
+                    ((Lifecycle) resources).stop();
+                }
+                if (webappResources instanceof BaseDirContext) {
+                    ((BaseDirContext) webappResources).release();
+                }
+                // Unregister the cache in JMX
+                if (isCachingAllowed()) {
+                    ObjectName resourcesName = 
+                        new ObjectName(this.getDomain()
+                                       + ":type=Cache,host=" 
+                                       + getHostname() + ",path=" 
+                                       + (("".equals(getPath()))?"/"
+                                          :getPath()));
+                    Registry.getRegistry(null, null)
+                        .unregisterComponent(resourcesName);
+                }
+            }
+        } catch (Throwable t) {
+            log.error(sm.getString("standardContext.resourcesStop"), t);
+            ok = false;
+        }
+
+        this.resources = null;
+
+        return (ok);
+
+    }
+
+
+    /**
+     * Load and initialize all servlets marked "load on startup" in the
+     * web application deployment descriptor.
+     *
+     * @param children Array of wrappers for all currently defined
+     *  servlets (including those not declared load on startup)
+     */
+    public void loadOnStartup(Container children[]) {
+
+        // Collect "load on startup" servlets that need to be initialized
+        TreeMap map = new TreeMap();
+        for (int i = 0; i < children.length; i++) {
+            Wrapper wrapper = (Wrapper) children[i];
+            int loadOnStartup = wrapper.getLoadOnStartup();
+            if (loadOnStartup < 0)
+                continue;
+            if (loadOnStartup == 0)     // Arbitrarily put them last
+                loadOnStartup = Integer.MAX_VALUE;
+            Integer key = new Integer(loadOnStartup);
+            ArrayList list = (ArrayList) map.get(key);
+            if (list == null) {
+                list = new ArrayList();
+                map.put(key, list);
+            }
+            list.add(wrapper);
+        }
+
+        // Load the collected "load on startup" servlets
+        Iterator keys = map.keySet().iterator();
+        while (keys.hasNext()) {
+            Integer key = (Integer) keys.next();
+            ArrayList list = (ArrayList) map.get(key);
+            Iterator wrappers = list.iterator();
+            while (wrappers.hasNext()) {
+                Wrapper wrapper = (Wrapper) wrappers.next();
+                try {
+                    wrapper.load();
+                } catch (ServletException e) {
+                    getLogger().error(sm.getString("standardWrapper.loadException",
+                                      getName()), StandardWrapper.getRootCause(e));
+                    // NOTE: load errors (including a servlet that throws
+                    // UnavailableException from tht init() method) are NOT
+                    // fatal to application startup
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Start this Context component.
+     *
+     * @exception LifecycleException if a startup error occurs
+     */
+    public synchronized void start() throws LifecycleException {
+        //if (lazy ) return;
+        if (started) {
+            if(log.isInfoEnabled())
+                log.info(sm.getString("containerBase.alreadyStarted", logName()));
+            return;
+        }
+        if( !initialized ) { 
+            try {
+                init();
+            } catch( Exception ex ) {
+                throw new LifecycleException("Error initializaing ", ex);
+            }
+        }
+        if(log.isDebugEnabled())
+            log.debug("Starting " + ("".equals(getName()) ? "ROOT" : getName()));
+
+        // Set JMX object name for proper pipeline registration
+        preRegisterJMX();
+
+        if ((oname != null) && 
+            (Registry.getRegistry(null, null).getMBeanServer().isRegistered(oname))) {
+            // As things depend on the JMX registration, the context
+            // must be reregistered again once properly initialized
+            Registry.getRegistry(null, null).unregisterComponent(oname);
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+        setAvailable(false);
+        setConfigured(false);
+        boolean ok = true;
+
+        // Add missing components as necessary
+        if (webappResources == null) {   // (1) Required by Loader
+            if (log.isDebugEnabled())
+                log.debug("Configuring default Resources");
+            try {
+                if ((docBase != null) && (docBase.endsWith(".war")) && (!(new File(getBasePath())).isDirectory()))
+                    setResources(new WARDirContext());
+                else
+                    setResources(new FileDirContext());
+            } catch (IllegalArgumentException e) {
+                log.error("Error initializing resources: " + e.getMessage());
+                ok = false;
+            }
+        }
+        if (ok) {
+            if (!resourcesStart()) {
+                log.error( "Error in resourceStart()");
+                ok = false;
+            }
+        }
+
+        // Look for a realm - that may have been configured earlier. 
+        // If the realm is added after context - it'll set itself.
+        if( realm == null && mserver != null ) {
+            ObjectName realmName=null;
+            try {
+                realmName=new ObjectName( getEngineName() + ":type=Realm,host=" + 
+                        getHostname() + ",path=" + getPath());
+                if( mserver.isRegistered(realmName ) ) {
+                    mserver.invoke(realmName, "init", 
+                            new Object[] {},
+                            new String[] {}
+                    );            
+                }
+            } catch( Throwable t ) {
+                if(log.isDebugEnabled())
+                    log.debug("No realm for this host " + realmName);
+            }
+        }
+        
+        if (getLoader() == null) {
+            ClassLoader parent = null;
+            if (getPrivileged()) {
+                if (log.isDebugEnabled())
+                    log.debug("Configuring privileged default Loader");
+                parent = this.getClass().getClassLoader();
+            } else {
+                if (log.isDebugEnabled())
+                    log.debug("Configuring non-privileged default Loader");
+                parent = getParentClassLoader();
+            }
+            WebappLoader webappLoader = new WebappLoader(parent);
+            webappLoader.setDelegate(getDelegate());
+            setLoader(webappLoader);
+        }
+
+        // Initialize character set mapper
+        getCharsetMapper();
+
+        // Post work directory
+        postWorkDirectory();
+
+        // Validate required extensions
+        boolean dependencyCheck = true;
+        try {
+            dependencyCheck = ExtensionValidator.validateApplication
+                (getResources(), this);
+        } catch (IOException ioe) {
+            log.error("Error in dependencyCheck", ioe);
+            dependencyCheck = false;
+        }
+
+        if (!dependencyCheck) {
+            // do not make application available if depency check fails
+            ok = false;
+        }
+
+        // Reading the "catalina.useNaming" environment variable
+        String useNamingProperty = System.getProperty("catalina.useNaming");
+        if ((useNamingProperty != null)
+            && (useNamingProperty.equals("false"))) {
+            useNaming = false;
+        }
+
+        if (ok && isUseNaming()) {
+            if (namingContextListener == null) {
+                namingContextListener = new NamingContextListener();
+                namingContextListener.setName(getNamingContextName());
+                addLifecycleListener(namingContextListener);
+            }
+        }
+
+        // Binding thread
+        ClassLoader oldCCL = bindThread();
+
+        // Standard container startup
+        if (log.isDebugEnabled())
+            log.debug("Processing standard container startup");
+
+        if (ok) {
+
+            boolean mainOk = false;
+            try {
+
+                started = true;
+
+                // Start our subordinate components, if any
+                if ((loader != null) && (loader instanceof Lifecycle))
+                    ((Lifecycle) loader).start();
+
+                // Unbinding thread
+                unbindThread(oldCCL);
+
+                // Binding thread
+                oldCCL = bindThread();
+
+                if ((logger != null) && (logger instanceof Lifecycle))
+                    ((Lifecycle) logger).start();
+                if ((cluster != null) && (cluster instanceof Lifecycle))
+                    ((Lifecycle) cluster).start();
+                if ((realm != null) && (realm instanceof Lifecycle))
+                    ((Lifecycle) realm).start();
+                if ((resources != null) && (resources instanceof Lifecycle))
+                    ((Lifecycle) resources).start();
+
+                // Start our child containers, if any
+                Container children[] = findChildren();
+                for (int i = 0; i < children.length; i++) {
+                    if (children[i] instanceof Lifecycle)
+                        ((Lifecycle) children[i]).start();
+                }
+
+                // Start the Valves in our pipeline (including the basic),
+                // if any
+                if (pipeline instanceof Lifecycle) {
+                    ((Lifecycle) pipeline).start();
+                }
+                
+                if(getProcessTlds()) {
+                    processTlds();
+                }
+                
+                // Notify our interested LifecycleListeners
+                lifecycle.fireLifecycleEvent(START_EVENT, null);
+
+                // Start manager
+                if ((manager != null) && (manager instanceof Lifecycle)) {
+                    ((Lifecycle) getManager()).start();
+                }
+
+                // Start ContainerBackgroundProcessor thread
+                super.threadStart();
+
+                mainOk = true;
+
+            } finally {
+                // Unbinding thread
+                unbindThread(oldCCL);
+                if (!mainOk) {
+                    // An exception occurred
+                    // Register with JMX anyway, to allow management
+                    registerJMX();
+                }
+            }
+
+        }
+        if (!getConfigured()) {
+            log.error( "Error getConfigured");
+            ok = false;
+        }
+
+        // We put the resources into the servlet context
+        if (ok)
+            getServletContext().setAttribute
+                (Globals.RESOURCES_ATTR, getResources());
+
+        // Initialize associated mapper
+        mapper.setContext(getPath(), welcomeFiles, resources);
+
+        // Binding thread
+        oldCCL = bindThread();
+
+        // Create context attributes that will be required
+        if (ok) {
+            postWelcomeFiles();
+        }
+
+        if (ok) {
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+        }
+
+        // Configure and call application event listeners and filters
+        if (ok) {
+            if (!listenerStart()) {
+                log.error( "Error listenerStart");
+                ok = false;
+            }
+        }
+        if (ok) {
+            if (!filterStart()) {
+                log.error( "Error filterStart");
+                ok = false;
+            }
+        }
+
+        // Load and initialize all "load on startup" servlets
+        if (ok) {
+            loadOnStartup(findChildren());
+        }
+
+        // Unbinding thread
+        unbindThread(oldCCL);
+
+        // Set available status depending upon startup success
+        if (ok) {
+            if (log.isDebugEnabled())
+                log.debug("Starting completed");
+            setAvailable(true);
+        } else {
+            log.error(sm.getString("standardContext.startFailed", getName()));
+            try {
+                stop();
+            } catch (Throwable t) {
+                log.error(sm.getString("standardContext.startCleanup"), t);
+            }
+            setAvailable(false);
+        }
+
+        // JMX registration
+        registerJMX();
+
+        startTime=System.currentTimeMillis();
+        
+        // Send j2ee.state.running notification 
+        if (ok && (this.getObjectName() != null)) {
+            Notification notification = 
+                new Notification("j2ee.state.running", this.getObjectName(), 
+                                sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        }
+
+        // Close all JARs right away to avoid always opening a peak number 
+        // of files on startup
+        if (getLoader() instanceof WebappLoader) {
+            ((WebappLoader) getLoader()).closeJARs(true);
+        }
+
+        // Reinitializing if something went wrong
+        if (!ok && started) {
+            stop();
+        }
+
+        //cacheContext();
+    }
+
+    /**
+     * Processes TLDs.
+     *
+     * @throws LifecycleException If an error occurs
+     */
+     protected void processTlds() throws LifecycleException {
+       TldConfig tldConfig = new TldConfig();
+       tldConfig.setContext(this);
+
+       // (1)  check if the attribute has been defined
+       //      on the context element.
+       tldConfig.setTldValidation(tldValidation);
+       tldConfig.setTldNamespaceAware(tldNamespaceAware);
+
+       // (2) if the attribute wasn't defined on the context
+       //     try the host.
+       if (!tldValidation) {
+         tldConfig.setTldValidation
+           (((StandardHost) getParent()).getXmlValidation());
+       }
+
+       if (!tldNamespaceAware) {
+         tldConfig.setTldNamespaceAware
+           (((StandardHost) getParent()).getXmlNamespaceAware());
+       }
+                    
+       try {
+         tldConfig.execute();
+       } catch (Exception ex) {
+         log.error("Error reading tld listeners " 
+                    + ex.toString(), ex); 
+       }
+     }
+    
+    private void cacheContext() {
+        try {
+            File workDir=new File( getWorkPath() );
+            
+            File ctxSer=new File( workDir, "_tomcat_context.ser");
+            FileOutputStream fos=new FileOutputStream( ctxSer );
+            ObjectOutputStream oos=new ObjectOutputStream( fos );
+            oos.writeObject(this);
+            oos.close();
+            fos.close();
+        } catch( Throwable t ) {
+            if(log.isInfoEnabled())
+                log.info("Error saving context.ser ", t);
+        }
+    }
+
+    
+    /**
+     * Stop this Context component.
+     *
+     * @exception LifecycleException if a shutdown error occurs
+     */
+    public synchronized void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started) {
+            if(log.isInfoEnabled())
+                log.info(sm.getString("containerBase.notStarted", logName()));
+            return;
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+        
+        // Send j2ee.state.stopping notification 
+        if (this.getObjectName() != null) {
+            Notification notification = 
+                new Notification("j2ee.state.stopping", this.getObjectName(), 
+                                sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        }
+        
+        // Mark this application as unavailable while we shut down
+        setAvailable(false);
+
+        // Binding thread
+        ClassLoader oldCCL = bindThread();
+
+        // Stop our filters
+        filterStop();
+
+        // Stop ContainerBackgroundProcessor thread
+        super.threadStop();
+
+        if ((manager != null) && (manager instanceof Lifecycle)) {
+            ((Lifecycle) manager).stop();
+        }
+
+        // Finalize our character set mapper
+        setCharsetMapper(null);
+
+        // Normal container shutdown processing
+        if (log.isDebugEnabled())
+            log.debug("Processing standard container shutdown");
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        try {
+
+            // Stop the Valves in our pipeline (including the basic), if any
+            if (pipeline instanceof Lifecycle) {
+                ((Lifecycle) pipeline).stop();
+            }
+
+            // Stop our child containers, if any
+            Container[] children = findChildren();
+            for (int i = 0; i < children.length; i++) {
+                if (children[i] instanceof Lifecycle)
+                    ((Lifecycle) children[i]).stop();
+            }
+
+            // Stop our application listeners
+            listenerStop();
+
+            // Clear all application-originated servlet context attributes
+            if (context != null)
+                context.clearAttributes();
+
+            // Stop resources
+            resourcesStop();
+
+            if ((realm != null) && (realm instanceof Lifecycle)) {
+                ((Lifecycle) realm).stop();
+            }
+            if ((cluster != null) && (cluster instanceof Lifecycle)) {
+                ((Lifecycle) cluster).stop();
+            }
+            if ((logger != null) && (logger instanceof Lifecycle)) {
+                ((Lifecycle) logger).stop();
+            }
+            if ((loader != null) && (loader instanceof Lifecycle)) {
+                ((Lifecycle) loader).stop();
+            }
+
+        } finally {
+
+            // Unbinding thread
+            unbindThread(oldCCL);
+
+        }
+
+        // Send j2ee.state.stopped notification 
+        if (this.getObjectName() != null) {
+            Notification notification = 
+                new Notification("j2ee.state.stopped", this.getObjectName(), 
+                                sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        }
+        
+        // Reset application context
+        context = null;
+
+        // This object will no longer be visible or used. 
+        try {
+            resetContext();
+        } catch( Exception ex ) {
+            log.error( "Error reseting context " + this + " " + ex, ex );
+        }
+        
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+
+        if (log.isDebugEnabled())
+            log.debug("Stopping complete");
+
+    }
+
+    /** Destroy needs to clean up the context completely.
+     * 
+     * The problem is that undoing all the config in start() and restoring 
+     * a 'fresh' state is impossible. After stop()/destroy()/init()/start()
+     * we should have the same state as if a fresh start was done - i.e
+     * read modified web.xml, etc. This can only be done by completely 
+     * removing the context object and remapping a new one, or by cleaning
+     * up everything.
+     * 
+     * XXX Should this be done in stop() ?
+     * 
+     */ 
+    public void destroy() throws Exception {
+        if( oname != null ) { 
+            // Send j2ee.object.deleted notification 
+            Notification notification = 
+                new Notification("j2ee.object.deleted", this.getObjectName(), 
+                                sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        } 
+        super.destroy();
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(DESTROY_EVENT, null);
+
+        instanceListeners = new String[0];
+        applicationListeners = new String[0];
+    }
+    
+    private void resetContext() throws Exception, MBeanRegistrationException {
+        // Restore the original state ( pre reading web.xml in start )
+        // If you extend this - override this method and make sure to clean up
+        children=new HashMap();
+        startupTime = 0;
+        startTime = 0;
+        tldScanTime = 0;
+
+        // Bugzilla 32867
+        distributable = false;
+
+        if(log.isDebugEnabled())
+            log.debug("resetContext " + oname + " " + mserver);
+    }
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer();
+        if (getParent() != null) {
+            sb.append(getParent().toString());
+            sb.append(".");
+        }
+        sb.append("StandardContext[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Adjust the URL pattern to begin with a leading slash, if appropriate
+     * (i.e. we are running a servlet 2.2 application).  Otherwise, return
+     * the specified URL pattern unchanged.
+     *
+     * @param urlPattern The URL pattern to be adjusted (if needed)
+     *  and returned
+     */
+    protected String adjustURLPattern(String urlPattern) {
+
+        if (urlPattern == null)
+            return (urlPattern);
+        if (urlPattern.startsWith("/") || urlPattern.startsWith("*."))
+            return (urlPattern);
+        if (!isServlet22())
+            return (urlPattern);
+        if(log.isDebugEnabled())
+            log.debug(sm.getString("standardContext.urlPattern.patternWarning",
+                         urlPattern));
+        return ("/" + urlPattern);
+
+    }
+
+
+    /**
+     * Are we processing a version 2.2 deployment descriptor?
+     */
+    protected boolean isServlet22() {
+
+        if (this.publicId == null)
+            return (false);
+        if (this.publicId.equals
+            (org.apache.catalina.startup.Constants.WebDtdPublicId_22))
+            return (true);
+        else
+            return (false);
+
+    }
+
+
+    /**
+     * Return a File object representing the base directory for the
+     * entire servlet container (i.e. the Engine container if present).
+     */
+    protected File engineBase() {
+        String base=System.getProperty("catalina.base");
+        if( base == null ) {
+            StandardEngine eng=(StandardEngine)this.getParent().getParent();
+            base=eng.getBaseDir();
+        }
+        return (new File(base));
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Bind current thread, both for CL purposes and for JNDI ENC support
+     * during : startup, shutdown and realoading of the context.
+     *
+     * @return the previous context class loader
+     */
+    private ClassLoader bindThread() {
+
+        ClassLoader oldContextClassLoader =
+            Thread.currentThread().getContextClassLoader();
+
+        if (getResources() == null)
+            return oldContextClassLoader;
+
+        if (getLoader().getClassLoader() != null) {
+            Thread.currentThread().setContextClassLoader
+                (getLoader().getClassLoader());
+        }
+
+        DirContextURLStreamHandler.bind(getResources());
+
+        if (isUseNaming()) {
+            try {
+                ContextBindings.bindThread(this, this);
+            } catch (NamingException e) {
+                // Silent catch, as this is a normal case during the early
+                // startup stages
+            }
+        }
+
+        return oldContextClassLoader;
+
+    }
+
+
+    /**
+     * Unbind thread.
+     */
+    private void unbindThread(ClassLoader oldContextClassLoader) {
+
+        Thread.currentThread().setContextClassLoader(oldContextClassLoader);
+
+        oldContextClassLoader = null;
+
+        if (isUseNaming()) {
+            ContextBindings.unbindThread(this, this);
+        }
+
+        DirContextURLStreamHandler.unbind();
+
+    }
+
+
+
+    /**
+     * Get base path.
+     */
+    private String getBasePath() {
+        String docBase = null;
+        Container container = this;
+        while (container != null) {
+            if (container instanceof Host)
+                break;
+            container = container.getParent();
+        }
+        File file = new File(getDocBase());
+        if (!file.isAbsolute()) {
+            if (container == null) {
+                docBase = (new File(engineBase(), getDocBase())).getPath();
+            } else {
+                // Use the "appBase" property of this container
+                String appBase = ((Host) container).getAppBase();
+                file = new File(appBase);
+                if (!file.isAbsolute())
+                    file = new File(engineBase(), appBase);
+                docBase = (new File(file, getDocBase())).getPath();
+            }
+        } else {
+            docBase = file.getPath();
+        }
+        return docBase;
+    }
+
+
+    /**
+     * Get app base.
+     */
+    private String getAppBase() {
+        String appBase = null;
+        Container container = this;
+        while (container != null) {
+            if (container instanceof Host)
+                break;
+            container = container.getParent();
+        }
+        if (container != null) {
+            appBase = ((Host) container).getAppBase();
+        }
+        return appBase;
+    }
+
+
+    /**
+     * Get config base.
+     */
+    public File getConfigBase() {
+        File configBase = 
+            new File(System.getProperty("catalina.base"), "conf");
+        if (!configBase.exists()) {
+            return null;
+        }
+        Container container = this;
+        Container host = null;
+        Container engine = null;
+        while (container != null) {
+            if (container instanceof Host)
+                host = container;
+            if (container instanceof Engine)
+                engine = container;
+            container = container.getParent();
+        }
+        if (engine != null) {
+            configBase = new File(configBase, engine.getName());
+        }
+        if (host != null) {
+            configBase = new File(configBase, host.getName());
+        }
+        if (saveConfig) {
+            configBase.mkdirs();
+        }
+        return configBase;
+    }
+
+
+    /**
+     * Given a context path, get the config file name.
+     */
+    protected String getDefaultConfigFile() {
+        String basename = null;
+        String path = getPath();
+        if (path.equals("")) {
+            basename = "ROOT";
+        } else {
+            basename = path.substring(1).replace('/', '#');
+        }
+        return (basename + ".xml");
+    }
+
+
+    /**
+     * Copy a file.
+     */
+    private boolean copy(File src, File dest) {
+        FileInputStream is = null;
+        FileOutputStream os = null;
+        try {
+            is = new FileInputStream(src);
+            os = new FileOutputStream(dest);
+            byte[] buf = new byte[4096];
+            while (true) {
+                int len = is.read(buf);
+                if (len < 0)
+                    break;
+                os.write(buf, 0, len);
+            }
+            is.close();
+            os.close();
+        } catch (IOException e) {
+            return false;
+        } finally {
+            try {
+                if (is != null) {
+                    is.close();
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+            try {
+                if (os != null) {
+                    os.close();
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+        return true;
+    }
+
+
+    /**
+     * Get naming context full name.
+     */
+    private String getNamingContextName() {
+    if (namingContextName == null) {
+        Container parent = getParent();
+        if (parent == null) {
+        namingContextName = getName();
+        } else {
+        Stack stk = new Stack();
+        StringBuffer buff = new StringBuffer();
+        while (parent != null) {
+            stk.push(parent.getName());
+            parent = parent.getParent();
+        }
+        while (!stk.empty()) {
+            buff.append("/" + stk.pop());
+        }
+        buff.append(getName());
+        namingContextName = buff.toString();
+        }
+    }
+    return namingContextName;
+    }
+
+
+    /**
+     * Return the request processing paused flag for this Context.
+     */
+    public boolean getPaused() {
+
+        return (this.paused);
+
+    }
+
+
+    /**
+     * Post a copy of our web application resources as a servlet context
+     * attribute.
+     */
+    private void postResources() {
+
+        getServletContext().setAttribute
+            (Globals.RESOURCES_ATTR, getResources());
+
+    }
+
+
+    /**
+     * Post a copy of our current list of welcome files as a servlet context
+     * attribute, so that the default servlet can find them.
+     */
+    private void postWelcomeFiles() {
+
+        getServletContext().setAttribute("org.apache.catalina.WELCOME_FILES",
+                                         welcomeFiles);
+
+    }
+
+    public String getHostname() {
+        Container parentHost = getParent();
+        if (parentHost != null) {
+            hostName = parentHost.getName();
+        }
+        if ((hostName == null) || (hostName.length() < 1))
+            hostName = "_";
+        return hostName;
+    }
+
+    /**
+     * Set the appropriate context attribute for our work directory.
+     */
+    private void postWorkDirectory() {
+
+        // Acquire (or calculate) the work directory path
+        String workDir = getWorkDir();
+        if (workDir == null) {
+
+            // Retrieve our parent (normally a host) name
+            String hostName = null;
+            String engineName = null;
+            String hostWorkDir = null;
+            Container parentHost = getParent();
+            if (parentHost != null) {
+                hostName = parentHost.getName();
+                if (parentHost instanceof StandardHost) {
+                    hostWorkDir = ((StandardHost)parentHost).getWorkDir();
+                }
+                Container parentEngine = parentHost.getParent();
+                if (parentEngine != null) {
+                   engineName = parentEngine.getName();
+                }
+            }
+            if ((hostName == null) || (hostName.length() < 1))
+                hostName = "_";
+            if ((engineName == null) || (engineName.length() < 1))
+                engineName = "_";
+
+            String temp = getPath();
+            if (temp.startsWith("/"))
+                temp = temp.substring(1);
+            temp = temp.replace('/', '_');
+            temp = temp.replace('\\', '_');
+            if (temp.length() < 1)
+                temp = "_";
+            if (hostWorkDir != null ) {
+                workDir = hostWorkDir + File.separator + temp;
+            } else {
+                workDir = "work" + File.separator + engineName +
+                    File.separator + hostName + File.separator + temp;
+            }
+            setWorkDir(workDir);
+        }
+
+        // Create this directory if necessary
+        File dir = new File(workDir);
+        if (!dir.isAbsolute()) {
+            File catalinaHome = engineBase();
+            String catalinaHomePath = null;
+            try {
+                catalinaHomePath = catalinaHome.getCanonicalPath();
+                dir = new File(catalinaHomePath, workDir);
+            } catch (IOException e) {
+            }
+        }
+        dir.mkdirs();
+
+        // Set the appropriate servlet context attribute
+        getServletContext().setAttribute(Globals.WORK_DIR_ATTR, dir);
+        if (getServletContext() instanceof ApplicationContext)
+            ((ApplicationContext) getServletContext()).setAttributeReadOnly
+                (Globals.WORK_DIR_ATTR);
+
+    }
+
+
+    /**
+     * Set the request processing paused flag for this Context.
+     *
+     * @param paused The new request processing paused flag
+     */
+    private void setPaused(boolean paused) {
+
+        this.paused = paused;
+
+    }
+
+
+    /**
+     * Validate the syntax of a proposed <code>&lt;url-pattern&gt;</code>
+     * for conformance with specification requirements.
+     *
+     * @param urlPattern URL pattern to be validated
+     */
+    private boolean validateURLPattern(String urlPattern) {
+
+        if (urlPattern == null)
+            return (false);
+        if (urlPattern.startsWith("*.")) {
+            if (urlPattern.indexOf('/') < 0)
+                return (true);
+            else
+                return (false);
+        }
+        if ( (urlPattern.startsWith("/")) &&
+                (urlPattern.indexOf("*.") < 0))
+            return (true);
+        else
+            return (false);
+
+    }
+
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * JSR77 deploymentDescriptor attribute
+     *
+     * @return string deployment descriptor 
+     */
+    public String getDeploymentDescriptor() {
+    
+        InputStream stream = null;
+        ServletContext servletContext = getServletContext();
+        if (servletContext != null) {
+            stream = servletContext.getResourceAsStream(
+                org.apache.catalina.startup.Constants.ApplicationWebXml);
+        }
+        if (stream == null) {
+            return "";
+        }
+        BufferedReader br = new BufferedReader(
+                                new InputStreamReader(stream));
+        StringBuffer sb = new StringBuffer();
+        String strRead = "";
+        try {
+            while (strRead != null) {
+                sb.append(strRead);
+                strRead = br.readLine();
+            }
+        } catch (IOException e) {
+            return "";
+        }
+
+        return sb.toString(); 
+    
+    }
+    
+    
+    /**
+     * JSR77 servlets attribute
+     *
+     * @return list of all servlets ( we know about )
+     */
+    public String[] getServlets() {
+        
+        String[] result = null;
+
+        Container[] children = findChildren();
+        if (children != null) {
+            result = new String[children.length];
+            for( int i=0; i< children.length; i++ ) {
+                result[i] = ((StandardWrapper)children[i]).getObjectName();
+            }
+        }
+
+        return result;
+    }
+    
+
+    public ObjectName createObjectName(String hostDomain, ObjectName parentName)
+            throws MalformedObjectNameException
+    {
+        String onameStr;
+        StandardHost hst=(StandardHost)getParent();
+        
+        String pathName=getName();
+        String hostName=getParent().getName();
+        String name= "//" + ((hostName==null)? "DEFAULT" : hostName) +
+                (("".equals(pathName))?"/":pathName );
+
+        String suffix=",J2EEApplication=" +
+                getJ2EEApplication() + ",J2EEServer=" +
+                getJ2EEServer();
+
+        onameStr="j2eeType=WebModule,name=" + name + suffix;
+        if( log.isDebugEnabled())
+            log.debug("Registering " + onameStr + " for " + oname);
+        
+        // default case - no domain explictely set.
+        if( getDomain() == null ) domain=hst.getDomain();
+
+        ObjectName oname=new ObjectName(getDomain() + ":" + onameStr);
+        return oname;        
+    }    
+    
+    private void preRegisterJMX() {
+        try {
+            StandardHost host = (StandardHost) getParent();
+            if ((oname == null) 
+                || (oname.getKeyProperty("j2eeType") == null)) {
+                oname = createObjectName(host.getDomain(), host.getJmxName());
+                controller = oname;
+            }
+        } catch(Exception ex) {
+            if(log.isInfoEnabled())
+                log.info("Error registering ctx with jmx " + this + " " +
+                     oname + " " + ex.toString(), ex );
+        }
+    }
+
+    private void registerJMX() {
+        try {
+            if (log.isDebugEnabled()) {
+                log.debug("Checking for " + oname );
+            }
+            if(! Registry.getRegistry(null, null)
+                .getMBeanServer().isRegistered(oname)) {
+                controller = oname;
+                Registry.getRegistry(null, null)
+                    .registerComponent(this, oname, null);
+                
+                // Send j2ee.object.created notification 
+                if (this.getObjectName() != null) {
+                    Notification notification = new Notification(
+                                                        "j2ee.object.created", 
+                                                        this.getObjectName(), 
+                                                        sequenceNumber++);
+                    broadcaster.sendNotification(notification);
+                }
+            }
+            Container children[] = findChildren();
+            for (int i=0; children!=null && i<children.length; i++) {
+                ((StandardWrapper)children[i]).registerJMX( this );
+            }
+        } catch (Exception ex) {
+            if(log.isInfoEnabled())
+                log.info("Error registering wrapper with jmx " + this + " " +
+                    oname + " " + ex.toString(), ex );
+        }
+    }
+
+    /** There are 2 cases:
+     *   1.The context is created and registered by internal APIS
+     *   2. The context is created by JMX, and it'll self-register.
+     *
+     * @param server The server
+     * @param name The object name
+     * @return ObjectName The name of the object
+     * @throws Exception If an error occurs
+     */
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name)
+            throws Exception
+    {
+        if( oname != null ) {
+            //log.info( "Already registered " + oname + " " + name);
+            // Temporary - /admin uses the old names
+            return name;
+        }
+        ObjectName result=super.preRegister(server,name);
+        return name;
+    }
+
+    public void preDeregister() throws Exception {
+        if( started ) {
+            try {
+                stop();
+            } catch( Exception ex ) {
+                log.error( "error stopping ", ex);
+            }
+        }
+    }
+
+    public void init() throws Exception {
+
+        if( this.getParent() == null ) {
+            ObjectName parentName=getParentName();
+            
+            if( ! mserver.isRegistered(parentName)) {
+                if(log.isDebugEnabled())
+                    log.debug("No host, creating one " + parentName);
+                StandardHost host=new StandardHost();
+                host.setName(hostName);
+                host.setAutoDeploy(false);
+                Registry.getRegistry(null, null)
+                    .registerComponent(host, parentName, null);
+                mserver.invoke(parentName, "init", new Object[] {}, new String[] {} );
+            }
+            ContextConfig config = new ContextConfig();
+            this.addLifecycleListener(config);
+
+            if(log.isDebugEnabled())
+                 log.debug( "AddChild " + parentName + " " + this);
+            try {
+                mserver.invoke(parentName, "addChild", new Object[] { this },
+                               new String[] {"org.apache.catalina.Container"});
+            } catch (Exception e) {
+                destroy();
+                throw e;
+            }
+        }
+        super.init();
+        
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(INIT_EVENT, null);
+
+        // Send j2ee.state.starting notification 
+        if (this.getObjectName() != null) {
+            Notification notification = new Notification("j2ee.state.starting", 
+                                                        this.getObjectName(), 
+                                                        sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        }
+        
+    }
+
+    public ObjectName getParentName() throws MalformedObjectNameException {
+        // "Life" update
+        String path=oname.getKeyProperty("name");
+        if( path == null ) {
+            log.error( "No name attribute " +name );
+            return null;
+        }
+        if( ! path.startsWith( "//")) {
+            log.error("Invalid name " + name);
+        }
+        path=path.substring(2);
+        int delim=path.indexOf( "/" );
+        hostName="localhost"; // Should be default...
+        if( delim > 0 ) {
+            hostName=path.substring(0, delim);
+            path = path.substring(delim);
+            if (path.equals("/")) {
+                this.setName("");
+            } else {
+                this.setName(path);
+            }
+        } else {
+            if(log.isDebugEnabled())
+                log.debug("Setting path " +  path );
+            this.setName( path );
+        }
+        // XXX The service and domain should be the same.
+        String parentDomain=getEngineName();
+        if( parentDomain == null ) parentDomain=domain;
+        ObjectName parentName=new ObjectName( parentDomain + ":" +
+                "type=Host,host=" + hostName);
+        return parentName;
+    }
+    
+    public void create() throws Exception{
+        init();
+    }
+
+    /* Remove a JMX notficationListener 
+     * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
+     */
+    public void removeNotificationListener(NotificationListener listener, 
+            NotificationFilter filter, Object object) throws ListenerNotFoundException {
+    	broadcaster.removeNotificationListener(listener,filter,object);
+    	
+    }
+    
+    private MBeanNotificationInfo[] notificationInfo;
+    
+    /* Get JMX Broadcaster Info
+     * @TODO use StringManager for international support!
+     * @TODO This two events we not send j2ee.state.failed and j2ee.attribute.changed!
+     * @see javax.management.NotificationBroadcaster#getNotificationInfo()
+     */
+    public MBeanNotificationInfo[] getNotificationInfo() {
+    	// FIXME: i18n
+    	if(notificationInfo == null) {
+    		notificationInfo = new MBeanNotificationInfo[]{
+    				new MBeanNotificationInfo(new String[] {
+    				"j2ee.object.created"},
+					Notification.class.getName(),
+					"web application is created"
+    				), 
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.state.starting"},
+					Notification.class.getName(),
+					"change web application is starting"
+					),
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.state.running"},
+					Notification.class.getName(),
+					"web application is running"
+					),
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.state.stopped"},
+					Notification.class.getName(),
+					"web application start to stopped"
+					),
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.object.stopped"},
+					Notification.class.getName(),
+					"web application is stopped"
+					),
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.object.deleted"},
+					Notification.class.getName(),
+					"web application is deleted"
+					)
+    		};
+    		
+    	}
+    	
+    	return notificationInfo;
+    }
+    
+    
+    /* Add a JMX-NotificationListener
+     * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
+     */
+    public void addNotificationListener(NotificationListener listener, 
+            NotificationFilter filter, Object object) throws IllegalArgumentException {
+    	broadcaster.addNotificationListener(listener,filter,object);
+    	
+    }
+    
+    
+    /**
+     * Remove a JMX-NotificationListener 
+     * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener)
+     */
+    public void removeNotificationListener(NotificationListener listener) 
+    throws ListenerNotFoundException {
+    	broadcaster.removeNotificationListener(listener);
+    	
+    }
+    
+    
+    // ------------------------------------------------------------- Attributes
+
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    public javax.naming.directory.DirContext getStaticResources() {
+
+        return getResources();
+
+    }
+
+
+    /**
+     * Return the naming resources associated with this web application.
+     * FIXME: Fooling introspection ... 
+     */
+    public javax.naming.directory.DirContext findStaticResources() {
+
+        return getResources();
+
+    }
+
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    public String[] getWelcomeFiles() {
+
+        return findWelcomeFiles();
+
+    }
+
+     /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param webXmlValidation true to enable xml instance validation
+     */
+    public void setXmlValidation(boolean webXmlValidation){
+        
+        this.webXmlValidation = webXmlValidation;
+
+    }
+
+    /**
+     * Get the server.xml <context> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getXmlValidation(){
+        return webXmlValidation;
+    }
+
+
+    /**
+     * Get the server.xml <context> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     */
+    public boolean getXmlNamespaceAware(){
+        return webXmlNamespaceAware;
+    }
+
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param webXmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean webXmlNamespaceAware){
+        this.webXmlNamespaceAware= webXmlNamespaceAware;
+    }    
+
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing tlds files. 
+     * @param tldValidation true to enable xml instance validation
+     */
+    public void setTldValidation(boolean tldValidation){
+        
+        this.tldValidation = tldValidation;
+
+    }
+
+    /**
+     * Get the server.xml <context> attribute's webXmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getTldValidation(){
+        return tldValidation;
+    }
+
+    /**
+     * Sets the process TLDs attribute.
+     *
+     * @param newProcessTlds The new value
+     */
+    public void setProcessTlds(boolean newProcessTlds) {
+	processTlds = newProcessTlds;
+    }
+
+    /**
+     * Returns the processTlds attribute value.
+     */
+    public boolean getProcessTlds() {
+	return processTlds;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     */
+    public boolean getTldNamespaceAware(){
+        return tldNamespaceAware;
+    }
+
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param tldNamespaceAware true to enable namespace awareness
+     */
+    public void setTldNamespaceAware(boolean tldNamespaceAware){
+        this.tldNamespaceAware= tldNamespaceAware;
+    }    
+
+
+    /** 
+     * Support for "stateManageable" JSR77 
+     */
+    public boolean isStateManageable() {
+        return true;
+    }
+    
+    public void startRecursive() throws LifecycleException {
+        // nothing to start recursive, the servlets will be started by load-on-startup
+        start();
+    }
+    
+    public int getState() {
+        if( started ) {
+            return 1; // RUNNING
+        }
+        if( initialized ) {
+            return 0; // starting ? 
+        }
+        if( ! available ) { 
+            return 4; //FAILED
+        }
+        // 2 - STOPPING
+        return 3; // STOPPED
+    }
+    
+    /**
+     * The J2EE Server ObjectName this module is deployed on.
+     */     
+    private String server = null;
+    
+    /**
+     * The Java virtual machines on which this module is running.
+     */       
+    private String[] javaVMs = null;
+    
+    public String getServer() {
+        return server;
+    }
+        
+    public String setServer(String server) {
+        return this.server=server;
+    }
+        
+    public String[] getJavaVMs() {
+        return javaVMs;
+    }
+        
+    public String[] setJavaVMs(String[] javaVMs) {
+        return this.javaVMs = javaVMs;
+    }
+    
+    /**
+     * Gets the time this context was started.
+     *
+     * @return Time (in milliseconds since January 1, 1970, 00:00:00) when this
+     * context was started 
+     */
+    public long getStartTime() {
+        return startTime;
+    }
+    
+    public boolean isEventProvider() {
+        return false;
+    }
+    
+    public boolean isStatisticsProvider() {
+        return false;
+    }
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardContextValve.java b/container/catalina/src/share/org/apache/catalina/core/StandardContextValve.java
new file mode 100644
index 0000000..5a38dfd
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardContextValve.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardContext</code> container implementation.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  This implementation is likely to be useful only
+ * when processing HTTP requests.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+final class StandardContextValve
+    extends ValveBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardContextValve/1.0";
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    private static Log log = LogFactory.getLog(StandardContextValve.class);
+
+    
+    private StandardContext context = null;
+    
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Cast to a StandardContext right away, as it will be needed later.
+     * 
+     * @see org.apache.catalina.Contained#setContainer(org.apache.catalina.Container)
+     */
+    public void setContainer(Container container) {
+        super.setContainer(container);
+        context = (StandardContext) container;
+    }
+
+    
+    /**
+     * Select the appropriate child Wrapper to process this request,
+     * based on the specified request URI.  If no matching Wrapper can
+     * be found, return an appropriate HTTP error.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     * @param valveContext Valve context used to forward to the next Valve
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+    public final void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // Disallow any direct access to resources under WEB-INF or META-INF
+        MessageBytes requestPathMB = request.getRequestPathMB();
+        if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
+            || (requestPathMB.equalsIgnoreCase("/META-INF"))
+            || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
+            || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
+            String requestURI = request.getDecodedRequestURI();
+            notFound(requestURI, response);
+            return;
+        }
+
+        // Wait if we are reloading
+        while (context.getPaused()) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                ;
+            }
+        }
+
+        // Select the Wrapper to be used for this Request
+        Wrapper wrapper = request.getWrapper();
+        if (wrapper == null) {
+            String requestURI = request.getDecodedRequestURI();
+            notFound(requestURI, response);
+            return;
+        }
+
+        // Normal request processing
+        Object instances[] = context.getApplicationEventListeners();
+
+        ServletRequestEvent event = null;
+
+        if ((instances != null) 
+                && (instances.length > 0)) {
+            event = new ServletRequestEvent
+                (((StandardContext) container).getServletContext(), 
+                 request.getRequest());
+            // create pre-service event
+            for (int i = 0; i < instances.length; i++) {
+                if (instances[i] == null)
+                    continue;
+                if (!(instances[i] instanceof ServletRequestListener))
+                    continue;
+                ServletRequestListener listener =
+                    (ServletRequestListener) instances[i];
+                try {
+                    listener.requestInitialized(event);
+                } catch (Throwable t) {
+                    container.getLogger().error(sm.getString("requestListenerValve.requestInit",
+                                     instances[i].getClass().getName()), t);
+                    ServletRequest sreq = request.getRequest();
+                    sreq.setAttribute(Globals.EXCEPTION_ATTR,t);
+                    return;
+                }
+            }
+        }
+
+        wrapper.getPipeline().getFirst().invoke(request, response);
+
+        if ((instances !=null ) &&
+                (instances.length > 0)) {
+            // create post-service event
+            for (int i = 0; i < instances.length; i++) {
+                if (instances[i] == null)
+                    continue;
+                if (!(instances[i] instanceof ServletRequestListener))
+                    continue;
+                ServletRequestListener listener =
+                    (ServletRequestListener) instances[i];
+                try {
+                    listener.requestDestroyed(event);
+                } catch (Throwable t) {
+                    container.getLogger().error(sm.getString("requestListenerValve.requestDestroy",
+                                     instances[i].getClass().getName()), t);
+                    ServletRequest sreq = request.getRequest();
+                    sreq.setAttribute(Globals.EXCEPTION_ATTR,t);
+                }
+            }
+        }
+                
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Report a "bad request" error for the specified resource.  FIXME:  We
+     * should really be using the error reporting settings for this web
+     * application, but currently that code runs at the wrapper level rather
+     * than the context level.
+     *
+     * @param requestURI The request URI for the requested resource
+     * @param response The response we are creating
+     */
+    private void badRequest(String requestURI, HttpServletResponse response) {
+
+        try {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, requestURI);
+        } catch (IllegalStateException e) {
+            ;
+        } catch (IOException e) {
+            ;
+        }
+
+    }
+    
+    
+    /**
+     * Report a "forbidden" error for the specified resource. 
+     *
+     * @param requestURI The request URI for the requested resource
+     * @param response The response we are creating
+     */
+    private void forbidden(String requestURI, HttpServletResponse response) {
+
+        try {
+            response.sendError(HttpServletResponse.SC_FORBIDDEN, requestURI);
+        } catch (IllegalStateException e) {
+            ;
+        } catch (IOException e) {
+            ;
+        }
+
+    }
+
+
+    /**
+     * Report a "not found" error for the specified resource.  FIXME:  We
+     * should really be using the error reporting settings for this web
+     * application, but currently that code runs at the wrapper level rather
+     * than the context level.
+     *
+     * @param requestURI The request URI for the requested resource
+     * @param response The response we are creating
+     */
+    private void notFound(String requestURI, HttpServletResponse response) {
+
+        try {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND, requestURI);
+        } catch (IllegalStateException e) {
+            ;
+        } catch (IOException e) {
+            ;
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardEngine.java b/container/catalina/src/share/org/apache/catalina/core/StandardEngine.java
new file mode 100644
index 0000000..2dc46ec
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardEngine.java
@@ -0,0 +1,537 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.File;
+import java.util.List;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Service;
+import org.apache.catalina.realm.JAASRealm;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+import org.apache.commons.modeler.modules.MbeansSource;
+
+/**
+ * Standard implementation of the <b>Engine</b> interface.  Each
+ * child container must be a Host implementation to process the specific
+ * fully qualified host name of that virtual host. <br/>
+ * You can set the jvmRoute direct or with the System.property <b>jvmRoute</b>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class StandardEngine
+    extends ContainerBase
+    implements Engine {
+
+    private static Log log = LogFactory.getLog(StandardEngine.class);
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new StandardEngine component with the default basic Valve.
+     */
+    public StandardEngine() {
+
+        super();
+        pipeline.setBasic(new StandardEngineValve());
+        /* Set the jmvRoute using the system property jvmRoute */
+        try {
+            setJvmRoute(System.getProperty("jvmRoute"));
+        } catch(Exception ex) {
+        }
+        // By default, the engine will hold the reloading thread
+        backgroundProcessorDelay = 10;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Host name to use when no server host, or an unknown host,
+     * is specified in the request.
+     */
+    private String defaultHost = null;
+
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardEngine/1.0";
+
+
+    /**
+     * The <code>Service</code> that owns this Engine, if any.
+     */
+    private Service service = null;
+
+    /** Allow the base dir to be specified explicitely for
+     * each engine. In time we should stop using catalina.base property -
+     * otherwise we loose some flexibility.
+     */
+    private String baseDir = null;
+
+    /** Optional mbeans config file. This will replace the "hacks" in
+     * jk and ServerListener. The mbeans file will support (transparent) 
+     * persistence - soon. It'll probably replace jk2.properties and could
+     * replace server.xml. Of course - the same beans could be loaded and 
+     * managed by an external entity - like the embedding app - which
+     *  can use a different persistence mechanism.
+     */ 
+    private String mbeansFile = null;
+    
+    /** Mbeans loaded by the engine.  
+     */ 
+    private List mbeans;
+    
+
+    /**
+     * The JVM Route ID for this Tomcat instance. All Route ID's must be unique
+     * across the cluster.
+     */
+    private String jvmRouteId;
+
+
+    // ------------------------------------------------------------- Properties
+
+    /** Provide a default in case no explicit configuration is set
+     *
+     * @return configured realm, or a JAAS realm by default
+     */
+    public Realm getRealm() {
+        Realm configured=super.getRealm();
+        // If no set realm has been called - default to JAAS
+        // This can be overriden at engine, context and host level  
+        if( configured==null ) {
+            configured=new JAASRealm();
+            this.setRealm( configured );
+        }
+        return configured;
+    }
+
+
+    /**
+     * Return the default host.
+     */
+    public String getDefaultHost() {
+
+        return (defaultHost);
+
+    }
+
+
+    /**
+     * Set the default host.
+     *
+     * @param host The new default host
+     */
+    public void setDefaultHost(String host) {
+
+        String oldDefaultHost = this.defaultHost;
+        if (host == null) {
+            this.defaultHost = null;
+        } else {
+            this.defaultHost = host.toLowerCase();
+        }
+        support.firePropertyChange("defaultHost", oldDefaultHost,
+                                   this.defaultHost);
+
+    }
+    
+    public void setName(String name ) {
+        if( domain != null ) {
+            // keep name==domain, ignore override
+            // we are already registered
+            super.setName( domain );
+            return;
+        }
+        // The engine name is used as domain
+        domain=name; // XXX should we set it in init() ? It shouldn't matter
+        super.setName( name );
+    }
+
+
+    /**
+     * Set the cluster-wide unique identifier for this Engine.
+     * This value is only useful in a load-balancing scenario.
+     * <p>
+     * This property should not be changed once it is set.
+     */
+    public void setJvmRoute(String routeId) {
+        jvmRouteId = routeId;
+    }
+
+
+    /**
+     * Retrieve the cluster-wide unique identifier for this Engine.
+     * This value is only useful in a load-balancing scenario.
+     */
+    public String getJvmRoute() {
+        return jvmRouteId;
+    }
+
+
+    /**
+     * Return the <code>Service</code> with which we are associated (if any).
+     */
+    public Service getService() {
+
+        return (this.service);
+
+    }
+
+
+    /**
+     * Set the <code>Service</code> with which we are associated (if any).
+     *
+     * @param service The service that owns this Engine
+     */
+    public void setService(Service service) {
+        this.service = service;
+    }
+
+    public String getMbeansFile() {
+        return mbeansFile;
+    }
+
+    public void setMbeansFile(String mbeansFile) {
+        this.mbeansFile = mbeansFile;
+    }
+
+    public String getBaseDir() {
+        if( baseDir==null ) {
+            baseDir=System.getProperty("catalina.base");
+        }
+        if( baseDir==null ) {
+            baseDir=System.getProperty("catalina.home");
+        }
+        return baseDir;
+    }
+
+    public void setBaseDir(String baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a child Container, only if the proposed child is an implementation
+     * of Host.
+     *
+     * @param child Child container to be added
+     */
+    public void addChild(Container child) {
+
+        if (!(child instanceof Host))
+            throw new IllegalArgumentException
+                (sm.getString("standardEngine.notHost"));
+        super.addChild(child);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * Disallow any attempt to set a parent for this Container, since an
+     * Engine is supposed to be at the top of the Container hierarchy.
+     *
+     * @param container Proposed parent Container
+     */
+    public void setParent(Container container) {
+
+        throw new IllegalArgumentException
+            (sm.getString("standardEngine.notParent"));
+
+    }
+
+
+    private boolean initialized=false;
+    
+    public void init() {
+        if( initialized ) return;
+        initialized=true;
+
+        if( oname==null ) {
+            // not registered in JMX yet - standalone mode
+            try {
+                if (domain==null) {
+                    domain=getName();
+                }
+                if(log.isDebugEnabled())
+                    log.debug( "Register " + domain );
+                oname=new ObjectName(domain + ":type=Engine");
+                controller=oname;
+                Registry.getRegistry(null, null)
+                    .registerComponent(this, oname, null);
+            } catch( Throwable t ) {
+                log.info("Error registering ", t );
+            }
+        }
+
+        if( mbeansFile == null ) {
+            String defaultMBeansFile=getBaseDir() + "/conf/tomcat5-mbeans.xml";
+            File f=new File( defaultMBeansFile );
+            if( f.exists() ) mbeansFile=f.getAbsolutePath();
+        }
+        if( mbeansFile != null ) {
+            readEngineMbeans();
+        }
+        if( mbeans != null ) {
+            try {
+                Registry.getRegistry(null, null).invoke(mbeans, "init", false);
+            } catch (Exception e) {
+                log.error("Error in init() for " + mbeansFile, e);
+            }
+        }
+        
+        // not needed since the following if statement does the same thing the right way
+        // remove later after checking
+        //if( service==null ) {
+        //    try {
+        //        ObjectName serviceName=getParentName();        
+        //        if( mserver.isRegistered( serviceName )) {
+        //            log.info("Registering with the service ");
+        //            try {
+        //                mserver.invoke( serviceName, "setContainer",
+        //                        new Object[] { this },
+        //                        new String[] { "org.apache.catalina.Container" } );
+        //            } catch( Exception ex ) {
+        //               ex.printStackTrace();
+        //            }
+        //        }
+        //    } catch( Exception ex ) {
+        //        log.error("Error registering with service ");
+        //    }
+        //}
+        
+        if( service==null ) {
+            // for consistency...: we are probably in embeded mode
+            try {
+                service=new StandardService();
+                service.setContainer( this );
+                service.initialize();
+            } catch( Throwable t ) {
+                log.error(t);
+            }
+        }
+        
+    }
+    
+    public void destroy() throws LifecycleException {
+        if( ! initialized ) return;
+        initialized=false;
+        
+        // if we created it, make sure it's also destroyed
+        // this call implizit this.stop()
+        ((StandardService)service).destroy();
+
+        if( mbeans != null ) {
+            try {
+                Registry.getRegistry(null, null)
+                    .invoke(mbeans, "destroy", false);
+            } catch (Exception e) {
+                log.error(sm.getString("standardEngine.unregister.mbeans.failed" ,mbeansFile), e);
+            }
+        }
+        // 
+        if( mbeans != null ) {
+            try {
+                for( int i=0; i<mbeans.size() ; i++ ) {
+                    Registry.getRegistry(null, null)
+                        .unregisterComponent((ObjectName)mbeans.get(i));
+                }
+            } catch (Exception e) {
+                log.error(sm.getString("standardEngine.unregister.mbeans.failed", mbeansFile), e);
+            }
+        }
+        
+        // force all metadata to be reloaded.
+        // That doesn't affect existing beans. We should make it per
+        // registry - and stop using the static.
+        Registry.getRegistry(null, null).resetMetadata();
+        
+    }
+    
+    /**
+     * Start this Engine component.
+     *
+     * @exception LifecycleException if a startup error occurs
+     */
+    public void start() throws LifecycleException {
+        if( started ) {
+            return;
+        }
+        if( !initialized ) {
+            init();
+        }
+
+        // Look for a realm - that may have been configured earlier. 
+        // If the realm is added after context - it'll set itself.
+        if( realm == null ) {
+            ObjectName realmName=null;
+            try {
+                realmName=new ObjectName( domain + ":type=Realm");
+                if( mserver.isRegistered(realmName ) ) {
+                    mserver.invoke(realmName, "init", 
+                            new Object[] {},
+                            new String[] {}
+                    );            
+                }
+            } catch( Throwable t ) {
+                log.debug("No realm for this engine " + realmName);
+            }
+        }
+            
+        // Log our server identification information
+        //System.out.println(ServerInfo.getServerInfo());
+        if(log.isInfoEnabled())
+            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
+        if( mbeans != null ) {
+            try {
+                Registry.getRegistry(null, null)
+                    .invoke(mbeans, "start", false);
+            } catch (Exception e) {
+                log.error("Error in start() for " + mbeansFile, e);
+            }
+        }
+
+        // Standard container startup
+        super.start();
+
+    }
+    
+    public void stop() throws LifecycleException {
+        super.stop();
+        if( mbeans != null ) {
+            try {
+                Registry.getRegistry(null, null).invoke(mbeans, "stop", false);
+            } catch (Exception e) {
+                log.error("Error in stop() for " + mbeansFile, e);
+            }
+        }
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("StandardEngine[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    // -------------------- JMX registration  --------------------
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception
+    {
+        super.preRegister(server,name);
+
+        this.setName( name.getDomain());
+
+        return name;
+    }
+
+    // FIXME Remove -- not used 
+    public ObjectName getParentName() throws MalformedObjectNameException {
+        if (getService()==null) {
+            return null;
+        }
+        String name = getService().getName();
+        ObjectName serviceName=new ObjectName(domain +
+                        ":type=Service,serviceName="+name);
+        return serviceName;                
+    }
+    
+    public ObjectName createObjectName(String domain, ObjectName parent)
+        throws Exception
+    {
+        if( log.isDebugEnabled())
+            log.debug("Create ObjectName " + domain + " " + parent );
+        return new ObjectName( domain + ":type=Engine");
+    }
+
+    
+    private void readEngineMbeans() {
+        try {
+            MbeansSource mbeansMB=new MbeansSource();
+            File mbeansF=new File( mbeansFile );
+            mbeansMB.setSource(mbeansF);
+            
+            Registry.getRegistry(null, null).registerComponent
+                (mbeansMB, domain + ":type=MbeansFile", null);
+            mbeansMB.load();
+            mbeansMB.init();
+            mbeansMB.setRegistry(Registry.getRegistry(null, null));
+            mbeans=mbeansMB.getMBeans();
+            
+        } catch( Throwable t ) {
+            log.error( "Error loading " + mbeansFile, t );
+        }
+        
+    }
+    
+    public String getDomain() {
+        if (domain!=null) {
+            return domain;
+        } else { 
+            return getName();
+        }
+    }
+    
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardEngineValve.java b/container/catalina/src/share/org/apache/catalina/core/StandardEngineValve.java
new file mode 100644
index 0000000..06d958f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardEngineValve.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Host;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardEngine</code> container implementation.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  This implementation is likely to be useful only
+ * when processing HTTP requests.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+final class StandardEngineValve
+    extends ValveBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardEngineValve/1.0";
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Select the appropriate child Host to process this request,
+     * based on the requested server name.  If no matching Host can
+     * be found, return an appropriate HTTP error.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     * @param valveContext Valve context used to forward to the next Valve
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+    public final void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // Select the Host to be used for this Request
+        Host host = request.getHost();
+        if (host == null) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 sm.getString("standardEngine.noHost", 
+                              request.getServerName()));
+            return;
+        }
+
+        // Ask this Host to process this request
+        host.getPipeline().getFirst().invoke(request, response);
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardHost.java b/container/catalina/src/share/org/apache/catalina/core/StandardHost.java
new file mode 100644
index 0000000..69d0d61
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardHost.java
@@ -0,0 +1,822 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Valve;
+import org.apache.catalina.startup.HostConfig;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.commons.modeler.Registry;
+
+
+/**
+ * Standard implementation of the <b>Host</b> interface.  Each
+ * child container must be a Context implementation to process the
+ * requests directed to a particular web application.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class StandardHost
+    extends ContainerBase
+    implements Host  
+ {
+    /* Why do we implement deployer and delegate to deployer ??? */
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( StandardHost.class );
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new StandardHost component with the default basic Valve.
+     */
+    public StandardHost() {
+
+        super();
+        pipeline.setBasic(new StandardHostValve());
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of aliases for this Host.
+     */
+    private String[] aliases = new String[0];
+
+
+    /**
+     * The application root for this Host.
+     */
+    private String appBase = ".";
+
+
+    /**
+     * The auto deploy flag for this Host.
+     */
+    private boolean autoDeploy = true;
+
+
+    /**
+     * The Java class name of the default context configuration class
+     * for deployed web applications.
+     */
+    private String configClass =
+        "org.apache.catalina.startup.ContextConfig";
+
+
+    /**
+     * The Java class name of the default Context implementation class for
+     * deployed web applications.
+     */
+    private String contextClass =
+        "org.apache.catalina.core.StandardContext";
+
+
+    /**
+     * The deploy on startup flag for this Host.
+     */
+    private boolean deployOnStartup = true;
+
+
+    /**
+     * deploy Context XML config files property.
+     */
+    private boolean deployXML = true;
+
+
+    /**
+     * The Java class name of the default error reporter implementation class 
+     * for deployed web applications.
+     */
+    private String errorReportValveClass =
+        "org.apache.catalina.valves.ErrorReportValve";
+
+    /**
+     * The object name for the errorReportValve.
+     */
+    private ObjectName errorReportValveObjectName = null;
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardHost/1.0";
+
+
+    /**
+     * The live deploy flag for this Host.
+     */
+    private boolean liveDeploy = true;
+
+
+    /**
+     * Unpack WARs property.
+     */
+    private boolean unpackWARs = true;
+
+
+    /**
+     * Work Directory base for applications.
+     */
+    private String workDir = null;
+
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+     private boolean xmlValidation = false;
+
+
+    /**
+     * Attribute value used to turn on/off XML namespace awarenes.
+     */
+     private boolean xmlNamespaceAware = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the application root for this Host.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     */
+    public String getAppBase() {
+
+        return (this.appBase);
+
+    }
+
+
+    /**
+     * Set the application root for this Host.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     *
+     * @param appBase The new application root
+     */
+    public void setAppBase(String appBase) {
+
+        String oldAppBase = this.appBase;
+        this.appBase = appBase;
+        support.firePropertyChange("appBase", oldAppBase, this.appBase);
+
+    }
+
+
+    /**
+     * Return the value of the auto deploy flag.  If true, it indicates that 
+     * this host's child webapps will be dynamically deployed.
+     */
+    public boolean getAutoDeploy() {
+
+        return (this.autoDeploy);
+
+    }
+
+
+    /**
+     * Set the auto deploy flag value for this host.
+     * 
+     * @param autoDeploy The new auto deploy flag
+     */
+    public void setAutoDeploy(boolean autoDeploy) {
+
+        boolean oldAutoDeploy = this.autoDeploy;
+        this.autoDeploy = autoDeploy;
+        support.firePropertyChange("autoDeploy", oldAutoDeploy, 
+                                   this.autoDeploy);
+
+    }
+
+
+    /**
+     * Return the Java class name of the context configuration class
+     * for new web applications.
+     */
+    public String getConfigClass() {
+
+        return (this.configClass);
+
+    }
+
+
+    /**
+     * Set the Java class name of the context configuration class
+     * for new web applications.
+     *
+     * @param configClass The new context configuration class
+     */
+    public void setConfigClass(String configClass) {
+
+        String oldConfigClass = this.configClass;
+        this.configClass = configClass;
+        support.firePropertyChange("configClass",
+                                   oldConfigClass, this.configClass);
+
+    }
+
+
+    /**
+     * Return the Java class name of the Context implementation class
+     * for new web applications.
+     */
+    public String getContextClass() {
+
+        return (this.contextClass);
+
+    }
+
+
+    /**
+     * Set the Java class name of the Context implementation class
+     * for new web applications.
+     *
+     * @param contextClass The new context implementation class
+     */
+    public void setContextClass(String contextClass) {
+
+        String oldContextClass = this.contextClass;
+        this.contextClass = contextClass;
+        support.firePropertyChange("contextClass",
+                                   oldContextClass, this.contextClass);
+
+    }
+
+
+    /**
+     * Return the value of the deploy on startup flag.  If true, it indicates 
+     * that this host's child webapps should be discovred and automatically 
+     * deployed at startup time.
+     */
+    public boolean getDeployOnStartup() {
+
+        return (this.deployOnStartup);
+
+    }
+
+
+    /**
+     * Set the deploy on startup flag value for this host.
+     * 
+     * @param deployOnStartup The new deploy on startup flag
+     */
+    public void setDeployOnStartup(boolean deployOnStartup) {
+
+        boolean oldDeployOnStartup = this.deployOnStartup;
+        this.deployOnStartup = deployOnStartup;
+        support.firePropertyChange("deployOnStartup", oldDeployOnStartup, 
+                                   this.deployOnStartup);
+
+    }
+
+
+    /**
+     * Deploy XML Context config files flag accessor.
+     */
+    public boolean isDeployXML() {
+
+        return (deployXML);
+
+    }
+
+
+    /**
+     * Deploy XML Context config files flag mutator.
+     */
+    public void setDeployXML(boolean deployXML) {
+
+        this.deployXML = deployXML;
+
+    }
+
+
+    /**
+     * Return the value of the live deploy flag.  If true, it indicates that 
+     * a background thread should be started that looks for web application
+     * context files, WAR files, or unpacked directories being dropped in to
+     * the <code>appBase</code> directory, and deploys new ones as they are
+     * encountered.
+     */
+    public boolean getLiveDeploy() {
+        return (this.autoDeploy);
+    }
+
+
+    /**
+     * Set the live deploy flag value for this host.
+     * 
+     * @param liveDeploy The new live deploy flag
+     */
+    public void setLiveDeploy(boolean liveDeploy) {
+        setAutoDeploy(liveDeploy);
+    }
+
+
+    /**
+     * Return the Java class name of the error report valve class
+     * for new web applications.
+     */
+    public String getErrorReportValveClass() {
+
+        return (this.errorReportValveClass);
+
+    }
+
+
+    /**
+     * Set the Java class name of the error report valve class
+     * for new web applications.
+     *
+     * @param errorReportValveClass The new error report valve class
+     */
+    public void setErrorReportValveClass(String errorReportValveClass) {
+
+        String oldErrorReportValveClassClass = this.errorReportValveClass;
+        this.errorReportValveClass = errorReportValveClass;
+        support.firePropertyChange("errorReportValveClass",
+                                   oldErrorReportValveClassClass, 
+                                   this.errorReportValveClass);
+
+    }
+
+
+    /**
+     * Return the canonical, fully qualified, name of the virtual host
+     * this Container represents.
+     */
+    public String getName() {
+
+        return (name);
+
+    }
+
+
+    /**
+     * Set the canonical, fully qualified, name of the virtual host
+     * this Container represents.
+     *
+     * @param name Virtual host name
+     *
+     * @exception IllegalArgumentException if name is null
+     */
+    public void setName(String name) {
+
+        if (name == null)
+            throw new IllegalArgumentException
+                (sm.getString("standardHost.nullName"));
+
+        name = name.toLowerCase();      // Internally all names are lower case
+
+        String oldName = this.name;
+        this.name = name;
+        support.firePropertyChange("name", oldName, this.name);
+
+    }
+
+
+    /**
+     * Unpack WARs flag accessor.
+     */
+    public boolean isUnpackWARs() {
+
+        return (unpackWARs);
+
+    }
+
+
+    /**
+     * Unpack WARs flag mutator.
+     */
+    public void setUnpackWARs(boolean unpackWARs) {
+
+        this.unpackWARs = unpackWARs;
+
+    }
+
+     /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlValidation true to enable xml instance validation
+     */
+    public void setXmlValidation(boolean xmlValidation){
+        
+        this.xmlValidation = xmlValidation;
+
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getXmlValidation(){
+        return xmlValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     *
+     */
+    public boolean getXmlNamespaceAware(){
+        return xmlNamespaceAware;
+    }
+
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean xmlNamespaceAware){
+        this.xmlNamespaceAware=xmlNamespaceAware;
+    }    
+    
+    /**
+     * Host work directory base.
+     */
+    public String getWorkDir() {
+
+        return (workDir);
+    }
+
+
+    /**
+     * Host work directory base.
+     */
+    public void setWorkDir(String workDir) {
+
+        this.workDir = workDir;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an alias name that should be mapped to this same Host.
+     *
+     * @param alias The alias to be added
+     */
+    public void addAlias(String alias) {
+
+        alias = alias.toLowerCase();
+
+        // Skip duplicate aliases
+        for (int i = 0; i < aliases.length; i++) {
+            if (aliases[i].equals(alias))
+                return;
+        }
+
+        // Add this alias to the list
+        String newAliases[] = new String[aliases.length + 1];
+        for (int i = 0; i < aliases.length; i++)
+            newAliases[i] = aliases[i];
+        newAliases[aliases.length] = alias;
+
+        aliases = newAliases;
+
+        // Inform interested listeners
+        fireContainerEvent(ADD_ALIAS_EVENT, alias);
+
+    }
+
+
+    /**
+     * Add a child Container, only if the proposed child is an implementation
+     * of Context.
+     *
+     * @param child Child container to be added
+     */
+    public void addChild(Container child) {
+
+        if (!(child instanceof Context))
+            throw new IllegalArgumentException
+                (sm.getString("standardHost.notContext"));
+        super.addChild(child);
+
+    }
+
+
+    /**
+     * Return the set of alias names for this Host.  If none are defined,
+     * a zero length array is returned.
+     */
+    public String[] findAliases() {
+
+        return (this.aliases);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the Context that would be used to process the specified
+     * host-relative request URI, if any; otherwise return <code>null</code>.
+     *
+     * @param uri Request URI to be mapped
+     */
+    public Context map(String uri) {
+
+        if (log.isDebugEnabled())
+            log.debug("Mapping request URI '" + uri + "'");
+        if (uri == null)
+            return (null);
+
+        // Match on the longest possible context path prefix
+        if (log.isTraceEnabled())
+            log.trace("  Trying the longest context path prefix");
+        Context context = null;
+        String mapuri = uri;
+        while (true) {
+            context = (Context) findChild(mapuri);
+            if (context != null)
+                break;
+            int slash = mapuri.lastIndexOf('/');
+            if (slash < 0)
+                break;
+            mapuri = mapuri.substring(0, slash);
+        }
+
+        // If no Context matches, select the default Context
+        if (context == null) {
+            if (log.isTraceEnabled())
+                log.trace("  Trying the default context");
+            context = (Context) findChild("");
+        }
+
+        // Complain if no Context has been selected
+        if (context == null) {
+            log.error(sm.getString("standardHost.mappingError", uri));
+            return (null);
+        }
+
+        // Return the mapped Context (if any)
+        if (log.isDebugEnabled())
+            log.debug(" Mapped to context '" + context.getPath() + "'");
+        return (context);
+
+    }
+
+
+    /**
+     * Remove the specified alias name from the aliases for this Host.
+     *
+     * @param alias Alias name to be removed
+     */
+    public void removeAlias(String alias) {
+
+        alias = alias.toLowerCase();
+
+        synchronized (aliases) {
+
+            // Make sure this alias is currently present
+            int n = -1;
+            for (int i = 0; i < aliases.length; i++) {
+                if (aliases[i].equals(alias)) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+
+            // Remove the specified alias
+            int j = 0;
+            String results[] = new String[aliases.length - 1];
+            for (int i = 0; i < aliases.length; i++) {
+                if (i != n)
+                    results[j++] = aliases[i];
+            }
+            aliases = results;
+
+        }
+
+        // Inform interested listeners
+        fireContainerEvent(REMOVE_ALIAS_EVENT, alias);
+
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer();
+        if (getParent() != null) {
+            sb.append(getParent().toString());
+            sb.append(".");
+        }
+        sb.append("StandardHost[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    /**
+     * Start this host.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public synchronized void start() throws LifecycleException {
+        if( started ) {
+            return;
+        }
+        if( ! initialized )
+            init();
+
+        // Look for a realm - that may have been configured earlier. 
+        // If the realm is added after context - it'll set itself.
+        if( realm == null ) {
+            ObjectName realmName=null;
+            try {
+                realmName=new ObjectName( domain + ":type=Realm,host=" + getName());
+                if( mserver.isRegistered(realmName ) ) {
+                    mserver.invoke(realmName, "init", 
+                            new Object[] {},
+                            new String[] {}
+                    );            
+                }
+            } catch( Throwable t ) {
+                log.debug("No realm for this host " + realmName);
+            }
+        }
+            
+        // Set error report valve
+        if ((errorReportValveClass != null)
+            && (!errorReportValveClass.equals(""))) {
+            try {
+                boolean found = false;
+                if(errorReportValveObjectName != null) {
+                    ObjectName[] names = 
+                        ((StandardPipeline)pipeline).getValveObjectNames();
+                    for (int i=0; !found && i<names.length; i++)
+                        if(errorReportValveObjectName.equals(names[i]))
+                            found = true ;
+                    }
+                    if(!found) {          	
+                        Valve valve = (Valve) Class.forName(errorReportValveClass)
+                        .newInstance();
+                        addValve(valve);
+                        errorReportValveObjectName = ((ValveBase)valve).getObjectName() ;
+                    }
+            } catch (Throwable t) {
+                log.error(sm.getString
+                    ("standardHost.invalidErrorReportValveClass", 
+                     errorReportValveClass));
+            }
+        }
+        if(log.isInfoEnabled()) {
+            if (xmlValidation)
+                log.info( sm.getString("standardHost.validationEnabled"));
+            else
+                log.info( sm.getString("standardHost.validationDisabled"));
+        }
+        super.start();
+
+    }
+
+
+    // -------------------- JMX  --------------------
+    /**
+      * Return the MBean Names of the Valves assoicated with this Host
+      *
+      * @exception Exception if an MBean cannot be created or registered
+      */
+     public String [] getValveNames()
+         throws Exception
+    {
+         Valve [] valves = this.getValves();
+         String [] mbeanNames = new String[valves.length];
+         for (int i = 0; i < valves.length; i++) {
+             if( valves[i] == null ) continue;
+             if( ((ValveBase)valves[i]).getObjectName() == null ) continue;
+             mbeanNames[i] = ((ValveBase)valves[i]).getObjectName().toString();
+         }
+
+         return mbeanNames;
+
+     }
+
+    public String[] getAliases() {
+        return aliases;
+    }
+
+    private boolean initialized=false;
+    
+    public void init() {
+        if( initialized ) return;
+        initialized=true;
+        
+        // already registered.
+        if( getParent() == null ) {
+            try {
+                // Register with the Engine
+                ObjectName serviceName=new ObjectName(domain + 
+                                        ":type=Engine");
+
+                HostConfig deployer = new HostConfig();
+                addLifecycleListener(deployer);                
+                if( mserver.isRegistered( serviceName )) {
+                    if(log.isDebugEnabled())
+                        log.debug("Registering "+ serviceName +" with the Engine");
+                    mserver.invoke( serviceName, "addChild",
+                            new Object[] { this },
+                            new String[] { "org.apache.catalina.Container" } );
+                }
+            } catch( Exception ex ) {
+                log.error("Host registering failed!",ex);
+            }
+        }
+        
+        if( oname==null ) {
+            // not registered in JMX yet - standalone mode
+            try {
+                StandardEngine engine=(StandardEngine)parent;
+                domain=engine.getName();
+                if(log.isDebugEnabled())
+                    log.debug( "Register host " + getName() + " with domain "+ domain );
+                oname=new ObjectName(domain + ":type=Host,host=" +
+                        this.getName());
+                controller = oname;
+                Registry.getRegistry(null, null)
+                    .registerComponent(this, oname, null);
+            } catch( Throwable t ) {
+                log.error("Host registering failed!", t );
+            }
+        }
+    }
+
+    public void destroy() throws Exception {
+        // destroy our child containers, if any
+        Container children[] = findChildren();
+        super.destroy();
+        for (int i = 0; i < children.length; i++) {
+            if(children[i] instanceof StandardContext)
+                ((StandardContext)children[i]).destroy();
+        }
+      
+    }
+    
+    public ObjectName preRegister(MBeanServer server, ObjectName oname ) 
+        throws Exception
+    {
+        ObjectName res=super.preRegister(server, oname);
+        String name=oname.getKeyProperty("host");
+        if( name != null )
+            setName( name );
+        return res;        
+    }
+    
+    public ObjectName createObjectName(String domain, ObjectName parent)
+        throws Exception
+    {
+        if( log.isDebugEnabled())
+            log.debug("Create ObjectName " + domain + " " + parent );
+        return new ObjectName( domain + ":type=Host,host=" + getName());
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardHostValve.java b/container/catalina/src/share/org/apache/catalina/core/StandardHostValve.java
new file mode 100644
index 0000000..a160193
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardHostValve.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.IOException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.ClientAbortException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.ErrorPage;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardHost</code> container implementation.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  This implementation is likely to be useful only
+ * when processing HTTP requests.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+final class StandardHostValve
+    extends ValveBase {
+
+
+    private static Log log = LogFactory.getLog(StandardHostValve.class);
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardHostValve/1.0";
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Select the appropriate child Context to process this request,
+     * based on the specified request URI.  If no matching Context can
+     * be found, return an appropriate HTTP error.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     * @param valveContext Valve context used to forward to the next Valve
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+    public final void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // Select the Context to be used for this Request
+        Context context = request.getContext();
+        if (context == null) {
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 sm.getString("standardHost.noContext"));
+            return;
+        }
+
+        // Bind the context CL to the current thread
+        if( context.getLoader() != null ) {
+            // Not started - it should check for availability first
+            // This should eventually move to Engine, it's generic.
+            Thread.currentThread().setContextClassLoader
+                    (context.getLoader().getClassLoader());
+        }
+
+        // Ask this Context to process this request
+        context.getPipeline().getFirst().invoke(request, response);
+
+        // Error page processing
+        response.setSuspended(false);
+
+        Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);
+
+        if (t != null) {
+            throwable(request, response, t);
+        } else {
+            status(request, response);
+        }
+
+        // Restore the context classloader
+        Thread.currentThread().setContextClassLoader
+            (StandardHostValve.class.getClassLoader());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Handle the specified Throwable encountered while processing
+     * the specified Request to produce the specified Response.  Any
+     * exceptions that occur during generation of the exception report are
+     * logged and swallowed.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     * @param throwable The exception that occurred (which possibly wraps
+     *  a root cause exception
+     */
+    protected void throwable(Request request, Response response,
+                             Throwable throwable) {
+        Context context = request.getContext();
+        if (context == null)
+            return;
+
+        Throwable realError = throwable;
+
+        if (realError instanceof ServletException) {
+            realError = ((ServletException) realError).getRootCause();
+            if (realError == null) {
+                realError = throwable;
+            }
+        }
+
+        // If this is an aborted request from a client just log it and return
+        if (realError instanceof ClientAbortException ) {
+            if (log.isDebugEnabled()) {
+                log.debug
+                    (sm.getString("standardHost.clientAbort",
+                        ((ClientAbortException) realError).getCause()
+                        .getMessage()));
+            }
+            return;
+        }
+
+        ErrorPage errorPage = findErrorPage(context, throwable);
+        if ((errorPage == null) && (realError != throwable)) {
+            errorPage = findErrorPage(context, realError);
+        }
+
+        if (errorPage != null) {
+            response.setAppCommitted(false);
+            request.setAttribute
+                (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
+                 errorPage.getLocation());
+            request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+                              new Integer(ApplicationFilterFactory.ERROR));
+            request.setAttribute
+                (Globals.STATUS_CODE_ATTR,
+                 new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
+            request.setAttribute(Globals.ERROR_MESSAGE_ATTR,
+                              throwable.getMessage());
+            request.setAttribute(Globals.EXCEPTION_ATTR,
+                              realError);
+            Wrapper wrapper = request.getWrapper();
+            if (wrapper != null)
+                request.setAttribute(Globals.SERVLET_NAME_ATTR,
+                                  wrapper.getName());
+            request.setAttribute(Globals.EXCEPTION_PAGE_ATTR,
+                                 request.getRequestURI());
+            request.setAttribute(Globals.EXCEPTION_TYPE_ATTR,
+                              realError.getClass());
+            if (custom(request, response, errorPage)) {
+                try {
+                    response.flushBuffer();
+                } catch (IOException e) {
+                    container.getLogger().warn("Exception Processing " + errorPage, e);
+                }
+            }
+        } else {
+            // A custom error-page has not been defined for the exception
+            // that was thrown during request processing. Check if an
+            // error-page for error code 500 was specified and if so,
+            // send that page back as the response.
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            // The response is an error
+            response.setError();
+
+            status(request, response);
+        }
+
+
+    }
+
+
+    /**
+     * Handle the HTTP status code (and corresponding message) generated
+     * while processing the specified Request to produce the specified
+     * Response.  Any exceptions that occur during generation of the error
+     * report are logged and swallowed.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     */
+    protected void status(Request request, Response response) {
+
+        int statusCode = response.getStatus();
+
+        // Handle a custom error page for this status code
+        Context context = request.getContext();
+        if (context == null)
+            return;
+
+        /* Only look for error pages when isError() is set.
+         * isError() is set when response.sendError() is invoked. This
+         * allows custom error pages without relying on default from
+         * web.xml.
+         */
+        if (!response.isError())
+            return;
+
+        ErrorPage errorPage = context.findErrorPage(statusCode);
+        if (errorPage != null) {
+            response.setAppCommitted(false);
+            request.setAttribute(Globals.STATUS_CODE_ATTR,
+                              new Integer(statusCode));
+
+            String message = RequestUtil.filter(response.getMessage());
+            if (message == null)
+                message = "";
+            request.setAttribute(Globals.ERROR_MESSAGE_ATTR, message);
+            request.setAttribute
+                (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
+                 errorPage.getLocation());
+            request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+                              new Integer(ApplicationFilterFactory.ERROR));
+
+
+            Wrapper wrapper = request.getWrapper();
+            if (wrapper != null)
+                request.setAttribute(Globals.SERVLET_NAME_ATTR,
+                                  wrapper.getName());
+            request.setAttribute(Globals.EXCEPTION_PAGE_ATTR,
+                                 request.getRequestURI());
+            if (custom(request, response, errorPage)) {
+                try {
+                    response.flushBuffer();
+                } catch (IOException e) {
+                    container.getLogger().warn("Exception Processing " + errorPage, e);
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Find and return the ErrorPage instance for the specified exception's
+     * class, or an ErrorPage instance for the closest superclass for which
+     * there is such a definition.  If no associated ErrorPage instance is
+     * found, return <code>null</code>.
+     *
+     * @param context The Context in which to search
+     * @param exception The exception for which to find an ErrorPage
+     */
+    protected static ErrorPage findErrorPage
+        (Context context, Throwable exception) {
+
+        if (exception == null)
+            return (null);
+        Class clazz = exception.getClass();
+        String name = clazz.getName();
+        while (!"java.lang.Object".equals(clazz)) {
+            ErrorPage errorPage = context.findErrorPage(name);
+            if (errorPage != null)
+                return (errorPage);
+            clazz = clazz.getSuperclass();
+            if (clazz == null)
+                break;
+            name = clazz.getName();
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Handle an HTTP status code or Java exception by forwarding control
+     * to the location included in the specified errorPage object.  It is
+     * assumed that the caller has already recorded any request attributes
+     * that are to be forwarded to this page.  Return <code>true</code> if
+     * we successfully utilized the specified error page location, or
+     * <code>false</code> if the default error report should be rendered.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     * @param errorPage The errorPage directive we are obeying
+     */
+    protected boolean custom(Request request, Response response,
+                             ErrorPage errorPage) {
+
+        if (container.getLogger().isDebugEnabled())
+            container.getLogger().debug("Processing " + errorPage);
+
+        request.setPathInfo(errorPage.getLocation());
+
+        try {
+
+            // Reset the response if possible (else IllegalStateException)
+            //hres.reset();
+            // Reset the response (keeping the real error code and message)
+            Integer statusCodeObj =
+                (Integer) request.getAttribute(Globals.STATUS_CODE_ATTR);
+            int statusCode = statusCodeObj.intValue();
+            String message =
+                (String) request.getAttribute(Globals.ERROR_MESSAGE_ATTR);
+            response.reset(statusCode, message);
+
+            // Forward control to the specified location
+            ServletContext servletContext =
+                request.getContext().getServletContext();
+            RequestDispatcher rd =
+                servletContext.getRequestDispatcher(errorPage.getLocation());
+            rd.forward(request.getRequest(), response.getResponse());
+
+            // If we forward, the response is suspended again
+            response.setSuspended(false);
+
+            // Indicate that we have successfully processed this custom page
+            return (true);
+
+        } catch (Throwable t) {
+
+            // Report our failure to process this custom page
+            container.getLogger().error("Exception Processing " + errorPage, t);
+            return (false);
+
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardPipeline.java b/container/catalina/src/share/org/apache/catalina/core/StandardPipeline.java
new file mode 100644
index 0000000..71b8dfa
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardPipeline.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.util.ArrayList;
+
+import javax.management.ObjectName;
+
+import org.apache.catalina.Contained;
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Valve;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+
+
+/**
+ * Standard implementation of a processing <b>Pipeline</b> that will invoke
+ * a series of Valves that have been configured to be called in order.  This
+ * implementation can be used for any type of Container.
+ *
+ * <b>IMPLEMENTATION WARNING</b> - This implementation assumes that no
+ * calls to <code>addValve()</code> or <code>removeValve</code> are allowed
+ * while a request is currently being processed.  Otherwise, the mechanism
+ * by which per-thread state is maintained will need to be modified.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public class StandardPipeline
+    implements Pipeline, Contained, Lifecycle 
+ {
+
+    private static Log log = LogFactory.getLog(StandardPipeline.class);
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new StandardPipeline instance with no associated Container.
+     */
+    public StandardPipeline() {
+
+        this(null);
+
+    }
+
+
+    /**
+     * Construct a new StandardPipeline instance that is associated with the
+     * specified Container.
+     *
+     * @param container The container we should be associated with
+     */
+    public StandardPipeline(Container container) {
+
+        super();
+        setContainer(container);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The basic Valve (if any) associated with this Pipeline.
+     */
+    protected Valve basic = null;
+
+
+    /**
+     * The Container with which this Pipeline is associated.
+     */
+    protected Container container = null;
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected String info = "org.apache.catalina.core.StandardPipeline/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+
+    /**
+     * The first valve associated with this Pipeline.
+     */
+    protected Valve first = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return descriptive information about this implementation class.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    // ------------------------------------------------------ Contained Methods
+
+
+    /**
+     * Return the Container with which this Pipeline is associated.
+     */
+    public Container getContainer() {
+
+        return (this.container);
+
+    }
+
+
+    /**
+     * Set the Container with which this Pipeline is associated.
+     *
+     * @param container The new associated container
+     */
+    public void setContainer(Container container) {
+
+        this.container = container;
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+    /**
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public synchronized void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (sm.getString("standardPipeline.alreadyStarted"));
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+        started = true;
+
+        // Start the Valves in our pipeline (including the basic), if any
+        Valve current = first;
+        if (current == null) {
+        	current = basic;
+        }
+        while (current != null) {
+            if (current instanceof Lifecycle)
+                ((Lifecycle) current).start();
+            registerValve(current);
+        	current = current.getNext();
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public synchronized void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("standardPipeline.notStarted"));
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Stop the Valves in our pipeline (including the basic), if any
+        Valve current = first;
+        if (current == null) {
+        	current = basic;
+        }
+        while (current != null) {
+            if (current instanceof Lifecycle)
+                ((Lifecycle) current).stop();
+            unregisterValve(current);
+        	current = current.getNext();
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+    }
+
+    private void registerValve(Valve valve) {
+
+        if( valve instanceof ValveBase &&
+                ((ValveBase)valve).getObjectName()==null ) {
+            try {
+                
+                String domain=((ContainerBase)container).getDomain();
+                if( container instanceof StandardContext ) {
+                    domain=((StandardContext)container).getEngineName();
+                }
+                if( container instanceof StandardWrapper) {
+                    Container ctx=((StandardWrapper)container).getParent();
+                    domain=((StandardContext)ctx).getEngineName();
+                }
+                ObjectName vname=((ValveBase)valve).createObjectName(
+                        domain,
+                        ((ContainerBase)container).getJmxName());
+                if( vname != null ) {
+                    ((ValveBase)valve).setObjectName(vname);
+                    Registry.getRegistry(null, null).registerComponent
+                        (valve, vname, valve.getClass().getName());
+                    ((ValveBase)valve).setController
+                        (((ContainerBase)container).getJmxName());
+                }
+            } catch( Throwable t ) {
+                log.info( "Can't register valve " + valve , t );
+            }
+        }
+    }
+    
+    private void unregisterValve(Valve valve) {
+        if( valve instanceof ValveBase ) {
+            try {
+                ValveBase vb=(ValveBase)valve;
+                if( vb.getController()!=null &&
+                        vb.getController() == 
+                        ((ContainerBase)container).getJmxName() ) {
+                    
+                    ObjectName vname=vb.getObjectName();
+                    Registry.getRegistry(null, null).getMBeanServer()
+                        .unregisterMBean(vname);
+                    ((ValveBase)valve).setObjectName(null);
+                }
+            } catch( Throwable t ) {
+                log.info( "Can't unregister valve " + valve , t );
+            }
+        }
+    }    
+
+    // ------------------------------------------------------- Pipeline Methods
+
+
+    /**
+     * <p>Return the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).
+     */
+    public Valve getBasic() {
+
+        return (this.basic);
+
+    }
+
+
+    /**
+     * <p>Set the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).  Prioer to setting the basic Valve,
+     * the Valve's <code>setContainer()</code> will be called, if it
+     * implements <code>Contained</code>, with the owning Container as an
+     * argument.  The method may throw an <code>IllegalArgumentException</code>
+     * if this Valve chooses not to be associated with this Container, or
+     * <code>IllegalStateException</code> if it is already associated with
+     * a different Container.</p>
+     *
+     * @param valve Valve to be distinguished as the basic Valve
+     */
+    public void setBasic(Valve valve) {
+
+        // Change components if necessary
+        Valve oldBasic = this.basic;
+        if (oldBasic == valve)
+            return;
+
+        // Stop the old component if necessary
+        if (oldBasic != null) {
+            if (started && (oldBasic instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) oldBasic).stop();
+                } catch (LifecycleException e) {
+                    log.error("StandardPipeline.setBasic: stop", e);
+                }
+            }
+            if (oldBasic instanceof Contained) {
+                try {
+                    ((Contained) oldBasic).setContainer(null);
+                } catch (Throwable t) {
+                    ;
+                }
+            }
+        }
+
+        // Start the new component if necessary
+        if (valve == null)
+            return;
+        if (valve instanceof Contained) {
+            ((Contained) valve).setContainer(this.container);
+        }
+        if (valve instanceof Lifecycle) {
+            try {
+                ((Lifecycle) valve).start();
+            } catch (LifecycleException e) {
+                log.error("StandardPipeline.setBasic: start", e);
+                return;
+            }
+        }
+
+        // Update the pipeline
+        Valve current = first;
+        while (current != null) {
+        	if (current.getNext() == oldBasic) {
+        		current.setNext(valve);
+        		break;
+        	}
+        	current = current.getNext();
+        }
+        
+        this.basic = valve;
+
+    }
+
+
+    /**
+     * <p>Add a new Valve to the end of the pipeline associated with this
+     * Container.  Prior to adding the Valve, the Valve's
+     * <code>setContainer()</code> method will be called, if it implements
+     * <code>Contained</code>, with the owning Container as an argument.
+     * The method may throw an
+     * <code>IllegalArgumentException</code> if this Valve chooses not to
+     * be associated with this Container, or <code>IllegalStateException</code>
+     * if it is already associated with a different Container.</p>
+     *
+     * @param valve Valve to be added
+     *
+     * @exception IllegalArgumentException if this Container refused to
+     *  accept the specified Valve
+     * @exception IllegalArgumentException if the specifie Valve refuses to be
+     *  associated with this Container
+     * @exception IllegalStateException if the specified Valve is already
+     *  associated with a different Container
+     */
+    public void addValve(Valve valve) {
+    
+        // Validate that we can add this Valve
+        if (valve instanceof Contained)
+            ((Contained) valve).setContainer(this.container);
+
+        // Start the new component if necessary
+        if (started) {
+            if (valve instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) valve).start();
+                } catch (LifecycleException e) {
+                    log.error("StandardPipeline.addValve: start: ", e);
+                }
+            }
+            // Register the newly added valve
+            registerValve(valve);
+        }
+
+        // Add this Valve to the set associated with this Pipeline
+        if (first == null) {
+        	first = valve;
+        	valve.setNext(basic);
+        } else {
+            Valve current = first;
+            while (current != null) {
+				if (current.getNext() == basic) {
+					current.setNext(valve);
+					valve.setNext(basic);
+					break;
+				}
+				current = current.getNext();
+			}
+        }
+
+    }
+
+
+    /**
+     * Return the set of Valves in the pipeline associated with this
+     * Container, including the basic Valve (if any).  If there are no
+     * such Valves, a zero-length array is returned.
+     */
+    public Valve[] getValves() {
+
+    	ArrayList valveList = new ArrayList();
+        Valve current = first;
+        if (current == null) {
+        	current = basic;
+        }
+        while (current != null) {
+        	valveList.add(current);
+        	current = current.getNext();
+        }
+
+        return ((Valve[]) valveList.toArray(new Valve[0]));
+
+    }
+
+    public ObjectName[] getValveObjectNames() {
+
+    	ArrayList valveList = new ArrayList();
+        Valve current = first;
+        if (current == null) {
+        	current = basic;
+        }
+        while (current != null) {
+        	if (current instanceof ValveBase) {
+        		valveList.add(((ValveBase) current).getObjectName());
+        	}
+        	current = current.getNext();
+        }
+
+        return ((ObjectName[]) valveList.toArray(new ObjectName[0]));
+
+    }
+
+    /**
+     * Remove the specified Valve from the pipeline associated with this
+     * Container, if it is found; otherwise, do nothing.  If the Valve is
+     * found and removed, the Valve's <code>setContainer(null)</code> method
+     * will be called if it implements <code>Contained</code>.
+     *
+     * @param valve Valve to be removed
+     */
+    public void removeValve(Valve valve) {
+
+        Valve current;
+        if(first == valve) {
+            first = first.getNext();
+            current = null;
+        } else {
+            current = first;
+        }
+        while (current != null) {
+            if (current.getNext() == valve) {
+                current.setNext(valve.getNext());
+                break;
+            }
+            current = current.getNext();
+        }
+
+        if (valve instanceof Contained)
+            ((Contained) valve).setContainer(null);
+
+        // Stop this valve if necessary
+        if (started) {
+            if (valve instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) valve).stop();
+                } catch (LifecycleException e) {
+                    log.error("StandardPipeline.removeValve: stop: ", e);
+                }
+            }
+            // Unregister the removed valave
+            unregisterValve(valve);
+        }
+    
+    }
+
+
+    public Valve getFirst() {
+        if (first != null) {
+            return first;
+        } else {
+            return basic;
+        }
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardServer.java b/container/catalina/src/share/org/apache/catalina/core/StandardServer.java
new file mode 100644
index 0000000..be5637b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardServer.java
@@ -0,0 +1,797 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.AccessControlException;
+import java.util.Random;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.Service;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.buf.StringCache;
+
+
+
+/**
+ * Standard implementation of the <b>Server</b> interface, available for use
+ * (but not required) when deploying and starting Catalina.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+public final class StandardServer
+    implements Lifecycle, Server, MBeanRegistration 
+ {
+    private static Log log = LogFactory.getLog(StandardServer.class);
+   
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * ServerLifecycleListener classname.
+     */
+    private static String SERVER_LISTENER_CLASS_NAME =
+        "org.apache.catalina.mbeans.ServerLifecycleListener";
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct a default instance of this class.
+     */
+    public StandardServer() {
+
+        super();
+        ServerFactory.setServer(this);
+
+        globalNamingResources = new NamingResources();
+        globalNamingResources.setContainer(this);
+
+        if (isUseNaming()) {
+            if (namingContextListener == null) {
+                namingContextListener = new NamingContextListener();
+                addLifecycleListener(namingContextListener);
+            }
+        }
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Global naming resources context.
+     */
+    private javax.naming.Context globalNamingContext = null;
+
+
+    /**
+     * Global naming resources.
+     */
+    private NamingResources globalNamingResources = null;
+
+
+    /**
+     * Descriptive information about this Server implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardServer/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The naming context listener for this web application.
+     */
+    private NamingContextListener namingContextListener = null;
+
+
+    /**
+     * The port number on which we wait for shutdown commands.
+     */
+    private int port = 8005;
+
+
+    /**
+     * A random number generator that is <strong>only</strong> used if
+     * the shutdown command string is longer than 1024 characters.
+     */
+    private Random random = null;
+
+
+    /**
+     * The set of Services associated with this Server.
+     */
+    private Service services[] = new Service[0];
+
+
+    /**
+     * The shutdown command string we are looking for.
+     */
+    private String shutdown = "SHUTDOWN";
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started?
+     */
+    private boolean started = false;
+
+
+    /**
+     * Has this component been initialized?
+     */
+    private boolean initialized = false;
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the global naming resources context.
+     */
+    public javax.naming.Context getGlobalNamingContext() {
+
+        return (this.globalNamingContext);
+
+    }
+
+
+    /**
+     * Set the global naming resources context.
+     *
+     * @param globalNamingContext The new global naming resource context
+     */
+    public void setGlobalNamingContext
+        (javax.naming.Context globalNamingContext) {
+
+        this.globalNamingContext = globalNamingContext;
+
+    }
+
+
+    /**
+     * Return the global naming resources.
+     */
+    public NamingResources getGlobalNamingResources() {
+
+        return (this.globalNamingResources);
+
+    }
+
+
+    /**
+     * Set the global naming resources.
+     *
+     * @param globalNamingResources The new global naming resources
+     */
+    public void setGlobalNamingResources
+        (NamingResources globalNamingResources) {
+
+        NamingResources oldGlobalNamingResources =
+            this.globalNamingResources;
+        this.globalNamingResources = globalNamingResources;
+        this.globalNamingResources.setContainer(this);
+        support.firePropertyChange("globalNamingResources",
+                                   oldGlobalNamingResources,
+                                   this.globalNamingResources);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Server implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the port number we listen to for shutdown commands.
+     */
+    public int getPort() {
+
+        return (this.port);
+
+    }
+
+
+    /**
+     * Set the port number we listen to for shutdown commands.
+     *
+     * @param port The new port number
+     */
+    public void setPort(int port) {
+
+        this.port = port;
+
+    }
+
+
+    /**
+     * Return the shutdown command string we are waiting for.
+     */
+    public String getShutdown() {
+
+        return (this.shutdown);
+
+    }
+
+
+    /**
+     * Set the shutdown command we are waiting for.
+     *
+     * @param shutdown The new shutdown command
+     */
+    public void setShutdown(String shutdown) {
+
+        this.shutdown = shutdown;
+
+    }
+
+
+    // --------------------------------------------------------- Server Methods
+
+
+    /**
+     * Add a new Service to the set of defined Services.
+     *
+     * @param service The Service to be added
+     */
+    public void addService(Service service) {
+
+        service.setServer(this);
+
+        synchronized (services) {
+            Service results[] = new Service[services.length + 1];
+            System.arraycopy(services, 0, results, 0, services.length);
+            results[services.length] = service;
+            services = results;
+
+            if (initialized) {
+                try {
+                    service.initialize();
+                } catch (LifecycleException e) {
+                    log.error(e);
+                }
+            }
+
+            if (started && (service instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) service).start();
+                } catch (LifecycleException e) {
+                    ;
+                }
+            }
+
+            // Report this property change to interested listeners
+            support.firePropertyChange("service", null, service);
+        }
+
+    }
+
+
+    /**
+     * Wait until a proper shutdown command is received, then return.
+     */
+    public void await() {
+
+        // Set up a server socket to wait on
+        ServerSocket serverSocket = null;
+        try {
+            serverSocket =
+                new ServerSocket(port, 1,
+                                 InetAddress.getByName("127.0.0.1"));
+        } catch (IOException e) {
+            log.error("StandardServer.await: create[" + port
+                               + "]: ", e);
+            System.exit(1);
+        }
+
+        // Loop waiting for a connection and a valid command
+        while (true) {
+
+            // Wait for the next connection
+            Socket socket = null;
+            InputStream stream = null;
+            try {
+                socket = serverSocket.accept();
+                socket.setSoTimeout(10 * 1000);  // Ten seconds
+                stream = socket.getInputStream();
+            } catch (AccessControlException ace) {
+                log.warn("StandardServer.accept security exception: "
+                                   + ace.getMessage(), ace);
+                continue;
+            } catch (IOException e) {
+                log.error("StandardServer.await: accept: ", e);
+                System.exit(1);
+            }
+
+            // Read a set of characters from the socket
+            StringBuffer command = new StringBuffer();
+            int expected = 1024; // Cut off to avoid DoS attack
+            while (expected < shutdown.length()) {
+                if (random == null)
+                    random = new Random(System.currentTimeMillis());
+                expected += (random.nextInt() % 1024);
+            }
+            while (expected > 0) {
+                int ch = -1;
+                try {
+                    ch = stream.read();
+                } catch (IOException e) {
+                    log.warn("StandardServer.await: read: ", e);
+                    ch = -1;
+                }
+                if (ch < 32)  // Control character or EOF terminates loop
+                    break;
+                command.append((char) ch);
+                expected--;
+            }
+
+            // Close the socket now that we are done with it
+            try {
+                socket.close();
+            } catch (IOException e) {
+                ;
+            }
+
+            // Match against our command string
+            boolean match = command.toString().equals(shutdown);
+            if (match) {
+                break;
+            } else
+                log.warn("StandardServer.await: Invalid command '" +
+                                   command.toString() + "' received");
+
+        }
+
+        // Close the server socket and return
+        try {
+            serverSocket.close();
+        } catch (IOException e) {
+            ;
+        }
+
+    }
+
+
+    /**
+     * Return the specified Service (if it exists); otherwise return
+     * <code>null</code>.
+     *
+     * @param name Name of the Service to be returned
+     */
+    public Service findService(String name) {
+
+        if (name == null) {
+            return (null);
+        }
+        synchronized (services) {
+            for (int i = 0; i < services.length; i++) {
+                if (name.equals(services[i].getName())) {
+                    return (services[i]);
+                }
+            }
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Return the set of Services defined within this Server.
+     */
+    public Service[] findServices() {
+
+        return (services);
+
+    }
+    
+    /** 
+     * Return the JMX service names.
+     */
+    public ObjectName[] getServiceNames() {
+        ObjectName onames[]=new ObjectName[ services.length ];
+        for( int i=0; i<services.length; i++ ) {
+            onames[i]=((StandardService)services[i]).getObjectName();
+        }
+        return onames;
+    }
+
+
+    /**
+     * Remove the specified Service from the set associated from this
+     * Server.
+     *
+     * @param service The Service to be removed
+     */
+    public void removeService(Service service) {
+
+        synchronized (services) {
+            int j = -1;
+            for (int i = 0; i < services.length; i++) {
+                if (service == services[i]) {
+                    j = i;
+                    break;
+                }
+            }
+            if (j < 0)
+                return;
+            if (services[j] instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) services[j]).stop();
+                } catch (LifecycleException e) {
+                    ;
+                }
+            }
+            int k = 0;
+            Service results[] = new Service[services.length - 1];
+            for (int i = 0; i < services.length; i++) {
+                if (i != j)
+                    results[k++] = services[i];
+            }
+            services = results;
+
+            // Report this property change to interested listeners
+            support.firePropertyChange("service", service, null);
+        }
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("StandardServer[");
+        sb.append(getPort());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    /**
+     * Write the configuration information for this entire <code>Server</code>
+     * out to the server.xml configuration file.
+     *
+     * @exception javax.management.InstanceNotFoundException if the managed resource object
+     *  cannot be found
+     * @exception javax.management.MBeanException if the initializer of the object throws
+     *  an exception, or persistence is not supported
+     * @exception javax.management.RuntimeOperationsException if an exception is reported
+     *  by the persistence mechanism
+     */
+    public synchronized void storeConfig() throws Exception {
+
+        ObjectName sname = null;    
+        try {
+           sname = new ObjectName("Catalina:type=StoreConfig");
+           if(mserver.isRegistered(sname)) {
+               mserver.invoke(sname, "storeConfig", null, null);            
+           } else
+               log.error("StoreConfig mbean not registered" + sname);
+        } catch (Throwable t) {
+            log.error(t);
+        }
+
+    }
+
+
+    /**
+     * Write the configuration information for <code>Context</code>
+     * out to the specified configuration file.
+     *
+     * @exception javax.management.InstanceNotFoundException if the managed resource object
+     *  cannot be found
+     * @exception javax.management.MBeanException if the initializer of the object throws
+     *  an exception, or persistence is not supported
+     * @exception javax.management.RuntimeOperationsException if an exception is reported
+     *  by the persistence mechanism
+     */
+    public synchronized void storeContext(Context context) throws Exception {
+        
+        ObjectName sname = null;    
+        try {
+           sname = new ObjectName("Catalina:type=StoreConfig");
+           if(mserver.isRegistered(sname)) {
+               mserver.invoke(sname, "store",
+                   new Object[] {context}, 
+                   new String [] { "java.lang.String"});
+           } else
+               log.error("StoreConfig mbean not registered" + sname);
+        } catch (Throwable t) {
+            log.error(t);
+        }
+ 
+    }
+
+
+    /**
+     * Return true if naming should be used.
+     */
+    private boolean isUseNaming() {
+        boolean useNaming = true;
+        // Reading the "catalina.useNaming" environment variable
+        String useNamingProperty = System.getProperty("catalina.useNaming");
+        if ((useNamingProperty != null)
+            && (useNamingProperty.equals("false"))) {
+            useNaming = false;
+        }
+        return useNaming;
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a LifecycleEvent listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a LifecycleEvent listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called before any of the public
+     * methods of this component are utilized.  It should also send a
+     * LifecycleEvent of type START_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started) {
+            log.debug(sm.getString("standardServer.start.started"));
+            return;
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Start our defined Services
+        synchronized (services) {
+            for (int i = 0; i < services.length; i++) {
+                if (services[i] instanceof Lifecycle)
+                    ((Lifecycle) services[i]).start();
+            }
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.  It should also send a LifecycleEvent
+     * of type STOP_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            return;
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Stop our defined Services
+        for (int i = 0; i < services.length; i++) {
+            if (services[i] instanceof Lifecycle)
+                ((Lifecycle) services[i]).stop();
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+
+    }
+
+    public void init() throws Exception {
+        initialize();
+    }
+    
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     */
+    public void initialize()
+        throws LifecycleException 
+    {
+        if (initialized) {
+                log.info(sm.getString("standardServer.initialize.initialized"));
+            return;
+        }
+        lifecycle.fireLifecycleEvent(INIT_EVENT, null);
+        initialized = true;
+
+        if( oname==null ) {
+            try {
+                oname=new ObjectName( "Catalina:type=Server");
+                Registry.getRegistry(null, null)
+                    .registerComponent(this, oname, null );
+            } catch (Exception e) {
+                log.error("Error registering ",e);
+            }
+        }
+        
+        // Register global String cache
+        try {
+            ObjectName oname2 = 
+                new ObjectName(oname.getDomain() + ":type=StringCache");
+            Registry.getRegistry(null, null)
+                .registerComponent(new StringCache(), oname2, null );
+        } catch (Exception e) {
+            log.error("Error registering ",e);
+        }
+
+        // Initialize our defined Services
+        for (int i = 0; i < services.length; i++) {
+            services[i].initialize();
+        }
+    }
+    
+    protected String type;
+    protected String domain;
+    protected String suffix;
+    protected ObjectName oname;
+    protected MBeanServer mserver;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardService.java b/container/catalina/src/share/org/apache/catalina/core/StandardService.java
new file mode 100644
index 0000000..1d08377
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardService.java
@@ -0,0 +1,630 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+
+
+/**
+ * Standard implementation of the <code>Service</code> interface.  The
+ * associated Container is generally an instance of Engine, but this is
+ * not required.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public class StandardService
+        implements Lifecycle, Service, MBeanRegistration 
+ {
+    private static Log log = LogFactory.getLog(StandardService.class);
+   
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Descriptive information about this component implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardService/1.0";
+
+
+    /**
+     * The name of this service.
+     */
+    private String name = null;
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    /**
+     * The <code>Server</code> that owns this Service, if any.
+     */
+    private Server server = null;
+
+    /**
+     * Has this component been started?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    /**
+     * The set of Connectors associated with this Service.
+     */
+    protected Connector connectors[] = new Connector[0];
+
+
+    /**
+     * The Container associated with this Service. (In the case of the
+     * org.apache.catalina.startup.Embedded subclass, this holds the most
+     * recently added Engine.)
+     */
+    protected Container container = null;
+
+
+    /**
+     * Has this component been initialized?
+     */
+    protected boolean initialized = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the <code>Container</code> that handles requests for all
+     * <code>Connectors</code> associated with this Service.
+     */
+    public Container getContainer() {
+
+        return (this.container);
+
+    }
+
+
+    /**
+     * Set the <code>Container</code> that handles requests for all
+     * <code>Connectors</code> associated with this Service.
+     *
+     * @param container The new Container
+     */
+    public void setContainer(Container container) {
+
+        Container oldContainer = this.container;
+        if ((oldContainer != null) && (oldContainer instanceof Engine))
+            ((Engine) oldContainer).setService(null);
+        this.container = container;
+        if ((this.container != null) && (this.container instanceof Engine))
+            ((Engine) this.container).setService(this);
+        if (started && (this.container != null) &&
+            (this.container instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) this.container).start();
+            } catch (LifecycleException e) {
+                ;
+            }
+        }
+        synchronized (connectors) {
+            for (int i = 0; i < connectors.length; i++)
+                connectors[i].setContainer(this.container);
+        }
+        if (started && (oldContainer != null) &&
+            (oldContainer instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) oldContainer).stop();
+            } catch (LifecycleException e) {
+                ;
+            }
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("container", oldContainer, this.container);
+
+    }
+
+    public ObjectName getContainerName() {
+        if( container instanceof ContainerBase ) {
+            return ((ContainerBase)container).getJmxName();
+        }
+        return null;
+    }
+
+
+    /**
+     * Return descriptive information about this Service implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the name of this Service.
+     */
+    public String getName() {
+
+        return (this.name);
+
+    }
+
+
+    /**
+     * Set the name of this Service.
+     *
+     * @param name The new service name
+     */
+    public void setName(String name) {
+
+        this.name = name;
+
+    }
+
+
+    /**
+     * Return the <code>Server</code> with which we are associated (if any).
+     */
+    public Server getServer() {
+
+        return (this.server);
+
+    }
+
+
+    /**
+     * Set the <code>Server</code> with which we are associated (if any).
+     *
+     * @param server The server that owns this Service
+     */
+    public void setServer(Server server) {
+
+        this.server = server;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new Connector to the set of defined Connectors, and associate it
+     * with this Service's Container.
+     *
+     * @param connector The Connector to be added
+     */
+    public void addConnector(Connector connector) {
+
+        synchronized (connectors) {
+            connector.setContainer(this.container);
+            connector.setService(this);
+            Connector results[] = new Connector[connectors.length + 1];
+            System.arraycopy(connectors, 0, results, 0, connectors.length);
+            results[connectors.length] = connector;
+            connectors = results;
+
+            if (initialized) {
+                try {
+                    connector.initialize();
+                } catch (LifecycleException e) {
+                    log.error("Connector.initialize", e);
+                }
+            }
+
+            if (started && (connector instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) connector).start();
+                } catch (LifecycleException e) {
+                    log.error("Connector.start", e);
+                }
+            }
+
+            // Report this property change to interested listeners
+            support.firePropertyChange("connector", null, connector);
+        }
+
+    }
+
+    public ObjectName[] getConnectorNames() {
+        ObjectName results[] = new ObjectName[connectors.length];
+        for( int i=0; i<results.length; i++ ) {
+            // if it's a coyote connector
+            //if( connectors[i] instanceof CoyoteConnector ) {
+            //    results[i]=((CoyoteConnector)connectors[i]).getJmxName();
+            //}
+        }
+        return results;
+    }
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Find and return the set of Connectors associated with this Service.
+     */
+    public Connector[] findConnectors() {
+
+        return (connectors);
+
+    }
+
+
+    /**
+     * Remove the specified Connector from the set associated from this
+     * Service.  The removed Connector will also be disassociated from our
+     * Container.
+     *
+     * @param connector The Connector to be removed
+     */
+    public void removeConnector(Connector connector) {
+
+        synchronized (connectors) {
+            int j = -1;
+            for (int i = 0; i < connectors.length; i++) {
+                if (connector == connectors[i]) {
+                    j = i;
+                    break;
+                }
+            }
+            if (j < 0)
+                return;
+            if (started && (connectors[j] instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) connectors[j]).stop();
+                } catch (LifecycleException e) {
+                    log.error("Connector.stop", e);
+                }
+            }
+            connectors[j].setContainer(null);
+            connector.setService(null);
+            int k = 0;
+            Connector results[] = new Connector[connectors.length - 1];
+            for (int i = 0; i < connectors.length; i++) {
+                if (i != j)
+                    results[k++] = connectors[i];
+            }
+            connectors = results;
+
+            // Report this property change to interested listeners
+            support.firePropertyChange("connector", connector, null);
+        }
+
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("StandardService[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a LifecycleEvent listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a LifecycleEvent listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called before any of the public
+     * methods of this component are utilized.  It should also send a
+     * LifecycleEvent of type START_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (log.isInfoEnabled() && started) {
+            log.info(sm.getString("standardService.start.started"));
+        }
+        
+        if( ! initialized )
+            init(); 
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+        if(log.isInfoEnabled())
+            log.info(sm.getString("standardService.start.name", this.name));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Start our defined Container first
+        if (container != null) {
+            synchronized (container) {
+                if (container instanceof Lifecycle) {
+                    ((Lifecycle) container).start();
+                }
+            }
+        }
+
+        // Start our defined Connectors second
+        synchronized (connectors) {
+            for (int i = 0; i < connectors.length; i++) {
+                if (connectors[i] instanceof Lifecycle)
+                    ((Lifecycle) connectors[i]).start();
+            }
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.  It should also send a LifecycleEvent
+     * of type STOP_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started) {
+            return;
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+        // Stop our defined Connectors first
+        synchronized (connectors) {
+            for (int i = 0; i < connectors.length; i++) {
+                connectors[i].pause();
+            }
+        }
+
+        // Heuristic: Sleep for a while to ensure pause of the connector
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        if(log.isInfoEnabled())
+            log.info
+                (sm.getString("standardService.stop.name", this.name));
+        started = false;
+
+        // Stop our defined Container second
+        if (container != null) {
+            synchronized (container) {
+                if (container instanceof Lifecycle) {
+                    ((Lifecycle) container).stop();
+                }
+            }
+        }
+        // FIXME pero -- Why container stop first? KeepAlive connetions can send request! 
+        // Stop our defined Connectors first
+        synchronized (connectors) {
+            for (int i = 0; i < connectors.length; i++) {
+                if (connectors[i] instanceof Lifecycle)
+                    ((Lifecycle) connectors[i]).stop();
+            }
+        }
+
+        if( oname==controller ) {
+            // we registered ourself on init().
+            // That should be the typical case - this object is just for
+            // backward compat, nobody should bother to load it explicitely
+            Registry.getRegistry(null, null).unregisterComponent(oname);
+        }
+        
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+
+    }
+
+
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     */
+    public void initialize()
+            throws LifecycleException
+    {
+        // Service shouldn't be used with embeded, so it doesn't matter
+        if (initialized) {
+            if(log.isInfoEnabled())
+                log.info(sm.getString("standardService.initialize.initialized"));
+            return;
+        }
+        initialized = true;
+
+        if( oname==null ) {
+            try {
+                // Hack - Server should be deprecated...
+                Container engine=this.getContainer();
+                domain=engine.getName();
+                oname=new ObjectName(domain + ":type=Service,serviceName="+name);
+                this.controller=oname;
+                Registry.getRegistry(null, null)
+                    .registerComponent(this, oname, null);
+            } catch (Exception e) {
+                log.error(sm.getString("standardService.register.failed",domain),e);
+            }
+            
+            
+        }
+        if( server==null ) {
+            // Register with the server 
+            // HACK: ServerFactory should be removed...
+            
+            ServerFactory.getServer().addService(this);
+        }
+               
+
+        // Initialize our defined Connectors
+        synchronized (connectors) {
+                for (int i = 0; i < connectors.length; i++) {
+                    connectors[i].initialize();
+                }
+        }
+    }
+    
+    public void destroy() throws LifecycleException {
+        if( started ) stop();
+        // FIXME unregister should be here probably -- stop doing that ?
+    }
+
+    public void init() {
+        try {
+            initialize();
+        } catch( Throwable t ) {
+            log.error(sm.getString("standardService.initialize.failed",domain),t);
+        }
+    }
+
+    protected String type;
+    protected String domain;
+    protected String suffix;
+    protected ObjectName oname;
+    protected ObjectName controller;
+    protected MBeanServer mserver;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardWrapper.java b/container/catalina/src/share/org/apache/catalina/core/StandardWrapper.java
new file mode 100644
index 0000000..a7f8000
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardWrapper.java
@@ -0,0 +1,1828 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+import java.lang.reflect.Method;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Stack;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.SingleThreadModel;
+import javax.servlet.UnavailableException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerServlet;
+import org.apache.catalina.Context;
+import org.apache.catalina.InstanceEvent;
+import org.apache.catalina.InstanceListener;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.InstanceSupport;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.log.SystemLogHandler;
+import org.apache.commons.modeler.Registry;
+
+/**
+ * Standard implementation of the <b>Wrapper</b> interface that represents
+ * an individual servlet definition.  No child Containers are allowed, and
+ * the parent Container must be a Context.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+public class StandardWrapper
+    extends ContainerBase
+    implements ServletConfig, Wrapper, NotificationEmitter {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( StandardWrapper.class );
+
+    private static final String[] DEFAULT_SERVLET_METHODS = new String[] {
+                                                    "GET", "HEAD", "POST" };
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new StandardWrapper component with the default basic Valve.
+     */
+    public StandardWrapper() {
+
+        super();
+        swValve=new StandardWrapperValve();
+        pipeline.setBasic(swValve);
+        broadcaster = new NotificationBroadcasterSupport();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The date and time at which this servlet will become available (in
+     * milliseconds since the epoch), or zero if the servlet is available.
+     * If this value equals Long.MAX_VALUE, the unavailability of this
+     * servlet is considered permanent.
+     */
+    private long available = 0L;
+    
+    /**
+     * The broadcaster that sends j2ee notifications. 
+     */
+    private NotificationBroadcasterSupport broadcaster = null;
+    
+    /**
+     * The count of allocations that are currently active (even if they
+     * are for the same instance, as will be true on a non-STM servlet).
+     */
+    private int countAllocated = 0;
+
+
+    /**
+     * The facade associated with this wrapper.
+     */
+    private StandardWrapperFacade facade =
+        new StandardWrapperFacade(this);
+
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardWrapper/1.0";
+
+
+    /**
+     * The (single) initialized instance of this servlet.
+     */
+    private Servlet instance = null;
+
+
+    /**
+     * The support object for our instance listeners.
+     */
+    private InstanceSupport instanceSupport = new InstanceSupport(this);
+
+
+    /**
+     * The context-relative URI of the JSP file for this servlet.
+     */
+    private String jspFile = null;
+
+
+    /**
+     * The load-on-startup order value (negative value means load on
+     * first call) for this servlet.
+     */
+    private int loadOnStartup = -1;
+
+
+    /**
+     * Mappings associated with the wrapper.
+     */
+    private ArrayList mappings = new ArrayList();
+
+
+    /**
+     * The initialization parameters for this servlet, keyed by
+     * parameter name.
+     */
+    private HashMap parameters = new HashMap();
+
+
+    /**
+     * The security role references for this servlet, keyed by role name
+     * used in the servlet.  The corresponding value is the role name of
+     * the web application itself.
+     */
+    private HashMap references = new HashMap();
+
+
+    /**
+     * The run-as identity for this servlet.
+     */
+    private String runAs = null;
+
+    /**
+     * The notification sequence number.
+     */
+    private long sequenceNumber = 0;
+
+    /**
+     * The fully qualified servlet class name for this servlet.
+     */
+    private String servletClass = null;
+
+
+    /**
+     * Does this servlet implement the SingleThreadModel interface?
+     */
+    private boolean singleThreadModel = false;
+
+
+    /**
+     * Are we unloading our servlet instance at the moment?
+     */
+    private boolean unloading = false;
+
+
+    /**
+     * Maximum number of STM instances.
+     */
+    private int maxInstances = 20;
+
+
+    /**
+     * Number of instances currently loaded for a STM servlet.
+     */
+    private int nInstances = 0;
+
+
+    /**
+     * Stack containing the STM instances.
+     */
+    private Stack instancePool = null;
+
+
+    /**
+     * True if this StandardWrapper is for the JspServlet
+     */
+    private boolean isJspServlet;
+
+
+    /**
+     * The ObjectName of the JSP monitoring mbean
+     */
+    private ObjectName jspMonitorON;
+
+
+    /**
+     * Should we swallow System.out
+     */
+    private boolean swallowOutput = false;
+
+    // To support jmx attributes
+    private StandardWrapperValve swValve;
+    private long loadTime=0;
+    private int classLoadTime=0;
+    
+    /**
+     * Static class array used when the SecurityManager is turned on and 
+     * <code>Servlet.init</code> is invoked.
+     */
+    private static Class[] classType = new Class[]{ServletConfig.class};
+    
+    
+    /**
+     * Static class array used when the SecurityManager is turned on and 
+     * <code>Servlet.service</code>  is invoked.
+     */                                                 
+    private static Class[] classTypeUsedInService = new Class[]{
+                                                         ServletRequest.class,
+                                                         ServletResponse.class};
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the available date/time for this servlet, in milliseconds since
+     * the epoch.  If this date/time is Long.MAX_VALUE, it is considered to mean
+     * that unavailability is permanent and any request for this servlet will return
+     * an SC_NOT_FOUND error.  If this date/time is in the future, any request for
+     * this servlet will return an SC_SERVICE_UNAVAILABLE error.  If it is zero,
+     * the servlet is currently available.
+     */
+    public long getAvailable() {
+
+        return (this.available);
+
+    }
+
+
+    /**
+     * Set the available date/time for this servlet, in milliseconds since the
+     * epoch.  If this date/time is Long.MAX_VALUE, it is considered to mean
+     * that unavailability is permanent and any request for this servlet will return
+     * an SC_NOT_FOUND error. If this date/time is in the future, any request for
+     * this servlet will return an SC_SERVICE_UNAVAILABLE error.
+     *
+     * @param available The new available date/time
+     */
+    public void setAvailable(long available) {
+
+        long oldAvailable = this.available;
+        if (available > System.currentTimeMillis())
+            this.available = available;
+        else
+            this.available = 0L;
+        support.firePropertyChange("available", new Long(oldAvailable),
+                                   new Long(this.available));
+
+    }
+
+
+    /**
+     * Return the number of active allocations of this servlet, even if they
+     * are all for the same instance (as will be true for servlets that do
+     * not implement <code>SingleThreadModel</code>.
+     */
+    public int getCountAllocated() {
+
+        return (this.countAllocated);
+
+    }
+
+
+    public String getEngineName() {
+        return ((StandardContext)getParent()).getEngineName();
+    }
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the InstanceSupport object for this Wrapper instance.
+     */
+    public InstanceSupport getInstanceSupport() {
+
+        return (this.instanceSupport);
+
+    }
+
+
+    /**
+     * Return the context-relative URI of the JSP file for this servlet.
+     */
+    public String getJspFile() {
+
+        return (this.jspFile);
+
+    }
+
+
+    /**
+     * Set the context-relative URI of the JSP file for this servlet.
+     *
+     * @param jspFile JSP file URI
+     */
+    public void setJspFile(String jspFile) {
+
+        String oldJspFile = this.jspFile;
+        this.jspFile = jspFile;
+        support.firePropertyChange("jspFile", oldJspFile, this.jspFile);
+
+        // Each jsp-file needs to be represented by its own JspServlet and
+        // corresponding JspMonitoring mbean, because it may be initialized
+        // with its own init params
+        isJspServlet = true;
+
+    }
+
+
+    /**
+     * Return the load-on-startup order value (negative value means
+     * load on first call).
+     */
+    public int getLoadOnStartup() {
+
+        if (isJspServlet && loadOnStartup < 0) {
+            /*
+             * JspServlet must always be preloaded, because its instance is
+             * used during registerJMX (when registering the JSP
+             * monitoring mbean)
+             */
+             return Integer.MAX_VALUE;
+        } else {
+            return (this.loadOnStartup);
+        }
+    }
+
+
+    /**
+     * Set the load-on-startup order value (negative value means
+     * load on first call).
+     *
+     * @param value New load-on-startup value
+     */
+    public void setLoadOnStartup(int value) {
+
+        int oldLoadOnStartup = this.loadOnStartup;
+        this.loadOnStartup = value;
+        support.firePropertyChange("loadOnStartup",
+                                   new Integer(oldLoadOnStartup),
+                                   new Integer(this.loadOnStartup));
+
+    }
+
+
+
+    /**
+     * Set the load-on-startup order value from a (possibly null) string.
+     * Per the specification, any missing or non-numeric value is converted
+     * to a zero, so that this servlet will still be loaded at startup
+     * time, but in an arbitrary order.
+     *
+     * @param value New load-on-startup value
+     */
+    public void setLoadOnStartupString(String value) {
+
+        try {
+            setLoadOnStartup(Integer.parseInt(value));
+        } catch (NumberFormatException e) {
+            setLoadOnStartup(0);
+        }
+    }
+
+    public String getLoadOnStartupString() {
+        return Integer.toString( getLoadOnStartup());
+    }
+
+
+    /**
+     * Return maximum number of instances that will be allocated when a single
+     * thread model servlet is used.
+     */
+    public int getMaxInstances() {
+
+        return (this.maxInstances);
+
+    }
+
+
+    /**
+     * Set the maximum number of instances that will be allocated when a single
+     * thread model servlet is used.
+     *
+     * @param maxInstances New value of maxInstances
+     */
+    public void setMaxInstances(int maxInstances) {
+
+        int oldMaxInstances = this.maxInstances;
+        this.maxInstances = maxInstances;
+        support.firePropertyChange("maxInstances", oldMaxInstances,
+                                   this.maxInstances);
+
+    }
+
+
+    /**
+     * Set the parent Container of this Wrapper, but only if it is a Context.
+     *
+     * @param container Proposed parent Container
+     */
+    public void setParent(Container container) {
+
+        if ((container != null) &&
+            !(container instanceof Context))
+            throw new IllegalArgumentException
+                (sm.getString("standardWrapper.notContext"));
+        if (container instanceof StandardContext) {
+            swallowOutput = ((StandardContext)container).getSwallowOutput();
+        }
+        super.setParent(container);
+
+    }
+
+
+    /**
+     * Return the run-as identity for this servlet.
+     */
+    public String getRunAs() {
+
+        return (this.runAs);
+
+    }
+
+
+    /**
+     * Set the run-as identity for this servlet.
+     *
+     * @param runAs New run-as identity value
+     */
+    public void setRunAs(String runAs) {
+
+        String oldRunAs = this.runAs;
+        this.runAs = runAs;
+        support.firePropertyChange("runAs", oldRunAs, this.runAs);
+
+    }
+
+
+    /**
+     * Return the fully qualified servlet class name for this servlet.
+     */
+    public String getServletClass() {
+
+        return (this.servletClass);
+
+    }
+
+
+    /**
+     * Set the fully qualified servlet class name for this servlet.
+     *
+     * @param servletClass Servlet class name
+     */
+    public void setServletClass(String servletClass) {
+
+        String oldServletClass = this.servletClass;
+        this.servletClass = servletClass;
+        support.firePropertyChange("servletClass", oldServletClass,
+                                   this.servletClass);
+        if (Constants.JSP_SERVLET_CLASS.equals(servletClass)) {
+            isJspServlet = true;
+        }
+    }
+
+
+
+    /**
+     * Set the name of this servlet.  This is an alias for the normal
+     * <code>Container.setName()</code> method, and complements the
+     * <code>getServletName()</code> method required by the
+     * <code>ServletConfig</code> interface.
+     *
+     * @param name The new name of this servlet
+     */
+    public void setServletName(String name) {
+
+        setName(name);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the servlet class represented by this
+     * component implements the <code>SingleThreadModel</code> interface.
+     */
+    public boolean isSingleThreadModel() {
+
+        try {
+            loadServlet();
+        } catch (Throwable t) {
+            ;
+        }
+        return (singleThreadModel);
+
+    }
+
+
+    /**
+     * Is this servlet currently unavailable?
+     */
+    public boolean isUnavailable() {
+
+        if (available == 0L)
+            return (false);
+        else if (available <= System.currentTimeMillis()) {
+            available = 0L;
+            return (false);
+        } else
+            return (true);
+
+    }
+
+
+    /**
+     * Gets the names of the methods supported by the underlying servlet.
+     *
+     * This is the same set of methods included in the Allow response header
+     * in response to an OPTIONS request method processed by the underlying
+     * servlet.
+     *
+     * @return Array of names of the methods supported by the underlying
+     * servlet
+     */
+    public String[] getServletMethods() throws ServletException {
+
+        Class servletClazz = loadServlet().getClass();
+        if (!javax.servlet.http.HttpServlet.class.isAssignableFrom(
+                                                        servletClazz)) {
+            return DEFAULT_SERVLET_METHODS;
+        }
+
+        HashSet allow = new HashSet();
+        allow.add("TRACE");
+        allow.add("OPTIONS");
+	
+        Method[] methods = getAllDeclaredMethods(servletClazz);
+        for (int i=0; methods != null && i<methods.length; i++) {
+            Method m = methods[i];
+	    
+            if (m.getName().equals("doGet")) {
+                allow.add("GET");
+                allow.add("HEAD");
+            } else if (m.getName().equals("doPost")) {
+                allow.add("POST");
+            } else if (m.getName().equals("doPut")) {
+                allow.add("PUT");
+            } else if (m.getName().equals("doDelete")) {
+                allow.add("DELETE");
+            }
+        }
+
+        String[] methodNames = new String[allow.size()];
+        return (String[]) allow.toArray(methodNames);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Extract the root cause from a servlet exception.
+     * 
+     * @param e The servlet exception
+     */
+    public static Throwable getRootCause(ServletException e) {
+        Throwable rootCause = e;
+        Throwable rootCauseCheck = null;
+        // Extra aggressive rootCause finding
+        do {
+            try {
+                rootCauseCheck = (Throwable)IntrospectionUtils.getProperty
+                                            (rootCause, "rootCause");
+                if (rootCauseCheck!=null)
+                    rootCause = rootCauseCheck;
+
+            } catch (ClassCastException ex) {
+                rootCauseCheck = null;
+            }
+        } while (rootCauseCheck != null);
+        return rootCause;
+    }
+
+
+    /**
+     * Refuse to add a child Container, because Wrappers are the lowest level
+     * of the Container hierarchy.
+     *
+     * @param child Child container to be added
+     */
+    public void addChild(Container child) {
+
+        throw new IllegalStateException
+            (sm.getString("standardWrapper.notChild"));
+
+    }
+
+
+    /**
+     * Add a new servlet initialization parameter for this servlet.
+     *
+     * @param name Name of this initialization parameter to add
+     * @param value Value of this initialization parameter to add
+     */
+    public void addInitParameter(String name, String value) {
+
+        synchronized (parameters) {
+            parameters.put(name, value);
+        }
+        fireContainerEvent("addInitParameter", name);
+
+    }
+
+
+    /**
+     * Add a new listener interested in InstanceEvents.
+     *
+     * @param listener The new listener
+     */
+    public void addInstanceListener(InstanceListener listener) {
+
+        instanceSupport.addInstanceListener(listener);
+
+    }
+
+
+    /**
+     * Add a mapping associated with the Wrapper.
+     *
+     * @param mapping The new wrapper mapping
+     */
+    public void addMapping(String mapping) {
+
+        synchronized (mappings) {
+            mappings.add(mapping);
+        }
+        fireContainerEvent("addMapping", mapping);
+
+    }
+
+
+    /**
+     * Add a new security role reference record to the set of records for
+     * this servlet.
+     *
+     * @param name Role name used within this servlet
+     * @param link Role name used within the web application
+     */
+    public void addSecurityReference(String name, String link) {
+
+        synchronized (references) {
+            references.put(name, link);
+        }
+        fireContainerEvent("addSecurityReference", name);
+
+    }
+
+
+    /**
+     * Allocate an initialized instance of this Servlet that is ready to have
+     * its <code>service()</code> method called.  If the servlet class does
+     * not implement <code>SingleThreadModel</code>, the (only) initialized
+     * instance may be returned immediately.  If the servlet class implements
+     * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
+     * that this instance is not allocated again until it is deallocated by a
+     * call to <code>deallocate()</code>.
+     *
+     * @exception ServletException if the servlet init() method threw
+     *  an exception
+     * @exception ServletException if a loading error occurs
+     */
+    public Servlet allocate() throws ServletException {
+
+        // If we are currently unloading this servlet, throw an exception
+        if (unloading)
+            throw new ServletException
+              (sm.getString("standardWrapper.unloading", getName()));
+
+        // If not SingleThreadedModel, return the same instance every time
+        if (!singleThreadModel) {
+
+            // Load and initialize our instance if necessary
+            if (instance == null) {
+                synchronized (this) {
+                    if (instance == null) {
+                        try {
+                            if (log.isDebugEnabled())
+                                log.debug("Allocating non-STM instance");
+
+                            instance = loadServlet();
+                        } catch (ServletException e) {
+                            throw e;
+                        } catch (Throwable e) {
+                            throw new ServletException
+                                (sm.getString("standardWrapper.allocate"), e);
+                        }
+                    }
+                }
+            }
+
+            if (!singleThreadModel) {
+                if (log.isTraceEnabled())
+                    log.trace("  Returning non-STM instance");
+                countAllocated++;
+                return (instance);
+            }
+
+        }
+
+        synchronized (instancePool) {
+
+            while (countAllocated >= nInstances) {
+                // Allocate a new instance if possible, or else wait
+                if (nInstances < maxInstances) {
+                    try {
+                        instancePool.push(loadServlet());
+                        nInstances++;
+                    } catch (ServletException e) {
+                        throw e;
+                    } catch (Throwable e) {
+                        throw new ServletException
+                            (sm.getString("standardWrapper.allocate"), e);
+                    }
+                } else {
+                    try {
+                        instancePool.wait();
+                    } catch (InterruptedException e) {
+                        ;
+                    }
+                }
+            }
+            if (log.isTraceEnabled())
+                log.trace("  Returning allocated STM instance");
+            countAllocated++;
+            return (Servlet) instancePool.pop();
+
+        }
+
+    }
+
+
+    /**
+     * Return this previously allocated servlet to the pool of available
+     * instances.  If this servlet class does not implement SingleThreadModel,
+     * no action is actually required.
+     *
+     * @param servlet The servlet to be returned
+     *
+     * @exception ServletException if a deallocation error occurs
+     */
+    public void deallocate(Servlet servlet) throws ServletException {
+
+        // If not SingleThreadModel, no action is required
+        if (!singleThreadModel) {
+            countAllocated--;
+            return;
+        }
+
+        // Unlock and free this instance
+        synchronized (instancePool) {
+            countAllocated--;
+            instancePool.push(servlet);
+            instancePool.notify();
+        }
+
+    }
+
+
+    /**
+     * Return the value for the specified initialization parameter name,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the requested initialization parameter
+     */
+    public String findInitParameter(String name) {
+
+        synchronized (parameters) {
+            return ((String) parameters.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the names of all defined initialization parameters for this
+     * servlet.
+     */
+    public String[] findInitParameters() {
+
+        synchronized (parameters) {
+            String results[] = new String[parameters.size()];
+            return ((String[]) parameters.keySet().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the mappings associated with this wrapper.
+     */
+    public String[] findMappings() {
+
+        synchronized (mappings) {
+            return (String[]) mappings.toArray(new String[mappings.size()]);
+        }
+
+    }
+
+
+    /**
+     * Return the security role link for the specified security role
+     * reference name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Security role reference used within this servlet
+     */
+    public String findSecurityReference(String name) {
+
+        synchronized (references) {
+            return ((String) references.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the set of security role reference names associated with
+     * this servlet, if any; otherwise return a zero-length array.
+     */
+    public String[] findSecurityReferences() {
+
+        synchronized (references) {
+            String results[] = new String[references.size()];
+            return ((String[]) references.keySet().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * FIXME: Fooling introspection ...
+     */
+    public Wrapper findMappingObject() {
+        return (Wrapper) getMappingObject();
+    }
+
+
+    /**
+     * Load and initialize an instance of this servlet, if there is not already
+     * at least one initialized instance.  This can be used, for example, to
+     * load servlets that are marked in the deployment descriptor to be loaded
+     * at server startup time.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  Servlets whose classnames begin with
+     * <code>org.apache.catalina.</code> (so-called "container" servlets)
+     * are loaded by the same classloader that loaded this class, rather than
+     * the classloader for the current web application.
+     * This gives such classes access to Catalina internals, which are
+     * prevented for classes loaded for web applications.
+     *
+     * @exception ServletException if the servlet init() method threw
+     *  an exception
+     * @exception ServletException if some other loading problem occurs
+     */
+    public synchronized void load() throws ServletException {
+        instance = loadServlet();
+    }
+
+
+    /**
+     * Load and initialize an instance of this servlet, if there is not already
+     * at least one initialized instance.  This can be used, for example, to
+     * load servlets that are marked in the deployment descriptor to be loaded
+     * at server startup time.
+     */
+    public synchronized Servlet loadServlet() throws ServletException {
+
+        // Nothing to do if we already have an instance or an instance pool
+        if (!singleThreadModel && (instance != null))
+            return instance;
+
+        PrintStream out = System.out;
+        if (swallowOutput) {
+            SystemLogHandler.startCapture();
+        }
+
+        Servlet servlet;
+        try {
+            long t1=System.currentTimeMillis();
+            // If this "servlet" is really a JSP file, get the right class.
+            // HOLD YOUR NOSE - this is a kludge that avoids having to do special
+            // case Catalina-specific code in Jasper - it also requires that the
+            // servlet path be replaced by the <jsp-file> element content in
+            // order to be completely effective
+            String actualClass = servletClass;
+            if ((actualClass == null) && (jspFile != null)) {
+                Wrapper jspWrapper = (Wrapper)
+                    ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
+                if (jspWrapper != null) {
+                    actualClass = jspWrapper.getServletClass();
+                    // Merge init parameters
+                    String paramNames[] = jspWrapper.findInitParameters();
+                    for (int i = 0; i < paramNames.length; i++) {
+                        if (parameters.get(paramNames[i]) == null) {
+                            parameters.put
+                                (paramNames[i], 
+                                 jspWrapper.findInitParameter(paramNames[i]));
+                        }
+                    }
+                }
+            }
+
+            // Complain if no servlet class has been specified
+            if (actualClass == null) {
+                unavailable(null);
+                throw new ServletException
+                    (sm.getString("standardWrapper.notClass", getName()));
+            }
+
+            // Acquire an instance of the class loader to be used
+            Loader loader = getLoader();
+            if (loader == null) {
+                unavailable(null);
+                throw new ServletException
+                    (sm.getString("standardWrapper.missingLoader", getName()));
+            }
+
+            ClassLoader classLoader = loader.getClassLoader();
+
+            // Special case class loader for a container provided servlet
+            //  
+            if (isContainerProvidedServlet(actualClass) && 
+                    ! ((Context)getParent()).getPrivileged() ) {
+                // If it is a priviledged context - using its own
+                // class loader will work, since it's a child of the container
+                // loader
+                classLoader = this.getClass().getClassLoader();
+            }
+
+            // Load the specified servlet class from the appropriate class loader
+            Class classClass = null;
+            try {
+                if (SecurityUtil.isPackageProtectionEnabled()){
+                    final ClassLoader fclassLoader = classLoader;
+                    final String factualClass = actualClass;
+                    try{
+                        classClass = (Class)AccessController.doPrivileged(
+                                new PrivilegedExceptionAction(){
+                                    public Object run() throws Exception{
+                                        if (fclassLoader != null) {
+                                            return fclassLoader.loadClass(factualClass);
+                                        } else {
+                                            return Class.forName(factualClass);
+                                        }
+                                    }
+                        });
+                    } catch(PrivilegedActionException pax){
+                        Exception ex = pax.getException();
+                        if (ex instanceof ClassNotFoundException){
+                            throw (ClassNotFoundException)ex;
+                        } else {
+                            getServletContext().log( "Error loading "
+                                + fclassLoader + " " + factualClass, ex );
+                        }
+                    }
+                } else {
+                    if (classLoader != null) {
+                        classClass = classLoader.loadClass(actualClass);
+                    } else {
+                        classClass = Class.forName(actualClass);
+                    }
+                }
+            } catch (ClassNotFoundException e) {
+                unavailable(null);
+                getServletContext().log( "Error loading " + classLoader + " " + actualClass, e );
+                throw new ServletException
+                    (sm.getString("standardWrapper.missingClass", actualClass),
+                     e);
+            }
+
+            if (classClass == null) {
+                unavailable(null);
+                throw new ServletException
+                    (sm.getString("standardWrapper.missingClass", actualClass));
+            }
+
+            // Instantiate and initialize an instance of the servlet class itself
+            try {
+                servlet = (Servlet) classClass.newInstance();
+            } catch (ClassCastException e) {
+                unavailable(null);
+                // Restore the context ClassLoader
+                throw new ServletException
+                    (sm.getString("standardWrapper.notServlet", actualClass), e);
+            } catch (Throwable e) {
+                unavailable(null);
+                // Restore the context ClassLoader
+                throw new ServletException
+                    (sm.getString("standardWrapper.instantiate", actualClass), e);
+            }
+
+            // Check if loading the servlet in this web application should be
+            // allowed
+            if (!isServletAllowed(servlet)) {
+                throw new SecurityException
+                    (sm.getString("standardWrapper.privilegedServlet",
+                                  actualClass));
+            }
+
+            // Special handling for ContainerServlet instances
+            if ((servlet instanceof ContainerServlet) &&
+                  (isContainerProvidedServlet(actualClass) ||
+                    ((Context)getParent()).getPrivileged() )) {
+                ((ContainerServlet) servlet).setWrapper(this);
+            }
+
+            classLoadTime=(int) (System.currentTimeMillis() -t1);
+            // Call the initialization method of this servlet
+            try {
+                instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
+                                                  servlet);
+
+                if( System.getSecurityManager() != null) {
+
+                    Object[] args = new Object[]{((ServletConfig)facade)};
+                    SecurityUtil.doAsPrivilege("init",
+                                               servlet,
+                                               classType,
+                                               args);
+                    args = null;
+                } else {
+                    servlet.init(facade);
+                }
+
+                // Invoke jspInit on JSP pages
+                if ((loadOnStartup >= 0) && (jspFile != null)) {
+                    // Invoking jspInit
+                    DummyRequest req = new DummyRequest();
+                    req.setServletPath(jspFile);
+                    req.setQueryString("jsp_precompile=true");
+                    DummyResponse res = new DummyResponse();
+
+                    if( System.getSecurityManager() != null) {
+                        Object[] args = new Object[]{req, res};
+                        SecurityUtil.doAsPrivilege("service",
+                                                   servlet,
+                                                   classTypeUsedInService,
+                                                   args);
+                        args = null;
+                    } else {
+                        servlet.service(req, res);
+                    }
+                }
+                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
+                                                  servlet);
+            } catch (UnavailableException f) {
+                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
+                                                  servlet, f);
+                unavailable(f);
+                throw f;
+            } catch (ServletException f) {
+                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
+                                                  servlet, f);
+                // If the servlet wanted to be unavailable it would have
+                // said so, so do not call unavailable(null).
+                throw f;
+            } catch (Throwable f) {
+                getServletContext().log("StandardWrapper.Throwable", f );
+                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
+                                                  servlet, f);
+                // If the servlet wanted to be unavailable it would have
+                // said so, so do not call unavailable(null).
+                throw new ServletException
+                    (sm.getString("standardWrapper.initException", getName()), f);
+            }
+
+            // Register our newly initialized instance
+            singleThreadModel = servlet instanceof SingleThreadModel;
+            if (singleThreadModel) {
+                if (instancePool == null)
+                    instancePool = new Stack();
+            }
+            fireContainerEvent("load", this);
+
+            loadTime=System.currentTimeMillis() -t1;
+        } finally {
+            if (swallowOutput) {
+                String log = SystemLogHandler.stopCapture();
+                if (log != null && log.length() > 0) {
+                    if (getServletContext() != null) {
+                        getServletContext().log(log);
+                    } else {
+                        out.println(log);
+                    }
+                }
+            }
+        }
+        return servlet;
+
+    }
+
+
+    /**
+     * Remove the specified initialization parameter from this servlet.
+     *
+     * @param name Name of the initialization parameter to remove
+     */
+    public void removeInitParameter(String name) {
+
+        synchronized (parameters) {
+            parameters.remove(name);
+        }
+        fireContainerEvent("removeInitParameter", name);
+
+    }
+
+
+    /**
+     * Remove a listener no longer interested in InstanceEvents.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeInstanceListener(InstanceListener listener) {
+
+        instanceSupport.removeInstanceListener(listener);
+
+    }
+
+
+    /**
+     * Remove a mapping associated with the wrapper.
+     *
+     * @param mapping The pattern to remove
+     */
+    public void removeMapping(String mapping) {
+
+        synchronized (mappings) {
+            mappings.remove(mapping);
+        }
+        fireContainerEvent("removeMapping", mapping);
+
+    }
+
+
+    /**
+     * Remove any security role reference for the specified role name.
+     *
+     * @param name Security role used within this servlet to be removed
+     */
+    public void removeSecurityReference(String name) {
+
+        synchronized (references) {
+            references.remove(name);
+        }
+        fireContainerEvent("removeSecurityReference", name);
+
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer();
+        if (getParent() != null) {
+            sb.append(getParent().toString());
+            sb.append(".");
+        }
+        sb.append("StandardWrapper[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    /**
+     * Process an UnavailableException, marking this servlet as unavailable
+     * for the specified amount of time.
+     *
+     * @param unavailable The exception that occurred, or <code>null</code>
+     *  to mark this servlet as permanently unavailable
+     */
+    public void unavailable(UnavailableException unavailable) {
+        getServletContext().log(sm.getString("standardWrapper.unavailable", getName()));
+        if (unavailable == null)
+            setAvailable(Long.MAX_VALUE);
+        else if (unavailable.isPermanent())
+            setAvailable(Long.MAX_VALUE);
+        else {
+            int unavailableSeconds = unavailable.getUnavailableSeconds();
+            if (unavailableSeconds <= 0)
+                unavailableSeconds = 60;        // Arbitrary default
+            setAvailable(System.currentTimeMillis() +
+                         (unavailableSeconds * 1000L));
+        }
+
+    }
+
+
+    /**
+     * Unload all initialized instances of this servlet, after calling the
+     * <code>destroy()</code> method for each instance.  This can be used,
+     * for example, prior to shutting down the entire servlet engine, or
+     * prior to reloading all of the classes from the Loader associated with
+     * our Loader's repository.
+     *
+     * @exception ServletException if an exception is thrown by the
+     *  destroy() method
+     */
+    public synchronized void unload() throws ServletException {
+
+        // Nothing to do if we have never loaded the instance
+        if (!singleThreadModel && (instance == null))
+            return;
+        unloading = true;
+
+        // Loaf a while if the current instance is allocated
+        // (possibly more than once if non-STM)
+        if (countAllocated > 0) {
+            int nRetries = 0;
+            while ((nRetries < 21) && (countAllocated > 0)) {
+                if ((nRetries % 10) == 0) {
+                    log.info(sm.getString("standardWrapper.waiting",
+                                          new Integer(countAllocated)));
+                }
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    ;
+                }
+                nRetries++;
+            }
+        }
+
+        ClassLoader oldCtxClassLoader =
+            Thread.currentThread().getContextClassLoader();
+        ClassLoader classLoader = instance.getClass().getClassLoader();
+
+        PrintStream out = System.out;
+        if (swallowOutput) {
+            SystemLogHandler.startCapture();
+        }
+
+        // Call the servlet destroy() method
+        try {
+            instanceSupport.fireInstanceEvent
+              (InstanceEvent.BEFORE_DESTROY_EVENT, instance);
+
+            Thread.currentThread().setContextClassLoader(classLoader);
+            if( System.getSecurityManager() != null) {
+                SecurityUtil.doAsPrivilege("destroy",
+                                           instance);
+                SecurityUtil.remove(instance);                           
+            } else {
+                instance.destroy();
+            }
+
+            instanceSupport.fireInstanceEvent
+              (InstanceEvent.AFTER_DESTROY_EVENT, instance);
+        } catch (Throwable t) {
+            instanceSupport.fireInstanceEvent
+              (InstanceEvent.AFTER_DESTROY_EVENT, instance, t);
+            instance = null;
+            instancePool = null;
+            nInstances = 0;
+            fireContainerEvent("unload", this);
+            unloading = false;
+            throw new ServletException
+                (sm.getString("standardWrapper.destroyException", getName()),
+                 t);
+        } finally {
+            // restore the context ClassLoader
+            Thread.currentThread().setContextClassLoader(oldCtxClassLoader);
+            // Write captured output
+            if (swallowOutput) {
+                String log = SystemLogHandler.stopCapture();
+                if (log != null && log.length() > 0) {
+                    if (getServletContext() != null) {
+                        getServletContext().log(log);
+                    } else {
+                        out.println(log);
+                    }
+                }
+            }
+        }
+
+        // Deregister the destroyed instance
+        instance = null;
+
+        if (singleThreadModel && (instancePool != null)) {
+            try {
+                Thread.currentThread().setContextClassLoader(classLoader);
+                while (!instancePool.isEmpty()) {
+                    if( System.getSecurityManager() != null) {
+                        SecurityUtil.doAsPrivilege("destroy",
+                                                   ((Servlet) instancePool.pop()));
+                        SecurityUtil.remove(instance);                           
+                    } else {
+                        ((Servlet) instancePool.pop()).destroy();
+                    }
+                }
+            } catch (Throwable t) {
+                instancePool = null;
+                nInstances = 0;
+                unloading = false;
+                fireContainerEvent("unload", this);
+                throw new ServletException
+                    (sm.getString("standardWrapper.destroyException",
+                                  getName()), t);
+            } finally {
+                // restore the context ClassLoader
+                Thread.currentThread().setContextClassLoader
+                    (oldCtxClassLoader);
+            }
+            instancePool = null;
+            nInstances = 0;
+        }
+
+        singleThreadModel = false;
+
+        unloading = false;
+        fireContainerEvent("unload", this);
+
+    }
+
+
+    // -------------------------------------------------- ServletConfig Methods
+
+
+    /**
+     * Return the initialization parameter value for the specified name,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the initialization parameter to retrieve
+     */
+    public String getInitParameter(String name) {
+
+        return (findInitParameter(name));
+
+    }
+
+
+    /**
+     * Return the set of initialization parameter names defined for this
+     * servlet.  If none are defined, an empty Enumeration is returned.
+     */
+    public Enumeration getInitParameterNames() {
+
+        synchronized (parameters) {
+            return (new Enumerator(parameters.keySet()));
+        }
+
+    }
+
+
+    /**
+     * Return the servlet context with which this servlet is associated.
+     */
+    public ServletContext getServletContext() {
+
+        if (parent == null)
+            return (null);
+        else if (!(parent instanceof Context))
+            return (null);
+        else
+            return (((Context) parent).getServletContext());
+
+    }
+
+
+    /**
+     * Return the name of this servlet.
+     */
+    public String getServletName() {
+
+        return (getName());
+
+    }
+
+    public long getProcessingTime() {
+        return swValve.getProcessingTime();
+    }
+
+    public void setProcessingTime(long processingTime) {
+        swValve.setProcessingTime(processingTime);
+    }
+
+    public long getMaxTime() {
+        return swValve.getMaxTime();
+    }
+
+    public void setMaxTime(long maxTime) {
+        swValve.setMaxTime(maxTime);
+    }
+
+    public long getMinTime() {
+        return swValve.getMinTime();
+    }
+
+    public void setMinTime(long minTime) {
+        swValve.setMinTime(minTime);
+    }
+
+    public int getRequestCount() {
+        return swValve.getRequestCount();
+    }
+
+    public void setRequestCount(int requestCount) {
+        swValve.setRequestCount(requestCount);
+    }
+
+    public int getErrorCount() {
+        return swValve.getErrorCount();
+    }
+
+    public void setErrorCount(int errorCount) {
+           swValve.setErrorCount(errorCount);
+    }
+
+    /**
+     * Increment the error count used for monitoring.
+     */
+    public void incrementErrorCount(){
+        swValve.setErrorCount(swValve.getErrorCount() + 1);
+    }
+
+    public long getLoadTime() {
+        return loadTime;
+    }
+
+    public void setLoadTime(long loadTime) {
+        this.loadTime = loadTime;
+    }
+
+    public int getClassLoadTime() {
+        return classLoadTime;
+    }
+
+    // -------------------------------------------------------- Package Methods
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Add a default Mapper implementation if none have been configured
+     * explicitly.
+     *
+     * @param mapperClass Java class name of the default Mapper
+     */
+    protected void addDefaultMapper(String mapperClass) {
+
+        ;       // No need for a default Mapper on a Wrapper
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified class name represents a
+     * container provided servlet class that should be loaded by the
+     * server class loader.
+     *
+     * @param classname Name of the class to be checked
+     */
+    private boolean isContainerProvidedServlet(String classname) {
+
+        if (classname.startsWith("org.apache.catalina.")) {
+            return (true);
+        }
+        try {
+            Class clazz =
+                this.getClass().getClassLoader().loadClass(classname);
+            return (ContainerServlet.class.isAssignableFrom(clazz));
+        } catch (Throwable t) {
+            return (false);
+        }
+
+    }
+
+
+    /**
+     * Return <code>true</code> if loading this servlet is allowed.
+     */
+    private boolean isServletAllowed(Object servlet) {
+
+        if (servlet instanceof ContainerServlet) {
+            if (((Context) getParent()).getPrivileged()
+                || (servlet.getClass().getName().equals
+                    ("org.apache.catalina.servlets.InvokerServlet"))) {
+                return (true);
+            } else {
+                return (false);
+            }
+        }
+
+        return (true);
+
+    }
+
+
+    private Method[] getAllDeclaredMethods(Class c) {
+
+        if (c.equals(javax.servlet.http.HttpServlet.class)) {
+            return null;
+        }
+
+        Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
+
+        Method[] thisMethods = c.getDeclaredMethods();
+        if (thisMethods == null) {
+            return parentMethods;
+        }
+
+        if ((parentMethods != null) && (parentMethods.length > 0)) {
+            Method[] allMethods =
+                new Method[parentMethods.length + thisMethods.length];
+	    System.arraycopy(parentMethods, 0, allMethods, 0,
+                             parentMethods.length);
+	    System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
+                             thisMethods.length);
+
+	    thisMethods = allMethods;
+	}
+
+	return thisMethods;
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Start this component, pre-loading the servlet if the load-on-startup
+     * value is set appropriately.
+     *
+     * @exception LifecycleException if a fatal error occurs during startup
+     */
+    public void start() throws LifecycleException {
+    
+        // Send j2ee.state.starting notification 
+        if (this.getObjectName() != null) {
+            Notification notification = new Notification("j2ee.state.starting", 
+                                                        this.getObjectName(), 
+                                                        sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        }
+        
+        // Start up this component
+        super.start();
+
+        if( oname != null )
+            registerJMX((StandardContext)getParent());
+        
+        // Load and initialize an instance of this servlet if requested
+        // MOVED TO StandardContext START() METHOD
+
+        setAvailable(0L);
+        
+        // Send j2ee.state.running notification 
+        if (this.getObjectName() != null) {
+            Notification notification = 
+                new Notification("j2ee.state.running", this.getObjectName(), 
+                                sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        }
+
+    }
+
+
+    /**
+     * Stop this component, gracefully shutting down the servlet if it has
+     * been initialized.
+     *
+     * @exception LifecycleException if a fatal error occurs during shutdown
+     */
+    public void stop() throws LifecycleException {
+
+        setAvailable(Long.MAX_VALUE);
+        
+        // Send j2ee.state.stopping notification 
+        if (this.getObjectName() != null) {
+            Notification notification = 
+                new Notification("j2ee.state.stopping", this.getObjectName(), 
+                                sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        }
+        
+        // Shut down our servlet instance (if it has been initialized)
+        try {
+            unload();
+        } catch (ServletException e) {
+            getServletContext().log(sm.getString
+                      ("standardWrapper.unloadException", getName()), e);
+        }
+
+        // Shut down this component
+        super.stop();
+
+        // Send j2ee.state.stoppped notification 
+        if (this.getObjectName() != null) {
+            Notification notification = 
+                new Notification("j2ee.state.stopped", this.getObjectName(), 
+                                sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        }
+        
+        if( oname != null ) {
+            Registry.getRegistry(null, null).unregisterComponent(oname);
+            
+            // Send j2ee.object.deleted notification 
+            Notification notification = 
+                new Notification("j2ee.object.deleted", this.getObjectName(), 
+                                sequenceNumber++);
+            broadcaster.sendNotification(notification);
+        }
+
+        if (isJspServlet && jspMonitorON != null ) {
+            Registry.getRegistry(null, null).unregisterComponent(jspMonitorON);
+        }
+
+    }
+
+    protected void registerJMX(StandardContext ctx) {
+
+        String parentName = ctx.getName();
+        parentName = ("".equals(parentName)) ? "/" : parentName;
+
+        String hostName = ctx.getParent().getName();
+        hostName = (hostName==null) ? "DEFAULT" : hostName;
+
+        String domain = ctx.getDomain();
+
+        String webMod= "//" + hostName + parentName;
+        String onameStr = domain + ":j2eeType=Servlet,name=" + getName() +
+                          ",WebModule=" + webMod + ",J2EEApplication=" +
+                          ctx.getJ2EEApplication() + ",J2EEServer=" +
+                          ctx.getJ2EEServer();
+        try {
+            oname=new ObjectName(onameStr);
+            controller=oname;
+            Registry.getRegistry(null, null)
+                .registerComponent(this, oname, null );
+            
+            // Send j2ee.object.created notification 
+            if (this.getObjectName() != null) {
+                Notification notification = new Notification(
+                                                "j2ee.object.created", 
+                                                this.getObjectName(), 
+                                                sequenceNumber++);
+                broadcaster.sendNotification(notification);
+            }
+        } catch( Exception ex ) {
+            log.info("Error registering servlet with jmx " + this);
+        }
+
+        if (isJspServlet) {
+            // Register JSP monitoring mbean
+            onameStr = domain + ":type=JspMonitor,name=" + getName()
+                       + ",WebModule=" + webMod
+                       + ",J2EEApplication=" + ctx.getJ2EEApplication()
+                       + ",J2EEServer=" + ctx.getJ2EEServer();
+            try {
+                jspMonitorON = new ObjectName(onameStr);
+                Registry.getRegistry(null, null)
+                    .registerComponent(instance, jspMonitorON, null);
+            } catch( Exception ex ) {
+                log.info("Error registering JSP monitoring with jmx " +
+                         instance);
+            }
+        }
+    }
+    
+
+    /* Remove a JMX notficationListener 
+     * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
+     */
+    public void removeNotificationListener(NotificationListener listener, 
+    		NotificationFilter filter, Object object) throws ListenerNotFoundException {
+    	broadcaster.removeNotificationListener(listener,filter,object);
+    	
+    }
+    
+    private MBeanNotificationInfo[] notificationInfo;
+    
+    /* Get JMX Broadcaster Info
+     * @TODO use StringManager for international support!
+     * @TODO This two events we not send j2ee.state.failed and j2ee.attribute.changed!
+     * @see javax.management.NotificationBroadcaster#getNotificationInfo()
+     */
+    public MBeanNotificationInfo[] getNotificationInfo() {
+    	
+    	if(notificationInfo == null) {
+    		notificationInfo = new MBeanNotificationInfo[]{
+    				new MBeanNotificationInfo(new String[] {
+    				"j2ee.object.created"},
+					Notification.class.getName(),
+					"servlet is created"
+    				), 
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.state.starting"},
+					Notification.class.getName(),
+					"servlet is starting"
+					),
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.state.running"},
+					Notification.class.getName(),
+					"servlet is running"
+					),
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.state.stopped"},
+					Notification.class.getName(),
+					"servlet start to stopped"
+					),
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.object.stopped"},
+					Notification.class.getName(),
+					"servlet is stopped"
+					),
+					new MBeanNotificationInfo(new String[] {
+					"j2ee.object.deleted"},
+					Notification.class.getName(),
+					"servlet is deleted"
+					)
+    		};
+    		
+    	}
+    	
+    	return notificationInfo;
+    }
+    
+    
+    /* Add a JMX-NotificationListener
+     * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
+     */
+    public void addNotificationListener(NotificationListener listener, 
+            NotificationFilter filter, Object object) throws IllegalArgumentException {
+    	broadcaster.addNotificationListener(listener,filter,object);
+    }
+    
+    
+    /**
+     * Remove a JMX-NotificationListener 
+     * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener)
+     */
+    public void removeNotificationListener(NotificationListener listener) 
+        throws ListenerNotFoundException {
+    	broadcaster.removeNotificationListener(listener);
+    }
+    
+    
+     // ------------------------------------------------------------- Attributes
+        
+        
+    public boolean isEventProvider() {
+        return false;
+    }
+    
+    public boolean isStateManageable() {
+        return false;
+    }
+    
+    public boolean isStatisticsProvider() {
+        return false;
+    }
+        
+        
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardWrapperFacade.java b/container/catalina/src/share/org/apache/catalina/core/StandardWrapperFacade.java
new file mode 100644
index 0000000..1dbc155
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardWrapperFacade.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+
+/**
+ * Facade for the <b>StandardWrapper</b> object.
+ *
+ * @author Remy Maucharat
+ * @version $Revision$ $Date$
+ */
+
+public final class StandardWrapperFacade
+    implements ServletConfig {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new facede around a StandardWrapper.
+     */
+    public StandardWrapperFacade(StandardWrapper config) {
+
+        super();
+        this.config = (ServletConfig) config;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Wrapped config.
+     */
+    private ServletConfig config = null;
+
+
+    // -------------------------------------------------- ServletConfig Methods
+
+
+    public String getServletName() {
+        return config.getServletName();
+    }
+
+
+    public ServletContext getServletContext() {
+        ServletContext theContext = config.getServletContext();
+        if ((theContext != null) &&
+            (theContext instanceof ApplicationContext))
+            theContext = ((ApplicationContext) theContext).getFacade();
+        return (theContext);
+    }
+
+
+    public String getInitParameter(String name) {
+        return config.getInitParameter(name);
+    }
+
+
+    public Enumeration getInitParameterNames() {
+        return config.getInitParameterNames();
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/StandardWrapperValve.java b/container/catalina/src/share/org/apache/catalina/core/StandardWrapperValve.java
new file mode 100644
index 0000000..b4e56d4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/StandardWrapperValve.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.core;
+
+
+import java.io.IOException;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.connector.ClientAbortException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.log.SystemLogHandler;
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardWrapper</code> container implementation.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+final class StandardWrapperValve
+    extends ValveBase {
+
+    private static Log log = LogFactory.getLog(StandardWrapperValve.class);
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // Some JMX statistics. This vavle is associated with a StandardWrapper.
+    // We exponse the StandardWrapper as JMX ( j2eeType=Servlet ). The fields
+    // are here for performance.
+    private volatile long processingTime;
+    private volatile long maxTime;
+    private volatile long minTime = Long.MAX_VALUE;
+    private volatile int requestCount;
+    private volatile int errorCount;
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Invoke the servlet we are managing, respecting the rules regarding
+     * servlet lifecycle and SingleThreadModel support.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     * @param valveContext Valve context used to forward to the next Valve
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+    public final void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // Initialize local variables we may need
+        boolean unavailable = false;
+        Throwable throwable = null;
+        // This should be a Request attribute...
+        long t1=System.currentTimeMillis();
+        requestCount++;
+        StandardWrapper wrapper = (StandardWrapper) getContainer();
+        Servlet servlet = null;
+        Context context = (Context) wrapper.getParent();
+        
+        // Check for the application being marked unavailable
+        if (!context.getAvailable()) {
+        	response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+                           sm.getString("standardContext.isUnavailable"));
+            unavailable = true;
+        }
+
+        // Check for the servlet being marked unavailable
+        if (!unavailable && wrapper.isUnavailable()) {
+            container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
+                    wrapper.getName()));
+            long available = wrapper.getAvailable();
+            if ((available > 0L) && (available < Long.MAX_VALUE)) {
+                response.setDateHeader("Retry-After", available);
+                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+                        sm.getString("standardWrapper.isUnavailable",
+                                wrapper.getName()));
+            } else if (available == Long.MAX_VALUE) {
+                response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                        sm.getString("standardWrapper.notFound",
+                                wrapper.getName()));
+            }
+            unavailable = true;
+        }
+
+        // Allocate a servlet instance to process this request
+        try {
+            if (!unavailable) {
+                servlet = wrapper.allocate();
+            }
+        } catch (UnavailableException e) {
+            long available = wrapper.getAvailable();
+            if ((available > 0L) && (available < Long.MAX_VALUE)) {
+            	response.setDateHeader("Retry-After", available);
+            	response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+                           sm.getString("standardWrapper.isUnavailable",
+                                        wrapper.getName()));
+            } else if (available == Long.MAX_VALUE) {
+            	response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                           sm.getString("standardWrapper.notFound",
+                                        wrapper.getName()));
+            }
+        } catch (ServletException e) {
+            container.getLogger().error(sm.getString("standardWrapper.allocateException",
+                             wrapper.getName()), StandardWrapper.getRootCause(e));
+            throwable = e;
+            exception(request, response, e);
+            servlet = null;
+        } catch (Throwable e) {
+            container.getLogger().error(sm.getString("standardWrapper.allocateException",
+                             wrapper.getName()), e);
+            throwable = e;
+            exception(request, response, e);
+            servlet = null;
+        }
+
+        // Acknowlege the request
+        try {
+            response.sendAcknowledgement();
+        } catch (IOException e) {
+        	request.removeAttribute(Globals.JSP_FILE_ATTR);
+            container.getLogger().warn(sm.getString("standardWrapper.acknowledgeException",
+                             wrapper.getName()), e);
+            throwable = e;
+            exception(request, response, e);
+        } catch (Throwable e) {
+            container.getLogger().error(sm.getString("standardWrapper.acknowledgeException",
+                             wrapper.getName()), e);
+            throwable = e;
+            exception(request, response, e);
+            servlet = null;
+        }
+        MessageBytes requestPathMB = null;
+        if (request != null) {
+            requestPathMB = request.getRequestPathMB();
+        }
+        request.setAttribute
+            (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+             ApplicationFilterFactory.REQUEST_INTEGER);
+        request.setAttribute
+            (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
+             requestPathMB);
+        // Create the filter chain for this request
+        ApplicationFilterFactory factory =
+            ApplicationFilterFactory.getInstance();
+        ApplicationFilterChain filterChain =
+            factory.createFilterChain(request, wrapper, servlet);
+
+        // Call the filter chain for this request
+        // NOTE: This also calls the servlet's service() method
+        try {
+            String jspFile = wrapper.getJspFile();
+            if (jspFile != null)
+            	request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
+            else
+            	request.removeAttribute(Globals.JSP_FILE_ATTR);
+            if ((servlet != null) && (filterChain != null)) {
+
+                // Swallow output if needed
+                if (context.getSwallowOutput()) {
+                    try {
+                        SystemLogHandler.startCapture();
+                        filterChain.doFilter(request.getRequest(), 
+                                response.getResponse());
+                    } finally {
+                        String log = SystemLogHandler.stopCapture();
+                        if (log != null && log.length() > 0) {
+                            context.getLogger().info(log);
+                        }
+                    }
+                } else {
+                    filterChain.doFilter
+                        (request.getRequest(), response.getResponse());
+                }
+
+            }
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+        } catch (ClientAbortException e) {
+        	request.removeAttribute(Globals.JSP_FILE_ATTR);
+            throwable = e;
+            exception(request, response, e);
+        } catch (IOException e) {
+        	request.removeAttribute(Globals.JSP_FILE_ATTR);
+            container.getLogger().warn(sm.getString("standardWrapper.serviceException",
+                             wrapper.getName()), e);
+            throwable = e;
+            exception(request, response, e);
+        } catch (UnavailableException e) {
+        	request.removeAttribute(Globals.JSP_FILE_ATTR);
+            container.getLogger().warn(sm.getString("standardWrapper.serviceException",
+                             wrapper.getName()), e);
+            //            throwable = e;
+            //            exception(request, response, e);
+            wrapper.unavailable(e);
+            long available = wrapper.getAvailable();
+            if ((available > 0L) && (available < Long.MAX_VALUE)) {
+                response.setDateHeader("Retry-After", available);
+                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+                           sm.getString("standardWrapper.isUnavailable",
+                                        wrapper.getName()));
+            } else if (available == Long.MAX_VALUE) {
+            	response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                            sm.getString("standardWrapper.notFound",
+                                        wrapper.getName()));
+            }
+            // Do not save exception in 'throwable', because we
+            // do not want to do exception(request, response, e) processing
+        } catch (ServletException e) {
+        	request.removeAttribute(Globals.JSP_FILE_ATTR);
+            Throwable rootCause = StandardWrapper.getRootCause(e);
+            if (!(rootCause instanceof ClientAbortException)) {
+                container.getLogger().error(sm.getString("standardWrapper.serviceException",
+                                 wrapper.getName()), rootCause);
+            }
+            throwable = e;
+            exception(request, response, e);
+        } catch (Throwable e) {
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            container.getLogger().error(sm.getString("standardWrapper.serviceException",
+                             wrapper.getName()), e);
+            throwable = e;
+            exception(request, response, e);
+        }
+
+        // Release the filter chain (if any) for this request
+        try {
+            if (filterChain != null)
+                filterChain.release();
+        } catch (Throwable e) {
+            container.getLogger().error(sm.getString("standardWrapper.releaseFilters",
+                             wrapper.getName()), e);
+            if (throwable == null) {
+                throwable = e;
+                exception(request, response, e);
+            }
+        }
+
+        // Deallocate the allocated servlet instance
+        try {
+            if (servlet != null) {
+                wrapper.deallocate(servlet);
+            }
+        } catch (Throwable e) {
+            container.getLogger().error(sm.getString("standardWrapper.deallocateException",
+                             wrapper.getName()), e);
+            if (throwable == null) {
+                throwable = e;
+                exception(request, response, e);
+            }
+        }
+
+        // If this servlet has been marked permanently unavailable,
+        // unload it and release this instance
+        try {
+            if ((servlet != null) &&
+                (wrapper.getAvailable() == Long.MAX_VALUE)) {
+                wrapper.unload();
+            }
+        } catch (Throwable e) {
+            container.getLogger().error(sm.getString("standardWrapper.unloadException",
+                             wrapper.getName()), e);
+            if (throwable == null) {
+                throwable = e;
+                exception(request, response, e);
+            }
+        }
+        long t2=System.currentTimeMillis();
+
+        long time=t2-t1;
+        processingTime += time;
+        if( time > maxTime) maxTime=time;
+        if( time < minTime) minTime=time;
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Handle the specified ServletException encountered while processing
+     * the specified Request to produce the specified Response.  Any
+     * exceptions that occur during generation of the exception report are
+     * logged and swallowed.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     * @param exception The exception that occurred (which possibly wraps
+     *  a root cause exception
+     */
+    private void exception(Request request, Response response,
+                           Throwable exception) {
+    	request.setAttribute(Globals.EXCEPTION_ATTR, exception);
+        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+
+    }
+
+    public long getProcessingTime() {
+        return processingTime;
+    }
+
+    public void setProcessingTime(long processingTime) {
+        this.processingTime = processingTime;
+    }
+
+    public long getMaxTime() {
+        return maxTime;
+    }
+
+    public void setMaxTime(long maxTime) {
+        this.maxTime = maxTime;
+    }
+
+    public long getMinTime() {
+        return minTime;
+    }
+
+    public void setMinTime(long minTime) {
+        this.minTime = minTime;
+    }
+
+    public int getRequestCount() {
+        return requestCount;
+    }
+
+    public void setRequestCount(int requestCount) {
+        this.requestCount = requestCount;
+    }
+
+    public int getErrorCount() {
+        return errorCount;
+    }
+
+    public void setErrorCount(int errorCount) {
+        this.errorCount = errorCount;
+    }
+
+    // Don't register in JMX
+
+    public ObjectName createObjectName(String domain, ObjectName parent)
+            throws MalformedObjectNameException
+    {
+        return null;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/core/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/core/mbeans-descriptors.xml
new file mode 100644
index 0000000..edb8310
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/core/mbeans-descriptors.xml
@@ -0,0 +1,655 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean name="NamingContextListener"
+         description="Helper class used to initialize and populate the JNDI context associated with each context and server"
+         domain="Catalina"
+         group="Listener"
+         type="org.apache.catalina.core.NamingContextListener">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+  </mbean>
+
+  <mbean name="StandardContext"
+         description="Standard Context Component"
+         domain="Catalina"
+         group="Context"
+         type="org.apache.catalina.core.StandardContext">
+    
+    <attribute name="allowLinking"
+               description="Allow symlinking to outside the webapp root directory, if the webapp is an exploded directory"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="antiJARLocking"
+               description="Take care to not lock jar files"
+               type="boolean" />
+
+    <attribute name="antiResourceLocking"
+               description="Take care to not lock resources"
+               type="boolean" />
+
+    <attribute name="cacheMaxSize"
+               description="Maximum cache size in KB"
+               type="int"/>
+      
+    <attribute name="cacheTTL"
+               description="Time interval in ms between cache refeshes"
+               type="int"/>
+      
+    <attribute name="cachingAllowed"
+               description="Should we cache static resources for this webapp"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="caseSensitive"
+               description="Should case sensitivity checks be performed"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="children"
+               description="Object names of all children"
+               type="[Ljavax.management.ObjectName;"/>
+               
+    <attribute name="configFile"
+               description="Location of the context.xml resource or file"
+               type="java.lang.String"/>
+               
+    <attribute   name="cookies"
+               description="Should we attempt to use cookies for session id
+               communication?"
+               type="boolean"/>
+      
+    <attribute name="compilerClasspath"
+               description="The compiler classpath to use"
+               type="java.lang.String"/>
+      
+    <attribute name="crossContext"
+               description="Should we allow the ServletContext.getContext() method to access the context of other web applications in this server?"
+               type="boolean"/>
+
+    <attribute name="defaultContextXml"
+               description="Location of the default context.xml resource or file"
+               type="java.lang.String"/>
+               
+    <attribute name="defaultWebXml"
+               description="Location of the default web.xml resource or file"
+               type="java.lang.String"/>
+               
+    <attribute name="delegate"
+               description=""
+               type="boolean"/>
+               
+    <attribute name="deploymentDescriptor"
+               description="String deployment descriptor "
+               type="java.lang.String"/>
+                     
+    <attribute name="docBase"
+               description="The document root for this web application"
+               type="java.lang.String"/>
+      
+    <attribute name="engineName"
+               description="Name of the engine domain, if different from the context domain"
+               type="java.lang.String"/>
+
+    <attribute name="eventProvider"
+               description="Event provider support for this managed object"
+               is="true"
+               type="boolean"/>
+                              
+    <attribute name="javaVMs"
+               description="The Java virtual machines on which this module is running"
+               type="[Ljava.lang.String;"/>
+
+    <attribute name="loader"
+               description="Associated loader."
+               type="org.apache.catalina.Loader" />
+      
+    <attribute name="logger"
+               description="Associated logger."
+               type="org.apache.commons.logging.Log" />
+      
+    <attribute name="managedResource"
+               description="The managed resource this MBean is associated with"
+               type="java.lang.Object"/>
+      
+    <attribute name="manager"
+               description="Associated manager."
+               type="org.apache.catalina.Manager" />
+      
+    <attribute name="mappingObject"
+               description="The object used for mapping"
+               type="java.lang.Object"/>
+      
+    <attribute name="objectName"
+               description="Name of the object"
+               type="java.lang.String"/>
+      
+    <attribute name="override"
+               description="The DefaultContext override flag for this web application"
+               type="boolean"/>
+      
+    <attribute name="parentClassLoader"
+               description="Parent class loader."
+               type="java.lang.ClassLoader" />
+      
+    <attribute name="path"
+               description="The context path for this Context"
+               type="java.lang.String"/>
+               
+    <attribute name="privileged"
+               description="Access to tomcat internals"
+               type="boolean"/>
+      
+    <attribute name="realm"
+               description="Associated realm."
+               type="org.apache.catalina.Realm" />
+      
+    <attribute name="reloadable"
+               description="The reloadable flag for this web application"
+               type="boolean"/>
+
+    <attribute name="saveConfig"
+               description="Should the configuration be written as needed on startup"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="server"
+               description="The J2EE Server this module is deployed on"
+               type="java.lang.String"/>
+                              
+    <attribute name="servlets"
+               description="JSR77 list of servlets"
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+               
+    <attribute name="startupTime"
+               description="Time (in milliseconds) it took to start this context"
+               type="long"/>
+
+    <attribute name="startTime"
+               description="Time (in milliseconds since January 1, 1970, 00:00:00) when this context was started"
+               type="long"/>
+               
+    <attribute name="processingTime"
+               description="Cumulative execution times of all servlets in this context"
+               type="long"
+               writeable="false" />
+
+    <attribute name="state"
+               description="Current state of this component"
+               type="int"/>
+                     
+    <attribute name="stateManageable"
+               description="State management support for this managed object"
+               is="true"
+               type="boolean"/>
+               
+    <attribute name="statisticsProvider"
+               description="Performance statistics support for this managed object"
+               is="true"
+               type="boolean"/>
+               
+    <attribute name="staticResources"
+               description="Static resources associated with the context."
+               type="javax.naming.directory.DirContext"
+               writeable="false"/>
+      
+    <attribute   name="swallowOutput"
+               description="Flag to set to cause the system.out and system.err to be redirected to the logger when executing a servlet"
+               type="boolean"/>
+               
+    <attribute name="tldScanTime"
+               description="Time spend scanning jars for TLDs for this context"
+               type="long"/>
+
+    <attribute name="useNaming"
+               description="Create a JNDI naming context for this application?"
+               is="true"
+               type="boolean"/>
+               
+    <attribute name="valveObjectNames"
+               description="ObjectNames for the valves associated with this container"
+               type="[Ljavax.management.ObjectName;"
+               writeable="false"/>
+
+    <attribute name="welcomeFiles"
+               description="The welcome files for this context"
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+      
+    <attribute name="workDir"
+               description="The pathname to the work directory for this context"
+               type="java.lang.String"/>
+
+    <operation   name="addValve"
+               description="Add a valve to this Context"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="valve"
+                 description="New valve to be added"
+                 type="org.apache.catalina.Valve"/>
+    </operation>
+    
+    <operation   name="removeValve"
+               description="Remove a valve from this Context"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="valve"
+                 description="New valve to be removed"
+                 type="org.apache.catalina.Valve"/>
+    </operation>
+    
+    <operation   name="reload"
+               description="Reload the webapplication"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation   name="init"
+               description="Register the context into the running server"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation   name="start"
+               description="Start the context"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="stop"
+               description="Stop the context"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="destroy"
+               description="Destroy the context"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="findMappingObject"
+               description="Return an object which may be utilized for mapping to this component"
+               impact="INFO"
+               returnType="org.apache.catalina.Context">    
+    </operation>    
+    
+    <operation name="findStaticResources"
+               description="Return the naming resources associated with this web application"
+               impact="INFO"
+               returnType="javax.naming.directory.DirContext">    
+    </operation>
+    
+  </mbean>
+  
+  <mbean name="StandardContextValve"
+         description="Valve that implements the default basic behavior for the
+         StandardContext container implementation"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.core.StandardContextValve">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+  </mbean>
+  
+  <mbean name="StandardEngine"
+         type="org.apache.catalina.core.StandardEngine"
+         description="Standard Engine Component"
+         domain="Catalina"
+         group="Engine">
+    
+    <attribute name="defaultHost"
+               description="Name of the default Host for this Engine"
+               type="java.lang.String"/>
+      
+    <attribute   name="managedResource"
+               description="The managed resource this MBean is associated with"
+               type="java.lang.Object"/>
+      
+    <attribute   name="name"
+               description="Unique name of this Engine"
+               type="java.lang.String"/>
+      
+    <attribute   name="baseDir"
+               description="Base dir for this engine, typically same as catalina.base system property"
+               type="java.lang.String"/>
+      
+    <attribute   name="jvmRoute"
+               description="Route used for load balancing"
+               type="java.lang.String"/>
+      
+    <attribute name="realm"
+               description="Associated realm."
+               type="org.apache.catalina.Realm" />
+      
+    <attribute name="valveObjectNames"
+               description="ObjectNames for the valves associated with this container"
+               type="[Ljavax.management.ObjectName;"
+               writeable="false"/>
+
+    <operation name="addChild"
+               description="Add a virtual host"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="child"
+                 description="Host object"
+                 type="org.apache.catalina.Container"/>
+    </operation>
+    
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+      
+  </mbean>
+
+  
+  <mbean name="StandardEngineValve"
+         description="Valve that implements the default basic behavior for the
+         StandardEngine container implementation"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.core.StandardEngineValve">
+    
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+  </mbean>
+  
+  <mbean name="StandardHost"
+         description="Standard Host Component"
+         domain="Catalina"
+         group="Host"
+         type="org.apache.catalina.core.StandardHost">
+    
+    <attribute name="appBase"
+               description="The application root for this Host"
+               type="java.lang.String"/>
+      
+    <attribute name="autoDeploy"
+               description="The auto deploy flag for this Host"
+               type="boolean"/>
+                 
+    <attribute name="deployOnStartup"
+               description="The deploy on startup flag for this Host"
+               type="boolean"/>
+
+    <attribute name="deployXML"
+               description="deploy Context XML config files property"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="managedResource"
+               description="The managed resource this MBean is associated with"
+               type="java.lang.Object"/>
+      
+    <attribute name="name"
+               description="Unique name of this Host"
+               type="java.lang.String"/>
+      
+    <attribute name="unpackWARs"
+               description="Unpack WARs property"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="xmlNamespaceAware"
+               description="Attribute value used to turn on/off XML namespace awareness"
+               type="boolean"/>
+      
+    <attribute name="xmlValidation"
+               description="Attribute value used to turn on/off XML validation"
+               type="boolean"/>
+
+    <attribute name="children"
+               description="Object names of all children"
+               type="[Ljavax.management.ObjectName;"/>
+      
+    <attribute name="aliases"
+               description="Host aliases"
+               type="[Ljava.lang.String;"/>
+      
+    <attribute name="realm"
+               description="Associated realm."
+               type="org.apache.catalina.Realm" />
+      
+    <attribute name="valveNames"
+               description="Return the MBean Names of the Valves associated with this Host"
+               type="[Ljava.lang.String;"/>
+      
+    <attribute name="valveObjectNames"
+               description="Return the MBean ObjectNames of the Valves associated with this Host"
+               type="[Ljavax.management.ObjectName;"/>
+      
+    <operation name="addAlias"
+               description="Add an alias name that should be mapped to this Host"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="alias"
+                 description="The alias to be added"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation name="findAliases"
+               description="Return the set of alias names for this Host"
+               impact="INFO"
+               returnType="[Ljava.lang.String;"/>
+
+    <operation name="addChild"
+               description="Add a context"
+               impact="ACTION">
+      <parameter name="child"
+                 description="Context to be added"
+                 type="org.apache.catalina.Container"/>
+    </operation>
+      
+    <operation   name="removeAlias"
+               description="Remove the specified alias name from the aliases for this  Host"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="alias"
+                 description="Alias name to be removed"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+  
+  <mbean name="StandardHostValve"
+         description="Valve that implements the default basic behavior for the
+         StandardHost container implementation"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.core.StandardHostValve">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+  </mbean>
+
+  <mbean name="StandardServer"
+         description="Standard Server Component"
+         domain="Catalina"
+         group="Server"
+         type="org.apache.catalina.core.StandardServer">
+    
+    <attribute name="managedResource"
+               description="The managed resource this MBean is associated with"
+               type="java.lang.Object"/>
+      
+    <attribute name="port"
+               description="TCP port for shutdown messages"
+               type="int"/>
+      
+    <attribute name="shutdown"
+               description="Shutdown password"
+               type="java.lang.String"/>
+      
+    <attribute name="serviceNames"
+               description="Object names of all services we know about"
+               type="[Ljavax.management.ObjectName;"/>
+      
+    <operation name="await"
+               description="Wait for the shutdown message"
+               impact="ACTION"
+               returnType="void" />
+
+    <operation name="storeConfig"
+               description="Save current state to server.xml file"
+               impact="ACTION"
+               returnType="void">
+
+    </operation>
+    
+  </mbean>
+
+  
+  <mbean name="StandardService"
+         description="Standard Service Component"
+         domain="Catalina"
+         group="Service"
+         type="org.apache.catalina.core.StandardService">
+    
+    <attribute name="managedResource"
+               description="The managed resource this MBean is associated with"
+               type="java.lang.Object"/>
+      
+    <attribute name="name"
+               description="Unique name of this Service"
+               type="java.lang.String"/>
+      
+    <attribute name="connectorNames"
+               description="ObjectNames of the connectors"
+               type="[Ljavax.management.ObjectName;"
+               writeable="false" />
+      
+    <attribute name="container"
+               description="Servlet engine that will process the requests"
+               type="org.apache.catalina.Container" />
+
+    <attribute name="containerName"
+               description="ObjectNames of the engine"
+               type="javax.management.ObjectName"
+               writeable="false" />
+
+    <operation name="addConnector"
+               description="Add a new connector"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="connector"
+                 description="Connector object"
+                 type="org.apache.catalina.connector.Connector"/>
+    </operation>
+    
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+  </mbean>
+
+  <mbean name="StandardWrapper"
+         description="Wrapper that represents an individual servlet definition"
+         domain="Catalina"
+         group="Wrapper"
+         type="org.apache.catalina.core.StandardWrapper">
+               
+    <attribute name="engineName"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+               
+    <attribute name="eventProvider"
+               description="Event provider support for this managed object"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="objectName"
+               description="Name of the object"
+               type="java.lang.String"/>
+
+    <attribute name="stateManageable"
+               description="State management support for this managed object"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="statisticsProvider"
+               description="Performance statistics support for this managed object"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="processingTime"
+               description="Total execution time of the servlet's service method"
+               type="long"
+               writeable="false" />
+
+    <attribute name="maxTime"
+               description="Maximum processing time of a request"
+               type="long"
+               writeable="false" />
+
+    <attribute name="minTime"
+               description="Minimum processing time of a request"
+               type="long"
+               writeable="false" />
+
+    <attribute name="requestCount"
+               description="Number of requests processed by this wrapper"
+               type="int"
+               writeable="false" />
+
+    <attribute name="errorCount"
+               description="Error count"
+               type="int"
+               writeable="false" />
+
+    <attribute name="loadTime"
+               description="Load time"
+               type="long"
+               writeable="false" />
+
+    <attribute name="classLoadTime"
+               description="Class loading time"
+               type="int"
+               writeable="false" />
+
+    <operation name="findMappings"
+               description="Return the mappings associated with this wrapper"
+               impact="INFO"
+               returnType="[Ljava.lang.String;">
+    </operation>
+               
+    <operation name="findMappingObject"
+               description="Return an object which may be utilized for mapping to this component"
+               impact="INFO"
+               returnType="org.apache.catalina.Wrapper">
+    </operation>
+    
+  </mbean>
+  
+  <mbean name="StandardWrapperValve"
+         description="Valve that implements the default basic behavior for the
+         StandardWrapper container implementation"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.core.StandardWrapperValve">
+         
+     <attribute name="className"
+                description="Fully qualified class name of the managed object"
+                type="java.lang.String"
+                writeable="false"/>  
+                    
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ApplicationParameter.java b/container/catalina/src/share/org/apache/catalina/deploy/ApplicationParameter.java
new file mode 100644
index 0000000..c98c302
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ApplicationParameter.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a context initialization parameter that is configured
+ * in the server configuration file, rather than the application deployment
+ * descriptor.  This is convenient for establishing default values (which
+ * may be configured to allow application overrides or not) without having
+ * to modify the application deployment descriptor itself.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ApplicationParameter implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this environment entry.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The name of this application parameter.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * Does this application parameter allow overrides by the application
+     * deployment descriptor?
+     */
+    private boolean override = true;
+
+    public boolean getOverride() {
+        return (this.override);
+    }
+
+    public void setOverride(boolean override) {
+        this.override = override;
+    }
+
+
+    /**
+     * The value of this application parameter.
+     */
+    private String value = null;
+
+    public String getValue() {
+        return (this.value);
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ApplicationParameter[");
+        sb.append("name=");
+        sb.append(name);
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        sb.append(", value=");
+        sb.append(value);
+        sb.append(", override=");
+        sb.append(override);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ContextEjb.java b/container/catalina/src/share/org/apache/catalina/deploy/ContextEjb.java
new file mode 100644
index 0000000..71e66ef
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ContextEjb.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of an EJB resource reference for a web application, as
+ * represented in a <code>&lt;ejb-ref&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @author Peter Rossbach (pero@apache.org)
+ * @version $Revision$ $Date$
+ */
+
+public class ContextEjb extends ResourceBase implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+
+    /**
+     * The name of the EJB home implementation class.
+     */
+    private String home = null;
+
+    public String getHome() {
+        return (this.home);
+    }
+
+    public void setHome(String home) {
+        this.home = home;
+    }
+
+
+    /**
+     * The link to a J2EE EJB definition.
+     */
+    private String link = null;
+
+    public String getLink() {
+        return (this.link);
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+    /**
+     * The name of the EJB remote implementation class.
+     */
+    private String remote = null;
+
+    public String getRemote() {
+        return (this.remote);
+    }
+
+    public void setRemote(String remote) {
+        this.remote = remote;
+    }
+
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ContextEjb[");
+        sb.append("name=");
+        sb.append(getName());
+        if (getDescription() != null) {
+            sb.append(", description=");
+            sb.append(getDescription());
+        }
+        if (getType() != null) {
+            sb.append(", type=");
+            sb.append(getType());
+        }
+        if (home != null) {
+            sb.append(", home=");
+            sb.append(home);
+        }
+        if (remote != null) {
+            sb.append(", remote=");
+            sb.append(remote);
+        }
+        if (link != null) {
+            sb.append(", link=");
+            sb.append(link);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ContextEnvironment.java b/container/catalina/src/share/org/apache/catalina/deploy/ContextEnvironment.java
new file mode 100644
index 0000000..edab8d8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ContextEnvironment.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of an application environment entry, as represented in
+ * an <code>&lt;env-entry&gt;</code> element in the deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ContextEnvironment implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this environment entry.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The name of this environment entry.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * Does this environment entry allow overrides by the application
+     * deployment descriptor?
+     */
+    private boolean override = true;
+
+    public boolean getOverride() {
+        return (this.override);
+    }
+
+    public void setOverride(boolean override) {
+        this.override = override;
+    }
+
+
+    /**
+     * The type of this environment entry.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    /**
+     * The value of this environment entry.
+     */
+    private String value = null;
+
+    public String getValue() {
+        return (this.value);
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ContextEnvironment[");
+        sb.append("name=");
+        sb.append(name);
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        if (type != null) {
+            sb.append(", type=");
+            sb.append(type);
+        }
+        if (value != null) {
+            sb.append(", value=");
+            sb.append(value);
+        }
+        sb.append(", override=");
+        sb.append(override);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * The NamingResources with which we are associated (if any).
+     */
+    protected NamingResources resources = null;
+
+    public NamingResources getNamingResources() {
+        return (this.resources);
+    }
+
+    void setNamingResources(NamingResources resources) {
+        this.resources = resources;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ContextLocalEjb.java b/container/catalina/src/share/org/apache/catalina/deploy/ContextLocalEjb.java
new file mode 100644
index 0000000..6b03405
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ContextLocalEjb.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a local EJB resource reference for a web application, as
+ * represented in a <code>&lt;ejb-local-ref&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @author Peter Rossbach (pero@apache.org)
+ * @version $Revision$ $Date$
+ */
+
+public class ContextLocalEjb extends ResourceBase implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * The name of the EJB home implementation class.
+     */
+    private String home = null;
+
+    public String getHome() {
+        return (this.home);
+    }
+
+    public void setHome(String home) {
+        this.home = home;
+    }
+
+
+    /**
+     * The link to a J2EE EJB definition.
+     */
+    private String link = null;
+
+    public String getLink() {
+        return (this.link);
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+
+    /**
+     * The name of the EJB local implementation class.
+     */
+    private String local = null;
+
+    public String getLocal() {
+        return (this.local);
+    }
+
+    public void setLocal(String local) {
+        this.local = local;
+    }
+
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ContextLocalEjb[");
+        sb.append("name=");
+        sb.append(getName());
+        if (getDescription() != null) {
+            sb.append(", description=");
+            sb.append(getDescription());
+        }
+        if (getType() != null) {
+            sb.append(", type=");
+            sb.append(getType());
+        }
+        if (home != null) {
+            sb.append(", home=");
+            sb.append(home);
+        }
+        if (link != null) {
+            sb.append(", link=");
+            sb.append(link);
+        }
+        if (local != null) {
+            sb.append(", local=");
+            sb.append(local);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ContextResource.java b/container/catalina/src/share/org/apache/catalina/deploy/ContextResource.java
new file mode 100644
index 0000000..b6de5ee
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ContextResource.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+/**
+ * Representation of a resource reference for a web application, as
+ * represented in a <code>&lt;resource-ref&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @author Peter Rossbach (pero@apache.org)
+ * @version $Revision$ $Date$
+ */
+
+public class ContextResource extends ResourceBase implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The authorization requirement for this resource
+     * (<code>Application</code> or <code>Container</code>).
+     */
+    private String auth = null;
+
+    public String getAuth() {
+        return (this.auth);
+    }
+
+    public void setAuth(String auth) {
+        this.auth = auth;
+    }
+
+    /**
+     * The sharing scope of this resource factory (<code>Shareable</code>
+     * or <code>Unshareable</code>).
+     */
+    private String scope = "Shareable";
+
+    public String getScope() {
+        return (this.scope);
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ContextResource[");
+        sb.append("name=");
+        sb.append(getName());
+        if (getDescription() != null) {
+            sb.append(", description=");
+            sb.append(getDescription());
+        }
+        if (getType() != null) {
+            sb.append(", type=");
+            sb.append(getType());
+        }
+        if (auth != null) {
+            sb.append(", auth=");
+            sb.append(auth);
+        }
+        if (scope != null) {
+            sb.append(", scope=");
+            sb.append(scope);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ContextResourceEnvRef.java b/container/catalina/src/share/org/apache/catalina/deploy/ContextResourceEnvRef.java
new file mode 100644
index 0000000..df111f3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ContextResourceEnvRef.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of an application resource reference, as represented in
+ * an <code>&lt;res-env-refy&gt;</code> element in the deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @author Peter Rossbach (pero@apache.org)
+ * @version $Revision$ $Date$
+ */
+
+public class ContextResourceEnvRef extends ResourceBase implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Does this environment entry allow overrides by the application
+     * deployment descriptor?
+     */
+    private boolean override = true;
+
+    public boolean getOverride() {
+        return (this.override);
+    }
+
+    public void setOverride(boolean override) {
+        this.override = override;
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ContextResourceEnvRef[");
+        sb.append("name=");
+        sb.append(getName());
+        if (getType() != null) {
+            sb.append(", type=");
+            sb.append(getType());
+        }
+        sb.append(", override=");
+        sb.append(override);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ContextResourceLink.java b/container/catalina/src/share/org/apache/catalina/deploy/ContextResourceLink.java
new file mode 100644
index 0000000..ee5965f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ContextResourceLink.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a resource link for a web application, as
+ * represented in a <code>&lt;ResourceLink&gt;</code> element in the
+ * server configuration file.
+ *
+ * @author Remy Maucherat
+ * @author Peter Rossbach (Peter Rossbach (pero@apache.org))
+ * @version $Revision$ $Date$
+ */
+
+public class ContextResourceLink extends ResourceBase implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+   /**
+     * The global name of this resource.
+     */
+    private String global = null;
+
+    public String getGlobal() {
+        return (this.global);
+    }
+
+    public void setGlobal(String global) {
+        this.global = global;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ContextResourceLink[");
+        sb.append("name=");
+        sb.append(getName());
+        if (getType() != null) {
+            sb.append(", type=");
+            sb.append(getType());
+        }
+        if (getGlobal() != null) {
+            sb.append(", global=");
+            sb.append(getGlobal());
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ContextTransaction.java b/container/catalina/src/share/org/apache/catalina/deploy/ContextTransaction.java
new file mode 100644
index 0000000..5143a73
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ContextTransaction.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+
+
+/**
+ * Representation of an application resource reference, as represented in
+ * an <code>&lt;res-env-refy&gt;</code> element in the deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ContextTransaction implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Holder for our configured properties.
+     */
+    private HashMap properties = new HashMap();
+
+    /**
+     * Return a configured property.
+     */
+    public Object getProperty(String name) {
+        return properties.get(name);
+    }
+
+    /**
+     * Set a configured property.
+     */
+    public void setProperty(String name, Object value) {
+        properties.put(name, value);
+    }
+
+    /** 
+     * remove a configured property.
+     */
+    public void removeProperty(String name) {
+        properties.remove(name);
+    }
+
+    /**
+     * List properties.
+     */
+    public Iterator listProperties() {
+        return properties.keySet().iterator();
+    }
+    
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("Transaction[");
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * The NamingResources with which we are associated (if any).
+     */
+    protected NamingResources resources = null;
+
+    public NamingResources getNamingResources() {
+        return (this.resources);
+    }
+
+    void setNamingResources(NamingResources resources) {
+        this.resources = resources;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ErrorPage.java b/container/catalina/src/share/org/apache/catalina/deploy/ErrorPage.java
new file mode 100644
index 0000000..72a5794
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ErrorPage.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+
+import org.apache.catalina.util.RequestUtil;
+import java.io.Serializable;
+
+
+/**
+ * Representation of an error page element for a web application,
+ * as represented in a <code>&lt;error-page&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ErrorPage implements Serializable {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The error (status) code for which this error page is active.
+     */
+    private int errorCode = 0;
+
+
+    /**
+     * The exception type for which this error page is active.
+     */
+    private String exceptionType = null;
+
+
+    /**
+     * The context-relative location to handle this error or exception.
+     */
+    private String location = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the error code.
+     */
+    public int getErrorCode() {
+
+        return (this.errorCode);
+
+    }
+
+
+    /**
+     * Set the error code.
+     *
+     * @param errorCode The new error code
+     */
+    public void setErrorCode(int errorCode) {
+
+        this.errorCode = errorCode;
+
+    }
+
+
+    /**
+     * Set the error code (hack for default XmlMapper data type).
+     *
+     * @param errorCode The new error code
+     */
+    public void setErrorCode(String errorCode) {
+
+        try {
+            this.errorCode = Integer.parseInt(errorCode);
+        } catch (Throwable t) {
+            this.errorCode = 0;
+        }
+
+    }
+
+
+    /**
+     * Return the exception type.
+     */
+    public String getExceptionType() {
+
+        return (this.exceptionType);
+
+    }
+
+
+    /**
+     * Set the exception type.
+     *
+     * @param exceptionType The new exception type
+     */
+    public void setExceptionType(String exceptionType) {
+
+        this.exceptionType = exceptionType;
+
+    }
+
+
+    /**
+     * Return the location.
+     */
+    public String getLocation() {
+
+        return (this.location);
+
+    }
+
+
+    /**
+     * Set the location.
+     *
+     * @param location The new location
+     */
+    public void setLocation(String location) {
+
+        //        if ((location == null) || !location.startsWith("/"))
+        //            throw new IllegalArgumentException
+        //                ("Error Page Location must start with a '/'");
+        this.location = RequestUtil.URLDecode(location);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Render a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ErrorPage[");
+        if (exceptionType == null) {
+            sb.append("errorCode=");
+            sb.append(errorCode);
+        } else {
+            sb.append("exceptionType=");
+            sb.append(exceptionType);
+        }
+        sb.append(", location=");
+        sb.append(location);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/FilterDef.java b/container/catalina/src/share/org/apache/catalina/deploy/FilterDef.java
new file mode 100644
index 0000000..a3d0033
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/FilterDef.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.io.Serializable;
+
+
+/**
+ * Representation of a filter definition for a web application, as represented
+ * in a <code>&lt;filter&gt;</code> element in the deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class FilterDef implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this filter.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The display name of this filter.
+     */
+    private String displayName = null;
+
+    public String getDisplayName() {
+        return (this.displayName);
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+
+    /**
+     * The fully qualified name of the Java class that implements this filter.
+     */
+    private String filterClass = null;
+
+    public String getFilterClass() {
+        return (this.filterClass);
+    }
+
+    public void setFilterClass(String filterClass) {
+        this.filterClass = filterClass;
+    }
+
+
+    /**
+     * The name of this filter, which must be unique among the filters
+     * defined for a particular web application.
+     */
+    private String filterName = null;
+
+    public String getFilterName() {
+        return (this.filterName);
+    }
+
+    public void setFilterName(String filterName) {
+        this.filterName = filterName;
+    }
+
+
+    /**
+     * The large icon associated with this filter.
+     */
+    private String largeIcon = null;
+
+    public String getLargeIcon() {
+        return (this.largeIcon);
+    }
+
+    public void setLargeIcon(String largeIcon) {
+        this.largeIcon = largeIcon;
+    }
+
+
+    /**
+     * The set of initialization parameters for this filter, keyed by
+     * parameter name.
+     */
+    private Map parameters = new HashMap();
+
+    public Map getParameterMap() {
+
+        return (this.parameters);
+
+    }
+
+
+    /**
+     * The small icon associated with this filter.
+     */
+    private String smallIcon = null;
+
+    public String getSmallIcon() {
+        return (this.smallIcon);
+    }
+
+    public void setSmallIcon(String smallIcon) {
+        this.smallIcon = smallIcon;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an initialization parameter to the set of parameters associated
+     * with this filter.
+     *
+     * @param name The initialization parameter name
+     * @param value The initialization parameter value
+     */
+    public void addInitParameter(String name, String value) {
+
+        parameters.put(name, value);
+
+    }
+
+
+    /**
+     * Render a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("FilterDef[");
+        sb.append("filterName=");
+        sb.append(this.filterName);
+        sb.append(", filterClass=");
+        sb.append(this.filterClass);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/FilterMap.java b/container/catalina/src/share/org/apache/catalina/deploy/FilterMap.java
new file mode 100644
index 0000000..0f74cea
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/FilterMap.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+
+import org.apache.catalina.util.RequestUtil;
+import java.io.Serializable;
+
+
+/**
+ * Representation of a filter mapping for a web application, as represented
+ * in a <code>&lt;filter-mapping&gt;</code> element in the deployment
+ * descriptor.  Each filter mapping must contain a filter name plus either
+ * a URL pattern or a servlet name.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class FilterMap implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The name of this filter to be executed when this mapping matches
+     * a particular request.
+     */
+    
+    public static final int ERROR = 1;
+    public static final int FORWARD = 2;
+    public static final int FORWARD_ERROR =3;  
+    public static final int INCLUDE = 4;
+    public static final int INCLUDE_ERROR  = 5;
+    public static final int INCLUDE_ERROR_FORWARD  =6;
+    public static final int INCLUDE_FORWARD  = 7;
+    public static final int REQUEST = 8;
+    public static final int REQUEST_ERROR = 9;
+    public static final int REQUEST_ERROR_FORWARD = 10;
+    public static final int REQUEST_ERROR_FORWARD_INCLUDE = 11;
+    public static final int REQUEST_ERROR_INCLUDE = 12;
+    public static final int REQUEST_FORWARD = 13;
+    public static final int REQUEST_INCLUDE = 14;
+    public static final int REQUEST_FORWARD_INCLUDE= 15;
+    
+    // represents nothing having been set. This will be seen 
+    // as equal to a REQUEST
+    private static final int NOT_SET = -1;
+    
+    private int dispatcherMapping=NOT_SET;
+    
+    private String filterName = null;    
+
+    public String getFilterName() {
+        return (this.filterName);
+    }
+
+    public void setFilterName(String filterName) {
+        this.filterName = filterName;
+    }
+
+
+    /**
+     * The servlet name this mapping matches.
+     */
+    private String servletName = null;
+
+    public String getServletName() {
+        return (this.servletName);
+    }
+
+    public void setServletName(String servletName) {
+        this.servletName = servletName;
+    }
+
+
+    /**
+     * The URL pattern this mapping matches.
+     */
+    private String urlPattern = null;
+
+    public String getURLPattern() {
+        return (this.urlPattern);
+    }
+
+    public void setURLPattern(String urlPattern) {
+        this.urlPattern = RequestUtil.URLDecode(urlPattern);
+    }
+    
+    /**
+     *
+     * This method will be used to set the current state of the FilterMap
+     * representing the state of when filters should be applied:
+     *
+     *        ERROR
+     *        FORWARD
+     *        FORWARD_ERROR
+     *        INCLUDE
+     *        INCLUDE_ERROR
+     *        INCLUDE_ERROR_FORWARD
+     *        REQUEST
+     *        REQUEST_ERROR
+     *        REQUEST_ERROR_INCLUDE
+     *        REQUEST_ERROR_FORWARD_INCLUDE
+     *        REQUEST_INCLUDE
+     *        REQUEST_FORWARD,
+     *        REQUEST_FORWARD_INCLUDE
+     *
+     */
+    public void setDispatcher(String dispatcherString) {
+        String dispatcher = dispatcherString.toUpperCase();
+        
+        if (dispatcher.equals("FORWARD")) {
+
+            // apply FORWARD to the global dispatcherMapping.
+            switch (dispatcherMapping) {
+                case NOT_SET  :  dispatcherMapping = FORWARD; break;
+                case ERROR : dispatcherMapping = FORWARD_ERROR; break;
+                case INCLUDE  :  dispatcherMapping = INCLUDE_FORWARD; break;
+                case INCLUDE_ERROR  :  dispatcherMapping = INCLUDE_ERROR_FORWARD; break;
+                case REQUEST : dispatcherMapping = REQUEST_FORWARD; break;
+                case REQUEST_ERROR : dispatcherMapping = REQUEST_ERROR_FORWARD; break;
+                case REQUEST_ERROR_INCLUDE : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break;
+                case REQUEST_INCLUDE : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break;
+            }
+        } else if (dispatcher.equals("INCLUDE")) {
+            // apply INCLUDE to the global dispatcherMapping.
+            switch (dispatcherMapping) {
+                case NOT_SET  :  dispatcherMapping = INCLUDE; break;
+                case ERROR : dispatcherMapping = INCLUDE_ERROR; break;
+                case FORWARD  :  dispatcherMapping = INCLUDE_FORWARD; break;
+                case FORWARD_ERROR  :  dispatcherMapping = INCLUDE_ERROR_FORWARD; break;
+                case REQUEST : dispatcherMapping = REQUEST_INCLUDE; break;
+                case REQUEST_ERROR : dispatcherMapping = REQUEST_ERROR_INCLUDE; break;
+                case REQUEST_ERROR_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break;
+                case REQUEST_FORWARD : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break;
+            }
+        } else if (dispatcher.equals("REQUEST")) {
+            // apply REQUEST to the global dispatcherMapping.
+            switch (dispatcherMapping) {
+                case NOT_SET  :  dispatcherMapping = REQUEST; break;
+                case ERROR : dispatcherMapping = REQUEST_ERROR; break;
+                case FORWARD  :  dispatcherMapping = REQUEST_FORWARD; break;
+                case FORWARD_ERROR  :  dispatcherMapping = REQUEST_ERROR_FORWARD; break;
+                case INCLUDE  :  dispatcherMapping = REQUEST_INCLUDE; break;
+                case INCLUDE_ERROR  :  dispatcherMapping = REQUEST_ERROR_INCLUDE; break;
+                case INCLUDE_FORWARD : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break;
+                case INCLUDE_ERROR_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break;
+            }
+        }  else if (dispatcher.equals("ERROR")) {
+            // apply ERROR to the global dispatcherMapping.
+            switch (dispatcherMapping) {
+                case NOT_SET  :  dispatcherMapping = ERROR; break;
+                case FORWARD  :  dispatcherMapping = FORWARD_ERROR; break;
+                case INCLUDE  :  dispatcherMapping = INCLUDE_ERROR; break;
+                case INCLUDE_FORWARD : dispatcherMapping = INCLUDE_ERROR_FORWARD; break;
+                case REQUEST : dispatcherMapping = REQUEST_ERROR; break;
+                case REQUEST_INCLUDE : dispatcherMapping = REQUEST_ERROR_INCLUDE; break;
+                case REQUEST_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD; break;
+                case REQUEST_FORWARD_INCLUDE : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break;
+            }
+        }
+    }
+    
+    public int getDispatcherMapping() {
+        // per the SRV.6.2.5 absence of any dispatcher elements is
+        // equivelant to a REQUEST value
+        if (dispatcherMapping == NOT_SET) return REQUEST;
+        else return dispatcherMapping; 
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Render a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("FilterMap[");
+        sb.append("filterName=");
+        sb.append(this.filterName);
+        if (servletName != null) {
+            sb.append(", servletName=");
+            sb.append(servletName);
+        }
+        if (urlPattern != null) {
+            sb.append(", urlPattern=");
+            sb.append(urlPattern);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/LoginConfig.java b/container/catalina/src/share/org/apache/catalina/deploy/LoginConfig.java
new file mode 100644
index 0000000..ff47181
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/LoginConfig.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+
+import org.apache.catalina.util.RequestUtil;
+import java.io.Serializable;
+
+
+/**
+ * Representation of a login configuration element for a web application,
+ * as represented in a <code>&lt;login-config&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class LoginConfig implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new LoginConfig with default properties.
+     */
+    public LoginConfig() {
+
+        super();
+
+    }
+
+
+    /**
+     * Construct a new LoginConfig with the specified properties.
+     *
+     * @param authMethod The authentication method
+     * @param realmName The realm name
+     * @param loginPage The login page URI
+     * @param errorPage The error page URI
+     */
+    public LoginConfig(String authMethod, String realmName,
+                       String loginPage, String errorPage) {
+
+        super();
+        setAuthMethod(authMethod);
+        setRealmName(realmName);
+        setLoginPage(loginPage);
+        setErrorPage(errorPage);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The authentication method to use for application login.  Must be
+     * BASIC, DIGEST, FORM, or CLIENT-CERT.
+     */
+    private String authMethod = null;
+
+    public String getAuthMethod() {
+        return (this.authMethod);
+    }
+
+    public void setAuthMethod(String authMethod) {
+        this.authMethod = authMethod;
+    }
+
+
+    /**
+     * The context-relative URI of the error page for form login.
+     */
+    private String errorPage = null;
+
+    public String getErrorPage() {
+        return (this.errorPage);
+    }
+
+    public void setErrorPage(String errorPage) {
+        //        if ((errorPage == null) || !errorPage.startsWith("/"))
+        //            throw new IllegalArgumentException
+        //                ("Error Page resource path must start with a '/'");
+        this.errorPage = RequestUtil.URLDecode(errorPage);
+    }
+
+
+    /**
+     * The context-relative URI of the login page for form login.
+     */
+    private String loginPage = null;
+
+    public String getLoginPage() {
+        return (this.loginPage);
+    }
+
+    public void setLoginPage(String loginPage) {
+        //        if ((loginPage == null) || !loginPage.startsWith("/"))
+        //            throw new IllegalArgumentException
+        //                ("Login Page resource path must start with a '/'");
+        this.loginPage = RequestUtil.URLDecode(loginPage);
+    }
+
+
+    /**
+     * The realm name used when challenging the user for authentication
+     * credentials.
+     */
+    private String realmName = null;
+
+    public String getRealmName() {
+        return (this.realmName);
+    }
+
+    public void setRealmName(String realmName) {
+        this.realmName = realmName;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("LoginConfig[");
+        sb.append("authMethod=");
+        sb.append(authMethod);
+        if (realmName != null) {
+            sb.append(", realmName=");
+            sb.append(realmName);
+        }
+        if (loginPage != null) {
+            sb.append(", loginPage=");
+            sb.append(loginPage);
+        }
+        if (errorPage != null) {
+            sb.append(", errorPage=");
+            sb.append(errorPage);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/MessageDestination.java b/container/catalina/src/share/org/apache/catalina/deploy/MessageDestination.java
new file mode 100644
index 0000000..20a0f6d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/MessageDestination.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+
+/**
+ * <p>Representation of a message destination for a web application, as
+ * represented in a <code>&lt;message-destination&gt;</code> element
+ * in the deployment descriptor.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since Tomcat 5.0
+ */
+
+public class MessageDestination {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this destination.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The display name of this destination.
+     */
+    private String displayName = null;
+
+    public String getDisplayName() {
+        return (this.displayName);
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+
+    /**
+     * The large icon of this destination.
+     */
+    private String largeIcon = null;
+
+    public String getLargeIcon() {
+        return (this.largeIcon);
+    }
+
+    public void setLargeIcon(String largeIcon) {
+        this.largeIcon = largeIcon;
+    }
+
+
+    /**
+     * The name of this destination.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The small icon of this destination.
+     */
+    private String smallIcon = null;
+
+    public String getSmallIcon() {
+        return (this.smallIcon);
+    }
+
+    public void setSmallIcon(String smallIcon) {
+        this.smallIcon = smallIcon;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("MessageDestination[");
+        sb.append("name=");
+        sb.append(name);
+        if (displayName != null) {
+            sb.append(", displayName=");
+            sb.append(displayName);
+        }
+        if (largeIcon != null) {
+            sb.append(", largeIcon=");
+            sb.append(largeIcon);
+        }
+        if (smallIcon != null) {
+            sb.append(", smallIcon=");
+            sb.append(smallIcon);
+        }
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/MessageDestinationRef.java b/container/catalina/src/share/org/apache/catalina/deploy/MessageDestinationRef.java
new file mode 100644
index 0000000..efcb771
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/MessageDestinationRef.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * <p>Representation of a message destination reference for a web application,
+ * as represented in a <code>&lt;message-destination-ref&gt;</code> element
+ * in the deployment descriptor.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since Tomcat 5.0
+ */
+
+public class MessageDestinationRef implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this destination ref.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The link of this destination ref.
+     */
+    private String link = null;
+
+    public String getLink() {
+        return (this.link);
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+
+    /**
+     * The name of this destination ref.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The type of this destination ref.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    /**
+     * The usage of this destination ref.
+     */
+    private String usage = null;
+
+    public String getUsage() {
+        return (this.usage);
+    }
+
+    public void setUsage(String usage) {
+        this.usage = usage;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("MessageDestination[");
+        sb.append("name=");
+        sb.append(name);
+        if (link != null) {
+            sb.append(", link=");
+            sb.append(link);
+        }
+        if (type != null) {
+            sb.append(", type=");
+            sb.append(type);
+        }
+        if (usage != null) {
+            sb.append(", usage=");
+            sb.append(usage);
+        }
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * The NamingResources with which we are associated (if any).
+     */
+    protected NamingResources resources = null;
+
+    public NamingResources getNamingResources() {
+        return (this.resources);
+    }
+
+    void setNamingResources(NamingResources resources) {
+        this.resources = resources;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/NamingResources.java b/container/catalina/src/share/org/apache/catalina/deploy/NamingResources.java
new file mode 100644
index 0000000..b9454b6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/NamingResources.java
@@ -0,0 +1,705 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.io.Serializable;
+
+
+/**
+ * Holds and manages the naming resources defined in the J2EE Enterprise 
+ * Naming Context and their associated JNDI context.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class NamingResources implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new NamingResources instance.
+     */
+    public NamingResources() {
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated container object.
+     */
+    private Object container = null;
+
+
+    /**
+     * List of naming entries, keyed by name. The value is the entry type, as
+     * declared by the user.
+     */
+    private Hashtable entries = new Hashtable();
+
+
+    /**
+     * The EJB resource references for this web application, keyed by name.
+     */
+    private HashMap ejbs = new HashMap();
+
+
+    /**
+     * The environment entries for this web application, keyed by name.
+     */
+    private HashMap envs = new HashMap();
+
+
+    /**
+     * The local  EJB resource references for this web application, keyed by
+     * name.
+     */
+    private HashMap localEjbs = new HashMap();
+
+
+    /**
+     * The message destination referencess for this web application,
+     * keyed by name.
+     */
+    private HashMap mdrs = new HashMap();
+
+
+    /**
+     * The resource environment references for this web application,
+     * keyed by name.
+     */
+    private HashMap resourceEnvRefs = new HashMap();
+
+
+    /**
+     * The resource references for this web application, keyed by name.
+     */
+    private HashMap resources = new HashMap();
+
+
+    /**
+     * The resource links for this web application, keyed by name.
+     */
+    private HashMap resourceLinks = new HashMap();
+
+
+    /**
+     * The transaction for this webapp.
+     */
+    private ContextTransaction transaction = null;
+    
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Get the container with which the naming resources are associated.
+     */
+    public Object getContainer() {
+        return container;
+    }
+
+
+    /**
+     * Set the container with which the naming resources are associated.
+     */
+    public void setContainer(Object container) {
+        this.container = container;
+    }
+
+    
+    /**
+     * Set the transaction object.
+     */
+    public void setTransaction(ContextTransaction transaction) {
+        this.transaction = transaction;
+    }
+    
+
+    /**
+     * Get the transaction object.
+     */
+    public ContextTransaction getTransaction() {
+        return transaction;
+    }
+    
+
+    /**
+     * Add an EJB resource reference for this web application.
+     *
+     * @param ejb New EJB resource reference
+     */
+    public void addEjb(ContextEjb ejb) {
+
+        if (entries.containsKey(ejb.getName())) {
+            return;
+        } else {
+            entries.put(ejb.getName(), ejb.getType());
+        }
+
+        synchronized (ejbs) {
+            ejb.setNamingResources(this);
+            ejbs.put(ejb.getName(), ejb);
+        }
+        support.firePropertyChange("ejb", null, ejb);
+
+    }
+
+
+    /**
+     * Add an environment entry for this web application.
+     *
+     * @param environment New environment entry
+     */
+    public void addEnvironment(ContextEnvironment environment) {
+
+        if (entries.containsKey(environment.getName())) {
+            return;
+        } else {
+            entries.put(environment.getName(), environment.getType());
+        }
+
+        synchronized (envs) {
+            environment.setNamingResources(this);
+            envs.put(environment.getName(), environment);
+        }
+        support.firePropertyChange("environment", null, environment);
+
+    }
+
+
+    /**
+     * Add a local EJB resource reference for this web application.
+     *
+     * @param ejb New EJB resource reference
+     */
+    public void addLocalEjb(ContextLocalEjb ejb) {
+
+        if (entries.containsKey(ejb.getName())) {
+            return;
+        } else {
+            entries.put(ejb.getName(), ejb.getType());
+        }
+
+        synchronized (localEjbs) {
+            ejb.setNamingResources(this);
+            localEjbs.put(ejb.getName(), ejb);
+        }
+        support.firePropertyChange("localEjb", null, ejb);
+
+    }
+
+
+    /**
+     * Add a message destination reference for this web application.
+     *
+     * @param mdr New message destination reference
+     */
+    public void addMessageDestinationRef(MessageDestinationRef mdr) {
+
+        if (entries.containsKey(mdr.getName())) {
+            return;
+        } else {
+            entries.put(mdr.getName(), mdr.getType());
+        }
+
+        synchronized (mdrs) {
+            mdr.setNamingResources(this);
+            mdrs.put(mdr.getName(), mdr);
+        }
+        support.firePropertyChange("messageDestinationRef", null, mdr);
+
+    }
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Add a resource reference for this web application.
+     *
+     * @param resource New resource reference
+     */
+    public void addResource(ContextResource resource) {
+
+        if (entries.containsKey(resource.getName())) {
+            return;
+        } else {
+            entries.put(resource.getName(), resource.getType());
+        }
+
+        synchronized (resources) {
+            resource.setNamingResources(this);
+            resources.put(resource.getName(), resource);
+        }
+        support.firePropertyChange("resource", null, resource);
+
+    }
+
+
+    /**
+     * Add a resource environment reference for this web application.
+     *
+     * @param resource The resource
+     */
+    public void addResourceEnvRef(ContextResourceEnvRef resource) {
+
+        if (entries.containsKey(resource.getName())) {
+            return;
+        } else {
+            entries.put(resource.getName(), resource.getType());
+        }
+
+        synchronized (localEjbs) {
+            resource.setNamingResources(this);
+            resourceEnvRefs.put(resource.getName(), resource);
+        }
+        support.firePropertyChange("resourceEnvRef", null, resource);
+
+    }
+
+
+    /**
+     * Add a resource link for this web application.
+     *
+     * @param resourceLink New resource link
+     */
+    public void addResourceLink(ContextResourceLink resourceLink) {
+
+        if (entries.containsKey(resourceLink.getName())) {
+            return;
+        } else {
+            Object value = resourceLink.getType();
+            if (value == null) {
+                value = "";
+            }
+            entries.put(resourceLink.getName(), value);
+        }
+
+        synchronized (resourceLinks) {
+            resourceLink.setNamingResources(this);
+            resourceLinks.put(resourceLink.getName(), resourceLink);
+        }
+        support.firePropertyChange("resourceLink", null, resourceLink);
+
+    }
+
+
+    /**
+     * Return the EJB resource reference with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired EJB resource reference
+     */
+    public ContextEjb findEjb(String name) {
+
+        synchronized (ejbs) {
+            return ((ContextEjb) ejbs.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the defined EJB resource references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    public ContextEjb[] findEjbs() {
+
+        synchronized (ejbs) {
+            ContextEjb results[] = new ContextEjb[ejbs.size()];
+            return ((ContextEjb[]) ejbs.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the environment entry with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired environment entry
+     */
+    public ContextEnvironment findEnvironment(String name) {
+
+        synchronized (envs) {
+            return ((ContextEnvironment) envs.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the set of defined environment entries for this web
+     * application.  If none have been defined, a zero-length array
+     * is returned.
+     */
+    public ContextEnvironment[] findEnvironments() {
+
+        synchronized (envs) {
+            ContextEnvironment results[] = new ContextEnvironment[envs.size()];
+            return ((ContextEnvironment[]) envs.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the local EJB resource reference with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired EJB resource reference
+     */
+    public ContextLocalEjb findLocalEjb(String name) {
+
+        synchronized (localEjbs) {
+            return ((ContextLocalEjb) localEjbs.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the defined local EJB resource references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    public ContextLocalEjb[] findLocalEjbs() {
+
+        synchronized (localEjbs) {
+            ContextLocalEjb results[] = new ContextLocalEjb[localEjbs.size()];
+            return ((ContextLocalEjb[]) localEjbs.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the message destination reference with the specified name,
+     * if any; otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired message destination reference
+     */
+    public MessageDestinationRef findMessageDestinationRef(String name) {
+
+        synchronized (mdrs) {
+            return ((MessageDestinationRef) mdrs.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the defined message destination references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    public MessageDestinationRef[] findMessageDestinationRefs() {
+
+        synchronized (mdrs) {
+            MessageDestinationRef results[] =
+                new MessageDestinationRef[mdrs.size()];
+            return ((MessageDestinationRef[]) mdrs.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the resource reference with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource reference
+     */
+    public ContextResource findResource(String name) {
+
+        synchronized (resources) {
+            return ((ContextResource) resources.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the resource link with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource link
+     */
+    public ContextResourceLink findResourceLink(String name) {
+
+        synchronized (resourceLinks) {
+            return ((ContextResourceLink) resourceLinks.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the defined resource links for this application.  If
+     * none have been defined, a zero-length array is returned.
+     */
+    public ContextResourceLink[] findResourceLinks() {
+
+        synchronized (resourceLinks) {
+            ContextResourceLink results[] = 
+                new ContextResourceLink[resourceLinks.size()];
+            return ((ContextResourceLink[]) resourceLinks.values()
+                    .toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the defined resource references for this application.  If
+     * none have been defined, a zero-length array is returned.
+     */
+    public ContextResource[] findResources() {
+
+        synchronized (resources) {
+            ContextResource results[] = new ContextResource[resources.size()];
+            return ((ContextResource[]) resources.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return the resource environment reference type for the specified
+     * name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource environment reference
+     */
+    public ContextResourceEnvRef findResourceEnvRef(String name) {
+
+        synchronized (resourceEnvRefs) {
+            return ((ContextResourceEnvRef) resourceEnvRefs.get(name));
+        }
+
+    }
+
+
+    /**
+     * Return the set of resource environment reference names for this
+     * web application.  If none have been specified, a zero-length
+     * array is returned.
+     */
+    public ContextResourceEnvRef[] findResourceEnvRefs() {
+
+        synchronized (resourceEnvRefs) {
+            ContextResourceEnvRef results[] = new ContextResourceEnvRef[resourceEnvRefs.size()];
+            return ((ContextResourceEnvRef[]) resourceEnvRefs.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Return true if the name specified already exists.
+     */
+    public boolean exists(String name) {
+
+        return (entries.containsKey(name));
+
+    }
+
+
+    /**
+     * Remove any EJB resource reference with the specified name.
+     *
+     * @param name Name of the EJB resource reference to remove
+     */
+    public void removeEjb(String name) {
+
+        entries.remove(name);
+
+        ContextEjb ejb = null;
+        synchronized (ejbs) {
+            ejb = (ContextEjb) ejbs.remove(name);
+        }
+        if (ejb != null) {
+            support.firePropertyChange("ejb", ejb, null);
+            ejb.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove any environment entry with the specified name.
+     *
+     * @param name Name of the environment entry to remove
+     */
+    public void removeEnvironment(String name) {
+
+        entries.remove(name);
+
+        ContextEnvironment environment = null;
+        synchronized (envs) {
+            environment = (ContextEnvironment) envs.remove(name);
+        }
+        if (environment != null) {
+            support.firePropertyChange("environment", environment, null);
+            environment.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove any local EJB resource reference with the specified name.
+     *
+     * @param name Name of the EJB resource reference to remove
+     */
+    public void removeLocalEjb(String name) {
+
+        entries.remove(name);
+
+        ContextLocalEjb localEjb = null;
+        synchronized (localEjbs) {
+            localEjb = (ContextLocalEjb) ejbs.remove(name);
+        }
+        if (localEjb != null) {
+            support.firePropertyChange("localEjb", localEjb, null);
+            localEjb.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove any message destination reference with the specified name.
+     *
+     * @param name Name of the message destination resource reference to remove
+     */
+    public void removeMessageDestinationRef(String name) {
+
+        entries.remove(name);
+
+        MessageDestinationRef mdr = null;
+        synchronized (mdrs) {
+            mdr = (MessageDestinationRef) mdrs.remove(name);
+        }
+        if (mdr != null) {
+            support.firePropertyChange("messageDestinationRef",
+                                       mdr, null);
+            mdr.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Remove any resource reference with the specified name.
+     *
+     * @param name Name of the resource reference to remove
+     */
+    public void removeResource(String name) {
+
+        entries.remove(name);
+
+        ContextResource resource = null;
+        synchronized (resources) {
+            resource = (ContextResource) resources.remove(name);
+        }
+        if (resource != null) {
+            support.firePropertyChange("resource", resource, null);
+            resource.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove any resource environment reference with the specified name.
+     *
+     * @param name Name of the resource environment reference to remove
+     */
+    public void removeResourceEnvRef(String name) {
+
+        entries.remove(name);
+
+        String type = null;
+        synchronized (resourceEnvRefs) {
+            type = (String) resourceEnvRefs.remove(name);
+        }
+        if (type != null) {
+            support.firePropertyChange("resourceEnvRef",
+                                       name + ":" + type, null);
+        }
+
+    }
+
+
+    /**
+     * Remove any resource link with the specified name.
+     *
+     * @param name Name of the resource link to remove
+     */
+    public void removeResourceLink(String name) {
+
+        entries.remove(name);
+
+        ContextResourceLink resourceLink = null;
+        synchronized (resourceLinks) {
+            resourceLink = (ContextResourceLink) resourceLinks.remove(name);
+        }
+        if (resourceLink != null) {
+            support.firePropertyChange("resourceLink", resourceLink, null);
+            resourceLink.setNamingResources(null);
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/ResourceBase.java b/container/catalina/src/share/org/apache/catalina/deploy/ResourceBase.java
new file mode 100644
index 0000000..c6ef7a6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/ResourceBase.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.HashMap;
+
+
+/**
+ * Representation of an Context element
+ *
+ * @author Peter Rossbach (pero@apache.org)
+ * @version $Revision$ $Date$
+ */
+
+public class ResourceBase implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this Context Element.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+
+    /**
+     * The name of this context Element.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The name of the EJB bean implementation class.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    /**
+     * Holder for our configured properties.
+     */
+    private HashMap properties = new HashMap();
+
+    /**
+     * Return a configured property.
+     */
+    public Object getProperty(String name) {
+        return properties.get(name);
+    }
+
+    /**
+     * Set a configured property.
+     */
+    public void setProperty(String name, Object value) {
+        properties.put(name, value);
+    }
+
+    /** 
+     * remove a configured property.
+     */
+    public void removeProperty(String name) {
+        properties.remove(name);
+    }
+
+    /**
+     * List properties.
+     */
+    public Iterator listProperties() {
+        return properties.keySet().iterator();
+    }
+    
+    
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * The NamingResources with which we are associated (if any).
+     */
+    protected NamingResources resources = null;
+
+    public NamingResources getNamingResources() {
+        return (this.resources);
+    }
+
+    void setNamingResources(NamingResources resources) {
+        this.resources = resources;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/SecurityCollection.java b/container/catalina/src/share/org/apache/catalina/deploy/SecurityCollection.java
new file mode 100644
index 0000000..70f22e5
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/SecurityCollection.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+
+import org.apache.catalina.util.RequestUtil;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a web resource collection for a web application's security
+ * constraint, as represented in a <code>&lt;web-resource-collection&gt;</code>
+ * element in the deployment descriptor.
+ * <p>
+ * <b>WARNING</b>:  It is assumed that instances of this class will be created
+ * and modified only within the context of a single thread, before the instance
+ * is made visible to the remainder of the application.  After that, only read
+ * access is expected.  Therefore, none of the read and write access within
+ * this class is synchronized.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SecurityCollection implements Serializable {
+
+    private static Log log = LogFactory.getLog(SecurityCollection.class);
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new security collection instance with default values.
+     */
+    public SecurityCollection() {
+
+        this(null, null);
+
+    }
+
+
+    /**
+     * Construct a new security collection instance with specified values.
+     *
+     * @param name Name of this security collection
+     */
+    public SecurityCollection(String name) {
+
+        this(name, null);
+
+    }
+
+
+    /**
+     * Construct a new security collection instance with specified values.
+     *
+     * @param name Name of this security collection
+     * @param description Description of this security collection
+     */
+    public SecurityCollection(String name, String description) {
+
+        super();
+        setName(name);
+        setDescription(description);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Description of this web resource collection.
+     */
+    private String description = null;
+
+
+    /**
+     * The HTTP methods covered by this web resource collection.
+     */
+    private String methods[] = new String[0];
+
+
+    /**
+     * The name of this web resource collection.
+     */
+    private String name = null;
+
+
+    /**
+     * The URL patterns protected by this security collection.
+     */
+    private String patterns[] = new String[0];
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the description of this web resource collection.
+     */
+    public String getDescription() {
+
+        return (this.description);
+
+    }
+
+
+    /**
+     * Set the description of this web resource collection.
+     *
+     * @param description The new description
+     */
+    public void setDescription(String description) {
+
+        this.description = description;
+
+    }
+
+
+    /**
+     * Return the name of this web resource collection.
+     */
+    public String getName() {
+
+        return (this.name);
+
+    }
+
+
+    /**
+     * Set the name of this web resource collection
+     *
+     * @param name The new name
+     */
+    public void setName(String name) {
+
+        this.name = name;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an HTTP request method to be part of this web resource collection.
+     */
+    public void addMethod(String method) {
+
+        if (method == null)
+            return;
+        String results[] = new String[methods.length + 1];
+        for (int i = 0; i < methods.length; i++)
+            results[i] = methods[i];
+        results[methods.length] = method;
+        methods = results;
+
+    }
+
+
+    /**
+     * Add a URL pattern to be part of this web resource collection.
+     */
+    public void addPattern(String pattern) {
+
+        if (pattern == null)
+            return;
+
+        // Bugzilla 34805: add friendly warning.
+        if(pattern.endsWith("*")) {
+          if (pattern.charAt(pattern.length()-1) != '/') {
+            if (log.isDebugEnabled()) {
+              log.warn("Suspicious url pattern: \"" + pattern + "\"" +
+                       " - see http://java.sun.com/aboutJava/communityprocess/first/jsr053/servlet23_PFD.pdf" +
+                       "  section 11.2" );
+            }
+          }
+        }
+
+        pattern = RequestUtil.URLDecode(pattern);
+        String results[] = new String[patterns.length + 1];
+        for (int i = 0; i < patterns.length; i++) {
+            results[i] = patterns[i];
+        }
+        results[patterns.length] = pattern;
+        patterns = results;
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified HTTP request method is
+     * part of this web resource collection.
+     *
+     * @param method Request method to check
+     */
+    public boolean findMethod(String method) {
+
+        if (methods.length == 0)
+            return (true);
+        for (int i = 0; i < methods.length; i++) {
+            if (methods[i].equals(method))
+                return (true);
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Return the set of HTTP request methods that are part of this web
+     * resource collection, or a zero-length array if all request methods
+     * are included.
+     */
+    public String[] findMethods() {
+
+        return (methods);
+
+    }
+
+
+    /**
+     * Is the specified pattern part of this web resource collection?
+     *
+     * @param pattern Pattern to be compared
+     */
+    public boolean findPattern(String pattern) {
+
+        for (int i = 0; i < patterns.length; i++) {
+            if (patterns[i].equals(pattern))
+                return (true);
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Return the set of URL patterns that are part of this web resource
+     * collection.  If none have been specified, a zero-length array is
+     * returned.
+     */
+    public String[] findPatterns() {
+
+        return (patterns);
+
+    }
+
+
+    /**
+     * Remove the specified HTTP request method from those that are part
+     * of this web resource collection.
+     *
+     * @param method Request method to be removed
+     */
+    public void removeMethod(String method) {
+
+        if (method == null)
+            return;
+        int n = -1;
+        for (int i = 0; i < methods.length; i++) {
+            if (methods[i].equals(method)) {
+                n = i;
+                break;
+            }
+        }
+        if (n >= 0) {
+            int j = 0;
+            String results[] = new String[methods.length - 1];
+            for (int i = 0; i < methods.length; i++) {
+                if (i != n)
+                    results[j++] = methods[i];
+            }
+            methods = results;
+        }
+
+    }
+
+
+    /**
+     * Remove the specified URL pattern from those that are part of this
+     * web resource collection.
+     *
+     * @param pattern Pattern to be removed
+     */
+    public void removePattern(String pattern) {
+
+        if (pattern == null)
+            return;
+        int n = -1;
+        for (int i = 0; i < patterns.length; i++) {
+            if (patterns[i].equals(pattern)) {
+                n = i;
+                break;
+            }
+        }
+        if (n >= 0) {
+            int j = 0;
+            String results[] = new String[patterns.length - 1];
+            for (int i = 0; i < patterns.length; i++) {
+                if (i != n)
+                    results[j++] = patterns[i];
+            }
+            patterns = results;
+        }
+
+    }
+
+
+    /**
+     * Return a String representation of this security collection.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SecurityCollection[");
+        sb.append(name);
+        if (description != null) {
+            sb.append(", ");
+            sb.append(description);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/SecurityConstraint.java b/container/catalina/src/share/org/apache/catalina/deploy/SecurityConstraint.java
new file mode 100644
index 0000000..43992f0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/SecurityConstraint.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a security constraint element for a web application,
+ * as represented in a <code>&lt;security-constraint&gt;</code> element in the
+ * deployment descriptor.
+ * <p>
+ * <b>WARNING</b>:  It is assumed that instances of this class will be created
+ * and modified only within the context of a single thread, before the instance
+ * is made visible to the remainder of the application.  After that, only read
+ * access is expected.  Therefore, none of the read and write access within
+ * this class is synchronized.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SecurityConstraint implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new security constraint instance with default values.
+     */
+    public SecurityConstraint() {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Was the "all roles" wildcard included in the authorization constraints
+     * for this security constraint?
+     */
+    private boolean allRoles = false;
+
+
+    /**
+     * Was an authorization constraint included in this security constraint?
+     * This is necessary to distinguish the case where an auth-constraint with
+     * no roles (signifying no direct access at all) was requested, versus
+     * a lack of auth-constraint which implies no access control checking.
+     */
+    private boolean authConstraint = false;
+
+
+    /**
+     * The set of roles permitted to access resources protected by this
+     * security constraint.
+     */
+    private String authRoles[] = new String[0];
+
+
+    /**
+     * The set of web resource collections protected by this security
+     * constraint.
+     */
+    private SecurityCollection collections[] = new SecurityCollection[0];
+
+
+    /**
+     * The display name of this security constraint.
+     */
+    private String displayName = null;
+
+
+    /**
+     * The user data constraint for this security constraint.  Must be NONE,
+     * INTEGRAL, or CONFIDENTIAL.
+     */
+    private String userConstraint = "NONE";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Was the "all roles" wildcard included in this authentication
+     * constraint?
+     */
+    public boolean getAllRoles() {
+
+        return (this.allRoles);
+
+    }
+
+
+    /**
+     * Return the authorization constraint present flag for this security
+     * constraint.
+     */
+    public boolean getAuthConstraint() {
+
+        return (this.authConstraint);
+
+    }
+
+
+    /**
+     * Set the authorization constraint present flag for this security
+     * constraint.
+     */
+    public void setAuthConstraint(boolean authConstraint) {
+
+        this.authConstraint = authConstraint;
+
+    }
+
+
+    /**
+     * Return the display name of this security constraint.
+     */
+    public String getDisplayName() {
+
+        return (this.displayName);
+
+    }
+
+
+    /**
+     * Set the display name of this security constraint.
+     */
+    public void setDisplayName(String displayName) {
+
+        this.displayName = displayName;
+
+    }
+
+
+    /**
+     * Return the user data constraint for this security constraint.
+     */
+    public String getUserConstraint() {
+
+        return (userConstraint);
+
+    }
+
+
+    /**
+     * Set the user data constraint for this security constraint.
+     *
+     * @param userConstraint The new user data constraint
+     */
+    public void setUserConstraint(String userConstraint) {
+
+        if (userConstraint != null)
+            this.userConstraint = userConstraint;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an authorization role, which is a role name that will be
+     * permitted access to the resources protected by this security constraint.
+     *
+     * @param authRole Role name to be added
+     */
+    public void addAuthRole(String authRole) {
+
+        if (authRole == null)
+            return;
+        if ("*".equals(authRole)) {
+            allRoles = true;
+            return;
+        }
+        String results[] = new String[authRoles.length + 1];
+        for (int i = 0; i < authRoles.length; i++)
+            results[i] = authRoles[i];
+        results[authRoles.length] = authRole;
+        authRoles = results;
+        authConstraint = true;
+
+    }
+
+
+    /**
+     * Add a new web resource collection to those protected by this
+     * security constraint.
+     *
+     * @param collection The new web resource collection
+     */
+    public void addCollection(SecurityCollection collection) {
+
+        if (collection == null)
+            return;
+        SecurityCollection results[] =
+            new SecurityCollection[collections.length + 1];
+        for (int i = 0; i < collections.length; i++)
+            results[i] = collections[i];
+        results[collections.length] = collection;
+        collections = results;
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified role is permitted access to
+     * the resources protected by this security constraint.
+     *
+     * @param role Role name to be checked
+     */
+    public boolean findAuthRole(String role) {
+
+        if (role == null)
+            return (false);
+        for (int i = 0; i < authRoles.length; i++) {
+            if (role.equals(authRoles[i]))
+                return (true);
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Return the set of roles that are permitted access to the resources
+     * protected by this security constraint.  If none have been defined,
+     * a zero-length array is returned (which implies that all authenticated
+     * users are permitted access).
+     */
+    public String[] findAuthRoles() {
+
+        return (authRoles);
+
+    }
+
+
+    /**
+     * Return the web resource collection for the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Web resource collection name to return
+     */
+    public SecurityCollection findCollection(String name) {
+
+        if (name == null)
+            return (null);
+        for (int i = 0; i < collections.length; i++) {
+            if (name.equals(collections[i].getName()))
+                return (collections[i]);
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Return all of the web resource collections protected by this
+     * security constraint.  If there are none, a zero-length array is
+     * returned.
+     */
+    public SecurityCollection[] findCollections() {
+
+        return (collections);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified context-relative URI (and
+     * associated HTTP method) are protected by this security constraint.
+     *
+     * @param uri Context-relative URI to check
+     * @param method Request method being used
+     */
+    public boolean included(String uri, String method) {
+
+        // We cannot match without a valid request method
+        if (method == null)
+            return (false);
+
+        // Check all of the collections included in this constraint
+        for (int i = 0; i < collections.length; i++) {
+            if (!collections[i].findMethod(method))
+                continue;
+            String patterns[] = collections[i].findPatterns();
+            for (int j = 0; j < patterns.length; j++) {
+                if (matchPattern(uri, patterns[j]))
+                    return (true);
+            }
+        }
+
+        // No collection included in this constraint matches this request
+        return (false);
+
+    }
+
+
+    /**
+     * Remove the specified role from the set of roles permitted to access
+     * the resources protected by this security constraint.
+     *
+     * @param authRole Role name to be removed
+     */
+    public void removeAuthRole(String authRole) {
+
+        if (authRole == null)
+            return;
+        int n = -1;
+        for (int i = 0; i < authRoles.length; i++) {
+            if (authRoles[i].equals(authRole)) {
+                n = i;
+                break;
+            }
+        }
+        if (n >= 0) {
+            int j = 0;
+            String results[] = new String[authRoles.length - 1];
+            for (int i = 0; i < authRoles.length; i++) {
+                if (i != n)
+                    results[j++] = authRoles[i];
+            }
+            authRoles = results;
+        }
+
+    }
+
+
+    /**
+     * Remove the specified web resource collection from those protected by
+     * this security constraint.
+     *
+     * @param collection Web resource collection to be removed
+     */
+    public void removeCollection(SecurityCollection collection) {
+
+        if (collection == null)
+            return;
+        int n = -1;
+        for (int i = 0; i < collections.length; i++) {
+            if (collections[i].equals(collection)) {
+                n = i;
+                break;
+            }
+        }
+        if (n >= 0) {
+            int j = 0;
+            SecurityCollection results[] =
+                new SecurityCollection[collections.length - 1];
+            for (int i = 0; i < collections.length; i++) {
+                if (i != n)
+                    results[j++] = collections[i];
+            }
+            collections = results;
+        }
+
+    }
+
+
+    /**
+     * Return a String representation of this security constraint.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SecurityConstraint[");
+        for (int i = 0; i < collections.length; i++) {
+            if (i > 0)
+                sb.append(", ");
+            sb.append(collections[i].getName());
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Does the specified request path match the specified URL pattern?
+     * This method follows the same rules (in the same order) as those used
+     * for mapping requests to servlets.
+     *
+     * @param path Context-relative request path to be checked
+     *  (must start with '/')
+     * @param pattern URL pattern to be compared against
+     */
+    private boolean matchPattern(String path, String pattern) {
+
+        // Normalize the argument strings
+        if ((path == null) || (path.length() == 0))
+            path = "/";
+        if ((pattern == null) || (pattern.length() == 0))
+            pattern = "/";
+
+        // Check for exact match
+        if (path.equals(pattern))
+            return (true);
+
+        // Check for path prefix matching
+        if (pattern.startsWith("/") && pattern.endsWith("/*")) {
+            pattern = pattern.substring(0, pattern.length() - 2);
+            if (pattern.length() == 0)
+                return (true);  // "/*" is the same as "/"
+            if (path.endsWith("/"))
+                path = path.substring(0, path.length() - 1);
+            while (true) {
+                if (pattern.equals(path))
+                    return (true);
+                int slash = path.lastIndexOf('/');
+                if (slash <= 0)
+                    break;
+                path = path.substring(0, slash);
+            }
+            return (false);
+        }
+
+        // Check for suffix matching
+        if (pattern.startsWith("*.")) {
+            int slash = path.lastIndexOf('/');
+            int period = path.lastIndexOf('.');
+            if ((slash >= 0) && (period > slash) &&
+                path.endsWith(pattern.substring(1))) {
+                return (true);
+            }
+            return (false);
+        }
+
+        // Check for universal mapping
+        if (pattern.equals("/"))
+            return (true);
+
+        return (false);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/deploy/mbeans-descriptors.xml
new file mode 100644
index 0000000..3dc13b2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/mbeans-descriptors.xml
@@ -0,0 +1,185 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean name="ContextEnvironment"
+         className="org.apache.catalina.mbeans.ContextEnvironmentMBean"
+         description="Representation of an application environment entry"
+         domain="Catalina"
+         group="Resources"
+         type="org.apache.catalina.deploy.ContextEnvironment">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="description"
+               description="The description of this environment entry"
+               type="java.lang.String"/>
+
+    <attribute name="name"
+               description="The name of this environment entry"
+               type="java.lang.String"/>
+      
+    <attribute name="override"
+               description="Does this environment entry allow overrides by the
+               application deployment descriptor"
+               type="boolean"/>
+
+    <attribute name="type"
+               description="The type of this environment entry"
+               type="java.lang.String"/>
+
+    <attribute name="value"
+               description="The value of this environment entry"
+               type="java.lang.String"/>
+  </mbean>
+
+
+  <mbean         name="ContextResource"
+         className="org.apache.catalina.mbeans.ContextResourceMBean"
+         description="Representation of a resource reference for a web application"
+         domain="Catalina"
+         group="Resources"
+         type="org.apache.catalina.deploy.ContextResource">
+    
+    <attribute   name="auth"
+               description="The authorization requirement for this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="description"
+               description="The description of this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="name"
+               description="The name of this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="scope"
+               description="The sharing scope of this resource factory"
+               type="java.lang.String"/>
+      
+    <attribute   name="type"
+               description="The type of this environment entry"
+               type="java.lang.String"/>
+  </mbean>
+  
+  
+   <mbean         name="ContextResourceLink"
+          className="org.apache.catalina.mbeans.ContextResourceLinkMBean"
+          description="Representation of a resource link for a web application"
+          domain="Catalina"
+          group="Resources"
+          type="org.apache.catalina.deploy.ContextResourceLink">
+    
+    <attribute   name="global"
+               description="The global name of this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="name"
+               description="The name of this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="type"
+               description="The type of this resource"
+               type="java.lang.String"/>
+      
+  </mbean>
+  
+  <mbean         name="NamingResources"
+            className="org.apache.catalina.mbeans.NamingResourcesMBean"
+          description="Holds and manages the naming resources defined in the
+                       J2EE Enterprise Naming Context and their associated 
+                       JNDI context"
+               domain="Catalina"
+                group="Resources"
+                 type="org.apache.catalina.deploy.NamingResources">
+
+    <attribute   name="environments"
+          description="MBean Names of the set of defined environment entries
+                       for this web application"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="resources"
+          description="MBean Names of all the defined resource references
+                       for this application."
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="resourceLinks"
+          description="MBean Names of all the defined resource link references
+                       for this application."
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <operation   name="addEnvironment"
+          description="Add an environment entry for this web application"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="envName"
+          description="New environment entry name"
+                 type="java.lang.String"/>
+      <parameter name="type"
+          description="New environment entry type"
+                 type="java.lang.String"/>
+      <parameter name="value"
+          description="New environment entry value"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="addResource"
+          description="Add a resource reference for this web application"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="resourceName"
+          description="New resource reference name"
+                 type="java.lang.String"/>
+      <parameter name="type"
+          description="New resource reference type"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="addResourceLink"
+          description="Add a resource link reference for this web application"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="resourceLinkName"
+          description="New resource reference name"
+                 type="java.lang.String"/>
+      <parameter name="type"
+          description="New resource reference type"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeEnvironment"
+          description="Remove any environment entry with the specified name"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="envName"
+          description="Name of the environment entry to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeResource"
+          description="Remove any resource reference with the specified name"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="resourceName"
+          description="Name of the resource reference to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeResourceLink"
+          description="Remove any resource link reference with the specified name"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="resourceLinkName"
+          description="Name of the resource reference to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/deploy/package.html b/container/catalina/src/share/org/apache/catalina/deploy/package.html
new file mode 100644
index 0000000..2612fa0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/deploy/package.html
@@ -0,0 +1,10 @@
+<body>
+
+<p>This package contains Java objects that represent complex data structures
+from the web application deployment descriptor file (<code>web.xml</code>).
+It is assumed that these objects will be initialized within the context of
+a single thread, and then referenced in a read-only manner subsequent to that
+time.  Therefore, no multi-thread synchronization is utilized within the
+implementation classes.</p>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/catalina/launcher/CatalinaLaunchFilter.java b/container/catalina/src/share/org/apache/catalina/launcher/CatalinaLaunchFilter.java
new file mode 100644
index 0000000..3ced3a3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/launcher/CatalinaLaunchFilter.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.launcher;
+
+
+import java.util.ArrayList;
+import org.apache.commons.launcher.LaunchCommand;
+import org.apache.commons.launcher.LaunchFilter;
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * This class implements the LaunchFilter interface. This class is designed to
+ * unconditionally force the "waitforchild" attribute for certain Catalina
+ * applications to true.
+ *
+ * @author Patrick Luby
+ */
+public class CatalinaLaunchFilter implements LaunchFilter {
+
+    //----------------------------------------------------------- Static Fields
+
+    /**
+     * The Catalina bootstrap class name.
+     */
+    private static String CATALINA_BOOTSTRAP_CLASS_NAME = "org.apache.catalina.startup.Bootstrap";
+
+    //----------------------------------------------------------------- Methods
+
+    /**
+     * This method allows dynamic configuration and error checking of the
+     * attributes and nested elements in a "launch" task that is launching a
+     * Catalina application. This method evaluates the nested command line
+     * arguments and, depending on which class is specified in the task's
+     * "classname" attribute, may force the application to run
+     * in the foreground by forcing the "waitforchild" attribute to "true".
+     *
+     * @param launchCommand a configured instance of the {@link LaunchCommand}
+     *  class
+     * @throws BuildException if any errors occur
+     */
+    public void filter(LaunchCommand launchCommand) throws BuildException {
+
+        // Get attributes
+        String mainClassName = launchCommand.getClassname();
+        boolean waitForChild = launchCommand.getWaitforchild();
+        ArrayList argsList = launchCommand.getArgs();
+        String[] args = (String[])argsList.toArray(new String[argsList.size()]);
+
+        // Evaluate main class
+        if (CatalinaLaunchFilter.CATALINA_BOOTSTRAP_CLASS_NAME.equals(mainClassName)) {
+            // If "start" is not the last argument, make "waitforchild" true
+            if (args.length == 0 || !"start".equals(args[args.length - 1])) {
+                launchCommand.setWaitforchild(true);
+                return;
+            }
+
+            // If "start" is the last argument, make sure that all of the
+            // preceding arguments are OK for running in the background
+            for (int i = 0; i < args.length - 1; i++) {
+                if ("-config".equals(args[i])) {
+                    // Skip next argument since it should be a file
+                    if (args.length > i + 1) {
+                        i++;
+                    } else {
+                        launchCommand.setWaitforchild(true);
+                        return;
+                    }
+                } else if ("-debug".equals(args[i])) {
+                    // Do nothing
+                } else if ("-nonaming".equals(args[i])) {
+                    // Do nothing
+                } else {
+                     launchCommand.setWaitforchild(true);
+                     return;
+                }
+            }
+        }
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/loader/Constants.java b/container/catalina/src/share/org/apache/catalina/loader/Constants.java
new file mode 100644
index 0000000..23cb78b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/Constants.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.loader;
+
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.loader";
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/loader/LocaStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/loader/LocaStrings_fr.properties
new file mode 100644
index 0000000..6c19abf
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/LocaStrings_fr.properties
@@ -0,0 +1,30 @@
+fileClassLoader.canRead=Le réceptacle (repository) {0} ne peut être lu
+fileClassLoader.exists=Le réceptacle (repository) {0} n''existe pas
+fileClassLoader.jarFile=Impossible de lire le fichier JAR {0}
+fileClassLoader.restricted=Impossible de charger la classe restreinte (restricted class) {0}
+standardLoader.addRepository=Ajout du réceptacle (repository) {0}
+standardLoader.alreadyStarted=Le chargeur (loader) a déjà été démarré
+standardLoader.checkInterval=Impossible de régler l''interval de vérification de rechargement à {0} secondes
+standardLoader.notContext=Impossible d''auto-recharger sans que le conteneur ne soit un contexte
+standardLoader.notReloadabe=Propriété de rechargement (reloadable property) mise à faux
+standardLoader.notStarted=Le chargeur (loader) n''a pas encore été démarré
+standardLoader.reloadable=Impossible de mettre la propriété de rechargement (reloadable property) à {0}
+standardLoader.reloading=Les vérifications de rechargement sont activées pour ce contexte
+standardLoader.removeRepository=Retrait du réceptacle (repository) {0}
+standardLoader.starting=Démarrage de ce chargeur (loader)
+standardLoader.stopping=Arrêt de ce chargeur (loader)
+webappLoader.addRepository=Ajout du réceptacle (repository) {0}
+webappLoader.deploy=Déploiement des classes des réceptacles (class repositories) vers le dossier de travail (work directory) {0}
+webappLoader.jarDeploy=Déploiement du JAR {0} vers {1}
+webappLoader.classDeploy=Déploiement des fichiers classes {0} vers {1}
+webappLoader.alreadyStarted=Le chargeur (loader) a déjà été démarré
+webappLoader.checkInterval=Impossible de régler l''interval de vérification de rechargement à {0} secondes
+webappLoader.notContext=Impossible d''auto-recharger sans que le conteneur ne soit un contexte
+webappLoader.notReloadabe=Propriété de rechargement (reloadable property) mise à faux
+webappLoader.notStarted=Le chargeur (loader) n''a pas encore été démarré
+webappLoader.reloadable=Impossible de mettre la propriété de rechargement (reloadable property) à {0}
+webappLoader.reloading=Les vérifications de rechargement sont activées pour ce contexte
+webappLoader.removeRepository=Retrait du réceptacle (repository) {0}
+webappLoader.starting=Démarrage de ce chargeur (loader)
+webappLoader.stopping=Arrêt de ce chargeur (loader)
+webappLoader.failModifiedCheck=Erreur dans le suivi des modifications
diff --git a/container/catalina/src/share/org/apache/catalina/loader/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/loader/LocalStrings.properties
new file mode 100644
index 0000000..45e50f0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/LocalStrings.properties
@@ -0,0 +1,31 @@
+fileClassLoader.canRead=Repository {0} cannot be read
+fileClassLoader.exists=Repository {0} does not exist
+fileClassLoader.jarFile=Cannot read JAR file {0}
+fileClassLoader.restricted=Cannot load restricted class {0}
+standardLoader.addRepository=Adding repository {0}
+standardLoader.alreadyStarted=Loader has already been started
+standardLoader.checkInterval=Cannot set reload check interval to {0} seconds
+standardLoader.notContext=Cannot auto-reload unless our Container is a Context
+standardLoader.notReloadabe=Reloadable property is set to false
+standardLoader.notStarted=Loader has not yet been started
+standardLoader.reloadable=Cannot set reloadable property to {0}
+standardLoader.reloading=Reloading checks are enabled for this Context
+standardLoader.removeRepository=Removing repository {0}
+standardLoader.starting=Starting this Loader
+standardLoader.stopping=Stopping this Loader
+webappClassLoader.stopped=Illegal access: this web application instance has been stopped already.  Could not load {0}.  The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
+webappLoader.addRepository=Adding repository {0}
+webappLoader.deploy=Deploying class repositories to work directory {0}
+webappLoader.jarDeploy=Deploy JAR {0} to {1}
+webappLoader.classDeploy=Deploy class files {0} to {1}
+webappLoader.alreadyStarted=Loader has already been started
+webappLoader.checkInterval=Cannot set reload check interval to {0} seconds
+webappLoader.notContext=Cannot auto-reload unless our Container is a Context
+webappLoader.notReloadabe=Reloadable property is set to false
+webappLoader.notStarted=Loader has not yet been started
+webappLoader.reloadable=Cannot set reloadable property to {0}
+webappLoader.reloading=Reloading checks are enabled for this Context
+webappLoader.removeRepository=Removing repository {0}
+webappLoader.starting=Starting this Loader
+webappLoader.stopping=Stopping this Loader
+webappLoader.failModifiedCheck=Error tracking modifications
diff --git a/container/catalina/src/share/org/apache/catalina/loader/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/loader/LocalStrings_es.properties
new file mode 100644
index 0000000..2e7c95b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/LocalStrings_es.properties
@@ -0,0 +1,31 @@
+fileClassLoader.canRead=No se puede leer el Repositorio {0}
+fileClassLoader.exists=No existe el Repositorio {0}
+fileClassLoader.jarFile=No puedo leer archivo JAR {0}
+fileClassLoader.restricted=No puedo cargar clase restringida {0}
+standardLoader.addRepository=Añadiendo repositorio {0}
+standardLoader.alreadyStarted=Ya ha sido arrancado el Cargador
+standardLoader.checkInterval=No puedo poner el intervalo de revisión de recarga a {0} segundos
+standardLoader.notContext=No puedo auto-recargar a menos que nuestro Contenedor sea un Contexto
+standardLoader.notReloadabe=La propiedad Recargable está puesta a falsa
+standardLoader.notStarted=Aún no se ha arrancado el Cargador
+standardLoader.reloadable=No puedo poner la propiedad recargable a {0}
+standardLoader.reloading=Se han activado las revisiones de Recarga para este Contexto
+standardLoader.removeRepository=Quitando repositorio {0}
+standardLoader.starting=Arrancando este Cargador
+standardLoader.stopping=Parando este Cargador
+webappClassLoader.stopped=Acceso ilegal: esta instancia de aplicación web ya ha sido parada.  Could not load {0}.  La eventual traza de pila que sigue ha sido motivada por un error lanzado con motivos de depuración así como para intentar terminar el hilo que motivó el acceso ilegal y no tiene impacto funcional.
+webappLoader.addRepository=Añadiendo repositorio {0}
+webappLoader.deploy=Desplegando repositorios de clase en directorio de trabajo {0}
+webappLoader.jarDeploy=Despliegue del JAR {0} en {1}
+webappLoader.classDeploy=Despliegue de archivos de clase {0} en {1}
+webappLoader.alreadyStarted=Ya se ha arrancado el Cargador
+webappLoader.checkInterval=No puedo poner el intervalo de revisión de recarga a {0} segundos
+webappLoader.notContext=No puedo auto-recargar a menos que nuestro Contenedor sea un Contexto
+webappLoader.notReloadabe=Se ha puesto la propiedad Recargable a falsa
+webappLoader.notStarted=Aún no se ha arrancado el Cargador
+webappLoader.reloadable=No puedo poner la propiedad recargable a {0}
+webappLoader.reloading=Se han activado los chequeos de Recarga para este Contexto
+webappLoader.removeRepository=Quitando repositorio {0}
+webappLoader.starting=Arrancando este Cargador
+webappLoader.stopping=Parando este Cargador
+webappLoader.failModifiedCheck=Modificaciones de pista de error
diff --git a/container/catalina/src/share/org/apache/catalina/loader/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/loader/LocalStrings_ja.properties
new file mode 100644
index 0000000..4b1e56b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/LocalStrings_ja.properties
@@ -0,0 +1,31 @@
+fileClassLoader.canRead=\u30ea\u30dd\u30b8\u30c8\u30ea {0} \u304c\u8aad\u3081\u307e\u305b\u3093
+fileClassLoader.exists=\u30ea\u30dd\u30b8\u30c8\u30ea {0} \u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+fileClassLoader.jarFile=JAR\u30d5\u30a1\u30a4\u30eb {0} \u304c\u8aad\u3081\u307e\u305b\u3093
+fileClassLoader.restricted=\u5236\u9650\u3055\u308c\u305f\u30af\u30e9\u30b9 {0} \u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093
+standardLoader.addRepository=\u30ea\u30dd\u30b8\u30c8\u30ea {0} \u3092\u8ffd\u52a0\u3057\u307e\u3059
+standardLoader.alreadyStarted=\u30ed\u30fc\u30c0\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardLoader.checkInterval=\u518d\u30ed\u30fc\u30c9\u30c1\u30a7\u30c3\u30af\u9593\u9694\u3092{0}\u79d2\u306b\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093
+standardLoader.notContext=\u3053\u306e\u30b3\u30f3\u30c6\u30ca\u304c\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3067\u306a\u3051\u308c\u3070\u3001\u81ea\u52d5\u518d\u30ed\u30fc\u30c9\u306f\u3067\u304d\u307e\u305b\u3093
+standardLoader.notReloadabe=reloadable\u30d7\u30ed\u30d1\u30c6\u30a3\u304cfalse\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059
+standardLoader.notStarted=\u30ed\u30fc\u30c0\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardLoader.reloadable=reloadable\u30d7\u30ed\u30d1\u30c6\u30a3\u3092 {0} \u306b\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093
+standardLoader.reloading=\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u518d\u30ed\u30fc\u30c9\u30c1\u30a7\u30c3\u30af\u306f\u6709\u52b9\u3067\u3059
+standardLoader.removeRepository=\u30ea\u30dd\u30b8\u30c8\u30ea {0} \u3092\u524a\u9664\u3057\u307e\u3059
+standardLoader.starting=\u3053\u306e\u30ed\u30fc\u30c0\u3092\u8d77\u52d5\u3057\u307e\u3059
+standardLoader.stopping=\u3053\u306e\u30ed\u30fc\u30c0\u3092\u505c\u6b62\u3057\u307e\u3059
+webappClassLoader.stopped=\u4e0d\u6b63\u306a\u30a2\u30af\u30bb\u30b9: \u3053\u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306f\u65e2\u306b\u505c\u6b62\u3055\u308c\u3066\u3044\u307e\u3059  Could not load {0}. \u4e0d\u6b63\u306a\u30a2\u30af\u30bb\u30b9\u3092\u5f15\u304d\u8d77\u3053\u3057\u305f\u30b9\u30ec\u30c3\u30c9\u3092\u7d42\u4e86\u3055\u305b\u3001\u6295\u3052\u3089\u308c\u305f\u30a8\u30e9\u30fc\u306b\u3088\u308a\u30c7\u30d0\u30c3\u30b0\u7528\u306b\u6b21\u306e\u30b9\u30bf\u30c3\u30af\u30c8\u30ec\u30fc\u30b9\u304c\u751f\u6210\u3055\u308c\u307e\u3057\u305f\u304c\uff0c\u6a5f\u80fd\u306b\u5f71\u97ff\u306f\u3042\u308a\u307e\u305b\u3093
+webappLoader.addRepository=\u30ea\u30dd\u30b8\u30c8\u30ea {0} \u3092\u8ffd\u52a0\u3057\u307e\u3059
+webappLoader.deploy=\u30af\u30e9\u30b9\u30ea\u30dd\u30b8\u30c8\u30ea\u3092\u4f5c\u696d\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u306b\u914d\u5099\u3057\u307e\u3059
+webappLoader.jarDeploy=JAR {0} \u3092 {1} \u306b\u914d\u5099\u3057\u307e\u3059
+webappLoader.classDeploy=\u30af\u30e9\u30b9\u30d5\u30a1\u30a4\u30eb {0} \u3092 {1} \u306b\u914d\u5099\u3057\u307e\u3059
+webappLoader.alreadyStarted=\u30ed\u30fc\u30c0\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+webappLoader.checkInterval=\u518d\u30ed\u30fc\u30c9\u30c1\u30a7\u30c3\u30af\u9593\u9694\u3092{0}\u79d2\u306b\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093
+webappLoader.notContext=\u3053\u306e\u30b3\u30f3\u30c6\u30ca\u304c\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3067\u306a\u3051\u308c\u3070\u3001\u81ea\u52d5\u518d\u30ed\u30fc\u30c9\u306f\u3067\u304d\u307e\u305b\u3093
+webappLoader.notReloadabe=reloadable\u30d7\u30ed\u30d1\u30c6\u30a3\u304cfalse\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059
+webappLoader.notStarted=\u30ed\u30fc\u30c0\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+webappLoader.reloadable=reloadable\u30d7\u30ed\u30d1\u30c6\u30a3\u3092 {0} \u306b\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093
+webappLoader.reloading=\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u518d\u30ed\u30fc\u30c9\u30c1\u30a7\u30c3\u30af\u306f\u6709\u52b9\u3067\u3059
+webappLoader.removeRepository=\u30ea\u30dd\u30b8\u30c8\u30ea {0} \u3092\u524a\u9664\u3057\u307e\u3059
+webappLoader.starting=\u3053\u306e\u30ed\u30fc\u30c0\u3092\u8d77\u52d5\u3057\u307e\u3059
+webappLoader.stopping=\u3053\u306e\u30ed\u30fc\u30c0\u3092\u505c\u6b62\u3057\u307e\u3059
+webappLoader.failModifiedCheck=\u5909\u66f4\u8ffd\u8de1\u30a8\u30e9\u30fc\u3067\u3059
diff --git a/container/catalina/src/share/org/apache/catalina/loader/Reloader.java b/container/catalina/src/share/org/apache/catalina/loader/Reloader.java
new file mode 100644
index 0000000..9b18b52
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/Reloader.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.loader;
+
+
+/**
+ * Internal interface that <code>ClassLoader</code> implementations may
+ * optionally implement to support the auto-reload functionality of
+ * the classloader associated with the context.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface Reloader {
+
+
+    /**
+     * Add a new repository to the set of places this ClassLoader can look for
+     * classes to be loaded.
+     *
+     * @param repository Name of a source of classes to be loaded, such as a
+     *  directory pathname, a JAR file pathname, or a ZIP file pathname
+     *
+     * @exception IllegalArgumentException if the specified repository is
+     *  invalid or does not exist
+     */
+    public void addRepository(String repository);
+
+
+    /**
+     * Return a String array of the current repositories for this class
+     * loader.  If there are no repositories, a zero-length array is
+     * returned.
+     */
+    public String[] findRepositories();
+
+
+    /**
+     * Have one or more classes or resources been modified so that a reload
+     * is appropriate?
+     */
+    public boolean modified();
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/loader/ResourceEntry.java b/container/catalina/src/share/org/apache/catalina/loader/ResourceEntry.java
new file mode 100644
index 0000000..a6db6c6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/ResourceEntry.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.loader;
+
+import java.net.URL;
+import java.security.cert.Certificate;
+import java.util.jar.Manifest;
+
+/**
+ * Resource entry.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+public class ResourceEntry {
+
+
+    /**
+     * The "last modified" time of the origin file at the time this class
+     * was loaded, in milliseconds since the epoch.
+     */
+    public long lastModified = -1;
+
+
+    /**
+     * Binary content of the resource.
+     */
+    public byte[] binaryContent = null;
+
+
+    /**
+     * Loaded class.
+     */
+    public Class loadedClass = null;
+
+
+    /**
+     * URL source from where the object was loaded.
+     */
+    public URL source = null;
+
+
+    /**
+     * URL of the codebase from where the object was loaded.
+     */
+    public URL codeBase = null;
+
+
+    /**
+     * Manifest (if the resource was loaded from a JAR).
+     */
+    public Manifest manifest = null;
+
+
+    /**
+     * Certificates (if the resource was loaded from a JAR).
+     */
+    public Certificate[] certificates = null;
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/loader/StandardClassLoader.java b/container/catalina/src/share/org/apache/catalina/loader/StandardClassLoader.java
new file mode 100644
index 0000000..47a4fa2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/StandardClassLoader.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.loader;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * Subclass implementation of <b>java.net.URLClassLoader</b> that knows how
+ * to load classes from disk directories, as well as local and remote JAR
+ * files.  It also implements the <code>Reloader</code> interface, to provide
+ * automatic reloading support to the associated loader.
+ * <p>
+ * In all cases, URLs must conform to the contract specified by
+ * <code>URLClassLoader</code> - any URL that ends with a "/" character is
+ * assumed to represent a directory; all other URLs are assumed to be the
+ * address of a JAR file.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
+ * the order they are added via the initial constructor and/or any subsequent
+ * calls to <code>addRepository()</code>.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - At present, there are no dependencies
+ * from this class to any other Catalina class, so that it could be used
+ * independently.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class StandardClassLoader
+    extends URLClassLoader
+    implements StandardClassLoaderMBean {
+
+	public StandardClassLoader(URL repositories[]) {
+        super(repositories);
+    }
+
+    public StandardClassLoader(URL repositories[], ClassLoader parent) {
+        super(repositories, parent);
+    }
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/loader/StandardClassLoaderMBean.java b/container/catalina/src/share/org/apache/catalina/loader/StandardClassLoaderMBean.java
new file mode 100644
index 0000000..b345c0d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/StandardClassLoaderMBean.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.loader;
+
+/**
+ * MBean interface for StandardClassLoader, to allow JMX remote management.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+public interface StandardClassLoaderMBean {
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java b/container/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java
new file mode 100644
index 0000000..7ac64f4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java
@@ -0,0 +1,2166 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.loader;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes.Name;
+
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.util.StringManager;
+import org.apache.naming.JndiPermission;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.ResourceAttributes;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * Specialized web application class loader.
+ * <p>
+ * This class loader is a full reimplementation of the 
+ * <code>URLClassLoader</code> from the JDK. It is desinged to be fully
+ * compatible with a normal <code>URLClassLoader</code>, although its internal
+ * behavior may be completely different.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - This class loader faithfully follows 
+ * the delegation model recommended in the specification. The system class 
+ * loader will be queried first, then the local repositories, and only then 
+ * delegation to the parent class loader will occur. This allows the web 
+ * application to override any shared class except the classes from J2SE.
+ * Special handling is provided from the JAXP XML parser interfaces, the JNDI
+ * interfaces, and the classes from the servlet API, which are never loaded 
+ * from the webapp repository.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - Due to limitations in Jasper 
+ * compilation technology, any repository which contains classes from 
+ * the servlet API will be ignored by the class loader.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - The class loader generates source
+ * URLs which include the full JAR URL when a class is loaded from a JAR file,
+ * which allows setting security permission at the class level, even when a
+ * class is contained inside a JAR.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
+ * the order they are added via the initial constructor and/or any subsequent
+ * calls to <code>addRepository()</code> or <code>addJar()</code>.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or
+ * security is made unless a security manager is present.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+public class WebappClassLoader
+    extends URLClassLoader
+    implements Reloader, Lifecycle
+ {
+
+    protected static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( WebappClassLoader.class );
+
+    protected class PrivilegedFindResource
+        implements PrivilegedAction {
+
+        protected File file;
+        protected String path;
+
+        PrivilegedFindResource(File file, String path) {
+            this.file = file;
+            this.path = path;
+        }
+
+        public Object run() {
+            return findResourceInternal(file, path);
+        }
+
+    }
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The set of trigger classes that will cause a proposed repository not
+     * to be added if this class is visible to the class loader that loaded
+     * this factory class.  Typically, trigger classes will be listed for
+     * components that have been integrated into the JDK for later versions,
+     * but where the corresponding JAR files are required to run on
+     * earlier versions.
+     */
+    protected static final String[] triggers = {
+        "javax.servlet.Servlet"                     // Servlet API
+    };
+
+    /** 
+     * Jdk Compatibility Support.
+     */
+    protected static JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+    /**
+     * Set of package names which are not allowed to be loaded from a webapp
+     * class loader without delegating first.
+     */
+    protected static final String[] packageTriggers = {
+    };
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    
+    /**
+     * Use anti JAR locking code, which does URL rerouting when accessing
+     * resources.
+     */
+    boolean antiJARLocking = false; 
+    
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new ClassLoader with no defined repositories and no
+     * parent ClassLoader.
+     */
+    public WebappClassLoader() {
+
+        super(new URL[0]);
+        this.parent = getParent();
+        system = getSystemClassLoader();
+        securityManager = System.getSecurityManager();
+
+        if (securityManager != null) {
+            refreshPolicy();
+        }
+
+    }
+
+
+    /**
+     * Construct a new ClassLoader with no defined repositories and no
+     * parent ClassLoader.
+     */
+    public WebappClassLoader(ClassLoader parent) {
+
+        super(new URL[0], parent);
+                
+        this.parent = getParent();
+        
+        system = getSystemClassLoader();
+        securityManager = System.getSecurityManager();
+
+        if (securityManager != null) {
+            refreshPolicy();
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated directory context giving access to the resources in this
+     * webapp.
+     */
+    protected DirContext resources = null;
+
+
+    /**
+     * The cache of ResourceEntry for classes and resources we have loaded,
+     * keyed by resource name.
+     */
+    protected HashMap resourceEntries = new HashMap();
+
+
+    /**
+     * The list of not found resources.
+     */
+    protected HashMap notFoundResources = new HashMap();
+
+
+    /**
+     * Should this class loader delegate to the parent class loader
+     * <strong>before</strong> searching its own repositories (i.e. the
+     * usual Java2 delegation model)?  If set to <code>false</code>,
+     * this class loader will search its own repositories first, and
+     * delegate to the parent only if the class or resource is not
+     * found locally.
+     */
+    protected boolean delegate = false;
+
+
+    /**
+     * Last time a JAR was accessed.
+     */
+    protected long lastJarAccessed = 0L;
+
+
+    /**
+     * The list of local repositories, in the order they should be searched
+     * for locally loaded classes or resources.
+     */
+    protected String[] repositories = new String[0];
+
+
+     /**
+      * Repositories URLs, used to cache the result of getURLs.
+      */
+     protected URL[] repositoryURLs = null;
+
+
+    /**
+     * Repositories translated as path in the work directory (for Jasper
+     * originally), but which is used to generate fake URLs should getURLs be
+     * called.
+     */
+    protected File[] files = new File[0];
+
+
+    /**
+     * The list of JARs, in the order they should be searched
+     * for locally loaded classes or resources.
+     */
+    protected JarFile[] jarFiles = new JarFile[0];
+
+
+    /**
+     * The list of JARs, in the order they should be searched
+     * for locally loaded classes or resources.
+     */
+    protected File[] jarRealFiles = new File[0];
+
+
+    /**
+     * The path which will be monitored for added Jar files.
+     */
+    protected String jarPath = null;
+
+
+    /**
+     * The list of JARs, in the order they should be searched
+     * for locally loaded classes or resources.
+     */
+    protected String[] jarNames = new String[0];
+
+
+    /**
+     * The list of JARs last modified dates, in the order they should be
+     * searched for locally loaded classes or resources.
+     */
+    protected long[] lastModifiedDates = new long[0];
+
+
+    /**
+     * The list of resources which should be checked when checking for
+     * modifications.
+     */
+    protected String[] paths = new String[0];
+
+
+    /**
+     * A list of read File and Jndi Permission's required if this loader
+     * is for a web application context.
+     */
+    protected ArrayList permissionList = new ArrayList();
+
+
+    /**
+     * Path where resources loaded from JARs will be extracted.
+     */
+    protected File loaderDir = null;
+
+
+    /**
+     * The PermissionCollection for each CodeSource for a web
+     * application context.
+     */
+    protected HashMap loaderPC = new HashMap();
+
+
+    /**
+     * Instance of the SecurityManager installed.
+     */
+    protected SecurityManager securityManager = null;
+
+
+    /**
+     * The parent class loader.
+     */
+    protected ClassLoader parent = null;
+
+
+    /**
+     * The system class loader.
+     */
+    protected ClassLoader system = null;
+
+
+    /**
+     * Has this component been started?
+     */
+    protected boolean started = false;
+
+
+    /**
+     * Has external repositories.
+     */
+    protected boolean hasExternalRepositories = false;
+
+    /**
+     * need conversion for properties files
+     */
+    protected boolean needConvert = false;
+
+
+    /**
+     * All permission.
+     */
+    protected Permission allPermission = new java.security.AllPermission();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Get associated resources.
+     */
+    public DirContext getResources() {
+
+        return this.resources;
+
+    }
+
+
+    /**
+     * Set associated resources.
+     */
+    public void setResources(DirContext resources) {
+
+        this.resources = resources;
+
+    }
+
+
+    /**
+     * Return the "delegate first" flag for this class loader.
+     */
+    public boolean getDelegate() {
+
+        return (this.delegate);
+
+    }
+
+
+    /**
+     * Set the "delegate first" flag for this class loader.
+     *
+     * @param delegate The new "delegate first" flag
+     */
+    public void setDelegate(boolean delegate) {
+
+        this.delegate = delegate;
+
+    }
+
+
+    /**
+     * @return Returns the antiJARLocking.
+     */
+    public boolean getAntiJARLocking() {
+        return antiJARLocking;
+    }
+    
+    
+    /**
+     * @param antiJARLocking The antiJARLocking to set.
+     */
+    public void setAntiJARLocking(boolean antiJARLocking) {
+        this.antiJARLocking = antiJARLocking;
+    }
+
+    
+    /**
+     * If there is a Java SecurityManager create a read FilePermission
+     * or JndiPermission for the file directory path.
+     *
+     * @param path file directory path
+     */
+    public void addPermission(String path) {
+        if (path == null) {
+            return;
+        }
+
+        if (securityManager != null) {
+            Permission permission = null;
+            if( path.startsWith("jndi:") || path.startsWith("jar:jndi:") ) {
+                if (!path.endsWith("/")) {
+                    path = path + "/";
+                }
+                permission = new JndiPermission(path + "*");
+                addPermission(permission);
+            } else {
+                if (!path.endsWith(File.separator)) {
+                    permission = new FilePermission(path, "read");
+                    addPermission(permission);
+                    path = path + File.separator;
+                }
+                permission = new FilePermission(path + "-", "read");
+                addPermission(permission);
+            }
+        }
+    }
+
+
+    /**
+     * If there is a Java SecurityManager create a read FilePermission
+     * or JndiPermission for URL.
+     *
+     * @param url URL for a file or directory on local system
+     */
+    public void addPermission(URL url) {
+        if (url != null) {
+            addPermission(url.toString());
+        }
+    }
+
+
+    /**
+     * If there is a Java SecurityManager create a Permission.
+     *
+     * @param permission The permission
+     */
+    public void addPermission(Permission permission) {
+        if ((securityManager != null) && (permission != null)) {
+            permissionList.add(permission);
+        }
+    }
+
+
+    /**
+     * Return the JAR path.
+     */
+    public String getJarPath() {
+
+        return this.jarPath;
+
+    }
+
+
+    /**
+     * Change the Jar path.
+     */
+    public void setJarPath(String jarPath) {
+
+        this.jarPath = jarPath;
+
+    }
+
+
+    /**
+     * Change the work directory.
+     */
+    public void setWorkDir(File workDir) {
+        this.loaderDir = new File(workDir, "loader");
+    }
+
+
+    // ------------------------------------------------------- Reloader Methods
+
+
+    /**
+     * Add a new repository to the set of places this ClassLoader can look for
+     * classes to be loaded.
+     *
+     * @param repository Name of a source of classes to be loaded, such as a
+     *  directory pathname, a JAR file pathname, or a ZIP file pathname
+     *
+     * @exception IllegalArgumentException if the specified repository is
+     *  invalid or does not exist
+     */
+    public void addRepository(String repository) {
+
+        // Ignore any of the standard repositories, as they are set up using
+        // either addJar or addRepository
+        if (repository.startsWith("/WEB-INF/lib")
+            || repository.startsWith("/WEB-INF/classes"))
+            return;
+
+        // Add this repository to our underlying class loader
+        try {
+            URL url = new URL(repository);
+            super.addURL(url);
+            hasExternalRepositories = true;
+            repositoryURLs = null;
+        } catch (MalformedURLException e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Invalid repository: " + repository); 
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+
+    }
+
+
+    /**
+     * Add a new repository to the set of places this ClassLoader can look for
+     * classes to be loaded.
+     *
+     * @param repository Name of a source of classes to be loaded, such as a
+     *  directory pathname, a JAR file pathname, or a ZIP file pathname
+     *
+     * @exception IllegalArgumentException if the specified repository is
+     *  invalid or does not exist
+     */
+    synchronized void addRepository(String repository, File file) {
+
+        // Note : There should be only one (of course), but I think we should
+        // keep this a bit generic
+
+        if (repository == null)
+            return;
+
+        if (log.isDebugEnabled())
+            log.debug("addRepository(" + repository + ")");
+
+        int i;
+
+        // Add this repository to our internal list
+        String[] result = new String[repositories.length + 1];
+        for (i = 0; i < repositories.length; i++) {
+            result[i] = repositories[i];
+        }
+        result[repositories.length] = repository;
+        repositories = result;
+
+        // Add the file to the list
+        File[] result2 = new File[files.length + 1];
+        for (i = 0; i < files.length; i++) {
+            result2[i] = files[i];
+        }
+        result2[files.length] = file;
+        files = result2;
+
+    }
+
+
+    synchronized void addJar(String jar, JarFile jarFile, File file)
+        throws IOException {
+
+        if (jar == null)
+            return;
+        if (jarFile == null)
+            return;
+        if (file == null)
+            return;
+
+        if (log.isDebugEnabled())
+            log.debug("addJar(" + jar + ")");
+
+        int i;
+
+        if ((jarPath != null) && (jar.startsWith(jarPath))) {
+
+            String jarName = jar.substring(jarPath.length());
+            while (jarName.startsWith("/"))
+                jarName = jarName.substring(1);
+
+            String[] result = new String[jarNames.length + 1];
+            for (i = 0; i < jarNames.length; i++) {
+                result[i] = jarNames[i];
+            }
+            result[jarNames.length] = jarName;
+            jarNames = result;
+
+        }
+
+        try {
+
+            // Register the JAR for tracking
+
+            long lastModified =
+                ((ResourceAttributes) resources.getAttributes(jar))
+                .getLastModified();
+
+            String[] result = new String[paths.length + 1];
+            for (i = 0; i < paths.length; i++) {
+                result[i] = paths[i];
+            }
+            result[paths.length] = jar;
+            paths = result;
+
+            long[] result3 = new long[lastModifiedDates.length + 1];
+            for (i = 0; i < lastModifiedDates.length; i++) {
+                result3[i] = lastModifiedDates[i];
+            }
+            result3[lastModifiedDates.length] = lastModified;
+            lastModifiedDates = result3;
+
+        } catch (NamingException e) {
+            // Ignore
+        }
+
+        // If the JAR currently contains invalid classes, don't actually use it
+        // for classloading
+        if (!validateJarFile(file))
+            return;
+
+        JarFile[] result2 = new JarFile[jarFiles.length + 1];
+        for (i = 0; i < jarFiles.length; i++) {
+            result2[i] = jarFiles[i];
+        }
+        result2[jarFiles.length] = jarFile;
+        jarFiles = result2;
+
+        // Add the file to the list
+        File[] result4 = new File[jarRealFiles.length + 1];
+        for (i = 0; i < jarRealFiles.length; i++) {
+            result4[i] = jarRealFiles[i];
+        }
+        result4[jarRealFiles.length] = file;
+        jarRealFiles = result4;
+    }
+
+
+    /**
+     * Return a String array of the current repositories for this class
+     * loader.  If there are no repositories, a zero-length array is
+     * returned.For security reason, returns a clone of the Array (since 
+     * String are immutable).
+     */
+    public String[] findRepositories() {
+
+        return ((String[])repositories.clone());
+
+    }
+
+
+    /**
+     * Have one or more classes or resources been modified so that a reload
+     * is appropriate?
+     */
+    public boolean modified() {
+
+        if (log.isDebugEnabled())
+            log.debug("modified()");
+
+        // Checking for modified loaded resources
+        int length = paths.length;
+
+        // A rare race condition can occur in the updates of the two arrays
+        // It's totally ok if the latest class added is not checked (it will
+        // be checked the next time
+        int length2 = lastModifiedDates.length;
+        if (length > length2)
+            length = length2;
+
+        for (int i = 0; i < length; i++) {
+            try {
+                long lastModified =
+                    ((ResourceAttributes) resources.getAttributes(paths[i]))
+                    .getLastModified();
+                if (lastModified != lastModifiedDates[i]) {
+                    if( log.isDebugEnabled() ) 
+                        log.debug("  Resource '" + paths[i]
+                                  + "' was modified; Date is now: "
+                                  + new java.util.Date(lastModified) + " Was: "
+                                  + new java.util.Date(lastModifiedDates[i]));
+                    return (true);
+                }
+            } catch (NamingException e) {
+                log.error("    Resource '" + paths[i] + "' is missing");
+                return (true);
+            }
+        }
+
+        length = jarNames.length;
+
+        // Check if JARs have been added or removed
+        if (getJarPath() != null) {
+
+            try {
+                NamingEnumeration enumeration = resources.listBindings(getJarPath());
+                int i = 0;
+                while (enumeration.hasMoreElements() && (i < length)) {
+                    NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
+                    String name = ncPair.getName();
+                    // Ignore non JARs present in the lib folder
+                    if (!name.endsWith(".jar"))
+                        continue;
+                    if (!name.equals(jarNames[i])) {
+                        // Missing JAR
+                        log.info("    Additional JARs have been added : '" 
+                                 + name + "'");
+                        return (true);
+                    }
+                    i++;
+                }
+                if (enumeration.hasMoreElements()) {
+                    while (enumeration.hasMoreElements()) {
+                        NameClassPair ncPair = 
+                            (NameClassPair) enumeration.nextElement();
+                        String name = ncPair.getName();
+                        // Additional non-JAR files are allowed
+                        if (name.endsWith(".jar")) {
+                            // There was more JARs
+                            log.info("    Additional JARs have been added");
+                            return (true);
+                        }
+                    }
+                } else if (i < jarNames.length) {
+                    // There was less JARs
+                    log.info("    Additional JARs have been added");
+                    return (true);
+                }
+            } catch (NamingException e) {
+                if (log.isDebugEnabled())
+                    log.debug("    Failed tracking modifications of '"
+                        + getJarPath() + "'");
+            } catch (ClassCastException e) {
+                log.error("    Failed tracking modifications of '"
+                          + getJarPath() + "' : " + e.getMessage());
+            }
+
+        }
+
+        // No classes have been modified
+        return (false);
+
+    }
+
+
+    /**
+     * Render a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("WebappClassLoader\r\n");
+        sb.append("  delegate: ");
+        sb.append(delegate);
+        sb.append("\r\n");
+        sb.append("  repositories:\r\n");
+        if (repositories != null) {
+            for (int i = 0; i < repositories.length; i++) {
+                sb.append("    ");
+                sb.append(repositories[i]);
+                sb.append("\r\n");
+            }
+        }
+        if (this.parent != null) {
+            sb.append("----------> Parent Classloader:\r\n");
+            sb.append(this.parent.toString());
+            sb.append("\r\n");
+        }
+        return (sb.toString());
+
+    }
+
+
+    // ---------------------------------------------------- ClassLoader Methods
+
+
+     /**
+      * Add the specified URL to the classloader.
+      */
+     protected void addURL(URL url) {
+         super.addURL(url);
+         hasExternalRepositories = true;
+         repositoryURLs = null;
+     }
+
+
+    /**
+     * Find the specified class in our local repositories, if possible.  If
+     * not found, throw <code>ClassNotFoundException</code>.
+     *
+     * @param name Name of the class to be loaded
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class findClass(String name) throws ClassNotFoundException {
+
+        if (log.isDebugEnabled())
+            log.debug("    findClass(" + name + ")");
+
+        // (1) Permission to define this class when using a SecurityManager
+        if (securityManager != null) {
+            int i = name.lastIndexOf('.');
+            if (i >= 0) {
+                try {
+                    if (log.isTraceEnabled())
+                        log.trace("      securityManager.checkPackageDefinition");
+                    securityManager.checkPackageDefinition(name.substring(0,i));
+                } catch (Exception se) {
+                    if (log.isTraceEnabled())
+                        log.trace("      -->Exception-->ClassNotFoundException", se);
+                    throw new ClassNotFoundException(name, se);
+                }
+            }
+        }
+
+        // Ask our superclass to locate this class, if possible
+        // (throws ClassNotFoundException if it is not found)
+        Class clazz = null;
+        try {
+            if (log.isTraceEnabled())
+                log.trace("      findClassInternal(" + name + ")");
+            try {
+                clazz = findClassInternal(name);
+            } catch(ClassNotFoundException cnfe) {
+                if (!hasExternalRepositories) {
+                    throw cnfe;
+                }
+            } catch(AccessControlException ace) {
+                throw new ClassNotFoundException(name, ace);
+            } catch (RuntimeException e) {
+                if (log.isTraceEnabled())
+                    log.trace("      -->RuntimeException Rethrown", e);
+                throw e;
+            }
+            if ((clazz == null) && hasExternalRepositories) {
+                try {
+                    clazz = super.findClass(name);
+                } catch(AccessControlException ace) {
+                    throw new ClassNotFoundException(name, ace);
+                } catch (RuntimeException e) {
+                    if (log.isTraceEnabled())
+                        log.trace("      -->RuntimeException Rethrown", e);
+                    throw e;
+                }
+            }
+            if (clazz == null) {
+                if (log.isDebugEnabled())
+                    log.debug("    --> Returning ClassNotFoundException");
+                throw new ClassNotFoundException(name);
+            }
+        } catch (ClassNotFoundException e) {
+            if (log.isTraceEnabled())
+                log.trace("    --> Passing on ClassNotFoundException");
+            throw e;
+        }
+
+        // Return the class we have located
+        if (log.isTraceEnabled())
+            log.debug("      Returning class " + clazz);
+        if ((log.isTraceEnabled()) && (clazz != null))
+            log.debug("      Loaded by " + clazz.getClassLoader());
+        return (clazz);
+
+    }
+
+
+    /**
+     * Find the specified resource in our local repository, and return a
+     * <code>URL</code> refering to it, or <code>null</code> if this resource
+     * cannot be found.
+     *
+     * @param name Name of the resource to be found
+     */
+    public URL findResource(final String name) {
+
+        if (log.isDebugEnabled())
+            log.debug("    findResource(" + name + ")");
+
+        URL url = null;
+
+        ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+        if (entry == null) {
+            entry = findResourceInternal(name, name);
+        }
+        if (entry != null) {
+            url = entry.source;
+        }
+
+        if ((url == null) && hasExternalRepositories)
+            url = super.findResource(name);
+
+        if (log.isDebugEnabled()) {
+            if (url != null)
+                log.debug("    --> Returning '" + url.toString() + "'");
+            else
+                log.debug("    --> Resource not found, returning null");
+        }
+        return (url);
+
+    }
+
+
+    /**
+     * Return an enumeration of <code>URLs</code> representing all of the
+     * resources with the given name.  If no resources with this name are
+     * found, return an empty enumeration.
+     *
+     * @param name Name of the resources to be found
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public Enumeration findResources(String name) throws IOException {
+
+        if (log.isDebugEnabled())
+            log.debug("    findResources(" + name + ")");
+
+        Vector result = new Vector();
+
+        int jarFilesLength = jarFiles.length;
+        int repositoriesLength = repositories.length;
+
+        int i;
+
+        // Looking at the repositories
+        for (i = 0; i < repositoriesLength; i++) {
+            try {
+                String fullPath = repositories[i] + name;
+                resources.lookup(fullPath);
+                // Note : Not getting an exception here means the resource was
+                // found
+                try {
+                    result.addElement(getURI(new File(files[i], name)));
+                } catch (MalformedURLException e) {
+                    // Ignore
+                }
+            } catch (NamingException e) {
+            }
+        }
+
+        // Looking at the JAR files
+        synchronized (jarFiles) {
+            openJARs();
+            for (i = 0; i < jarFilesLength; i++) {
+                JarEntry jarEntry = jarFiles[i].getJarEntry(name);
+                if (jarEntry != null) {
+                    try {
+                        String jarFakeUrl = getURI(jarRealFiles[i]).toString();
+                        jarFakeUrl = "jar:" + jarFakeUrl + "!/" + name;
+                        result.addElement(new URL(jarFakeUrl));
+                    } catch (MalformedURLException e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+
+        // Adding the results of a call to the superclass
+        if (hasExternalRepositories) {
+
+            Enumeration otherResourcePaths = super.findResources(name);
+
+            while (otherResourcePaths.hasMoreElements()) {
+                result.addElement(otherResourcePaths.nextElement());
+            }
+
+        }
+
+        return result.elements();
+
+    }
+
+
+    /**
+     * Find the resource with the given name.  A resource is some data
+     * (images, audio, text, etc.) that can be accessed by class code in a
+     * way that is independent of the location of the code.  The name of a
+     * resource is a "/"-separated path name that identifies the resource.
+     * If the resource cannot be found, return <code>null</code>.
+     * <p>
+     * This method searches according to the following algorithm, returning
+     * as soon as it finds the appropriate URL.  If the resource cannot be
+     * found, returns <code>null</code>.
+     * <ul>
+     * <li>If the <code>delegate</code> property is set to <code>true</code>,
+     *     call the <code>getResource()</code> method of the parent class
+     *     loader, if any.</li>
+     * <li>Call <code>findResource()</code> to find this resource in our
+     *     locally defined repositories.</li>
+     * <li>Call the <code>getResource()</code> method of the parent class
+     *     loader, if any.</li>
+     * </ul>
+     *
+     * @param name Name of the resource to return a URL for
+     */
+    public URL getResource(String name) {
+
+        if (log.isDebugEnabled())
+            log.debug("getResource(" + name + ")");
+        URL url = null;
+
+        // (1) Delegate to parent if requested
+        if (delegate) {
+            if (log.isDebugEnabled())
+                log.debug("  Delegating to parent classloader " + parent);
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            url = loader.getResource(name);
+            if (url != null) {
+                if (log.isDebugEnabled())
+                    log.debug("  --> Returning '" + url.toString() + "'");
+                return (url);
+            }
+        }
+
+        // (2) Search local repositories
+        url = findResource(name);
+        if (url != null) {
+            // Locating the repository for special handling in the case 
+            // of a JAR
+            if (antiJARLocking) {
+                ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+                try {
+                    String repository = entry.codeBase.toString();
+                    if ((repository.endsWith(".jar")) 
+                            && (!(name.endsWith(".class")))) {
+                        // Copy binary content to the work directory if not present
+                        File resourceFile = new File(loaderDir, name);
+                        url = resourceFile.toURL();
+                    }
+                } catch (Exception e) {
+                    // Ignore
+                }
+            }
+            if (log.isDebugEnabled())
+                log.debug("  --> Returning '" + url.toString() + "'");
+            return (url);
+        }
+
+        // (3) Delegate to parent unconditionally if not already attempted
+        if( !delegate ) {
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            url = loader.getResource(name);
+            if (url != null) {
+                if (log.isDebugEnabled())
+                    log.debug("  --> Returning '" + url.toString() + "'");
+                return (url);
+            }
+        }
+
+        // (4) Resource was not found
+        if (log.isDebugEnabled())
+            log.debug("  --> Resource not found, returning null");
+        return (null);
+
+    }
+
+
+    /**
+     * Find the resource with the given name, and return an input stream
+     * that can be used for reading it.  The search order is as described
+     * for <code>getResource()</code>, after checking to see if the resource
+     * data has been previously cached.  If the resource cannot be found,
+     * return <code>null</code>.
+     *
+     * @param name Name of the resource to return an input stream for
+     */
+    public InputStream getResourceAsStream(String name) {
+
+        if (log.isDebugEnabled())
+            log.debug("getResourceAsStream(" + name + ")");
+        InputStream stream = null;
+
+        // (0) Check for a cached copy of this resource
+        stream = findLoadedResource(name);
+        if (stream != null) {
+            if (log.isDebugEnabled())
+                log.debug("  --> Returning stream from cache");
+            return (stream);
+        }
+
+        // (1) Delegate to parent if requested
+        if (delegate) {
+            if (log.isDebugEnabled())
+                log.debug("  Delegating to parent classloader " + parent);
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            stream = loader.getResourceAsStream(name);
+            if (stream != null) {
+                // FIXME - cache???
+                if (log.isDebugEnabled())
+                    log.debug("  --> Returning stream from parent");
+                return (stream);
+            }
+        }
+
+        // (2) Search local repositories
+        if (log.isDebugEnabled())
+            log.debug("  Searching local repositories");
+        URL url = findResource(name);
+        if (url != null) {
+            // FIXME - cache???
+            if (log.isDebugEnabled())
+                log.debug("  --> Returning stream from local");
+            stream = findLoadedResource(name);
+            try {
+                if (hasExternalRepositories && (stream == null))
+                    stream = url.openStream();
+            } catch (IOException e) {
+                ; // Ignore
+            }
+            if (stream != null)
+                return (stream);
+        }
+
+        // (3) Delegate to parent unconditionally
+        if (!delegate) {
+            if (log.isDebugEnabled())
+                log.debug("  Delegating to parent classloader unconditionally " + parent);
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            stream = loader.getResourceAsStream(name);
+            if (stream != null) {
+                // FIXME - cache???
+                if (log.isDebugEnabled())
+                    log.debug("  --> Returning stream from parent");
+                return (stream);
+            }
+        }
+
+        // (4) Resource was not found
+        if (log.isDebugEnabled())
+            log.debug("  --> Resource not found, returning null");
+        return (null);
+
+    }
+
+
+    /**
+     * Load the class with the specified name.  This method searches for
+     * classes in the same manner as <code>loadClass(String, boolean)</code>
+     * with <code>false</code> as the second argument.
+     *
+     * @param name Name of the class to be loaded
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class loadClass(String name) throws ClassNotFoundException {
+
+        return (loadClass(name, false));
+
+    }
+
+
+    /**
+     * Load the class with the specified name, searching using the following
+     * algorithm until it finds and returns the class.  If the class cannot
+     * be found, returns <code>ClassNotFoundException</code>.
+     * <ul>
+     * <li>Call <code>findLoadedClass(String)</code> to check if the
+     *     class has already been loaded.  If it has, the same
+     *     <code>Class</code> object is returned.</li>
+     * <li>If the <code>delegate</code> property is set to <code>true</code>,
+     *     call the <code>loadClass()</code> method of the parent class
+     *     loader, if any.</li>
+     * <li>Call <code>findClass()</code> to find this class in our locally
+     *     defined repositories.</li>
+     * <li>Call the <code>loadClass()</code> method of our parent
+     *     class loader, if any.</li>
+     * </ul>
+     * If the class was found using the above steps, and the
+     * <code>resolve</code> flag is <code>true</code>, this method will then
+     * call <code>resolveClass(Class)</code> on the resulting Class object.
+     *
+     * @param name Name of the class to be loaded
+     * @param resolve If <code>true</code> then resolve the class
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class loadClass(String name, boolean resolve)
+        throws ClassNotFoundException {
+
+        if (log.isDebugEnabled())
+            log.debug("loadClass(" + name + ", " + resolve + ")");
+        Class clazz = null;
+
+        // Don't load classes if class loader is stopped
+        if (!started) {
+            log.info(sm.getString("webappClassLoader.stopped", name));
+            throw new ThreadDeath();
+        }
+
+        // (0) Check our previously loaded local class cache
+        clazz = findLoadedClass0(name);
+        if (clazz != null) {
+            if (log.isDebugEnabled())
+                log.debug("  Returning class from cache");
+            if (resolve)
+                resolveClass(clazz);
+            return (clazz);
+        }
+
+        // (0.1) Check our previously loaded class cache
+        clazz = findLoadedClass(name);
+        if (clazz != null) {
+            if (log.isDebugEnabled())
+                log.debug("  Returning class from cache");
+            if (resolve)
+                resolveClass(clazz);
+            return (clazz);
+        }
+
+        // (0.2) Try loading the class with the system class loader, to prevent
+        //       the webapp from overriding J2SE classes
+        try {
+            clazz = system.loadClass(name);
+            if (clazz != null) {
+                if (resolve)
+                    resolveClass(clazz);
+                return (clazz);
+            }
+        } catch (ClassNotFoundException e) {
+            // Ignore
+        }
+
+        // (0.5) Permission to access this class when using a SecurityManager
+        if (securityManager != null) {
+            int i = name.lastIndexOf('.');
+            if (i >= 0) {
+                try {
+                    securityManager.checkPackageAccess(name.substring(0,i));
+                } catch (SecurityException se) {
+                    String error = "Security Violation, attempt to use " +
+                        "Restricted Class: " + name;
+                    log.info(error, se);
+                    throw new ClassNotFoundException(error, se);
+                }
+            }
+        }
+
+        boolean delegateLoad = delegate || filter(name);
+
+        // (1) Delegate to our parent if requested
+        if (delegateLoad) {
+            if (log.isDebugEnabled())
+                log.debug("  Delegating to parent classloader1 " + parent);
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            try {
+                clazz = loader.loadClass(name);
+                if (clazz != null) {
+                    if (log.isDebugEnabled())
+                        log.debug("  Loading class from parent");
+                    if (resolve)
+                        resolveClass(clazz);
+                    return (clazz);
+                }
+            } catch (ClassNotFoundException e) {
+                ;
+            }
+        }
+
+        // (2) Search local repositories
+        if (log.isDebugEnabled())
+            log.debug("  Searching local repositories");
+        try {
+            clazz = findClass(name);
+            if (clazz != null) {
+                if (log.isDebugEnabled())
+                    log.debug("  Loading class from local repository");
+                if (resolve)
+                    resolveClass(clazz);
+                return (clazz);
+            }
+        } catch (ClassNotFoundException e) {
+            ;
+        }
+
+        // (3) Delegate to parent unconditionally
+        if (!delegateLoad) {
+            if (log.isDebugEnabled())
+                log.debug("  Delegating to parent classloader at end: " + parent);
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            try {
+                clazz = loader.loadClass(name);
+                if (clazz != null) {
+                    if (log.isDebugEnabled())
+                        log.debug("  Loading class from parent");
+                    if (resolve)
+                        resolveClass(clazz);
+                    return (clazz);
+                }
+            } catch (ClassNotFoundException e) {
+                ;
+            }
+        }
+
+        throw new ClassNotFoundException(name);
+    }
+
+
+    /**
+     * Get the Permissions for a CodeSource.  If this instance
+     * of WebappClassLoader is for a web application context,
+     * add read FilePermission or JndiPermissions for the base
+     * directory (if unpacked),
+     * the context URL, and jar file resources.
+     *
+     * @param codeSource where the code was loaded from
+     * @return PermissionCollection for CodeSource
+     */
+    protected PermissionCollection getPermissions(CodeSource codeSource) {
+
+        String codeUrl = codeSource.getLocation().toString();
+        PermissionCollection pc;
+        if ((pc = (PermissionCollection)loaderPC.get(codeUrl)) == null) {
+            pc = super.getPermissions(codeSource);
+            if (pc != null) {
+                Iterator perms = permissionList.iterator();
+                while (perms.hasNext()) {
+                    Permission p = (Permission)perms.next();
+                    pc.add(p);
+                }
+                loaderPC.put(codeUrl,pc);
+            }
+        }
+        return (pc);
+
+    }
+
+
+    /**
+     * Returns the search path of URLs for loading classes and resources.
+     * This includes the original list of URLs specified to the constructor,
+     * along with any URLs subsequently appended by the addURL() method.
+     * @return the search path of URLs for loading classes and resources.
+     */
+    public URL[] getURLs() {
+
+        if (repositoryURLs != null) {
+            return repositoryURLs;
+        }
+
+        URL[] external = super.getURLs();
+
+        int filesLength = files.length;
+        int jarFilesLength = jarRealFiles.length;
+        int length = filesLength + jarFilesLength + external.length;
+        int i;
+
+        try {
+
+            URL[] urls = new URL[length];
+            for (i = 0; i < length; i++) {
+                if (i < filesLength) {
+                    urls[i] = getURL(files[i]);
+                } else if (i < filesLength + jarFilesLength) {
+                    urls[i] = getURL(jarRealFiles[i - filesLength]);
+                } else {
+                    urls[i] = external[i - filesLength - jarFilesLength];
+                }
+            }
+
+            repositoryURLs = urls;
+
+        } catch (MalformedURLException e) {
+            repositoryURLs = new URL[0];
+        }
+
+        return repositoryURLs;
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+        return new LifecycleListener[0];
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+    }
+
+
+    /**
+     * Start the class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    public void start() throws LifecycleException {
+
+        started = true;
+        String encoding = null;
+        try {
+            encoding = System.getProperty("file.encoding");
+        } catch (Exception e) {
+            return;
+        }
+        if (encoding.indexOf("EBCDIC")!=-1) {
+            needConvert = true;
+        }
+
+    }
+
+
+    /**
+     * Stop the class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    public void stop() throws LifecycleException {
+
+        started = false;
+
+        int length = files.length;
+        for (int i = 0; i < length; i++) {
+            files[i] = null;
+        }
+
+        length = jarFiles.length;
+        for (int i = 0; i < length; i++) {
+            try {
+                if (jarFiles[i] != null) {
+                    jarFiles[i].close();
+                }
+            } catch (IOException e) {
+                // Ignore
+            }
+            jarFiles[i] = null;
+        }
+
+        notFoundResources.clear();
+        resourceEntries.clear();
+        resources = null;
+        repositories = null;
+        repositoryURLs = null;
+        files = null;
+        jarFiles = null;
+        jarRealFiles = null;
+        jarPath = null;
+        jarNames = null;
+        lastModifiedDates = null;
+        paths = null;
+        hasExternalRepositories = false;
+        parent = null;
+
+        permissionList.clear();
+        loaderPC.clear();
+
+        if (loaderDir != null) {
+            deleteDir(loaderDir);
+        }
+
+        // Clear the classloader reference in common-logging
+        org.apache.commons.logging.LogFactory.release(this);
+        // Clear the classloader reference in the VM's bean introspector
+        java.beans.Introspector.flushCaches();
+
+    }
+
+
+    /**
+     * Used to periodically signal to the classloader to release 
+     * JAR resources.
+     */
+    public void closeJARs(boolean force) {
+        if (jarFiles.length > 0) {
+                synchronized (jarFiles) {
+                    if (force || (System.currentTimeMillis() 
+                                  > (lastJarAccessed + 90000))) {
+                        for (int i = 0; i < jarFiles.length; i++) {
+                            try {
+                            	if (jarFiles[i] != null) {
+                            		jarFiles[i].close();
+                            		jarFiles[i] = null;
+                            	}
+                            } catch (IOException e) {
+                                log.warn("Failed to close JAR", e);
+                            }
+                        }
+                    }
+                }
+        }
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Used to periodically signal to the classloader to release JAR resources.
+     */
+    protected void openJARs() {
+        if (started && (jarFiles.length > 0)) {
+            lastJarAccessed = System.currentTimeMillis();
+            if (jarFiles[0] == null) {
+                for (int i = 0; i < jarFiles.length; i++) {
+                	try {
+                		jarFiles[i] = new JarFile(jarRealFiles[i]);
+                	} catch (IOException e) {
+                		log.warn("Failed to open JAR", e);
+                	}
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Find specified class in local repositories.
+     *
+     * @return the loaded class, or null if the class isn't found
+     */
+    protected Class findClassInternal(String name)
+        throws ClassNotFoundException {
+
+        if (!validate(name))
+            throw new ClassNotFoundException(name);
+
+        String tempPath = name.replace('.', '/');
+        String classPath = tempPath + ".class";
+
+        ResourceEntry entry = null;
+
+        entry = findResourceInternal(name, classPath);
+
+        if (entry == null)
+            throw new ClassNotFoundException(name);
+
+        Class clazz = entry.loadedClass;
+        if (clazz != null)
+            return clazz;
+
+        synchronized (this) {
+            if (entry.binaryContent == null && entry.loadedClass == null)
+                throw new ClassNotFoundException(name);
+        }
+
+        // Looking up the package
+        String packageName = null;
+        int pos = name.lastIndexOf('.');
+        if (pos != -1)
+            packageName = name.substring(0, pos);
+
+        Package pkg = null;
+
+        if (packageName != null) {
+
+            pkg = getPackage(packageName);
+
+            // Define the package (if null)
+            if (pkg == null) {
+                if (entry.manifest == null) {
+                    definePackage(packageName, null, null, null, null, null,
+                                  null, null);
+                } else {
+                    definePackage(packageName, entry.manifest, entry.codeBase);
+                }
+            }
+
+        }
+
+        // Create the code source object
+        CodeSource codeSource =
+            new CodeSource(entry.codeBase, entry.certificates);
+
+        if (securityManager != null) {
+
+            // Checking sealing
+            if (pkg != null) {
+                boolean sealCheck = true;
+                if (pkg.isSealed()) {
+                    sealCheck = pkg.isSealed(entry.codeBase);
+                } else {
+                    sealCheck = (entry.manifest == null)
+                        || !isPackageSealed(packageName, entry.manifest);
+                }
+                if (!sealCheck)
+                    throw new SecurityException
+                        ("Sealing violation loading " + name + " : Package "
+                         + packageName + " is sealed.");
+            }
+
+        }
+
+        synchronized (this) {
+            if (entry.loadedClass == null) {
+                clazz = defineClass(name, entry.binaryContent, 0,
+                        entry.binaryContent.length, 
+                        codeSource);
+                entry.loadedClass = clazz;
+                entry.binaryContent = null;
+                entry.source = null;
+                entry.codeBase = null;
+                entry.manifest = null;
+                entry.certificates = null;
+            } else {
+                clazz = entry.loadedClass;
+            }
+        }
+        
+        return clazz;
+
+    }
+
+    /**
+     * Find specified resource in local repositories. This block
+     * will execute under an AccessControl.doPrivilege block.
+     *
+     * @return the loaded resource, or null if the resource isn't found
+     */
+    protected ResourceEntry findResourceInternal(File file, String path){
+        ResourceEntry entry = new ResourceEntry();
+        try {
+            entry.source = getURI(new File(file, path));
+            entry.codeBase = getURL(new File(file, path));
+        } catch (MalformedURLException e) {
+            return null;
+        }   
+        return entry;
+    }
+    
+
+    /**
+     * Find specified resource in local repositories.
+     *
+     * @return the loaded resource, or null if the resource isn't found
+     */
+    protected ResourceEntry findResourceInternal(String name, String path) {
+
+        if (!started) {
+            log.info(sm.getString("webappClassLoader.stopped", name));
+            return null;
+        }
+
+        if ((name == null) || (path == null))
+            return null;
+
+        ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+        if (entry != null)
+            return entry;
+
+        int contentLength = -1;
+        InputStream binaryStream = null;
+
+        int jarFilesLength = jarFiles.length;
+        int repositoriesLength = repositories.length;
+
+        int i;
+
+        Resource resource = null;
+
+        boolean fileNeedConvert = false;
+
+        for (i = 0; (entry == null) && (i < repositoriesLength); i++) {
+            try {
+
+                String fullPath = repositories[i] + path;
+
+                Object lookupResult = resources.lookup(fullPath);
+                if (lookupResult instanceof Resource) {
+                    resource = (Resource) lookupResult;
+                }
+
+                // Note : Not getting an exception here means the resource was
+                // found
+                 if (securityManager != null) {
+                    PrivilegedAction dp =
+                        new PrivilegedFindResource(files[i], path);
+                    entry = (ResourceEntry)AccessController.doPrivileged(dp);
+                 } else {
+                    entry = findResourceInternal(files[i], path);
+                 }
+
+                ResourceAttributes attributes =
+                    (ResourceAttributes) resources.getAttributes(fullPath);
+                contentLength = (int) attributes.getContentLength();
+                entry.lastModified = attributes.getLastModified();
+
+                if (resource != null) {
+
+                    try {
+                        binaryStream = resource.streamContent();
+                    } catch (IOException e) {
+                        return null;
+                    }
+
+                    if (needConvert) {
+                        if (path.endsWith(".properties")) {
+                            fileNeedConvert = true;
+                        }
+                    }
+
+                    // Register the full path for modification checking
+                    // Note: Only syncing on a 'constant' object is needed
+                    synchronized (allPermission) {
+
+                        int j;
+
+                        long[] result2 = 
+                            new long[lastModifiedDates.length + 1];
+                        for (j = 0; j < lastModifiedDates.length; j++) {
+                            result2[j] = lastModifiedDates[j];
+                        }
+                        result2[lastModifiedDates.length] = entry.lastModified;
+                        lastModifiedDates = result2;
+
+                        String[] result = new String[paths.length + 1];
+                        for (j = 0; j < paths.length; j++) {
+                            result[j] = paths[j];
+                        }
+                        result[paths.length] = fullPath;
+                        paths = result;
+
+                    }
+
+                }
+
+            } catch (NamingException e) {
+            }
+        }
+
+        if ((entry == null) && (notFoundResources.containsKey(name)))
+            return null;
+
+        JarEntry jarEntry = null;
+
+        synchronized (jarFiles) {
+
+            openJARs();
+            for (i = 0; (entry == null) && (i < jarFilesLength); i++) {
+
+                jarEntry = jarFiles[i].getJarEntry(path);
+
+                if (jarEntry != null) {
+
+                    entry = new ResourceEntry();
+                    try {
+                        entry.codeBase = getURL(jarRealFiles[i]);
+                        String jarFakeUrl = getURI(jarRealFiles[i]).toString();
+                        jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path;
+                        entry.source = new URL(jarFakeUrl);
+                        entry.lastModified = jarRealFiles[i].lastModified();
+                    } catch (MalformedURLException e) {
+                        return null;
+                    }
+                    contentLength = (int) jarEntry.getSize();
+                    try {
+                        entry.manifest = jarFiles[i].getManifest();
+                        binaryStream = jarFiles[i].getInputStream(jarEntry);
+                    } catch (IOException e) {
+                        return null;
+                    }
+
+                    // Extract resources contained in JAR to the workdir
+                    if (antiJARLocking && !(path.endsWith(".class"))) {
+                        byte[] buf = new byte[1024];
+                        File resourceFile = new File
+                            (loaderDir, jarEntry.getName());
+                        if (!resourceFile.exists()) {
+                            Enumeration entries = jarFiles[i].entries();
+                            while (entries.hasMoreElements()) {
+                                JarEntry jarEntry2 = 
+                                    (JarEntry) entries.nextElement();
+                                if (!(jarEntry2.isDirectory()) 
+                                    && (!jarEntry2.getName().endsWith
+                                        (".class"))) {
+                                    resourceFile = new File
+                                        (loaderDir, jarEntry2.getName());
+                                    resourceFile.getParentFile().mkdirs();
+                                    FileOutputStream os = null;
+                                    InputStream is = null;
+                                    try {
+                                        is = jarFiles[i].getInputStream
+                                            (jarEntry2);
+                                        os = new FileOutputStream
+                                            (resourceFile);
+                                        while (true) {
+                                            int n = is.read(buf);
+                                            if (n <= 0) {
+                                                break;
+                                            }
+                                            os.write(buf, 0, n);
+                                        }
+                                    } catch (IOException e) {
+                                        // Ignore
+                                    } finally {
+                                        try {
+                                            if (is != null) {
+                                                is.close();
+                                            }
+                                        } catch (IOException e) {
+                                        }
+                                        try {
+                                            if (os != null) {
+                                                os.close();
+                                            }
+                                        } catch (IOException e) {
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                }
+
+            }
+
+            if (entry == null) {
+                synchronized (notFoundResources) {
+                    notFoundResources.put(name, name);
+                }
+                return null;
+            }
+
+            if (binaryStream != null) {
+
+                byte[] binaryContent = new byte[contentLength];
+
+                int pos = 0;
+                try {
+
+                    while (true) {
+                        int n = binaryStream.read(binaryContent, pos,
+                                                  binaryContent.length - pos);
+                        if (n <= 0)
+                            break;
+                        pos += n;
+                    }
+                    binaryStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    return null;
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    return null;
+                }
+
+                if (fileNeedConvert) {
+                    String str = new String(binaryContent,0,pos);
+                    try {
+                        binaryContent = str.getBytes("UTF-8");
+                    } catch (Exception e) {
+                        return null;
+                    }
+                }
+                entry.binaryContent = binaryContent;
+
+                // The certificates are only available after the JarEntry 
+                // associated input stream has been fully read
+                if (jarEntry != null) {
+                    entry.certificates = jarEntry.getCertificates();
+                }
+
+            }
+
+        }
+
+        // Add the entry in the local resource repository
+        synchronized (resourceEntries) {
+            // Ensures that all the threads which may be in a race to load
+            // a particular class all end up with the same ResourceEntry
+            // instance
+            ResourceEntry entry2 = (ResourceEntry) resourceEntries.get(name);
+            if (entry2 == null) {
+                resourceEntries.put(name, entry);
+            } else {
+                entry = entry2;
+            }
+        }
+
+        return entry;
+
+    }
+
+
+    /**
+     * Returns true if the specified package name is sealed according to the
+     * given manifest.
+     */
+    protected boolean isPackageSealed(String name, Manifest man) {
+
+        String path = name.replace('.', '/') + '/';
+        Attributes attr = man.getAttributes(path); 
+        String sealed = null;
+        if (attr != null) {
+            sealed = attr.getValue(Name.SEALED);
+        }
+        if (sealed == null) {
+            if ((attr = man.getMainAttributes()) != null) {
+                sealed = attr.getValue(Name.SEALED);
+            }
+        }
+        return "true".equalsIgnoreCase(sealed);
+
+    }
+
+
+    /**
+     * Finds the resource with the given name if it has previously been
+     * loaded and cached by this class loader, and return an input stream
+     * to the resource data.  If this resource has not been cached, return
+     * <code>null</code>.
+     *
+     * @param name Name of the resource to return
+     */
+    protected InputStream findLoadedResource(String name) {
+
+        ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+        if (entry != null) {
+            if (entry.binaryContent != null)
+                return new ByteArrayInputStream(entry.binaryContent);
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Finds the class with the given name if it has previously been
+     * loaded and cached by this class loader, and return the Class object.
+     * If this class has not been cached, return <code>null</code>.
+     *
+     * @param name Name of the resource to return
+     */
+    protected Class findLoadedClass0(String name) {
+
+        ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+        if (entry != null) {
+            return entry.loadedClass;
+        }
+        return (null);  // FIXME - findLoadedResource()
+
+    }
+
+
+    /**
+     * Refresh the system policy file, to pick up eventual changes.
+     */
+    protected void refreshPolicy() {
+
+        try {
+            // The policy file may have been modified to adjust 
+            // permissions, so we're reloading it when loading or 
+            // reloading a Context
+            Policy policy = Policy.getPolicy();
+            policy.refresh();
+        } catch (AccessControlException e) {
+            // Some policy files may restrict this, even for the core,
+            // so this exception is ignored
+        }
+
+    }
+
+
+    /**
+     * Filter classes.
+     * 
+     * @param name class name
+     * @return true if the class should be filtered
+     */
+    protected boolean filter(String name) {
+
+        if (name == null)
+            return false;
+
+        // Looking up the package
+        String packageName = null;
+        int pos = name.lastIndexOf('.');
+        if (pos != -1)
+            packageName = name.substring(0, pos);
+        else
+            return false;
+
+        for (int i = 0; i < packageTriggers.length; i++) {
+            if (packageName.startsWith(packageTriggers[i]))
+                return true;
+        }
+
+        return false;
+
+    }
+
+
+    /**
+     * Validate a classname. As per SRV.9.7.2, we must restict loading of 
+     * classes from J2SE (java.*) and classes of the servlet API 
+     * (javax.servlet.*). That should enhance robustness and prevent a number
+     * of user error (where an older version of servlet.jar would be present
+     * in /WEB-INF/lib).
+     * 
+     * @param name class name
+     * @return true if the name is valid
+     */
+    protected boolean validate(String name) {
+
+        if (name == null)
+            return false;
+        if (name.startsWith("java."))
+            return false;
+
+        return true;
+
+    }
+
+
+    /**
+     * Check the specified JAR file, and return <code>true</code> if it does
+     * not contain any of the trigger classes.
+     *
+     * @param jarfile The JAR file to be checked
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected boolean validateJarFile(File jarfile)
+        throws IOException {
+
+        if (triggers == null)
+            return (true);
+        JarFile jarFile = new JarFile(jarfile);
+        for (int i = 0; i < triggers.length; i++) {
+            Class clazz = null;
+            try {
+                if (parent != null) {
+                    clazz = parent.loadClass(triggers[i]);
+                } else {
+                    clazz = Class.forName(triggers[i]);
+                }
+            } catch (Throwable t) {
+                clazz = null;
+            }
+            if (clazz == null)
+                continue;
+            String name = triggers[i].replace('.', '/') + ".class";
+            if (log.isDebugEnabled())
+                log.debug(" Checking for " + name);
+            JarEntry jarEntry = jarFile.getJarEntry(name);
+            if (jarEntry != null) {
+                log.info("validateJarFile(" + jarfile + 
+                    ") - jar not loaded. See Servlet Spec 2.3, "
+                    + "section 9.7.2. Offending class: " + name);
+                jarFile.close();
+                return (false);
+            }
+        }
+        jarFile.close();
+        return (true);
+
+    }
+
+
+    /**
+     * Get URL.
+     */
+    protected URL getURL(File file)
+        throws MalformedURLException {
+
+        File realFile = file;
+        try {
+            realFile = realFile.getCanonicalFile();
+        } catch (IOException e) {
+            // Ignore
+        }
+        return realFile.toURL();
+
+    }
+
+
+    /**
+     * Get URL.
+     */
+    protected URL getURI(File file)
+        throws MalformedURLException {
+
+        return jdkCompat.getURI(file);
+
+    }
+
+
+    /**
+     * Delete the specified directory, including all of its contents and
+     * subdirectories recursively.
+     *
+     * @param dir File object representing the directory to be deleted
+     */
+    protected static void deleteDir(File dir) {
+
+        String files[] = dir.list();
+        if (files == null) {
+            files = new String[0];
+        }
+        for (int i = 0; i < files.length; i++) {
+            File file = new File(dir, files[i]);
+            if (file.isDirectory()) {
+                deleteDir(file);
+            } else {
+                file.delete();
+            }
+        }
+        dir.delete();
+
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/loader/WebappLoader.java b/container/catalina/src/share/org/apache/catalina/loader/WebappLoader.java
new file mode 100644
index 0000000..ce4eead
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/WebappLoader.java
@@ -0,0 +1,1227 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.loader;
+
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLStreamHandlerFactory;
+import java.util.ArrayList;
+import java.util.jar.JarFile;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.naming.Binding;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletContext;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.modeler.Registry;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
+import org.apache.naming.resources.Resource;
+
+
+/**
+ * Classloader implementation which is specialized for handling web
+ * applications in the most efficient way, while being Catalina aware (all
+ * accesses to resources are made through the DirContext interface).
+ * This class loader supports detection of modified
+ * Java classes, which can be used to implement auto-reload support.
+ * <p>
+ * This class loader is configured by adding the pathnames of directories,
+ * JAR files, and ZIP files with the <code>addRepository()</code> method,
+ * prior to calling <code>start()</code>.  When a new class is required,
+ * these repositories will be consulted first to locate the class.  If it
+ * is not present, the system class loader will be used instead.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class WebappLoader
+    implements Lifecycle, Loader, PropertyChangeListener, MBeanRegistration  {
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new WebappLoader with no defined parent class loader
+     * (so that the actual parent will be the system class loader).
+     */
+    public WebappLoader() {
+
+        this(null);
+
+    }
+
+
+    /**
+     * Construct a new WebappLoader with the specified class loader
+     * to be defined as the parent of the ClassLoader we ultimately create.
+     *
+     * @param parent The parent class loader
+     */
+    public WebappLoader(ClassLoader parent) {
+        super();
+        this.parentClassLoader = parent;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * First load of the class.
+     */
+    private static boolean first = true;
+
+
+    /**
+     * The class loader being managed by this Loader component.
+     */
+    private WebappClassLoader classLoader = null;
+
+
+    /**
+     * The Container with which this Loader has been associated.
+     */
+    private Container container = null;
+
+
+    /**
+     * The "follow standard delegation model" flag that will be used to
+     * configure our ClassLoader.
+     */
+    private boolean delegate = false;
+
+
+    /**
+     * The descriptive information about this Loader implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.loader.WebappLoader/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The Java class name of the ClassLoader implementation to be used.
+     * This class should extend WebappClassLoader, otherwise, a different 
+     * loader implementation must be used.
+     */
+    private String loaderClass =
+        "org.apache.catalina.loader.WebappClassLoader";
+
+
+    /**
+     * The parent class loader of the class loader we will create.
+     */
+    private ClassLoader parentClassLoader = null;
+
+
+    /**
+     * The reloadable flag for this Loader.
+     */
+    private boolean reloadable = false;
+
+
+    /**
+     * The set of repositories associated with this class loader.
+     */
+    private String repositories[] = new String[0];
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    /**
+     * Classpath set in the loader.
+     */
+    private String classpath = null;
+
+
+    /**
+     * Repositories that are set in the loader, for JMX.
+     */
+    private ArrayList loaderRepositories = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Java class loader to be used by this Container.
+     */
+    public ClassLoader getClassLoader() {
+
+        return ((ClassLoader) classLoader);
+
+    }
+
+
+    /**
+     * Return the Container with which this Logger has been associated.
+     */
+    public Container getContainer() {
+
+        return (container);
+
+    }
+
+
+    /**
+     * Set the Container with which this Logger has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container) {
+
+        // Deregister from the old Container (if any)
+        if ((this.container != null) && (this.container instanceof Context))
+            ((Context) this.container).removePropertyChangeListener(this);
+
+        // Process this property change
+        Container oldContainer = this.container;
+        this.container = container;
+        support.firePropertyChange("container", oldContainer, this.container);
+
+        // Register with the new Container (if any)
+        if ((this.container != null) && (this.container instanceof Context)) {
+            setReloadable( ((Context) this.container).getReloadable() );
+            ((Context) this.container).addPropertyChangeListener(this);
+        }
+
+    }
+
+
+    /**
+     * Return the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     */
+    public boolean getDelegate() {
+
+        return (this.delegate);
+
+    }
+
+
+    /**
+     * Set the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     *
+     * @param delegate The new flag
+     */
+    public void setDelegate(boolean delegate) {
+
+        boolean oldDelegate = this.delegate;
+        this.delegate = delegate;
+        support.firePropertyChange("delegate", new Boolean(oldDelegate),
+                                   new Boolean(this.delegate));
+
+    }
+
+
+    /**
+     * Return descriptive information about this Loader implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the ClassLoader class name.
+     */
+    public String getLoaderClass() {
+
+        return (this.loaderClass);
+
+    }
+
+
+    /**
+     * Set the ClassLoader class name.
+     *
+     * @param loaderClass The new ClassLoader class name
+     */
+    public void setLoaderClass(String loaderClass) {
+
+        this.loaderClass = loaderClass;
+
+    }
+
+
+    /**
+     * Return the reloadable flag for this Loader.
+     */
+    public boolean getReloadable() {
+
+        return (this.reloadable);
+
+    }
+
+
+    /**
+     * Set the reloadable flag for this Loader.
+     *
+     * @param reloadable The new reloadable flag
+     */
+    public void setReloadable(boolean reloadable) {
+
+        // Process this property change
+        boolean oldReloadable = this.reloadable;
+        this.reloadable = reloadable;
+        support.firePropertyChange("reloadable",
+                                   new Boolean(oldReloadable),
+                                   new Boolean(this.reloadable));
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Add a new repository to the set of repositories for this class loader.
+     *
+     * @param repository Repository to be added
+     */
+    public void addRepository(String repository) {
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("webappLoader.addRepository", repository));
+
+        for (int i = 0; i < repositories.length; i++) {
+            if (repository.equals(repositories[i]))
+                return;
+        }
+        String results[] = new String[repositories.length + 1];
+        for (int i = 0; i < repositories.length; i++)
+            results[i] = repositories[i];
+        results[repositories.length] = repository;
+        repositories = results;
+
+        if (started && (classLoader != null)) {
+            classLoader.addRepository(repository);
+            if( loaderRepositories != null ) loaderRepositories.add(repository);
+            setClassPath();
+        }
+
+    }
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess() {
+        if (reloadable && modified()) {
+            try {
+                Thread.currentThread().setContextClassLoader
+                    (WebappLoader.class.getClassLoader());
+                if (container instanceof StandardContext) {
+                    ((StandardContext) container).reload();
+                }
+            } finally {
+                if (container.getLoader() != null) {
+                    Thread.currentThread().setContextClassLoader
+                        (container.getLoader().getClassLoader());
+                }
+            }
+        } else {
+            closeJARs(false);
+        }
+    }
+
+
+    /**
+     * Return the set of repositories defined for this class loader.
+     * If none are defined, a zero-length array is returned.
+     * For security reason, returns a clone of the Array (since 
+     * String are immutable).
+     */
+    public String[] findRepositories() {
+
+        return ((String[])repositories.clone());
+
+    }
+
+    public String[] getRepositories() {
+        return ((String[])repositories.clone());
+    }
+
+    /** Extra repositories for this loader
+     */
+    public String getRepositoriesString() {
+        StringBuffer sb=new StringBuffer();
+        for( int i=0; i<repositories.length ; i++ ) {
+            sb.append( repositories[i]).append(":");
+        }
+        return sb.toString();
+    }
+
+    public String[] getLoaderRepositories() {
+        if( loaderRepositories==null ) return  null;
+        String res[]=new String[ loaderRepositories.size()];
+        loaderRepositories.toArray(res);
+        return res;
+    }
+
+    public String getLoaderRepositoriesString() {
+        String repositories[]=getLoaderRepositories();
+        StringBuffer sb=new StringBuffer();
+        for( int i=0; i<repositories.length ; i++ ) {
+            sb.append( repositories[i]).append(":");
+        }
+        return sb.toString();
+    }
+
+
+    /** 
+     * Classpath, as set in org.apache.catalina.jsp_classpath context
+     * property
+     *
+     * @return The classpath
+     */
+    public String getClasspath() {
+        return classpath;
+    }
+
+
+    /**
+     * Has the internal repository associated with this Loader been modified,
+     * such that the loaded classes should be reloaded?
+     */
+    public boolean modified() {
+
+        return (classLoader.modified());
+
+    }
+
+
+    /**
+     * Used to periodically signal to the classloader to release JAR resources.
+     */
+    public void closeJARs(boolean force) {
+        if (classLoader !=null){
+            classLoader.closeJARs(force);
+        }
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("WebappLoader[");
+        if (container != null)
+            sb.append(container.getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+    private boolean initialized=false;
+
+    public void init() {
+        initialized=true;
+
+        if( oname==null ) {
+            // not registered yet - standalone or API
+            if( container instanceof StandardContext) {
+                // Register ourself. The container must be a webapp
+                try {
+                    StandardContext ctx=(StandardContext)container;
+                    Engine eng=(Engine)ctx.getParent().getParent();
+                    String path = ctx.getPath();
+                    if (path.equals("")) {
+                        path = "/";
+                    }   
+                    oname=new ObjectName(ctx.getEngineName() + ":type=Loader,path=" +
+                                path + ",host=" + ctx.getParent().getName());
+                    Registry.getRegistry(null, null).registerComponent(this, oname, null);
+                    controller=oname;
+                } catch (Exception e) {
+                    log.error("Error registering loader", e );
+                }
+            }
+        }
+
+        if( container == null ) {
+            // JMX created the loader
+            // TODO
+
+        }
+    }
+
+    public void destroy() {
+        if( controller==oname ) {
+            // Self-registration, undo it
+            Registry.getRegistry(null, null).unregisterComponent(oname);
+            oname = null;
+        }
+        initialized = false;
+
+    }
+
+    /**
+     * Start this component, initializing our associated class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    public void start() throws LifecycleException {
+        // Validate and update our current component state
+        if( ! initialized ) init();
+        if (started)
+            throw new LifecycleException
+                (sm.getString("webappLoader.alreadyStarted"));
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("webappLoader.starting"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        if (container.getResources() == null) {
+            log.info("No resources for " + container);
+            return;
+        }
+        // Register a stream handler factory for the JNDI protocol
+        URLStreamHandlerFactory streamHandlerFactory =
+            new DirContextURLStreamHandlerFactory();
+        if (first) {
+            first = false;
+            try {
+                URL.setURLStreamHandlerFactory(streamHandlerFactory);
+            } catch (Exception e) {
+                // Log and continue anyway, this is not critical
+                log.error("Error registering jndi stream handler", e);
+            } catch (Throwable t) {
+                // This is likely a dual registration
+                log.info("Dual registration of jndi stream handler: " 
+                         + t.getMessage());
+            }
+        }
+
+        // Construct a class loader based on our current repositories list
+        try {
+
+            classLoader = createClassLoader();
+            classLoader.setResources(container.getResources());
+            classLoader.setDelegate(this.delegate);
+            if (container instanceof StandardContext)
+                classLoader.setAntiJARLocking(((StandardContext) container).getAntiJARLocking());
+
+            for (int i = 0; i < repositories.length; i++) {
+                classLoader.addRepository(repositories[i]);
+            }
+
+            // Configure our repositories
+            setRepositories();
+            setClassPath();
+
+            setPermissions();
+
+            if (classLoader instanceof Lifecycle)
+                ((Lifecycle) classLoader).start();
+
+            // Binding the Webapp class loader to the directory context
+            DirContextURLStreamHandler.bind
+                ((ClassLoader) classLoader, this.container.getResources());
+
+            StandardContext ctx=(StandardContext)container;
+            Engine eng=(Engine)ctx.getParent().getParent();
+            String path = ctx.getPath();
+            if (path.equals("")) {
+                path = "/";
+            }   
+            ObjectName cloname = new ObjectName
+                (ctx.getEngineName() + ":type=WebappClassLoader,path="
+                 + path + ",host=" + ctx.getParent().getName());
+            Registry.getRegistry(null, null)
+                .registerComponent(classLoader, cloname, null);
+
+        } catch (Throwable t) {
+            log.error( "LifecycleException ", t );
+            throw new LifecycleException("start: ", t);
+        }
+
+    }
+
+
+    /**
+     * Stop this component, finalizing our associated class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("webappLoader.notStarted"));
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("webappLoader.stopping"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Remove context attributes as appropriate
+        if (container instanceof Context) {
+            ServletContext servletContext =
+                ((Context) container).getServletContext();
+            servletContext.removeAttribute(Globals.CLASS_PATH_ATTR);
+        }
+
+        // Throw away our current class loader
+        if (classLoader instanceof Lifecycle)
+            ((Lifecycle) classLoader).stop();
+        DirContextURLStreamHandler.unbind((ClassLoader) classLoader);
+
+        try {
+            StandardContext ctx=(StandardContext)container;
+            Engine eng=(Engine)ctx.getParent().getParent();
+            String path = ctx.getPath();
+            if (path.equals("")) {
+                path = "/";
+            }
+            ObjectName cloname = new ObjectName
+                (ctx.getEngineName() + ":type=WebappClassLoader,path="
+                 + path + ",host=" + ctx.getParent().getName());
+            Registry.getRegistry(null, null).unregisterComponent(cloname);
+        } catch (Throwable t) {
+            log.error( "LifecycleException ", t );
+        }
+
+        classLoader = null;
+
+        destroy();
+
+    }
+
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+
+    /**
+     * Process property change events from our associated Context.
+     *
+     * @param event The property change event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        // Validate the source of this event
+        if (!(event.getSource() instanceof Context))
+            return;
+        Context context = (Context) event.getSource();
+
+        // Process a relevant property change
+        if (event.getPropertyName().equals("reloadable")) {
+            try {
+                setReloadable
+                    ( ((Boolean) event.getNewValue()).booleanValue() );
+            } catch (NumberFormatException e) {
+                log.error(sm.getString("webappLoader.reloadable",
+                                 event.getNewValue().toString()));
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------- Private Methods
+
+
+    /**
+     * Create associated classLoader.
+     */
+    private WebappClassLoader createClassLoader()
+        throws Exception {
+
+        Class clazz = Class.forName(loaderClass);
+        WebappClassLoader classLoader = null;
+
+        if (parentClassLoader == null) {
+            parentClassLoader = Thread.currentThread().getContextClassLoader();
+        }
+        Class[] argTypes = { ClassLoader.class };
+        Object[] args = { parentClassLoader };
+        Constructor constr = clazz.getConstructor(argTypes);
+        classLoader = (WebappClassLoader) constr.newInstance(args);
+
+        return classLoader;
+
+    }
+
+
+    /**
+     * Configure associated class loader permissions.
+     */
+    private void setPermissions() {
+
+        if (System.getSecurityManager() == null)
+            return;
+        if (!(container instanceof Context))
+            return;
+
+        // Tell the class loader the root of the context
+        ServletContext servletContext =
+            ((Context) container).getServletContext();
+
+        // Assigning permissions for the work directory
+        File workDir =
+            (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
+        if (workDir != null) {
+            try {
+                String workDirPath = workDir.getCanonicalPath();
+                classLoader.addPermission
+                    (new FilePermission(workDirPath, "read,write"));
+                classLoader.addPermission
+                    (new FilePermission(workDirPath + File.separator + "-", 
+                                        "read,write,delete"));
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+
+        try {
+
+            URL rootURL = servletContext.getResource("/");
+            classLoader.addPermission(rootURL);
+
+            String contextRoot = servletContext.getRealPath("/");
+            if (contextRoot != null) {
+                try {
+                    contextRoot = (new File(contextRoot)).getCanonicalPath();
+                    classLoader.addPermission(contextRoot);
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+
+            URL classesURL = servletContext.getResource("/WEB-INF/classes/");
+            classLoader.addPermission(classesURL);
+            URL libURL = servletContext.getResource("/WEB-INF/lib/");
+            classLoader.addPermission(libURL);
+
+            if (contextRoot != null) {
+
+                if (libURL != null) {
+                    File rootDir = new File(contextRoot);
+                    File libDir = new File(rootDir, "WEB-INF/lib/");
+                    try {
+                        String path = libDir.getCanonicalPath();
+                        classLoader.addPermission(path);
+                    } catch (IOException e) {
+                    }
+                }
+
+            } else {
+
+                if (workDir != null) {
+                    if (libURL != null) {
+                        File libDir = new File(workDir, "WEB-INF/lib/");
+                        try {
+                            String path = libDir.getCanonicalPath();
+                            classLoader.addPermission(path);
+                        } catch (IOException e) {
+                        }
+                    }
+                    if (classesURL != null) {
+                        File classesDir = new File(workDir, "WEB-INF/classes/");
+                        try {
+                            String path = classesDir.getCanonicalPath();
+                            classLoader.addPermission(path);
+                        } catch (IOException e) {
+                        }
+                    }
+                }
+
+            }
+
+        } catch (MalformedURLException e) {
+        }
+
+    }
+
+
+    /**
+     * Configure the repositories for our class loader, based on the
+     * associated Context.
+     */
+    private void setRepositories() {
+
+        if (!(container instanceof Context))
+            return;
+        ServletContext servletContext =
+            ((Context) container).getServletContext();
+        if (servletContext == null)
+            return;
+
+        loaderRepositories=new ArrayList();
+        // Loading the work directory
+        File workDir =
+            (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
+        if (workDir == null) {
+            log.info("No work dir for " + servletContext);
+        }
+
+        if( log.isDebugEnabled()) 
+            log.debug(sm.getString("webappLoader.deploy", workDir.getAbsolutePath()));
+
+        classLoader.setWorkDir(workDir);
+
+        DirContext resources = container.getResources();
+
+        // Setting up the class repository (/WEB-INF/classes), if it exists
+
+        String classesPath = "/WEB-INF/classes";
+        DirContext classes = null;
+
+        try {
+            Object object = resources.lookup(classesPath);
+            if (object instanceof DirContext) {
+                classes = (DirContext) object;
+            }
+        } catch(NamingException e) {
+            // Silent catch: it's valid that no /WEB-INF/classes collection
+            // exists
+        }
+
+        if (classes != null) {
+
+            File classRepository = null;
+
+            String absoluteClassesPath =
+                servletContext.getRealPath(classesPath);
+
+            if (absoluteClassesPath != null) {
+
+                classRepository = new File(absoluteClassesPath);
+
+            } else {
+
+                classRepository = new File(workDir, classesPath);
+                classRepository.mkdirs();
+                copyDir(classes, classRepository);
+
+            }
+
+            if(log.isDebugEnabled())
+                log.debug(sm.getString("webappLoader.classDeploy", classesPath,
+                             classRepository.getAbsolutePath()));
+
+
+            // Adding the repository to the class loader
+            classLoader.addRepository(classesPath + "/", classRepository);
+            loaderRepositories.add(classesPath + "/" );
+
+        }
+
+        // Setting up the JAR repository (/WEB-INF/lib), if it exists
+
+        String libPath = "/WEB-INF/lib";
+
+        classLoader.setJarPath(libPath);
+
+        DirContext libDir = null;
+        // Looking up directory /WEB-INF/lib in the context
+        try {
+            Object object = resources.lookup(libPath);
+            if (object instanceof DirContext)
+                libDir = (DirContext) object;
+        } catch(NamingException e) {
+            // Silent catch: it's valid that no /WEB-INF/lib collection
+            // exists
+        }
+
+        if (libDir != null) {
+
+            boolean copyJars = false;
+            String absoluteLibPath = servletContext.getRealPath(libPath);
+
+            File destDir = null;
+
+            if (absoluteLibPath != null) {
+                destDir = new File(absoluteLibPath);
+            } else {
+                copyJars = true;
+                destDir = new File(workDir, libPath);
+                destDir.mkdirs();
+            }
+
+            // Looking up directory /WEB-INF/lib in the context
+            try {
+                NamingEnumeration enumeration = resources.listBindings(libPath);
+                while (enumeration.hasMoreElements()) {
+
+                    Binding binding = (Binding) enumeration.nextElement();
+                    String filename = libPath + "/" + binding.getName();
+                    if (!filename.endsWith(".jar"))
+                        continue;
+
+                    // Copy JAR in the work directory, always (the JAR file
+                    // would get locked otherwise, which would make it
+                    // impossible to update it or remove it at runtime)
+                    File destFile = new File(destDir, binding.getName());
+
+                    if( log.isDebugEnabled())
+                    log.debug(sm.getString("webappLoader.jarDeploy", filename,
+                                     destFile.getAbsolutePath()));
+
+                    Resource jarResource = (Resource) binding.getObject();
+                    if (copyJars) {
+                        if (!copy(jarResource.streamContent(),
+                                  new FileOutputStream(destFile)))
+                            continue;
+                    }
+
+                    try {
+                        JarFile jarFile = new JarFile(destFile);
+                        classLoader.addJar(filename, jarFile, destFile);
+                    } catch (Exception ex) {
+                        // Catch the exception if there is an empty jar file
+                        // Should ignore and continute loading other jar files 
+                        // in the dir
+                    }
+                    
+                    loaderRepositories.add( filename );
+
+                }
+            } catch (NamingException e) {
+                // Silent catch: it's valid that no /WEB-INF/lib directory
+                // exists
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Set the appropriate context attribute for our class path.  This
+     * is required only because Jasper depends on it.
+     */
+    private void setClassPath() {
+
+        // Validate our current state information
+        if (!(container instanceof Context))
+            return;
+        ServletContext servletContext =
+            ((Context) container).getServletContext();
+        if (servletContext == null)
+            return;
+
+        if (container instanceof StandardContext) {
+            String baseClasspath = 
+                ((StandardContext) container).getCompilerClasspath();
+            if (baseClasspath != null) {
+                servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+                                            baseClasspath);
+                return;
+            }
+        }
+
+        StringBuffer classpath = new StringBuffer();
+
+        // Assemble the class path information from our class loader chain
+        ClassLoader loader = getClassLoader();
+        int layers = 0;
+        int n = 0;
+        while (loader != null) {
+            if (!(loader instanceof URLClassLoader)) {
+                String cp=getClasspath( loader );
+                if( cp==null ) {
+                    log.info( "Unknown loader " + loader + " " + loader.getClass());
+                    break;
+                } else {
+                    if (n > 0) 
+                        classpath.append(File.pathSeparator);
+                    classpath.append(cp);
+                    n++;
+                }
+                break;
+                //continue;
+            }
+            URL repositories[] =
+                ((URLClassLoader) loader).getURLs();
+            for (int i = 0; i < repositories.length; i++) {
+                String repository = repositories[i].toString();
+                if (repository.startsWith("file://"))
+                    repository = repository.substring(7);
+                else if (repository.startsWith("file:"))
+                    repository = repository.substring(5);
+                else if (repository.startsWith("jndi:"))
+                    repository =
+                        servletContext.getRealPath(repository.substring(5));
+                else
+                    continue;
+                if (repository == null)
+                    continue;
+                if (n > 0)
+                    classpath.append(File.pathSeparator);
+                classpath.append(repository);
+                n++;
+            }
+            loader = loader.getParent();
+            layers++;
+        }
+
+        this.classpath=classpath.toString();
+
+        // Store the assembled class path as a servlet context attribute
+        servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+                                    classpath.toString());
+
+    }
+
+    // try to extract the classpath from a loader that is not URLClassLoader
+    private String getClasspath( ClassLoader loader ) {
+        try {
+            Method m=loader.getClass().getMethod("getClasspath", new Class[] {});
+            if( log.isTraceEnabled())
+                log.trace("getClasspath " + m );
+            if( m==null ) return null;
+            Object o=m.invoke( loader, new Object[] {} );
+            if( log.isDebugEnabled() )
+                log.debug("gotClasspath " + o);
+            if( o instanceof String )
+                return (String)o;
+            return null;
+        } catch( Exception ex ) {
+            if (log.isDebugEnabled())
+                log.debug("getClasspath ", ex);
+        }
+        return null;
+    }
+
+    /**
+     * Copy directory.
+     */
+    private boolean copyDir(DirContext srcDir, File destDir) {
+
+        try {
+
+            NamingEnumeration enumeration = srcDir.list("");
+            while (enumeration.hasMoreElements()) {
+                NameClassPair ncPair =
+                    (NameClassPair) enumeration.nextElement();
+                String name = ncPair.getName();
+                Object object = srcDir.lookup(name);
+                File currentFile = new File(destDir, name);
+                if (object instanceof Resource) {
+                    InputStream is = ((Resource) object).streamContent();
+                    OutputStream os = new FileOutputStream(currentFile);
+                    if (!copy(is, os))
+                        return false;
+                } else if (object instanceof InputStream) {
+                    OutputStream os = new FileOutputStream(currentFile);
+                    if (!copy((InputStream) object, os))
+                        return false;
+                } else if (object instanceof DirContext) {
+                    currentFile.mkdir();
+                    copyDir((DirContext) object, currentFile);
+                }
+            }
+
+        } catch (NamingException e) {
+            return false;
+        } catch (IOException e) {
+            return false;
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Copy a file to the specified temp directory. This is required only
+     * because Jasper depends on it.
+     */
+    private boolean copy(InputStream is, OutputStream os) {
+
+        try {
+            byte[] buf = new byte[4096];
+            while (true) {
+                int len = is.read(buf);
+                if (len < 0)
+                    break;
+                os.write(buf, 0, len);
+            }
+            is.close();
+            os.close();
+        } catch (IOException e) {
+            return false;
+        }
+
+        return true;
+
+    }
+
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( WebappLoader.class );
+
+    private ObjectName oname;
+    private MBeanServer mserver;
+    private String domain;
+    private ObjectName controller;
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+
+    public ObjectName getController() {
+        return controller;
+    }
+
+    public void setController(ObjectName controller) {
+        this.controller = controller;
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/loader/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/loader/mbeans-descriptors.xml
new file mode 100644
index 0000000..33bc62c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/loader/mbeans-descriptors.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean         name="WebappLoader"
+          description="Classloader implementation which is specialized for
+                       handling web applications"
+               domain="Catalina"
+                group="Loader"
+                 type="org.apache.catalina.loader.WebappLoader">
+                 
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="delegate"
+          description="The 'follow standard delegation model' flag that will be
+                       used to configure our ClassLoader"
+                 type="boolean"/>
+
+    <attribute   name="reloadable"
+          description="The reloadable flag for this Loader"
+                 type="boolean"/>
+
+    <attribute   name="repositories"
+          description="Extra repositories managed by this loader"
+                 type="[Ljava.lang.String;"/>
+
+    <attribute   name="repositoriesString"
+          description="Extra repositories managed by this loader"
+             writeable="false" 
+                 type="java.lang.String"/>
+
+    <attribute   name="loaderRepositories"
+          description="Repositories set in the real loader"
+                 type="[Ljava.lang.String;"
+            writeable="false" />
+
+    <attribute   name="loaderRepositoriesString"
+          description="Repositories set in the real loader"
+                 type="java.lang.String"
+             writeable="false" />
+
+    <operation   name="toString"
+          description="Info about the loader"
+               impact="INFO"
+           returnType="String">
+    </operation>
+  </mbean>
+
+
+  <mbean         name="WebappClassLoader"
+          description="Classloader implementation which is specialized for
+                       handling web applications"
+               domain="Catalina"
+                group="Loader"
+                 type="org.apache.catalina.loader.WebappClassLoader" />
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/mbeans-descriptors.xml
new file mode 100644
index 0000000..54daf1c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans-descriptors.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean name="Group"
+         className="org.apache.catalina.mbeans.GroupMBean"
+          description="Group from a user database"
+               domain="Users"
+                group="Group"
+                 type="org.apache.catalina.Group">
+
+    <attribute   name="description"
+          description="Description of this group"
+                 type="java.lang.String"/>
+
+    <attribute   name="groupname"
+          description="Group name of this group"
+                 type="java.lang.String"/>
+
+    <attribute   name="roles"
+          description="MBean Names of roles for this group"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="users"
+          description="MBean Names of user members of this group"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <operation   name="addRole"
+          description="Add a new authorized role for this group"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="role"
+          description="Role to be added"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRole"
+          description="Remove an old authorized role for this group"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="role"
+          description="Role to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRoles"
+          description="Remove all authorized roles for this group"
+               impact="ACTION"
+           returnType="void">
+    </operation>
+
+  </mbean>
+
+  <mbean         name="Role"
+            className="org.apache.catalina.mbeans.RoleMBean"
+          description="Security role from a user database"
+               domain="Users"
+                group="Role"
+                 type="org.apache.catalina.Role">
+
+    <attribute   name="description"
+          description="Description of this role"
+                 type="java.lang.String"/>
+
+    <attribute   name="rolename"
+          description="Role name of this role"
+                 type="java.lang.String"/>
+
+  </mbean>
+
+  <mbean         name="User"
+            className="org.apache.catalina.mbeans.UserMBean"
+          description="User from a user database"
+               domain="Users"
+                group="User"
+                 type="org.apache.catalina.User">
+
+    <attribute   name="fullName"
+          description="Full name of this user"
+                 type="java.lang.String"/>
+
+    <attribute   name="groups"
+          description="MBean Names of groups this user is a member of"
+                 type="[Ljava.lang.String;"/>
+
+    <attribute   name="password"
+          description="Password of this user"
+                 type="java.lang.String"/>
+
+    <attribute   name="roles"
+          description="MBean Names of roles for this user"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="username"
+          description="User name of this user"
+                 type="java.lang.String"/>
+
+    <operation   name="addGroup"
+          description="Add a new group membership for this user"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="groupname"
+          description="Group name of the new group"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="addRole"
+          description="Add a new authorized role for this user"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="role"
+          description="Role to be added"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeGroup"
+          description="Remove an old group membership for this user"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="groupname"
+          description="Group name of the old group"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeGroups"
+          description="Remove all group memberships for this user"
+               impact="ACTION"
+           returnType="void">
+    </operation>
+
+    <operation   name="removeRole"
+          description="Remove an old authorized role for this user"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="role"
+          description="Role to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRoles"
+          description="Remove all authorized roles for this user"
+               impact="ACTION"
+           returnType="void">
+    </operation>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/ClassNameMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/ClassNameMBean.java
new file mode 100644
index 0000000..3d23a62
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/ClassNameMBean.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import javax.management.MBeanException;
+import javax.management.RuntimeOperationsException;
+import org.apache.commons.modeler.BaseModelMBean;
+
+
+/**
+ * <p>A convenience base class for <strong>ModelMBean</strong> implementations
+ * where the underlying base class (and therefore the set of supported
+ * properties) is different for varying implementations of a standard
+ * interface.  For Catalina, that includes at least the following:
+ * Connector, Logger, Realm, and Valve.  This class creates an artificial
+ * MBean attribute named <code>className</code>, which reports the fully
+ * qualified class name of the managed object as its value.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ClassNameMBean extends BaseModelMBean {
+
+
+     // ---------------------------------------------------------- Constructors
+
+
+     /**
+      * Construct a <code>ModelMBean</code> with default
+      * <code>ModelMBeanInfo</code> information.
+      *
+      * @exception MBeanException if the initialize of an object
+      *  throws an exception
+      * @exception RuntimeOperationsException if an IllegalArgumentException
+      *  occurs
+      */
+     public ClassNameMBean()
+         throws MBeanException, RuntimeOperationsException {
+
+         super();
+
+     }
+
+
+     // ------------------------------------------------------------ Properties
+
+
+     /**
+      * Return the fully qualified Java class name of the managed object
+      * for this MBean.
+      */
+     public String getClassName() {
+
+         return (this.resource.getClass().getName());
+
+     }
+
+
+ }
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/ConnectorMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/ConnectorMBean.java
new file mode 100644
index 0000000..787a4ad
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/ConnectorMBean.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import javax.management.modelmbean.InvalidTargetObjectTypeException;
+
+import org.apache.catalina.connector.Connector;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.coyote.tomcat5.CoyoteConnector</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class ConnectorMBean extends ClassNameMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public ConnectorMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+    /**
+     * Obtain and return the value of a specific attribute of this MBean.
+     *
+     * @param name Name of the requested attribute
+     *
+     * @exception AttributeNotFoundException if this attribute is not
+     *  supported by this MBean
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception ReflectionException if a Java reflection exception
+     *  occurs when invoking the getter
+     */
+    public Object getAttribute(String name) throws AttributeNotFoundException,
+            MBeanException, ReflectionException {
+
+        Object attribute = null;
+        // Validate the input parameters
+        if (name == null)
+            throw new RuntimeOperationsException(new IllegalArgumentException(
+                    "Attribute name is null"), "Attribute name is null");
+
+        Object result = null;
+        try {
+            Connector connector = (Connector) getManagedResource();
+            result = IntrospectionUtils.getProperty(connector, name);
+        } catch (InstanceNotFoundException e) {
+            throw new MBeanException(e);
+        } catch (InvalidTargetObjectTypeException e) {
+            throw new MBeanException(e);
+        }
+
+        return result;
+
+    }
+
+    
+    /**
+     * Set the value of a specific attribute of this MBean.
+     *
+     * @param attribute The identification of the attribute to be set
+     *  and the new value
+     *
+     * @exception AttributeNotFoundException if this attribute is not
+     *  supported by this MBean
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception ReflectionException if a Java reflection exception
+     *  occurs when invoking the getter
+     */
+     public void setAttribute(Attribute attribute)
+            throws AttributeNotFoundException, MBeanException,
+            ReflectionException {
+
+        // Validate the input parameters
+        if (attribute == null)
+            throw new RuntimeOperationsException(new IllegalArgumentException(
+                    "Attribute is null"), "Attribute is null");
+        String name = attribute.getName();
+        Object value = attribute.getValue();
+        if (name == null)
+            throw new RuntimeOperationsException(new IllegalArgumentException(
+                    "Attribute name is null"), "Attribute name is null");
+
+        try {
+            Connector connector = (Connector) getManagedResource();
+            IntrospectionUtils.setProperty(connector, name, String.valueOf(value));
+        } catch (InstanceNotFoundException e) {
+            throw new MBeanException(e);
+        } catch (InvalidTargetObjectTypeException e) {
+            throw new MBeanException(e);
+        }
+  
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/ContextEnvironmentMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/ContextEnvironmentMBean.java
new file mode 100644
index 0000000..25b39a0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/ContextEnvironmentMBean.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import javax.management.modelmbean.InvalidTargetObjectTypeException;
+
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.modeler.BaseModelMBean;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.deploy.ContextEnvironment</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class ContextEnvironmentMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public ContextEnvironmentMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Attributes
+
+    
+    /**
+     * Set the value of a specific attribute of this MBean.
+     *
+     * @param attribute The identification of the attribute to be set
+     *  and the new value
+     *
+     * @exception AttributeNotFoundException if this attribute is not
+     *  supported by this MBean
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception ReflectionException if a Java reflection exception
+     *  occurs when invoking the getter
+     */
+     public void setAttribute(Attribute attribute)
+        throws AttributeNotFoundException, MBeanException,
+        ReflectionException {
+
+        super.setAttribute(attribute);
+        
+        ContextEnvironment ce = null;
+        try {
+            ce = (ContextEnvironment) getManagedResource();
+        } catch (InstanceNotFoundException e) {
+            throw new MBeanException(e);
+        } catch (InvalidTargetObjectTypeException e) {
+             throw new MBeanException(e);
+        }
+        
+        // cannot use side-efects.  It's removed and added back each time 
+        // there is a modification in a resource.
+        NamingResources nr = ce.getNamingResources();
+        nr.removeEnvironment(ce.getName());
+        nr.addEnvironment(ce);
+    }
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/ContextResourceLinkMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/ContextResourceLinkMBean.java
new file mode 100644
index 0000000..ab7ac0d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/ContextResourceLinkMBean.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import javax.management.modelmbean.InvalidTargetObjectTypeException;
+
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.modeler.BaseModelMBean;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.deploy.ContextResourceLink</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class ContextResourceLinkMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public ContextResourceLinkMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Attributes
+
+    
+    /**
+     * Set the value of a specific attribute of this MBean.
+     *
+     * @param attribute The identification of the attribute to be set
+     *  and the new value
+     *
+     * @exception AttributeNotFoundException if this attribute is not
+     *  supported by this MBean
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception ReflectionException if a Java reflection exception
+     *  occurs when invoking the getter
+     */
+     public void setAttribute(Attribute attribute)
+        throws AttributeNotFoundException, MBeanException,
+        ReflectionException {
+
+        super.setAttribute(attribute);
+        
+        ContextResourceLink crl = null;
+        try {
+            crl = (ContextResourceLink) getManagedResource();
+        } catch (InstanceNotFoundException e) {
+            throw new MBeanException(e);
+        } catch (InvalidTargetObjectTypeException e) {
+             throw new MBeanException(e);
+        }
+        
+        // cannot use side-efects.  It's removed and added back each time 
+        // there is a modification in a resource.
+        NamingResources nr = crl.getNamingResources();
+        nr.removeResourceLink(crl.getName());
+        nr.addResourceLink(crl);
+    }
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/ContextResourceMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/ContextResourceMBean.java
new file mode 100644
index 0000000..b4b8f58
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/ContextResourceMBean.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import javax.management.modelmbean.InvalidTargetObjectTypeException;
+
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.modeler.BaseModelMBean;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.deploy.ContextResource</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class ContextResourceMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public ContextResourceMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+    /**
+     * Obtain and return the value of a specific attribute of this MBean.
+     *
+     * @param name Name of the requested attribute
+     *
+     * @exception AttributeNotFoundException if this attribute is not
+     *  supported by this MBean
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception ReflectionException if a Java reflection exception
+     *  occurs when invoking the getter
+     */
+    public Object getAttribute(String name)
+        throws AttributeNotFoundException, MBeanException,
+        ReflectionException {
+ 
+        // Validate the input parameters
+        if (name == null)
+            throw new RuntimeOperationsException
+                (new IllegalArgumentException("Attribute name is null"),
+                 "Attribute name is null");
+
+        ContextResource cr = null;
+        try {
+            cr = (ContextResource) getManagedResource();
+        } catch (InstanceNotFoundException e) {
+            throw new MBeanException(e);
+        } catch (InvalidTargetObjectTypeException e) {
+             throw new MBeanException(e);
+        }
+        
+        String value = null;
+        if ("auth".equals(name)) {
+            return (cr.getAuth());
+        } else if ("description".equals(name)) {
+            return (cr.getDescription());
+        } else if ("name".equals(name)) {
+            return (cr.getName());              
+        } else if ("scope".equals(name)) {
+            return (cr.getScope());  
+        } else if ("type".equals(name)) {
+            return (cr.getType());
+        } else {
+            value = (String) cr.getProperty(name);
+            if (value == null) {
+                throw new AttributeNotFoundException
+                    ("Cannot find attribute "+name);
+            }
+        }
+        
+        return value;
+        
+    }
+
+    
+    /**
+     * Set the value of a specific attribute of this MBean.
+     *
+     * @param attribute The identification of the attribute to be set
+     *  and the new value
+     *
+     * @exception AttributeNotFoundException if this attribute is not
+     *  supported by this MBean
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception ReflectionException if a Java reflection exception
+     *  occurs when invoking the getter
+     */
+     public void setAttribute(Attribute attribute)
+        throws AttributeNotFoundException, MBeanException,
+        ReflectionException {
+
+        // Validate the input parameters
+        if (attribute == null)
+            throw new RuntimeOperationsException
+                (new IllegalArgumentException("Attribute is null"),
+                 "Attribute is null");
+        String name = attribute.getName();
+        Object value = attribute.getValue();
+        if (name == null)
+            throw new RuntimeOperationsException
+                (new IllegalArgumentException("Attribute name is null"),
+                 "Attribute name is null"); 
+        
+        ContextResource cr = null;
+        try {
+            cr = (ContextResource) getManagedResource();
+        } catch (InstanceNotFoundException e) {
+            throw new MBeanException(e);
+        } catch (InvalidTargetObjectTypeException e) {
+             throw new MBeanException(e);
+        }
+        
+        if ("auth".equals(name)) {
+            cr.setAuth((String)value);
+        } else if ("description".equals(name)) {
+            cr.setDescription((String)value);
+        } else if ("name".equals(name)) {
+            cr.setName((String)value);              
+        } else if ("scope".equals(name)) {
+            cr.setScope((String)value);  
+        } else if ("type".equals(name)) {
+            cr.setType((String)value);
+        } else {
+            cr.setProperty(name, ""+value);
+        }
+        
+        // cannot use side-efects.  It's removed and added back each time 
+        // there is a modification in a resource.
+        NamingResources nr = cr.getNamingResources();
+        nr.removeResource(cr.getName());
+        nr.addResource(cr);
+    }
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/DefaultContextMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/DefaultContextMBean.java
new file mode 100644
index 0000000..4219fbe
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/DefaultContextMBean.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+import java.util.ArrayList;
+
+import javax.management.MBeanException;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.core.StandardDefaultContext</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class DefaultContextMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public DefaultContextMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+    
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * JDK compatibility support
+     */
+    private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    protected Registry registry = MBeanUtils.createRegistry();
+
+    /**
+     * The <code>ManagedBean</code> information describing this MBean.
+     */
+    protected ManagedBean managed =
+        registry.findManagedBean("DefaultContext");
+
+    
+    // ------------------------------------------------------------- Attributes
+
+    
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    private NamingResources getNamingResources() {
+        
+        return ((Context)this.resource).getNamingResources();
+    
+    }
+    
+    
+    /**
+     * Return the MBean Names of the set of defined environment entries for  
+     * this web application
+     */
+    public String[] getEnvironments() {
+        ContextEnvironment[] envs = getNamingResources().findEnvironments();
+        ArrayList results = new ArrayList();
+        for (int i = 0; i < envs.length; i++) {
+            try {
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), envs[i]);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for environment " + envs[i]);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+    
+    
+    /**
+     * Return the MBean Names of all the defined resource references for this
+     * application.
+     */
+    public String[] getResources() {
+        
+        ContextResource[] resources = getNamingResources().findResources();
+        ArrayList results = new ArrayList();
+        for (int i = 0; i < resources.length; i++) {
+            try {
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), resources[i]);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for resource " + resources[i]);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+      
+    /**
+     * Return the MBean Names of all the defined resource links for this 
+     * application
+     */
+    public String[] getResourceLinks() {
+        
+        ContextResourceLink[] links = getNamingResources().findResourceLinks();
+        ArrayList results = new ArrayList();
+        for (int i = 0; i < links.length; i++) {
+            try {
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), links[i]);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for resource " + links[i]);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * Add an environment entry for this web application.
+     *
+     * @param envName New environment entry name
+     */
+    public String addEnvironment(String envName, String type) 
+        throws MalformedObjectNameException {
+
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return null;
+        }
+        ContextEnvironment env = nresources.findEnvironment(envName);
+        if (env != null) {
+            throw new IllegalArgumentException
+                ("Invalid environment name - already exists '" + envName + "'");
+        }
+        env = new ContextEnvironment();
+        env.setName(envName);
+        env.setType(type);
+        nresources.addEnvironment(env);
+        
+        // Return the corresponding MBean name
+        ManagedBean managed = registry.findManagedBean("ContextEnvironment");
+        ObjectName oname =
+            MBeanUtils.createObjectName(managed.getDomain(), env);
+        return (oname.toString());
+        
+    }
+
+    
+    /**
+     * Add a resource reference for this web application.
+     *
+     * @param resourceName New resource reference name
+     */
+    public String addResource(String resourceName, String type) 
+        throws MalformedObjectNameException {
+        
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return null;
+        }
+        ContextResource resource = nresources.findResource(resourceName);
+        if (resource != null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name - already exists'" + resourceName + "'");
+        }
+        resource = new ContextResource();
+        resource.setName(resourceName);
+        resource.setType(type);
+        nresources.addResource(resource);
+        
+        // Return the corresponding MBean name
+        ManagedBean managed = registry.findManagedBean("ContextResource");
+        ObjectName oname =
+            MBeanUtils.createObjectName(managed.getDomain(), resource);
+        
+        return (oname.toString());
+    }
+
+    
+    /**
+     * Add a resource link for this web application.
+     *
+     * @param resourceLinkName New resource link name
+     */
+    public String addResourceLink(String resourceLinkName, String global, 
+                String name, String type) throws MalformedObjectNameException {
+        
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return null;
+        }
+        ContextResourceLink resourceLink = 
+                                nresources.findResourceLink(resourceLinkName);
+        if (resourceLink != null) {
+            throw new IllegalArgumentException
+                ("Invalid resource link name - already exists'" + 
+                                                        resourceLinkName + "'");
+        }
+        resourceLink = new ContextResourceLink();
+        resourceLink.setGlobal(global);
+        resourceLink.setName(resourceLinkName);
+        resourceLink.setType(type);
+        nresources.addResourceLink(resourceLink);
+        
+        // Return the corresponding MBean name
+        ManagedBean managed = registry.findManagedBean("ContextResourceLink");
+        ObjectName oname =
+            MBeanUtils.createObjectName(managed.getDomain(), resourceLink);
+        return (oname.toString());
+    }    
+    
+    
+    /**
+     * Remove any environment entry with the specified name.
+     *
+     * @param envName Name of the environment entry to remove
+     */
+    public void removeEnvironment(String envName) {
+
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return;
+        }
+        ContextEnvironment env = nresources.findEnvironment(envName);
+        if (env == null) {
+            throw new IllegalArgumentException
+                ("Invalid environment name '" + envName + "'");
+        }
+        nresources.removeEnvironment(envName);
+
+    }
+    
+    
+    /**
+     * Remove any resource reference with the specified name.
+     *
+     * @param resourceName Name of the resource reference to remove
+     */
+    public void removeResource(String resourceName) {
+
+        resourceName = ObjectName.unquote(resourceName);
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return;
+        }
+        ContextResource resource = nresources.findResource(resourceName);
+        if (resource == null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name '" + resourceName + "'");
+        }
+        nresources.removeResource(resourceName);
+    }
+    
+    
+    /**
+     * Remove any resource link with the specified name.
+     *
+     * @param resourceLinkName Name of the resource reference to remove
+     */
+    public void removeResourceLink(String resourceLinkName) {
+
+        resourceLinkName = ObjectName.unquote(resourceLinkName);
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return;
+        }
+        ContextResourceLink resource = nresources.findResourceLink(resourceLinkName);
+        if (resource == null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name '" + resourceLinkName + "'");
+        }
+        nresources.removeResourceLink(resourceLinkName);
+    }
+ 
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/GlobalResourcesLifecycleListener.java b/container/catalina/src/share/org/apache/catalina/mbeans/GlobalResourcesLifecycleListener.java
new file mode 100644
index 0000000..b159c52
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/GlobalResourcesLifecycleListener.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import java.util.Iterator;
+import javax.naming.Binding;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.OperationNotSupportedException;
+import org.apache.catalina.Group;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Role;
+import org.apache.catalina.User;
+import org.apache.catalina.UserDatabase;
+import org.apache.commons.modeler.Registry;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Implementation of <code>LifecycleListener</code> that instantiates the
+ * set of MBeans associated with global JNDI resources that are subject to
+ * management.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class GlobalResourcesLifecycleListener
+    implements LifecycleListener {
+    private static Log log = LogFactory.getLog(GlobalResourcesLifecycleListener.class);
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The owning Catalina component that we are attached to.
+     */
+    protected Lifecycle component = null;
+
+
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    protected static Registry registry = MBeanUtils.createRegistry();
+
+
+    // ---------------------------------------------- LifecycleListener Methods
+
+
+    /**
+     * Primary entry point for startup and shutdown events.
+     *
+     * @param event The event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        if (Lifecycle.START_EVENT.equals(event.getType())) {
+            component = event.getLifecycle();
+            createMBeans();
+        } else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
+            destroyMBeans();
+            component = null;
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Create the MBeans for the interesting global JNDI resources.
+     */
+    protected void createMBeans() {
+
+        // Look up our global naming context
+        Context context = null;
+        try {
+            context = (Context) (new InitialContext()).lookup("java:/");
+        } catch (NamingException e) {
+            log.error("No global naming context defined for server");
+            return;
+        }
+
+        // Recurse through the defined global JNDI resources context
+        try {
+            createMBeans("", context);
+        } catch (NamingException e) {
+            log.error("Exception processing Global JNDI Resources", e);
+        }
+
+    }
+
+
+    /**
+     * Create the MBeans for the interesting global JNDI resources in
+     * the specified naming context.
+     *
+     * @param prefix Prefix for complete object name paths
+     * @param context Context to be scanned
+     *
+     * @exception NamingException if a JNDI exception occurs
+     */
+    protected void createMBeans(String prefix, Context context)
+        throws NamingException {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Creating MBeans for Global JNDI Resources in Context '" +
+                prefix + "'");
+        }
+
+        try {
+            NamingEnumeration bindings = context.listBindings("");
+            while (bindings.hasMore()) {
+                Binding binding = (Binding) bindings.next();
+                String name = prefix + binding.getName();
+                Object value = context.lookup(binding.getName());
+                if (log.isDebugEnabled()) {
+                    log.debug("Checking resource " + name);
+                }
+                if (value instanceof Context) {
+                    createMBeans(name + "/", (Context) value);
+                } else if (value instanceof UserDatabase) {
+                    try {
+                        createMBeans(name, (UserDatabase) value);
+                    } catch (Exception e) {
+                        log.error("Exception creating UserDatabase MBeans for " + name,
+                                e);
+                    }
+                }
+            }
+        } catch( RuntimeException ex) {
+            log.error("RuntimeException " + ex);
+        } catch( OperationNotSupportedException ex) {
+            log.error("Operation not supported " + ex);
+        }
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified UserDatabase and its contents.
+     *
+     * @param name Complete resource name of this UserDatabase
+     * @param database The UserDatabase to be processed
+     *
+     * @exception Exception if an exception occurs while creating MBeans
+     */
+    protected void createMBeans(String name, UserDatabase database)
+        throws Exception {
+
+        // Create the MBean for the UserDatabase itself
+        if (log.isDebugEnabled()) {
+            log.debug("Creating UserDatabase MBeans for resource " + name);
+            log.debug("Database=" + database);
+        }
+        if (MBeanUtils.createMBean(database) == null) {
+            throw new IllegalArgumentException
+                ("Cannot create UserDatabase MBean for resource " + name);
+        }
+
+        // Create the MBeans for each defined Role
+        Iterator roles = database.getRoles();
+        while (roles.hasNext()) {
+            Role role = (Role) roles.next();
+            if (log.isDebugEnabled()) {
+                log.debug("  Creating Role MBean for role " + role);
+            }
+            if (MBeanUtils.createMBean(role) == null) {
+                throw new IllegalArgumentException
+                    ("Cannot create Role MBean for role " + role);
+            }
+        }
+
+        // Create the MBeans for each defined Group
+        Iterator groups = database.getGroups();
+        while (groups.hasNext()) {
+            Group group = (Group) groups.next();
+            if (log.isDebugEnabled()) {
+                log.debug("  Creating Group MBean for group " + group);
+            }
+            if (MBeanUtils.createMBean(group) == null) {
+                throw new IllegalArgumentException
+                    ("Cannot create Group MBean for group " + group);
+            }
+        }
+
+        // Create the MBeans for each defined User
+        Iterator users = database.getUsers();
+        while (users.hasNext()) {
+            User user = (User) users.next();
+            if (log.isDebugEnabled()) {
+                log.debug("  Creating User MBean for user " + user);
+            }
+            if (MBeanUtils.createMBean(user) == null) {
+                throw new IllegalArgumentException
+                    ("Cannot create User MBean for user " + user);
+            }
+        }
+
+    }
+
+
+    /**
+     * Destroy the MBeans for the interesting global JNDI resources.
+     */
+    protected void destroyMBeans() {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Destroying MBeans for Global JNDI Resources");
+        }
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/GroupMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/GroupMBean.java
new file mode 100644
index 0000000..56fc809
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/GroupMBean.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.catalina.Group;
+import org.apache.catalina.Role;
+import org.apache.catalina.User;
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.Group</code> component.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class GroupMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public GroupMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * JDK compatibility support
+     */
+    private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    protected Registry registry = MBeanUtils.createRegistry();
+
+
+    /**
+     * The <code>MBeanServer</code> in which we are registered.
+     */
+    protected MBeanServer mserver = MBeanUtils.createServer();
+
+
+    /**
+     * The <code>ManagedBean</code> information describing this MBean.
+     */
+    protected ManagedBean managed =
+        registry.findManagedBean("Group");
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+    /**
+     * Return the MBean Names of all authorized roles for this group.
+     */
+    public String[] getRoles() {
+
+        Group group = (Group) this.resource;
+        ArrayList results = new ArrayList();
+        Iterator roles = group.getRoles();
+        while (roles.hasNext()) {
+            Role role = null;
+            try {
+                role = (Role) roles.next();
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), role);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for role " + role);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+
+    /**
+     * Return the MBean Names of all users that are members of this group.
+     */
+    public String[] getUsers() {
+
+        Group group = (Group) this.resource;
+        ArrayList results = new ArrayList();
+        Iterator users = group.getUsers();
+        while (users.hasNext()) {
+            User user = null;
+            try {
+                user = (User) users.next();
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), user);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for user " + user);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * Add a new {@link Role} to those this group belongs to.
+     *
+     * @param rolename Role name of the new role
+     */
+    public void addRole(String rolename) {
+
+        Group group = (Group) this.resource;
+        if (group == null) {
+            return;
+        }
+        Role role = group.getUserDatabase().findRole(rolename);
+        if (role == null) {
+            throw new IllegalArgumentException
+                ("Invalid role name '" + rolename + "'");
+        }
+        group.addRole(role);
+
+    }
+
+
+    /**
+     * Remove a {@link Role} from those this group belongs to.
+     *
+     * @param rolename Role name of the old role
+     */
+    public void removeRole(String rolename) {
+
+        Group group = (Group) this.resource;
+        if (group == null) {
+            return;
+        }
+        Role role = group.getUserDatabase().findRole(rolename);
+        if (role == null) {
+            throw new IllegalArgumentException
+                ("Invalid role name '" + rolename + "'");
+        }
+        group.removeRole(role);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/MBeanFactory.java b/container/catalina/src/share/org/apache/catalina/mbeans/MBeanFactory.java
new file mode 100644
index 0000000..6c3a03a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/MBeanFactory.java
@@ -0,0 +1,1076 @@
+/*
+ * Copyright 1999-2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+import java.io.File;
+import java.util.Vector;
+
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Server;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.Service;
+import org.apache.catalina.Valve;
+import org.apache.catalina.authenticator.SingleSignOn;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.core.StandardService;
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.realm.DataSourceRealm;
+import org.apache.catalina.realm.JDBCRealm;
+import org.apache.catalina.realm.JNDIRealm;
+import org.apache.catalina.realm.MemoryRealm;
+import org.apache.catalina.realm.UserDatabaseRealm;
+import org.apache.catalina.session.StandardManager;
+import org.apache.catalina.startup.ContextConfig;
+import org.apache.catalina.startup.HostConfig;
+import org.apache.catalina.valves.AccessLogValve;
+import org.apache.catalina.valves.RemoteAddrValve;
+import org.apache.catalina.valves.RemoteHostValve;
+import org.apache.catalina.valves.RequestDumperValve;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.Registry;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.core.StandardServer</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class MBeanFactory extends BaseModelMBean {
+
+    private static org.apache.commons.logging.Log log = 
+        org.apache.commons.logging.LogFactory.getLog(MBeanFactory.class);
+
+    /**
+     * The <code>MBeanServer</code> for this application.
+     */
+    private static MBeanServer mserver = MBeanUtils.createServer();
+
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    private static Registry registry = MBeanUtils.createRegistry();
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public MBeanFactory()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * Return the managed bean definition for the specified bean type
+     *
+     * @param type MBean type
+     */
+    public String findObjectName(String type) {
+
+        if (type.equals("org.apache.catalina.core.StandardContext")) {
+            return "StandardContext";
+        } else if (type.equals("org.apache.catalina.core.StandardEngine")) {
+            return "Engine";
+        } else if (type.equals("org.apache.catalina.core.StandardHost")) {
+            return "Host";
+        } else {
+            return null;
+        }
+
+    }
+
+
+    /**
+     * Little convenience method to remove redundant code
+     * when retrieving the path string
+     *
+     * @param t path string
+     * @return empty string if t==null || t.equals("/")
+     */
+    private final String getPathStr(String t) {
+        if (t == null || t.equals("/")) {
+            return "";
+        }
+        return t;
+    }
+    
+   /**
+     * Get Parent ContainerBase to add its child component 
+     * from parent's ObjectName
+     */
+    private ContainerBase getParentContainerFromParent(ObjectName pname) 
+        throws Exception {
+        
+        String type = pname.getKeyProperty("type");
+        String j2eeType = pname.getKeyProperty("j2eeType");
+        Service service = getService(pname);
+        StandardEngine engine = (StandardEngine) service.getContainer();
+        if ((j2eeType!=null) && (j2eeType.equals("WebModule"))) {
+            String name = pname.getKeyProperty("name");
+            name = name.substring(2);
+            int i = name.indexOf("/");
+            String hostName = name.substring(0,i);
+            String path = name.substring(i);
+            Host host = (Host) engine.findChild(hostName);
+            String pathStr = getPathStr(path);
+            StandardContext context = (StandardContext)host.findChild(pathStr);
+            return context;
+        } else if (type != null) {
+            if (type.equals("Engine")) {
+                return engine;
+            } else if (type.equals("Host")) {
+                String hostName = pname.getKeyProperty("host");
+                StandardHost host = (StandardHost) engine.findChild(hostName);
+                return host;
+            }
+        }
+        return null;
+        
+    }
+
+
+    /**
+     * Get Parent ContainerBase to add its child component 
+     * from child component's ObjectName  as a String
+     */    
+    private ContainerBase getParentContainerFromChild(ObjectName oname) 
+        throws Exception {
+        
+        String hostName = oname.getKeyProperty("host");
+        String path = oname.getKeyProperty("path");
+        Service service = getService(oname);
+        StandardEngine engine = (StandardEngine) service.getContainer();
+        if (hostName == null) {             
+            // child's container is Engine
+            return engine;
+        } else if (path == null) {      
+            // child's container is Host
+            StandardHost host = (StandardHost) engine.findChild(hostName);
+            return host;
+        } else {                
+            // child's container is Context
+            StandardHost host = (StandardHost) engine.findChild(hostName);
+            path = getPathStr(path);
+            StandardContext context = (StandardContext) host.findChild(path);
+            return context;
+        }
+    }
+
+    
+    private Service getService(ObjectName oname) throws Exception {
+    
+        String domain = oname.getDomain();
+        Server server = ServerFactory.getServer();
+        Service[] services = server.findServices();
+        StandardService service = null;
+        for (int i = 0; i < services.length; i++) {
+            service = (StandardService) services[i];
+            if (domain.equals(service.getObjectName().getDomain())) {
+                break;
+            }
+        }
+        if (!service.getObjectName().getDomain().equals(domain)) {
+            throw new Exception("Service with the domain is not found");
+        }        
+        return service;
+
+    }
+    
+    
+    /**
+     * Create a new AccessLoggerValve.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createAccessLoggerValve(String parent)
+        throws Exception {
+
+        ObjectName pname = new ObjectName(parent);
+        // Create a new AccessLogValve instance
+        AccessLogValve accessLogger = new AccessLogValve();
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        // Add the new instance to its parent component
+        containerBase.addValve(accessLogger);
+        ObjectName oname = accessLogger.getObjectName();
+        return (oname.toString());
+
+    }
+        
+
+    /**
+     * Create a new AjpConnector
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param address The IP address on which to bind
+     * @param port TCP port number to listen on
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createAjpConnector(String parent, String address, int port)
+        throws Exception {
+
+        return createConnector(parent, address, port, true, false);
+    }
+    
+    /**
+     * Create a new DataSource Realm.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createDataSourceRealm(String parent, String dataSourceName, 
+        String roleNameCol, String userCredCol, String userNameCol, 
+        String userRoleTable, String userTable) throws Exception {
+
+        // Create a new DataSourceRealm instance
+        DataSourceRealm realm = new DataSourceRealm();
+        realm.setDataSourceName(dataSourceName);
+        realm.setRoleNameCol(roleNameCol);
+        realm.setUserCredCol(userCredCol);
+        realm.setUserNameCol(userNameCol);
+        realm.setUserRoleTable(userRoleTable);
+        realm.setUserTable(userTable);
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        // Add the new instance to its parent component
+        containerBase.setRealm(realm);
+        // Return the corresponding MBean name
+        ObjectName oname = realm.getObjectName();
+        if (oname != null) {
+            return (oname.toString());
+        } else {
+            return null;
+        }   
+
+    }
+
+    /**
+     * Create a new HttpConnector
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param address The IP address on which to bind
+     * @param port TCP port number to listen on
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createHttpConnector(String parent, String address, int port)
+        throws Exception {
+	return createConnector(parent, address, port, false, false);
+    }
+
+    /**
+     * Create a new Connector
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param address The IP address on which to bind
+     * @param port TCP port number to listen on
+     * @param isAjp Create a AJP/1.3 Connector
+     * @param isSSL Create a secure Connector
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    private String createConnector(String parent, String address, int port, boolean isAjp, boolean isSSL)
+        throws Exception {
+        Connector retobj = new Connector();
+        if ((address!=null) && (address.length()>0)) {
+            retobj.setProperty("address", address);
+        }
+        // Set port number
+        retobj.setPort(port);
+        // Set the protocol
+        retobj.setProtocol(isAjp ? "AJP/1.3" : "HTTP/1.1");
+        // Set SSL
+        retobj.setSecure(isSSL);
+        retobj.setScheme(isSSL ? "https" : "http");
+        // Add the new instance to its parent component
+        // FIX ME - addConnector will fail
+        ObjectName pname = new ObjectName(parent);
+        Service service = getService(pname);
+        service.addConnector(retobj);
+        
+        // Return the corresponding MBean name
+        ObjectName coname = retobj.getObjectName();
+        
+        return (coname.toString());
+    }
+
+
+    /**
+     * Create a new HttpsConnector
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param address The IP address on which to bind
+     * @param port TCP port number to listen on
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createHttpsConnector(String parent, String address, int port)
+        throws Exception {
+        return createConnector(parent, address, port, false, true);
+    }
+
+    /**
+     * Create a new JDBC Realm.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createJDBCRealm(String parent, String driverName, 
+    	String connectionName, String connectionPassword, String connectionURL)
+        throws Exception {
+
+        // Create a new JDBCRealm instance
+        JDBCRealm realm = new JDBCRealm();
+        realm.setDriverName(driverName);
+        realm.setConnectionName(connectionName);
+        realm.setConnectionPassword(connectionPassword);
+        realm.setConnectionURL(connectionURL);
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        // Add the new instance to its parent component
+        containerBase.setRealm(realm);
+        // Return the corresponding MBean name
+        ObjectName oname = realm.getObjectName();
+
+        if (oname != null) {
+            return (oname.toString());
+        } else {
+            return null;
+        }   
+
+    }
+
+
+    /**
+     * Create a new JNDI Realm.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createJNDIRealm(String parent)
+        throws Exception {
+
+         // Create a new JNDIRealm instance
+        JNDIRealm realm = new JNDIRealm();
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        // Add the new instance to its parent component
+        containerBase.setRealm(realm);
+        // Return the corresponding MBean name
+        ObjectName oname = realm.getObjectName();
+
+        if (oname != null) {
+            return (oname.toString());
+        } else {
+            return null;
+        }   
+
+
+    }
+
+
+    /**
+     * Create a new Memory Realm.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createMemoryRealm(String parent)
+        throws Exception {
+
+         // Create a new MemoryRealm instance
+        MemoryRealm realm = new MemoryRealm();
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        // Add the new instance to its parent component
+        containerBase.setRealm(realm);
+        // Return the corresponding MBean name
+        ObjectName oname = realm.getObjectName();
+        if (oname != null) {
+            return (oname.toString());
+        } else {
+            return null;
+        }   
+
+    }
+
+
+    /**
+     * Create a new Remote Address Filter Valve.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createRemoteAddrValve(String parent)
+        throws Exception {
+
+        // Create a new RemoteAddrValve instance
+        RemoteAddrValve valve = new RemoteAddrValve();
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        containerBase.addValve(valve);
+        ObjectName oname = valve.getObjectName();
+        return (oname.toString());
+
+    }
+
+
+     /**
+     * Create a new Remote Host Filter Valve.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createRemoteHostValve(String parent)
+        throws Exception {
+
+        // Create a new RemoteHostValve instance
+        RemoteHostValve valve = new RemoteHostValve();
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        containerBase.addValve(valve);
+        ObjectName oname = valve.getObjectName();
+        return (oname.toString());
+        
+    }
+
+
+    /**
+     * Create a new Request Dumper Valve.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createRequestDumperValve(String parent)
+        throws Exception {
+
+        // Create a new RequestDumperValve instance
+        RequestDumperValve valve = new RequestDumperValve();
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        containerBase.addValve(valve);
+        ObjectName oname = valve.getObjectName();
+        return (oname.toString());
+
+    }
+
+
+    /**
+     * Create a new Single Sign On Valve.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createSingleSignOn(String parent)
+        throws Exception {
+
+        // Create a new SingleSignOn instance
+        SingleSignOn valve = new SingleSignOn();
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        containerBase.addValve(valve);
+        ObjectName oname = valve.getObjectName();
+        return (oname.toString());
+
+    }
+    
+    
+   /**
+     * Create a new StandardContext.
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param path The context path for this Context
+     * @param docBase Document base directory (or WAR) for this Context
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createStandardContext(String parent, 
+                                        String path,
+                                        String docBase)
+        throws Exception {
+                                            
+        // XXX for backward compatibility. Remove it once supported by the admin
+        return 
+            createStandardContext(parent,path,docBase,false,false,false,false);                                  
+    }
+
+    /**
+     * Given a context path, get the config file name.
+     */
+    private String getConfigFile(String path) {
+        String basename = null;
+        if (path.equals("")) {
+            basename = "ROOT";
+        } else {
+            basename = path.substring(1).replace('/', '#');
+        }
+        return (basename);
+    }
+
+   /**
+     * Create a new StandardContext.
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param path The context path for this Context
+     * @param docBase Document base directory (or WAR) for this Context
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createStandardContext(String parent, 
+                                        String path,
+                                        String docBase,
+                                        boolean xmlValidation,
+                                        boolean xmlNamespaceAware,
+                                        boolean tldValidation,
+                                        boolean tldNamespaceAware)
+        throws Exception {
+
+        // Create a new StandardContext instance
+        StandardContext context = new StandardContext();
+        path = getPathStr(path);
+        context.setPath(path);
+        context.setDocBase(docBase);
+        context.setXmlValidation(xmlValidation);
+        context.setXmlNamespaceAware(xmlNamespaceAware);
+        context.setTldValidation(tldValidation);
+        context.setTldNamespaceAware(tldNamespaceAware);
+        
+        ContextConfig contextConfig = new ContextConfig();
+        context.addLifecycleListener(contextConfig);
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ObjectName deployer = new ObjectName(pname.getDomain()+
+                                             ":type=Deployer,host="+
+                                             pname.getKeyProperty("host"));
+        if(mserver.isRegistered(deployer)) {
+            String contextPath = context.getPath();
+            mserver.invoke(deployer, "addServiced",
+                           new Object [] {contextPath},
+                           new String [] {"java.lang.String"});
+            String configPath = (String)mserver.getAttribute(deployer,
+                                                             "configBaseName");
+            String baseName = getConfigFile(contextPath);
+            File configFile = new File(new File(configPath), baseName+".xml");
+            context.setConfigFile(configFile.getAbsolutePath());
+            mserver.invoke(deployer, "manageApp",
+                           new Object[] {context},
+                           new String[] {"org.apache.catalina.Context"});
+            mserver.invoke(deployer, "removeServiced",
+                           new Object [] {contextPath},
+                           new String [] {"java.lang.String"});
+        } else {
+            log.warn("Deployer not found for "+pname.getKeyProperty("host"));
+            Service service = getService(pname);
+            Engine engine = (Engine) service.getContainer();
+            Host host = (Host) engine.findChild(pname.getKeyProperty("host"));
+            host.addChild(context);
+        }
+
+        // Return the corresponding MBean name
+        ObjectName oname = context.getJmxName();
+
+        return (oname.toString());
+
+    }
+
+
+   /**
+     * Create a new StandardEngine.
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param engineName Unique name of this Engine
+     * @param defaultHost Default hostname of this Engine
+     * @param serviceName Unique name of this Service
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+
+    public Vector createStandardEngineService(String parent, 
+            String engineName, String defaultHost, String serviceName)
+        throws Exception {
+
+        // Create a new StandardService instance
+        StandardService service = new StandardService();
+        service.setName(serviceName);
+        // Create a new StandardEngine instance
+        StandardEngine engine = new StandardEngine();
+        engine.setName(engineName);
+        engine.setDefaultHost(defaultHost);
+        // Need to set engine before adding it to server in order to set domain
+        service.setContainer(engine);
+        // Add the new instance to its parent component
+        Server server = ServerFactory.getServer();
+        server.addService(service);
+        Vector onames = new Vector();
+        // FIXME service & engine.getObjectName
+        //ObjectName oname = engine.getObjectName();
+        ObjectName oname = 
+            MBeanUtils.createObjectName(engineName, engine);
+        onames.add(0, oname);
+        //oname = service.getObjectName();
+        oname = 
+            MBeanUtils.createObjectName(engineName, service);
+        onames.add(1, oname);
+        return (onames);
+
+    }
+
+
+    /**
+     * Create a new StandardHost.
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param name Unique name of this Host
+     * @param appBase Application base directory name
+     * @param autoDeploy Should we auto deploy?
+     * @param deployOnStartup Deploy on server startup?
+     * @param deployXML Should we deploy Context XML config files property?
+     * @param unpackWARs Should we unpack WARs when auto deploying?
+     * @param xmlNamespaceAware Should we turn on/off XML namespace awareness?
+     * @param xmlValidation Should we turn on/off XML validation?        
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createStandardHost(String parent, String name,
+                                     String appBase,
+                                     boolean autoDeploy,
+                                     boolean deployOnStartup,
+                                     boolean deployXML,                                       
+                                     boolean unpackWARs,
+                                     boolean xmlNamespaceAware,
+                                     boolean xmlValidation)
+        throws Exception {
+
+        // Create a new StandardHost instance
+        StandardHost host = new StandardHost();
+        host.setName(name);
+        host.setAppBase(appBase);
+        host.setAutoDeploy(autoDeploy);
+        host.setDeployOnStartup(deployOnStartup);
+        host.setDeployXML(deployXML);
+        host.setUnpackWARs(unpackWARs);
+        host.setXmlNamespaceAware(xmlNamespaceAware);
+        host.setXmlValidation(xmlValidation);
+	
+        // add HostConfig for active reloading
+        HostConfig hostConfig = new HostConfig();
+        host.addLifecycleListener(hostConfig);
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        Service service = getService(pname);
+        Engine engine = (Engine) service.getContainer();
+        engine.addChild(host);
+
+        // Return the corresponding MBean name
+        return (host.getObjectName().toString());
+
+    }
+
+
+    /**
+     * Create a new StandardManager.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createStandardManager(String parent)
+        throws Exception {
+
+        // Create a new StandardManager instance
+        StandardManager manager = new StandardManager();
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        if (containerBase != null) {
+            containerBase.setManager(manager);
+        } 
+        ObjectName oname = manager.getObjectName();
+        if (oname != null) {
+            return (oname.toString());
+        } else {
+            return null;
+        }
+        
+    }
+
+
+    /**
+     * Create a new StandardService.
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param name Unique name of this StandardService
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createStandardService(String parent, String name, String domain)
+        throws Exception {
+
+        // Create a new StandardService instance
+        StandardService service = new StandardService();
+        service.setName(name);
+
+        // Add the new instance to its parent component
+        Server server = ServerFactory.getServer();
+        server.addService(service);
+
+        // Return the corresponding MBean name
+        return (service.getObjectName().toString());
+
+    }
+
+
+
+    /**
+     * Create a new  UserDatabaseRealm.
+     *
+     * @param parent MBean Name of the associated parent component
+     * @param resourceName Global JNDI resource name of the associated
+     *  UserDatabase
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createUserDatabaseRealm(String parent, String resourceName)
+        throws Exception {
+
+         // Create a new UserDatabaseRealm instance
+        UserDatabaseRealm realm = new UserDatabaseRealm();
+        realm.setResourceName(resourceName);
+        
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        // Add the new instance to its parent component
+        containerBase.setRealm(realm);
+        // Return the corresponding MBean name
+        ObjectName oname = realm.getObjectName();
+        // FIXME getObjectName() returns null
+        //ObjectName oname = 
+        //    MBeanUtils.createObjectName(pname.getDomain(), realm);
+        if (oname != null) {
+            return (oname.toString());
+        } else {
+            return null;
+        }   
+
+    }
+
+
+    /**
+     * Create a new Web Application Loader.
+     *
+     * @param parent MBean Name of the associated parent component
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String createWebappLoader(String parent)
+        throws Exception {
+
+        // Create a new WebappLoader instance
+        WebappLoader loader = new WebappLoader();
+
+        // Add the new instance to its parent component
+        ObjectName pname = new ObjectName(parent);
+        ContainerBase containerBase = getParentContainerFromParent(pname);
+        if (containerBase != null) {
+            containerBase.setLoader(loader);
+        } 
+        // FIXME add Loader.getObjectName
+        //ObjectName oname = loader.getObjectName();
+        ObjectName oname = 
+            MBeanUtils.createObjectName(pname.getDomain(), loader);
+        return (oname.toString());
+        
+    }
+
+
+    /**
+     * Remove an existing Connector.
+     *
+     * @param name MBean Name of the component to remove
+     *
+     * @exception Exception if a component cannot be removed
+     */
+    public void removeConnector(String name) throws Exception {
+
+        // Acquire a reference to the component to be removed
+        ObjectName oname = new ObjectName(name);
+        Server server = ServerFactory.getServer();
+        Service service = getService(oname);
+        String port = oname.getKeyProperty("port");
+        //String address = oname.getKeyProperty("address");
+
+        Connector conns[] = (Connector[]) service.findConnectors();
+
+        for (int i = 0; i < conns.length; i++) {
+            String connAddress = String.valueOf(conns[i].getProperty("address"));
+            String connPort = ""+conns[i].getPort();
+
+            // if (((address.equals("null")) &&
+            if ((connAddress==null) && port.equals(connPort)) {
+                service.removeConnector(conns[i]);
+                conns[i].destroy();
+                break;
+            }
+            // } else if (address.equals(connAddress))
+            if (port.equals(connPort)) {
+                // Remove this component from its parent component
+                service.removeConnector(conns[i]);
+                conns[i].destroy();
+                break;
+            }
+        }
+
+    }
+
+
+    /**
+     * Remove an existing Context.
+     *
+     * @param contextName MBean Name of the comonent to remove
+     *
+     * @exception Exception if a component cannot be removed
+     */
+    public void removeContext(String contextName) throws Exception {
+
+        // Acquire a reference to the component to be removed
+        ObjectName oname = new ObjectName(contextName);
+        String domain = oname.getDomain();
+        StandardService service = (StandardService) getService(oname);
+
+        Engine engine = (Engine) service.getContainer();
+        String name = oname.getKeyProperty("name");
+        name = name.substring(2);
+        int i = name.indexOf("/");
+        String hostName = name.substring(0,i);
+        String path = name.substring(i);
+        ObjectName deployer = new ObjectName(domain+":type=Deployer,host="+
+                                             hostName);
+        String pathStr = getPathStr(path);
+        if(mserver.isRegistered(deployer)) {
+            mserver.invoke(deployer,"addServiced",
+                           new Object[]{pathStr},
+                           new String[] {"java.lang.String"});
+            mserver.invoke(deployer,"unmanageApp",
+                           new Object[] {pathStr},
+                           new String[] {"java.lang.String"});
+            mserver.invoke(deployer,"removeServiced",
+                           new Object[] {pathStr},
+                           new String[] {"java.lang.String"});
+        } else {
+            log.warn("Deployer not found for "+hostName);
+            Host host = (Host) engine.findChild(hostName);
+            Context context = (Context) host.findChild(pathStr);
+            // Remove this component from its parent component
+            host.removeChild(context);
+            if(context instanceof StandardContext)
+            try {
+                ((StandardContext)context).destroy();
+            } catch (Exception e) {
+                log.warn("Error during context [" + context.getName() + "] destroy ", e);
+           }
+   
+        }
+
+    }
+
+
+    /**
+     * Remove an existing Host.
+     *
+     * @param name MBean Name of the comonent to remove
+     *
+     * @exception Exception if a component cannot be removed
+     */
+    public void removeHost(String name) throws Exception {
+
+        // Acquire a reference to the component to be removed
+        ObjectName oname = new ObjectName(name);
+        String hostName = oname.getKeyProperty("host");
+        Service service = getService(oname);
+        Engine engine = (Engine) service.getContainer();
+        Host host = (Host) engine.findChild(hostName);
+
+        // Remove this component from its parent component
+        if(host!=null) {
+            if(host instanceof StandardHost)
+                ((StandardHost)host).destroy();
+            else
+                engine.removeChild(host);
+        }
+
+    }
+
+
+    /**
+     * Remove an existing Loader.
+     *
+     * @param name MBean Name of the comonent to remove
+     *
+     * @exception Exception if a component cannot be removed
+     */
+    public void removeLoader(String name) throws Exception {
+
+        ObjectName oname = new ObjectName(name);
+        // Acquire a reference to the component to be removed
+        ContainerBase container = getParentContainerFromChild(oname);    
+        container.setLoader(null);
+        
+    }
+
+
+    /**
+     * Remove an existing Manager.
+     *
+     * @param name MBean Name of the comonent to remove
+     *
+     * @exception Exception if a component cannot be removed
+     */
+    public void removeManager(String name) throws Exception {
+
+        ObjectName oname = new ObjectName(name);
+        // Acquire a reference to the component to be removed
+        ContainerBase container = getParentContainerFromChild(oname);    
+        container.setManager(null);
+
+    }
+
+
+    /**
+     * Remove an existing Realm.
+     *
+     * @param name MBean Name of the comonent to remove
+     *
+     * @exception Exception if a component cannot be removed
+     */
+    public void removeRealm(String name) throws Exception {
+
+        ObjectName oname = new ObjectName(name);
+        // Acquire a reference to the component to be removed
+        ContainerBase container = getParentContainerFromChild(oname); 
+        container.setRealm(null);
+    }
+
+
+    /**
+     * Remove an existing Service.
+     *
+     * @param name MBean Name of the component to remove
+     *
+     * @exception Exception if a component cannot be removed
+     */
+    public void removeService(String name) throws Exception {
+
+        // Acquire a reference to the component to be removed
+        ObjectName oname = new ObjectName(name);
+        String serviceName = oname.getKeyProperty("serviceName");
+        Server server = ServerFactory.getServer();
+        Service service = server.findService(serviceName);
+
+        // Remove this component from its parent component
+        server.removeService(service);
+
+    }
+
+
+    /**
+     * Remove an existing Valve.
+     *
+     * @param name MBean Name of the comonent to remove
+     *
+     * @exception Exception if a component cannot be removed
+     */
+    public void removeValve(String name) throws Exception {
+
+        // Acquire a reference to the component to be removed
+        ObjectName oname = new ObjectName(name);
+        ContainerBase container = getParentContainerFromChild(oname);
+        String sequence = oname.getKeyProperty("seq");
+        Valve[] valves = (Valve[])container.getValves();
+        for (int i = 0; i < valves.length; i++) {
+            ObjectName voname = ((ValveBase) valves[i]).getObjectName();
+            if (voname.equals(oname)) {
+                container.removeValve(valves[i]);
+            }
+        }
+    }
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/MBeanUtils.java b/container/catalina/src/share/org/apache/catalina/mbeans/MBeanUtils.java
new file mode 100644
index 0000000..165d073
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/MBeanUtils.java
@@ -0,0 +1,1872 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import java.util.Hashtable;
+
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.modelmbean.ModelMBean;
+
+import org.apache.catalina.Contained;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Group;
+import org.apache.catalina.Host;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Role;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.User;
+import org.apache.catalina.UserDatabase;
+import org.apache.catalina.Valve;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+
+/**
+ * Public utility methods in support of the server side MBeans implementation.
+ *
+ * @author Craig R. McClanahan
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class MBeanUtils {
+    private static Log log = LogFactory.getLog(MBeanUtils.class);
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The set of exceptions to the normal rules used by
+     * <code>createManagedBean()</code>.  The first element of each pair
+     * is a class name, and the second element is the managed bean name.
+     */
+    private static String exceptions[][] = {
+        { "org.apache.ajp.tomcat4.Ajp13Connector",
+          "Ajp13Connector" },
+        { "org.apache.coyote.tomcat4.Ajp13Connector",
+          "CoyoteConnector" },
+        { "org.apache.catalina.users.JDBCGroup",
+          "Group" },
+        { "org.apache.catalina.users.JDBCRole",
+          "Role" },
+        { "org.apache.catalina.users.JDBCUser",
+          "User" },
+        { "org.apache.catalina.users.MemoryGroup",
+          "Group" },
+        { "org.apache.catalina.users.MemoryRole",
+          "Role" },
+        { "org.apache.catalina.users.MemoryUser",
+          "User" },
+    };
+
+
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    private static Registry registry = createRegistry();
+
+
+    /**
+     * The <code>MBeanServer</code> for this application.
+     */
+    private static MBeanServer mserver = createServer();
+
+
+    // --------------------------------------------------------- Static Methods
+
+    /**
+     * Create and return the name of the <code>ManagedBean</code> that
+     * corresponds to this Catalina component.
+     *
+     * @param component The component for which to create a name
+     */
+    static String createManagedName(Object component) {
+
+        // Deal with exceptions to the standard rule
+        String className = component.getClass().getName();
+        for (int i = 0; i < exceptions.length; i++) {
+            if (className.equals(exceptions[i][0])) {
+                return (exceptions[i][1]);
+            }
+        }
+
+        // Perform the standard transformation
+        int period = className.lastIndexOf('.');
+        if (period >= 0)
+            className = className.substring(period + 1);
+        return (className);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Connector</code> object.
+     *
+     * @param connector The Connector to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Connector connector)
+        throws Exception {
+
+        String mname = createManagedName(connector);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(connector);
+        ObjectName oname = createObjectName(domain, connector);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Context</code> object.
+     *
+     * @param context The Context to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Context context)
+        throws Exception {
+
+        String mname = createManagedName(context);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(context);
+        ObjectName oname = createObjectName(domain, context);
+        if( mserver.isRegistered(oname)) {
+            log.debug("Already registered " + oname);
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+    
+    /**
+     * Create, register, and return an MBean for this
+     * <code>ContextEnvironment</code> object.
+     *
+     * @param environment The ContextEnvironment to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(ContextEnvironment environment)
+        throws Exception {
+
+        String mname = createManagedName(environment);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(environment);
+        ObjectName oname = createObjectName(domain, environment);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>ContextResource</code> object.
+     *
+     * @param resource The ContextResource to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(ContextResource resource)
+        throws Exception {
+
+        String mname = createManagedName(resource);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(resource);
+        ObjectName oname = createObjectName(domain, resource);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>ContextResourceLink</code> object.
+     *
+     * @param resourceLink The ContextResourceLink to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(ContextResourceLink resourceLink)
+        throws Exception {
+
+        String mname = createManagedName(resourceLink);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(resourceLink);
+        ObjectName oname = createObjectName(domain, resourceLink);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }    
+ 
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Engine</code> object.
+     *
+     * @param engine The Engine to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Engine engine)
+        throws Exception {
+
+        String mname = createManagedName(engine);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(engine);
+        ObjectName oname = createObjectName(domain, engine);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Group</code> object.
+     *
+     * @param group The Group to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Group group)
+        throws Exception {
+
+        String mname = createManagedName(group);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(group);
+        ObjectName oname = createObjectName(domain, group);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Host</code> object.
+     *
+     * @param host The Host to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Host host)
+        throws Exception {
+
+        String mname = createManagedName(host);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(host);
+        ObjectName oname = createObjectName(domain, host);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Loader</code> object.
+     *
+     * @param loader The Loader to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Loader loader)
+        throws Exception {
+
+        String mname = createManagedName(loader);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(loader);
+        ObjectName oname = createObjectName(domain, loader);
+        if( mserver.isRegistered( oname ))  {
+            // side effect: stop it
+            mserver.unregisterMBean( oname );
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Manager</code> object.
+     *
+     * @param manager The Manager to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Manager manager)
+        throws Exception {
+
+        String mname = createManagedName(manager);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(manager);
+        ObjectName oname = createObjectName(domain, manager);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>MBeanFactory</code> object.
+     *
+     * @param factory The MBeanFactory to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(MBeanFactory factory)
+        throws Exception {
+
+        String mname = createManagedName(factory);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(factory);
+        ObjectName oname = createObjectName(domain, factory);
+        if( mserver.isRegistered(oname )) {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>NamingResources</code> object.
+     *
+     * @param resource The NamingResources to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(NamingResources resource)
+        throws Exception {
+
+        String mname = createManagedName(resource);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(resource);
+        ObjectName oname = createObjectName(domain, resource);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+    
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Realm</code> object.
+     *
+     * @param realm The Realm to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Realm realm)
+        throws Exception {
+
+        String mname = createManagedName(realm);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(realm);
+        ObjectName oname = createObjectName(domain, realm);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Role</code> object.
+     *
+     * @param role The Role to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Role role)
+        throws Exception {
+
+        String mname = createManagedName(role);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(role);
+        ObjectName oname = createObjectName(domain, role);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Server</code> object.
+     *
+     * @param server The Server to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Server server)
+        throws Exception {
+
+        String mname = createManagedName(server);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(server);
+        ObjectName oname = createObjectName(domain, server);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Service</code> object.
+     *
+     * @param service The Service to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Service service)
+        throws Exception {
+
+        String mname = createManagedName(service);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(service);
+        ObjectName oname = createObjectName(domain, service);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>User</code> object.
+     *
+     * @param user The User to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(User user)
+        throws Exception {
+
+        String mname = createManagedName(user);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(user);
+        ObjectName oname = createObjectName(domain, user);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>UserDatabase</code> object.
+     *
+     * @param userDatabase The UserDatabase to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(UserDatabase userDatabase)
+        throws Exception {
+
+        String mname = createManagedName(userDatabase);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(userDatabase);
+        ObjectName oname = createObjectName(domain, userDatabase);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+
+    /**
+     * Create, register, and return an MBean for this
+     * <code>Valve</code> object.
+     *
+     * @param valve The Valve to be managed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    static ModelMBean createMBean(Valve valve)
+        throws Exception {
+
+        String mname = createManagedName(valve);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            Exception e = new Exception("ManagedBean is not found with "+mname);
+            throw new MBeanException(e);
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ModelMBean mbean = managed.createMBean(valve);
+        ObjectName oname = createObjectName(domain, valve);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+        mserver.registerMBean(mbean, oname);
+        return (mbean);
+
+    }
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Connector</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param connector The Connector to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                        Connector connector)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        if (connector.getClass().getName().indexOf("CoyoteConnector") >= 0 ) {
+            try {
+                String address = (String)
+                    IntrospectionUtils.getProperty(connector, "address");
+                Integer port = (Integer)
+                    IntrospectionUtils.getProperty(connector, "port");
+                Service service = connector.getService();
+                String serviceName = null;
+                if (service != null)
+                    serviceName = service.getName();
+                StringBuffer sb = new StringBuffer(domain);
+                sb.append(":type=Connector");
+                sb.append(",port=" + port);
+                if ((address != null) && (address.length()>0)) {
+                    sb.append(",address=" + address);
+                }
+                name = new ObjectName(sb.toString());
+                return (name);
+            } catch (Exception e) {
+                throw new MalformedObjectNameException
+                    ("Cannot create object name for " + connector+e);
+            }
+        } else {
+            throw new MalformedObjectNameException
+                ("Cannot create object name for " + connector);
+        }
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Context</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param context The Context to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Context context)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        Host host = (Host)context.getParent();
+        Service service = ((Engine)host.getParent()).getService();
+        String path = context.getPath();
+        if (path.length() < 1)
+            path = "/";
+        // FIXME 
+        name = new ObjectName(domain + ":j2eeType=WebModule,name=//" +
+                              host.getName()+ path +
+                              ",J2EEApplication=none,J2EEServer=none");
+    
+        return (name);
+
+    }
+
+    
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Service</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param environment The ContextEnvironment to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    public static ObjectName createObjectName(String domain,
+                                              ContextEnvironment environment)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        Object container = 
+                environment.getNamingResources().getContainer();
+        if (container instanceof Server) {
+            name = new ObjectName(domain + ":type=Environment" + 
+                        ",resourcetype=Global,name=" + environment.getName());
+        } else if (container instanceof Context) {        
+            String path = ((Context)container).getPath();
+            if (path.length() < 1)
+                path = "/";
+            Host host = (Host) ((Context)container).getParent();
+            Engine engine = (Engine) host.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=Environment" + 
+                        ",resourcetype=Context,path=" + path + 
+                        ",host=" + host.getName() +
+                        ",name=" + environment.getName());
+        }        
+        return (name);
+
+    }
+    
+    
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>ContextResource</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param resource The ContextResource to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    public static ObjectName createObjectName(String domain,
+                                              ContextResource resource)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        String quotedResourceName = ObjectName.quote(resource.getName());
+        Object container = 
+                resource.getNamingResources().getContainer();
+        if (container instanceof Server) {        
+            name = new ObjectName(domain + ":type=Resource" +
+                        ",resourcetype=Global,class=" + resource.getType() + 
+                        ",name=" + quotedResourceName);
+        } else if (container instanceof Context) {                    
+            String path = ((Context)container).getPath();
+            if (path.length() < 1)
+                path = "/";
+            Host host = (Host) ((Context)container).getParent();
+            Engine engine = (Engine) host.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=Resource" +
+                        ",resourcetype=Context,path=" + path + 
+                        ",host=" + host.getName() +
+                        ",class=" + resource.getType() +
+                        ",name=" + quotedResourceName);
+        }
+        
+        return (name);
+
+    }
+  
+    
+     /**
+     * Create an <code>ObjectName</code> for this
+     * <code>ContextResourceLink</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param resourceLink The ContextResourceLink to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    public static ObjectName createObjectName(String domain,
+                                              ContextResourceLink resourceLink)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        String quotedResourceLinkName
+                = ObjectName.quote(resourceLink.getName());        
+        Object container = 
+                resourceLink.getNamingResources().getContainer();
+        if (container instanceof Server) {        
+            name = new ObjectName(domain + ":type=ResourceLink" +
+                        ",resourcetype=Global" + 
+                        ",name=" + quotedResourceLinkName);
+        } else if (container instanceof Context) {                    
+            String path = ((Context)container).getPath();
+            if (path.length() < 1)
+                path = "/";
+            Host host = (Host) ((Context)container).getParent();
+            Engine engine = (Engine) host.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=ResourceLink" +
+                        ",resourcetype=Context,path=" + path + 
+                        ",host=" + host.getName() +
+                        ",name=" + quotedResourceLinkName);
+        }
+        
+        return (name);
+
+    }
+    
+    
+ 
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Engine</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param engine The Engine to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Engine engine)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        name = new ObjectName(domain + ":type=Engine");
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Group</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param group The Group to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Group group)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        name = new ObjectName(domain + ":type=Group,groupname=" +
+                              ObjectName.quote(group.getGroupname()) +
+                              ",database=" + group.getUserDatabase().getId());
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Host</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param host The Host to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Host host)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        Engine engine = (Engine)host.getParent();
+        Service service = engine.getService();
+        name = new ObjectName(domain + ":type=Host,host=" +
+                              host.getName());
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Loader</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param loader The Loader to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Loader loader)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        Container container = loader.getContainer();
+
+        if (container instanceof Engine) {
+            Service service = ((Engine)container).getService();
+            name = new ObjectName(domain + ":type=Loader");
+        } else if (container instanceof Host) {
+            Engine engine = (Engine) container.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=Loader,host=" +
+                              container.getName());
+        } else if (container instanceof Context) {
+            String path = ((Context)container).getPath();
+            if (path.length() < 1) {
+                path = "/";
+            }
+            Host host = (Host) container.getParent();
+            Engine engine = (Engine) host.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=Loader,path=" + path +
+                              ",host=" + host.getName());
+        }
+
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Manager</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param manager The Manager to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Manager manager)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        Container container = manager.getContainer();
+
+        if (container instanceof Engine) {
+            Service service = ((Engine)container).getService();
+            name = new ObjectName(domain + ":type=Manager");
+        } else if (container instanceof Host) {
+            Engine engine = (Engine) container.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=Manager,host=" +
+                              container.getName());
+        } else if (container instanceof Context) {
+            String path = ((Context)container).getPath();
+            if (path.length() < 1) {
+                path = "/";
+            }
+            Host host = (Host) container.getParent();
+            Engine engine = (Engine) host.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=Manager,path=" + path +
+                              ",host=" + host.getName());
+        }
+
+        return (name);
+
+    }
+    
+    
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Server</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param resources The NamingResources to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              NamingResources resources)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        Object container = resources.getContainer();        
+        if (container instanceof Server) {        
+            name = new ObjectName(domain + ":type=NamingResources" + 
+                        ",resourcetype=Global");
+        } else if (container instanceof Context) {        
+            String path = ((Context)container).getPath();
+            if (path.length() < 1)
+                path = "/";
+            Host host = (Host) ((Context)container).getParent();
+            Engine engine = (Engine) host.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=NamingResources" + 
+                        ",resourcetype=Context,path=" + path + 
+                        ",host=" + host.getName());
+        }
+        
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>MBeanFactory</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param factory The MBeanFactory to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              MBeanFactory factory)
+        throws MalformedObjectNameException {
+
+        ObjectName name = new ObjectName(domain + ":type=MBeanFactory");
+
+        return (name);
+
+    }
+
+    
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Realm</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param realm The Realm to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Realm realm)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        Container container = realm.getContainer();
+
+        if (container instanceof Engine) {
+            Service service = ((Engine)container).getService();
+            name = new ObjectName(domain + ":type=Realm");
+        } else if (container instanceof Host) {
+            Engine engine = (Engine) container.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=Realm,host=" +
+                              container.getName());
+        } else if (container instanceof Context) {
+            String path = ((Context)container).getPath();
+            if (path.length() < 1) {
+                path = "/";
+            }
+            Host host = (Host) container.getParent();
+            Engine engine = (Engine) host.getParent();
+            Service service = engine.getService();
+            name = new ObjectName(domain + ":type=Realm,path=" + path +
+                              ",host=" + host.getName());
+        }
+
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Role</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param role The Role to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Role role)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        name = new ObjectName(domain + ":type=Role,rolename=" +
+                              role.getRolename() + ",database=" +
+                              role.getUserDatabase().getId());
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Server</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param server The Server to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Server server)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        name = new ObjectName(domain + ":type=Server");
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Service</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param service The Service to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              Service service)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        name = new ObjectName(domain + ":type=Service,serviceName=" + 
+                            service.getName());
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>User</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param user The User to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              User user)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        name = new ObjectName(domain + ":type=User,username=" +
+                              ObjectName.quote(user.getUsername())
+                              + ",database=" + user.getUserDatabase().getId());
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>UserDatabase</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param userDatabase The UserDatabase to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                              UserDatabase userDatabase)
+        throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        name = new ObjectName(domain + ":type=UserDatabase,database=" +
+                              userDatabase.getId());
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for this
+     * <code>Valve</code> object.
+     *
+     * @param domain Domain in which this name is to be created
+     * @param valve The Valve to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    static ObjectName createObjectName(String domain,
+                                       Valve valve)
+        throws MalformedObjectNameException {
+        if( valve instanceof ValveBase ) {
+            ObjectName name=((ValveBase)valve).getObjectName();
+            if( name != null )
+                return name;
+        }
+
+        ObjectName name = null;
+        Container container = null;
+        String className=valve.getClass().getName();
+        int period = className.lastIndexOf('.');
+        if (period >= 0)
+            className = className.substring(period + 1);
+        if( valve instanceof Contained ) {
+            container = ((Contained)valve).getContainer();
+        }
+        if( container == null ) {
+            throw new MalformedObjectNameException(
+                               "Cannot create mbean for non-contained valve " +
+                               valve);
+        }        
+        if (container instanceof Engine) {
+            Service service = ((Engine)container).getService();
+            String local="";
+            int seq = getSeq(local);
+            String ext="";
+            if( seq > 0 ) {
+                ext=",seq=" + seq;
+            }
+            name = new ObjectName(domain + ":type=Valve,name=" + className + 
+                                    ext + local );
+        } else if (container instanceof Host) {
+            Service service = ((Engine)container.getParent()).getService();
+            String local=",host=" +container.getName();
+            int seq = getSeq(local);
+            String ext="";
+            if( seq > 0 ) {
+                ext=",seq=" + seq;
+            }
+            name = new ObjectName(domain + ":type=Valve,name=" + className + 
+                                    ext + local );
+        } else if (container instanceof Context) {
+            String path = ((Context)container).getPath();
+            if (path.length() < 1) {
+                path = "/";
+            }
+            Host host = (Host) container.getParent();
+            Service service = ((Engine) host.getParent()).getService();
+            String local=",path=" + path + ",host=" +
+                    host.getName();
+            int seq = getSeq(local);
+            String ext="";
+            if( seq > 0 ) {
+                ext=",seq=" + seq;
+            }
+            name = new ObjectName(domain + ":type=Valve,name=" + className + 
+                                    ext + local );
+        }
+
+        return (name);
+
+    }
+
+    static Hashtable seq=new Hashtable();
+    static int getSeq( String key ) {
+        int i[]=(int [])seq.get( key );
+        if (i == null ) {
+            i=new int[1];
+            i[0]=0;
+            seq.put( key, i);
+        } else {
+            i[0]++;
+        }
+        return i[0];
+    }
+
+    /**
+     * Create and configure (if necessary) and return the registry of
+     * managed object descriptions.
+     */
+    public synchronized static Registry createRegistry() {
+
+        if (registry == null) {
+            registry = Registry.getRegistry(null, null);
+            ClassLoader cl=ServerLifecycleListener.class.getClassLoader();
+
+            registry.loadDescriptors("org.apache.catalina.mbeans",  cl);
+            registry.loadDescriptors("org.apache.catalina.authenticator", cl);
+            registry.loadDescriptors("org.apache.catalina.core", cl);
+            registry.loadDescriptors("org.apache.catalina", cl);
+            registry.loadDescriptors("org.apache.catalina.deploy", cl);
+            registry.loadDescriptors("org.apache.catalina.loader", cl);
+            registry.loadDescriptors("org.apache.catalina.realm", cl);
+            registry.loadDescriptors("org.apache.catalina.session", cl);
+            registry.loadDescriptors("org.apache.catalina.startup", cl);
+            registry.loadDescriptors("org.apache.catalina.users", cl);
+            registry.loadDescriptors("org.apache.catalina.cluster", cl);
+            registry.loadDescriptors("org.apache.catalina.connector", cl);
+            registry.loadDescriptors("org.apache.catalina.valves",  cl);
+        }
+        return (registry);
+
+    }
+
+
+    /**
+     * Create and configure (if necessary) and return the
+     * <code>MBeanServer</code> with which we will be
+     * registering our <code>ModelMBean</code> implementations.
+     */
+    public synchronized static MBeanServer createServer() {
+
+        if (mserver == null) {
+            try {
+                mserver = Registry.getRegistry(null, null).getMBeanServer();
+            } catch (Throwable t) {
+                t.printStackTrace(System.out);
+                System.exit(1);
+            }
+        }
+        return (mserver);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Connector</code> object.
+     *
+     * @param connector The Connector to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Connector connector, Service service)
+        throws Exception {
+
+        connector.setService(service);
+        String mname = createManagedName(connector);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, connector);
+        connector.setService(null);
+        if( mserver.isRegistered( oname ))  {
+            mserver.unregisterMBean(oname);
+        }
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Context</code> object.
+     *
+     * @param context The Context to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Context context)
+        throws Exception {
+
+        String mname = createManagedName(context);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, context);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+    
+    /**
+     * Deregister the MBean for this
+     * <code>ContextEnvironment</code> object.
+     *
+     * @param environment The ContextEnvironment to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(ContextEnvironment environment)
+        throws Exception {
+
+        String mname = createManagedName(environment);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, environment);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+    
+    
+    /**
+     * Deregister the MBean for this
+     * <code>ContextResource</code> object.
+     *
+     * @param resource The ContextResource to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(ContextResource resource)
+        throws Exception {
+
+        String mname = createManagedName(resource);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, resource);
+        if( mserver.isRegistered(oname ))
+            mserver.unregisterMBean(oname);
+
+    }
+     
+    
+    /**
+     * Deregister the MBean for this
+     * <code>ContextResourceLink</code> object.
+     *
+     * @param resourceLink The ContextResourceLink to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(ContextResourceLink resourceLink)
+        throws Exception {
+
+        String mname = createManagedName(resourceLink);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, resourceLink);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }   
+    
+    /**
+     * Deregister the MBean for this
+     * <code>Engine</code> object.
+     *
+     * @param engine The Engine to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Engine engine)
+        throws Exception {
+
+        String mname = createManagedName(engine);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, engine);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Group</code> object.
+     *
+     * @param group The Group to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Group group)
+        throws Exception {
+
+        String mname = createManagedName(group);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, group);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Host</code> object.
+     *
+     * @param host The Host to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Host host)
+        throws Exception {
+
+        String mname = createManagedName(host);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, host);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Loader</code> object.
+     *
+     * @param loader The Loader to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Loader loader)
+        throws Exception {
+
+        String mname = createManagedName(loader);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, loader);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Manager</code> object.
+     *
+     * @param manager The Manager to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Manager manager)
+        throws Exception {
+
+        String mname = createManagedName(manager);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, manager);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+    
+    
+   /**
+     * Deregister the MBean for this
+     * <code>NamingResources</code> object.
+     *
+     * @param resources The NamingResources to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(NamingResources resources)
+        throws Exception {
+
+        String mname = createManagedName(resources);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, resources);
+       if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+    
+    
+    /**
+     * Deregister the MBean for this
+     * <code>Realm</code> object.
+     *
+     * @param realm The Realm to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Realm realm)
+        throws Exception {
+
+        String mname = createManagedName(realm);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, realm);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Role</code> object.
+     *
+     * @param role The Role to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Role role)
+        throws Exception {
+
+        String mname = createManagedName(role);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, role);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Server</code> object.
+     *
+     * @param server The Server to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Server server)
+        throws Exception {
+
+        String mname = createManagedName(server);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, server);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Service</code> object.
+     *
+     * @param service The Service to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Service service)
+        throws Exception {
+
+        String mname = createManagedName(service);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, service);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>User</code> object.
+     *
+     * @param user The User to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(User user)
+        throws Exception {
+
+        String mname = createManagedName(user);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, user);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>UserDatabase</code> object.
+     *
+     * @param userDatabase The UserDatabase to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(UserDatabase userDatabase)
+        throws Exception {
+
+        String mname = createManagedName(userDatabase);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, userDatabase);
+        if( mserver.isRegistered(oname) )
+            mserver.unregisterMBean(oname);
+
+    }
+
+
+    /**
+     * Deregister the MBean for this
+     * <code>Valve</code> object.
+     *
+     * @param valve The Valve to be managed
+     *
+     * @exception Exception if an MBean cannot be deregistered
+     */
+    static void destroyMBean(Valve valve, Container container)
+        throws Exception {
+
+        ((Contained)valve).setContainer(container);
+        String mname = createManagedName(valve);
+        ManagedBean managed = registry.findManagedBean(mname);
+        if (managed == null) {
+            return;
+        }
+        String domain = managed.getDomain();
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        ObjectName oname = createObjectName(domain, valve);
+        try {
+            ((Contained)valve).setContainer(null);
+        } catch (Throwable t) {
+        ;
+        }
+        if( mserver.isRegistered(oname) ) {
+            mserver.unregisterMBean(oname);
+        }
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java
new file mode 100644
index 0000000..782d994
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import javax.management.MalformedObjectNameException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+import org.apache.catalina.Group;
+import org.apache.catalina.Role;
+import org.apache.catalina.User;
+import org.apache.catalina.UserDatabase;
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.users.MemoryUserDatabase</code> component.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class MemoryUserDatabaseMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public MemoryUserDatabaseMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * JDK compatibility support
+     */
+    private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    protected Registry registry = MBeanUtils.createRegistry();
+
+
+    /**
+     * The <code>MBeanServer</code> in which we are registered.
+     */
+    protected MBeanServer mserver = MBeanUtils.createServer();
+
+
+    /**
+     * The <code>ManagedBean</code> information describing this MBean.
+     */
+    protected ManagedBean managed =
+        registry.findManagedBean("MemoryUserDatabase");
+
+
+    /**
+     * The <code>ManagedBean</code> information describing Group MBeans.
+     */
+    protected ManagedBean managedGroup =
+        registry.findManagedBean("Group");
+
+
+    /**
+     * The <code>ManagedBean</code> information describing Group MBeans.
+     */
+    protected ManagedBean managedRole =
+        registry.findManagedBean("Role");
+
+
+    /**
+     * The <code>ManagedBean</code> information describing User MBeans.
+     */
+    protected ManagedBean managedUser =
+        registry.findManagedBean("User");
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+    /**
+     * Return the MBean Names of all groups defined in this database.
+     */
+    public String[] getGroups() {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        ArrayList results = new ArrayList();
+        Iterator groups = database.getGroups();
+        while (groups.hasNext()) {
+            Group group = (Group) groups.next();
+            results.add(findGroup(group.getGroupname()));
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+
+    /**
+     * Return the MBean Names of all roles defined in this database.
+     */
+    public String[] getRoles() {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        ArrayList results = new ArrayList();
+        Iterator roles = database.getRoles();
+        while (roles.hasNext()) {
+            Role role = (Role) roles.next();
+            results.add(findRole(role.getRolename()));
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+
+    /**
+     * Return the MBean Names of all users defined in this database.
+     */
+    public String[] getUsers() {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        ArrayList results = new ArrayList();
+        Iterator users = database.getUsers();
+        while (users.hasNext()) {
+            User user = (User) users.next();
+            results.add(findUser(user.getUsername()));
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * Create a new Group and return the corresponding MBean Name.
+     *
+     * @param groupname Group name of the new group
+     * @param description Description of the new group
+     */
+    public String createGroup(String groupname, String description) {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        Group group = database.createGroup(groupname, description);
+        try {
+            MBeanUtils.createMBean(group);
+        } catch (Exception e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Exception creating group " + group + " MBean");
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+        return (findGroup(groupname));
+
+    }
+
+
+    /**
+     * Create a new Role and return the corresponding MBean Name.
+     *
+     * @param rolename Group name of the new group
+     * @param description Description of the new group
+     */
+    public String createRole(String rolename, String description) {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        Role role = database.createRole(rolename, description);
+        try {
+            MBeanUtils.createMBean(role);
+        } catch (Exception e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Exception creating role " + role + " MBean");
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+        return (findRole(rolename));
+
+    }
+
+
+    /**
+     * Create a new User and return the corresponding MBean Name.
+     *
+     * @param username User name of the new user
+     * @param password Password for the new user
+     * @param fullName Full name for the new user
+     */
+    public String createUser(String username, String password,
+                             String fullName) {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        User user = database.createUser(username, password, fullName);
+        try {
+            MBeanUtils.createMBean(user);
+        } catch (Exception e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Exception creating user " + user + " MBean");
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+        return (findUser(username));
+
+    }
+
+
+    /**
+     * Return the MBean Name for the specified group name (if any);
+     * otherwise return <code>null</code>.
+     *
+     * @param groupname Group name to look up
+     */
+    public String findGroup(String groupname) {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        Group group = database.findGroup(groupname);
+        if (group == null) {
+            return (null);
+        }
+        try {
+            ObjectName oname =
+                MBeanUtils.createObjectName(managedGroup.getDomain(), group);
+            return (oname.toString());
+        } catch (MalformedObjectNameException e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Cannot create object name for group " + group);
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+
+    }
+
+
+    /**
+     * Return the MBean Name for the specified role name (if any);
+     * otherwise return <code>null</code>.
+     *
+     * @param rolename Role name to look up
+     */
+    public String findRole(String rolename) {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        Role role = database.findRole(rolename);
+        if (role == null) {
+            return (null);
+        }
+        try {
+            ObjectName oname =
+                MBeanUtils.createObjectName(managedRole.getDomain(), role);
+            return (oname.toString());
+        } catch (MalformedObjectNameException e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Cannot create object name for role " + role);
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+
+    }
+
+
+    /**
+     * Return the MBean Name for the specified user name (if any);
+     * otherwise return <code>null</code>.
+     *
+     * @param username User name to look up
+     */
+    public String findUser(String username) {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        User user = database.findUser(username);
+        if (user == null) {
+            return (null);
+        }
+        try {
+            ObjectName oname =
+                MBeanUtils.createObjectName(managedUser.getDomain(), user);
+            return (oname.toString());
+        } catch (MalformedObjectNameException e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Cannot create object name for user " + user);
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+
+    }
+
+
+    /**
+     * Remove an existing group and destroy the corresponding MBean.
+     *
+     * @param groupname Group name to remove
+     */
+    public void removeGroup(String groupname) {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        Group group = database.findGroup(groupname);
+        if (group == null) {
+            return;
+        }
+        try {
+            MBeanUtils.destroyMBean(group);
+            database.removeGroup(group);
+        } catch (Exception e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Exception destroying group " + group + " MBean");
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+
+    }
+
+
+    /**
+     * Remove an existing role and destroy the corresponding MBean.
+     *
+     * @param rolename Role name to remove
+     */
+    public void removeRole(String rolename) {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        Role role = database.findRole(rolename);
+        if (role == null) {
+            return;
+        }
+        try {
+            MBeanUtils.destroyMBean(role);
+            database.removeRole(role);
+        } catch (Exception e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Exception destroying role " + role + " MBean");
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+
+    }
+
+
+    /**
+     * Remove an existing user and destroy the corresponding MBean.
+     *
+     * @param username User name to remove
+     */
+    public void removeUser(String username) {
+
+        UserDatabase database = (UserDatabase) this.resource;
+        User user = database.findUser(username);
+        if (user == null) {
+            return;
+        }
+        try {
+            MBeanUtils.destroyMBean(user);
+            database.removeUser(user);
+        } catch (Exception e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Exception destroying user " + user + " MBean");
+            jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/NamingResourcesMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/NamingResourcesMBean.java
new file mode 100644
index 0000000..a0084fd
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/NamingResourcesMBean.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+import java.util.ArrayList;
+
+import javax.management.MBeanException;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.deploy.NamingResources</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class NamingResourcesMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public NamingResourcesMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * JDK compatibility support
+     */
+    private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+
+    // ----------------------------------------------------- Instance Variables
+    
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    protected Registry registry = MBeanUtils.createRegistry();
+
+
+    /**
+     * The <code>ManagedBean</code> information describing this MBean.
+     */
+    protected ManagedBean managed =
+        registry.findManagedBean("NamingResources");
+
+    // ------------------------------------------------------------- Attributes
+    
+
+    /**
+     * Return the MBean Names of the set of defined environment entries for  
+     * this web application
+     */
+    public String[] getEnvironments() {
+        ContextEnvironment[] envs = 
+                            ((NamingResources)this.resource).findEnvironments();
+        ArrayList results = new ArrayList();
+        for (int i = 0; i < envs.length; i++) {
+            try {
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), envs[i]);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for environment " + envs[i]);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+    
+    
+    /**
+     * Return the MBean Names of all the defined resource references for this
+     * application.
+     */
+    public String[] getResources() {
+        
+        ContextResource[] resources = 
+                            ((NamingResources)this.resource).findResources();
+        ArrayList results = new ArrayList();
+        for (int i = 0; i < resources.length; i++) {
+            try {
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), resources[i]);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for resource " + resources[i]);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+    
+    
+    /**
+     * Return the MBean Names of all the defined resource link references for 
+     * this application.
+     */
+    public String[] getResourceLinks() {
+        
+        ContextResourceLink[] resourceLinks = 
+                            ((NamingResources)this.resource).findResourceLinks();
+        ArrayList results = new ArrayList();
+        for (int i = 0; i < resourceLinks.length; i++) {
+            try {
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), resourceLinks[i]);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for resource " + resourceLinks[i]);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * Add an environment entry for this web application.
+     *
+     * @param envName New environment entry name
+     * @param type The type of the new environment entry
+     * @param value The value of the new environment entry 
+     */
+    public String addEnvironment(String envName, String type, String value) 
+        throws MalformedObjectNameException {
+
+        NamingResources nresources = (NamingResources) this.resource;
+        if (nresources == null) {
+            return null;
+        }
+        ContextEnvironment env = nresources.findEnvironment(envName);
+        if (env != null) {
+            throw new IllegalArgumentException
+                ("Invalid environment name - already exists '" + envName + "'");
+        }
+        env = new ContextEnvironment();
+        env.setName(envName);
+        env.setType(type);
+        env.setValue(value);
+        nresources.addEnvironment(env);
+        
+        // Return the corresponding MBean name
+        ManagedBean managed = registry.findManagedBean("ContextEnvironment");
+        ObjectName oname =
+            MBeanUtils.createObjectName(managed.getDomain(), env);
+        return (oname.toString());
+        
+    }
+
+    
+    /**
+     * Add a resource reference for this web application.
+     *
+     * @param resourceName New resource reference name
+     * @param type New resource reference type
+     */
+    public String addResource(String resourceName, String type) 
+        throws MalformedObjectNameException {
+        
+        NamingResources nresources = (NamingResources) this.resource;
+        if (nresources == null) {
+            return null;
+        }
+        ContextResource resource = nresources.findResource(resourceName);
+        if (resource != null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name - already exists'" + resourceName + "'");
+        }
+        resource = new ContextResource();
+        resource.setName(resourceName);
+        resource.setType(type);
+        nresources.addResource(resource);
+        
+        // Return the corresponding MBean name
+        ManagedBean managed = registry.findManagedBean("ContextResource");
+        ObjectName oname =
+            MBeanUtils.createObjectName(managed.getDomain(), resource);
+        return (oname.toString());
+    }
+
+    
+    /**
+     * Add a resource link reference for this web application.
+     *
+     * @param resourceLinkName New resource link reference name
+     * @param type New resource link reference type
+     */
+    public String addResourceLink(String resourceLinkName, String type)
+        throws MalformedObjectNameException {
+        
+        NamingResources nresources = (NamingResources) this.resource;
+        if (nresources == null) {
+            return null;
+        }
+        ContextResourceLink resourceLink = 
+                            nresources.findResourceLink(resourceLinkName);
+        if (resourceLink != null) {
+            throw new IllegalArgumentException
+                ("Invalid resource link name - already exists'" + 
+                resourceLinkName + "'");
+        }
+        resourceLink = new ContextResourceLink();
+        resourceLink.setName(resourceLinkName);
+        resourceLink.setType(type);
+        nresources.addResourceLink(resourceLink);
+        
+        // Return the corresponding MBean name
+        ManagedBean managed = registry.findManagedBean("ContextResourceLink");
+        ObjectName oname =
+            MBeanUtils.createObjectName(managed.getDomain(), resourceLink);
+        return (oname.toString());
+    }
+    
+    
+    /**
+     * Remove any environment entry with the specified name.
+     *
+     * @param envName Name of the environment entry to remove
+     */
+    public void removeEnvironment(String envName) {
+
+        NamingResources nresources = (NamingResources) this.resource;
+        if (nresources == null) {
+            return;
+        }
+        ContextEnvironment env = nresources.findEnvironment(envName);
+        if (env == null) {
+            throw new IllegalArgumentException
+                ("Invalid environment name '" + envName + "'");
+        }
+        nresources.removeEnvironment(envName);
+
+    }
+    
+    
+    /**
+     * Remove any resource reference with the specified name.
+     *
+     * @param resourceName Name of the resource reference to remove
+     */
+    public void removeResource(String resourceName) {
+
+        resourceName = ObjectName.unquote(resourceName);
+        NamingResources nresources = (NamingResources) this.resource;
+        if (nresources == null) {
+            return;
+        }
+        ContextResource resource = nresources.findResource(resourceName);
+        if (resource == null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name '" + resourceName + "'");
+        }
+        nresources.removeResource(resourceName);
+    
+    }
+    
+    
+    /**
+     * Remove any resource link reference with the specified name.
+     *
+     * @param resourceLinkName Name of the resource link reference to remove
+     */
+    public void removeResourceLink(String resourceLinkName) {
+
+        resourceLinkName = ObjectName.unquote(resourceLinkName);
+        NamingResources nresources = (NamingResources) this.resource;
+        if (nresources == null) {
+            return;
+        }
+        ContextResourceLink resourceLink = 
+                            nresources.findResourceLink(resourceLinkName);
+        if (resourceLink == null) {
+            throw new IllegalArgumentException
+                ("Invalid resource Link name '" + resourceLinkName + "'");
+        }
+        nresources.removeResourceLink(resourceLinkName);
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/RoleMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/RoleMBean.java
new file mode 100644
index 0000000..4f0a1a0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/RoleMBean.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.Role</code> component.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class RoleMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public RoleMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    protected Registry registry = MBeanUtils.createRegistry();
+
+
+    /**
+     * The <code>MBeanServer</code> in which we are registered.
+     */
+    protected MBeanServer mserver = MBeanUtils.createServer();
+
+
+    /**
+     * The <code>ManagedBean</code> information describing this MBean.
+     */
+    protected ManagedBean managed =
+        registry.findManagedBean("Role");
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+    // ------------------------------------------------------------- Operations
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/ServerLifecycleListener.java b/container/catalina/src/share/org/apache/catalina/mbeans/ServerLifecycleListener.java
new file mode 100644
index 0000000..cded1de
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/ServerLifecycleListener.java
@@ -0,0 +1,1411 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import javax.management.MBeanException;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.ContainerListener;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Server;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.Service;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.core.StandardService;
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Implementation of <code>LifecycleListener</code> that
+ * instantiates the set of MBeans associated with the components of a
+ * running instance of Catalina.
+ *
+ * @author Craig R. McClanahan
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class ServerLifecycleListener
+    implements ContainerListener, LifecycleListener, PropertyChangeListener {
+
+    private static Log log = LogFactory.getLog(ServerLifecycleListener.class);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Semicolon separated list of paths containing MBean desciptor resources.
+     */
+    protected String descriptors = null;
+
+    public String getDescriptors() {
+        return (this.descriptors);
+    }
+
+    public void setDescriptors(String descriptors) {
+        this.descriptors = descriptors;
+    }
+
+
+    // ---------------------------------------------- ContainerListener Methods
+
+
+    /**
+     * Handle a <code>ContainerEvent</code> from one of the Containers we are
+     * interested in.
+     *
+     * @param event The event that has occurred
+     */
+    public void containerEvent(ContainerEvent event) {
+
+        try {
+            String type = event.getType();
+            if (Container.ADD_CHILD_EVENT.equals(type)) {
+                processContainerAddChild(event.getContainer(),
+                                         (Container) event.getData());
+            } else if (Container.REMOVE_CHILD_EVENT.equals(type)) {
+                processContainerRemoveChild(event.getContainer(),
+                                            (Container) event.getData());
+            }
+        } catch (Exception e) {
+            log.error("Exception processing event " + event, e);
+        }
+
+    }
+
+
+    // ---------------------------------------------- LifecycleListener Methods
+
+
+    /**
+     * Primary entry point for startup and shutdown events.
+     *
+     * @param event The event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        Lifecycle lifecycle = event.getLifecycle();
+        if (Lifecycle.START_EVENT.equals(event.getType())) {
+
+            if (lifecycle instanceof Server) {
+                createMBeans();
+            }
+
+            // We are embedded.
+            if( lifecycle instanceof Service ) {
+                try {
+                    MBeanFactory factory = new MBeanFactory();
+                    createMBeans(factory);
+                    createMBeans((Service)lifecycle);
+                } catch( Exception ex ) {
+                    log.error("Create mbean factory");
+                }
+            }
+
+            /*
+            // Ignore events from StandardContext objects to avoid
+            // reregistering the context
+            if (lifecycle instanceof StandardContext)
+                return;
+            createMBeans();
+            */
+
+        } else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
+            try {
+                if (lifecycle instanceof Server) {
+                    destroyMBeans((Server)lifecycle);
+                }
+                if (lifecycle instanceof Service) {
+                    destroyMBeans((Service)lifecycle);
+                }
+            } catch (MBeanException t) {
+
+                Exception e = t.getTargetException();
+                if (e == null) {
+                    e = t;
+                }
+                log.error("destroyMBeans: MBeanException", e);
+
+            } catch (Throwable t) {
+
+                log.error("destroyMBeans: Throwable", t);
+
+            }
+            // FIXME: RMI adaptor should be stopped; however, this is
+            // undocumented in MX4J, and reports exist in the MX4J bug DB that
+            // this doesn't work
+
+        }
+
+        if ((Context.RELOAD_EVENT.equals(event.getType()))
+            || (Lifecycle.START_EVENT.equals(event.getType()))) {
+
+            // Give context a new handle to the MBean server if the
+            // context has been reloaded since reloading causes the
+            // context to lose its previous handle to the server
+            if (lifecycle instanceof StandardContext) {
+                // If the context is privileged, give a reference to it
+                // in a servlet context attribute
+                StandardContext context = (StandardContext)lifecycle;
+                if (context.getPrivileged()) {
+                    context.getServletContext().setAttribute
+                        (Globals.MBEAN_REGISTRY_ATTR,
+                         MBeanUtils.createRegistry());
+                    context.getServletContext().setAttribute
+                        (Globals.MBEAN_SERVER_ATTR,
+                         MBeanUtils.createServer());
+                }
+            }
+
+        }
+
+    }
+
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+
+    /**
+     * Handle a <code>PropertyChangeEvent</code> from one of the Containers
+     * we are interested in.
+     *
+     * @param event The event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        if (event.getSource() instanceof Container) {
+            try {
+                processContainerPropertyChange((Container) event.getSource(),
+                                               event.getPropertyName(),
+                                               event.getOldValue(),
+                                               event.getNewValue());
+            } catch (Exception e) {
+                log.error("Exception handling Container property change", e);
+            }
+        }/* else if (event.getSource() instanceof DefaultContext) {
+            try {
+                processDefaultContextPropertyChange
+                    ((DefaultContext) event.getSource(),
+                     event.getPropertyName(),
+                     event.getOldValue(),
+                     event.getNewValue());
+            } catch (Exception e) {
+                log.error("Exception handling DefaultContext property change", e);
+            }
+        }*/ else if (event.getSource() instanceof NamingResources) {
+            try {
+                processNamingResourcesPropertyChange
+                    ((NamingResources) event.getSource(),
+                     event.getPropertyName(),
+                     event.getOldValue(),
+                     event.getNewValue());
+            } catch (Exception e) {
+                log.error("Exception handling NamingResources property change", e);
+            }
+        } else if (event.getSource() instanceof Server) {
+            try {
+                processServerPropertyChange((Server) event.getSource(),
+                                            event.getPropertyName(),
+                                            event.getOldValue(),
+                                            event.getNewValue());
+            } catch (Exception e) {
+                log.error("Exception handing Server property change", e);
+            }
+        } else if (event.getSource() instanceof Service) {
+            try {
+                processServicePropertyChange((Service) event.getSource(),
+                                             event.getPropertyName(),
+                                             event.getOldValue(),
+                                             event.getNewValue());
+            } catch (Exception e) {
+                log.error("Exception handing Service property change", e);
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Create the MBeans that correspond to every existing node of our tree.
+     */
+    protected void createMBeans() {
+
+        try {
+
+            MBeanFactory factory = new MBeanFactory();
+            createMBeans(factory);
+            createMBeans(ServerFactory.getServer());
+
+        } catch (MBeanException t) {
+
+            Exception e = t.getTargetException();
+            if (e == null)
+                e = t;
+            log.error("createMBeans: MBeanException", e);
+
+        } catch (Throwable t) {
+
+            log.error("createMBeans: Throwable", t);
+
+        }
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified Connector and its nested components.
+     *
+     * @param connector Connector for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(Connector connector) throws Exception {
+
+        // Create the MBean for the Connnector itself
+//        if (log.isDebugEnabled())
+//            log.debug("Creating MBean for Connector " + connector);
+//        MBeanUtils.createMBean(connector);
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified Context and its nested components.
+     *
+     * @param context Context for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(Context context) throws Exception {
+
+        // Create the MBean for the Context itself
+//        if (log.isDebugEnabled())
+//            log.debug("Creating MBean for Context " + context);
+//        MBeanUtils.createMBean(context);
+        context.addContainerListener(this);
+        if (context instanceof StandardContext) {
+            ((StandardContext) context).addPropertyChangeListener(this);
+            ((StandardContext) context).addLifecycleListener(this);
+        }
+
+        // If the context is privileged, give a reference to it
+        // in a servlet context attribute
+        if (context.getPrivileged()) {
+            context.getServletContext().setAttribute
+                (Globals.MBEAN_REGISTRY_ATTR,
+                 MBeanUtils.createRegistry());
+            context.getServletContext().setAttribute
+                (Globals.MBEAN_SERVER_ATTR,
+                 MBeanUtils.createServer());
+        }
+
+        // Create the MBeans for the associated nested components
+        Loader cLoader = context.getLoader();
+        if (cLoader != null) {
+            if (log.isDebugEnabled())
+                log.debug("Creating MBean for Loader " + cLoader);
+            //MBeanUtils.createMBean(cLoader);
+        }
+        Manager cManager = context.getManager();
+        if (cManager != null) {
+            if (log.isDebugEnabled())
+                log.debug("Creating MBean for Manager " + cManager);
+            //MBeanUtils.createMBean(cManager);
+        }
+        Realm hRealm = context.getParent().getRealm();
+        Realm cRealm = context.getRealm();
+        if ((cRealm != null) && (cRealm != hRealm)) {
+            if (log.isDebugEnabled())
+                log.debug("Creating MBean for Realm " + cRealm);
+            //MBeanUtils.createMBean(cRealm);
+        }
+
+        // Create the MBeans for the NamingResources (if any)
+        NamingResources resources = context.getNamingResources();
+        createMBeans(resources);
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified ContextEnvironment entry.
+     *
+     * @param environment ContextEnvironment for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(ContextEnvironment environment)
+        throws Exception {
+
+        // Create the MBean for the ContextEnvironment itself
+        if (log.isDebugEnabled()) {
+            log.debug("Creating MBean for ContextEnvironment " + environment);
+        }
+        MBeanUtils.createMBean(environment);
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified ContextResource entry.
+     *
+     * @param resource ContextResource for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(ContextResource resource)
+        throws Exception {
+
+        // Create the MBean for the ContextResource itself
+        if (log.isDebugEnabled()) {
+            log.debug("Creating MBean for ContextResource " + resource);
+        }
+        MBeanUtils.createMBean(resource);
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified ContextResourceLink entry.
+     *
+     * @param resourceLink ContextResourceLink for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(ContextResourceLink resourceLink)
+        throws Exception {
+
+        // Create the MBean for the ContextResourceLink itself
+        if (log.isDebugEnabled()) {
+            log.debug("Creating MBean for ContextResourceLink " + resourceLink);
+        }
+        MBeanUtils.createMBean(resourceLink);
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified DefaultContext and its nested components.
+     *
+     * @param dcontext DefaultContext for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    /*
+    protected void createMBeans(DefaultContext dcontext) throws Exception {
+
+        // Create the MBean for the DefaultContext itself
+        if (log.isDebugEnabled())
+            log.debug("Creating MBean for DefaultContext " + dcontext);
+        MBeanUtils.createMBean(dcontext);
+
+        dcontext.addPropertyChangeListener(this);
+
+        // Create the MBeans for the associated nested components
+        Loader dLoader = dcontext.getLoader();
+        if (dLoader != null) {
+            if (log.isDebugEnabled())
+                log.debug("Creating MBean for Loader " + dLoader);
+            //MBeanUtils.createMBean(dLoader);
+        }
+
+        Manager dManager = dcontext.getManager();
+        if (dManager != null) {
+            if (log.isDebugEnabled())
+                log.debug("Creating MBean for Manager " + dManager);
+            //MBeanUtils.createMBean(dManager);
+        }
+
+        // Create the MBeans for the NamingResources (if any)
+        NamingResources resources = dcontext.getNamingResources();
+        createMBeans(resources);
+
+    }
+    */
+
+
+    /**
+     * Create the MBeans for the specified Engine and its nested components.
+     *
+     * @param engine Engine for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(Engine engine) throws Exception {
+
+        // Create the MBean for the Engine itself
+        if (log.isDebugEnabled()) {
+            log.debug("Creating MBean for Engine " + engine);
+        }
+        //MBeanUtils.createMBean(engine);
+        engine.addContainerListener(this);
+        if (engine instanceof StandardEngine) {
+            ((StandardEngine) engine).addPropertyChangeListener(this);
+        }
+
+        // Create the MBeans for the associated nested components
+        Realm eRealm = engine.getRealm();
+        if (eRealm != null) {
+            if (log.isDebugEnabled())
+                log.debug("Creating MBean for Realm " + eRealm);
+            //MBeanUtils.createMBean(eRealm);
+        }
+
+        // Create the MBeans for each child Host
+        Container hosts[] = engine.findChildren();
+        for (int j = 0; j < hosts.length; j++) {
+            createMBeans((Host) hosts[j]);
+        }
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified Host and its nested components.
+     *
+     * @param host Host for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(Host host) throws Exception {
+
+        // Create the MBean for the Host itself
+        if (log.isDebugEnabled()) {
+            log.debug("Creating MBean for Host " + host);
+        }
+        //MBeanUtils.createMBean(host);
+        host.addContainerListener(this);
+        if (host instanceof StandardHost) {
+            ((StandardHost) host).addPropertyChangeListener(this);
+        }
+
+        // Create the MBeans for the associated nested components
+        Realm eRealm = host.getParent().getRealm();
+        Realm hRealm = host.getRealm();
+        if ((hRealm != null) && (hRealm != eRealm)) {
+            if (log.isDebugEnabled())
+                log.debug("Creating MBean for Realm " + hRealm);
+            //MBeanUtils.createMBean(hRealm);
+        }
+
+        // Create the MBeans for each child Context
+        Container contexts[] = host.findChildren();
+        for (int k = 0; k < contexts.length; k++) {
+            createMBeans((Context) contexts[k]);
+        }
+
+    }
+
+
+    /**
+     * Create the MBeans for MBeanFactory.
+     *
+     * @param factory MBeanFactory for which to create MBean
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(MBeanFactory factory) throws Exception {
+
+        // Create the MBean for the MBeanFactory
+        if (log.isDebugEnabled())
+            log.debug("Creating MBean for MBeanFactory " + factory);
+        MBeanUtils.createMBean(factory);
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified NamingResources and its
+     * nested components.
+     *
+     * @param resources NamingResources for which to create MBeans
+     */
+    protected void createMBeans(NamingResources resources) throws Exception {
+
+        // Create the MBean for the NamingResources itself
+        if (log.isDebugEnabled()) {
+            log.debug("Creating MBean for NamingResources " + resources);
+        }
+        MBeanUtils.createMBean(resources);
+        resources.addPropertyChangeListener(this);
+
+        // Create the MBeans for each child environment entry
+        ContextEnvironment environments[] = resources.findEnvironments();
+        for (int i = 0; i < environments.length; i++) {
+            createMBeans(environments[i]);
+        }
+
+        // Create the MBeans for each child resource entry
+        ContextResource cresources[] = resources.findResources();
+        for (int i = 0; i < cresources.length; i++) {
+            createMBeans(cresources[i]);
+        }
+
+        // Create the MBeans for each child resource link entry
+        ContextResourceLink cresourcelinks[] = resources.findResourceLinks();
+        for (int i = 0; i < cresourcelinks.length; i++) {
+            createMBeans(cresourcelinks[i]);
+        }
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified Server and its nested components.
+     *
+     * @param server Server for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(Server server) throws Exception {
+
+        // Create the MBean for the Server itself
+        if (log.isDebugEnabled())
+            log.debug("Creating MBean for Server " + server);
+        //MBeanUtils.createMBean(server);
+        if (server instanceof StandardServer) {
+            ((StandardServer) server).addPropertyChangeListener(this);
+        }
+
+        // Create the MBeans for the global NamingResources (if any)
+        NamingResources resources = server.getGlobalNamingResources();
+        if (resources != null) {
+            createMBeans(resources);
+        }
+
+        // Create the MBeans for each child Service
+        Service services[] = server.findServices();
+        for (int i = 0; i < services.length; i++) {
+            // FIXME - Warp object hierarchy not currently supported
+            if (services[i].getContainer().getClass().getName().equals
+                ("org.apache.catalina.connector.warp.WarpEngine")) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Skipping MBean for Service " + services[i]);
+                }
+                continue;
+            }
+            createMBeans(services[i]);
+        }
+
+    }
+
+
+    /**
+     * Create the MBeans for the specified Service and its nested components.
+     *
+     * @param service Service for which to create MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean creation
+     */
+    protected void createMBeans(Service service) throws Exception {
+
+        // Create the MBean for the Service itself
+        if (log.isDebugEnabled())
+            log.debug("Creating MBean for Service " + service);
+        //MBeanUtils.createMBean(service);
+        if (service instanceof StandardService) {
+            ((StandardService) service).addPropertyChangeListener(this);
+        }
+
+        // Create the MBeans for the corresponding Connectors
+        Connector connectors[] = service.findConnectors();
+        for (int j = 0; j < connectors.length; j++) {
+            createMBeans(connectors[j]);
+        }
+
+        // Create the MBean for the associated Engine and friends
+        Engine engine = (Engine) service.getContainer();
+        if (engine != null) {
+            createMBeans(engine);
+        }
+
+    }
+
+
+
+
+    /**
+     * Deregister the MBeans for the specified Connector and its nested
+     * components.
+     *
+     * @param connector Connector for which to deregister MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(Connector connector, Service service)
+        throws Exception {
+
+//        // deregister the MBean for the Connector itself
+//        if (log.isDebugEnabled())
+//            log.debug("Destroying MBean for Connector " + connector);
+//        MBeanUtils.destroyMBean(connector, service);
+
+    }
+
+
+    /**
+     * Deregister the MBeans for the specified Context and its nested
+     * components.
+     *
+     * @param context Context for which to deregister MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(Context context) throws Exception {
+
+        // Deregister ourselves as a ContainerListener
+        context.removeContainerListener(this);
+
+        // Destroy the MBeans for the associated nested components
+        Realm hRealm = context.getParent().getRealm();
+        Realm cRealm = context.getRealm();
+        if ((cRealm != null) && (cRealm != hRealm)) {
+            if (log.isDebugEnabled())
+                log.debug("Destroying MBean for Realm " + cRealm);
+            //MBeanUtils.destroyMBean(cRealm);
+        }
+        Manager cManager = context.getManager();
+        if (cManager != null) {
+            if (log.isDebugEnabled())
+                log.debug("Destroying MBean for Manager " + cManager);
+            //MBeanUtils.destroyMBean(cManager);
+        }
+        Loader cLoader = context.getLoader();
+        if (cLoader != null) {
+            if (log.isDebugEnabled())
+                log.debug("Destroying MBean for Loader " + cLoader);
+            //MBeanUtils.destroyMBean(cLoader);
+        }
+
+        // Destroy the MBeans for the NamingResources (if any)
+        NamingResources resources = context.getNamingResources();
+        if (resources != null) {
+            destroyMBeans(resources);
+        }
+
+        // deregister the MBean for the Context itself
+        if (log.isDebugEnabled())
+            log.debug("Destroying MBean for Context " + context);
+        //MBeanUtils.destroyMBean(context);
+        if (context instanceof StandardContext) {
+            ((StandardContext) context).
+                removePropertyChangeListener(this);
+        }
+
+    }
+
+
+    /**
+     * Deregister the MBeans for the specified ContextEnvironment entry.
+     *
+     * @param environment ContextEnvironment for which to destroy MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(ContextEnvironment environment)
+        throws Exception {
+
+        // Destroy the MBean for the ContextEnvironment itself
+        if (log.isDebugEnabled()) {
+            log.debug("Destroying MBean for ContextEnvironment " + environment);
+        }
+        MBeanUtils.destroyMBean(environment);
+
+    }
+
+
+    /**
+     * Deregister the MBeans for the specified ContextResource entry.
+     *
+     * @param resource ContextResource for which to destroy MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(ContextResource resource)
+        throws Exception {
+
+        // Destroy the MBean for the ContextResource itself
+        if (log.isDebugEnabled()) {
+            log.debug("Destroying MBean for ContextResource " + resource);
+        }
+        MBeanUtils.destroyMBean(resource);
+
+    }
+
+
+    /**
+     * Deregister the MBeans for the specified ContextResourceLink entry.
+     *
+     * @param resourceLink ContextResourceLink for which to destroy MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(ContextResourceLink resourceLink)
+        throws Exception {
+
+        // Destroy the MBean for the ContextResourceLink itself
+        if (log.isDebugEnabled()) {
+            log.debug("Destroying MBean for ContextResourceLink " + resourceLink);
+        }
+        MBeanUtils.destroyMBean(resourceLink);
+
+    }
+
+
+    /**
+     * Deregister the MBeans for the specified DefaultContext and its nested
+     * components.
+     *
+     * @param dcontext DefaultContext for which to deregister MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    /*
+    protected void destroyMBeans(DefaultContext dcontext) throws Exception {
+
+        Manager dManager = dcontext.getManager();
+        if (dManager != null) {
+            if (log.isDebugEnabled())
+                log.debug("Destroying MBean for Manager " + dManager);
+            //MBeanUtils.destroyMBean(dManager);
+        }
+
+        Loader dLoader = dcontext.getLoader();
+        if (dLoader != null) {
+            if (log.isDebugEnabled())
+                log.debug("Destroying MBean for Loader " + dLoader);
+            //MBeanUtils.destroyMBean(dLoader);
+        }
+
+        // Destroy the MBeans for the NamingResources (if any)
+        NamingResources resources = dcontext.getNamingResources();
+        if (resources != null) {
+            destroyMBeans(resources);
+        }
+
+        // deregister the MBean for the DefaultContext itself
+        if (log.isDebugEnabled())
+            log.debug("Destroying MBean for Context " + dcontext);
+        MBeanUtils.destroyMBean(dcontext);
+        dcontext.removePropertyChangeListener(this);
+
+    }
+    */
+
+
+    /**
+     * Deregister the MBeans for the specified Engine and its nested
+     * components.
+     *
+     * @param engine Engine for which to destroy MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(Engine engine) throws Exception {
+
+        // Deregister ourselves as a ContainerListener
+        engine.removeContainerListener(this);
+
+        // Deregister the MBeans for each child Host
+        Container hosts[] = engine.findChildren();
+        for (int k = 0; k < hosts.length; k++) {
+            destroyMBeans((Host) hosts[k]);
+        }
+
+        // Deregister the MBeans for the associated nested components
+        Realm eRealm = engine.getRealm();
+        if (eRealm != null) {
+            if (log.isDebugEnabled())
+                log.debug("Destroying MBean for Realm " + eRealm);
+            //MBeanUtils.destroyMBean(eRealm);
+        }
+
+        // Deregister the MBean for the Engine itself
+        if (log.isDebugEnabled()) {
+            log.debug("Destroying MBean for Engine " + engine);
+        }
+        //MBeanUtils.destroyMBean(engine);
+
+    }
+
+
+    /**
+     * Deregister the MBeans for the specified Host and its nested components.
+     *
+     * @param host Host for which to destroy MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(Host host) throws Exception {
+
+        // Deregister ourselves as a ContainerListener
+        host.removeContainerListener(this);
+
+        // Deregister the MBeans for each child Context
+        Container contexts[] = host.findChildren();
+        for (int k = 0; k < contexts.length; k++) {
+            destroyMBeans((Context) contexts[k]);
+        }
+
+
+        // Deregister the MBeans for the associated nested components
+        Realm eRealm = host.getParent().getRealm();
+        Realm hRealm = host.getRealm();
+        if ((hRealm != null) && (hRealm != eRealm)) {
+            if (log.isDebugEnabled())
+                log.debug("Destroying MBean for Realm " + hRealm);
+            //MBeanUtils.destroyMBean(hRealm);
+        }
+
+        // Deregister the MBean for the Host itself
+        if (log.isDebugEnabled()) {
+            log.debug("Destroying MBean for Host " + host);
+        }
+        //MBeanUtils.destroyMBean(host);
+
+    }
+
+
+    /**
+     * Deregister the MBeans for the specified NamingResources and its
+     * nested components.
+     *
+     * @param resources NamingResources for which to destroy MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(NamingResources resources) throws Exception {
+
+        // Destroy the MBeans for each child resource entry
+        ContextResource cresources[] = resources.findResources();
+        for (int i = 0; i < cresources.length; i++) {
+            destroyMBeans(cresources[i]);
+        }
+
+        // Destroy the MBeans for each child resource link entry
+        ContextResourceLink cresourcelinks[] = resources.findResourceLinks();
+        for (int i = 0; i < cresourcelinks.length; i++) {
+            destroyMBeans(cresourcelinks[i]);
+        }
+
+        // Destroy the MBeans for each child environment entry
+        ContextEnvironment environments[] = resources.findEnvironments();
+        for (int i = 0; i < environments.length; i++) {
+            destroyMBeans(environments[i]);
+        }
+
+        // Destroy the MBean for the NamingResources itself
+        if (log.isDebugEnabled()) {
+            log.debug("Destroying MBean for NamingResources " + resources);
+        }
+        MBeanUtils.destroyMBean(resources);
+        resources.removePropertyChangeListener(this);
+
+    }
+
+
+    /**
+     * Deregister the MBeans for the specified Server and its related
+     * components.
+     *
+     * @param server Server for which to destroy MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(Server server) throws Exception {
+
+        // Destroy the MBeans for the global NamingResources (if any)
+        NamingResources resources = server.getGlobalNamingResources();
+        if (resources != null) {
+            destroyMBeans(resources);
+        }
+
+        // Destroy the MBeans for each child Service
+        Service services[] = server.findServices();
+        for (int i = 0; i < services.length; i++) {
+            // FIXME - Warp object hierarchy not currently supported
+            if (services[i].getContainer().getClass().getName().equals
+                ("org.apache.catalina.connector.warp.WarpEngine")) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Skipping MBean for Service " + services[i]);
+                }
+                continue;
+            }
+            destroyMBeans(services[i]);
+        }
+
+        // Destroy the MBean for the Server itself
+        if (log.isDebugEnabled()) {
+            log.debug("Destroying MBean for Server " + server);
+        }
+        //MBeanUtils.destroyMBean(server);
+        if (server instanceof StandardServer) {
+            ((StandardServer) server).removePropertyChangeListener(this);
+        }
+
+    }
+
+
+    /**
+     * Deregister the MBeans for the specified Service and its nested
+     * components.
+     *
+     * @param service Service for which to destroy MBeans
+     *
+     * @exception Exception if an exception is thrown during MBean destruction
+     */
+    protected void destroyMBeans(Service service) throws Exception {
+
+        // Deregister the MBeans for the associated Engine
+        Engine engine = (Engine) service.getContainer();
+        if (engine != null) {
+            //destroyMBeans(engine);
+        }
+
+//        // Deregister the MBeans for the corresponding Connectors
+//        Connector connectors[] = service.findConnectors();
+//        for (int j = 0; j < connectors.length; j++) {
+//            destroyMBeans(connectors[j], service);
+//        }
+
+        // Deregister the MBean for the Service itself
+        if (log.isDebugEnabled()) {
+            log.debug("Destroying MBean for Service " + service);
+        }
+        //MBeanUtils.destroyMBean(service);
+        if (service instanceof StandardService) {
+            ((StandardService) service).removePropertyChangeListener(this);
+        }
+
+    }
+
+
+    /**
+     * Process the addition of a new child Container to a parent Container.
+     *
+     * @param parent Parent container
+     * @param child Child container
+     */
+    protected void processContainerAddChild(Container parent,
+                                            Container child) {
+
+        if (log.isDebugEnabled())
+            log.debug("Process addChild[parent=" + parent + ",child=" + child + "]");
+
+        try {
+            if (child instanceof Context) {
+                createMBeans((Context) child);
+            } else if (child instanceof Engine) {
+                createMBeans((Engine) child);
+            } else if (child instanceof Host) {
+                createMBeans((Host) child);
+            }
+        } catch (MBeanException t) {
+            Exception e = t.getTargetException();
+            if (e == null)
+                e = t;
+            log.error("processContainerAddChild: MBeanException", e);
+        } catch (Throwable t) {
+            log.error("processContainerAddChild: Throwable", t);
+        }
+
+    }
+
+
+
+
+    /**
+     * Process a property change event on a Container.
+     *
+     * @param container The container on which this event occurred
+     * @param propertyName The name of the property that changed
+     * @param oldValue The previous value (may be <code>null</code>)
+     * @param newValue The new value (may be <code>null</code>)
+     *
+     * @exception Exception if an exception is thrown
+     */
+    protected void processContainerPropertyChange(Container container,
+                                                  String propertyName,
+                                                  Object oldValue,
+                                                  Object newValue)
+        throws Exception {
+
+        if (log.isTraceEnabled()) {
+            log.trace("propertyChange[container=" + container +
+                ",propertyName=" + propertyName +
+                ",oldValue=" + oldValue +
+                ",newValue=" + newValue + "]");
+        }
+        if ("loader".equals(propertyName)) {
+            if (oldValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Removing MBean for Loader " + oldValue);
+                }
+                MBeanUtils.destroyMBean((Loader) oldValue);
+            }
+            if (newValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Creating MBean for Loader " + newValue);
+                }
+                MBeanUtils.createMBean((Loader) newValue);
+            }
+        } else if ("logger".equals(propertyName)) {
+            if (oldValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Removing MBean for Logger " + oldValue);
+                }
+               // MBeanUtils.destroyMBean((Logger) oldValue);
+            }
+            if (newValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Creating MBean for Logger " + newValue);
+                }
+                //MBeanUtils.createMBean((Logger) newValue);
+            }
+        } else if ("manager".equals(propertyName)) {
+            if (oldValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Removing MBean for Manager " + oldValue);
+                }
+                //MBeanUtils.destroyMBean((Manager) oldValue);
+            }
+            if (newValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Creating MBean for Manager " + newValue);
+                }
+                //MBeanUtils.createMBean((Manager) newValue);
+            }
+        } else if ("realm".equals(propertyName)) {
+            if (oldValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Removing MBean for Realm " + oldValue);
+                }
+                MBeanUtils.destroyMBean((Realm) oldValue);
+            }
+            if (newValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Creating MBean for Realm " + newValue);
+                }
+                //MBeanUtils.createMBean((Realm) newValue);
+            }
+        } else if ("service".equals(propertyName)) {
+            if (oldValue != null) {
+                destroyMBeans((Service) oldValue);
+            }
+            if (newValue != null) {
+                createMBeans((Service) newValue);
+            }
+        }
+
+    }
+
+
+    /**
+     * Process a property change event on a DefaultContext.
+     *
+     * @param defaultContext The DefaultContext on which this event occurred
+     * @param propertyName The name of the property that changed
+     * @param oldValue The previous value (may be <code>null</code>)
+     * @param newValue The new value (may be <code>null</code>)
+     *
+     * @exception Exception if an exception is thrown
+     */
+    /*
+    protected void processDefaultContextPropertyChange(DefaultContext defaultContext,
+                                                  String propertyName,
+                                                  Object oldValue,
+                                                  Object newValue)
+        throws Exception {
+
+        if (log.isTraceEnabled()) {
+            log.trace("propertyChange[defaultContext=" + defaultContext +
+                ",propertyName=" + propertyName +
+                ",oldValue=" + oldValue +
+                ",newValue=" + newValue + "]");
+        }
+        if ("loader".equals(propertyName)) {
+            if (oldValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Removing MBean for Loader " + oldValue);
+                }
+                MBeanUtils.destroyMBean((Loader) oldValue);
+            }
+            if (newValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Creating MBean for Loader " + newValue);
+                }
+                MBeanUtils.createMBean((Loader) newValue);
+            }
+        } else if ("logger".equals(propertyName)) {
+            if (oldValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Removing MBean for Logger " + oldValue);
+                }
+                //MBeanUtils.destroyMBean((Logger) oldValue);
+            }
+            if (newValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Creating MBean for Logger " + newValue);
+                }
+                //MBeanUtils.createMBean((Logger) newValue);
+            }
+        } else if ("manager".equals(propertyName)) {
+            if (oldValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Removing MBean for Manager " + oldValue);
+                }
+                MBeanUtils.destroyMBean((Manager) oldValue);
+            }
+            if (newValue != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Creating MBean for Manager " + newValue);
+                }
+                MBeanUtils.createMBean((Manager) newValue);
+            }
+        } else if ("realm".equals(propertyName)) {
+            if (oldValue != null) {
+//                if (log.isDebugEnabled()) {
+//                    log.debug("Removing MBean for Realm " + oldValue);
+//                }
+//                //MBeanUtils.destroyMBean((Realm) oldValue);
+            }
+            if (newValue != null) {
+//                if (log.isDebugEnabled()) {
+//                    log.debug("Creating MBean for Realm " + newValue);
+//                }
+//                //MBeanUtils.createMBean((Realm) newValue);
+            }
+        } else if ("service".equals(propertyName)) {
+            if (oldValue != null) {
+                destroyMBeans((Service) oldValue);
+            }
+            if (newValue != null) {
+                createMBeans((Service) newValue);
+            }
+        }
+
+    }*/
+
+
+    /**
+     * Process the removal of a child Container from a parent Container.
+     *
+     * @param parent Parent container
+     * @param child Child container
+     */
+    protected void processContainerRemoveChild(Container parent,
+                                               Container child) {
+
+        if (log.isDebugEnabled())
+            log.debug("Process removeChild[parent=" + parent + ",child=" +
+                child + "]");
+
+        try {
+            if (child instanceof Context) {
+                Context context = (Context) child;
+                if (context.getPrivileged()) {
+                    context.getServletContext().removeAttribute
+                        (Globals.MBEAN_REGISTRY_ATTR);
+                    context.getServletContext().removeAttribute
+                        (Globals.MBEAN_SERVER_ATTR);
+                }
+                if (log.isDebugEnabled())
+                    log.debug("  Removing MBean for Context " + context);
+                destroyMBeans(context);
+                if (context instanceof StandardContext) {
+                    ((StandardContext) context).
+                        removePropertyChangeListener(this);
+                }
+            } else if (child instanceof Host) {
+                Host host = (Host) child;
+                destroyMBeans(host);
+                if (host instanceof StandardHost) {
+                    ((StandardHost) host).
+                        removePropertyChangeListener(this);
+                }
+            }
+        } catch (MBeanException t) {
+            Exception e = t.getTargetException();
+            if (e == null)
+                e = t;
+            log.error("processContainerRemoveChild: MBeanException", e);
+        } catch (Throwable t) {
+            log.error("processContainerRemoveChild: Throwable", t);
+        }
+
+    }
+
+
+    /**
+     * Process a property change event on a NamingResources.
+     *
+     * @param resources The global naming resources on which this
+     *  event occurred
+     * @param propertyName The name of the property that changed
+     * @param oldValue The previous value (may be <code>null</code>)
+     * @param newValue The new value (may be <code>null</code>)
+     *
+     * @exception Exception if an exception is thrown
+     */
+    protected void processNamingResourcesPropertyChange
+        (NamingResources resources, String propertyName,
+         Object oldValue, Object newValue)
+        throws Exception {
+
+        if (log.isTraceEnabled()) {
+            log.trace("propertyChange[namingResources=" + resources +
+                ",propertyName=" + propertyName +
+                ",oldValue=" + oldValue +
+                ",newValue=" + newValue + "]");
+        }
+
+        // FIXME - Add other resource types when supported by admin tool
+        if ("environment".equals(propertyName)) {
+            if (oldValue != null) {
+                destroyMBeans((ContextEnvironment) oldValue);
+            }
+            if (newValue != null) {
+                createMBeans((ContextEnvironment) newValue);
+            }
+        } else if ("resource".equals(propertyName)) {
+            if (oldValue != null) {
+                destroyMBeans((ContextResource) oldValue);
+            }
+            if (newValue != null) {
+                createMBeans((ContextResource) newValue);
+            }
+        } else if ("resourceLink".equals(propertyName)) {
+            if (oldValue != null) {
+                destroyMBeans((ContextResourceLink) oldValue);
+            }
+            if (newValue != null) {
+                createMBeans((ContextResourceLink) newValue);
+            }
+        }
+
+    }
+
+
+    /**
+     * Process a property change event on a Server.
+     *
+     * @param server The server on which this event occurred
+     * @param propertyName The name of the property that changed
+     * @param oldValue The previous value (may be <code>null</code>)
+     * @param newValue The new value (may be <code>null</code>)
+     *
+     * @exception Exception if an exception is thrown
+     */
+    protected void processServerPropertyChange(Server server,
+                                               String propertyName,
+                                               Object oldValue,
+                                               Object newValue)
+        throws Exception {
+
+        if (log.isTraceEnabled()) {
+            log.trace("propertyChange[server=" + server +
+                ",propertyName=" + propertyName +
+                ",oldValue=" + oldValue +
+                ",newValue=" + newValue + "]");
+        }
+        if ("globalNamingResources".equals(propertyName)) {
+            if (oldValue != null) {
+                destroyMBeans((NamingResources) oldValue);
+            }
+            if (newValue != null) {
+                createMBeans((NamingResources) newValue);
+            }
+        } else if ("service".equals(propertyName)) {
+            if (oldValue != null) {
+                destroyMBeans((Service) oldValue);
+            }
+            if (newValue != null) {
+                createMBeans((Service) newValue);
+            }
+        }
+
+    }
+
+
+    /**
+     * Process a property change event on a Service.
+     *
+     * @param service The service on which this event occurred
+     * @param propertyName The name of the property that changed
+     * @param oldValue The previous value (may be <code>null</code>)
+     * @param newValue The new value (may be <code>null</code>)
+     *
+     * @exception Exception if an exception is thrown
+     */
+    protected void processServicePropertyChange(Service service,
+                                                String propertyName,
+                                                Object oldValue,
+                                                Object newValue)
+        throws Exception {
+
+        if (log.isTraceEnabled()) {
+            log.trace("propertyChange[service=" + service +
+                ",propertyName=" + propertyName +
+                ",oldValue=" + oldValue +
+                ",newValue=" + newValue + "]");
+        }
+        if ("connector".equals(propertyName)) {
+            if (oldValue != null) {
+                destroyMBeans((Connector) oldValue, service);
+            }
+            if (newValue != null) {
+                createMBeans((Connector) newValue);
+            }
+        } else if ("container".equals(propertyName)) {
+            if (oldValue != null) {
+                destroyMBeans((Engine) oldValue);
+            }
+            if (newValue != null) {
+                createMBeans((Engine) newValue);
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/StandardContextMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/StandardContextMBean.java
new file mode 100644
index 0000000..771422d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/StandardContextMBean.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+import java.util.ArrayList;
+
+import javax.management.MBeanException;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.core.StandardContext</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class StandardContextMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public StandardContextMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+    
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * JDK compatibility support
+     */
+    private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    protected Registry registry = MBeanUtils.createRegistry();
+
+    /**
+     * The <code>ManagedBean</code> information describing this MBean.
+     */
+    protected ManagedBean managed =
+        registry.findManagedBean("StandardContext");
+
+    
+    // ------------------------------------------------------------- Attributes
+
+    
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    private NamingResources getNamingResources() {
+        
+        return ((StandardContext)this.resource).getNamingResources();
+    
+    }
+    
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    public void reload() {
+        
+        ((StandardContext)this.resource).reload();
+    
+    }
+    
+    
+    /**
+     * Return the MBean Names of the set of defined environment entries for  
+     * this web application
+     */
+    public String[] getEnvironments() {
+        ContextEnvironment[] envs = getNamingResources().findEnvironments();
+        ArrayList results = new ArrayList();
+        for (int i = 0; i < envs.length; i++) {
+            try {
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), envs[i]);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for environment " + envs[i]);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+    
+    
+    /**
+     * Return the MBean Names of all the defined resource references for this
+     * application.
+     */
+    public String[] getResources() {
+        
+        ContextResource[] resources = getNamingResources().findResources();
+        ArrayList results = new ArrayList();
+        for (int i = 0; i < resources.length; i++) {
+            try {
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), resources[i]);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for resource " + resources[i]);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+      
+    /**
+     * Return the MBean Names of all the defined resource links for this 
+     * application
+     */
+    public String[] getResourceLinks() {
+        
+        ContextResourceLink[] links = getNamingResources().findResourceLinks();
+        ArrayList results = new ArrayList();
+        for (int i = 0; i < links.length; i++) {
+            try {
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), links[i]);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for resource " + links[i]);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    public javax.naming.directory.DirContext getStaticResources() {
+
+        return ((StandardContext)this.resource).getResources();
+
+    }
+
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    public String[] getWelcomeFiles() {
+
+        return ((StandardContext)this.resource).findWelcomeFiles();
+
+    }
+
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * Add an environment entry for this web application.
+     *
+     * @param envName New environment entry name
+     */
+    public String addEnvironment(String envName, String type) 
+        throws MalformedObjectNameException {
+
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return null;
+        }
+        ContextEnvironment env = nresources.findEnvironment(envName);
+        if (env != null) {
+            throw new IllegalArgumentException
+                ("Invalid environment name - already exists '" + envName + "'");
+        }
+        env = new ContextEnvironment();
+        env.setName(envName);
+        env.setType(type);
+        nresources.addEnvironment(env);
+        
+        // Return the corresponding MBean name
+        ManagedBean managed = registry.findManagedBean("ContextEnvironment");
+        ObjectName oname =
+            MBeanUtils.createObjectName(managed.getDomain(), env);
+        return (oname.toString());
+        
+    }
+
+    
+    /**
+     * Add a resource reference for this web application.
+     *
+     * @param resourceName New resource reference name
+     */
+    public String addResource(String resourceName, String type) 
+        throws MalformedObjectNameException {
+        
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return null;
+        }
+        ContextResource resource = nresources.findResource(resourceName);
+        if (resource != null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name - already exists'" + resourceName + "'");
+        }
+        resource = new ContextResource();
+        resource.setName(resourceName);
+        resource.setType(type);
+        nresources.addResource(resource);
+        
+        // Return the corresponding MBean name
+        ManagedBean managed = registry.findManagedBean("ContextResource");
+        ObjectName oname =
+            MBeanUtils.createObjectName(managed.getDomain(), resource);
+        return (oname.toString());
+    }
+
+    
+    /**
+     * Add a resource link for this web application.
+     *
+     * @param resourceLinkName New resource link name
+     */
+    public String addResourceLink(String resourceLinkName, String global, 
+                String name, String type) throws MalformedObjectNameException {
+        
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return null;
+        }
+        ContextResourceLink resourceLink = 
+                                nresources.findResourceLink(resourceLinkName);
+        if (resourceLink != null) {
+            throw new IllegalArgumentException
+                ("Invalid resource link name - already exists'" + 
+                                                        resourceLinkName + "'");
+        }
+        resourceLink = new ContextResourceLink();
+        resourceLink.setGlobal(global);
+        resourceLink.setName(resourceLinkName);
+        resourceLink.setType(type);
+        nresources.addResourceLink(resourceLink);
+        
+        // Return the corresponding MBean name
+        ManagedBean managed = registry.findManagedBean("ContextResourceLink");
+        ObjectName oname =
+            MBeanUtils.createObjectName(managed.getDomain(), resourceLink);
+        return (oname.toString());
+    }    
+    
+    
+    /**
+     * Remove any environment entry with the specified name.
+     *
+     * @param envName Name of the environment entry to remove
+     */
+    public void removeEnvironment(String envName) {
+
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return;
+        }
+        ContextEnvironment env = nresources.findEnvironment(envName);
+        if (env == null) {
+            throw new IllegalArgumentException
+                ("Invalid environment name '" + envName + "'");
+        }
+        nresources.removeEnvironment(envName);
+
+    }
+    
+    
+    /**
+     * Remove any resource reference with the specified name.
+     *
+     * @param resourceName Name of the resource reference to remove
+     */
+    public void removeResource(String resourceName) {
+
+        resourceName = ObjectName.unquote(resourceName);
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return;
+        }
+        ContextResource resource = nresources.findResource(resourceName);
+        if (resource == null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name '" + resourceName + "'");
+        }
+        nresources.removeResource(resourceName);
+    }
+    
+    
+    /**
+     * Remove any resource link with the specified name.
+     *
+     * @param resourceLinkName Name of the resource reference to remove
+     */
+    public void removeResourceLink(String resourceLinkName) {
+
+        resourceLinkName = ObjectName.unquote(resourceLinkName);
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return;
+        }
+        ContextResourceLink resource = nresources.findResourceLink(resourceLinkName);
+        if (resource == null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name '" + resourceLinkName + "'");
+        }
+        nresources.removeResourceLink(resourceLinkName);
+    }
+ 
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/StandardEngineMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/StandardEngineMBean.java
new file mode 100644
index 0000000..1769c27
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/StandardEngineMBean.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.commons.modeler.BaseModelMBean;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.core.StandardEngine</code> component.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class StandardEngineMBean extends BaseModelMBean {
+
+    /**
+     * The <code>MBeanServer</code> for this application.
+     */
+    private static MBeanServer mserver = MBeanUtils.createServer();
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public StandardEngineMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+
+    // ------------------------------------------------------------- Operations
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/StandardHostMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/StandardHostMBean.java
new file mode 100644
index 0000000..03715fa
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/StandardHostMBean.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.StandardHost;
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.core.StandardHost</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class StandardHostMBean extends BaseModelMBean {
+
+    /**
+     * The <code>MBeanServer</code> for this application.
+     */
+    private static MBeanServer mserver = MBeanUtils.createServer();
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public StandardHostMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+
+    // ------------------------------------------------------------- Operations
+
+
+   /**
+     * Add an alias name that should be mapped to this Host
+     *
+     * @param alias The alias to be added
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public void addAlias(String alias)
+        throws Exception {
+
+        StandardHost host = (StandardHost) this.resource;
+        host.addAlias(alias);
+
+    }
+
+
+   /**
+     * Return the set of alias names for this Host
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String [] findAliases()
+        throws Exception {
+
+        StandardHost host = (StandardHost) this.resource;
+        return host.findAliases();
+
+    }
+
+
+   /**
+     * Return the MBean Names of the Valves assoicated with this Host
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public String [] getValves()
+        throws Exception {
+
+        Registry registry = MBeanUtils.createRegistry();
+        StandardHost host = (StandardHost) this.resource;
+        String mname = MBeanUtils.createManagedName(host);
+        ManagedBean managed = registry.findManagedBean(mname);
+        String domain = null;
+        if (managed != null) {
+            domain = managed.getDomain();
+        }
+        if (domain == null)
+            domain = mserver.getDefaultDomain();
+        Valve [] valves = host.getValves();
+        String [] mbeanNames = new String[valves.length];
+        for (int i = 0; i < valves.length; i++) {
+            mbeanNames[i] =
+                MBeanUtils.createObjectName(domain, valves[i]).toString();
+        }
+
+        return mbeanNames;
+
+    }
+
+
+   /**
+     * Return the specified alias name from the aliases for this Host
+     *
+     * @param alias Alias name to be removed
+     *
+     * @exception Exception if an MBean cannot be created or registered
+     */
+    public void removeAlias(String alias)
+        throws Exception {
+
+        StandardHost host = (StandardHost) this.resource;
+        host.removeAlias(alias);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/StandardServerMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/StandardServerMBean.java
new file mode 100644
index 0000000..27e04c6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/StandardServerMBean.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.RuntimeOperationsException;
+import org.apache.catalina.Server;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.core.StandardServer;
+import org.apache.commons.modeler.BaseModelMBean;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.core.StandardServer</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class StandardServerMBean extends BaseModelMBean {
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The <code>MBeanServer</code> for this application.
+     */
+    private static MBeanServer mserver = MBeanUtils.createServer();
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public StandardServerMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * Write the configuration information for this entire <code>Server</code>
+     * out to the server.xml configuration file.
+     *
+     * @exception InstanceNotFoundException if the managed resource object
+     *  cannot be found
+     * @exception MBeanException if the initializer of the object throws
+     *  an exception, or persistence is not supported
+     * @exception RuntimeOperationsException if an exception is reported
+     *  by the persistence mechanism
+     */
+    public synchronized void store() throws InstanceNotFoundException,
+        MBeanException, RuntimeOperationsException {
+
+        Server server = ServerFactory.getServer();
+        if (server instanceof StandardServer) {
+            try {
+                ((StandardServer) server).storeConfig();
+            } catch (Exception e) {
+                throw new MBeanException(e, "Error updating conf/server.xml");
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/StandardServiceMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/StandardServiceMBean.java
new file mode 100644
index 0000000..ad26dc3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/StandardServiceMBean.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.commons.modeler.BaseModelMBean;
+
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.core.StandardService</code> component.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class StandardServiceMBean extends BaseModelMBean {
+
+    /**
+     * The <code>MBeanServer</code> for this application.
+     */
+    private static MBeanServer mserver = MBeanUtils.createServer();
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public StandardServiceMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+
+    // ------------------------------------------------------------- Operations
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/UserMBean.java b/container/catalina/src/share/org/apache/catalina/mbeans/UserMBean.java
new file mode 100644
index 0000000..08c3e43
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/UserMBean.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.mbeans;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.catalina.Group;
+import org.apache.catalina.Role;
+import org.apache.catalina.User;
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * <p>A <strong>ModelMBean</strong> implementation for the
+ * <code>org.apache.catalina.User</code> component.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UserMBean extends BaseModelMBean {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a <code>ModelMBean</code> with default
+     * <code>ModelMBeanInfo</code> information.
+     *
+     * @exception MBeanException if the initializer of an object
+     *  throws an exception
+     * @exception RuntimeOperationsException if an IllegalArgumentException
+     *  occurs
+     */
+    public UserMBean()
+        throws MBeanException, RuntimeOperationsException {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * JDK compatibility support
+     */
+    private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The configuration information registry for our managed beans.
+     */
+    protected Registry registry = MBeanUtils.createRegistry();
+
+
+    /**
+     * The <code>MBeanServer</code> in which we are registered.
+     */
+    protected MBeanServer mserver = MBeanUtils.createServer();
+
+
+    /**
+     * The <code>ManagedBean</code> information describing this MBean.
+     */
+    protected ManagedBean managed =
+        registry.findManagedBean("User");
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+    /**
+     * Return the MBean Names of all groups this user is a member of.
+     */
+    public String[] getGroups() {
+
+        User user = (User) this.resource;
+        ArrayList results = new ArrayList();
+        Iterator groups = user.getGroups();
+        while (groups.hasNext()) {
+            Group group = null;
+            try {
+                group = (Group) groups.next();
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), group);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for group " + group);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+
+    /**
+     * Return the MBean Names of all roles assigned to this user.
+     */
+    public String[] getRoles() {
+
+        User user = (User) this.resource;
+        ArrayList results = new ArrayList();
+        Iterator roles = user.getRoles();
+        while (roles.hasNext()) {
+            Role role = null;
+            try {
+                role = (Role) roles.next();
+                ObjectName oname =
+                    MBeanUtils.createObjectName(managed.getDomain(), role);
+                results.add(oname.toString());
+            } catch (MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for role " + role);
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+        }
+        return ((String[]) results.toArray(new String[results.size()]));
+
+    }
+
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * Add a new {@link Group} to those this user belongs to.
+     *
+     * @param groupname Group name of the new group
+     */
+    public void addGroup(String groupname) {
+
+        User user = (User) this.resource;
+        if (user == null) {
+            return;
+        }
+        Group group = user.getUserDatabase().findGroup(groupname);
+        if (group == null) {
+            throw new IllegalArgumentException
+                ("Invalid group name '" + groupname + "'");
+        }
+        user.addGroup(group);
+
+    }
+
+
+    /**
+     * Add a new {@link Role} to those this user belongs to.
+     *
+     * @param rolename Role name of the new role
+     */
+    public void addRole(String rolename) {
+
+        User user = (User) this.resource;
+        if (user == null) {
+            return;
+        }
+        Role role = user.getUserDatabase().findRole(rolename);
+        if (role == null) {
+            throw new IllegalArgumentException
+                ("Invalid role name '" + rolename + "'");
+        }
+        user.addRole(role);
+
+    }
+
+
+    /**
+     * Remove a {@link Group} from those this user belongs to.
+     *
+     * @param groupname Group name of the old group
+     */
+    public void removeGroup(String groupname) {
+
+        User user = (User) this.resource;
+        if (user == null) {
+            return;
+        }
+        Group group = user.getUserDatabase().findGroup(groupname);
+        if (group == null) {
+            throw new IllegalArgumentException
+                ("Invalid group name '" + groupname + "'");
+        }
+        user.removeGroup(group);
+
+    }
+
+
+    /**
+     * Remove a {@link Role} from those this user belongs to.
+     *
+     * @param rolename Role name of the old role
+     */
+    public void removeRole(String rolename) {
+
+        User user = (User) this.resource;
+        if (user == null) {
+            return;
+        }
+        Role role = user.getUserDatabase().findRole(rolename);
+        if (role == null) {
+            throw new IllegalArgumentException
+                ("Invalid role name '" + rolename + "'");
+        }
+        user.removeRole(role);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/mbeans/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/mbeans/mbeans-descriptors.xml
new file mode 100644
index 0000000..bb1cd0b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/mbeans/mbeans-descriptors.xml
@@ -0,0 +1,402 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean         name="MBeanFactory"
+            className="org.apache.catalina.mbeans.MBeanFactory"
+          description="Factory for MBeans and corresponding components"
+               domain="Catalina">
+
+    <!-- IMPLEMENTATION NOTE - all of the createXxxxx methods create a new  -->
+    <!-- component and attach it to Catalina's component tree.  The return  -->
+    <!-- value is the object name of the corresponding MBean for the new    -->
+    <!-- component.                                                         -->
+
+    <operation   name="createAccessLoggerValve"
+          description="Create a new AccessLoggerValve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createAjpConnector"
+          description="Create a new AjpConnector"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="address"
+          description="The IP address on which to bind"
+                 type="java.lang.String"/>
+      <parameter name="port"
+          description="TCP port number to listen on"
+                 type="int"/>
+    </operation>
+
+    <operation   name="createDataSourceRealm"
+          description="Create a new DataSource Realm"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="dataSourceName"
+          description="The JNDI named JDBC DataSource"
+                 type="java.lang.String"/>
+      <parameter name="roleNameCol"
+          description="The column in the user role table that names a role"
+                 type="java.lang.String"/>
+      <parameter name="userCredCol"
+          description="The column in the user table that holds the user's
+                        credentials"
+                 type="java.lang.String"/>
+      <parameter name="userNameCol"
+          description="The column in the user table that holds the user's
+                        username"
+                 type="java.lang.String"/>
+      <parameter name="userRoleTable"
+          description="The table that holds the relation between user's and
+                        roles"
+                 type="java.lang.String"/>
+      <parameter name="userTable"
+          description="The table that holds user data"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createFileLogger"
+          description="Create a new FileLogger"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createHttpConnector"
+          description="Create a new HttpConnector"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+     <parameter name="address"
+          description="The IP address on which to bind"
+                 type="java.lang.String"/>
+      <parameter name="port"
+          description="TCP port number to listen on"
+                 type="int"/>
+    </operation>
+
+    <operation   name="createHttpsConnector"
+          description="Create a new HttpsConnector"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="address"
+          description="The IP address on which to bind"
+                 type="java.lang.String"/>
+      <parameter name="port"
+          description="TCP port number to listen on"
+                 type="int"/>
+    </operation>
+
+    <operation   name="createJDBCRealm"
+          description="Create a new JDBC Realm"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createJNDIRealm"
+          description="Create a new JNDI Realm"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createMemoryRealm"
+          description="Create a new Memory Realm"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createRemoteAddrValve"
+          description="Create a new Remote Address Filter Valve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createRemoteHostValve"
+          description="Create a new Remote Host Filter Valve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createRequestDumperValve"
+          description="Create a new Request Dumper Valve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createSingleSignOn"
+          description="Create a new Single Sign On Valve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardContext"
+          description="Create a new StandardContext"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="path"
+          description="The context path for this Context"
+                 type="java.lang.String"/>
+      <parameter name="docBase"
+          description="Document base directory (or WAR) for ths Context"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardEngine"
+          description="Create a new StandardEngine"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="name"
+          description="Unique name of this Engine"
+                 type="java.lang.String"/>
+      <parameter name="defaultHost"
+          description="Default host name for this Engine"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardEngineService"
+          description="Create a new StandardEngine and StandardService"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent Service"
+                 type="java.lang.String"/>
+      <parameter name="engineName"
+          description="Unique name of this Engine"
+                 type="java.lang.String"/>
+      <parameter name="defaultHost"
+          description="Default host name for this Engine"
+                 type="java.lang.String"/>
+      <parameter name="serviceName"
+          description="Unique name of this Service"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardHost"
+          description="Create a new StandardHost"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="name"
+          description="Unique name of this Host"
+                 type="java.lang.String"/>
+      <parameter name="autoDeploy"
+          description="The auto deploy flag for this Host"
+               type="boolean"/>           
+      <parameter name="deployOnStartup"
+          description="The deploy on startup flag for this Host"
+               type="boolean"/>
+      <parameter name="deployXML"
+          description="deploy Context XML config files property"
+               type="boolean"/> 
+      <parameter name="unpackWARs"
+          description="Unpack WARs property"
+               type="boolean"/>
+      <parameter name="xmlNamespaceAware"
+          description="Attribute value used to turn on/off XML namespace awareness"
+               type="boolean"/>
+      <parameter name="xmlValidation"
+               description="Attribute value used to turn on/off XML validation"
+               type="boolean"/>
+    </operation>
+
+
+
+
+    <operation   name="createStandardManager"
+          description="Create a new StandardManager"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardService"
+          description="Create a new StandardService"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="name"
+          description="Unique name of this Service"
+                 type="java.lang.String"/>
+      <parameter name="domain"
+          description="The domain of this Service"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createSystemErrLogger"
+          description="Create a new System Error Logger"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createSystemOutLogger"
+          description="Create a new System Output Logger"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createUserDatabaseRealm"
+          description="Create a new UserDatabase Realm"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="resourceName"
+          description="Global JNDI resource name of our UserDatabase instance"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createWebappLoader"
+          description="Create a new Web Application Loader"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <!-- IMPLEMENTATION NOTE - all of the removeXxxxx methods cause the     -->
+    <!-- corresponding Catalina component (and any related child            -->
+    <!-- components to be stopped (if necessary) and removed, and the       -->
+    <!-- corresponding MBeans to be destroyed.                              -->
+
+    <operation   name="removeConnector"
+          description="Remove an existing Connector"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeContext"
+          description="Remove an existing Context"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeHost"
+          description="Remove an existing Host"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeLoader"
+          description="Remove an existing Loader"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeLogger"
+          description="Remove an existing Logger"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeManager"
+          description="Remove an existing Manager"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRealm"
+          description="Remove an existing Realm"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeService"
+          description="Remove an existing Service"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeValve"
+          description="Remove an existing Valve"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/realm/Constants.java b/container/catalina/src/share/org/apache/catalina/realm/Constants.java
new file mode 100644
index 0000000..90ff136
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/Constants.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+/**
+ * Manifest constants for this Java package.
+ *
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.catalina.realm";
+    
+        // Authentication methods for login configuration
+    public static final String FORM_METHOD = "FORM";
+
+    // Form based authentication constants
+    public static final String FORM_ACTION = "/j_security_check";
+
+    // User data constraints for transport guarantee
+    public static final String NONE_TRANSPORT = "NONE";
+    public static final String INTEGRAL_TRANSPORT = "INTEGRAL";
+    public static final String CONFIDENTIAL_TRANSPORT = "CONFIDENTIAL";
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/DataSourceRealm.java b/container/catalina/src/share/org/apache/catalina/realm/DataSourceRealm.java
new file mode 100644
index 0000000..00715cf
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/DataSourceRealm.java
@@ -0,0 +1,661 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import java.security.Principal;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+
+import javax.naming.Context;
+import javax.sql.DataSource;
+
+import org.apache.naming.ContextBindings;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.util.StringManager;
+
+/**
+*
+* Implmentation of <b>Realm</b> that works with any JDBC JNDI DataSource.
+* See the JDBCRealm.howto for more details on how to set up the database and
+* for configuration options.
+*
+* @author Glenn L. Nielsen
+* @author Craig R. McClanahan
+* @author Carson McDonald
+* @author Ignacio Ortega
+* @version $Revision$
+*/
+
+public class DataSourceRealm
+    extends RealmBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The generated string for the roles PreparedStatement
+     */
+    private StringBuffer preparedRoles = null;
+
+
+    /**
+     * The generated string for the credentials PreparedStatement
+     */
+    private StringBuffer preparedCredentials = null;
+
+
+    /**
+     * The name of the JNDI JDBC DataSource
+     */
+    protected String dataSourceName = null;
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.realm.DataSourceRealm/1.0";
+
+
+    /**
+     * Context local datasource.
+     */
+    protected boolean localDataSource = false;
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String name = "DataSourceRealm";
+
+
+    /**
+     * The column in the user role table that names a role
+     */
+    protected String roleNameCol = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The column in the user table that holds the user's credintials
+     */
+    protected String userCredCol = null;
+
+
+    /**
+     * The column in the user table that holds the user's name
+     */
+    protected String userNameCol = null;
+
+
+    /**
+     * The table that holds the relation between user's and roles
+     */
+    protected String userRoleTable = null;
+
+
+    /**
+     * The table that holds user data.
+     */
+    protected String userTable = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the name of the JNDI JDBC DataSource.
+     *
+     */
+    public String getDataSourceName() {
+        return dataSourceName;
+    }
+
+    /**
+     * Set the name of the JNDI JDBC DataSource.
+     *
+     * @param dataSourceName the name of the JNDI JDBC DataSource
+     */
+    public void setDataSourceName( String dataSourceName) {
+      this.dataSourceName = dataSourceName;
+    }
+
+    /**
+     * Return if the datasource will be looked up in the webapp JNDI Context.
+     */
+    public boolean getLocalDataSource() {
+        return localDataSource;
+    }
+
+    /**
+     * Set to true to cause the datasource to be looked up in the webapp JNDI
+     * Context.
+     *
+     * @param localDataSource the new flag value
+     */
+    public void setLocalDataSource(boolean localDataSource) {
+      this.localDataSource = localDataSource;
+    }
+
+    /**
+     * Return the column in the user role table that names a role.
+     *
+     */
+    public String getRoleNameCol() {
+        return roleNameCol;
+    }
+
+    /**
+     * Set the column in the user role table that names a role.
+     *
+     * @param roleNameCol The column name
+     */
+    public void setRoleNameCol( String roleNameCol ) {
+        this.roleNameCol = roleNameCol;
+    }
+
+    /**
+     * Return the column in the user table that holds the user's credentials.
+     *
+     */
+    public String getUserCredCol() {
+        return userCredCol;
+    }
+
+    /**
+     * Set the column in the user table that holds the user's credentials.
+     *
+     * @param userCredCol The column name
+     */
+    public void setUserCredCol( String userCredCol ) {
+       this.userCredCol = userCredCol;
+    }
+
+    /**
+     * Return the column in the user table that holds the user's name.
+     *
+     */
+    public String getUserNameCol() {
+        return userNameCol;
+    }
+
+    /**
+     * Set the column in the user table that holds the user's name.
+     *
+     * @param userNameCol The column name
+     */
+    public void setUserNameCol( String userNameCol ) {
+       this.userNameCol = userNameCol;
+    }
+
+    /**
+     * Return the table that holds the relation between user's and roles.
+     *
+     */
+    public String getUserRoleTable() {
+        return userRoleTable;
+    }
+
+    /**
+     * Set the table that holds the relation between user's and roles.
+     *
+     * @param userRoleTable The table name
+     */
+    public void setUserRoleTable( String userRoleTable ) {
+        this.userRoleTable = userRoleTable;
+    }
+
+    /**
+     * Return the table that holds user data..
+     *
+     */
+    public String getUserTable() {
+        return userTable;
+    }
+
+    /**
+     * Set the table that holds user data.
+     *
+     * @param userTable The table name
+     */
+    public void setUserTable( String userTable ) {
+      this.userTable = userTable;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * If there are any errors with the JDBC connection, executing
+     * the query or anything we return null (don't authenticate). This
+     * event is also logged, and the connection will be closed so that
+     * a subsequent request will automatically re-open it.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, String credentials) {
+    	
+    	// No user - can't possibly authenticate, don't bother the database then
+    	if (username == null) {
+    		return null;
+    	}
+        
+    	Connection dbConnection = null;
+
+        try {
+
+            // Ensure that we have an open database connection
+            dbConnection = open();
+            if (dbConnection == null) {
+                // If the db connection open fails, return "not authenticated"
+                return null;
+            }
+            
+            // Acquire a Principal object for this user
+            return authenticate(dbConnection, username, credentials);
+            
+        } catch (SQLException e) {
+            // Log the problem for posterity
+            containerLog.error(sm.getString("dataSourceRealm.exception"), e);
+
+            // Return "not authenticated" for this request
+            return (null);
+
+        } finally {
+        	close(dbConnection);
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param dbConnection The database connection to be used
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    protected Principal authenticate(Connection dbConnection,
+                                               String username,
+                                               String credentials) throws SQLException{
+
+        String dbCredentials = getPassword(dbConnection, username);
+
+        // Validate the user's credentials
+        boolean validated = false;
+        if (hasMessageDigest()) {
+            // Hex hashes should be compared case-insensitive
+            validated = (digest(credentials).equalsIgnoreCase(dbCredentials));
+        } else
+            validated = (digest(credentials).equals(dbCredentials));
+
+        if (validated) {
+            if (containerLog.isTraceEnabled())
+                containerLog.trace(
+                    sm.getString("dataSourceRealm.authenticateSuccess",
+                                 username));
+        } else {
+            if (containerLog.isTraceEnabled())
+                containerLog.trace(
+                    sm.getString("dataSourceRealm.authenticateFailure",
+                                 username));
+            return (null);
+        }
+
+        ArrayList list = getRoles(dbConnection, username);
+
+        // Create and return a suitable Principal for this user
+        return (new GenericPrincipal(this, username, credentials, list));
+
+    }
+
+
+    /**
+     * Close the specified database connection.
+     *
+     * @param dbConnection The connection to be closed
+     */
+    protected void close(Connection dbConnection) {
+
+        // Do nothing if the database connection is already closed
+        if (dbConnection == null)
+            return;
+
+        // Commit if not auto committed
+        try {
+            if (!dbConnection.getAutoCommit()) {
+                dbConnection.commit();
+            }            
+        } catch (SQLException e) {
+            containerLog.error("Exception committing connection before closing:", e);
+        }
+
+        // Close this database connection, and log any errors
+        try {
+            dbConnection.close();
+        } catch (SQLException e) {
+            containerLog.error(sm.getString("dataSourceRealm.close"), e); // Just log it here
+        }
+
+    }
+
+    /**
+     * Open the specified database connection.
+     *
+     * @return Connection to the database
+     */
+    protected Connection open() {
+
+        try {
+            Context context = null;
+            if (localDataSource) {
+                context = ContextBindings.getClassLoader();
+                context = (Context) context.lookup("comp/env");
+            } else {
+                StandardServer server = 
+                    (StandardServer) ServerFactory.getServer();
+                context = server.getGlobalNamingContext();
+            }
+            DataSource dataSource = (DataSource)context.lookup(dataSourceName);
+	    return dataSource.getConnection();
+        } catch (Exception e) {
+            // Log the problem for posterity
+            containerLog.error(sm.getString("dataSourceRealm.exception"), e);
+        }  
+        return null;
+    }
+
+    /**
+     * Return a short name for this Realm implementation.
+     */
+    protected String getName() {
+
+        return (name);
+
+    }
+
+    /**
+     * Return the password associated with the given principal's user name.
+     */
+    protected String getPassword(String username) {
+
+        Connection dbConnection = null;
+
+        // Ensure that we have an open database connection
+        dbConnection = open();
+        if (dbConnection == null) {
+            return null;
+        }
+
+        try {
+        	return getPassword(dbConnection, username);        	
+        } finally {
+            close(dbConnection);
+        }
+    }
+    
+    /**
+     * Return the password associated with the given principal's user name.
+     * @param dbConnection The database connection to be used
+     * @param username Username for which password should be retrieved
+     */
+    protected String getPassword(Connection dbConnection, 
+								 String username) {
+
+        ResultSet rs = null;
+        PreparedStatement stmt = null;
+        String dbCredentials = null;
+
+        try {
+            stmt = credentials(dbConnection, username);
+            rs = stmt.executeQuery();
+            if (rs.next()) {
+                dbCredentials = rs.getString(1);
+            }
+
+            return (dbCredentials != null) ? dbCredentials.trim() : null;
+            
+        } catch(SQLException e) {
+            containerLog.error(
+                    sm.getString("dataSourceRealm.getPassword.exception",
+                                 username));
+        } finally {
+        	try {
+	            if (rs != null) {
+	                rs.close();
+	            }
+	            if (stmt != null) {
+	                stmt.close();
+	            }
+        	} catch (SQLException e) {
+                    containerLog.error(
+                        sm.getString("dataSourceRealm.getPassword.exception",
+        		             username));
+        		
+        	}
+        }
+        
+        return null;
+    }
+
+
+    /**
+     * Return the Principal associated with the given user name.
+     */
+    protected Principal getPrincipal(String username) {
+    	Connection dbConnection = open();
+        if (dbConnection == null) {
+            return new GenericPrincipal(this,username, null, null);
+        }
+        try {
+        	return (new GenericPrincipal(this,
+        			username,
+					getPassword(dbConnection, username),
+					getRoles(dbConnection, username)));
+        } finally {
+        	close(dbConnection);
+        }
+
+    }
+
+    /**
+     * Return the roles associated with the given user name.
+     * @param username Username for which roles should be retrieved
+     */
+    protected ArrayList getRoles(String username) {
+
+        Connection dbConnection = null;
+
+        // Ensure that we have an open database connection
+        dbConnection = open();
+        if (dbConnection == null) {
+            return null;
+        }
+
+        try {
+            return getRoles(dbConnection, username);
+        } finally {
+        	close(dbConnection);
+        }
+    }
+    
+    /**
+     * Return the roles associated with the given user name
+     * @param dbConnection The database connection to be used
+     * @param username Username for which roles should be retrieved
+     */
+    protected ArrayList getRoles(Connection dbConnection,
+                                     String username) {
+    	
+        ResultSet rs = null;
+        PreparedStatement stmt = null;
+        ArrayList list = null;
+    	
+        try {
+    		stmt = roles(dbConnection, username);
+    		rs = stmt.executeQuery();
+    		list = new ArrayList();
+    		
+    		while (rs.next()) {
+    			String role = rs.getString(1);
+    			if (role != null) {
+    				list.add(role.trim());
+    			}
+    		}
+    		return list;
+    	} catch(SQLException e) {
+            containerLog.error(
+                sm.getString("dataSourceRealm.getRoles.exception", username));
+        }
+    	finally {
+        	try {
+	            if (rs != null) {
+	                rs.close();
+	            }
+	            if (stmt != null) {
+	                stmt.close();
+	            }
+        	} catch (SQLException e) {
+                    containerLog.error(
+                        sm.getString("dataSourceRealm.getRoles.exception",
+                                     username));
+        	}
+        }
+    	
+    	return null;
+    }
+
+    /**
+     * Return a PreparedStatement configured to perform the SELECT required
+     * to retrieve user credentials for the specified username.
+     *
+     * @param dbConnection The database connection to be used
+     * @param username Username for which credentials should be retrieved
+     *
+     * @exception SQLException if a database error occurs
+     */
+    private PreparedStatement credentials(Connection dbConnection,
+                                            String username)
+        throws SQLException {
+
+        PreparedStatement credentials =
+            dbConnection.prepareStatement(preparedCredentials.toString());
+
+        credentials.setString(1, username);
+        return (credentials);
+
+    }
+    
+    /**
+     * Return a PreparedStatement configured to perform the SELECT required
+     * to retrieve user roles for the specified username.
+     *
+     * @param dbConnection The database connection to be used
+     * @param username Username for which roles should be retrieved
+     *
+     * @exception SQLException if a database error occurs
+     */
+    private PreparedStatement roles(Connection dbConnection, String username)
+        throws SQLException {
+
+        PreparedStatement roles = 
+            dbConnection.prepareStatement(preparedRoles.toString());
+
+        roles.setString(1, username);
+        return (roles);
+
+    }
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     *
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public void start() throws LifecycleException {
+
+        // Create the roles PreparedStatement string
+        preparedRoles = new StringBuffer("SELECT ");
+        preparedRoles.append(roleNameCol);
+        preparedRoles.append(" FROM ");
+        preparedRoles.append(userRoleTable);
+        preparedRoles.append(" WHERE ");
+        preparedRoles.append(userNameCol);
+        preparedRoles.append(" = ?");
+
+        // Create the credentials PreparedStatement string
+        preparedCredentials = new StringBuffer("SELECT ");
+        preparedCredentials.append(userCredCol);
+        preparedCredentials.append(" FROM ");
+        preparedCredentials.append(userTable);
+        preparedCredentials.append(" WHERE ");
+        preparedCredentials.append(userNameCol);
+        preparedCredentials.append(" = ?");
+
+        // Perform normal superclass initialization
+        super.start();
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Perform normal superclass finalization
+        super.stop();
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/GenericPrincipal.java b/container/catalina/src/share/org/apache/catalina/realm/GenericPrincipal.java
new file mode 100644
index 0000000..294943a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/GenericPrincipal.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.catalina.Realm;
+
+
+/**
+ * Generic implementation of <strong>java.security.Principal</strong> that
+ * is available for use by <code>Realm</code> implementations.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class GenericPrincipal implements Principal {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new Principal, associated with the specified Realm, for the
+     * specified username and password.
+     *
+     * @param realm The Realm that owns this Principal
+     * @param name The username of the user represented by this Principal
+     * @param password Credentials used to authenticate this user
+     */
+    public GenericPrincipal(Realm realm, String name, String password) {
+
+        this(realm, name, password, null);
+
+    }
+
+
+    /**
+     * Construct a new Principal, associated with the specified Realm, for the
+     * specified username and password, with the specified role names
+     * (as Strings).
+     *
+     * @param realm The Realm that owns this principal
+     * @param name The username of the user represented by this Principal
+     * @param password Credentials used to authenticate this user
+     * @param roles List of roles (must be Strings) possessed by this user
+     */
+    public GenericPrincipal(Realm realm, String name, String password,
+                            List roles) {
+        this(realm, name, password, roles, null);
+    }
+
+    /**
+     * Construct a new Principal, associated with the specified Realm, for the
+     * specified username and password, with the specified role names
+     * (as Strings).
+     *
+     * @param realm The Realm that owns this principal
+     * @param name The username of the user represented by this Principal
+     * @param password Credentials used to authenticate this user
+     * @param roles List of roles (must be Strings) possessed by this user
+     * @param userPrincipal - the principal to be returned from the request 
+     *        getUserPrincipal call if not null; if null, this will be returned
+     */
+    public GenericPrincipal(Realm realm, String name, String password,
+                            List roles, Principal userPrincipal) {
+
+        super();
+        this.realm = realm;
+        this.name = name;
+        this.password = password;
+        this.userPrincipal = userPrincipal;
+        if (roles != null) {
+            this.roles = new String[roles.size()];
+            this.roles = (String[]) roles.toArray(this.roles);
+            if (this.roles.length > 0)
+                Arrays.sort(this.roles);
+        }
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The username of the user represented by this Principal.
+     */
+    protected String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+
+    /**
+     * The authentication credentials for the user represented by
+     * this Principal.
+     */
+    protected String password = null;
+
+    public String getPassword() {
+        return (this.password);
+    }
+
+
+    /**
+     * The Realm with which this Principal is associated.
+     */
+    protected Realm realm = null;
+
+    public Realm getRealm() {
+        return (this.realm);
+    }
+
+    void setRealm( Realm realm ) {
+        this.realm=realm;
+    }
+
+
+    /**
+     * The set of roles associated with this user.
+     */
+    protected String roles[] = new String[0];
+
+    public String[] getRoles() {
+        return (this.roles);
+    }
+
+
+    /**
+     * The authenticated Principal to be exposed to applications.
+     */
+    protected Principal userPrincipal = null;
+
+    public Principal getUserPrincipal() {
+        if (userPrincipal != null) {
+            return userPrincipal;
+        } else {
+            return this;
+        }
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Does the user represented by this Principal possess the specified role?
+     *
+     * @param role Role to be tested
+     */
+    public boolean hasRole(String role) {
+
+        if("*".equals(role)) // Special 2.4 role meaning everyone
+            return true;
+        if (role == null)
+            return (false);
+        return (Arrays.binarySearch(roles, role) >= 0);
+
+    }
+
+
+    /**
+     * Return a String representation of this object, which exposes only
+     * information that should be public.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("GenericPrincipal[");
+        sb.append(this.name);
+        sb.append("(");
+        for( int i=0;i<roles.length; i++ ) {
+            sb.append( roles[i]).append(",");
+        }
+        sb.append(")]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/JAASCallbackHandler.java b/container/catalina/src/share/org/apache/catalina/realm/JAASCallbackHandler.java
new file mode 100644
index 0000000..1bc1615
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/JAASCallbackHandler.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import java.io.IOException;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * <p>Implementation of the JAAS <code>CallbackHandler</code> interface,
+ * used to negotiate delivery of the username and credentials that were
+ * specified to our constructor.  No interaction with the user is required
+ * (or possible).</p>
+ *
+ * <p>This <code>CallbackHandler</code> will pre-digest the supplied
+ * password, if required by the <code>&lt;Realm&gt;</code> element in 
+ * <code>server.xml</code>.</p>
+ * <p>At present, <code>JAASCallbackHandler</code> knows how to handle callbacks of
+ * type <code>javax.security.auth.callback.NameCallback</code> and
+ * <code>javax.security.auth.callback.PasswordCallback</code>.</p>
+ *
+ * @author Craig R. McClanahan
+ * @author Andrew R. Jaquith
+ * @version $Revision$ $Date$
+ */
+
+public class JAASCallbackHandler implements CallbackHandler {
+    private static Log log = LogFactory.getLog(JAASCallbackHandler.class);
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct a callback handler configured with the specified values.
+     * Note that if the <code>JAASRealm</code> instance specifies digested passwords,
+     * the <code>password</code> parameter will be pre-digested here.
+     *
+     * @param realm Our associated JAASRealm instance
+     * @param username Username to be authenticated with
+     * @param password Password to be authenticated with
+     */
+    public JAASCallbackHandler(JAASRealm realm, String username,
+                               String password) {
+
+        super();
+        this.realm = realm;
+        this.username = username;
+
+        if (realm.hasMessageDigest()) {
+            this.password = realm.digest(password);
+        }
+        else {
+            this.password = password;
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    /**
+     * The password to be authenticated with.
+     */
+    protected String password = null;
+
+
+    /**
+     * The associated <code>JAASRealm</code> instance.
+     */
+    protected JAASRealm realm = null;
+
+
+    /**
+     * The username to be authenticated with.
+     */
+    protected String username = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieve the information requested in the provided <code>Callbacks</code>.
+     * This implementation only recognizes <code>NameCallback</code> and
+     * <code>PasswordCallback</code> instances.
+     *
+     * @param callbacks The set of <code>Callback</code>s to be processed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception UnsupportedCallbackException if the login method requests
+     *  an unsupported callback type
+     */
+    public void handle(Callback callbacks[])
+        throws IOException, UnsupportedCallbackException {
+
+        for (int i = 0; i < callbacks.length; i++) {
+
+            if (callbacks[i] instanceof NameCallback) {
+                if (realm.getContainer().getLogger().isTraceEnabled())
+                    realm.getContainer().getLogger().trace(sm.getString("jaasCallback.username", username));
+                ((NameCallback) callbacks[i]).setName(username);
+            } else if (callbacks[i] instanceof PasswordCallback) {
+                final char[] passwordcontents;
+                if (password != null) {
+                    passwordcontents = password.toCharArray();
+                } else {
+                    passwordcontents = new char[0];
+                }
+                ((PasswordCallback) callbacks[i]).setPassword
+                    (passwordcontents);
+            } else {
+                throw new UnsupportedCallbackException(callbacks[i]);
+            }
+        }
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/JAASMemoryLoginModule.java b/container/catalina/src/share/org/apache/catalina/realm/JAASMemoryLoginModule.java
new file mode 100644
index 0000000..12b2ae5
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/JAASMemoryLoginModule.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Realm;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.digester.Digester;
+
+
+/**
+ * <p>Implementation of the JAAS <strong>LoginModule</strong> interface,
+ * primarily for use in testing <code>JAASRealm</code>.  It utilizes an
+ * XML-format data file of username/password/role information identical to
+ * that supported by <code>org.apache.catalina.realm.MemoryRealm</code>
+ * (except that digested passwords are not supported).</p>
+ *
+ * <p>This class recognizes the following string-valued options, which are
+ * specified in the configuration file (and passed to our constructor in
+ * the <code>options</code> argument:</p>
+ * <ul>
+ * <li><strong>debug</strong> - Set to "true" to get debugging messages
+ *     generated to System.out.  The default value is <code>false</code>.</li>
+ * <li><strong>pathname</strong> - Relative (to the pathname specified by the
+ *     "catalina.base" system property) or absolute pahtname to the
+ *     XML file containing our user information, in the format supported by
+ *     {@link MemoryRealm}.  The default value matches the MemoryRealm
+ *     default.</li>
+ * </ul>
+ *
+ * <p><strong>IMPLEMENTATION NOTE</strong> - This class implements
+ * <code>Realm</code> only to satisfy the calling requirements of the
+ * <code>GenericPrincipal</code> constructor.  It does not actually perform
+ * the functionality required of a <code>Realm</code> implementation.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule, Realm {
+    // We need to extend MemoryRealm to avoid class cast
+
+    private static Log log = LogFactory.getLog(JAASMemoryLoginModule.class);
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The callback handler responsible for answering our requests.
+     */
+    protected CallbackHandler callbackHandler = null;
+
+
+    /**
+     * Has our own <code>commit()</code> returned successfully?
+     */
+    protected boolean committed = false;
+
+
+    /**
+     * The configuration information for this <code>LoginModule</code>.
+     */
+    protected Map options = null;
+
+
+    /**
+     * The absolute or relative pathname to the XML configuration file.
+     */
+    protected String pathname = "conf/tomcat-users.xml";
+
+
+    /**
+     * The <code>Principal</code> identified by our validation, or
+     * <code>null</code> if validation falied.
+     */
+    protected Principal principal = null;
+
+
+    /**
+     * The set of <code>Principals</code> loaded from our configuration file.
+     */
+    protected HashMap principals = new HashMap();
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    /**
+     * The state information that is shared with other configured
+     * <code>LoginModule</code> instances.
+     */
+    protected Map sharedState = null;
+
+
+    /**
+     * The subject for which we are performing authentication.
+     */
+    protected Subject subject = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    public JAASMemoryLoginModule() {
+        log.debug("MEMORY LOGIN MODULE");
+    }
+
+    /**
+     * Phase 2 of authenticating a <code>Subject</code> when Phase 1
+     * fails.  This method is called if the <code>LoginContext</code>
+     * failed somewhere in the overall authentication chain.
+     *
+     * @return <code>true</code> if this method succeeded, or
+     *  <code>false</code> if this <code>LoginModule</code> should be
+     *  ignored
+     *
+     * @exception LoginException if the abort fails
+     */
+    public boolean abort() throws LoginException {
+
+        // If our authentication was not successful, just return false
+        if (principal == null)
+            return (false);
+
+        // Clean up if overall authentication failed
+        if (committed)
+            logout();
+        else {
+            committed = false;
+            principal = null;
+        }
+        log.debug("Abort");
+        return (true);
+
+    }
+
+
+    /**
+     * Phase 2 of authenticating a <code>Subject</code> when Phase 1
+     * was successful.  This method is called if the <code>LoginContext</code>
+     * succeeded in the overall authentication chain.
+     *
+     * @return <code>true</code> if the authentication succeeded, or
+     *  <code>false</code> if this <code>LoginModule</code> should be
+     *  ignored
+     *
+     * @exception LoginException if the commit fails
+     */
+    public boolean commit() throws LoginException {
+        log.debug("commit " + principal);
+
+        // If authentication was not successful, just return false
+        if (principal == null)
+            return (false);
+
+        // Add our Principal to the Subject if needed
+        if (!subject.getPrincipals().contains(principal))
+            subject.getPrincipals().add(principal);
+
+        committed = true;
+        return (true);
+
+    }
+
+    
+    /**
+     * Return the SecurityConstraints configured to guard the request URI for
+     * this request, or <code>null</code> if there is no such constraint.
+     *
+     * @param request Request we are processing
+     * @param context Context the Request is mapped to
+     */
+    public SecurityConstraint [] findSecurityConstraints(Request request,
+                                                     Context context) {
+        ArrayList results = null;
+        // Are there any defined security constraints?
+        SecurityConstraint constraints[] = context.findConstraints();
+        if ((constraints == null) || (constraints.length == 0)) {
+            if (context.getLogger().isDebugEnabled())
+                context.getLogger().debug("  No applicable constraints defined");
+            return (null);
+        }
+
+        // Check each defined security constraint
+        String uri = request.getDecodedRequestURI();
+        String contextPath = request.getContextPath();
+        if (contextPath.length() > 0)
+            uri = uri.substring(contextPath.length());
+        uri = RequestUtil.URLDecode(uri); // Before checking constraints
+        String method = request.getMethod();
+        for (int i = 0; i < constraints.length; i++) {
+            if (context.getLogger().isDebugEnabled())
+                context.getLogger().debug("  Checking constraint '" + constraints[i] +
+                    "' against " + method + " " + uri + " --> " +
+                    constraints[i].included(uri, method));
+            if (constraints[i].included(uri, method)) {
+                if(results == null) {
+                    results = new ArrayList();
+                }
+                results.add(constraints[i]);
+            }
+        }
+
+        // No applicable security constraint was found
+        if (context.getLogger().isDebugEnabled())
+            context.getLogger().debug("  No applicable constraint located");
+        if(results == null)
+            return null;
+        SecurityConstraint [] array = new SecurityConstraint[results.size()];
+        System.arraycopy(results.toArray(), 0, array, 0, array.length);
+        return array;
+    }
+    
+    
+    /**
+     * Initialize this <code>LoginModule</code> with the specified
+     * configuration information.
+     *
+     * @param subject The <code>Subject</code> to be authenticated
+     * @param callbackHandler A <code>CallbackHandler</code> for communicating
+     *  with the end user as necessary
+     * @param sharedState State information shared with other
+     *  <code>LoginModule</code> instances
+     * @param options Configuration information for this specific
+     *  <code>LoginModule</code> instance
+     */
+    public void initialize(Subject subject, CallbackHandler callbackHandler,
+                           Map sharedState, Map options) {
+        log.debug("Init");
+
+        // Save configuration values
+        this.subject = subject;
+        this.callbackHandler = callbackHandler;
+        this.sharedState = sharedState;
+        this.options = options;
+
+        // Perform instance-specific initialization
+        if (options.get("pathname") != null)
+            this.pathname = (String) options.get("pathname");
+
+        // Load our defined Principals
+        load();
+
+    }
+
+
+    /**
+     * Phase 1 of authenticating a <code>Subject</code>.
+     *
+     * @return <code>true</code> if the authentication succeeded, or
+     *  <code>false</code> if this <code>LoginModule</code> should be
+     *  ignored
+     *
+     * @exception LoginException if the authentication fails
+     */
+    public boolean login() throws LoginException {
+
+        // Set up our CallbackHandler requests
+        if (callbackHandler == null)
+            throw new LoginException("No CallbackHandler specified");
+        Callback callbacks[] = new Callback[2];
+        callbacks[0] = new NameCallback("Username: ");
+        callbacks[1] = new PasswordCallback("Password: ", false);
+
+        // Interact with the user to retrieve the username and password
+        String username = null;
+        String password = null;
+        try {
+            callbackHandler.handle(callbacks);
+            username = ((NameCallback) callbacks[0]).getName();
+            password =
+                new String(((PasswordCallback) callbacks[1]).getPassword());
+        } catch (IOException e) {
+            throw new LoginException(e.toString());
+        } catch (UnsupportedCallbackException e) {
+            throw new LoginException(e.toString());
+        }
+
+        // Validate the username and password we have received
+        principal = super.authenticate(username, password);
+
+        log.debug("login " + username + " " + principal);
+
+        // Report results based on success or failure
+        if (principal != null) {
+            return (true);
+        } else {
+            throw new
+                FailedLoginException("Username or password is incorrect");
+        }
+
+    }
+
+
+    /**
+     * Log out this user.
+     *
+     * @return <code>true</code> in all cases because thie
+     *  <code>LoginModule</code> should not be ignored
+     *
+     * @exception LoginException if logging out failed
+     */
+    public boolean logout() throws LoginException {
+
+        subject.getPrincipals().remove(principal);
+        committed = false;
+        principal = null;
+        return (true);
+
+    }
+
+
+    // ---------------------------------------------------------- Realm Methods
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Load the contents of our configuration file.
+     */
+    protected void load() {
+
+        // Validate the existence of our configuration file
+        File file = new File(pathname);
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"), pathname);
+        if (!file.exists() || !file.canRead()) {
+            log.warn("Cannot load configuration file " + file.getAbsolutePath());
+            return;
+        }
+
+        // Load the contents of our configuration file
+        Digester digester = new Digester();
+        digester.setValidating(false);
+        digester.addRuleSet(new MemoryRuleSet());
+        try {
+            digester.push(this);
+            digester.parse(file);
+        } catch (Exception e) {
+            log.warn("Error processing configuration file " +
+                file.getAbsolutePath(), e);
+            return;
+        } finally {
+            digester.reset();
+        }
+
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/JAASRealm.java b/container/catalina/src/share/org/apache/catalina/realm/JAASRealm.java
new file mode 100644
index 0000000..eb504b6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/JAASRealm.java
@@ -0,0 +1,571 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.AccountExpiredException;
+import javax.security.auth.login.CredentialExpiredException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * <p>Implmentation of <b>Realm</b> that authenticates users via the <em>Java
+ * Authentication and Authorization Service</em> (JAAS).  JAAS support requires
+ * either JDK 1.4 (which includes it as part of the standard platform) or
+ * JDK 1.3 (with the plug-in <code>jaas.jar</code> file).</p>
+ *
+ * <p>The value configured for the <code>appName</code> property is passed to
+ * the <code>javax.security.auth.login.LoginContext</code> constructor, to
+ * specify the <em>application name</em> used to select the set of relevant
+ * <code>LoginModules</code> required.</p>
+ *
+ * <p>The JAAS Specification describes the result of a successful login as a
+ * <code>javax.security.auth.Subject</code> instance, which can contain zero
+ * or more <code>java.security.Principal</code> objects in the return value
+ * of the <code>Subject.getPrincipals()</code> method.  However, it provides
+ * no guidance on how to distinguish Principals that describe the individual
+ * user (and are thus appropriate to return as the value of
+ * request.getUserPrincipal() in a web application) from the Principal(s)
+ * that describe the authorized roles for this user.  To maintain as much
+ * independence as possible from the underlying <code>LoginMethod</code>
+ * implementation executed by JAAS, the following policy is implemented by
+ * this Realm:</p>
+ * <ul>
+ * <li>The JAAS <code>LoginModule</code> is assumed to return a
+ *     <code>Subject</code> with at least one <code>Principal</code> instance
+ *     representing the user himself or herself, and zero or more separate
+ *     <code>Principals</code> representing the security roles authorized
+ *     for this user.</li>
+ * <li>On the <code>Principal</code> representing the user, the Principal
+ *     name is an appropriate value to return via the Servlet API method
+ *     <code>HttpServletRequest.getRemoteUser()</code>.</li>
+ * <li>On the <code>Principals</code> representing the security roles, the
+ *     name is the name of the authorized security role.</li>
+ * <li>This Realm will be configured with two lists of fully qualified Java
+ *     class names of classes that implement
+ *     <code>java.security.Principal</code> - one that identifies class(es)
+ *     representing a user, and one that identifies class(es) representing
+ *     a security role.</li>
+ * <li>As this Realm iterates over the <code>Principals</code> returned by
+ *     <code>Subject.getPrincipals()</code>, it will identify the first
+ *     <code>Principal</code> that matches the "user classes" list as the
+ *     <code>Principal</code> for this user.</li>
+ * <li>As this Realm iterates over the <code>Princpals</code> returned by
+ *     <code>Subject.getPrincipals()</code>, it will accumulate the set of
+ *     all <code>Principals</code> matching the "role classes" list as
+ *     identifying the security roles for this user.</li>
+ * <li>It is a configuration error for the JAAS login method to return a
+ *     validated <code>Subject</code> without a <code>Principal</code> that
+ *     matches the "user classes" list.</li>
+ * <li>By default, the enclosing Container's name serves as the
+ *     application name used to obtain the JAAS LoginContext ("Catalina" in
+ *     a default installation). Tomcat must be able to find an application
+ *     with this name in the JAAS configuration file. Here is a hypothetical
+ *     JAAS configuration file entry for a database-oriented login module that uses 
+ *     a Tomcat-managed JNDI database resource:
+ *     <blockquote><pre>Catalina {
+org.foobar.auth.DatabaseLoginModule REQUIRED
+    JNDI_RESOURCE=jdbc/AuthDB
+  USER_TABLE=users
+  USER_ID_COLUMN=id
+  USER_NAME_COLUMN=name
+  USER_CREDENTIAL_COLUMN=password
+  ROLE_TABLE=roles
+  ROLE_NAME_COLUMN=name
+  PRINCIPAL_FACTORY=org.foobar.auth.impl.SimplePrincipalFactory;
+};</pre></blockquote></li>
+ * <li>To set the JAAS configuration file
+ *     location, set the <code>CATALINA_OPTS</code> environment variable
+ *     similar to the following:
+<blockquote><code>CATALINA_OPTS="-Djava.security.auth.login.config=$CATALINA_HOME/conf/jaas.config"</code></blockquote>
+ * </li>
+ * <li>As part of the login process, JAASRealm registers its own <code>CallbackHandler</code>,
+ *     called (unsurprisingly) <code>JAASCallbackHandler</code>. This handler supplies the 
+ *     HTTP requests's username and credentials to the user-supplied <code>LoginModule</code></li>
+ * <li>As with other <code>Realm</code> implementations, digested passwords are supported if
+ *     the <code>&lt;Realm&gt;</code> element in <code>server.xml</code> contains a 
+ *     <code>digest</code> attribute; <code>JAASCallbackHandler</code> will digest the password
+ *     prior to passing it back to the <code>LoginModule</code></li>  
+* </ul>
+*
+* @author Craig R. McClanahan
+* @author Yoav Shapira
+ * @version $Revision$ $Date$
+ */
+
+public class JAASRealm
+    extends RealmBase
+ {
+    private static Log log = LogFactory.getLog(JAASRealm.class);
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The application name passed to the JAAS <code>LoginContext</code>,
+     * which uses it to select the set of relevant <code>LoginModule</code>s.
+     */
+    protected String appName = null;
+
+
+    /**
+     * Descriptive information about this <code>Realm</code> implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.realm.JAASRealm/1.0";
+
+
+    /**
+     * Descriptive information about this <code>Realm</code> implementation.
+     */
+    protected static final String name = "JAASRealm";
+
+
+    /**
+     * The list of role class names, split out for easy processing.
+     */
+    protected List roleClasses = new ArrayList();
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The set of user class names, split out for easy processing.
+     */
+    protected List userClasses = new ArrayList();
+
+
+    /**
+     * Whether to use context ClassLoader or default ClassLoader.
+     * True means use context ClassLoader, and True is the default
+     * value.
+     */
+     protected boolean useContextClassLoader = true;
+
+
+    // ------------------------------------------------------------- Properties
+
+    
+    /**
+     * setter for the <code>appName</code> member variable
+     * @deprecated JAAS should use the <code>Engine</code> (domain) name and webpp/host overrides
+     */
+    public void setAppName(String name) {
+        appName = name;
+    }
+    
+    /**
+     * getter for the <code>appName</code> member variable
+     */
+    public String getAppName() {
+        return appName;
+    }
+
+    /**
+     * Sets whether to use the context or default ClassLoader.
+     * True means use context ClassLoader.
+     *
+     * @param useContext True means use context ClassLoader
+     */
+    public void setUseContextClassLoader(boolean useContext) {
+      useContextClassLoader = useContext;
+      log.info("Setting useContextClassLoader = " + useContext);
+    }
+
+    /**
+     * Returns whether to use the context or default ClassLoader.
+     * True means to use the context ClassLoader.
+     *
+     * @return The value of useContextClassLoader
+     */
+    public boolean isUseContextClassLoader() {
+	return useContextClassLoader;
+    } 
+
+    public void setContainer(Container container) {
+        super.setContainer(container);
+
+        if( appName==null  ) {
+            String name=container.getName();
+            name = makeLegalForJAAS(name);
+
+            appName=name;
+
+            log.info("Set JAAS app name " + appName);
+        }
+    }
+
+    /**
+     * Comma-delimited list of <code>java.security.Principal</code> classes
+     * that represent security roles.
+     */
+    protected String roleClassNames = null;
+
+    public String getRoleClassNames() {
+        return (this.roleClassNames);
+    }
+
+     /**
+      * Sets the list of comma-delimited classes that represent 
+      * roles. The classes in the list must implement <code>java.security.Principal</code>.
+      * When this accessor is called (for example, by a <code>Digester</code>
+      * instance parsing the
+      * configuration file), it will parse the class names and store the resulting
+      * string(s) into the <code>ArrayList</code> field </code>roleClasses</code>.
+      */
+     public void setRoleClassNames(String roleClassNames) {
+         this.roleClassNames = roleClassNames;
+        roleClasses.clear();
+        String temp = this.roleClassNames;
+        if (temp == null) {
+            return;
+        }
+        while (true) {
+            int comma = temp.indexOf(',');
+            if (comma < 0) {
+                break;
+            }
+            roleClasses.add(temp.substring(0, comma).trim());
+            temp = temp.substring(comma + 1);
+        }
+        temp = temp.trim();
+        if (temp.length() > 0) {
+            roleClasses.add(temp);
+        }
+    }
+
+
+    /**
+     * Comma-delimited list of <code>java.security.Principal</code> classes
+     * that represent individual users.
+     */
+    protected String userClassNames = null;
+
+    public String getUserClassNames() {
+        return (this.userClassNames);
+    }
+
+     /**
+     * Sets the list of comma-delimited classes that represent individual
+     * users. The classes in the list must implement <code>java.security.Principal</code>.
+     * When this accessor is called (for example, by a <code>Digester</code>
+     * instance parsing the
+     * configuration file), it will parse the class names and store the resulting
+     * string(s) into the <code>ArrayList</code> field </code>userClasses</code>.
+     */
+    public void setUserClassNames(String userClassNames) {
+        this.userClassNames = userClassNames;
+        userClasses.clear();
+        String temp = this.userClassNames;
+        if (temp == null) {
+            return;
+        }
+        while (true) {
+            int comma = temp.indexOf(',');
+            if (comma < 0) {
+                break;
+            }
+            userClasses.add(temp.substring(0, comma).trim());
+            temp = temp.substring(comma + 1);
+        }
+        temp = temp.trim();
+        if (temp.length() > 0) {
+            userClasses.add(temp);
+        }
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the <code>Principal</code> associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * If there are any errors with the JDBC connection, executing
+     * the query or anything we return null (don't authenticate). This
+     * event is also logged, and the connection will be closed so that
+     * a subsequent request will automatically re-open it.
+     *
+     * @param username Username of the <code>Principal</code> to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, String credentials) {
+
+        // Establish a LoginContext to use for authentication
+        try {
+        LoginContext loginContext = null;
+        if( appName==null ) appName="Tomcat";
+
+        if( log.isDebugEnabled())
+            log.debug(sm.getString("jaasRealm.beginLogin", username, appName));
+
+        // What if the LoginModule is in the container class loader ?
+        ClassLoader ocl = null;
+
+        if (isUseContextClassLoader()) {
+          ocl=Thread.currentThread().getContextClassLoader();
+          Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+        }
+
+        try {
+            loginContext = new LoginContext
+                (appName, new JAASCallbackHandler(this, username,
+                                                  credentials));
+        } catch (Throwable e) {
+            log.error(sm.getString("jaasRealm.unexpectedError"), e);
+            return (null);
+        } finally {
+            if( isUseContextClassLoader()) {
+              Thread.currentThread().setContextClassLoader(ocl);
+            }
+        }
+
+        if( log.isDebugEnabled())
+            log.debug("Login context created " + username);
+
+        // Negotiate a login via this LoginContext
+        Subject subject = null;
+        try {
+            loginContext.login();
+            subject = loginContext.getSubject();
+            if (subject == null) {
+                if( log.isDebugEnabled())
+                    log.debug(sm.getString("jaasRealm.failedLogin", username));
+                return (null);
+            }
+        } catch (AccountExpiredException e) {
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("jaasRealm.accountExpired", username));
+            return (null);
+        } catch (CredentialExpiredException e) {
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("jaasRealm.credentialExpired", username));
+            return (null);
+        } catch (FailedLoginException e) {
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("jaasRealm.failedLogin", username));
+            return (null);
+        } catch (LoginException e) {
+            log.warn(sm.getString("jaasRealm.loginException", username), e);
+            return (null);
+        } catch (Throwable e) {
+            log.error(sm.getString("jaasRealm.unexpectedError"), e);
+            return (null);
+        }
+
+        if( log.isDebugEnabled())
+            log.debug(sm.getString("jaasRealm.loginContextCreated", username));
+
+        // Return the appropriate Principal for this authenticated Subject
+        Principal principal = createPrincipal(username, subject);
+        if (principal == null) {
+            log.debug(sm.getString("jaasRealm.authenticateFailure", username));
+            return (null);
+        }
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("jaasRealm.authenticateSuccess", username));
+        }
+
+        return (principal);
+        } catch( Throwable t) {
+            log.error( "error ", t);
+            return null;
+        }
+    }
+     
+
+    // -------------------------------------------------------- Package Methods
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return a short name for this <code>Realm</code> implementation.
+     */
+    protected String getName() {
+
+        return (name);
+
+    }
+
+
+    /**
+     * Return the password associated with the given principal's user name.
+     */
+    protected String getPassword(String username) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the <code>Principal</code> associated with the given user name.
+     */
+    protected Principal getPrincipal(String username) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Identify and return a <code>java.security.Principal</code> instance
+     * representing the authenticated user for the specified <code>Subject</code>.
+     * The Principal is constructed by scanning the list of Principals returned
+     * by the JAASLoginModule. The first <code>Principal</code> object that matches
+     * one of the class names supplied as a "user class" is the user Principal.
+     * This object is returned to tha caller.
+     * Any remaining principal objects returned by the LoginModules are mapped to  
+     * roles, but only if their respective classes match one of the "role class" classes. 
+     * If a user Principal cannot be constructed, return <code>null</code>.
+     * @param subject The <code>Subject</code> representing the logged-in user
+     */
+    protected Principal createPrincipal(String username, Subject subject) {
+        // Prepare to scan the Principals for this Subject
+        String password = null; // Will not be carried forward
+
+        List roles = new ArrayList();
+        Principal userPrincipal = null;
+
+        // Scan the Principals for this Subject
+        Iterator principals = subject.getPrincipals().iterator();
+        while (principals.hasNext()) {
+            Principal principal = (Principal) principals.next();
+
+            String principalClass = principal.getClass().getName();
+
+            if( log.isDebugEnabled() ) {
+                log.debug(sm.getString("jaasRealm.checkPrincipal", principal, principalClass));
+            }
+
+            if (userPrincipal == null && userClasses.contains(principalClass)) {
+                userPrincipal = principal;
+                if( log.isDebugEnabled() ) {
+                    log.debug(sm.getString("jaasRealm.userPrincipalSuccess", principal.getName()));
+                }
+            }
+            
+            if (roleClasses.contains(principalClass)) {
+                roles.add(principal.getName());
+                if( log.isDebugEnabled() ) {
+                    log.debug(sm.getString("jaasRealm.rolePrincipalAdd", principal.getName()));
+                }
+            }
+        }
+
+        // Print failure message if needed
+        if (userPrincipal == null) {
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("jaasRealm.userPrincipalFailure"));
+                log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
+            }
+        } else {
+            if (roles.size() == 0) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
+                }
+            }
+        }
+
+        // Return the resulting Principal for our authenticated user
+        return new GenericPrincipal(this, username, null, roles, userPrincipal);
+    }
+
+     /**
+      * Ensure the given name is legal for JAAS configuration.
+      * Added for Bugzilla 30869, made protected for easy customization
+      * in case my implementation is insufficient, which I think is
+      * very likely.
+      *
+      * @param src The name to validate
+      * @return A string that's a valid JAAS realm name
+      */
+     protected String makeLegalForJAAS(final String src) {
+         String result = src;
+         
+         // Default name is "other" per JAAS spec
+         if(result == null) {
+             result = "other";
+         }
+
+         // Strip leading slash if present, as Sun JAAS impl
+         // barfs on it (see Bugzilla 30869 bug report).
+         if(result.startsWith("/")) {
+             result = result.substring(1);
+         }
+
+         return result;
+     }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     *
+     * Prepare for active use of the public methods of this <code>Component</code>.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public void start() throws LifecycleException {
+
+        // Perform normal superclass initialization
+        super.start();
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this <code>Component</code>.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Perform normal superclass finalization
+        super.stop();
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/JDBCRealm.java b/container/catalina/src/share/org/apache/catalina/realm/JDBCRealm.java
new file mode 100644
index 0000000..910a73a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/JDBCRealm.java
@@ -0,0 +1,791 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import java.security.Principal;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Properties;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+*
+* Implmentation of <b>Realm</b> that works with any JDBC supported database.
+* See the JDBCRealm.howto for more details on how to set up the database and
+* for configuration options.
+*
+* <p><strong>TODO</strong> - Support connection pooling (including message
+* format objects) so that <code>authenticate()</code> does not have to be
+* synchronized and would fix the ugly connection logic. </p>
+*
+* @author Craig R. McClanahan
+* @author Carson McDonald
+* @author Ignacio Ortega
+* @version $Revision$ $Date$
+*/
+
+public class JDBCRealm
+    extends RealmBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The connection username to use when trying to connect to the database.
+     */
+    protected String connectionName = null;
+
+
+    /**
+     * The connection URL to use when trying to connect to the database.
+     */
+    protected String connectionPassword = null;
+
+
+    /**
+     * The connection URL to use when trying to connect to the database.
+     */
+    protected String connectionURL = null;
+
+
+    /**
+     * The connection to the database.
+     */
+    protected Connection dbConnection = null;
+
+
+    /**
+     * Instance of the JDBC Driver class we use as a connection factory.
+     */
+    protected Driver driver = null;
+
+
+    /**
+     * The JDBC driver to use.
+     */
+    protected String driverName = null;
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.realm.JDBCRealm/1.0";
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String name = "JDBCRealm";
+
+
+    /**
+     * The PreparedStatement to use for authenticating users.
+     */
+    protected PreparedStatement preparedCredentials = null;
+
+
+    /**
+     * The PreparedStatement to use for identifying the roles for
+     * a specified user.
+     */
+    protected PreparedStatement preparedRoles = null;
+
+
+    /**
+     * The column in the user role table that names a role
+     */
+    protected String roleNameCol = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The column in the user table that holds the user's credintials
+     */
+    protected String userCredCol = null;
+
+
+    /**
+     * The column in the user table that holds the user's name
+     */
+    protected String userNameCol = null;
+
+
+    /**
+     * The table that holds the relation between user's and roles
+     */
+    protected String userRoleTable = null;
+
+
+    /**
+     * The table that holds user data.
+     */
+    protected String userTable = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the username to use to connect to the database.
+     *
+     */
+    public String getConnectionName() {
+        return connectionName;
+    }
+
+    /**
+     * Set the username to use to connect to the database.
+     *
+     * @param connectionName Username
+     */
+    public void setConnectionName(String connectionName) {
+        this.connectionName = connectionName;
+    }
+
+    /**
+     * Return the password to use to connect to the database.
+     *
+     */
+    public String getConnectionPassword() {
+        return connectionPassword;
+    }
+
+    /**
+     * Set the password to use to connect to the database.
+     *
+     * @param connectionPassword User password
+     */
+    public void setConnectionPassword(String connectionPassword) {
+        this.connectionPassword = connectionPassword;
+    }
+
+    /**
+     * Return the URL to use to connect to the database.
+     *
+     */
+    public String getConnectionURL() {
+        return connectionURL;
+    }
+
+    /**
+     * Set the URL to use to connect to the database.
+     *
+     * @param connectionURL The new connection URL
+     */
+    public void setConnectionURL( String connectionURL ) {
+      this.connectionURL = connectionURL;
+    }
+
+    /**
+     * Return the JDBC driver that will be used.
+     *
+     */
+    public String getDriverName() {
+        return driverName;
+    }
+
+    /**
+     * Set the JDBC driver that will be used.
+     *
+     * @param driverName The driver name
+     */
+    public void setDriverName( String driverName ) {
+      this.driverName = driverName;
+    }
+
+    /**
+     * Return the column in the user role table that names a role.
+     *
+     */
+    public String getRoleNameCol() {
+        return roleNameCol;
+    }
+
+    /**
+     * Set the column in the user role table that names a role.
+     *
+     * @param roleNameCol The column name
+     */
+    public void setRoleNameCol( String roleNameCol ) {
+        this.roleNameCol = roleNameCol;
+    }
+
+    /**
+     * Return the column in the user table that holds the user's credentials.
+     *
+     */
+    public String getUserCredCol() {
+        return userCredCol;
+    }
+
+    /**
+     * Set the column in the user table that holds the user's credentials.
+     *
+     * @param userCredCol The column name
+     */
+    public void setUserCredCol( String userCredCol ) {
+       this.userCredCol = userCredCol;
+    }
+
+    /**
+     * Return the column in the user table that holds the user's name.
+     *
+     */
+    public String getUserNameCol() {
+        return userNameCol;
+    }
+
+    /**
+     * Set the column in the user table that holds the user's name.
+     *
+     * @param userNameCol The column name
+     */
+    public void setUserNameCol( String userNameCol ) {
+       this.userNameCol = userNameCol;
+    }
+
+    /**
+     * Return the table that holds the relation between user's and roles.
+     *
+     */
+    public String getUserRoleTable() {
+        return userRoleTable;
+    }
+
+    /**
+     * Set the table that holds the relation between user's and roles.
+     *
+     * @param userRoleTable The table name
+     */
+    public void setUserRoleTable( String userRoleTable ) {
+        this.userRoleTable = userRoleTable;
+    }
+
+    /**
+     * Return the table that holds user data..
+     *
+     */
+    public String getUserTable() {
+        return userTable;
+    }
+
+    /**
+     * Set the table that holds user data.
+     *
+     * @param userTable The table name
+     */
+    public void setUserTable( String userTable ) {
+      this.userTable = userTable;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * If there are any errors with the JDBC connection, executing
+     * the query or anything we return null (don't authenticate). This
+     * event is also logged, and the connection will be closed so that
+     * a subsequent request will automatically re-open it.
+     *
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public synchronized Principal authenticate(String username, String credentials) {
+
+        // Number of tries is the numebr of attempts to connect to the database
+        // during this login attempt (if we need to open the database)
+        // This needs rewritten wuth better pooling support, the existing code
+        // needs signature changes since the Prepared statements needs cached
+        // with the connections.
+        // The code below will try twice if there is a SQLException so the
+        // connection may try to be opened again. On normal conditions (including
+        // invalid login - the above is only used once.
+        int numberOfTries = 2;
+        while (numberOfTries>0) {
+            try {
+
+                // Ensure that we have an open database connection
+                open();
+
+                // Acquire a Principal object for this user
+                Principal principal = authenticate(dbConnection,
+                                                   username, credentials);
+
+
+                // Return the Principal (if any)
+                return (principal);
+
+            } catch (SQLException e) {
+
+                // Log the problem for posterity
+                containerLog.error(sm.getString("jdbcRealm.exception"), e);
+
+                // Close the connection so that it gets reopened next time
+                if (dbConnection != null)
+                    close(dbConnection);
+
+            }
+
+            numberOfTries--;
+        }
+
+        // Worst case scenario
+        return null;
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param dbConnection The database connection to be used
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public synchronized Principal authenticate(Connection dbConnection,
+                                               String username,
+                                               String credentials) {
+
+        // No user - can't possibly authenticate
+        if (username == null) {
+            return (null);
+        }
+
+        // Look up the user's credentials
+        String dbCredentials = getPassword(username);
+
+        // Validate the user's credentials
+        boolean validated = false;
+        if (hasMessageDigest()) {
+            // Hex hashes should be compared case-insensitive
+            validated = (digest(credentials).equalsIgnoreCase(dbCredentials));
+        } else {
+            validated = (digest(credentials).equals(dbCredentials));
+        }
+
+        if (validated) {
+            if (containerLog.isTraceEnabled())
+                containerLog.trace(sm.getString("jdbcRealm.authenticateSuccess",
+                                                username));
+        } else {
+            if (containerLog.isTraceEnabled())
+                containerLog.trace(sm.getString("jdbcRealm.authenticateFailure",
+                                                username));
+            return (null);
+        }
+
+        ArrayList roles = getRoles(username);
+        
+        // Create and return a suitable Principal for this user
+        return (new GenericPrincipal(this, username, credentials, roles));
+
+    }
+
+
+    /**
+     * Close the specified database connection.
+     *
+     * @param dbConnection The connection to be closed
+     */
+    protected void close(Connection dbConnection) {
+
+        // Do nothing if the database connection is already closed
+        if (dbConnection == null)
+            return;
+
+        // Close our prepared statements (if any)
+        try {
+            preparedCredentials.close();
+        } catch (Throwable f) {
+            ;
+        }
+        this.preparedCredentials = null;
+
+
+        try {
+            preparedRoles.close();
+        } catch (Throwable f) {
+            ;
+        }
+        this.preparedRoles = null;
+
+
+        // Close this database connection, and log any errors
+        try {
+            dbConnection.close();
+        } catch (SQLException e) {
+            containerLog.warn(sm.getString("jdbcRealm.close"), e); // Just log it here
+        } finally {
+           this.dbConnection = null;
+        }
+
+    }
+
+
+    /**
+     * Return a PreparedStatement configured to perform the SELECT required
+     * to retrieve user credentials for the specified username.
+     *
+     * @param dbConnection The database connection to be used
+     * @param username Username for which credentials should be retrieved
+     *
+     * @exception SQLException if a database error occurs
+     */
+    protected PreparedStatement credentials(Connection dbConnection,
+                                            String username)
+        throws SQLException {
+
+        if (preparedCredentials == null) {
+            StringBuffer sb = new StringBuffer("SELECT ");
+            sb.append(userCredCol);
+            sb.append(" FROM ");
+            sb.append(userTable);
+            sb.append(" WHERE ");
+            sb.append(userNameCol);
+            sb.append(" = ?");
+
+            if(containerLog.isDebugEnabled()) {
+                containerLog.debug("credentials query: " + sb.toString());
+            }
+
+            preparedCredentials =
+                dbConnection.prepareStatement(sb.toString());
+        }
+
+        if (username == null) {
+            preparedCredentials.setNull(1,java.sql.Types.VARCHAR);
+        } else {
+            preparedCredentials.setString(1, username);
+        }
+
+        return (preparedCredentials);
+    }
+
+
+    /**
+     * Return a short name for this Realm implementation.
+     */
+    protected String getName() {
+
+        return (name);
+
+    }
+
+
+    /**
+     * Return the password associated with the given principal's user name.
+     */
+    protected String getPassword(String username) {
+
+        // Look up the user's credentials
+        String dbCredentials = null;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+
+        // Number of tries is the numebr of attempts to connect to the database
+        // during this login attempt (if we need to open the database)
+        // This needs rewritten wuth better pooling support, the existing code
+        // needs signature changes since the Prepared statements needs cached
+        // with the connections.
+        // The code below will try twice if there is a SQLException so the
+        // connection may try to be opened again. On normal conditions (including
+        // invalid login - the above is only used once.
+        int numberOfTries = 2;
+        while (numberOfTries>0) {
+            try {
+                
+                // Ensure that we have an open database connection
+                open();
+                
+                try {
+                    stmt = credentials(dbConnection, username);
+                    rs = stmt.executeQuery();
+                    
+                    if (rs.next()) {
+                        dbCredentials = rs.getString(1);
+                    }
+                    rs.close();
+                    rs = null;
+                    if (dbCredentials == null) {
+                        return (null);
+                    }
+                    
+                    dbCredentials = dbCredentials.trim();
+                    return dbCredentials;
+                    
+                } finally {
+                    if (rs!=null) {
+                        try {
+                            rs.close();
+                        } catch(SQLException e) {
+                            containerLog.warn(sm.getString("jdbcRealm.abnormalCloseResultSet"));
+                        }
+                    }
+                    dbConnection.commit();
+                }
+                
+            } catch (SQLException e) {
+                
+                // Log the problem for posterity
+                containerLog.error(sm.getString("jdbcRealm.exception"), e);
+                
+                // Close the connection so that it gets reopened next time
+                if (dbConnection != null)
+                    close(dbConnection);
+                
+            }
+            
+            numberOfTries--;
+        }
+        
+        return (null);
+    }
+
+
+    /**
+     * Return the Principal associated with the given user name.
+     */
+    protected Principal getPrincipal(String username) {
+
+        return (new GenericPrincipal(this,
+                                     username,
+                                     getPassword(username),
+                                     getRoles(username)));
+
+    }
+
+
+    /**
+     * Return the roles associated with the gven user name.
+     */
+    protected ArrayList getRoles(String username) {
+        
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+
+        // Number of tries is the numebr of attempts to connect to the database
+        // during this login attempt (if we need to open the database)
+        // This needs rewritten wuth better pooling support, the existing code
+        // needs signature changes since the Prepared statements needs cached
+        // with the connections.
+        // The code below will try twice if there is a SQLException so the
+        // connection may try to be opened again. On normal conditions (including
+        // invalid login - the above is only used once.
+        int numberOfTries = 2;
+        while (numberOfTries>0) {
+            try {
+                
+                // Ensure that we have an open database connection
+                open();
+                
+                try {
+                    // Accumulate the user's roles
+                    ArrayList roleList = new ArrayList();
+                    stmt = roles(dbConnection, username);
+                    rs = stmt.executeQuery();
+                    while (rs.next()) {
+                        String role = rs.getString(1);
+                        if (null!=role) {
+                            roleList.add(role.trim());
+                        }
+                    }
+                    rs.close();
+                    rs = null;
+                    
+                    return (roleList);
+                    
+                } finally {
+                    if (rs!=null) {
+                        try {
+                            rs.close();
+                        } catch(SQLException e) {
+                            containerLog.warn(sm.getString("jdbcRealm.abnormalCloseResultSet"));
+                        }
+                    }
+                    dbConnection.commit();
+                }
+                
+            } catch (SQLException e) {
+                
+                // Log the problem for posterity
+                containerLog.error(sm.getString("jdbcRealm.exception"), e);
+                
+                // Close the connection so that it gets reopened next time
+                if (dbConnection != null)
+                    close(dbConnection);
+                
+            }
+            
+            numberOfTries--;
+        }
+        
+        return (null);
+        
+    }
+    
+    
+    /**
+     * Open (if necessary) and return a database connection for use by
+     * this Realm.
+     *
+     * @exception SQLException if a database error occurs
+     */
+    protected Connection open() throws SQLException {
+
+        // Do nothing if there is a database connection already open
+        if (dbConnection != null)
+            return (dbConnection);
+
+        // Instantiate our database driver if necessary
+        if (driver == null) {
+            try {
+                Class clazz = Class.forName(driverName);
+                driver = (Driver) clazz.newInstance();
+            } catch (Throwable e) {
+                throw new SQLException(e.getMessage());
+            }
+        }
+
+        // Open a new connection
+        Properties props = new Properties();
+        if (connectionName != null)
+            props.put("user", connectionName);
+        if (connectionPassword != null)
+            props.put("password", connectionPassword);
+        dbConnection = driver.connect(connectionURL, props);
+        dbConnection.setAutoCommit(false);
+        return (dbConnection);
+
+    }
+
+
+    /**
+     * Release our use of this connection so that it can be recycled.
+     *
+     * @param dbConnection The connection to be released
+     */
+    protected void release(Connection dbConnection) {
+
+        ; // NO-OP since we are not pooling anything
+
+    }
+
+
+    /**
+     * Return a PreparedStatement configured to perform the SELECT required
+     * to retrieve user roles for the specified username.
+     *
+     * @param dbConnection The database connection to be used
+     * @param username Username for which roles should be retrieved
+     *
+     * @exception SQLException if a database error occurs
+     */
+    protected PreparedStatement roles(Connection dbConnection, String username)
+        throws SQLException {
+
+        if (preparedRoles == null) {
+            StringBuffer sb = new StringBuffer("SELECT ");
+            sb.append(roleNameCol);
+            sb.append(" FROM ");
+            sb.append(userRoleTable);
+            sb.append(" WHERE ");
+            sb.append(userNameCol);
+            sb.append(" = ?");
+            preparedRoles =
+                dbConnection.prepareStatement(sb.toString());
+        }
+
+        preparedRoles.setString(1, username);
+        return (preparedRoles);
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     *
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public void start() throws LifecycleException {
+
+        // Validate that we can open our connection - but let tomcat
+        // startup in case the database is temporarily unavailable
+        try {
+            open();
+        } catch (SQLException e) {
+            containerLog.error(sm.getString("jdbcRealm.open"), e);
+        }
+
+        // Perform normal superclass initialization
+        super.start();
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Perform normal superclass finalization
+        super.stop();
+
+        // Close any open DB connection
+        close(this.dbConnection);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/JNDIRealm.java b/container/catalina/src/share/org/apache/catalina/realm/JNDIRealm.java
new file mode 100644
index 0000000..e54f53a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/JNDIRealm.java
@@ -0,0 +1,1738 @@
+/*
+ * Copyright 1999-2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.realm;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.naming.Context;
+import javax.naming.CommunicationException;
+import javax.naming.CompositeName;
+import javax.naming.InvalidNameException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NameParser;
+import javax.naming.Name;
+import javax.naming.AuthenticationException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.util.Base64;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+
+/**
+ * <p>Implementation of <strong>Realm</strong> that works with a directory
+ * server accessed via the Java Naming and Directory Interface (JNDI) APIs.
+ * The following constraints are imposed on the data structure in the
+ * underlying directory server:</p>
+ * <ul>
+ *
+ * <li>Each user that can be authenticated is represented by an individual
+ *     element in the top level <code>DirContext</code> that is accessed
+ *     via the <code>connectionURL</code> property.</li>
+ *
+ * <li>If a socket connection can not be made to the <code>connectURL</code>
+ *     an attempt will be made to use the <code>alternateURL</code> if it
+ *     exists.</li>
+ *
+ * <li>Each user element has a distinguished name that can be formed by
+ *     substituting the presented username into a pattern configured by the
+ *     <code>userPattern</code> property.</li>
+ *
+ * <li>Alternatively, if the <code>userPattern</code> property is not
+ *     specified, a unique element can be located by searching the directory
+ *     context. In this case:
+ *     <ul>
+ *     <li>The <code>userSearch</code> pattern specifies the search filter
+ *         after substitution of the username.</li>
+ *     <li>The <code>userBase</code> property can be set to the element that
+ *         is the base of the subtree containing users.  If not specified,
+ *         the search base is the top-level context.</li>
+ *     <li>The <code>userSubtree</code> property can be set to
+ *         <code>true</code> if you wish to search the entire subtree of the
+ *         directory context.  The default value of <code>false</code>
+ *         requests a search of only the current level.</li>
+ *    </ul>
+ * </li>
+ *
+ * <li>The user may be authenticated by binding to the directory with the
+ *      username and password presented. This method is used when the
+ *      <code>userPassword</code> property is not specified.</li>
+ *
+ * <li>The user may be authenticated by retrieving the value of an attribute
+ *     from the directory and comparing it explicitly with the value presented
+ *     by the user. This method is used when the <code>userPassword</code>
+ *     property is specified, in which case:
+ *     <ul>
+ *     <li>The element for this user must contain an attribute named by the
+ *         <code>userPassword</code> property.
+ *     <li>The value of the user password attribute is either a cleartext
+ *         String, or the result of passing a cleartext String through the
+ *         <code>RealmBase.digest()</code> method (using the standard digest
+ *         support included in <code>RealmBase</code>).
+ *     <li>The user is considered to be authenticated if the presented
+ *         credentials (after being passed through
+ *         <code>RealmBase.digest()</code>) are equal to the retrieved value
+ *         for the user password attribute.</li>
+ *     </ul></li>
+ *
+ * <li>Each group of users that has been assigned a particular role may be
+ *     represented by an individual element in the top level
+ *     <code>DirContext</code> that is accessed via the
+ *     <code>connectionURL</code> property.  This element has the following
+ *     characteristics:
+ *     <ul>
+ *     <li>The set of all possible groups of interest can be selected by a
+ *         search pattern configured by the <code>roleSearch</code>
+ *         property.</li>
+ *     <li>The <code>roleSearch</code> pattern optionally includes pattern
+ *         replacements "{0}" for the distinguished name, and/or "{1}" for
+ *         the username, of the authenticated user for which roles will be
+ *         retrieved.</li>
+ *     <li>The <code>roleBase</code> property can be set to the element that
+ *         is the base of the search for matching roles.  If not specified,
+ *         the entire context will be searched.</li>
+ *     <li>The <code>roleSubtree</code> property can be set to
+ *         <code>true</code> if you wish to search the entire subtree of the
+ *         directory context.  The default value of <code>false</code>
+ *         requests a search of only the current level.</li>
+ *     <li>The element includes an attribute (whose name is configured by
+ *         the <code>roleName</code> property) containing the name of the
+ *         role represented by this element.</li>
+ *     </ul></li>
+ *
+ * <li>In addition, roles may be represented by the values of an attribute
+ * in the user's element whose name is configured by the
+ * <code>userRoleName</code> property.</li>
+ *
+ * <li>Note that the standard <code>&lt;security-role-ref&gt;</code> element in
+ *     the web application deployment descriptor allows applications to refer
+ *     to roles programmatically by names other than those used in the
+ *     directory server itself.</li>
+ * </ul>
+ *
+ * <p><strong>TODO</strong> - Support connection pooling (including message
+ * format objects) so that <code>authenticate()</code> does not have to be
+ * synchronized.</p>
+ *
+ * <p><strong>WARNING</strong> - There is a reported bug against the Netscape
+ * provider code (com.netscape.jndi.ldap.LdapContextFactory) with respect to
+ * successfully authenticated a non-existing user. The
+ * report is here: http://issues.apache.org/bugzilla/show_bug.cgi?id=11210 .
+ * With luck, Netscape has updated their provider code and this is not an
+ * issue. </p>
+ *
+ * @author John Holman
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class JNDIRealm extends RealmBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     *  The type of authentication to use
+     */
+    protected String authentication = null;
+
+    /**
+     * The connection username for the server we will contact.
+     */
+    protected String connectionName = null;
+
+
+    /**
+     * The connection password for the server we will contact.
+     */
+    protected String connectionPassword = null;
+
+
+    /**
+     * The connection URL for the server we will contact.
+     */
+    protected String connectionURL = null;
+
+
+    /**
+     * The directory context linking us to our directory server.
+     */
+    protected DirContext context = null;
+
+
+    /**
+     * The JNDI context factory used to acquire our InitialContext.  By
+     * default, assumes use of an LDAP server using the standard JNDI LDAP
+     * provider.
+     */
+    protected String contextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
+
+    
+    /**
+     * How aliases should be dereferenced during search operations.
+     */
+    protected String derefAliases = null;
+
+    /**
+     * Constant that holds the name of the environment property for specifying 
+     * the manner in which aliases should be dereferenced.
+     */
+    public final static String DEREF_ALIASES = "java.naming.ldap.derefAliases";
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.realm.JNDIRealm/1.0";
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String name = "JNDIRealm";
+
+
+    /**
+     * The protocol that will be used in the communication with the
+     * directory server.
+     */
+    protected String protocol = null;
+
+
+    /**
+     * How should we handle referrals?  Microsoft Active Directory can't handle
+     * the default case, so an application authenticating against AD must
+     * set referrals to "follow".
+     */
+    protected String referrals = null;
+
+
+    /**
+     * The base element for user searches.
+     */
+    protected String userBase = "";
+
+
+    /**
+     * The message format used to search for a user, with "{0}" marking
+     * the spot where the username goes.
+     */
+    protected String userSearch = null;
+
+
+    /**
+     * The MessageFormat object associated with the current
+     * <code>userSearch</code>.
+     */
+    protected MessageFormat userSearchFormat = null;
+
+
+    /**
+     * Should we search the entire subtree for matching users?
+     */
+    protected boolean userSubtree = false;
+
+
+    /**
+     * The attribute name used to retrieve the user password.
+     */
+    protected String userPassword = null;
+
+
+    /**
+     * A string of LDAP user patterns or paths, ":"-separated
+     * These will be used to form the distinguished name of a
+     * user, with "{0}" marking the spot where the specified username
+     * goes.
+     * This is similar to userPattern, but allows for multiple searches
+     * for a user.
+     */
+    protected String[] userPatternArray = null;
+
+
+    /**
+     * The message format used to form the distinguished name of a
+     * user, with "{0}" marking the spot where the specified username
+     * goes.
+     */
+    protected String userPattern = null;
+
+
+    /**
+     * An array of MessageFormat objects associated with the current
+     * <code>userPatternArray</code>.
+     */
+    protected MessageFormat[] userPatternFormatArray = null;
+
+
+    /**
+     * The base element for role searches.
+     */
+    protected String roleBase = "";
+
+
+    /**
+     * The MessageFormat object associated with the current
+     * <code>roleSearch</code>.
+     */
+    protected MessageFormat roleFormat = null;
+
+
+    /**
+     * The name of an attribute in the user's entry containing
+     * roles for that user
+     */
+    protected String userRoleName = null;
+
+
+    /**
+     * The name of the attribute containing roles held elsewhere
+     */
+    protected String roleName = null;
+
+
+    /**
+     * The message format used to select roles for a user, with "{0}" marking
+     * the spot where the distinguished name of the user goes.
+     */
+    protected String roleSearch = null;
+
+
+    /**
+     * Should we search the entire subtree for matching memberships?
+     */
+    protected boolean roleSubtree = false;
+
+    /**
+     * An alternate URL, to which, we should connect if connectionURL fails.
+     */
+    protected String alternateURL;
+
+    /**
+     * The number of connection attempts.  If greater than zero we use the
+     * alternate url.
+     */
+    protected int connectionAttempt = 0;
+
+    /**
+     * The current user pattern to be used for lookup and binding of a user.
+     */
+    protected int curUserPattern = 0;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the type of authentication to use.
+     */
+    public String getAuthentication() {
+
+        return authentication;
+
+    }
+
+    /**
+     * Set the type of authentication to use.
+     *
+     * @param authentication The authentication
+     */
+    public void setAuthentication(String authentication) {
+
+        this.authentication = authentication;
+
+    }
+
+    /**
+     * Return the connection username for this Realm.
+     */
+    public String getConnectionName() {
+
+        return (this.connectionName);
+
+    }
+
+
+    /**
+     * Set the connection username for this Realm.
+     *
+     * @param connectionName The new connection username
+     */
+    public void setConnectionName(String connectionName) {
+
+        this.connectionName = connectionName;
+
+    }
+
+
+    /**
+     * Return the connection password for this Realm.
+     */
+    public String getConnectionPassword() {
+
+        return (this.connectionPassword);
+
+    }
+
+
+    /**
+     * Set the connection password for this Realm.
+     *
+     * @param connectionPassword The new connection password
+     */
+    public void setConnectionPassword(String connectionPassword) {
+
+        this.connectionPassword = connectionPassword;
+
+    }
+
+
+    /**
+     * Return the connection URL for this Realm.
+     */
+    public String getConnectionURL() {
+
+        return (this.connectionURL);
+
+    }
+
+
+    /**
+     * Set the connection URL for this Realm.
+     *
+     * @param connectionURL The new connection URL
+     */
+    public void setConnectionURL(String connectionURL) {
+
+        this.connectionURL = connectionURL;
+
+    }
+
+
+    /**
+     * Return the JNDI context factory for this Realm.
+     */
+    public String getContextFactory() {
+
+        return (this.contextFactory);
+
+    }
+
+
+    /**
+     * Set the JNDI context factory for this Realm.
+     *
+     * @param contextFactory The new context factory
+     */
+    public void setContextFactory(String contextFactory) {
+
+        this.contextFactory = contextFactory;
+
+    }
+
+    /**
+     * Return the derefAliases setting to be used.
+     */
+    public java.lang.String getDerefAliases() {
+        return derefAliases;
+    }  
+    
+    /**
+     * Set the value for derefAliases to be used when searching the directory.
+     * 
+     * @param derefAliases New value of property derefAliases.
+     */
+    public void setDerefAliases(java.lang.String derefAliases) {
+      this.derefAliases = derefAliases;
+    }
+
+    /**
+     * Return the protocol to be used.
+     */
+    public String getProtocol() {
+
+        return protocol;
+
+    }
+
+    /**
+     * Set the protocol for this Realm.
+     *
+     * @param protocol The new protocol.
+     */
+    public void setProtocol(String protocol) {
+
+        this.protocol = protocol;
+
+    }
+
+
+    /**
+     * Returns the current settings for handling JNDI referrals.
+     */
+    public String getReferrals () {
+        return referrals;
+    }
+
+
+    /**
+     * How do we handle JNDI referrals? ignore, follow, or throw
+     * (see javax.naming.Context.REFERRAL for more information).
+     */
+    public void setReferrals (String referrals) {
+        this.referrals = referrals;
+    }
+
+
+    /**
+     * Return the base element for user searches.
+     */
+    public String getUserBase() {
+
+        return (this.userBase);
+
+    }
+
+
+    /**
+     * Set the base element for user searches.
+     *
+     * @param userBase The new base element
+     */
+    public void setUserBase(String userBase) {
+
+        this.userBase = userBase;
+
+    }
+
+
+    /**
+     * Return the message format pattern for selecting users in this Realm.
+     */
+    public String getUserSearch() {
+
+        return (this.userSearch);
+
+    }
+
+
+    /**
+     * Set the message format pattern for selecting users in this Realm.
+     *
+     * @param userSearch The new user search pattern
+     */
+    public void setUserSearch(String userSearch) {
+
+        this.userSearch = userSearch;
+        if (userSearch == null)
+            userSearchFormat = null;
+        else
+            userSearchFormat = new MessageFormat(userSearch);
+
+    }
+
+
+    /**
+     * Return the "search subtree for users" flag.
+     */
+    public boolean getUserSubtree() {
+
+        return (this.userSubtree);
+
+    }
+
+
+    /**
+     * Set the "search subtree for users" flag.
+     *
+     * @param userSubtree The new search flag
+     */
+    public void setUserSubtree(boolean userSubtree) {
+
+        this.userSubtree = userSubtree;
+
+    }
+
+
+    /**
+     * Return the user role name attribute name for this Realm.
+     */
+    public String getUserRoleName() {
+
+        return userRoleName;
+    }
+
+
+    /**
+     * Set the user role name attribute name for this Realm.
+     *
+     * @param userRoleName The new userRole name attribute name
+     */
+    public void setUserRoleName(String userRoleName) {
+
+        this.userRoleName = userRoleName;
+
+    }
+
+
+    /**
+     * Return the base element for role searches.
+     */
+    public String getRoleBase() {
+
+        return (this.roleBase);
+
+    }
+
+
+    /**
+     * Set the base element for role searches.
+     *
+     * @param roleBase The new base element
+     */
+    public void setRoleBase(String roleBase) {
+
+        this.roleBase = roleBase;
+
+    }
+
+
+    /**
+     * Return the role name attribute name for this Realm.
+     */
+    public String getRoleName() {
+
+        return (this.roleName);
+
+    }
+
+
+    /**
+     * Set the role name attribute name for this Realm.
+     *
+     * @param roleName The new role name attribute name
+     */
+    public void setRoleName(String roleName) {
+
+        this.roleName = roleName;
+
+    }
+
+
+    /**
+     * Return the message format pattern for selecting roles in this Realm.
+     */
+    public String getRoleSearch() {
+
+        return (this.roleSearch);
+
+    }
+
+
+    /**
+     * Set the message format pattern for selecting roles in this Realm.
+     *
+     * @param roleSearch The new role search pattern
+     */
+    public void setRoleSearch(String roleSearch) {
+
+        this.roleSearch = roleSearch;
+        if (roleSearch == null)
+            roleFormat = null;
+        else
+            roleFormat = new MessageFormat(roleSearch);
+
+    }
+
+
+    /**
+     * Return the "search subtree for roles" flag.
+     */
+    public boolean getRoleSubtree() {
+
+        return (this.roleSubtree);
+
+    }
+
+
+    /**
+     * Set the "search subtree for roles" flag.
+     *
+     * @param roleSubtree The new search flag
+     */
+    public void setRoleSubtree(boolean roleSubtree) {
+
+        this.roleSubtree = roleSubtree;
+
+    }
+
+
+    /**
+     * Return the password attribute used to retrieve the user password.
+     */
+    public String getUserPassword() {
+
+        return (this.userPassword);
+
+    }
+
+
+    /**
+     * Set the password attribute used to retrieve the user password.
+     *
+     * @param userPassword The new password attribute
+     */
+    public void setUserPassword(String userPassword) {
+
+        this.userPassword = userPassword;
+
+    }
+
+
+    /**
+     * Return the message format pattern for selecting users in this Realm.
+     */
+    public String getUserPattern() {
+
+        return (this.userPattern);
+
+    }
+
+
+    /**
+     * Set the message format pattern for selecting users in this Realm.
+     * This may be one simple pattern, or multiple patterns to be tried,
+     * separated by parentheses. (for example, either "cn={0}", or
+     * "(cn={0})(cn={0},o=myorg)" Full LDAP search strings are also supported,
+     * but only the "OR", "|" syntax, so "(|(cn={0})(cn={0},o=myorg))" is
+     * also valid. Complex search strings with &, etc are NOT supported.
+     *
+     * @param userPattern The new user pattern
+     */
+    public void setUserPattern(String userPattern) {
+
+        this.userPattern = userPattern;
+        if (userPattern == null)
+            userPatternArray = null;
+        else {
+            userPatternArray = parseUserPatternString(userPattern);
+            int len = this.userPatternArray.length;
+            userPatternFormatArray = new MessageFormat[len];
+            for (int i=0; i < len; i++) {
+                userPatternFormatArray[i] =
+                    new MessageFormat(userPatternArray[i]);
+            }
+        }
+    }
+
+
+    /**
+     * Getter for property alternateURL.
+     *
+     * @return Value of property alternateURL.
+     */
+    public String getAlternateURL() {
+
+        return this.alternateURL;
+
+    }
+
+
+    /**
+     * Setter for property alternateURL.
+     *
+     * @param alternateURL New value of property alternateURL.
+     */
+    public void setAlternateURL(String alternateURL) {
+
+        this.alternateURL = alternateURL;
+
+    }
+
+
+    // ---------------------------------------------------------- Realm Methods
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * If there are any errors with the JDBC connection, executing
+     * the query or anything we return null (don't authenticate). This
+     * event is also logged, and the connection will be closed so that
+     * a subsequent request will automatically re-open it.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, String credentials) {
+
+        DirContext context = null;
+        Principal principal = null;
+
+        try {
+
+            // Ensure that we have a directory context available
+            context = open();
+
+            // Occassionally the directory context will timeout.  Try one more
+            // time before giving up.
+            try {
+
+                // Authenticate the specified username if possible
+                principal = authenticate(context, username, credentials);
+
+            } catch (CommunicationException e) {
+
+                // log the exception so we know it's there.
+                containerLog.warn(sm.getString("jndiRealm.exception"), e);
+
+                // close the connection so we know it will be reopened.
+                if (context != null)
+                    close(context);
+
+                // open a new directory context.
+                context = open();
+
+                // Try the authentication again.
+                principal = authenticate(context, username, credentials);
+
+            }
+
+
+            // Release this context
+            release(context);
+
+            // Return the authenticated Principal (if any)
+            return (principal);
+
+        } catch (NamingException e) {
+
+            // Log the problem for posterity
+            containerLog.error(sm.getString("jndiRealm.exception"), e);
+
+            // Close the connection so that it gets reopened next time
+            if (context != null)
+                close(context);
+
+            // Return "not authenticated" for this request
+            return (null);
+
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param context The directory context
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    public synchronized Principal authenticate(DirContext context,
+                                               String username,
+                                               String credentials)
+        throws NamingException {
+
+        if (username == null || username.equals("")
+            || credentials == null || credentials.equals(""))
+            return (null);
+
+        if (userPatternArray != null) {
+            for (curUserPattern = 0;
+                 curUserPattern < userPatternFormatArray.length;
+                 curUserPattern++) {
+                // Retrieve user information
+                User user = getUser(context, username);
+                if (user != null) {
+                    try {
+                        // Check the user's credentials
+                        if (checkCredentials(context, user, credentials)) {
+                            // Search for additional roles
+                            List roles = getRoles(context, user);
+                            return (new GenericPrincipal(this,
+                                                         username,
+                                                         credentials,
+                                                         roles));
+                        }
+                    } catch (InvalidNameException ine) {
+                        // Log the problem for posterity
+                        containerLog.warn(sm.getString("jndiRealm.exception"), ine);
+                        // ignore; this is probably due to a name not fitting
+                        // the search path format exactly, as in a fully-
+                        // qualified name being munged into a search path
+                        // that already contains cn= or vice-versa
+                    }
+                }
+            }
+            return null;
+        } else {
+            // Retrieve user information
+            User user = getUser(context, username);
+            if (user == null)
+                return (null);
+
+            // Check the user's credentials
+            if (!checkCredentials(context, user, credentials))
+                return (null);
+
+            // Search for additional roles
+            List roles = getRoles(context, user);
+
+            // Create and return a suitable Principal for this user
+            return (new GenericPrincipal(this, username, credentials, roles));
+        }
+    }
+
+
+    /**
+     * Return a User object containing information about the user
+     * with the specified username, if found in the directory;
+     * otherwise return <code>null</code>.
+     *
+     * If the <code>userPassword</code> configuration attribute is
+     * specified, the value of that attribute is retrieved from the
+     * user's directory entry. If the <code>userRoleName</code>
+     * configuration attribute is specified, all values of that
+     * attribute are retrieved from the directory entry.
+     *
+     * @param context The directory context
+     * @param username Username to be looked up
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    protected User getUser(DirContext context, String username)
+        throws NamingException {
+
+        User user = null;
+
+        // Get attributes to retrieve from user entry
+        ArrayList list = new ArrayList();
+        if (userPassword != null)
+            list.add(userPassword);
+        if (userRoleName != null)
+            list.add(userRoleName);
+        String[] attrIds = new String[list.size()];
+        list.toArray(attrIds);
+
+        // Use pattern or search for user entry
+        if (userPatternFormatArray != null) {
+            user = getUserByPattern(context, username, attrIds);
+        } else {
+            user = getUserBySearch(context, username, attrIds);
+        }
+
+        return user;
+    }
+
+
+    /**
+     * Use the <code>UserPattern</code> configuration attribute to
+     * locate the directory entry for the user with the specified
+     * username and return a User object; otherwise return
+     * <code>null</code>.
+     *
+     * @param context The directory context
+     * @param username The username
+     * @param attrIds String[]containing names of attributes to
+     * retrieve.
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    protected User getUserByPattern(DirContext context,
+                                              String username,
+                                              String[] attrIds)
+        throws NamingException {
+
+        if (username == null || userPatternFormatArray[curUserPattern] == null)
+            return (null);
+
+        // Form the dn from the user pattern
+        String dn = userPatternFormatArray[curUserPattern].format(new String[] { username });
+
+        // Return if no attributes to retrieve
+        if (attrIds == null || attrIds.length == 0)
+            return new User(username, dn, null, null);
+
+        // Get required attributes from user entry
+        Attributes attrs = null;
+        try {
+            attrs = context.getAttributes(dn, attrIds);
+        } catch (NameNotFoundException e) {
+            return (null);
+        }
+        if (attrs == null)
+            return (null);
+
+        // Retrieve value of userPassword
+        String password = null;
+        if (userPassword != null)
+            password = getAttributeValue(userPassword, attrs);
+
+        // Retrieve values of userRoleName attribute
+        ArrayList roles = null;
+        if (userRoleName != null)
+            roles = addAttributeValues(userRoleName, attrs, roles);
+
+        return new User(username, dn, password, roles);
+    }
+
+
+    /**
+     * Search the directory to return a User object containing
+     * information about the user with the specified username, if
+     * found in the directory; otherwise return <code>null</code>.
+     *
+     * @param context The directory context
+     * @param username The username
+     * @param attrIds String[]containing names of attributes to retrieve.
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    protected User getUserBySearch(DirContext context,
+                                           String username,
+                                           String[] attrIds)
+        throws NamingException {
+
+        if (username == null || userSearchFormat == null)
+            return (null);
+
+        // Form the search filter
+        String filter = userSearchFormat.format(new String[] { username });
+
+        // Set up the search controls
+        SearchControls constraints = new SearchControls();
+
+        if (userSubtree) {
+            constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
+        }
+        else {
+            constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE);
+        }
+
+        // Specify the attributes to be retrieved
+        if (attrIds == null)
+            attrIds = new String[0];
+        constraints.setReturningAttributes(attrIds);
+
+        NamingEnumeration results =
+            context.search(userBase, filter, constraints);
+
+
+        // Fail if no entries found
+        if (results == null || !results.hasMore()) {
+            return (null);
+        }
+
+        // Get result for the first entry found
+        SearchResult result = (SearchResult)results.next();
+
+        // Check no further entries were found
+        if (results.hasMore()) {
+            if(containerLog.isInfoEnabled())
+                containerLog.info("username " + username + " has multiple entries");
+            return (null);
+        }
+
+        // Get the entry's distinguished name
+        NameParser parser = context.getNameParser("");
+        Name contextName = parser.parse(context.getNameInNamespace());
+        Name baseName = parser.parse(userBase);
+
+        // Bugzilla 32269
+        Name entryName = parser.parse(new CompositeName(result.getName()).get(0));
+
+        Name name = contextName.addAll(baseName);
+        name = name.addAll(entryName);
+        String dn = name.toString();
+
+        if (containerLog.isTraceEnabled())
+            containerLog.trace("  entry found for " + username + " with dn " + dn);
+
+        // Get the entry's attributes
+        Attributes attrs = result.getAttributes();
+        if (attrs == null)
+            return null;
+
+        // Retrieve value of userPassword
+        String password = null;
+        if (userPassword != null)
+            password = getAttributeValue(userPassword, attrs);
+
+        // Retrieve values of userRoleName attribute
+        ArrayList roles = null;
+        if (userRoleName != null)
+            roles = addAttributeValues(userRoleName, attrs, roles);
+
+        return new User(username, dn, password, roles);
+    }
+
+
+    /**
+     * Check whether the given User can be authenticated with the
+     * given credentials. If the <code>userPassword</code>
+     * configuration attribute is specified, the credentials
+     * previously retrieved from the directory are compared explicitly
+     * with those presented by the user. Otherwise the presented
+     * credentials are checked by binding to the directory as the
+     * user.
+     *
+     * @param context The directory context
+     * @param user The User to be authenticated
+     * @param credentials The credentials presented by the user
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    protected boolean checkCredentials(DirContext context,
+                                     User user,
+                                     String credentials)
+         throws NamingException {
+
+         boolean validated = false;
+
+         if (userPassword == null) {
+             validated = bindAsUser(context, user, credentials);
+         } else {
+             validated = compareCredentials(context, user, credentials);
+         }
+
+         if (containerLog.isTraceEnabled()) {
+             if (validated) {
+                 containerLog.trace(sm.getString("jndiRealm.authenticateSuccess",
+                                  user.username));
+             } else {
+                 containerLog.trace(sm.getString("jndiRealm.authenticateFailure",
+                                  user.username));
+             }
+         }
+         return (validated);
+     }
+
+
+
+    /**
+     * Check whether the credentials presented by the user match those
+     * retrieved from the directory.
+     *
+     * @param context The directory context
+     * @param info The User to be authenticated
+     * @param credentials Authentication credentials
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    protected boolean compareCredentials(DirContext context,
+                                         User info,
+                                         String credentials)
+        throws NamingException {
+
+        if (info == null || credentials == null)
+            return (false);
+
+        String password = info.password;
+        if (password == null)
+            return (false);
+
+        // Validate the credentials specified by the user
+        if (containerLog.isTraceEnabled())
+            containerLog.trace("  validating credentials");
+
+        boolean validated = false;
+        if (hasMessageDigest()) {
+            // iPlanet support if the values starts with {SHA1}
+            // The string is in a format compatible with Base64.encode not
+            // the Hex encoding of the parent class.
+            if (password.startsWith("{SHA}")) {
+                /* sync since super.digest() does this same thing */
+                synchronized (this) {
+                    password = password.substring(5);
+                    md.reset();
+                    md.update(credentials.getBytes());
+                    String digestedPassword =
+                        new String(Base64.encode(md.digest()));
+                    validated = password.equals(digestedPassword);
+                }
+            } else if (password.startsWith("{SSHA}")) {
+                // Bugzilla 32938
+                /* sync since super.digest() does this same thing */
+                synchronized (this) {
+                    password = password.substring(6);
+
+                    md.reset();
+                    md.update(credentials.getBytes());
+
+                    // Decode stored password.
+                    ByteChunk pwbc = new ByteChunk(password.length());
+                    try {
+                        pwbc.append(password.getBytes(), 0, password.length());
+                    } catch (IOException e) {
+                        // Should never happen
+                        containerLog.error("Could not append password bytes to chunk: ", e);
+                    }
+
+                    CharChunk decoded = new CharChunk();
+                    Base64.decode(pwbc, decoded);
+                    char[] pwarray = decoded.getBuffer();
+
+                    // Split decoded password into hash and salt.
+                    final int saltpos = 20;
+                    byte[] hash = new byte[saltpos];
+                    for (int i=0; i< hash.length; i++) {
+                        hash[i] = (byte) pwarray[i];
+                    }
+
+                    byte[] salt = new byte[pwarray.length - saltpos];
+                    for (int i=0; i< salt.length; i++)
+                        salt[i] = (byte)pwarray[i+saltpos];
+
+                    md.update(salt);
+                    byte[] dp = md.digest();
+
+                    validated = Arrays.equals(dp, hash);
+                } // End synchronized(this) block
+            } else {
+                // Hex hashes should be compared case-insensitive
+                validated = (digest(credentials).equalsIgnoreCase(password));
+            }
+        } else
+            validated = (digest(credentials).equals(password));
+        return (validated);
+
+    }
+
+
+
+    /**
+     * Check credentials by binding to the directory as the user
+     *
+     * @param context The directory context
+     * @param user The User to be authenticated
+     * @param credentials Authentication credentials
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+     protected boolean bindAsUser(DirContext context,
+                                  User user,
+                                  String credentials)
+         throws NamingException {
+         Attributes attr;
+
+         if (credentials == null || user == null)
+             return (false);
+
+         String dn = user.dn;
+         if (dn == null)
+             return (false);
+
+         // Validate the credentials specified by the user
+         if (containerLog.isTraceEnabled()) {
+             containerLog.trace("  validating credentials by binding as the user");
+        }
+
+        // Set up security environment to bind as the user
+        context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
+        context.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials);
+
+        // Elicit an LDAP bind operation
+        boolean validated = false;
+        try {
+            if (containerLog.isTraceEnabled()) {
+                containerLog.trace("  binding as "  + dn);
+            }
+            attr = context.getAttributes("", null);
+            validated = true;
+        }
+        catch (AuthenticationException e) {
+            if (containerLog.isTraceEnabled()) {
+                containerLog.trace("  bind attempt failed");
+            }
+        }
+
+        // Restore the original security environment
+        if (connectionName != null) {
+            context.addToEnvironment(Context.SECURITY_PRINCIPAL,
+                                     connectionName);
+        } else {
+            context.removeFromEnvironment(Context.SECURITY_PRINCIPAL);
+        }
+
+        if (connectionPassword != null) {
+            context.addToEnvironment(Context.SECURITY_CREDENTIALS,
+                                     connectionPassword);
+        }
+        else {
+            context.removeFromEnvironment(Context.SECURITY_CREDENTIALS);
+        }
+
+        return (validated);
+     }
+
+
+    /**
+     * Return a List of roles associated with the given User.  Any
+     * roles present in the user's directory entry are supplemented by
+     * a directory search. If no roles are associated with this user,
+     * a zero-length List is returned.
+     *
+     * @param context The directory context we are searching
+     * @param user The User to be checked
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    protected List getRoles(DirContext context, User user)
+        throws NamingException {
+
+        if (user == null)
+            return (null);
+
+        String dn = user.dn;
+        String username = user.username;
+
+        if (dn == null || username == null)
+            return (null);
+
+        if (containerLog.isTraceEnabled())
+            containerLog.trace("  getRoles(" + dn + ")");
+
+        // Start with roles retrieved from the user entry
+        ArrayList list = user.roles;
+        if (list == null) {
+            list = new ArrayList();
+        }
+
+        // Are we configured to do role searches?
+        if ((roleFormat == null) || (roleName == null))
+            return (list);
+
+        // Set up parameters for an appropriate search
+        String filter = roleFormat.format(new String[] { doRFC2254Encoding(dn), username });
+        SearchControls controls = new SearchControls();
+        if (roleSubtree)
+            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+        else
+            controls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
+        controls.setReturningAttributes(new String[] {roleName});
+
+        // Perform the configured search and process the results
+        NamingEnumeration results =
+            context.search(roleBase, filter, controls);
+        if (results == null)
+            return (list);  // Should never happen, but just in case ...
+        while (results.hasMore()) {
+            SearchResult result = (SearchResult) results.next();
+            Attributes attrs = result.getAttributes();
+            if (attrs == null)
+                continue;
+            list = addAttributeValues(roleName, attrs, list);
+        }
+
+
+        if (containerLog.isTraceEnabled()) {
+            if (list != null) {
+                containerLog.trace("  Returning " + list.size() + " roles");
+                for (int i=0; i<list.size(); i++)
+                    containerLog.trace(  "  Found role " + list.get(i));
+            } else {
+                containerLog.trace("  getRoles about to return null ");
+            }
+        }
+
+        return (list);
+    }
+
+
+    /**
+     * Return a String representing the value of the specified attribute.
+     *
+     * @param attrId Attribute name
+     * @param attrs Attributes containing the required value
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    private String getAttributeValue(String attrId, Attributes attrs)
+        throws NamingException {
+
+        if (containerLog.isTraceEnabled())
+            containerLog.trace("  retrieving attribute " + attrId);
+
+        if (attrId == null || attrs == null)
+            return null;
+
+        Attribute attr = attrs.get(attrId);
+        if (attr == null)
+            return (null);
+        Object value = attr.get();
+        if (value == null)
+            return (null);
+        String valueString = null;
+        if (value instanceof byte[])
+            valueString = new String((byte[]) value);
+        else
+            valueString = value.toString();
+
+        return valueString;
+    }
+
+
+
+    /**
+     * Add values of a specified attribute to a list
+     *
+     * @param attrId Attribute name
+     * @param attrs Attributes containing the new values
+     * @param values ArrayList containing values found so far
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    private ArrayList addAttributeValues(String attrId,
+                                         Attributes attrs,
+                                         ArrayList values)
+        throws NamingException{
+
+        if (containerLog.isTraceEnabled())
+            containerLog.trace("  retrieving values for attribute " + attrId);
+        if (attrId == null || attrs == null)
+            return values;
+        if (values == null)
+            values = new ArrayList();
+        Attribute attr = attrs.get(attrId);
+        if (attr == null)
+            return (values);
+        NamingEnumeration e = attr.getAll();
+        while(e.hasMore()) {
+            String value = (String)e.next();
+            values.add(value);
+        }
+        return values;
+    }
+
+
+    /**
+     * Close any open connection to the directory server for this Realm.
+     *
+     * @param context The directory context to be closed
+     */
+    protected void close(DirContext context) {
+
+        // Do nothing if there is no opened connection
+        if (context == null)
+            return;
+
+        // Close our opened connection
+        try {
+            if (containerLog.isDebugEnabled())
+                containerLog.debug("Closing directory context");
+            context.close();
+        } catch (NamingException e) {
+            containerLog.error(sm.getString("jndiRealm.close"), e);
+        }
+        this.context = null;
+
+    }
+
+
+    /**
+     * Return a short name for this Realm implementation.
+     */
+    protected String getName() {
+
+        return (name);
+
+    }
+
+
+    /**
+     * Return the password associated with the given principal's user name.
+     */
+    protected String getPassword(String username) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the Principal associated with the given user name.
+     */
+    protected Principal getPrincipal(String username) {
+
+        return (null);
+
+    }
+
+
+
+    /**
+     * Open (if necessary) and return a connection to the configured
+     * directory server for this Realm.
+     *
+     * @exception NamingException if a directory server error occurs
+     */
+    protected DirContext open() throws NamingException {
+
+        // Do nothing if there is a directory server connection already open
+        if (context != null)
+            return (context);
+
+        try {
+
+            // Ensure that we have a directory context available
+            context = new InitialDirContext(getDirectoryContextEnvironment());
+
+        } catch (Exception e) {
+
+            connectionAttempt = 1;
+
+            // log the first exception.
+            containerLog.warn(sm.getString("jndiRealm.exception"), e);
+
+            // Try connecting to the alternate url.
+            context = new InitialDirContext(getDirectoryContextEnvironment());
+
+        } finally {
+
+            // reset it in case the connection times out.
+            // the primary may come back.
+            connectionAttempt = 0;
+
+        }
+
+        return (context);
+
+    }
+
+    /**
+     * Create our directory context configuration.
+     *
+     * @return java.util.Hashtable the configuration for the directory context.
+     */
+    protected Hashtable getDirectoryContextEnvironment() {
+
+        Hashtable env = new Hashtable();
+
+        // Configure our directory context environment.
+        if (containerLog.isDebugEnabled() && connectionAttempt == 0)
+            containerLog.debug("Connecting to URL " + connectionURL);
+        else if (containerLog.isDebugEnabled() && connectionAttempt > 0)
+            containerLog.debug("Connecting to URL " + alternateURL);
+        env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
+        if (connectionName != null)
+            env.put(Context.SECURITY_PRINCIPAL, connectionName);
+        if (connectionPassword != null)
+            env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
+        if (connectionURL != null && connectionAttempt == 0)
+            env.put(Context.PROVIDER_URL, connectionURL);
+        else if (alternateURL != null && connectionAttempt > 0)
+            env.put(Context.PROVIDER_URL, alternateURL);
+        if (authentication != null)
+            env.put(Context.SECURITY_AUTHENTICATION, authentication);
+        if (protocol != null)
+            env.put(Context.SECURITY_PROTOCOL, protocol);
+        if (referrals != null)
+            env.put(Context.REFERRAL, referrals);
+        if (derefAliases != null)
+            env.put(JNDIRealm.DEREF_ALIASES, derefAliases);
+
+        return env;
+
+    }
+
+
+    /**
+     * Release our use of this connection so that it can be recycled.
+     *
+     * @param context The directory context to release
+     */
+    protected void release(DirContext context) {
+
+        ; // NO-OP since we are not pooling anything
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public void start() throws LifecycleException {
+
+        // Validate that we can open our connection
+        try {
+            open();
+        } catch (NamingException e) {
+            throw new LifecycleException(sm.getString("jndiRealm.open"), e);
+        }
+
+        // Perform normal superclass initialization
+        super.start();
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Perform normal superclass finalization
+        super.stop();
+
+        // Close any open directory server connection
+        close(this.context);
+
+    }
+
+    /**
+     * Given a string containing LDAP patterns for user locations (separated by
+     * parentheses in a pseudo-LDAP search string format -
+     * "(location1)(location2)", returns an array of those paths.  Real LDAP
+     * search strings are supported as well (though only the "|" "OR" type).
+     *
+     * @param userPatternString - a string LDAP search paths surrounded by
+     * parentheses
+     */
+    protected String[] parseUserPatternString(String userPatternString) {
+
+        if (userPatternString != null) {
+            ArrayList pathList = new ArrayList();
+            int startParenLoc = userPatternString.indexOf('(');
+            if (startParenLoc == -1) {
+                // no parens here; return whole thing
+                return new String[] {userPatternString};
+            }
+            int startingPoint = 0;
+            while (startParenLoc > -1) {
+                int endParenLoc = 0;
+                // weed out escaped open parens and parens enclosing the
+                // whole statement (in the case of valid LDAP search
+                // strings: (|(something)(somethingelse))
+                while ( (userPatternString.charAt(startParenLoc + 1) == '|') ||
+                        (startParenLoc != 0 && userPatternString.charAt(startParenLoc - 1) == '\\') ) {
+                    startParenLoc = userPatternString.indexOf("(", startParenLoc+1);
+                }
+                endParenLoc = userPatternString.indexOf(")", startParenLoc+1);
+                // weed out escaped end-parens
+                while (userPatternString.charAt(endParenLoc - 1) == '\\') {
+                    endParenLoc = userPatternString.indexOf(")", endParenLoc+1);
+                }
+                String nextPathPart = userPatternString.substring
+                    (startParenLoc+1, endParenLoc);
+                pathList.add(nextPathPart);
+                startingPoint = endParenLoc+1;
+                startParenLoc = userPatternString.indexOf('(', startingPoint);
+            }
+            return (String[])pathList.toArray(new String[] {});
+        }
+        return null;
+
+    }
+
+
+    /**
+     * Given an LDAP search string, returns the string with certain characters
+     * escaped according to RFC 2254 guidelines.
+     * The character mapping is as follows:
+     *     char ->  Replacement
+     *    ---------------------------
+     *     *  -> \2a
+     *     (  -> \28
+     *     )  -> \29
+     *     \  -> \5c
+     *     \0 -> \00
+     * @param inString string to escape according to RFC 2254 guidelines
+     * @return String the escaped/encoded result
+     */
+    protected String doRFC2254Encoding(String inString) {
+        StringBuffer buf = new StringBuffer(inString.length());
+        for (int i = 0; i < inString.length(); i++) {
+            char c = inString.charAt(i);
+            switch (c) {
+                case '\\':
+                    buf.append("\\5c");
+                    break;
+                case '*':
+                    buf.append("\\2a");
+                    break;
+                case '(':
+                    buf.append("\\28");
+                    break;
+                case ')':
+                    buf.append("\\29");
+                    break;
+                case '\0':
+                    buf.append("\\00");
+                    break;
+                default:
+                    buf.append(c);
+                    break;
+            }
+        }
+        return buf.toString();
+    }
+
+
+}
+
+// ------------------------------------------------------ Private Classes
+
+/**
+ * A private class representing a User
+ */
+class User {
+    String username = null;
+    String dn = null;
+    String password = null;
+    ArrayList roles = null;
+
+
+    User(String username, String dn, String password, ArrayList roles) {
+        this.username = username;
+        this.dn = dn;
+        this.password = password;
+        this.roles = roles;
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/realm/LocalStrings.properties
new file mode 100644
index 0000000..5f90f80
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/LocalStrings.properties
@@ -0,0 +1,72 @@
+# $Id$
+
+# language
+
+# package org.apache.catalina.realm
+
+jaasRealm.beginLogin=JAASRealm login requested for username "{0}" using LoginContext for application "{1}"
+jaasRealm.accountExpired=Username "{0}" NOT authenticated due to expired account
+jaasRealm.authenticateFailure=Username "{0}" NOT successfully authenticated
+jaasRealm.authenticateSuccess=Username "{0}" successfully authenticated as Principal "{1}" -- Subject was created too
+jaasRealm.credentialExpired=Username "{0}" NOT authenticated due to expired credential
+jaasRealm.failedLogin=Username "{0}" NOT authenticated due to failed login
+jaasRealm.loginException=Login exception authenticating username "{0}"
+jaasRealm.unexpectedError=Unexpected error
+jaasRealm.loginContextCreated=JAAS LoginContext created for username "{0}"
+jaasRealm.userPrincipalSuccess=Subject for username "{0}" returned user Principal "{1}"
+jaasRealm.userPrincipalFailure=Subject for username "{0}" did not return a valid user Principal
+jaasRealm.cachePrincipal=User Principal "{0}" cached; has {1} role Principal(s)
+jaasRealm.checkPrincipal=Checking Principal "{0}" [{1}]
+jaasRealm.userPrincipalSuccess=Principal "{0}" is a valid user class. We will use this as the user Principal.
+jaasRealm.userPrincipalFailure=No valid user Principal found
+jaasRealm.rolePrincipalAdd=Adding role Principal "{0}" to this user Principal''s roles
+jaasRealm.rolePrincipalSuccess={0} role Principal(s) found
+jaasRealm.rolePrincipalFailure=No valid role Principals found.
+jaasRealm.isInRole.start=Checking if user Principal "{0}" possesses role "{1}"
+jaasRealm.isInRole.noPrincipalOrRole=No roles Principals found. User Principal or Subject is null, or user Principal not in cache
+jaasRealm.isInRole.principalCached=User Principal has {0} role Principal(s)
+jaasRealm.isInRole.possessesRole=User Principal has a role Principal called "{0}"
+jaasRealm.isInRole.match=Matching role Principal found.
+jaasRealm.isInRole.noMatch=Matching role Principal NOT found.
+jaasCallback.digestpassword=Digested password "{0}" as "{1}"
+jaasCallback.username=Returned username "{0}"
+jaasCallback.password=Returned password "{0}"
+jdbcRealm.authenticateFailure=Username {0} NOT successfully authenticated
+jdbcRealm.authenticateSuccess=Username {0} successfully authenticated
+jdbcRealm.close=Exception closing database connection
+jdbcRealm.exception=Exception performing authentication
+jdbcRealm.getPassword.exception=Exception retrieving password for "{0}"
+jdbcRealm.getRoles.exception=Exception retrieving roles for "{0}"
+jdbcRealm.open=Exception opening database connection
+jndiRealm.authenticateFailure=Username {0} NOT successfully authenticated
+jndiRealm.authenticateSuccess=Username {0} successfully authenticated
+jndiRealm.close=Exception closing directory server connection
+jndiRealm.exception=Exception performing authentication
+jndiRealm.open=Exception opening directory server connection
+memoryRealm.authenticateFailure=Username {0} NOT successfully authenticated
+memoryRealm.authenticateSuccess=Username {0} successfully authenticated
+memoryRealm.loadExist=Memory database file {0} cannot be read
+memoryRealm.loadPath=Loading users from memory database file {0}
+memoryRealm.readXml=Exception while reading memory database file
+realmBase.algorithm=Invalid message digest algorithm {0} specified
+realmBase.alreadyStarted=This Realm has already been started
+realmBase.digest=Error digesting user credentials
+realmBase.forbidden=Access to the requested resource has been denied
+realmBase.hasRoleFailure=Username {0} does NOT have role {1}
+realmBase.hasRoleSuccess=Username {0} has role {1}
+realmBase.notAuthenticated=Configuration error:  Cannot perform access control without an authenticated principal
+realmBase.notStarted=This Realm has not yet been started
+realmBase.authenticateFailure=Username {0} NOT successfully authenticated
+realmBase.authenticateSuccess=Username {0} successfully authenticated
+userDatabaseRealm.authenticateError=Login configuration error authenticating username {0}
+userDatabaseRealm.lookup=Exception looking up UserDatabase under key {0}
+userDatabaseRealm.noDatabase=No UserDatabase component found under key {0}
+userDatabaseRealm.noEngine=No Engine component found in container hierarchy
+userDatabaseRealm.noGlobal=No global JNDI resources context found
+dataSourceRealm.authenticateFailure=Username {0} NOT successfully authenticated
+dataSourceRealm.authenticateSuccess=Username {0} successfully authenticated
+dataSourceRealm.close=Exception closing database connection
+dataSourceRealm.exception=Exception performing authentication
+dataSourceRealm.getPassword.exception=Exception retrieving password for "{0}"
+dataSourceRealm.getRoles.exception=Exception retrieving roles for "{0}"
+dataSourceRealm.open=Exception opening database connection
diff --git a/container/catalina/src/share/org/apache/catalina/realm/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/realm/LocalStrings_es.properties
new file mode 100644
index 0000000..105f348
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/LocalStrings_es.properties
@@ -0,0 +1,47 @@
+# $Id$
+
+# language es
+
+# package org.apache.catalina.realm
+
+jaasRealm.accountExpired=El usuario {0} NO ha sido autentificado porque ha expirado su cuenta
+jaasRealm.authenticatedSuccess=El usuario {0} ha sido autentificado con éxito
+jaasRealm.credentialExpired=El usuario {0} NO ha sido autentificado porque ha expirado su credencial
+jaasRealm.failedLogin=El usuario {0} NO ha sido autentificado porque ha fallado el login
+jaasRealm.loginException=Excepción de Login autenticando nombre de usuario {0}
+jaasRealm.unexpectedError=Error inesperado
+jdbcRealm.authenticateFailure=El usuario {0} NO ha sido autentificado correctamente
+jdbcRealm.authenticateSuccess=El usuario {0} ha sido autentificado correctamente
+jdbcRealm.close=Excepción al cerrar la conexión a la base de datos
+jdbcRealm.exception=Excepción al realizar la autentificación
+jdbcRealm.open=Excepción abriendo la conexión a la base de datos
+jndiRealm.authenticateFailure=Autentificación fallida para el usuario {0}
+jndiRealm.authenticateSuccess=Autentificación correcta para el usuario {0}
+jndiRealm.close=Excepción al cerrar la conexión al servidor de directorio
+jndiRealm.exception=Excepción al realizar la autentificación
+jndiRealm.open=Excepción al abrir la conexión al servidor de directorio
+memoryRealm.authenticateFailure=Autentificación fallida para el usuario {0}
+memoryRealm.authenticateSuccess=Autentificación correcta para el usuario {0}
+memoryRealm.loadExist=El fichero de usuarios {0} no ha podido ser leído
+memoryRealm.loadPath=Cargando los usuarios desde el fichero {0}
+memoryRealm.readXml=Excepción mientras se leía el fichero de usuarios
+realmBase.algorithm=El algoritmo resumen (digest) {0} es inválido
+realmBase.alreadyStarted=Este dominio ya ha sido inicializado
+realmBase.digest=Error procesando las credenciales del usuario
+realmBase.forbidden=El acceso al recurso pedido ha sido denegado
+realmBase.hasRoleFailure=El usuario {0} NO desempeña el papel de {1}
+realmBase.hasRoleSuccess=El usuario {0} desempeña el papel de {1}
+realmBase.notAuthenticated=Error de Configuración: No se pueden realizar funciones de control de acceso sin un principal autentificado
+realmBase.notStarted=Este dominio aún no ha sido inicializado
+realmBase.authenticateFailure=Nombre de usuario {0} NO autenticado con éxito
+realmBase.authenticateSuccess=Nombre de usuario {0} autenticado con éxito
+userDatabaseRealm.authenticateError=Error de configuración de Login autenticando nombre de usuario {0}
+userDatabaseRealm.lookup=Excepción buscando en Base de datos de Usuario mediante la clave {0}
+userDatabaseRealm.noDatabase=No se ha hallado componente de Base de datos de Usuario mediante la clave {0}
+userDatabaseRealm.noEngine=No se ha hallado componente de Motor en jerarquía de contenedor
+userDatabaseRealm.noGlobal=No se ha hallado contexto de recursos globales JNDI
+dataSourceRealm.authenticateFailure=Nombre de usuario {0} NO autenticado con éxito
+dataSourceRealm.authenticateSuccess=Nombre de usuario {0} autenticado con éxito
+dataSourceRealm.close=Excepción cerrando conexión a base de datos
+dataSourceRealm.exception=Excepción realizando autenticación
+dataSourceRealm.open=Excepción abriendo conexión a base de datos
diff --git a/container/catalina/src/share/org/apache/catalina/realm/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/realm/LocalStrings_fr.properties
new file mode 100644
index 0000000..46b3ba0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/LocalStrings_fr.properties
@@ -0,0 +1,41 @@
+# $Id$
+
+# language
+
+# package org.apache.catalina.realm
+
+jaasRealm.accountExpired=le nom d''utilisateur {0} N''A PAS été authentifié car le compte a expiré
+jaasRealm.authenticateSuccess=le nom d''utilisateur {0} a été authentifié avec succès
+jaasRealm.credentialExpired=le nom d''utilisateur {0} N''A PAS été authentifié car son crédit a expiré (expired credential)
+jaasRealm.failedLogin=le nom d''utilisateur {0} N''A PAS été authentifié car son contrôle d''accès (login) a échoué
+jaasRealm.loginException=Exception lors de l''authentification par login du nom d''utilisateur {0}
+jdbcRealm.authenticateFailure=le nom d''utilisateur {0} N''A PAS été authentifié
+jdbcRealm.authenticateSuccess=le nom d''utilisateur {0} a été authentifié avec succès
+jdbcRealm.close=Exception lors de la fermeture de la connexion à la base de données
+jdbcRealm.exception=Exception pendant le traitement de l''authentification
+jdbcRealm.open=Exception lors de l''ouverture de la base de données
+jndiRealm.authenticateFailure=Le nom d''utilisateur {0} N''A PAS été authentifié
+jndiRealm.authenticateSuccess=Le nom d''utilisateur {0} a été authentifié avec succès
+jndiRealm.close=Exception lors de la fermeture de la connexion au serveur d''accès (directory server)
+jndiRealm.exception=Exception pendant le traitement de l''authentification
+jndiRealm.open=Exception lors de l''ouverture de la connexion au serveur d''accès (directory server)
+memoryRealm.authenticateFailure=le nom d''utilisateur {0} N''A PAS été authentifié
+memoryRealm.authenticateSuccess=le nom d''utilisateur {0} a été authentifié avec succès
+memoryRealm.loadExist=Le fichier base de données mémoire (memory database) {0} ne peut être lu
+memoryRealm.loadPath=Chargement des utilisateurs depuis le fichier base de données mémoire (memory database) {0}
+memoryRealm.readXml=Exception lors de la lecture du fichier base de données mémoire (memory database)
+realmBase.algorithm=L''algorithme d''empreinte de message (message digest) {0} indiqué est invalide
+realmBase.alreadyStarted=Ce royaume (Realm) a déjà été démarré
+realmBase.digest=Erreur lors du traitement des empreintes (digest) des crédits utilisateur (user credentials)
+realmBase.forbidden=L''accès à la ressource demandée a été interdit
+realmBase.hasRoleFailure=Le nom d''utilisateur {0} N''A PAS de rôle {1}
+realmBase.hasRoleSuccess=Le nom d''utilisateur {0} a pour rôle {1}
+realmBase.notAuthenticated=Erreur de configuration:  Impossible de conduire un contrôle d''accès sans un authentifié principal (authenticated principal)
+realmBase.notStarted=Ce royaume (Realm) n''a pas encore été démarré
+realmBase.authenticateFailure=Le nom d''utilisateur {0} N''A PAS été authentifié
+realmBase.authenticateSuccess=Le nom d''utilisateur {0} a été authentifié avec succès
+userDatabaseRealm.authenticateError=Erreur de configuration du contrôle d''accès (login) lors de l''authentification du nom d''utilisateur {0}
+userDatabaseRealm.lookup=Exception lors de la recherche dans la base de données utilisateurs avec la clef {0}
+userDatabaseRealm.noDatabase=Aucun composant base de données utilisateurs trouvé pour la clef {0}
+userDatabaseRealm.noEngine=Aucun composant moteur (engine component) trouvé dans la hiérarchie des conteneurs
+userDatabaseRealm.noGlobal=Aucune ressource globale JNDI trouvée
diff --git a/container/catalina/src/share/org/apache/catalina/realm/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/realm/LocalStrings_ja.properties
new file mode 100644
index 0000000..7dcf209
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/LocalStrings_ja.properties
@@ -0,0 +1,47 @@
+# $Id$
+
+# language ja
+
+# package org.apache.catalina.realm
+
+jaasRealm.accountExpired=\u30e6\u30fc\u30b6\u540d {0} \u306f\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u671f\u9650\u304c\u5207\u308c\u3066\u3044\u308b\u305f\u3081\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u305b\u3093
+jaasRealm.authenticateSuccess=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f
+jaasRealm.credentialExpired=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a3c\u660e\u66f8\u306e\u671f\u9650\u304c\u5207\u308c\u305f\u305f\u3081\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u305b\u3093
+jaasRealm.failedLogin=\u30e6\u30fc\u30b6\u540d {0} \u306f\u30ed\u30b0\u30a4\u30f3\u306b\u5931\u6557\u3057\u305f\u305f\u3081\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f
+jaasRealm.loginException=\u30e6\u30fc\u30b6\u540d {0} \u306e\u8a8d\u8a3c\u4e2d\u306b\u30ed\u30b0\u30a4\u30f3\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+jaasRealm.unexpectedError=\u4e88\u6e2c\u3057\u306a\u3044\u30a8\u30e9\u30fc\u3067\u3059
+jdbcRealm.authenticateFailure=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+jdbcRealm.authenticateSuccess=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f
+jdbcRealm.close=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u30af\u30ed\u30fc\u30ba\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+jdbcRealm.exception=\u8a8d\u8a3c\u5b9f\u884c\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+jdbcRealm.open=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u30aa\u30fc\u30d7\u30f3\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+jndiRealm.authenticateFailure=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+jndiRealm.authenticateSuccess=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f
+jndiRealm.close=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30b5\u30fc\u30d0\u63a5\u7d9a\u30af\u30ed\u30fc\u30ba\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+jndiRealm.exception=\u8a8d\u8a3c\u5b9f\u884c\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+jndiRealm.open=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30b5\u30fc\u30d0\u63a5\u7d9a\u30aa\u30fc\u30d7\u30f3\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+memoryRealm.authenticateFailure=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+memoryRealm.authenticateSuccess=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f
+memoryRealm.loadExist=\u30e1\u30e2\u30ea\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb {0} \u3092\u8aad\u3080\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093
+memoryRealm.loadPath=\u30e1\u30e2\u30ea\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb {0} \u304b\u3089\u30e6\u30fc\u30b6\u3092\u30ed\u30fc\u30c9\u3057\u307e\u3059
+memoryRealm.readXml=\u30e1\u30e2\u30ea\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+realmBase.algorithm=\u7121\u52b9\u306a\u30e1\u30c3\u30bb\u30fc\u30b8\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0 {0} \u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059
+realmBase.alreadyStarted=\u3053\u306e\u30ec\u30eb\u30e0\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+realmBase.digest=\u30e6\u30fc\u30b6\u306e\u8a3c\u660e\u66f8\u306e\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30a8\u30e9\u30fc
+realmBase.forbidden=\u8981\u6c42\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9\u3078\u306e\u30a2\u30af\u30bb\u30b9\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f
+realmBase.hasRoleFailure=\u30e6\u30fc\u30b6\u540d {0} \u306f\u30ed\u30fc\u30eb {1} \u3092\u6301\u3063\u3066\u3044\u307e\u305b\u3093
+realmBase.hasRoleSuccess=\u30e6\u30fc\u30b6\u540d {0} \u306f\u30ed\u30fc\u30eb {1} \u3092\u6301\u3063\u3066\u3044\u307e\u3059
+realmBase.notAuthenticated=\u8a2d\u5b9a\u30a8\u30e9\u30fc:  \u8a8d\u8a3c\u3055\u308c\u305f\u4e3b\u4f53\u306a\u3057\u3067\u306f\u30a2\u30af\u30bb\u30b9\u5236\u5fa1\u304c\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093
+realmBase.notStarted=\u3053\u306e\u30ec\u30eb\u30e0\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+realmBase.authenticateFailure=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+realmBase.authenticateSuccess=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f
+userDatabaseRealm.authenticateError=\u30e6\u30fc\u30b6\u540d {0} \u3092\u8a8d\u8a3c\u4e2d\u306b\u30ed\u30b0\u30a4\u30f3\u8a2d\u5b9a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+userDatabaseRealm.lookup=\u30ad\u30fc {0} \u3067\u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u691c\u7d22\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+userDatabaseRealm.noDatabase=\u30ad\u30fc {0} \u3067\u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+userDatabaseRealm.noEngine=\u30b3\u30f3\u30c6\u30ca\u968e\u5c64\u4e2d\u306b\u30a8\u30f3\u30b8\u30f3\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+userDatabaseRealm.noGlobal=\u30b0\u30ed\u30fc\u30d0\u30eb\u306aJNDI\u30ea\u30bd\u30fc\u30b9\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+dataSourceRealm.authenticateFailure=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+dataSourceRealm.authenticateSuccess=\u30e6\u30fc\u30b6\u540d {0} \u306f\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f
+dataSourceRealm.close=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u3092\u30af\u30ed\u30fc\u30ba\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+dataSourceRealm.exception=\u8a8d\u8a3c\u3092\u5b9f\u884c\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+dataSourceRealm.open=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u3092\u30aa\u30fc\u30d7\u30f3\u4e2d\u306e\u4f8b\u5916\u3067\u3059
diff --git a/container/catalina/src/share/org/apache/catalina/realm/MemoryRealm.java b/container/catalina/src/share/org/apache/catalina/realm/MemoryRealm.java
new file mode 100644
index 0000000..0ca0e39
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/MemoryRealm.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import java.security.Principal;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.catalina.Container;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.digester.Digester;
+
+
+/**
+ * Simple implementation of <b>Realm</b> that reads an XML file to configure
+ * the valid users, passwords, and roles.  The file format (and default file
+ * location) are identical to those currently supported by Tomcat 3.X.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong>: It is assumed that the in-memory
+ * collection representing our defined users (and their roles) is initialized
+ * at application startup and never modified again.  Therefore, no thread
+ * synchronization is performed around accesses to the principals collection.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class MemoryRealm  extends RealmBase {
+
+    private static Log log = LogFactory.getLog(MemoryRealm.class);
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Container with which this Realm is associated.
+     */
+    private Container container = null;
+
+
+    /**
+     * The Digester we will use to process in-memory database files.
+     */
+    private static Digester digester = null;
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected final String info =
+        "org.apache.catalina.realm.MemoryRealm/1.0";
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+
+    protected static final String name = "MemoryRealm";
+
+
+    /**
+     * The pathname (absolute or relative to Catalina's current working
+     * directory) of the XML file containing our database information.
+     */
+    private String pathname = "conf/tomcat-users.xml";
+
+
+    /**
+     * The set of valid Principals for this Realm, keyed by user name.
+     */
+    private Map principals = new HashMap();
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started?
+     */
+    private boolean started = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Realm implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return info;
+
+    }
+
+
+    /**
+     * Return the pathname of our XML file containing user definitions.
+     */
+    public String getPathname() {
+
+        return pathname;
+
+    }
+
+
+    /**
+     * Set the pathname of our XML file containing user definitions.  If a
+     * relative pathname is specified, it is resolved against "catalina.base".
+     *
+     * @param pathname The new pathname
+     */
+    public void setPathname(String pathname) {
+
+        this.pathname = pathname;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, String credentials) {
+
+        GenericPrincipal principal =
+            (GenericPrincipal) principals.get(username);
+
+        boolean validated = false;
+        if (principal != null) {
+            if (hasMessageDigest()) {
+                // Hex hashes should be compared case-insensitive
+                validated = (digest(credentials)
+                             .equalsIgnoreCase(principal.getPassword()));
+            } else {
+                validated =
+                    (digest(credentials).equals(principal.getPassword()));
+            }
+        }
+
+        if (validated) {
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("memoryRealm.authenticateSuccess", username));
+            return (principal);
+        } else {
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("memoryRealm.authenticateFailure", username));
+            return (null);
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Add a new user to the in-memory database.
+     *
+     * @param username User's username
+     * @param password User's password (clear text)
+     * @param roles Comma-delimited set of roles associated with this user
+     */
+    void addUser(String username, String password, String roles) {
+
+        // Accumulate the list of roles for this user
+        ArrayList list = new ArrayList();
+        roles += ",";
+        while (true) {
+            int comma = roles.indexOf(',');
+            if (comma < 0)
+                break;
+            String role = roles.substring(0, comma).trim();
+            list.add(role);
+            roles = roles.substring(comma + 1);
+        }
+
+        // Construct and cache the Principal for this user
+        GenericPrincipal principal =
+            new GenericPrincipal(this, username, password, list);
+        principals.put(username, principal);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return a configured <code>Digester</code> to use for processing
+     * the XML input file, creating a new one if necessary.
+     */
+    protected synchronized Digester getDigester() {
+
+        if (digester == null) {
+            digester = new Digester();
+            digester.setValidating(false);
+            digester.addRuleSet(new MemoryRuleSet());
+        }
+        return (digester);
+
+    }
+
+
+    /**
+     * Return a short name for this Realm implementation.
+     */
+    protected String getName() {
+
+        return (name);
+
+    }
+
+
+    /**
+     * Return the password associated with the given principal's user name.
+     */
+    protected String getPassword(String username) {
+
+        GenericPrincipal principal =
+            (GenericPrincipal) principals.get(username);
+        if (principal != null) {
+            return (principal.getPassword());
+        } else {
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Return the Principal associated with the given user name.
+     */
+    protected Principal getPrincipal(String username) {
+
+        return (Principal) principals.get(username);
+
+    }
+
+    /**
+     * Returns the principals for this realm.
+     *
+     * @return The principals, keyed by user name (a String)
+     */
+    protected Map getPrincipals() {
+        return principals;
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public synchronized void start() throws LifecycleException {
+
+        // Validate the existence of our database file
+        File file = new File(pathname);
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"), pathname);
+        if (!file.exists() || !file.canRead())
+            throw new LifecycleException
+                (sm.getString("memoryRealm.loadExist",
+                              file.getAbsolutePath()));
+
+        // Load the contents of the database file
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("memoryRealm.loadPath",
+                             file.getAbsolutePath()));
+        Digester digester = getDigester();
+        try {
+            synchronized (digester) {
+                digester.push(this);
+                digester.parse(file);
+            }
+        } catch (Exception e) {
+            throw new LifecycleException("memoryRealm.readXml", e);
+        } finally {
+            digester.reset();
+        }
+
+        // Perform normal superclass initialization
+        super.start();
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public synchronized void stop() throws LifecycleException {
+
+        // Perform normal superclass finalization
+        super.stop();
+
+        // No shutdown activities required
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/MemoryRuleSet.java b/container/catalina/src/share/org/apache/catalina/realm/MemoryRuleSet.java
new file mode 100644
index 0000000..addb5c8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/MemoryRuleSet.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.apache.tomcat.util.digester.RuleSetBase;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p><strong>RuleSet</strong> for recognizing the users defined in the
+ * XML file processed by <code>MemoryRealm</code>.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class MemoryRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public MemoryRuleSet() {
+
+        this("tomcat-users/");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public MemoryRuleSet(String prefix) {
+
+        super();
+        this.namespaceURI = null;
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+
+        digester.addRule(prefix + "user", new MemoryUserRule());
+
+    }
+
+
+}
+
+
+/**
+ * Private class used when parsing the XML database file.
+ */
+final class MemoryUserRule extends Rule {
+
+
+    /**
+     * Construct a new instance of this <code>Rule</code>.
+     */
+    public MemoryUserRule() {
+    }
+
+
+    /**
+     * Process a <code>&lt;user&gt;</code> element from the XML database file.
+     *
+     * @param attributes The attribute list for this element
+     */
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+
+        String username = attributes.getValue("name");
+        if (username == null) {
+            username = attributes.getValue("username");
+        }
+        String password = attributes.getValue("password");
+        String roles = attributes.getValue("roles");
+
+        MemoryRealm realm =
+            (MemoryRealm) digester.peek(digester.getCount() - 1);
+        realm.addUser(username, password, roles);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/RealmBase.java b/container/catalina/src/share/org/apache/catalina/realm/RealmBase.java
new file mode 100644
index 0000000..1b9621d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/RealmBase.java
@@ -0,0 +1,1301 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+
+import javax.management.Attribute;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Realm;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.deploy.SecurityCollection;
+import org.apache.catalina.util.HexUtils;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.MD5Encoder;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+
+/**
+ * Simple implementation of <b>Realm</b> that reads an XML file to configure
+ * the valid users, passwords, and roles.  The file format (and default file
+ * location) are identical to those currently supported by Tomcat 3.X.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public abstract class RealmBase
+    implements Lifecycle, Realm, MBeanRegistration {
+
+    private static Log log = LogFactory.getLog(RealmBase.class);
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Container with which this Realm is associated.
+     */
+    protected Container container = null;
+
+
+    /**
+     * Container log
+     */
+    protected Log containerLog = null;
+
+
+    /**
+     * Digest algorithm used in storing passwords in a non-plaintext format.
+     * Valid values are those accepted for the algorithm name by the
+     * MessageDigest class, or <code>null</code> if no digesting should
+     * be performed.
+     */
+    protected String digest = null;
+
+    /**
+     * The encoding charset for the digest.
+     */
+    protected String digestEncoding = null;
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.realm.RealmBase/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The MessageDigest object for digesting user credentials (passwords).
+     */
+    protected MessageDigest md = null;
+
+
+    /**
+     * The MD5 helper object for this class.
+     */
+    protected static final MD5Encoder md5Encoder = new MD5Encoder();
+
+
+    /**
+     * MD5 message digest provider.
+     */
+    protected static MessageDigest md5Helper;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started?
+     */
+    protected boolean started = false;
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    /**
+     * Should we validate client certificate chains when they are presented?
+     */
+    protected boolean validate = true;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Container with which this Realm has been associated.
+     */
+    public Container getContainer() {
+
+        return (container);
+
+    }
+
+
+    /**
+     * Set the Container with which this Realm has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container) {
+
+        Container oldContainer = this.container;
+        this.container = container;
+        this.containerLog = container.getLogger();
+        support.firePropertyChange("container", oldContainer, this.container);
+
+    }
+
+    /**
+     * Return the digest algorithm  used for storing credentials.
+     */
+    public String getDigest() {
+
+        return digest;
+
+    }
+
+
+    /**
+     * Set the digest algorithm used for storing credentials.
+     *
+     * @param digest The new digest algorithm
+     */
+    public void setDigest(String digest) {
+
+        this.digest = digest;
+
+    }
+
+    /**
+     * Returns the digest encoding charset.
+     *
+     * @return The charset (may be null) for platform default
+     */
+    public String getDigestEncoding() {
+        return digestEncoding;
+    }
+
+    /**
+     * Sets the digest encoding charset.
+     *
+     * @param charset The charset (null for platform default)
+     */
+    public void setDigestEncoding(String charset) {
+        digestEncoding = charset;
+    }
+
+    /**
+     * Return descriptive information about this Realm implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return info;
+
+    }
+
+
+    /**
+     * Return the "validate certificate chains" flag.
+     */
+    public boolean getValidate() {
+
+        return (this.validate);
+
+    }
+
+
+    /**
+     * Set the "validate certificate chains" flag.
+     *
+     * @param validate The new validate certificate chains flag
+     */
+    public void setValidate(boolean validate) {
+
+        this.validate = validate;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, String credentials) {
+
+        String serverCredentials = getPassword(username);
+
+        boolean validated ;
+        if ( serverCredentials == null ) {
+            validated = false;
+        } else if(hasMessageDigest()) {
+            validated = serverCredentials.equalsIgnoreCase(digest(credentials));
+        } else {
+            validated = serverCredentials.equals(credentials);
+        }
+        if(! validated ) {
+            if (containerLog.isTraceEnabled()) {
+                containerLog.trace(sm.getString("realmBase.authenticateFailure",
+                                                username));
+            }
+            return null;
+        }
+        if (containerLog.isTraceEnabled()) {
+            containerLog.trace(sm.getString("realmBase.authenticateSuccess",
+                                            username));
+        }
+
+        return getPrincipal(username);
+    }
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, byte[] credentials) {
+
+        return (authenticate(username, credentials.toString()));
+
+    }
+
+
+    /**
+     * Return the Principal associated with the specified username, which
+     * matches the digest calculated using the given parameters using the
+     * method described in RFC 2069; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param clientDigest Digest which has been submitted by the client
+     * @param nOnce Unique (or supposedly unique) token which has been used
+     * for this request
+     * @param realm Realm name
+     * @param md5a2 Second MD5 digest used to calculate the digest :
+     * MD5(Method + ":" + uri)
+     */
+    public Principal authenticate(String username, String clientDigest,
+                                  String nOnce, String nc, String cnonce,
+                                  String qop, String realm,
+                                  String md5a2) {
+
+        String md5a1 = getDigest(username, realm);
+        if (md5a1 == null)
+            return null;
+        String serverDigestValue = md5a1 + ":" + nOnce + ":" + nc + ":"
+            + cnonce + ":" + qop + ":" + md5a2;
+
+        byte[] valueBytes = null;
+        if(getDigestEncoding() == null) {
+            valueBytes = serverDigestValue.getBytes();
+        } else {
+            try {
+                valueBytes = serverDigestValue.getBytes(getDigestEncoding());
+            } catch (UnsupportedEncodingException uee) {
+                log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
+                throw new IllegalArgumentException(uee.getMessage());
+            }
+        }
+
+        String serverDigest = null;
+        // Bugzilla 32137
+        synchronized(md5Helper) {
+            serverDigest = md5Encoder.encode(md5Helper.digest(valueBytes));
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("Digest : " + clientDigest + " Username:" + username 
+                    + " ClientSigest:" + clientDigest + " nOnce:" + nOnce 
+                    + " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop 
+                    + " realm:" + realm + "md5a2:" + md5a2 
+                    + " Server digest:" + serverDigest);
+        }
+        
+        if (serverDigest.equals(clientDigest))
+            return getPrincipal(username);
+        else
+            return null;
+    }
+
+
+
+    /**
+     * Return the Principal associated with the specified chain of X509
+     * client certificates.  If there is none, return <code>null</code>.
+     *
+     * @param certs Array of client certificates, with the first one in
+     *  the array being the certificate of the client itself.
+     */
+    public Principal authenticate(X509Certificate certs[]) {
+
+        if ((certs == null) || (certs.length < 1))
+            return (null);
+
+        // Check the validity of each certificate in the chain
+        if (log.isDebugEnabled())
+            log.debug("Authenticating client certificate chain");
+        if (validate) {
+            for (int i = 0; i < certs.length; i++) {
+                if (log.isDebugEnabled())
+                    log.debug(" Checking validity for '" +
+                        certs[i].getSubjectDN().getName() + "'");
+                try {
+                    certs[i].checkValidity();
+                } catch (Exception e) {
+                    if (log.isDebugEnabled())
+                        log.debug("  Validity exception", e);
+                    return (null);
+                }
+            }
+        }
+
+        // Check the existence of the client Principal in our database
+        return (getPrincipal(certs[0].getSubjectDN().getName()));
+
+    }
+
+    
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess() {
+    }
+
+
+    /**
+     * Return the SecurityConstraints configured to guard the request URI for
+     * this request, or <code>null</code> if there is no such constraint.
+     *
+     * @param request Request we are processing
+     * @param context Context the Request is mapped to
+     */
+    public SecurityConstraint [] findSecurityConstraints(Request request,
+                                                         Context context) {
+
+        ArrayList results = null;
+        // Are there any defined security constraints?
+        SecurityConstraint constraints[] = context.findConstraints();
+        if ((constraints == null) || (constraints.length == 0)) {
+            if (log.isDebugEnabled())
+                log.debug("  No applicable constraints defined");
+            return (null);
+        }
+
+        // Check each defined security constraint
+        String uri = request.getRequestPathMB().toString();
+        
+        String method = request.getMethod();
+        int i;
+        boolean found = false;
+        for (i = 0; i < constraints.length; i++) {
+            SecurityCollection [] collection = constraints[i].findCollections();
+                     
+            // If collection is null, continue to avoid an NPE
+            // See Bugzilla 30624
+            if ( collection == null) {
+		continue;
+            }
+
+            if (log.isDebugEnabled()) {
+                log.debug("  Checking constraint '" + constraints[i] +
+                    "' against " + method + " " + uri + " --> " +
+                    constraints[i].included(uri, method));
+	    }
+
+            for(int j=0; j < collection.length; j++){
+                String [] patterns = collection[j].findPatterns();
+ 
+                // If patterns is null, continue to avoid an NPE
+                // See Bugzilla 30624
+                if ( patterns == null) {
+		    continue;
+                }
+
+                for(int k=0; k < patterns.length; k++) {
+                    if(uri.equals(patterns[k])) {
+                        found = true;
+                        if(collection[j].findMethod(method)) {
+                            if(results == null) {
+                                results = new ArrayList();
+                            }
+                            results.add(constraints[i]);
+                        }
+                    }
+                }
+            }
+        }
+
+        if(found) {
+            return resultsToArray(results);
+        }
+
+        int longest = -1;
+
+        for (i = 0; i < constraints.length; i++) {
+            SecurityCollection [] collection = constraints[i].findCollections();
+            
+            // If collection is null, continue to avoid an NPE
+            // See Bugzilla 30624
+            if ( collection == null) {
+		continue;
+            }
+
+            if (log.isDebugEnabled()) {
+                log.debug("  Checking constraint '" + constraints[i] +
+                    "' against " + method + " " + uri + " --> " +
+                    constraints[i].included(uri, method));
+	    }
+
+            for(int j=0; j < collection.length; j++){
+                String [] patterns = collection[j].findPatterns();
+
+                // If patterns is null, continue to avoid an NPE
+                // See Bugzilla 30624
+                if ( patterns == null) {
+		    continue;
+                }
+
+                boolean matched = false;
+                int length = -1;
+                for(int k=0; k < patterns.length; k++) {
+                    String pattern = patterns[k];
+                    if(pattern.startsWith("/") && pattern.endsWith("/*") && 
+                       pattern.length() >= longest) {
+                            
+                        if(pattern.length() == 2) {
+                            matched = true;
+                            length = pattern.length();
+                        } else if(pattern.regionMatches(0,uri,0,
+                                                        pattern.length()-1) ||
+                                  (pattern.length()-2 == uri.length() &&
+                                   pattern.regionMatches(0,uri,0,
+                                                        pattern.length()-2))) {
+                            matched = true;
+                            length = pattern.length();
+                        }
+                    }
+                }
+                if(matched) {
+                    found = true;
+                    if(length > longest) {
+                        if(results != null) {
+                            results.clear();
+                        }
+                        longest = length;
+                    }
+                    if(collection[j].findMethod(method)) {
+                        if(results == null) {
+                            results = new ArrayList();
+                        }
+                        results.add(constraints[i]);
+                    }
+                }
+            }
+        }
+
+        if(found) {
+            return  resultsToArray(results);
+        }
+
+        for (i = 0; i < constraints.length; i++) {
+            SecurityCollection [] collection = constraints[i].findCollections();
+
+            // If collection is null, continue to avoid an NPE
+            // See Bugzilla 30624
+            if ( collection == null) {
+		continue;
+            }
+            
+            if (log.isDebugEnabled()) {
+                log.debug("  Checking constraint '" + constraints[i] +
+                    "' against " + method + " " + uri + " --> " +
+                    constraints[i].included(uri, method));
+	    }
+
+            boolean matched = false;
+            int pos = -1;
+            for(int j=0; j < collection.length; j++){
+                String [] patterns = collection[j].findPatterns();
+
+                // If patterns is null, continue to avoid an NPE
+                // See Bugzilla 30624
+                if ( patterns == null) {
+		    continue;
+                }
+
+                for(int k=0; k < patterns.length && !matched; k++) {
+                    String pattern = patterns[k];
+                    if(pattern.startsWith("*.")){
+                        int slash = uri.lastIndexOf("/");
+                        int dot = uri.lastIndexOf(".");
+                        if(slash >= 0 && dot > slash &&
+                           dot != uri.length()-1 &&
+                           uri.length()-dot == pattern.length()-1) {
+                            if(pattern.regionMatches(1,uri,dot,uri.length()-dot)) {
+                                matched = true;
+                                pos = j;
+                            }
+                        }
+                    }
+                }
+            }
+            if(matched) {
+                found = true;
+                if(collection[pos].findMethod(method)) {
+                    if(results == null) {
+                        results = new ArrayList();
+                    }
+                    results.add(constraints[i]);
+                }
+            }
+        }
+
+        if(found) {
+            return resultsToArray(results);
+        }
+
+        for (i = 0; i < constraints.length; i++) {
+            SecurityCollection [] collection = constraints[i].findCollections();
+            
+            // If collection is null, continue to avoid an NPE
+            // See Bugzilla 30624
+            if ( collection == null) {
+		continue;
+            }
+
+            if (log.isDebugEnabled()) {
+                log.debug("  Checking constraint '" + constraints[i] +
+                    "' against " + method + " " + uri + " --> " +
+                    constraints[i].included(uri, method));
+	    }
+
+            for(int j=0; j < collection.length; j++){
+                String [] patterns = collection[j].findPatterns();
+
+                // If patterns is null, continue to avoid an NPE
+                // See Bugzilla 30624
+                if ( patterns == null) {
+		    continue;
+                }
+
+                boolean matched = false;
+                for(int k=0; k < patterns.length && !matched; k++) {
+                    String pattern = patterns[k];
+                    if(pattern.equals("/")){
+                        matched = true;
+                    }
+                }
+                if(matched) {
+                    if(results == null) {
+                        results = new ArrayList();
+                    }                    
+                    results.add(constraints[i]);
+                }
+            }
+        }
+
+        if(results == null) {
+            // No applicable security constraint was found
+            if (log.isDebugEnabled())
+                log.debug("  No applicable constraint located");
+        }
+        return resultsToArray(results);
+    }
+ 
+    /**
+     * Convert an ArrayList to a SecurityContraint [].
+     */
+    private SecurityConstraint [] resultsToArray(ArrayList results) {
+        if(results == null) {
+            return null;
+        }
+        SecurityConstraint [] array = new SecurityConstraint[results.size()];
+        results.toArray(array);
+        return array;
+    }
+
+    
+    /**
+     * Perform access control based on the specified authorization constraint.
+     * Return <code>true</code> if this constraint is satisfied and processing
+     * should continue, or <code>false</code> otherwise.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraints Security constraint we are enforcing
+     * @param context The Context to which client of this class is attached.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean hasResourcePermission(Request request,
+                                         Response response,
+                                         SecurityConstraint []constraints,
+                                         Context context)
+        throws IOException {
+
+        if (constraints == null || constraints.length == 0)
+            return (true);
+
+        // Specifically allow access to the form login and form error pages
+        // and the "j_security_check" action
+        LoginConfig config = context.getLoginConfig();
+        if ((config != null) &&
+            (Constants.FORM_METHOD.equals(config.getAuthMethod()))) {
+            String requestURI = request.getRequestPathMB().toString();
+            String loginPage = config.getLoginPage();
+            if (loginPage.equals(requestURI)) {
+                if (log.isDebugEnabled())
+                    log.debug(" Allow access to login page " + loginPage);
+                return (true);
+            }
+            String errorPage = config.getErrorPage();
+            if (errorPage.equals(requestURI)) {
+                if (log.isDebugEnabled())
+                    log.debug(" Allow access to error page " + errorPage);
+                return (true);
+            }
+            if (requestURI.endsWith(Constants.FORM_ACTION)) {
+                if (log.isDebugEnabled())
+                    log.debug(" Allow access to username/password submission");
+                return (true);
+            }
+        }
+
+        // Which user principal have we already authenticated?
+        Principal principal = request.getUserPrincipal();
+        for(int i=0; i < constraints.length; i++) {
+            SecurityConstraint constraint = constraints[i];
+            String roles[] = constraint.findAuthRoles();
+            if (roles == null)
+                roles = new String[0];
+
+            if (constraint.getAllRoles())
+                return (true);
+
+            if (log.isDebugEnabled())
+                log.debug("  Checking roles " + principal);
+
+            if (roles.length == 0) {
+                if(constraint.getAuthConstraint()) {
+                    response.sendError
+                        (HttpServletResponse.SC_FORBIDDEN,
+                         sm.getString("realmBase.forbidden"));
+                    if( log.isDebugEnabled() )
+                        log.debug("No roles ");
+                    return (false); // No listed roles means no access at all
+                } else {
+                    if(log.isDebugEnabled())
+                        log.debug("Passing all access");
+                    return (true);
+                }
+            } else if (principal == null) {
+                if (log.isDebugEnabled())
+                    log.debug("  No user authenticated, cannot grant access");
+                response.sendError
+                    (HttpServletResponse.SC_FORBIDDEN,
+                     sm.getString("realmBase.notAuthenticated"));
+                return (false);
+            }
+
+
+            for (int j = 0; j < roles.length; j++) {
+                if (hasRole(principal, roles[j]))
+                    return (true);
+                if( log.isDebugEnabled() )
+                    log.debug( "No role found:  " + roles[j]);
+            }
+        }
+        // Return a "Forbidden" message denying access to this resource
+        response.sendError
+            (HttpServletResponse.SC_FORBIDDEN,
+             sm.getString("realmBase.forbidden"));
+        return (false);
+
+    }
+    
+    
+    /**
+     * Return <code>true</code> if the specified Principal has the specified
+     * security role, within the context of this Realm; otherwise return
+     * <code>false</code>.  This method can be overridden by Realm
+     * implementations, but the default is adequate when an instance of
+     * <code>GenericPrincipal</code> is used to represent authenticated
+     * Principals from this Realm.
+     *
+     * @param principal Principal for whom the role is to be checked
+     * @param role Security role to be checked
+     */
+    public boolean hasRole(Principal principal, String role) {
+
+        // Should be overriten in JAASRealm - to avoid pretty inefficient conversions
+        if ((principal == null) || (role == null) ||
+            !(principal instanceof GenericPrincipal))
+            return (false);
+
+        GenericPrincipal gp = (GenericPrincipal) principal;
+        if (!(gp.getRealm() == this)) {
+            if(log.isDebugEnabled())
+                log.debug("Different realm " + this + " " + gp.getRealm());//    return (false);
+        }
+        boolean result = gp.hasRole(role);
+        if (log.isDebugEnabled()) {
+            String name = principal.getName();
+            if (result)
+                log.debug(sm.getString("realmBase.hasRoleSuccess", name, role));
+            else
+                log.debug(sm.getString("realmBase.hasRoleFailure", name, role));
+        }
+        return (result);
+
+    }
+
+    
+    /**
+     * Enforce any user data constraint required by the security constraint
+     * guarding this request URI.  Return <code>true</code> if this constraint
+     * was not violated and processing should continue, or <code>false</code>
+     * if we have created a response already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraints Security constraint being checked
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean hasUserDataPermission(Request request,
+                                         Response response,
+                                         SecurityConstraint []constraints)
+        throws IOException {
+
+        // Is there a relevant user data constraint?
+        if (constraints == null || constraints.length == 0) {
+            if (log.isDebugEnabled())
+                log.debug("  No applicable security constraint defined");
+            return (true);
+        }
+        for(int i=0; i < constraints.length; i++) {
+            SecurityConstraint constraint = constraints[i];
+            String userConstraint = constraint.getUserConstraint();
+            if (userConstraint == null) {
+                if (log.isDebugEnabled())
+                    log.debug("  No applicable user data constraint defined");
+                return (true);
+            }
+            if (userConstraint.equals(Constants.NONE_TRANSPORT)) {
+                if (log.isDebugEnabled())
+                    log.debug("  User data constraint has no restrictions");
+                return (true);
+            }
+
+        }
+        // Validate the request against the user data constraint
+        if (request.getRequest().isSecure()) {
+            if (log.isDebugEnabled())
+                log.debug("  User data constraint already satisfied");
+            return (true);
+        }
+        // Initialize variables we need to determine the appropriate action
+        int redirectPort = request.getConnector().getRedirectPort();
+
+        // Is redirecting disabled?
+        if (redirectPort <= 0) {
+            if (log.isDebugEnabled())
+                log.debug("  SSL redirect is disabled");
+            response.sendError
+                (HttpServletResponse.SC_FORBIDDEN,
+                 request.getRequestURI());
+            return (false);
+        }
+
+        // Redirect to the corresponding SSL port
+        StringBuffer file = new StringBuffer();
+        String protocol = "https";
+        String host = request.getServerName();
+        // Protocol
+        file.append(protocol).append("://");
+        // Host with port
+        file.append(host).append(":").append(redirectPort);
+        // URI
+        file.append(request.getRequestURI());
+        String requestedSessionId = request.getRequestedSessionId();
+        if ((requestedSessionId != null) &&
+            request.isRequestedSessionIdFromURL()) {
+            file.append(";jsessionid=");
+            file.append(requestedSessionId);
+        }
+        String queryString = request.getQueryString();
+        if (queryString != null) {
+            file.append('?');
+            file.append(queryString);
+        }
+        if (log.isDebugEnabled())
+            log.debug("  Redirecting to " + file.toString());
+        response.sendRedirect(file.toString());
+        return (false);
+
+    }
+    
+    
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called before any of the public
+     * methods of this component are utilized.  It should also send a
+     * LifecycleEvent of type START_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started) {
+            if(log.isInfoEnabled())
+                log.info(sm.getString("realmBase.alreadyStarted"));
+            return;
+        }
+        if( !initialized ) {
+            init();
+        }
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Create a MessageDigest instance for credentials, if desired
+        if (digest != null) {
+            try {
+                md = MessageDigest.getInstance(digest);
+            } catch (NoSuchAlgorithmException e) {
+                throw new LifecycleException
+                    (sm.getString("realmBase.algorithm", digest), e);
+            }
+        }
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.  It should also send a LifecycleEvent
+     * of type STOP_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop()
+        throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started) {
+            if(log.isInfoEnabled())
+                log.info(sm.getString("realmBase.notStarted"));
+            return;
+        }
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Clean up allocated resources
+        md = null;
+        
+        destroy();
+    
+    }
+    
+    public void destroy() {
+    
+        // unregister this realm
+        if ( oname!=null ) {   
+            try {   
+                Registry.getRegistry(null, null).unregisterComponent(oname); 
+                if(log.isDebugEnabled())
+                    log.debug( "unregistering realm " + oname );   
+            } catch( Exception ex ) {   
+                log.error( "Can't unregister realm " + oname, ex);   
+            }      
+        }
+          
+    }
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Digest the password using the specified algorithm and
+     * convert the result to a corresponding hexadecimal string.
+     * If exception, the plain credentials string is returned.
+     *
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    protected String digest(String credentials)  {
+
+        // If no MessageDigest instance is specified, return unchanged
+        if (hasMessageDigest() == false)
+            return (credentials);
+
+        // Digest the user credentials and return as hexadecimal
+        synchronized (this) {
+            try {
+                md.reset();
+    
+                byte[] bytes = null;
+                if(getDigestEncoding() == null) {
+                    bytes = credentials.getBytes();
+                } else {
+                    try {
+                        bytes = credentials.getBytes(getDigestEncoding());
+                    } catch (UnsupportedEncodingException uee) {
+                        log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
+                        throw new IllegalArgumentException(uee.getMessage());
+                    }
+                }
+                md.update(bytes);
+
+                return (HexUtils.convert(md.digest()));
+            } catch (Exception e) {
+                log.error(sm.getString("realmBase.digest"), e);
+                return (credentials);
+            }
+        }
+
+    }
+
+    protected boolean hasMessageDigest() {
+        return !(md == null);
+    }
+
+    /**
+     * Return the digest associated with given principal's user name.
+     */
+    protected String getDigest(String username, String realmName) {
+        if (md5Helper == null) {
+            try {
+                md5Helper = MessageDigest.getInstance("MD5");
+            } catch (NoSuchAlgorithmException e) {
+                log.error("Couldn't get MD5 digest: ", e);
+                throw new IllegalStateException(e.getMessage());
+            }
+        }
+
+    	if (hasMessageDigest()) {
+    		// Use pre-generated digest
+    		return getPassword(username);
+    	}
+    	
+        String digestValue = username + ":" + realmName + ":"
+            + getPassword(username);
+
+        byte[] valueBytes = null;
+        if(getDigestEncoding() == null) {
+            valueBytes = digestValue.getBytes();
+        } else {
+            try {
+                valueBytes = digestValue.getBytes(getDigestEncoding());
+            } catch (UnsupportedEncodingException uee) {
+                log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
+                throw new IllegalArgumentException(uee.getMessage());
+            }
+        }
+
+        byte[] digest = null;
+        // Bugzilla 32137
+        synchronized(md5Helper) {
+            digest = md5Helper.digest(valueBytes);
+        }
+
+        return md5Encoder.encode(digest);
+    }
+
+
+    /**
+     * Return a short name for this Realm implementation, for use in
+     * log messages.
+     */
+    protected abstract String getName();
+
+
+    /**
+     * Return the password associated with the given principal's user name.
+     */
+    protected abstract String getPassword(String username);
+
+
+    /**
+     * Return the Principal associated with the given user name.
+     */
+    protected abstract Principal getPrincipal(String username);
+
+
+    // --------------------------------------------------------- Static Methods
+
+
+    /**
+     * Digest password using the algorithm especificied and
+     * convert the result to a corresponding hex string.
+     * If exception, the plain credentials string is returned
+     *
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     * @param algorithm Algorithm used to do the digest
+     * @param encoding Character encoding of the string to digest
+     */
+    public final static String Digest(String credentials, String algorithm,
+                                      String encoding) {
+
+        try {
+            // Obtain a new message digest with "digest" encryption
+            MessageDigest md =
+                (MessageDigest) MessageDigest.getInstance(algorithm).clone();
+
+            // encode the credentials
+            // Should use the digestEncoding, but that's not a static field
+            if (encoding == null) {
+                md.update(credentials.getBytes());
+            } else {
+                md.update(credentials.getBytes(encoding));                
+            }
+
+            // Digest the credentials and return as hexadecimal
+            return (HexUtils.convert(md.digest()));
+        } catch(Exception ex) {
+            log.error(ex);
+            return credentials;
+        }
+
+    }
+
+
+    /**
+     * Digest password using the algorithm especificied and
+     * convert the result to a corresponding hex string.
+     * If exception, the plain credentials string is returned
+     */
+    public static void main(String args[]) {
+
+        String encoding = null;
+        int firstCredentialArg = 2;
+        
+        if (args.length > 4 && args[2].equalsIgnoreCase("-e")) {
+            encoding = args[3];
+            firstCredentialArg = 4;
+        }
+        
+        if(args.length > firstCredentialArg && args[0].equalsIgnoreCase("-a")) {
+            for(int i=firstCredentialArg; i < args.length ; i++){
+                System.out.print(args[i]+":");
+                System.out.println(Digest(args[i], args[1], encoding));
+            }
+        } else {
+            System.out.println
+                ("Usage: RealmBase -a <algorithm> [-e <encoding>] <credentials>");
+        }
+
+    }
+
+
+    // -------------------- JMX and Registration  --------------------
+    protected String type;
+    protected String domain;
+    protected String host;
+    protected String path;
+    protected ObjectName oname;
+    protected ObjectName controller;
+    protected MBeanServer mserver;
+
+    public ObjectName getController() {
+        return controller;
+    }
+
+    public void setController(ObjectName controller) {
+        this.controller = controller;
+    }
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+
+        type=name.getKeyProperty("type");
+        host=name.getKeyProperty("host");
+        path=name.getKeyProperty("path");
+
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+
+    protected boolean initialized=false;
+    
+    public void init() {
+        if( initialized && container != null ) return;
+        
+        initialized=true;
+        if( container== null ) {
+            ObjectName parent=null;
+            // Register with the parent
+            try {
+                if( host == null ) {
+                    // global
+                    parent=new ObjectName(domain +":type=Engine");
+                } else if( path==null ) {
+                    parent=new ObjectName(domain +
+                            ":type=Host,host=" + host);
+                } else {
+                    parent=new ObjectName(domain +":j2eeType=WebModule,name=//" +
+                            host + path);
+                }
+                if( mserver.isRegistered(parent ))  {
+                    if(log.isDebugEnabled())
+                        log.debug("Register with " + parent);
+                    mserver.setAttribute(parent, new Attribute("realm", this));
+                }
+            } catch (Exception e) {
+                log.error("Parent not available yet: " + parent);  
+            }
+        }
+        
+        if( oname==null ) {
+            // register
+            try {
+                ContainerBase cb=(ContainerBase)container;
+                oname=new ObjectName(cb.getDomain()+":type=Realm" + cb.getContainerSuffix());
+                Registry.getRegistry(null, null).registerComponent(this, oname, null );
+                if(log.isDebugEnabled())
+                    log.debug("Register Realm "+oname);
+            } catch (Throwable e) {
+                log.error( "Can't register " + oname, e);
+            }
+        }
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/UserDatabaseRealm.java b/container/catalina/src/share/org/apache/catalina/realm/UserDatabaseRealm.java
new file mode 100644
index 0000000..2ea77db
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/UserDatabaseRealm.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.realm;
+
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.naming.Context;
+
+import org.apache.catalina.Group;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Role;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.User;
+import org.apache.catalina.UserDatabase;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.users.MemoryUser;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * <p>Implementation of {@link org.apache.catalina.Realm} that is based on an implementation of
+ * {@link UserDatabase} made available through the global JNDI resources
+ * configured for this instance of Catalina.  Set the <code>resourceName</code>
+ * parameter to the global JNDI resources name for the configured instance
+ * of <code>UserDatabase</code> that we should consult.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class UserDatabaseRealm
+    extends RealmBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>UserDatabase</code> we will use to authenticate users
+     * and identify associated roles.
+     */
+    protected UserDatabase database = null;
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected final String info =
+        "org.apache.catalina.realm.UserDatabaseRealm/1.0";
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String name = "UserDatabaseRealm";
+
+
+    /**
+     * The global JNDI name of the <code>UserDatabase</code> resource
+     * we will be utilizing.
+     */
+    protected String resourceName = "UserDatabase";
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Realm implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return info;
+
+    }
+
+
+    /**
+     * Return the global JNDI name of the <code>UserDatabase</code> resource
+     * we will be using.
+     */
+    public String getResourceName() {
+
+        return resourceName;
+
+    }
+
+
+    /**
+     * Set the global JNDI name of the <code>UserDatabase</code> resource
+     * we will be using.
+     *
+     * @param resourceName The new global JNDI name
+     */
+    public void setResourceName(String resourceName) {
+
+        this.resourceName = resourceName;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return <code>true</code> if the specified Principal has the specified
+     * security role, within the context of this Realm; otherwise return
+     * <code>false</code>. This implementation returns <code>true</code>
+     * if the <code>User</code> has the role, or if any <code>Group</code>
+     * that the <code>User</code> is a member of has the role. 
+     *
+     * @param principal Principal for whom the role is to be checked
+     * @param role Security role to be checked
+     */
+    public boolean hasRole(Principal principal, String role) {
+        if( principal instanceof GenericPrincipal) {
+            GenericPrincipal gp = (GenericPrincipal)principal;
+            if(gp.getUserPrincipal() instanceof User) {
+                principal = gp.getUserPrincipal();
+            }
+        }
+        if(! (principal instanceof User) ) {
+            //Play nice with SSO and mixed Realms
+            return super.hasRole(principal, role);
+        }
+        if("*".equals(role)) {
+            return true;
+        } else if(role == null) {
+            return false;
+        }
+        User user = (User)principal;
+        Role dbrole = database.findRole(role);
+        if(dbrole == null) {
+            return false; 
+        }
+        if(user.isInRole(dbrole)) {
+            return true;
+        }
+        Iterator groups = user.getGroups();
+        while(groups.hasNext()) {
+            Group group = (Group)groups.next();
+            if(group.isInRole(dbrole)) {
+                return true;
+            }
+        }
+        return false;
+    }
+		
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return a short name for this Realm implementation.
+     */
+    protected String getName() {
+
+        return (name);
+
+    }
+
+
+    /**
+     * Return the password associated with the given principal's user name.
+     */
+    protected String getPassword(String username) {
+
+        User user = database.findUser(username);
+
+        if (user == null) {
+            return null;
+        } 
+
+        return (user.getPassword());
+
+    }
+
+
+    /**
+     * Return the Principal associated with the given user name.
+     */
+    protected Principal getPrincipal(String username) {
+
+        User user = database.findUser(username);
+        if(user == null) {
+            return null;
+        }
+
+        List roles = new ArrayList();
+        Iterator uroles = user.getRoles();
+        while(uroles.hasNext()) {
+            Role role = (Role)uroles.next();
+            roles.add(role.getName());
+        }
+        Iterator groups = user.getGroups();
+        while(groups.hasNext()) {
+            Group group = (Group)groups.next();
+            uroles = user.getRoles();
+            while(uroles.hasNext()) {
+                Role role = (Role)uroles.next();
+                roles.add(role.getName());
+            }
+        }
+        return new GenericPrincipal(this, username, user.getPassword(), roles, user);
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public synchronized void start() throws LifecycleException {
+
+        try {
+            StandardServer server = (StandardServer) ServerFactory.getServer();
+            Context context = server.getGlobalNamingContext();
+            database = (UserDatabase) context.lookup(resourceName);
+        } catch (Throwable e) {
+            containerLog.error(sm.getString("userDatabaseRealm.lookup",
+                                            resourceName),
+                               e);
+            database = null;
+        }
+        if (database == null) {
+            throw new LifecycleException
+                (sm.getString("userDatabaseRealm.noDatabase", resourceName));
+        }
+
+        // Perform normal superclass initialization
+        super.start();
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public synchronized void stop() throws LifecycleException {
+
+        // Perform normal superclass finalization
+        super.stop();
+
+        // Release reference to our user database
+        database = null;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/realm/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/realm/mbeans-descriptors.xml
new file mode 100644
index 0000000..21eb3b7
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/mbeans-descriptors.xml
@@ -0,0 +1,298 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean         name="DataSourceRealm"
+            className="org.apache.catalina.mbeans.ClassNameMBean"
+          description="Implementation of Realm that works with any JNDI configured DataSource"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.DataSourceRealm">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="dataSourceName"
+          description="The JNDI named JDBC DataSource for your database"
+                 type="java.lang.String"/>
+
+    <attribute   name="digest"
+          description="Digest algorithm used in storing passwords in a
+                        non-plaintext format"
+                 type="java.lang.String"/>
+
+    <attribute   name="localDataSource"
+          description="Configures if the DataSource is local to the webapp"
+                 type="boolean"/>
+
+    <attribute   name="roleNameCol"
+          description="The column in the user role table that names a role"
+                 type="java.lang.String"/>
+
+    <attribute   name="userCredCol"
+          description="The column in the user table that holds the user's
+                        credentials"
+                 type="java.lang.String"/>
+
+    <attribute   name="userNameCol"
+          description="The column in the user table that holds the user's
+                        username"
+                 type="java.lang.String"/>
+
+    <attribute   name="userRoleTable"
+          description="The table that holds the relation between user's and
+                        roles"
+                 type="java.lang.String"/>
+
+    <attribute   name="userTable"
+          description="The table that holds user data"
+                 type="java.lang.String"/>
+
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+
+  </mbean>
+
+  <mbean         name="JAASRealm"
+          description="Implmentation of Realm that authenticates users via the
+                       Java Authentication and Authorization Service (JAAS)"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.JAASRealm">
+
+    <attribute   name="appName"
+          description="The application name passed to the JAAS LoginContext,
+                       which uses it to select the set of relevant
+                       LoginModules"
+                 type="java.lang.String"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="digest"
+          description="Digest algorithm used in storing passwords in a
+                       non-plaintext format"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleClassNames"
+          description="Comma-delimited list of javax.security.Principal classes
+                       that represent security roles"
+                 type="java.lang.String"/>
+
+    <attribute   name="userClassNames"
+          description="Comma-delimited list of javax.security.Principal classes
+                       that represent individual users"
+                 type="java.lang.String"/>
+
+    <attribute   name="validate"
+          description="Should we validate client certificate chains when they
+                       are presented?"
+                 type="java.lang.String"/>
+
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+
+
+  <mbean         name="JDBCRealm"
+          description="Implementation of Realm that works with any JDBC
+                       supported database"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.JDBCRealm">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="connectionName"
+          description="The connection username to use when trying to connect to
+                       the database"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionPassword"
+          description="The connection URL to use when trying to connect to the
+                       database"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionURL"
+          description="The connection URL to use when trying to connect to the
+                       database"
+                 type="java.lang.String"/>
+
+    <attribute   name="digest"
+          description="Digest algorithm used in storing passwords in a
+                       non-plaintext format"
+                 type="java.lang.String"/>
+
+    <attribute   name="driverName"
+          description="The JDBC driver to use"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleNameCol"
+          description="The column in the user role table that names a role"
+                 type="java.lang.String"/>
+
+    <attribute   name="userCredCol"
+          description="The column in the user table that holds the user's
+                       credentials"
+                 type="java.lang.String"/>
+
+    <attribute   name="userNameCol"
+          description="The column in the user table that holds the user's
+                       username"
+                 type="java.lang.String"/>
+
+    <attribute   name="userRoleTable"
+          description="The table that holds the relation between user's and
+                       roles"
+                 type="java.lang.String"/>
+
+    <attribute   name="userTable"
+          description="The table that holds user data"
+                 type="java.lang.String"/>
+
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+
+  <mbean         name="JNDIRealm"
+          description="Implementation of Realm that works with a directory
+                       server accessed via the Java Naming and Directory
+                       Interface (JNDI) APIs"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.JNDIRealm">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="connectionName"
+          description="The connection username for the server we will contact"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionPassword"
+          description="The connection password for the server we will contact"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionURL"
+          description="The connection URL for the server we will contact"
+                 type="java.lang.String"/>
+
+    <attribute   name="contextFactory"
+          description="The JNDI context factory for this Realm"
+                 type="java.lang.String"/>
+
+    <attribute   name="digest"
+          description="Digest algorithm used in storing passwords in a
+                       non-plaintext format"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleBase"
+          description="The base element for role searches"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleName"
+          description="The name of the attribute containing roles held elsewhere"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleSearch"
+          description="The message format used to select roles for a user"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleSubtree"
+          description="Should we search the entire subtree for matching
+                       memberships?"
+                 type="boolean"/>
+
+    <attribute   name="userBase"
+          description="The base element for user searches"
+                 type="java.lang.String"/>
+
+    <attribute   name="userPassword"
+          description="The attribute name used to retrieve the user password"
+                 type="java.lang.String"/>
+
+    <attribute   name="userPattern"
+          description="The message format used to select a user"
+                 type="java.lang.String"/>
+
+     <attribute   name="userRoleName"
+          description="The name of the attribute in the user's entry containing
+                       roles for that user"
+                 type="java.lang.String"/>
+
+   <attribute   name="userSearch"
+         description="The message format used to search for a user"
+                type="java.lang.String"/>
+
+    <attribute   name="userSubtree"
+          description="Should we search the entire subtree for matching
+                       users?"
+                 type="boolean"/>
+
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+
+  <mbean         name="MemoryRealm"
+          description="Simple implementation of Realm that reads an XML file to
+                       configure the valid users, passwords, and roles"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.MemoryRealm">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="pathname"
+          description="The pathname of the XML file containing our database
+                       information"
+                 type="java.lang.String"/>
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+
+  </mbean>
+
+  <mbean         name="UserDatabaseRealm"
+          description="Realm connected to a UserDatabase as a global JNDI
+                       resource"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.UserDatabaseRealm">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="resourceName"
+          description="The global JNDI name of the UserDatabase resource to use"
+                 type="java.lang.String"/>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/realm/package.html b/container/catalina/src/share/org/apache/catalina/realm/package.html
new file mode 100644
index 0000000..f6efcfe
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/realm/package.html
@@ -0,0 +1,64 @@
+<body>
+
+<p>This package contains <code>Realm</code> implementations for the
+various supported realm technologies for authenticating users and
+identifying their associated roles.  The <code>Realm</code> that is
+associated with a web application's <code>Context</code> (or a hierarchically
+superior Container) is used to resolve authentication and role presence
+questions when a web application uses container managed security as described
+in the Servlet API Specification, version 2.2.</p>
+
+<p>The implementations share a common base class that supports basic
+functionality for all of the standard <code>Realm</code> implementations,
+and can be configured by setting the following properties (default values
+are in square brackets):</p>
+<ul>
+<li><b>debug</b> - Debugging detail level for this component. [0]</li>
+</ul>
+
+<p>The standard <code>Realm</code> implementations that are currently
+available include the following (with additional configuration properties
+as specified):</p>
+<ul>
+<li><b>JDBCRealm</b> - Implementation of <code>Realm</code> that operates
+    from data stored in a relational database that is accessed via a JDBC
+    driver.  The name of the driver, database connection information, and
+    the names of the relevant tables and columns are configured with the
+    following additional properties:
+    <ul>
+    <li><b>connectionURL</b> - The URL to use when connecting to this database.
+        [REQUIRED - NO DEFAULT]</li>
+    <li><b>driverName</b> - Fully qualified Java class name of the JDBC driver
+        to be used.  [REQUIRED - NO DEFAULT]</li>
+    <li><b>roleNameCol</b> - Name of the database column that contains role
+        names.  [REQUIRED - NO DEFAULT]</li>
+    <li><b>userCredCol</b> - Name of the database column that contains the
+        user's credentials (i.e. password) in cleartext.  [REQUIRED -
+        NO DEFAULT]</li>
+    <li><b>userNameCol</b> - Name of the database column that contains the
+        user's logon username.  [REQUIRED - NO DEFAULT]</li>
+    <li><b>userRoleTable</b> - Name of the database table containing user
+        role information.  This table must include the columns specified by
+        the <code>userNameCol</code> and <code>roleNameCol</code> properties.
+        [REQUIRED - NO DEFAULT]</li>
+    <li><b>userTable</b> - Name of the database table containing user
+        information.  This table must include the columns specified by the
+        <code>userNameCol</code> and <code>userCredCol</code> properties.
+        [REQUIRED - NO DEFAULT]</li>
+    </ul>
+    </li>
+<li><b>MemoryRealm</b> - Implementation of <code>Realm</code> that uses the
+    contents of a simple XML file (<code>conf/tomcat-users.xml</code>) as the
+    list of valid users and their roles.  This implementation is primarily to
+    demonstrate that the authentication technology functions correctly, and is
+    not anticipated as adequate for general purpose use.  This component
+    supports the following additional properties:
+    <ul>
+    <li><b>pathname</b> - Pathname of the XML file containing our user and
+        role information.  If a relative pathname is specified, it is resolved
+        against the pathname specified by the "catalina.home" system property.
+        [conf/tomcat-users.xml]</li>
+    </ul>
+</ul>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/catalina/security/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/security/LocalStrings.properties
new file mode 100644
index 0000000..443fd39
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/security/LocalStrings.properties
@@ -0,0 +1,2 @@
+SecurityUtil.doAsPrivilege=An exception occurs when running the PrivilegedExceptionAction block.
+
diff --git a/container/catalina/src/share/org/apache/catalina/security/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/security/LocalStrings_es.properties
new file mode 100644
index 0000000..a05aaf1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/security/LocalStrings_es.properties
@@ -0,0 +1,2 @@
+SecurityUtil.doAsPrivilege=Ha tenido lugar una excepción al ejecutar el bloque PrivilegedExceptionAction.
+
diff --git a/container/catalina/src/share/org/apache/catalina/security/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/security/LocalStrings_fr.properties
new file mode 100644
index 0000000..d199035
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/security/LocalStrings_fr.properties
@@ -0,0 +1,2 @@
+SecurityUtil.doAsPrivilege=Une exception s''est produite lors de l''exécution du bloc "PrivilegedExceptionAction".
+
diff --git a/container/catalina/src/share/org/apache/catalina/security/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/security/LocalStrings_ja.properties
new file mode 100644
index 0000000..4e4af30
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/security/LocalStrings_ja.properties
@@ -0,0 +1,2 @@
+SecurityUtil.doAsPrivilege=PrivilegedExceptionAction\u30d6\u30ed\u30c3\u30af\u3092\u5b9f\u884c\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
+
diff --git a/container/catalina/src/share/org/apache/catalina/security/SecurityClassLoad.java b/container/catalina/src/share/org/apache/catalina/security/SecurityClassLoad.java
new file mode 100644
index 0000000..7b20b52
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/security/SecurityClassLoad.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.security;
+
+/**
+ * Static class used to preload java classes when using the
+ * Java SecurityManager so that the defineClassInPackage
+ * RuntimePermission does not trigger an AccessControlException.
+ *
+ * @author Glenn L. Nielsen
+ * @author Jean-Francois Arcand
+ * @version $Revision$ $Date$
+ */
+
+public final class SecurityClassLoad {
+
+    public static void securityClassLoad(ClassLoader loader)
+        throws Exception {
+
+        if( System.getSecurityManager() == null ){
+            return;
+        }
+        
+        loadCorePackage(loader);
+        loadLoaderPackage(loader);
+        loadSessionPackage(loader);
+        loadUtilPackage(loader);
+        loadJavaxPackage(loader);
+        loadCoyotePackage(loader);        
+        loadHttp11Package(loader);        
+    }
+    
+    
+    private final static void loadCorePackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.";
+        loader.loadClass
+            (basePackage +
+             "core.ApplicationContextFacade$1");
+        loader.loadClass
+            (basePackage +
+             "core.ApplicationDispatcher$PrivilegedForward");
+        loader.loadClass
+            (basePackage +
+             "core.ApplicationDispatcher$PrivilegedInclude");
+        loader.loadClass
+            (basePackage +
+             "core.ContainerBase$PrivilegedAddChild");
+        loader.loadClass
+            (basePackage +
+             "core.StandardWrapper$1");
+    }
+    
+    
+    private final static void loadLoaderPackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.";
+        loader.loadClass
+            (basePackage +
+             "loader.WebappClassLoader$PrivilegedFindResource");
+    }
+    
+    
+    private final static void loadSessionPackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.";
+        loader.loadClass
+            (basePackage + "session.StandardSession");
+        loader.loadClass
+            (basePackage +
+             "session.StandardSession$1");
+        loader.loadClass
+            (basePackage +
+             "session.StandardManager$PrivilegedDoUnload");
+    }
+    
+    
+    private final static void loadUtilPackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.";
+        loader.loadClass
+            (basePackage + "util.URL");
+        loader.loadClass(basePackage + "util.Enumerator");
+    }
+    
+    
+    private final static void loadJavaxPackage(ClassLoader loader)
+        throws Exception {
+        loader.loadClass("javax.servlet.http.Cookie");
+    }
+    
+
+    private final static void loadHttp11Package(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.coyote.http11.";
+        loader.loadClass(basePackage + "Http11Processor$1");
+        loader.loadClass(basePackage + "InternalOutputBuffer$1");
+        loader.loadClass(basePackage + "InternalOutputBuffer$2");
+    }
+    
+    
+    private final static void loadCoyotePackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.connector.";
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetAttributePrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetParameterMapPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetRequestDispatcherPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetParameterPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetParameterNamesPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetParameterValuePrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetCharacterEncodingPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetHeadersPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetHeaderNamesPrivilegedAction");  
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetCookiesPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetLocalePrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetLocalesPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "ResponseFacade$SetContentTypePrivilegedAction");
+        loader.loadClass
+            (basePackage + 
+             "ResponseFacade$DateHeaderPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetSessionPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "ResponseFacade$1");
+        loader.loadClass
+            (basePackage +
+             "OutputBuffer$1");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$1");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$2");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$3");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$4");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$5");
+        loader.loadClass
+            (basePackage +
+             "InputBuffer$1");
+        loader.loadClass
+            (basePackage +
+             "Response$1");
+        loader.loadClass
+            (basePackage +
+             "Response$2");
+        loader.loadClass
+            (basePackage +
+             "Response$3");
+    }
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/security/SecurityConfig.java b/container/catalina/src/share/org/apache/catalina/security/SecurityConfig.java
new file mode 100644
index 0000000..b066acd
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/security/SecurityConfig.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.security;
+
+import java.security.Security;
+import org.apache.catalina.startup.CatalinaProperties;
+
+/**
+ * Util class to protect Catalina against package access and insertion.
+ * The code are been moved from Catalina.java
+ * @author the Catalina.java authors
+ * @author Jean-Francois Arcand
+ */
+public final class SecurityConfig{
+    private static SecurityConfig singleton = null;
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( SecurityConfig.class );
+
+    
+    private final static String PACKAGE_ACCESS =  "sun.,"
+                                                + "org.apache.catalina." 
+                                                + ",org.apache.jasper."
+                                                + ",org.apache.coyote."
+                                                + ",org.apache.tomcat.";
+    
+    private final static String PACKAGE_DEFINITION= "java.,sun."
+                                                + ",org.apache.catalina." 
+                                                + ",org.apache.coyote."
+                                                + ",org.apache.tomcat."
+                                                + ",org.apache.jasper.";
+    /**
+     * List of protected package from conf/catalina.properties
+     */
+    private String packageDefinition;
+    
+    
+    /**
+     * List of protected package from conf/catalina.properties
+     */
+    private String packageAccess; 
+    
+    
+    /**
+     * Create a single instance of this class.
+     */
+    private SecurityConfig(){  
+        try{
+            packageDefinition = CatalinaProperties.getProperty("package.definition");
+            packageAccess = CatalinaProperties.getProperty("package.access");
+        } catch (java.lang.Exception ex){
+            if (log.isDebugEnabled()){
+                log.debug("Unable to load properties using CatalinaProperties", ex); 
+            }            
+        }
+    }
+    
+    
+    /**
+     * Returns the singleton instance of that class.
+     * @return an instance of that class.
+     */
+    public static SecurityConfig newInstance(){
+        if (singleton == null){
+            singleton = new SecurityConfig();
+        }
+        return singleton;
+    }
+    
+    
+    /**
+     * Set the security package.access value.
+     */
+    public void setPackageAccess(){
+        // If catalina.properties is missing, protect all by default.
+        if (packageAccess == null){
+            setSecurityProperty("package.access", PACKAGE_ACCESS);   
+        } else {
+            setSecurityProperty("package.access", packageAccess);   
+        }
+    }
+    
+    
+    /**
+     * Set the security package.definition value.
+     */
+     public void setPackageDefinition(){
+        // If catalina.properties is missing, protect all by default.
+         if (packageDefinition == null){
+            setSecurityProperty("package.definition", PACKAGE_DEFINITION);
+         } else {
+            setSecurityProperty("package.definition", packageDefinition);
+         }
+    }
+     
+     
+    /**
+     * Set the proper security property
+     * @param properties the package.* property.
+     */
+    private final void setSecurityProperty(String properties, String packageList){
+        if (System.getSecurityManager() != null){
+            String definition = Security.getProperty(properties);
+            if( definition != null && definition.length() > 0 ){
+                definition += ",";
+            }
+
+            Security.setProperty(properties,
+                // FIX ME package "javax." was removed to prevent HotSpot
+                // fatal internal errors
+                definition + packageList);      
+        }
+    }
+    
+    
+}
+
+
+
+
diff --git a/container/catalina/src/share/org/apache/catalina/security/SecurityUtil.java b/container/catalina/src/share/org/apache/catalina/security/SecurityUtil.java
new file mode 100644
index 0000000..a287051
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/security/SecurityUtil.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.security;
+
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+
+import javax.security.auth.Subject;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.util.StringManager;
+/**
+ * This utility class associates a <code>Subject</code> to the current 
+ * <code>AccessControlContext</code>. When a <code>SecurityManager</code> is
+ * used, * the container will always associate the called thread with an 
+ * AccessControlContext * containing only the principal of the requested
+ * Servlet/Filter.
+ *
+ * This class uses reflection to invoke the invoke methods.
+ *
+ * @author Jean-Francois Arcand
+ */
+
+public final class SecurityUtil{
+    
+    private final static int INIT= 0;
+    private final static int SERVICE = 1;
+    private final static int DOFILTER = 1;
+    private final static int DESTROY = 2;
+    
+    private final static String INIT_METHOD = "init";
+    private final static String DOFILTER_METHOD = "doFilter";
+    private final static String SERVICE_METHOD = "service";
+    private final static String DESTROY_METHOD = "destroy";
+   
+    /**
+     * Cache every object for which we are creating method on it.
+     */
+    private static HashMap objectCache = new HashMap();
+        
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( SecurityUtil.class );
+    
+    private static String PACKAGE = "org.apache.catalina.security";
+    
+    private static boolean packageDefinitionEnabled =  
+         (System.getProperty("package.definition") == null && 
+           System.getProperty("package.access")  == null) ? false : true;
+    
+    /**
+     * The string resources for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(PACKAGE);    
+    
+    
+    /**
+     * Perform work as a particular </code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     */
+    public static void doAsPrivilege(final String methodName, 
+                                     final Servlet targetObject) throws java.lang.Exception{
+         doAsPrivilege(methodName, targetObject, null, null, null);                                
+    }
+
+    
+    /**
+     * Perform work as a particular </code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     * @param targetType <code>Class</code> array used to instanciate a i
+     * <code>Method</code> object.
+     * @param targetArguments <code>Object</code> array contains the runtime 
+     * parameters instance.
+     */
+    public static void doAsPrivilege(final String methodName, 
+                                     final Servlet targetObject, 
+                                     final Class[] targetType,
+                                     final Object[] targetArguments) 
+        throws java.lang.Exception{    
+
+         doAsPrivilege(methodName, 
+                       targetObject, 
+                       targetType, 
+                       targetArguments, 
+                       null);                                
+    }
+    
+    
+    /**
+     * Perform work as a particular </code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     * @param targetType <code>Class</code> array used to instanciate a 
+     * <code>Method</code> object.
+     * @param targetArguments <code>Object</code> array contains the 
+     * runtime parameters instance.
+     * @param principal the <code>Principal</code> to which the security 
+     * privilege apply..
+     */    
+    public static void doAsPrivilege(final String methodName, 
+                                     final Servlet targetObject, 
+                                     final Class[] targetType,
+                                     final Object[] targetArguments,
+                                     Principal principal) 
+        throws java.lang.Exception{
+
+        Method method = null;
+        Method[] methodsCache = null;
+        if(objectCache.containsKey(targetObject)){
+            methodsCache = (Method[])objectCache.get(targetObject);
+            method = findMethod(methodsCache, methodName);
+            if (method == null){
+                method = createMethodAndCacheIt(methodsCache,
+                                                methodName,
+                                                targetObject,
+                                                targetType);
+            }
+        } else {
+            method = createMethodAndCacheIt(methodsCache,
+                                            methodName,
+                                            targetObject,
+                                            targetType);                     
+        }
+
+        execute(method, targetObject, targetArguments, principal);
+    }
+ 
+    
+    /**
+     * Perform work as a particular </code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Filter</code> on which the method will 
+     * be called.
+     */    
+    public static void doAsPrivilege(final String methodName, 
+                                     final Filter targetObject) 
+        throws java.lang.Exception{
+
+         doAsPrivilege(methodName, targetObject, null, null);                                
+    }
+ 
+    
+    /**
+     * Perform work as a particular </code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Filter</code> on which the method will 
+     * be called.
+     * @param targetType <code>Class</code> array used to instanciate a
+     * <code>Method</code> object.
+     * @param targetArguments <code>Object</code> array contains the 
+     * runtime parameters instance.
+     */    
+    public static void doAsPrivilege(final String methodName, 
+                                     final Filter targetObject, 
+                                     final Class[] targetType,
+                                     final Object[] targetArguments) 
+        throws java.lang.Exception{
+        Method method = null;
+
+        Method[] methodsCache = null;
+        if(objectCache.containsKey(targetObject)){
+            methodsCache = (Method[])objectCache.get(targetObject);
+            method = findMethod(methodsCache, methodName);
+            if (method == null){
+                method = createMethodAndCacheIt(methodsCache,
+                                                methodName,
+                                                targetObject,
+                                                targetType);
+            }
+        } else {
+            method = createMethodAndCacheIt(methodsCache,
+                                            methodName,
+                                            targetObject,
+                                            targetType);                     
+        }
+
+        execute(method, targetObject, targetArguments, null);
+    }
+    
+    
+    /**
+     * Perform work as a particular </code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     * @param targetArguments <code>Object</code> array contains the 
+     * runtime parameters instance.
+     * @param principal the <code>Principal</code> to which the security 
+     * privilege applies
+     */    
+    private static void execute(final Method method,
+                                final Object targetObject, 
+                                final Object[] targetArguments,
+                                Principal principal) 
+        throws java.lang.Exception{
+       
+        try{   
+            Subject subject = null;
+            PrivilegedExceptionAction pea = new PrivilegedExceptionAction(){
+                    public Object run() throws Exception{
+                       method.invoke(targetObject, targetArguments);
+                       return null;
+                    }
+            };
+
+            // The first argument is always the request object
+            if (targetArguments != null 
+                    && targetArguments[0] instanceof HttpServletRequest){
+                HttpServletRequest request = 
+                    (HttpServletRequest)targetArguments[0];
+
+                boolean hasSubject = false;
+                HttpSession session = request.getSession(false);
+                if (session != null){
+                    subject = 
+                        (Subject)session.getAttribute(Globals.SUBJECT_ATTR);
+                    hasSubject = (subject != null);
+                }
+
+                if (subject == null){
+                    subject = new Subject();
+                    
+                    if (principal != null){
+                        subject.getPrincipals().add(principal);
+                    }
+                }
+
+                if (session != null && !hasSubject) {
+                    session.setAttribute(Globals.SUBJECT_ATTR, subject);
+                }
+            }
+
+            Subject.doAsPrivileged(subject, pea, null);       
+       } catch( PrivilegedActionException pe) {
+            Throwable e = ((InvocationTargetException)pe.getException())
+                                .getTargetException();
+            
+            if (log.isDebugEnabled()){
+                log.debug(sm.getString("SecurityUtil.doAsPrivilege"), e); 
+            }
+            
+            if (e instanceof UnavailableException)
+                throw (UnavailableException) e;
+            else if (e instanceof ServletException)
+                throw (ServletException) e;
+            else if (e instanceof IOException)
+                throw (IOException) e;
+            else if (e instanceof RuntimeException)
+                throw (RuntimeException) e;
+            else
+                throw new ServletException(e.getMessage(), e);
+        }  
+    }
+    
+    
+    /**
+     * Find a method stored within the cache.
+     * @param methodsCache the cache used to store method instance
+     * @param methodName the method to apply the security restriction
+     * @return the method instance, null if not yet created.
+     */
+    private static Method findMethod(Method[] methodsCache,
+                                     String methodName){
+        if (methodName.equalsIgnoreCase(INIT_METHOD) 
+                && methodsCache[INIT] != null){
+            return methodsCache[INIT];
+        } else if (methodName.equalsIgnoreCase(DESTROY_METHOD) 
+                && methodsCache[DESTROY] != null){
+            return methodsCache[DESTROY];            
+        } else if (methodName.equalsIgnoreCase(SERVICE_METHOD) 
+                && methodsCache[SERVICE] != null){
+            return methodsCache[SERVICE];
+        } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD) 
+                && methodsCache[DOFILTER] != null){
+            return methodsCache[DOFILTER];          
+        } 
+        return null;
+    }
+    
+    
+    /**
+     * Create the method and cache it for further re-use.
+     * @param methodsCache the cache used to store method instance
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     * @param targetType <code>Class</code> array used to instanciate a 
+     * <code>Method</code> object.
+     * @return the method instance.
+     */
+    private static Method createMethodAndCacheIt(Method[] methodsCache,
+                                                 String methodName,
+                                                 Object targetObject,
+                                                 Class[] targetType) 
+            throws Exception{
+        
+        if ( methodsCache == null){
+            methodsCache = new Method[3];
+        }               
+                
+        Method method = 
+            targetObject.getClass().getMethod(methodName, targetType); 
+
+        if (methodName.equalsIgnoreCase(INIT_METHOD)){
+            methodsCache[INIT] = method;
+        } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)){
+            methodsCache[DESTROY] = method;
+        } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)){
+            methodsCache[SERVICE] = method;
+        } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)){
+            methodsCache[DOFILTER] = method;
+        } 
+         
+        objectCache.put(targetObject, methodsCache );
+                                           
+        return method;
+    }
+
+    
+    /**
+     * Remove the object from the cache.
+     *
+     * @param cachedObject The object to remove
+     */
+    public static void remove(Object cachedObject){
+        objectCache.remove(cachedObject);
+    }
+    
+    
+    /**
+     * Return the <code>SecurityManager</code> only if Security is enabled AND
+     * package protection mechanism is enabled.
+     */
+    public static boolean isPackageProtectionEnabled(){
+        if (packageDefinitionEnabled && System.getSecurityManager() !=  null){
+            return true;
+        }
+        return false;
+    }
+    
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/CGIServlet.java b/container/catalina/src/share/org/apache/catalina/servlets/CGIServlet.java
new file mode 100644
index 0000000..ba0620c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/CGIServlet.java
@@ -0,0 +1,2021 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.servlets;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.util.IOTools;
+
+
+/**
+ *  CGI-invoking servlet for web applications, used to execute scripts which
+ *  comply to the Common Gateway Interface (CGI) specification and are named
+ *  in the path-info used to invoke this servlet.
+ *
+ * <p>
+ * <i>Note: This code compiles and even works for simple CGI cases.
+ *          Exhaustive testing has not been done.  Please consider it beta
+ *          quality.  Feedback is appreciated to the author (see below).</i>
+ * </p>
+ * <p>
+ *
+ * <b>Example</b>:<br>
+ * If an instance of this servlet was mapped (using
+ *       <code>&lt;web-app&gt;/WEB-INF/web.xml</code>) to:
+ * </p>
+ * <p>
+ * <code>
+ * &lt;web-app&gt;/cgi-bin/*
+ * </code>
+ * </p>
+ * <p>
+ * then the following request:
+ * </p>
+ * <p>
+ * <code>
+ * http://localhost:8080/&lt;web-app&gt;/cgi-bin/dir1/script/pathinfo1
+ * </code>
+ * </p>
+ * <p>
+ * would result in the execution of the script
+ * </p>
+ * <p>
+ * <code>
+ * &lt;web-app-root&gt;/WEB-INF/cgi/dir1/script
+ * </code>
+ * </p>
+ * <p>
+ * with the script's <code>PATH_INFO</code> set to <code>/pathinfo1</code>.
+ * </p>
+ * <p>
+ * Recommendation:  House all your CGI scripts under
+ * <code>&lt;webapp&gt;/WEB-INF/cgi</code>.  This will ensure that you do not
+ * accidentally expose your cgi scripts' code to the outside world and that
+ * your cgis will be cleanly ensconced underneath the WEB-INF (i.e.,
+ * non-content) area.
+ * </p>
+ * <p>
+ * The default CGI location is mentioned above.  You have the flexibility to
+ * put CGIs wherever you want, however:
+ * </p>
+ * <p>
+ *   The CGI search path will start at
+ *   webAppRootDir + File.separator + cgiPathPrefix
+ *   (or webAppRootDir alone if cgiPathPrefix is
+ *   null).
+ * </p>
+ * <p>
+ *   cgiPathPrefix is defined by setting
+ *   this servlet's cgiPathPrefix init parameter
+ * </p>
+ *
+ * <p>
+ *
+ * <B>CGI Specification</B>:<br> derived from
+ * <a href="http://cgi-spec.golux.com">http://cgi-spec.golux.com</a>.
+ * A work-in-progress & expired Internet Draft.  Note no actual RFC describing
+ * the CGI specification exists.  Where the behavior of this servlet differs
+ * from the specification cited above, it is either documented here, a bug,
+ * or an instance where the specification cited differs from Best
+ * Community Practice (BCP).
+ * Such instances should be well-documented here.  Please email the
+ * <a href="mailto:tomcat-dev@jakarta.apache.org">Jakarta Tomcat group [tomcat-dev@jakarta.apache.org]</a>
+ * with amendments.
+ *
+ * </p>
+ * <p>
+ *
+ * <b>Canonical metavariables</b>:<br>
+ * The CGI specification defines the following canonical metavariables:
+ * <br>
+ * [excerpt from CGI specification]
+ * <PRE>
+ *  AUTH_TYPE
+ *  CONTENT_LENGTH
+ *  CONTENT_TYPE
+ *  GATEWAY_INTERFACE
+ *  PATH_INFO
+ *  PATH_TRANSLATED
+ *  QUERY_STRING
+ *  REMOTE_ADDR
+ *  REMOTE_HOST
+ *  REMOTE_IDENT
+ *  REMOTE_USER
+ *  REQUEST_METHOD
+ *  SCRIPT_NAME
+ *  SERVER_NAME
+ *  SERVER_PORT
+ *  SERVER_PROTOCOL
+ *  SERVER_SOFTWARE
+ * </PRE>
+ * <p>
+ * Metavariables with names beginning with the protocol name (<EM>e.g.</EM>,
+ * "HTTP_ACCEPT") are also canonical in their description of request header
+ * fields.  The number and meaning of these fields may change independently
+ * of this specification.  (See also section 6.1.5 [of the CGI specification].)
+ * </p>
+ * [end excerpt]
+ *
+ * </p>
+ * <h2> Implementation notes</h2>
+ * <p>
+ *
+ * <b>standard input handling</b>: If your script accepts standard input,
+ * then the client must start sending input within a certain timeout period,
+ * otherwise the servlet will assume no input is coming and carry on running
+ * the script.  The script's the standard input will be closed and handling of
+ * any further input from the client is undefined.  Most likely it will be
+ * ignored.  If this behavior becomes undesirable, then this servlet needs
+ * to be enhanced to handle threading of the spawned process' stdin, stdout,
+ * and stderr (which should not be too hard).
+ * <br>
+ * If you find your cgi scripts are timing out receiving input, you can set
+ * the init parameter <code></code> of your webapps' cgi-handling servlet
+ * to be
+ * </p>
+ * <p>
+ *
+ * <b>Metavariable Values</b>: According to the CGI specificion,
+ * implementations may choose to represent both null or missing values in an
+ * implementation-specific manner, but must define that manner.  This
+ * implementation chooses to always define all required metavariables, but
+ * set the value to "" for all metavariables whose value is either null or
+ * undefined.  PATH_TRANSLATED is the sole exception to this rule, as per the
+ * CGI Specification.
+ *
+ * </p>
+ * <p>
+ *
+ * <b>NPH --  Non-parsed-header implementation</b>:  This implementation does
+ * not support the CGI NPH concept, whereby server ensures that the data
+ * supplied to the script are preceisely as supplied by the client and
+ * unaltered by the server.
+ * </p>
+ * <p>
+ * The function of a servlet container (including Tomcat) is specifically
+ * designed to parse and possible alter CGI-specific variables, and as
+ * such makes NPH functionality difficult to support.
+ * </p>
+ * <p>
+ * The CGI specification states that compliant servers MAY support NPH output.
+ * It does not state servers MUST support NPH output to be unconditionally
+ * compliant.  Thus, this implementation maintains unconditional compliance
+ * with the specification though NPH support is not present.
+ * </p>
+ * <p>
+ *
+ * The CGI specification is located at
+ * <a href="http://cgi-spec.golux.com">http://cgi-spec.golux.com</a>.
+ *
+ * </p>
+ * <p>
+ * <h3>TODO:</h3>
+ * <ul>
+ * <li> Support for setting headers (for example, Location headers don't work)
+ * <li> Support for collapsing multiple header lines (per RFC 2616)
+ * <li> Ensure handling of POST method does not interfere with 2.3 Filters
+ * <li> Refactor some debug code out of core
+ * <li> Ensure header handling preserves encoding
+ * <li> Possibly rewrite CGIRunner.run()?
+ * <li> Possibly refactor CGIRunner and CGIEnvironment as non-inner classes?
+ * <li> Document handling of cgi stdin when there is no stdin
+ * <li> Revisit IOException handling in CGIRunner.run()
+ * <li> Better documentation
+ * <li> Confirm use of ServletInputStream.available() in CGIRunner.run() is
+ *      not needed
+ * <li> Make checking for "." and ".." in servlet & cgi PATH_INFO less
+ *      draconian
+ * <li> [add more to this TODO list]
+ * </ul>
+ * </p>
+ *
+ * @author Martin T Dengler [root@martindengler.com]
+ * @author Amy Roh
+ * @version $Revision$, $Date$
+ * @since Tomcat 4.0
+ *
+ */
+
+
+public final class CGIServlet extends HttpServlet {
+
+    /* some vars below copied from Craig R. McClanahan's InvokerServlet */
+
+    /** the Context container associated with our web application. */
+    private ServletContext context = null;
+
+    /** the debugging detail level for this servlet. */
+    private int debug = 0;
+
+    /**
+     *  The CGI search path will start at
+     *    webAppRootDir + File.separator + cgiPathPrefix
+     *    (or webAppRootDir alone if cgiPathPrefix is
+     *    null)
+     */
+    private String cgiPathPrefix = null;
+
+    /** the executable to use with the script */
+    private String cgiExecutable = "perl";
+    
+    /** the encoding to use for parameters */
+    private String parameterEncoding = System.getProperty("file.encoding",
+                                                          "UTF-8");
+
+    /** object used to ensure multiple threads don't try to expand same file */
+    static Object expandFileLock = new Object();
+
+    /** the shell environment variables to be passed to the CGI script */
+    static Hashtable shellEnv = new Hashtable();
+
+    /**
+     * Sets instance variables.
+     * <P>
+     * Modified from Craig R. McClanahan's InvokerServlet
+     * </P>
+     *
+     * @param config                    a <code>ServletConfig</code> object
+     *                                  containing the servlet's
+     *                                  configuration and initialization
+     *                                  parameters
+     *
+     * @exception ServletException      if an exception has occurred that
+     *                                  interferes with the servlet's normal
+     *                                  operation
+     */
+    public void init(ServletConfig config) throws ServletException {
+
+        super.init(config);
+
+        // Verify that we were not accessed using the invoker servlet
+        String servletName = getServletConfig().getServletName();
+        if (servletName == null)
+            servletName = "";
+        if (servletName.startsWith("org.apache.catalina.INVOKER."))
+            throw new UnavailableException
+                ("Cannot invoke CGIServlet through the invoker");
+
+        boolean passShellEnvironment = false;
+        
+        // Set our properties from the initialization parameters
+        String value = null;
+        try {
+            value = getServletConfig().getInitParameter("debug");
+            debug = Integer.parseInt(value);
+            cgiPathPrefix =
+                getServletConfig().getInitParameter("cgiPathPrefix");
+            value = getServletConfig().getInitParameter("passShellEnvironment");
+            passShellEnvironment = Boolean.valueOf(value).booleanValue();
+        } catch (Throwable t) {
+            //NOOP
+        }
+        log("init: loglevel set to " + debug);
+
+        if (passShellEnvironment) {
+            try {
+                shellEnv.putAll(getShellEnvironment());
+            } catch (IOException ioe) {
+                ServletException e = new ServletException(
+                        "Unable to read shell environment variables", ioe);
+            }
+        }
+
+        value = getServletConfig().getInitParameter("executable");
+        if (value != null) {
+            cgiExecutable = value;
+        }
+
+        value = getServletConfig().getInitParameter("parameterEncoding");
+        if (value != null) {
+            parameterEncoding = value;
+        }
+
+        // Identify the internal container resources we need
+        context = config.getServletContext();
+
+    }
+
+
+
+    /**
+     * Prints out important Servlet API and container information
+     *
+     * <p>
+     * Copied from SnoopAllServlet by Craig R. McClanahan
+     * </p>
+     *
+     * @param  out    ServletOutputStream as target of the information
+     * @param  req    HttpServletRequest object used as source of information
+     * @param  res    HttpServletResponse object currently not used but could
+     *                provide future information
+     *
+     * @exception  IOException  if a write operation exception occurs
+     *
+     */
+    protected void printServletEnvironment(ServletOutputStream out,
+        HttpServletRequest req, HttpServletResponse res) throws IOException {
+
+        // Document the properties from ServletRequest
+        out.println("<h1>ServletRequest Properties</h1>");
+        out.println("<ul>");
+        Enumeration attrs = req.getAttributeNames();
+        while (attrs.hasMoreElements()) {
+            String attr = (String) attrs.nextElement();
+            out.println("<li><b>attribute</b> " + attr + " = " +
+                           req.getAttribute(attr));
+        }
+        out.println("<li><b>characterEncoding</b> = " +
+                       req.getCharacterEncoding());
+        out.println("<li><b>contentLength</b> = " +
+                       req.getContentLength());
+        out.println("<li><b>contentType</b> = " +
+                       req.getContentType());
+        Enumeration locales = req.getLocales();
+        while (locales.hasMoreElements()) {
+            Locale locale = (Locale) locales.nextElement();
+            out.println("<li><b>locale</b> = " + locale);
+        }
+        Enumeration params = req.getParameterNames();
+        while (params.hasMoreElements()) {
+            String param = (String) params.nextElement();
+            String values[] = req.getParameterValues(param);
+            for (int i = 0; i < values.length; i++)
+                out.println("<li><b>parameter</b> " + param + " = " +
+                               values[i]);
+        }
+        out.println("<li><b>protocol</b> = " + req.getProtocol());
+        out.println("<li><b>remoteAddr</b> = " + req.getRemoteAddr());
+        out.println("<li><b>remoteHost</b> = " + req.getRemoteHost());
+        out.println("<li><b>scheme</b> = " + req.getScheme());
+        out.println("<li><b>secure</b> = " + req.isSecure());
+        out.println("<li><b>serverName</b> = " + req.getServerName());
+        out.println("<li><b>serverPort</b> = " + req.getServerPort());
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the properties from HttpServletRequest
+        out.println("<h1>HttpServletRequest Properties</h1>");
+        out.println("<ul>");
+        out.println("<li><b>authType</b> = " + req.getAuthType());
+        out.println("<li><b>contextPath</b> = " +
+                       req.getContextPath());
+        Cookie cookies[] = req.getCookies();
+        if (cookies!=null) {
+            for (int i = 0; i < cookies.length; i++)
+                out.println("<li><b>cookie</b> " + cookies[i].getName() +" = " +cookies[i].getValue());
+        }
+        Enumeration headers = req.getHeaderNames();
+        while (headers.hasMoreElements()) {
+            String header = (String) headers.nextElement();
+            out.println("<li><b>header</b> " + header + " = " +
+                           req.getHeader(header));
+        }
+        out.println("<li><b>method</b> = " + req.getMethod());
+        out.println("<li><a name=\"pathInfo\"><b>pathInfo</b></a> = "
+                    + req.getPathInfo());
+        out.println("<li><b>pathTranslated</b> = " +
+                       req.getPathTranslated());
+        out.println("<li><b>queryString</b> = " +
+                       req.getQueryString());
+        out.println("<li><b>remoteUser</b> = " +
+                       req.getRemoteUser());
+        out.println("<li><b>requestedSessionId</b> = " +
+                       req.getRequestedSessionId());
+        out.println("<li><b>requestedSessionIdFromCookie</b> = " +
+                       req.isRequestedSessionIdFromCookie());
+        out.println("<li><b>requestedSessionIdFromURL</b> = " +
+                       req.isRequestedSessionIdFromURL());
+        out.println("<li><b>requestedSessionIdValid</b> = " +
+                       req.isRequestedSessionIdValid());
+        out.println("<li><b>requestURI</b> = " +
+                       req.getRequestURI());
+        out.println("<li><b>servletPath</b> = " +
+                       req.getServletPath());
+        out.println("<li><b>userPrincipal</b> = " +
+                       req.getUserPrincipal());
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet request attributes
+        out.println("<h1>ServletRequest Attributes</h1>");
+        out.println("<ul>");
+        attrs = req.getAttributeNames();
+        while (attrs.hasMoreElements()) {
+            String attr = (String) attrs.nextElement();
+            out.println("<li><b>" + attr + "</b> = " +
+                           req.getAttribute(attr));
+        }
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Process the current session (if there is one)
+        HttpSession session = req.getSession(false);
+        if (session != null) {
+
+            // Document the session properties
+            out.println("<h1>HttpSession Properties</h1>");
+            out.println("<ul>");
+            out.println("<li><b>id</b> = " +
+                           session.getId());
+            out.println("<li><b>creationTime</b> = " +
+                           new Date(session.getCreationTime()));
+            out.println("<li><b>lastAccessedTime</b> = " +
+                           new Date(session.getLastAccessedTime()));
+            out.println("<li><b>maxInactiveInterval</b> = " +
+                           session.getMaxInactiveInterval());
+            out.println("</ul>");
+            out.println("<hr>");
+
+            // Document the session attributes
+            out.println("<h1>HttpSession Attributes</h1>");
+            out.println("<ul>");
+            attrs = session.getAttributeNames();
+            while (attrs.hasMoreElements()) {
+                String attr = (String) attrs.nextElement();
+                out.println("<li><b>" + attr + "</b> = " +
+                               session.getAttribute(attr));
+            }
+            out.println("</ul>");
+            out.println("<hr>");
+
+        }
+
+        // Document the servlet configuration properties
+        out.println("<h1>ServletConfig Properties</h1>");
+        out.println("<ul>");
+        out.println("<li><b>servletName</b> = " +
+                       getServletConfig().getServletName());
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet configuration initialization parameters
+        out.println("<h1>ServletConfig Initialization Parameters</h1>");
+        out.println("<ul>");
+        params = getServletConfig().getInitParameterNames();
+        while (params.hasMoreElements()) {
+            String param = (String) params.nextElement();
+            String value = getServletConfig().getInitParameter(param);
+            out.println("<li><b>" + param + "</b> = " + value);
+        }
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet context properties
+        out.println("<h1>ServletContext Properties</h1>");
+        out.println("<ul>");
+        out.println("<li><b>majorVersion</b> = " +
+                       getServletContext().getMajorVersion());
+        out.println("<li><b>minorVersion</b> = " +
+                       getServletContext().getMinorVersion());
+        out.println("<li><b>realPath('/')</b> = " +
+                       getServletContext().getRealPath("/"));
+        out.println("<li><b>serverInfo</b> = " +
+                       getServletContext().getServerInfo());
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet context initialization parameters
+        out.println("<h1>ServletContext Initialization Parameters</h1>");
+        out.println("<ul>");
+        params = getServletContext().getInitParameterNames();
+        while (params.hasMoreElements()) {
+            String param = (String) params.nextElement();
+            String value = getServletContext().getInitParameter(param);
+            out.println("<li><b>" + param + "</b> = " + value);
+        }
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet context attributes
+        out.println("<h1>ServletContext Attributes</h1>");
+        out.println("<ul>");
+        attrs = getServletContext().getAttributeNames();
+        while (attrs.hasMoreElements()) {
+            String attr = (String) attrs.nextElement();
+            out.println("<li><b>" + attr + "</b> = " +
+                           getServletContext().getAttribute(attr));
+        }
+        out.println("</ul>");
+        out.println("<hr>");
+
+
+
+    }
+
+
+
+    /**
+     * Provides CGI Gateway service -- delegates to <code>doGet</code>
+     *
+     * @param  req   HttpServletRequest passed in by servlet container
+     * @param  res   HttpServletResponse passed in by servlet container
+     *
+     * @exception  ServletException  if a servlet-specific exception occurs
+     * @exception  IOException  if a read/write exception occurs
+     *
+     * @see javax.servlet.http.HttpServlet
+     *
+     */
+    protected void doPost(HttpServletRequest req, HttpServletResponse res)
+        throws IOException, ServletException {
+        doGet(req, res);
+    }
+
+
+
+    /**
+     * Provides CGI Gateway service
+     *
+     * @param  req   HttpServletRequest passed in by servlet container
+     * @param  res   HttpServletResponse passed in by servlet container
+     *
+     * @exception  ServletException  if a servlet-specific exception occurs
+     * @exception  IOException  if a read/write exception occurs
+     *
+     * @see javax.servlet.http.HttpServlet
+     *
+     */
+    protected void doGet(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException {
+
+        // Verify that we were not accessed using the invoker servlet
+        if (req.getAttribute(Globals.INVOKED_ATTR) != null)
+            throw new UnavailableException
+                ("Cannot invoke CGIServlet through the invoker");
+
+        CGIEnvironment cgiEnv = new CGIEnvironment(req, getServletContext());
+
+        if (cgiEnv.isValid()) {
+            CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(),
+                                          cgiEnv.getEnvironment(),
+                                          cgiEnv.getWorkingDirectory(),
+                                          cgiEnv.getParameters());
+            //if POST, we need to cgi.setInput
+            //REMIND: how does this interact with Servlet API 2.3's Filters?!
+            if ("POST".equals(req.getMethod())) {
+                cgi.setInput(req.getInputStream());
+            }
+            cgi.setResponse(res);
+            cgi.run();
+        }
+
+        if (!cgiEnv.isValid()) {
+            res.setStatus(404);
+        }
+ 
+        if (debug >= 10) {
+            try {
+                ServletOutputStream out = res.getOutputStream();
+                out.println("<HTML><HEAD><TITLE>$Name$</TITLE></HEAD>");
+                out.println("<BODY>$Header$<p>");
+
+                if (cgiEnv.isValid()) {
+                    out.println(cgiEnv.toString());
+                } else {
+                    out.println("<H3>");
+                    out.println("CGI script not found or not specified.");
+                    out.println("</H3>");
+                    out.println("<H4>");
+                    out.println("Check the <b>HttpServletRequest ");
+                    out.println("<a href=\"#pathInfo\">pathInfo</a></b> ");
+                    out.println("property to see if it is what you meant ");
+                    out.println("it to be.  You must specify an existant ");
+                    out.println("and executable file as part of the ");
+                    out.println("path-info.");
+                    out.println("</H4>");
+                    out.println("<H4>");
+                    out.println("For a good discussion of how CGI scripts ");
+                    out.println("work and what their environment variables ");
+                    out.println("mean, please visit the <a ");
+                    out.println("href=\"http://cgi-spec.golux.com\">CGI ");
+                    out.println("Specification page</a>.");
+                    out.println("</H4>");
+
+                }
+
+                printServletEnvironment(out, req, res);
+
+                out.println("</BODY></HTML>");
+
+            } catch (IOException ignored) {
+            }
+
+        } //debugging
+
+
+    } //doGet
+
+
+
+    /** For future testing use only; does nothing right now */
+    public static void main(String[] args) {
+        System.out.println("$Header$");
+    }
+
+    /**
+     * Get all shell environment variables. Have to do it this rather ugly way
+     * as the API to obtain is not available in 1.4 and earlier APIs.
+     *
+     * See <a href="http://www.rgagnon.com/javadetails/java-0150.html">Read environment
+     * variables from an application</a> for original source and article.
+     */
+    private Hashtable getShellEnvironment() throws IOException {
+        Hashtable envVars = new Hashtable();
+        Process p = null;
+        Runtime r = Runtime.getRuntime();
+        String OS = System.getProperty("os.name").toLowerCase();
+        boolean ignoreCase;
+
+        if (OS.indexOf("windows 9") > -1) {
+            p = r.exec( "command.com /c set" );
+            ignoreCase = true;
+        } else if ( (OS.indexOf("nt") > -1)
+                || (OS.indexOf("windows 2000") > -1)
+                || (OS.indexOf("windows xp") > -1) ) {
+            // thanks to JuanFran for the xp fix!
+            p = r.exec( "cmd.exe /c set" );
+            ignoreCase = true;
+        } else {
+            // our last hope, we assume Unix (thanks to H. Ware for the fix)
+            p = r.exec( "env" );
+            ignoreCase = false;
+        }
+
+        BufferedReader br = new BufferedReader
+            ( new InputStreamReader( p.getInputStream() ) );
+        String line;
+        while( (line = br.readLine()) != null ) {
+            int idx = line.indexOf( '=' );
+            String key = line.substring( 0, idx );
+            String value = line.substring( idx+1 );
+            if (ignoreCase) {
+                key = key.toUpperCase();
+            }
+            envVars.put(key, value);
+        }
+        return envVars;
+    }
+
+
+
+
+
+
+
+    /**
+     * Encapsulates the CGI environment and rules to derive
+     * that environment from the servlet container and request information.
+     *
+     * <p>
+     * </p>
+     *
+     * @version  $Revision$, $Date$
+     * @since    Tomcat 4.0
+     *
+     */
+    protected class CGIEnvironment {
+
+
+        /** context of the enclosing servlet */
+        private ServletContext context = null;
+
+        /** context path of enclosing servlet */
+        private String contextPath = null;
+
+        /** servlet URI of the enclosing servlet */
+        private String servletPath = null;
+
+        /** pathInfo for the current request */
+        private String pathInfo = null;
+
+        /** real file system directory of the enclosing servlet's web app */
+        private String webAppRootDir = null;
+
+        /** tempdir for context - used to expand scripts in unexpanded wars */
+        private File tmpDir = null;
+
+        /** derived cgi environment */
+        private Hashtable env = null;
+
+        /** cgi command to be invoked */
+        private String command = null;
+
+        /** cgi command's desired working directory */
+        private File workingDirectory = null;
+
+        /** cgi command's query parameters */
+        private ArrayList queryParameters = new ArrayList();
+
+        /** whether or not this object is valid or not */
+        private boolean valid = false;
+
+
+        /**
+         * Creates a CGIEnvironment and derives the necessary environment,
+         * query parameters, working directory, cgi command, etc.
+         *
+         * @param  req       HttpServletRequest for information provided by
+         *                   the Servlet API
+         * @param  context   ServletContext for information provided by the
+         *                   Servlet API
+         *
+         */
+        protected CGIEnvironment(HttpServletRequest req,
+                                 ServletContext context) throws IOException {
+            setupFromContext(context);
+            setupFromRequest(req);
+
+            Enumeration paramNames = req.getParameterNames();
+            while (paramNames != null && paramNames.hasMoreElements()) {
+                String param = paramNames.nextElement().toString();
+                if (param != null) {
+                    String values[] = req.getParameterValues(param);
+                    for (int i=0; i < values.length; i++) {
+                        String value = URLEncoder.encode(values[i],
+                                                         parameterEncoding);
+                        NameValuePair nvp = new NameValuePair(param, value);
+                        queryParameters.add(nvp);
+                    }
+                }
+            }
+
+            this.valid = setCGIEnvironment(req);
+
+            if (this.valid) {
+                workingDirectory = new File(command.substring(0,
+                      command.lastIndexOf(File.separator)));
+            }
+
+        }
+
+
+
+        /**
+         * Uses the ServletContext to set some CGI variables
+         *
+         * @param  context   ServletContext for information provided by the
+         *                   Servlet API
+         */
+        protected void setupFromContext(ServletContext context) {
+            this.context = context;
+            this.webAppRootDir = context.getRealPath("/");
+            this.tmpDir = (File) context.getAttribute(Globals.WORK_DIR_ATTR);
+        }
+
+
+
+        /**
+         * Uses the HttpServletRequest to set most CGI variables
+         *
+         * @param  req   HttpServletRequest for information provided by
+         *               the Servlet API
+         */
+        protected void setupFromRequest(HttpServletRequest req) {
+            this.contextPath = req.getContextPath();
+            this.servletPath = req.getServletPath();
+            this.pathInfo = req.getPathInfo();
+            // If getPathInfo() returns null, must be using extension mapping
+            // In this case, pathInfo should be same as servletPath
+            if (this.pathInfo == null) {
+                this.pathInfo = this.servletPath;
+            }
+        }
+
+
+
+        /**
+         * Resolves core information about the cgi script.
+         *
+         * <p>
+         * Example URI:
+         * <PRE> /servlet/cgigateway/dir1/realCGIscript/pathinfo1 </PRE>
+         * <ul>
+         * <LI><b>path</b> = $CATALINA_HOME/mywebapp/dir1/realCGIscript
+         * <LI><b>scriptName</b> = /servlet/cgigateway/dir1/realCGIscript
+         * <LI><b>cgiName</b> = /dir1/realCGIscript
+         * <LI><b>name</b> = realCGIscript
+         * </ul>
+         * </p>
+         * <p>
+         * CGI search algorithm: search the real path below
+         *    &lt;my-webapp-root&gt; and find the first non-directory in
+         *    the getPathTranslated("/"), reading/searching from left-to-right.
+         *</p>
+         *<p>
+         *   The CGI search path will start at
+         *   webAppRootDir + File.separator + cgiPathPrefix
+         *   (or webAppRootDir alone if cgiPathPrefix is
+         *   null).
+         *</p>
+         *<p>
+         *   cgiPathPrefix is defined by setting
+         *   this servlet's cgiPathPrefix init parameter
+         *
+         *</p>
+         *
+         * @param pathInfo       String from HttpServletRequest.getPathInfo()
+         * @param webAppRootDir  String from context.getRealPath("/")
+         * @param contextPath    String as from
+         *                       HttpServletRequest.getContextPath()
+         * @param servletPath    String as from
+         *                       HttpServletRequest.getServletPath()
+         * @param cgiPathPrefix  subdirectory of webAppRootDir below which
+         *                       the web app's CGIs may be stored; can be null.
+         *                       The CGI search path will start at
+         *                       webAppRootDir + File.separator + cgiPathPrefix
+         *                       (or webAppRootDir alone if cgiPathPrefix is
+         *                       null).  cgiPathPrefix is defined by setting
+         *                       the servlet's cgiPathPrefix init parameter.
+         *
+         *
+         * @return
+         * <ul>
+         * <li>
+         * <code>path</code> -    full file-system path to valid cgi script,
+         *                        or null if no cgi was found
+         * <li>
+         * <code>scriptName</code> -
+         *                        CGI variable SCRIPT_NAME; the full URL path
+         *                        to valid cgi script or null if no cgi was
+         *                        found
+         * <li>
+         * <code>cgiName</code> - servlet pathInfo fragment corresponding to
+         *                        the cgi script itself, or null if not found
+         * <li>
+         * <code>name</code> -    simple name (no directories) of the
+         *                        cgi script, or null if no cgi was found
+         * </ul>
+         *
+         * @since Tomcat 4.0
+         */
+        protected String[] findCGI(String pathInfo, String webAppRootDir,
+                                   String contextPath, String servletPath,
+                                   String cgiPathPrefix) {
+            String path = null;
+            String name = null;
+            String scriptname = null;
+            String cginame = null;
+
+            if ((webAppRootDir != null)
+                && (webAppRootDir.lastIndexOf(File.separator) ==
+                    (webAppRootDir.length() - 1))) {
+                    //strip the trailing "/" from the webAppRootDir
+                    webAppRootDir =
+                    webAppRootDir.substring(0, (webAppRootDir.length() - 1));
+            }
+
+            if (cgiPathPrefix != null) {
+                webAppRootDir = webAppRootDir + File.separator
+                    + cgiPathPrefix;
+            }
+
+            if (debug >= 2) {
+                log("findCGI: path=" + pathInfo + ", " + webAppRootDir);
+            }
+
+            File currentLocation = new File(webAppRootDir);
+            StringTokenizer dirWalker =
+            new StringTokenizer(pathInfo, "/");
+            if (debug >= 3) {
+                log("findCGI: currentLoc=" + currentLocation);
+            }
+            while (!currentLocation.isFile() && dirWalker.hasMoreElements()) {
+                if (debug >= 3) {
+                    log("findCGI: currentLoc=" + currentLocation);
+                }
+                currentLocation = new File(currentLocation,
+                                           (String) dirWalker.nextElement());
+            }
+            if (!currentLocation.isFile()) {
+                return new String[] { null, null, null, null };
+            } else {
+                if (debug >= 2) {
+                    log("findCGI: FOUND cgi at " + currentLocation);
+                }
+                path = currentLocation.getAbsolutePath();
+                name = currentLocation.getName();
+                cginame =
+                currentLocation.getParent().substring(webAppRootDir.length())
+                + File.separator
+                + name;
+
+                if (".".equals(contextPath)) {
+                    scriptname = servletPath + cginame;
+                } else {
+                    scriptname = contextPath + servletPath + cginame;
+                }
+            }
+
+            if (debug >= 1) {
+                log("findCGI calc: name=" + name + ", path=" + path
+                    + ", scriptname=" + scriptname + ", cginame=" + cginame);
+            }
+            return new String[] { path, scriptname, cginame, name };
+        }
+
+        /**
+         * Constructs the CGI environment to be supplied to the invoked CGI
+         * script; relies heavliy on Servlet API methods and findCGI
+         *
+         * @param    req request associated with the CGI
+         *           invokation
+         *
+         * @return   true if environment was set OK, false if there
+         *           was a problem and no environment was set
+         */
+        protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException {
+
+            /*
+             * This method is slightly ugly; c'est la vie.
+             * "You cannot stop [ugliness], you can only hope to contain [it]"
+             * (apologies to Marv Albert regarding MJ)
+             */
+
+            Hashtable envp = new Hashtable();
+
+            // Add the shell environment variables (if any)
+            envp.putAll(shellEnv);
+
+            // Add the CGI environment variables
+            String sPathInfoOrig = null;
+            String sPathTranslatedOrig = null;
+            String sPathInfoCGI = null;
+            String sPathTranslatedCGI = null;
+            String sCGIFullPath = null;
+            String sCGIScriptName = null;
+            String sCGIFullName = null;
+            String sCGIName = null;
+            String[] sCGINames;
+
+
+            sPathInfoOrig = this.pathInfo;
+            sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig;
+
+            sPathTranslatedOrig = req.getPathTranslated();
+            sPathTranslatedOrig =
+                sPathTranslatedOrig == null ? "" : sPathTranslatedOrig;
+
+            if (webAppRootDir == null ) {
+                // The app has not been deployed in exploded form
+                webAppRootDir = tmpDir.toString();
+                expandCGIScript();
+            } 
+            
+            sCGINames = findCGI(sPathInfoOrig,
+                                webAppRootDir,
+                                contextPath,
+                                servletPath,
+                                cgiPathPrefix);
+
+            sCGIFullPath = sCGINames[0];
+            sCGIScriptName = sCGINames[1];
+            sCGIFullName = sCGINames[2];
+            sCGIName = sCGINames[3];
+
+            if (sCGIFullPath == null
+                || sCGIScriptName == null
+                || sCGIFullName == null
+                || sCGIName == null) {
+                return false;
+            }
+
+            envp.put("SERVER_SOFTWARE", "TOMCAT");
+
+            envp.put("SERVER_NAME", nullsToBlanks(req.getServerName()));
+
+            envp.put("GATEWAY_INTERFACE", "CGI/1.1");
+
+            envp.put("SERVER_PROTOCOL", nullsToBlanks(req.getProtocol()));
+
+            int port = req.getServerPort();
+            Integer iPort = (port == 0 ? new Integer(-1) : new Integer(port));
+            envp.put("SERVER_PORT", iPort.toString());
+
+            envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod()));
+
+
+
+            /*-
+             * PATH_INFO should be determined by using sCGIFullName:
+             * 1) Let sCGIFullName not end in a "/" (see method findCGI)
+             * 2) Let sCGIFullName equal the pathInfo fragment which
+             *    corresponds to the actual cgi script.
+             * 3) Thus, PATH_INFO = request.getPathInfo().substring(
+             *                      sCGIFullName.length())
+             *
+             * (see method findCGI, where the real work is done)
+             *
+             */
+            if (pathInfo == null
+                || (pathInfo.substring(sCGIFullName.length()).length() <= 0)) {
+                sPathInfoCGI = "";
+            } else {
+                sPathInfoCGI = pathInfo.substring(sCGIFullName.length());
+            }
+            envp.put("PATH_INFO", sPathInfoCGI);
+
+
+            /*-
+             * PATH_TRANSLATED must be determined after PATH_INFO (and the
+             * implied real cgi-script) has been taken into account.
+             *
+             * The following example demonstrates:
+             *
+             * servlet info   = /servlet/cgigw/dir1/dir2/cgi1/trans1/trans2
+             * cgifullpath    = /servlet/cgigw/dir1/dir2/cgi1
+             * path_info      = /trans1/trans2
+             * webAppRootDir  = servletContext.getRealPath("/")
+             *
+             * path_translated = servletContext.getRealPath("/trans1/trans2")
+             *
+             * That is, PATH_TRANSLATED = webAppRootDir + sPathInfoCGI
+             * (unless sPathInfoCGI is null or blank, then the CGI
+             * specification dictates that the PATH_TRANSLATED metavariable
+             * SHOULD NOT be defined.
+             *
+             */
+            if (sPathInfoCGI != null && !("".equals(sPathInfoCGI))) {
+                sPathTranslatedCGI = context.getRealPath(sPathInfoCGI);
+            } else {
+                sPathTranslatedCGI = null;
+            }
+            if (sPathTranslatedCGI == null || "".equals(sPathTranslatedCGI)) {
+                //NOOP
+            } else {
+                envp.put("PATH_TRANSLATED", nullsToBlanks(sPathTranslatedCGI));
+            }
+
+
+            envp.put("SCRIPT_NAME", nullsToBlanks(sCGIScriptName));
+
+            envp.put("QUERY_STRING", nullsToBlanks(req.getQueryString()));
+
+            envp.put("REMOTE_HOST", nullsToBlanks(req.getRemoteHost()));
+
+            envp.put("REMOTE_ADDR", nullsToBlanks(req.getRemoteAddr()));
+
+            envp.put("AUTH_TYPE", nullsToBlanks(req.getAuthType()));
+
+            envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser()));
+
+            envp.put("REMOTE_IDENT", ""); //not necessary for full compliance
+
+            envp.put("CONTENT_TYPE", nullsToBlanks(req.getContentType()));
+
+
+            /* Note CGI spec says CONTENT_LENGTH must be NULL ("") or undefined
+             * if there is no content, so we cannot put 0 or -1 in as per the
+             * Servlet API spec.
+             */
+            int contentLength = req.getContentLength();
+            String sContentLength = (contentLength <= 0 ? "" :
+                                     (new Integer(contentLength)).toString());
+            envp.put("CONTENT_LENGTH", sContentLength);
+
+
+            Enumeration headers = req.getHeaderNames();
+            String header = null;
+            while (headers.hasMoreElements()) {
+                header = null;
+                header = ((String) headers.nextElement()).toUpperCase();
+                //REMIND: rewrite multiple headers as if received as single
+                //REMIND: change character set
+                //REMIND: I forgot what the previous REMIND means
+                if ("AUTHORIZATION".equalsIgnoreCase(header) ||
+                    "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) {
+                    //NOOP per CGI specification section 11.2
+                } else {
+                    envp.put("HTTP_" + header.replace('-', '_'),
+                             req.getHeader(header));
+                }
+            }
+
+            File fCGIFullPath = new File(sCGIFullPath);
+            command = fCGIFullPath.getCanonicalPath();
+
+            envp.put("X_TOMCAT_SCRIPT_PATH", command);  //for kicks
+
+            this.env = envp;
+
+            return true;
+
+        }
+
+        /**
+         * Extracts requested resource from web app archive to context work 
+         * directory to enable CGI script to be executed.
+         */
+        protected void expandCGIScript() {
+            StringBuffer srcPath = new StringBuffer();
+            StringBuffer destPath = new StringBuffer();
+            InputStream is = null;
+
+            // paths depend on mapping
+            if (cgiPathPrefix == null ) {
+                srcPath.append(pathInfo);
+                is = context.getResourceAsStream(srcPath.toString());
+                destPath.append(tmpDir);
+                destPath.append(pathInfo);
+            } else {
+                // essentially same search algorithm as findCGI()
+                srcPath.append(cgiPathPrefix);
+                StringTokenizer pathWalker =
+                        new StringTokenizer (pathInfo, "/");
+                // start with first element
+                while (pathWalker.hasMoreElements() && (is == null)) {
+                    srcPath.append("/");
+                    srcPath.append(pathWalker.nextElement());
+                    is = context.getResourceAsStream(srcPath.toString());
+                }
+                destPath.append(tmpDir);
+                destPath.append("/");
+                destPath.append(srcPath);
+            }
+
+            if (is == null) {
+                // didn't find anything, give up now
+                if (debug >= 2) {
+                    log("expandCGIScript: source '" + srcPath + "' not found");
+                }
+                 return;
+            }
+
+            File f = new File(destPath.toString());
+            if (f.exists()) {
+                // Don't need to expand if it already exists
+                return;
+            } 
+
+            // create directories
+            String dirPath = new String (destPath.toString().substring(
+                    0,destPath.toString().lastIndexOf("/")));
+            File dir = new File(dirPath);
+            dir.mkdirs();
+
+            try {
+                synchronized (expandFileLock) {
+                    // make sure file doesn't exist
+                    if (f.exists()) {
+                        return;
+                    }
+
+                    // create file
+                    if (!f.createNewFile()) {
+                        return;
+                    }
+                    FileOutputStream fos = new FileOutputStream(f);
+
+                    // copy data
+                    IOTools.flow(is, fos);
+                    is.close();
+                    fos.close();
+                    if (debug >= 2) {
+                        log("expandCGIScript: expanded '" + srcPath + "' to '" + destPath + "'");
+                    }
+                }
+            } catch (IOException ioe) {
+                // delete in case file is corrupted 
+                if (f.exists()) {
+                    f.delete();
+                }
+            }
+        }
+
+
+        /**
+         * Print important CGI environment information in a easy-to-read HTML
+         * table
+         *
+         * @return  HTML string containing CGI environment info
+         *
+         */
+        public String toString() {
+
+            StringBuffer sb = new StringBuffer();
+
+            sb.append("<TABLE border=2>");
+
+            sb.append("<tr><th colspan=2 bgcolor=grey>");
+            sb.append("CGIEnvironment Info</th></tr>");
+
+            sb.append("<tr><td>Debug Level</td><td>");
+            sb.append(debug);
+            sb.append("</td></tr>");
+
+            sb.append("<tr><td>Validity:</td><td>");
+            sb.append(isValid());
+            sb.append("</td></tr>");
+
+            if (isValid()) {
+                Enumeration envk = env.keys();
+                while (envk.hasMoreElements()) {
+                    String s = (String) envk.nextElement();
+                    sb.append("<tr><td>");
+                    sb.append(s);
+                    sb.append("</td><td>");
+                    sb.append(blanksToString((String) env.get(s),
+                                             "[will be set to blank]"));
+                    sb.append("</td></tr>");
+                }
+            }
+
+            sb.append("<tr><td colspan=2><HR></td></tr>");
+
+            sb.append("<tr><td>Derived Command</td><td>");
+            sb.append(nullsToBlanks(command));
+            sb.append("</td></tr>");
+
+            sb.append("<tr><td>Working Directory</td><td>");
+            if (workingDirectory != null) {
+                sb.append(workingDirectory.toString());
+            }
+            sb.append("</td></tr>");
+
+            sb.append("<tr><td colspan=2>Query Params</td></tr>");
+            for (int i=0; i < queryParameters.size(); i++) {
+                NameValuePair nvp = (NameValuePair) queryParameters.get(i);
+                sb.append("<tr><td>");
+                sb.append(nvp.getName());
+                sb.append("</td><td>");
+                sb.append(nvp.getValue());
+                sb.append("</td></tr>");
+            }
+
+            sb.append("</TABLE><p>end.");
+
+            return sb.toString();
+        }
+
+
+
+        /**
+         * Gets derived command string
+         *
+         * @return  command string
+         *
+         */
+        protected String getCommand() {
+            return command;
+        }
+
+
+
+        /**
+         * Gets derived CGI working directory
+         *
+         * @return  working directory
+         *
+         */
+        protected File getWorkingDirectory() {
+            return workingDirectory;
+        }
+
+
+
+        /**
+         * Gets derived CGI environment
+         *
+         * @return   CGI environment
+         *
+         */
+        protected Hashtable getEnvironment() {
+            return env;
+        }
+
+
+
+        /**
+         * Gets derived CGI query parameters
+         *
+         * @return   CGI query parameters
+         *
+         */
+        protected ArrayList getParameters() {
+            return queryParameters;
+        }
+
+
+
+        /**
+         * Gets validity status
+         *
+         * @return   true if this environment is valid, false
+         *           otherwise
+         *
+         */
+        protected boolean isValid() {
+            return valid;
+        }
+
+
+
+        /**
+         * Converts null strings to blank strings ("")
+         *
+         * @param    s string to be converted if necessary
+         * @return   a non-null string, either the original or the empty string
+         *           ("") if the original was <code>null</code>
+         */
+        protected String nullsToBlanks(String s) {
+            return nullsToString(s, "");
+        }
+
+
+
+        /**
+         * Converts null strings to another string
+         *
+         * @param    couldBeNull string to be converted if necessary
+         * @param    subForNulls string to return instead of a null string
+         * @return   a non-null string, either the original or the substitute
+         *           string if the original was <code>null</code>
+         */
+        protected String nullsToString(String couldBeNull,
+                                       String subForNulls) {
+            return (couldBeNull == null ? subForNulls : couldBeNull);
+        }
+
+
+
+        /**
+         * Converts blank strings to another string
+         *
+         * @param    couldBeBlank string to be converted if necessary
+         * @param    subForBlanks string to return instead of a blank string
+         * @return   a non-null string, either the original or the substitute
+         *           string if the original was <code>null</code> or empty ("")
+         */
+        protected String blanksToString(String couldBeBlank,
+                                      String subForBlanks) {
+            return (("".equals(couldBeBlank) || couldBeBlank == null)
+                    ? subForBlanks
+                    : couldBeBlank);
+        }
+
+
+
+    } //class CGIEnvironment
+
+
+
+
+
+
+    /**
+     * Encapsulates the knowledge of how to run a CGI script, given the
+     * script's desired environment and (optionally) input/output streams
+     *
+     * <p>
+     *
+     * Exposes a <code>run</code> method used to actually invoke the
+     * CGI.
+     *
+     * </p>
+     * <p>
+     *
+     * The CGI environment and settings are derived from the information
+     * passed to the constuctor.
+     *
+     * </p>
+     * <p>
+     *
+     * The input and output streams can be set by the <code>setInput</code>
+     * and <code>setResponse</code> methods, respectively.
+     * </p>
+     *
+     * @version   $Revision$, $Date$
+     */
+
+    protected class CGIRunner {
+
+        /** script/command to be executed */
+        private String command = null;
+
+        /** environment used when invoking the cgi script */
+        private Hashtable env = null;
+
+        /** working directory used when invoking the cgi script */
+        private File wd = null;
+
+        /** query parameters to be passed to the invoked script */
+        private ArrayList params = null;
+
+        /** stdin to be passed to cgi script */
+        private InputStream stdin = null;
+
+        /** response object used to set headers & get output stream */
+        private HttpServletResponse response = null;
+
+        /** boolean tracking whether this object has enough info to run() */
+        private boolean readyToRun = false;
+
+
+
+
+        /**
+         *  Creates a CGIRunner and initializes its environment, working
+         *  directory, and query parameters.
+         *  <BR>
+         *  Input/output streams (optional) are set using the
+         *  <code>setInput</code> and <code>setResponse</code> methods,
+         *  respectively.
+         *
+         * @param  command  string full path to command to be executed
+         * @param  env      Hashtable with the desired script environment
+         * @param  wd       File with the script's desired working directory
+         * @param  params   ArrayList with the script's query parameters as
+         *                  NameValuePairs
+         */
+        protected CGIRunner(String command, Hashtable env, File wd,
+                            ArrayList params) {
+            this.command = command;
+            this.env = env;
+            this.wd = wd;
+            this.params = params;
+            updateReadyStatus();
+        }
+
+
+
+        /**
+         * Checks & sets ready status
+         */
+        protected void updateReadyStatus() {
+            if (command != null
+                && env != null
+                && wd != null
+                && params != null
+                && response != null) {
+                readyToRun = true;
+            } else {
+                readyToRun = false;
+            }
+        }
+
+
+
+        /**
+         * Gets ready status
+         *
+         * @return   false if not ready (<code>run</code> will throw
+         *           an exception), true if ready
+         */
+        protected boolean isReady() {
+            return readyToRun;
+        }
+
+
+
+        /**
+         * Sets HttpServletResponse object used to set headers and send
+         * output to
+         *
+         * @param  response   HttpServletResponse to be used
+         *
+         */
+        protected void setResponse(HttpServletResponse response) {
+            this.response = response;
+            updateReadyStatus();
+        }
+
+
+
+        /**
+         * Sets standard input to be passed on to the invoked cgi script
+         *
+         * @param  stdin   InputStream to be used
+         *
+         */
+        protected void setInput(InputStream stdin) {
+            this.stdin = stdin;
+            updateReadyStatus();
+        }
+
+
+
+        /**
+         * Converts a Hashtable to a String array by converting each
+         * key/value pair in the Hashtable to a String in the form
+         * "key=value" (hashkey + "=" + hash.get(hashkey).toString())
+         *
+         * @param  h   Hashtable to convert
+         *
+         * @return     converted string array
+         *
+         * @exception  NullPointerException   if a hash key has a null value
+         *
+         */
+        protected String[] hashToStringArray(Hashtable h)
+            throws NullPointerException {
+            Vector v = new Vector();
+            Enumeration e = h.keys();
+            while (e.hasMoreElements()) {
+                String k = e.nextElement().toString();
+                v.add(k + "=" + h.get(k));
+            }
+            String[] strArr = new String[v.size()];
+            v.copyInto(strArr);
+            return strArr;
+        }
+
+
+
+        /**
+         * Executes a CGI script with the desired environment, current working
+         * directory, and input/output streams
+         *
+         * <p>
+         * This implements the following CGI specification recommedations:
+         * <UL>
+         * <LI> Servers SHOULD provide the "<code>query</code>" component of
+         *      the script-URI as command-line arguments to scripts if it
+         *      does not contain any unencoded "=" characters and the
+         *      command-line arguments can be generated in an unambiguous
+         *      manner.
+         * <LI> Servers SHOULD set the AUTH_TYPE metavariable to the value
+         *      of the "<code>auth-scheme</code>" token of the
+         *      "<code>Authorization</code>" if it was supplied as part of the
+         *      request header.  See <code>getCGIEnvironment</code> method.
+         * <LI> Where applicable, servers SHOULD set the current working
+         *      directory to the directory in which the script is located
+         *      before invoking it.
+         * <LI> Server implementations SHOULD define their behavior for the
+         *      following cases:
+         *     <ul>
+         *     <LI> <u>Allowed characters in pathInfo</u>:  This implementation
+         *             does not allow ASCII NUL nor any character which cannot
+         *             be URL-encoded according to internet standards;
+         *     <LI> <u>Allowed characters in path segments</u>: This
+         *             implementation does not allow non-terminal NULL
+         *             segments in the the path -- IOExceptions may be thrown;
+         *     <LI> <u>"<code>.</code>" and "<code>..</code>" path
+         *             segments</u>:
+         *             This implementation does not allow "<code>.</code>" and
+         *             "<code>..</code>" in the the path, and such characters
+         *             will result in an IOException being thrown;
+         *     <LI> <u>Implementation limitations</u>: This implementation
+         *             does not impose any limitations except as documented
+         *             above.  This implementation may be limited by the
+         *             servlet container used to house this implementation.
+         *             In particular, all the primary CGI variable values
+         *             are derived either directly or indirectly from the
+         *             container's implementation of the Servlet API methods.
+         *     </ul>
+         * </UL>
+         * </p>
+         *
+         * @exception IOException if problems during reading/writing occur
+         *
+         * @see    java.lang.Runtime#exec(String command, String[] envp,
+         *                                File dir)
+         */
+        protected void run() throws IOException {
+
+            /*
+             * REMIND:  this method feels too big; should it be re-written?
+             */
+
+            if (!isReady()) {
+                throw new IOException(this.getClass().getName()
+                                      + ": not ready to run.");
+            }
+
+            if (debug >= 1 ) {
+                log("runCGI(envp=[" + env + "], command=" + command + ")");
+            }
+
+            if ((command.indexOf(File.separator + "." + File.separator) >= 0)
+                || (command.indexOf(File.separator + "..") >= 0)
+                || (command.indexOf(".." + File.separator) >= 0)) {
+                throw new IOException(this.getClass().getName()
+                                      + "Illegal Character in CGI command "
+                                      + "path ('.' or '..') detected.  Not "
+                                      + "running CGI [" + command + "].");
+            }
+
+            /* original content/structure of this section taken from
+             * http://developer.java.sun.com/developer/
+             *                               bugParade/bugs/4216884.html
+             * with major modifications by Martin Dengler
+             */
+            Runtime rt = null;
+            BufferedReader commandsStdOut = null;
+            InputStream cgiOutput = null;
+            BufferedReader commandsStdErr = null;
+            BufferedOutputStream commandsStdIn = null;
+            Process proc = null;
+            int bufRead = -1;
+
+            //create query arguments
+            StringBuffer cmdAndArgs = new StringBuffer();
+            if (command.indexOf(" ") < 0) {
+                cmdAndArgs.append(command);
+            } else {
+                // Spaces used as delimiter, so need to use quotes
+                cmdAndArgs.append("\"");
+                cmdAndArgs.append(command);
+                cmdAndArgs.append("\"");
+            }
+
+            for (int i=0; i < params.size(); i++) {
+                cmdAndArgs.append(" ");
+                NameValuePair nvp = (NameValuePair) params.get(i); 
+                String k = nvp.getName();
+                String v = nvp.getValue();
+                if ((k.indexOf("=") < 0) && (v.indexOf("=") < 0)) {
+                    StringBuffer arg = new StringBuffer(k);
+                    arg.append("=");
+                    arg.append(v);
+                    if (arg.toString().indexOf(" ") < 0) {
+                        cmdAndArgs.append(arg);
+                    } else {
+                        // Spaces used as delimiter, so need to use quotes
+                        cmdAndArgs.append("\"");
+                        cmdAndArgs.append(arg);
+                        cmdAndArgs.append("\"");
+                    }
+                }
+            }
+
+            StringBuffer command = new StringBuffer(cgiExecutable);
+            command.append(" ");
+            command.append(cmdAndArgs.toString());
+            cmdAndArgs = command;
+
+            String sContentLength = (String) env.get("CONTENT_LENGTH");
+            ByteArrayOutputStream contentStream = null;
+            if(!"".equals(sContentLength)) {
+                byte[] content = new byte[Integer.parseInt(sContentLength)];
+
+                // Bugzilla 32023
+                int lenRead = 0;
+                do {
+                    int partRead = stdin.read(content,lenRead,content.length-lenRead);
+                    lenRead += partRead;
+                } while (lenRead > 0 && lenRead < content.length);
+
+                contentStream = new ByteArrayOutputStream(
+                        Integer.parseInt(sContentLength));
+                if ("POST".equals(env.get("REQUEST_METHOD"))) {
+                    String paramStr = getPostInput(params);
+                    if (paramStr != null) {
+                        byte[] paramBytes = paramStr.getBytes();
+                        contentStream.write(paramBytes);
+
+                        int contentLength = paramBytes.length;
+                        if (lenRead > 0) {
+                            String lineSep = System.getProperty("line.separator");
+                            contentStream.write(lineSep.getBytes());
+                            contentLength = lineSep.length() + lenRead;
+                        }
+                        env.put("CONTENT_LENGTH", Integer.toString(contentLength));
+                    }
+                }
+
+                if (lenRead > 0) {
+                    contentStream.write(content, 0, lenRead);
+                }
+                contentStream.close();
+            }
+
+            rt = Runtime.getRuntime();
+            proc = rt.exec(cmdAndArgs.toString(), hashToStringArray(env), wd);
+
+            if(contentStream != null) {
+                commandsStdIn = new BufferedOutputStream(proc.getOutputStream());
+                commandsStdIn.write(contentStream.toByteArray());
+                commandsStdIn.flush();
+                commandsStdIn.close();
+            }
+
+            /* we want to wait for the process to exit,  Process.waitFor()
+             * is useless in our situation; see
+             * http://developer.java.sun.com/developer/
+             *                               bugParade/bugs/4223650.html
+             */
+
+            boolean isRunning = true;
+            commandsStdErr = new BufferedReader
+                (new InputStreamReader(proc.getErrorStream()));
+            BufferedWriter servletContainerStdout = null;
+
+            try {
+                if (response.getOutputStream() != null) {
+                    servletContainerStdout =
+                        new BufferedWriter(new OutputStreamWriter
+                            (response.getOutputStream()));
+                }
+            } catch (IOException ignored) {
+                //NOOP: no output will be written
+            }
+            final BufferedReader stdErrRdr = commandsStdErr ;
+
+            new Thread() {
+                public void run () {
+                    sendToLog(stdErrRdr) ;
+                } ;
+            }.start() ;
+
+            InputStream cgiHeaderStream =
+                new HTTPHeaderInputStream(proc.getInputStream());
+            BufferedReader cgiHeaderReader =
+                new BufferedReader(new InputStreamReader(cgiHeaderStream));
+            boolean isBinaryContent = false;
+            
+            while (isRunning) {
+                try {
+                    //set headers
+                    String line = null;
+                    while (((line = cgiHeaderReader.readLine()) != null)
+                           && !("".equals(line))) {
+                        if (debug >= 2) {
+                            log("runCGI: addHeader(\"" + line + "\")");
+                        }
+                        if (line.startsWith("HTTP")) {
+                            //TODO: should set status codes (NPH support)
+                            /*
+                             * response.setStatus(getStatusCode(line));
+                             */
+                        } else if (line.indexOf(":") >= 0) {
+                            String header =
+                                line.substring(0, line.indexOf(":")).trim();
+                            String value =
+                                line.substring(line.indexOf(":") + 1).trim(); 
+                            response.addHeader(header , value);
+                            if ((header.toLowerCase().equals("content-type"))
+                                && (!value.toLowerCase().startsWith("text"))) {
+                                isBinaryContent = true;
+                            }
+                        } else {
+                            log("runCGI: bad header line \"" + line + "\"");
+                        }
+                    }
+
+                    //write output
+                    if (isBinaryContent) {
+                        byte[] bBuf = new byte[2048];
+                        OutputStream out = response.getOutputStream();
+                        cgiOutput = proc.getInputStream();
+                        while ((bufRead = cgiOutput.read(bBuf)) != -1) {
+                            if (debug >= 4) {
+                                log("runCGI: output " + bufRead +
+                                    " bytes of binary data");
+                            }
+                            out.write(bBuf, 0, bufRead);
+                        }
+                    } else {
+                        commandsStdOut = new BufferedReader
+                            (new InputStreamReader(proc.getInputStream()));
+
+                        char[] cBuf = new char[1024];
+                        try {
+                            while ((bufRead = commandsStdOut.read(cBuf)) != -1) {
+                                if (servletContainerStdout != null) {
+                                    if (debug >= 4) {
+                                        log("runCGI: write(\"" +
+                                                new String(cBuf, 0, bufRead) + "\")");
+                                    }
+                                    servletContainerStdout.write(cBuf, 0, bufRead);
+                                }
+                            }
+                        } finally {
+                            // Attempt to consume any leftover byte if something bad happens,
+                            // such as a socket disconnect on the servlet side; otherwise, the
+                            // external process could hang
+                            if (bufRead != -1) {
+                                while ((bufRead = commandsStdOut.read(cBuf)) != -1) {}
+                            }
+                        }
+    
+                        if (servletContainerStdout != null) {
+                            servletContainerStdout.flush();
+                        }
+                    }
+
+                    proc.exitValue(); // Throws exception if alive
+
+                    isRunning = false;
+
+                } catch (IllegalThreadStateException e) {
+                    try {
+                        Thread.sleep(500);
+                    } catch (InterruptedException ignored) {
+                    }
+                }
+            } //replacement for Process.waitFor()
+            // Close the output stream used
+            if (isBinaryContent) {
+                cgiOutput.close();
+            } else {
+                commandsStdOut.close();
+            }
+        }
+
+        private void sendToLog(BufferedReader rdr) {
+            String line = null;
+            int lineCount = 0 ;
+            try {
+                while ((line = rdr.readLine()) != null) {
+                    log("runCGI (stderr):" +  line) ;
+                    lineCount++ ;
+                }
+            } catch (IOException e) {
+                log("sendToLog error", e) ;
+            } finally {
+                try {
+                    rdr.close() ;
+                } catch (IOException ce) {
+                    log("sendToLog error", ce) ;
+                } ;
+            } ;
+            if ( lineCount > 0 && debug > 2) {
+                log("runCGI: " + lineCount + " lines received on stderr") ;
+            } ;
+        }
+
+
+        /**
+         * Gets a string for input to a POST cgi script
+         *
+         * @param  params   ArrayList of query parameters to be passed to
+         *                  the CGI script
+         * @return          for use as input to the CGI script
+         */
+
+        protected String getPostInput(ArrayList params) {
+            String lineSeparator = System.getProperty("line.separator");
+            StringBuffer qs = new StringBuffer("");
+            for (int i=0; i < params.size(); i++) {
+                NameValuePair nvp = (NameValuePair) this.params.get(i); 
+                String k = nvp.getName();
+                String v = nvp.getValue();
+                if ((k.indexOf("=") < 0) && (v.indexOf("=") < 0)) {
+                    qs.append(k);
+                    qs.append("=");
+                    qs.append(v);
+                    qs.append("&");
+                }
+            }
+            if (qs.length() > 0) {
+                // Remove last "&"
+                qs.setLength(qs.length() - 1);
+                return qs.toString();
+            } else {
+                return null;
+            }
+        }
+    } //class CGIRunner
+
+    /**
+     * This is a simple class for storing name-value pairs.
+     * 
+     * TODO: It might be worth moving this to the utils package there is a
+     * wider requirement for this functionality.
+     */
+    protected class NameValuePair {
+        private String name;
+        private String value;
+        
+        NameValuePair(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+        
+        protected String getName() {
+            return name;
+        }
+        
+        protected String getValue() {
+            return value;
+        }
+    }
+
+    /**
+     * This is an input stream specifically for reading HTTP headers. It reads
+     * upto and including the two blank lines terminating the headers. It
+     * allows the content to be read using bytes or characters as appropriate.
+     */
+    protected class HTTPHeaderInputStream extends InputStream {
+        private static final int STATE_CHARACTER = 0;
+        private static final int STATE_FIRST_CR = 1;
+        private static final int STATE_FIRST_LF = 2;
+        private static final int STATE_SECOND_CR = 3;
+        private static final int STATE_HEADER_END = 4;
+        
+        private InputStream input;
+        private int state;
+        
+        HTTPHeaderInputStream(InputStream theInput) {
+            input = theInput;
+            state = STATE_CHARACTER;
+        }
+
+        /**
+         * @see java.io.InputStream#read()
+         */
+        public int read() throws IOException {
+            if (state == STATE_HEADER_END) {
+                return -1;
+            }
+
+            int i = input.read();
+
+            // Update the state
+            // State machine looks like this
+            //
+            //    -------->--------
+            //   |      (CR)       |
+            //   |                 |
+            //  CR1--->---         |
+            //   |        |        |
+            //   ^(CR)    |(LF)    |
+            //   |        |        |
+            // CHAR--->--LF1--->--EOH
+            //      (LF)  |  (LF)  |
+            //            |(CR)    ^(LF)
+            //            |        |
+            //          (CR2)-->---
+            
+            if (i == 10) {
+                // LF
+                switch(state) {
+                    case STATE_CHARACTER:
+                        state = STATE_FIRST_LF;
+                        break;
+                    case STATE_FIRST_CR:
+                        state = STATE_FIRST_LF;
+                        break;
+                    case STATE_FIRST_LF:
+                    case STATE_SECOND_CR:
+                        state = STATE_HEADER_END;
+                        break;
+                }
+
+            } else if (i == 13) {
+                // CR
+                switch(state) {
+                    case STATE_CHARACTER:
+                        state = STATE_FIRST_CR;
+                        break;
+                    case STATE_FIRST_CR:
+                        state = STATE_HEADER_END;
+                        break;
+                    case STATE_FIRST_LF:
+                        state = STATE_SECOND_CR;
+                        break;
+                }
+
+            } else {
+                state = STATE_CHARACTER;
+            }
+            
+            return i;            
+        }
+    }  // class HTTPHeaderInputStream
+
+} //class CGIServlet
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/Constants.java b/container/catalina/src/share/org/apache/catalina/servlets/Constants.java
new file mode 100644
index 0000000..7909f9f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/Constants.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.servlets;
+
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.servlets";
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java b/container/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java
new file mode 100644
index 0000000..b885229
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java
@@ -0,0 +1,2205 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.servlets;
+
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.RandomAccessFile;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.naming.InitialContext;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.util.URLEncoder;
+import org.apache.naming.resources.CacheEntry;
+import org.apache.naming.resources.ProxyDirContext;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.ResourceAttributes;
+
+
+/**
+ * The default resource-serving servlet for most web applications,
+ * used to serve static resources such as HTML pages and images.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class DefaultServlet
+    extends HttpServlet {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The debugging detail level for this servlet.
+     */
+    protected int debug = 0;
+
+
+    /**
+     * The input buffer size to use when serving resources.
+     */
+    protected int input = 2048;
+
+
+    /**
+     * Should we generate directory listings?
+     */
+    protected boolean listings = true;
+
+
+    /**
+     * Read only flag. By default, it's set to true.
+     */
+    protected boolean readOnly = true;
+
+
+    /**
+     * The output buffer size to use when serving resources.
+     */
+    protected int output = 2048;
+
+
+    /**
+     * Array containing the safe characters set.
+     */
+    protected static URLEncoder urlEncoder;
+
+
+    /**
+     * Allow customized directory listing per directory.
+     */
+    protected String  localXsltFile = null;
+
+
+    /**
+     * Allow customized directory listing per instance.
+     */
+    protected String  globalXsltFile = null;
+
+
+    /**
+     * Allow a readme file to be included.
+     */
+    protected String readmeFile = null;
+
+
+    /**
+     * Proxy directory context.
+     */
+    protected ProxyDirContext resources = null;
+
+
+    /**
+     * File encoding to be used when reading static files. If none is specified
+     * the platform default is used.
+     */
+    protected String fileEncoding = null;
+    
+    
+    /**
+     * Minimum size for sendfile usage in bytes.
+     */
+    protected int sendfileSize = 48 * 1024;
+    
+    
+    // ----------------------------------------------------- Static Initializer
+
+
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    static {
+        urlEncoder = new URLEncoder();
+        urlEncoder.addSafeCharacter('-');
+        urlEncoder.addSafeCharacter('_');
+        urlEncoder.addSafeCharacter('.');
+        urlEncoder.addSafeCharacter('*');
+        urlEncoder.addSafeCharacter('/');
+    }
+
+
+    /**
+     * MIME multipart separation string
+     */
+    protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY";
+
+
+    /**
+     * JNDI resources name.
+     */
+    protected static final String RESOURCES_JNDI_NAME = "java:/comp/Resources";
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Size of file transfer buffer in bytes.
+     */
+    private static final int BUFFER_SIZE = 4096;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Finalize this servlet.
+     */
+    public void destroy() {
+    }
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init() throws ServletException {
+
+        // Set our properties from the initialization parameters
+        String value = null;
+        try {
+            value = getServletConfig().getInitParameter("debug");
+            debug = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter("input");
+            input = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter("listings");
+            listings = (new Boolean(value)).booleanValue();
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter("readonly");
+            if (value != null)
+                readOnly = (new Boolean(value)).booleanValue();
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter("output");
+            output = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter("sendfileSize");
+            sendfileSize = Integer.parseInt(value) * 1024;
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter("fileEncoding");
+            fileEncoding = value;
+        } catch (Throwable t) {
+            ;
+        }
+
+        globalXsltFile = getServletConfig().getInitParameter("globalXsltFile");
+        localXsltFile = getServletConfig().getInitParameter("localXsltFile");
+        readmeFile = getServletConfig().getInitParameter("readmeFile");
+
+        // Sanity check on the specified buffer sizes
+        if (input < 256)
+            input = 256;
+        if (output < 256)
+            output = 256;
+
+        if (debug > 0) {
+            log("DefaultServlet.init:  input buffer size=" + input +
+                ", output buffer size=" + output);
+        }
+
+        // Load the proxy dir context.
+        try {
+            resources = (ProxyDirContext) getServletContext()
+                .getAttribute(Globals.RESOURCES_ATTR);
+        } catch(ClassCastException e) {
+            // Failed : Not the right type
+        }
+        if (resources == null) {
+            try {
+                resources =
+                    (ProxyDirContext) new InitialContext()
+                    .lookup(RESOURCES_JNDI_NAME);
+            } catch (NamingException e) {
+                // Failed
+            } catch (ClassCastException e) {
+                // Failed : Not the right type
+            }
+        }
+
+        if (resources == null) {
+            throw new UnavailableException("No resources");
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return the relative path associated with this servlet.
+     *
+     * @param request The servlet request we are processing
+     */
+    protected String getRelativePath(HttpServletRequest request) {
+
+        // Are we being processed by a RequestDispatcher.include()?
+        if (request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null) {
+            String result = (String) request.getAttribute(
+                                            Globals.INCLUDE_PATH_INFO_ATTR);
+            if (result == null)
+                result = (String) request.getAttribute(
+                                            Globals.INCLUDE_SERVLET_PATH_ATTR);
+            if ((result == null) || (result.equals("")))
+                result = "/";
+            return (result);
+        }
+
+        // No, extract the desired path directly from the request
+        String result = request.getPathInfo();
+        if (result == null) {
+            result = request.getServletPath();
+        }
+        if ((result == null) || (result.equals(""))) {
+            result = "/";
+        }
+        return (result);
+
+    }
+
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doGet(HttpServletRequest request,
+                         HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Serve the requested resource, including the data content
+        try {
+            serveResource(request, response, true);
+        } catch( IOException ex ) {
+            // we probably have this check somewhere else too.
+            if( ex.getMessage() != null
+                && ex.getMessage().indexOf("Broken pipe") >= 0 ) {
+                // ignore it.
+            }
+            throw ex;
+        }
+
+    }
+
+
+    /**
+     * Process a HEAD request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doHead(HttpServletRequest request,
+                          HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Serve the requested resource, without the data content
+        serveResource(request, response, false);
+
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doPost(HttpServletRequest request,
+                          HttpServletResponse response)
+        throws IOException, ServletException {
+        doGet(request, response);
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param req The servlet request we are processing
+     * @param resp The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        boolean exists = true;
+        try {
+            resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        boolean result = true;
+
+        // Temp. content file used to support partial PUT
+        File contentFile = null;
+
+        // Input stream for temp. content file used to support partial PUT
+        FileInputStream contentFileInStream = null;
+
+        Range range = parseContentRange(req, resp);
+
+        InputStream resourceInputStream = null;
+
+        // Append data specified in ranges to existing content for this
+        // resource - create a temp. file on the local filesystem to
+        // perform this operation
+        // Assume just one range is specified for now
+        if (range != null) {
+            contentFile = executePartialPut(req, range, path);
+            resourceInputStream = new FileInputStream(contentFile);
+        } else {
+            resourceInputStream = req.getInputStream();
+        }
+
+        try {
+            Resource newResource = new Resource(resourceInputStream);
+            // FIXME: Add attributes
+            if (exists) {
+                resources.rebind(path, newResource);
+            } else {
+                resources.bind(path, newResource);
+            }
+        } catch(NamingException e) {
+            result = false;
+        }
+
+        if (result) {
+            if (exists) {
+                resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
+            } else {
+                resp.setStatus(HttpServletResponse.SC_CREATED);
+            }
+        } else {
+            resp.sendError(HttpServletResponse.SC_CONFLICT);
+        }
+
+    }
+
+
+    /**
+     * Handle a partial PUT.  New content specified in request is appended to
+     * existing content in oldRevisionContent (if present). This code does
+     * not support simultaneous partial updates to the same resource.
+     */
+    protected File executePartialPut(HttpServletRequest req, Range range,
+                                     String path)
+        throws IOException {
+
+        // Append data specified in ranges to existing content for this
+        // resource - create a temp. file on the local filesystem to
+        // perform this operation
+        File tempDir = (File) getServletContext().getAttribute
+            ("javax.servlet.context.tempdir");
+        // Convert all '/' characters to '.' in resourcePath
+        String convertedResourcePath = path.replace('/', '.');
+        File contentFile = new File(tempDir, convertedResourcePath);
+        if (contentFile.createNewFile()) {
+            // Clean up contentFile when Tomcat is terminated
+            contentFile.deleteOnExit();
+        }
+
+        RandomAccessFile randAccessContentFile =
+            new RandomAccessFile(contentFile, "rw");
+
+        Resource oldResource = null;
+        try {
+            Object obj = resources.lookup(path);
+            if (obj instanceof Resource)
+                oldResource = (Resource) obj;
+        } catch (NamingException e) {
+        }
+
+        // Copy data in oldRevisionContent to contentFile
+        if (oldResource != null) {
+            BufferedInputStream bufOldRevStream =
+                new BufferedInputStream(oldResource.streamContent(),
+                                        BUFFER_SIZE);
+
+            int numBytesRead;
+            byte[] copyBuffer = new byte[BUFFER_SIZE];
+            while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) {
+                randAccessContentFile.write(copyBuffer, 0, numBytesRead);
+            }
+
+            bufOldRevStream.close();
+        }
+
+        randAccessContentFile.setLength(range.length);
+
+        // Append data in request input stream to contentFile
+        randAccessContentFile.seek(range.start);
+        int numBytesRead;
+        byte[] transferBuffer = new byte[BUFFER_SIZE];
+        BufferedInputStream requestBufInStream =
+            new BufferedInputStream(req.getInputStream(), BUFFER_SIZE);
+        while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) {
+            randAccessContentFile.write(transferBuffer, 0, numBytesRead);
+        }
+        randAccessContentFile.close();
+        requestBufInStream.close();
+
+        return contentFile;
+
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param req The servlet request we are processing
+     * @param resp The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        boolean exists = true;
+        try {
+            resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        if (exists) {
+            boolean result = true;
+            try {
+                resources.unbind(path);
+            } catch (NamingException e) {
+                result = false;
+            }
+            if (result) {
+                resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
+            } else {
+                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+            }
+        } else {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+        }
+
+    }
+
+
+    /**
+     * Check if the conditions specified in the optional If headers are
+     * satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceAttributes The resource information
+     * @return boolean true if the resource meets all the specified conditions,
+     * and false if any of the conditions is not satisfied, in which case
+     * request processing is stopped
+     */
+    protected boolean checkIfHeaders(HttpServletRequest request,
+                                     HttpServletResponse response,
+                                     ResourceAttributes resourceAttributes)
+        throws IOException {
+
+        return checkIfMatch(request, response, resourceAttributes)
+            && checkIfModifiedSince(request, response, resourceAttributes)
+            && checkIfNoneMatch(request, response, resourceAttributes)
+            && checkIfUnmodifiedSince(request, response, resourceAttributes);
+
+    }
+
+
+    /**
+     * Get the ETag associated with a file.
+     *
+     * @param resourceAttributes The resource information
+     */
+    protected String getETag(ResourceAttributes resourceAttributes) {
+        String result = null;
+        if ((result = resourceAttributes.getETag(true)) != null) {
+            return result;
+        } else if ((result = resourceAttributes.getETag()) != null) {
+            return result;
+        } else {
+            return "W/\"" + resourceAttributes.getContentLength() + "-"
+                + resourceAttributes.getLastModified() + "\"";
+        }
+    }
+
+
+    /**
+     * URL rewriter.
+     *
+     * @param path Path which has to be rewiten
+     */
+    protected String rewriteUrl(String path) {
+        return urlEncoder.encode( path );
+    }
+
+
+    /**
+     * Display the size of a file.
+     */
+    protected void displaySize(StringBuffer buf, int filesize) {
+
+        int leftside = filesize / 1024;
+        int rightside = (filesize % 1024) / 103;  // makes 1 digit
+        // To avoid 0.0 for non-zero file, we bump to 0.1
+        if (leftside == 0 && rightside == 0 && filesize != 0)
+            rightside = 1;
+        buf.append(leftside).append(".").append(rightside);
+        buf.append(" KB");
+
+    }
+
+
+    /**
+     * Serve the specified resource, optionally including the data content.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param content Should the content be included?
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void serveResource(HttpServletRequest request,
+                                 HttpServletResponse response,
+                                 boolean content)
+        throws IOException, ServletException {
+
+        // Identify the requested resource path
+        String path = getRelativePath(request);
+        if (debug > 0) {
+            if (content)
+                log("DefaultServlet.serveResource:  Serving resource '" +
+                    path + "' headers and data");
+            else
+                log("DefaultServlet.serveResource:  Serving resource '" +
+                    path + "' headers only");
+        }
+
+        CacheEntry cacheEntry = resources.lookupCache(path);
+
+        if (!cacheEntry.exists) {
+            // Check if we're included so we can return the appropriate 
+            // missing resource name in the error
+            String requestUri = (String) request.getAttribute(
+                                            Globals.INCLUDE_REQUEST_URI_ATTR);
+            if (requestUri == null) {
+                requestUri = request.getRequestURI();
+            } else {
+                // We're included, and the response.sendError() below is going
+                // to be ignored by the resource that is including us.
+                // Therefore, the only way we can let the including resource
+                // know is by including warning message in response
+                response.getWriter().write(
+                    sm.getString("defaultServlet.missingResource",
+                    requestUri));
+            }
+
+            response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                               requestUri);
+            return;
+        }
+
+        // If the resource is not a collection, and the resource path
+        // ends with "/" or "\", return NOT FOUND
+        if (cacheEntry.context == null) {
+            if (path.endsWith("/") || (path.endsWith("\\"))) {
+                // Check if we're included so we can return the appropriate 
+                // missing resource name in the error
+                String requestUri = (String) request.getAttribute(
+                                            Globals.INCLUDE_REQUEST_URI_ATTR);
+                if (requestUri == null) {
+                    requestUri = request.getRequestURI();
+                }
+                response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                   requestUri);
+                return;
+            }
+        }
+
+        // Check if the conditions specified in the optional If headers are
+        // satisfied.
+        if (cacheEntry.context == null) {
+
+            // Checking If headers
+            boolean included =
+                (request.getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR) != null);
+            if (!included
+                && !checkIfHeaders(request, response, cacheEntry.attributes)) {
+                return;
+            }
+
+        }
+
+        // Find content type.
+        String contentType = cacheEntry.attributes.getMimeType();
+        if (contentType == null) {
+            contentType = getServletContext().getMimeType(cacheEntry.name);
+            cacheEntry.attributes.setMimeType(contentType);
+        }
+
+        Vector ranges = null;
+        long contentLength = -1L;
+
+        if (cacheEntry.context != null) {
+
+            // Skip directory listings if we have been configured to
+            // suppress them
+            if (!listings) {
+                response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                   request.getRequestURI());
+                return;
+            }
+            contentType = "text/html;charset=UTF-8";
+
+        } else {
+
+            // Parse range specifier
+
+            ranges = parseRange(request, response, cacheEntry.attributes);
+
+            // ETag header
+            response.setHeader("ETag", getETag(cacheEntry.attributes));
+
+            // Last-Modified header
+            response.setHeader("Last-Modified",
+                    cacheEntry.attributes.getLastModifiedHttp());
+
+            // Get content length
+            contentLength = cacheEntry.attributes.getContentLength();
+            // Special case for zero length files, which would cause a
+            // (silent) ISE when setting the output buffer size
+            if (contentLength == 0L) {
+                content = false;
+            }
+
+        }
+
+        ServletOutputStream ostream = null;
+        PrintWriter writer = null;
+
+        if (content) {
+
+            // Trying to retrieve the servlet output stream
+
+            try {
+                ostream = response.getOutputStream();
+            } catch (IllegalStateException e) {
+                // If it fails, we try to get a Writer instead if we're
+                // trying to serve a text file
+                if ( (contentType == null)
+                     || (contentType.startsWith("text")) ) {
+                    writer = response.getWriter();
+                } else {
+                    throw e;
+                }
+            }
+
+        }
+
+        if ( (cacheEntry.context != null) ||
+             ( ((ranges == null) || (ranges.isEmpty()))
+               && (request.getHeader("Range") == null) ) ) {
+
+            // Set the appropriate output headers
+            if (contentType != null) {
+                if (debug > 0)
+                    log("DefaultServlet.serveFile:  contentType='" +
+                        contentType + "'");
+                response.setContentType(contentType);
+            }
+            if ((cacheEntry.resource != null) && (contentLength >= 0)) {
+                if (debug > 0)
+                    log("DefaultServlet.serveFile:  contentLength=" +
+                        contentLength);
+                if (contentLength < Integer.MAX_VALUE) {
+                    response.setContentLength((int) contentLength);
+                } else {
+                    // Set the content-length as String to be able to use a long
+                    response.setHeader("content-length", "" + contentLength);
+                }
+            }
+
+            InputStream renderResult = null;
+            if (cacheEntry.context != null) {
+
+                if (content) {
+                    // Serve the directory browser
+                    renderResult =
+                        render(request.getContextPath(), cacheEntry);
+                }
+
+            }
+
+            // Copy the input stream to our output stream (if requested)
+            if (content) {
+                try {
+                    response.setBufferSize(output);
+                } catch (IllegalStateException e) {
+                    // Silent catch
+                }
+                if (ostream != null) {
+                    if (!checkSendfile(request, response, cacheEntry, contentLength, null))
+                        copy(cacheEntry, renderResult, ostream);
+                } else {
+                    copy(cacheEntry, renderResult, writer);
+                }
+            }
+
+        } else {
+
+            if ((ranges == null) || (ranges.isEmpty()))
+                return;
+
+            // Partial content response.
+
+            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
+
+            if (ranges.size() == 1) {
+
+                Range range = (Range) ranges.elementAt(0);
+                response.addHeader("Content-Range", "bytes "
+                                   + range.start
+                                   + "-" + range.end + "/"
+                                   + range.length);
+                long length = range.end - range.start + 1;
+                if (length < Integer.MAX_VALUE) {
+                    response.setContentLength((int) length);
+                } else {
+                    // Set the content-length as String to be able to use a long
+                    response.setHeader("content-length", "" + length);
+                }
+
+                if (contentType != null) {
+                    if (debug > 0)
+                        log("DefaultServlet.serveFile:  contentType='" +
+                            contentType + "'");
+                    response.setContentType(contentType);
+                }
+
+                if (content) {
+                    try {
+                        response.setBufferSize(output);
+                    } catch (IllegalStateException e) {
+                        // Silent catch
+                    }
+                    if (ostream != null) {
+                        if (!checkSendfile(request, response, cacheEntry, range.end - range.start + 1, range))
+                            copy(cacheEntry, ostream, range);
+                    } else {
+                        copy(cacheEntry, writer, range);
+                    }
+                }
+
+            } else {
+
+                response.setContentType("multipart/byteranges; boundary="
+                                        + mimeSeparation);
+
+                if (content) {
+                    try {
+                        response.setBufferSize(output);
+                    } catch (IllegalStateException e) {
+                        // Silent catch
+                    }
+                    if (ostream != null) {
+                        copy(cacheEntry, ostream, ranges.elements(),
+                             contentType);
+                    } else {
+                        copy(cacheEntry, writer, ranges.elements(),
+                             contentType);
+                    }
+                }
+
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Parse the content-range header.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @return Range
+     */
+    protected Range parseContentRange(HttpServletRequest request,
+                                      HttpServletResponse response)
+        throws IOException {
+
+        // Retrieving the content-range header (if any is specified
+        String rangeHeader = request.getHeader("Content-Range");
+
+        if (rangeHeader == null)
+            return null;
+
+        // bytes is the only range unit supported
+        if (!rangeHeader.startsWith("bytes")) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        rangeHeader = rangeHeader.substring(6).trim();
+
+        int dashPos = rangeHeader.indexOf('-');
+        int slashPos = rangeHeader.indexOf('/');
+
+        if (dashPos == -1) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        if (slashPos == -1) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        Range range = new Range();
+
+        try {
+            range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
+            range.end =
+                Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
+            range.length = Long.parseLong
+                (rangeHeader.substring(slashPos + 1, rangeHeader.length()));
+        } catch (NumberFormatException e) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        if (!range.validate()) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        return range;
+
+    }
+
+
+    /**
+     * Parse the range header.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @return Vector of ranges
+     */
+    protected Vector parseRange(HttpServletRequest request,
+                                HttpServletResponse response,
+                                ResourceAttributes resourceAttributes)
+        throws IOException {
+
+        // Checking If-Range
+        String headerValue = request.getHeader("If-Range");
+
+        if (headerValue != null) {
+
+            long headerValueTime = (-1L);
+            try {
+                headerValueTime = request.getDateHeader("If-Range");
+            } catch (Exception e) {
+                ;
+            }
+
+            String eTag = getETag(resourceAttributes);
+            long lastModified = resourceAttributes.getLastModified();
+
+            if (headerValueTime == (-1L)) {
+
+                // If the ETag the client gave does not match the entity
+                // etag, then the entire entity is returned.
+                if (!eTag.equals(headerValue.trim()))
+                    return null;
+
+            } else {
+
+                // If the timestamp of the entity the client got is older than
+                // the last modification date of the entity, the entire entity
+                // is returned.
+                if (lastModified > (headerValueTime + 1000))
+                    return null;
+
+            }
+
+        }
+
+        long fileLength = resourceAttributes.getContentLength();
+
+        if (fileLength == 0)
+            return null;
+
+        // Retrieving the range header (if any is specified
+        String rangeHeader = request.getHeader("Range");
+
+        if (rangeHeader == null)
+            return null;
+        // bytes is the only range unit supported (and I don't see the point
+        // of adding new ones).
+        if (!rangeHeader.startsWith("bytes")) {
+            response.addHeader("Content-Range", "bytes */" + fileLength);
+            response.sendError
+                (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+            return null;
+        }
+
+        rangeHeader = rangeHeader.substring(6);
+
+        // Vector which will contain all the ranges which are successfully
+        // parsed.
+        Vector result = new Vector();
+        StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
+
+        // Parsing the range list
+        while (commaTokenizer.hasMoreTokens()) {
+            String rangeDefinition = commaTokenizer.nextToken().trim();
+
+            Range currentRange = new Range();
+            currentRange.length = fileLength;
+
+            int dashPos = rangeDefinition.indexOf('-');
+
+            if (dashPos == -1) {
+                response.addHeader("Content-Range", "bytes */" + fileLength);
+                response.sendError
+                    (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+                return null;
+            }
+
+            if (dashPos == 0) {
+
+                try {
+                    long offset = Long.parseLong(rangeDefinition);
+                    currentRange.start = fileLength + offset;
+                    currentRange.end = fileLength - 1;
+                } catch (NumberFormatException e) {
+                    response.addHeader("Content-Range",
+                                       "bytes */" + fileLength);
+                    response.sendError
+                        (HttpServletResponse
+                         .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+                    return null;
+                }
+
+            } else {
+
+                try {
+                    currentRange.start = Long.parseLong
+                        (rangeDefinition.substring(0, dashPos));
+                    if (dashPos < rangeDefinition.length() - 1)
+                        currentRange.end = Long.parseLong
+                            (rangeDefinition.substring
+                             (dashPos + 1, rangeDefinition.length()));
+                    else
+                        currentRange.end = fileLength - 1;
+                } catch (NumberFormatException e) {
+                    response.addHeader("Content-Range",
+                                       "bytes */" + fileLength);
+                    response.sendError
+                        (HttpServletResponse
+                         .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+                    return null;
+                }
+
+            }
+
+            if (!currentRange.validate()) {
+                response.addHeader("Content-Range", "bytes */" + fileLength);
+                response.sendError
+                    (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+                return null;
+            }
+
+            result.addElement(currentRange);
+        }
+
+        return result;
+    }
+
+
+
+    /**
+     *  Decide which way to render. HTML or XML.
+     */
+    protected InputStream render
+        (String contextPath, CacheEntry cacheEntry) {
+        InputStream xsltInputStream =
+            findXsltInputStream(cacheEntry.context);
+
+        if (xsltInputStream==null) {
+            return renderHtml(contextPath, cacheEntry);
+        } else {
+            return renderXml(contextPath, cacheEntry, xsltInputStream);
+        }
+
+    }
+
+    /**
+     * Return an InputStream to an HTML representation of the contents
+     * of this directory.
+     *
+     * @param contextPath Context path to which our internal paths are
+     *  relative
+     */
+    protected InputStream renderXml(String contextPath,
+                                    CacheEntry cacheEntry,
+                                    InputStream xsltInputStream) {
+
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("<?xml version=\"1.0\"?>");
+        sb.append("<listing ");
+        sb.append(" contextPath='");
+        sb.append(contextPath);
+        sb.append("'");
+        sb.append(" directory='");
+        sb.append(cacheEntry.name);
+        sb.append("' ");
+        sb.append(" hasParent='").append(!cacheEntry.name.equals("/"));
+        sb.append("'>");
+
+        sb.append("<entries>");
+
+        try {
+
+            // Render the directory entries within this directory
+            DirContext directory = cacheEntry.context;
+            NamingEnumeration enumeration = resources.list(cacheEntry.name);
+            while (enumeration.hasMoreElements()) {
+
+                NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
+                String resourceName = ncPair.getName();
+                String trimmed = resourceName/*.substring(trim)*/;
+                if (trimmed.equalsIgnoreCase("WEB-INF") ||
+                    trimmed.equalsIgnoreCase("META-INF") ||
+                    trimmed.equalsIgnoreCase(localXsltFile))
+                    continue;
+
+                CacheEntry childCacheEntry =
+                    resources.lookupCache(cacheEntry.name + resourceName);
+                if (!childCacheEntry.exists) {
+                    continue;
+                }
+
+                sb.append("<entry");
+                sb.append(" type='")
+                  .append((childCacheEntry.context != null)?"dir":"file")
+                  .append("'");
+                sb.append(" urlPath='")
+                  .append(rewriteUrl(contextPath))
+                  .append(rewriteUrl(cacheEntry.name + resourceName))
+                  .append((childCacheEntry.context != null)?"/":"")
+                  .append("'");
+                if (childCacheEntry.resource != null) {
+                    sb.append(" size='")
+                      .append(renderSize(childCacheEntry.attributes.getContentLength()))
+                      .append("'");
+                }
+                sb.append(" date='")
+                  .append(childCacheEntry.attributes.getLastModifiedHttp())
+                  .append("'");
+
+                sb.append(">");
+                sb.append(trimmed);
+                if (childCacheEntry.context != null)
+                    sb.append("/");
+                sb.append("</entry>");
+
+            }
+
+        } catch (NamingException e) {
+            // Something went wrong
+            e.printStackTrace();
+        }
+
+        sb.append("</entries>");
+
+        String readme = getReadme(cacheEntry.context);
+
+        if (readme!=null) {
+            sb.append("<readme><![CDATA[");
+            sb.append(readme);
+            sb.append("]]></readme>");
+        }
+
+
+        sb.append("</listing>");
+
+
+        try {
+            TransformerFactory tFactory = TransformerFactory.newInstance();
+            Source xmlSource = new StreamSource(new StringReader(sb.toString()));
+            Source xslSource = new StreamSource(xsltInputStream);
+            Transformer transformer = tFactory.newTransformer(xslSource);
+
+            ByteArrayOutputStream stream = new ByteArrayOutputStream();
+            OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
+            StreamResult out = new StreamResult(osWriter);
+            transformer.transform(xmlSource, out);
+            osWriter.flush();
+            return (new ByteArrayInputStream(stream.toByteArray()));
+        } catch (Exception e) {
+            log("directory transform failure: " + e.getMessage());
+            return renderHtml(contextPath, cacheEntry);
+        }
+    }
+
+    /**
+     * Return an InputStream to an HTML representation of the contents
+     * of this directory.
+     *
+     * @param contextPath Context path to which our internal paths are
+     *  relative
+     */
+    protected InputStream renderHtml
+        (String contextPath, CacheEntry cacheEntry) {
+
+        String name = cacheEntry.name;
+
+        // Number of characters to trim from the beginnings of filenames
+        int trim = name.length();
+        if (!name.endsWith("/"))
+            trim += 1;
+        if (name.equals("/"))
+            trim = 1;
+
+        // Prepare a writer to a buffered area
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        OutputStreamWriter osWriter = null;
+        try {
+            osWriter = new OutputStreamWriter(stream, "UTF8");
+        } catch (Exception e) {
+            // Should never happen
+            osWriter = new OutputStreamWriter(stream);
+        }
+        PrintWriter writer = new PrintWriter(osWriter);
+
+        StringBuffer sb = new StringBuffer();
+
+        // Render the page header
+        sb.append("<html>\r\n");
+        sb.append("<head>\r\n");
+        sb.append("<title>");
+        sb.append(sm.getString("directory.title", name));
+        sb.append("</title>\r\n");
+        sb.append("<STYLE><!--");
+        sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
+        sb.append("--></STYLE> ");
+        sb.append("</head>\r\n");
+        sb.append("<body>");
+        sb.append("<h1>");
+        sb.append(sm.getString("directory.title", name));
+
+        // Render the link to our parent (if required)
+        String parentDirectory = name;
+        if (parentDirectory.endsWith("/")) {
+            parentDirectory =
+                parentDirectory.substring(0, parentDirectory.length() - 1);
+        }
+        int slash = parentDirectory.lastIndexOf('/');
+        if (slash >= 0) {
+            String parent = name.substring(0, slash);
+            sb.append(" - <a href=\"");
+            sb.append(rewriteUrl(contextPath));
+            if (parent.equals(""))
+                parent = "/";
+            sb.append(rewriteUrl(parent));
+            if (!parent.endsWith("/"))
+                sb.append("/");
+            sb.append("\">");
+            sb.append("<b>");
+            sb.append(sm.getString("directory.parent", parent));
+            sb.append("</b>");
+            sb.append("</a>");
+        }
+
+        sb.append("</h1>");
+        sb.append("<HR size=\"1\" noshade=\"noshade\">");
+
+        sb.append("<table width=\"100%\" cellspacing=\"0\"" +
+                     " cellpadding=\"5\" align=\"center\">\r\n");
+
+        // Render the column headings
+        sb.append("<tr>\r\n");
+        sb.append("<td align=\"left\"><font size=\"+1\"><strong>");
+        sb.append(sm.getString("directory.filename"));
+        sb.append("</strong></font></td>\r\n");
+        sb.append("<td align=\"center\"><font size=\"+1\"><strong>");
+        sb.append(sm.getString("directory.size"));
+        sb.append("</strong></font></td>\r\n");
+        sb.append("<td align=\"right\"><font size=\"+1\"><strong>");
+        sb.append(sm.getString("directory.lastModified"));
+        sb.append("</strong></font></td>\r\n");
+        sb.append("</tr>");
+
+        try {
+
+            // Render the directory entries within this directory
+            DirContext directory = cacheEntry.context;
+            NamingEnumeration enumeration = resources.list(cacheEntry.name);
+            boolean shade = false;
+            while (enumeration.hasMoreElements()) {
+
+                NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
+                String resourceName = ncPair.getName();
+                String trimmed = resourceName/*.substring(trim)*/;
+                if (trimmed.equalsIgnoreCase("WEB-INF") ||
+                    trimmed.equalsIgnoreCase("META-INF"))
+                    continue;
+
+                CacheEntry childCacheEntry =
+                    resources.lookupCache(cacheEntry.name + resourceName);
+                if (!childCacheEntry.exists) {
+                    continue;
+                }
+
+                sb.append("<tr");
+                if (shade)
+                    sb.append(" bgcolor=\"#eeeeee\"");
+                sb.append(">\r\n");
+                shade = !shade;
+
+                sb.append("<td align=\"left\">&nbsp;&nbsp;\r\n");
+                sb.append("<a href=\"");
+                sb.append(rewriteUrl(contextPath));
+                resourceName = rewriteUrl(name + resourceName);
+                sb.append(resourceName);
+                if (childCacheEntry.context != null)
+                    sb.append("/");
+                sb.append("\"><tt>");
+                sb.append(trimmed);
+                if (childCacheEntry.context != null)
+                    sb.append("/");
+                sb.append("</tt></a></td>\r\n");
+
+                sb.append("<td align=\"right\"><tt>");
+                if (childCacheEntry.context != null)
+                    sb.append("&nbsp;");
+                else
+                    sb.append(renderSize(childCacheEntry.attributes.getContentLength()));
+                sb.append("</tt></td>\r\n");
+
+                sb.append("<td align=\"right\"><tt>");
+                sb.append(childCacheEntry.attributes.getLastModifiedHttp());
+                sb.append("</tt></td>\r\n");
+
+                sb.append("</tr>\r\n");
+            }
+
+        } catch (NamingException e) {
+            // Something went wrong
+            e.printStackTrace();
+        }
+
+        // Render the page footer
+        sb.append("</table>\r\n");
+
+        sb.append("<HR size=\"1\" noshade=\"noshade\">");
+
+        String readme = getReadme(cacheEntry.context);
+        if (readme!=null) {
+            sb.append(readme);
+            sb.append("<HR size=\"1\" noshade=\"noshade\">");
+        }
+
+        sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
+        sb.append("</body>\r\n");
+        sb.append("</html>\r\n");
+
+        // Return an input stream to the underlying bytes
+        writer.write(sb.toString());
+        writer.flush();
+        return (new ByteArrayInputStream(stream.toByteArray()));
+
+    }
+
+
+    /**
+     * Render the specified file size (in bytes).
+     *
+     * @param size File size (in bytes)
+     */
+    protected String renderSize(long size) {
+
+        long leftSide = size / 1024;
+        long rightSide = (size % 1024) / 103;   // Makes 1 digit
+        if ((leftSide == 0) && (rightSide == 0) && (size > 0))
+            rightSide = 1;
+
+        return ("" + leftSide + "." + rightSide + " kb");
+
+    }
+
+
+    /**
+     * Get the readme file as a string.
+     */
+    protected String getReadme(DirContext directory) {
+        if (readmeFile!=null) {
+            try {
+                Object obj = directory.lookup(readmeFile);
+
+                if (obj!=null && obj instanceof Resource) {
+                    StringWriter buffer = new StringWriter();
+                    InputStream is = ((Resource)obj).streamContent();
+                    copyRange(new InputStreamReader(is),
+                              new PrintWriter(buffer));
+
+                    return buffer.toString();
+                 }
+             } catch(Throwable e) {
+                 ; /* Should only be IOException or NamingException
+                    * can be ignored
+                    */
+             }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Return the xsl template inputstream (if possible)
+     */
+    protected InputStream findXsltInputStream(DirContext directory) {
+
+        if (localXsltFile!=null) {
+            try {
+                Object obj = directory.lookup(localXsltFile);
+                if (obj!=null && obj instanceof Resource) {
+                    InputStream is = ((Resource)obj).streamContent();
+                    if (is!=null)
+                        return is;
+                }
+             } catch(Throwable e) {
+                 ; /* Should only be IOException or NamingException
+                    * can be ignored
+                    */
+             }
+        }
+
+        /*  Open and read in file in one fell swoop to reduce chance
+         *  chance of leaving handle open.
+         */
+        if (globalXsltFile!=null) {
+            FileInputStream fis = null;
+
+            try {
+                File f = new File(globalXsltFile);
+                if (f.exists()){
+                    fis =new FileInputStream(f);
+                    byte b[] = new byte[(int)f.length()]; /* danger! */
+                    fis.read(b);
+                    return new ByteArrayInputStream(b);
+                }
+            } catch(Throwable e) {
+                log("This shouldn't happen (?)...", e);
+                return null;
+            } finally {
+                try {
+                    if (fis!=null)
+                        fis.close();
+                } catch(Throwable e){
+                    ;
+                }
+            }
+        }
+
+        return null;
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Check if sendfile can be used.
+     */
+    private boolean checkSendfile(HttpServletRequest request,
+                                  HttpServletResponse response,
+                                  CacheEntry entry,
+                                  long length, Range range) {
+        if ((sendfileSize > 0)
+            && (entry.resource != null)
+            && ((length > sendfileSize) || (entry.resource.getContent() == null))
+            && (entry.attributes.getCanonicalPath() != null)
+            && (Boolean.TRUE == request.getAttribute("org.apache.tomcat.sendfile.support"))
+            && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade"))
+            && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))) {
+            request.setAttribute("org.apache.tomcat.sendfile.filename", entry.attributes.getCanonicalPath());
+            if (range == null) {
+                request.setAttribute("org.apache.tomcat.sendfile.start", new Long(0L));
+                request.setAttribute("org.apache.tomcat.sendfile.end", new Long(length));
+            } else {
+                request.setAttribute("org.apache.tomcat.sendfile.start", new Long(range.start));
+                request.setAttribute("org.apache.tomcat.sendfile.end", new Long(range.end + 1));
+            }
+            request.setAttribute("org.apache.tomcat.sendfile.token", this);
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    
+    /**
+     * Check if the if-match condition is satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceInfo File object
+     * @return boolean true if the resource meets the specified condition,
+     * and false if the condition is not satisfied, in which case request
+     * processing is stopped
+     */
+    private boolean checkIfMatch(HttpServletRequest request,
+                                 HttpServletResponse response,
+                                 ResourceAttributes resourceAttributes)
+        throws IOException {
+
+        String eTag = getETag(resourceAttributes);
+        String headerValue = request.getHeader("If-Match");
+        if (headerValue != null) {
+            if (headerValue.indexOf('*') == -1) {
+
+                StringTokenizer commaTokenizer = new StringTokenizer
+                    (headerValue, ",");
+                boolean conditionSatisfied = false;
+
+                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
+                    String currentToken = commaTokenizer.nextToken();
+                    if (currentToken.trim().equals(eTag))
+                        conditionSatisfied = true;
+                }
+
+                // If none of the given ETags match, 412 Precodition failed is
+                // sent back
+                if (!conditionSatisfied) {
+                    response.sendError
+                        (HttpServletResponse.SC_PRECONDITION_FAILED);
+                    return false;
+                }
+
+            }
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Check if the if-modified-since condition is satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceInfo File object
+     * @return boolean true if the resource meets the specified condition,
+     * and false if the condition is not satisfied, in which case request
+     * processing is stopped
+     */
+    private boolean checkIfModifiedSince(HttpServletRequest request,
+                                         HttpServletResponse response,
+                                         ResourceAttributes resourceAttributes)
+        throws IOException {
+        try {
+            long headerValue = request.getDateHeader("If-Modified-Since");
+            long lastModified = resourceAttributes.getLastModified();
+            if (headerValue != -1) {
+
+                // If an If-None-Match header has been specified, if modified since
+                // is ignored.
+                if ((request.getHeader("If-None-Match") == null)
+                    && (lastModified <= headerValue + 1000)) {
+                    // The entity has not been modified since the date
+                    // specified by the client. This is not an error case.
+                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+                    return false;
+                }
+            }
+        } catch(IllegalArgumentException illegalArgument) {
+            return true;
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Check if the if-none-match condition is satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceInfo File object
+     * @return boolean true if the resource meets the specified condition,
+     * and false if the condition is not satisfied, in which case request
+     * processing is stopped
+     */
+    private boolean checkIfNoneMatch(HttpServletRequest request,
+                                     HttpServletResponse response,
+                                     ResourceAttributes resourceAttributes)
+        throws IOException {
+
+        String eTag = getETag(resourceAttributes);
+        String headerValue = request.getHeader("If-None-Match");
+        if (headerValue != null) {
+
+            boolean conditionSatisfied = false;
+
+            if (!headerValue.equals("*")) {
+
+                StringTokenizer commaTokenizer =
+                    new StringTokenizer(headerValue, ",");
+
+                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
+                    String currentToken = commaTokenizer.nextToken();
+                    if (currentToken.trim().equals(eTag))
+                        conditionSatisfied = true;
+                }
+
+            } else {
+                conditionSatisfied = true;
+            }
+
+            if (conditionSatisfied) {
+
+                // For GET and HEAD, we should respond with
+                // 304 Not Modified.
+                // For every other method, 412 Precondition Failed is sent
+                // back.
+                if ( ("GET".equals(request.getMethod()))
+                     || ("HEAD".equals(request.getMethod())) ) {
+                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+                    return false;
+                } else {
+                    response.sendError
+                        (HttpServletResponse.SC_PRECONDITION_FAILED);
+                    return false;
+                }
+            }
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Check if the if-unmodified-since condition is satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceInfo File object
+     * @return boolean true if the resource meets the specified condition,
+     * and false if the condition is not satisfied, in which case request
+     * processing is stopped
+     */
+    private boolean checkIfUnmodifiedSince(HttpServletRequest request,
+                                           HttpServletResponse response,
+                                           ResourceAttributes resourceAttributes)
+        throws IOException {
+        try {
+            long lastModified = resourceAttributes.getLastModified();
+            long headerValue = request.getDateHeader("If-Unmodified-Since");
+            if (headerValue != -1) {
+                if ( lastModified > (headerValue + 1000)) {
+                    // The entity has not been modified since the date
+                    // specified by the client. This is not an error case.
+                    response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+                    return false;
+                }
+            }
+        } catch(IllegalArgumentException illegalArgument) {
+            return true;
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The resource information
+     * @param ostream The output stream to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    private void copy(CacheEntry cacheEntry, InputStream is,
+                      ServletOutputStream ostream)
+        throws IOException {
+
+        IOException exception = null;
+        InputStream resourceInputStream = null;
+
+        // Optimization: If the binary content has already been loaded, send
+        // it directly
+        if (cacheEntry.resource != null) {
+            byte buffer[] = cacheEntry.resource.getContent();
+            if (buffer != null) {
+                ostream.write(buffer, 0, buffer.length);
+                return;
+            }
+            resourceInputStream = cacheEntry.resource.streamContent();
+        } else {
+            resourceInputStream = is;
+        }
+
+        InputStream istream = new BufferedInputStream
+            (resourceInputStream, input);
+
+        // Copy the input stream to the output stream
+        exception = copyRange(istream, ostream);
+
+        // Clean up the input stream
+        try {
+            istream.close();
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The resource info
+     * @param writer The writer to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    private void copy(CacheEntry cacheEntry, InputStream is, PrintWriter writer)
+        throws IOException {
+
+        IOException exception = null;
+
+        InputStream resourceInputStream = null;
+        if (cacheEntry.resource != null) {
+            resourceInputStream = cacheEntry.resource.streamContent();
+        } else {
+            resourceInputStream = is;
+        }
+
+        Reader reader;
+        if (fileEncoding == null) {
+            reader = new InputStreamReader(resourceInputStream);
+        } else {
+            reader = new InputStreamReader(resourceInputStream,
+                                           fileEncoding);
+        }
+
+        // Copy the input stream to the output stream
+        exception = copyRange(reader, writer);
+
+        // Clean up the reader
+        try {
+            reader.close();
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The ResourceInfo object
+     * @param ostream The output stream to write to
+     * @param range Range the client wanted to retrieve
+     * @exception IOException if an input/output error occurs
+     */
+    private void copy(CacheEntry cacheEntry, ServletOutputStream ostream,
+                      Range range)
+        throws IOException {
+
+        IOException exception = null;
+
+        InputStream resourceInputStream = cacheEntry.resource.streamContent();
+        InputStream istream =
+            new BufferedInputStream(resourceInputStream, input);
+        exception = copyRange(istream, ostream, range.start, range.end);
+
+        // Clean up the input stream
+        try {
+            istream.close();
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The ResourceInfo object
+     * @param writer The writer to write to
+     * @param range Range the client wanted to retrieve
+     * @exception IOException if an input/output error occurs
+     */
+    private void copy(CacheEntry cacheEntry, PrintWriter writer,
+                      Range range)
+        throws IOException {
+
+        IOException exception = null;
+
+        InputStream resourceInputStream = cacheEntry.resource.streamContent();
+
+        Reader reader;
+        if (fileEncoding == null) {
+            reader = new InputStreamReader(resourceInputStream);
+        } else {
+            reader = new InputStreamReader(resourceInputStream,
+                                           fileEncoding);
+        }
+
+        exception = copyRange(reader, writer, range.start, range.end);
+
+        // Clean up the input stream
+        try {
+            reader.close();
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The ResourceInfo object
+     * @param ostream The output stream to write to
+     * @param ranges Enumeration of the ranges the client wanted to retrieve
+     * @param contentType Content type of the resource
+     * @exception IOException if an input/output error occurs
+     */
+    private void copy(CacheEntry cacheEntry, ServletOutputStream ostream,
+                      Enumeration ranges, String contentType)
+        throws IOException {
+
+        IOException exception = null;
+
+        while ( (exception == null) && (ranges.hasMoreElements()) ) {
+
+            InputStream resourceInputStream = cacheEntry.resource.streamContent();
+            InputStream istream =
+                new BufferedInputStream(resourceInputStream, input);
+
+            Range currentRange = (Range) ranges.nextElement();
+
+            // Writing MIME header.
+            ostream.println();
+            ostream.println("--" + mimeSeparation);
+            if (contentType != null)
+                ostream.println("Content-Type: " + contentType);
+            ostream.println("Content-Range: bytes " + currentRange.start
+                           + "-" + currentRange.end + "/"
+                           + currentRange.length);
+            ostream.println();
+
+            // Printing content
+            exception = copyRange(istream, ostream, currentRange.start,
+                                  currentRange.end);
+
+            try {
+                istream.close();
+            } catch (Throwable t) {
+                ;
+            }
+
+        }
+
+        ostream.println();
+        ostream.print("--" + mimeSeparation + "--");
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The ResourceInfo object
+     * @param writer The writer to write to
+     * @param ranges Enumeration of the ranges the client wanted to retrieve
+     * @param contentType Content type of the resource
+     * @exception IOException if an input/output error occurs
+     */
+    private void copy(CacheEntry cacheEntry, PrintWriter writer,
+                      Enumeration ranges, String contentType)
+        throws IOException {
+
+        IOException exception = null;
+
+        while ( (exception == null) && (ranges.hasMoreElements()) ) {
+
+            InputStream resourceInputStream = cacheEntry.resource.streamContent();
+            
+            Reader reader;
+            if (fileEncoding == null) {
+                reader = new InputStreamReader(resourceInputStream);
+            } else {
+                reader = new InputStreamReader(resourceInputStream,
+                                               fileEncoding);
+            }
+
+            Range currentRange = (Range) ranges.nextElement();
+
+            // Writing MIME header.
+            writer.println();
+            writer.println("--" + mimeSeparation);
+            if (contentType != null)
+                writer.println("Content-Type: " + contentType);
+            writer.println("Content-Range: bytes " + currentRange.start
+                           + "-" + currentRange.end + "/"
+                           + currentRange.length);
+            writer.println();
+
+            // Printing content
+            exception = copyRange(reader, writer, currentRange.start,
+                                  currentRange.end);
+
+            try {
+                reader.close();
+            } catch (Throwable t) {
+                ;
+            }
+
+        }
+
+        writer.println();
+        writer.print("--" + mimeSeparation + "--");
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param istream The input stream to read from
+     * @param ostream The output stream to write to
+     * @return Exception which occurred during processing
+     */
+    private IOException copyRange(InputStream istream,
+                                  ServletOutputStream ostream) {
+
+        // Copy the input stream to the output stream
+        IOException exception = null;
+        byte buffer[] = new byte[input];
+        int len = buffer.length;
+        while (true) {
+            try {
+                len = istream.read(buffer);
+                if (len == -1)
+                    break;
+                ostream.write(buffer, 0, len);
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+                break;
+            }
+        }
+        return exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param reader The reader to read from
+     * @param writer The writer to write to
+     * @return Exception which occurred during processing
+     */
+    private IOException copyRange(Reader reader, PrintWriter writer) {
+
+        // Copy the input stream to the output stream
+        IOException exception = null;
+        char buffer[] = new char[input];
+        int len = buffer.length;
+        while (true) {
+            try {
+                len = reader.read(buffer);
+                if (len == -1)
+                    break;
+                writer.write(buffer, 0, len);
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+                break;
+            }
+        }
+        return exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param istream The input stream to read from
+     * @param ostream The output stream to write to
+     * @param start Start of the range which will be copied
+     * @param end End of the range which will be copied
+     * @return Exception which occurred during processing
+     */
+    private IOException copyRange(InputStream istream,
+                                  ServletOutputStream ostream,
+                                  long start, long end) {
+
+        if (debug > 10)
+            log("Serving bytes:" + start + "-" + end);
+
+        try {
+            istream.skip(start);
+        } catch (IOException e) {
+            return e;
+        }
+
+        IOException exception = null;
+        long bytesToRead = end - start + 1;
+
+        byte buffer[] = new byte[input];
+        int len = buffer.length;
+        while ( (bytesToRead > 0) && (len >= buffer.length)) {
+            try {
+                len = istream.read(buffer);
+                if (bytesToRead >= len) {
+                    ostream.write(buffer, 0, len);
+                    bytesToRead -= len;
+                } else {
+                    ostream.write(buffer, 0, (int) bytesToRead);
+                    bytesToRead = 0;
+                }
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+            }
+            if (len < buffer.length)
+                break;
+        }
+
+        return exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param reader The reader to read from
+     * @param writer The writer to write to
+     * @param start Start of the range which will be copied
+     * @param end End of the range which will be copied
+     * @return Exception which occurred during processing
+     */
+    private IOException copyRange(Reader reader, PrintWriter writer,
+                                  long start, long end) {
+
+        try {
+            reader.skip(start);
+        } catch (IOException e) {
+            return e;
+        }
+
+        IOException exception = null;
+        long bytesToRead = end - start + 1;
+
+        char buffer[] = new char[input];
+        int len = buffer.length;
+        while ( (bytesToRead > 0) && (len >= buffer.length)) {
+            try {
+                len = reader.read(buffer);
+                if (bytesToRead >= len) {
+                    writer.write(buffer, 0, len);
+                    bytesToRead -= len;
+                } else {
+                    writer.write(buffer, 0, (int) bytesToRead);
+                    bytesToRead = 0;
+                }
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+            }
+            if (len < buffer.length)
+                break;
+        }
+
+        return exception;
+
+    }
+
+
+
+    // ------------------------------------------------------ Range Inner Class
+
+
+    private class Range {
+
+        public long start;
+        public long end;
+        public long length;
+
+        /**
+         * Validate range.
+         */
+        public boolean validate() {
+            if (end >= length)
+                end = length - 1;
+            return ( (start >= 0) && (end >= 0) && (start <= end)
+                     && (length > 0) );
+        }
+
+        public void recycle() {
+            start = 0;
+            end = 0;
+            length = 0;
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/InvokerHttpRequest.java b/container/catalina/src/share/org/apache/catalina/servlets/InvokerHttpRequest.java
new file mode 100644
index 0000000..36915c3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/InvokerHttpRequest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.servlets;
+
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.http.HttpServletRequest</code>
+ * utilized when <code>InvokerServlet</code> processes the initial request
+ * for an invoked servlet.  Subsequent requests will be mapped directly
+ * to the servlet, because a new servlet mapping will have been created.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+class InvokerHttpRequest extends HttpServletRequestWrapper {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new wrapped request around the specified servlet request.
+     *
+     * @param request The servlet request being wrapped
+     */
+    public InvokerHttpRequest(HttpServletRequest request) {
+
+        super(request);
+        this.pathInfo = request.getPathInfo();
+        this.pathTranslated = request.getPathTranslated();
+        this.requestURI = request.getRequestURI();
+        this.servletPath = request.getServletPath();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.servlets.InvokerHttpRequest/1.0";
+
+
+    /**
+     * The path information for this request.
+     */
+    protected String pathInfo = null;
+
+
+    /**
+     * The translated path information for this request.
+     */
+    protected String pathTranslated = null;
+
+
+    /**
+     * The request URI for this request.
+     */
+    protected String requestURI = null;
+
+
+    /**
+     * The servlet path for this request.
+     */
+    protected String servletPath = null;
+
+
+    // --------------------------------------------- HttpServletRequest Methods
+
+
+    /**
+     * Override the <code>getPathInfo()</code> method of the wrapped request.
+     */
+    public String getPathInfo() {
+
+        return (this.pathInfo);
+
+    }
+
+
+    /**
+     * Override the <code>getPathTranslated()</code> method of the
+     * wrapped request.
+     */
+    public String getPathTranslated() {
+
+        return (this.pathTranslated);
+
+    }
+
+
+    /**
+     * Override the <code>getRequestURI()</code> method of the wrapped request.
+     */
+    public String getRequestURI() {
+
+        return (this.requestURI);
+
+    }
+
+
+    /**
+     * Override the <code>getServletPath()</code> method of the wrapped
+     * request.
+     */
+    public String getServletPath() {
+
+        return (this.servletPath);
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Set the path information for this request.
+     *
+     * @param pathInfo The new path info
+     */
+    void setPathInfo(String pathInfo) {
+
+        this.pathInfo = pathInfo;
+
+    }
+
+
+    /**
+     * Set the translated path info for this request.
+     *
+     * @param pathTranslated The new translated path info
+     */
+    void setPathTranslated(String pathTranslated) {
+
+        this.pathTranslated = pathTranslated;
+
+    }
+
+
+    /**
+     * Set the request URI for this request.
+     *
+     * @param requestURI The new request URI
+     */
+    void setRequestURI(String requestURI) {
+
+        this.requestURI = requestURI;
+
+    }
+
+
+    /**
+     * Set the servlet path for this request.
+     *
+     * @param servletPath The new servlet path
+     */
+    void setServletPath(String servletPath) {
+
+        this.servletPath = servletPath;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/InvokerServlet.java b/container/catalina/src/share/org/apache/catalina/servlets/InvokerServlet.java
new file mode 100644
index 0000000..aa29fdb
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/InvokerServlet.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.servlets;
+
+
+import java.io.IOException;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.ContainerServlet;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * The default servlet-invoking servlet for most web applications,
+ * used to serve requests to servlets that have not been registered
+ * in the web application deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class InvokerServlet
+    extends HttpServlet implements ContainerServlet {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Context container associated with our web application.
+     */
+    private Context context = null;
+
+
+    /**
+     * The debugging detail level for this servlet.
+     */
+    private int debug = 0;
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The Wrapper container associated with this servlet.
+     */
+    private Wrapper wrapper = null;
+
+
+    // ----------------------------------------------- ContainerServlet Methods
+
+
+    /**
+     * Return the Wrapper with which we are associated.
+     */
+    public Wrapper getWrapper() {
+
+        return (this.wrapper);
+
+    }
+
+
+    /**
+     * Set the Wrapper with which we are associated.
+     *
+     * @param wrapper The new wrapper
+     */
+    public void setWrapper(Wrapper wrapper) {
+
+        this.wrapper = wrapper;
+        if (wrapper == null)
+            context = null;
+        else
+            context = (Context) wrapper.getParent();
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Finalize this servlet.
+     */
+    public void destroy() {
+
+        ;       // No actions necessary
+
+    }
+
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        serveRequest(request, response);
+
+    }
+
+
+    /**
+     * Process a HEAD request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doHead(HttpServletRequest request,
+                       HttpServletResponse response)
+        throws IOException, ServletException {
+
+        serveRequest(request, response);
+
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doPost(HttpServletRequest request,
+                       HttpServletResponse response)
+        throws IOException, ServletException {
+
+        serveRequest(request, response);
+
+    }
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init() throws ServletException {
+
+        // Ensure that our ContainerServlet properties have been set
+        if ((wrapper == null) || (context == null))
+            throw new UnavailableException
+                (sm.getString("invokerServlet.noWrapper"));
+
+        // Set our properties from the initialization parameters
+        String value = null;
+        try {
+            value = getServletConfig().getInitParameter("debug");
+            debug = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+        if (debug >= 1)
+            log("init: Associated with Context '" + context.getPath() + "'");
+
+    }
+
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Serve the specified request, creating the corresponding response.
+     * After the first time a particular servlet class is requested, it will
+     * be served directly (like any registered servlet) because it will have
+     * been registered and mapped in our associated Context.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void serveRequest(HttpServletRequest request,
+                             HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Disallow calling this servlet via a named dispatcher
+        if (request.getAttribute(Globals.NAMED_DISPATCHER_ATTR) != null)
+            throw new ServletException
+                (sm.getString("invokerServlet.notNamed"));
+
+        // Identify the input parameters and our "included" state
+        String inRequestURI = null;
+        String inServletPath = null;
+        String inPathInfo = null;
+        boolean included =
+            (request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null);
+
+        if (included) {
+            inRequestURI =
+                (String) request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR);
+            inServletPath =
+                (String) request.getAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR);
+            inPathInfo =
+                (String) request.getAttribute(Globals.INCLUDE_PATH_INFO_ATTR);
+        } else {
+            inRequestURI = request.getRequestURI();
+            inServletPath = request.getServletPath();
+            inPathInfo = request.getPathInfo();
+        }
+        if (debug >= 1) {
+            log("included='" + included + "', requestURI='" +
+                inRequestURI + "'");
+            log("  servletPath='" + inServletPath + "', pathInfo='" +
+                inPathInfo + "'");
+        }
+
+        // Make sure a servlet name or class name was specified
+        if (inPathInfo == null) {
+            if (debug >= 1)
+                log("Invalid pathInfo '" + inPathInfo + "'");
+            if (included)
+                throw new ServletException
+                    (sm.getString("invokerServlet.invalidPath", inRequestURI));
+            else {
+                response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                   inRequestURI);
+                return;
+            }
+        }
+
+        // Identify the outgoing servlet name or class, and outgoing path info
+        String pathInfo = inPathInfo;
+        String servletClass = pathInfo.substring(1);
+        int slash = servletClass.indexOf('/');
+        //        if (debug >= 2)
+        //            log("  Calculating with servletClass='" + servletClass +
+        //                "', pathInfo='" + pathInfo + "', slash=" + slash);
+        if (slash >= 0) {
+            pathInfo = servletClass.substring(slash);
+            servletClass = servletClass.substring(0, slash);
+        } else {
+            pathInfo = "";
+        }
+
+        if (servletClass.startsWith("org.apache.catalina")) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                               inRequestURI);
+            return;
+        }
+
+        if (debug >= 1)
+            log("Processing servlet '" + servletClass +
+                "' with path info '" + pathInfo + "'");
+        String name = "org.apache.catalina.INVOKER." + servletClass;
+        String pattern = inServletPath + "/" + servletClass + "/*";
+        Wrapper wrapper = null;
+
+        // Synchronize to avoid race conditions when multiple requests
+        // try to initialize the same servlet at the same time
+        synchronized (this) {
+
+            // Are we referencing an existing servlet class or name?
+            wrapper = (Wrapper) context.findChild(servletClass);
+            if (wrapper == null)
+                wrapper = (Wrapper) context.findChild(name);
+            if (wrapper != null) {
+                String actualServletClass = wrapper.getServletClass();
+                if ((actualServletClass != null)
+                    && (actualServletClass.startsWith
+                        ("org.apache.catalina"))) {
+                    response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                       inRequestURI);
+                    return;
+                }
+                if (debug >= 1)
+                    log("Using wrapper for servlet '" +
+                        wrapper.getName() + "' with mapping '" +
+                        pattern + "'");
+                context.addServletMapping(pattern, wrapper.getName());
+            }
+
+            // No, create a new wrapper for the specified servlet class
+            else {
+
+                if (debug >= 1)
+                    log("Creating wrapper for '" + servletClass +
+                        "' with mapping '" + pattern + "'");
+
+                try {
+                    wrapper = context.createWrapper();
+                    wrapper.setName(name);
+                    wrapper.setLoadOnStartup(1);
+                    wrapper.setServletClass(servletClass);
+                    context.addChild(wrapper);
+                    context.addServletMapping(pattern, name);
+                } catch (Throwable t) {
+                    log(sm.getString("invokerServlet.cannotCreate",
+                                     inRequestURI), t);
+                    context.removeServletMapping(pattern);
+                    context.removeChild(wrapper);
+                    if (included)
+                        throw new ServletException
+                            (sm.getString("invokerServlet.cannotCreate",
+                                          inRequestURI), t);
+                    else {
+                        response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                           inRequestURI);
+                        return;
+                    }
+                }
+            }
+
+        }
+
+        // Create a request wrapper to pass on to the invoked servlet
+        InvokerHttpRequest wrequest =
+            new InvokerHttpRequest(request);
+        wrequest.setRequestURI(inRequestURI);
+        StringBuffer sb = new StringBuffer(inServletPath);
+        sb.append("/");
+        sb.append(servletClass);
+        wrequest.setServletPath(sb.toString());
+        if ((pathInfo == null) || (pathInfo.length() < 1)) {
+            wrequest.setPathInfo(null);
+            wrequest.setPathTranslated(null);
+        } else {
+            wrequest.setPathInfo(pathInfo);
+            wrequest.setPathTranslated
+                (getServletContext().getRealPath(pathInfo));
+        }
+
+        // Allocate a servlet instance to perform this request
+        Servlet instance = null;
+        try {
+            //            if (debug >= 2)
+            //                log("  Allocating servlet instance");
+            instance = wrapper.allocate();
+        } catch (ServletException e) {
+            log(sm.getString("invokerServlet.allocate", inRequestURI), e);
+            context.removeServletMapping(pattern);
+            context.removeChild(wrapper);
+            Throwable rootCause = e.getRootCause();
+            if (rootCause == null)
+                rootCause = e;
+            if (rootCause instanceof ClassNotFoundException) {
+                response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                   inRequestURI);
+                return;
+            } else if (rootCause instanceof IOException) {
+                throw (IOException) rootCause;
+            } else if (rootCause instanceof RuntimeException) {
+                throw (RuntimeException) rootCause;
+            } else if (rootCause instanceof ServletException) {
+                throw (ServletException) rootCause;
+            } else {
+                throw new ServletException
+                    (sm.getString("invokerServlet.allocate", inRequestURI),
+                     rootCause);
+            }
+        } catch (Throwable e) {
+            log(sm.getString("invokerServlet.allocate", inRequestURI), e);
+            context.removeServletMapping(pattern);
+            context.removeChild(wrapper);
+            throw new ServletException
+                (sm.getString("invokerServlet.allocate", inRequestURI), e);
+        }
+
+        // After loading the wrapper, restore some of the fields when including
+        if (included) {
+            wrequest.setRequestURI(request.getRequestURI());
+            wrequest.setPathInfo(request.getPathInfo());
+            wrequest.setServletPath(request.getServletPath());
+        }
+
+        // Invoke the service() method of the allocated servlet
+        try {
+            String jspFile = wrapper.getJspFile();
+            if (jspFile != null)
+                request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
+            else
+                request.removeAttribute(Globals.JSP_FILE_ATTR);
+            request.setAttribute(Globals.INVOKED_ATTR,
+                                 request.getServletPath());
+            //            if (debug >= 2)
+            //                log("  Calling service() method, jspFile=" +
+            //                    jspFile);
+            instance.service(wrequest, response);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+        } catch (IOException e) {
+            //            if (debug >= 2)
+            //                log("  service() method IOException", e);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw e;
+        } catch (UnavailableException e) {
+            //            if (debug >= 2)
+            //                log("  service() method UnavailableException", e);
+            context.removeServletMapping(pattern);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw e;
+        } catch (ServletException e) {
+            //            if (debug >= 2)
+            //                log("  service() method ServletException", e);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw e;
+        } catch (RuntimeException e) {
+            //            if (debug >= 2)
+            //                log("  service() method RuntimeException", e);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw e;
+        } catch (Throwable e) {
+            //            if (debug >= 2)
+            //                log("  service() method Throwable", e);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw new ServletException("Invoker service() exception", e);
+        }
+
+        // Deallocate the allocated servlet instance
+        try {
+            //            if (debug >= 2)
+            //                log("  deallocate servlet instance");
+            wrapper.deallocate(instance);
+        } catch (ServletException e) {
+            log(sm.getString("invokerServlet.deallocate", inRequestURI), e);
+            throw e;
+        } catch (Throwable e) {
+            log(sm.getString("invokerServlet.deallocate", inRequestURI), e);
+            throw new ServletException
+                (sm.getString("invokerServlet.deallocate", inRequestURI), e);
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings.properties
new file mode 100644
index 0000000..d4c2971
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings.properties
@@ -0,0 +1,19 @@
+defaultServlet.missingResource=The requested resource ({0}) is not available
+defaultservlet.directorylistingfor=Directory Listing for:
+defaultservlet.upto=Up to:
+defaultservlet.subdirectories=Subdirectories:
+defaultservlet.files=Files:
+invokerServlet.allocate=Cannot allocate servlet instance for path {0}
+invokerServlet.cannotCreate=Cannot create servlet wrapper for path {0}
+invokerServlet.deallocate=Cannot deallocate servlet instance for path {0}
+invokerServlet.invalidPath=No servlet name or class was specified in path {0}
+invokerServlet.notNamed=Cannot call invoker servlet with a named dispatcher
+invokerServlet.noWrapper=Container has not called setWrapper() for this servlet
+webdavservlet.jaxpfailed=JAXP initialization failed
+directory.filename=Filename
+directory.lastModified=Last Modified
+directory.parent=Up To {0}
+directory.size=Size
+directory.title=Directory Listing For {0}
+directory.version=Tomcat Catalina version 4.0
+
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings_es.properties
new file mode 100644
index 0000000..4f55f63
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings_es.properties
@@ -0,0 +1,18 @@
+defaultservlet.directorylistingfor=Listado de Directorio para:
+defaultservlet.upto=Atrás a:
+defaultservlet.subdirectories=Subdirectorios:
+defaultservlet.files=Archivos:
+invokerServlet.allocate=No puedo reservar espacio para instancia de servlet para trayectoria {0}
+invokerServlet.cannotCreate=No puedo crear arropador (wrapper) de servlet para trayectoria {0}
+invokerServlet.deallocate=No puedo recuperar instancia de servlet para trayectoria {0}
+invokerServlet.invalidPath=No se ha especificado nombre de servlet o clase en trayectoria {0}
+invokerServlet.notNamed=No puedo llamar a servlet invocador mediante un despachador nombrado (named)
+invokerServlet.noWrapper=El Contenedor no ha llamado a setWrapper() para este servlet
+webdavservlet.jaxpfailed=Falló la inicialización de JAXP
+directory.filename=Nombre de Archivo
+directory.lastModified=Última Modificación
+directory.parent=Atrás A {0}
+directory.size=Medida
+directory.title=Listado de Directorio Para {0}
+directory.version=Tomcat Catalina versión 4.0
+
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings_fr.properties
new file mode 100644
index 0000000..d694e06
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings_fr.properties
@@ -0,0 +1,18 @@
+defaultservlet.directorylistingfor=Liste du répertoire pour :
+defaultservlet.upto=Jusqu''à:
+defaultservlet.subdirectories=Sous-répertoires:
+defaultservlet.files=Fichiers:
+invokerServlet.allocate=Impossible d''allouer une instance de servlet pour le chemin {0}
+invokerServlet.cannotCreate=Impossible de créer un enrobeur (wrapper) de servlet pour le chemin {0}
+invokerServlet.deallocate=Impossible de désallouer une instance de servlet pour le chemin {0}
+invokerServlet.invalidPath=Aucun nom de servlet ou de classe n''a été spécifié pour le chemin {0}
+invokerServlet.notNamed=Impossible d''appeler le délégué (invoker) de servlet avec un aiguilleur (dispatcher) nommé
+invokerServlet.noWrapper=Le conteneur n''a pas appelé "setWrapper()" pour cette servlet
+webdavservlet.jaxpfailed=Erreur d''initialisation de JAXP
+directory.filename=Nom de fichier
+directory.lastModified=Dernière modification
+directory.parent=Jusqu''à {0}
+directory.size=Taille
+directory.title=Liste du répertoire pour {0}
+directory.version=Tomcat Catalina version 4.0
+
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings_ja.properties
new file mode 100644
index 0000000..d4802c3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/LocalStrings_ja.properties
@@ -0,0 +1,18 @@
+defaultservlet.directorylistingfor=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306e\u4e00\u89a7: 
+defaultservlet.upto=\u89aa\u30c7\u30a3\u30ec\u30af\u30c8\u30ea: 
+defaultservlet.subdirectories=\u30b5\u30d6\u30c7\u30a3\u30ec\u30af\u30c8\u30ea:
+defaultservlet.files=\u30d5\u30a1\u30a4\u30eb:
+invokerServlet.allocate=\u30d1\u30b9 {0} \u306b\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u5272\u308a\u5f53\u3066\u3089\u308c\u307e\u305b\u3093
+invokerServlet.cannotCreate=\u30d1\u30b9 {0} \u306b\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30e9\u30c3\u30d1\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093
+invokerServlet.deallocate=\u30d1\u30b9 {0} \u306e\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u5272\u308a\u5f53\u3066\u3092\u89e3\u9664\u3067\u304d\u307e\u305b\u3093
+invokerServlet.invalidPath=\u30d1\u30b9 {0} \u306b\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u540d\u53c8\u306f\u30af\u30e9\u30b9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+invokerServlet.notNamed=\u305d\u306e\u540d\u524d\u306e\u30c7\u30a3\u30b9\u30d1\u30c3\u30c1\u30e3\u3067\u30a4\u30f3\u30dc\u30fc\u30ab\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u3092\u547c\u3073\u51fa\u305b\u307e\u305b\u3093
+invokerServlet.noWrapper=\u30b3\u30f3\u30c6\u30ca\u306f\u3053\u306e\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u306b\u5bfe\u3057\u3066\u547c\u3073\u51fa\u3055\u308c\u305fsetWrapper()\u3092\u6301\u3063\u3066\u3044\u307e\u305b\u3093
+webdavservlet.jaxpfailed=JAXP\u306e\u521d\u671f\u5316\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+directory.filename=\u30d5\u30a1\u30a4\u30eb\u540d
+directory.lastModified=\u6700\u7d42\u66f4\u65b0
+directory.parent={0} \u306b\u79fb\u52d5
+directory.size=\u30b5\u30a4\u30ba
+directory.title={0} \u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306e\u4e00\u89a7
+directory.version=Tomcat Catalina \u30d0\u30fc\u30b8\u30e7\u30f3 4.0
+
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/WebdavServlet.java b/container/catalina/src/share/org/apache/catalina/servlets/WebdavServlet.java
new file mode 100644
index 0000000..9ebe440
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/WebdavServlet.java
@@ -0,0 +1,3082 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.servlets;
+
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Stack;
+import java.util.TimeZone;
+import java.util.Vector;
+
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.catalina.util.DOMWriter;
+import org.apache.catalina.util.MD5Encoder;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.XMLWriter;
+import org.apache.naming.resources.CacheEntry;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.ResourceAttributes;
+import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+
+/**
+ * Servlet which adds support for WebDAV level 2. All the basic HTTP requests
+ * are handled by the DefaultServlet.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class WebdavServlet
+    extends DefaultServlet {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    private static final String METHOD_HEAD = "HEAD";
+    private static final String METHOD_PROPFIND = "PROPFIND";
+    private static final String METHOD_PROPPATCH = "PROPPATCH";
+    private static final String METHOD_MKCOL = "MKCOL";
+    private static final String METHOD_COPY = "COPY";
+    private static final String METHOD_MOVE = "MOVE";
+    private static final String METHOD_LOCK = "LOCK";
+    private static final String METHOD_UNLOCK = "UNLOCK";
+
+
+    /**
+     * Default depth is infite.
+     */
+    private static final int INFINITY = 3; // To limit tree browsing a bit
+
+
+    /**
+     * PROPFIND - Specify a property mask.
+     */
+    private static final int FIND_BY_PROPERTY = 0;
+
+
+    /**
+     * PROPFIND - Display all properties.
+     */
+    private static final int FIND_ALL_PROP = 1;
+
+
+    /**
+     * PROPFIND - Return property names.
+     */
+    private static final int FIND_PROPERTY_NAMES = 2;
+
+
+    /**
+     * Create a new lock.
+     */
+    private static final int LOCK_CREATION = 0;
+
+
+    /**
+     * Refresh lock.
+     */
+    private static final int LOCK_REFRESH = 1;
+
+
+    /**
+     * Default lock timeout value.
+     */
+    private static final int DEFAULT_TIMEOUT = 3600;
+
+
+    /**
+     * Maximum lock timeout.
+     */
+    private static final int MAX_TIMEOUT = 604800;
+
+
+    /**
+     * Default namespace.
+     */
+    protected static final String DEFAULT_NAMESPACE = "DAV:";
+
+
+    /**
+     * Simple date format for the creation date ISO representation (partial).
+     */
+    protected static final SimpleDateFormat creationDateFormat =
+        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+
+
+     /**
+     * MD5 message digest provider.
+     */
+    protected static MessageDigest md5Helper;
+
+
+    /**
+     * The MD5 helper object for this class.
+     */
+    protected static final MD5Encoder md5Encoder = new MD5Encoder();
+
+
+
+    static {
+        creationDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Repository of the locks put on single resources.
+     * <p>
+     * Key : path <br>
+     * Value : LockInfo
+     */
+    private Hashtable resourceLocks = new Hashtable();
+
+
+    /**
+     * Repository of the lock-null resources.
+     * <p>
+     * Key : path of the collection containing the lock-null resource<br>
+     * Value : Vector of lock-null resource which are members of the
+     * collection. Each element of the Vector is the path associated with
+     * the lock-null resource.
+     */
+    private Hashtable lockNullResources = new Hashtable();
+
+
+    /**
+     * Vector of the heritable locks.
+     * <p>
+     * Key : path <br>
+     * Value : LockInfo
+     */
+    private Vector collectionLocks = new Vector();
+
+
+    /**
+     * Secret information used to generate reasonably secure lock ids.
+     */
+    private String secret = "catalina";
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init()
+        throws ServletException {
+
+        super.init();
+
+        String value = null;
+        try {
+            value = getServletConfig().getInitParameter("secret");
+            if (value != null)
+                secret = value;
+        } catch (Throwable t) {
+            ;
+        }
+
+
+        // Load the MD5 helper used to calculate signatures.
+        try {
+            md5Helper = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw new UnavailableException("No MD5");
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return JAXP document builder instance.
+     */
+    protected DocumentBuilder getDocumentBuilder()
+        throws ServletException {
+        DocumentBuilder documentBuilder = null;
+        DocumentBuilderFactory documentBuilderFactory = null;
+        try {
+            documentBuilderFactory = DocumentBuilderFactory.newInstance();
+            documentBuilderFactory.setNamespaceAware(true);
+            documentBuilder = documentBuilderFactory.newDocumentBuilder();
+        } catch(ParserConfigurationException e) {
+            throw new ServletException
+                (sm.getString("webdavservlet.jaxpfailed"));
+        }
+        return documentBuilder;
+    }
+
+
+    /**
+     * Handles the special WebDAV methods.
+     */
+    protected void service(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        String method = req.getMethod();
+
+        if (debug > 0) {
+            String path = getRelativePath(req);
+            log("[" + method + "] " + path);
+        }
+
+        if (method.equals(METHOD_PROPFIND)) {
+            doPropfind(req, resp);
+        } else if (method.equals(METHOD_PROPPATCH)) {
+            doProppatch(req, resp);
+        } else if (method.equals(METHOD_MKCOL)) {
+            doMkcol(req, resp);
+        } else if (method.equals(METHOD_COPY)) {
+            doCopy(req, resp);
+        } else if (method.equals(METHOD_MOVE)) {
+            doMove(req, resp);
+        } else if (method.equals(METHOD_LOCK)) {
+            doLock(req, resp);
+        } else if (method.equals(METHOD_UNLOCK)) {
+            doUnlock(req, resp);
+        } else {
+            // DefaultServlet processing
+            super.service(req, resp);
+        }
+
+    }
+
+
+    /**
+     * Check if the conditions specified in the optional If headers are
+     * satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceAttributes The resource information
+     * @return boolean true if the resource meets all the specified conditions,
+     * and false if any of the conditions is not satisfied, in which case
+     * request processing is stopped
+     */
+    protected boolean checkIfHeaders(HttpServletRequest request,
+                                     HttpServletResponse response,
+                                     ResourceAttributes resourceAttributes)
+        throws IOException {
+
+        if (!super.checkIfHeaders(request, response, resourceAttributes))
+            return false;
+
+        // TODO : Checking the WebDAV If header
+        return true;
+
+    }
+
+
+    /**
+     * OPTIONS Method.
+     *
+     * @param req The request
+     * @param resp The response
+     * @throws ServletException If an error occurs
+     * @throws IOException If an IO error occurs
+     */
+    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        resp.addHeader("DAV", "1,2");
+
+        StringBuffer methodsAllowed = determineMethodsAllowed(resources,
+                                                              req);
+
+        resp.addHeader("Allow", methodsAllowed.toString());
+        resp.addHeader("MS-Author-Via", "DAV");
+
+    }
+
+
+    /**
+     * PROPFIND Method.
+     */
+    protected void doPropfind(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (!listings) {
+            // Get allowed methods
+            StringBuffer methodsAllowed = determineMethodsAllowed(resources,
+                                                                  req);
+
+            resp.addHeader("Allow", methodsAllowed.toString());
+            resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+        if (path.endsWith("/"))
+            path = path.substring(0, path.length() - 1);
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        // Properties which are to be displayed.
+        Vector properties = null;
+        // Propfind depth
+        int depth = INFINITY;
+        // Propfind type
+        int type = FIND_ALL_PROP;
+
+        String depthStr = req.getHeader("Depth");
+
+        if (depthStr == null) {
+            depth = INFINITY;
+        } else {
+            if (depthStr.equals("0")) {
+                depth = 0;
+            } else if (depthStr.equals("1")) {
+                depth = 1;
+            } else if (depthStr.equals("infinity")) {
+                depth = INFINITY;
+            }
+        }
+
+        Node propNode = null;
+
+        DocumentBuilder documentBuilder = getDocumentBuilder();
+
+        try {
+            Document document = documentBuilder.parse
+                (new InputSource(req.getInputStream()));
+
+            // Get the root element of the document
+            Element rootElement = document.getDocumentElement();
+            NodeList childList = rootElement.getChildNodes();
+
+            for (int i=0; i < childList.getLength(); i++) {
+                Node currentNode = childList.item(i);
+                switch (currentNode.getNodeType()) {
+                case Node.TEXT_NODE:
+                    break;
+                case Node.ELEMENT_NODE:
+                    if (currentNode.getNodeName().endsWith("prop")) {
+                        type = FIND_BY_PROPERTY;
+                        propNode = currentNode;
+                    }
+                    if (currentNode.getNodeName().endsWith("propname")) {
+                        type = FIND_PROPERTY_NAMES;
+                    }
+                    if (currentNode.getNodeName().endsWith("allprop")) {
+                        type = FIND_ALL_PROP;
+                    }
+                    break;
+                }
+            }
+        } catch(Exception e) {
+            // Most likely there was no content : we use the defaults.
+            // TODO : Enhance that !
+        }
+
+        if (type == FIND_BY_PROPERTY) {
+            properties = new Vector();
+            NodeList childList = propNode.getChildNodes();
+
+            for (int i=0; i < childList.getLength(); i++) {
+                Node currentNode = childList.item(i);
+                switch (currentNode.getNodeType()) {
+                case Node.TEXT_NODE:
+                    break;
+                case Node.ELEMENT_NODE:
+                    String nodeName = currentNode.getNodeName();
+                    String propertyName = null;
+                    if (nodeName.indexOf(':') != -1) {
+                        propertyName = nodeName.substring
+                            (nodeName.indexOf(':') + 1);
+                    } else {
+                        propertyName = nodeName;
+                    }
+                    // href is a live property which is handled differently
+                    properties.addElement(propertyName);
+                    break;
+                }
+            }
+
+        }
+
+        boolean exists = true;
+        Object object = null;
+        try {
+            object = resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+            int slash = path.lastIndexOf('/');
+            if (slash != -1) {
+                String parentPath = path.substring(0, slash);
+                Vector currentLockNullResources =
+                    (Vector) lockNullResources.get(parentPath);
+                if (currentLockNullResources != null) {
+                    Enumeration lockNullResourcesList =
+                        currentLockNullResources.elements();
+                    while (lockNullResourcesList.hasMoreElements()) {
+                        String lockNullPath = (String)
+                            lockNullResourcesList.nextElement();
+                        if (lockNullPath.equals(path)) {
+                            resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
+                            resp.setContentType("text/xml; charset=UTF-8");
+                            // Create multistatus object
+                            XMLWriter generatedXML =
+                                new XMLWriter(resp.getWriter());
+                            generatedXML.writeXMLHeader();
+                            generatedXML.writeElement
+                                (null, "multistatus"
+                                 + generateNamespaceDeclarations(),
+                                 XMLWriter.OPENING);
+                            parseLockNullProperties
+                                (req, generatedXML, lockNullPath, type,
+                                 properties);
+                            generatedXML.writeElement(null, "multistatus",
+                                                      XMLWriter.CLOSING);
+                            generatedXML.sendData();
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!exists) {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+            return;
+        }
+
+        resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
+
+        resp.setContentType("text/xml; charset=UTF-8");
+
+        // Create multistatus object
+        XMLWriter generatedXML = new XMLWriter(resp.getWriter());
+        generatedXML.writeXMLHeader();
+
+        generatedXML.writeElement(null, "multistatus"
+                                  + generateNamespaceDeclarations(),
+                                  XMLWriter.OPENING);
+
+        if (depth == 0) {
+            parseProperties(req, generatedXML, path, type,
+                            properties);
+        } else {
+            // The stack always contains the object of the current level
+            Stack stack = new Stack();
+            stack.push(path);
+
+            // Stack of the objects one level below
+            Stack stackBelow = new Stack();
+
+            while ((!stack.isEmpty()) && (depth >= 0)) {
+
+                String currentPath = (String) stack.pop();
+                parseProperties(req, generatedXML, currentPath,
+                                type, properties);
+
+                try {
+                    object = resources.lookup(currentPath);
+                } catch (NamingException e) {
+                    continue;
+                }
+
+                if ((object instanceof DirContext) && (depth > 0)) {
+
+                    try {
+                        NamingEnumeration enumeration = resources.list(currentPath);
+                        while (enumeration.hasMoreElements()) {
+                            NameClassPair ncPair =
+                                (NameClassPair) enumeration.nextElement();
+                            String newPath = currentPath;
+                            if (!(newPath.endsWith("/")))
+                                newPath += "/";
+                            newPath += ncPair.getName();
+                            stackBelow.push(newPath);
+                        }
+                    } catch (NamingException e) {
+                        resp.sendError
+                            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                             path);
+                        return;
+                    }
+
+                    // Displaying the lock-null resources present in that
+                    // collection
+                    String lockPath = currentPath;
+                    if (lockPath.endsWith("/"))
+                        lockPath =
+                            lockPath.substring(0, lockPath.length() - 1);
+                    Vector currentLockNullResources =
+                        (Vector) lockNullResources.get(lockPath);
+                    if (currentLockNullResources != null) {
+                        Enumeration lockNullResourcesList =
+                            currentLockNullResources.elements();
+                        while (lockNullResourcesList.hasMoreElements()) {
+                            String lockNullPath = (String)
+                                lockNullResourcesList.nextElement();
+                            parseLockNullProperties
+                                (req, generatedXML, lockNullPath, type,
+                                 properties);
+                        }
+                    }
+
+                }
+
+                if (stack.isEmpty()) {
+                    depth--;
+                    stack = stackBelow;
+                    stackBelow = new Stack();
+                }
+
+                generatedXML.sendData();
+
+            }
+        }
+
+        generatedXML.writeElement(null, "multistatus",
+                                  XMLWriter.CLOSING);
+
+        generatedXML.sendData();
+
+    }
+
+
+    /**
+     * PROPPATCH Method.
+     */
+    protected void doProppatch(HttpServletRequest req,
+                               HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
+
+    }
+
+
+    /**
+     * MKCOL Method.
+     */
+    protected void doMkcol(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        boolean exists = true;
+        Object object = null;
+        try {
+            object = resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        // Can't create a collection if a resource already exists at the given
+        // path
+        if (exists) {
+            // Get allowed methods
+            StringBuffer methodsAllowed = determineMethodsAllowed(resources,
+                                                                  req);
+
+            resp.addHeader("Allow", methodsAllowed.toString());
+
+            resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
+            return;
+        }
+
+        if (req.getInputStream().available() > 0) {
+            DocumentBuilder documentBuilder = getDocumentBuilder();
+            try {
+                Document document = documentBuilder.parse
+                    (new InputSource(req.getInputStream()));
+                // TODO : Process this request body
+                resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED);
+                return;
+
+            } catch(SAXException saxe) {
+                // Parse error - assume invalid content
+                resp.sendError(WebdavStatus.SC_BAD_REQUEST);
+                return;
+            }
+        }
+
+        boolean result = true;
+        try {
+            resources.createSubcontext(path);
+        } catch (NamingException e) {
+            result = false;
+        }
+
+        if (!result) {
+            resp.sendError(WebdavStatus.SC_CONFLICT,
+                           WebdavStatus.getStatusText
+                           (WebdavStatus.SC_CONFLICT));
+        } else {
+            resp.setStatus(WebdavStatus.SC_CREATED);
+            // Removing any lock-null resource which would be present
+            lockNullResources.remove(path);
+        }
+
+    }
+
+
+    /**
+     * DELETE Method.
+     */
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        deleteResource(req, resp);
+
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param req The servlet request we are processing
+     * @param resp The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        super.doPut(req, resp);
+
+        String path = getRelativePath(req);
+
+        // Removing any lock-null resource which would be present
+        lockNullResources.remove(path);
+
+    }
+
+    /**
+     * COPY Method.
+     */
+    protected void doCopy(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        copyResource(req, resp);
+
+    }
+
+
+    /**
+     * MOVE Method.
+     */
+    protected void doMove(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        if (copyResource(req, resp)) {
+            deleteResource(path, req, resp, false);
+        }
+
+    }
+
+
+    /**
+     * LOCK Method.
+     */
+    protected void doLock(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        LockInfo lock = new LockInfo();
+
+        // Parsing lock request
+
+        // Parsing depth header
+
+        String depthStr = req.getHeader("Depth");
+
+        if (depthStr == null) {
+            lock.depth = INFINITY;
+        } else {
+            if (depthStr.equals("0")) {
+                lock.depth = 0;
+            } else {
+                lock.depth = INFINITY;
+            }
+        }
+
+        // Parsing timeout header
+
+        int lockDuration = DEFAULT_TIMEOUT;
+        String lockDurationStr = req.getHeader("Timeout");
+        if (lockDurationStr == null) {
+            lockDuration = DEFAULT_TIMEOUT;
+        } else {
+            int commaPos = lockDurationStr.indexOf(",");
+            // If multiple timeouts, just use the first
+            if (commaPos != -1) {
+                lockDurationStr = lockDurationStr.substring(0,commaPos);
+            }
+            if (lockDurationStr.startsWith("Second-")) {
+                lockDuration =
+                    (new Integer(lockDurationStr.substring(7))).intValue();
+            } else {
+                if (lockDurationStr.equalsIgnoreCase("infinity")) {
+                    lockDuration = MAX_TIMEOUT;
+                } else {
+                    try {
+                        lockDuration =
+                            (new Integer(lockDurationStr)).intValue();
+                    } catch (NumberFormatException e) {
+                        lockDuration = MAX_TIMEOUT;
+                    }
+                }
+            }
+            if (lockDuration == 0) {
+                lockDuration = DEFAULT_TIMEOUT;
+            }
+            if (lockDuration > MAX_TIMEOUT) {
+                lockDuration = MAX_TIMEOUT;
+            }
+        }
+        lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000);
+
+        int lockRequestType = LOCK_CREATION;
+
+        Node lockInfoNode = null;
+
+        DocumentBuilder documentBuilder = getDocumentBuilder();
+
+        try {
+            Document document = documentBuilder.parse(new InputSource
+                (req.getInputStream()));
+
+            // Get the root element of the document
+            Element rootElement = document.getDocumentElement();
+            lockInfoNode = rootElement;
+        } catch(Exception e) {
+            lockRequestType = LOCK_REFRESH;
+        }
+
+        if (lockInfoNode != null) {
+
+            // Reading lock information
+
+            NodeList childList = lockInfoNode.getChildNodes();
+            StringWriter strWriter = null;
+            DOMWriter domWriter = null;
+
+            Node lockScopeNode = null;
+            Node lockTypeNode = null;
+            Node lockOwnerNode = null;
+
+            for (int i=0; i < childList.getLength(); i++) {
+                Node currentNode = childList.item(i);
+                switch (currentNode.getNodeType()) {
+                case Node.TEXT_NODE:
+                    break;
+                case Node.ELEMENT_NODE:
+                    String nodeName = currentNode.getNodeName();
+                    if (nodeName.endsWith("lockscope")) {
+                        lockScopeNode = currentNode;
+                    }
+                    if (nodeName.endsWith("locktype")) {
+                        lockTypeNode = currentNode;
+                    }
+                    if (nodeName.endsWith("owner")) {
+                        lockOwnerNode = currentNode;
+                    }
+                    break;
+                }
+            }
+
+            if (lockScopeNode != null) {
+
+                childList = lockScopeNode.getChildNodes();
+                for (int i=0; i < childList.getLength(); i++) {
+                    Node currentNode = childList.item(i);
+                    switch (currentNode.getNodeType()) {
+                    case Node.TEXT_NODE:
+                        break;
+                    case Node.ELEMENT_NODE:
+                        String tempScope = currentNode.getNodeName();
+                        if (tempScope.indexOf(':') != -1) {
+                            lock.scope = tempScope.substring
+                                (tempScope.indexOf(':') + 1);
+                        } else {
+                            lock.scope = tempScope;
+                        }
+                        break;
+                    }
+                }
+
+                if (lock.scope == null) {
+                    // Bad request
+                    resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+                }
+
+            } else {
+                // Bad request
+                resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+            }
+
+            if (lockTypeNode != null) {
+
+                childList = lockTypeNode.getChildNodes();
+                for (int i=0; i < childList.getLength(); i++) {
+                    Node currentNode = childList.item(i);
+                    switch (currentNode.getNodeType()) {
+                    case Node.TEXT_NODE:
+                        break;
+                    case Node.ELEMENT_NODE:
+                        String tempType = currentNode.getNodeName();
+                        if (tempType.indexOf(':') != -1) {
+                            lock.type =
+                                tempType.substring(tempType.indexOf(':') + 1);
+                        } else {
+                            lock.type = tempType;
+                        }
+                        break;
+                    }
+                }
+
+                if (lock.type == null) {
+                    // Bad request
+                    resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+                }
+
+            } else {
+                // Bad request
+                resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+            }
+
+            if (lockOwnerNode != null) {
+
+                childList = lockOwnerNode.getChildNodes();
+                for (int i=0; i < childList.getLength(); i++) {
+                    Node currentNode = childList.item(i);
+                    switch (currentNode.getNodeType()) {
+                    case Node.TEXT_NODE:
+                        lock.owner += currentNode.getNodeValue();
+                        break;
+                    case Node.ELEMENT_NODE:
+                        strWriter = new StringWriter();
+                        domWriter = new DOMWriter(strWriter, true);
+                        domWriter.setQualifiedNames(false);
+                        domWriter.print(currentNode);
+                        lock.owner += strWriter.toString();
+                        break;
+                    }
+                }
+
+                if (lock.owner == null) {
+                    // Bad request
+                    resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+                }
+
+            } else {
+                lock.owner = new String();
+            }
+
+        }
+
+        String path = getRelativePath(req);
+
+        lock.path = path;
+
+        boolean exists = true;
+        Object object = null;
+        try {
+            object = resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        Enumeration locksList = null;
+
+        if (lockRequestType == LOCK_CREATION) {
+
+            // Generating lock id
+            String lockTokenStr = req.getServletPath() + "-" + lock.type + "-"
+                + lock.scope + "-" + req.getUserPrincipal() + "-"
+                + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-"
+                + lock.expiresAt + "-" + System.currentTimeMillis() + "-"
+                + secret;
+            String lockToken =
+                md5Encoder.encode(md5Helper.digest(lockTokenStr.getBytes()));
+
+            if ( (exists) && (object instanceof DirContext) &&
+                 (lock.depth == INFINITY) ) {
+
+                // Locking a collection (and all its member resources)
+
+                // Checking if a child resource of this collection is
+                // already locked
+                Vector lockPaths = new Vector();
+                locksList = collectionLocks.elements();
+                while (locksList.hasMoreElements()) {
+                    LockInfo currentLock = (LockInfo) locksList.nextElement();
+                    if (currentLock.hasExpired()) {
+                        resourceLocks.remove(currentLock.path);
+                        continue;
+                    }
+                    if ( (currentLock.path.startsWith(lock.path)) &&
+                         ((currentLock.isExclusive()) ||
+                          (lock.isExclusive())) ) {
+                        // A child collection of this collection is locked
+                        lockPaths.addElement(currentLock.path);
+                    }
+                }
+                locksList = resourceLocks.elements();
+                while (locksList.hasMoreElements()) {
+                    LockInfo currentLock = (LockInfo) locksList.nextElement();
+                    if (currentLock.hasExpired()) {
+                        resourceLocks.remove(currentLock.path);
+                        continue;
+                    }
+                    if ( (currentLock.path.startsWith(lock.path)) &&
+                         ((currentLock.isExclusive()) ||
+                          (lock.isExclusive())) ) {
+                        // A child resource of this collection is locked
+                        lockPaths.addElement(currentLock.path);
+                    }
+                }
+
+                if (!lockPaths.isEmpty()) {
+
+                    // One of the child paths was locked
+                    // We generate a multistatus error report
+
+                    Enumeration lockPathsList = lockPaths.elements();
+
+                    resp.setStatus(WebdavStatus.SC_CONFLICT);
+
+                    XMLWriter generatedXML = new XMLWriter();
+                    generatedXML.writeXMLHeader();
+
+                    generatedXML.writeElement
+                        (null, "multistatus" + generateNamespaceDeclarations(),
+                         XMLWriter.OPENING);
+
+                    while (lockPathsList.hasMoreElements()) {
+                        generatedXML.writeElement(null, "response",
+                                                  XMLWriter.OPENING);
+                        generatedXML.writeElement(null, "href",
+                                                  XMLWriter.OPENING);
+                        generatedXML
+                            .writeText((String) lockPathsList.nextElement());
+                        generatedXML.writeElement(null, "href",
+                                                  XMLWriter.CLOSING);
+                        generatedXML.writeElement(null, "status",
+                                                  XMLWriter.OPENING);
+                        generatedXML
+                            .writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED
+                                       + " " + WebdavStatus
+                                       .getStatusText(WebdavStatus.SC_LOCKED));
+                        generatedXML.writeElement(null, "status",
+                                                  XMLWriter.CLOSING);
+
+                        generatedXML.writeElement(null, "response",
+                                                  XMLWriter.CLOSING);
+                    }
+
+                    generatedXML.writeElement(null, "multistatus",
+                                          XMLWriter.CLOSING);
+
+                    Writer writer = resp.getWriter();
+                    writer.write(generatedXML.toString());
+                    writer.close();
+
+                    return;
+
+                }
+
+                boolean addLock = true;
+
+                // Checking if there is already a shared lock on this path
+                locksList = collectionLocks.elements();
+                while (locksList.hasMoreElements()) {
+
+                    LockInfo currentLock = (LockInfo) locksList.nextElement();
+                    if (currentLock.path.equals(lock.path)) {
+
+                        if (currentLock.isExclusive()) {
+                            resp.sendError(WebdavStatus.SC_LOCKED);
+                            return;
+                        } else {
+                            if (lock.isExclusive()) {
+                                resp.sendError(WebdavStatus.SC_LOCKED);
+                                return;
+                            }
+                        }
+
+                        currentLock.tokens.addElement(lockToken);
+                        lock = currentLock;
+                        addLock = false;
+
+                    }
+
+                }
+
+                if (addLock) {
+                    lock.tokens.addElement(lockToken);
+                    collectionLocks.addElement(lock);
+                }
+
+            } else {
+
+                // Locking a single resource
+
+                // Retrieving an already existing lock on that resource
+                LockInfo presentLock = (LockInfo) resourceLocks.get(lock.path);
+                if (presentLock != null) {
+
+                    if ((presentLock.isExclusive()) || (lock.isExclusive())) {
+                        // If either lock is exclusive, the lock can't be
+                        // granted
+                        resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
+                        return;
+                    } else {
+                        presentLock.tokens.addElement(lockToken);
+                        lock = presentLock;
+                    }
+
+                } else {
+
+                    lock.tokens.addElement(lockToken);
+                    resourceLocks.put(lock.path, lock);
+
+                    // Checking if a resource exists at this path
+                    exists = true;
+                    try {
+                        object = resources.lookup(path);
+                    } catch (NamingException e) {
+                        exists = false;
+                    }
+                    if (!exists) {
+
+                        // "Creating" a lock-null resource
+                        int slash = lock.path.lastIndexOf('/');
+                        String parentPath = lock.path.substring(0, slash);
+
+                        Vector lockNulls =
+                            (Vector) lockNullResources.get(parentPath);
+                        if (lockNulls == null) {
+                            lockNulls = new Vector();
+                            lockNullResources.put(parentPath, lockNulls);
+                        }
+
+                        lockNulls.addElement(lock.path);
+
+                    }
+                    // Add the Lock-Token header as by RFC 2518 8.10.1
+                    // - only do this for newly created locks
+                    resp.addHeader("Lock-Token", "<opaquelocktoken:"
+                                   + lockToken + ">");
+                }
+
+            }
+
+        }
+
+        if (lockRequestType == LOCK_REFRESH) {
+
+            String ifHeader = req.getHeader("If");
+            if (ifHeader == null)
+                ifHeader = "";
+
+            // Checking resource locks
+
+            LockInfo toRenew = (LockInfo) resourceLocks.get(path);
+            Enumeration tokenList = null;
+            if (lock != null) {
+
+                // At least one of the tokens of the locks must have been given
+
+                tokenList = toRenew.tokens.elements();
+                while (tokenList.hasMoreElements()) {
+                    String token = (String) tokenList.nextElement();
+                    if (ifHeader.indexOf(token) != -1) {
+                        toRenew.expiresAt = lock.expiresAt;
+                        lock = toRenew;
+                    }
+                }
+
+            }
+
+            // Checking inheritable collection locks
+
+            Enumeration collectionLocksList = collectionLocks.elements();
+            while (collectionLocksList.hasMoreElements()) {
+                toRenew = (LockInfo) collectionLocksList.nextElement();
+                if (path.equals(toRenew.path)) {
+
+                    tokenList = toRenew.tokens.elements();
+                    while (tokenList.hasMoreElements()) {
+                        String token = (String) tokenList.nextElement();
+                        if (ifHeader.indexOf(token) != -1) {
+                            toRenew.expiresAt = lock.expiresAt;
+                            lock = toRenew;
+                        }
+                    }
+
+                }
+            }
+
+        }
+
+        // Set the status, then generate the XML response containing
+        // the lock information
+        XMLWriter generatedXML = new XMLWriter();
+        generatedXML.writeXMLHeader();
+        generatedXML.writeElement(null, "prop"
+                                  + generateNamespaceDeclarations(),
+                                  XMLWriter.OPENING);
+
+        generatedXML.writeElement(null, "lockdiscovery",
+                                  XMLWriter.OPENING);
+
+        lock.toXML(generatedXML);
+
+        generatedXML.writeElement(null, "lockdiscovery",
+                                  XMLWriter.CLOSING);
+
+        generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+
+        resp.setStatus(WebdavStatus.SC_OK);
+        resp.setContentType("text/xml; charset=UTF-8");
+        Writer writer = resp.getWriter();
+        writer.write(generatedXML.toString());
+        writer.close();
+
+    }
+
+
+    /**
+     * UNLOCK Method.
+     */
+    protected void doUnlock(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        // Checking resource locks
+
+        LockInfo lock = (LockInfo) resourceLocks.get(path);
+        Enumeration tokenList = null;
+        if (lock != null) {
+
+            // At least one of the tokens of the locks must have been given
+
+            tokenList = lock.tokens.elements();
+            while (tokenList.hasMoreElements()) {
+                String token = (String) tokenList.nextElement();
+                if (lockTokenHeader.indexOf(token) != -1) {
+                    lock.tokens.removeElement(token);
+                }
+            }
+
+            if (lock.tokens.isEmpty()) {
+                resourceLocks.remove(path);
+                // Removing any lock-null resource which would be present
+                lockNullResources.remove(path);
+            }
+
+        }
+
+        // Checking inheritable collection locks
+
+        Enumeration collectionLocksList = collectionLocks.elements();
+        while (collectionLocksList.hasMoreElements()) {
+            lock = (LockInfo) collectionLocksList.nextElement();
+            if (path.equals(lock.path)) {
+
+                tokenList = lock.tokens.elements();
+                while (tokenList.hasMoreElements()) {
+                    String token = (String) tokenList.nextElement();
+                    if (lockTokenHeader.indexOf(token) != -1) {
+                        lock.tokens.removeElement(token);
+                        break;
+                    }
+                }
+
+                if (lock.tokens.isEmpty()) {
+                    collectionLocks.removeElement(lock);
+                    // Removing any lock-null resource which would be present
+                    lockNullResources.remove(path);
+                }
+
+            }
+        }
+
+        resp.setStatus(WebdavStatus.SC_NO_CONTENT);
+
+    }
+
+    /**
+     * Return a context-relative path, beginning with a "/", that represents
+     * the canonical version of the specified path after ".." and "." elements
+     * are resolved out.  If the specified path attempts to go outside the
+     * boundaries of the current context (i.e. too many ".." path elements
+     * are present), return <code>null</code> instead.
+     *
+     * @param path Path to be normalized
+     */
+    protected String normalize(String path) {
+
+        if (path == null)
+            return null;
+
+        // Create a place for the normalized path
+        String normalized = path;
+
+        if (normalized == null)
+            return (null);
+
+        if (normalized.equals("/."))
+            return "/";
+
+        // Normalize the slashes and add leading slash if necessary
+        if (normalized.indexOf('\\') >= 0)
+            normalized = normalized.replace('\\', '/');
+        if (!normalized.startsWith("/"))
+            normalized = "/" + normalized;
+
+        // Resolve occurrences of "//" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("//");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 1);
+        }
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/./");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 2);
+        }
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/../");
+            if (index < 0)
+                break;
+            if (index == 0)
+                return (null);  // Trying to go outside our context
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            normalized = normalized.substring(0, index2) +
+                normalized.substring(index + 3);
+        }
+
+        // Return the normalized path that we have completed
+        return (normalized);
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+    /**
+     * Generate the namespace declarations.
+     */
+    private String generateNamespaceDeclarations() {
+        return " xmlns=\"" + DEFAULT_NAMESPACE + "\"";
+    }
+
+
+    /**
+     * Check to see if a resource is currently write locked. The method
+     * will look at the "If" header to make sure the client
+     * has give the appropriate lock tokens.
+     *
+     * @param req Servlet request
+     * @return boolean true if the resource is locked (and no appropriate
+     * lock token has been found for at least one of the non-shared locks which
+     * are present on the resource).
+     */
+    private boolean isLocked(HttpServletRequest req) {
+
+        String path = getRelativePath(req);
+
+        String ifHeader = req.getHeader("If");
+        if (ifHeader == null)
+            ifHeader = "";
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        return isLocked(path, ifHeader + lockTokenHeader);
+
+    }
+
+
+    /**
+     * Check to see if a resource is currently write locked.
+     *
+     * @param path Path of the resource
+     * @param ifHeader "If" HTTP header which was included in the request
+     * @return boolean true if the resource is locked (and no appropriate
+     * lock token has been found for at least one of the non-shared locks which
+     * are present on the resource).
+     */
+    private boolean isLocked(String path, String ifHeader) {
+
+        // Checking resource locks
+
+        LockInfo lock = (LockInfo) resourceLocks.get(path);
+        Enumeration tokenList = null;
+        if ((lock != null) && (lock.hasExpired())) {
+            resourceLocks.remove(path);
+        } else if (lock != null) {
+
+            // At least one of the tokens of the locks must have been given
+
+            tokenList = lock.tokens.elements();
+            boolean tokenMatch = false;
+            while (tokenList.hasMoreElements()) {
+                String token = (String) tokenList.nextElement();
+                if (ifHeader.indexOf(token) != -1)
+                    tokenMatch = true;
+            }
+            if (!tokenMatch)
+                return true;
+
+        }
+
+        // Checking inheritable collection locks
+
+        Enumeration collectionLocksList = collectionLocks.elements();
+        while (collectionLocksList.hasMoreElements()) {
+            lock = (LockInfo) collectionLocksList.nextElement();
+            if (lock.hasExpired()) {
+                collectionLocks.removeElement(lock);
+            } else if (path.startsWith(lock.path)) {
+
+                tokenList = lock.tokens.elements();
+                boolean tokenMatch = false;
+                while (tokenList.hasMoreElements()) {
+                    String token = (String) tokenList.nextElement();
+                    if (ifHeader.indexOf(token) != -1)
+                        tokenMatch = true;
+                }
+                if (!tokenMatch)
+                    return true;
+
+            }
+        }
+
+        return false;
+
+    }
+
+
+    /**
+     * Copy a resource.
+     *
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @return boolean true if the copy is successful
+     */
+    private boolean copyResource(HttpServletRequest req,
+                                 HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        // Parsing destination header
+
+        String destinationPath = req.getHeader("Destination");
+
+        if (destinationPath == null) {
+            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
+            return false;
+        }
+
+        // Remove url encoding from destination
+        destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");
+
+        int protocolIndex = destinationPath.indexOf("://");
+        if (protocolIndex >= 0) {
+            // if the Destination URL contains the protocol, we can safely
+            // trim everything upto the first "/" character after "://"
+            int firstSeparator =
+                destinationPath.indexOf("/", protocolIndex + 4);
+            if (firstSeparator < 0) {
+                destinationPath = "/";
+            } else {
+                destinationPath = destinationPath.substring(firstSeparator);
+            }
+        } else {
+            String hostName = req.getServerName();
+            if ((hostName != null) && (destinationPath.startsWith(hostName))) {
+                destinationPath = destinationPath.substring(hostName.length());
+            }
+
+            int portIndex = destinationPath.indexOf(":");
+            if (portIndex >= 0) {
+                destinationPath = destinationPath.substring(portIndex);
+            }
+
+            if (destinationPath.startsWith(":")) {
+                int firstSeparator = destinationPath.indexOf("/");
+                if (firstSeparator < 0) {
+                    destinationPath = "/";
+                } else {
+                    destinationPath =
+                        destinationPath.substring(firstSeparator);
+                }
+            }
+        }
+
+        // Normalise destination path (remove '.' and '..')
+        destinationPath = normalize(destinationPath);
+
+        String contextPath = req.getContextPath();
+        if ((contextPath != null) &&
+            (destinationPath.startsWith(contextPath))) {
+            destinationPath = destinationPath.substring(contextPath.length());
+        }
+
+        String pathInfo = req.getPathInfo();
+        if (pathInfo != null) {
+            String servletPath = req.getServletPath();
+            if ((servletPath != null) &&
+                (destinationPath.startsWith(servletPath))) {
+                destinationPath = destinationPath
+                    .substring(servletPath.length());
+            }
+        }
+
+        if (debug > 0)
+            log("Dest path :" + destinationPath);
+
+        if ((destinationPath.toUpperCase().startsWith("/WEB-INF")) ||
+            (destinationPath.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        String path = getRelativePath(req);
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        if (destinationPath.equals(path)) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        // Parsing overwrite header
+
+        boolean overwrite = true;
+        String overwriteHeader = req.getHeader("Overwrite");
+
+        if (overwriteHeader != null) {
+            if (overwriteHeader.equalsIgnoreCase("T")) {
+                overwrite = true;
+            } else {
+                overwrite = false;
+            }
+        }
+
+        // Overwriting the destination
+
+        boolean exists = true;
+        try {
+            resources.lookup(destinationPath);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        if (overwrite) {
+
+            // Delete destination resource, if it exists
+            if (exists) {
+                if (!deleteResource(destinationPath, req, resp, true)) {
+                    return false;
+                }
+            } else {
+                resp.setStatus(WebdavStatus.SC_CREATED);
+            }
+
+        } else {
+
+            // If the destination exists, then it's a conflict
+            if (exists) {
+                resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
+                return false;
+            }
+
+        }
+
+        // Copying source to destination
+
+        Hashtable errorList = new Hashtable();
+
+        boolean result = copyResource(resources, errorList,
+                                      path, destinationPath);
+
+        if ((!result) || (!errorList.isEmpty())) {
+
+            sendReport(req, resp, errorList);
+            return false;
+
+        }
+
+        // Removing any lock-null resource which would be present at
+        // the destination path
+        lockNullResources.remove(destinationPath);
+
+        return true;
+
+    }
+
+
+    /**
+     * Copy a collection.
+     *
+     * @param resources Resources implementation to be used
+     * @param errorList Hashtable containing the list of errors which occurred
+     * during the copy operation
+     * @param source Path of the resource to be copied
+     * @param dest Destination path
+     */
+    private boolean copyResource(DirContext resources, Hashtable errorList,
+                                 String source, String dest) {
+
+        if (debug > 1)
+            log("Copy: " + source + " To: " + dest);
+
+        Object object = null;
+        try {
+            object = resources.lookup(source);
+        } catch (NamingException e) {
+        }
+
+        if (object instanceof DirContext) {
+
+            try {
+                resources.createSubcontext(dest);
+            } catch (NamingException e) {
+                errorList.put
+                    (dest, new Integer(WebdavStatus.SC_CONFLICT));
+                return false;
+            }
+
+            try {
+                NamingEnumeration enumeration = resources.list(source);
+                while (enumeration.hasMoreElements()) {
+                    NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
+                    String childDest = dest;
+                    if (!childDest.equals("/"))
+                        childDest += "/";
+                    childDest += ncPair.getName();
+                    String childSrc = source;
+                    if (!childSrc.equals("/"))
+                        childSrc += "/";
+                    childSrc += ncPair.getName();
+                    copyResource(resources, errorList, childSrc, childDest);
+                }
+            } catch (NamingException e) {
+                errorList.put
+                    (dest, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                return false;
+            }
+
+        } else {
+
+            if (object instanceof Resource) {
+                try {
+                    resources.bind(dest, object);
+                } catch (NamingException e) {
+                    errorList.put
+                        (source,
+                         new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                    return false;
+                }
+            } else {
+                errorList.put
+                    (source,
+                     new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                return false;
+            }
+
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Delete a resource.
+     *
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @return boolean true if the copy is successful
+     */
+    private boolean deleteResource(HttpServletRequest req,
+                                   HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        String path = getRelativePath(req);
+
+        return deleteResource(path, req, resp, true);
+
+    }
+
+
+    /**
+     * Delete a resource.
+     *
+     * @param path Path of the resource which is to be deleted
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @param setStatus Should the response status be set on successful
+     *                  completion
+     */
+    private boolean deleteResource(String path, HttpServletRequest req,
+                                   HttpServletResponse resp, boolean setStatus)
+        throws ServletException, IOException {
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        String ifHeader = req.getHeader("If");
+        if (ifHeader == null)
+            ifHeader = "";
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        if (isLocked(path, ifHeader + lockTokenHeader)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return false;
+        }
+
+        boolean exists = true;
+        Object object = null;
+        try {
+            object = resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        if (!exists) {
+            resp.sendError(WebdavStatus.SC_NOT_FOUND);
+            return false;
+        }
+
+        boolean collection = (object instanceof DirContext);
+
+        if (!collection) {
+            try {
+                resources.unbind(path);
+            } catch (NamingException e) {
+                resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
+                return false;
+            }
+        } else {
+
+            Hashtable errorList = new Hashtable();
+
+            deleteCollection(req, resources, path, errorList);
+            try {
+                resources.unbind(path);
+            } catch (NamingException e) {
+                errorList.put(path, new Integer
+                    (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+            }
+
+            if (!errorList.isEmpty()) {
+
+                sendReport(req, resp, errorList);
+                return false;
+
+            }
+
+        }
+        if (setStatus) {
+            resp.setStatus(WebdavStatus.SC_NO_CONTENT);
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Deletes a collection.
+     *
+     * @param resources Resources implementation associated with the context
+     * @param path Path to the collection to be deleted
+     * @param errorList Contains the list of the errors which occurred
+     */
+    private void deleteCollection(HttpServletRequest req,
+                                  DirContext resources,
+                                  String path, Hashtable errorList) {
+
+        if (debug > 1)
+            log("Delete:" + path);
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            errorList.put(path, new Integer(WebdavStatus.SC_FORBIDDEN));
+            return;
+        }
+
+        String ifHeader = req.getHeader("If");
+        if (ifHeader == null)
+            ifHeader = "";
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        Enumeration enumeration = null;
+        try {
+            enumeration = resources.list(path);
+        } catch (NamingException e) {
+            errorList.put(path, new Integer
+                (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+            return;
+        }
+
+        while (enumeration.hasMoreElements()) {
+            NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
+            String childName = path;
+            if (!childName.equals("/"))
+                childName += "/";
+            childName += ncPair.getName();
+
+            if (isLocked(childName, ifHeader + lockTokenHeader)) {
+
+                errorList.put(childName, new Integer(WebdavStatus.SC_LOCKED));
+
+            } else {
+
+                try {
+                    Object object = resources.lookup(childName);
+                    if (object instanceof DirContext) {
+                        deleteCollection(req, resources, childName, errorList);
+                    }
+
+                    try {
+                        resources.unbind(childName);
+                    } catch (NamingException e) {
+                        if (!(object instanceof DirContext)) {
+                            // If it's not a collection, then it's an unknown
+                            // error
+                            errorList.put
+                                (childName, new Integer
+                                    (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                        }
+                    }
+                } catch (NamingException e) {
+                    errorList.put
+                        (childName, new Integer
+                            (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                }
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Send a multistatus element containing a complete error report to the
+     * client.
+     *
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @param errorList List of error to be displayed
+     */
+    private void sendReport(HttpServletRequest req, HttpServletResponse resp,
+                            Hashtable errorList)
+        throws ServletException, IOException {
+
+        resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
+
+        String absoluteUri = req.getRequestURI();
+        String relativePath = getRelativePath(req);
+
+        XMLWriter generatedXML = new XMLWriter();
+        generatedXML.writeXMLHeader();
+
+        generatedXML.writeElement(null, "multistatus"
+                                  + generateNamespaceDeclarations(),
+                                  XMLWriter.OPENING);
+
+        Enumeration pathList = errorList.keys();
+        while (pathList.hasMoreElements()) {
+
+            String errorPath = (String) pathList.nextElement();
+            int errorCode = ((Integer) errorList.get(errorPath)).intValue();
+
+            generatedXML.writeElement(null, "response", XMLWriter.OPENING);
+
+            generatedXML.writeElement(null, "href", XMLWriter.OPENING);
+            String toAppend = errorPath.substring(relativePath.length());
+            if (!toAppend.startsWith("/"))
+                toAppend = "/" + toAppend;
+            generatedXML.writeText(absoluteUri + toAppend);
+            generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML
+                .writeText("HTTP/1.1 " + errorCode + " "
+                           + WebdavStatus.getStatusText(errorCode));
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
+
+        }
+
+        generatedXML.writeElement(null, "multistatus", XMLWriter.CLOSING);
+
+        Writer writer = resp.getWriter();
+        writer.write(generatedXML.toString());
+        writer.close();
+
+    }
+
+
+    /**
+     * Propfind helper method.
+     *
+     * @param req The servlet request
+     * @param resources Resources object associated with this context
+     * @param generatedXML XML response to the Propfind request
+     * @param path Path of the current resource
+     * @param type Propfind type
+     * @param propertiesVector If the propfind type is find properties by
+     * name, then this Vector contains those properties
+     */
+    private void parseProperties(HttpServletRequest req,
+                                 XMLWriter generatedXML,
+                                 String path, int type,
+                                 Vector propertiesVector) {
+
+        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
+        // (the "toUpperCase()" avoids problems on Windows systems)
+        if (path.toUpperCase().startsWith("/WEB-INF") ||
+            path.toUpperCase().startsWith("/META-INF"))
+            return;
+
+        CacheEntry cacheEntry = resources.lookupCache(path);
+
+        generatedXML.writeElement(null, "response", XMLWriter.OPENING);
+        String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
+                                   + WebdavStatus.getStatusText
+                                   (WebdavStatus.SC_OK));
+
+        // Generating href element
+        generatedXML.writeElement(null, "href", XMLWriter.OPENING);
+
+        String href = req.getContextPath() + req.getServletPath();
+        if ((href.endsWith("/")) && (path.startsWith("/")))
+            href += path.substring(1);
+        else
+            href += path;
+        if ((cacheEntry.context != null) && (!href.endsWith("/")))
+            href += "/";
+
+        generatedXML.writeText(rewriteUrl(href));
+
+        generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
+
+        String resourceName = path;
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash != -1)
+            resourceName = resourceName.substring(lastSlash + 1);
+
+        switch (type) {
+
+        case FIND_ALL_PROP :
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            generatedXML.writeProperty
+                (null, "creationdate",
+                 getISOCreationDate(cacheEntry.attributes.getCreation()));
+            generatedXML.writeElement(null, "displayname", XMLWriter.OPENING);
+            generatedXML.writeData(resourceName);
+            generatedXML.writeElement(null, "displayname", XMLWriter.CLOSING);
+            if (cacheEntry.resource != null) {
+                generatedXML.writeProperty
+                    (null, "getlastmodified", FastHttpDateFormat.formatDate
+                           (cacheEntry.attributes.getLastModified(), null));
+                generatedXML.writeProperty
+                    (null, "getcontentlength",
+                     String.valueOf(cacheEntry.attributes.getContentLength()));
+                String contentType = getServletContext().getMimeType
+                    (cacheEntry.name);
+                if (contentType != null) {
+                    generatedXML.writeProperty(null, "getcontenttype",
+                                               contentType);
+                }
+                generatedXML.writeProperty(null, "getetag",
+                                           getETag(cacheEntry.attributes));
+                generatedXML.writeElement(null, "resourcetype",
+                                          XMLWriter.NO_CONTENT);
+            } else {
+                generatedXML.writeElement(null, "resourcetype",
+                                          XMLWriter.OPENING);
+                generatedXML.writeElement(null, "collection",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "resourcetype",
+                                          XMLWriter.CLOSING);
+            }
+
+            generatedXML.writeProperty(null, "source", "");
+
+            String supportedLocks = "<lockentry>"
+                + "<lockscope><exclusive/></lockscope>"
+                + "<locktype><write/></locktype>"
+                + "</lockentry>" + "<lockentry>"
+                + "<lockscope><shared/></lockscope>"
+                + "<locktype><write/></locktype>"
+                + "</lockentry>";
+            generatedXML.writeElement(null, "supportedlock",
+                                      XMLWriter.OPENING);
+            generatedXML.writeText(supportedLocks);
+            generatedXML.writeElement(null, "supportedlock",
+                                      XMLWriter.CLOSING);
+
+            generateLockDiscovery(path, generatedXML);
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            break;
+
+        case FIND_PROPERTY_NAMES :
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            generatedXML.writeElement(null, "creationdate",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "displayname",
+                                      XMLWriter.NO_CONTENT);
+            if (cacheEntry.resource != null) {
+                generatedXML.writeElement(null, "getcontentlanguage",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "getcontentlength",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "getcontenttype",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "getetag",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "getlastmodified",
+                                          XMLWriter.NO_CONTENT);
+            }
+            generatedXML.writeElement(null, "resourcetype",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "source", XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "lockdiscovery",
+                                      XMLWriter.NO_CONTENT);
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            break;
+
+        case FIND_BY_PROPERTY :
+
+            Vector propertiesNotFound = new Vector();
+
+            // Parse the list of properties
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            Enumeration properties = propertiesVector.elements();
+
+            while (properties.hasMoreElements()) {
+
+                String property = (String) properties.nextElement();
+
+                if (property.equals("creationdate")) {
+                    generatedXML.writeProperty
+                        (null, "creationdate",
+                         getISOCreationDate(cacheEntry.attributes.getCreation()));
+                } else if (property.equals("displayname")) {
+                    generatedXML.writeElement
+                        (null, "displayname", XMLWriter.OPENING);
+                    generatedXML.writeData(resourceName);
+                    generatedXML.writeElement
+                        (null, "displayname", XMLWriter.CLOSING);
+                } else if (property.equals("getcontentlanguage")) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeElement(null, "getcontentlanguage",
+                                                  XMLWriter.NO_CONTENT);
+                    }
+                } else if (property.equals("getcontentlength")) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeProperty
+                            (null, "getcontentlength",
+                             (String.valueOf(cacheEntry.attributes.getContentLength())));
+                    }
+                } else if (property.equals("getcontenttype")) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeProperty
+                            (null, "getcontenttype",
+                             getServletContext().getMimeType
+                             (cacheEntry.name));
+                    }
+                } else if (property.equals("getetag")) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeProperty
+                            (null, "getetag", getETag(cacheEntry.attributes));
+                    }
+                } else if (property.equals("getlastmodified")) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeProperty
+                            (null, "getlastmodified", FastHttpDateFormat.formatDate
+                                    (cacheEntry.attributes.getLastModified(), null));
+                    }
+                } else if (property.equals("resourcetype")) {
+                    if (cacheEntry.context != null) {
+                        generatedXML.writeElement(null, "resourcetype",
+                                                  XMLWriter.OPENING);
+                        generatedXML.writeElement(null, "collection",
+                                                  XMLWriter.NO_CONTENT);
+                        generatedXML.writeElement(null, "resourcetype",
+                                                  XMLWriter.CLOSING);
+                    } else {
+                        generatedXML.writeElement(null, "resourcetype",
+                                                  XMLWriter.NO_CONTENT);
+                    }
+                } else if (property.equals("source")) {
+                    generatedXML.writeProperty(null, "source", "");
+                } else if (property.equals("supportedlock")) {
+                    supportedLocks = "<lockentry>"
+                        + "<lockscope><exclusive/></lockscope>"
+                        + "<locktype><write/></locktype>"
+                        + "</lockentry>" + "<lockentry>"
+                        + "<lockscope><shared/></lockscope>"
+                        + "<locktype><write/></locktype>"
+                        + "</lockentry>";
+                    generatedXML.writeElement(null, "supportedlock",
+                                              XMLWriter.OPENING);
+                    generatedXML.writeText(supportedLocks);
+                    generatedXML.writeElement(null, "supportedlock",
+                                              XMLWriter.CLOSING);
+                } else if (property.equals("lockdiscovery")) {
+                    if (!generateLockDiscovery(path, generatedXML))
+                        propertiesNotFound.addElement(property);
+                } else {
+                    propertiesNotFound.addElement(property);
+                }
+
+            }
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            Enumeration propertiesNotFoundList = propertiesNotFound.elements();
+
+            if (propertiesNotFoundList.hasMoreElements()) {
+
+                status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
+                                    + " " + WebdavStatus.getStatusText
+                                    (WebdavStatus.SC_NOT_FOUND));
+
+                generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+                generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+                while (propertiesNotFoundList.hasMoreElements()) {
+                    generatedXML.writeElement
+                        (null, (String) propertiesNotFoundList.nextElement(),
+                         XMLWriter.NO_CONTENT);
+                }
+
+                generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+                generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+                generatedXML.writeText(status);
+                generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+                generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            }
+
+            break;
+
+        }
+
+        generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
+
+    }
+
+
+    /**
+     * Propfind helper method. Dispays the properties of a lock-null resource.
+     *
+     * @param resources Resources object associated with this context
+     * @param generatedXML XML response to the Propfind request
+     * @param path Path of the current resource
+     * @param type Propfind type
+     * @param propertiesVector If the propfind type is find properties by
+     * name, then this Vector contains those properties
+     */
+    private void parseLockNullProperties(HttpServletRequest req,
+                                         XMLWriter generatedXML,
+                                         String path, int type,
+                                         Vector propertiesVector) {
+
+        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
+        // (the "toUpperCase()" avoids problems on Windows systems)
+        if (path.toUpperCase().startsWith("/WEB-INF") ||
+            path.toUpperCase().startsWith("/META-INF"))
+            return;
+
+        // Retrieving the lock associated with the lock-null resource
+        LockInfo lock = (LockInfo) resourceLocks.get(path);
+
+        if (lock == null)
+            return;
+
+        generatedXML.writeElement(null, "response", XMLWriter.OPENING);
+        String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
+                                   + WebdavStatus.getStatusText
+                                   (WebdavStatus.SC_OK));
+
+        // Generating href element
+        generatedXML.writeElement(null, "href", XMLWriter.OPENING);
+
+        String absoluteUri = req.getRequestURI();
+        String relativePath = getRelativePath(req);
+        String toAppend = path.substring(relativePath.length());
+        if (!toAppend.startsWith("/"))
+            toAppend = "/" + toAppend;
+
+        generatedXML.writeText(rewriteUrl(normalize(absoluteUri + toAppend)));
+
+        generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
+
+        String resourceName = path;
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash != -1)
+            resourceName = resourceName.substring(lastSlash + 1);
+
+        switch (type) {
+
+        case FIND_ALL_PROP :
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            generatedXML.writeProperty
+                (null, "creationdate",
+                 getISOCreationDate(lock.creationDate.getTime()));
+            generatedXML.writeElement
+                (null, "displayname", XMLWriter.OPENING);
+            generatedXML.writeData(resourceName);
+            generatedXML.writeElement
+                (null, "displayname", XMLWriter.CLOSING);
+            generatedXML.writeProperty(null, "getlastmodified",
+                                       FastHttpDateFormat.formatDate
+                                       (lock.creationDate.getTime(), null));
+            generatedXML.writeProperty
+                (null, "getcontentlength", String.valueOf(0));
+            generatedXML.writeProperty(null, "getcontenttype", "");
+            generatedXML.writeProperty(null, "getetag", "");
+            generatedXML.writeElement(null, "resourcetype",
+                                      XMLWriter.OPENING);
+            generatedXML.writeElement(null, "lock-null", XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "resourcetype",
+                                      XMLWriter.CLOSING);
+
+            generatedXML.writeProperty(null, "source", "");
+
+            String supportedLocks = "<lockentry>"
+                + "<lockscope><exclusive/></lockscope>"
+                + "<locktype><write/></locktype>"
+                + "</lockentry>" + "<lockentry>"
+                + "<lockscope><shared/></lockscope>"
+                + "<locktype><write/></locktype>"
+                + "</lockentry>";
+            generatedXML.writeElement(null, "supportedlock",
+                                      XMLWriter.OPENING);
+            generatedXML.writeText(supportedLocks);
+            generatedXML.writeElement(null, "supportedlock",
+                                      XMLWriter.CLOSING);
+
+            generateLockDiscovery(path, generatedXML);
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            break;
+
+        case FIND_PROPERTY_NAMES :
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            generatedXML.writeElement(null, "creationdate",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "displayname",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getcontentlanguage",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getcontentlength",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getcontenttype",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getetag",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getlastmodified",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "resourcetype",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "source",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "lockdiscovery",
+                                      XMLWriter.NO_CONTENT);
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            break;
+
+        case FIND_BY_PROPERTY :
+
+            Vector propertiesNotFound = new Vector();
+
+            // Parse the list of properties
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            Enumeration properties = propertiesVector.elements();
+
+            while (properties.hasMoreElements()) {
+
+                String property = (String) properties.nextElement();
+
+                if (property.equals("creationdate")) {
+                    generatedXML.writeProperty
+                        (null, "creationdate",
+                         getISOCreationDate(lock.creationDate.getTime()));
+                } else if (property.equals("displayname")) {
+                    generatedXML.writeElement
+                        (null, "displayname", XMLWriter.OPENING);
+                    generatedXML.writeData(resourceName);
+                    generatedXML.writeElement
+                        (null, "displayname", XMLWriter.CLOSING);
+                } else if (property.equals("getcontentlanguage")) {
+                    generatedXML.writeElement(null, "getcontentlanguage",
+                                              XMLWriter.NO_CONTENT);
+                } else if (property.equals("getcontentlength")) {
+                    generatedXML.writeProperty
+                        (null, "getcontentlength", (String.valueOf(0)));
+                } else if (property.equals("getcontenttype")) {
+                    generatedXML.writeProperty
+                        (null, "getcontenttype", "");
+                } else if (property.equals("getetag")) {
+                    generatedXML.writeProperty(null, "getetag", "");
+                } else if (property.equals("getlastmodified")) {
+                    generatedXML.writeProperty
+                        (null, "getlastmodified",
+                          FastHttpDateFormat.formatDate
+                         (lock.creationDate.getTime(), null));
+                } else if (property.equals("resourcetype")) {
+                    generatedXML.writeElement(null, "resourcetype",
+                                              XMLWriter.OPENING);
+                    generatedXML.writeElement(null, "lock-null",
+                                              XMLWriter.NO_CONTENT);
+                    generatedXML.writeElement(null, "resourcetype",
+                                              XMLWriter.CLOSING);
+                } else if (property.equals("source")) {
+                    generatedXML.writeProperty(null, "source", "");
+                } else if (property.equals("supportedlock")) {
+                    supportedLocks = "<lockentry>"
+                        + "<lockscope><exclusive/></lockscope>"
+                        + "<locktype><write/></locktype>"
+                        + "</lockentry>" + "<lockentry>"
+                        + "<lockscope><shared/></lockscope>"
+                        + "<locktype><write/></locktype>"
+                        + "</lockentry>";
+                    generatedXML.writeElement(null, "supportedlock",
+                                              XMLWriter.OPENING);
+                    generatedXML.writeText(supportedLocks);
+                    generatedXML.writeElement(null, "supportedlock",
+                                              XMLWriter.CLOSING);
+                } else if (property.equals("lockdiscovery")) {
+                    if (!generateLockDiscovery(path, generatedXML))
+                        propertiesNotFound.addElement(property);
+                } else {
+                    propertiesNotFound.addElement(property);
+                }
+
+            }
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            Enumeration propertiesNotFoundList = propertiesNotFound.elements();
+
+            if (propertiesNotFoundList.hasMoreElements()) {
+
+                status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
+                                    + " " + WebdavStatus.getStatusText
+                                    (WebdavStatus.SC_NOT_FOUND));
+
+                generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+                generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+                while (propertiesNotFoundList.hasMoreElements()) {
+                    generatedXML.writeElement
+                        (null, (String) propertiesNotFoundList.nextElement(),
+                         XMLWriter.NO_CONTENT);
+                }
+
+                generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+                generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+                generatedXML.writeText(status);
+                generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+                generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            }
+
+            break;
+
+        }
+
+        generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
+
+    }
+
+
+    /**
+     * Print the lock discovery information associated with a path.
+     *
+     * @param path Path
+     * @param generatedXML XML data to which the locks info will be appended
+     * @return true if at least one lock was displayed
+     */
+    private boolean generateLockDiscovery
+        (String path, XMLWriter generatedXML) {
+
+        LockInfo resourceLock = (LockInfo) resourceLocks.get(path);
+        Enumeration collectionLocksList = collectionLocks.elements();
+
+        boolean wroteStart = false;
+
+        if (resourceLock != null) {
+            wroteStart = true;
+            generatedXML.writeElement(null, "lockdiscovery",
+                                      XMLWriter.OPENING);
+            resourceLock.toXML(generatedXML);
+        }
+
+        while (collectionLocksList.hasMoreElements()) {
+            LockInfo currentLock =
+                (LockInfo) collectionLocksList.nextElement();
+            if (path.startsWith(currentLock.path)) {
+                if (!wroteStart) {
+                    wroteStart = true;
+                    generatedXML.writeElement(null, "lockdiscovery",
+                                              XMLWriter.OPENING);
+                }
+                currentLock.toXML(generatedXML);
+            }
+        }
+
+        if (wroteStart) {
+            generatedXML.writeElement(null, "lockdiscovery",
+                                      XMLWriter.CLOSING);
+        } else {
+            return false;
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Get creation date in ISO format.
+     */
+    private String getISOCreationDate(long creationDate) {
+        StringBuffer creationDateValue = new StringBuffer
+            (creationDateFormat.format
+             (new Date(creationDate)));
+        /*
+        int offset = Calendar.getInstance().getTimeZone().getRawOffset()
+            / 3600000; // FIXME ?
+        if (offset < 0) {
+            creationDateValue.append("-");
+            offset = -offset;
+        } else if (offset > 0) {
+            creationDateValue.append("+");
+        }
+        if (offset != 0) {
+            if (offset < 10)
+                creationDateValue.append("0");
+            creationDateValue.append(offset + ":00");
+        } else {
+            creationDateValue.append("Z");
+        }
+        */
+        return creationDateValue.toString();
+    }
+
+    /**
+     * Determines the methods normally allowed for the resource.
+     *
+     */
+    private StringBuffer determineMethodsAllowed(DirContext resources,
+                                                 HttpServletRequest req) {
+
+        StringBuffer methodsAllowed = new StringBuffer();
+        boolean exists = true;
+        Object object = null;
+        try {
+            String path = getRelativePath(req);
+
+            object = resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        if (!exists) {
+            methodsAllowed.append("OPTIONS, MKCOL, PUT, LOCK");
+            return methodsAllowed;
+        }
+
+        methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE");
+        methodsAllowed.append(", PROPPATCH, COPY, MOVE, LOCK, UNLOCK");
+
+        if (listings) {
+            methodsAllowed.append(", PROPFIND");
+        }
+
+        if (!(object instanceof DirContext)) {
+            methodsAllowed.append(", PUT");
+        }
+
+        return methodsAllowed;
+    }
+
+    // --------------------------------------------------  LockInfo Inner Class
+
+
+    /**
+     * Holds a lock information.
+     */
+    private class LockInfo {
+
+
+        // -------------------------------------------------------- Constructor
+
+
+        /**
+         * Constructor.
+         */
+        public LockInfo() {
+
+        }
+
+
+        // ------------------------------------------------- Instance Variables
+
+
+        String path = "/";
+        String type = "write";
+        String scope = "exclusive";
+        int depth = 0;
+        String owner = "";
+        Vector tokens = new Vector();
+        long expiresAt = 0;
+        Date creationDate = new Date();
+
+
+        // ----------------------------------------------------- Public Methods
+
+
+        /**
+         * Get a String representation of this lock token.
+         */
+        public String toString() {
+
+            String result =  "Type:" + type + "\n";
+            result += "Scope:" + scope + "\n";
+            result += "Depth:" + depth + "\n";
+            result += "Owner:" + owner + "\n";
+            result += "Expiration:"
+                + FastHttpDateFormat.formatDate(expiresAt, null) + "\n";
+            Enumeration tokensList = tokens.elements();
+            while (tokensList.hasMoreElements()) {
+                result += "Token:" + tokensList.nextElement() + "\n";
+            }
+            return result;
+
+        }
+
+
+        /**
+         * Return true if the lock has expired.
+         */
+        public boolean hasExpired() {
+            return (System.currentTimeMillis() > expiresAt);
+        }
+
+
+        /**
+         * Return true if the lock is exclusive.
+         */
+        public boolean isExclusive() {
+
+            return (scope.equals("exclusive"));
+
+        }
+
+
+        /**
+         * Get an XML representation of this lock token. This method will
+         * append an XML fragment to the given XML writer.
+         */
+        public void toXML(XMLWriter generatedXML) {
+
+            generatedXML.writeElement(null, "activelock", XMLWriter.OPENING);
+
+            generatedXML.writeElement(null, "locktype", XMLWriter.OPENING);
+            generatedXML.writeElement(null, type, XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "locktype", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "lockscope", XMLWriter.OPENING);
+            generatedXML.writeElement(null, scope, XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "lockscope", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "depth", XMLWriter.OPENING);
+            if (depth == INFINITY) {
+                generatedXML.writeText("Infinity");
+            } else {
+                generatedXML.writeText("0");
+            }
+            generatedXML.writeElement(null, "depth", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "owner", XMLWriter.OPENING);
+            generatedXML.writeText(owner);
+            generatedXML.writeElement(null, "owner", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "timeout", XMLWriter.OPENING);
+            long timeout = (expiresAt - System.currentTimeMillis()) / 1000;
+            generatedXML.writeText("Second-" + timeout);
+            generatedXML.writeElement(null, "timeout", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "locktoken", XMLWriter.OPENING);
+            Enumeration tokensList = tokens.elements();
+            while (tokensList.hasMoreElements()) {
+                generatedXML.writeElement(null, "href", XMLWriter.OPENING);
+                generatedXML.writeText("opaquelocktoken:"
+                                       + tokensList.nextElement());
+                generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
+            }
+            generatedXML.writeElement(null, "locktoken", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "activelock", XMLWriter.CLOSING);
+
+        }
+
+
+    }
+
+
+    // --------------------------------------------------- Property Inner Class
+
+
+    private class Property {
+
+        public String name;
+        public String value;
+        public String namespace;
+        public String namespaceAbbrev;
+        public int status = WebdavStatus.SC_OK;
+
+    }
+
+
+};
+
+
+// --------------------------------------------------------  WebdavStatus Class
+
+
+/**
+ * Wraps the HttpServletResponse class to abstract the
+ * specific protocol used.  To support other protocols
+ * we would only need to modify this class and the
+ * WebDavRetCode classes.
+ *
+ * @author              Marc Eaddy
+ * @version             1.0, 16 Nov 1997
+ */
+class WebdavStatus {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * This Hashtable contains the mapping of HTTP and WebDAV
+     * status codes to descriptive text.  This is a static
+     * variable.
+     */
+    private static Hashtable mapStatusCodes = new Hashtable();
+
+
+    // ------------------------------------------------------ HTTP Status Codes
+
+
+    /**
+     * Status code (200) indicating the request succeeded normally.
+     */
+    public static final int SC_OK = HttpServletResponse.SC_OK;
+
+
+    /**
+     * Status code (201) indicating the request succeeded and created
+     * a new resource on the server.
+     */
+    public static final int SC_CREATED = HttpServletResponse.SC_CREATED;
+
+
+    /**
+     * Status code (202) indicating that a request was accepted for
+     * processing, but was not completed.
+     */
+    public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED;
+
+
+    /**
+     * Status code (204) indicating that the request succeeded but that
+     * there was no new information to return.
+     */
+    public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT;
+
+
+    /**
+     * Status code (301) indicating that the resource has permanently
+     * moved to a new location, and that future references should use a
+     * new URI with their requests.
+     */
+    public static final int SC_MOVED_PERMANENTLY =
+        HttpServletResponse.SC_MOVED_PERMANENTLY;
+
+
+    /**
+     * Status code (302) indicating that the resource has temporarily
+     * moved to another location, but that future references should
+     * still use the original URI to access the resource.
+     */
+    public static final int SC_MOVED_TEMPORARILY =
+        HttpServletResponse.SC_MOVED_TEMPORARILY;
+
+
+    /**
+     * Status code (304) indicating that a conditional GET operation
+     * found that the resource was available and not modified.
+     */
+    public static final int SC_NOT_MODIFIED =
+        HttpServletResponse.SC_NOT_MODIFIED;
+
+
+    /**
+     * Status code (400) indicating the request sent by the client was
+     * syntactically incorrect.
+     */
+    public static final int SC_BAD_REQUEST =
+        HttpServletResponse.SC_BAD_REQUEST;
+
+
+    /**
+     * Status code (401) indicating that the request requires HTTP
+     * authentication.
+     */
+    public static final int SC_UNAUTHORIZED =
+        HttpServletResponse.SC_UNAUTHORIZED;
+
+
+    /**
+     * Status code (403) indicating the server understood the request
+     * but refused to fulfill it.
+     */
+    public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
+
+
+    /**
+     * Status code (404) indicating that the requested resource is not
+     * available.
+     */
+    public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
+
+
+    /**
+     * Status code (500) indicating an error inside the HTTP service
+     * which prevented it from fulfilling the request.
+     */
+    public static final int SC_INTERNAL_SERVER_ERROR =
+        HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+
+
+    /**
+     * Status code (501) indicating the HTTP service does not support
+     * the functionality needed to fulfill the request.
+     */
+    public static final int SC_NOT_IMPLEMENTED =
+        HttpServletResponse.SC_NOT_IMPLEMENTED;
+
+
+    /**
+     * Status code (502) indicating that the HTTP server received an
+     * invalid response from a server it consulted when acting as a
+     * proxy or gateway.
+     */
+    public static final int SC_BAD_GATEWAY =
+        HttpServletResponse.SC_BAD_GATEWAY;
+
+
+    /**
+     * Status code (503) indicating that the HTTP service is
+     * temporarily overloaded, and unable to handle the request.
+     */
+    public static final int SC_SERVICE_UNAVAILABLE =
+        HttpServletResponse.SC_SERVICE_UNAVAILABLE;
+
+
+    /**
+     * Status code (100) indicating the client may continue with
+     * its request.  This interim response is used to inform the
+     * client that the initial part of the request has been
+     * received and has not yet been rejected by the server.
+     */
+    public static final int SC_CONTINUE = 100;
+
+
+    /**
+     * Status code (405) indicating the method specified is not
+     * allowed for the resource.
+     */
+    public static final int SC_METHOD_NOT_ALLOWED = 405;
+
+
+    /**
+     * Status code (409) indicating that the request could not be
+     * completed due to a conflict with the current state of the
+     * resource.
+     */
+    public static final int SC_CONFLICT = 409;
+
+
+    /**
+     * Status code (412) indicating the precondition given in one
+     * or more of the request-header fields evaluated to false
+     * when it was tested on the server.
+     */
+    public static final int SC_PRECONDITION_FAILED = 412;
+
+
+    /**
+     * Status code (413) indicating the server is refusing to
+     * process a request because the request entity is larger
+     * than the server is willing or able to process.
+     */
+    public static final int SC_REQUEST_TOO_LONG = 413;
+
+
+    /**
+     * Status code (415) indicating the server is refusing to service
+     * the request because the entity of the request is in a format
+     * not supported by the requested resource for the requested
+     * method.
+     */
+    public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+
+
+    // -------------------------------------------- Extended WebDav status code
+
+
+    /**
+     * Status code (207) indicating that the response requires
+     * providing status for multiple independent operations.
+     */
+    public static final int SC_MULTI_STATUS = 207;
+    // This one colides with HTTP 1.1
+    // "207 Parital Update OK"
+
+
+    /**
+     * Status code (418) indicating the entity body submitted with
+     * the PATCH method was not understood by the resource.
+     */
+    public static final int SC_UNPROCESSABLE_ENTITY = 418;
+    // This one colides with HTTP 1.1
+    // "418 Reauthentication Required"
+
+
+    /**
+     * Status code (419) indicating that the resource does not have
+     * sufficient space to record the state of the resource after the
+     * execution of this method.
+     */
+    public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
+    // This one colides with HTTP 1.1
+    // "419 Proxy Reauthentication Required"
+
+
+    /**
+     * Status code (420) indicating the method was not executed on
+     * a particular resource within its scope because some part of
+     * the method's execution failed causing the entire method to be
+     * aborted.
+     */
+    public static final int SC_METHOD_FAILURE = 420;
+
+
+    /**
+     * Status code (423) indicating the destination resource of a
+     * method is locked, and either the request did not contain a
+     * valid Lock-Info header, or the Lock-Info header identifies
+     * a lock held by another principal.
+     */
+    public static final int SC_LOCKED = 423;
+
+
+    // ------------------------------------------------------------ Initializer
+
+
+    static {
+        // HTTP 1.0 tatus Code
+        addStatusCodeMap(SC_OK, "OK");
+        addStatusCodeMap(SC_CREATED, "Created");
+        addStatusCodeMap(SC_ACCEPTED, "Accepted");
+        addStatusCodeMap(SC_NO_CONTENT, "No Content");
+        addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
+        addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
+        addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
+        addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
+        addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
+        addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
+        addStatusCodeMap(SC_NOT_FOUND, "Not Found");
+        addStatusCodeMap(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
+        addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
+        addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
+        addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+        addStatusCodeMap(SC_CONTINUE, "Continue");
+        addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
+        addStatusCodeMap(SC_CONFLICT, "Conflict");
+        addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
+        addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
+        addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
+        // WebDav Status Codes
+        addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
+        addStatusCodeMap(SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity");
+        addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE,
+                         "Insufficient Space On Resource");
+        addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
+        addStatusCodeMap(SC_LOCKED, "Locked");
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Returns the HTTP status text for the HTTP or WebDav status code
+     * specified by looking it up in the static mapping.  This is a
+     * static function.
+     *
+     * @param   nHttpStatusCode [IN] HTTP or WebDAV status code
+     * @return  A string with a short descriptive phrase for the
+     *                  HTTP status code (e.g., "OK").
+     */
+    public static String getStatusText(int nHttpStatusCode) {
+        Integer intKey = new Integer(nHttpStatusCode);
+
+        if (!mapStatusCodes.containsKey(intKey)) {
+            return "";
+        } else {
+            return (String) mapStatusCodes.get(intKey);
+        }
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Adds a new status code -> status text mapping.  This is a static
+     * method because the mapping is a static variable.
+     *
+     * @param   nKey    [IN] HTTP or WebDAV status code
+     * @param   strVal  [IN] HTTP status text
+     */
+    private static void addStatusCodeMap(int nKey, String strVal) {
+        mapStatusCodes.put(new Integer(nKey), strVal);
+    }
+
+};
+
diff --git a/container/catalina/src/share/org/apache/catalina/servlets/package.html b/container/catalina/src/share/org/apache/catalina/servlets/package.html
new file mode 100644
index 0000000..1c92843
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/servlets/package.html
@@ -0,0 +1,17 @@
+<body>
+
+<p>This package contains <code>Servlets</code> that implement some of the
+standard functionality provided by the Catalina servlet container.  Because
+these servlets are in the <code>org.apache.catalina</code> package hierarchy,
+they are in the privileged position of being able to reference internal server
+data structures, which application level servlets are prevented from
+accessing (by the application class loader implementation).</p>
+
+<p>To the extent that these servlets depend upon internal Catalina data
+structures, they are obviously not portable to other servlet container
+environments.  However, they can be used as models for creating application
+level servlets that provide similar capabilities -- most obviously the
+<a href="DefaultServlet.html">DefaultServlet</a> implementation, which
+serves static resources when Catalina runs stand-alone.</p>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/catalina/session/Constants.java b/container/catalina/src/share/org/apache/catalina/session/Constants.java
new file mode 100644
index 0000000..f719852
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/Constants.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.session;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.session</code>
+ * package.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.session";
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/session/FileStore.java b/container/catalina/src/share/org/apache/catalina/session/FileStore.java
new file mode 100644
index 0000000..f6877b3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/FileStore.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.session;
+
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+
+import javax.servlet.ServletContext;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Session;
+import org.apache.catalina.Store;
+import org.apache.catalina.util.CustomObjectInputStream;
+
+
+/**
+ * Concrete implementation of the <b>Store</b> interface that utilizes
+ * a file per saved Session in a configured directory.  Sessions that are
+ * saved are still subject to being expired based on inactivity.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class FileStore
+    extends StoreBase implements Store {
+
+
+    // ----------------------------------------------------- Constants
+
+
+    /**
+     * The extension to use for serialized session filenames.
+     */
+    private static final String FILE_EXT = ".session";
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The pathname of the directory in which Sessions are stored.
+     * This may be an absolute pathname, or a relative path that is
+     * resolved against the temporary work directory for this application.
+     */
+    private String directory = ".";
+
+
+    /**
+     * A File representing the directory in which Sessions are stored.
+     */
+    private File directoryFile = null;
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "FileStore/1.0";
+
+    /**
+     * Name to register for this Store, used for logging.
+     */
+    private static final String storeName = "fileStore";
+
+    /**
+     * Name to register for the background thread.
+     */
+    private static final String threadName = "FileStore";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the directory path for this Store.
+     */
+    public String getDirectory() {
+
+        return (directory);
+
+    }
+
+
+    /**
+     * Set the directory path for this Store.
+     *
+     * @param path The new directory path
+     */
+    public void setDirectory(String path) {
+
+        String oldDirectory = this.directory;
+        this.directory = path;
+        this.directoryFile = null;
+        support.firePropertyChange("directory", oldDirectory,
+                                   this.directory);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Store implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * Return the thread name for this Store.
+     */
+    public String getThreadName() {
+        return(threadName);
+    }
+
+    /**
+     * Return the name for this Store, used for logging.
+     */
+    public String getStoreName() {
+        return(storeName);
+    }
+
+
+    /**
+     * Return the number of Sessions present in this Store.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public int getSize() throws IOException {
+
+        // Acquire the list of files in our storage directory
+        File file = directory();
+        if (file == null) {
+            return (0);
+        }
+        String files[] = file.list();
+
+        // Figure out which files are sessions
+        int keycount = 0;
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].endsWith(FILE_EXT)) {
+                keycount++;
+            }
+        }
+        return (keycount);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Remove all of the Sessions in this Store.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void clear()
+        throws IOException {
+
+        String[] keys = keys();
+        for (int i = 0; i < keys.length; i++) {
+            remove(keys[i]);
+        }
+
+    }
+
+
+    /**
+     * Return an array containing the session identifiers of all Sessions
+     * currently saved in this Store.  If there are no such Sessions, a
+     * zero-length array is returned.
+     *
+     * @exception IOException if an input/output error occurred
+     */
+    public String[] keys() throws IOException {
+
+        // Acquire the list of files in our storage directory
+        File file = directory();
+        if (file == null) {
+            return (new String[0]);
+        }
+
+        String files[] = file.list();
+        
+        // Bugzilla 32130
+        if((files == null) || (files.length < 1)) {
+            return (new String[0]);
+        }
+
+        // Build and return the list of session identifiers
+        ArrayList list = new ArrayList();
+        int n = FILE_EXT.length();
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].endsWith(FILE_EXT)) {
+                list.add(files[i].substring(0, files[i].length() - n));
+            }
+        }
+        return ((String[]) list.toArray(new String[list.size()]));
+
+    }
+
+
+    /**
+     * Load and return the Session associated with the specified session
+     * identifier from this Store, without removing it.  If there is no
+     * such stored Session, return <code>null</code>.
+     *
+     * @param id Session identifier of the session to load
+     *
+     * @exception ClassNotFoundException if a deserialization error occurs
+     * @exception IOException if an input/output error occurs
+     */
+    public Session load(String id)
+        throws ClassNotFoundException, IOException {
+
+        // Open an input stream to the specified pathname, if any
+        File file = file(id);
+        if (file == null) {
+            return (null);
+        }
+
+        if (! file.exists()) {
+            return (null);
+        }
+        if (manager.getContainer().getLogger().isDebugEnabled()) {
+            manager.getContainer().getLogger().debug(sm.getString(getStoreName()+".loading",
+                             id, file.getAbsolutePath()));
+        }
+
+        FileInputStream fis = null;
+        ObjectInputStream ois = null;
+        Loader loader = null;
+        ClassLoader classLoader = null;
+        try {
+            fis = new FileInputStream(file.getAbsolutePath());
+            BufferedInputStream bis = new BufferedInputStream(fis);
+            Container container = manager.getContainer();
+            if (container != null)
+                loader = container.getLoader();
+            if (loader != null)
+                classLoader = loader.getClassLoader();
+            if (classLoader != null)
+                ois = new CustomObjectInputStream(bis, classLoader);
+            else
+                ois = new ObjectInputStream(bis);
+        } catch (FileNotFoundException e) {
+            if (manager.getContainer().getLogger().isDebugEnabled())
+                manager.getContainer().getLogger().debug("No persisted data file found");
+            return (null);
+        } catch (IOException e) {
+            if (ois != null) {
+                try {
+                    ois.close();
+                } catch (IOException f) {
+                    ;
+                }
+                ois = null;
+            }
+            throw e;
+        }
+
+        try {
+            StandardSession session =
+                (StandardSession) manager.createEmptySession();
+            session.readObjectData(ois);
+            session.setManager(manager);
+            return (session);
+        } finally {
+            // Close the input stream
+            if (ois != null) {
+                try {
+                    ois.close();
+                } catch (IOException f) {
+                    ;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Remove the Session with the specified session identifier from
+     * this Store, if present.  If no such Session is present, this method
+     * takes no action.
+     *
+     * @param id Session identifier of the Session to be removed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void remove(String id) throws IOException {
+
+        File file = file(id);
+        if (file == null) {
+            return;
+        }
+        if (manager.getContainer().getLogger().isDebugEnabled()) {
+            manager.getContainer().getLogger().debug(sm.getString(getStoreName()+".removing",
+                             id, file.getAbsolutePath()));
+        }
+        file.delete();
+
+    }
+
+
+    /**
+     * Save the specified Session into this Store.  Any previously saved
+     * information for the associated session identifier is replaced.
+     *
+     * @param session Session to be saved
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void save(Session session) throws IOException {
+
+        // Open an output stream to the specified pathname, if any
+        File file = file(session.getIdInternal());
+        if (file == null) {
+            return;
+        }
+        if (manager.getContainer().getLogger().isDebugEnabled()) {
+            manager.getContainer().getLogger().debug(sm.getString(getStoreName()+".saving",
+                             session.getIdInternal(), file.getAbsolutePath()));
+        }
+        FileOutputStream fos = null;
+        ObjectOutputStream oos = null;
+        try {
+            fos = new FileOutputStream(file.getAbsolutePath());
+            oos = new ObjectOutputStream(new BufferedOutputStream(fos));
+        } catch (IOException e) {
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (IOException f) {
+                    ;
+                }
+            }
+            throw e;
+        }
+
+        try {
+            ((StandardSession)session).writeObjectData(oos);
+        } finally {
+            oos.close();
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Return a File object representing the pathname to our
+     * session persistence directory, if any.  The directory will be
+     * created if it does not already exist.
+     */
+    private File directory() {
+
+        if (this.directory == null) {
+            return (null);
+        }
+        if (this.directoryFile != null) {
+            // NOTE:  Race condition is harmless, so do not synchronize
+            return (this.directoryFile);
+        }
+        File file = new File(this.directory);
+        if (!file.isAbsolute()) {
+            Container container = manager.getContainer();
+            if (container instanceof Context) {
+                ServletContext servletContext =
+                    ((Context) container).getServletContext();
+                File work = (File)
+                    servletContext.getAttribute(Globals.WORK_DIR_ATTR);
+                file = new File(work, this.directory);
+            } else {
+                throw new IllegalArgumentException
+                    ("Parent Container is not a Context");
+            }
+        }
+        if (!file.exists() || !file.isDirectory()) {
+            file.delete();
+            file.mkdirs();
+        }
+        this.directoryFile = file;
+        return (file);
+
+    }
+
+
+    /**
+     * Return a File object representing the pathname to our
+     * session persistence file, if any.
+     *
+     * @param id The ID of the Session to be retrieved. This is
+     *    used in the file naming.
+     */
+    private File file(String id) {
+
+        if (this.directory == null) {
+            return (null);
+        }
+        String filename = id + FILE_EXT;
+        File file = new File(directory(), filename);
+        return (file);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/session/JDBCStore.java b/container/catalina/src/share/org/apache/catalina/session/JDBCStore.java
new file mode 100644
index 0000000..d0b4439
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/JDBCStore.java
@@ -0,0 +1,988 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Session;
+import org.apache.catalina.Store;
+import org.apache.catalina.util.CustomObjectInputStream;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Properties;
+
+/**
+ * Implementation of the <code>Store</code> interface that stores
+ * serialized session objects in a database.  Sessions that are
+ * saved are still subject to being expired based on inactivity.
+ *
+ * @author Bip Thelin
+ * @version $Revision$, $Date$
+ */
+
+public class JDBCStore
+        extends StoreBase implements Store {
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static String info = "JDBCStore/1.0";
+
+    /**
+     * Context name associated with this Store
+     */
+    private String name = null;
+
+    /**
+     * Name to register for this Store, used for logging.
+     */
+    protected static String storeName = "JDBCStore";
+
+    /**
+     * Name to register for the background thread.
+     */
+    protected String threadName = "JDBCStore";
+
+    /**
+     * The connection username to use when trying to connect to the database.
+     */
+    protected String connectionName = null;
+
+
+    /**
+     * The connection URL to use when trying to connect to the database.
+     */
+    protected String connectionPassword = null;
+
+    /**
+     * Connection string to use when connecting to the DB.
+     */
+    protected String connectionURL = null;
+
+    /**
+     * The database connection.
+     */
+    private Connection dbConnection = null;
+
+    /**
+     * Instance of the JDBC Driver class we use as a connection factory.
+     */
+    protected Driver driver = null;
+
+    /**
+     * Driver to use.
+     */
+    protected String driverName = null;
+
+    // ------------------------------------------------------------- Table & cols
+
+    /**
+     * Table to use.
+     */
+    protected String sessionTable = "tomcat$sessions";
+
+    /**
+     * Column to use for /Engine/Host/Context name
+     */
+    protected String sessionAppCol = "app";
+
+    /**
+     * Id column to use.
+     */
+    protected String sessionIdCol = "id";
+
+    /**
+     * Data column to use.
+     */
+    protected String sessionDataCol = "data";
+
+    /**
+     * Is Valid column to use.
+     */
+    protected String sessionValidCol = "valid";
+
+    /**
+     * Max Inactive column to use.
+     */
+    protected String sessionMaxInactiveCol = "maxinactive";
+
+    /**
+     * Last Accessed column to use.
+     */
+    protected String sessionLastAccessedCol = "lastaccess";
+
+    // ------------------------------------------------------------- SQL Variables
+
+    /**
+     * Variable to hold the <code>getSize()</code> prepared statement.
+     */
+    protected PreparedStatement preparedSizeSql = null;
+
+    /**
+     * Variable to hold the <code>keys()</code> prepared statement.
+     */
+    protected PreparedStatement preparedKeysSql = null;
+
+    /**
+     * Variable to hold the <code>save()</code> prepared statement.
+     */
+    protected PreparedStatement preparedSaveSql = null;
+
+    /**
+     * Variable to hold the <code>clear()</code> prepared statement.
+     */
+    protected PreparedStatement preparedClearSql = null;
+
+    /**
+     * Variable to hold the <code>remove()</code> prepared statement.
+     */
+    protected PreparedStatement preparedRemoveSql = null;
+
+    /**
+     * Variable to hold the <code>load()</code> prepared statement.
+     */
+    protected PreparedStatement preparedLoadSql = null;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the info for this Store.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+    /**
+     * Return the name for this instance (built from container name)
+     */
+    public String getName() {
+        if (name == null) {
+            Container container = manager.getContainer();
+            String contextName = container.getName();
+            String hostName = "";
+            String engineName = "";
+
+            if (container.getParent() != null) {
+                Container host = container.getParent();
+                hostName = host.getName();
+                if (host.getParent() != null) {
+                    engineName = host.getParent().getName();
+                }
+            }
+            name = "/" + engineName + "/" + hostName + contextName;
+        }
+        return name;
+    }
+
+    /**
+     * Return the thread name for this Store.
+     */
+    public String getThreadName() {
+        return (threadName);
+    }
+
+    /**
+     * Return the name for this Store, used for logging.
+     */
+    public String getStoreName() {
+        return (storeName);
+    }
+
+    /**
+     * Set the driver for this Store.
+     *
+     * @param driverName The new driver
+     */
+    public void setDriverName(String driverName) {
+        String oldDriverName = this.driverName;
+        this.driverName = driverName;
+        support.firePropertyChange("driverName",
+                oldDriverName,
+                this.driverName);
+        this.driverName = driverName;
+    }
+
+    /**
+     * Return the driver for this Store.
+     */
+    public String getDriverName() {
+        return (this.driverName);
+    }
+
+    /**
+     * Return the username to use to connect to the database.
+     *
+     */
+    public String getConnectionName() {
+        return connectionName;
+    }
+
+    /**
+     * Set the username to use to connect to the database.
+     *
+     * @param connectionName Username
+     */
+    public void setConnectionName(String connectionName) {
+        this.connectionName = connectionName;
+    }
+
+    /**
+     * Return the password to use to connect to the database.
+     *
+     */
+    public String getConnectionPassword() {
+        return connectionPassword;
+    }
+
+    /**
+     * Set the password to use to connect to the database.
+     *
+     * @param connectionPassword User password
+     */
+    public void setConnectionPassword(String connectionPassword) {
+        this.connectionPassword = connectionPassword;
+    }
+
+    /**
+     * Set the Connection URL for this Store.
+     *
+     * @param connectionURL The new Connection URL
+     */
+    public void setConnectionURL(String connectionURL) {
+        String oldConnString = this.connectionURL;
+        this.connectionURL = connectionURL;
+        support.firePropertyChange("connectionURL",
+                oldConnString,
+                this.connectionURL);
+    }
+
+    /**
+     * Return the Connection URL for this Store.
+     */
+    public String getConnectionURL() {
+        return (this.connectionURL);
+    }
+
+    /**
+     * Set the table for this Store.
+     *
+     * @param sessionTable The new table
+     */
+    public void setSessionTable(String sessionTable) {
+        String oldSessionTable = this.sessionTable;
+        this.sessionTable = sessionTable;
+        support.firePropertyChange("sessionTable",
+                oldSessionTable,
+                this.sessionTable);
+    }
+
+    /**
+     * Return the table for this Store.
+     */
+    public String getSessionTable() {
+        return (this.sessionTable);
+    }
+
+    /**
+     * Set the App column for the table.
+     *
+     * @param sessionAppCol the column name
+     */
+    public void setSessionAppCol(String sessionAppCol) {
+        String oldSessionAppCol = this.sessionAppCol;
+        this.sessionAppCol = sessionAppCol;
+        support.firePropertyChange("sessionAppCol",
+                oldSessionAppCol,
+                this.sessionAppCol);
+    }
+
+    /**
+     * Return the web application name column for the table.
+     */
+    public String getSessionAppCol() {
+        return (this.sessionAppCol);
+    }
+
+    /**
+     * Set the Id column for the table.
+     *
+     * @param sessionIdCol the column name
+     */
+    public void setSessionIdCol(String sessionIdCol) {
+        String oldSessionIdCol = this.sessionIdCol;
+        this.sessionIdCol = sessionIdCol;
+        support.firePropertyChange("sessionIdCol",
+                oldSessionIdCol,
+                this.sessionIdCol);
+    }
+
+    /**
+     * Return the Id column for the table.
+     */
+    public String getSessionIdCol() {
+        return (this.sessionIdCol);
+    }
+
+    /**
+     * Set the Data column for the table
+     *
+     * @param sessionDataCol the column name
+     */
+    public void setSessionDataCol(String sessionDataCol) {
+        String oldSessionDataCol = this.sessionDataCol;
+        this.sessionDataCol = sessionDataCol;
+        support.firePropertyChange("sessionDataCol",
+                oldSessionDataCol,
+                this.sessionDataCol);
+    }
+
+    /**
+     * Return the data column for the table
+     */
+    public String getSessionDataCol() {
+        return (this.sessionDataCol);
+    }
+
+    /**
+     * Set the Is Valid column for the table
+     *
+     * @param sessionValidCol The column name
+     */
+    public void setSessionValidCol(String sessionValidCol) {
+        String oldSessionValidCol = this.sessionValidCol;
+        this.sessionValidCol = sessionValidCol;
+        support.firePropertyChange("sessionValidCol",
+                oldSessionValidCol,
+                this.sessionValidCol);
+    }
+
+    /**
+     * Return the Is Valid column
+     */
+    public String getSessionValidCol() {
+        return (this.sessionValidCol);
+    }
+
+    /**
+     * Set the Max Inactive column for the table
+     *
+     * @param sessionMaxInactiveCol The column name
+     */
+    public void setSessionMaxInactiveCol(String sessionMaxInactiveCol) {
+        String oldSessionMaxInactiveCol = this.sessionMaxInactiveCol;
+        this.sessionMaxInactiveCol = sessionMaxInactiveCol;
+        support.firePropertyChange("sessionMaxInactiveCol",
+                oldSessionMaxInactiveCol,
+                this.sessionMaxInactiveCol);
+    }
+
+    /**
+     * Return the Max Inactive column
+     */
+    public String getSessionMaxInactiveCol() {
+        return (this.sessionMaxInactiveCol);
+    }
+
+    /**
+     * Set the Last Accessed column for the table
+     *
+     * @param sessionLastAccessedCol The column name
+     */
+    public void setSessionLastAccessedCol(String sessionLastAccessedCol) {
+        String oldSessionLastAccessedCol = this.sessionLastAccessedCol;
+        this.sessionLastAccessedCol = sessionLastAccessedCol;
+        support.firePropertyChange("sessionLastAccessedCol",
+                oldSessionLastAccessedCol,
+                this.sessionLastAccessedCol);
+    }
+
+    /**
+     * Return the Last Accessed column
+     */
+    public String getSessionLastAccessedCol() {
+        return (this.sessionLastAccessedCol);
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Return an array containing the session identifiers of all Sessions
+     * currently saved in this Store.  If there are no such Sessions, a
+     * zero-length array is returned.
+     *
+     * @exception IOException if an input/output error occurred
+     */
+    public String[] keys() throws IOException {
+        ResultSet rst = null;
+        String keys[] = null;
+        synchronized (this) {
+            int numberOfTries = 2;
+            while (numberOfTries > 0) {
+
+                Connection _conn = getConnection();
+                if (_conn == null) {
+                    return (new String[0]);
+                }
+                try {
+                    if (preparedKeysSql == null) {
+                        String keysSql = "SELECT " + sessionIdCol + " FROM "
+                                + sessionTable + " WHERE " + sessionAppCol
+                                + " = ?";
+                        preparedKeysSql = _conn.prepareStatement(keysSql);
+					}
+
+                    preparedKeysSql.setString(1, getName());
+                    rst = preparedKeysSql.executeQuery();
+                    ArrayList tmpkeys = new ArrayList();
+                    if (rst != null) {
+                        while (rst.next()) {
+                            tmpkeys.add(rst.getString(1));
+                        }
+                    }
+                    keys = (String[]) tmpkeys.toArray(new String[tmpkeys.size()]);
+                    // Break out after the finally block
+                    numberOfTries = 0;
+                } catch (SQLException e) {
+                    manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".SQLException", e));
+                    keys = new String[0];
+                    // Close the connection so that it gets reopened next time
+                    if (dbConnection != null)
+                        close(dbConnection);
+                } finally {
+                    try {
+                        if (rst != null) {
+                            rst.close();
+                        }
+                    } catch (SQLException e) {
+                        ;
+                    }
+
+                    release(_conn);
+                }
+                numberOfTries--;
+            }
+        }
+
+        return (keys);
+    }
+
+    /**
+     * Return an integer containing a count of all Sessions
+     * currently saved in this Store.  If there are no Sessions,
+     * <code>0</code> is returned.
+     *
+     * @exception IOException if an input/output error occurred
+     */
+    public int getSize() throws IOException {
+        int size = 0;
+        ResultSet rst = null;
+
+        synchronized (this) {
+            int numberOfTries = 2;
+            while (numberOfTries > 0) {
+                Connection _conn = getConnection();
+
+                if (_conn == null) {
+                    return (size);
+                }
+
+                try {
+                    if (preparedSizeSql == null) {
+                        String sizeSql = "SELECT COUNT(" + sessionIdCol
+                                + ") FROM " + sessionTable + " WHERE "
+                                + sessionAppCol + " = ?";
+                        preparedSizeSql = _conn.prepareStatement(sizeSql);
+					}
+
+                    preparedSizeSql.setString(1, getName());
+                    rst = preparedSizeSql.executeQuery();
+                    if (rst.next()) {
+                        size = rst.getInt(1);
+                    }
+                    // Break out after the finally block
+                    numberOfTries = 0;
+                } catch (SQLException e) {
+                    manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".SQLException", e));
+                    if (dbConnection != null)
+                        close(dbConnection);
+                } finally {
+                    try {
+                        if (rst != null)
+                            rst.close();
+                    } catch (SQLException e) {
+                        ;
+                    }
+
+                    release(_conn);
+                }
+                numberOfTries--;
+            }
+        }
+        return (size);
+    }
+
+    /**
+     * Load the Session associated with the id <code>id</code>.
+     * If no such session is found <code>null</code> is returned.
+     *
+     * @param id a value of type <code>String</code>
+     * @return the stored <code>Session</code>
+     * @exception ClassNotFoundException if an error occurs
+     * @exception IOException if an input/output error occurred
+     */
+    public Session load(String id)
+            throws ClassNotFoundException, IOException {
+        ResultSet rst = null;
+        StandardSession _session = null;
+        Loader loader = null;
+        ClassLoader classLoader = null;
+        ObjectInputStream ois = null;
+        BufferedInputStream bis = null;
+        Container container = manager.getContainer();
+ 
+        synchronized (this) {
+            int numberOfTries = 2;
+            while (numberOfTries > 0) {
+                Connection _conn = getConnection();
+                if (_conn == null) {
+                    return (null);
+                }
+
+                try {
+                    if (preparedLoadSql == null) {
+                        String loadSql = "SELECT " + sessionIdCol + ", "
+                                + sessionDataCol + " FROM " + sessionTable
+                                + " WHERE " + sessionIdCol + " = ? AND "
+                                + sessionAppCol + " = ?";
+                        preparedLoadSql = _conn.prepareStatement(loadSql);
+                    }
+
+                    preparedLoadSql.setString(1, id);
+                    preparedLoadSql.setString(2, getName());
+                    rst = preparedLoadSql.executeQuery();
+                    if (rst.next()) {
+                        bis = new BufferedInputStream(rst.getBinaryStream(2));
+
+                        if (container != null) {
+                            loader = container.getLoader();
+                        }
+                        if (loader != null) {
+                            classLoader = loader.getClassLoader();
+                        }
+                        if (classLoader != null) {
+                            ois = new CustomObjectInputStream(bis,
+                                    classLoader);
+                        } else {
+                            ois = new ObjectInputStream(bis);
+                        }
+
+                        if (manager.getContainer().getLogger().isDebugEnabled()) {
+                            manager.getContainer().getLogger().debug(sm.getString(getStoreName() + ".loading",
+                                    id, sessionTable));
+                        }
+
+                        _session = (StandardSession) manager.createEmptySession();
+                        _session.readObjectData(ois);
+                        _session.setManager(manager);
+                      } else if (manager.getContainer().getLogger().isDebugEnabled()) {
+                        manager.getContainer().getLogger().debug(getStoreName() + ": No persisted data object found");
+                    }
+                    // Break out after the finally block
+                    numberOfTries = 0;
+                } catch (SQLException e) {
+                    manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".SQLException", e));
+                    if (dbConnection != null)
+                        close(dbConnection);
+                } finally {
+                    try {
+                        if (rst != null) {
+                            rst.close();
+                        }
+                    } catch (SQLException e) {
+                        ;
+                    }
+                    if (ois != null) {
+                        try {
+                            ois.close();
+                        } catch (IOException e) {
+                            ;
+                        }
+                    }
+                    release(_conn);
+                }
+                numberOfTries--;
+            }
+        }
+
+        return (_session);
+    }
+
+    /**
+     * Remove the Session with the specified session identifier from
+     * this Store, if present.  If no such Session is present, this method
+     * takes no action.
+     *
+     * @param id Session identifier of the Session to be removed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void remove(String id) throws IOException {
+
+        synchronized (this) {
+            int numberOfTries = 2;
+            while (numberOfTries > 0) {
+                Connection _conn = getConnection();
+
+                if (_conn == null) {
+                    return;
+                }
+
+                try {
+                    if (preparedRemoveSql == null) {
+                        String removeSql = "DELETE FROM " + sessionTable
+                                + " WHERE " + sessionIdCol + " = ?  AND "
+                                + sessionAppCol + " = ?";
+                        preparedRemoveSql = _conn.prepareStatement(removeSql);
+                    }
+
+                    preparedRemoveSql.setString(1, id);
+                    preparedRemoveSql.setString(2, getName());
+                    preparedRemoveSql.execute();
+                    // Break out after the finally block
+                    numberOfTries = 0;
+                } catch (SQLException e) {
+                    manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".SQLException", e));
+                    if (dbConnection != null)
+                        close(dbConnection);
+                } finally {
+                    release(_conn);
+                }
+                numberOfTries--;
+            }
+        }
+
+        if (manager.getContainer().getLogger().isDebugEnabled()) {
+            manager.getContainer().getLogger().debug(sm.getString(getStoreName() + ".removing", id, sessionTable));
+        }
+    }
+
+    /**
+     * Remove all of the Sessions in this Store.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void clear() throws IOException {
+
+        synchronized (this) {
+            int numberOfTries = 2;
+            while (numberOfTries > 0) {
+                Connection _conn = getConnection();
+                if (_conn == null) {
+                    return;
+                }
+
+                try {
+                    if (preparedClearSql == null) {
+                        String clearSql = "DELETE FROM " + sessionTable
+                             + " WHERE " + sessionAppCol + " = ?";
+                        preparedClearSql = _conn.prepareStatement(clearSql);
+                    }
+
+                    preparedClearSql.setString(1, getName());
+                    preparedClearSql.execute();
+                    // Break out after the finally block
+                    numberOfTries = 0;
+                } catch (SQLException e) {
+                    manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".SQLException", e));
+                    if (dbConnection != null)
+                        close(dbConnection);
+                } finally {
+                    release(_conn);
+                }
+                numberOfTries--;
+            }
+        }
+    }
+
+    /**
+     * Save a session to the Store.
+     *
+     * @param session the session to be stored
+     * @exception IOException if an input/output error occurs
+     */
+    public void save(Session session) throws IOException {
+        ObjectOutputStream oos = null;
+        ByteArrayOutputStream bos = null;
+        ByteArrayInputStream bis = null;
+        InputStream in = null;
+
+        synchronized (this) {
+            int numberOfTries = 2;
+            while (numberOfTries > 0) {
+                Connection _conn = getConnection();
+                if (_conn == null) {
+                    return;
+                }
+
+                // If sessions already exist in DB, remove and insert again.
+                // TODO:
+                // * Check if ID exists in database and if so use UPDATE.
+                remove(session.getIdInternal());
+
+                try {
+                    bos = new ByteArrayOutputStream();
+                    oos = new ObjectOutputStream(new BufferedOutputStream(bos));
+
+                    ((StandardSession) session).writeObjectData(oos);
+                    oos.close();
+                    oos = null;
+                    byte[] obs = bos.toByteArray();
+                    int size = obs.length;
+                    bis = new ByteArrayInputStream(obs, 0, size);
+                    in = new BufferedInputStream(bis, size);
+
+                    if (preparedSaveSql == null) {
+                        String saveSql = "INSERT INTO " + sessionTable + " ("
+                           + sessionIdCol + ", " + sessionAppCol + ", "
+                           + sessionDataCol + ", " + sessionValidCol
+                           + ", " + sessionMaxInactiveCol + ", "
+                           + sessionLastAccessedCol
+                           + ") VALUES (?, ?, ?, ?, ?, ?)";
+                       preparedSaveSql = _conn.prepareStatement(saveSql);
+					}
+
+                    preparedSaveSql.setString(1, session.getIdInternal());
+                    preparedSaveSql.setString(2, getName());
+                    preparedSaveSql.setBinaryStream(3, in, size);
+                    preparedSaveSql.setString(4, session.isValid() ? "1" : "0");
+                    preparedSaveSql.setInt(5, session.getMaxInactiveInterval());
+                    preparedSaveSql.setLong(6, session.getLastAccessedTime());
+                    preparedSaveSql.execute();
+                    // Break out after the finally block
+                    numberOfTries = 0;
+                } catch (SQLException e) {
+                    manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".SQLException", e));
+                    if (dbConnection != null)
+                        close(dbConnection);
+                } catch (IOException e) {
+                    ;
+                } finally {
+                    if (oos != null) {
+                        oos.close();
+                    }
+                    if (bis != null) {
+                        bis.close();
+                    }
+                    if (in != null) {
+                        in.close();
+                    }
+
+                    release(_conn);
+                }
+                numberOfTries--;
+            }
+        }
+
+        if (manager.getContainer().getLogger().isDebugEnabled()) {
+            manager.getContainer().getLogger().debug(sm.getString(getStoreName() + ".saving",
+                    session.getIdInternal(), sessionTable));
+        }
+    }
+
+    // --------------------------------------------------------- Protected Methods
+
+    /**
+     * Check the connection associated with this store, if it's
+     * <code>null</code> or closed try to reopen it.
+     * Returns <code>null</code> if the connection could not be established.
+     *
+     * @return <code>Connection</code> if the connection suceeded
+     */
+    protected Connection getConnection() {
+        try {
+            if (dbConnection == null || dbConnection.isClosed()) {
+                manager.getContainer().getLogger().info(sm.getString(getStoreName() + ".checkConnectionDBClosed"));
+                open();
+                if (dbConnection == null || dbConnection.isClosed()) {
+                    manager.getContainer().getLogger().info(sm.getString(getStoreName() + ".checkConnectionDBReOpenFail"));
+                }
+            }
+        } catch (SQLException ex) {
+            manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".checkConnectionSQLException",
+                    ex.toString()));
+        }
+
+        return dbConnection;
+    }
+
+    /**
+     * Open (if necessary) and return a database connection for use by
+     * this Realm.
+     *
+     * @exception SQLException if a database error occurs
+     */
+    protected Connection open() throws SQLException {
+
+        // Do nothing if there is a database connection already open
+        if (dbConnection != null)
+            return (dbConnection);
+
+        // Instantiate our database driver if necessary
+        if (driver == null) {
+            try {
+                Class clazz = Class.forName(driverName);
+                driver = (Driver) clazz.newInstance();
+            } catch (ClassNotFoundException ex) {
+                manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".checkConnectionClassNotFoundException",
+                        ex.toString()));
+            } catch (InstantiationException ex) {
+                manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".checkConnectionClassNotFoundException",
+                        ex.toString()));
+            } catch (IllegalAccessException ex) {
+                manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".checkConnectionClassNotFoundException",
+                        ex.toString()));
+            }
+        }
+
+        // Open a new connection
+        Properties props = new Properties();
+        if (connectionName != null)
+            props.put("user", connectionName);
+        if (connectionPassword != null)
+            props.put("password", connectionPassword);
+        dbConnection = driver.connect(connectionURL, props);
+        dbConnection.setAutoCommit(true);
+        return (dbConnection);
+
+    }
+
+    /**
+     * Close the specified database connection.
+     *
+     * @param dbConnection The connection to be closed
+     */
+    protected void close(Connection dbConnection) {
+
+        // Do nothing if the database connection is already closed
+        if (dbConnection == null)
+            return;
+
+        // Close our prepared statements (if any)
+        try {
+            preparedSizeSql.close();
+        } catch (Throwable f) {
+            ;
+        }
+        this.preparedSizeSql = null;
+
+        try {
+            preparedKeysSql.close();
+        } catch (Throwable f) {
+            ;
+        }
+        this.preparedKeysSql = null;
+
+        try {
+            preparedSaveSql.close();
+        } catch (Throwable f) {
+            ;
+        }
+        this.preparedSaveSql = null;
+
+        try {
+            preparedClearSql.close();
+        } catch (Throwable f) {
+            ;
+        }
+         
+		try {
+            preparedRemoveSql.close();
+        } catch (Throwable f) {
+            ;
+        }
+        this.preparedRemoveSql = null;
+
+        try {
+            preparedLoadSql.close();
+        } catch (Throwable f) {
+            ;
+        }
+        this.preparedLoadSql = null;
+
+        // Close this database connection, and log any errors
+        try {
+            dbConnection.close();
+        } catch (SQLException e) {
+            manager.getContainer().getLogger().error(sm.getString(getStoreName() + ".close", e.toString())); // Just log it here
+        } finally {
+            this.dbConnection = null;
+        }
+
+    }
+
+    /**
+     * Release the connection, not needed here since the
+     * connection is not associated with a connection pool.
+     *
+     * @param conn The connection to be released
+     */
+    protected void release(Connection conn) {
+        ;
+    }
+
+    /**
+     * Called once when this Store is first started.
+     */
+    public void start() throws LifecycleException {
+        super.start();
+
+        // Open connection to the database
+        this.dbConnection = getConnection();
+    }
+
+    /**
+     * Gracefully terminate everything associated with our db.
+     * Called once when this Store is stoping.
+     *
+     */
+    public void stop() throws LifecycleException {
+        super.stop();
+
+        // Close and release everything associated with our db.
+        if (dbConnection != null) {
+            try {
+                dbConnection.commit();
+            } catch (SQLException e) {
+                ;
+            }
+            close(dbConnection);
+        }
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/session/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/session/LocalStrings.properties
new file mode 100644
index 0000000..705a62a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/LocalStrings.properties
@@ -0,0 +1,67 @@
+applicationSession.session.ise=invalid session state
+applicationSession.value.iae=null value
+fileStore.alreadyStarted=File Store has already been started
+fileStore.notStarted=File Store has not yet been started
+fileStore.saving=Saving Session {0} to file {1}
+fileStore.loading=Loading Session {0} from file {1}
+fileStore.removing=Removing Session {0} at file {1}
+JDBCStore.alreadyStarted=JDBC Store has already been started
+JDBCStore.close=Exception closing database connection {0}
+JDBCStore.notStarted=JDBC Store has not yet been started
+JDBCStore.saving=Saving Session {0} to database {1}
+JDBCStore.loading=Loading Session {0} from database {1}
+JDBCStore.removing=Removing Session {0} at database {1}
+JDBCStore.SQLException=SQL Error {0}
+JDBCStore.checkConnectionDBClosed=The database connection is null or was found to be closed. Trying to re-open it.
+JDBCStore.checkConnectionDBReOpenFail=The re-open on the database failed. The database could be down.
+JDBCStore.checkConnectionSQLException=A SQL exception occurred {0}
+JDBCStore.checkConnectionClassNotFoundException=JDBC driver class not found {0}
+managerBase.complete=Seeding of random number generator has been completed
+managerBase.getting=Getting message digest component for algorithm {0}
+managerBase.gotten=Completed getting message digest component
+managerBase.random=Exception initializing random number generator of class {0}
+managerBase.seeding=Seeding random number generator class {0}
+serverSession.value.iae=null value
+standardManager.alreadyStarted=Manager has already been started
+standardManager.createSession.ise=createSession: Too many active sessions
+standardManager.expireException=processsExpire:  Exception during session expiration
+standardManager.loading=Loading persisted sessions from {0}
+standardManager.loading.cnfe=ClassNotFoundException while loading persisted sessions: {0}
+standardManager.loading.ioe=IOException while loading persisted sessions: {0}
+standardManager.notStarted=Manager has not yet been started
+standardManager.sessionTimeout=Invalid session timeout setting {0}
+standardManager.unloading=Saving persisted sessions to {0}
+standardManager.unloading.ioe=IOException while saving persisted sessions: {0}
+standardManager.managerLoad=Exception loading sessions from persistent storage
+standardManager.managerUnload=Exception unloading sessions to persistent storage
+standardSession.attributeEvent=Session attribute event listener threw exception
+standardSession.bindingEvent=Session binding event listener threw exception
+standardSession.invalidate.ise=invalidate: Session already invalidated
+standardSession.isNew.ise=isNew: Session already invalidated
+standardSession.getAttribute.ise=getAttribute: Session already invalidated
+standardSession.getAttributeNames.ise=getAttributeNames: Session already invalidated
+standardSession.getCreationTime.ise=getCreationTime: Session already invalidated
+standardSession.getLastAccessedTime.ise=getLastAccessedTime: Session already invalidated
+standardSession.getId.ise=getId: Session already invalidated
+standardSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: Session already invalidated
+standardSession.getValueNames.ise=getValueNames: Session already invalidated
+standardSession.notSerializable=Cannot serialize session attribute {0} for session {1}
+standardSession.removeAttribute.ise=removeAttribute: Session already invalidated
+standardSession.sessionEvent=Session event listener threw exception
+standardSession.setAttribute.iae=setAttribute: Non-serializable attribute
+standardSession.setAttribute.ise=setAttribute: Session already invalidated
+standardSession.setAttribute.namenull=setAttribute: name parameter cannot be null
+standardSession.sessionCreated=Created Session id = {0}
+persistentManager.loading=Loading {0} persisted sessions
+persistentManager.unloading=Saving {0} persisted sessions
+persistentManager.expiring=Expiring {0} sessions before saving them
+persistentManager.deserializeError=Error deserializing Session {0}: {1}
+persistentManager.serializeError=Error serializing Session {0}: {1}
+persistentManager.swapMaxIdle=Swapping session {0} to Store, idle for {1} seconds
+persistentManager.backupMaxIdle=Backing up session {0} to Store, idle for {1} seconds
+persistentManager.backupException=Exception occurred when backing up Session {0}: {1}
+persistentManager.tooManyActive=Too many active sessions, {0}, looking for idle sessions to swap out
+persistentManager.swapTooManyActive=Swapping out session {0}, idle for {1} seconds too many sessions active
+persistentManager.processSwaps=Checking for sessions to swap out, {0} active sessions in memory
+persistentManager.activeSession=Session {0} has been idle for {1} seconds
+persistentManager.swapIn=Swapping session {0} in from Store
diff --git a/container/catalina/src/share/org/apache/catalina/session/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/session/LocalStrings_es.properties
new file mode 100644
index 0000000..e902798
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/LocalStrings_es.properties
@@ -0,0 +1,66 @@
+applicationSession.session.ise=estado inválido de sesión
+applicationSession.value.iae=valor nulo
+fileStore.alreadyStarted=Ya ha sido arrancado el Almacén de Archivos
+fileStore.notStarted=Aún no se ha arrancado el Almacén de Archivos
+fileStore.saving=Salvando Sesión {0} en archivo {1}
+fileStore.loading=Cargando Sesión {0} desde archivo {1}
+fileStore.removing=Quitando Sesión {0} en archivo {1}
+JDBCStore.alreadyStarted=Ya ha sido arrancado el Almacén JDBC
+JDBCStore.close=Excepción cerrando conexión a base de datos {0}
+JDBCStore.notStarted=Aún no se ha arrancado el Almacén JDBC
+JDBCStore.saving=Salvando Sesión {0} en base de datos {1}
+JDBCStore.loading=Cargando Sesión {0} desde base de datos {1}
+JDBCStore.removing=Quitando Sesión {0} en base de datos {1}
+JDBCStore.SQLException=Error SQL {0}
+JDBCStore.checkConnectionDBClosed=La conexióna a base de datos es nula o está cerrada. Intentando reabrirla.
+JDBCStore.checkConnectionDBReOpenFail=Falló la reapertura de la base de datos. Puede que la base de datos esté caída.
+JDBCStore.checkConnectionSQLException=Ha tenido lugar una excepción SQL {0}
+JDBCStore.checkConnectionClassNotFoundException=No se ha hallado la clase del manejador (driver) JDBC {0}
+managerBase.complete=Se ha completado la siembra del generador de números aleatorios
+managerBase.getting=Obteniendo mensaje de componente de resumen (digest) para algoritmo {0}
+managerBase.gotten=Completada la obtención de mensaje de componente de resumen (digest)
+managerBase.random=Excepción inicializando generador de números aleatorios de clase {0}
+managerBase.seeding=Sembrando clase de generador de números aleatorios {0}
+serverSession.value.iae=valor nulo
+standardManager.alreadyStarted=Ya ha sido arrancado el Gestor
+standardManager.createSession.ise=createSession: Demasiadas sesiones activas
+standardManager.expireException=processsExpire: Excepción durante la expiración de sesión
+standardManager.loading=Cargando sesiones persistidas desde {0}
+standardManager.loading.cnfe=ClassNotFoundException al cargar sesiones persistidas: {0}
+standardManager.loading.ioe=IOException al cargar sesiones persistidas: {0}
+standardManager.notStarted=Aún no se ha arrancado el Gestor
+standardManager.sessionTimeout=Valor inválido de Tiempo Agotado de sesión {0}
+standardManager.unloading=Salvando sesiones persistidas a {0}
+standardManager.unloading.ioe=IOException al salvar sesiones persistidas: {0}
+standardManager.managerLoad=Excepción cargando sesiones desde almacenamiento persistente
+standardManager.managerUnload=Excepción descargando sesiones a almacenamiento persistente
+standardSession.attributeEvent=El escuchador de eventos de atributo de Sesión lanzó una excepción
+standardSession.invalidate.ise=invalidate: La Sesión ya ha sido invalidada
+standardSession.isNew.ise=isNew: La Sesión ya ha sido invalidada
+standardSession.getAttribute.ise=getAttribute: La Sesión ya ha sido invalidada
+standardSession.getAttributeNames.ise=getAttributeNames: La Sesión ya ha sido invalidada
+standardSession.getCreationTime.ise=getCreationTime: La Sesión ya ha sido invalidada
+standardSession.getLastAccessedTime.ise=getLastAccessedTime: La Sesión ya ha sido invalidada
+standardSession.getId.ise=getId: La Sesión ya ha sido invalidada
+standardSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: La Sesión ya ha sido invalidada
+standardSession.getValueNames.ise=getValueNames: La Sesión ya ha sido invalidada
+standardSession.notSerializable=No puedo serializar atributo de sesión {0} para sesión {1}
+standardSession.removeAttribute.ise=removeAttribute: La Sesión ya ha sido invalidada
+standardSession.sessionEvent=El escuchador de evento de Sesión lanzó una execpción
+standardSession.setAttribute.iae=setAttribute: Atributo no serializable
+standardSession.setAttribute.ise=setAttribute: La Sesión ya ha sido invalidada
+standardSession.setAttribute.namenull=setAttribute: el nuevo parámetro no puede ser nulo
+standardSession.sessionCreated=Creada Sesión id = {0}
+persistentManager.loading=Cargando {0} sesiones persistidas
+persistentManager.unloading=Salvando {0} sesiones persistidas
+persistentManager.expiring=Expirando {0} sesiones antes de salvarlas
+persistentManager.deserializeError=Error des-serializando Sesión {0}: {1}
+persistentManager.serializeError=Error serializando Sesión {0}: {1}
+persistentManager.swapMaxIdle=Intercambiando sesión {0} a fuera a Almacén, ociosa durante {1} segundos
+persistentManager.backupMaxIdle=Respaldando sesión {0} a Almacén, ociosa durante {1} segundos
+persistentManager.backupException=Ha tenido lugar una excepción al respaldar la Sesión {0}: {1}
+persistentManager.tooManyActive=Demasiadas sesiones activas, {0}, buscando sesiones ociosas para intercambiar
+persistentManager.swapTooManyActive=Intercambiando sesión {0} a fuera, ociosa durante {1} segundos: Demasiadas sesiones activas
+persistentManager.processSwaps=Mirando qué sesiones intercambiar a fuera, {0} sesiones activas en memoria
+persistentManager.activeSession=La sesión {0} ha estado ociosa durante {1} segundos
+persistentManager.swapIn=Intercambiando sesión {0} a dentro desde Almacén
diff --git a/container/catalina/src/share/org/apache/catalina/session/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/session/LocalStrings_fr.properties
new file mode 100644
index 0000000..3c9b690
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/LocalStrings_fr.properties
@@ -0,0 +1,65 @@
+applicationSession.session.ise=état de session invalide
+applicationSession.value.iae=valeur nulle
+fileStore.alreadyStarted=Le "File Store" a déjà été démarré
+fileStore.notStarted=Le "File Store" n''a pas encore été démarré
+fileStore.saving=Sauvegarde de la Session {0} vers le fichier {1}
+fileStore.loading=Chargement de la Session {0} depuis le fichier {1}
+fileStore.removing=Retrait de la Session {0} du fichier {1}
+JDBCStore.alreadyStarted=Le "JDBC Store" a déjà été démarré
+JDBCStore.notStarted=Le "JDBC Store" n''a pas encore été démarré
+JDBCStore.saving=Sauvegarde de la Session {0} vers la base de données {1}
+JDBCStore.loading=Chargement de la Session {0} depuis la base de données {1}
+JDBCStore.removing=Retrait de la Session {0} de la base de données {1}
+JDBCStore.SQLException=Erreur SQL {0}
+JDBCStore.checkConnectionDBClosed=La connexion à la base de données est nulle ou a été trouvé fermé. Tentative de réouverture.
+JDBCStore.checkConnectionDBReOpenFail=La tentative de réouverture re-open de la base de données a échoué. La base de données est peut être arrétée.
+JDBCStore.checkConnectionSQLException=Une exception SQL s''est produite {0}
+JDBCStore.checkConnectionClassNotFoundException=La classe du driver JDBC n''a pas été trouvée {0}
+managerBase.complete=L''alimentation du générateur de nombre aléatoire est terminé
+managerBase.getting=Prise du composant d''algorithme empreinte de message (message digest) pour l''algorithme {0}
+managerBase.gotten=Prise du composant d''algorithme empreinte de message (message digest) terminée
+managerBase.random=Exception durant l''initialisation de la classe du générateur de nombre aléatoire {0}
+managerBase.seeding=Alimentation de la classe du générateur de nombre aléatoire {0}
+serverSession.value.iae=valeur nulle
+standardManager.alreadyStarted=Le "Manager" a été démarré
+standardManager.createSession.ise="createSession": Trop de sessions actives
+standardManager.expireException="processsExpire":  Exception lors de l''expiration de la session
+standardManager.loading=Chargement des sessions qui ont persistés depuis {0}
+standardManager.loading.cnfe="ClassNotFoundException" lors du chargement de sessions persistantes: {0}
+standardManager.loading.ioe="IOException" lors du chargement des sessions persistantes: {0}
+standardManager.notStarted=Le "Manager" n''a pas encore été démarré
+standardManager.sessionTimeout=Réglage du délai d''inactivité (timeout) de session invalide {0}
+standardManager.unloading=Sauvegarde des sessions ayant persistées vers {0}
+standardManager.unloading.ioe="IOException" lors de la sauvegarde de sessions persistantes: {0}
+standardManager.managerLoad=Exception au chargement des sessions depuis le stockage persistant (persistent storage)
+standardManager.managerUnload=Exception au déchargement des sessions vers le stockage persistant (persistent storage)
+standardSession.attributeEvent=L''écouteur d''évènement Attribut de Session (attribute event listener) a généré une exception
+standardSession.invalidate.ise="invalidate": Session déjà invalidée
+standardSession.isNew.ise="isNew": Session déjà invalidée
+standardSession.getAttribute.ise="getAttribute": Session déjà invalidée
+standardSession.getAttributeNames.ise="getAttributeNames": Session déjà invalidée
+standardSession.getCreationTime.ise="getCreationTime": Session déjà invalidée
+standardSession.getLastAccessedTime.ise="getLastAccessedTime": Session d\u00E9j\u00E0 invalid\u00E9e
+standardSession.getId.ise=getId: Session déjà invalidée
+standardSession.getMaxInactiveInterval.ise="getMaxInactiveInterval": Session déjà invalidée
+standardSession.getValueNames.ise="getValueNames": Session déjà invalidée
+standardSession.notSerializable=Impossible de sérialiser l''attribut de session {0} pour la session {1}
+standardSession.removeAttribute.ise="removeAttribute": Session déjà invalidée
+standardSession.sessionEvent=L''écouteur d''évènement de session (session event listener) a généré une exception
+standardSession.setAttribute.iae="setAttribute": attribut non sérialisable
+standardSession.setAttribute.ise="setAttribute": Session déjà invalidée
+standardSession.setAttribute.namenull="setAttribute": le nom de paramètre ne peut être nul
+standardSession.sessionCreated=Création de l''Id de Session = {0}
+persistentManager.loading=Chargement de {0} sessions persistantes
+persistentManager.unloading=Sauvegarde de {0} sessions persistantes
+persistentManager.expiring=Expiration de {0} sessions avant leur sauvegarde
+persistentManager.deserializeError=Erreur lors de la désérialisation de la session {0}: {1}
+persistentManager.serializeError=Erreur lors de la sérialisation de la session {0}: {1}
+persistentManager.swapMaxIdle=Basculement de la session {0} vers le stockage (Store), en attente pour {1} secondes
+persistentManager.backupMaxIdle=Sauvegarde de la session {0} vers le stockage (Store), en attente pour {1} secondes
+persistentManager.backupException=Exception lors de la sauvegarde de la session {0}: {1}
+persistentManager.tooManyActive=Trop de sessions actives, {0}, à la recherche de sessions en attente pour basculement vers stockage (swap out)
+persistentManager.swapTooManyActive=Basculement vers stockage (swap out) de la session {0}, en attente pour {1} secondes trop de sessions actives
+persistentManager.processSwaps=Recherche de sessions à basculer vers stockage (swap out), {0} sessions actives en mémoire
+persistentManager.activeSession=La session {0} a été en attente durant {1} secondes
+persistentManager.swapIn=Basculement depuis le stockage (swap in) de la session {0}
diff --git a/container/catalina/src/share/org/apache/catalina/session/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/session/LocalStrings_ja.properties
new file mode 100644
index 0000000..b5f7621
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/LocalStrings_ja.properties
@@ -0,0 +1,67 @@
+applicationSession.session.ise=\u7121\u52b9\u306a\u30bb\u30c3\u30b7\u30e7\u30f3\u72b6\u614b\u3067\u3059
+applicationSession.value.iae=null\u5024\u3067\u3059
+fileStore.alreadyStarted=\u30d5\u30a1\u30a4\u30eb\u30b9\u30c8\u30a2\u304c\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+fileStore.notStarted=\u30d5\u30a1\u30a4\u30eb\u30b9\u30c8\u30a2\u304c\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+fileStore.saving=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30d5\u30a1\u30a4\u30eb {1} \u306b\u4fdd\u5b58\u3057\u307e\u3059
+fileStore.loading=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30d5\u30a1\u30a4\u30eb {1} \u304b\u3089\u30ed\u30fc\u30c9\u3057\u307e\u3059
+fileStore.removing=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30d5\u30a1\u30a4\u30eb {1} \u304b\u3089\u524a\u9664\u3057\u307e\u3059
+JDBCStore.alreadyStarted=JDBC\u30b9\u30c8\u30a2\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+JDBCStore.close=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a {0} \u3092\u30af\u30ed\u30fc\u30ba\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+JDBCStore.notStarted=JDBC\u30b9\u30c8\u30a2\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u307e\u305b\u3093
+JDBCStore.saving=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9 {1} \u306b\u4fdd\u5b58\u3057\u307e\u3059
+JDBCStore.loading=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9 {1} \u304b\u3089\u30ed\u30fc\u30c9\u3057\u307e\u3059
+JDBCStore.removing= \u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9 {1} \u304b\u3089\u524a\u9664\u3057\u307e\u3059
+JDBCStore.SQLException=SQL\u30a8\u30e9\u30fc {0}
+JDBCStore.checkConnectionDBClosed=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u304cnull\u3067\u3042\u308b\u304b\u3001\u30af\u30ed\u30fc\u30ba\u3055\u308c\u3066\u3044\u308b\u306e\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002\u518d\u30aa\u30fc\u30d7\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+JDBCStore.checkConnectionDBReOpenFail=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u518d\u30aa\u30fc\u30d7\u30f3\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
+JDBCStore.checkConnectionSQLException=SQL\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f {0}
+JDBCStore.checkConnectionClassNotFoundException=JDBC\u30c9\u30e9\u30a4\u30d0\u30af\u30e9\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 {0}
+managerBase.complete=\u4e71\u6570\u767a\u751f\u5668\u306e\u30b7\u30fc\u30c9\u306e\u751f\u6210\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f
+managerBase.getting=\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0 {0} \u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u3092\u53d6\u5f97\u3057\u307e\u3059
+managerBase.gotten=\u30e1\u30c3\u30bb\u30fc\u30b8\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u306e\u53d6\u5f97\u3092\u5b8c\u4e86\u3057\u307e\u3057\u305f
+managerBase.random=\u30af\u30e9\u30b9 {0} \u306e\u4e71\u6570\u767a\u751f\u5668\u306e\u521d\u671f\u5316\u306e\u4f8b\u5916\u3067\u3059
+managerBase.seeding=\u4e71\u6570\u767a\u751f\u5668\u30af\u30e9\u30b9 {0} \u306e\u30b7\u30fc\u30c9\u3092\u751f\u6210\u3057\u3066\u3044\u307e\u3059
+serverSession.value.iae=null\u5024\u3067\u3059
+standardManager.alreadyStarted=\u30de\u30cd\u30fc\u30b8\u30e3\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardManager.createSession.ise=createSession: \u30a2\u30af\u30c6\u30a3\u30d6\u30bb\u30c3\u30b7\u30e7\u30f3\u304c\u591a\u3059\u304e\u307e\u3059
+standardManager.expireException=processsExpire: \u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u7d42\u4e86\u51e6\u7406\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardManager.loading={0} \u304b\u3089\u6301\u7d9a\u3055\u308c\u305f\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u30ed\u30fc\u30c9\u3057\u3066\u3044\u307e\u3059
+standardManager.loading.cnfe=\u6301\u7d9a\u3055\u308c\u305f\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u30ed\u30fc\u30c9\u4e2d\u306bClassNotFoundException\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
+standardManager.loading.ioe=\u6301\u7d9a\u3055\u308c\u305f\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u30ed\u30fc\u30c9\u4e2d\u306eIOException\u3067\u3059: {0}
+standardManager.notStarted=\u30de\u30cd\u30fc\u30b8\u30e3\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardManager.sessionTimeout=\u7121\u52b9\u306a\u30bb\u30c3\u30b7\u30e7\u30f3\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u8a2d\u5b9a\u3067\u3059 {0}
+standardManager.unloading=\u6301\u7d9a\u3055\u308c\u305f\u30bb\u30c3\u30b7\u30e7\u30f3\u3092 {0} \u306b\u4fdd\u5b58\u3057\u307e\u3059
+standardManager.unloading.ioe=\u6301\u7d9a\u3055\u308c\u305f\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u4fdd\u5b58\u4e2d\u306eIOException\u3067\u3059: {0}
+standardManager.managerLoad=\u6c38\u7d9a\u8a18\u61b6\u88c5\u7f6e\u304b\u3089\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u30ed\u30fc\u30c9\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardManager.managerUnload=\u6c38\u7d9a\u8a18\u61b6\u88c5\u7f6e\u306b\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u30a2\u30f3\u30ed\u30fc\u30c9\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+standardSession.attributeEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u5c5e\u6027\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+standardSession.bindingEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u30d0\u30a4\u30f3\u30c7\u30a3\u30f3\u30b0\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+standardSession.invalidate.ise=invalidate: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.isNew.ise=isNew: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.getAttribute.ise=getAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.getAttributeNames.ise=getAttributeNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.getCreationTime.ise=getCreationTime: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.getLastAccessedTime.ise=getLastAccessedTime: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.getId.ise=getId: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.getValueNames.ise=getValueNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.notSerializable=\u30bb\u30c3\u30b7\u30e7\u30f3 {1} \u306e\u305f\u3081\u306b\u30bb\u30c3\u30b7\u30e7\u30f3\u5c5e\u6027 {0} \u3092\u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3067\u304d\u307e\u305b\u3093
+standardSession.removeAttribute.ise=removeAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.sessionEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+standardSession.setAttribute.iae=setAttribute: \u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3067\u304d\u306a\u3044\u5c5e\u6027\u3067\u3059
+standardSession.setAttribute.ise=setAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+standardSession.setAttribute.namenull=setAttribute: name\u30d1\u30e9\u30e1\u30bf\u306fnull\u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+standardSession.sessionCreated=\u30bb\u30c3\u30b7\u30e7\u30f3ID = {0} \u3092\u751f\u6210\u3057\u307e\u3057\u305f
+persistentManager.loading={0} \u306e\u6301\u7d9a\u3055\u308c\u305f\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u30ed\u30fc\u30c9\u3057\u307e\u3059
+persistentManager.unloading={0} \u306e\u6301\u7d9a\u3055\u308c\u305f\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u4fdd\u5b58\u3057\u307e\u3059
+persistentManager.expiring= {0} \u306e\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u4fdd\u5b58\u3059\u308b\u524d\u306b\u671f\u9650\u5207\u308c\u306b\u306a\u308a\u307e\u3057\u305f
+persistentManager.deserializeError=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30c7\u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059: {1}
+persistentManager.serializeError=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059: {1}
+persistentManager.swapMaxIdle={1}\u79d2\u9593\u30a2\u30a4\u30c9\u30eb\u3057\u3066\u3044\u308b\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u4fdd\u5b58\u3059\u308b\u305f\u3081\u306b\u30b9\u30ef\u30c3\u30d7\u3057\u3066\u3044\u307e\u3059
+persistentManager.backupMaxIdle={1}\u79d2\u9593\u30a2\u30a4\u30c9\u30eb\u3057\u3066\u3044\u308b\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u4fdd\u5b58\u3059\u308b\u305f\u3081\u306b\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3057\u3066\u3044\u307e\u3059
+persistentManager.backupException=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3059\u308b\u6642\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {1}
+persistentManager.tooManyActive=\u30a2\u30af\u30c6\u30a3\u30d6\u30bb\u30c3\u30b7\u30e7\u30f3\u304c\u591a\u3059\u304e\u307e\u3059\u3001{0}\u3001\u30b9\u30ef\u30c3\u30d7\u30a2\u30a6\u30c8\u3059\u308b\u305f\u3081\u306b\u30a2\u30a4\u30c9\u30eb\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u63a2\u3057\u3066\u3044\u307e\u3059
+persistentManager.swapTooManyActive=\u30bb\u30c3\u30b7\u30e7\u30f3\u304c\u591a\u3059\u304e\u308b\u306e\u3067\u3001{1}\u79d2\u30a2\u30a4\u30c9\u30eb\u3057\u3066\u3044\u308b\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30b9\u30ef\u30c3\u30d7\u30a2\u30a6\u30c8\u3057\u307e\u3059
+persistentManager.processSwaps=\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u30b9\u30ef\u30c3\u30d7\u3059\u308b\u305f\u3081\u306b\u30c1\u30a7\u30c3\u30af\u3057\u3066\u3044\u307e\u3059, \u30e1\u30e2\u30ea\u4e2d\u306b {0} \u30a2\u30af\u30c6\u30a3\u30d6\u30bb\u30c3\u30b7\u30e7\u30f3\u304c\u5b58\u5728\u3057\u307e\u3059
+persistentManager.activeSession=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u306f{1}\u79d2\u9593\u30a2\u30a4\u30c9\u30eb\u3057\u3066\u3044\u307e\u3059
+persistentManager.swapIn=\u30bb\u30c3\u30b7\u30e7\u30f3 {0} \u3092\u30b9\u30ef\u30c3\u30d7\u30a4\u30f3\u3057\u3066\u3044\u307e\u3059
diff --git a/container/catalina/src/share/org/apache/catalina/session/ManagerBase.java b/container/catalina/src/share/org/apache/catalina/session/ManagerBase.java
new file mode 100644
index 0000000..6ad104f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/ManagerBase.java
@@ -0,0 +1,1253 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.session;
+
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedAction;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Random;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.Registry;
+
+
+/**
+ * Minimal implementation of the <b>Manager</b> interface that supports
+ * no session persistence or distributable capabilities.  This class may
+ * be subclassed to create more sophisticated Manager implementations.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public abstract class ManagerBase implements Manager, MBeanRegistration {
+    protected Log log = LogFactory.getLog(ManagerBase.class);
+
+    // ----------------------------------------------------- Instance Variables
+
+    protected DataInputStream randomIS=null;
+    protected String devRandomSource="/dev/urandom";
+
+    /**
+     * The default message digest algorithm to use if we cannot use
+     * the requested one.
+     */
+    protected static final String DEFAULT_ALGORITHM = "MD5";
+
+
+    /**
+     * The message digest algorithm to be used when generating session
+     * identifiers.  This must be an algorithm supported by the
+     * <code>java.security.MessageDigest</code> class on your platform.
+     */
+    protected String algorithm = DEFAULT_ALGORITHM;
+
+
+    /**
+     * The Container with which this Manager is associated.
+     */
+    protected Container container;
+
+
+    /**
+     * Return the MessageDigest implementation to be used when
+     * creating session identifiers.
+     */
+    protected MessageDigest digest = null;
+
+
+    /**
+     * The distributable flag for Sessions created by this Manager.  If this
+     * flag is set to <code>true</code>, any user attributes added to a
+     * session controlled by this Manager must be Serializable.
+     */
+    protected boolean distributable;
+
+
+    /**
+     * A String initialization parameter used to increase the entropy of
+     * the initialization of our random number generator.
+     */
+    protected String entropy = null;
+
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info = "ManagerBase/1.0";
+
+
+    /**
+     * The default maximum inactive interval for Sessions created by
+     * this Manager.
+     */
+    protected int maxInactiveInterval = 60;
+
+
+    /**
+     * The session id length of Sessions created by this Manager.
+     */
+    protected int sessionIdLength = 16;
+
+
+    /**
+     * The descriptive name of this Manager implementation (for logging).
+     */
+    protected static String name = "ManagerBase";
+
+
+    /**
+     * A random number generator to use when generating session identifiers.
+     */
+    protected Random random = null;
+
+
+    /**
+     * The Java class name of the random number generator class to be used
+     * when generating session identifiers.
+     */
+    protected String randomClass = "java.security.SecureRandom";
+
+
+    /**
+     * The longest time (in seconds) that an expired session had been alive.
+     */
+    protected int sessionMaxAliveTime;
+
+
+    /**
+     * Average time (in seconds) that expired sessions had been alive.
+     */
+    protected int sessionAverageAliveTime;
+
+
+    /**
+     * Number of sessions that have expired.
+     */
+    protected int expiredSessions = 0;
+
+
+    /**
+     * The set of currently active Sessions for this Manager, keyed by
+     * session identifier.
+     */
+    protected HashMap sessions = new HashMap();
+
+    // Number of sessions created by this manager
+    protected int sessionCounter=0;
+
+    protected int maxActive=0;
+
+    // number of duplicated session ids - anything >0 means we have problems
+    protected int duplicates=0;
+
+    protected boolean initialized=false;
+    
+    /**
+     * Processing time during session expiration.
+     */
+    protected long processingTime = 0;
+
+    /**
+     * Iteration count for background processing.
+     */
+    private int count = 0;
+
+
+    /**
+     * Frequency of the session expiration, and related manager operations.
+     * Manager operations will be done once for the specified amount of
+     * backgrondProcess calls (ie, the lower the amount, the most often the
+     * checks will occur).
+     */
+    protected int processExpiresFrequency = 6;
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+    
+    
+    // ------------------------------------------------------------- Security classes
+
+
+    private class PrivilegedSetRandomFile implements PrivilegedAction{
+        
+        public Object run(){               
+            try {
+                File f=new File( devRandomSource );
+                if( ! f.exists() ) return null;
+                randomIS= new DataInputStream( new FileInputStream(f));
+                randomIS.readLong();
+                if( log.isDebugEnabled() )
+                    log.debug( "Opening " + devRandomSource );
+                return randomIS;
+            } catch (IOException ex){
+                return null;
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the message digest algorithm for this Manager.
+     */
+    public String getAlgorithm() {
+
+        return (this.algorithm);
+
+    }
+
+
+    /**
+     * Set the message digest algorithm for this Manager.
+     *
+     * @param algorithm The new message digest algorithm
+     */
+    public void setAlgorithm(String algorithm) {
+
+        String oldAlgorithm = this.algorithm;
+        this.algorithm = algorithm;
+        support.firePropertyChange("algorithm", oldAlgorithm, this.algorithm);
+
+    }
+
+
+    /**
+     * Return the Container with which this Manager is associated.
+     */
+    public Container getContainer() {
+
+        return (this.container);
+
+    }
+
+
+    /**
+     * Set the Container with which this Manager is associated.
+     *
+     * @param container The newly associated Container
+     */
+    public void setContainer(Container container) {
+
+        Container oldContainer = this.container;
+        this.container = container;
+        support.firePropertyChange("container", oldContainer, this.container);
+    }
+
+
+    /** Returns the name of the implementation class.
+     */
+    public String getClassName() {
+        return this.getClass().getName();
+    }
+
+
+    /**
+     * Return the MessageDigest object to be used for calculating
+     * session identifiers.  If none has been created yet, initialize
+     * one the first time this method is called.
+     */
+    public synchronized MessageDigest getDigest() {
+
+        if (this.digest == null) {
+            long t1=System.currentTimeMillis();
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("managerBase.getting", algorithm));
+            try {
+                this.digest = MessageDigest.getInstance(algorithm);
+            } catch (NoSuchAlgorithmException e) {
+                log.error(sm.getString("managerBase.digest", algorithm), e);
+                try {
+                    this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
+                } catch (NoSuchAlgorithmException f) {
+                    log.error(sm.getString("managerBase.digest",
+                                     DEFAULT_ALGORITHM), e);
+                    this.digest = null;
+                }
+            }
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("managerBase.gotten"));
+            long t2=System.currentTimeMillis();
+            if( log.isDebugEnabled() )
+                log.debug("getDigest() " + (t2-t1));
+        }
+
+        return (this.digest);
+
+    }
+
+
+    /**
+     * Return the distributable flag for the sessions supported by
+     * this Manager.
+     */
+    public boolean getDistributable() {
+
+        return (this.distributable);
+
+    }
+
+
+    /**
+     * Set the distributable flag for the sessions supported by this
+     * Manager.  If this flag is set, all user data objects added to
+     * sessions associated with this manager must implement Serializable.
+     *
+     * @param distributable The new distributable flag
+     */
+    public void setDistributable(boolean distributable) {
+
+        boolean oldDistributable = this.distributable;
+        this.distributable = distributable;
+        support.firePropertyChange("distributable",
+                                   new Boolean(oldDistributable),
+                                   new Boolean(this.distributable));
+
+    }
+
+
+    /**
+     * Return the entropy increaser value, or compute a semi-useful value
+     * if this String has not yet been set.
+     */
+    public String getEntropy() {
+
+        // Calculate a semi-useful value if this has not been set
+        if (this.entropy == null) {
+            // Use APR to get a crypto secure entropy value
+            byte[] result = new byte[32];
+            boolean apr = false;
+            try {
+                String methodName = "random";
+                Class paramTypes[] = new Class[2];
+                paramTypes[0] = result.getClass();
+                paramTypes[1] = int.class;
+                Object paramValues[] = new Object[2];
+                paramValues[0] = result;
+                paramValues[1] = new Integer(32);
+                Method method = Class.forName("org.apache.tomcat.jni.OS")
+                    .getMethod(methodName, paramTypes);
+                method.invoke(null, paramValues);
+                apr = true;
+            } catch (Throwable t) {
+                // Ignore
+            }
+            if (apr) {
+                setEntropy(new String(result));
+            } else {
+                setEntropy(this.toString());
+            }
+        }
+
+        return (this.entropy);
+
+    }
+
+
+    /**
+     * Set the entropy increaser value.
+     *
+     * @param entropy The new entropy increaser value
+     */
+    public void setEntropy(String entropy) {
+
+        String oldEntropy = entropy;
+        this.entropy = entropy;
+        support.firePropertyChange("entropy", oldEntropy, this.entropy);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the default maximum inactive interval (in seconds)
+     * for Sessions created by this Manager.
+     */
+    public int getMaxInactiveInterval() {
+
+        return (this.maxInactiveInterval);
+
+    }
+
+
+    /**
+     * Set the default maximum inactive interval (in seconds)
+     * for Sessions created by this Manager.
+     *
+     * @param interval The new default value
+     */
+    public void setMaxInactiveInterval(int interval) {
+
+        int oldMaxInactiveInterval = this.maxInactiveInterval;
+        this.maxInactiveInterval = interval;
+        support.firePropertyChange("maxInactiveInterval",
+                                   new Integer(oldMaxInactiveInterval),
+                                   new Integer(this.maxInactiveInterval));
+
+    }
+
+
+    /**
+     * Gets the session id length (in bytes) of Sessions created by
+     * this Manager.
+     *
+     * @return The session id length
+     */
+    public int getSessionIdLength() {
+
+        return (this.sessionIdLength);
+
+    }
+
+
+    /**
+     * Sets the session id length (in bytes) for Sessions created by this
+     * Manager.
+     *
+     * @param idLength The session id length
+     */
+    public void setSessionIdLength(int idLength) {
+
+        int oldSessionIdLength = this.sessionIdLength;
+        this.sessionIdLength = idLength;
+        support.firePropertyChange("sessionIdLength",
+                                   new Integer(oldSessionIdLength),
+                                   new Integer(this.sessionIdLength));
+
+    }
+
+
+    /**
+     * Return the descriptive short name of this Manager implementation.
+     */
+    public String getName() {
+
+        return (name);
+
+    }
+
+    /** 
+     * Use /dev/random-type special device. This is new code, but may reduce
+     * the big delay in generating the random.
+     *
+     *  You must specify a path to a random generator file. Use /dev/urandom
+     *  for linux ( or similar ) systems. Use /dev/random for maximum security
+     *  ( it may block if not enough "random" exist ). You can also use
+     *  a pipe that generates random.
+     *
+     *  The code will check if the file exists, and default to java Random
+     *  if not found. There is a significant performance difference, very
+     *  visible on the first call to getSession ( like in the first JSP )
+     *  - so use it if available.
+     */
+    public void setRandomFile( String s ) {
+        // as a hack, you can use a static file - and genarate the same
+        // session ids ( good for strange debugging )
+        if (System.getSecurityManager() != null){
+            randomIS = (DataInputStream)AccessController.doPrivileged(new PrivilegedSetRandomFile());          
+        } else {
+            try{
+                devRandomSource=s;
+                File f=new File( devRandomSource );
+                if( ! f.exists() ) return;
+                randomIS= new DataInputStream( new FileInputStream(f));
+                randomIS.readLong();
+                if( log.isDebugEnabled() )
+                    log.debug( "Opening " + devRandomSource );
+            } catch( IOException ex ) {
+                try {
+                    randomIS.close();
+                } catch (Exception e) {
+                    log.warn("Failed to close randomIS.");
+                }
+                
+                randomIS=null;
+            }
+        }
+    }
+
+    public String getRandomFile() {
+        return devRandomSource;
+    }
+
+
+    /**
+     * Return the random number generator instance we should use for
+     * generating session identifiers.  If there is no such generator
+     * currently defined, construct and seed a new one.
+     */
+    public Random getRandom() {
+        if (this.random == null) {
+            // Calculate the new random number generator seed
+            long seed = System.currentTimeMillis();
+            long t1 = seed;
+            char entropy[] = getEntropy().toCharArray();
+            for (int i = 0; i < entropy.length; i++) {
+                long update = ((byte) entropy[i]) << ((i % 8) * 8);
+                seed ^= update;
+            }
+            try {
+                // Construct and seed a new random number generator
+                Class clazz = Class.forName(randomClass);
+                this.random = (Random) clazz.newInstance();
+                this.random.setSeed(seed);
+            } catch (Exception e) {
+                // Fall back to the simple case
+                log.error(sm.getString("managerBase.random", randomClass),
+                        e);
+                this.random = new java.util.Random();
+                this.random.setSeed(seed);
+            }
+            if(log.isDebugEnabled()) {
+                long t2=System.currentTimeMillis();
+                if( (t2-t1) > 100 )
+                    log.debug(sm.getString("managerBase.seeding", randomClass) + " " + (t2-t1));
+            }
+        }
+        
+        return (this.random);
+
+    }
+
+
+    /**
+     * Return the random number generator class name.
+     */
+    public String getRandomClass() {
+
+        return (this.randomClass);
+
+    }
+
+
+    /**
+     * Set the random number generator class name.
+     *
+     * @param randomClass The new random number generator class name
+     */
+    public void setRandomClass(String randomClass) {
+
+        String oldRandomClass = this.randomClass;
+        this.randomClass = randomClass;
+        support.firePropertyChange("randomClass", oldRandomClass,
+                                   this.randomClass);
+
+    }
+
+
+    /**
+     * Gets the number of sessions that have expired.
+     *
+     * @return Number of sessions that have expired
+     */
+    public int getExpiredSessions() {
+        return expiredSessions;
+    }
+
+
+    /**
+     * Sets the number of sessions that have expired.
+     *
+     * @param expiredSessions Number of sessions that have expired
+     */
+    public void setExpiredSessions(int expiredSessions) {
+        this.expiredSessions = expiredSessions;
+    }
+
+    public long getProcessingTime() {
+        return processingTime;
+    }
+
+
+    public void setProcessingTime(long processingTime) {
+        this.processingTime = processingTime;
+    }
+    
+    /**
+     * Return the frequency of manager checks.
+     */
+    public int getProcessExpiresFrequency() {
+
+        return (this.processExpiresFrequency);
+
+    }
+
+    /**
+     * Set the manager checks frequency.
+     *
+     * @param processExpiresFrequency the new manager checks frequency
+     */
+    public void setProcessExpiresFrequency(int processExpiresFrequency) {
+
+        if (processExpiresFrequency <= 0) {
+            return;
+        }
+
+        int oldProcessExpiresFrequency = this.processExpiresFrequency;
+        this.processExpiresFrequency = processExpiresFrequency;
+        support.firePropertyChange("processExpiresFrequency",
+                                   new Integer(oldProcessExpiresFrequency),
+                                   new Integer(this.processExpiresFrequency));
+
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Implements the Manager interface, direct call to processExpires
+     */
+    public void backgroundProcess() {
+        count = (count + 1) % processExpiresFrequency;
+        if (count == 0)
+            processExpires();
+    }
+
+    /**
+     * Invalidate all sessions that have expired.
+     */
+    public void processExpires() {
+
+        long timeNow = System.currentTimeMillis();
+        Session sessions[] = findSessions();
+        int expireHere = 0 ;
+        
+        if(log.isDebugEnabled())
+            log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
+        for (int i = 0; i < sessions.length; i++) {
+            if (!sessions[i].isValid()) {
+                expiredSessions++;
+                expireHere++;
+            }
+        }
+        long timeEnd = System.currentTimeMillis();
+        if(log.isDebugEnabled())
+             log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere);
+        processingTime += ( timeEnd - timeNow );
+
+    }
+
+    public void destroy() {
+        if( oname != null )
+            Registry.getRegistry(null, null).unregisterComponent(oname);
+        initialized=false;
+        oname = null;
+    }
+    
+    public void init() {
+        if( initialized ) return;
+        initialized=true;        
+        
+        if( oname==null ) {
+            try {
+                StandardContext ctx=(StandardContext)this.getContainer();
+                Engine eng=(Engine)ctx.getParent().getParent();
+                domain=ctx.getEngineName();
+                distributable = ctx.getDistributable();
+                StandardHost hst=(StandardHost)ctx.getParent();
+                String path = ctx.getPath();
+                if (path.equals("")) {
+                    path = "/";
+                }   
+                oname=new ObjectName(domain + ":type=Manager,path="
+                + path + ",host=" + hst.getName());
+                Registry.getRegistry(null, null).registerComponent(this, oname, null );
+            } catch (Exception e) {
+                log.error("Error registering ",e);
+            }
+        }
+        
+        // Initialize random number generation
+        getRandomBytes(new byte[16]);
+        
+        if(log.isDebugEnabled())
+            log.debug("Registering " + oname );
+               
+    }
+
+    /**
+     * Add this Session to the set of active Sessions for this Manager.
+     *
+     * @param session Session to be added
+     */
+    public void add(Session session) {
+
+        synchronized (sessions) {
+            sessions.put(session.getIdInternal(), session);
+            if( sessions.size() > maxActive ) {
+                maxActive=sessions.size();
+            }
+        }
+    }
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id will be assigned by this method, and available via the getId()
+     * method of the returned session.  If a new session cannot be created
+     * for any reason, return <code>null</code>.
+     * 
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @deprecated
+     */
+    public Session createSession() {
+        return createSession(null);
+    }
+    
+    
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id specified will be used as the session id.  
+     * If a new session cannot be created for any reason, return 
+     * <code>null</code>.
+     * 
+     * @param sessionId The session id which should be used to create the
+     *  new session; if <code>null</code>, a new session id will be
+     *  generated
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     */
+    public Session createSession(String sessionId) {
+        
+        // Recycle or create a Session instance
+        Session session = createEmptySession();
+
+        // Initialize the properties of the new session and return it
+        session.setNew(true);
+        session.setValid(true);
+        session.setCreationTime(System.currentTimeMillis());
+        session.setMaxInactiveInterval(this.maxInactiveInterval);
+        if (sessionId == null) {
+            sessionId = generateSessionId();
+        // FIXME WHy we need no duplication check?
+        /*         
+             synchronized (sessions) {
+                while (sessions.get(sessionId) != null) { // Guarantee
+                    // uniqueness
+                    duplicates++;
+                    sessionId = generateSessionId();
+                }
+            }
+        */
+            
+            // FIXME: Code to be used in case route replacement is needed
+            /*
+        } else {
+            String jvmRoute = getJvmRoute();
+            if (getJvmRoute() != null) {
+                String requestJvmRoute = null;
+                int index = sessionId.indexOf(".");
+                if (index > 0) {
+                    requestJvmRoute = sessionId
+                            .substring(index + 1, sessionId.length());
+                }
+                if (requestJvmRoute != null && !requestJvmRoute.equals(jvmRoute)) {
+                    sessionId = sessionId.substring(0, index) + "." + jvmRoute;
+                }
+            }
+            */
+        }
+        session.setId(sessionId);
+        sessionCounter++;
+
+        return (session);
+
+    }
+    
+    
+    /**
+     * Get a session from the recycled ones or create a new empty one.
+     * The PersistentManager manager does not need to create session data
+     * because it reads it from the Store.
+     */
+    public Session createEmptySession() {
+        return (getNewSession());
+    }
+
+
+    /**
+     * Return the active Session, associated with this Manager, with the
+     * specified session id (if any); otherwise return <code>null</code>.
+     *
+     * @param id The session id for the session to be returned
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @exception IOException if an input/output error occurs while
+     *  processing this request
+     */
+    public Session findSession(String id) throws IOException {
+
+        if (id == null)
+            return (null);
+        synchronized (sessions) {
+            Session session = (Session) sessions.get(id);
+            return (session);
+        }
+
+    }
+
+
+    /**
+     * Return the set of active Sessions associated with this Manager.
+     * If this Manager has no active Sessions, a zero-length array is returned.
+     */
+    public Session[] findSessions() {
+
+        Session results[] = null;
+        synchronized (sessions) {
+            results = new Session[sessions.size()];
+            results = (Session[]) sessions.values().toArray(results);
+        }
+        return (results);
+
+    }
+
+
+    /**
+     * Remove this Session from the active Sessions for this Manager.
+     *
+     * @param session Session to be removed
+     */
+    public void remove(Session session) {
+
+        synchronized (sessions) {
+            sessions.remove(session.getIdInternal());
+        }
+
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Get new session class to be used in the doLoad() method.
+     */
+    protected StandardSession getNewSession() {
+        return new StandardSession(this);
+    }
+
+
+    protected void getRandomBytes(byte bytes[]) {
+        // Generate a byte array containing a session identifier
+        if (devRandomSource != null && randomIS == null) {
+            setRandomFile(devRandomSource);
+        }
+        if (randomIS != null) {
+            try {
+                int len = randomIS.read(bytes);
+                if (len == bytes.length) {
+                    return;
+                }
+                if(log.isDebugEnabled())
+                    log.debug("Got " + len + " " + bytes.length );
+            } catch (Exception ex) {
+                // Ignore
+            }
+            devRandomSource = null;
+            
+            try {
+                randomIS.close();
+            } catch (Exception e) {
+                log.warn("Failed to close randomIS.");
+            }
+            
+            randomIS = null;
+        }
+        getRandom().nextBytes(bytes);
+    }
+
+
+    /**
+     * Generate and return a new session identifier.
+     */
+    protected synchronized String generateSessionId() {
+
+        byte random[] = new byte[16];
+        String jvmRoute = getJvmRoute();
+        String result = null;
+
+        // Render the result as a String of hexadecimal digits
+        StringBuffer buffer = new StringBuffer();
+        do {
+            int resultLenBytes = 0;
+            if (result != null) {
+                buffer = new StringBuffer();
+                duplicates++;
+            }
+
+            while (resultLenBytes < this.sessionIdLength) {
+                getRandomBytes(random);
+                random = getDigest().digest(random);
+                for (int j = 0;
+                j < random.length && resultLenBytes < this.sessionIdLength;
+                j++) {
+                    byte b1 = (byte) ((random[j] & 0xf0) >> 4);
+                    byte b2 = (byte) (random[j] & 0x0f);
+                    if (b1 < 10)
+                        buffer.append((char) ('0' + b1));
+                    else
+                        buffer.append((char) ('A' + (b1 - 10)));
+                    if (b2 < 10)
+                        buffer.append((char) ('0' + b2));
+                    else
+                        buffer.append((char) ('A' + (b2 - 10)));
+                    resultLenBytes++;
+                }
+            }
+            if (jvmRoute != null) {
+                buffer.append('.').append(jvmRoute);
+            }
+            result = buffer.toString();
+        } while (sessions.get(result) != null);
+        return (result);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Retrieve the enclosing Engine for this Manager.
+     *
+     * @return an Engine object (or null).
+     */
+    public Engine getEngine() {
+        Engine e = null;
+        for (Container c = getContainer(); e == null && c != null ; c = c.getParent()) {
+            if (c != null && c instanceof Engine) {
+                e = (Engine)c;
+            }
+        }
+        return e;
+    }
+
+
+    /**
+     * Retrieve the JvmRoute for the enclosing Engine.
+     * @return the JvmRoute or null.
+     */
+    public String getJvmRoute() {
+        Engine e = getEngine();
+        return e == null ? null : e.getJvmRoute();
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    public void setSessionCounter(int sessionCounter) {
+        this.sessionCounter = sessionCounter;
+    }
+
+
+    /** 
+     * Total sessions created by this manager.
+     *
+     * @return sessions created
+     */
+    public int getSessionCounter() {
+        return sessionCounter;
+    }
+
+
+    /** 
+     * Number of duplicated session IDs generated by the random source.
+     * Anything bigger than 0 means problems.
+     *
+     * @return The count of duplicates
+     */
+    public int getDuplicates() {
+        return duplicates;
+    }
+
+
+    public void setDuplicates(int duplicates) {
+        this.duplicates = duplicates;
+    }
+
+
+    /** 
+     * Returns the number of active sessions
+     *
+     * @return number of sessions active
+     */
+    public int getActiveSessions() {
+        return sessions.size();
+    }
+
+
+    /**
+     * Max number of concurrent active sessions
+     *
+     * @return The highest number of concurrent active sessions
+     */
+    public int getMaxActive() {
+        return maxActive;
+    }
+
+
+    public void setMaxActive(int maxActive) {
+        this.maxActive = maxActive;
+    }
+
+
+    /**
+     * Gets the longest time (in seconds) that an expired session had been
+     * alive.
+     *
+     * @return Longest time (in seconds) that an expired session had been
+     * alive.
+     */
+    public int getSessionMaxAliveTime() {
+        return sessionMaxAliveTime;
+    }
+
+
+    /**
+     * Sets the longest time (in seconds) that an expired session had been
+     * alive.
+     *
+     * @param sessionMaxAliveTime Longest time (in seconds) that an expired
+     * session had been alive.
+     */
+    public void setSessionMaxAliveTime(int sessionMaxAliveTime) {
+        this.sessionMaxAliveTime = sessionMaxAliveTime;
+    }
+
+
+    /**
+     * Gets the average time (in seconds) that expired sessions had been
+     * alive.
+     *
+     * @return Average time (in seconds) that expired sessions had been
+     * alive.
+     */
+    public int getSessionAverageAliveTime() {
+        return sessionAverageAliveTime;
+    }
+
+
+    /**
+     * Sets the average time (in seconds) that expired sessions had been
+     * alive.
+     *
+     * @param sessionAverageAliveTime Average time (in seconds) that expired
+     * sessions had been alive.
+     */
+    public void setSessionAverageAliveTime(int sessionAverageAliveTime) {
+        this.sessionAverageAliveTime = sessionAverageAliveTime;
+    }
+
+
+    /** 
+     * For debugging: return a list of all session ids currently active
+     *
+     */
+    public String listSessionIds() {
+        StringBuffer sb=new StringBuffer();
+        Iterator keys=sessions.keySet().iterator();
+        while( keys.hasNext() ) {
+            sb.append(keys.next()).append(" ");
+        }
+        return sb.toString();
+    }
+
+
+    /** 
+     * For debugging: get a session attribute
+     *
+     * @param sessionId
+     * @param key
+     * @return The attribute value, if found, null otherwise
+     */
+    public String getSessionAttribute( String sessionId, String key ) {
+        Session s=(Session)sessions.get(sessionId);
+        if( s==null ) {
+            if(log.isInfoEnabled())
+                log.info("Session not found " + sessionId);
+            return null;
+        }
+        Object o=s.getSession().getAttribute(key);
+        if( o==null ) return null;
+        return o.toString();
+    }
+
+
+    /**
+     * Returns information about the session with the given session id.
+     * 
+     * <p>The session information is organized as a HashMap, mapping 
+     * session attribute names to the String representation of their values.
+     *
+     * @param sessionId Session id
+     * 
+     * @return HashMap mapping session attribute names to the String
+     * representation of their values, or null if no session with the
+     * specified id exists, or if the session does not have any attributes
+     */
+    public HashMap getSession(String sessionId) {
+        Session s = (Session) sessions.get(sessionId);
+        if (s == null) {
+            if (log.isInfoEnabled()) {
+                log.info("Session not found " + sessionId);
+            }
+            return null;
+        }
+
+        Enumeration ee = s.getSession().getAttributeNames();
+        if (ee == null || !ee.hasMoreElements()) {
+            return null;
+        }
+
+        HashMap map = new HashMap();
+        while (ee.hasMoreElements()) {
+            String attrName = (String) ee.nextElement();
+            map.put(attrName, getSessionAttribute(sessionId, attrName));
+        }
+
+        return map;
+    }
+
+
+    public void expireSession( String sessionId ) {
+        Session s=(Session)sessions.get(sessionId);
+        if( s==null ) {
+            if(log.isInfoEnabled())
+                log.info("Session not found " + sessionId);
+            return;
+        }
+        s.expire();
+    }
+
+
+    public String getLastAccessedTime( String sessionId ) {
+        Session s=(Session)sessions.get(sessionId);
+        if( s==null ) {
+            log.info("Session not found " + sessionId);
+            return "";
+        }
+        return new Date(s.getLastAccessedTime()).toString();
+    }
+
+
+    // -------------------- JMX and Registration  --------------------
+    protected String domain;
+    protected ObjectName oname;
+    protected MBeanServer mserver;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/session/PersistentManager.java b/container/catalina/src/share/org/apache/catalina/session/PersistentManager.java
new file mode 100644
index 0000000..700282a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/PersistentManager.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.session;
+
+/**
+ * Implementation of the <b>Manager</b> interface that makes use of
+ * a Store to swap active Sessions to disk. It can be configured to
+ * achieve several different goals:
+ *
+ * <li>Persist sessions across restarts of the Container</li>
+ * <li>Fault tolerance, keep sessions backed up on disk to allow
+ *     recovery in the event of unplanned restarts.</li>
+ * <li>Limit the number of active sessions kept in memory by
+ *     swapping less active sessions out to disk.</li>
+ *
+ * @version $Revision$
+ * @author Kief Morris (kief@kief.com)
+ */
+
+public final class PersistentManager extends PersistentManagerBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "PersistentManager/1.0";
+
+
+    /**
+     * The descriptive name of this Manager implementation (for logging).
+     */
+    protected static String name = "PersistentManager";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * Return the descriptive short name of this Manager implementation.
+     */
+    public String getName() {
+
+        return (name);
+
+    }
+ }
+
diff --git a/container/catalina/src/share/org/apache/catalina/session/PersistentManagerBase.java b/container/catalina/src/share/org/apache/catalina/session/PersistentManagerBase.java
new file mode 100644
index 0000000..02bf60c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/PersistentManagerBase.java
@@ -0,0 +1,1130 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.session;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Session;
+import org.apache.catalina.Store;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.catalina.security.SecurityUtil;
+/**
+ * Extends the <b>ManagerBase</b> class to implement most of the
+ * functionality required by a Manager which supports any kind of
+ * persistence, even if onlyfor  restarts.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b>:  Correct behavior of session storing and
+ * reloading depends upon external calls to the <code>start()</code> and
+ * <code>stop()</code> methods of this class at the correct times.
+ *
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @version $Revision$ $Date$
+ */
+
+public abstract class PersistentManagerBase
+    extends ManagerBase
+    implements Lifecycle, PropertyChangeListener {
+
+    private static Log log = LogFactory.getLog(PersistentManagerBase.class);
+
+    // ---------------------------------------------------- Security Classes
+
+    private class PrivilegedStoreClear
+        implements PrivilegedExceptionAction {
+
+        PrivilegedStoreClear() {            
+        }
+
+        public Object run() throws Exception{
+           store.clear();
+           return null;
+        }                       
+    }   
+     
+    private class PrivilegedStoreRemove
+        implements PrivilegedExceptionAction {
+
+        private String id;    
+            
+        PrivilegedStoreRemove(String id) {     
+            this.id = id;
+        }
+
+        public Object run() throws Exception{
+           store.remove(id);
+           return null;
+        }                       
+    }   
+     
+    private class PrivilegedStoreLoad
+        implements PrivilegedExceptionAction {
+
+        private String id;    
+            
+        PrivilegedStoreLoad(String id) {     
+            this.id = id;
+        }
+
+        public Object run() throws Exception{
+           return store.load(id);
+        }                       
+    }   
+          
+    private class PrivilegedStoreSave
+        implements PrivilegedExceptionAction {
+
+        private Session session;    
+            
+        PrivilegedStoreSave(Session session) {     
+            this.session = session;
+        }
+
+        public Object run() throws Exception{
+           store.save(session);
+           return null;
+        }                       
+    }   
+     
+    private class PrivilegedStoreKeys
+        implements PrivilegedExceptionAction {
+
+        PrivilegedStoreKeys() {     
+        }
+
+        public Object run() throws Exception{
+           return store.keys();
+        }                       
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "PersistentManagerBase/1.1";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The maximum number of active Sessions allowed, or -1 for no limit.
+     */
+    protected int maxActiveSessions = -1;
+
+
+    /**
+     * The descriptive name of this Manager implementation (for logging).
+     */
+    private static String name = "PersistentManagerBase";
+
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+
+    /**
+     * Store object which will manage the Session store.
+     */
+    protected Store store = null;
+
+
+    /**
+     * Whether to save and reload sessions when the Manager <code>unload</code>
+     * and <code>load</code> methods are called.
+     */
+    protected boolean saveOnRestart = true;
+
+
+    /**
+     * How long a session must be idle before it should be backed up.
+     * -1 means sessions won't be backed up.
+     */
+    protected int maxIdleBackup = -1;
+
+
+    /**
+     * Minimum time a session must be idle before it is swapped to disk.
+     * This overrides maxActiveSessions, to prevent thrashing if there are lots
+     * of active sessions. Setting to -1 means it's ignored.
+     */
+    protected int minIdleSwap = -1;
+
+    /**
+     * The maximum time a session may be idle before it should be swapped
+     * to file just on general principle. Setting this to -1 means sessions
+     * should not be forced out.
+     */
+    protected int maxIdleSwap = -1;
+
+
+    /**
+     * Number of session creations that failed due to maxActiveSessions.
+     */
+    protected int rejectedSessions = 0;
+
+
+    /**
+     * Processing time during session expiration and passivation.
+     */
+    protected long processingTime = 0;
+
+
+    // ------------------------------------------------------------- Properties
+
+    
+  
+
+
+    /**
+	 * Indicates how many seconds old a session can get, after its last use in a
+	 * request, before it should be backed up to the store. -1 means sessions
+	 * are not backed up.
+	 */
+    public int getMaxIdleBackup() {
+
+        return maxIdleBackup;
+
+    }
+
+
+    /**
+     * Sets the option to back sessions up to the Store after they
+     * are used in a request. Sessions remain available in memory
+     * after being backed up, so they are not passivated as they are
+     * when swapped out. The value set indicates how old a session
+     * may get (since its last use) before it must be backed up: -1
+     * means sessions are not backed up.
+     * <p>
+     * Note that this is not a hard limit: sessions are checked
+     * against this age limit periodically according to <b>processExpiresFrequency</b>.
+     * This value should be considered to indicate when a session is
+     * ripe for backing up.
+     * <p>
+     * So it is possible that a session may be idle for maxIdleBackup +
+     * processExpiresFrequency * engine.backgroundProcessorDelay seconds, plus the time it takes to handle other
+     * session expiration, swapping, etc. tasks.
+     *
+     * @param backup The number of seconds after their last accessed
+     * time when they should be written to the Store.
+     */
+    public void setMaxIdleBackup (int backup) {
+
+        if (backup == this.maxIdleBackup)
+            return;
+        int oldBackup = this.maxIdleBackup;
+        this.maxIdleBackup = backup;
+        support.firePropertyChange("maxIdleBackup",
+                                   new Integer(oldBackup),
+                                   new Integer(this.maxIdleBackup));
+
+    }
+
+
+    /**
+     * The time in seconds after which a session should be swapped out of
+     * memory to disk.
+     */
+    public int getMaxIdleSwap() {
+
+        return maxIdleSwap;
+
+    }
+
+
+    /**
+     * Sets the time in seconds after which a session should be swapped out of
+     * memory to disk.
+     */
+    public void setMaxIdleSwap(int max) {
+
+        if (max == this.maxIdleSwap)
+            return;
+        int oldMaxIdleSwap = this.maxIdleSwap;
+        this.maxIdleSwap = max;
+        support.firePropertyChange("maxIdleSwap",
+                                   new Integer(oldMaxIdleSwap),
+                                   new Integer(this.maxIdleSwap));
+
+    }
+
+
+    /**
+     * The minimum time in seconds that a session must be idle before
+     * it can be swapped out of memory, or -1 if it can be swapped out
+     * at any time.
+     */
+    public int getMinIdleSwap() {
+
+        return minIdleSwap;
+
+    }
+
+
+    /**
+     * Sets the minimum time in seconds that a session must be idle before
+     * it can be swapped out of memory due to maxActiveSession. Set it to -1
+     * if it can be swapped out at any time.
+     */
+    public void setMinIdleSwap(int min) {
+
+        if (this.minIdleSwap == min)
+            return;
+        int oldMinIdleSwap = this.minIdleSwap;
+        this.minIdleSwap = min;
+        support.firePropertyChange("minIdleSwap",
+                                   new Integer(oldMinIdleSwap),
+                                   new Integer(this.minIdleSwap));
+
+    }
+
+
+    /**
+	 * Set the Container with which this Manager has been associated. If it is a
+	 * Context (the usual case), listen for changes to the session timeout
+	 * property.
+	 * 
+	 * @param container
+	 *            The associated Container
+	 */
+    public void setContainer(Container container) {
+
+        // De-register from the old Container (if any)
+        if ((this.container != null) && (this.container instanceof Context))
+            ((Context) this.container).removePropertyChangeListener(this);
+
+        // Default processing provided by our superclass
+        super.setContainer(container);
+
+        // Register with the new Container (if any)
+        if ((this.container != null) && (this.container instanceof Context)) {
+            setMaxInactiveInterval
+                ( ((Context) this.container).getSessionTimeout()*60 );
+            ((Context) this.container).addPropertyChangeListener(this);
+        }
+
+    }
+
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return true, if the session id is loaded in memory
+     * otherwise false is returned
+     *
+     * @param id The session id for the session to be searched for
+     */
+    public boolean isLoaded( String id ){
+        try {
+            if ( super.findSession(id) != null )
+                return true;
+        } catch (IOException e) {
+            log.error("checking isLoaded for id, " + id + ", "+e.getMessage(), e);
+        }
+        return false;
+    }
+
+
+    /**
+     * Return the maximum number of active Sessions allowed, or -1 for
+     * no limit.
+     */
+    public int getMaxActiveSessions() {
+
+        return (this.maxActiveSessions);
+
+    }
+
+
+    /**
+     * Set the maximum number of actives Sessions allowed, or -1 for
+     * no limit.
+     *
+     * @param max The new maximum number of sessions
+     */
+    public void setMaxActiveSessions(int max) {
+
+        int oldMaxActiveSessions = this.maxActiveSessions;
+        this.maxActiveSessions = max;
+        support.firePropertyChange("maxActiveSessions",
+                                   new Integer(oldMaxActiveSessions),
+                                   new Integer(this.maxActiveSessions));
+
+    }
+
+
+    /** 
+     * Number of session creations that failed due to maxActiveSessions.
+     *
+     * @return The count
+     */
+    public int getRejectedSessions() {
+        return rejectedSessions;
+    }
+
+    
+    public void setRejectedSessions(int rejectedSessions) {
+        this.rejectedSessions = rejectedSessions;
+    }
+
+    /**
+     * Return the descriptive short name of this Manager implementation.
+     */
+    public String getName() {
+
+        return (name);
+
+    }
+
+
+    /**
+     * Get the started status.
+     */
+    protected boolean isStarted() {
+
+        return started;
+
+    }
+
+
+    /**
+     * Set the started flag
+     */
+    protected void setStarted(boolean started) {
+
+        this.started = started;
+
+    }
+
+
+    /**
+     * Set the Store object which will manage persistent Session
+     * storage for this Manager.
+     *
+     * @param store the associated Store
+     */
+    public void setStore(Store store) {
+        this.store = store;
+        store.setManager(this);
+
+    }
+
+
+    /**
+     * Return the Store object which manages persistent Session
+     * storage for this Manager.
+     */
+    public Store getStore() {
+
+        return (this.store);
+
+    }
+
+
+
+    /**
+     * Indicates whether sessions are saved when the Manager is shut down
+     * properly. This requires the unload() method to be called.
+     */
+    public boolean getSaveOnRestart() {
+
+        return saveOnRestart;
+
+    }
+
+
+    /**
+     * Set the option to save sessions to the Store when the Manager is
+     * shut down, then loaded when the Manager starts again. If set to
+     * false, any sessions found in the Store may still be picked up when
+     * the Manager is started again.
+     *
+     * @param saveOnRestart true if sessions should be saved on restart, false if
+     *     they should be ignored.
+     */
+    public void setSaveOnRestart(boolean saveOnRestart) {
+
+        if (saveOnRestart == this.saveOnRestart)
+            return;
+
+        boolean oldSaveOnRestart = this.saveOnRestart;
+        this.saveOnRestart = saveOnRestart;
+        support.firePropertyChange("saveOnRestart",
+                                   new Boolean(oldSaveOnRestart),
+                                   new Boolean(this.saveOnRestart));
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Clear all sessions from the Store.
+     */
+    public void clearStore() {
+
+        if (store == null)
+            return;
+
+        try {     
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    AccessController.doPrivileged(new PrivilegedStoreClear());
+                }catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.error("Exception clearing the Store: " + exception);
+                    exception.printStackTrace();                        
+                }
+            } else {
+                store.clear();
+            }
+        } catch (IOException e) {
+            log.error("Exception clearing the Store: " + e);
+            e.printStackTrace();
+        }
+
+    }
+
+
+    /**
+     * Implements the Manager interface, direct call to processExpires and processPersistenceChecks
+     */
+	public void processExpires() {
+		
+        long timeNow = System.currentTimeMillis();
+        Session sessions[] = findSessions();
+        int expireHere = 0 ;
+        if(log.isDebugEnabled())
+             log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
+        for (int i = 0; i < sessions.length; i++) {
+            if (!sessions[i].isValid()) {
+                expiredSessions++;
+                expireHere++;
+            }
+        }
+        processPersistenceChecks();
+        if ((getStore() != null) && (getStore() instanceof StoreBase)) {
+            ((StoreBase) getStore()).processExpires();
+        }
+        
+        long timeEnd = System.currentTimeMillis();
+        if(log.isDebugEnabled())
+             log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere);
+        processingTime += (timeEnd - timeNow);
+ 		
+	}
+
+
+    /**
+     * Called by the background thread after active sessions have been checked
+     * for expiration, to allow sessions to be swapped out, backed up, etc.
+     */
+    public void processPersistenceChecks() {
+
+        processMaxIdleSwaps();
+        processMaxActiveSwaps();
+        processMaxIdleBackups();
+
+    }
+
+
+    /**
+     * Return the active Session, associated with this Manager, with the
+     * specified session id (if any); otherwise return <code>null</code>.
+     * This method checks the persistence store if persistence is enabled,
+     * otherwise just uses the functionality from ManagerBase.
+     *
+     * @param id The session id for the session to be returned
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @exception IOException if an input/output error occurs while
+     *  processing this request
+     */
+    public Session findSession(String id) throws IOException {
+
+        Session session = super.findSession(id);
+        if (session != null)
+            return (session);
+
+        // See if the Session is in the Store
+        session = swapIn(id);
+        return (session);
+
+    }
+
+    /**
+     * Remove this Session from the active Sessions for this Manager,
+     * but not from the Store. (Used by the PersistentValve)
+     *
+     * @param session Session to be removed
+     */
+    public void removeSuper(Session session) {
+        super.remove (session);
+    }
+
+    /**
+     * Load all sessions found in the persistence mechanism, assuming
+     * they are marked as valid and have not passed their expiration
+     * limit. If persistence is not supported, this method returns
+     * without doing anything.
+     * <p>
+     * Note that by default, this method is not called by the MiddleManager
+     * class. In order to use it, a subclass must specifically call it,
+     * for example in the start() and/or processPersistenceChecks() methods.
+     */
+    public void load() {
+
+        // Initialize our internal data structures
+        sessions.clear();
+
+        if (store == null)
+            return;
+
+        String[] ids = null;
+        try {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    ids = (String[])
+                        AccessController.doPrivileged(new PrivilegedStoreKeys());
+                }catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.error("Exception in the Store during load: "
+                              + exception);
+                    exception.printStackTrace();                        
+                }
+            } else {
+                ids = store.keys();
+            }
+        } catch (IOException e) {
+            log.error("Can't load sessions from store, " + e.getMessage(), e);
+            return;
+        }
+
+        int n = ids.length;
+        if (n == 0)
+            return;
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("persistentManager.loading", String.valueOf(n)));
+
+        for (int i = 0; i < n; i++)
+            try {
+                swapIn(ids[i]);
+            } catch (IOException e) {
+                log.error("Failed load session from store, " + e.getMessage(), e);
+            }
+
+    }
+
+
+    /**
+     * Remove this Session from the active Sessions for this Manager,
+     * and from the Store.
+     *
+     * @param session Session to be removed
+     */
+    public void remove(Session session) {
+
+        super.remove (session);
+
+        if (store != null){
+            removeSession(session.getIdInternal());
+        }
+    }
+
+    
+    /**
+     * Remove this Session from the active Sessions for this Manager,
+     * and from the Store.
+     *
+     * @param id Session's id to be removed
+     */    
+    protected void removeSession(String id){
+        try {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    AccessController.doPrivileged(new PrivilegedStoreRemove(id));
+                }catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.error("Exception in the Store during removeSession: "
+                              + exception);
+                    exception.printStackTrace();                        
+                }
+            } else {
+                 store.remove(id);
+            }               
+        } catch (IOException e) {
+            log.error("Exception removing session  " + e.getMessage());
+            e.printStackTrace();
+        }        
+    }
+
+    /**
+     * Save all currently active sessions in the appropriate persistence
+     * mechanism, if any.  If persistence is not supported, this method
+     * returns without doing anything.
+     * <p>
+     * Note that by default, this method is not called by the MiddleManager
+     * class. In order to use it, a subclass must specifically call it,
+     * for example in the stop() and/or processPersistenceChecks() methods.
+     */
+    public void unload() {
+
+        if (store == null)
+            return;
+
+        Session sessions[] = findSessions();
+        int n = sessions.length;
+        if (n == 0)
+            return;
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("persistentManager.unloading",
+                             String.valueOf(n)));
+
+        for (int i = 0; i < n; i++)
+            try {
+                swapOut(sessions[i]);
+            } catch (IOException e) {
+                ;   // This is logged in writeSession()
+            }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Look for a session in the Store and, if found, restore
+     * it in the Manager's list of active sessions if appropriate.
+     * The session will be removed from the Store after swapping
+     * in, but will not be added to the active session list if it
+     * is invalid or past its expiration.
+     */
+    protected Session swapIn(String id) throws IOException {
+
+        if (store == null)
+            return null;
+
+        Session session = null;
+        try {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    session = (Session) 
+                      AccessController.doPrivileged(new PrivilegedStoreLoad(id));
+                }catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.error("Exception in the Store during swapIn: "
+                              + exception);
+                    if (exception instanceof IOException){
+                        throw (IOException)exception;
+                    } else if (exception instanceof ClassNotFoundException) {
+                        throw (ClassNotFoundException)exception;
+                    }
+                }
+            } else {
+                 session = store.load(id);
+            }   
+        } catch (ClassNotFoundException e) {
+            log.error(sm.getString("persistentManager.deserializeError", id, e));
+            throw new IllegalStateException
+                (sm.getString("persistentManager.deserializeError", id, e));
+        }
+
+        if (session == null)
+            return (null);
+
+        if (!session.isValid()) {
+            log.error("session swapped in is invalid or expired");
+            session.expire();
+            removeSession(id);
+            return (null);
+        }
+
+        if(log.isDebugEnabled())
+            log.debug(sm.getString("persistentManager.swapIn", id));
+
+        session.setManager(this);
+        // make sure the listeners know about it.
+        ((StandardSession)session).tellNew();
+        add(session);
+        ((StandardSession)session).activate();
+        session.endAccess();
+
+        return (session);
+
+    }
+
+
+    /**
+     * Remove the session from the Manager's list of active
+     * sessions and write it out to the Store. If the session
+     * is past its expiration or invalid, this method does
+     * nothing.
+     *
+     * @param session The Session to write out.
+     */
+    protected void swapOut(Session session) throws IOException {
+
+        if (store == null || !session.isValid()) {
+            return;
+        }
+
+        ((StandardSession)session).passivate();
+        writeSession(session);
+        super.remove(session);
+        session.recycle();
+
+    }
+
+
+    /**
+     * Write the provided session to the Store without modifying
+     * the copy in memory or triggering passivation events. Does
+     * nothing if the session is invalid or past its expiration.
+     */
+    protected void writeSession(Session session) throws IOException {
+
+        if (store == null || !session.isValid()) {
+            return;
+        }
+
+        try {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    AccessController.doPrivileged(new PrivilegedStoreSave(session));
+                }catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.error("Exception in the Store during writeSession: "
+                              + exception);
+                    exception.printStackTrace();                        
+                }
+            } else {
+                 store.save(session);
+            }   
+        } catch (IOException e) {
+            log.error(sm.getString
+                ("persistentManager.serializeError", session.getIdInternal(), e));
+            throw e;
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started) {
+            log.info(sm.getString("standardManager.alreadyStarted"));
+            return;
+        }
+        if( ! initialized )
+            init();
+        
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Force initialization of the random number generator
+        if (log.isDebugEnabled())
+            log.debug("Force random number initialization starting");
+        String dummy = generateSessionId();
+        if (log.isDebugEnabled())
+            log.debug("Force random number initialization completed");
+
+        if (store == null)
+            log.error("No Store configured, persistence disabled");
+        else if (store instanceof Lifecycle)
+            ((Lifecycle)store).start();
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+   public void stop() throws LifecycleException {
+
+        if (log.isDebugEnabled())
+            log.debug("Stopping");
+
+        // Validate and update our current component state
+        if (!isStarted()) {
+            log.info(sm.getString("standardManager.notStarted"));
+            return;
+        }
+        
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        setStarted(false);
+
+        if (getStore() != null && saveOnRestart) {
+            unload();
+        } else {
+            // Expire all active sessions
+            Session sessions[] = findSessions();
+            for (int i = 0; i < sessions.length; i++) {
+                StandardSession session = (StandardSession) sessions[i];
+                if (!session.isValid())
+                    continue;
+                session.expire();
+            }
+        }
+
+        if (getStore() != null && getStore() instanceof Lifecycle)
+            ((Lifecycle)getStore()).stop();
+
+        // Require a new random number generator if we are restarted
+        this.random = null;
+
+        if( initialized )
+            destroy();
+
+    }
+
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+
+    /**
+     * Process property change events from our associated Context.
+     *
+     * @param event The property change event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        // Validate the source of this event
+        if (!(event.getSource() instanceof Context))
+            return;
+        Context context = (Context) event.getSource();
+
+        // Process a relevant property change
+        if (event.getPropertyName().equals("sessionTimeout")) {
+            try {
+                setMaxInactiveInterval
+                    ( ((Integer) event.getNewValue()).intValue()*60 );
+            } catch (NumberFormatException e) {
+                log.error(sm.getString("standardManager.sessionTimeout",
+                                 event.getNewValue().toString()));
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Swap idle sessions out to Store if they are idle too long.
+     */
+    protected void processMaxIdleSwaps() {
+
+        if (!isStarted() || maxIdleSwap < 0)
+            return;
+
+        Session sessions[] = findSessions();
+        long timeNow = System.currentTimeMillis();
+
+        // Swap out all sessions idle longer than maxIdleSwap
+        // FIXME: What's preventing us from mangling a session during
+        // a request?
+        if (maxIdleSwap >= 0) {
+            for (int i = 0; i < sessions.length; i++) {
+                StandardSession session = (StandardSession) sessions[i];
+                if (!session.isValid())
+                    continue;
+                int timeIdle = // Truncate, do not round up
+                    (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+                if (timeIdle > maxIdleSwap && timeIdle > minIdleSwap) {
+                    if (log.isDebugEnabled())
+                        log.debug(sm.getString
+                            ("persistentManager.swapMaxIdle",
+                             session.getIdInternal(), new Integer(timeIdle)));
+                    try {
+                        swapOut(session);
+                    } catch (IOException e) {
+                        ;   // This is logged in writeSession()
+                    }
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Swap idle sessions out to Store if too many are active
+     */
+    protected void processMaxActiveSwaps() {
+
+        if (!isStarted() || getMaxActiveSessions() < 0)
+            return;
+
+        Session sessions[] = findSessions();
+
+        // FIXME: Smarter algorithm (LRU)
+        if (getMaxActiveSessions() >= sessions.length)
+            return;
+
+        if(log.isDebugEnabled())
+            log.debug(sm.getString
+                ("persistentManager.tooManyActive",
+                 new Integer(sessions.length)));
+
+        int toswap = sessions.length - getMaxActiveSessions();
+        long timeNow = System.currentTimeMillis();
+
+        for (int i = 0; i < sessions.length && toswap > 0; i++) {
+            int timeIdle = // Truncate, do not round up
+                (int) ((timeNow - sessions[i].getLastAccessedTime()) / 1000L);
+            if (timeIdle > minIdleSwap) {
+                if(log.isDebugEnabled())
+                    log.debug(sm.getString
+                        ("persistentManager.swapTooManyActive",
+                         sessions[i].getIdInternal(), new Integer(timeIdle)));
+                try {
+                    swapOut(sessions[i]);
+                } catch (IOException e) {
+                    ;   // This is logged in writeSession()
+                }
+                toswap--;
+            }
+        }
+
+    }
+
+
+    /**
+     * Back up idle sessions.
+     */
+    protected void processMaxIdleBackups() {
+
+        if (!isStarted() || maxIdleBackup < 0)
+            return;
+
+        Session sessions[] = findSessions();
+        long timeNow = System.currentTimeMillis();
+
+        // Back up all sessions idle longer than maxIdleBackup
+        if (maxIdleBackup >= 0) {
+            for (int i = 0; i < sessions.length; i++) {
+                StandardSession session = (StandardSession) sessions[i];
+                if (!session.isValid())
+                    continue;
+                int timeIdle = // Truncate, do not round up
+                    (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+                if (timeIdle > maxIdleBackup) {
+                    if (log.isDebugEnabled())
+                        log.debug(sm.getString
+                            ("persistentManager.backupMaxIdle",
+                            session.getIdInternal(), new Integer(timeIdle)));
+
+                    try {
+                        writeSession(session);
+                    } catch (IOException e) {
+                        ;   // This is logged in writeSession()
+                    }
+                }
+            }
+        }
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/session/StandardManager.java b/container/catalina/src/share/org/apache/catalina/session/StandardManager.java
new file mode 100644
index 0000000..2e8d978
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/StandardManager.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.session;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Iterator;
+import javax.servlet.ServletContext;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Session;
+import org.apache.catalina.util.CustomObjectInputStream;
+import org.apache.catalina.util.LifecycleSupport;
+
+import org.apache.catalina.security.SecurityUtil;
+/**
+ * Standard implementation of the <b>Manager</b> interface that provides
+ * simple session persistence across restarts of this component (such as
+ * when the entire server is shut down and restarted, or when a particular
+ * web application is reloaded.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b>:  Correct behavior of session storing and
+ * reloading depends upon external calls to the <code>start()</code> and
+ * <code>stop()</code> methods of this class at the correct times.
+ *
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @version $Revision$ $Date$
+ */
+
+public class StandardManager
+    extends ManagerBase
+    implements Lifecycle, PropertyChangeListener {
+
+    // ---------------------------------------------------- Security Classes
+    private class PrivilegedDoLoad
+        implements PrivilegedExceptionAction {
+
+        PrivilegedDoLoad() {
+        }
+
+        public Object run() throws Exception{
+           doLoad();
+           return null;
+        }
+    }
+
+    private class PrivilegedDoUnload
+        implements PrivilegedExceptionAction {
+
+        PrivilegedDoUnload() {
+        }
+
+        public Object run() throws Exception{
+            doUnload();
+            return null;
+        }
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info = "StandardManager/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The maximum number of active Sessions allowed, or -1 for no limit.
+     */
+    protected int maxActiveSessions = -1;
+
+
+    /**
+     * The descriptive name of this Manager implementation (for logging).
+     */
+    protected static String name = "StandardManager";
+
+
+    /**
+     * Path name of the disk file in which active sessions are saved
+     * when we stop, and from which these sessions are loaded when we start.
+     * A <code>null</code> value indicates that no persistence is desired.
+     * If this pathname is relative, it will be resolved against the
+     * temporary working directory provided by our context, available via
+     * the <code>javax.servlet.context.tempdir</code> context attribute.
+     */
+    protected String pathname = "SESSIONS.ser";
+
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+
+    /**
+     * Number of session creations that failed due to maxActiveSessions.
+     */
+    protected int rejectedSessions = 0;
+
+
+    /**
+     * Processing time during session expiration.
+     */
+    protected long processingTime = 0;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the Container with which this Manager has been associated.  If
+     * it is a Context (the usual case), listen for changes to the session
+     * timeout property.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container) {
+
+        // De-register from the old Container (if any)
+        if ((this.container != null) && (this.container instanceof Context))
+            ((Context) this.container).removePropertyChangeListener(this);
+
+        // Default processing provided by our superclass
+        super.setContainer(container);
+
+        // Register with the new Container (if any)
+        if ((this.container != null) && (this.container instanceof Context)) {
+            setMaxInactiveInterval
+                ( ((Context) this.container).getSessionTimeout()*60 );
+            ((Context) this.container).addPropertyChangeListener(this);
+        }
+
+    }
+
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the maximum number of active Sessions allowed, or -1 for
+     * no limit.
+     */
+    public int getMaxActiveSessions() {
+
+        return (this.maxActiveSessions);
+
+    }
+
+
+    /** Number of session creations that failed due to maxActiveSessions
+     *
+     * @return The count
+     */
+    public int getRejectedSessions() {
+        return rejectedSessions;
+    }
+
+
+    public void setRejectedSessions(int rejectedSessions) {
+        this.rejectedSessions = rejectedSessions;
+    }
+
+
+    /**
+     * Set the maximum number of actives Sessions allowed, or -1 for
+     * no limit.
+     *
+     * @param max The new maximum number of sessions
+     */
+    public void setMaxActiveSessions(int max) {
+
+        int oldMaxActiveSessions = this.maxActiveSessions;
+        this.maxActiveSessions = max;
+        support.firePropertyChange("maxActiveSessions",
+                                   new Integer(oldMaxActiveSessions),
+                                   new Integer(this.maxActiveSessions));
+
+    }
+
+
+    /**
+     * Return the descriptive short name of this Manager implementation.
+     */
+    public String getName() {
+
+        return (name);
+
+    }
+
+
+    /**
+     * Return the session persistence pathname, if any.
+     */
+    public String getPathname() {
+
+        return (this.pathname);
+
+    }
+
+
+    /**
+     * Set the session persistence pathname to the specified value.  If no
+     * persistence support is desired, set the pathname to <code>null</code>.
+     *
+     * @param pathname New session persistence pathname
+     */
+    public void setPathname(String pathname) {
+
+        String oldPathname = this.pathname;
+        this.pathname = pathname;
+        support.firePropertyChange("pathname", oldPathname, this.pathname);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id will be assigned by this method, and available via the getId()
+     * method of the returned session.  If a new session cannot be created
+     * for any reason, return <code>null</code>.
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     */
+    public Session createSession(String sessionId) {
+
+        if ((maxActiveSessions >= 0) &&
+            (sessions.size() >= maxActiveSessions)) {
+            rejectedSessions++;
+            throw new IllegalStateException
+                (sm.getString("standardManager.createSession.ise"));
+        }
+
+        return (super.createSession(sessionId));
+
+    }
+
+
+    /**
+     * Load any currently active sessions that were previously unloaded
+     * to the appropriate persistence mechanism, if any.  If persistence is not
+     * supported, this method returns without doing anything.
+     *
+     * @exception ClassNotFoundException if a serialized class cannot be
+     *  found during the reload
+     * @exception IOException if an input/output error occurs
+     */
+    public void load() throws ClassNotFoundException, IOException {
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                AccessController.doPrivileged( new PrivilegedDoLoad() );
+            } catch (PrivilegedActionException ex){
+                Exception exception = ex.getException();
+                if (exception instanceof ClassNotFoundException){
+                    throw (ClassNotFoundException)exception;
+                } else if (exception instanceof IOException){
+                    throw (IOException)exception;
+                }
+                if (log.isDebugEnabled())
+                    log.debug("Unreported exception in load() "
+                        + exception);
+            }
+        } else {
+            doLoad();
+        }
+    }
+
+
+    /**
+     * Load any currently active sessions that were previously unloaded
+     * to the appropriate persistence mechanism, if any.  If persistence is not
+     * supported, this method returns without doing anything.
+     *
+     * @exception ClassNotFoundException if a serialized class cannot be
+     *  found during the reload
+     * @exception IOException if an input/output error occurs
+     */
+    protected void doLoad() throws ClassNotFoundException, IOException {
+        if (log.isDebugEnabled())
+            log.debug("Start: Loading persisted sessions");
+
+        // Initialize our internal data structures
+        sessions.clear();
+
+        // Open an input stream to the specified pathname, if any
+        File file = file();
+        if (file == null)
+            return;
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("standardManager.loading", pathname));
+        FileInputStream fis = null;
+        ObjectInputStream ois = null;
+        Loader loader = null;
+        ClassLoader classLoader = null;
+        try {
+            fis = new FileInputStream(file.getAbsolutePath());
+            BufferedInputStream bis = new BufferedInputStream(fis);
+            if (container != null)
+                loader = container.getLoader();
+            if (loader != null)
+                classLoader = loader.getClassLoader();
+            if (classLoader != null) {
+                if (log.isDebugEnabled())
+                    log.debug("Creating custom object input stream for class loader ");
+                ois = new CustomObjectInputStream(bis, classLoader);
+            } else {
+                if (log.isDebugEnabled())
+                    log.debug("Creating standard object input stream");
+                ois = new ObjectInputStream(bis);
+            }
+        } catch (FileNotFoundException e) {
+            if (log.isDebugEnabled())
+                log.debug("No persisted data file found");
+            return;
+        } catch (IOException e) {
+            log.error(sm.getString("standardManager.loading.ioe", e), e);
+            if (ois != null) {
+                try {
+                    ois.close();
+                } catch (IOException f) {
+                    ;
+                }
+                ois = null;
+            }
+            throw e;
+        }
+
+        // Load the previously unloaded active sessions
+        synchronized (sessions) {
+            try {
+                Integer count = (Integer) ois.readObject();
+                int n = count.intValue();
+                if (log.isDebugEnabled())
+                    log.debug("Loading " + n + " persisted sessions");
+                for (int i = 0; i < n; i++) {
+                    StandardSession session = getNewSession();
+                    session.readObjectData(ois);
+                    session.setManager(this);
+                    sessions.put(session.getIdInternal(), session);
+                    session.activate();
+                    session.endAccess();
+                }
+            } catch (ClassNotFoundException e) {
+              log.error(sm.getString("standardManager.loading.cnfe", e), e);
+                if (ois != null) {
+                    try {
+                        ois.close();
+                    } catch (IOException f) {
+                        ;
+                    }
+                    ois = null;
+                }
+                throw e;
+            } catch (IOException e) {
+              log.error(sm.getString("standardManager.loading.ioe", e), e);
+                if (ois != null) {
+                    try {
+                        ois.close();
+                    } catch (IOException f) {
+                        ;
+                    }
+                    ois = null;
+                }
+                throw e;
+            } finally {
+                // Close the input stream
+                try {
+                    if (ois != null)
+                        ois.close();
+                } catch (IOException f) {
+                    // ignored
+                }
+
+                // Delete the persistent storage file
+                if (file != null && file.exists() )
+                    file.delete();
+            }
+        }
+
+        if (log.isDebugEnabled())
+            log.debug("Finish: Loading persisted sessions");
+    }
+
+
+    /**
+     * Save any currently active sessions in the appropriate persistence
+     * mechanism, if any.  If persistence is not supported, this method
+     * returns without doing anything.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void unload() throws IOException {
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                AccessController.doPrivileged( new PrivilegedDoUnload() );
+            } catch (PrivilegedActionException ex){
+                Exception exception = ex.getException();
+                if (exception instanceof IOException){
+                    throw (IOException)exception;
+                }
+                if (log.isDebugEnabled())
+                    log.debug("Unreported exception in unLoad() "
+                        + exception);
+            }
+        } else {
+            doUnload();
+        }
+    }
+
+
+    /**
+     * Save any currently active sessions in the appropriate persistence
+     * mechanism, if any.  If persistence is not supported, this method
+     * returns without doing anything.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected void doUnload() throws IOException {
+
+        if (log.isDebugEnabled())
+            log.debug("Unloading persisted sessions");
+
+        // Open an output stream to the specified pathname, if any
+        File file = file();
+        if (file == null)
+            return;
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("standardManager.unloading", pathname));
+        FileOutputStream fos = null;
+        ObjectOutputStream oos = null;
+        try {
+            fos = new FileOutputStream(file.getAbsolutePath());
+            oos = new ObjectOutputStream(new BufferedOutputStream(fos));
+        } catch (IOException e) {
+            log.error(sm.getString("standardManager.unloading.ioe", e), e);
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (IOException f) {
+                    ;
+                }
+                oos = null;
+            }
+            throw e;
+        }
+
+        // Write the number of active sessions, followed by the details
+        ArrayList list = new ArrayList();
+        synchronized (sessions) {
+            if (log.isDebugEnabled())
+                log.debug("Unloading " + sessions.size() + " sessions");
+            try {
+                oos.writeObject(new Integer(sessions.size()));
+                Iterator elements = sessions.values().iterator();
+                while (elements.hasNext()) {
+                    StandardSession session =
+                        (StandardSession) elements.next();
+                    list.add(session);
+                    ((StandardSession) session).passivate();
+                    session.writeObjectData(oos);
+                }
+            } catch (IOException e) {
+                log.error(sm.getString("standardManager.unloading.ioe", e), e);
+                if (oos != null) {
+                    try {
+                        oos.close();
+                    } catch (IOException f) {
+                        ;
+                    }
+                    oos = null;
+                }
+                throw e;
+            }
+        }
+
+        // Flush and close the output stream
+        try {
+            oos.flush();
+            oos.close();
+            oos = null;
+        } catch (IOException e) {
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (IOException f) {
+                    ;
+                }
+                oos = null;
+            }
+            throw e;
+        }
+
+        // Expire all the sessions we just wrote
+        if (log.isDebugEnabled())
+            log.debug("Expiring " + list.size() + " persisted sessions");
+        Iterator expires = list.iterator();
+        while (expires.hasNext()) {
+            StandardSession session = (StandardSession) expires.next();
+            try {
+                session.expire(false);
+            } catch (Throwable t) {
+                ;
+            } finally {
+                session.recycle();
+            }
+        }
+
+        if (log.isDebugEnabled())
+            log.debug("Unloading complete");
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        if( ! initialized )
+            init();
+
+        // Validate and update our current component state
+        if (started) {
+            return;
+        }
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Force initialization of the random number generator
+        if (log.isDebugEnabled())
+            log.debug("Force random number initialization starting");
+        String dummy = generateSessionId();
+        if (log.isDebugEnabled())
+            log.debug("Force random number initialization completed");
+
+        // Load unloaded sessions, if any
+        try {
+            load();
+        } catch (Throwable t) {
+            log.error(sm.getString("standardManager.managerLoad"), t);
+        }
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        if (log.isDebugEnabled())
+            log.debug("Stopping");
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("standardManager.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Write out sessions
+        try {
+            unload();
+        } catch (Throwable t) {
+            log.error(sm.getString("standardManager.managerUnload"), t);
+        }
+
+        // Expire all active sessions
+        Session sessions[] = findSessions();
+        for (int i = 0; i < sessions.length; i++) {
+            StandardSession session = (StandardSession) sessions[i];
+            try {
+                if (session.isValid()) {
+                    session.expire();
+                }
+            } catch (Throwable t) {
+                ;
+            } finally {
+                // Measure against memory leaking if references to the session
+                // object are kept in a shared field somewhere
+                session.recycle();
+            }
+        }
+
+        // Require a new random number generator if we are restarted
+        this.random = null;
+
+        if( initialized ) {
+            destroy();
+        }
+    }
+
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+
+    /**
+     * Process property change events from our associated Context.
+     *
+     * @param event The property change event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        // Validate the source of this event
+        if (!(event.getSource() instanceof Context))
+            return;
+        Context context = (Context) event.getSource();
+
+        // Process a relevant property change
+        if (event.getPropertyName().equals("sessionTimeout")) {
+            try {
+                setMaxInactiveInterval
+                    ( ((Integer) event.getNewValue()).intValue()*60 );
+            } catch (NumberFormatException e) {
+                log.error(sm.getString("standardManager.sessionTimeout",
+                                 event.getNewValue().toString()));
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return a File object representing the pathname to our
+     * persistence file, if any.
+     */
+    protected File file() {
+
+        if ((pathname == null) || (pathname.length() == 0))
+            return (null);
+        File file = new File(pathname);
+        if (!file.isAbsolute()) {
+            if (container instanceof Context) {
+                ServletContext servletContext =
+                    ((Context) container).getServletContext();
+                File tempdir = (File)
+                    servletContext.getAttribute(Globals.WORK_DIR_ATTR);
+                if (tempdir != null)
+                    file = new File(tempdir, pathname);
+            }
+        }
+//        if (!file.isAbsolute())
+//            return (null);
+        return (file);
+
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/session/StandardSession.java b/container/catalina/src/share/org/apache/catalina/session/StandardSession.java
new file mode 100644
index 0000000..ecaa98e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/StandardSession.java
@@ -0,0 +1,1712 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.session;
+
+
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.SessionListener;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.StringManager;
+
+import org.apache.catalina.security.SecurityUtil;
+
+/**
+ * Standard implementation of the <b>Session</b> interface.  This object is
+ * serializable, so that it can be stored in persistent storage or transferred
+ * to a different JVM for distributable session support.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b>:  An instance of this class represents both the
+ * internal (Session) and application level (HttpSession) view of the session.
+ * However, because the class itself is not declared public, Java logic outside
+ * of the <code>org.apache.catalina.session</code> package cannot cast an
+ * HttpSession view of this instance back to a Session view.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b>:  If you add fields to this class, you must
+ * make sure that you carry them over in the read/writeObject methods so
+ * that this class is properly serialized.
+ *
+ * @author Craig R. McClanahan
+ * @author Sean Legassick
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @version $Revision$ $Date$
+ */
+
+public class StandardSession
+    implements HttpSession, Session, Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new Session associated with the specified Manager.
+     *
+     * @param manager The manager with which this Session is associated
+     */
+    public StandardSession(Manager manager) {
+
+        super();
+        this.manager = manager;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Type array.
+     */
+    protected static final String EMPTY_ARRAY[] = new String[0];
+
+
+    /**
+     * The dummy attribute value serialized when a NotSerializableException is
+     * encountered in <code>writeObject()</code>.
+     */
+    protected static final String NOT_SERIALIZED =
+        "___NOT_SERIALIZABLE_EXCEPTION___";
+
+
+    /**
+     * The collection of user data attributes associated with this Session.
+     */
+    protected HashMap attributes = new HashMap();
+
+
+    /**
+     * The authentication type used to authenticate our cached Principal,
+     * if any.  NOTE:  This value is not included in the serialized
+     * version of this object.
+     */
+    protected transient String authType = null;
+
+
+    /**
+     * The <code>java.lang.Method</code> for the
+     * <code>fireContainerEvent()</code> method of the
+     * <code>org.apache.catalina.core.StandardContext</code> method,
+     * if our Context implementation is of this class.  This value is
+     * computed dynamically the first time it is needed, or after
+     * a session reload (since it is declared transient).
+     */
+    protected transient Method containerEventMethod = null;
+
+
+    /**
+     * The method signature for the <code>fireContainerEvent</code> method.
+     */
+    protected static final Class containerEventTypes[] =
+    { String.class, Object.class };
+
+
+    /**
+     * The time this session was created, in milliseconds since midnight,
+     * January 1, 1970 GMT.
+     */
+    protected long creationTime = 0L;
+
+
+    /**
+     * Set of attribute names which are not allowed to be persisted.
+     */
+    private static final String[] excludedAttributes = {
+        Globals.SUBJECT_ATTR
+    };
+
+
+    /**
+     * We are currently processing a session expiration, so bypass
+     * certain IllegalStateException tests.  NOTE:  This value is not
+     * included in the serialized version of this object.
+     */
+    protected transient boolean expiring = false;
+
+
+    /**
+     * The facade associated with this session.  NOTE:  This value is not
+     * included in the serialized version of this object.
+     */
+    protected transient StandardSessionFacade facade = null;
+
+
+    /**
+     * The session identifier of this Session.
+     */
+    protected String id = null;
+
+
+    /**
+     * Descriptive information describing this Session implementation.
+     */
+    protected static final String info = "StandardSession/1.0";
+
+
+    /**
+     * The last accessed time for this Session.
+     */
+    protected long lastAccessedTime = creationTime;
+
+
+    /**
+     * The session event listeners for this Session.
+     */
+    protected transient ArrayList listeners = new ArrayList();
+
+
+    /**
+     * The Manager with which this Session is associated.
+     */
+    protected transient Manager manager = null;
+
+
+    /**
+     * The maximum time interval, in seconds, between client requests before
+     * the servlet container may invalidate this session.  A negative time
+     * indicates that the session should never time out.
+     */
+    protected int maxInactiveInterval = -1;
+
+
+    /**
+     * Flag indicating whether this session is new or not.
+     */
+    protected boolean isNew = false;
+
+
+    /**
+     * Flag indicating whether this session is valid or not.
+     */
+    protected boolean isValid = false;
+
+    
+    /**
+     * Internal notes associated with this session by Catalina components
+     * and event listeners.  <b>IMPLEMENTATION NOTE:</b> This object is
+     * <em>not</em> saved and restored across session serializations!
+     */
+    protected transient HashMap notes = new HashMap();
+
+
+    /**
+     * The authenticated Principal associated with this session, if any.
+     * <b>IMPLEMENTATION NOTE:</b>  This object is <i>not</i> saved and
+     * restored across session serializations!
+     */
+    protected transient Principal principal = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The HTTP session context associated with this session.
+     */
+    protected static HttpSessionContext sessionContext = null;
+
+
+    /**
+     * The property change support for this component.  NOTE:  This value
+     * is not included in the serialized version of this object.
+     */
+    protected transient PropertyChangeSupport support =
+        new PropertyChangeSupport(this);
+
+
+    /**
+     * The current accessed time for this session.
+     */
+    protected long thisAccessedTime = creationTime;
+
+
+    /**
+     * The access count for this session.
+     */
+    protected transient int accessCount = 0;
+
+
+    // ----------------------------------------------------- Session Properties
+
+
+    /**
+     * Return the authentication type used to authenticate our cached
+     * Principal, if any.
+     */
+    public String getAuthType() {
+
+        return (this.authType);
+
+    }
+
+
+    /**
+     * Set the authentication type used to authenticate our cached
+     * Principal, if any.
+     *
+     * @param authType The new cached authentication type
+     */
+    public void setAuthType(String authType) {
+
+        String oldAuthType = this.authType;
+        this.authType = authType;
+        support.firePropertyChange("authType", oldAuthType, this.authType);
+
+    }
+
+
+    /**
+     * Set the creation time for this session.  This method is called by the
+     * Manager when an existing Session instance is reused.
+     *
+     * @param time The new creation time
+     */
+    public void setCreationTime(long time) {
+
+        this.creationTime = time;
+        this.lastAccessedTime = time;
+        this.thisAccessedTime = time;
+
+    }
+
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getId() {
+
+        if ( !isValid() ) {
+            throw new IllegalStateException
+            (sm.getString("standardSession.getId.ise"));
+        }
+
+        return (this.id);
+
+    }
+
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getIdInternal() {
+
+        return (this.id);
+
+    }
+
+
+    /**
+     * Set the session identifier for this session.
+     *
+     * @param id The new session identifier
+     */
+    public void setId(String id) {
+
+        if ((this.id != null) && (manager != null))
+            manager.remove(this);
+
+        this.id = id;
+
+        if (manager != null)
+            manager.add(this);
+        tellNew();
+    }
+
+
+    /**
+     * Inform the listeners about the new session.
+     *
+     */
+    public void tellNew() {
+
+        // Notify interested session event listeners
+        fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
+
+        // Notify interested application event listeners
+        Context context = (Context) manager.getContainer();
+        Object listeners[] = context.getApplicationLifecycleListeners();
+        if (listeners != null) {
+            HttpSessionEvent event =
+                new HttpSessionEvent(getSession());
+            for (int i = 0; i < listeners.length; i++) {
+                if (!(listeners[i] instanceof HttpSessionListener))
+                    continue;
+                HttpSessionListener listener =
+                    (HttpSessionListener) listeners[i];
+                try {
+                    fireContainerEvent(context,
+                                       "beforeSessionCreated",
+                                       listener);
+                    listener.sessionCreated(event);
+                    fireContainerEvent(context,
+                                       "afterSessionCreated",
+                                       listener);
+                } catch (Throwable t) {
+                    try {
+                        fireContainerEvent(context,
+                                           "afterSessionCreated",
+                                           listener);
+                    } catch (Exception e) {
+                        ;
+                    }
+                    manager.getContainer().getLogger().error
+                        (sm.getString("standardSession.sessionEvent"), t);
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Return descriptive information about this Session implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the last time the client sent a request associated with this
+     * session, as the number of milliseconds since midnight, January 1, 1970
+     * GMT.  Actions that your application takes, such as getting or setting
+     * a value associated with the session, do not affect the access time.
+     */
+    public long getLastAccessedTime() {
+
+         if ( !isValid() ) {
+             throw new IllegalStateException
+                 (sm.getString("standardSession.getLastAccessedTime.ise"));
+         }
+         return (this.lastAccessedTime);
+
+    }
+
+
+    /**
+     * Return the Manager within which this Session is valid.
+     */
+    public Manager getManager() {
+
+        return (this.manager);
+
+    }
+
+
+    /**
+     * Set the Manager within which this Session is valid.
+     *
+     * @param manager The new Manager
+     */
+    public void setManager(Manager manager) {
+
+        this.manager = manager;
+
+    }
+
+
+    /**
+     * Return the maximum time interval, in seconds, between client requests
+     * before the servlet container will invalidate the session.  A negative
+     * time indicates that the session should never time out.
+     */
+    public int getMaxInactiveInterval() {
+
+        return (this.maxInactiveInterval);
+
+    }
+
+
+    /**
+     * Set the maximum time interval, in seconds, between client requests
+     * before the servlet container will invalidate the session.  A negative
+     * time indicates that the session should never time out.
+     *
+     * @param interval The new maximum interval
+     */
+    public void setMaxInactiveInterval(int interval) {
+
+        this.maxInactiveInterval = interval;
+        if (isValid && interval == 0) {
+            expire();
+        }
+
+    }
+
+
+    /**
+     * Set the <code>isNew</code> flag for this session.
+     *
+     * @param isNew The new value for the <code>isNew</code> flag
+     */
+    public void setNew(boolean isNew) {
+
+        this.isNew = isNew;
+
+    }
+
+
+    /**
+     * Return the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.  If there
+     * is no current associated Principal, return <code>null</code>.
+     */
+    public Principal getPrincipal() {
+
+        return (this.principal);
+
+    }
+
+
+    /**
+     * Set the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.
+     *
+     * @param principal The new Principal, or <code>null</code> if none
+     */
+    public void setPrincipal(Principal principal) {
+
+        Principal oldPrincipal = this.principal;
+        this.principal = principal;
+        support.firePropertyChange("principal", oldPrincipal, this.principal);
+
+    }
+
+
+    /**
+     * Return the <code>HttpSession</code> for which this object
+     * is the facade.
+     */
+    public HttpSession getSession() {
+
+        if (facade == null){
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                final StandardSession fsession = this;
+                facade = (StandardSessionFacade)AccessController.doPrivileged(new PrivilegedAction(){
+                    public Object run(){
+                        return new StandardSessionFacade(fsession);
+                    }
+                });
+            } else {
+                facade = new StandardSessionFacade(this);
+            }
+        }
+        return (facade);
+
+    }
+
+
+    /**
+     * Return the <code>isValid</code> flag for this session.
+     */
+    public boolean isValid() {
+
+        if (this.expiring) {
+            return true;
+        }
+
+        if (!this.isValid ) {
+            return false;
+        }
+
+        if (accessCount > 0) {
+            return true;
+        }
+
+        if (maxInactiveInterval >= 0) { 
+            long timeNow = System.currentTimeMillis();
+            int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);
+            if (timeIdle >= maxInactiveInterval) {
+                expire(true);
+            }
+        }
+
+        return (this.isValid);
+    }
+
+
+    /**
+     * Set the <code>isValid</code> flag for this session.
+     *
+     * @param isValid The new value for the <code>isValid</code> flag
+     */
+    public void setValid(boolean isValid) {
+
+        this.isValid = isValid;
+    }
+
+
+    // ------------------------------------------------- Session Public Methods
+
+
+    /**
+     * Update the accessed time information for this session.  This method
+     * should be called by the context when a request comes in for a particular
+     * session, even if the application does not reference it.
+     */
+    public void access() {
+
+        this.lastAccessedTime = this.thisAccessedTime;
+        this.thisAccessedTime = System.currentTimeMillis();
+
+        evaluateIfValid();
+
+        accessCount++;
+
+    }
+
+
+    /**
+     * End the access.
+     */
+    public void endAccess() {
+
+        isNew = false;
+        accessCount--;
+
+    }
+
+
+    /**
+     * Add a session event listener to this component.
+     */
+    public void addSessionListener(SessionListener listener) {
+
+        listeners.add(listener);
+
+    }
+
+
+    /**
+     * Perform the internal processing required to invalidate this session,
+     * without triggering an exception if the session has already expired.
+     */
+    public void expire() {
+
+        expire(true);
+
+    }
+
+
+    /**
+     * Perform the internal processing required to invalidate this session,
+     * without triggering an exception if the session has already expired.
+     *
+     * @param notify Should we notify listeners about the demise of
+     *  this session?
+     */
+    public void expire(boolean notify) {
+
+        // Mark this session as "being expired" if needed
+        if (expiring)
+            return;
+
+        synchronized (this) {
+
+            if (manager == null)
+                return;
+
+            expiring = true;
+        
+            // Notify interested application event listeners
+            // FIXME - Assumes we call listeners in reverse order
+            Context context = (Context) manager.getContainer();
+            Object listeners[] = context.getApplicationLifecycleListeners();
+            if (notify && (listeners != null)) {
+                HttpSessionEvent event =
+                    new HttpSessionEvent(getSession());
+                for (int i = 0; i < listeners.length; i++) {
+                    int j = (listeners.length - 1) - i;
+                    if (!(listeners[j] instanceof HttpSessionListener))
+                        continue;
+                    HttpSessionListener listener =
+                        (HttpSessionListener) listeners[j];
+                    try {
+                        fireContainerEvent(context,
+                                           "beforeSessionDestroyed",
+                                           listener);
+                        listener.sessionDestroyed(event);
+                        fireContainerEvent(context,
+                                           "afterSessionDestroyed",
+                                           listener);
+                    } catch (Throwable t) {
+                        try {
+                            fireContainerEvent(context,
+                                               "afterSessionDestroyed",
+                                               listener);
+                        } catch (Exception e) {
+                            ;
+                        }
+                        manager.getContainer().getLogger().error
+                            (sm.getString("standardSession.sessionEvent"), t);
+                    }
+                }
+            }
+            accessCount = 0;
+            setValid(false);
+
+            /*
+             * Compute how long this session has been alive, and update
+             * session manager's related properties accordingly
+             */
+            long timeNow = System.currentTimeMillis();
+            int timeAlive = (int) ((timeNow - creationTime)/1000);
+            synchronized (manager) {
+                if (timeAlive > manager.getSessionMaxAliveTime()) {
+                    manager.setSessionMaxAliveTime(timeAlive);
+                }
+                int numExpired = manager.getExpiredSessions();
+                numExpired++;
+                manager.setExpiredSessions(numExpired);
+                int average = manager.getSessionAverageAliveTime();
+                average = ((average * (numExpired-1)) + timeAlive)/numExpired;
+                manager.setSessionAverageAliveTime(average);
+            }
+
+            // Remove this session from our manager's active sessions
+            manager.remove(this);
+
+            // Notify interested session event listeners
+            if (notify) {
+                fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
+            }
+
+            // We have completed expire of this session
+            expiring = false;
+
+            // Unbind any objects associated with this session
+            String keys[] = keys();
+            for (int i = 0; i < keys.length; i++)
+                removeAttributeInternal(keys[i], notify);
+
+        }
+
+    }
+
+
+    /**
+     * Perform the internal processing required to passivate
+     * this session.
+     */
+    public void passivate() {
+
+        // Notify interested session event listeners
+        fireSessionEvent(Session.SESSION_PASSIVATED_EVENT, null);
+
+        // Notify ActivationListeners
+        HttpSessionEvent event = null;
+        String keys[] = keys();
+        for (int i = 0; i < keys.length; i++) {
+            Object attribute = attributes.get(keys[i]);
+            if (attribute instanceof HttpSessionActivationListener) {
+                if (event == null)
+                    event = new HttpSessionEvent(getSession());
+                try {
+                    ((HttpSessionActivationListener)attribute)
+                        .sessionWillPassivate(event);
+                } catch (Throwable t) {
+                    manager.getContainer().getLogger().error
+                        (sm.getString("standardSession.attributeEvent"), t);
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Perform internal processing required to activate this
+     * session.
+     */
+    public void activate() {
+
+        // Notify interested session event listeners
+        fireSessionEvent(Session.SESSION_ACTIVATED_EVENT, null);
+
+        // Notify ActivationListeners
+        HttpSessionEvent event = null;
+        String keys[] = keys();
+        for (int i = 0; i < keys.length; i++) {
+            Object attribute = attributes.get(keys[i]);
+            if (attribute instanceof HttpSessionActivationListener) {
+                if (event == null)
+                    event = new HttpSessionEvent(getSession());
+                try {
+                    ((HttpSessionActivationListener)attribute)
+                        .sessionDidActivate(event);
+                } catch (Throwable t) {
+                    manager.getContainer().getLogger().error
+                        (sm.getString("standardSession.attributeEvent"), t);
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Return the object bound with the specified name to the internal notes
+     * for this session, or <code>null</code> if no such binding exists.
+     *
+     * @param name Name of the note to be returned
+     */
+    public Object getNote(String name) {
+
+        return (notes.get(name));
+
+    }
+
+
+    /**
+     * Return an Iterator containing the String names of all notes bindings
+     * that exist for this session.
+     */
+    public Iterator getNoteNames() {
+
+        return (notes.keySet().iterator());
+
+    }
+
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle() {
+
+        // Reset the instance variables associated with this Session
+        attributes.clear();
+        setAuthType(null);
+        creationTime = 0L;
+        expiring = false;
+        id = null;
+        lastAccessedTime = 0L;
+        maxInactiveInterval = -1;
+        accessCount = 0;
+        notes.clear();
+        setPrincipal(null);
+        isNew = false;
+        isValid = false;
+        manager = null;
+
+    }
+
+
+    /**
+     * Remove any object bound to the specified name in the internal notes
+     * for this session.
+     *
+     * @param name Name of the note to be removed
+     */
+    public void removeNote(String name) {
+
+        notes.remove(name);
+
+    }
+
+
+    /**
+     * Remove a session event listener from this component.
+     */
+    public void removeSessionListener(SessionListener listener) {
+
+        listeners.remove(listener);
+
+    }
+
+
+    /**
+     * Bind an object to a specified name in the internal notes associated
+     * with this session, replacing any existing binding for this name.
+     *
+     * @param name Name to which the object should be bound
+     * @param value Object to be bound to the specified name
+     */
+    public void setNote(String name, Object value) {
+
+        notes.put(name, value);
+
+    }
+
+
+    /**
+     * Return a string representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer();
+        sb.append("StandardSession[");
+        sb.append(id);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------ Session Package Methods
+
+
+    /**
+     * Read a serialized version of the contents of this session object from
+     * the specified object input stream, without requiring that the
+     * StandardSession itself have been serialized.
+     *
+     * @param stream The object input stream to read from
+     *
+     * @exception ClassNotFoundException if an unknown class is specified
+     * @exception IOException if an input/output error occurs
+     */
+    public void readObjectData(ObjectInputStream stream)
+        throws ClassNotFoundException, IOException {
+
+        readObject(stream);
+
+    }
+
+
+    /**
+     * Write a serialized version of the contents of this session object to
+     * the specified object output stream, without requiring that the
+     * StandardSession itself have been serialized.
+     *
+     * @param stream The object output stream to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void writeObjectData(ObjectOutputStream stream)
+        throws IOException {
+
+        writeObject(stream);
+
+    }
+
+
+    // ------------------------------------------------- HttpSession Properties
+
+
+    /**
+     * Return the time when this session was created, in milliseconds since
+     * midnight, January 1, 1970 GMT.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public long getCreationTime() {
+
+        if (!isValid())
+            throw new IllegalStateException
+                (sm.getString("standardSession.getCreationTime.ise"));
+
+        return (this.creationTime);
+
+    }
+
+
+    /**
+     * Return the ServletContext to which this session belongs.
+     */
+    public ServletContext getServletContext() {
+
+        if (manager == null)
+            return (null);
+        Context context = (Context) manager.getContainer();
+        if (context == null)
+            return (null);
+        else
+            return (context.getServletContext());
+
+    }
+
+
+    /**
+     * Return the session context with which this session is associated.
+     *
+     * @deprecated As of Version 2.1, this method is deprecated and has no
+     *  replacement.  It will be removed in a future version of the
+     *  Java Servlet API.
+     */
+    public HttpSessionContext getSessionContext() {
+
+        if (sessionContext == null)
+            sessionContext = new StandardSessionContext();
+        return (sessionContext);
+
+    }
+
+
+    // ----------------------------------------------HttpSession Public Methods
+
+
+    /**
+     * Return the object bound with the specified name in this session, or
+     * <code>null</code> if no object is bound with that name.
+     *
+     * @param name Name of the attribute to be returned
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public Object getAttribute(String name) {
+
+        if (!isValid())
+            throw new IllegalStateException
+                (sm.getString("standardSession.getAttribute.ise"));
+
+        return (attributes.get(name));
+
+    }
+
+
+    /**
+     * Return an <code>Enumeration</code> of <code>String</code> objects
+     * containing the names of the objects bound to this session.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public Enumeration getAttributeNames() {
+
+        if (!isValid())
+            throw new IllegalStateException
+                (sm.getString("standardSession.getAttributeNames.ise"));
+
+        return (new Enumerator(attributes.keySet(), true));
+
+    }
+
+
+    /**
+     * Return the object bound with the specified name in this session, or
+     * <code>null</code> if no object is bound with that name.
+     *
+     * @param name Name of the value to be returned
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     *
+     * @deprecated As of Version 2.2, this method is replaced by
+     *  <code>getAttribute()</code>
+     */
+    public Object getValue(String name) {
+
+        return (getAttribute(name));
+
+    }
+
+
+    /**
+     * Return the set of names of objects bound to this session.  If there
+     * are no such objects, a zero-length array is returned.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     *
+     * @deprecated As of Version 2.2, this method is replaced by
+     *  <code>getAttributeNames()</code>
+     */
+    public String[] getValueNames() {
+
+        if (!isValid())
+            throw new IllegalStateException
+                (sm.getString("standardSession.getValueNames.ise"));
+
+        return (keys());
+
+    }
+
+
+    /**
+     * Invalidates this session and unbinds any objects bound to it.
+     *
+     * @exception IllegalStateException if this method is called on
+     *  an invalidated session
+     */
+    public void invalidate() {
+
+        if (!isValid())
+            throw new IllegalStateException
+                (sm.getString("standardSession.invalidate.ise"));
+
+        // Cause this session to expire
+        expire();
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the client does not yet know about the
+     * session, or if the client chooses not to join the session.  For
+     * example, if the server used only cookie-based sessions, and the client
+     * has disabled the use of cookies, then a session would be new on each
+     * request.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public boolean isNew() {
+
+        if (!isValid())
+            throw new IllegalStateException
+                (sm.getString("standardSession.isNew.ise"));
+
+        return (this.isNew);
+
+    }
+
+
+
+    /**
+     * Bind an object to this session, using the specified name.  If an object
+     * of the same name is already bound to this session, the object is
+     * replaced.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueBound()</code> on the object.
+     *
+     * @param name Name to which the object is bound, cannot be null
+     * @param value Object to be bound, cannot be null
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     *
+     * @deprecated As of Version 2.2, this method is replaced by
+     *  <code>setAttribute()</code>
+     */
+    public void putValue(String name, Object value) {
+
+        setAttribute(name, value);
+
+    }
+
+
+    /**
+     * Remove the object bound with the specified name from this session.  If
+     * the session does not have an object bound with this name, this method
+     * does nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     *
+     * @param name Name of the object to remove from this session.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public void removeAttribute(String name) {
+
+        removeAttribute(name, true);
+
+    }
+
+
+    /**
+     * Remove the object bound with the specified name from this session.  If
+     * the session does not have an object bound with this name, this method
+     * does nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     *
+     * @param name Name of the object to remove from this session.
+     * @param notify Should we notify interested listeners that this
+     *  attribute is being removed?
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public void removeAttribute(String name, boolean notify) {
+
+        // Validate our current state
+        if (!isValid())
+            throw new IllegalStateException
+                (sm.getString("standardSession.removeAttribute.ise"));
+
+        removeAttributeInternal(name, notify);
+
+    }
+
+
+    /**
+     * Remove the object bound with the specified name from this session.  If
+     * the session does not have an object bound with this name, this method
+     * does nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     *
+     * @param name Name of the object to remove from this session.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     *
+     * @deprecated As of Version 2.2, this method is replaced by
+     *  <code>removeAttribute()</code>
+     */
+    public void removeValue(String name) {
+
+        removeAttribute(name);
+
+    }
+
+
+    /**
+     * Bind an object to this session, using the specified name.  If an object
+     * of the same name is already bound to this session, the object is
+     * replaced.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueBound()</code> on the object.
+     *
+     * @param name Name to which the object is bound, cannot be null
+     * @param value Object to be bound, cannot be null
+     *
+     * @exception IllegalArgumentException if an attempt is made to add a
+     *  non-serializable object in an environment marked distributable.
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public void setAttribute(String name, Object value) {
+
+        // Name cannot be null
+        if (name == null)
+            throw new IllegalArgumentException
+                (sm.getString("standardSession.setAttribute.namenull"));
+
+        // Null value is the same as removeAttribute()
+        if (value == null) {
+            removeAttribute(name);
+            return;
+        }
+
+        // Validate our current state
+        if (!isValid())
+            throw new IllegalStateException
+                (sm.getString("standardSession.setAttribute.ise"));
+        if ((manager != null) && manager.getDistributable() &&
+          !(value instanceof Serializable))
+            throw new IllegalArgumentException
+                (sm.getString("standardSession.setAttribute.iae"));
+
+        // Construct an event with the new value
+        HttpSessionBindingEvent event = null;
+
+        // Call the valueBound() method if necessary
+        if (value instanceof HttpSessionBindingListener) {
+            // Don't call any notification if replacing with the same value
+            Object oldValue = attributes.get(name);
+            if (value != oldValue) {
+                event = new HttpSessionBindingEvent(getSession(), name, value);
+                try {
+                    ((HttpSessionBindingListener) value).valueBound(event);
+                } catch (Throwable t){
+                    manager.getContainer().getLogger().error
+                    (sm.getString("standardSession.bindingEvent"), t); 
+                }
+            }
+        }
+
+        // Replace or add this attribute
+        Object unbound = null;
+        synchronized (attributes) {
+            unbound = attributes.put(name, value);
+        }
+
+        // Call the valueUnbound() method if necessary
+        if ((unbound != null) && (unbound != value) &&
+            (unbound instanceof HttpSessionBindingListener)) {
+            try {
+                ((HttpSessionBindingListener) unbound).valueUnbound
+                    (new HttpSessionBindingEvent(getSession(), name));
+            } catch (Throwable t) {
+                manager.getContainer().getLogger().error
+                    (sm.getString("standardSession.bindingEvent"), t);
+            }
+        }
+
+        // Notify interested application event listeners
+        Context context = (Context) manager.getContainer();
+        Object listeners[] = context.getApplicationEventListeners();
+        if (listeners == null)
+            return;
+        for (int i = 0; i < listeners.length; i++) {
+            if (!(listeners[i] instanceof HttpSessionAttributeListener))
+                continue;
+            HttpSessionAttributeListener listener =
+                (HttpSessionAttributeListener) listeners[i];
+            try {
+                if (unbound != null) {
+                    fireContainerEvent(context,
+                                       "beforeSessionAttributeReplaced",
+                                       listener);
+                    if (event == null) {
+                        event = new HttpSessionBindingEvent
+                            (getSession(), name, unbound);
+                    }
+                    listener.attributeReplaced(event);
+                    fireContainerEvent(context,
+                                       "afterSessionAttributeReplaced",
+                                       listener);
+                } else {
+                    fireContainerEvent(context,
+                                       "beforeSessionAttributeAdded",
+                                       listener);
+                    if (event == null) {
+                        event = new HttpSessionBindingEvent
+                            (getSession(), name, value);
+                    }
+                    listener.attributeAdded(event);
+                    fireContainerEvent(context,
+                                       "afterSessionAttributeAdded",
+                                       listener);
+                }
+            } catch (Throwable t) {
+                try {
+                    if (unbound != null) {
+                        fireContainerEvent(context,
+                                           "afterSessionAttributeReplaced",
+                                           listener);
+                    } else {
+                        fireContainerEvent(context,
+                                           "afterSessionAttributeAdded",
+                                           listener);
+                    }
+                } catch (Exception e) {
+                    ;
+                }
+                manager.getContainer().getLogger().error
+                    (sm.getString("standardSession.attributeEvent"), t);
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------ HttpSession Protected Methods
+
+
+    /**
+     * Read a serialized version of this session object from the specified
+     * object input stream.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  The reference to the owning Manager
+     * is not restored by this method, and must be set explicitly.
+     *
+     * @param stream The input stream to read from
+     *
+     * @exception ClassNotFoundException if an unknown class is specified
+     * @exception IOException if an input/output error occurs
+     */
+    private void readObject(ObjectInputStream stream)
+        throws ClassNotFoundException, IOException {
+
+        // Deserialize the scalar instance variables (except Manager)
+        authType = null;        // Transient only
+        creationTime = ((Long) stream.readObject()).longValue();
+        lastAccessedTime = ((Long) stream.readObject()).longValue();
+        maxInactiveInterval = ((Integer) stream.readObject()).intValue();
+        isNew = ((Boolean) stream.readObject()).booleanValue();
+        isValid = ((Boolean) stream.readObject()).booleanValue();
+        thisAccessedTime = ((Long) stream.readObject()).longValue();
+        principal = null;        // Transient only
+        //        setId((String) stream.readObject());
+        id = (String) stream.readObject();
+        if (manager.getContainer().getLogger().isDebugEnabled())
+            manager.getContainer().getLogger().debug
+                ("readObject() loading session " + id);
+
+        // Deserialize the attribute count and attribute values
+        if (attributes == null)
+            attributes = new HashMap();
+        int n = ((Integer) stream.readObject()).intValue();
+        boolean isValidSave = isValid;
+        isValid = true;
+        for (int i = 0; i < n; i++) {
+            String name = (String) stream.readObject();
+            Object value = (Object) stream.readObject();
+            if ((value instanceof String) && (value.equals(NOT_SERIALIZED)))
+                continue;
+            if (manager.getContainer().getLogger().isDebugEnabled())
+                manager.getContainer().getLogger().debug("  loading attribute '" + name +
+                    "' with value '" + value + "'");
+            synchronized (attributes) {
+                attributes.put(name, value);
+            }
+        }
+        isValid = isValidSave;
+
+        if (listeners == null) {
+            listeners = new ArrayList();
+        }
+
+        if (notes == null) {
+            notes = new HashMap();
+        }
+    }
+
+
+    /**
+     * Write a serialized version of this session object to the specified
+     * object output stream.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  The owning Manager will not be stored
+     * in the serialized representation of this Session.  After calling
+     * <code>readObject()</code>, you must set the associated Manager
+     * explicitly.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  Any attribute that is not Serializable
+     * will be unbound from the session, with appropriate actions if it
+     * implements HttpSessionBindingListener.  If you do not want any such
+     * attributes, be sure the <code>distributable</code> property of the
+     * associated Manager is set to <code>true</code>.
+     *
+     * @param stream The output stream to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+
+        // Write the scalar instance variables (except Manager)
+        stream.writeObject(new Long(creationTime));
+        stream.writeObject(new Long(lastAccessedTime));
+        stream.writeObject(new Integer(maxInactiveInterval));
+        stream.writeObject(new Boolean(isNew));
+        stream.writeObject(new Boolean(isValid));
+        stream.writeObject(new Long(thisAccessedTime));
+        stream.writeObject(id);
+        if (manager.getContainer().getLogger().isDebugEnabled())
+            manager.getContainer().getLogger().debug
+                ("writeObject() storing session " + id);
+
+        // Accumulate the names of serializable and non-serializable attributes
+        String keys[] = keys();
+        ArrayList saveNames = new ArrayList();
+        ArrayList saveValues = new ArrayList();
+        for (int i = 0; i < keys.length; i++) {
+            Object value = null;
+            synchronized (attributes) {
+                value = attributes.get(keys[i]);
+            }
+            if (value == null)
+                continue;
+            else if ( (value instanceof Serializable) 
+                    && (!exclude(keys[i]) )) {
+                saveNames.add(keys[i]);
+                saveValues.add(value);
+            } else {
+                removeAttributeInternal(keys[i], true);
+            }
+        }
+
+        // Serialize the attribute count and the Serializable attributes
+        int n = saveNames.size();
+        stream.writeObject(new Integer(n));
+        for (int i = 0; i < n; i++) {
+            stream.writeObject((String) saveNames.get(i));
+            try {
+                stream.writeObject(saveValues.get(i));
+                if (manager.getContainer().getLogger().isDebugEnabled())
+                    manager.getContainer().getLogger().debug
+                        ("  storing attribute '" + saveNames.get(i) +
+                        "' with value '" + saveValues.get(i) + "'");
+            } catch (NotSerializableException e) {
+                manager.getContainer().getLogger().warn
+                    (sm.getString("standardSession.notSerializable",
+                     saveNames.get(i), id), e);
+                stream.writeObject(NOT_SERIALIZED);
+                if (manager.getContainer().getLogger().isDebugEnabled())
+                    manager.getContainer().getLogger().debug
+                       ("  storing attribute '" + saveNames.get(i) +
+                        "' with value NOT_SERIALIZED");
+            }
+        }
+
+    }
+
+
+    /**
+     * Exclude attribute that cannot be serialized.
+     * @param name the attribute's name
+     */
+    protected boolean exclude(String name){
+
+        for (int i = 0; i < excludedAttributes.length; i++) {
+            if (name.equalsIgnoreCase(excludedAttributes[i]))
+                return true;
+        }
+
+        return false;
+    }
+
+
+    protected void evaluateIfValid() {
+        /*
+     * If this session has expired or is in the process of expiring or
+     * will never expire, return
+     */
+        if (!this.isValid || expiring || maxInactiveInterval < 0)
+            return;
+
+        isValid();
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Fire container events if the Context implementation is the
+     * <code>org.apache.catalina.core.StandardContext</code>.
+     *
+     * @param context Context for which to fire events
+     * @param type Event type
+     * @param data Event data
+     *
+     * @exception Exception occurred during event firing
+     */
+    protected void fireContainerEvent(Context context,
+                                    String type, Object data)
+        throws Exception {
+
+        if (!"org.apache.catalina.core.StandardContext".equals
+            (context.getClass().getName())) {
+            return; // Container events are not supported
+        }
+        // NOTE:  Race condition is harmless, so do not synchronize
+        if (containerEventMethod == null) {
+            containerEventMethod =
+                context.getClass().getMethod("fireContainerEvent",
+                                             containerEventTypes);
+        }
+        Object containerEventParams[] = new Object[2];
+        containerEventParams[0] = type;
+        containerEventParams[1] = data;
+        containerEventMethod.invoke(context, containerEventParams);
+
+    }
+                                      
+
+
+    /**
+     * Notify all session event listeners that a particular event has
+     * occurred for this Session.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param data Event data
+     */
+    public void fireSessionEvent(String type, Object data) {
+        if (listeners.size() < 1)
+            return;
+        SessionEvent event = new SessionEvent(this, type, data);
+        SessionListener list[] = new SessionListener[0];
+        synchronized (listeners) {
+            list = (SessionListener[]) listeners.toArray(list);
+        }
+
+        for (int i = 0; i < list.length; i++){
+            ((SessionListener) list[i]).sessionEvent(event);
+        }
+
+    }
+
+
+    /**
+     * Return the names of all currently defined session attributes
+     * as an array of Strings.  If there are no defined attributes, a
+     * zero-length array is returned.
+     */
+    protected String[] keys() {
+
+        return ((String[]) attributes.keySet().toArray(EMPTY_ARRAY));
+
+    }
+
+
+    /**
+     * Remove the object bound with the specified name from this session.  If
+     * the session does not have an object bound with this name, this method
+     * does nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     *
+     * @param name Name of the object to remove from this session.
+     * @param notify Should we notify interested listeners that this
+     *  attribute is being removed?
+     */
+    protected void removeAttributeInternal(String name, boolean notify) {
+
+        // Remove this attribute from our collection
+        Object value = null;
+        synchronized (attributes) {
+            value = attributes.remove(name);
+        }
+
+        // Do we need to do valueUnbound() and attributeRemoved() notification?
+        if (!notify || (value == null)) {
+            return;
+        }
+
+        // Call the valueUnbound() method if necessary
+        HttpSessionBindingEvent event = null;
+        if (value instanceof HttpSessionBindingListener) {
+            event = new HttpSessionBindingEvent(getSession(), name, value);
+            ((HttpSessionBindingListener) value).valueUnbound(event);
+        }
+
+        // Notify interested application event listeners
+        Context context = (Context) manager.getContainer();
+        Object listeners[] = context.getApplicationEventListeners();
+        if (listeners == null)
+            return;
+        for (int i = 0; i < listeners.length; i++) {
+            if (!(listeners[i] instanceof HttpSessionAttributeListener))
+                continue;
+            HttpSessionAttributeListener listener =
+                (HttpSessionAttributeListener) listeners[i];
+            try {
+                fireContainerEvent(context,
+                                   "beforeSessionAttributeRemoved",
+                                   listener);
+                if (event == null) {
+                    event = new HttpSessionBindingEvent
+                        (getSession(), name, value);
+                }
+                listener.attributeRemoved(event);
+                fireContainerEvent(context,
+                                   "afterSessionAttributeRemoved",
+                                   listener);
+            } catch (Throwable t) {
+                try {
+                    fireContainerEvent(context,
+                                       "afterSessionAttributeRemoved",
+                                       listener);
+                } catch (Exception e) {
+                    ;
+                }
+                manager.getContainer().getLogger().error
+                    (sm.getString("standardSession.attributeEvent"), t);
+            }
+        }
+
+    }
+
+
+}
+
+
+// ------------------------------------------------------------ Protected Class
+
+
+/**
+ * This class is a dummy implementation of the <code>HttpSessionContext</code>
+ * interface, to conform to the requirement that such an object be returned
+ * when <code>HttpSession.getSessionContext()</code> is called.
+ *
+ * @author Craig R. McClanahan
+ *
+ * @deprecated As of Java Servlet API 2.1 with no replacement.  The
+ *  interface will be removed in a future version of this API.
+ */
+
+final class StandardSessionContext implements HttpSessionContext {
+
+
+    protected HashMap dummy = new HashMap();
+
+    /**
+     * Return the session identifiers of all sessions defined
+     * within this context.
+     *
+     * @deprecated As of Java Servlet API 2.1 with no replacement.
+     *  This method must return an empty <code>Enumeration</code>
+     *  and will be removed in a future version of the API.
+     */
+    public Enumeration getIds() {
+
+        return (new Enumerator(dummy));
+
+    }
+
+
+    /**
+     * Return the <code>HttpSession</code> associated with the
+     * specified session identifier.
+     *
+     * @param id Session identifier for which to look up a session
+     *
+     * @deprecated As of Java Servlet API 2.1 with no replacement.
+     *  This method must return null and will be removed in a
+     *  future version of the API.
+     */
+    public HttpSession getSession(String id) {
+
+        return (null);
+
+    }
+
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/session/StandardSessionFacade.java b/container/catalina/src/share/org/apache/catalina/session/StandardSessionFacade.java
new file mode 100644
index 0000000..de36e23
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/StandardSessionFacade.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.session;
+
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+
+
+/**
+ * Facade for the StandardSession object.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class StandardSessionFacade
+    implements HttpSession {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new session facade.
+     */
+    public StandardSessionFacade(StandardSession session) {
+        super();
+        this.session = (HttpSession) session;
+    }
+
+
+    /**
+     * Construct a new session facade.
+     */
+    public StandardSessionFacade(HttpSession session) {
+        super();
+        this.session = session;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Wrapped session object.
+     */
+    private HttpSession session = null;
+
+
+    // ---------------------------------------------------- HttpSession Methods
+
+
+    public long getCreationTime() {
+        return session.getCreationTime();
+    }
+
+
+    public String getId() {
+        return session.getId();
+    }
+
+
+    public long getLastAccessedTime() {
+        return session.getLastAccessedTime();
+    }
+
+
+    public ServletContext getServletContext() {
+        // FIXME : Facade this object ?
+        return session.getServletContext();
+    }
+
+
+    public void setMaxInactiveInterval(int interval) {
+        session.setMaxInactiveInterval(interval);
+    }
+
+
+    public int getMaxInactiveInterval() {
+        return session.getMaxInactiveInterval();
+    }
+
+
+    public HttpSessionContext getSessionContext() {
+        return session.getSessionContext();
+    }
+
+
+    public Object getAttribute(String name) {
+        return session.getAttribute(name);
+    }
+
+
+    public Object getValue(String name) {
+        return session.getAttribute(name);
+    }
+
+
+    public Enumeration getAttributeNames() {
+        return session.getAttributeNames();
+    }
+
+
+    public String[] getValueNames() {
+        return session.getValueNames();
+    }
+
+
+    public void setAttribute(String name, Object value) {
+        session.setAttribute(name, value);
+    }
+
+
+    public void putValue(String name, Object value) {
+        session.setAttribute(name, value);
+    }
+
+
+    public void removeAttribute(String name) {
+        session.removeAttribute(name);
+    }
+
+
+    public void removeValue(String name) {
+        session.removeAttribute(name);
+    }
+
+
+    public void invalidate() {
+        session.invalidate();
+    }
+
+
+    public boolean isNew() {
+        return session.isNew();
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/session/StoreBase.java b/container/catalina/src/share/org/apache/catalina/session/StoreBase.java
new file mode 100644
index 0000000..196fbb8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/StoreBase.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Store;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * Abstract implementation of the Store interface to
+ * support most of the functionality required by a Store.
+ *
+ * @author Bip Thelin
+ * @version $Revision$, $Date$
+ */
+
+public abstract class StoreBase
+    implements Lifecycle, Store {
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static String info = "StoreBase/1.0";
+
+    /**
+     * Name to register for this Store, used for logging.
+     */
+    protected static String storeName = "StoreBase";
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+    /**
+     * The Manager with which this JDBCStore is associated.
+     */
+    protected Manager manager;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the info for this Store.
+     */
+    public String getInfo() {
+        return(info);
+    }
+
+
+    /**
+     * Return the name for this Store, used for logging.
+     */
+    public String getStoreName() {
+        return(storeName);
+    }
+
+
+    /**
+     * Set the Manager with which this Store is associated.
+     *
+     * @param manager The newly associated Manager
+     */
+    public void setManager(Manager manager) {
+        Manager oldManager = this.manager;
+        this.manager = manager;
+        support.firePropertyChange("manager", oldManager, this.manager);
+    }
+
+    /**
+     * Return the Manager with which the Store is associated.
+     */
+    public Manager getManager() {
+        return(this.manager);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener a value of type 'PropertyChangeListener'
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        support.addPropertyChangeListener(listener);
+    }
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        support.removePropertyChangeListener(listener);
+    }
+
+    // --------------------------------------------------------- Protected Methods
+
+    /**
+     * Called by our background reaper thread to check if Sessions
+     * saved in our store are subject of being expired. If so expire
+     * the Session and remove it from the Store.
+     *
+     */
+    public void processExpires() {
+        long timeNow = System.currentTimeMillis();
+        String[] keys = null;
+
+         if(!started) {
+            return;
+        }
+
+        try {
+            keys = keys();
+        } catch (IOException e) {
+            manager.getContainer().getLogger().error("Error getting keys", e);
+            return;
+        }
+        if (manager.getContainer().getLogger().isDebugEnabled()) {
+            manager.getContainer().getLogger().debug(getStoreName()+ ": processExpires check number of " + keys.length + " sessions" );
+        }
+    
+        for (int i = 0; i < keys.length; i++) {
+            try {
+                StandardSession session = (StandardSession) load(keys[i]);
+                if (session == null) {
+                    continue;
+                }
+                if (session.isValid()) {
+                    continue;
+                }
+                if (manager.getContainer().getLogger().isDebugEnabled()) {
+                    manager.getContainer().getLogger().debug(getStoreName()+ ": processExpires expire store session " + keys[i] );
+                }
+                if ( ( (PersistentManagerBase) manager).isLoaded( keys[i] )) {
+                    // recycle old backup session
+                    session.recycle();
+                } else {
+                    // expire swapped out session
+                    session.expire();
+                }
+                remove(session.getIdInternal());
+            } catch (Exception e) {
+                manager.getContainer().getLogger().error("Session: "+keys[i]+"; ", e);
+                try {
+                    remove(keys[i]);
+                } catch (IOException e2) {
+                    manager.getContainer().getLogger().error("Error removing key", e2);
+                }
+            }
+        }
+    }
+
+
+    // --------------------------------------------------------- Thread Methods
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (sm.getString(getStoreName()+".alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString(getStoreName()+".notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/session/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/session/mbeans-descriptors.xml
new file mode 100644
index 0000000..aa35352
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/mbeans-descriptors.xml
@@ -0,0 +1,292 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean         name="StandardManager"
+          description="Standard implementation of the Manager interface"
+               domain="Catalina"
+                group="Manager"
+                 type="org.apache.catalina.session.StandardManager">
+
+    <attribute   name="algorithm"
+          description="The message digest algorithm to be used when generating
+                       session identifiers"
+                 type="java.lang.String"/>
+
+
+    <attribute   name="randomFile"
+          description="File source of random - /dev/urandom or a pipe"
+                 type="java.lang.String"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="distributable"
+          description="The distributable flag for Sessions created by this
+                       Manager"
+                 type="boolean"/>
+
+    <attribute   name="entropy"
+          description="A String initialization parameter used to increase the
+                       entropy of the initialization of our random number
+                       generator"
+                 type="java.lang.String"/>
+
+    <attribute   name="maxActiveSessions"
+          description="The maximum number of active Sessions allowed, or -1
+                       for no limit"
+                 type="int"/>
+
+    <attribute   name="maxInactiveInterval"
+          description="The default maximum inactive interval for Sessions
+                       created by this Manager"
+                 type="int"/>
+
+    <attribute name="processExpiresFrequency"
+               description="The frequency of the manager checks (expiration and passivation)"
+               type="int"/>
+               
+    <attribute   name="sessionIdLength"
+          description="The session id length (in bytes) of Sessions
+                       created by this Manager"
+                 type="int"/>
+
+    <attribute   name="name"
+          description="The descriptive name of this Manager implementation
+                       (for logging)"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="pathname"
+          description="Path name of the disk file in which active sessions"
+                 type="java.lang.String"/>
+
+    <attribute   name="activeSessions"
+          description="Number of active sessions at this moment"
+                 type="int" 
+            writeable="false"/>
+
+    <attribute   name="sessionCounter"
+          description="Total number of sessions created by this manager"
+                 type="int" />
+
+    <attribute   name="maxActive"
+          description="Maximum number of active sessions so far"
+                 type="int" />
+
+    <attribute   name="sessionMaxAliveTime"
+          description="Longest time an expired session had been alive"
+                 type="int" />
+
+    <attribute   name="sessionAverageAliveTime"
+          description="Average time an expired session had been alive"
+                 type="int" />
+
+    <attribute   name="rejectedSessions"
+          description="Number of sessions we rejected due to maxActive beeing reached"
+                 type="int" />
+
+    <attribute   name="expiredSessions"
+          description="Number of sessions that expired ( doesn't include explicit invalidations )"
+                 type="int" />
+
+    <attribute   name="processingTime"
+          description="Time spent doing housekeeping and expiration"
+                 type="long" />
+
+    <attribute   name="duplicates"
+          description="Number of duplicated session ids generated"
+                 type="int" />
+
+    <operation   name="listSessionIds"
+          description="Return the list of active session ids"
+               impact="ACTION"
+           returnType="java.lang.String">
+    </operation>
+
+    <operation   name="getSessionAttribute"
+          description="Return a session attribute"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+      <parameter name="key"
+          description="key of the attribute"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="expireSession"
+          description="Expire a session"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="getLastAccessedTime"
+          description="Get the last access time"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+
+  <mbean         name="PersistentManager"
+          description="Persistent Manager"
+               domain="Catalina"
+                group="Manager"
+                 type="org.apache.catalina.session.PersistentManager">
+
+    <attribute   name="algorithm"
+          description="The message digest algorithm to be used when generating
+                       session identifiers"
+                 type="java.lang.String"/>
+
+    <attribute   name="randomFile"
+          description="File source of random - /dev/urandom or a pipe"
+                 type="java.lang.String"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="distributable"
+          description="The distributable flag for Sessions created by this
+                       Manager"
+                 type="boolean"/>
+
+    <attribute   name="entropy"
+          description="A String initialization parameter used to increase the
+                       entropy of the initialization of our random number
+                       generator"
+                 type="java.lang.String"/>
+ 
+    <attribute   name="managedResource"
+          description="The managed resource this MBean is associated with"
+                 type="java.lang.Object"/>
+
+    <attribute   name="maxActiveSessions"
+          description="The maximum number of active Sessions allowed, or -1
+                       for no limit"
+                 type="int"/>
+
+    <attribute   name="maxInactiveInterval"
+          description="The default maximum inactive interval for Sessions
+                       created by this Manager"
+                 type="int"/>
+                 
+    <attribute name="processExpiresFrequency"
+               description="The frequency of the manager checks (expiration and passivation)"
+               type="int"/>
+               
+    <attribute   name="sessionIdLength"
+          description="The session id length (in bytes) of Sessions
+                       created by this Manager"
+                 type="int"/>
+
+    <attribute   name="name"
+          description="The descriptive name of this Manager implementation
+                       (for logging)"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="pathname"
+          description="Path name of the disk file in which active sessions"
+                 type="java.lang.String"/>
+
+    <attribute   name="activeSessions"
+          description="Number of active sessions at this moment"
+                 type="int" 
+            writeable="false"/>
+
+    <attribute   name="sessionCounter"
+          description="Total number of sessions created by this manager"
+                 type="int" />
+
+    <attribute   name="maxActive"
+          description="Maximum number of active sessions so far"
+                 type="int" />
+
+    <attribute   name="maxIdleBackup"
+          description="How long a session must be idle before it should be backed up"
+                 type="int" />
+
+    <attribute   name="minIdleSwap"
+          description="Minimum time a session must be idle before it is swapped to disk"
+                 type="int" />
+
+    <attribute   name="maxIdleSwap"
+          description="The maximum time a session may be idle before it should be swapped to file just on general principle"
+                 type="int" />
+
+    <attribute   name="rejectedSessions"
+          description="Number of sessions we rejected due to maxActive beeing reached"
+                 type="int" />
+
+    <attribute   name="expiredSessions"
+          description="Number of sessions that expired ( doesn't include explicit invalidations )"
+                 type="int" />
+
+    <attribute   name="processingTime"
+          description="Time spent doing housekeeping and expiration"
+                 type="long" />
+
+    <attribute   name="duplicates"
+          description="Number of duplicated session ids generated"
+                 type="int" />
+
+    <operation   name="listSessionIds"
+          description="Return the list of active session ids"
+               impact="ACTION"
+           returnType="java.lang.String">
+    </operation>
+
+    <operation   name="getSessionAttribute"
+          description="Return a session attribute"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+      <parameter name="key"
+          description="key of the attribute"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="getSession"
+          description="Get information about a session"
+               impact="ACTION"
+           returnType="java.util.HashMap">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="expireSession"
+          description="Expire a session"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="getLastAccessedTime"
+          description="Get the last access time"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/session/package.html b/container/catalina/src/share/org/apache/catalina/session/package.html
new file mode 100644
index 0000000..3c3d9a8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/session/package.html
@@ -0,0 +1,52 @@
+<body>
+
+<p>This package contains the standard <code>Manager</code> and
+<code>Session</code> implementations that represent the collection of
+active sessions and the individual sessions themselves, respectively,
+that are associated with a <code>Context</code>.  Additional implementations
+of the <code>Manager</code> interface can be based upon the supplied
+convenience base class (<code>ManagerBase</code>), if desired.  Different
+implementations of <code>Session</code> are possible, but a need for
+functionality beyond what is provided by the standard implementation
+(<code>StandardSession</code>) is not expected.</p>
+
+<p>The convenience <code>ManagerBase</code> base class is configured by
+setting the following properties:</p>
+<ul>
+<li><b>algorithm</b> - Message digest algorithm to be used when
+    generating session identifiers.  This must be the name of an
+    algorithm supported by the <code>java.security.MessageDigest</code>
+    class on your platform.  [DEFAULT_ALGORITHM]</li>
+<li><b>debug</b> - Debugging detail level for this component. [0]</li>
+<li><b>distributable</b> - Has the web application we are associated with
+    been marked as "distributable"?  If it has, attempts to add or replace
+    a session attribute object that does not implement the
+    <code>java.io.Serializable</code> interface will be rejected.
+    [false]</li>
+<li><b>entropy</b> - A string initialization parameter that is used to
+    increase the entropy of the seeding of the random number generator
+    used in creation of session identifiers.  [NONE]</li>
+<li><b>maxInactiveInterval</b> - The default maximum inactive interval,
+    in minutes, for sessions created by this Manager.  The standard
+    implementation automatically updates this value based on the configuration
+    settings in the web application deployment descriptor.  [60]</li>
+<li><b>randomClass</b> - The Java class name of the random number generator
+    to be used when creating session identifiers for this Manager.
+    [java.security.SecureRandom]</li>
+</ul>
+
+<p>The standard implementation of the <code>Manager</code> interface
+(<code>StandardManager</code>) supports the following additional configuration
+properties:</p>
+<ul>
+<li><b>checkInterval</b> - The interval, in seconds, between checks for
+    sessions that have expired and should be invalidated.  [60]</li>
+<li><b>maxActiveSessions</b> - The maximum number of active sessions that
+    will be allowed, or -1 for no limit.  [-1]</li>
+<li><b>pathname</b> - Pathname to the file that is used to store session
+    data persistently across container restarts.  If this pathname is relative,
+    it is resolved against the temporary working directory provided by our
+    associated Context, if any.  ["sessions.ser"]</li>
+</ul>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/ByteArrayServletOutputStream.java b/container/catalina/src/share/org/apache/catalina/ssi/ByteArrayServletOutputStream.java
new file mode 100644
index 0000000..2f46f6d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/ByteArrayServletOutputStream.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999-2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+import java.io.ByteArrayOutputStream;
+import javax.servlet.ServletOutputStream;
+
+
+/**
+ * Class that extends ServletOuputStream, used as a wrapper from within
+ * <code>SsiInclude</code>
+ *
+ * @author Bip Thelin
+ * @version $Revision$, $Date$
+ * @see ServletOutputStream and ByteArrayOutputStream
+ */
+public class ByteArrayServletOutputStream extends ServletOutputStream {
+    /**
+     * Our buffer to hold the stream.
+     */
+    protected ByteArrayOutputStream buf = null;
+
+
+    /**
+     * Construct a new ServletOutputStream.
+     */
+    public ByteArrayServletOutputStream() {
+        buf = new ByteArrayOutputStream();
+    }
+
+
+    /**
+     * @return the byte array.
+     */
+    public byte[] toByteArray() {
+        return buf.toByteArray();
+    }
+
+
+    /**
+     * Write to our buffer.
+     *
+     * @param b The parameter to write
+     */
+    public void write(int b) {
+        buf.write(b);
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/ExpressionParseTree.java b/container/catalina/src/share/org/apache/catalina/ssi/ExpressionParseTree.java
new file mode 100644
index 0000000..6328f43
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/ExpressionParseTree.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.text.ParseException;
+import java.util.LinkedList;
+import java.util.List;
+/**
+ * Represents a parsed expression.
+ * 
+ * @version $Revision$
+ * @author Paul Speed
+ */
+public class ExpressionParseTree {
+    /**
+     * Contains the current set of completed nodes. This is a workspace for the
+     * parser.
+     */
+    private LinkedList nodeStack = new LinkedList();
+    /**
+     * Contains operator nodes that don't yet have values. This is a workspace
+     * for the parser.
+     */
+    private LinkedList oppStack = new LinkedList();
+    /**
+     * The root node after the expression has been parsed.
+     */
+    private Node root;
+    /**
+     * The SSIMediator to use when evaluating the expressions.
+     */
+    private SSIMediator ssiMediator;
+
+
+    /**
+     * Creates a new parse tree for the specified expression.
+     */
+    public ExpressionParseTree(String expr, SSIMediator ssiMediator)
+            throws ParseException {
+        this.ssiMediator = ssiMediator;
+        parseExpression(expr);
+    }
+
+
+    /**
+     * Evaluates the tree and returns true or false. The specified SSIMediator
+     * is used to resolve variable references.
+     */
+    public boolean evaluateTree() {
+        return root.evaluate();
+    }
+
+
+    /**
+     * Pushes a new operator onto the opp stack, resolving existing opps as
+     * needed.
+     */
+    private void pushOpp(OppNode node) {
+        // If node is null then it's just a group marker
+        if (node == null) {
+            oppStack.add(0, node);
+            return;
+        }
+        while (true) {
+            if (oppStack.size() == 0) break;
+            OppNode top = (OppNode)oppStack.get(0);
+            // If the top is a spacer then don't pop
+            // anything
+            if (top == null) break;
+            // If the top node has a lower precedence then
+            // let it stay
+            if (top.getPrecedence() < node.getPrecedence()) break;
+            // Remove the top node
+            oppStack.remove(0);
+            // Let it fill its branches
+            top.popValues(nodeStack);
+            // Stick it on the resolved node stack
+            nodeStack.add(0, top);
+        }
+        // Add the new node to the opp stack
+        oppStack.add(0, node);
+    }
+
+
+    /**
+     * Resolves all pending opp nodes on the stack until the next group marker
+     * is reached.
+     */
+    private void resolveGroup() {
+        OppNode top = null;
+        while ((top = (OppNode)oppStack.remove(0)) != null) {
+            // Let it fill its branches
+            top.popValues(nodeStack);
+            // Stick it on the resolved node stack
+            nodeStack.add(0, top);
+        }
+    }
+
+
+    /**
+     * Parses the specified expression into a tree of parse nodes.
+     */
+    private void parseExpression(String expr) throws ParseException {
+        StringNode currStringNode = null;
+        // We cheat a little and start an artificial
+        // group right away. It makes finishing easier.
+        pushOpp(null);
+        ExpressionTokenizer et = new ExpressionTokenizer(expr);
+        while (et.hasMoreTokens()) {
+            int token = et.nextToken();
+            if (token != ExpressionTokenizer.TOKEN_STRING)
+                currStringNode = null;
+            switch (token) {
+                case ExpressionTokenizer.TOKEN_STRING :
+                    if (currStringNode == null) {
+                        currStringNode = new StringNode(et.getTokenValue());
+                        nodeStack.add(0, currStringNode);
+                    } else {
+                        // Add to the existing
+                        currStringNode.value.append(" ");
+                        currStringNode.value.append(et.getTokenValue());
+                    }
+                    break;
+                case ExpressionTokenizer.TOKEN_AND :
+                    pushOpp(new AndNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_OR :
+                    pushOpp(new OrNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_NOT :
+                    pushOpp(new NotNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_EQ :
+                    pushOpp(new EqualNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_NOT_EQ :
+                    pushOpp(new NotNode());
+                    // Sneak the regular node in. The NOT will
+                    // be resolved when the next opp comes along.
+                    oppStack.add(0, new EqualNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_RBRACE :
+                    // Closeout the current group
+                    resolveGroup();
+                    break;
+                case ExpressionTokenizer.TOKEN_LBRACE :
+                    // Push a group marker
+                    pushOpp(null);
+                    break;
+                case ExpressionTokenizer.TOKEN_GE :
+                    pushOpp(new NotNode());
+                    // Similar stategy to NOT_EQ above, except this
+                    // is NOT less than
+                    oppStack.add(0, new LessThanNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_LE :
+                    pushOpp(new NotNode());
+                    // Similar stategy to NOT_EQ above, except this
+                    // is NOT greater than
+                    oppStack.add(0, new GreaterThanNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_GT :
+                    pushOpp(new GreaterThanNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_LT :
+                    pushOpp(new LessThanNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_END :
+                    break;
+            }
+        }
+        // Finish off the rest of the opps
+        resolveGroup();
+        if (nodeStack.size() == 0) {
+            throw new ParseException("No nodes created.", et.getIndex());
+        }
+        if (nodeStack.size() > 1) {
+            throw new ParseException("Extra nodes created.", et.getIndex());
+        }
+        if (oppStack.size() != 0) {
+            throw new ParseException("Unused opp nodes exist.", et.getIndex());
+        }
+        root = (Node)nodeStack.get(0);
+    }
+
+    /**
+     * A node in the expression parse tree.
+     */
+    private abstract class Node {
+        /**
+         * Return true if the node evaluates to true.
+         */
+        public abstract boolean evaluate();
+    }
+    /**
+     * A node the represents a String value
+     */
+    private class StringNode extends Node {
+        StringBuffer value;
+        String resolved = null;
+
+
+        public StringNode(String value) {
+            this.value = new StringBuffer(value);
+        }
+
+
+        /**
+         * Resolves any variable references and returns the value string.
+         */
+        public String getValue() {
+            if (resolved == null)
+                resolved = ssiMediator.substituteVariables(value.toString());
+            return resolved;
+        }
+
+
+        /**
+         * Returns true if the string is not empty.
+         */
+        public boolean evaluate() {
+            return !(getValue().length() == 0);
+        }
+
+
+        public String toString() {
+            return value.toString();
+        }
+    }
+
+    private static final int PRECEDENCE_NOT = 5;
+    private static final int PRECEDENCE_COMPARE = 4;
+    private static final int PRECEDENCE_LOGICAL = 1;
+
+    /**
+     * A node implementation that represents an operation.
+     */
+    private abstract class OppNode extends Node {
+        /**
+         * The left branch.
+         */
+        Node left;
+        /**
+         * The right branch.
+         */
+        Node right;
+
+
+        /**
+         * Returns a preference level suitable for comparison to other OppNode
+         * preference levels.
+         */
+        public abstract int getPrecedence();
+
+
+        /**
+         * Lets the node pop its own branch nodes off the front of the
+         * specified list. The default pulls two.
+         */
+        public void popValues(List values) {
+            right = (Node)values.remove(0);
+            left = (Node)values.remove(0);
+        }
+    }
+    private final class NotNode extends OppNode {
+        public boolean evaluate() {
+            return !left.evaluate();
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_NOT;
+        }
+
+
+        /**
+         * Overridden to pop only one value.
+         */
+        public void popValues(List values) {
+            left = (Node)values.remove(0);
+        }
+
+
+        public String toString() {
+            return left + " NOT";
+        }
+    }
+    private final class AndNode extends OppNode {
+        public boolean evaluate() {
+            if (!left.evaluate()) // Short circuit
+                return false;
+            return right.evaluate();
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_LOGICAL;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " AND";
+        }
+    }
+    private final class OrNode extends OppNode {
+        public boolean evaluate() {
+            if (left.evaluate()) // Short circuit
+                return true;
+            return right.evaluate();
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_LOGICAL;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " OR";
+        }
+    }
+    private abstract class CompareNode extends OppNode {
+        protected int compareBranches() {
+            String val1 = ((StringNode)left).getValue();
+            String val2 = ((StringNode)right).getValue();
+            return val1.compareTo(val2);
+        }
+    }
+    private final class EqualNode extends CompareNode {
+        public boolean evaluate() {
+            return (compareBranches() == 0);
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_COMPARE;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " EQ";
+        }
+    }
+    private final class GreaterThanNode extends CompareNode {
+        public boolean evaluate() {
+            return (compareBranches() > 0);
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_COMPARE;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " GT";
+        }
+    }
+    private final class LessThanNode extends CompareNode {
+        public boolean evaluate() {
+            return (compareBranches() < 0);
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_COMPARE;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " LT";
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/ExpressionTokenizer.java b/container/catalina/src/share/org/apache/catalina/ssi/ExpressionTokenizer.java
new file mode 100644
index 0000000..2427361
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/ExpressionTokenizer.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+/**
+ * Parses an expression string to return the individual tokens. This is
+ * patterned similar to the StreamTokenizer in the JDK but customized for SSI
+ * conditional expression parsing.
+ * 
+ * @version $Revision$
+ * @author Paul Speed
+ */
+public class ExpressionTokenizer {
+    public static final int TOKEN_STRING = 0;
+    public static final int TOKEN_AND = 1;
+    public static final int TOKEN_OR = 2;
+    public static final int TOKEN_NOT = 3;
+    public static final int TOKEN_EQ = 4;
+    public static final int TOKEN_NOT_EQ = 5;
+    public static final int TOKEN_RBRACE = 6;
+    public static final int TOKEN_LBRACE = 7;
+    public static final int TOKEN_GE = 8;
+    public static final int TOKEN_LE = 9;
+    public static final int TOKEN_GT = 10;
+    public static final int TOKEN_LT = 11;
+    public static final int TOKEN_END = 12;
+    private char[] expr;
+    private String tokenVal = null;
+    private int index;
+    private int length;
+
+
+    /**
+     * Creates a new parser for the specified expression.
+     */
+    public ExpressionTokenizer(String expr) {
+        this.expr = expr.trim().toCharArray();
+        this.length = this.expr.length;
+    }
+
+
+    /**
+     * Returns true if there are more tokens.
+     */
+    public boolean hasMoreTokens() {
+        return index < length;
+    }
+
+
+    /**
+     * Returns the current index for error reporting purposes.
+     */
+    public int getIndex() {
+        return index;
+    }
+
+
+    protected boolean isMetaChar(char c) {
+        return Character.isWhitespace(c) || c == '(' || c == ')' || c == '!'
+                || c == '<' || c == '>' || c == '|' || c == '&' || c == '=';
+    }
+
+
+    /**
+     * Returns the next token type and initializes any state variables
+     * accordingly.
+     */
+    public int nextToken() {
+        // Skip any leading white space
+        while (index < length && Character.isWhitespace(expr[index]))
+            index++;
+        // Clear the current token val
+        tokenVal = null;
+        if (index == length) return TOKEN_END; // End of string
+        int start = index;
+        char currentChar = expr[index];
+        char nextChar = (char)0;
+        index++;
+        if (index < length) nextChar = expr[index];
+        // Check for a known token start
+        switch (currentChar) {
+            case '(' :
+                return TOKEN_LBRACE;
+            case ')' :
+                return TOKEN_RBRACE;
+            case '=' :
+                return TOKEN_EQ;
+            case '!' :
+                if (nextChar == '=') {
+                    index++;
+                    return TOKEN_NOT_EQ;
+                } else {
+                    return TOKEN_NOT;
+                }
+            case '|' :
+                if (nextChar == '|') {
+                    index++;
+                    return TOKEN_OR;
+                }
+                break;
+            case '&' :
+                if (nextChar == '&') {
+                    index++;
+                    return TOKEN_AND;
+                }
+                break;
+            case '>' :
+                if (nextChar == '=') {
+                    index++;
+                    return TOKEN_GE; // Greater than or equal
+                } else {
+                    return TOKEN_GT; // Greater than
+                }
+            case '<' :
+                if (nextChar == '=') {
+                    index++;
+                    return TOKEN_LE; // Less than or equal
+                } else {
+                    return TOKEN_LT; // Less than
+                }
+            default :
+                // Otherwise it's a string
+                break;
+        }
+        int end = index;
+        // If it's a quoted string then end is the next unescaped quote
+        if (currentChar == '"' || currentChar == '\'') {
+            char endChar = currentChar;
+            boolean escaped = false;
+            start++;
+            for (; index < length; index++) {
+                if (expr[index] == '\\' && !escaped) {
+                    escaped = true;
+                    continue;
+                }
+                if (expr[index] == endChar && !escaped) break;
+                escaped = false;
+            }
+            end = index;
+            index++; // Skip the end quote
+        } else {
+            // End is the next whitespace character
+            for (; index < length; index++) {
+                if (isMetaChar(expr[index])) break;
+            }
+            end = index;
+        }
+        // Extract the string from the array
+        this.tokenVal = new String(expr, start, end - start);
+        return TOKEN_STRING;
+    }
+
+
+    /**
+     * Returns the String value of the token if it was type TOKEN_STRING.
+     * Otherwise null is returned.
+     */
+    public String getTokenValue() {
+        return tokenVal;
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/ResponseIncludeWrapper.java b/container/catalina/src/share/org/apache/catalina/ssi/ResponseIncludeWrapper.java
new file mode 100644
index 0000000..caa8239
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/ResponseIncludeWrapper.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 1999-2002,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.catalina.util.DateTool;
+/**
+ * A HttpServletResponseWrapper, used from
+ * <code>SSIServletExternalResolver</code>
+ * 
+ * @author Bip Thelin
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public class ResponseIncludeWrapper extends HttpServletResponseWrapper {
+    /**
+     * The names of some headers we want to capture.
+     */
+    private static final String CONTENT_TYPE = "content-type";
+    private static final String LAST_MODIFIED = "last-modified";
+    protected long lastModified = -1;
+    private String contentType = null;
+
+    /**
+     * Our ServletOutputStream
+     */
+    protected ServletOutputStream captureServletOutputStream;
+    protected ServletOutputStream servletOutputStream;
+    protected PrintWriter printWriter;
+    
+    private ServletContext context;
+    private HttpServletRequest request;
+
+
+    /**
+     * Initialize our wrapper with the current HttpServletResponse and
+     * ServletOutputStream.
+     * 
+     * @param context The servlet context
+     * @param request The HttpServletResponse to use
+     * @param response The response to use
+     * @param captureServletOutputStream The ServletOutputStream to use
+     */
+    public ResponseIncludeWrapper(ServletContext context, 
+    		HttpServletRequest request, HttpServletResponse response,
+           ServletOutputStream captureServletOutputStream) {
+        super(response);
+        this.context = context;
+        this.request = request;
+        this.captureServletOutputStream = captureServletOutputStream;
+    }
+
+
+    /**
+     * Flush the servletOutputStream or printWriter ( only one will be non-null )
+     * This must be called after a requestDispatcher.include, since we can't
+     * assume that the included servlet flushed its stream.
+     */
+    public void flushOutputStreamOrWriter() throws IOException {
+        if (servletOutputStream != null) {
+            servletOutputStream.flush();
+        }
+        if (printWriter != null) {
+            printWriter.flush();
+        }
+    }
+
+
+    /**
+     * Return a printwriter, throws and exception if a OutputStream already
+     * been returned.
+     * 
+     * @return a PrintWriter object
+     * @exception java.io.IOException
+     *                if the outputstream already been called
+     */
+    public PrintWriter getWriter() throws java.io.IOException {
+        if (servletOutputStream == null) {
+            if (printWriter == null) {
+                setCharacterEncoding(getCharacterEncoding());
+                printWriter = new PrintWriter(
+                        new OutputStreamWriter(captureServletOutputStream,
+                                               getCharacterEncoding()));
+            }
+            return printWriter;
+        }
+        throw new IllegalStateException();
+    }
+
+
+    /**
+     * Return a OutputStream, throws and exception if a printwriter already
+     * been returned.
+     * 
+     * @return a OutputStream object
+     * @exception java.io.IOException
+     *                if the printwriter already been called
+     */
+    public ServletOutputStream getOutputStream() throws java.io.IOException {
+        if (printWriter == null) {
+            if (servletOutputStream == null) {
+                servletOutputStream = captureServletOutputStream;
+            }
+            return servletOutputStream;
+        }
+        throw new IllegalStateException();
+    }
+    
+    
+    /**
+     * Returns the value of the <code>last-modified</code> header field. The
+     * result is the number of milliseconds since January 1, 1970 GMT.
+     *
+     * @return the date the resource referenced by this
+     *   <code>ResponseIncludeWrapper</code> was last modified, or -1 if not
+     *   known.                                                             
+     */
+    public long getLastModified() {                                                                                                                                                           
+        if (lastModified == -1) {
+            // javadocs say to return -1 if date not known, if you want another
+            // default, put it here
+            return -1;
+        }
+        return lastModified;
+    }
+
+    /**
+     * Sets the value of the <code>last-modified</code> header field.
+     *
+     * @param lastModified The number of milliseconds since January 1, 1970 GMT.
+     */
+    public void setLastModified(long lastModified) {
+        this.lastModified = lastModified;
+        ((HttpServletResponse) getResponse()).setDateHeader(LAST_MODIFIED,
+                lastModified);
+    }
+
+    /**
+     * Returns the value of the <code>content-type</code> header field.
+     *
+     * @return the content type of the resource referenced by this
+     *   <code>ResponseIncludeWrapper</code>, or <code>null</code> if not known.
+     */
+    public String getContentType() {
+        if (contentType == null) {
+            String url = request.getRequestURI();
+            String mime = context.getMimeType(url);
+            if (mime != null)
+            {
+                setContentType(mime);
+            }
+            else
+            {
+            	// return a safe value
+               setContentType("application/x-octet-stream");
+            }
+        }
+        return contentType;
+    }
+    
+    /**
+     * Sets the value of the <code>content-type</code> header field.
+     *
+     * @param mime a mime type
+     */
+    public void setContentType(String mime) {
+        contentType = mime;
+        if (contentType != null) {
+            getResponse().setContentType(contentType);
+        }
+    }
+
+
+    public void addDateHeader(String name, long value) {
+        super.addDateHeader(name, value);
+        String lname = name.toLowerCase();
+        if (lname.equals(LAST_MODIFIED)) {
+            lastModified = value;
+        }
+    }
+
+    public void addHeader(String name, String value) {
+        super.addHeader(name, value);
+        String lname = name.toLowerCase();
+        if (lname.equals(LAST_MODIFIED)) {
+            try {
+                lastModified = DateTool.rfc1123Format.parse(value).getTime();
+            } catch (Throwable ignore) { }
+        } else if (lname.equals(CONTENT_TYPE)) {
+            contentType = value;
+        }
+    }
+
+    public void setDateHeader(String name, long value) {
+        super.setDateHeader(name, value);
+        String lname = name.toLowerCase();
+        if (lname.equals(LAST_MODIFIED)) {
+            lastModified = value;
+        }
+    }
+
+    public void setHeader(String name, String value) {
+        super.setHeader(name, value);
+        String lname = name.toLowerCase();
+        if (lname.equals(LAST_MODIFIED)) {
+            try {
+                lastModified = DateTool.rfc1123Format.parse(value).getTime();
+            } catch (Throwable ignore) { }
+        }
+        else if (lname.equals(CONTENT_TYPE))
+        {
+            contentType = value;
+        }
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSICommand.java b/container/catalina/src/share/org/apache/catalina/ssi/SSICommand.java
new file mode 100644
index 0000000..d0cbbd9
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSICommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.PrintWriter;
+/**
+ * The interface that all SSI commands ( SSIEcho, SSIInclude, ...) must
+ * implement.
+ * 
+ * @author Bip Thelin
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public interface SSICommand {
+    /**
+     * Write the output of the command to the writer.
+     * 
+     * @param ssiMediator
+     *            the ssi mediator
+     * @param commandName
+     *            the name of the actual command ( ie. echo )
+     * @param paramNames
+     *            The parameter names
+     * @param paramValues
+     *            The parameter values
+     * @param writer
+     *            the writer to output to
+     * @return the most current modified date resulting from any SSI commands
+     * @throws SSIStopProcessingException
+     *             if SSI processing should be aborted
+     */
+	public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer)
+            throws SSIStopProcessingException;
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIConditional.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIConditional.java
new file mode 100644
index 0000000..6a2664d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIConditional.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.PrintWriter;
+import java.text.ParseException;
+/**
+ * SSI command that handles all conditional directives.
+ * 
+ * @version $Revision$
+ * @author Paul Speed
+ * @author David Becker
+ */
+public class SSIConditional implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer)
+            throws SSIStopProcessingException {
+    	// Assume anything using conditionals was modified by it
+    	long lastModified = System.currentTimeMillis();
+        // Retrieve the current state information
+        SSIConditionalState state = ssiMediator.getConditionalState();
+        if ("if".equalsIgnoreCase(commandName)) {
+            // Do nothing if we are nested in a false branch
+            // except count it
+            if (state.processConditionalCommandsOnly) {
+                state.nestingCount++;
+                return lastModified;
+            }
+            state.nestingCount = 0;
+            // Evaluate the expression
+            if (evaluateArguments(paramNames, paramValues, ssiMediator)) {
+                // No more branches can be taken for this if block
+                state.branchTaken = true;
+            } else {
+                // Do not process this branch
+                state.processConditionalCommandsOnly = true;
+                state.branchTaken = false;
+            }
+        } else if ("elif".equalsIgnoreCase(commandName)) {
+            // No need to even execute if we are nested in
+            // a false branch
+            if (state.nestingCount > 0) return lastModified;
+            // If a branch was already taken in this if block
+            // then disable output and return
+            if (state.branchTaken) {
+                state.processConditionalCommandsOnly = true;
+                return lastModified;
+            }
+            // Evaluate the expression
+            if (evaluateArguments(paramNames, paramValues, ssiMediator)) {
+                // Turn back on output and mark the branch
+                state.processConditionalCommandsOnly = false;
+                state.branchTaken = true;
+            } else {
+                // Do not process this branch
+                state.processConditionalCommandsOnly = true;
+                state.branchTaken = false;
+            }
+        } else if ("else".equalsIgnoreCase(commandName)) {
+            // No need to even execute if we are nested in
+            // a false branch
+            if (state.nestingCount > 0) return lastModified;
+            // If we've already taken another branch then
+            // disable output otherwise enable it.
+            state.processConditionalCommandsOnly = state.branchTaken;
+            // And in any case, it's safe to say a branch
+            // has been taken.
+            state.branchTaken = true;
+        } else if ("endif".equalsIgnoreCase(commandName)) {
+            // If we are nested inside a false branch then pop out
+            // one level on the nesting count
+            if (state.nestingCount > 0) {
+                state.nestingCount--;
+                return lastModified;
+            }
+            // Turn output back on
+            state.processConditionalCommandsOnly = false;
+            // Reset the branch status for any outer if blocks,
+            // since clearly we took a branch to have gotten here
+            // in the first place.
+            state.branchTaken = true;
+        } else {
+            throw new SSIStopProcessingException();
+            //throw new SsiCommandException( "Not a conditional command:" +
+            // cmdName );
+        }
+        return lastModified;
+    }
+
+
+    /**
+     * Retrieves the expression from the specified arguments and peforms the
+     * necessary evaluation steps.
+     */
+    private boolean evaluateArguments(String[] names, String[] values,
+            SSIMediator ssiMediator) throws SSIStopProcessingException {
+        String expr = getExpression(names, values);
+        if (expr == null) {
+            throw new SSIStopProcessingException();
+            //throw new SsiCommandException( "No expression specified." );
+        }
+        try {
+            ExpressionParseTree tree = new ExpressionParseTree(expr,
+                    ssiMediator);
+            return tree.evaluateTree();
+        } catch (ParseException e) {
+            //throw new SsiCommandException( "Error parsing expression." );
+            throw new SSIStopProcessingException();
+        }
+    }
+
+
+    /**
+     * Returns the "expr" if the arg name is appropriate, otherwise returns
+     * null.
+     */
+    private String getExpression(String[] paramNames, String[] paramValues) {
+        if ("expr".equalsIgnoreCase(paramNames[0])) return paramValues[0];
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIConditionalState.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIConditionalState.java
new file mode 100644
index 0000000..31c1db6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIConditionalState.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+/**
+ * This class is used by SSIMediator and SSIConditional to keep track of state
+ * information necessary to process the nested conditional commands ( if, elif,
+ * else, endif ).
+ * 
+ * @version $Revision$
+ * @author Dan Sandberg
+ * @author Paul Speed
+ */
+class SSIConditionalState {
+    /**
+     * Set to true if the current conditional has already been completed, i.e.:
+     * a branch was taken.
+     */
+    boolean branchTaken = false;
+    /**
+     * Counts the number of nested false branches.
+     */
+    int nestingCount = 0;
+    /**
+     * Set to true if only conditional commands ( if, elif, else, endif )
+     * should be processed.
+     */
+    boolean processConditionalCommandsOnly = false;
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIConfig.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIConfig.java
new file mode 100644
index 0000000..6ef561e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIConfig.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.PrintWriter;
+/**
+ * Implements the Server-side #exec command
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public final class SSIConfig implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            String substitutedValue = ssiMediator
+                    .substituteVariables(paramValue);
+            if (paramName.equalsIgnoreCase("errmsg")) {
+                ssiMediator.setConfigErrMsg(substitutedValue);
+            } else if (paramName.equalsIgnoreCase("sizefmt")) {
+                ssiMediator.setConfigSizeFmt(substitutedValue);
+            } else if (paramName.equalsIgnoreCase("timefmt")) {
+                ssiMediator.setConfigTimeFmt(substitutedValue);
+            } else {
+                ssiMediator.log("#config--Invalid attribute: " + paramName);
+                //We need to fetch this value each time, since it may change
+                // during the
+                // loop
+                String configErrMsg = ssiMediator.getConfigErrMsg();
+                writer.write(configErrMsg);
+            }
+        }
+        // Setting config options doesn't really change the page
+        return 0;
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIEcho.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIEcho.java
new file mode 100644
index 0000000..8df9441
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIEcho.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.PrintWriter;
+/**
+ * Return the result associated with the supplied Server Variable.
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public class SSIEcho implements SSICommand {
+    protected final static String DEFAULT_ENCODING = "entity";
+    protected final static String MISSING_VARIABLE_VALUE = "(none)";
+
+
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+    	long lastModified = 0;
+        String encoding = DEFAULT_ENCODING;
+        String errorMessage = ssiMediator.getConfigErrMsg();
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            if (paramName.equalsIgnoreCase("var")) {
+                String variableValue = ssiMediator.getVariableValue(
+                        paramValue, encoding);
+                if (variableValue == null) {
+                    variableValue = MISSING_VARIABLE_VALUE;
+                }
+                writer.write(variableValue);
+                lastModified = System.currentTimeMillis();
+            } else if (paramName.equalsIgnoreCase("encoding")) {
+                if (isValidEncoding(paramValue)) {
+                    encoding = paramValue;
+                } else {
+                    ssiMediator.log("#echo--Invalid encoding: " + paramValue);
+                    writer.write(errorMessage);
+                }
+            } else {
+                ssiMediator.log("#echo--Invalid attribute: " + paramName);
+                writer.write(errorMessage);
+            }
+        }
+        return lastModified;
+    }
+
+
+    protected boolean isValidEncoding(String encoding) {
+        return encoding.equalsIgnoreCase("url")
+                || encoding.equalsIgnoreCase("entity")
+                || encoding.equalsIgnoreCase("none");
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIExec.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIExec.java
new file mode 100644
index 0000000..aa40645
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIExec.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import org.apache.catalina.util.IOTools;
+/**
+ * Implements the Server-side #exec command
+ * 
+ * @author Bip Thelin
+ * @author Amy Roh
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public class SSIExec implements SSICommand {
+    protected SSIInclude ssiInclude = new SSIInclude();
+    protected final static int BUFFER_SIZE = 1024;
+
+
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
+        String configErrMsg = ssiMediator.getConfigErrMsg();
+        String paramName = paramNames[0];
+        String paramValue = paramValues[0];
+        String substitutedValue = ssiMediator.substituteVariables(paramValue);
+        if (paramName.equalsIgnoreCase("cgi")) {
+            lastModified = ssiInclude.process(ssiMediator, "include",
+                    			new String[]{"virtual"}, new String[]{substitutedValue},
+								writer);
+        } else if (paramName.equalsIgnoreCase("cmd")) {
+            boolean foundProgram = false;
+            try {
+                Runtime rt = Runtime.getRuntime();
+                Process proc = rt.exec(substitutedValue);
+                foundProgram = true;
+                BufferedReader stdOutReader = new BufferedReader(
+                        new InputStreamReader(proc.getInputStream()));
+                BufferedReader stdErrReader = new BufferedReader(
+                        new InputStreamReader(proc.getErrorStream()));
+                char[] buf = new char[BUFFER_SIZE];
+                IOTools.flow(stdErrReader, writer, buf);
+                IOTools.flow(stdOutReader, writer, buf);
+                proc.waitFor();
+                lastModified = System.currentTimeMillis();                
+            } catch (InterruptedException e) {
+                ssiMediator.log("Couldn't exec file: " + substitutedValue, e);
+                writer.write(configErrMsg);
+            } catch (IOException e) {
+                if (!foundProgram) {
+                    //apache doesn't output an error message if it can't find
+                    // a program
+                }
+                ssiMediator.log("Couldn't exec file: " + substitutedValue, e);
+            }
+        }
+        return lastModified;
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIExternalResolver.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIExternalResolver.java
new file mode 100644
index 0000000..9364d8d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIExternalResolver.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+/**
+ * Interface used by SSIMediator to talk to the 'outside world' ( usually a
+ * servlet )
+ * 
+ * @author Dan Sandberg
+ * @version $Revision$, $Date$
+ */
+public interface SSIExternalResolver {
+    /**
+     * Adds any external variables to the variableNames collection.
+     * 
+     * @param variableNames
+     *            the collection to add to
+     */
+    public void addVariableNames(Collection variableNames);
+
+
+    public String getVariableValue(String name);
+
+
+    /**
+     * Set the named variable to the specified value. If value is null, then
+     * the variable will be removed ( ie. a call to getVariableValue will
+     * return null )
+     * 
+     * @param name
+     *            of the variable
+     * @param value
+     *            of the variable
+     */
+    public void setVariableValue(String name, String value);
+
+
+    /**
+     * Returns the current date. This is useful for putting the SSI stuff in a
+     * regression test. Since you can make the current date a constant, it
+     * makes testing easier since the output won't change.
+     * 
+     * @return the data
+     */
+    public Date getCurrentDate();
+
+
+    public long getFileSize(String path, boolean virtual) throws IOException;
+
+
+    public long getFileLastModified(String path, boolean virtual)
+            throws IOException;
+
+
+    public String getFileText(String path, boolean virtual) throws IOException;
+
+
+    public void log(String message, Throwable throwable);
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIFilter.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIFilter.java
new file mode 100644
index 0000000..9c1c3d2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIFilter.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Globals;
+/**
+ * Filter to process SSI requests within a webpage. Mapped to a content types
+ * from within web.xml.
+ * 
+ * @author David Becker
+ * @version $Revision$, $Date$
+ * @see org.apache.catalina.ssi.SSIServlet
+ */
+public class SSIFilter implements Filter {
+	protected FilterConfig config = null;
+    /** Debug level for this servlet. */
+    protected int debug = 0;
+    /** Expiration time in seconds for the doc. */
+    protected Long expires = null;
+    /** virtual path can be webapp-relative */
+    protected boolean isVirtualWebappRelative = false;
+    /** regex pattern to match when evaluating content types */
+	protected Pattern contentTypeRegEx = null;
+	/** default pattern for ssi filter content type matching */
+	protected Pattern shtmlRegEx =
+        Pattern.compile("text/x-server-parsed-html(;.*)?");
+
+
+    //----------------- Public methods.
+    /**
+     * Initialize this servlet.
+     * 
+     * @exception ServletException
+     *                if an error occurs
+     */
+    public void init(FilterConfig config) throws ServletException {
+    	this.config = config;
+    	
+        String value = null;
+        try {
+            value = config.getInitParameter("debug");
+            debug = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = config.getInitParameter("contentType");
+            contentTypeRegEx = Pattern.compile(value);
+        } catch (Throwable t) {
+            contentTypeRegEx = shtmlRegEx;
+            StringBuffer msg = new StringBuffer();
+            msg.append("Invalid format or no contentType initParam; ");
+            msg.append("expected regular expression; defaulting to ");
+            msg.append(shtmlRegEx.pattern());
+            config.getServletContext().log(msg.toString());
+        }
+        try {
+            value = config.getInitParameter(
+                    "isVirtualWebappRelative");
+            isVirtualWebappRelative = Integer.parseInt(value) > 0?true:false;
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = config.getInitParameter("expires");
+            expires = Long.valueOf(value);
+        } catch (NumberFormatException e) {
+            expires = null;
+            config.getServletContext().log(
+                "Invalid format for expires initParam; expected integer (seconds)"
+            );
+        } catch (Throwable t) {
+            ;
+        }
+        if (debug > 0)
+            config.getServletContext().log(
+                    "SSIFilter.init() SSI invoker started with 'debug'=" + debug);
+    }
+
+    public void doFilter(ServletRequest request, ServletResponse response,
+            FilterChain chain) throws IOException, ServletException {
+        // cast once
+        HttpServletRequest req = (HttpServletRequest)request;
+        HttpServletResponse res = (HttpServletResponse)response;
+        
+        // indicate that we're in SSI processing
+        req.setAttribute(Globals.SSI_FLAG_ATTR, "true");           
+
+        // setup to capture output
+        ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream();
+        ResponseIncludeWrapper responseIncludeWrapper =
+            new ResponseIncludeWrapper(config.getServletContext(),req, res, basos);
+
+        // process remainder of filter chain
+        chain.doFilter(req, responseIncludeWrapper);
+
+        // we can't assume the chain flushed its output
+        responseIncludeWrapper.flushOutputStreamOrWriter();
+        byte[] bytes = basos.toByteArray();
+
+        // get content type
+        String contentType = responseIncludeWrapper.getContentType();
+
+        // is this an allowed type for SSI processing?
+        if (contentTypeRegEx.matcher(contentType).matches()) {
+            String encoding = res.getCharacterEncoding();
+
+            // set up SSI processing 
+            SSIExternalResolver ssiExternalResolver =
+                new SSIServletExternalResolver(config.getServletContext(), req,
+                        res, isVirtualWebappRelative, debug, encoding);
+            SSIProcessor ssiProcessor = new SSIProcessor(ssiExternalResolver,
+                    debug);
+            
+            // prepare readers/writers
+            Reader reader =
+                new InputStreamReader(new ByteArrayInputStream(bytes), encoding);
+            ByteArrayOutputStream ssiout = new ByteArrayOutputStream();
+            PrintWriter writer =
+                new PrintWriter(new OutputStreamWriter(ssiout, encoding));
+            
+            // do SSI processing  
+            long lastModified = ssiProcessor.process(reader,
+                    responseIncludeWrapper.getLastModified(), writer);
+            
+            // set output bytes
+            writer.flush();
+            bytes = ssiout.toByteArray();
+            
+            // override headers
+            if (expires != null) {
+                res.setDateHeader("expires", (new java.util.Date()).getTime()
+                        + expires.longValue() * 1000);
+            }
+            if (lastModified > 0) {
+                res.setDateHeader("last-modified", lastModified);
+            }
+            res.setContentLength(bytes.length);
+            
+            Matcher shtmlMatcher =
+                shtmlRegEx.matcher(responseIncludeWrapper.getContentType());
+            if (shtmlMatcher.matches()) {
+            	// Convert shtml mime type to ordinary html mime type but preserve
+                // encoding, if any.
+            	String enc = shtmlMatcher.group(1);
+            	res.setContentType("text/html" + ((enc != null) ? enc : ""));
+            }
+        }
+
+        // write output
+        try {
+            OutputStream out = res.getOutputStream();
+            out.write(bytes);
+        } catch (Throwable t) {
+            Writer out = res.getWriter();
+            out.write(new String(bytes));
+        }
+    }
+
+    public void destroy() {
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIFlastmod.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIFlastmod.java
new file mode 100644
index 0000000..2d3bef5
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIFlastmod.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+import org.apache.catalina.util.DateTool;
+import org.apache.catalina.util.Strftime;
+/**
+ * Implements the Server-side #flastmod command
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public final class SSIFlastmod implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+    	long lastModified = 0;
+        String configErrMsg = ssiMediator.getConfigErrMsg();
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            String substitutedValue = ssiMediator
+                    .substituteVariables(paramValue);
+            try {
+                if (paramName.equalsIgnoreCase("file")
+                        || paramName.equalsIgnoreCase("virtual")) {
+                    boolean virtual = paramName.equalsIgnoreCase("virtual");
+                    lastModified = ssiMediator.getFileLastModified(
+                            substitutedValue, virtual);
+                    Date date = new Date(lastModified);
+                    String configTimeFmt = ssiMediator.getConfigTimeFmt();
+                    writer.write(formatDate(date, configTimeFmt));
+                } else {
+                    ssiMediator.log("#flastmod--Invalid attribute: "
+                            + paramName);
+                    writer.write(configErrMsg);
+                }
+            } catch (IOException e) {
+                ssiMediator.log(
+                        "#flastmod--Couldn't get last modified for file: "
+                                + substitutedValue, e);
+                writer.write(configErrMsg);
+            }
+        }
+        return lastModified;
+    }
+
+
+    protected String formatDate(Date date, String configTimeFmt) {
+        Strftime strftime = new Strftime(configTimeFmt, DateTool.LOCALE_US);
+        return strftime.format(date);
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIFsize.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIFsize.java
new file mode 100644
index 0000000..683c2b8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIFsize.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.DecimalFormat;
+/**
+ * Implements the Server-side #fsize command
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public final class SSIFsize implements SSICommand {
+    protected final static int ONE_KILOBYTE = 1024;
+    protected final static int ONE_MEGABYTE = 1024 * 1024;
+
+
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
+        String configErrMsg = ssiMediator.getConfigErrMsg();
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            String substitutedValue = ssiMediator
+                    .substituteVariables(paramValue);
+            try {
+                if (paramName.equalsIgnoreCase("file")
+                        || paramName.equalsIgnoreCase("virtual")) {
+                    boolean virtual = paramName.equalsIgnoreCase("virtual");
+                    lastModified = ssiMediator.getFileLastModified(
+                            substitutedValue, virtual);
+                    long size = ssiMediator.getFileSize(substitutedValue,
+                            virtual);
+                    String configSizeFmt = ssiMediator.getConfigSizeFmt();
+                    writer.write(formatSize(size, configSizeFmt));
+                } else {
+                    ssiMediator.log("#fsize--Invalid attribute: " + paramName);
+                    writer.write(configErrMsg);
+                }
+            } catch (IOException e) {
+                ssiMediator.log("#fsize--Couldn't get size for file: "
+                        + substitutedValue, e);
+                writer.write(configErrMsg);
+            }
+        }
+        return lastModified;
+    }
+
+
+    public String repeat(char aChar, int numChars) {
+        if (numChars < 0) {
+            throw new IllegalArgumentException("Num chars can't be negative");
+        }
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < numChars; i++) {
+            buf.append(aChar);
+        }
+        return buf.toString();
+    }
+
+
+    public String padLeft(String str, int maxChars) {
+        String result = str;
+        int charsToAdd = maxChars - str.length();
+        if (charsToAdd > 0) {
+            result = repeat(' ', charsToAdd) + str;
+        }
+        return result;
+    }
+
+
+    //We try to mimick Apache here, as we do everywhere
+    //All the 'magic' numbers are from the util_script.c Apache source file.
+    protected String formatSize(long size, String format) {
+        String retString = "";
+        if (format.equalsIgnoreCase("bytes")) {
+            DecimalFormat decimalFormat = new DecimalFormat("#,##0");
+            retString = decimalFormat.format(size);
+        } else {
+            if (size == 0) {
+                retString = "0k";
+            } else if (size < ONE_KILOBYTE) {
+                retString = "1k";
+            } else if (size < ONE_MEGABYTE) {
+                retString = Long.toString((size + 512) / ONE_KILOBYTE);
+                retString += "k";
+            } else if (size < 99 * ONE_MEGABYTE) {
+                DecimalFormat decimalFormat = new DecimalFormat("0.0M");
+                retString = decimalFormat.format(size / (double)ONE_MEGABYTE);
+            } else {
+                retString = Long.toString((size + (529 * ONE_KILOBYTE))
+                        / ONE_MEGABYTE);
+                retString += "M";
+            }
+            retString = padLeft(retString, 5);
+        }
+        return retString;
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIInclude.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIInclude.java
new file mode 100644
index 0000000..4e25683
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIInclude.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+/**
+ * Implements the Server-side #include command
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public final class SSIInclude implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
+        String configErrMsg = ssiMediator.getConfigErrMsg();
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            String substitutedValue = ssiMediator
+                    .substituteVariables(paramValue);
+            try {
+                if (paramName.equalsIgnoreCase("file")
+                        || paramName.equalsIgnoreCase("virtual")) {
+                    boolean virtual = paramName.equalsIgnoreCase("virtual");
+                    lastModified = ssiMediator.getFileLastModified(
+                    		 substitutedValue, virtual);
+                    String text = ssiMediator.getFileText(substitutedValue,
+                            virtual);
+                    writer.write(text);
+                } else {
+                    ssiMediator.log("#include--Invalid attribute: "
+                            + paramName);
+                    writer.write(configErrMsg);
+                }
+            } catch (IOException e) {
+                ssiMediator.log("#include--Couldn't include file: "
+                        + substitutedValue, e);
+                writer.write(configErrMsg);
+            }
+        }
+        return lastModified;
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIMediator.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIMediator.java
new file mode 100644
index 0000000..18f4e87
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIMediator.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TimeZone;
+import org.apache.catalina.util.DateTool;
+import org.apache.catalina.util.Strftime;
+import org.apache.catalina.util.URLEncoder;
+/**
+ * Allows the different SSICommand implementations to share data/talk to each
+ * other
+ * 
+ * @author Bip Thelin
+ * @author Amy Roh
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public class SSIMediator {
+    protected final static String DEFAULT_CONFIG_ERR_MSG = "[an error occurred while processing this directive]";
+    protected final static String DEFAULT_CONFIG_TIME_FMT = "%A, %d-%b-%Y %T %Z";
+    protected final static String DEFAULT_CONFIG_SIZE_FMT = "abbrev";
+    protected static URLEncoder urlEncoder;
+    protected String configErrMsg = DEFAULT_CONFIG_ERR_MSG;
+    protected String configTimeFmt = DEFAULT_CONFIG_TIME_FMT;
+    protected String configSizeFmt = DEFAULT_CONFIG_SIZE_FMT;
+    protected String className = getClass().getName();
+    protected SSIExternalResolver ssiExternalResolver;
+    protected long lastModifiedDate;
+    protected int debug;
+    protected Strftime strftime;
+    protected SSIConditionalState conditionalState = new SSIConditionalState();
+    static {
+        //We try to encode only the same characters that apache does
+        urlEncoder = new URLEncoder();
+        urlEncoder.addSafeCharacter(',');
+        urlEncoder.addSafeCharacter(':');
+        urlEncoder.addSafeCharacter('-');
+        urlEncoder.addSafeCharacter('_');
+        urlEncoder.addSafeCharacter('.');
+        urlEncoder.addSafeCharacter('*');
+        urlEncoder.addSafeCharacter('/');
+        urlEncoder.addSafeCharacter('!');
+        urlEncoder.addSafeCharacter('~');
+        urlEncoder.addSafeCharacter('\'');
+        urlEncoder.addSafeCharacter('(');
+        urlEncoder.addSafeCharacter(')');
+    }
+
+
+    public SSIMediator(SSIExternalResolver ssiExternalResolver,
+            long lastModifiedDate, int debug) {
+        this.ssiExternalResolver = ssiExternalResolver;
+        this.lastModifiedDate = lastModifiedDate;
+        this.debug = debug;
+        setConfigTimeFmt(DEFAULT_CONFIG_TIME_FMT, true);
+    }
+
+
+    public void setConfigErrMsg(String configErrMsg) {
+        this.configErrMsg = configErrMsg;
+    }
+
+
+    public void setConfigTimeFmt(String configTimeFmt) {
+        setConfigTimeFmt(configTimeFmt, false);
+    }
+
+
+    public void setConfigTimeFmt(String configTimeFmt, boolean fromConstructor) {
+        this.configTimeFmt = configTimeFmt;
+        //What's the story here with DateTool.LOCALE_US?? Why??
+        this.strftime = new Strftime(configTimeFmt, DateTool.LOCALE_US);
+        //Variables like DATE_LOCAL, DATE_GMT, and LAST_MODIFIED need to be
+        // updated when
+        //the timefmt changes. This is what Apache SSI does.
+        setDateVariables(fromConstructor);
+    }
+
+
+    public void setConfigSizeFmt(String configSizeFmt) {
+        this.configSizeFmt = configSizeFmt;
+    }
+
+
+    public String getConfigErrMsg() {
+        return configErrMsg;
+    }
+
+
+    public String getConfigTimeFmt() {
+        return configTimeFmt;
+    }
+
+
+    public String getConfigSizeFmt() {
+        return configSizeFmt;
+    }
+
+
+    public SSIConditionalState getConditionalState() {
+        return conditionalState;
+    }
+
+
+    public Collection getVariableNames() {
+        Set variableNames = new HashSet();
+        //These built-in variables are supplied by the mediator ( if not
+        // over-written by
+        // the user ) and always exist
+        variableNames.add("DATE_GMT");
+        variableNames.add("DATE_LOCAL");
+        variableNames.add("LAST_MODIFIED");
+        ssiExternalResolver.addVariableNames(variableNames);
+        //Remove any variables that are reserved by this class
+        Iterator iter = variableNames.iterator();
+        while (iter.hasNext()) {
+            String name = (String)iter.next();
+            if (isNameReserved(name)) {
+                iter.remove();
+            }
+        }
+        return variableNames;
+    }
+
+
+    public long getFileSize(String path, boolean virtual) throws IOException {
+        return ssiExternalResolver.getFileSize(path, virtual);
+    }
+
+
+    public long getFileLastModified(String path, boolean virtual)
+            throws IOException {
+        return ssiExternalResolver.getFileLastModified(path, virtual);
+    }
+
+
+    public String getFileText(String path, boolean virtual) throws IOException {
+        return ssiExternalResolver.getFileText(path, virtual);
+    }
+
+
+    protected boolean isNameReserved(String name) {
+        return name.startsWith(className + ".");
+    }
+
+
+    public String getVariableValue(String variableName) {
+        return getVariableValue(variableName, "none");
+    }
+
+
+    public void setVariableValue(String variableName, String variableValue) {
+        if (!isNameReserved(variableName)) {
+            ssiExternalResolver.setVariableValue(variableName, variableValue);
+        }
+    }
+
+
+    public String getVariableValue(String variableName, String encoding) {
+        String lowerCaseVariableName = variableName.toLowerCase();
+        String variableValue = null;
+        if (!isNameReserved(lowerCaseVariableName)) {
+            //Try getting it externally first, if it fails, try getting the
+            // 'built-in'
+            // value
+            variableValue = ssiExternalResolver.getVariableValue(variableName);
+            if (variableValue == null) {
+                variableName = variableName.toUpperCase();
+                variableValue = (String)ssiExternalResolver
+                        .getVariableValue(className + "." + variableName);
+            }
+            if (variableValue != null) {
+                variableValue = encode(variableValue, encoding);
+            }
+        }
+        return variableValue;
+    }
+
+
+    /**
+     * Applies variable substitution to the specified String and returns the
+     * new resolved string.
+     */
+    public String substituteVariables(String val) {
+        // If it has no variable references then no work
+        // need to be done
+        if (val.indexOf('$') < 0) return val;
+        StringBuffer sb = new StringBuffer(val);
+        for (int i = 0; i < sb.length();) {
+            // Find the next $
+            for (; i < sb.length(); i++) {
+                if (sb.charAt(i) == '$') {
+                    i++;
+                    break;
+                }
+            }
+            if (i == sb.length()) break;
+            // Check to see if the $ is escaped
+            if (i > 1 && sb.charAt(i - 2) == '\\') {
+                sb.deleteCharAt(i - 2);
+                i--;
+                continue;
+            }
+            int nameStart = i;
+            int start = i - 1;
+            int end = -1;
+            int nameEnd = -1;
+            char endChar = ' ';
+            // Check for {} wrapped var
+            if (sb.charAt(i) == '{') {
+                nameStart++;
+                endChar = '}';
+            }
+            // Find the end of the var reference
+            for (; i < sb.length(); i++) {
+                if (sb.charAt(i) == endChar) break;
+            }
+            end = i;
+            nameEnd = end;
+            if (endChar == '}') end++;
+            // We should now have enough to extract the var name
+            String varName = sb.substring(nameStart, nameEnd);
+            String value = getVariableValue(varName);
+            if (value == null) value = "";
+            // Replace the var name with its value
+            sb.replace(start, end, value);
+            // Start searching for the next $ after the value
+            // that was just substituted.
+            i = start + value.length();
+        }
+        return sb.toString();
+    }
+
+
+    protected String formatDate(Date date, TimeZone timeZone) {
+        String retVal;
+        if (timeZone != null) {
+            //we temporarily change strftime. Since SSIMediator is inherently
+            // single-threaded, this
+            //isn't a problem
+            TimeZone oldTimeZone = strftime.getTimeZone();
+            strftime.setTimeZone(timeZone);
+            retVal = strftime.format(date);
+            strftime.setTimeZone(oldTimeZone);
+        } else {
+            retVal = strftime.format(date);
+        }
+        return retVal;
+    }
+
+
+    protected String encode(String value, String encoding) {
+        String retVal = null;
+        if (encoding.equalsIgnoreCase("url")) {
+            retVal = urlEncoder.encode(value);
+        } else if (encoding.equalsIgnoreCase("none")) {
+            retVal = value;
+        } else if (encoding.equalsIgnoreCase("entity")) {
+            //Not sure how this is really different than none
+            retVal = value;
+        } else {
+            //This shouldn't be possible
+            throw new IllegalArgumentException("Unknown encoding: " + encoding);
+        }
+        return retVal;
+    }
+
+
+    public void log(String message) {
+        ssiExternalResolver.log(message, null);
+    }
+
+
+    public void log(String message, Throwable throwable) {
+        ssiExternalResolver.log(message, throwable);
+    }
+
+
+    protected void setDateVariables(boolean fromConstructor) {
+        boolean alreadySet = ssiExternalResolver.getVariableValue(className
+                + ".alreadyset") != null;
+        //skip this if we are being called from the constructor, and this has
+        // already
+        // been set
+        if (!(fromConstructor && alreadySet)) {
+            ssiExternalResolver.setVariableValue(className + ".alreadyset",
+                    "true");
+            Date date = new Date();
+            TimeZone timeZone = TimeZone.getTimeZone("GMT");
+            String retVal = formatDate(date, timeZone);
+            //If we are setting on of the date variables, we want to remove
+            // them from the
+            // user
+            //defined list of variables, because this is what Apache does
+            setVariableValue("DATE_GMT", null);
+            ssiExternalResolver.setVariableValue(className + ".DATE_GMT",
+                    retVal);
+            retVal = formatDate(date, null);
+            setVariableValue("DATE_LOCAL", null);
+            ssiExternalResolver.setVariableValue(className + ".DATE_LOCAL",
+                    retVal);
+            retVal = formatDate(new Date(lastModifiedDate), null);
+            setVariableValue("LAST_MODIFIED", null);
+            ssiExternalResolver.setVariableValue(className + ".LAST_MODIFIED",
+                    retVal);
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIPrintenv.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIPrintenv.java
new file mode 100644
index 0000000..772097d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIPrintenv.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Iterator;
+/**
+ * Implements the Server-side #printenv command
+ * 
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public class SSIPrintenv implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+    	long lastModified = 0;
+        //any arguments should produce an error
+        if (paramNames.length > 0) {
+            String errorMessage = ssiMediator.getConfigErrMsg();
+            writer.write(errorMessage);
+        } else {
+            Collection variableNames = ssiMediator.getVariableNames();
+            Iterator iter = variableNames.iterator();
+            while (iter.hasNext()) {
+                String variableName = (String)iter.next();
+                String variableValue = ssiMediator
+                        .getVariableValue(variableName);
+                //This shouldn't happen, since all the variable names must
+                // have values
+                if (variableValue == null) {
+                    variableValue = "(none)";
+                }
+                writer.write(variableName);
+                writer.write('=');
+                writer.write(variableValue);
+                writer.write('\n');
+                lastModified = System.currentTimeMillis();
+            }
+        }
+        return lastModified;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIProcessor.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIProcessor.java
new file mode 100644
index 0000000..53fc7ea
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIProcessor.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import org.apache.catalina.util.IOTools;
+/**
+ * The entry point to SSI processing. This class does the actual parsing,
+ * delegating to the SSIMediator, SSICommand, and SSIExternalResolver as
+ * necessary[
+ * 
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public class SSIProcessor {
+    /** The start pattern */
+    protected final static String COMMAND_START = "<!--#";
+    /** The end pattern */
+    protected final static String COMMAND_END = "-->";
+    protected final static int BUFFER_SIZE = 4096;
+    protected SSIExternalResolver ssiExternalResolver;
+    protected HashMap commands = new HashMap();
+    protected int debug;
+
+
+    public SSIProcessor(SSIExternalResolver ssiExternalResolver, int debug) {
+        this.ssiExternalResolver = ssiExternalResolver;
+        this.debug = debug;
+        addBuiltinCommands();
+    }
+
+
+    protected void addBuiltinCommands() {
+        addCommand("config", new SSIConfig());
+        addCommand("echo", new SSIEcho());
+        addCommand("exec", new SSIExec());
+        addCommand("include", new SSIInclude());
+        addCommand("flastmod", new SSIFlastmod());
+        addCommand("fsize", new SSIFsize());
+        addCommand("printenv", new SSIPrintenv());
+        addCommand("set", new SSISet());
+        SSIConditional ssiConditional = new SSIConditional();
+        addCommand("if", ssiConditional);
+        addCommand("elif", ssiConditional);
+        addCommand("endif", ssiConditional);
+        addCommand("else", ssiConditional);
+    }
+
+
+    public void addCommand(String name, SSICommand command) {
+        commands.put(name, command);
+    }
+
+
+    /**
+     * Process a file with server-side commands, reading from reader and
+     * writing the processed version to writer. NOTE: We really should be doing
+     * this in a streaming way rather than converting it to an array first.
+     * 
+     * @param reader
+     *            the reader to read the file containing SSIs from
+     * @param writer
+     *            the writer to write the file with the SSIs processed.
+     * @return the most current modified date resulting from any SSI commands
+     * @throws IOException
+     *             when things go horribly awry. Should be unlikely since the
+     *             SSICommand usually catches 'normal' IOExceptions.
+     */
+    public long process(Reader reader, long lastModifiedDate,
+            PrintWriter writer) throws IOException {
+        SSIMediator ssiMediator = new SSIMediator(ssiExternalResolver,
+                lastModifiedDate, debug);
+        StringWriter stringWriter = new StringWriter();
+        IOTools.flow(reader, stringWriter);
+        String fileContents = stringWriter.toString();
+        stringWriter = null;
+        int index = 0;
+        boolean inside = false;
+        StringBuffer command = new StringBuffer();
+        try {
+            while (index < fileContents.length()) {
+                char c = fileContents.charAt(index);
+                if (!inside) {
+                    if (c == COMMAND_START.charAt(0)
+                            && charCmp(fileContents, index, COMMAND_START)) {
+                        inside = true;
+                        index += COMMAND_START.length();
+                        command.setLength(0); //clear the command string
+                    } else {
+                        if (!ssiMediator.getConditionalState().processConditionalCommandsOnly) {
+                            writer.write(c);
+                        }
+                        index++;
+                    }
+                } else {
+                    if (c == COMMAND_END.charAt(0)
+                            && charCmp(fileContents, index, COMMAND_END)) {
+                        inside = false;
+                        index += COMMAND_END.length();
+                        String strCmd = parseCmd(command);
+                        if (debug > 0) {
+                            ssiExternalResolver.log(
+                                    "SSIProcessor.process -- processing command: "
+                                            + strCmd, null);
+                        }
+                        String[] paramNames = parseParamNames(command, strCmd
+                                .length());
+                        String[] paramValues = parseParamValues(command,
+                                strCmd.length(), paramNames.length);
+                        //We need to fetch this value each time, since it may
+                        // change
+                        // during the loop
+                        String configErrMsg = ssiMediator.getConfigErrMsg();
+                        SSICommand ssiCommand = (SSICommand)commands
+                                .get(strCmd.toLowerCase());
+                        String errorMessage = null;
+                        if (ssiCommand == null) {
+                            errorMessage = "Unknown command: " + strCmd;
+                        } else if (paramValues == null) {
+                            errorMessage = "Error parsing directive parameters.";
+                        } else if (paramNames.length != paramValues.length) {
+                            errorMessage = "Parameter names count does not match parameter values count on command: "
+                                    + strCmd;
+                        } else {
+                            // don't process the command if we are processing
+                            // conditional
+                            // commands only and the
+                            // command is not conditional
+                            if (!ssiMediator.getConditionalState().processConditionalCommandsOnly
+                                    || ssiCommand instanceof SSIConditional) {
+                                long lmd = ssiCommand.process(ssiMediator, strCmd,
+                                               paramNames, paramValues, writer);
+                                if (lmd > lastModifiedDate) {
+                                    lastModifiedDate = lmd;
+                                }                                    
+                            }
+                        }
+                        if (errorMessage != null) {
+                            ssiExternalResolver.log(errorMessage, null);
+                            writer.write(configErrMsg);
+                        }
+                    } else {
+                        command.append(c);
+                        index++;
+                    }
+                }
+            }
+        } catch (SSIStopProcessingException e) {
+            //If we are here, then we have already stopped processing, so all
+            // is good
+        }
+        return lastModifiedDate;
+    }
+
+
+    /**
+     * Parse a StringBuffer and take out the param type token. Called from
+     * <code>requestHandler</code>
+     * 
+     * @param cmd
+     *            a value of type 'StringBuffer'
+     * @return a value of type 'String[]'
+     */
+    protected String[] parseParamNames(StringBuffer cmd, int start) {
+        int bIdx = start;
+        int i = 0;
+        int quotes = 0;
+        boolean inside = false;
+        StringBuffer retBuf = new StringBuffer();
+        while (bIdx < cmd.length()) {
+            if (!inside) {
+                while (bIdx < cmd.length() && isSpace(cmd.charAt(bIdx)))
+                    bIdx++;
+                if (bIdx >= cmd.length()) break;
+                inside = !inside;
+            } else {
+                while (bIdx < cmd.length() && cmd.charAt(bIdx) != '=') {
+                    retBuf.append(cmd.charAt(bIdx));
+                    bIdx++;
+                }
+                retBuf.append('=');
+                inside = !inside;
+                quotes = 0;
+                boolean escaped = false;
+                for (; bIdx < cmd.length() && quotes != 2; bIdx++) {
+                    char c = cmd.charAt(bIdx);
+                    // Need to skip escaped characters
+                    if (c == '\\' && !escaped) {
+                        escaped = true;
+                        bIdx++;
+                        continue;
+                    }
+                    escaped = false;
+                    if (c == '"') quotes++;
+                }
+            }
+        }
+        StringTokenizer str = new StringTokenizer(retBuf.toString(), "=");
+        String[] retString = new String[str.countTokens()];
+        while (str.hasMoreTokens()) {
+            retString[i++] = str.nextToken().trim();
+        }
+        return retString;
+    }
+
+
+    /**
+     * Parse a StringBuffer and take out the param token. Called from
+     * <code>requestHandler</code>
+     * 
+     * @param cmd
+     *            a value of type 'StringBuffer'
+     * @return a value of type 'String[]'
+     */
+    protected String[] parseParamValues(StringBuffer cmd, int start, int count) {
+        int valIndex = 0;
+        boolean inside = false;
+        String[] vals = new String[count];
+        StringBuffer sb = new StringBuffer();
+        for (int bIdx = start; bIdx < cmd.length(); bIdx++) {
+            if (!inside) {
+                while (bIdx < cmd.length() && cmd.charAt(bIdx) != '"')
+                    bIdx++;
+                if (bIdx >= cmd.length()) break;
+                inside = !inside;
+            } else {
+                boolean escaped = false;
+                for (; bIdx < cmd.length(); bIdx++) {
+                    char c = cmd.charAt(bIdx);
+                    // Check for escapes
+                    if (c == '\\' && !escaped) {
+                        escaped = true;
+                        continue;
+                    }
+                    // If we reach the other " then stop
+                    if (c == '"' && !escaped) break;
+                    // Since parsing of attributes and var
+                    // substitution is done in separate places,
+                    // we need to leave escape in the string
+                    if (c == '$' && escaped) sb.append('\\');
+                    escaped = false;
+                    sb.append(c);
+                }
+                // If we hit the end without seeing a quote
+                // the signal an error
+                if (bIdx == cmd.length()) return null;
+                vals[valIndex++] = sb.toString();
+                sb.delete(0, sb.length()); // clear the buffer
+                inside = !inside;
+            }
+        }
+        return vals;
+    }
+
+
+    /**
+     * Parse a StringBuffer and take out the command token. Called from
+     * <code>requestHandler</code>
+     * 
+     * @param cmd
+     *            a value of type 'StringBuffer'
+     * @return a value of type 'String', or null if there is none
+     */
+    private String parseCmd(StringBuffer cmd) {
+        int firstLetter = -1;
+        int lastLetter = -1;
+        for (int i = 0; i < cmd.length(); i++) {
+            char c = cmd.charAt(i);
+            if (Character.isLetter(c)) {
+                if (firstLetter == -1) {
+                    firstLetter = i;
+                }
+                lastLetter = i;
+            } else if (isSpace(c)) {
+                if (lastLetter > -1) {
+                    break;
+                }
+            } else {
+                break;
+            }
+        }
+        String command = null;
+        if (firstLetter != -1) {
+            command = cmd.substring(firstLetter, lastLetter + 1);
+        }
+        return command;
+    }
+
+
+    protected boolean charCmp(String buf, int index, String command) {
+        return buf.regionMatches(index, command, 0, command.length());
+    }
+
+
+    protected boolean isSpace(char c) {
+        return c == ' ' || c == '\n' || c == '\t' || c == '\r';
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIServlet.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIServlet.java
new file mode 100644
index 0000000..d20838e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIServlet.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.catalina.Globals;
+/**
+ * Servlet to process SSI requests within a webpage. Mapped to a path from
+ * within web.xml.
+ * 
+ * @author Bip Thelin
+ * @author Amy Roh
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public class SSIServlet extends HttpServlet {
+    /** Debug level for this servlet. */
+    protected int debug = 0;
+    /** Should the output be buffered. */
+    protected boolean buffered = false;
+    /** Expiration time in seconds for the doc. */
+    protected Long expires = null;
+    /** virtual path can be webapp-relative */
+    protected boolean isVirtualWebappRelative = false;
+    /** Input encoding. If not specified, uses platform default */
+    protected String inputEncoding = null;
+    /** Output encoding. If not specified, uses platform default */
+    protected String outputEncoding = "UTF-8";
+
+
+    //----------------- Public methods.
+    /**
+     * Initialize this servlet.
+     * 
+     * @exception ServletException
+     *                if an error occurs
+     */
+    public void init() throws ServletException {
+        String value = null;
+        try {
+            value = getServletConfig().getInitParameter("debug");
+            debug = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter(
+                    "isVirtualWebappRelative");
+            isVirtualWebappRelative = Integer.parseInt(value) > 0?true:false;
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter("expires");
+            expires = Long.valueOf(value);
+        } catch (NumberFormatException e) {
+            expires = null;
+            log("Invalid format for expires initParam; expected integer (seconds)");
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter("buffered");
+            buffered = Integer.parseInt(value) > 0?true:false;
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            inputEncoding = getServletConfig().getInitParameter("inputEncoding");
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = getServletConfig().getInitParameter("outputEncoding");
+            if (value != null) {
+                outputEncoding = value;
+            }
+        } catch (Throwable t) {
+            ;
+        }
+        if (debug > 0)
+            log("SSIServlet.init() SSI invoker started with 'debug'=" + debug);
+    }
+
+
+    /**
+     * Process and forward the GET request to our <code>requestHandler()</code>*
+     * 
+     * @param req
+     *            a value of type 'HttpServletRequest'
+     * @param res
+     *            a value of type 'HttpServletResponse'
+     * @exception IOException
+     *                if an error occurs
+     * @exception ServletException
+     *                if an error occurs
+     */
+    public void doGet(HttpServletRequest req, HttpServletResponse res)
+            throws IOException, ServletException {
+        if (debug > 0) log("SSIServlet.doGet()");
+        requestHandler(req, res);
+    }
+
+
+    /**
+     * Process and forward the POST request to our
+     * <code>requestHandler()</code>.
+     * 
+     * @param req
+     *            a value of type 'HttpServletRequest'
+     * @param res
+     *            a value of type 'HttpServletResponse'
+     * @exception IOException
+     *                if an error occurs
+     * @exception ServletException
+     *                if an error occurs
+     */
+    public void doPost(HttpServletRequest req, HttpServletResponse res)
+            throws IOException, ServletException {
+        if (debug > 0) log("SSIServlet.doPost()");
+        requestHandler(req, res);
+    }
+
+
+    /**
+     * Process our request and locate right SSI command.
+     * 
+     * @param req
+     *            a value of type 'HttpServletRequest'
+     * @param res
+     *            a value of type 'HttpServletResponse'
+     */
+    protected void requestHandler(HttpServletRequest req,
+            HttpServletResponse res) throws IOException, ServletException {
+        ServletContext servletContext = getServletContext();
+        String path = SSIServletRequestUtil.getRelativePath(req);
+        if (debug > 0)
+            log("SSIServlet.requestHandler()\n" + "Serving "
+                    + (buffered?"buffered ":"unbuffered ") + "resource '"
+                    + path + "'");
+        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
+        // (the "toUpperCase()" avoids problems on Windows systems)
+        if (path == null || path.toUpperCase().startsWith("/WEB-INF")
+                || path.toUpperCase().startsWith("/META-INF")) {
+            res.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+            log("Can't serve file: " + path);
+            return;
+        }
+        URL resource = servletContext.getResource(path);
+        if (resource == null) {
+            res.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+            log("Can't find file: " + path);
+            return;
+        }
+        String resourceMimeType = servletContext.getMimeType(path);
+        if (resourceMimeType == null) {
+            resourceMimeType = "text/html";
+        }
+        res.setContentType(resourceMimeType + ";charset=" + outputEncoding);
+        if (expires != null) {
+            res.setDateHeader("Expires", (new java.util.Date()).getTime()
+                    + expires.longValue() * 1000);
+        }
+        req.setAttribute(Globals.SSI_FLAG_ATTR, "true");
+        processSSI(req, res, resource);
+    }
+
+
+    protected void processSSI(HttpServletRequest req, HttpServletResponse res,
+            URL resource) throws IOException {
+        SSIExternalResolver ssiExternalResolver =
+            new SSIServletExternalResolver(getServletContext(), req, res,
+                    isVirtualWebappRelative, debug, inputEncoding);
+        SSIProcessor ssiProcessor = new SSIProcessor(ssiExternalResolver,
+                debug);
+        PrintWriter printWriter = null;
+        StringWriter stringWriter = null;
+        if (buffered) {
+            stringWriter = new StringWriter();
+            printWriter = new PrintWriter(stringWriter);
+        } else {
+            printWriter = res.getWriter();
+        }
+
+        URLConnection resourceInfo = resource.openConnection();
+        InputStream resourceInputStream = resourceInfo.getInputStream();
+        String encoding = resourceInfo.getContentEncoding();
+        if (encoding == null) {
+            encoding = inputEncoding;
+        }
+        InputStreamReader isr;
+        if (encoding == null) {
+            isr = new InputStreamReader(resourceInputStream);
+        } else {
+            isr = new InputStreamReader(resourceInputStream, encoding);
+        }
+        BufferedReader bufferedReader = new BufferedReader(isr);
+
+        long lastModified = ssiProcessor.process(bufferedReader,
+                resourceInfo.getLastModified(), printWriter);
+        if (lastModified > 0) {
+            res.setDateHeader("last-modified", lastModified);
+        }
+        if (buffered) {
+            printWriter.flush();
+            String text = stringWriter.toString();
+            res.getWriter().write(text);
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIServletExternalResolver.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIServletExternalResolver.java
new file mode 100644
index 0000000..0b64247
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIServletExternalResolver.java
@@ -0,0 +1,567 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.catalina.connector.Request;
+import org.apache.coyote.Constants;
+
+/**
+ * An implementation of SSIExternalResolver that is used with servlets.
+ * 
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public class SSIServletExternalResolver implements SSIExternalResolver {
+    protected final String VARIABLE_NAMES[] = {"AUTH_TYPE", "CONTENT_LENGTH",
+            "CONTENT_TYPE", "DOCUMENT_NAME", "DOCUMENT_URI",
+            "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING",
+            "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", "HTTP_HOST",
+            "HTTP_REFERER", "HTTP_USER_AGENT", "PATH_INFO", "PATH_TRANSLATED",
+            "QUERY_STRING", "QUERY_STRING_UNESCAPED", "REMOTE_ADDR",
+            "REMOTE_HOST", "REMOTE_PORT", "REMOTE_USER", "REQUEST_METHOD",
+            "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_ADDR",
+            "SERVER_NAME", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE",
+            "UNIQUE_ID"};
+    protected ServletContext context;
+    protected HttpServletRequest req;
+    protected HttpServletResponse res;
+    protected boolean isVirtualWebappRelative;
+    protected int debug;
+    protected String inputEncoding;
+
+    public SSIServletExternalResolver(ServletContext context,
+            HttpServletRequest req, HttpServletResponse res,
+            boolean isVirtualWebappRelative, int debug, String inputEncoding) {
+        this.context = context;
+        this.req = req;
+        this.res = res;
+        this.isVirtualWebappRelative = isVirtualWebappRelative;
+        this.debug = debug;
+        this.inputEncoding = inputEncoding;
+    }
+
+
+    public void log(String message, Throwable throwable) {
+        //We can't assume that Servlet.log( message, null )
+        //is the same as Servlet.log( message ), since API
+        //doesn't seem to say so.
+        if (throwable != null) {
+            context.log(message, throwable);
+        } else {
+            context.log(message);
+        }
+    }
+
+
+    public void addVariableNames(Collection variableNames) {
+        for (int i = 0; i < VARIABLE_NAMES.length; i++) {
+            String variableName = VARIABLE_NAMES[i];
+            String variableValue = getVariableValue(variableName);
+            if (variableValue != null) {
+                variableNames.add(variableName);
+            }
+        }
+        Enumeration e = req.getAttributeNames();
+        while (e.hasMoreElements()) {
+            String name = (String)e.nextElement();
+            if (!isNameReserved(name)) {
+                variableNames.add(name);
+            }
+        }
+    }
+
+
+    protected Object getReqAttributeIgnoreCase(String targetName) {
+        Object object = null;
+        if (!isNameReserved(targetName)) {
+            object = req.getAttribute(targetName);
+            if (object == null) {
+                Enumeration e = req.getAttributeNames();
+                while (e.hasMoreElements()) {
+                    String name = (String)e.nextElement();
+                    if (targetName.equalsIgnoreCase(name)
+                            && !isNameReserved(name)) {
+                        object = req.getAttribute(name);
+                        if (object != null) {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return object;
+    }
+
+
+    protected boolean isNameReserved(String name) {
+        return name.startsWith("java.") || name.startsWith("javax.")
+                || name.startsWith("sun.");
+    }
+
+
+    public void setVariableValue(String name, String value) {
+        if (!isNameReserved(name)) {
+            req.setAttribute(name, value);
+        }
+    }
+
+
+    public String getVariableValue(String name) {
+        String retVal = null;
+        Object object = getReqAttributeIgnoreCase(name);
+        if (object != null) {
+            retVal = object.toString();
+        } else {
+            retVal = getCGIVariable(name);
+        }
+        return retVal;
+    }
+
+
+    protected String getCGIVariable(String name) {
+        String retVal = null;
+        String[] nameParts = name.toUpperCase().split("_");
+        int requiredParts = 2;
+        if (nameParts.length == 1) {
+            if (nameParts[0].equals("PATH")) {
+                requiredParts = 1;
+                retVal = null; // Not implemented
+            }
+        }
+        else if (nameParts[0].equals("AUTH")) {
+            if (nameParts[1].equals("TYPE")) {
+                retVal = req.getAuthType();
+            }
+        } else if(nameParts[0].equals("CONTENT")) {
+            if (nameParts[1].equals("LENGTH")) {
+                int contentLength = req.getContentLength();
+                if (contentLength >= 0) {
+                    retVal = Integer.toString(contentLength);
+                }
+            } else if (nameParts[1].equals("TYPE")) {
+                retVal = req.getContentType();
+            }
+        } else if (nameParts[0].equals("DOCUMENT")) {
+            if (nameParts[1].equals("NAME")) {
+                String requestURI = req.getRequestURI();
+                retVal = requestURI.substring(requestURI.lastIndexOf('/') + 1);
+            } else if (nameParts[1].equals("URI")) {
+                retVal = req.getRequestURI();
+            }
+        } else if (name.equalsIgnoreCase("GATEWAY_INTERFACE")) {
+            retVal = "CGI/1.1";
+        } else if (nameParts[0].equals("HTTP")) {
+            if (nameParts[1].equals("ACCEPT")) {
+                String accept = null;
+                if (nameParts.length == 2) {
+                    accept = "Accept";
+                } else if (nameParts[2].equals("ENCODING")) {
+                    requiredParts = 3;
+                    accept = "Accept-Encoding";
+                } else if (nameParts[2].equals("LANGUAGE")) {
+                    requiredParts = 3;
+                    accept = "Accept-Language";
+                }
+                if (accept != null) {
+                    Enumeration acceptHeaders = req.getHeaders(accept);
+                    if (acceptHeaders != null)
+                        if (acceptHeaders.hasMoreElements()) {
+                            StringBuffer rv = new StringBuffer(
+                                    (String) acceptHeaders.nextElement());
+                            while (acceptHeaders.hasMoreElements()) {
+                                rv.append(", ");
+                                rv.append((String) acceptHeaders.nextElement());
+                            }
+                        retVal = rv.toString();
+                    }
+                }
+            }
+            else if (nameParts[1].equals("CONNECTION")) {
+                retVal = req.getHeader("Connection");
+            }
+            else if (nameParts[1].equals("HOST")) {
+                retVal = req.getHeader("Host");
+            }
+            else if (nameParts[1].equals("REFERER")) {
+                retVal = req.getHeader("Referer");
+            }
+            else if (nameParts[1].equals("USER"))
+                if (nameParts.length == 3)
+                    if (nameParts[2].equals("AGENT")) {
+                        requiredParts = 3;
+                        retVal = req.getHeader("User-Agent");
+                    }
+
+        } else if (nameParts[0].equals("PATH")) {
+            if (nameParts[1].equals("INFO")) {
+                retVal = req.getPathInfo();
+            } else if (nameParts[1].equals("TRANSLATED")) {
+                retVal = req.getPathTranslated();
+            }
+        } else if (nameParts[0].equals("QUERY")) {
+            if (nameParts[1].equals("STRING")) {
+                String queryString = req.getQueryString();
+                if (nameParts.length == 2) {
+                    //apache displays this as an empty string rather than (none)
+                    retVal = nullToEmptyString(queryString);
+                } else if (nameParts[2].equals("UNESCAPED")) {
+                    requiredParts = 3;
+                    if (queryString != null) {
+                        // Use default as a last resort
+                        String queryStringEncoding =
+                            Constants.DEFAULT_CHARACTER_ENCODING;
+                
+                        String uriEncoding = null;
+                        boolean useBodyEncodingForURI = false;
+                
+                        // Get encoding settings from request / connector if
+                        // possible
+                        String requestEncoding = req.getCharacterEncoding();
+                        if (req instanceof Request) {
+                            uriEncoding =
+                                ((Request)req).getConnector().getURIEncoding();
+                            useBodyEncodingForURI = ((Request)req)
+                                    .getConnector().getUseBodyEncodingForURI();
+                        }
+                
+                        // If valid, apply settings from request / connector
+                        if (uriEncoding != null) {
+                            queryStringEncoding = uriEncoding;
+                        } else if(useBodyEncodingForURI) {
+                            if (requestEncoding != null) {
+                                queryStringEncoding = requestEncoding;
+                            }
+                        }
+                
+                        try {
+                            retVal = URLDecoder.decode(queryString,
+                                    queryStringEncoding);                       
+                        } catch (UnsupportedEncodingException e) {
+                            retVal = queryString;
+                        }
+                    }
+                }
+            }
+        } else if(nameParts[0].equals("REMOTE")) {
+            if (nameParts[1].equals("ADDR")) {
+                retVal = req.getRemoteAddr();
+            } else if (nameParts[1].equals("HOST")) {
+                retVal = req.getRemoteHost();
+            } else if (nameParts[1].equals("IDENT")) {
+                retVal = null; // Not implemented
+            } else if (nameParts[1].equals("PORT")) {
+                retVal = Integer.toString( req.getRemotePort());
+            } else if (nameParts[1].equals("USER")) {
+                retVal = req.getRemoteUser();
+            }
+        } else if(nameParts[0].equals("REQUEST")) {
+            if (nameParts[1].equals("METHOD")) {
+                retVal = req.getMethod();
+            }
+            else if (nameParts[1].equals("URI")) {
+                // If this is an error page, get the original URI
+                retVal = (String) req.getAttribute(
+                        "javax.servlet.forward.request_uri");
+                if (retVal == null) retVal=req.getRequestURI();
+            }
+        } else if (nameParts[0].equals("SCRIPT")) {
+            String scriptName = req.getServletPath();
+            if (nameParts[1].equals("FILENAME")) {
+                retVal = context.getRealPath(scriptName);
+            }
+            else if (nameParts[1].equals("NAME")) {
+                retVal = scriptName;
+            }
+        } else if (nameParts[0].equals("SERVER")) {
+            if (nameParts[1].equals("ADDR")) {
+                retVal = req.getLocalAddr();
+            }
+            if (nameParts[1].equals("NAME")) {
+                retVal = req.getServerName();
+            } else if (nameParts[1].equals("PORT")) {
+                retVal = Integer.toString(req.getServerPort());
+            } else if (nameParts[1].equals("PROTOCOL")) {
+                retVal = req.getProtocol();
+            } else if (nameParts[1].equals("SOFTWARE")) {
+                StringBuffer rv = new StringBuffer(context.getServerInfo());
+                rv.append(" ");
+                rv.append(System.getProperty("java.vm.name"));
+                rv.append("/");
+                rv.append(System.getProperty("java.vm.version"));
+                rv.append(" ");
+                rv.append(System.getProperty("os.name"));
+                retVal = rv.toString();
+            }
+        } else if (name.equalsIgnoreCase("UNIQUE_ID")) {
+            retVal = req.getRequestedSessionId();
+        }
+        if (requiredParts != nameParts.length) return null;
+            return retVal;
+    }
+
+    public Date getCurrentDate() {
+        return new Date();
+    }
+
+
+    protected String nullToEmptyString(String string) {
+        String retVal = string;
+        if (retVal == null) {
+            retVal = "";
+        }
+        return retVal;
+    }
+
+
+    protected String getPathWithoutFileName(String servletPath) {
+        String retVal = null;
+        int lastSlash = servletPath.lastIndexOf('/');
+        if (lastSlash >= 0) {
+            //cut off file namee
+            retVal = servletPath.substring(0, lastSlash + 1);
+        }
+        return retVal;
+    }
+
+
+    protected String getPathWithoutContext(String servletPath) {
+        String retVal = null;
+        int secondSlash = servletPath.indexOf('/', 1);
+        if (secondSlash >= 0) {
+            //cut off context
+            retVal = servletPath.substring(secondSlash);
+        }
+        return retVal;
+    }
+
+
+    protected String getAbsolutePath(String path) throws IOException {
+        String pathWithoutContext = SSIServletRequestUtil.getRelativePath(req);
+        String prefix = getPathWithoutFileName(pathWithoutContext);
+        if (prefix == null) {
+            throw new IOException("Couldn't remove filename from path: "
+                    + pathWithoutContext);
+        }
+        String fullPath = prefix + path;
+        String retVal = SSIServletRequestUtil.normalize(fullPath);
+        if (retVal == null) {
+            throw new IOException("Normalization yielded null on path: "
+                    + fullPath);
+        }
+        return retVal;
+    }
+
+
+    protected ServletContextAndPath getServletContextAndPathFromNonVirtualPath(
+            String nonVirtualPath) throws IOException {
+        if (nonVirtualPath.startsWith("/") || nonVirtualPath.startsWith("\\")) {
+            throw new IOException("A non-virtual path can't be absolute: "
+                    + nonVirtualPath);
+        }
+        if (nonVirtualPath.indexOf("../") >= 0) {
+            throw new IOException("A non-virtual path can't contain '../' : "
+                    + nonVirtualPath);
+        }
+        String path = getAbsolutePath(nonVirtualPath);
+        ServletContextAndPath csAndP = new ServletContextAndPath(
+                context, path);
+        return csAndP;
+    }
+
+
+    protected ServletContextAndPath getServletContextAndPathFromVirtualPath(
+            String virtualPath) throws IOException {
+
+        if (!virtualPath.startsWith("/") && !virtualPath.startsWith("\\")) {
+            return new ServletContextAndPath(context,
+                    getAbsolutePath(virtualPath));
+        } else {
+            String normalized = SSIServletRequestUtil.normalize(virtualPath);
+            if (isVirtualWebappRelative) {
+                return new ServletContextAndPath(context, normalized);
+            } else {
+                ServletContext normContext = context.getContext(normalized);
+                if (normContext == null) {
+                    throw new IOException("Couldn't get context for path: "
+                            + normalized);
+                }
+                //If it's the root context, then there is no context element
+                // to remove,
+                // ie:
+                // '/file1.shtml' vs '/appName1/file1.shtml'
+                if (!isRootContext(normContext)) {
+                    String noContext = getPathWithoutContext(normalized);
+                    if (noContext == null) {
+                        throw new IOException(
+                                "Couldn't remove context from path: "
+                                        + normalized);
+                    }
+                    return new ServletContextAndPath(normContext, noContext);
+                } else {
+                    return new ServletContextAndPath(normContext, normalized);
+                }
+            }
+        }
+    }
+
+
+    //Assumes servletContext is not-null
+    //Assumes that identity comparison will be true for the same context
+    //Assuming the above, getContext("/") will be non-null as long as the root
+    // context is
+    // accessible.
+    //If it isn't, then servletContext can't be the root context anyway, hence
+    // they will
+    // not match.
+    protected boolean isRootContext(ServletContext servletContext) {
+        return servletContext == servletContext.getContext("/");
+    }
+
+
+    protected ServletContextAndPath getServletContextAndPath(
+            String originalPath, boolean virtual) throws IOException {
+        ServletContextAndPath csAndP = null;
+        if (debug > 0) {
+            log("SSIServletExternalResolver.getServletContextAndPath( "
+                    + originalPath + ", " + virtual + ")", null);
+        }
+        if (virtual) {
+            csAndP = getServletContextAndPathFromVirtualPath(originalPath);
+        } else {
+            csAndP = getServletContextAndPathFromNonVirtualPath(originalPath);
+        }
+        return csAndP;
+    }
+
+
+    protected URLConnection getURLConnection(String originalPath,
+            boolean virtual) throws IOException {
+        ServletContextAndPath csAndP = getServletContextAndPath(originalPath,
+                virtual);
+        ServletContext context = csAndP.getServletContext();
+        String path = csAndP.getPath();
+        URL url = context.getResource(path);
+        if (url == null) {
+            throw new IOException("Context did not contain resource: " + path);
+        }
+        URLConnection urlConnection = url.openConnection();
+        return urlConnection;
+    }
+
+
+    public long getFileLastModified(String path, boolean virtual)
+            throws IOException {
+        long lastModified = 0;
+        try {
+            URLConnection urlConnection = getURLConnection(path, virtual);
+            lastModified = urlConnection.getLastModified();
+        } catch (IOException e) {
+            // Ignore this. It will always fail for non-file based includes
+        }
+        return lastModified;
+    }
+
+
+    public long getFileSize(String path, boolean virtual) throws IOException {
+        long fileSize = -1;
+        try {
+            URLConnection urlConnection = getURLConnection(path, virtual);
+            fileSize = urlConnection.getContentLength();
+        } catch (IOException e) {
+            // Ignore this. It will always fail for non-file based includes
+        }
+        return fileSize;
+    }
+
+
+    //We are making lots of unnecessary copies of the included data here. If
+    //someone ever complains that this is slow, we should connect the included
+    // stream to the print writer that SSICommand uses.
+    public String getFileText(String originalPath, boolean virtual)
+            throws IOException {
+        try {
+            ServletContextAndPath csAndP = getServletContextAndPath(
+                    originalPath, virtual);
+            ServletContext context = csAndP.getServletContext();
+            String path = csAndP.getPath();
+            RequestDispatcher rd = context.getRequestDispatcher(path);
+            if (rd == null) {
+                throw new IOException(
+                        "Couldn't get request dispatcher for path: " + path);
+            }
+            ByteArrayServletOutputStream basos =
+                new ByteArrayServletOutputStream();
+            ResponseIncludeWrapper responseIncludeWrapper =
+                new ResponseIncludeWrapper(context, req, res, basos);
+            rd.include(req, responseIncludeWrapper);
+            //We can't assume the included servlet flushed its output
+            responseIncludeWrapper.flushOutputStreamOrWriter();
+            byte[] bytes = basos.toByteArray();
+
+            //Assume platform default encoding unless otherwise specified
+            String retVal;
+            if (inputEncoding == null) {
+                retVal = new String( bytes );
+            } else {
+                retVal = new String (bytes, inputEncoding);
+            }
+
+            //make an assumption that an empty response is a failure. This is
+            // a problem
+            // if a truly empty file
+            //were included, but not sure how else to tell.
+            if (retVal.equals("")) {
+                throw new IOException("Couldn't find file: " + path);
+            }
+            return retVal;
+        } catch (ServletException e) {
+            throw new IOException("Couldn't include file: " + originalPath
+                    + " because of ServletException: " + e.getMessage());
+        }
+    }
+
+    protected class ServletContextAndPath {
+        protected ServletContext servletContext;
+        protected String path;
+
+
+        public ServletContextAndPath(ServletContext servletContext,
+                                     String path) {
+            this.servletContext = servletContext;
+            this.path = path;
+        }
+
+
+        public ServletContext getServletContext() {
+            return servletContext;
+        }
+
+
+        public String getPath() {
+            return path;
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIServletRequestUtil.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIServletRequestUtil.java
new file mode 100644
index 0000000..2a227e8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIServletRequestUtil.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.catalina.util.RequestUtil;
+public class SSIServletRequestUtil {
+    /**
+     * Return the relative path associated with this servlet. Taken from
+     * DefaultServlet.java. Perhaps this should be put in
+     * org.apache.catalina.util somewhere? Seems like it would be widely used.
+     * 
+     * @param request
+     *            The servlet request we are processing
+     */
+    public static String getRelativePath(HttpServletRequest request) {
+        // Are we being processed by a RequestDispatcher.include()?
+        if (request.getAttribute("javax.servlet.include.request_uri") != null) {
+            String result = (String)request
+                    .getAttribute("javax.servlet.include.path_info");
+            if (result == null)
+                result = (String)request
+                        .getAttribute("javax.servlet.include.servlet_path");
+            if ((result == null) || (result.equals(""))) result = "/";
+            return (result);
+        }
+        // No, extract the desired path directly from the request
+        String result = request.getPathInfo();
+        if (result == null) {
+            result = request.getServletPath();
+        }
+        if ((result == null) || (result.equals(""))) {
+            result = "/";
+        }
+        return normalize(result);
+    }
+
+
+    /**
+     * Return a context-relative path, beginning with a "/", that represents
+     * the canonical version of the specified path after ".." and "." elements
+     * are resolved out. If the specified path attempts to go outside the
+     * boundaries of the current context (i.e. too many ".." path elements are
+     * present), return <code>null</code> instead. This normalize should be
+     * the same as DefaultServlet.normalize, which is almost the same ( see
+     * source code below ) as RequestUtil.normalize. Do we need all this
+     * duplication?
+     * 
+     * @param path
+     *            Path to be normalized
+     */
+    public static String normalize(String path) {
+        if (path == null) return null;
+        String normalized = path;
+        //Why doesn't RequestUtil do this??
+        // Normalize the slashes and add leading slash if necessary
+        if (normalized.indexOf('\\') >= 0)
+            normalized = normalized.replace('\\', '/');
+        normalized = RequestUtil.normalize(path);
+        return normalized;
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSISet.java b/container/catalina/src/share/org/apache/catalina/ssi/SSISet.java
new file mode 100644
index 0000000..ea9f871
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSISet.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.PrintWriter;
+/**
+ * Implements the Server-side #set command
+ * 
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision$, $Date$
+ */
+public class SSISet implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer)
+            throws SSIStopProcessingException {
+        long lastModified = 0;
+        String errorMessage = ssiMediator.getConfigErrMsg();
+        String variableName = null;
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            if (paramName.equalsIgnoreCase("var")) {
+                variableName = paramValue;
+            } else if (paramName.equalsIgnoreCase("value")) {
+                if (variableName != null) {
+                    String substitutedValue = ssiMediator
+                            .substituteVariables(paramValue);
+                    ssiMediator.setVariableValue(variableName,
+                            substitutedValue);
+                    lastModified = System.currentTimeMillis();
+                } else {
+                    ssiMediator.log("#set--no variable specified");
+                    writer.write(errorMessage);
+                    throw new SSIStopProcessingException();
+                }
+            } else {
+                ssiMediator.log("#set--Invalid attribute: " + paramName);
+                writer.write(errorMessage);
+                throw new SSIStopProcessingException();
+            }
+        }
+        return lastModified;
+    }
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/SSIStopProcessingException.java b/container/catalina/src/share/org/apache/catalina/ssi/SSIStopProcessingException.java
new file mode 100644
index 0000000..3f62bca
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/SSIStopProcessingException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+/**
+ * Exception used to tell SSIProcessor that it should stop processing SSI
+ * commands. This is used to mimick the Apache behavior in #set with invalid
+ * attributes.
+ * 
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @version $Revision$, $Date$
+ */
+public class SSIStopProcessingException extends Exception {
+}
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/ssi/package.html b/container/catalina/src/share/org/apache/catalina/ssi/package.html
new file mode 100644
index 0000000..9d6f011
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/ssi/package.html
@@ -0,0 +1,16 @@
+<body>
+<p>This package contains code that is used by the SsiInvoker.</p>
+<p>This class consists of <code>SsiMediator.java</code> which works as a
+mediator between the different SsiCommands. To add a command you have to
+implement the <code>SsiCommand</code> interface and extend the
+<code>SsiMediator</code>. Commands currently implemented are</p>
+
+<ul>
+<li><b>SsiConfig</b> - Implementation of the NCSA command Config i.e. &lt;!--#config errmsg="error?"--&gt;</li>
+<li><b>SsiEcho</b> - Implementation of the NCSA command Echo i.e. &lt;!--#echo var="SERVER_NAME"--&gt;</li>
+<li><b>SsiExec</b> - Not implemented</li>
+<li><b>SsiFlastMod</b> - Implementation of the NCSA command flastmod i.e. &lt;!--#flastmod virtual="file"--&gt;</li>
+<li><b>SsiFsize</b> - Implementation of the NCSA command fsize i.e. &lt;!--#fsize file="file"--&gt;</li>
+<li><b>SsiInclude</b> - Implementation of the NCSA command Include i.e. &lt;!--#config virtual="includefile"--&gt;</li>
+</ul>
+</body>
diff --git a/container/catalina/src/share/org/apache/catalina/startup/Authenticators.properties b/container/catalina/src/share/org/apache/catalina/startup/Authenticators.properties
new file mode 100644
index 0000000..e5e623a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/Authenticators.properties
@@ -0,0 +1,5 @@
+BASIC=org.apache.catalina.authenticator.BasicAuthenticator
+CLIENT-CERT=org.apache.catalina.authenticator.SSLAuthenticator
+DIGEST=org.apache.catalina.authenticator.DigestAuthenticator
+FORM=org.apache.catalina.authenticator.FormAuthenticator
+NONE=org.apache.catalina.authenticator.NonLoginAuthenticator
diff --git a/container/catalina/src/share/org/apache/catalina/startup/Bootstrap.java b/container/catalina/src/share/org/apache/catalina/startup/Bootstrap.java
new file mode 100644
index 0000000..f59a45a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/Bootstrap.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.apache.catalina.security.SecurityClassLoad;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Boostrap loader for Catalina.  This application constructs a class loader
+ * for use in loading the Catalina internal classes (by accumulating all of the
+ * JAR files found in the "server" directory under "catalina.home"), and
+ * starts the regular execution of the container.  The purpose of this
+ * roundabout approach is to keep the Catalina internal classes (and any
+ * other classes they depend on, such as an XML parser) out of the system
+ * class path and therefore not visible to application level classes.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public final class Bootstrap {
+
+    private static Log log = LogFactory.getLog(Bootstrap.class);
+    
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final String CATALINA_HOME_TOKEN = "${catalina.home}";
+    protected static final String CATALINA_BASE_TOKEN = "${catalina.base}";
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    private static final String JMX_ERROR_MESSAGE =
+        "This release of Apache Tomcat was packaged to run on J2SE 5.0 \n"
+        + "or later. It can be run on earlier JVMs by downloading and \n"
+        + "installing a compatibility package from the Apache Tomcat \n"
+        + "binary download page.";
+
+
+    /**
+     * Daemon object used by main.
+     */
+    private static Bootstrap daemon = null;
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Daemon reference.
+     */
+    private Object catalinaDaemon = null;
+
+
+    protected ClassLoader commonLoader = null;
+    protected ClassLoader catalinaLoader = null;
+    protected ClassLoader sharedLoader = null;
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    private void initClassLoaders() {
+        try {
+            commonLoader = createClassLoader("common", null);
+            catalinaLoader = createClassLoader("server", commonLoader);
+            sharedLoader = createClassLoader("shared", commonLoader);
+        } catch (Throwable t) {
+            log.error("Class loader creation threw exception", t);
+            System.exit(1);
+        }
+    }
+
+
+    private ClassLoader createClassLoader(String name, ClassLoader parent)
+        throws Exception {
+
+        String value = CatalinaProperties.getProperty(name + ".loader");
+        if ((value == null) || (value.equals("")))
+            return parent;
+
+        ArrayList unpackedList = new ArrayList();
+        ArrayList packedList = new ArrayList();
+        ArrayList urlList = new ArrayList();
+
+        StringTokenizer tokenizer = new StringTokenizer(value, ",");
+        while (tokenizer.hasMoreElements()) {
+            String repository = tokenizer.nextToken();
+
+            // Local repository
+            boolean packed = false;
+            if (repository.startsWith(CATALINA_HOME_TOKEN)) {
+                repository = getCatalinaHome() 
+                    + repository.substring(CATALINA_HOME_TOKEN.length());
+            } else if (repository.startsWith(CATALINA_BASE_TOKEN)) {
+                repository = getCatalinaBase() 
+                    + repository.substring(CATALINA_BASE_TOKEN.length());
+            }
+
+            // Check for a JAR URL repository
+            try {
+                urlList.add(new URL(repository));
+                continue;
+            } catch (MalformedURLException e) {
+                // Ignore
+            }
+
+            if (repository.endsWith("*.jar")) {
+                packed = true;
+                repository = repository.substring
+                    (0, repository.length() - "*.jar".length());
+            }
+            if (packed) {
+                packedList.add(new File(repository));
+            } else {
+                unpackedList.add(new File(repository));
+            }
+        }
+
+        File[] unpacked = (File[]) unpackedList.toArray(new File[0]);
+        File[] packed = (File[]) packedList.toArray(new File[0]);
+        URL[] urls = (URL[]) urlList.toArray(new URL[0]);
+
+        ClassLoader classLoader = ClassLoaderFactory.createClassLoader
+            (unpacked, packed, urls, parent);
+
+        // Retrieving MBean server
+        MBeanServer mBeanServer = null;
+        if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
+            mBeanServer = 
+                (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
+        } else {
+            mBeanServer = MBeanServerFactory.createMBeanServer();
+        }
+
+        // Register the server classloader
+        ObjectName objectName = 
+            new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
+        mBeanServer.registerMBean(classLoader, objectName);
+
+        return classLoader;
+
+    }
+
+
+    /**
+     * Initialize daemon.
+     */
+    public void init()
+        throws Exception
+    {
+
+        // Set Catalina path
+        setCatalinaHome();
+        setCatalinaBase();
+
+        initClassLoaders();
+
+        Thread.currentThread().setContextClassLoader(catalinaLoader);
+
+        SecurityClassLoad.securityClassLoad(catalinaLoader);
+
+        // Load our startup class and call its process() method
+        if (log.isDebugEnabled())
+            log.debug("Loading startup class");
+        Class startupClass =
+            catalinaLoader.loadClass
+            ("org.apache.catalina.startup.Catalina");
+        Object startupInstance = startupClass.newInstance();
+
+        // Set the shared extensions class loader
+        if (log.isDebugEnabled())
+            log.debug("Setting startup class properties");
+        String methodName = "setParentClassLoader";
+        Class paramTypes[] = new Class[1];
+        paramTypes[0] = Class.forName("java.lang.ClassLoader");
+        Object paramValues[] = new Object[1];
+        paramValues[0] = sharedLoader;
+        Method method =
+            startupInstance.getClass().getMethod(methodName, paramTypes);
+        method.invoke(startupInstance, paramValues);
+
+        catalinaDaemon = startupInstance;
+
+    }
+
+
+    /**
+     * Load daemon.
+     */
+    private void load(String[] arguments)
+        throws Exception {
+
+        // Call the load() method
+        String methodName = "load";
+        Object param[];
+        Class paramTypes[];
+        if (arguments==null || arguments.length==0) {
+            paramTypes = null;
+            param = null;
+        } else {
+            paramTypes = new Class[1];
+            paramTypes[0] = arguments.getClass();
+            param = new Object[1];
+            param[0] = arguments;
+        }
+        Method method = 
+            catalinaDaemon.getClass().getMethod(methodName, paramTypes);
+        if (log.isDebugEnabled())
+            log.debug("Calling startup class " + method);
+        method.invoke(catalinaDaemon, param);
+
+    }
+
+
+    // ----------------------------------------------------------- Main Program
+
+
+    /**
+     * Load the Catalina daemon.
+     */
+    public void init(String[] arguments)
+        throws Exception {
+
+        init();
+        load(arguments);
+
+    }
+
+
+    /**
+     * Start the Catalina daemon.
+     */
+    public void start()
+        throws Exception {
+        if( catalinaDaemon==null ) init();
+
+        Method method = catalinaDaemon.getClass().getMethod("start", null);
+        method.invoke(catalinaDaemon, null);
+
+    }
+
+
+    /**
+     * Stop the Catalina Daemon.
+     */
+    public void stop()
+        throws Exception {
+
+        Method method = catalinaDaemon.getClass().getMethod("stop", null);
+        method.invoke(catalinaDaemon, null);
+
+    }
+
+
+    /**
+     * Stop the standlone server.
+     */
+    public void stopServer()
+        throws Exception {
+
+        Method method = 
+            catalinaDaemon.getClass().getMethod("stopServer", null);
+        method.invoke(catalinaDaemon, null);
+
+    }
+
+
+   /**
+     * Stop the standlone server.
+     */
+    public void stopServer(String[] arguments)
+        throws Exception {
+
+        Object param[];
+        Class paramTypes[];
+        if (arguments==null || arguments.length==0) {
+            paramTypes = null;
+            param = null;
+        } else {
+            paramTypes = new Class[1];
+            paramTypes[0] = arguments.getClass();
+            param = new Object[1];
+            param[0] = arguments;
+        }
+        Method method = 
+            catalinaDaemon.getClass().getMethod("stopServer", paramTypes);
+        method.invoke(catalinaDaemon, param);
+
+    }
+
+
+    /**
+     * Set flag.
+     */
+    public void setAwait(boolean await)
+        throws Exception {
+
+        Class paramTypes[] = new Class[1];
+        paramTypes[0] = Boolean.TYPE;
+        Object paramValues[] = new Object[1];
+        paramValues[0] = new Boolean(await);
+        Method method = 
+            catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
+        method.invoke(catalinaDaemon, paramValues);
+
+    }
+
+    public boolean getAwait()
+        throws Exception
+    {
+        Class paramTypes[] = new Class[0];
+        Object paramValues[] = new Object[0];
+        Method method =
+            catalinaDaemon.getClass().getMethod("getAwait", paramTypes);
+        Boolean b=(Boolean)method.invoke(catalinaDaemon, paramValues);
+        return b.booleanValue();
+    }
+
+
+    /**
+     * Destroy the Catalina Daemon.
+     */
+    public void destroy() {
+
+        // FIXME
+
+    }
+
+
+    /**
+     * Main method, used for testing only.
+     *
+     * @param args Command line arguments to be processed
+     */
+    public static void main(String args[]) {
+
+        try {
+            // Attempt to load JMX class
+            new ObjectName("test:foo=bar");
+        } catch (Throwable t) {
+            System.out.println(JMX_ERROR_MESSAGE);
+            try {
+                // Give users some time to read the message before exiting
+                Thread.sleep(5000);
+            } catch (Exception ex) {
+            }
+            return;
+        }
+
+        if (daemon == null) {
+            daemon = new Bootstrap();
+            try {
+                daemon.init();
+            } catch (Throwable t) {
+                t.printStackTrace();
+                return;
+            }
+        }
+
+        try {
+            String command = "start";
+            if (args.length > 0) {
+                command = args[args.length - 1];
+            }
+
+            if (command.equals("startd")) {
+                args[0] = "start";
+                daemon.load(args);
+                daemon.start();
+            } else if (command.equals("stopd")) {
+                args[0] = "stop";
+                daemon.stop();
+            } else if (command.equals("start")) {
+                daemon.setAwait(true);
+                daemon.load(args);
+                daemon.start();
+            } else if (command.equals("stop")) {
+                daemon.stopServer(args);
+            } else {
+                log.warn("Bootsrap: command \"" + command + "\" does not exist.");
+            }
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+
+    }
+
+    public void setCatalinaHome(String s) {
+        System.setProperty( "catalina.home", s );
+    }
+
+    public void setCatalinaBase(String s) {
+        System.setProperty( "catalina.base", s );
+    }
+
+
+    /**
+     * Set the <code>catalina.base</code> System property to the current
+     * working directory if it has not been set.
+     */
+    private void setCatalinaBase() {
+
+        if (System.getProperty("catalina.base") != null)
+            return;
+        if (System.getProperty("catalina.home") != null)
+            System.setProperty("catalina.base",
+                               System.getProperty("catalina.home"));
+        else
+            System.setProperty("catalina.base",
+                               System.getProperty("user.dir"));
+
+    }
+
+
+    /**
+     * Set the <code>catalina.home</code> System property to the current
+     * working directory if it has not been set.
+     */
+    private void setCatalinaHome() {
+
+        if (System.getProperty("catalina.home") != null)
+            return;
+        File bootstrapJar = 
+            new File(System.getProperty("user.dir"), "bootstrap.jar");
+        if (bootstrapJar.exists()) {
+            try {
+                System.setProperty
+                    ("catalina.home", 
+                     (new File(System.getProperty("user.dir"), ".."))
+                     .getCanonicalPath());
+            } catch (Exception e) {
+                // Ignore
+                System.setProperty("catalina.home",
+                                   System.getProperty("user.dir"));
+            }
+        } else {
+            System.setProperty("catalina.home",
+                               System.getProperty("user.dir"));
+        }
+
+    }
+
+
+    /**
+     * Get the value of the catalina.home environment variable.
+     */
+    public static String getCatalinaHome() {
+        return System.getProperty("catalina.home",
+                                  System.getProperty("user.dir"));
+    }
+
+
+    /**
+     * Get the value of the catalina.base environment variable.
+     */
+    public static String getCatalinaBase() {
+        return System.getProperty("catalina.base", getCatalinaHome());
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/Catalina.java b/container/catalina/src/share/org/apache/catalina/startup/Catalina.java
new file mode 100644
index 0000000..079bf26
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/Catalina.java
@@ -0,0 +1,677 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Server;
+import org.apache.catalina.core.StandardServer;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.apache.tomcat.util.log.SystemLogHandler;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+
+
+/**
+ * Startup/Shutdown shell program for Catalina.  The following command line
+ * options are recognized:
+ * <ul>
+ * <li><b>-config {pathname}</b> - Set the pathname of the configuration file
+ *     to be processed.  If a relative path is specified, it will be
+ *     interpreted as relative to the directory pathname specified by the
+ *     "catalina.base" system property.   [conf/server.xml]
+ * <li><b>-help</b> - Display usage information.
+ * <li><b>-stop</b> - Stop the currently running instance of Catalina.
+ * </u>
+ *
+ * Should do the same thing as Embedded, but using a server.xml file.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class Catalina extends Embedded {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Pathname to the server configuration file.
+     */
+    protected String configFile = "conf/server.xml";
+
+    // XXX Should be moved to embedded
+    /**
+     * The shared extensions class loader for this server.
+     */
+    protected ClassLoader parentClassLoader =
+        Catalina.class.getClassLoader();
+
+
+    /**
+     * The server component we are starting or stopping
+     */
+    protected Server server = null;
+
+
+    /**
+     * Are we starting a new server?
+     */
+    protected boolean starting = false;
+
+
+    /**
+     * Are we stopping an existing server?
+     */
+    protected boolean stopping = false;
+
+
+    /**
+     * Use shutdown hook flag.
+     */
+    protected boolean useShutdownHook = true;
+
+
+    /**
+     * Shutdown hook.
+     */
+    protected Thread shutdownHook = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    public void setConfig(String file) {
+        configFile = file;
+    }
+
+
+    public void setConfigFile(String file) {
+        configFile = file;
+    }
+
+
+    public String getConfigFile() {
+        return configFile;
+    }
+
+
+    public void setUseShutdownHook(boolean useShutdownHook) {
+        this.useShutdownHook = useShutdownHook;
+    }
+
+
+    public boolean getUseShutdownHook() {
+        return useShutdownHook;
+    }
+
+
+    /**
+     * Set the shared extensions class loader.
+     *
+     * @param parentClassLoader The shared extensions class loader.
+     */
+    public void setParentClassLoader(ClassLoader parentClassLoader) {
+
+        this.parentClassLoader = parentClassLoader;
+
+    }
+
+
+    /**
+     * Set the server instance we are configuring.
+     *
+     * @param server The new server
+     */
+    public void setServer(Server server) {
+
+        this.server = server;
+
+    }
+
+    // ----------------------------------------------------------- Main Program
+
+    /**
+     * The application main program.
+     *
+     * @param args Command line arguments
+     */
+    public static void main(String args[]) {
+        (new Catalina()).process(args);
+    }
+
+
+    /**
+     * The instance main program.
+     *
+     * @param args Command line arguments
+     */
+    public void process(String args[]) {
+
+        setAwait(true);
+        setCatalinaHome();
+        setCatalinaBase();
+        try {
+            if (arguments(args)) {
+                if (starting) {
+                    load(args);
+                    start();
+                } else if (stopping) {
+                    stopServer();
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Process the specified command line arguments, and return
+     * <code>true</code> if we should continue processing; otherwise
+     * return <code>false</code>.
+     *
+     * @param args Command line arguments to process
+     */
+    protected boolean arguments(String args[]) {
+
+        boolean isConfig = false;
+
+        if (args.length < 1) {
+            usage();
+            return (false);
+        }
+
+        for (int i = 0; i < args.length; i++) {
+            if (isConfig) {
+                configFile = args[i];
+                isConfig = false;
+            } else if (args[i].equals("-config")) {
+                isConfig = true;
+            } else if (args[i].equals("-nonaming")) {
+                setUseNaming( false );
+            } else if (args[i].equals("-help")) {
+                usage();
+                return (false);
+            } else if (args[i].equals("start")) {
+                starting = true;
+                stopping = false;
+            } else if (args[i].equals("stop")) {
+                starting = false;
+                stopping = true;
+            } else {
+                usage();
+                return (false);
+            }
+        }
+
+        return (true);
+
+    }
+
+
+    /**
+     * Return a File object representing our configuration file.
+     */
+    protected File configFile() {
+
+        File file = new File(configFile);
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"), configFile);
+        return (file);
+
+    }
+
+
+    /**
+     * Create and configure the Digester we will be using for startup.
+     */
+    protected Digester createStartDigester() {
+        long t1=System.currentTimeMillis();
+        // Initialize the digester
+        Digester digester = new Digester();
+        digester.setValidating(false);
+        digester.setClassLoader(StandardServer.class.getClassLoader());
+
+        // Configure the actions we will be using
+        digester.addObjectCreate("Server",
+                                 "org.apache.catalina.core.StandardServer",
+                                 "className");
+        digester.addSetProperties("Server");
+        digester.addSetNext("Server",
+                            "setServer",
+                            "org.apache.catalina.Server");
+
+        digester.addObjectCreate("Server/GlobalNamingResources",
+                                 "org.apache.catalina.deploy.NamingResources");
+        digester.addSetProperties("Server/GlobalNamingResources");
+        digester.addSetNext("Server/GlobalNamingResources",
+                            "setGlobalNamingResources",
+                            "org.apache.catalina.deploy.NamingResources");
+
+        digester.addObjectCreate("Server/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties("Server/Listener");
+        digester.addSetNext("Server/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        digester.addObjectCreate("Server/Service",
+                                 "org.apache.catalina.core.StandardService",
+                                 "className");
+        digester.addSetProperties("Server/Service");
+        digester.addSetNext("Server/Service",
+                            "addService",
+                            "org.apache.catalina.Service");
+
+        digester.addObjectCreate("Server/Service/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties("Server/Service/Listener");
+        digester.addSetNext("Server/Service/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        digester.addRule("Server/Service/Connector",
+                         new ConnectorCreateRule());
+        digester.addRule("Server/Service/Connector", 
+                         new SetAllPropertiesRule());
+        digester.addSetNext("Server/Service/Connector",
+                            "addConnector",
+                            "org.apache.catalina.connector.Connector");
+
+        digester.addObjectCreate("Server/Service/Connector/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties("Server/Service/Connector/Listener");
+        digester.addSetNext("Server/Service/Connector/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        // Add RuleSets for nested elements
+        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
+        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
+        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
+        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
+        digester.addRuleSet(new ClusterRuleSet("Server/Service/Engine/Host/Cluster/"));
+        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
+
+        // When the 'engine' is found, set the parentClassLoader.
+        digester.addRule("Server/Service/Engine",
+                         new SetParentClassLoaderRule(parentClassLoader));
+        digester.addRuleSet(new ClusterRuleSet("Server/Service/Engine/Cluster/"));
+
+        long t2=System.currentTimeMillis();
+        if (log.isDebugEnabled())
+            log.debug("Digester for server.xml created " + ( t2-t1 ));
+        return (digester);
+
+    }
+
+
+    /**
+     * Create and configure the Digester we will be using for shutdown.
+     */
+    protected Digester createStopDigester() {
+
+        // Initialize the digester
+        Digester digester = new Digester();
+
+        // Configure the rules we need for shutting down
+        digester.addObjectCreate("Server",
+                                 "org.apache.catalina.core.StandardServer",
+                                 "className");
+        digester.addSetProperties("Server");
+        digester.addSetNext("Server",
+                            "setServer",
+                            "org.apache.catalina.Server");
+
+        return (digester);
+
+    }
+
+
+    public void stopServer() {
+        stopServer(null);
+    }
+
+    public void stopServer(String[] arguments) {
+
+        if (arguments != null) {
+            arguments(arguments);
+        }
+
+        if( server == null ) {
+            // Create and execute our Digester
+            Digester digester = createStopDigester();
+            digester.setClassLoader(Thread.currentThread().getContextClassLoader());
+            File file = configFile();
+            try {
+                InputSource is =
+                    new InputSource("file://" + file.getAbsolutePath());
+                FileInputStream fis = new FileInputStream(file);
+                is.setByteStream(fis);
+                digester.push(this);
+                digester.parse(is);
+                fis.close();
+            } catch (Exception e) {
+                log.error("Catalina.stop: ", e);
+                System.exit(1);
+            }
+        }
+
+        // Stop the existing server
+        try {
+            Socket socket = new Socket("127.0.0.1", server.getPort());
+            OutputStream stream = socket.getOutputStream();
+            String shutdown = server.getShutdown();
+            for (int i = 0; i < shutdown.length(); i++)
+                stream.write(shutdown.charAt(i));
+            stream.flush();
+            stream.close();
+            socket.close();
+        } catch (IOException e) {
+            log.error("Catalina.stop: ", e);
+            System.exit(1);
+        }
+
+    }
+
+
+    /**
+     * Set the <code>catalina.base</code> System property to the current
+     * working directory if it has not been set.
+     * @deprecated Use initDirs()
+     */
+    public void setCatalinaBase() {
+        initDirs();
+    }
+
+    /**
+     * Set the <code>catalina.home</code> System property to the current
+     * working directory if it has not been set.
+     * @deprecated Use initDirs()
+     */
+    public void setCatalinaHome() {
+        initDirs();
+    }
+
+    /**
+     * Start a new server instance.
+     */
+    public void load() {
+
+        initDirs();
+
+        // Before digester - it may be needed
+
+        initNaming();
+
+        // Create and execute our Digester
+        Digester digester = createStartDigester();
+        long t1 = System.currentTimeMillis();
+
+        Exception ex = null;
+        InputSource inputSource = null;
+        InputStream inputStream = null;
+        File file = null;
+        try {
+            file = configFile();
+            inputStream = new FileInputStream(file);
+            inputSource = new InputSource("file://" + file.getAbsolutePath());
+        } catch (Exception e) {
+            ;
+        }
+        if (inputStream == null) {
+            try {
+                inputStream = getClass().getClassLoader()
+                    .getResourceAsStream(getConfigFile());
+                inputSource = new InputSource
+                    (getClass().getClassLoader()
+                     .getResource(getConfigFile()).toString());
+            } catch (Exception e) {
+                ;
+            }
+        }
+
+        if ((inputStream == null) && (file != null)) {
+            log.warn("Can't load server.xml from " + file.getAbsolutePath());
+            return;
+        }
+
+        try {
+            inputSource.setByteStream(inputStream);
+            digester.push(this);
+            digester.parse(inputSource);
+            inputStream.close();
+        } catch (Exception e) {
+            log.warn("Catalina.start using "
+                               + getConfigFile() + ": " , e);
+            return;
+        }
+
+        // Replace System.out and System.err with a custom PrintStream
+        // TODO: move to Embedded, make it configurable
+        SystemLogHandler systemlog = new SystemLogHandler(System.out);
+        System.setOut(systemlog);
+        System.setErr(systemlog);
+
+        // Start the new server
+        if (server instanceof Lifecycle) {
+            try {
+                server.initialize();
+            } catch (LifecycleException e) {
+                log.error("Catalina.start", e);
+            }
+        }
+
+        long t2 = System.currentTimeMillis();
+        if(log.isInfoEnabled())
+            log.info("Initialization processed in " + (t2 - t1) + " ms");
+
+    }
+
+
+    /* 
+     * Load using arguments
+     */
+    public void load(String args[]) {
+
+        try {
+            if (arguments(args))
+                load();
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+    }
+
+    public void create() {
+
+    }
+
+    public void destroy() {
+
+    }
+
+    /**
+     * Start a new server instance.
+     */
+    public void start() {
+
+        if (server == null) {
+            load();
+        }
+
+        long t1 = System.currentTimeMillis();
+
+        // Start the new server
+        if (server instanceof Lifecycle) {
+            try {
+                ((Lifecycle) server).start();
+            } catch (LifecycleException e) {
+                log.error("Catalina.start: ", e);
+            }
+        }
+
+        long t2 = System.currentTimeMillis();
+        if(log.isInfoEnabled())
+            log.info("Server startup in " + (t2 - t1) + " ms");
+
+        try {
+            // Register shutdown hook
+            if (useShutdownHook) {
+                if (shutdownHook == null) {
+                    shutdownHook = new CatalinaShutdownHook();
+                }
+                Runtime.getRuntime().addShutdownHook(shutdownHook);
+            }
+        } catch (Throwable t) {
+            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
+            // fine without the shutdown hook.
+        }
+
+        if (await) {
+            await();
+            stop();
+        }
+
+    }
+
+
+    /**
+     * Stop an existing server instance.
+     */
+    public void stop() {
+
+        try {
+            // Remove the ShutdownHook first so that server.stop() 
+            // doesn't get invoked twice
+            if (useShutdownHook) {
+                Runtime.getRuntime().removeShutdownHook(shutdownHook);
+            }
+        } catch (Throwable t) {
+            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
+            // fine without the shutdown hook.
+        }
+
+        // Shut down the server
+        if (server instanceof Lifecycle) {
+            try {
+                ((Lifecycle) server).stop();
+            } catch (LifecycleException e) {
+                log.error("Catalina.stop", e);
+            }
+        }
+
+    }
+
+
+    /**
+     * Await and shutdown.
+     */
+    public void await() {
+
+        server.await();
+
+    }
+
+
+    /**
+     * Print usage information for this application.
+     */
+    protected void usage() {
+
+        System.out.println
+            ("usage: java org.apache.catalina.startup.Catalina"
+             + " [ -config {pathname} ]"
+             + " [ -nonaming ] { start | stop }");
+
+    }
+
+
+    // --------------------------------------- CatalinaShutdownHook Inner Class
+
+    // XXX Should be moved to embedded !
+    /**
+     * Shutdown hook which will perform a clean shutdown of Catalina if needed.
+     */
+    protected class CatalinaShutdownHook extends Thread {
+
+        public void run() {
+
+            if (server != null) {
+                Catalina.this.stop();
+            }
+            
+        }
+
+    }
+    
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( Catalina.class );
+
+}
+
+
+// ------------------------------------------------------------ Private Classes
+
+
+/**
+ * Rule that sets the parent class loader for the top object on the stack,
+ * which must be a <code>Container</code>.
+ */
+
+final class SetParentClassLoaderRule extends Rule {
+
+    public SetParentClassLoaderRule(ClassLoader parentClassLoader) {
+
+        this.parentClassLoader = parentClassLoader;
+
+    }
+
+    ClassLoader parentClassLoader = null;
+
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+
+        if (digester.getLogger().isDebugEnabled())
+            digester.getLogger().debug("Setting parent class loader");
+
+        Container top = (Container) digester.peek();
+        top.setParentClassLoader(parentClassLoader);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/CatalinaProperties.java b/container/catalina/src/share/org/apache/catalina/startup/CatalinaProperties.java
new file mode 100644
index 0000000..8f0aea6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/CatalinaProperties.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+
+
+/**
+ * Utility class to read the bootstrap Catalina configuration.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class CatalinaProperties {
+
+
+    // ------------------------------------------------------- Static Variables
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( CatalinaProperties.class );
+
+    private static Properties properties = null;
+
+
+    static {
+
+        loadProperties();
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return specified property value.
+     */
+    public static String getProperty(String name) {
+
+        return properties.getProperty(name);
+
+    }
+
+
+    /**
+     * Return specified property value.
+     */
+    public static String getProperty(String name, String defaultValue) {
+
+        return properties.getProperty(name, defaultValue);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Load properties.
+     */
+    private static void loadProperties() {
+
+        InputStream is = null;
+        Throwable error = null;
+
+        try {
+            String configUrl = getConfigUrl();
+            if (configUrl != null) {
+                is = (new URL(configUrl)).openStream();
+            }
+        } catch (Throwable t) {
+            // Ignore
+        }
+
+        if (is == null) {
+            try {
+                File home = new File(getCatalinaBase());
+                File conf = new File(home, "conf");
+                File properties = new File(conf, "catalina.properties");
+                is = new FileInputStream(properties);
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+
+        if (is == null) {
+            try {
+                is = CatalinaProperties.class.getResourceAsStream
+                    ("/org/apache/catalina/startup/catalina.properties");
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+
+        if (is != null) {
+            try {
+                properties = new Properties();
+                properties.load(is);
+                is.close();
+            } catch (Throwable t) {
+                error = t;
+            }
+        }
+
+        if ((is == null) || (error != null)) {
+            // Do something
+            log.warn("Failed to load catalina.properties", error);
+        }
+
+        // Register the properties as system properties
+        Enumeration enumeration = properties.propertyNames();
+        while (enumeration.hasMoreElements()) {
+            String name = (String) enumeration.nextElement();
+            String value = properties.getProperty(name);
+            if (value != null) {
+                System.setProperty(name, value);
+            }
+        }
+
+    }
+
+
+    /**
+     * Get the value of the catalina.home environment variable.
+     */
+    private static String getCatalinaHome() {
+        return System.getProperty("catalina.home",
+                                  System.getProperty("user.dir"));
+    }
+    
+    
+    /**
+     * Get the value of the catalina.base environment variable.
+     */
+    private static String getCatalinaBase() {
+        return System.getProperty("catalina.base", getCatalinaHome());
+    }
+
+
+    /**
+     * Get the value of the configuration URL.
+     */
+    private static String getConfigUrl() {
+        return System.getProperty("catalina.config");
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/ClassLoaderFactory.java b/container/catalina/src/share/org/apache/catalina/startup/ClassLoaderFactory.java
new file mode 100644
index 0000000..c18dd82
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/ClassLoaderFactory.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+
+import org.apache.catalina.loader.StandardClassLoader;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * <p>Utility class for building class loaders for Catalina.  The factory
+ * method requires the following parameters in order to build a new class
+ * loader (with suitable defaults in all cases):</p>
+ * <ul>
+ * <li>A set of directories containing unpacked classes (and resources)
+ *     that should be included in the class loader's
+ *     repositories.</li>
+ * <li>A set of directories containing classes and resources in JAR files.
+ *     Each readable JAR file discovered in these directories will be
+ *     added to the class loader's repositories.</li>
+ * <li><code>ClassLoader</code> instance that should become the parent of
+ *     the new class loader.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class ClassLoaderFactory {
+
+
+    private static Log log = LogFactory.getLog(ClassLoaderFactory.class);
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Create and return a new class loader, based on the configuration
+     * defaults and the specified directory paths:
+     *
+     * @param unpacked Array of pathnames to unpacked directories that should
+     *  be added to the repositories of the class loader, or <code>null</code> 
+     * for no unpacked directories to be considered
+     * @param packed Array of pathnames to directories containing JAR files
+     *  that should be added to the repositories of the class loader, 
+     * or <code>null</code> for no directories of JAR files to be considered
+     * @param parent Parent class loader for the new class loader, or
+     *  <code>null</code> for the system class loader.
+     *
+     * @exception Exception if an error occurs constructing the class loader
+     */
+    public static ClassLoader createClassLoader(File unpacked[],
+                                                File packed[],
+                                                ClassLoader parent)
+        throws Exception {
+        return createClassLoader(unpacked, packed, null, parent);
+    }
+
+
+    /**
+     * Create and return a new class loader, based on the configuration
+     * defaults and the specified directory paths:
+     *
+     * @param unpacked Array of pathnames to unpacked directories that should
+     *  be added to the repositories of the class loader, or <code>null</code> 
+     * for no unpacked directories to be considered
+     * @param packed Array of pathnames to directories containing JAR files
+     *  that should be added to the repositories of the class loader, 
+     * or <code>null</code> for no directories of JAR files to be considered
+     * @param urls Array of URLs to remote repositories, designing either JAR 
+     *  resources or uncompressed directories that should be added to 
+     *  the repositories of the class loader, or <code>null</code> for no 
+     *  directories of JAR files to be considered
+     * @param parent Parent class loader for the new class loader, or
+     *  <code>null</code> for the system class loader.
+     *
+     * @exception Exception if an error occurs constructing the class loader
+     */
+    public static ClassLoader createClassLoader(File unpacked[],
+                                                File packed[],
+                                                URL urls[],
+                                                ClassLoader parent)
+        throws Exception {
+
+        if (log.isDebugEnabled())
+            log.debug("Creating new class loader");
+
+        // Construct the "class path" for this class loader
+        ArrayList list = new ArrayList();
+
+        // Add unpacked directories
+        if (unpacked != null) {
+            for (int i = 0; i < unpacked.length; i++)  {
+                File file = unpacked[i];
+                if (!file.exists() || !file.canRead())
+                    continue;
+                file = new File(file.getCanonicalPath() + File.separator);
+                URL url = file.toURL();
+                if (log.isDebugEnabled())
+                    log.debug("  Including directory " + url);
+                list.add(url);
+            }
+        }
+
+        // Add packed directory JAR files
+        if (packed != null) {
+            for (int i = 0; i < packed.length; i++) {
+                File directory = packed[i];
+                if (!directory.isDirectory() || !directory.exists() ||
+                    !directory.canRead())
+                    continue;
+                String filenames[] = directory.list();
+                for (int j = 0; j < filenames.length; j++) {
+                    String filename = filenames[j].toLowerCase();
+                    if (!filename.endsWith(".jar"))
+                        continue;
+                    File file = new File(directory, filenames[j]);
+                    if (log.isDebugEnabled())
+                        log.debug("  Including jar file " + file.getAbsolutePath());
+                    URL url = file.toURL();
+                    list.add(url);
+                }
+            }
+        }
+
+        // Add URLs
+        if (urls != null) {
+            for (int i = 0; i < urls.length; i++) {
+                if (log.isDebugEnabled())
+                    log.debug("  Including URL " + urls[i]);
+                list.add(urls[i]);
+            }
+        }
+
+        // Construct the class loader itself
+        URL[] array = (URL[]) list.toArray(new URL[list.size()]);
+        StandardClassLoader classLoader = null;
+        if (parent == null)
+            classLoader = new StandardClassLoader(array);
+        else
+            classLoader = new StandardClassLoader(array, parent);
+        return (classLoader);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/ClusterRuleSet.java b/container/catalina/src/share/org/apache/catalina/startup/ClusterRuleSet.java
new file mode 100644
index 0000000..0c690a2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/ClusterRuleSet.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSetBase;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a
+ * Cluster definition element.  </p>
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class ClusterRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public ClusterRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public ClusterRuleSet(String prefix) {
+        super();
+        this.namespaceURI = null;
+        this.prefix = prefix;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+        //Cluster configuration start
+        digester.addObjectCreate(prefix + "Membership",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Membership");
+        digester.addSetNext(prefix + "Membership",
+                            "setMembershipService",
+                            "org.apache.catalina.cluster.MembershipService");
+        
+        digester.addObjectCreate(prefix + "Sender",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Sender");
+        digester.addSetNext(prefix + "Sender",
+                            "setClusterSender",
+                            "org.apache.catalina.cluster.ClusterSender");
+
+        digester.addObjectCreate(prefix + "Receiver",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Receiver");
+        digester.addSetNext(prefix + "Receiver",
+                            "setClusterReceiver",
+                            "org.apache.catalina.cluster.ClusterReceiver");
+
+        digester.addObjectCreate(prefix + "Valve",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Valve");
+        digester.addSetNext(prefix + "Valve",
+                            "addValve",
+                            "org.apache.catalina.Valve");
+        
+        digester.addObjectCreate(prefix + "Deployer",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Deployer");
+        digester.addSetNext(prefix + "Deployer",
+                            "setClusterDeployer",
+                            "org.apache.catalina.cluster.ClusterDeployer");
+        
+        digester.addObjectCreate(prefix + "Listener",
+                null, // MUST be specified in the element
+                "className");
+        digester.addSetProperties(prefix + "Listener");
+        digester.addSetNext(prefix + "Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+        
+        digester.addObjectCreate(prefix + "ClusterListener",
+                null, // MUST be specified in the element
+                "className");
+        digester.addSetProperties(prefix + "ClusterListener");
+        digester.addSetNext(prefix + "ClusterListener",
+                            "addClusterListener",
+                            "org.apache.catalina.cluster.MessageListener");
+        //Cluster configuration end
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/ConnectorCreateRule.java b/container/catalina/src/share/org/apache/catalina/startup/ConnectorCreateRule.java
new file mode 100644
index 0000000..503b139
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/ConnectorCreateRule.java
@@ -0,0 +1,55 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.connector.Connector;
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+
+
+/**
+ * Rule implementation that creates a connector.
+ */
+
+public class ConnectorCreateRule extends Rule {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the beginning of this element.
+     *
+     * @param attributes The attribute list of this element
+     */
+    public void begin(Attributes attributes) throws Exception {
+        digester.push(new Connector(attributes.getValue("protocol")));
+    }
+
+
+    /**
+     * Process the end of this element.
+     */
+    public void end() throws Exception {
+        Object top = digester.pop();
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/Constants.java b/container/catalina/src/share/org/apache/catalina/startup/Constants.java
new file mode 100644
index 0000000..9ec7966
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/Constants.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+/**
+ * String constants for the startup package.
+ *
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @version $Revision$ $Date$
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.catalina.startup";
+
+    public static final String ApplicationContextXml = "META-INF/context.xml";
+    public static final String ApplicationWebXml = "/WEB-INF/web.xml";
+    public static final String DefaultContextXml = "conf/context.xml";
+    public static final String DefaultWebXml = "conf/web.xml";
+    public static final String HostContextXml = "context.xml.default";
+    public static final String HostWebXml = "web.xml.default";
+
+    public static final String TldDtdPublicId_11 =
+        "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN";
+    public static final String TldDtdResourcePath_11 =
+        "/javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd";
+
+    public static final String TldDtdPublicId_12 =
+        "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN";
+    public static final String TldDtdResourcePath_12 =
+        "/javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd";
+
+    public static final String TldSchemaPublicId_20 =
+        "web-jsptaglibrary_2_0.xsd";;
+    public static final String TldSchemaResourcePath_20 =
+        "/javax/servlet/resources/web-jsptaglibrary_2_0.xsd";
+
+    public static final String WebDtdPublicId_22 =
+        "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN";
+    public static final String WebDtdResourcePath_22 =
+        "/javax/servlet/resources/web-app_2_2.dtd";
+
+    public static final String WebDtdPublicId_23 =
+        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN";
+    public static final String WebDtdResourcePath_23 =
+        "/javax/servlet/resources/web-app_2_3.dtd";
+
+    public static final String WebSchemaPublicId_24 =
+        "web-app_2_4.xsd";;
+    public static final String WebSchemaResourcePath_24 =
+        "/javax/servlet/resources/web-app_2_4.xsd";
+
+    public static final String J2eeSchemaPublicId_14 =
+        "j2ee_1_4.xsd";;
+    public static final String J2eeSchemaResourcePath_14 =
+        "/javax/servlet/resources/j2ee_1_4.xsd";
+
+    public static final String W3cSchemaPublicId_10 =
+        "xml.xsd";;
+    public static final String W3cSchemaResourcePath_10 =
+        "/javax/servlet/resources/xml.xsd";
+
+    public static final String JspSchemaPublicId_20 =
+        "jsp_2_0.xsd";;
+    public static final String JspSchemaResourcePath_20 =
+        "/javax/servlet/resources/jsp_2_0.xsd";
+    
+    public static final String J2eeWebServiceSchemaPublicId_11 =
+            "j2ee_web_services_1_1.xsd";
+    public static final String J2eeWebServiceSchemaResourcePath_11 =
+            "/javax/servlet/resources/j2ee_web_services_1_1.xsd";
+    
+    public static final String J2eeWebServiceClientSchemaPublicId_11 =
+            "j2ee_web_services_client_1_1.xsd";
+    public static final String J2eeWebServiceClientSchemaResourcePath_11 =
+            "/javax/servlet/resources/j2ee_web_services_client_1_1.xsd";
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/ContextConfig.java b/container/catalina/src/share/org/apache/catalina/startup/ContextConfig.java
new file mode 100644
index 0000000..faec546
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/ContextConfig.java
@@ -0,0 +1,1350 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Properties;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.apache.catalina.Authenticator;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Valve;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.deploy.ErrorPage;
+import org.apache.catalina.deploy.FilterDef;
+import org.apache.catalina.deploy.FilterMap;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.session.StandardManager;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSet;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Startup event listener for a <b>Context</b> that configures the properties
+ * of that Context, and the associated defined servlets.
+ *
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @version $Revision$ $Date$
+ */
+
+public final class ContextConfig
+    implements LifecycleListener {
+
+    protected static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( ContextConfig.class );
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /*
+     * Custom mappings of login methods to authenticators
+     */
+    private Map customAuthenticators;
+
+
+    /**
+     * The set of Authenticators that we know how to configure.  The key is
+     * the name of the implemented authentication method, and the value is
+     * the fully qualified Java class name of the corresponding Valve.
+     */
+    protected static Properties authenticators = null;
+
+
+    /**
+     * The Context we are associated with.
+     */
+    protected Context context = null;
+
+
+    /**
+     * The default web application's context file location.
+     */
+    protected String defaultContextXml = null;
+    
+    
+    /**
+     * The default web application's deployment descriptor location.
+     */
+    protected String defaultWebXml = null;
+    
+    
+    /**
+     * Track any fatal errors during startup configuration processing.
+     */
+    protected boolean ok = false;
+
+
+    /**
+     * Any parse error which occurred while parsing XML descriptors.
+     */
+    protected SAXParseException parseException = null;
+
+    
+    /**
+     * Original docBase.
+     */
+    protected String originalDocBase = null;
+    
+
+    /**
+     * The string resources for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The <code>Digester</code> we will use to process web application
+     * context files.
+     */
+    protected static Digester contextDigester = null;
+    
+    
+    /**
+     * The <code>Digester</code> we will use to process web application
+     * deployment descriptor files.
+     */
+    protected static Digester webDigester = null;
+    
+    
+    /**
+     * The <code>Rule</code> used to parse the web.xml
+     */
+    protected static WebRuleSet webRuleSet = new WebRuleSet();
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+     protected static boolean xmlValidation = false;
+
+
+    /**
+     * Attribute value used to turn on/off XML namespace awarenes.
+     */
+    protected static boolean xmlNamespaceAware = false;
+
+    
+    /**
+     * Deployment count.
+     */
+    protected static long deploymentCount = 0L;
+    
+    
+    private static final LoginConfig DUMMY_LOGIN_CONFIG =
+                                new LoginConfig("NONE", null, null, null);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the location of the default deployment descriptor
+     */
+    public String getDefaultWebXml() {
+        if( defaultWebXml == null ) defaultWebXml=Constants.DefaultWebXml;
+        return (this.defaultWebXml);
+
+    }
+
+
+    /**
+     * Set the location of the default deployment descriptor
+     *
+     * @param path Absolute/relative path to the default web.xml
+     */
+    public void setDefaultWebXml(String path) {
+
+        this.defaultWebXml = path;
+
+    }
+
+
+    /**
+     * Return the location of the default context file
+     */
+    public String getDefaultContextXml() {
+        if( defaultContextXml == null ) defaultContextXml=Constants.DefaultContextXml;
+        return (this.defaultContextXml);
+
+    }
+
+
+    /**
+     * Set the location of the default context file
+     *
+     * @param path Absolute/relative path to the default context.xml
+     */
+    public void setDefaultContextXml(String path) {
+
+        this.defaultContextXml = path;
+
+    }
+
+
+    /**
+     * Sets custom mappings of login methods to authenticators.
+     *
+     * @param customAuthenticators Custom mappings of login methods to
+     * authenticators
+     */
+    public void setCustomAuthenticators(Map customAuthenticators) {
+        this.customAuthenticators = customAuthenticators;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process events for an associated Context.
+     *
+     * @param event The lifecycle event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        // Identify the context we are associated with
+        try {
+            context = (Context) event.getLifecycle();
+        } catch (ClassCastException e) {
+            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
+            return;
+        }
+
+        // Process the event that has occurred
+        if (event.getType().equals(Lifecycle.START_EVENT)) {
+            start();
+        } else if (event.getType().equals(StandardContext.BEFORE_START_EVENT)) {
+            beforeStart();
+        } else if (event.getType().equals(StandardContext.AFTER_START_EVENT)) {
+            // Restore docBase for management tools
+            if (originalDocBase != null) {
+                String docBase = context.getDocBase();
+                context.setDocBase(originalDocBase);
+                originalDocBase = docBase;
+            }
+        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
+            if (originalDocBase != null) {
+                String docBase = context.getDocBase();
+                context.setDocBase(originalDocBase);
+                originalDocBase = docBase;
+            }
+            stop();
+        } else if (event.getType().equals(Lifecycle.INIT_EVENT)) {
+            init();
+        } else if (event.getType().equals(Lifecycle.DESTROY_EVENT)) {
+            destroy();
+        }
+
+    }
+
+
+    // -------------------------------------------------------- protected Methods
+
+
+    /**
+     * Process the application configuration file, if it exists.
+     */
+    protected void applicationWebConfig() {
+
+        String altDDName = null;
+
+        // Open the application web.xml file, if it exists
+        InputStream stream = null;
+        ServletContext servletContext = context.getServletContext();
+        if (servletContext != null) {
+            altDDName = (String)servletContext.getAttribute(
+                                                        Globals.ALT_DD_ATTR);
+            if (altDDName != null) {
+                try {
+                    stream = new FileInputStream(altDDName);
+                } catch (FileNotFoundException e) {
+                    log.error(sm.getString("contextConfig.altDDNotFound",
+                                           altDDName));
+                }
+            }
+            else {
+                stream = servletContext.getResourceAsStream
+                    (Constants.ApplicationWebXml);
+            }
+        }
+        if (stream == null) {
+            log.info(sm.getString("contextConfig.applicationMissing") + " " + context);
+            return;
+        }
+        
+        long t1=System.currentTimeMillis();
+
+        if (webDigester == null){
+            webDigester = createWebDigester();
+        }
+        
+        URL url=null;
+        // Process the application web.xml file
+        synchronized (webDigester) {
+            try {
+                if (altDDName != null) {
+                    url = new File(altDDName).toURL();
+                } else {
+                    url = servletContext.getResource(
+                                                Constants.ApplicationWebXml);
+                }
+                if( url!=null ) {
+                    InputSource is = new InputSource(url.toExternalForm());
+                    is.setByteStream(stream);
+                    if (context instanceof StandardContext) {
+                        ((StandardContext) context).setReplaceWelcomeFiles(true);
+                    }
+                    webDigester.push(context);
+                    webDigester.setErrorHandler(new ContextErrorHandler());
+                    webDigester.parse(is);
+                    if (parseException != null) {
+                        ok = false;
+                    }
+                } else {
+                    log.info("No web.xml, using defaults " + context );
+                }
+            } catch (SAXParseException e) {
+                log.error(sm.getString("contextConfig.applicationParse"), e);
+                log.error(sm.getString("contextConfig.applicationPosition",
+                                 "" + e.getLineNumber(),
+                                 "" + e.getColumnNumber()));
+                ok = false;
+            } catch (Exception e) {
+                log.error(sm.getString("contextConfig.applicationParse"), e);
+                ok = false;
+            } finally {
+                webDigester.reset();
+                parseException = null;
+                try {
+                    if (stream != null) {
+                        stream.close();
+                    }
+                } catch (IOException e) {
+                    log.error(sm.getString("contextConfig.applicationClose"), e);
+                }
+            }
+        }
+        webRuleSet.recycle();
+
+        long t2=System.currentTimeMillis();
+        if (context instanceof StandardContext) {
+            ((StandardContext) context).setStartupTime(t2-t1);
+        }
+    }
+
+
+    /**
+     * Set up a manager.
+     */
+    protected synchronized void managerConfig() {
+
+        if (context.getManager() == null) {
+            if ((context.getCluster() != null) && context.getDistributable()) {
+                try {
+                    context.setManager(context.getCluster().createManager
+                                       (context.getName()));
+                } catch (Exception ex) {
+                    log.error("contextConfig.clusteringInit", ex);
+                    ok = false;
+                }
+            } else {
+                context.setManager(new StandardManager());
+            }
+        }
+
+    }
+
+
+    /**
+     * Set up an Authenticator automatically if required, and one has not
+     * already been configured.
+     */
+    protected synchronized void authenticatorConfig() {
+
+        // Does this Context require an Authenticator?
+        SecurityConstraint constraints[] = context.findConstraints();
+        if ((constraints == null) || (constraints.length == 0))
+            return;
+        LoginConfig loginConfig = context.getLoginConfig();
+        if (loginConfig == null) {
+            loginConfig = DUMMY_LOGIN_CONFIG;
+            context.setLoginConfig(loginConfig);
+        }
+
+        // Has an authenticator been configured already?
+        if (context instanceof Authenticator)
+            return;
+        if (context instanceof ContainerBase) {
+            Pipeline pipeline = ((ContainerBase) context).getPipeline();
+            if (pipeline != null) {
+                Valve basic = pipeline.getBasic();
+                if ((basic != null) && (basic instanceof Authenticator))
+                    return;
+                Valve valves[] = pipeline.getValves();
+                for (int i = 0; i < valves.length; i++) {
+                    if (valves[i] instanceof Authenticator)
+                        return;
+                }
+            }
+        } else {
+            return;     // Cannot install a Valve even if it would be needed
+        }
+
+        // Has a Realm been configured for us to authenticate against?
+        if (context.getRealm() == null) {
+            log.error(sm.getString("contextConfig.missingRealm"));
+            ok = false;
+            return;
+        }
+
+        /*
+         * First check to see if there is a custom mapping for the login
+         * method. If so, use it. Otherwise, check if there is a mapping in
+         * org/apache/catalina/startup/Authenticators.properties.
+         */
+        Valve authenticator = null;
+        if (customAuthenticators != null) {
+            authenticator = (Valve)
+                customAuthenticators.get(loginConfig.getAuthMethod());
+        }
+        if (authenticator == null) {
+            // Load our mapping properties if necessary
+            if (authenticators == null) {
+                try {
+                    InputStream is=this.getClass().getClassLoader().getResourceAsStream("org/apache/catalina/startup/Authenticators.properties");
+                    if( is!=null ) {
+                        authenticators = new Properties();
+                        authenticators.load(is);
+                    } else {
+                        log.error(sm.getString(
+                                "contextConfig.authenticatorResources"));
+                        ok=false;
+                        return;
+                    }
+                } catch (IOException e) {
+                    log.error(sm.getString(
+                                "contextConfig.authenticatorResources"), e);
+                    ok = false;
+                    return;
+                }
+            }
+
+            // Identify the class name of the Valve we should configure
+            String authenticatorName = null;
+            authenticatorName =
+                    authenticators.getProperty(loginConfig.getAuthMethod());
+            if (authenticatorName == null) {
+                log.error(sm.getString("contextConfig.authenticatorMissing",
+                                 loginConfig.getAuthMethod()));
+                ok = false;
+                return;
+            }
+
+            // Instantiate and install an Authenticator of the requested class
+            try {
+                Class authenticatorClass = Class.forName(authenticatorName);
+                authenticator = (Valve) authenticatorClass.newInstance();
+            } catch (Throwable t) {
+                log.error(sm.getString(
+                                    "contextConfig.authenticatorInstantiate",
+                                    authenticatorName),
+                          t);
+                ok = false;
+            }
+        }
+
+        if (authenticator != null && context instanceof ContainerBase) {
+            Pipeline pipeline = ((ContainerBase) context).getPipeline();
+            if (pipeline != null) {
+                ((ContainerBase) context).addValve(authenticator);
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString(
+                                    "contextConfig.authenticatorConfigured",
+                                    loginConfig.getAuthMethod()));
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Create (if necessary) and return a Digester configured to process the
+     * web application deployment descriptor (web.xml).
+     */
+    protected static Digester createWebDigester() {
+        Digester webDigester =
+            createWebXmlDigester(xmlNamespaceAware, xmlValidation);
+        return webDigester;
+    }
+
+
+    /**
+     * Create (if necessary) and return a Digester configured to process the
+     * web application deployment descriptor (web.xml).
+     */
+    public static Digester createWebXmlDigester(boolean namespaceAware,
+                                                boolean validation) {
+        
+        Digester webDigester =  DigesterFactory.newDigester(xmlValidation,
+                                                            xmlNamespaceAware,
+                                                            webRuleSet);
+        return webDigester;
+    }
+
+    
+    /**
+     * Create (if necessary) and return a Digester configured to process the
+     * context configuration descriptor for an application.
+     */
+    protected Digester createContextDigester() {
+        Digester digester = new Digester();
+        digester.setValidating(false);
+        RuleSet contextRuleSet = new ContextRuleSet("", false);
+        digester.addRuleSet(contextRuleSet);
+        RuleSet namingRuleSet = new NamingRuleSet("Context/");
+        digester.addRuleSet(namingRuleSet);
+        return digester;
+    }
+
+
+    protected String getBaseDir() {
+        Container engineC=context.getParent().getParent();
+        if( engineC instanceof StandardEngine ) {
+            return ((StandardEngine)engineC).getBaseDir();
+        }
+        return System.getProperty("catalina.base");
+    }
+
+    /**
+     * Process the default configuration file, if it exists.
+     * The default config must be read with the container loader - so
+     * container servlets can be loaded
+     */
+    protected void defaultWebConfig() {
+        long t1=System.currentTimeMillis();
+
+        // Open the default web.xml file, if it exists
+        if( defaultWebXml==null && context instanceof StandardContext ) {
+            defaultWebXml=((StandardContext)context).getDefaultWebXml();
+        }
+        // set the default if we don't have any overrides
+        if( defaultWebXml==null ) getDefaultWebXml();
+
+        File file = new File(this.defaultWebXml);
+        if (!file.isAbsolute()) {
+            file = new File(getBaseDir(),
+                            this.defaultWebXml);
+        }
+
+        InputStream stream = null;
+        InputSource source = null;
+
+        try {
+            if ( ! file.exists() ) {
+                // Use getResource and getResourceAsStream
+                stream = getClass().getClassLoader()
+                    .getResourceAsStream(defaultWebXml);
+                if( stream != null ) {
+                    source = new InputSource
+                            (getClass().getClassLoader()
+                            .getResource(defaultWebXml).toString());
+                } else {
+                    log.info("No default web.xml");
+                }
+            } else {
+                source =
+                    new InputSource("file://" + file.getAbsolutePath());
+                stream = new FileInputStream(file);
+            }
+        } catch (Exception e) {
+            log.error(sm.getString("contextConfig.defaultMissing") 
+                      + " " + defaultWebXml + " " + file , e);
+        }
+
+        if (webDigester == null){
+            webDigester = createWebDigester();
+        }
+        
+        if (stream != null) {
+            processDefaultWebConfig(webDigester, stream, source);
+            webRuleSet.recycle();
+        }
+
+        long t2=System.currentTimeMillis();
+        if( (t2-t1) > 200 )
+            log.debug("Processed default web.xml " + file + " "  + ( t2-t1));
+
+        stream = null;
+        source = null;
+
+        String resourceName = getHostConfigPath(Constants.HostWebXml);
+        file = new File(getConfigBase(), resourceName);
+        
+        try {
+            if ( ! file.exists() ) {
+                // Use getResource and getResourceAsStream
+                stream = getClass().getClassLoader()
+                    .getResourceAsStream(resourceName);
+                if( stream != null ) {
+                    source = new InputSource
+                            (getClass().getClassLoader()
+                            .getResource(resourceName).toString());
+                }
+            } else {
+                source =
+                    new InputSource("file://" + file.getAbsolutePath());
+                stream = new FileInputStream(file);
+            }
+        } catch (Exception e) {
+            log.error(sm.getString("contextConfig.defaultMissing") 
+                      + " " + resourceName + " " + file , e);
+        }
+
+        if (stream != null) {
+            processDefaultWebConfig(webDigester, stream, source);
+            webRuleSet.recycle();
+        }
+
+    }
+
+
+    /**
+     * Process a default web.xml.
+     */
+    protected void processDefaultWebConfig(Digester digester, InputStream stream, 
+            InputSource source) {
+
+        if (log.isDebugEnabled())
+            log.debug("Processing context [" + context.getName() 
+                    + "] web configuration resource " + source.getSystemId());
+
+        // Process the default web.xml file
+        synchronized (digester) {
+            try {
+                source.setByteStream(stream);
+                
+                if (context instanceof StandardContext)
+                    ((StandardContext) context).setReplaceWelcomeFiles(true);
+                digester.setClassLoader(this.getClass().getClassLoader());
+                digester.setUseContextClassLoader(false);
+                digester.push(context);
+                digester.setErrorHandler(new ContextErrorHandler());
+                digester.parse(source);
+                if (parseException != null) {
+                    ok = false;
+                }
+            } catch (SAXParseException e) {
+                log.error(sm.getString("contextConfig.defaultParse"), e);
+                log.error(sm.getString("contextConfig.defaultPosition",
+                                 "" + e.getLineNumber(),
+                                 "" + e.getColumnNumber()));
+                ok = false;
+            } catch (Exception e) {
+                log.error(sm.getString("contextConfig.defaultParse"), e);
+                ok = false;
+            } finally {
+                digester.reset();
+                parseException = null;
+                try {
+                    if (stream != null) {
+                        stream.close();
+                    }
+                } catch (IOException e) {
+                    log.error(sm.getString("contextConfig.defaultClose"), e);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Process the default configuration file, if it exists.
+     */
+    protected void contextConfig() {
+        
+        // Open the default web.xml file, if it exists
+        if( defaultContextXml==null && context instanceof StandardContext ) {
+            defaultContextXml = ((StandardContext)context).getDefaultContextXml();
+        }
+        // set the default if we don't have any overrides
+        if( defaultContextXml==null ) getDefaultContextXml();
+
+        if (!context.getOverride()) {
+            processContextConfig(new File(getBaseDir()), defaultContextXml);
+            processContextConfig(getConfigBase(), getHostConfigPath(Constants.HostContextXml));
+        }
+        if (context.getConfigFile() != null)
+            processContextConfig(new File(context.getConfigFile()), null);
+        
+    }
+
+    
+    /**
+     * Process a context.xml.
+     */
+    protected void processContextConfig(File baseDir, String resourceName) {
+        
+        if (log.isDebugEnabled())
+            log.debug("Processing context [" + context.getName() 
+                    + "] configuration file " + baseDir + " " + resourceName);
+
+        InputSource source = null;
+        InputStream stream = null;
+
+        File file = baseDir;
+        if (resourceName != null) {
+            file = new File(baseDir, resourceName);
+        }
+        
+        try {
+            if ( !file.exists() ) {
+                if (resourceName != null) {
+                    // Use getResource and getResourceAsStream
+                    stream = getClass().getClassLoader()
+                        .getResourceAsStream(resourceName);
+                    if( stream != null ) {
+                        source = new InputSource
+                            (getClass().getClassLoader()
+                            .getResource(resourceName).toString());
+                    }
+                }
+            } else {
+                source =
+                    new InputSource("file://" + file.getAbsolutePath());
+                stream = new FileInputStream(file);
+                // Add as watched resource so that cascade reload occurs if a default
+                // config file is modified/added/removed
+                context.addWatchedResource(file.getAbsolutePath());
+            }
+        } catch (Exception e) {
+            log.error(sm.getString("contextConfig.defaultMissing") 
+                      + " " + resourceName + " " + file , e);
+        }
+        
+        if (source == null)
+            return;
+        if (contextDigester == null){
+            contextDigester = createContextDigester();
+        }
+        synchronized (contextDigester) {
+            try {
+                source.setByteStream(stream);
+                contextDigester.setClassLoader(this.getClass().getClassLoader());
+                contextDigester.setUseContextClassLoader(false);
+                contextDigester.push(context.getParent());
+                contextDigester.push(context);
+                contextDigester.setErrorHandler(new ContextErrorHandler());
+                contextDigester.parse(source);
+                if (parseException != null) {
+                    ok = false;
+                }
+                if (log.isDebugEnabled())
+                    log.debug("Successfully processed context [" + context.getName() 
+                            + "] configuration file " + baseDir + " " + resourceName);
+            } catch (SAXParseException e) {
+                log.error(sm.getString("contextConfig.defaultParse"), e);
+                log.error(sm.getString("contextConfig.defaultPosition",
+                                 "" + e.getLineNumber(),
+                                 "" + e.getColumnNumber()));
+                ok = false;
+            } catch (Exception e) {
+                log.error(sm.getString("contextConfig.defaultParse"), e);
+                ok = false;
+            } finally {
+                contextDigester.reset();
+                parseException = null;
+                try {
+                    if (stream != null) {
+                        stream.close();
+                    }
+                } catch (IOException e) {
+                    log.error(sm.getString("contextConfig.defaultClose"), e);
+                }
+            }
+        }
+    }
+
+    
+    /**
+     * Adjust docBase.
+     */
+    protected void fixDocBase()
+        throws IOException {
+        
+        Host host = (Host) context.getParent();
+        String appBase = host.getAppBase();
+
+        boolean unpackWARs = true;
+        if (host instanceof StandardHost) {
+            unpackWARs = ((StandardHost) host).isUnpackWARs() 
+                && ((StandardContext) context).getUnpackWAR();
+        }
+
+        File canonicalAppBase = new File(appBase);
+        if (canonicalAppBase.isAbsolute()) {
+            canonicalAppBase = canonicalAppBase.getCanonicalFile();
+        } else {
+            canonicalAppBase = 
+                new File(System.getProperty("catalina.base"), appBase)
+                .getCanonicalFile();
+        }
+
+        String docBase = context.getDocBase();
+        if (docBase == null) {
+            // Trying to guess the docBase according to the path
+            String path = context.getPath();
+            if (path == null) {
+                return;
+            }
+            if (path.equals("")) {
+                docBase = "ROOT";
+            } else {
+                if (path.startsWith("/")) {
+                    docBase = path.substring(1);
+                } else {
+                    docBase = path;
+                }
+            }
+        }
+
+        File file = new File(docBase);
+        if (!file.isAbsolute()) {
+            docBase = (new File(canonicalAppBase, docBase)).getPath();
+        } else {
+            docBase = file.getCanonicalPath();
+        }
+        file = new File(docBase);
+
+        if (docBase.toLowerCase().endsWith(".war") && !file.isDirectory() && unpackWARs) {
+            URL war = new URL("jar:" + (new File(docBase)).toURL() + "!/");
+            String contextPath = context.getPath();
+            if (contextPath.equals("")) {
+                contextPath = "ROOT";
+            }
+            docBase = ExpandWar.expand(host, war, contextPath);
+            file = new File(docBase);
+            docBase = file.getCanonicalPath();
+        } else {
+            File docDir = new File(docBase);
+            if (!docDir.exists()) {
+                File warFile = new File(docBase + ".war");
+                if (warFile.exists()) {
+                    if (unpackWARs) {
+                        URL war = new URL("jar:" + warFile.toURL() + "!/");
+                        docBase = ExpandWar.expand(host, war, context.getPath());
+                        file = new File(docBase);
+                        docBase = file.getCanonicalPath();
+                    } else {
+                        docBase = warFile.getCanonicalPath();
+                    }
+                }
+            }
+        }
+
+        if (docBase.startsWith(canonicalAppBase.getPath())) {
+            docBase = docBase.substring(canonicalAppBase.getPath().length());
+            docBase = docBase.replace(File.separatorChar, '/');
+            if (docBase.startsWith("/")) {
+                docBase = docBase.substring(1);
+            }
+        } else {
+            docBase = docBase.replace(File.separatorChar, '/');
+        }
+
+        context.setDocBase(docBase);
+
+    }
+    
+    
+    protected void antiLocking()
+        throws IOException {
+
+        if ((context instanceof StandardContext) 
+            && ((StandardContext) context).getAntiResourceLocking()) {
+            
+            Host host = (Host) context.getParent();
+            String appBase = host.getAppBase();
+            String docBase = context.getDocBase();
+            if (docBase == null)
+                return;
+            if (originalDocBase == null) {
+                originalDocBase = docBase;
+            } else {
+                docBase = originalDocBase;
+            }
+            File docBaseFile = new File(docBase);
+            if (!docBaseFile.isAbsolute()) {
+                File file = new File(appBase);
+                if (!file.isAbsolute()) {
+                    file = new File(System.getProperty("catalina.base"), appBase);
+                }
+                docBaseFile = new File(file, docBase);
+            }
+            
+            String path = context.getPath();
+            if (path == null) {
+                return;
+            }
+            if (path.equals("")) {
+                docBase = "ROOT";
+            } else {
+                if (path.startsWith("/")) {
+                    docBase = path.substring(1);
+                } else {
+                    docBase = path;
+                }
+            }
+
+            File file = null;
+            if (docBase.toLowerCase().endsWith(".war")) {
+                file = new File(System.getProperty("java.io.tmpdir"),
+                        deploymentCount++ + "-" + docBase + ".war");
+            } else {
+                file = new File(System.getProperty("java.io.tmpdir"), 
+                        deploymentCount++ + "-" + docBase);
+            }
+            
+            if (log.isDebugEnabled())
+                log.debug("Anti locking context[" + context.getPath() 
+                        + "] setting docBase to " + file);
+            
+            // Cleanup just in case an old deployment is lying around
+            ExpandWar.delete(file);
+            if (ExpandWar.copy(docBaseFile, file)) {
+                context.setDocBase(file.getAbsolutePath());
+            }
+            
+        }
+        
+    }
+    
+
+    /**
+     * Process a "init" event for this Context.
+     */
+    protected void init() {
+        // Called from StandardContext.init()
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("contextConfig.init"));
+        context.setConfigured(false);
+        ok = true;
+        
+        contextConfig();
+        
+        try {
+            fixDocBase();
+        } catch (IOException e) {
+            log.error(sm.getString("contextConfig.fixDocBase"), e);
+        }
+        
+    }
+    
+    
+    /**
+     * Process a "before start" event for this Context.
+     */
+    protected synchronized void beforeStart() {
+        
+        try {
+            antiLocking();
+        } catch (IOException e) {
+            log.error(sm.getString("contextConfig.antiLocking"), e);
+        }
+        
+    }
+    
+    
+    /**
+     * Process a "start" event for this Context.
+     */
+    protected synchronized void start() {
+        // Called from StandardContext.start()
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("contextConfig.start"));
+
+        // Set properties based on DefaultContext
+        Container container = context.getParent();
+        if( !context.getOverride() ) {
+            if( container instanceof Host ) {
+                // Reset the value only if the attribute wasn't
+                // set on the context.
+                xmlValidation = context.getXmlValidation();
+                if (!xmlValidation) {
+                    xmlValidation = ((Host)container).getXmlValidation();
+                }
+                
+                xmlNamespaceAware = context.getXmlNamespaceAware();
+                if (!xmlNamespaceAware){
+                    xmlNamespaceAware 
+                                = ((Host)container).getXmlNamespaceAware();
+                }
+
+                container = container.getParent();
+            }
+        }
+
+        // Process the default and application web.xml files
+        defaultWebConfig();
+        applicationWebConfig();
+        if (ok) {
+            validateSecurityRoles();
+        }
+
+        // Configure an authenticator if we need one
+        if (ok)
+            authenticatorConfig();
+
+        // Configure a manager
+        if (ok)
+            managerConfig();
+
+        // Dump the contents of this pipeline if requested
+        if ((log.isDebugEnabled()) && (context instanceof ContainerBase)) {
+            log.debug("Pipline Configuration:");
+            Pipeline pipeline = ((ContainerBase) context).getPipeline();
+            Valve valves[] = null;
+            if (pipeline != null)
+                valves = pipeline.getValves();
+            if (valves != null) {
+                for (int i = 0; i < valves.length; i++) {
+                    log.debug("  " + valves[i].getInfo());
+                }
+            }
+            log.debug("======================");
+        }
+
+        // Make our application available if no problems were encountered
+        if (ok)
+            context.setConfigured(true);
+        else {
+            log.error(sm.getString("contextConfig.unavailable"));
+            context.setConfigured(false);
+        }
+
+    }
+
+
+    /**
+     * Process a "stop" event for this Context.
+     */
+    protected synchronized void stop() {
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("contextConfig.stop"));
+
+        int i;
+
+        // Removing children
+        Container[] children = context.findChildren();
+        for (i = 0; i < children.length; i++) {
+            context.removeChild(children[i]);
+        }
+
+        // Removing application parameters
+        /*
+        ApplicationParameter[] applicationParameters =
+            context.findApplicationParameters();
+        for (i = 0; i < applicationParameters.length; i++) {
+            context.removeApplicationParameter
+                (applicationParameters[i].getName());
+        }
+        */
+
+        // Removing security constraints
+        SecurityConstraint[] securityConstraints = context.findConstraints();
+        for (i = 0; i < securityConstraints.length; i++) {
+            context.removeConstraint(securityConstraints[i]);
+        }
+
+        // Removing Ejbs
+        /*
+        ContextEjb[] contextEjbs = context.findEjbs();
+        for (i = 0; i < contextEjbs.length; i++) {
+            context.removeEjb(contextEjbs[i].getName());
+        }
+        */
+
+        // Removing environments
+        /*
+        ContextEnvironment[] contextEnvironments = context.findEnvironments();
+        for (i = 0; i < contextEnvironments.length; i++) {
+            context.removeEnvironment(contextEnvironments[i].getName());
+        }
+        */
+
+        // Removing errors pages
+        ErrorPage[] errorPages = context.findErrorPages();
+        for (i = 0; i < errorPages.length; i++) {
+            context.removeErrorPage(errorPages[i]);
+        }
+
+        // Removing filter defs
+        FilterDef[] filterDefs = context.findFilterDefs();
+        for (i = 0; i < filterDefs.length; i++) {
+            context.removeFilterDef(filterDefs[i]);
+        }
+
+        // Removing filter maps
+        FilterMap[] filterMaps = context.findFilterMaps();
+        for (i = 0; i < filterMaps.length; i++) {
+            context.removeFilterMap(filterMaps[i]);
+        }
+
+        // Removing local ejbs
+        /*
+        ContextLocalEjb[] contextLocalEjbs = context.findLocalEjbs();
+        for (i = 0; i < contextLocalEjbs.length; i++) {
+            context.removeLocalEjb(contextLocalEjbs[i].getName());
+        }
+        */
+
+        // Removing Mime mappings
+        String[] mimeMappings = context.findMimeMappings();
+        for (i = 0; i < mimeMappings.length; i++) {
+            context.removeMimeMapping(mimeMappings[i]);
+        }
+
+        // Removing parameters
+        String[] parameters = context.findParameters();
+        for (i = 0; i < parameters.length; i++) {
+            context.removeParameter(parameters[i]);
+        }
+
+        // Removing resource env refs
+        /*
+        String[] resourceEnvRefs = context.findResourceEnvRefs();
+        for (i = 0; i < resourceEnvRefs.length; i++) {
+            context.removeResourceEnvRef(resourceEnvRefs[i]);
+        }
+        */
+
+        // Removing resource links
+        /*
+        ContextResourceLink[] contextResourceLinks =
+            context.findResourceLinks();
+        for (i = 0; i < contextResourceLinks.length; i++) {
+            context.removeResourceLink(contextResourceLinks[i].getName());
+        }
+        */
+
+        // Removing resources
+        /*
+        ContextResource[] contextResources = context.findResources();
+        for (i = 0; i < contextResources.length; i++) {
+            context.removeResource(contextResources[i].getName());
+        }
+        */
+
+        // Removing sercurity role
+        String[] securityRoles = context.findSecurityRoles();
+        for (i = 0; i < securityRoles.length; i++) {
+            context.removeSecurityRole(securityRoles[i]);
+        }
+
+        // Removing servlet mappings
+        String[] servletMappings = context.findServletMappings();
+        for (i = 0; i < servletMappings.length; i++) {
+            context.removeServletMapping(servletMappings[i]);
+        }
+
+        // FIXME : Removing status pages
+
+        // Removing taglibs
+        String[] taglibs = context.findTaglibs();
+        for (i = 0; i < taglibs.length; i++) {
+            context.removeTaglib(taglibs[i]);
+        }
+
+        // Removing welcome files
+        String[] welcomeFiles = context.findWelcomeFiles();
+        for (i = 0; i < welcomeFiles.length; i++) {
+            context.removeWelcomeFile(welcomeFiles[i]);
+        }
+
+        // Removing wrapper lifecycles
+        String[] wrapperLifecycles = context.findWrapperLifecycles();
+        for (i = 0; i < wrapperLifecycles.length; i++) {
+            context.removeWrapperLifecycle(wrapperLifecycles[i]);
+        }
+
+        // Removing wrapper listeners
+        String[] wrapperListeners = context.findWrapperListeners();
+        for (i = 0; i < wrapperListeners.length; i++) {
+            context.removeWrapperListener(wrapperListeners[i]);
+        }
+
+        // Remove (partially) folders and files created by antiLocking
+        Host host = (Host) context.getParent();
+        String appBase = host.getAppBase();
+        String docBase = context.getDocBase();
+        if ((docBase != null) && (originalDocBase != null)) {
+            File docBaseFile = new File(docBase);
+            if (!docBaseFile.isAbsolute()) {
+                docBaseFile = new File(appBase, docBase);
+            }
+            ExpandWar.delete(docBaseFile);
+        }
+        
+        ok = true;
+
+    }
+    
+    
+    /**
+     * Process a "destroy" event for this Context.
+     */
+    protected synchronized void destroy() {
+        // Called from StandardContext.destroy()
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("contextConfig.destroy"));
+
+        // Changed to getWorkPath per Bugzilla 35819.
+        String workDir = ((StandardContext) context).getWorkPath();
+        if (workDir != null)
+            ExpandWar.delete(new File(workDir));
+    }
+    
+    
+    /**
+     * Validate the usage of security role names in the web application
+     * deployment descriptor.  If any problems are found, issue warning
+     * messages (for backwards compatibility) and add the missing roles.
+     * (To make these problems fatal instead, simply set the <code>ok</code>
+     * instance variable to <code>false</code> as well).
+     */
+    protected void validateSecurityRoles() {
+
+        // Check role names used in <security-constraint> elements
+        SecurityConstraint constraints[] = context.findConstraints();
+        for (int i = 0; i < constraints.length; i++) {
+            String roles[] = constraints[i].findAuthRoles();
+            for (int j = 0; j < roles.length; j++) {
+                if (!"*".equals(roles[j]) &&
+                    !context.findSecurityRole(roles[j])) {
+                    log.info(sm.getString("contextConfig.role.auth", roles[j]));
+                    context.addSecurityRole(roles[j]);
+                }
+            }
+        }
+
+        // Check role names used in <servlet> elements
+        Container wrappers[] = context.findChildren();
+        for (int i = 0; i < wrappers.length; i++) {
+            Wrapper wrapper = (Wrapper) wrappers[i];
+            String runAs = wrapper.getRunAs();
+            if ((runAs != null) && !context.findSecurityRole(runAs)) {
+                log.info(sm.getString("contextConfig.role.runas", runAs));
+                context.addSecurityRole(runAs);
+            }
+            String names[] = wrapper.findSecurityReferences();
+            for (int j = 0; j < names.length; j++) {
+                String link = wrapper.findSecurityReference(names[j]);
+                if ((link != null) && !context.findSecurityRole(link)) {
+                    log.info(sm.getString("contextConfig.role.link", link));
+                    context.addSecurityRole(link);
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Get config base.
+     */
+    protected File getConfigBase() {
+        File configBase = 
+            new File(System.getProperty("catalina.base"), "conf");
+        if (!configBase.exists()) {
+            return null;
+        } else {
+            return configBase;
+        }
+    }  
+
+    
+    protected String getHostConfigPath(String resourceName) {
+        StringBuffer result = new StringBuffer();
+        Container container = context;
+        Container host = null;
+        Container engine = null;
+        while (container != null) {
+            if (container instanceof Host)
+                host = container;
+            if (container instanceof Engine)
+                engine = container;
+            container = container.getParent();
+        }
+        if (engine != null) {
+            result.append(engine.getName()).append('/');
+        }
+        if (host != null) {
+            result.append(host.getName()).append('/');
+        }
+        result.append(resourceName);
+        return result.toString();
+    }
+
+
+    protected class ContextErrorHandler
+        implements ErrorHandler {
+
+        public void error(SAXParseException exception) {
+            parseException = exception;
+        }
+
+        public void fatalError(SAXParseException exception) {
+            parseException = exception;
+        }
+
+        public void warning(SAXParseException exception) {
+            parseException = exception;
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/ContextRuleSet.java b/container/catalina/src/share/org/apache/catalina/startup/ContextRuleSet.java
new file mode 100644
index 0000000..320ab10
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/ContextRuleSet.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.lang.reflect.Constructor;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Loader;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.apache.tomcat.util.digester.RuleSetBase;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a
+ * Context or DefaultContext definition element.  To enable parsing of a
+ * DefaultContext, be sure to specify a prefix that ends with "/Default".</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ContextRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    /**
+     * Should the context be created.
+     */
+    protected boolean create = true;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public ContextRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public ContextRuleSet(String prefix) {
+
+        super();
+        this.namespaceURI = null;
+        this.prefix = prefix;
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public ContextRuleSet(String prefix, boolean create) {
+
+        super();
+        this.namespaceURI = null;
+        this.prefix = prefix;
+        this.create = create;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+
+        if (create) {
+            digester.addObjectCreate(prefix + "Context",
+                    "org.apache.catalina.core.StandardContext", "className");
+            digester.addSetProperties(prefix + "Context");
+        } else {
+            digester.addRule(prefix + "Context", new SetContextPropertiesRule());
+        }
+
+        if (create) {
+            digester.addRule(prefix + "Context",
+                             new LifecycleListenerRule
+                                 ("org.apache.catalina.startup.ContextConfig",
+                                  "configClass"));
+            digester.addSetNext(prefix + "Context",
+                                "addChild",
+                                "org.apache.catalina.Container");
+        }
+        digester.addCallMethod(prefix + "Context/InstanceListener",
+                               "addInstanceListener", 0);
+
+        digester.addObjectCreate(prefix + "Context/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Listener");
+        digester.addSetNext(prefix + "Context/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        digester.addRule(prefix + "Context/Loader",
+                         new CreateLoaderRule
+                             ("org.apache.catalina.loader.WebappLoader",
+                              "className"));
+        digester.addSetProperties(prefix + "Context/Loader");
+        digester.addSetNext(prefix + "Context/Loader",
+                            "setLoader",
+                            "org.apache.catalina.Loader");
+
+        digester.addObjectCreate(prefix + "Context/Manager",
+                                 "org.apache.catalina.session.StandardManager",
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Manager");
+        digester.addSetNext(prefix + "Context/Manager",
+                            "setManager",
+                            "org.apache.catalina.Manager");
+
+        digester.addObjectCreate(prefix + "Context/Manager/Store",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Manager/Store");
+        digester.addSetNext(prefix + "Context/Manager/Store",
+                            "setStore",
+                            "org.apache.catalina.Store");
+
+        digester.addObjectCreate(prefix + "Context/Parameter",
+                                 "org.apache.catalina.deploy.ApplicationParameter");
+        digester.addSetProperties(prefix + "Context/Parameter");
+        digester.addSetNext(prefix + "Context/Parameter",
+                            "addApplicationParameter",
+                            "org.apache.catalina.deploy.ApplicationParameter");
+
+        digester.addObjectCreate(prefix + "Context/Realm",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Realm");
+        digester.addSetNext(prefix + "Context/Realm",
+                            "setRealm",
+                            "org.apache.catalina.Realm");
+
+        digester.addObjectCreate(prefix + "Context/Resources",
+                                 "org.apache.naming.resources.FileDirContext",
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Resources");
+        digester.addSetNext(prefix + "Context/Resources",
+                            "setResources",
+                            "javax.naming.directory.DirContext");
+
+        digester.addObjectCreate(prefix + "Context/ResourceLink",
+                "org.apache.catalina.deploy.ContextResourceLink");
+        digester.addSetProperties(prefix + "Context/ResourceLink");
+        digester.addRule(prefix + "Context/ResourceLink",
+                new SetNextNamingRule("addResourceLink",
+                        "org.apache.catalina.deploy.ContextResourceLink"));
+
+        digester.addObjectCreate(prefix + "Context/Valve",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Valve");
+        digester.addSetNext(prefix + "Context/Valve",
+                            "addValve",
+                            "org.apache.catalina.Valve");
+
+        digester.addCallMethod(prefix + "Context/WatchedResource",
+                               "addWatchedResource", 0);
+
+        digester.addCallMethod(prefix + "Context/WrapperLifecycle",
+                               "addWrapperLifecycle", 0);
+
+        digester.addCallMethod(prefix + "Context/WrapperListener",
+                               "addWrapperListener", 0);
+
+    }
+
+}
+
+
+// ----------------------------------------------------------- Private Classes
+
+
+/**
+ * Rule that creates a new <code>Loader</code> instance, with the parent
+ * class loader associated with the top object on the stack (which must be
+ * a <code>Container</code>), and pushes it on to the stack.
+ */
+
+final class CreateLoaderRule extends Rule {
+
+    public CreateLoaderRule(String loaderClass, String attributeName) {
+
+        this.loaderClass = loaderClass;
+        this.attributeName = attributeName;
+
+    }
+
+    private String attributeName;
+
+    private String loaderClass;
+
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+
+        // Look up the required parent class loader
+        ClassLoader parentClassLoader = null;
+        Object ojb = digester.peek();
+        if (ojb instanceof Container) {
+            parentClassLoader = ((Container)ojb).getParentClassLoader();
+        }
+
+        // Instantiate a new Loader implementation object
+        String className = loaderClass;
+        if (attributeName != null) {
+            String value = attributes.getValue(attributeName);
+            if (value != null)
+                className = value;
+        }
+        Class clazz = Class.forName(className);
+        Class types[] = { ClassLoader.class };
+        Object args[] = { parentClassLoader };
+        Constructor constructor = clazz.getDeclaredConstructor(types);
+        Loader loader = (Loader) constructor.newInstance(args);
+
+        // Push the new loader onto the stack
+        digester.push(loader);
+        if (digester.getLogger().isDebugEnabled())
+            digester.getLogger().debug("new " + loader.getClass().getName());
+
+    }
+
+    public void end(String namespace, String name)
+        throws Exception {
+
+        Loader loader = (Loader) digester.pop();
+        if (digester.getLogger().isDebugEnabled())
+            digester.getLogger().debug("pop " + loader.getClass().getName());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/CopyParentClassLoaderRule.java b/container/catalina/src/share/org/apache/catalina/startup/CopyParentClassLoaderRule.java
new file mode 100644
index 0000000..96a33bb
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/CopyParentClassLoaderRule.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.lang.reflect.Method;
+import org.apache.catalina.Container;
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Rule that copies the <code>parentClassLoader</code> property from the
+ * next-to-top item on the stack (which must be a <code>Container</code>)
+ * to the top item on the stack (which must also be a
+ * <code>Container</code>).</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class CopyParentClassLoaderRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this Rule.
+     */
+    public CopyParentClassLoaderRule() {
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Handle the beginning of an XML element.
+     *
+     * @param attributes The attributes of this element
+     *
+     * @exception Exception if a processing error occurs
+     */
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+
+        if (digester.getLogger().isDebugEnabled())
+            digester.getLogger().debug("Copying parent class loader");
+        Container child = (Container) digester.peek(0);
+        Object parent = digester.peek(1);
+        Method method =
+            parent.getClass().getMethod("getParentClassLoader", new Class[0]);
+        ClassLoader classLoader =
+            (ClassLoader) method.invoke(parent, new Object[0]);
+        child.setParentClassLoader(classLoader);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/DigesterFactory.java b/container/catalina/src/share/org/apache/catalina/startup/DigesterFactory.java
new file mode 100644
index 0000000..048d182
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/DigesterFactory.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+import java.net.URL;
+
+import org.apache.catalina.util.SchemaResolver;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSet;
+
+/**
+ * Wrapper class around the Digester that hide Digester's initialization details
+ *
+ * @author Jean-Francois Arcand
+ */
+public class DigesterFactory {
+    /**
+     * The log.
+     */
+   protected static org.apache.commons.logging.Log log = 
+       org.apache.commons.logging.LogFactory.getLog(DigesterFactory.class);
+
+    /**
+     * The XML entiry resolver used by the Digester.
+     */
+    private static SchemaResolver schemaResolver;
+
+
+    /**
+     * Create a <code>Digester</code> parser with no <code>Rule</code>
+     * associated and XML validation turned off.
+     */
+    public static Digester newDigester(){
+        return newDigester(false, false, null);
+    }
+
+    
+    /**
+     * Create a <code>Digester</code> parser with XML validation turned off.
+     * @param rule an instance of <code>RuleSet</code> used for parsing the xml.
+     */
+    public static Digester newDigester(RuleSet rule){
+        return newDigester(false,false,rule);
+    }
+
+    
+    /**
+     * Create a <code>Digester</code> parser.
+     * @param xmlValidation turn on/off xml validation
+     * @param xmlNamespaceAware turn on/off namespace validation
+     * @param rule an instance of <code>RuleSet</code> used for parsing the xml.
+     */
+    public static Digester newDigester(boolean xmlValidation,
+                                       boolean xmlNamespaceAware,
+                                       RuleSet rule) {
+        Digester digester = new Digester();
+        digester.setNamespaceAware(xmlNamespaceAware);
+        digester.setValidating(xmlValidation);
+        digester.setUseContextClassLoader(true);
+
+        if (xmlValidation || xmlNamespaceAware){
+            configureSchema(digester);        
+        }
+
+        schemaResolver = new SchemaResolver(digester);
+        registerLocalSchema();
+        
+        digester.setEntityResolver(schemaResolver);
+        if ( rule != null ) {
+            digester.addRuleSet(rule);
+        }
+
+        return (digester);
+    }
+
+
+    /**
+     * Utilities used to force the parser to use local schema, when available,
+     * instead of the <code>schemaLocation</code> XML element.
+     */
+    protected static void registerLocalSchema(){
+        // J2EE
+        register(Constants.J2eeSchemaResourcePath_14,
+                 Constants.J2eeSchemaPublicId_14);
+        // W3C
+        register(Constants.W3cSchemaResourcePath_10,
+                 Constants.W3cSchemaPublicId_10);
+        // JSP
+        register(Constants.JspSchemaResourcePath_20,
+                 Constants.JspSchemaPublicId_20);
+        // TLD
+        register(Constants.TldDtdResourcePath_11,  
+                 Constants.TldDtdPublicId_11);
+        
+        register(Constants.TldDtdResourcePath_12,
+                 Constants.TldDtdPublicId_12);
+
+        register(Constants.TldSchemaResourcePath_20,
+                 Constants.TldSchemaPublicId_20);
+
+        // web.xml    
+        register(Constants.WebDtdResourcePath_22,
+                 Constants.WebDtdPublicId_22);
+
+        register(Constants.WebDtdResourcePath_23,
+                 Constants.WebDtdPublicId_23);
+
+        register(Constants.WebSchemaResourcePath_24,
+                 Constants.WebSchemaPublicId_24);
+
+        // Web Service
+        register(Constants.J2eeWebServiceSchemaResourcePath_11,
+                 Constants.J2eeWebServiceSchemaPublicId_11);
+
+        register(Constants.J2eeWebServiceClientSchemaResourcePath_11,
+                 Constants.J2eeWebServiceClientSchemaPublicId_11);
+
+    }
+
+
+    /**
+     * Load the resource and add it to the resolver.
+     */
+    protected static void register(String resourceURL, String resourcePublicId){
+        URL url = DigesterFactory.class.getResource(resourceURL);
+   
+        if(url == null) {
+            log.warn("Could not get url for " + resourceURL);
+        } else {
+            schemaResolver.register(resourcePublicId , url.toString() );
+        }
+    }
+
+
+    /**
+     * Turn on DTD and/or validation (based on the parser implementation)
+     */
+    protected static void configureSchema(Digester digester){
+        URL url = DigesterFactory.class
+                        .getResource(Constants.WebSchemaResourcePath_24);
+  
+        if(url == null) {
+            log.error("Could not get url for " 
+                                        + Constants.WebSchemaResourcePath_24);
+        } else {
+            digester.setSchema(url.toString());     
+        }
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/Embedded.java b/container/catalina/src/share/org/apache/catalina/startup/Embedded.java
new file mode 100644
index 0000000..363f65c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/Embedded.java
@@ -0,0 +1,947 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.HashMap;
+
+import org.apache.catalina.Authenticator;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Valve;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.core.StandardService;
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.security.SecurityConfig;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+
+/**
+ * Convenience class to embed a Catalina servlet container environment
+ * inside another application.  You must call the methods of this class in the
+ * following order to ensure correct operation.
+ *
+ * <ul>
+ * <li>Instantiate a new instance of this class.</li>
+ * <li>Set the relevant properties of this object itself.  In particular,
+ *     you will want to establish the default Logger to be used, as well
+ *     as the default Realm if you are using container-managed security.</li>
+ * <li>Call <code>createEngine()</code> to create an Engine object, and then
+ *     call its property setters as desired.</li>
+ * <li>Call <code>createHost()</code> to create at least one virtual Host
+ *     associated with the newly created Engine, and then call its property
+ *     setters as desired.  After you customize this Host, add it to the
+ *     corresponding Engine with <code>engine.addChild(host)</code>.</li>
+ * <li>Call <code>createContext()</code> to create at least one Context
+ *     associated with each newly created Host, and then call its property
+ *     setters as desired.  You <strong>SHOULD</strong> create a Context with
+ *     a pathname equal to a zero-length string, which will be used to process
+ *     all requests not mapped to some other Context.  After you customize
+ *     this Context, add it to the corresponding Host with
+ *     <code>host.addChild(context)</code>.</li>
+ * <li>Call <code>addEngine()</code> to attach this Engine to the set of
+ *     defined Engines for this object.</li>
+ * <li>Call <code>createConnector()</code> to create at least one TCP/IP
+ *     connector, and then call its property setters as desired.</li>
+ * <li>Call <code>addConnector()</code> to attach this Connector to the set
+ *     of defined Connectors for this object.  The added Connector will use
+ *     the most recently added Engine to process its received requests.</li>
+ * <li>Repeat the above series of steps as often as required (although there
+ *     will typically be only one Engine instance created).</li>
+ * <li>Call <code>start()</code> to initiate normal operations of all the
+ *     attached components.</li>
+ * </ul>
+ *
+ * After normal operations have begun, you can add and remove Connectors,
+ * Engines, Hosts, and Contexts on the fly.  However, once you have removed
+ * a particular component, it must be thrown away -- you can create a new one
+ * with the same characteristics if you merely want to do a restart.
+ * <p>
+ * To initiate a normal shutdown, call the <code>stop()</code> method of
+ * this object.
+ * <p>
+ * @see org.apache.catalina.startup.Catalina#main For a complete example
+ * of how Tomcat is set up and launched as an Embedded application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Embedded  extends StandardService implements Lifecycle {
+    private static Log log = LogFactory.getLog(Embedded.class);
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class with default properties.
+     */
+    public Embedded() {
+
+        this(null);
+
+    }
+
+
+    /**
+     * Construct a new instance of this class with specified properties.
+     *
+     * @param realm Realm implementation to be inherited by all components
+     *  (unless overridden further down the container hierarchy)
+     */
+    public Embedded(Realm realm) {
+
+        super();
+        setRealm(realm);
+        setSecurityProtection();
+        
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Is naming enabled ?
+     */
+    protected boolean useNaming = true;
+
+
+    /**
+     * The set of Engines that have been deployed in this server.  Normally
+     * there will only be one.
+     */
+    protected Engine engines[] = new Engine[0];
+
+
+    /**
+     * Custom mappings of login methods to authenticators
+     */
+    protected HashMap authenticators;
+
+
+    /**
+     * Descriptive information about this server implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.startup.Embedded/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The default realm to be used by all containers associated with
+     * this compoennt.
+     */
+    protected Realm realm = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+    /**
+     * Use await.
+     */
+    protected boolean await = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return true if naming is enabled.
+     */
+    public boolean isUseNaming() {
+
+        return (this.useNaming);
+
+    }
+
+
+    /**
+     * Enables or disables naming support.
+     *
+     * @param useNaming The new use naming value
+     */
+    public void setUseNaming(boolean useNaming) {
+
+        boolean oldUseNaming = this.useNaming;
+        this.useNaming = useNaming;
+        support.firePropertyChange("useNaming", new Boolean(oldUseNaming),
+                                   new Boolean(this.useNaming));
+
+    }
+
+
+    /**
+     * Return the default Realm for our Containers.
+     */
+    public Realm getRealm() {
+
+        return (this.realm);
+
+    }
+
+
+    /**
+     * Set the default Realm for our Containers.
+     *
+     * @param realm The new default realm
+     */
+    public void setRealm(Realm realm) {
+
+        Realm oldRealm = this.realm;
+        this.realm = realm;
+        support.firePropertyChange("realm", oldRealm, this.realm);
+
+    }
+
+    public void setAwait(boolean b) {
+        await = b;
+    }
+
+    public boolean isAwait() {
+        return await;
+    }
+
+    public void setCatalinaHome( String s ) {
+        System.setProperty( "catalina.home", s);
+    }
+
+    public void setCatalinaBase( String s ) {
+        System.setProperty( "catalina.base", s);
+    }
+
+    public String getCatalinaHome() {
+        return System.getProperty("catalina.home");
+    }
+
+    public String getCatalinaBase() {
+        return System.getProperty("catalina.base");
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Add a new Connector to the set of defined Connectors.  The newly
+     * added Connector will be associated with the most recently added Engine.
+     *
+     * @param connector The connector to be added
+     *
+     * @exception IllegalStateException if no engines have been added yet
+     */
+    public synchronized void addConnector(Connector connector) {
+
+        if( log.isDebugEnabled() ) {
+            log.debug("Adding connector (" + connector.getInfo() + ")");
+        }
+
+        // Make sure we have a Container to send requests to
+        if (engines.length < 1)
+            throw new IllegalStateException
+                (sm.getString("embedded.noEngines"));
+
+        /*
+         * Add the connector. This will set the connector's container to the
+         * most recently added Engine
+         */
+        super.addConnector(connector);
+    }
+
+
+    /**
+     * Add a new Engine to the set of defined Engines.
+     *
+     * @param engine The engine to be added
+     */
+    public synchronized void addEngine(Engine engine) {
+
+        if( log.isDebugEnabled() )
+            log.debug("Adding engine (" + engine.getInfo() + ")");
+
+        // Add this Engine to our set of defined Engines
+        Engine results[] = new Engine[engines.length + 1];
+        for (int i = 0; i < engines.length; i++)
+            results[i] = engines[i];
+        results[engines.length] = engine;
+        engines = results;
+
+        // Start this Engine if necessary
+        if (started && (engine instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) engine).start();
+            } catch (LifecycleException e) {
+                log.error("Engine.start", e);
+            }
+        }
+
+        this.container = engine;
+    }
+
+
+    /**
+     * Create, configure, and return a new TCP/IP socket connector
+     * based on the specified properties.
+     *
+     * @param address InetAddress to bind to, or <code>null</code> if the
+     * connector is supposed to bind to all addresses on this server
+     * @param port Port number to listen to
+     * @param secure true if the generated connector is supposed to be
+     * SSL-enabled, and false otherwise
+     */
+    public Connector createConnector(InetAddress address, int port,
+                                     boolean secure) {
+	return createConnector(address != null? address.toString() : null,
+			       port, secure);
+    }
+
+    public Connector createConnector(String address, int port,
+                                     boolean secure) {
+        String protocol = "http";
+        if (secure) {
+            protocol = "https";
+        }
+
+        return createConnector(address, port, protocol);
+    }
+
+
+    public Connector createConnector(InetAddress address, int port,
+                                     String protocol) {
+	return createConnector(address != null? address.toString() : null,
+			       port, protocol);
+    }
+
+    public Connector createConnector(String address, int port,
+				     String protocol) {
+
+        Connector connector = null;
+
+	if (address != null) {
+	    /*
+	     * InetAddress.toString() returns a string of the form
+	     * "<hostname>/<literal_IP>". Get the latter part, so that the
+	     * address can be parsed (back) into an InetAddress using
+	     * InetAddress.getByName().
+	     */
+	    int index = address.indexOf('/');
+	    if (index != -1) {
+		address = address.substring(index + 1);
+	    }
+	}
+
+	if (log.isDebugEnabled()) {
+            log.debug("Creating connector for address='" +
+		      ((address == null) ? "ALL" : address) +
+		      "' port='" + port + "' protocol='" + protocol + "'");
+	}
+
+        try {
+
+            if (protocol.equals("ajp")) {
+                connector = new Connector("org.apache.jk.server.JkCoyoteHandler");
+            } else if (protocol.equals("memory")) {
+                connector = new Connector("org.apache.coyote.memory.MemoryProtocolHandler");
+            } else if (protocol.equals("http")) {
+                connector = new Connector();
+            } else if (protocol.equals("https")) {
+                connector = new Connector();
+                connector.setScheme("https");
+                connector.setSecure(true);
+                // FIXME !!!! SET SSL PROPERTIES
+            }
+
+            if (address != null) {
+                IntrospectionUtils.setProperty(connector, "address", 
+                                               "" + address);
+            }
+            IntrospectionUtils.setProperty(connector, "port", "" + port);
+
+        } catch (Exception e) {
+            log.error("Couldn't create connector.");
+        } 
+
+        return (connector);
+
+    }
+
+    /**
+     * Create, configure, and return a Context that will process all
+     * HTTP requests received from one of the associated Connectors,
+     * and directed to the specified context path on the virtual host
+     * to which this Context is connected.
+     * <p>
+     * After you have customized the properties, listeners, and Valves
+     * for this Context, you must attach it to the corresponding Host
+     * by calling:
+     * <pre>
+     *   host.addChild(context);
+     * </pre>
+     * which will also cause the Context to be started if the Host has
+     * already been started.
+     *
+     * @param path Context path of this application ("" for the default
+     *  application for this host, must start with a slash otherwise)
+     * @param docBase Absolute pathname to the document base directory
+     *  for this web application
+     *
+     * @exception IllegalArgumentException if an invalid parameter
+     *  is specified
+     */
+    public Context createContext(String path, String docBase) {
+
+        if( log.isDebugEnabled() )
+            log.debug("Creating context '" + path + "' with docBase '" +
+                       docBase + "'");
+
+        StandardContext context = new StandardContext();
+
+        context.setDocBase(docBase);
+        context.setPath(path);
+
+        ContextConfig config = new ContextConfig();
+        config.setCustomAuthenticators(authenticators);
+        ((Lifecycle) context).addLifecycleListener(config);
+
+        return (context);
+
+    }
+
+
+    /**
+     * Create, configure, and return an Engine that will process all
+     * HTTP requests received from one of the associated Connectors,
+     * based on the specified properties.
+     */
+    public Engine createEngine() {
+
+        if( log.isDebugEnabled() )
+            log.debug("Creating engine");
+
+        StandardEngine engine = new StandardEngine();
+
+        // Default host will be set to the first host added
+        engine.setRealm(realm);         // Inherited by all children
+
+        return (engine);
+
+    }
+
+
+    /**
+     * Create, configure, and return a Host that will process all
+     * HTTP requests received from one of the associated Connectors,
+     * and directed to the specified virtual host.
+     * <p>
+     * After you have customized the properties, listeners, and Valves
+     * for this Host, you must attach it to the corresponding Engine
+     * by calling:
+     * <pre>
+     *   engine.addChild(host);
+     * </pre>
+     * which will also cause the Host to be started if the Engine has
+     * already been started.  If this is the default (or only) Host you
+     * will be defining, you may also tell the Engine to pass all requests
+     * not assigned to another virtual host to this one:
+     * <pre>
+     *   engine.setDefaultHost(host.getName());
+     * </pre>
+     *
+     * @param name Canonical name of this virtual host
+     * @param appBase Absolute pathname to the application base directory
+     *  for this virtual host
+     *
+     * @exception IllegalArgumentException if an invalid parameter
+     *  is specified
+     */
+    public Host createHost(String name, String appBase) {
+
+        if( log.isDebugEnabled() )
+            log.debug("Creating host '" + name + "' with appBase '" +
+                       appBase + "'");
+
+        StandardHost host = new StandardHost();
+
+        host.setAppBase(appBase);
+        host.setName(name);
+
+        return (host);
+
+    }
+
+
+    /**
+     * Create and return a class loader manager that can be customized, and
+     * then attached to a Context, before it is started.
+     *
+     * @param parent ClassLoader that will be the parent of the one
+     *  created by this Loader
+     */
+    public Loader createLoader(ClassLoader parent) {
+
+        if( log.isDebugEnabled() )
+            log.debug("Creating Loader with parent class loader '" +
+                       parent + "'");
+
+        WebappLoader loader = new WebappLoader(parent);
+        return (loader);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Server implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Remove the specified Context from the set of defined Contexts for its
+     * associated Host.  If this is the last Context for this Host, the Host
+     * will also be removed.
+     *
+     * @param context The Context to be removed
+     */
+    public synchronized void removeContext(Context context) {
+
+        if( log.isDebugEnabled() )
+            log.debug("Removing context[" + context.getPath() + "]");
+
+        // Is this Context actually among those that are defined?
+        boolean found = false;
+        for (int i = 0; i < engines.length; i++) {
+            Container hosts[] = engines[i].findChildren();
+            for (int j = 0; j < hosts.length; j++) {
+                Container contexts[] = hosts[j].findChildren();
+                for (int k = 0; k < contexts.length; k++) {
+                    if (context == (Context) contexts[k]) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (found)
+                    break;
+            }
+            if (found)
+                break;
+        }
+        if (!found)
+            return;
+
+        // Remove this Context from the associated Host
+        if( log.isDebugEnabled() )
+            log.debug(" Removing this Context");
+        context.getParent().removeChild(context);
+
+    }
+
+
+    /**
+     * Remove the specified Engine from the set of defined Engines, along with
+     * all of its related Hosts and Contexts.  All associated Connectors are
+     * also removed.
+     *
+     * @param engine The Engine to be removed
+     */
+    public synchronized void removeEngine(Engine engine) {
+
+        if( log.isDebugEnabled() )
+            log.debug("Removing engine (" + engine.getInfo() + ")");
+
+        // Is the specified Engine actually defined?
+        int j = -1;
+        for (int i = 0; i < engines.length; i++) {
+            if (engine == engines[i]) {
+                j = i;
+                break;
+            }
+        }
+        if (j < 0)
+            return;
+
+        // Remove any Connector that is using this Engine
+        if( log.isDebugEnabled() )
+            log.debug(" Removing related Containers");
+        while (true) {
+            int n = -1;
+            for (int i = 0; i < connectors.length; i++) {
+                if (connectors[i].getContainer() == (Container) engine) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                break;
+            removeConnector(connectors[n]);
+        }
+
+        // Stop this Engine if necessary
+        if (engine instanceof Lifecycle) {
+            if( log.isDebugEnabled() )
+                log.debug(" Stopping this Engine");
+            try {
+                ((Lifecycle) engine).stop();
+            } catch (LifecycleException e) {
+                log.error("Engine.stop", e);
+            }
+        }
+
+        // Remove this Engine from our set of defined Engines
+        if( log.isDebugEnabled() )
+            log.debug(" Removing this Engine");
+        int k = 0;
+        Engine results[] = new Engine[engines.length - 1];
+        for (int i = 0; i < engines.length; i++) {
+            if (i != j)
+                results[k++] = engines[i];
+        }
+        engines = results;
+
+    }
+
+
+    /**
+     * Remove the specified Host, along with all of its related Contexts,
+     * from the set of defined Hosts for its associated Engine.  If this is
+     * the last Host for this Engine, the Engine will also be removed.
+     *
+     * @param host The Host to be removed
+     */
+    public synchronized void removeHost(Host host) {
+
+        if( log.isDebugEnabled() )
+            log.debug("Removing host[" + host.getName() + "]");
+
+        // Is this Host actually among those that are defined?
+        boolean found = false;
+        for (int i = 0; i < engines.length; i++) {
+            Container hosts[] = engines[i].findChildren();
+            for (int j = 0; j < hosts.length; j++) {
+                if (host == (Host) hosts[j]) {
+                    found = true;
+                    break;
+
+                }
+            }
+            if (found)
+                break;
+        }
+        if (!found)
+            return;
+
+        // Remove this Host from the associated Engine
+        if( log.isDebugEnabled() )
+            log.debug(" Removing this Host");
+        host.getParent().removeChild(host);
+
+    }
+
+
+    /*
+     * Maps the specified login method to the specified authenticator, allowing
+     * the mappings in org/apache/catalina/startup/Authenticators.properties
+     * to be overridden.
+     *
+     * @param authenticator Authenticator to handle authentication for the
+     * specified login method
+     * @param loginMethod Login method that maps to the specified authenticator
+     *
+     * @throws IllegalArgumentException if the specified authenticator does not
+     * implement the org.apache.catalina.Valve interface
+     */
+    public void addAuthenticator(Authenticator authenticator,
+                                 String loginMethod) {
+        if (!(authenticator instanceof Valve)) {
+            throw new IllegalArgumentException(
+                sm.getString("embedded.authenticatorNotInstanceOfValve"));
+        }
+        if (authenticators == null) {
+            synchronized (this) {
+                if (authenticators == null) {
+                    authenticators = new HashMap();
+                }
+            }
+        }
+        authenticators.put(loginMethod, authenticator);
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        if( log.isInfoEnabled() )
+            log.info("Starting tomcat server");
+
+        // Validate the setup of our required system properties
+        initDirs();
+
+        // Initialize some naming specific properties
+        initNaming();
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (sm.getString("embedded.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+        initialized = true;
+
+        // Start our defined Engines first
+        for (int i = 0; i < engines.length; i++) {
+            if (engines[i] instanceof Lifecycle)
+                ((Lifecycle) engines[i]).start();
+        }
+
+        // Start our defined Connectors second
+        for (int i = 0; i < connectors.length; i++) {
+            connectors[i].initialize();
+            if (connectors[i] instanceof Lifecycle)
+                ((Lifecycle) connectors[i]).start();
+        }
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        if( log.isDebugEnabled() )
+            log.debug("Stopping embedded server");
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("embedded.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Stop our defined Connectors first
+        for (int i = 0; i < connectors.length; i++) {
+            if (connectors[i] instanceof Lifecycle)
+                ((Lifecycle) connectors[i]).stop();
+        }
+
+        // Stop our defined Engines second
+        for (int i = 0; i < engines.length; i++) {
+            if (engines[i] instanceof Lifecycle)
+                ((Lifecycle) engines[i]).stop();
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /** Initialize naming - this should only enable java:env and root naming.
+     * If tomcat is embeded in an application that already defines those -
+     * it shouldn't do it.
+     *
+     * XXX The 2 should be separated, you may want to enable java: but not
+     * the initial context and the reverse
+     * XXX Can we "guess" - i.e. lookup java: and if something is returned assume
+     * false ?
+     * XXX We have a major problem with the current setting for java: url
+     */
+    protected void initNaming() {
+        // Setting additional variables
+        if (!useNaming) {
+            log.info( "Catalina naming disabled");
+            System.setProperty("catalina.useNaming", "false");
+        } else {
+            System.setProperty("catalina.useNaming", "true");
+            String value = "org.apache.naming";
+            String oldValue =
+                System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
+            if (oldValue != null) {
+                value = value + ":" + oldValue;
+            }
+            System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
+            if( log.isDebugEnabled() )
+                log.debug("Setting naming prefix=" + value);
+            value = System.getProperty
+                (javax.naming.Context.INITIAL_CONTEXT_FACTORY);
+            if (value == null) {
+                System.setProperty
+                    (javax.naming.Context.INITIAL_CONTEXT_FACTORY,
+                     "org.apache.naming.java.javaURLContextFactory");
+            } else {
+                log.debug( "INITIAL_CONTEXT_FACTORY alread set " + value );
+            }
+        }
+    }
+
+
+    protected void initDirs() {
+
+        String catalinaHome = System.getProperty("catalina.home");
+        if (catalinaHome == null) {
+            // Backwards compatibility patch for J2EE RI 1.3
+            String j2eeHome = System.getProperty("com.sun.enterprise.home");
+            if (j2eeHome != null) {
+                catalinaHome=System.getProperty("com.sun.enterprise.home");
+            } else if (System.getProperty("catalina.base") != null) {
+                catalinaHome = System.getProperty("catalina.base");
+            } else {
+                // Use IntrospectionUtils and guess the dir
+                catalinaHome = IntrospectionUtils.guessInstall
+                    ("catalina.home", "catalina.base", "catalina.jar");
+                if (catalinaHome == null) {
+                    catalinaHome = IntrospectionUtils.guessInstall
+                        ("tomcat.install", "catalina.home", "tomcat.jar");
+                }
+            }
+        }
+        if (catalinaHome != null) {
+            File home = new File(catalinaHome);
+            if (!home.isAbsolute()) {
+                try {
+                    catalinaHome = home.getCanonicalPath();
+                } catch (IOException e) {
+                    catalinaHome = home.getAbsolutePath();
+                }
+            }
+            System.setProperty("catalina.home", catalinaHome);
+        }
+
+        if (System.getProperty("catalina.base") == null) {
+            System.setProperty("catalina.base",
+                               System.getProperty("catalina.home"));
+        } else {
+            String catalinaBase = System.getProperty("catalina.base");
+            File base = new File(catalinaBase);
+            if (!base.isAbsolute()) {
+                try {
+                    catalinaBase = base.getCanonicalPath();
+                } catch (IOException e) {
+                    catalinaBase = base.getAbsolutePath();
+                }
+            }
+            System.setProperty("catalina.base", catalinaBase);
+        }
+        
+        String temp = System.getProperty("java.io.tmpdir");
+        if (temp == null || (!(new File(temp)).exists())
+                || (!(new File(temp)).isDirectory())) {
+            log.error(sm.getString("embedded.notmp", temp));
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+    /**
+     * Set the security package access/protection.
+     */
+    protected void setSecurityProtection(){
+        SecurityConfig securityConfig = SecurityConfig.newInstance();
+        securityConfig.setPackageDefinition();
+        securityConfig.setPackageAccess();
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/EngineConfig.java b/container/catalina/src/share/org/apache/catalina/startup/EngineConfig.java
new file mode 100644
index 0000000..19b500c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/EngineConfig.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.Engine;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * Startup event listener for a <b>Engine</b> that configures the properties
+ * of that Engine, and the associated defined contexts.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class EngineConfig
+    implements LifecycleListener {
+
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( EngineConfig.class );
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Engine we are associated with.
+     */
+    private Engine engine = null;
+
+
+    /**
+     * The string resources for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the START event for an associated Engine.
+     *
+     * @param event The lifecycle event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        // Identify the engine we are associated with
+        try {
+            engine = (Engine) event.getLifecycle();
+        } catch (ClassCastException e) {
+            log.error(sm.getString("engineConfig.cce", event.getLifecycle()), e);
+            return;
+        }
+
+        // Process the event that has occurred
+        if (event.getType().equals(Lifecycle.START_EVENT))
+            start();
+        else if (event.getType().equals(Lifecycle.STOP_EVENT))
+            stop();
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Process a "start" event for this Engine.
+     */
+    private void start() {
+
+        if (engine.getLogger().isDebugEnabled())
+            engine.getLogger().debug(sm.getString("engineConfig.start"));
+
+    }
+
+
+    /**
+     * Process a "stop" event for this Engine.
+     */
+    private void stop() {
+
+        if (engine.getLogger().isDebugEnabled())
+            engine.getLogger().debug(sm.getString("engineConfig.stop"));
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/EngineRuleSet.java b/container/catalina/src/share/org/apache/catalina/startup/EngineRuleSet.java
new file mode 100644
index 0000000..f9e788e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/EngineRuleSet.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSetBase;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a
+ * Engine definition element.  This <code>RuleSet</code> does NOT include
+ * any rules for nested Host or DefaultContext elements, which should
+ * be added via instances of <code>HostRuleSet</code> or
+ * <code>ContextRuleSet</code>, respectively.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class EngineRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public EngineRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public EngineRuleSet(String prefix) {
+
+        super();
+        this.namespaceURI = null;
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+
+        digester.addObjectCreate(prefix + "Engine",
+                                 "org.apache.catalina.core.StandardEngine",
+                                 "className");
+        digester.addSetProperties(prefix + "Engine");
+        digester.addRule(prefix + "Engine",
+                         new LifecycleListenerRule
+                         ("org.apache.catalina.startup.EngineConfig",
+                          "engineConfigClass"));
+        digester.addSetNext(prefix + "Engine",
+                            "setContainer",
+                            "org.apache.catalina.Container");
+
+        //Cluster configuration start
+        digester.addObjectCreate(prefix + "Engine/Cluster",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Engine/Cluster");
+        digester.addSetNext(prefix + "Engine/Cluster",
+                            "setCluster",
+                            "org.apache.catalina.Cluster");
+        //Cluster configuration end
+
+        digester.addObjectCreate(prefix + "Engine/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Engine/Listener");
+        digester.addSetNext(prefix + "Engine/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        digester.addObjectCreate(prefix + "Engine/Logger",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Engine/Logger");
+        digester.addSetNext(prefix + "Engine/Logger",
+                            "setLogger",
+                            "org.apache.catalina.Logger");
+
+        digester.addObjectCreate(prefix + "Engine/Realm",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Engine/Realm");
+        digester.addSetNext(prefix + "Engine/Realm",
+                            "setRealm",
+                            "org.apache.catalina.Realm");
+
+        digester.addObjectCreate(prefix + "Engine/Valve",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Engine/Valve");
+        digester.addSetNext(prefix + "Engine/Valve",
+                            "addValve",
+                            "org.apache.catalina.Valve");
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/ExpandWar.java b/container/catalina/src/share/org/apache/catalina/startup/ExpandWar.java
new file mode 100644
index 0000000..14d7e8d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/ExpandWar.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.nio.channels.FileChannel;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.apache.catalina.Host;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Expand out a WAR in a Host's appBase.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @author Glenn L. Nielsen
+ * @version $Revision$
+ */
+
+public class ExpandWar {
+
+    private static Log log = LogFactory.getLog(ExpandWar.class);
+
+    /**
+     * The string resources for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Expand the WAR file found at the specified URL into an unpacked
+     * directory structure, and return the absolute pathname to the expanded
+     * directory.
+     *
+     * @param host Host war is being installed for
+     * @param war URL of the web application archive to be expanded
+     *  (must start with "jar:")
+     *
+     * @exception IllegalArgumentException if this is not a "jar:" URL
+     * @exception IOException if an input/output error was encountered
+     *  during expansion
+     */
+    public static String expand(Host host, URL war)
+        throws IOException {
+
+        // Calculate the directory name of the expanded directory
+        if (host.getLogger().isDebugEnabled()) {
+            host.getLogger().debug("expand(" + war.toString() + ")");
+        }
+        String pathname = war.toString().replace('\\', '/');
+        if (pathname.endsWith("!/")) {
+            pathname = pathname.substring(0, pathname.length() - 2);
+        }
+        int period = pathname.lastIndexOf('.');
+        if (period >= pathname.length() - 4)
+            pathname = pathname.substring(0, period);
+        int slash = pathname.lastIndexOf('/');
+        if (slash >= 0) {
+            pathname = pathname.substring(slash + 1);
+        }
+        if (host.getLogger().isDebugEnabled()) {
+            host.getLogger().debug("  Proposed directory name: " + pathname);
+        }
+        return expand(host, war, pathname);
+
+    }
+
+
+    /**
+     * Expand the WAR file found at the specified URL into an unpacked
+     * directory structure, and return the absolute pathname to the expanded
+     * directory.
+     *
+     * @param host Host war is being installed for
+     * @param war URL of the web application archive to be expanded
+     *  (must start with "jar:")
+     * @param pathname Context path name for web application
+     *
+     * @exception IllegalArgumentException if this is not a "jar:" URL
+     * @exception IOException if an input/output error was encountered
+     *  during expansion
+     */
+    public static String expand(Host host, URL war, String pathname)
+        throws IOException {
+
+        // Make sure that there is no such directory already existing
+        File appBase = new File(host.getAppBase());
+        if (!appBase.isAbsolute()) {
+            appBase = new File(System.getProperty("catalina.base"),
+                               host.getAppBase());
+        }
+        if (!appBase.exists() || !appBase.isDirectory()) {
+            throw new IOException
+                (sm.getString("hostConfig.appBase",
+                              appBase.getAbsolutePath()));
+        }
+        File docBase = new File(appBase, pathname);
+        if (docBase.exists()) {
+            // War file is already installed
+            return (docBase.getAbsolutePath());
+        }
+
+        // Create the new document base directory
+        docBase.mkdir();
+
+        // Expand the WAR into the new document base directory
+        JarURLConnection juc = (JarURLConnection) war.openConnection();
+        juc.setUseCaches(false);
+        JarFile jarFile = null;
+        InputStream input = null;
+        try {
+            jarFile = juc.getJarFile();
+            Enumeration jarEntries = jarFile.entries();
+            while (jarEntries.hasMoreElements()) {
+                JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
+                String name = jarEntry.getName();
+                int last = name.lastIndexOf('/');
+                if (last >= 0) {
+                    File parent = new File(docBase,
+                                           name.substring(0, last));
+                    parent.mkdirs();
+                }
+                if (name.endsWith("/")) {
+                    continue;
+                }
+                input = jarFile.getInputStream(jarEntry);
+
+                // Bugzilla 33636
+                File expandedFile = expand(input, docBase, name);
+                long lastModified = jarEntry.getTime();
+                if ((lastModified != -1) && (lastModified != 0) && (expandedFile != null)) {
+                    expandedFile.setLastModified(lastModified);
+                }
+
+                input.close();
+                input = null;
+            }
+        } catch (IOException e) {
+            // If something went wrong, delete expanded dir to keep things 
+            // clean
+            deleteDir(docBase);
+            throw e;
+        } finally {
+            if (input != null) {
+                try {
+                    input.close();
+                } catch (Throwable t) {
+                    ;
+                }
+                input = null;
+            }
+            if (jarFile != null) {
+                try {
+                    jarFile.close();
+                } catch (Throwable t) {
+                    ;
+                }
+                jarFile = null;
+            }
+        }
+
+        // Return the absolute path to our new document base directory
+        return (docBase.getAbsolutePath());
+
+    }
+
+
+    /**
+     * Copy the specified file or directory to the destination.
+     *
+     * @param src File object representing the source
+     * @param dest File object representing the destination
+     */
+    public static boolean copy(File src, File dest) {
+        
+        boolean result = true;
+        
+        String files[] = null;
+        if (src.isDirectory()) {
+            files = src.list();
+            result = dest.mkdir();
+        } else {
+            files = new String[1];
+            files[0] = "";
+        }
+        if (files == null) {
+            files = new String[0];
+        }
+        for (int i = 0; (i < files.length) && result; i++) {
+            File fileSrc = new File(src, files[i]);
+            File fileDest = new File(dest, files[i]);
+            if (fileSrc.isDirectory()) {
+                result = copy(fileSrc, fileDest);
+            } else {
+                FileChannel ic = null;
+                FileChannel oc = null;
+                try {
+                    ic = (new FileInputStream(fileSrc)).getChannel();
+                    oc = (new FileOutputStream(fileDest)).getChannel();
+                    ic.transferTo(0, ic.size(), oc);
+                } catch (IOException e) {
+                    log.error(sm.getString
+                            ("expandWar.copy", fileSrc, fileDest), e);
+                    result = false;
+                } finally {
+                    if (ic != null) {
+                        try {
+                            ic.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                    if (oc != null) {
+                        try {
+                            oc.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                }
+            }
+        }
+        return result;
+        
+    }
+    
+    
+    /**
+     * Delete the specified directory, including all of its contents and
+     * subdirectories recursively.
+     *
+     * @param dir File object representing the directory to be deleted
+     */
+    public static boolean delete(File dir) {
+        if (dir.isDirectory()) {
+            return deleteDir(dir);
+        } else {
+            return dir.delete();
+        }
+    }
+    
+    
+    /**
+     * Delete the specified directory, including all of its contents and
+     * subdirectories recursively.
+     *
+     * @param dir File object representing the directory to be deleted
+     */
+    public static boolean deleteDir(File dir) {
+
+        String files[] = dir.list();
+        if (files == null) {
+            files = new String[0];
+        }
+        for (int i = 0; i < files.length; i++) {
+            File file = new File(dir, files[i]);
+            if (file.isDirectory()) {
+                deleteDir(file);
+            } else {
+                file.delete();
+            }
+        }
+        return dir.delete();
+
+    }
+
+
+    /**
+     * Expand the specified input stream into the specified directory, creating
+     * a file named from the specified relative path.
+     *
+     * @param input InputStream to be copied
+     * @param docBase Document base directory into which we are expanding
+     * @param name Relative pathname of the file to be created
+     * @return A handle to the expanded File
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected static File expand(InputStream input, File docBase, String name)
+        throws IOException {
+
+        File file = new File(docBase, name);
+        BufferedOutputStream output = null;
+        try {
+            output = 
+                new BufferedOutputStream(new FileOutputStream(file));
+            byte buffer[] = new byte[2048];
+            while (true) {
+                int n = input.read(buffer);
+                if (n <= 0)
+                    break;
+                output.write(buffer, 0, n);
+            }
+        } finally {
+            if (output != null) {
+                try {
+                    output.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+
+        return file;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/HomesUserDatabase.java b/container/catalina/src/share/org/apache/catalina/startup/HomesUserDatabase.java
new file mode 100644
index 0000000..af991b3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/HomesUserDatabase.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+
+/**
+ * Concrete implementation of the <strong>UserDatabase</code> interface
+ * considers all directories in a directory whose pathname is specified
+ * to our constructor to be "home" directories for those users.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class HomesUserDatabase
+    implements UserDatabase {
+
+
+    // --------------------------------------------------------- Constructors
+
+
+    /**
+     * Initialize a new instance of this user database component.
+     */
+    public HomesUserDatabase() {
+
+        super();
+
+    }
+
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of home directories for all defined users, keyed by username.
+     */
+    private Hashtable homes = new Hashtable();
+
+
+    /**
+     * The UserConfig listener with which we are associated.
+     */
+    private UserConfig userConfig = null;
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return the UserConfig listener with which we are associated.
+     */
+    public UserConfig getUserConfig() {
+
+        return (this.userConfig);
+
+    }
+
+
+    /**
+     * Set the UserConfig listener with which we are associated.
+     *
+     * @param userConfig The new UserConfig listener
+     */
+    public void setUserConfig(UserConfig userConfig) {
+
+        this.userConfig = userConfig;
+        init();
+
+    }
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return an absolute pathname to the home directory for the specified user.
+     *
+     * @param user User for which a home directory should be retrieved
+     */
+    public String getHome(String user) {
+
+        return ((String) homes.get(user));
+
+    }
+
+
+    /**
+     * Return an enumeration of the usernames defined on this server.
+     */
+    public Enumeration getUsers() {
+
+        return (homes.keys());
+
+    }
+
+
+    // ------------------------------------------------------ Private Methods
+
+
+    /**
+     * Initialize our set of users and home directories.
+     */
+    private void init() {
+
+        String homeBase = userConfig.getHomeBase();
+        File homeBaseDir = new File(homeBase);
+        if (!homeBaseDir.exists() || !homeBaseDir.isDirectory())
+            return;
+        String homeBaseFiles[] = homeBaseDir.list();
+
+        for (int i = 0; i < homeBaseFiles.length; i++) {
+            File homeDir = new File(homeBaseDir, homeBaseFiles[i]);
+            if (!homeDir.isDirectory() || !homeDir.canRead())
+                continue;
+            homes.put(homeBaseFiles[i], homeDir.toString());
+        }
+
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/HostConfig.java b/container/catalina/src/share/org/apache/catalina/startup/HostConfig.java
new file mode 100644
index 0000000..fbf2c94
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/HostConfig.java
@@ -0,0 +1,1306 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.digester.Digester;
+
+
+/**
+ * Startup event listener for a <b>Host</b> that configures the properties
+ * of that Host, and the associated defined contexts.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+public class HostConfig
+    implements LifecycleListener {
+    
+    protected static org.apache.commons.logging.Log log=
+         org.apache.commons.logging.LogFactory.getLog( HostConfig.class );
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * App base.
+     */
+    protected File appBase = null;
+
+
+    /**
+     * Config base.
+     */
+    protected File configBase = null;
+
+
+    /**
+     * The Java class name of the Context configuration class we should use.
+     */
+    protected String configClass = "org.apache.catalina.startup.ContextConfig";
+
+
+    /**
+     * The Java class name of the Context implementation we should use.
+     */
+    protected String contextClass = "org.apache.catalina.core.StandardContext";
+
+
+    /**
+     * The Host we are associated with.
+     */
+    protected Host host = null;
+
+    
+    /**
+     * The JMX ObjectName of this component.
+     */
+    protected ObjectName oname = null;
+    
+
+    /**
+     * The string resources for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Should we deploy XML Context config files?
+     */
+    protected boolean deployXML = false;
+
+
+    /**
+     * Should we unpack WAR files when auto-deploying applications in the
+     * <code>appBase</code> directory?
+     */
+    protected boolean unpackWARs = false;
+
+
+    /**
+     * Map of deployed applications.
+     */
+    protected HashMap deployed = new HashMap();
+
+    
+    /**
+     * List of applications which are being serviced, and shouldn't be 
+     * deployed/undeployed/redeployed at the moment.
+     */
+    protected ArrayList serviced = new ArrayList();
+    
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+    protected boolean xmlValidation = false;
+
+
+    /**
+     * Attribute value used to turn on/off XML namespace awarenes.
+     */
+    protected boolean xmlNamespaceAware = false;
+
+
+    /**
+     * The <code>Digester</code> instance used to parse context descriptors.
+     */
+    protected static Digester digester = createDigester();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Context configuration class name.
+     */
+    public String getConfigClass() {
+
+        return (this.configClass);
+
+    }
+
+
+    /**
+     * Set the Context configuration class name.
+     *
+     * @param configClass The new Context configuration class name.
+     */
+    public void setConfigClass(String configClass) {
+
+        this.configClass = configClass;
+
+    }
+
+
+    /**
+     * Return the Context implementation class name.
+     */
+    public String getContextClass() {
+
+        return (this.contextClass);
+
+    }
+
+
+    /**
+     * Set the Context implementation class name.
+     *
+     * @param contextClass The new Context implementation class name.
+     */
+    public void setContextClass(String contextClass) {
+
+        this.contextClass = contextClass;
+
+    }
+
+
+    /**
+     * Return the deploy XML config file flag for this component.
+     */
+    public boolean isDeployXML() {
+
+        return (this.deployXML);
+
+    }
+
+
+    /**
+     * Set the deploy XML config file flag for this component.
+     *
+     * @param deployXML The new deploy XML flag
+     */
+    public void setDeployXML(boolean deployXML) {
+
+        this.deployXML= deployXML;
+
+    }
+
+
+    /**
+     * Return the unpack WARs flag.
+     */
+    public boolean isUnpackWARs() {
+
+        return (this.unpackWARs);
+
+    }
+
+
+    /**
+     * Set the unpack WARs flag.
+     *
+     * @param unpackWARs The new unpack WARs flag
+     */
+    public void setUnpackWARs(boolean unpackWARs) {
+
+        this.unpackWARs = unpackWARs;
+
+    }
+    
+    
+     /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlValidation true to enable xml instance validation
+     */
+    public void setXmlValidation(boolean xmlValidation){
+        this.xmlValidation = xmlValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getXmlValidation(){
+        return xmlValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     *
+     */
+    public boolean getXmlNamespaceAware(){
+        return xmlNamespaceAware;
+    }
+
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean xmlNamespaceAware){
+        this.xmlNamespaceAware=xmlNamespaceAware;
+    }    
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the START event for an associated Host.
+     *
+     * @param event The lifecycle event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        if (event.getType().equals(Lifecycle.PERIODIC_EVENT))
+            check();
+
+        // Identify the host we are associated with
+        try {
+            host = (Host) event.getLifecycle();
+            if (host instanceof StandardHost) {
+                setDeployXML(((StandardHost) host).isDeployXML());
+                setUnpackWARs(((StandardHost) host).isUnpackWARs());
+                setXmlNamespaceAware(((StandardHost) host).getXmlNamespaceAware());
+                setXmlValidation(((StandardHost) host).getXmlValidation());
+            }
+        } catch (ClassCastException e) {
+            log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
+            return;
+        }
+
+        // Process the event that has occurred
+        if (event.getType().equals(Lifecycle.START_EVENT))
+            start();
+        else if (event.getType().equals(Lifecycle.STOP_EVENT))
+            stop();
+
+    }
+
+    
+    /**
+     * Add a serviced application to the list.
+     */
+    public synchronized void addServiced(String name) {
+        serviced.add(name);
+    }
+    
+    
+    /**
+     * Is application serviced ?
+     * @return state of the application
+     */
+    public synchronized boolean isServiced(String name) {
+        return (serviced.contains(name));
+    }
+    
+
+    /**
+     * Removed a serviced application from the list.
+     */
+    public synchronized void removeServiced(String name) {
+        serviced.remove(name);
+    }
+
+    
+    /**
+     * Get the instant where an application was deployed.
+     * @return 0L if no application with that name is deployed, or the instant
+     * on which the application was deployed
+     */
+    public long getDeploymentTime(String name) {
+    	DeployedApplication app = (DeployedApplication) deployed.get(name);
+    	if (app == null) {
+    		return 0L;
+    	} else {
+    		return app.timestamp;
+    	}
+    }
+    
+    
+    // ------------------------------------------------------ Protected Methods
+
+    
+    /**
+     * Create the digester which will be used to parse context config files.
+     */
+    protected static Digester createDigester() {
+        Digester digester = new Digester();
+        digester.setValidating(false);
+        // Add object creation rule
+        digester.addObjectCreate("Context", "org.apache.catalina.core.StandardContext",
+            "className");
+        // Set the properties on that object (it doesn't matter if extra 
+        // properties are set)
+        digester.addSetProperties("Context");
+        return (digester);
+    }
+    
+
+    /**
+     * Return a File object representing the "application root" directory
+     * for our associated Host.
+     */
+    protected File appBase() {
+
+        if (appBase != null) {
+            return appBase;
+        }
+
+        File file = new File(host.getAppBase());
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"),
+                            host.getAppBase());
+        try {
+            appBase = file.getCanonicalFile();
+        } catch (IOException e) {
+            appBase = file;
+        }
+        return (appBase);
+
+    }
+
+
+    /**
+     * Return a File object representing the "configuration root" directory
+     * for our associated Host.
+     */
+    protected File configBase() {
+
+        if (configBase != null) {
+            return configBase;
+        }
+
+        File file = new File(System.getProperty("catalina.base"), "conf");
+        Container parent = host.getParent();
+        if ((parent != null) && (parent instanceof Engine)) {
+            file = new File(file, parent.getName());
+        }
+        file = new File(file, host.getName());
+        try {
+            configBase = file.getCanonicalFile();
+        } catch (IOException e) {
+            configBase = file;
+        }
+        return (configBase);
+
+    }
+
+    /**
+     * Get the name of the configBase.
+     * For use with JMX management.
+     */
+    public String getConfigBaseName() {
+        return configBase().getAbsolutePath();
+    }
+
+    /**
+     * Given a context path, get the config file name.
+     */
+    protected String getConfigFile(String path) {
+        String basename = null;
+        if (path.equals("")) {
+            basename = "ROOT";
+        } else {
+            basename = path.substring(1).replace('/', '#');
+        }
+        return (basename);
+    }
+
+    
+    /**
+     * Given a context path, get the config file name.
+     */
+    protected String getDocBase(String path) {
+        String basename = null;
+        if (path.equals("")) {
+            basename = "ROOT";
+        } else {
+            basename = path.substring(1);
+        }
+        return (basename);
+    }
+
+    
+    /**
+     * Deploy applications for any directories or WAR files that are found
+     * in our "application root" directory.
+     */
+    protected void deployApps() {
+
+        File appBase = appBase();
+        File configBase = configBase();
+        // Deploy XML descriptors from configBase
+        deployDescriptors(configBase, configBase.list());
+        // Deploy WARs, and loop if additional descriptors are found
+        deployWARs(appBase, appBase.list());
+        // Deploy expanded folders
+        deployDirectories(appBase, appBase.list());
+        
+    }
+
+
+    /**
+     * Deploy applications for any directories or WAR files that are found
+     * in our "application root" directory.
+     */
+    protected void deployApps(String name) {
+
+        File appBase = appBase();
+        File configBase = configBase();
+        String baseName = getConfigFile(name);
+        String docBase = getConfigFile(name);
+        
+        // Deploy XML descriptors from configBase
+        File xml = new File(configBase, baseName + ".xml");
+        if (xml.exists())
+            deployDescriptor(name, xml, baseName + ".xml");
+        // Deploy WARs, and loop if additional descriptors are found
+        File war = new File(appBase, docBase + ".war");
+        if (war.exists())
+            deployWAR(name, war, docBase + ".war");
+        // Deploy expanded folders
+        File dir = new File(appBase, docBase);
+        if (dir.exists())
+            deployDirectory(name, dir, docBase);
+        
+    }
+
+
+    /**
+     * Deploy XML context descriptors.
+     */
+    protected void deployDescriptors(File configBase, String[] files) {
+
+        if (files == null)
+            return;
+        
+        for (int i = 0; i < files.length; i++) {
+
+            if (files[i].equalsIgnoreCase("META-INF"))
+                continue;
+            if (files[i].equalsIgnoreCase("WEB-INF"))
+                continue;
+            File contextXml = new File(configBase, files[i]);
+            if (files[i].toLowerCase().endsWith(".xml")) {
+
+                // Calculate the context path and make sure it is unique
+                String nameTmp = files[i].substring(0, files[i].length() - 4);
+                String contextPath = "/" + nameTmp.replace('#', '/');
+                if (nameTmp.equals("ROOT")) {
+                    contextPath = "";
+                }
+
+                if (isServiced(contextPath))
+                    continue;
+                
+                String file = files[i];
+
+                deployDescriptor(contextPath, contextXml, file);
+                
+            }
+
+        }
+
+    }
+
+
+    /**
+     * @param contextPath
+     * @param contextXml
+     * @param file
+     */
+    protected void deployDescriptor(String contextPath, File contextXml, String file) {
+        if (deploymentExists(contextPath)) {
+            return;
+        }
+        
+        DeployedApplication deployedApp = new DeployedApplication(contextPath);
+
+        // Assume this is a configuration descriptor and deploy it
+        if(log.isDebugEnabled()) {
+            log.debug(sm.getString("hostConfig.deployDescriptor", file));
+        }
+
+        Context context = null;
+        try {
+            synchronized (digester) {
+                try {
+                    context = (Context) digester.parse(contextXml);
+                } finally {
+                    digester.reset();
+                }
+            }
+            if (context instanceof Lifecycle) {
+                Class clazz = Class.forName(host.getConfigClass());
+                LifecycleListener listener =
+                    (LifecycleListener) clazz.newInstance();
+                ((Lifecycle) context).addLifecycleListener(listener);
+            }
+            context.setConfigFile(contextXml.getAbsolutePath());
+            context.setPath(contextPath);
+            // Add the associated docBase to the redeployed list if it's a WAR
+            boolean isWar = false;
+            boolean isExternal = false;
+            if (context.getDocBase() != null) {
+                File docBase = new File(context.getDocBase());
+                if (!docBase.isAbsolute()) {
+                    docBase = new File(appBase(), context.getDocBase());
+                }
+                // If external docBase, register .xml as redeploy first
+                if (!docBase.getCanonicalPath().startsWith(appBase().getAbsolutePath())) {
+                    isExternal = true;
+                    deployedApp.redeployResources.put
+                        (contextXml.getAbsolutePath(), new Long(contextXml.lastModified()));
+                    deployedApp.redeployResources.put(docBase.getAbsolutePath(),
+                        new Long(docBase.lastModified()));
+                    if (docBase.getAbsolutePath().toLowerCase().endsWith(".war")) {
+                        isWar = true;
+                    }
+                } else {
+                    log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified",
+                             docBase));
+                    // Ignore specified docBase
+                    context.setDocBase(null);
+                }
+            }
+            host.addChild(context);
+            // Get paths for WAR and expanded WAR in appBase
+            String name = null;
+            String path = context.getPath();
+            if (path.equals("")) {
+                name = "ROOT";
+            } else {
+                if (path.startsWith("/")) {
+                    name = path.substring(1);
+                } else {
+                    name = path;
+                }
+            }
+            File expandedDocBase = new File(name);
+            File warDocBase = new File(name + ".war");
+            if (!expandedDocBase.isAbsolute()) {
+                expandedDocBase = new File(appBase(), name);
+                warDocBase = new File(appBase(), name + ".war");
+            }
+            // Add the eventual unpacked WAR and all the resources which will be
+            // watched inside it
+            if (isWar && unpackWARs) {
+                deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
+                        new Long(expandedDocBase.lastModified()));
+                deployedApp.redeployResources.put
+                    (contextXml.getAbsolutePath(), new Long(contextXml.lastModified()));
+                addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context);
+            } else {
+                // Find an existing matching war and expanded folder
+                if (warDocBase.exists()) {
+                    deployedApp.redeployResources.put(warDocBase.getAbsolutePath(),
+                            new Long(warDocBase.lastModified()));
+                }
+                if (expandedDocBase.exists()) {
+                    deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
+                            new Long(expandedDocBase.lastModified()));
+                    addWatchedResources(deployedApp, 
+                            expandedDocBase.getAbsolutePath(), context);
+                } else {
+                    addWatchedResources(deployedApp, null, context);
+                }
+                // Add the context XML to the list of files which should trigger a redeployment
+                if (!isExternal) {
+                    deployedApp.redeployResources.put
+                        (contextXml.getAbsolutePath(), new Long(contextXml.lastModified()));
+                }
+            }
+        } catch (Throwable t) {
+            log.error(sm.getString("hostConfig.deployDescriptor.error",
+                                   file), t);
+        }
+
+        if (context != null && host.findChild(context.getName()) != null) {
+            deployed.put(contextPath, deployedApp);
+        }
+    }
+
+
+    /**
+     * Deploy WAR files.
+     */
+    protected void deployWARs(File appBase, String[] files) {
+        
+        if (files == null)
+            return;
+        
+        boolean checkAdditionalDeployments = false;
+        
+        for (int i = 0; i < files.length; i++) {
+            
+            if (files[i].equalsIgnoreCase("META-INF"))
+                continue;
+            if (files[i].equalsIgnoreCase("WEB-INF"))
+                continue;
+            File dir = new File(appBase, files[i]);
+            if (files[i].toLowerCase().endsWith(".war")) {
+                
+                // Calculate the context path and make sure it is unique
+                String contextPath = "/" + files[i];
+                int period = contextPath.lastIndexOf(".");
+                if (period >= 0)
+                    contextPath = contextPath.substring(0, period);
+                if (contextPath.equals("/ROOT"))
+                    contextPath = "";
+                
+                if (isServiced(contextPath))
+                    continue;
+                
+                String file = files[i];
+                
+                deployWAR(contextPath, dir, file);
+                
+            }
+            
+        }
+        
+    }
+
+
+    /**
+     * @param contextPath
+     * @param dir
+     * @param file
+     */
+    protected void deployWAR(String contextPath, File dir, String file) {
+        
+        if (deploymentExists(contextPath))
+            return;
+        
+        // Checking for a nested /META-INF/context.xml
+        JarFile jar = null;
+        JarEntry entry = null;
+        InputStream istream = null;
+        BufferedOutputStream ostream = null;
+        File xml = new File
+            (configBase, file.substring(0, file.lastIndexOf(".")) + ".xml");
+        if (deployXML && !xml.exists()) {
+            try {
+                jar = new JarFile(dir);
+                entry = jar.getJarEntry(Constants.ApplicationContextXml);
+                if (entry != null) {
+                    istream = jar.getInputStream(entry);
+                    
+                    configBase.mkdirs();
+                    
+                    ostream =
+                        new BufferedOutputStream
+                        (new FileOutputStream(xml), 1024);
+                    byte buffer[] = new byte[1024];
+                    while (true) {
+                        int n = istream.read(buffer);
+                        if (n < 0) {
+                            break;
+                        }
+                        ostream.write(buffer, 0, n);
+                    }
+                    ostream.flush();
+                    ostream.close();
+                    ostream = null;
+                    istream.close();
+                    istream = null;
+                    entry = null;
+                    jar.close();
+                    jar = null;
+                }
+            } catch (Exception e) {
+                // Ignore and continue
+                if (ostream != null) {
+                    try {
+                        ostream.close();
+                    } catch (Throwable t) {
+                        ;
+                    }
+                    ostream = null;
+                }
+                if (istream != null) {
+                    try {
+                        istream.close();
+                    } catch (Throwable t) {
+                        ;
+                    }
+                    istream = null;
+                }
+            } finally {
+                entry = null;
+                if (jar != null) {
+                    try {
+                        jar.close();
+                    } catch (Throwable t) {
+                        ;
+                    }
+                    jar = null;
+                }
+            }
+        }
+        
+        DeployedApplication deployedApp = new DeployedApplication(contextPath);
+        
+        // Deploy the application in this WAR file
+        if(log.isInfoEnabled()) 
+            log.info(sm.getString("hostConfig.deployJar", file));
+
+        // Populate redeploy resources with the WAR file
+        deployedApp.redeployResources.put
+            (dir.getAbsolutePath(), new Long(dir.lastModified()));
+
+        try {
+            Context context = (Context) Class.forName(contextClass).newInstance();
+            if (context instanceof Lifecycle) {
+                Class clazz = Class.forName(host.getConfigClass());
+                LifecycleListener listener =
+                    (LifecycleListener) clazz.newInstance();
+                ((Lifecycle) context).addLifecycleListener(listener);
+            }
+            context.setPath(contextPath);
+            context.setDocBase(file);
+            if (xml.exists()) {
+                context.setConfigFile(xml.getAbsolutePath());
+                deployedApp.redeployResources.put
+                    (xml.getAbsolutePath(), new Long(xml.lastModified()));
+            }
+            host.addChild(context);
+            // If we're unpacking WARs, the docBase will be mutated after
+            // starting the context
+            if (unpackWARs && (context.getDocBase() != null)) {
+                String name = null;
+                String path = context.getPath();
+                if (path.equals("")) {
+                    name = "ROOT";
+                } else {
+                    if (path.startsWith("/")) {
+                        name = path.substring(1);
+                    } else {
+                        name = path;
+                    }
+                }
+                File docBase = new File(name);
+                if (!docBase.isAbsolute()) {
+                    docBase = new File(appBase(), name);
+                }
+                deployedApp.redeployResources.put(docBase.getAbsolutePath(),
+                        new Long(docBase.lastModified()));
+                addWatchedResources(deployedApp, docBase.getAbsolutePath(), context);
+            } else {
+                addWatchedResources(deployedApp, null, context);
+            }
+        } catch (Throwable t) {
+            log.error(sm.getString("hostConfig.deployJar.error", file), t);
+        }
+        
+        deployed.put(contextPath, deployedApp);
+    }
+
+
+    /**
+     * Deploy directories.
+     */
+    protected void deployDirectories(File appBase, String[] files) {
+
+        if (files == null)
+            return;
+        
+        for (int i = 0; i < files.length; i++) {
+
+            if (files[i].equalsIgnoreCase("META-INF"))
+                continue;
+            if (files[i].equalsIgnoreCase("WEB-INF"))
+                continue;
+            File dir = new File(appBase, files[i]);
+            if (dir.isDirectory()) {
+
+                // Make sure there is an application configuration directory
+                // This is needed if the Context appBase is the same as the
+                // web server document root to make sure only web applications
+                // are deployed and not directories for web space.
+                File webInf = new File(dir, "/WEB-INF");
+                if (!webInf.exists() || !webInf.isDirectory() ||
+                    !webInf.canRead())
+                    continue;
+
+                // Calculate the context path and make sure it is unique
+                String contextPath = "/" + files[i];
+                if (files[i].equals("ROOT"))
+                    contextPath = "";
+
+                if (isServiced(contextPath))
+                    continue;
+
+                String file = files[i];
+                
+                deployDirectory(contextPath, dir, file);
+            
+            }
+
+        }
+
+    }
+
+    
+    /**
+     * @param contextPath
+     * @param dir
+     * @param file
+     */
+    protected void deployDirectory(String contextPath, File dir, String file) {
+        DeployedApplication deployedApp = new DeployedApplication(contextPath);
+        
+        if (deploymentExists(contextPath))
+            return;
+
+        // Deploy the application in this directory
+        if( log.isDebugEnabled() ) 
+            log.debug(sm.getString("hostConfig.deployDir", file));
+        try {
+            Context context = (Context) Class.forName(contextClass).newInstance();
+            if (context instanceof Lifecycle) {
+                Class clazz = Class.forName(host.getConfigClass());
+                LifecycleListener listener =
+                    (LifecycleListener) clazz.newInstance();
+                ((Lifecycle) context).addLifecycleListener(listener);
+            }
+            context.setPath(contextPath);
+            context.setDocBase(file);
+            File configFile = new File(dir, Constants.ApplicationContextXml);
+            if (deployXML) {
+                context.setConfigFile(configFile.getAbsolutePath());
+            }
+            host.addChild(context);
+            deployedApp.redeployResources.put(dir.getAbsolutePath(),
+                    new Long(dir.lastModified()));
+            if (deployXML) {
+                deployedApp.redeployResources.put(configFile.getAbsolutePath(),
+                        new Long(configFile.lastModified()));
+            }
+            addWatchedResources(deployedApp, dir.getAbsolutePath(), context);
+        } catch (Throwable t) {
+            log.error(sm.getString("hostConfig.deployDir.error", file), t);
+        }
+
+        deployed.put(contextPath, deployedApp);
+    }
+
+    
+    /**
+     * Check if a webapp is already deployed in this host.
+     * 
+     * @param contextPath of the context which will be checked
+     */
+    protected boolean deploymentExists(String contextPath) {
+        return (deployed.containsKey(contextPath) || (host.findChild(contextPath) != null));
+    }
+    
+
+    /**
+     * Add watched resources to the specified Context.
+     * @param app HostConfig deployed app
+     * @param docBase web app docBase
+     * @param context web application context
+     */
+    protected void addWatchedResources(DeployedApplication app, String docBase, Context context) {
+        // FIXME: Feature idea. Add support for patterns (ex: WEB-INF/*, WEB-INF/*.xml), where
+        //        we would only check if at least one resource is newer than app.timestamp
+        File docBaseFile = null;
+        if (docBase != null) {
+            docBaseFile = new File(docBase);
+            if (!docBaseFile.isAbsolute()) {
+                docBaseFile = new File(appBase(), docBase);
+            }
+        }
+        String[] watchedResources = context.findWatchedResources();
+        for (int i = 0; i < watchedResources.length; i++) {
+            File resource = new File(watchedResources[i]);
+            if (!resource.isAbsolute()) {
+                if (docBase != null) {
+                    resource = new File(docBaseFile, watchedResources[i]);
+                } else {
+                    continue;
+                }
+            }
+            app.reloadResources.put(resource.getAbsolutePath(), 
+                    new Long(resource.lastModified()));
+        }
+    }
+    
+
+    /**
+     * Check resources for redeployment and reloading.
+     */
+    protected synchronized void checkResources(DeployedApplication app) {
+        String[] resources = (String[]) app.redeployResources.keySet().toArray(new String[0]);
+        for (int i = 0; i < resources.length; i++) {
+            File resource = new File(resources[i]);
+            if (log.isDebugEnabled())
+                log.debug("Checking context[" + app.name + "] redeploy resource " + resource);
+            if (resource.exists()) {
+                long lastModified = ((Long) app.redeployResources.get(resources[i])).longValue();
+                if ((!resource.isDirectory()) && resource.lastModified() > lastModified) {
+                    // Undeploy application
+                    if (log.isInfoEnabled())
+                        log.info(sm.getString("hostConfig.undeploy", app.name));
+                    ContainerBase context = (ContainerBase) host.findChild(app.name);
+                    host.removeChild(context);
+                    try {
+                        context.destroy();
+                    } catch (Exception e) {
+                        log.warn(sm.getString
+                                 ("hostConfig.context.destroy", app.name), e);
+                    }
+                    // Delete other redeploy resources
+                    for (int j = i + 1; j < resources.length; j++) {
+                        try {
+                            File current = new File(resources[j]);
+                            current = current.getCanonicalFile();
+                            if ((current.getAbsolutePath().startsWith(appBase().getAbsolutePath()))
+                                    || (current.getAbsolutePath().startsWith(configBase().getAbsolutePath()))) {
+                                if (log.isDebugEnabled())
+                                    log.debug("Delete " + current);
+                                ExpandWar.delete(current);
+                            }
+                        } catch (IOException e) {
+                            log.warn(sm.getString
+                                    ("hostConfig.canonicalizing", app.name), e);
+                        }
+                    }
+                    deployed.remove(app.name);
+                    return;
+                }
+            } else {
+                long lastModified = ((Long) app.redeployResources.get(resources[i])).longValue();
+                if (lastModified == 0L) {
+                    continue;
+                }
+                // Undeploy application
+                if (log.isInfoEnabled())
+                    log.info(sm.getString("hostConfig.undeploy", app.name));
+                ContainerBase context = (ContainerBase) host.findChild(app.name);
+                host.removeChild(context);
+                try {
+                    context.destroy();
+                } catch (Exception e) {
+                    log.warn(sm.getString
+                             ("hostConfig.context.destroy", app.name), e);
+                }
+                // Delete all redeploy resources
+                for (int j = i + 1; j < resources.length; j++) {
+                    try {
+                        File current = new File(resources[j]);
+                        current = current.getCanonicalFile();
+                        if ((current.getAbsolutePath().startsWith(appBase().getAbsolutePath()))
+                            || (current.getAbsolutePath().startsWith(configBase().getAbsolutePath()))) {
+                            if (log.isDebugEnabled())
+                                log.debug("Delete " + current);
+                            ExpandWar.delete(current);
+                        }
+                    } catch (IOException e) {
+                        log.warn(sm.getString
+                                ("hostConfig.canonicalizing", app.name), e);
+                    }
+                }
+                // Delete reload resources as well (to remove any remaining .xml descriptor)
+                String[] resources2 = (String[]) app.reloadResources.keySet().toArray(new String[0]);
+                for (int j = 0; j < resources2.length; j++) {
+                    try {
+                        File current = new File(resources2[j]);
+                        current = current.getCanonicalFile();
+                        if ((current.getAbsolutePath().startsWith(appBase().getAbsolutePath()))
+                            || ((current.getAbsolutePath().startsWith(configBase().getAbsolutePath())
+                                 && (current.getAbsolutePath().endsWith(".xml"))))) {
+                            if (log.isDebugEnabled())
+                                log.debug("Delete " + current);
+                            ExpandWar.delete(current);
+                        }
+                    } catch (IOException e) {
+                        log.warn(sm.getString
+                                ("hostConfig.canonicalizing", app.name), e);
+                    }
+                }
+                deployed.remove(app.name);
+                return;
+            }
+        }
+        resources = (String[]) app.reloadResources.keySet().toArray(new String[0]);
+        for (int i = 0; i < resources.length; i++) {
+            File resource = new File(resources[i]);
+            if (log.isDebugEnabled())
+                log.debug("Checking context[" + app.name + "] reload resource " + resource);
+            long lastModified = ((Long) app.reloadResources.get(resources[i])).longValue();
+            if ((!resource.exists() && lastModified != 0L) 
+                || (resource.lastModified() != lastModified)) {
+                // Reload application
+                if(log.isInfoEnabled())
+                    log.info(sm.getString("hostConfig.reload", app.name));
+                Container context = host.findChild(app.name);
+                try {
+                    ((Lifecycle) context).stop();
+                } catch (Exception e) {
+                    log.warn(sm.getString
+                             ("hostConfig.context.restart", app.name), e);
+                }
+                // If the context was not started (for example an error 
+                // in web.xml) we'll still get to try to start
+                try {
+                    ((Lifecycle) context).start();
+                } catch (Exception e) {
+                    log.warn(sm.getString
+                             ("hostConfig.context.restart", app.name), e);
+                }
+                // Update times
+                app.reloadResources.put(resources[i], new Long(resource.lastModified()));
+                app.timestamp = System.currentTimeMillis();
+                return;
+            }
+        }
+    }
+    
+    
+    /**
+     * Process a "start" event for this Host.
+     */
+    public void start() {
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("hostConfig.start"));
+
+        try {
+            ObjectName hostON = new ObjectName(host.getObjectName());
+            oname = new ObjectName
+                (hostON.getDomain() + ":type=Deployer,host=" + host.getName());
+            Registry.getRegistry(null, null).registerComponent
+                (this, oname, this.getClass().getName());
+        } catch (Exception e) {
+            log.error(sm.getString("hostConfig.jmx.register", oname), e);
+        }
+
+        if (host.getDeployOnStartup())
+            deployApps();
+        
+    }
+
+
+    /**
+     * Process a "stop" event for this Host.
+     */
+    public void stop() {
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("hostConfig.stop"));
+
+        undeployApps();
+
+        if (oname != null) {
+            try {
+                Registry.getRegistry(null, null).unregisterComponent(oname);
+            } catch (Exception e) {
+                log.error(sm.getString("hostConfig.jmx.unregister", oname), e);
+            }
+        }
+        oname = null;
+        appBase = null;
+        configBase = null;
+
+    }
+
+
+    /**
+     * Undeploy all deployed applications.
+     */
+    protected void undeployApps() {
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("hostConfig.undeploying"));
+
+        // Soft undeploy all contexts we have deployed
+        DeployedApplication[] apps = 
+            (DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
+        for (int i = 0; i < apps.length; i++) {
+            host.removeChild(host.findChild(apps[i].name));
+        }
+        
+        deployed.clear();
+
+    }
+
+
+    /**
+     * Check status of all webapps.
+     */
+    protected void check() {
+
+        if (host.getAutoDeploy()) {
+            // Check for resources modification to trigger redeployment
+            DeployedApplication[] apps = 
+                (DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
+            for (int i = 0; i < apps.length; i++) {
+                if (!isServiced(apps[i].name))
+                    checkResources(apps[i]);
+            }
+            // Hotdeploy applications
+            deployApps();
+        }
+
+    }
+
+    
+    /**
+     * Check status of a specific webapp, for use with stuff like management webapps.
+     */
+    public void check(String name) {
+        DeployedApplication app = (DeployedApplication) deployed.get(name);
+        if (app != null) {
+            checkResources(app);
+        } else {
+            deployApps(name);
+        }
+    }
+
+    /**
+     * Add a new Context to be managed by us.
+     * Entry point for the admin webapp, and other JMX Context controlers.
+     */
+    public void manageApp(Context context)  {    
+
+        String contextPath = context.getPath();
+        
+        if (deployed.containsKey(contextPath))
+            return;
+
+        DeployedApplication deployedApp = new DeployedApplication(contextPath);
+        
+        // Add the associated docBase to the redeployed list if it's a WAR
+        boolean isWar = false;
+        if (context.getDocBase() != null) {
+            File docBase = new File(context.getDocBase());
+            if (!docBase.isAbsolute()) {
+                docBase = new File(appBase(), context.getDocBase());
+            }
+            deployedApp.redeployResources.put(docBase.getAbsolutePath(),
+                                          new Long(docBase.lastModified()));
+            if (docBase.getAbsolutePath().toLowerCase().endsWith(".war")) {
+                isWar = true;
+            }
+        }
+        host.addChild(context);
+        // Add the eventual unpacked WAR and all the resources which will be
+        // watched inside it
+        if (isWar && unpackWARs) {
+            String name = null;
+            String path = context.getPath();
+            if (path.equals("")) {
+                name = "ROOT";
+            } else {
+                if (path.startsWith("/")) {
+                    name = path.substring(1);
+                } else {
+                    name = path;
+                }
+            }
+            File docBase = new File(name);
+            if (!docBase.isAbsolute()) {
+                docBase = new File(appBase(), name);
+            }
+            deployedApp.redeployResources.put(docBase.getAbsolutePath(),
+                        new Long(docBase.lastModified()));
+            addWatchedResources(deployedApp, docBase.getAbsolutePath(), context);
+        } else {
+            addWatchedResources(deployedApp, null, context);
+        }
+        deployed.put(contextPath, deployedApp);
+    }
+
+    /**
+     * Remove a webapp from our control.
+     * Entry point for the admin webapp, and other JMX Context controlers.
+     */
+    public void unmanageApp(String contextPath) {
+        if(isServiced(contextPath)) {
+            deployed.remove(contextPath);
+            host.removeChild(host.findChild(contextPath));
+        }
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * This class represents the state of a deployed application, as well as 
+     * the monitored resources.
+     */
+    protected class DeployedApplication {
+    	public DeployedApplication(String name) {
+    		this.name = name;
+    	}
+    	
+    	/**
+    	 * Application context path. The assertion is that 
+    	 * (host.getChild(name) != null).
+    	 */
+    	public String name;
+    	
+    	/**
+    	 * Any modification of the specified (static) resources will cause a 
+    	 * redeployment of the application. If any of the specified resources is
+    	 * removed, the application will be undeployed. Typically, this will
+    	 * contain resources like the context.xml file, a compressed WAR path.
+         * The value is the last modification time.
+    	 */
+    	public LinkedHashMap redeployResources = new LinkedHashMap();
+
+    	/**
+    	 * Any modification of the specified (static) resources will cause a 
+    	 * reload of the application. This will typically contain resources
+    	 * such as the web.xml of a webapp, but can be configured to contain
+    	 * additional descriptors.
+         * The value is the last modification time.
+    	 */
+    	public HashMap reloadResources = new HashMap();
+
+    	/**
+    	 * Instant where the application was last put in service.
+    	 */
+    	public long timestamp = System.currentTimeMillis();
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/HostRuleSet.java b/container/catalina/src/share/org/apache/catalina/startup/HostRuleSet.java
new file mode 100644
index 0000000..b3364ef
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/HostRuleSet.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSetBase;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a
+ * Host definition element.  This <code>RuleSet</code> does NOT include
+ * any rules for nested Context or DefaultContext elements, which should
+ * be added via instances of <code>ContextRuleSet</code>.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class HostRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public HostRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public HostRuleSet(String prefix) {
+
+        super();
+        this.namespaceURI = null;
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+
+        digester.addObjectCreate(prefix + "Host",
+                                 "org.apache.catalina.core.StandardHost",
+                                 "className");
+        digester.addSetProperties(prefix + "Host");
+        digester.addRule(prefix + "Host",
+                         new CopyParentClassLoaderRule());
+        digester.addRule(prefix + "Host",
+                         new LifecycleListenerRule
+                         ("org.apache.catalina.startup.HostConfig",
+                          "hostConfigClass"));
+        digester.addSetNext(prefix + "Host",
+                            "addChild",
+                            "org.apache.catalina.Container");
+
+        digester.addCallMethod(prefix + "Host/Alias",
+                               "addAlias", 0);
+
+        //Cluster configuration start
+        digester.addObjectCreate(prefix + "Host/Cluster",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Host/Cluster");
+        digester.addSetNext(prefix + "Host/Cluster",
+                            "setCluster",
+                            "org.apache.catalina.Cluster");
+        //Cluster configuration end
+
+        digester.addObjectCreate(prefix + "Host/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Host/Listener");
+        digester.addSetNext(prefix + "Host/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        digester.addObjectCreate(prefix + "Host/Realm",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Host/Realm");
+        digester.addSetNext(prefix + "Host/Realm",
+                            "setRealm",
+                            "org.apache.catalina.Realm");
+
+        digester.addObjectCreate(prefix + "Host/Valve",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Host/Valve");
+        digester.addSetNext(prefix + "Host/Valve",
+                            "addValve",
+                            "org.apache.catalina.Valve");
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/LifecycleListenerRule.java b/container/catalina/src/share/org/apache/catalina/startup/LifecycleListenerRule.java
new file mode 100644
index 0000000..e8b2938
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/LifecycleListenerRule.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Rule that creates a new <code>LifecycleListener</code> instance,
+ * and associates it with the top object on the stack (which must
+ * implement <code>LifecycleListener</code>).</p>
+ */
+
+public class LifecycleListenerRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this Rule.
+     *
+     * @param listenerClass Default name of the LifecycleListener
+     *  implementation class to be created
+     * @param attributeName Name of the attribute that optionally
+     *  includes an override name of the LifecycleListener class
+     */
+    public LifecycleListenerRule(String listenerClass, String attributeName) {
+
+        this.listenerClass = listenerClass;
+        this.attributeName = attributeName;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The attribute name of an attribute that can override the
+     * implementation class name.
+     */
+    private String attributeName;
+
+
+    /**
+     * The name of the <code>LifecycleListener</code> implementation class.
+     */
+    private String listenerClass;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Handle the beginning of an XML element.
+     *
+     * @param attributes The attributes of this element
+     *
+     * @exception Exception if a processing error occurs
+     */
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+
+        // Instantiate a new LifecyleListener implementation object
+        String className = listenerClass;
+        if (attributeName != null) {
+            String value = attributes.getValue(attributeName);
+            if (value != null)
+                className = value;
+        }
+        Class clazz = Class.forName(className);
+        LifecycleListener listener =
+            (LifecycleListener) clazz.newInstance();
+
+        // Add this LifecycleListener to our associated component
+        Lifecycle lifecycle = (Lifecycle) digester.peek();
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/startup/LocalStrings.properties
new file mode 100644
index 0000000..175d4c5
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/LocalStrings.properties
@@ -0,0 +1,76 @@
+contextConfig.applicationClose=Error closing application web.xml
+contextConfig.applicationConfig=Configuration error in application web.xml
+contextConfig.applicationListener=Exception creating listener of class {0}
+contextConfig.applicationMissing=Missing application web.xml, using defaults only
+contextConfig.applicationParse=Parse error in application web.xml
+contextConfig.applicationPosition=Occurred at line {0} column {1}
+contextConfig.authenticatorConfigured=Configured an authenticator for method {0}
+contextConfig.authenticatorInstantiate=Cannot instantiate an authenticator of class {0}
+contextConfig.authenticatorMissing=Cannot configure an authenticator for method {0}
+contextConfig.authenticatorResources=Cannot load authenticators mapping list
+contextConfig.cce=Lifecycle event data object {0} is not a Context
+contextConfig.certificatesConfig.added=Added certificates -> request attribute Valve
+contextConfig.certificatesConfig.error=Exception adding CertificatesValve:
+contextConfig.defaultClose=Error closing default web.xml
+contextConfig.defaultConfig=Configuration error in default web.xml
+contextConfig.defaultMissing=Missing default web.xml, using application web.xml only
+contextConfig.defaultParse=Parse error in default web.xml
+contextConfig.defaultPosition=Occurred at line {0} column {1}
+contextConfig.fixDocBase=Exception fixing docBase: {0} 
+contextConfig.init=ContextConfig: Initializing
+contextConfig.missingRealm=No Realm has been configured to authenticate against
+contextConfig.role.auth=WARNING: Security role name {0} used in an <auth-constraint> without being defined in a <security-role>
+contextConfig.role.link=WARNING: Security role name {0} used in a <role-link> without being defined in a <security-role>
+contextConfig.role.runas=WARNING: Security role name {0} used in a <run-as> without being defined in a <security-role>
+contextConfig.start=ContextConfig: Processing START
+contextConfig.stop=ContextConfig: Processing STOP
+contextConfig.tldEntryException=Exception processing TLD {0} in JAR at resource path {1} in context {2}
+contextConfig.tldFileException=Exception processing TLD at resource path {0} in context {1}
+contextConfig.tldJarException=Exception processing JAR at resource path {0} in context {1}
+contextConfig.tldResourcePath=Invalid TLD resource path {0}
+contextConfig.unavailable=Marking this application unavailable due to previous error(s)
+contextConfig.altDDNotFound=alt-dd file {0} not found
+embedded.alreadyStarted=Embedded service has already been started
+embedded.noEngines=No engines have been defined yet
+embedded.notmp=Cannot find specified temporary folder at {0}
+embedded.notStarted=Embedded service has not yet been started
+embedded.authenticatorNotInstanceOfValve=Specified Authenticator is not a Valve
+engineConfig.cce=Lifecycle event data object {0} is not an Engine
+engineConfig.start=EngineConfig: Processing START
+engineConfig.stop=EngineConfig: Processing STOP
+expandWar.copy=Error copying {0} to {1}
+hostConfig.appBase=Application base directory {0} does not exist
+hostConfig.canonicalizing=Error delete redeploy resources from context [{0}]
+hostConfig.cce=Lifecycle event data object {0} is not a Host
+hostConfig.context.destroy=Error during context [{0}] destroy
+hostConfig.context.restart=Error during context [{0}] restart
+hostConfig.deploy=Deploying web application directory {0}
+hostConfig.deployDescriptor=Deploying configuration descriptor {0}
+hostConfig.deployDescriptor.error=Error deploying configuration descriptor {0}
+hostConfig.deployDescriptor.localDocBaseSpecified=A docBase {0} inside the host appBase has been specified, and will be ignored
+hostConfig.deployDir=Deploying web application directory {0}
+hostConfig.deployDir.error=Error deploying web application directory {0}
+hostConfig.deployJar=Deploying web application archive {0}
+hostConfig.deployJar.error=Error deploying web application archive {0}
+hostConfig.deploy.error=Exception while deploying web application directory {0}
+hostConfig.deploying=Deploying discovered web applications
+hostConfig.expand=Expanding web application archive {0}
+hostConfig.expand.error=Exception while expanding web application archive {0}
+hostConfig.expanding=Expanding discovered web application archives
+hostConfig.jmx.register=Register context [{0}] failed
+hostConfig.jmx.unregister=Unregister context [{0}] failed
+hostConfig.reload=Reloading context [{0}]
+hostConfig.removeXML=Context [{0}] is undeployed
+hostConfig.removeDIR=Directory {0} is undeployed
+hostConfig.removeWAR=War {0} is undeployed
+hostConfig.start=HostConfig: Processing START
+hostConfig.stop=HostConfig: Processing STOP
+hostConfig.undeploy=Undeploying context [{0}]
+hostConfig.undeploy.error=Error undeploying web application at context path {0}
+hostConfig.undeploying=Undeploying deployed web applications
+userConfig.database=Exception loading user database
+userConfig.deploy=Deploying web application for user {0}
+userConfig.deploying=Deploying user web applications
+userConfig.error=Error deploying web application for user {0}
+userConfig.start=UserConfig: Processing START
+userConfig.stop=UserConfig: Processing STOP
diff --git a/container/catalina/src/share/org/apache/catalina/startup/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/startup/LocalStrings_es.properties
new file mode 100644
index 0000000..0ecf8b1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/LocalStrings_es.properties
@@ -0,0 +1,61 @@
+contextConfig.applicationClose=Error durante el cierre del archivo web.xml de la aplicación
+contextConfig.applicationConfig=Errror de configuración en el archivo web.xml de la aplicación
+contextConfig.applicationListener=Excepción durante la creación de la clase de escucha (listener) {0}
+contextConfig.applicationMissing=Falta el archivo web.xml de la aplicaciónb. Utilizando los parámetros por defecto
+contextConfig.applicationParse=Error de evaluación (parse) en el archivo web.xml de la aplicación
+contextConfig.applicationPosition=Se ha producido en la línea {0} columna {1}
+contextConfig.authenticatorConfigured=Configuración de un autentificador (authenticator) para el método {0}
+contextConfig.authenticatorInstantiate=Imposible de instanciar un autenticador (authenticator) para la clase {0}
+contextConfig.authenticatorMissing=Imposible de configurar un autentificador (authenticator) para el método {0}
+contextConfig.authenticatorResources=Imposible de cargar la lista de correspondencia de autenticadores (authenticators)
+contextConfig.cce=El objeto de los datos de evento de ciclo de vida (Lifecycle event data object) {0} no es un Contexto
+contextConfig.certificatesConfig.added=Adición de certificados -> requiere válvula de atributo (attribute Valve)
+contextConfig.certificatesConfig.error=Excepción durante la adición de "CertificatesValve":
+contextConfig.defaultClose=Error durante el cierre del archivo web.xml por defecto
+contextConfig.defaultConfig=Error de configuración en el archivo web.xml por defecto
+contextConfig.defaultMissing=Falta el archivo web.xml por defecto, utilizando sólo el archivo web.xml de la aplicación
+contextConfig.defaultParse=Error de evaluación (parse) en el arcivo web.xml por defecto
+contextConfig.defaultPosition=Se ha producido en la línea {0} columna {1}
+contextConfig.missingRealm=Algún reino (realm) no ha sido configurado para realizar la autenticación
+contextConfig.role.auth=ATENCIÓN: El nombre de papel de seguridad {0} es usado en un <auth-constraint> sin haber sido definido en <security-role>
+contextConfig.role.link=ATENCIÓN: El nombre de papel de seguridad {0} es usado en un <role-link> sin haber sido definido en <security-role>
+contextConfig.role.runas=ATENCIÓN: El nombre de papel de seguridad {0} es usado en un <run-as> sin haber sido definido en <security-role>
+contextConfig.start="ContextConfig": Tratamiento del "START"
+contextConfig.stop="ContextConfig": Tratamiento del "STOP"
+contextConfig.tldEntryException=Excepción durante el tratamiento de la TLD {0} en el JAR indicado por la trayectoria de recurso {1}
+contextConfig.tldFileException=Excepción durante el tratamiento de la TLD indicada por la trayectoria de recurso {0}
+contextConfig.tldJarException=Excepción durante el tratamiento del JAR indicado por la trayectoria de recurso {0}
+contextConfig.tldResourcePath=Trayectoria de recurso TLD {0} inválida
+contextConfig.unavailable=Esta aplicación está marcada como no disponible debido a los errores precedentes
+embedded.alreadyStarted=El servicio embebido (embedded service) ya ha sido arrancado
+embedded.noEngines=Algún motor (engine) no ha sido aún definido
+embedded.notStarted=El servicio embebido (embedded service) aún no ha sido arrancado
+engineConfig.cce=El objeto de los datos de evento de ciclo de vida (Lifecycle event data object) {0} no es un motor (engine)
+engineConfig.start="EngineConfig": Tratamiento del "START"
+engineConfig.stop="EngineConfig": Tratamiento del "STOP"
+hostConfig.appBase=No existe el directorio base de la aplicación {0}
+hostConfig.cce=El objeto de los datos de evento de ciclo de vida (Lifecycle event data object) {0} no es una máquina (host)
+hostConfig.deploy=Desplieque del directorio {0} de la aplicación web
+hostConfig.deployDescriptor=Desplieque del descriptor de configuración {0}
+hostConfig.deployDescriptor.error=Error durante el despliegue del descriptor de configuración {0}
+hostConfig.deployDir=Despliegue del directorio {0} de la aplicación web
+hostConfig.deployDir.error=Error durante el despliegue del directorio {0} de la aplicación web
+hostConfig.deployJar=Despliegue del archivo {0} de la aplicación web
+hostConfig.deployJar.error=Error durante el despliegue del archivo {0} de la aplicación web
+hostConfig.deploy.error=Excepción en el directorio {0} de la aplicación web
+hostConfig.deploying=Desplegando aplicaciones web descubiertas
+hostConfig.expand=Descompresión del archivo {0} de la aplicación web
+hostConfig.expand.error=Excepción durante la descompresión del archivo {0} de la aplicación web
+hostConfig.expanding=Descubierta descompresión de archivos de aplicaciones web
+hostConfig.context.restart=Error durante el arranque del contexto {0}
+hostConfig.start="HostConfig": Tratamiento del "START"
+hostConfig.stop="HostConfig": Tratamiento del "STOP"
+hostConfig.undeploy=Repliegue (undeploy) de la aplicación web que tiene como trayectoria de contexto {0}
+hostConfig.undeploy.error=Error durante el repliegue (undeploy) de la aplicación web que tiene como trayectoria de contexto {0}
+hostConfig.undeploying=Repliegue de las aplicaciones web desplegadas
+userConfig.database=Excepción durante la carga de base de datos de usuario
+userConfig.deploy=Despliegue de la aplicación web para el usuario {0}
+userConfig.deploying=Desplegando aplicaciones web para el usuario
+userConfig.error=Error durante el despliegue de la aplicación web para el usario {0}
+userConfig.start="UserConfig": Tratamiento del "START"
+userConfig.stop="UserConfig": Tratamiento del "STOP"
diff --git a/container/catalina/src/share/org/apache/catalina/startup/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/startup/LocalStrings_fr.properties
new file mode 100644
index 0000000..4f51967
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/LocalStrings_fr.properties
@@ -0,0 +1,59 @@
+contextConfig.applicationClose=Erreur lors de la fermeture du fichier web.xml de l''application
+contextConfig.applicationConfig=Erreur de configuration dans le fichier web.xml de l''application
+contextConfig.applicationListener=Exception lors de la création de la classe d''écoute (listener) {0}
+contextConfig.applicationMissing=Le fichier web.xml de l''application est absent, utilisation des paramêtres par défaut
+contextConfig.applicationParse=Erreur d''évaluation (parse) dans le fichier web.xml de l''application
+contextConfig.applicationPosition=S''est produite à la ligne {0} colonne {1}
+contextConfig.authenticatorConfigured=Configuration d''un authentificateur (authenticator) pour la méthode {0}
+contextConfig.authenticatorInstantiate=Impossible d''instancier un authentificateur (authenticator) pour la classe {0}
+contextConfig.authenticatorMissing=Impossible de configurer un authentificateur (authenticator) pour la méthode {0}
+contextConfig.authenticatorResources=Impossible de charger la liste de correspondance des authentificateur (authenticators)
+contextConfig.cce=L''objet donnée évènement cycle de vie (Lifecycle event data object) {0} n''est pas un Contexte
+contextConfig.certificatesConfig.added=Ajout de certificats -> requête Attribut de Valve (attribute Valve)
+contextConfig.certificatesConfig.error=Exception lors de l''ajout des "CertificatesValve":
+contextConfig.defaultClose=Erreur lors de la fermeture du fichier web.xml par défaut
+contextConfig.defaultConfig=Erreur de configuration dans le fichier web.xml par défaut
+contextConfig.defaultMissing=Le fichier web.xml par défaut est absent, utilisation du fichier web.xml de l''application web.xml seulement
+contextConfig.defaultParse=Erreur d''évaluation (parse) dans le fichier web.xml par défaut
+contextConfig.defaultPosition=S''est produite à la ligne {0} colonne {1}
+contextConfig.missingRealm=Aucun royaume (realm) n''a été configuré pour réaliser l''authentification
+contextConfig.role.auth=ATTENTION: Le nom de rôle de sécurité {0} est utilisé dans un <auth-constraint> sans avoir été défini dans <security-role>
+contextConfig.role.link=ATTENTION: Le nom de rôle de sécurité {0} est utilisé dans un <role-link> sans avoir été défini dans <security-role>
+contextConfig.role.runas=ATTENTION: Le nom de rôle de sécurité {0} est utilisé dans un <run-as> sans avoir été défini dans <security-role>
+contextConfig.start="ContextConfig": Traitement du "START"
+contextConfig.stop="ContextConfig": Traitement du "STOP"
+contextConfig.tldEntryException=Exception lors du traitement de la TLD {0} dans le JAR indiqué par le chemin de ressource {1}
+contextConfig.tldFileException=Exception lors du traitement de la TLD indiqué par le chemin de ressource {0}
+contextConfig.tldJarException=Exception lors du traitement du JAR indiqué par le chemin de ressource {0}
+contextConfig.tldResourcePath=Chemin de ressource TLD {0} invalide
+contextConfig.unavailable=Cette application est marquée comme non disponible suite aux erreurs précédentes
+embedded.alreadyStarted=Le service enfoui (embedded service) a déjà été démarré
+embedded.noEngines=Aucun moteur (engine) n''a encore été défini
+embedded.notStarted=Le service enfoui (embedded service) n''a pas encore été démarré
+engineConfig.cce=L''objet donnée évènement cycle de vie (Lifecycle event data object) {0} n''est pas un moteur (engine)
+engineConfig.start="EngineConfig": Traitement du "START"
+engineConfig.stop="EngineConfig": Traitement du "STOP"
+hostConfig.cce=L''objet donnée évènement cycle de vie (Lifecycle event data object) {0} n''est pas un hôte
+hostConfig.deploy=Déploiement du répertoire {0} de l''application web
+hostConfig.deployDescriptor=Déploiement du descripteur de configuration {0}
+hostConfig.deployDescriptor.error=Erreur lors du déploiement du descripteur de configuration {0}
+hostConfig.deployDir=Déploiement du répertoire {0} de l''application web
+hostConfig.deployDir.error=Erreur lors du déploiement du répertoire {0} de l''application web
+hostConfig.deployJar=Déploiement de l''archive {0} de l''application web
+hostConfig.deployJar.error=Erreur lors du déploiement de l''archive {0} de l''application web
+hostConfig.deploy.error=Exception lors du répertoire {0} de l''application web
+hostConfig.deploying=Déploiement des applications web découvertes (discovered)
+hostConfig.expand=Décompression de l''archive {0} de l''application web
+hostConfig.expand.error=Exception lors de la décompression de l''archive {0} de l''application web
+hostConfig.expanding=Décompression des archives des applications web découvertes (discovered)
+hostConfig.start="HostConfig": Traitement du "START"
+hostConfig.stop="HostConfig": Traitement du "STOP"
+hostConfig.undeploy=Repli (undeploy) de l''application web ayant pour chemin de contexte {0}
+hostConfig.undeploy.error=Erreur lors du repli (undeploy) de l''application web ayant pour chemin de contexte {0}
+hostConfig.undeploying=Repli des applications web déployées
+userConfig.database=Exception lors du chargement de la base de données utilisateur
+userConfig.deploy=Déploiement de l''application web pour l''utilisateur {0}
+userConfig.deploying=Déploiement des applications web utilisateur
+userConfig.error=Erreur lors du déploiement de l''application web pour l''utilisateur {0}
+userConfig.start="UserConfig": Traitement du "START"
+userConfig.stop="UserConfig": Traitement du "STOP"
diff --git a/container/catalina/src/share/org/apache/catalina/startup/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/startup/LocalStrings_ja.properties
new file mode 100644
index 0000000..8495157
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/LocalStrings_ja.properties
@@ -0,0 +1,64 @@
+contextConfig.applicationClose=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306eweb.xml\u3092\u30af\u30ed\u30fc\u30ba\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+contextConfig.applicationConfig=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306eweb.xml\u306e\u8a2d\u5b9a\u30a8\u30e9\u30fc\u3067\u3059
+contextConfig.applicationListener=\u30af\u30e9\u30b9 {0} \u306e\u30ea\u30b9\u30ca\u3092\u4f5c\u6210\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+contextConfig.applicationMissing=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306eweb.xml\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u3060\u3051\u3092\u4f7f\u7528\u3057\u307e\u3059
+contextConfig.applicationParse=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306eweb.xml\u4e2d\u306e\u89e3\u6790\u30a8\u30e9\u30fc\u3067\u3059
+contextConfig.applicationPosition={0}\u884c\u306e{1}\u5217\u76ee\u3067\u767a\u751f\u3057\u307e\u3057\u305f
+contextConfig.authenticatorConfigured=\u30e1\u30bd\u30c3\u30c9 {0} \u306e\u30aa\u30fc\u30bb\u30f3\u30c6\u30a3\u30b1\u30fc\u30bf\u3092\u8a2d\u5b9a\u3057\u307e\u3059
+contextConfig.authenticatorInstantiate=\u30af\u30e9\u30b9 {0} \u306e\u30aa\u30fc\u30bb\u30f3\u30c6\u30a3\u30b1\u30fc\u30bf\u3092\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5316\u3067\u304d\u307e\u305b\u3093
+contextConfig.authenticatorMissing=\u30e1\u30bd\u30c3\u30c9 {0} \u306e\u30aa\u30fc\u30bb\u30f3\u30c6\u30a3\u30b1\u30fc\u30bf\u3092\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093
+contextConfig.authenticatorResources=\u30aa\u30fc\u30bb\u30f3\u30c6\u30a3\u30b1\u30fc\u30bf\u306e\u30de\u30c3\u30d7\u30ea\u30b9\u30c8\u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093
+contextConfig.cce=\u30e9\u30a4\u30d5\u30b5\u30a4\u30af\u30eb\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30aa\u30d6\u30b8\u30a7\u30af\u30c8 {0} \u306f\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+contextConfig.certificatesConfig.added=\u8a3c\u660e\u66f8\u3092\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u5c5e\u6027\u5024\u306b\u8ffd\u52a0\u3057\u307e\u3059
+contextConfig.certificatesConfig.error=CertificatesValve\u3092\u8ffd\u52a0\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f:
+contextConfig.defaultClose=\u30c7\u30d5\u30a9\u30eb\u30c8\u306eweb.xml\u306e\u30af\u30ed\u30fc\u30ba\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+contextConfig.defaultConfig=\u30c7\u30d5\u30a9\u30eb\u30c8\u306eweb.xml\u306e\u4e2d\u306e\u8a2d\u5b9a\u30a8\u30e9\u30fc\u3067\u3059
+contextConfig.defaultMissing=\u30c7\u30d5\u30a9\u30eb\u30c8\u306eweb.xml\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306eweb.xml\u3060\u3051\u3092\u4f7f\u7528\u3057\u307e\u3059
+contextConfig.defaultParse=\u30c7\u30d5\u30a9\u30eb\u30c8\u306eweb.xml\u4e2d\u306e\u89e3\u6790\u30a8\u30e9\u30fc\u3067\u3059
+contextConfig.defaultPosition={0}\u884c\u306e{1}\u5217\u76ee\u3067\u767a\u751f\u3057\u307e\u3057\u305f
+contextConfig.missingRealm=\u8a8d\u8a3c\u3059\u308b\u305f\u3081\u306b\u30ec\u30eb\u30e0\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+contextConfig.role.auth=\u8b66\u544a: <security-role>\u306b\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u306a\u3044\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ed\u30fc\u30eb\u540d {0} \u304c<auth-constraint>\u306e\u4e2d\u3067\u4f7f\u7528\u3055\u308c\u307e\u3057\u305f
+contextConfig.role.link=\u8b66\u544a: <security-role>\u306b\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u306a\u3044\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ed\u30fc\u30eb\u540d {0} \u304c<role-link>\u306e\u4e2d\u3067\u4f7f\u7528\u3055\u308c\u307e\u3057\u305f
+contextConfig.role.runas=\u8b66\u544a: <security-role>\u306b\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u306a\u3044\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ed\u30fc\u30eb\u540d {0} \u304c<run-as>\u306e\u4e2d\u3067\u4f7f\u7528\u3055\u308c\u307e\u3057\u305f
+contextConfig.start=ContextConfig: \u51e6\u7406\u3092\u958b\u59cb\u3057\u307e\u3059
+contextConfig.stop=ContextConfig: \u51e6\u7406\u3092\u505c\u6b62\u3057\u307e\u3059
+contextConfig.tldEntryException=\u30ea\u30bd\u30fc\u30b9\u30d1\u30b9 {1} \u306eJAR\u30d5\u30a1\u30a4\u30eb\u306eTLD {0} \u3092\u51e6\u7406\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+contextConfig.tldFileException=\u30ea\u30bd\u30fc\u30b9\u30d1\u30b9 {0} \u306eTLD\u3092\u51e6\u7406\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+contextConfig.tldJarException=\u30ea\u30bd\u30fc\u30b9\u30d1\u30b9 {0} \u306eJAR\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+contextConfig.tldResourcePath=\u7121\u52b9\u306aTLD\u306e\u30ea\u30bd\u30fc\u30b9\u30d1\u30b9 {0}
+contextConfig.unavailable=\u524d\u306e\u30a8\u30e9\u30fc\u306e\u305f\u3081\u306b\u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u5229\u7528\u3067\u304d\u306a\u3044\u3088\u3046\u306b\u30de\u30fc\u30af\u3057\u307e\u3059
+embedded.alreadyStarted=\u7d44\u307f\u8fbc\u307f\u30b5\u30fc\u30d3\u30b9\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+embedded.noEngines=\u307e\u3060\u30a8\u30f3\u30b8\u30f3\u304c\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+embedded.notStarted=\u7d44\u307f\u8fbc\u307f\u30b5\u30fc\u30d3\u30b9\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+engineConfig.cce=\u30e9\u30a4\u30d5\u30b5\u30a4\u30af\u30eb\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30aa\u30d6\u30b8\u30a7\u30af\u30c8 {0} \u306f\u30a8\u30f3\u30b8\u30f3\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+engineConfig.start=EngineConfig: \u51e6\u7406\u3092\u958b\u59cb\u3057\u307e\u3059
+engineConfig.stop=EngineConfig: \u51e6\u7406\u3092\u505c\u6b62\u3057\u307e\u3059
+hostConfig.appBase=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u306f\u5b58\u5728\u3057\u307e\u305b\u3093
+hostConfig.cce=\u30e9\u30a4\u30d5\u30b5\u30a4\u30af\u30eb\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30aa\u30d6\u30b8\u30a7\u30af\u30c8 {0} \u306f\u30db\u30b9\u30c8\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+hostConfig.deploy=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u3092\u914d\u5099\u3057\u307e\u3059
+hostConfig.deployDescriptor=\u8a2d\u5b9a\u8a18\u8ff0\u5b50 {0} \u3092\u914d\u5099\u3057\u307e\u3059
+hostConfig.deployDescriptor.error=\u8a2d\u5b9a\u8a18\u8ff0\u5b50 {0} \u3092\u914d\u5099\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+hostConfig.deployDir=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u3092\u914d\u5099\u3057\u307e\u3059
+hostConfig.deployDir.error=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u3092\u914d\u5099\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+hostConfig.deployJar=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30a2\u30fc\u30ab\u30a4\u30d6 {0} \u3092\u914d\u5099\u3057\u307e\u3059
+hostConfig.deployJar.error=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30a2\u30fc\u30ab\u30a4\u30d6 {0} \u3092\u914d\u5099\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+hostConfig.deploy.error=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u3092\u914d\u5099\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+hostConfig.deploying=\u898b\u3064\u304b\u3063\u305fWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u3057\u307e\u3059
+hostConfig.expand=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30a2\u30fc\u30ab\u30a4\u30d6 {0} \u3092\u5c55\u958b\u3057\u307e\u3059
+hostConfig.expand.error=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30a2\u30fc\u30ab\u30a4\u30d6 {0} \u3092\u5c55\u958b\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+hostConfig.expanding=\u898b\u3064\u304b\u3063\u305fWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30a2\u30fc\u30ab\u30a4\u30d6\u3092\u5c55\u958b\u3057\u307e\u3059
+hostConfig.context.restart=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8 {0} \u3092\u518d\u8d77\u52d5\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+hostConfig.removeXML=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8 {0} \u306e\u914d\u5099\u3092\u89e3\u9664\u3057\u307e\u3057\u305f
+hostConfig.removeDIR=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u306e\u914d\u5099\u3092\u89e3\u9664\u3057\u307e\u3057\u305f
+hostConfig.removeWAR=WAR\u30d5\u30a1\u30a4\u30eb {0} \u306e\u914d\u5099\u3092\u89e3\u9664\u3057\u307e\u3057\u305f
+hostConfig.start=HostConfig: \u51e6\u7406\u3092\u505c\u6b62\u3057\u307e\u3059
+hostConfig.stop=HostConfig: \u51e6\u7406\u3092\u505c\u6b62\u3057\u307e\u3059
+hostConfig.undeploy=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u914d\u5099\u3092\u89e3\u9664\u3057\u307e\u3059
+hostConfig.undeploy.error=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u914d\u5099\u3092\u89e3\u9664\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+hostConfig.undeploying=\u914d\u5099\u3055\u308c\u305fWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u914d\u5099\u3092\u89e3\u9664\u3057\u3066\u3044\u307e\u3059
+userConfig.database=\u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30ed\u30fc\u30c9\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+userConfig.deploy=\u30e6\u30fc\u30b6 {0} \u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u3057\u307e\u3059
+userConfig.deploying=\u30e6\u30fc\u30b6\u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u3057\u307e\u3059
+userConfig.error=\u30e6\u30fc\u30b6 {0} \u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+userConfig.start=UserConfig: \u51e6\u7406\u3092\u958b\u59cb\u3057\u307e\u3059
+userConfig.stop=UserConfig: \u51e6\u7406\u3092\u505c\u6b62\u3057\u307e\u3059
diff --git a/container/catalina/src/share/org/apache/catalina/startup/NamingRuleSet.java b/container/catalina/src/share/org/apache/catalina/startup/NamingRuleSet.java
new file mode 100644
index 0000000..bb272e3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/NamingRuleSet.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSetBase;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the JNDI Enterprise Naming
+ * Context resource declaration elements.</p>
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class NamingRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public NamingRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public NamingRuleSet(String prefix) {
+
+        super();
+        this.namespaceURI = null;
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+
+        digester.addObjectCreate(prefix + "Ejb",
+                                 "org.apache.catalina.deploy.ContextEjb");
+        digester.addRule(prefix + "Ejb", new SetAllPropertiesRule());
+        digester.addRule(prefix + "Ejb",
+                new SetNextNamingRule("addEjb",
+                            "org.apache.catalina.deploy.ContextEjb"));
+
+        digester.addObjectCreate(prefix + "Environment",
+                                 "org.apache.catalina.deploy.ContextEnvironment");
+        digester.addSetProperties(prefix + "Environment");
+        digester.addRule(prefix + "Environment",
+                            new SetNextNamingRule("addEnvironment",
+                            "org.apache.catalina.deploy.ContextEnvironment"));
+
+        digester.addObjectCreate(prefix + "LocalEjb",
+                                 "org.apache.catalina.deploy.ContextLocalEjb");
+        digester.addRule(prefix + "LocalEjb", new SetAllPropertiesRule());
+        digester.addRule(prefix + "LocalEjb",
+                new SetNextNamingRule("addLocalEjb",
+                            "org.apache.catalina.deploy.ContextLocalEjb"));
+
+        digester.addObjectCreate(prefix + "Resource",
+                                 "org.apache.catalina.deploy.ContextResource");
+        digester.addRule(prefix + "Resource", new SetAllPropertiesRule());
+        digester.addRule(prefix + "Resource",
+                new SetNextNamingRule("addResource",
+                            "org.apache.catalina.deploy.ContextResource"));
+
+        digester.addObjectCreate(prefix + "ResourceEnvRef",
+            "org.apache.catalina.deploy.ContextResourceEnvRef");
+        digester.addRule(prefix + "ResourceEnvRef", new SetAllPropertiesRule());
+        digester.addRule(prefix + "ResourceEnvRef",
+                new SetNextNamingRule("addResourceEnvRef",
+                            "org.apache.catalina.deploy.ContextResourceEnvRef"));
+
+        digester.addObjectCreate(prefix + "Transaction",
+            "org.apache.catalina.deploy.ContextTransaction");
+        digester.addRule(prefix + "Transaction", new SetAllPropertiesRule());
+        digester.addRule(prefix + "Transaction",
+                new SetNextNamingRule("setTransaction",
+                            "org.apache.catalina.deploy.ContextTransaction"));
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/PasswdUserDatabase.java b/container/catalina/src/share/org/apache/catalina/startup/PasswdUserDatabase.java
new file mode 100644
index 0000000..d7395b6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/PasswdUserDatabase.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+
+/**
+ * Concrete implementation of the <strong>UserDatabase</code> interface
+ * that processes the <code>/etc/passwd</code> file on a Unix system.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class PasswdUserDatabase
+    implements UserDatabase {
+
+
+    // --------------------------------------------------------- Constructors
+
+
+    /**
+     * Initialize a new instance of this user database component.
+     */
+    public PasswdUserDatabase() {
+
+        super();
+
+    }
+
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /**
+     * The pathname of the Unix password file.
+     */
+    private static final String PASSWORD_FILE = "/etc/passwd";
+
+
+    /**
+     * The set of home directories for all defined users, keyed by username.
+     */
+    private Hashtable homes = new Hashtable();
+
+
+    /**
+     * The UserConfig listener with which we are associated.
+     */
+    private UserConfig userConfig = null;
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return the UserConfig listener with which we are associated.
+     */
+    public UserConfig getUserConfig() {
+
+        return (this.userConfig);
+
+    }
+
+
+    /**
+     * Set the UserConfig listener with which we are associated.
+     *
+     * @param userConfig The new UserConfig listener
+     */
+    public void setUserConfig(UserConfig userConfig) {
+
+        this.userConfig = userConfig;
+        init();
+
+    }
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return an absolute pathname to the home directory for the specified user.
+     *
+     * @param user User for which a home directory should be retrieved
+     */
+    public String getHome(String user) {
+
+        return ((String) homes.get(user));
+
+    }
+
+
+    /**
+     * Return an enumeration of the usernames defined on this server.
+     */
+    public Enumeration getUsers() {
+
+        return (homes.keys());
+
+    }
+
+
+    // ------------------------------------------------------ Private Methods
+
+
+    /**
+     * Initialize our set of users and home directories.
+     */
+    private void init() {
+
+        BufferedReader reader = null;
+        try {
+
+            reader = new BufferedReader(new FileReader(PASSWORD_FILE));
+
+            while (true) {
+
+                // Accumulate the next line
+                StringBuffer buffer = new StringBuffer();
+                while (true) {
+                    int ch = reader.read();
+                    if ((ch < 0) || (ch == '\n'))
+                        break;
+                    buffer.append((char) ch);
+                }
+                String line = buffer.toString();
+                if (line.length() < 1)
+                    break;
+
+                // Parse the line into constituent elements
+                int n = 0;
+                String tokens[] = new String[7];
+                for (int i = 0; i < tokens.length; i++)
+                    tokens[i] = null;
+                while (n < tokens.length) {
+                    String token = null;
+                    int colon = line.indexOf(':');
+                    if (colon >= 0) {
+                        token = line.substring(0, colon);
+                        line = line.substring(colon + 1);
+                    } else {
+                        token = line;
+                        line = "";
+                    }
+                    tokens[n++] = token;
+                }
+
+                // Add this user and corresponding directory
+                if ((tokens[0] != null) && (tokens[5] != null))
+                    homes.put(tokens[0], tokens[5]);
+
+            }
+
+            reader.close();
+            reader = null;
+
+        } catch (Exception e) {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException f) {
+                    ;
+                }
+                reader = null;
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/SetAllPropertiesRule.java b/container/catalina/src/share/org/apache/catalina/startup/SetAllPropertiesRule.java
new file mode 100644
index 0000000..a9a6492
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/SetAllPropertiesRule.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+import org.xml.sax.Attributes;
+
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.digester.Rule;
+
+/**
+ * Rule that uses the introspection utils to set properties.
+ * 
+ * @author Remy Maucherat
+ */
+public class SetAllPropertiesRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Handle the beginning of an XML element.
+     *
+     * @param attributes The attributes of this element
+     *
+     * @exception Exception if a processing error occurs
+     */
+    public void begin(String namespace, String nameX, Attributes attributes)
+        throws Exception {
+
+        for (int i = 0; i < attributes.getLength(); i++) {
+            String name = attributes.getLocalName(i);
+            if ("".equals(name)) {
+                name = attributes.getQName(i);
+            }
+            String value = attributes.getValue(i);
+            IntrospectionUtils.setProperty(digester.peek(), name, value);
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/SetContextPropertiesRule.java b/container/catalina/src/share/org/apache/catalina/startup/SetContextPropertiesRule.java
new file mode 100644
index 0000000..2216565
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/SetContextPropertiesRule.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+import org.xml.sax.Attributes;
+
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.digester.Rule;
+
+/**
+ * Rule that uses the introspection utils to set properties of a context 
+ * (everything except "path").
+ * 
+ * @author Remy Maucherat
+ */
+public class SetContextPropertiesRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Handle the beginning of an XML element.
+     *
+     * @param attributes The attributes of this element
+     *
+     * @exception Exception if a processing error occurs
+     */
+    public void begin(String namespace, String nameX, Attributes attributes)
+        throws Exception {
+
+        for (int i = 0; i < attributes.getLength(); i++) {
+            String name = attributes.getLocalName(i);
+            if ("".equals(name)) {
+                name = attributes.getQName(i);
+            }
+            if ("path".equals(name) || "docBase".equals(name)) {
+                continue;
+            }
+            String value = attributes.getValue(i);
+            IntrospectionUtils.setProperty(digester.peek(), name, value);
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/SetNextNamingRule.java b/container/catalina/src/share/org/apache/catalina/startup/SetNextNamingRule.java
new file mode 100644
index 0000000..b29666b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/SetNextNamingRule.java
@@ -0,0 +1,119 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.catalina.startup;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.digester.Rule;
+
+
+/**
+ * <p>Rule implementation that calls a method on the (top-1) (parent)
+ * object, passing the top object (child) as an argument.  It is
+ * commonly used to establish parent-child relationships.</p>
+ *
+ * <p>This rule now supports more flexible method matching by default.
+ * It is possible that this may break (some) code 
+ * written against release 1.1.1 or earlier.
+ * </p> 
+ */
+
+public class SetNextNamingRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+    
+    /**
+     * Construct a "set next" rule with the specified method name.
+     *
+     * @param methodName Method name of the parent method to call
+     * @param paramType Java class of the parent method's argument
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     */
+    public SetNextNamingRule(String methodName,
+                       String paramType) {
+
+        this.methodName = methodName;
+        this.paramType = paramType;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The method name to call on the parent object.
+     */
+    protected String methodName = null;
+
+
+    /**
+     * The Java class name of the parameter type expected by the method.
+     */
+    protected String paramType = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the end of this element.
+     */
+    public void end() throws Exception {
+
+        // Identify the objects to be used
+        Object child = digester.peek(0);
+        Object parent = digester.peek(1);
+
+        NamingResources namingResources = null;
+        if (parent instanceof Context) {
+            namingResources = ((Context) parent).getNamingResources();
+        } else {
+            namingResources = (NamingResources) parent;
+        }
+        
+        // Call the specified method
+        IntrospectionUtils.callMethod1(namingResources, methodName,
+                child, paramType, digester.getClassLoader());
+
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SetNextRule[");
+        sb.append("methodName=");
+        sb.append(methodName);
+        sb.append(", paramType=");
+        sb.append(paramType);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/TldConfig.java b/container/catalina/src/share/org/apache/catalina/startup/TldConfig.java
new file mode 100644
index 0000000..116f6a4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/TldConfig.java
@@ -0,0 +1,716 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.digester.Digester;
+import org.xml.sax.InputSource;
+
+/**
+ * Startup event listener for a <b>Context</b> that configures the properties
+ * of that Context, and the associated defined servlets.
+ *
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @author Costin Manolache
+ */
+public final class TldConfig  {
+
+    // Names of JARs that are known not to contain any TLDs
+    private static HashSet noTldJars;
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( TldConfig.class );
+
+    private static final String FILE_URL_PREFIX = "file:";
+    private static final int FILE_URL_PREFIX_LEN = FILE_URL_PREFIX.length();
+
+
+    /*
+     * Initializes the set of JARs that are known not to contain any TLDs
+     */
+    static {
+        noTldJars = new HashSet();
+        noTldJars.add("catalina.jar");
+        noTldJars.add("catalina-ant.jar");
+        noTldJars.add("catalina-cluster.jar");
+        noTldJars.add("catalina-optional.jar");
+        noTldJars.add("commons-el.jar");
+        noTldJars.add("commons-logging-api.jar");
+        noTldJars.add("commons-modeler.jar");
+        noTldJars.add("jasper-compiler.jar");
+        noTldJars.add("jasper-compiler-jdt.jar");
+        noTldJars.add("jasper-runtime.jar");
+        noTldJars.add("jsp-api.jar");
+        noTldJars.add("naming-resources.jar");
+        noTldJars.add("naming-factory.jar");
+        noTldJars.add("naming-factory-dbcp.jar");
+        noTldJars.add("servlet-api.jar");
+        noTldJars.add("servlets-cgi.jar");
+        noTldJars.add("servlets-default.jar");
+        noTldJars.add("servlets-invoker.jar");
+        noTldJars.add("servlets-ssi.jar");
+        noTldJars.add("servlets-webdav.jar");
+        noTldJars.add("tomcat-ajp.jar");
+        noTldJars.add("tomcat-coyote.jar");
+        noTldJars.add("tomcat-http.jar");
+        noTldJars.add("tomcat-util.jar");
+        // i18n JARs
+        noTldJars.add("catalina-i18n-en.jar");
+        noTldJars.add("catalina-i18n-es.jar");
+        noTldJars.add("catalina-i18n-fr.jar");
+        noTldJars.add("catalina-i18n-ja.jar");
+        // Misc JARs not included with Tomcat
+        noTldJars.add("ant.jar");
+        noTldJars.add("commons-dbcp.jar");
+        noTldJars.add("commons-beanutils.jar");
+        noTldJars.add("commons-fileupload-1.0.jar");
+        noTldJars.add("commons-pool.jar");
+        noTldJars.add("commons-digester.jar");
+        noTldJars.add("commons-logging.jar");
+        noTldJars.add("commons-collections.jar");
+        noTldJars.add("jmx.jar");
+        noTldJars.add("jmx-tools.jar");
+        noTldJars.add("xercesImpl.jar");
+        noTldJars.add("xmlParserAPIs.jar");
+        noTldJars.add("xml-apis.jar");
+        // JARs from J2SE runtime
+        noTldJars.add("sunjce_provider.jar");
+        noTldJars.add("ldapsec.jar");
+        noTldJars.add("localedata.jar");
+        noTldJars.add("dnsns.jar");
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The Context we are associated with.
+     */
+    private Context context = null;
+
+
+    /**
+     * The string resources for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    /**
+     * The <code>Digester</code> we will use to process tag library
+     * descriptor files.
+     */
+    private static Digester tldDigester = null;
+
+
+    /**
+     * Attribute value used to turn on/off TLD validation
+     */
+     private static boolean tldValidation = false;
+
+
+    /**
+     * Attribute value used to turn on/off TLD  namespace awarenes.
+     */
+    private static boolean tldNamespaceAware = false;
+
+    private boolean rescan=true;
+
+    private ArrayList listeners=new ArrayList();
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Sets the list of JARs that are known not to contain any TLDs.
+     *
+     * @param jarNames List of comma-separated names of JAR files that are 
+     * known not to contain any TLDs 
+     */
+    public static void setNoTldJars(String jarNames) {
+        if (jarNames != null) {
+            noTldJars.clear();
+            StringTokenizer tokenizer = new StringTokenizer(jarNames, ",");
+            while (tokenizer.hasMoreElements()) {
+                noTldJars.add(tokenizer.nextToken());
+            }
+        }
+    }
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param tldValidation true to enable xml instance validation
+     */
+    public void setTldValidation(boolean tldValidation){
+        TldConfig.tldValidation = tldValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getTldValidation(){
+        return tldValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     *
+     */
+    public boolean getTldNamespaceAware(){
+        return tldNamespaceAware;
+    }
+
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param tldNamespaceAware true to enable namespace awareness
+     */
+    public void setTldNamespaceAware(boolean tldNamespaceAware){
+        TldConfig.tldNamespaceAware = tldNamespaceAware;
+    }    
+
+
+    public boolean isRescan() {
+        return rescan;
+    }
+
+    public void setRescan(boolean rescan) {
+        this.rescan = rescan;
+    }
+
+    public Context getContext() {
+        return context;
+    }
+
+    public void setContext(Context context) {
+        this.context = context;
+    }
+
+    public void addApplicationListener( String s ) {
+        //if(log.isDebugEnabled())
+            log.debug( "Add tld listener " + s);
+        listeners.add(s);
+    }
+
+    public String[] getTldListeners() {
+        String result[]=new String[listeners.size()];
+        listeners.toArray(result);
+        return result;
+    }
+
+
+    /**
+     * Scan for and configure all tag library descriptors found in this
+     * web application.
+     *
+     * @exception Exception if a fatal input/output or parsing error occurs
+     */
+    public void execute() throws Exception {
+        long t1=System.currentTimeMillis();
+
+        File tldCache=null;
+
+        if (context instanceof StandardContext) {
+            File workDir= (File)
+                ((StandardContext)context).getServletContext().getAttribute(Globals.WORK_DIR_ATTR);
+            tldCache=new File( workDir, "tldCache.ser");
+        }
+
+        // Option to not rescan
+        if( ! rescan ) {
+            // find the cache
+            if( tldCache!= null && tldCache.exists()) {
+                // just read it...
+                processCache(tldCache);
+                return;
+            }
+        }
+
+        /*
+         * Acquire the list of TLD resource paths, possibly embedded in JAR
+         * files, to be processed
+         */
+        Set resourcePaths = tldScanResourcePaths();
+        Map jarPaths = getJarPaths();
+
+        // Check to see if we can use cached listeners
+        if (tldCache != null && tldCache.exists()) {
+            long lastModified = getLastModified(resourcePaths, jarPaths);
+            if (lastModified < tldCache.lastModified()) {
+                processCache(tldCache);
+                return;
+            }
+        }
+
+        // Scan each accumulated resource path for TLDs to be processed
+        Iterator paths = resourcePaths.iterator();
+        while (paths.hasNext()) {
+            String path = (String) paths.next();
+            if (path.endsWith(".jar")) {
+                tldScanJar(path);
+            } else {
+                tldScanTld(path);
+            }
+        }
+        if (jarPaths != null) {
+            paths = jarPaths.values().iterator();
+            while (paths.hasNext()) {
+                tldScanJar((File) paths.next());
+            }
+        }
+
+        String list[] = getTldListeners();
+
+        if( tldCache!= null ) {
+            log.debug( "Saving tld cache: " + tldCache + " " + list.length);
+            try {
+                FileOutputStream out=new FileOutputStream(tldCache);
+                ObjectOutputStream oos=new ObjectOutputStream( out );
+                oos.writeObject( list );
+                oos.close();
+            } catch( IOException ex ) {
+                ex.printStackTrace();
+            }
+        }
+
+        if( log.isDebugEnabled() )
+            log.debug( "Adding tld listeners:" + list.length);
+        for( int i=0; list!=null && i<list.length; i++ ) {
+            context.addApplicationListener(list[i]);
+        }
+
+        long t2=System.currentTimeMillis();
+        if( context instanceof StandardContext ) {
+            ((StandardContext)context).setTldScanTime(t2-t1);
+        }
+
+    }
+
+    // -------------------------------------------------------- Private Methods
+
+    /*
+     * Returns the last modification date of the given sets of resources.
+     *
+     * @param resourcePaths
+     * @param jarPaths
+     *
+     * @return Last modification date
+     */
+    private long getLastModified(Set resourcePaths, Map jarPaths)
+            throws Exception {
+
+        long lastModified = 0;
+
+        Iterator paths = resourcePaths.iterator();
+        while (paths.hasNext()) {
+            String path = (String) paths.next();
+            URL url = context.getServletContext().getResource(path);
+            if (url == null) {
+                log.debug( "Null url "+ path );
+                break;
+            }
+            long lastM = url.openConnection().getLastModified();
+            if (lastM > lastModified) lastModified = lastM;
+            if (log.isDebugEnabled()) {
+                log.debug( "Last modified " + path + " " + lastM);
+            }
+        }
+
+        if (jarPaths != null) {
+            paths = jarPaths.values().iterator();
+            while (paths.hasNext()) {
+                File jarFile = (File) paths.next();
+                long lastM = jarFile.lastModified();
+                if (lastM > lastModified) lastModified = lastM;
+                if (log.isDebugEnabled()) {
+                    log.debug("Last modified " + jarFile.getAbsolutePath()
+                              + " " + lastM);
+                }
+            }
+        }
+
+        return lastModified;
+    }
+
+    private void processCache(File tldCache ) throws IOException {
+        // read the cache and return;
+        try {
+            FileInputStream in=new FileInputStream(tldCache);
+            ObjectInputStream ois=new ObjectInputStream( in );
+            String list[]=(String [])ois.readObject();
+            if( log.isDebugEnabled() )
+                log.debug("Reusing tldCache " + tldCache + " " + list.length);
+            for( int i=0; list!=null && i<list.length; i++ ) {
+                context.addApplicationListener(list[i]);
+            }
+            ois.close();
+        } catch( ClassNotFoundException ex ) {
+            ex.printStackTrace();
+        }
+    }
+
+    /**
+     * Create (if necessary) and return a Digester configured to process a tag
+     * library descriptor, looking for additional listener classes to be
+     * registered.
+     */
+    private static Digester createTldDigester() {
+
+        return DigesterFactory.newDigester(tldValidation, 
+                                           tldNamespaceAware, 
+                                           new TldRuleSet());
+
+    }
+
+
+    /**
+     * Scan the JAR file at the specified resource path for TLDs in the
+     * <code>META-INF</code> subdirectory, and scan each TLD for application
+     * event listeners that need to be registered.
+     *
+     * @param resourcePath Resource path of the JAR file to scan
+     *
+     * @exception Exception if an exception occurs while scanning this JAR
+     */
+    private void tldScanJar(String resourcePath) throws Exception {
+
+        if (log.isDebugEnabled()) {
+            log.debug(" Scanning JAR at resource path '" + resourcePath + "'");
+        }
+
+        URL url = context.getServletContext().getResource(resourcePath);
+        if (url == null) {
+            throw new IllegalArgumentException
+                                (sm.getString("contextConfig.tldResourcePath",
+                                              resourcePath));
+        }
+
+        File file = new File(url.getFile());
+        file = file.getCanonicalFile();
+        tldScanJar(file);
+
+    }
+
+    /**
+     * Scans all TLD entries in the given JAR for application listeners.
+     *
+     * @param file JAR file whose TLD entries are scanned for application
+     * listeners
+     */
+    private void tldScanJar(File file) throws Exception {
+
+        JarFile jarFile = null;
+        String name = null;
+
+        String jarPath = file.getAbsolutePath();
+
+        try {
+            jarFile = new JarFile(file);
+            Enumeration entries = jarFile.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry entry = (JarEntry) entries.nextElement();
+                name = entry.getName();
+                if (!name.startsWith("META-INF/")) {
+                    continue;
+                }
+                if (!name.endsWith(".tld")) {
+                    continue;
+                }
+                if (log.isTraceEnabled()) {
+                    log.trace("  Processing TLD at '" + name + "'");
+                }
+                try {
+                    tldScanStream(new InputSource(jarFile.getInputStream(entry)));
+                } catch (Exception e) {
+                    log.error(sm.getString("contextConfig.tldEntryException",
+                                           name, jarPath, context.getPath()),
+                              e);
+                }
+            }
+        } catch (Exception e) {
+            log.error(sm.getString("contextConfig.tldJarException",
+                                   jarPath, context.getPath()),
+                      e);
+        } finally {
+            if (jarFile != null) {
+                try {
+                    jarFile.close();
+                } catch (Throwable t) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Scan the TLD contents in the specified input stream, and register
+     * any application event listeners found there.  <b>NOTE</b> - It is
+     * the responsibility of the caller to close the InputStream after this
+     * method returns.
+     *
+     * @param resourceStream InputStream containing a tag library descriptor
+     *
+     * @exception Exception if an exception occurs while scanning this TLD
+     */
+    private void tldScanStream(InputSource resourceStream)
+        throws Exception {
+
+        if (tldDigester == null){
+            tldDigester = createTldDigester();
+        }
+        
+        synchronized (tldDigester) {
+            try {
+                tldDigester.push(this);
+                tldDigester.parse(resourceStream);
+            } finally {
+                tldDigester.reset();
+            }
+        }
+
+    }
+
+    /**
+     * Scan the TLD contents at the specified resource path, and register
+     * any application event listeners found there.
+     *
+     * @param resourcePath Resource path being scanned
+     *
+     * @exception Exception if an exception occurs while scanning this TLD
+     */
+    private void tldScanTld(String resourcePath) throws Exception {
+
+        if (log.isDebugEnabled()) {
+            log.debug(" Scanning TLD at resource path '" + resourcePath + "'");
+        }
+
+        InputSource inputSource = null;
+        try {
+            inputSource =
+                new InputSource(
+                    context.getServletContext().getResourceAsStream(resourcePath));
+            if (inputSource == null) {
+                throw new IllegalArgumentException
+                    (sm.getString("contextConfig.tldResourcePath",
+                                  resourcePath));
+            }
+            tldScanStream(inputSource);
+        } catch (Exception e) {
+             throw new ServletException
+                 (sm.getString("contextConfig.tldFileException", resourcePath,
+                               context.getPath()),
+                  e);
+        } 
+
+    }
+
+    /**
+     * Accumulate and return a Set of resource paths to be analyzed for
+     * tag library descriptors.  Each element of the returned set will be
+     * the context-relative path to either a tag library descriptor file,
+     * or to a JAR file that may contain tag library descriptors in its
+     * <code>META-INF</code> subdirectory.
+     *
+     * @exception IOException if an input/output error occurs while
+     *  accumulating the list of resource paths
+     */
+    private Set tldScanResourcePaths() throws IOException {
+        if (log.isDebugEnabled()) {
+            log.debug(" Accumulating TLD resource paths");
+        }
+        Set resourcePaths = new HashSet();
+
+        // Accumulate resource paths explicitly listed in the web application
+        // deployment descriptor
+        if (log.isTraceEnabled()) {
+            log.trace("  Scanning <taglib> elements in web.xml");
+        }
+        String taglibs[] = context.findTaglibs();
+        for (int i = 0; i < taglibs.length; i++) {
+            String resourcePath = context.findTaglib(taglibs[i]);
+            // FIXME - Servlet 2.4 DTD implies that the location MUST be
+            // a context-relative path starting with '/'?
+            if (!resourcePath.startsWith("/")) {
+                resourcePath = "/WEB-INF/" + resourcePath;
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("   Adding path '" + resourcePath +
+                    "' for URI '" + taglibs[i] + "'");
+            }
+            resourcePaths.add(resourcePath);
+        }
+
+        DirContext resources = context.getResources();
+        if (resources != null) {
+            tldScanResourcePathsWebInf(resources, "/WEB-INF", resourcePaths);
+        }
+
+        // Return the completed set
+        return (resourcePaths);
+
+    }
+
+    /*
+     * Scans the web application's subdirectory identified by rootPath,
+     * along with its subdirectories, for TLDs.
+     *
+     * Initially, rootPath equals /WEB-INF. The /WEB-INF/classes and
+     * /WEB-INF/lib subdirectories are excluded from the search, as per the
+     * JSP 2.0 spec.
+     *
+     * @param resources The web application's resources
+     * @param rootPath The path whose subdirectories are to be searched for
+     * TLDs
+     * @param tldPaths The set of TLD resource paths to add to
+     */
+    private void tldScanResourcePathsWebInf(DirContext resources,
+                                            String rootPath,
+                                            Set tldPaths) 
+            throws IOException {
+
+        if (log.isTraceEnabled()) {
+            log.trace("  Scanning TLDs in " + rootPath + " subdirectory");
+        }
+
+        try {
+            NamingEnumeration items = resources.list(rootPath);
+            while (items.hasMoreElements()) {
+                NameClassPair item = (NameClassPair) items.nextElement();
+                String resourcePath = rootPath + "/" + item.getName();
+                if (!resourcePath.endsWith(".tld")
+                        && (resourcePath.startsWith("/WEB-INF/classes")
+                            || resourcePath.startsWith("/WEB-INF/lib"))) {
+                    continue;
+                }
+                if (resourcePath.endsWith(".tld")) {
+                    if (log.isTraceEnabled()) {
+                        log.trace("   Adding path '" + resourcePath + "'");
+                    }
+                    tldPaths.add(resourcePath);
+                } else {
+                    tldScanResourcePathsWebInf(resources, resourcePath,
+                                               tldPaths);
+                }
+            }
+        } catch (NamingException e) {
+            ; // Silent catch: it's valid that no /WEB-INF directory exists
+        }
+    }
+
+    /**
+     * Returns a map of the paths to all JAR files that are accessible to the
+     * webapp and will be scanned for TLDs.
+     *
+     * The map always includes all the JARs under WEB-INF/lib, as well as
+     * shared JARs in the classloader delegation chain of the webapp's
+     * classloader.
+     *
+     * The latter constitutes a Tomcat-specific extension to the TLD search
+     * order defined in the JSP spec. It allows tag libraries packaged as JAR
+     * files to be shared by web applications by simply dropping them in a 
+     * location that all web applications have access to (e.g.,
+     * <CATALINA_HOME>/common/lib).
+     *
+     * The set of shared JARs to be scanned for TLDs is narrowed down by
+     * the <tt>noTldJars</tt> class variable, which contains the names of JARs
+     * that are known not to contain any TLDs.
+     *
+     * @return Map of JAR file paths
+     */
+    private Map getJarPaths() {
+
+        HashMap jarPathMap = null;
+
+        ClassLoader webappLoader = Thread.currentThread().getContextClassLoader();
+        ClassLoader loader = webappLoader;
+        while (loader != null) {
+            if (loader instanceof URLClassLoader) {
+                URL[] urls = ((URLClassLoader) loader).getURLs();
+                for (int i=0; i<urls.length; i++) {
+                    // Expect file URLs
+                    // This is definitely not as clean as using JAR URLs either
+                    // over file or the custom jndi handler, but a lot less
+                    // buggy overall
+                    File file = new File(urls[i].getFile());
+                    try {
+                        file = file.getCanonicalFile();
+                    } catch (IOException e) {
+                        // Ignore
+                    }
+                    if (!file.exists()) {
+                        continue;
+                    }
+                    String path = file.getAbsolutePath();
+                    if (!path.endsWith(".jar")) {
+                        continue;
+                    }
+                    /*
+                     * Scan all JARs from WEB-INF/lib, plus any shared JARs
+                     * that are not known not to contain any TLDs
+                     */
+                    if (loader == webappLoader
+                            || noTldJars == null
+                            || !noTldJars.contains(file.getName())) {
+                        if (jarPathMap == null) {
+                            jarPathMap = new HashMap();
+                            jarPathMap.put(path, file);
+                        } else if (!jarPathMap.containsKey(path)) {
+                            jarPathMap.put(path, file);
+                        }
+                    }
+                }
+            }
+            loader = loader.getParent();
+        }
+
+        return jarPathMap;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/TldRuleSet.java b/container/catalina/src/share/org/apache/catalina/startup/TldRuleSet.java
new file mode 100644
index 0000000..12fd4a8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/TldRuleSet.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSetBase;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a tag library
+ * descriptor resource.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TldRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public TldRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public TldRuleSet(String prefix) {
+
+        super();
+        this.namespaceURI = null;
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+
+        digester.addCallMethod(prefix + "taglib/listener/listener-class",
+                               "addApplicationListener", 0);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/Tool.java b/container/catalina/src/share/org/apache/catalina/startup/Tool.java
new file mode 100644
index 0000000..d82eb11
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/Tool.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * <p>General purpose wrapper for command line tools that should execute in an
+ * environment with the common class loader environment set up by Catalina.
+ * This should be executed from a command line script that conforms to
+ * the following requirements:</p>
+ * <ul>
+ * <li>Passes the <code>catalina.home</code> system property configured with
+ *     the pathname of the Tomcat installation directory.</li>
+ * <li>Sets the system classpath to include <code>bootstrap.jar</code> and
+ *     <code>$JAVA_HOME/lib/tools.jar</code>.</li>
+ * </ul>
+ *
+ * <p>The command line to execute the tool looks like:</p>
+ * <pre>
+ *   java -classpath $CLASSPATH org.apache.catalina.startup.Tool \
+ *     ${options} ${classname} ${arguments}
+ * </pre>
+ *
+ * <p>with the following replacement contents:
+ * <ul>
+ * <li><strong>${options}</strong> - Command line options for this Tool wrapper.
+ *     The following options are supported:
+ *     <ul>
+ *     <li><em>-ant</em> : Set the <code>ant.home</code> system property
+ *         to corresponding to the value of <code>catalina.home</code>
+ *         (useful when your command line tool runs Ant).</li>
+ *     <li><em>-common</em> : Add <code>common/classes</code> and
+ *         <code>common/lib</codE) to the class loader repositories.</li>
+ *     <li><em>-server</em> : Add <code>server/classes</code> and
+ *         <code>server/lib</code> to the class loader repositories.</li>
+ *     <li><em>-shared</em> : Add <code>shared/classes</code> and
+ *         <code>shared/lib</code> to the class loader repositories.</li>
+ *     </ul>
+ * <li><strong>${classname}</strong> - Fully qualified Java class name of the
+ *     application's main class.</li>
+ * <li><strong>${arguments}</strong> - Command line arguments to be passed to
+ *     the application's <code>main()</code> method.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class Tool {
+
+
+    private static Log log = LogFactory.getLog(Tool.class);
+    
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * Set <code>ant.home</code> system property?
+     */
+    private static boolean ant = false;
+
+
+    /**
+     * The pathname of our installation base directory.
+     */
+    private static String catalinaHome = System.getProperty("catalina.home");
+
+
+    /**
+     * Include common classes in the repositories?
+     */
+    private static boolean common = false;
+
+
+    /**
+     * Include server classes in the repositories?
+     */
+    private static boolean server = false;
+
+
+    /**
+     * Include shared classes in the repositories?
+     */
+    private static boolean shared = false;
+
+
+    // ----------------------------------------------------------- Main Program
+
+
+    /**
+     * The main program for the bootstrap.
+     *
+     * @param args Command line arguments to be processed
+     */
+    public static void main(String args[]) {
+
+        // Verify that "catalina.home" was passed.
+        if (catalinaHome == null) {
+            log.error("Must set 'catalina.home' system property");
+            System.exit(1);
+        }
+
+        // Process command line options
+        int index = 0;
+        while (true) {
+            if (index == args.length) {
+                usage();
+                System.exit(1);
+            }
+            if ("-ant".equals(args[index]))
+                ant = true;
+            else if ("-common".equals(args[index]))
+                common = true;
+            else if ("-server".equals(args[index]))
+                server = true;
+            else if ("-shared".equals(args[index]))
+                shared = true;
+            else
+                break;
+            index++;
+        }
+        if (index > args.length) {
+            usage();
+            System.exit(1);
+        }
+
+        // Set "ant.home" if requested
+        if (ant)
+            System.setProperty("ant.home", catalinaHome);
+
+        // Construct the class loader we will be using
+        ClassLoader classLoader = null;
+        try {
+            ArrayList packed = new ArrayList();
+            ArrayList unpacked = new ArrayList();
+            unpacked.add(new File(catalinaHome, "classes"));
+            packed.add(new File(catalinaHome, "lib"));
+            if (common) {
+                unpacked.add(new File(catalinaHome,
+                                      "common" + File.separator + "classes"));
+                packed.add(new File(catalinaHome,
+                                    "common" + File.separator + "lib"));
+            }
+            if (server) {
+                unpacked.add(new File(catalinaHome,
+                                      "server" + File.separator + "classes"));
+                packed.add(new File(catalinaHome,
+                                    "server" + File.separator + "lib"));
+            }
+            if (shared) {
+                unpacked.add(new File(catalinaHome,
+                                      "shared" + File.separator + "classes"));
+                packed.add(new File(catalinaHome,
+                                    "shared" + File.separator + "lib"));
+            }
+            classLoader =
+                ClassLoaderFactory.createClassLoader
+                ((File[]) unpacked.toArray(new File[0]),
+                 (File[]) packed.toArray(new File[0]),
+                 null);
+        } catch (Throwable t) {
+            log.error("Class loader creation threw exception", t);
+            System.exit(1);
+        }
+        Thread.currentThread().setContextClassLoader(classLoader);
+
+        // Load our application class
+        Class clazz = null;
+        String className = args[index++];
+        try {
+            if (log.isDebugEnabled())
+                log.debug("Loading application class " + className);
+            clazz = classLoader.loadClass(className);
+        } catch (Throwable t) {
+            log.error("Exception creating instance of " + className, t);
+            System.exit(1);
+        }
+
+        // Locate the static main() method of the application class
+        Method method = null;
+        String params[] = new String[args.length - index];
+        System.arraycopy(args, index, params, 0, params.length);
+        try {
+            if (log.isDebugEnabled())
+                log.debug("Identifying main() method");
+            String methodName = "main";
+            Class paramTypes[] = new Class[1];
+            paramTypes[0] = params.getClass();
+            method = clazz.getMethod(methodName, paramTypes);
+        } catch (Throwable t) {
+            log.error("Exception locating main() method", t);
+            System.exit(1);
+        }
+
+        // Invoke the main method of the application class
+        try {
+            if (log.isDebugEnabled())
+                log.debug("Calling main() method");
+            Object paramValues[] = new Object[1];
+            paramValues[0] = params;
+            method.invoke(null, paramValues);
+        } catch (Throwable t) {
+            log.error("Exception calling main() method", t);
+            System.exit(1);
+        }
+
+    }
+
+
+    /**
+     * Display usage information about this tool.
+     */
+    private static void usage() {
+
+        log.info("Usage:  java org.apache.catalina.startup.Tool [<options>] <class> [<arguments>]");
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/UserConfig.java b/container/catalina/src/share/org/apache/catalina/startup/UserConfig.java
new file mode 100644
index 0000000..8c2cc68
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/UserConfig.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.util.Enumeration;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * Startup event listener for a <b>Host</b> that configures Contexts (web
+ * applications) for all defined "users" who have a web application in a
+ * directory with the specified name in their home directories.  The context
+ * path of each deployed application will be set to <code>~xxxxx</code>, where
+ * xxxxx is the username of the owning user for that web application
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class UserConfig
+    implements LifecycleListener {
+
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( UserConfig.class );
+
+    
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Java class name of the Context configuration class we should use.
+     */
+    private String configClass = "org.apache.catalina.startup.ContextConfig";
+
+
+    /**
+     * The Java class name of the Context implementation we should use.
+     */
+    private String contextClass = "org.apache.catalina.core.StandardContext";
+
+
+    /**
+     * The directory name to be searched for within each user home directory.
+     */
+    private String directoryName = "public_html";
+
+
+    /**
+     * The base directory containing user home directories.
+     */
+    private String homeBase = null;
+
+
+    /**
+     * The Host we are associated with.
+     */
+    private Host host = null;
+
+
+    /**
+     * The string resources for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The Java class name of the user database class we should use.
+     */
+    private String userClass =
+        "org.apache.catalina.startup.PasswdUserDatabase";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Context configuration class name.
+     */
+    public String getConfigClass() {
+
+        return (this.configClass);
+
+    }
+
+
+    /**
+     * Set the Context configuration class name.
+     *
+     * @param configClass The new Context configuration class name.
+     */
+    public void setConfigClass(String configClass) {
+
+        this.configClass = configClass;
+
+    }
+
+
+    /**
+     * Return the Context implementation class name.
+     */
+    public String getContextClass() {
+
+        return (this.contextClass);
+
+    }
+
+
+    /**
+     * Set the Context implementation class name.
+     *
+     * @param contextClass The new Context implementation class name.
+     */
+    public void setContextClass(String contextClass) {
+
+        this.contextClass = contextClass;
+
+    }
+
+
+    /**
+     * Return the directory name for user web applications.
+     */
+    public String getDirectoryName() {
+
+        return (this.directoryName);
+
+    }
+
+
+    /**
+     * Set the directory name for user web applications.
+     *
+     * @param directoryName The new directory name
+     */
+    public void setDirectoryName(String directoryName) {
+
+        this.directoryName = directoryName;
+
+    }
+
+
+    /**
+     * Return the base directory containing user home directories.
+     */
+    public String getHomeBase() {
+
+        return (this.homeBase);
+
+    }
+
+
+    /**
+     * Set the base directory containing user home directories.
+     *
+     * @param homeBase The new base directory
+     */
+    public void setHomeBase(String homeBase) {
+
+        this.homeBase = homeBase;
+
+    }
+
+
+    /**
+     * Return the user database class name for this component.
+     */
+    public String getUserClass() {
+
+        return (this.userClass);
+
+    }
+
+
+    /**
+     * Set the user database class name for this component.
+     */
+    public void setUserClass(String userClass) {
+
+        this.userClass = userClass;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the START event for an associated Host.
+     *
+     * @param event The lifecycle event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        // Identify the host we are associated with
+        try {
+            host = (Host) event.getLifecycle();
+        } catch (ClassCastException e) {
+            log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
+            return;
+        }
+
+        // Process the event that has occurred
+        if (event.getType().equals(Lifecycle.START_EVENT))
+            start();
+        else if (event.getType().equals(Lifecycle.STOP_EVENT))
+            stop();
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Deploy a web application for any user who has a web application present
+     * in a directory with a specified name within their home directory.
+     */
+    private void deploy() {
+
+        if (host.getLogger().isDebugEnabled())
+            host.getLogger().debug(sm.getString("userConfig.deploying"));
+
+        // Load the user database object for this host
+        UserDatabase database = null;
+        try {
+            Class clazz = Class.forName(userClass);
+            database = (UserDatabase) clazz.newInstance();
+            database.setUserConfig(this);
+        } catch (Exception e) {
+            host.getLogger().error(sm.getString("userConfig.database"), e);
+            return;
+        }
+
+        // Deploy the web application (if any) for each defined user
+        Enumeration users = database.getUsers();
+        while (users.hasMoreElements()) {
+            String user = (String) users.nextElement();
+            String home = database.getHome(user);
+            deploy(user, home);
+        }
+
+    }
+
+
+    /**
+     * Deploy a web application for the specified user if they have such an
+     * application in the defined directory within their home directory.
+     *
+     * @param user Username owning the application to be deployed
+     * @param home Home directory of this user
+     */
+    private void deploy(String user, String home) {
+
+        // Does this user have a web application to be deployed?
+        String contextPath = "/~" + user;
+        if (host.findChild(contextPath) != null)
+            return;
+        File app = new File(home, directoryName);
+        if (!app.exists() || !app.isDirectory())
+            return;
+        /*
+        File dd = new File(app, "/WEB-INF/web.xml");
+        if (!dd.exists() || !dd.isFile() || !dd.canRead())
+            return;
+        */
+        host.getLogger().info(sm.getString("userConfig.deploy", user));
+
+        // Deploy the web application for this user
+        try {
+            Class clazz = Class.forName(contextClass);
+            Context context =
+              (Context) clazz.newInstance();
+            context.setPath(contextPath);
+            context.setDocBase(app.toString());
+            if (context instanceof Lifecycle) {
+                clazz = Class.forName(configClass);
+                LifecycleListener listener =
+                  (LifecycleListener) clazz.newInstance();
+                ((Lifecycle) context).addLifecycleListener(listener);
+            }
+            host.addChild(context);
+        } catch (Exception e) {
+            host.getLogger().error(sm.getString("userConfig.error", user), e);
+        }
+
+    }
+
+
+    /**
+     * Process a "start" event for this Host.
+     */
+    private void start() {
+
+        if (host.getLogger().isDebugEnabled())
+            host.getLogger().debug(sm.getString("userConfig.start"));
+
+        deploy();
+
+    }
+
+
+    /**
+     * Process a "stop" event for this Host.
+     */
+    private void stop() {
+
+        if (host.getLogger().isDebugEnabled())
+            host.getLogger().debug(sm.getString("userConfig.stop"));
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/UserDatabase.java b/container/catalina/src/share/org/apache/catalina/startup/UserDatabase.java
new file mode 100644
index 0000000..4b9eee1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/UserDatabase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.util.Enumeration;
+
+
+/**
+ * Abstraction of the set of users defined by the operating system on the
+ * current server platform.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public interface UserDatabase {
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return the UserConfig listener with which we are associated.
+     */
+    public UserConfig getUserConfig();
+
+
+    /**
+     * Set the UserConfig listener with which we are associated.
+     *
+     * @param userConfig The new UserConfig listener
+     */
+    public void setUserConfig(UserConfig userConfig);
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return an absolute pathname to the home directory for the specified user.
+     *
+     * @param user User for which a home directory should be retrieved
+     */
+    public String getHome(String user);
+
+
+    /**
+     * Return an enumeration of the usernames defined on this server.
+     */
+    public Enumeration getUsers();
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/WebRuleSet.java b/container/catalina/src/share/org/apache/catalina/startup/WebRuleSet.java
new file mode 100644
index 0000000..4e4a7d0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/WebRuleSet.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.startup;
+
+
+import java.lang.reflect.Method;
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.apache.tomcat.util.digester.RuleSetBase;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a web application
+ * deployment descriptor (<code>/WEB-INF/web.xml</code>) resource.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class WebRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+    
+    
+    /**
+     * The <code>SetSessionConfig</code> rule used to parse the web.xml
+     */
+    protected SetSessionConfig sessionConfig;
+    
+    
+    /**
+     * The <code>SetLoginConfig</code> rule used to parse the web.xml
+     */
+    protected SetLoginConfig loginConfig;
+
+    
+    /**
+     * The <code>SetJspConfig</code> rule used to parse the web.xml
+     */    
+    protected SetJspConfig jspConfig;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public WebRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public WebRuleSet(String prefix) {
+
+        super();
+        this.namespaceURI = null;
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+        sessionConfig = new SetSessionConfig();
+        jspConfig = new SetJspConfig();
+        loginConfig = new SetLoginConfig();
+        
+        digester.addRule(prefix + "web-app",
+                         new SetPublicIdRule("setPublicId"));
+
+        digester.addCallMethod(prefix + "web-app/context-param",
+                               "addParameter", 2);
+        digester.addCallParam(prefix + "web-app/context-param/param-name", 0);
+        digester.addCallParam(prefix + "web-app/context-param/param-value", 1);
+
+        digester.addCallMethod(prefix + "web-app/display-name",
+                               "setDisplayName", 0);
+
+        digester.addRule(prefix + "web-app/distributable",
+                         new SetDistributableRule());
+
+        digester.addObjectCreate(prefix + "web-app/ejb-local-ref",
+                                 "org.apache.catalina.deploy.ContextLocalEjb");
+        digester.addRule(prefix + "web-app/ejb-local-ref",
+                new SetNextNamingRule("addLocalEjb",
+                            "org.apache.catalina.deploy.ContextLocalEjb"));
+
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-link",
+                               "setLink", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-type",
+                               "setType", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/local",
+                               "setLocal", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/local-home",
+                               "setHome", 0);
+
+        digester.addObjectCreate(prefix + "web-app/ejb-ref",
+                                 "org.apache.catalina.deploy.ContextEjb");
+        digester.addRule(prefix + "web-app/ejb-ref",
+                new SetNextNamingRule("addEjb",
+                            "org.apache.catalina.deploy.ContextEjb"));
+
+        digester.addCallMethod(prefix + "web-app/ejb-ref/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-link",
+                               "setLink", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-type",
+                               "setType", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/home",
+                               "setHome", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/remote",
+                               "setRemote", 0);
+
+        digester.addObjectCreate(prefix + "web-app/env-entry",
+                                 "org.apache.catalina.deploy.ContextEnvironment");
+        digester.addRule(prefix + "web-app/env-entry",
+                new SetNextNamingRule("addEnvironment",
+                            "org.apache.catalina.deploy.ContextEnvironment"));
+
+        digester.addCallMethod(prefix + "web-app/env-entry/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/env-entry/env-entry-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/env-entry/env-entry-type",
+                               "setType", 0);
+        digester.addCallMethod(prefix + "web-app/env-entry/env-entry-value",
+                               "setValue", 0);
+
+        digester.addObjectCreate(prefix + "web-app/error-page",
+                                 "org.apache.catalina.deploy.ErrorPage");
+        digester.addSetNext(prefix + "web-app/error-page",
+                            "addErrorPage",
+                            "org.apache.catalina.deploy.ErrorPage");
+
+        digester.addCallMethod(prefix + "web-app/error-page/error-code",
+                               "setErrorCode", 0);
+        digester.addCallMethod(prefix + "web-app/error-page/exception-type",
+                               "setExceptionType", 0);
+        digester.addCallMethod(prefix + "web-app/error-page/location",
+                               "setLocation", 0);
+
+        digester.addObjectCreate(prefix + "web-app/filter",
+                                 "org.apache.catalina.deploy.FilterDef");
+        digester.addSetNext(prefix + "web-app/filter",
+                            "addFilterDef",
+                            "org.apache.catalina.deploy.FilterDef");
+
+        digester.addCallMethod(prefix + "web-app/filter/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/filter/display-name",
+                               "setDisplayName", 0);
+        digester.addCallMethod(prefix + "web-app/filter/filter-class",
+                               "setFilterClass", 0);
+        digester.addCallMethod(prefix + "web-app/filter/filter-name",
+                               "setFilterName", 0);
+        digester.addCallMethod(prefix + "web-app/filter/large-icon",
+                               "setLargeIcon", 0);
+        digester.addCallMethod(prefix + "web-app/filter/small-icon",
+                               "setSmallIcon", 0);
+
+        digester.addCallMethod(prefix + "web-app/filter/init-param",
+                               "addInitParameter", 2);
+        digester.addCallParam(prefix + "web-app/filter/init-param/param-name",
+                              0);
+        digester.addCallParam(prefix + "web-app/filter/init-param/param-value",
+                              1);
+
+        digester.addObjectCreate(prefix + "web-app/filter-mapping",
+                                 "org.apache.catalina.deploy.FilterMap");
+        digester.addSetNext(prefix + "web-app/filter-mapping",
+                            "addFilterMap",
+                            "org.apache.catalina.deploy.FilterMap");
+
+        digester.addCallMethod(prefix + "web-app/filter-mapping/filter-name",
+                               "setFilterName", 0);
+        digester.addCallMethod(prefix + "web-app/filter-mapping/servlet-name",
+                               "setServletName", 0);
+        digester.addCallMethod(prefix + "web-app/filter-mapping/url-pattern",
+                               "setURLPattern", 0);
+
+        digester.addCallMethod(prefix + "web-app/filter-mapping/dispatcher",
+                               "setDispatcher", 0);
+
+         digester.addCallMethod(prefix + "web-app/listener/listener-class",
+                                "addApplicationListener", 0);
+         
+        digester.addRule(prefix + "web-app/jsp-config",
+                         jspConfig);
+        
+        digester.addCallMethod(prefix + "web-app/jsp-config/jsp-property-group/url-pattern",
+                               "addJspMapping", 0);
+
+        digester.addCallMethod(prefix + "web-app/listener/listener-class",
+                               "addApplicationListener", 0);
+        
+        digester.addRule(prefix + "web-app/login-config",
+                         loginConfig);
+
+        digester.addObjectCreate(prefix + "web-app/login-config",
+                                 "org.apache.catalina.deploy.LoginConfig");
+        digester.addSetNext(prefix + "web-app/login-config",
+                            "setLoginConfig",
+                            "org.apache.catalina.deploy.LoginConfig");
+
+        digester.addCallMethod(prefix + "web-app/login-config/auth-method",
+                               "setAuthMethod", 0);
+        digester.addCallMethod(prefix + "web-app/login-config/realm-name",
+                               "setRealmName", 0);
+        digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-error-page",
+                               "setErrorPage", 0);
+        digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-login-page",
+                               "setLoginPage", 0);
+
+        digester.addCallMethod(prefix + "web-app/mime-mapping",
+                               "addMimeMapping", 2);
+        digester.addCallParam(prefix + "web-app/mime-mapping/extension", 0);
+        digester.addCallParam(prefix + "web-app/mime-mapping/mime-type", 1);
+
+        digester.addObjectCreate(prefix + "web-app/resource-env-ref",
+            "org.apache.catalina.deploy.ContextResourceEnvRef");
+        digester.addRule(prefix + "web-app/resource-env-ref",
+                    new SetNextNamingRule("addResourceEnvRef",
+                        "org.apache.catalina.deploy.ContextResourceEnvRef"));
+
+        digester.addCallMethod(prefix + "web-app/resource-env-ref/resource-env-ref-name",
+                "setName", 0);
+        digester.addCallMethod(prefix + "web-app/resource-env-ref/resource-env-ref-type",
+                "setType", 0);
+
+        digester.addObjectCreate(prefix + "web-app/message-destination",
+                                 "org.apache.catalina.deploy.MessageDestination");
+        digester.addSetNext(prefix + "web-app/message-destination",
+                            "addMessageDestination",
+                            "org.apache.catalina.deploy.MessageDestination");
+
+        digester.addCallMethod(prefix + "web-app/message-destination/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination/display-name",
+                               "setDisplayName", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination/icon/large-icon",
+                               "setLargeIcon", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination/icon/small-icon",
+                               "setSmallIcon", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination/message-destination-name",
+                               "setName", 0);
+
+        digester.addObjectCreate(prefix + "web-app/message-destination-ref",
+                                 "org.apache.catalina.deploy.MessageDestinationRef");
+        digester.addSetNext(prefix + "web-app/message-destination-ref",
+                            "addMessageDestinationRef",
+                            "org.apache.catalina.deploy.MessageDestinationRef");
+
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/message-destination-link",
+                               "setLink", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/message-destination-ref-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/message-destination-type",
+                               "setType", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/message-destination-usage",
+                               "setUsage", 0);
+
+        digester.addObjectCreate(prefix + "web-app/resource-ref",
+                                 "org.apache.catalina.deploy.ContextResource");
+        digester.addRule(prefix + "web-app/resource-ref",
+                new SetNextNamingRule("addResource",
+                            "org.apache.catalina.deploy.ContextResource"));
+
+        digester.addCallMethod(prefix + "web-app/resource-ref/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/resource-ref/res-auth",
+                               "setAuth", 0);
+        digester.addCallMethod(prefix + "web-app/resource-ref/res-ref-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/resource-ref/res-sharing-scope",
+                               "setScope", 0);
+        digester.addCallMethod(prefix + "web-app/resource-ref/res-type",
+                               "setType", 0);
+
+        digester.addObjectCreate(prefix + "web-app/security-constraint",
+                                 "org.apache.catalina.deploy.SecurityConstraint");
+        digester.addSetNext(prefix + "web-app/security-constraint",
+                            "addConstraint",
+                            "org.apache.catalina.deploy.SecurityConstraint");
+
+        digester.addRule(prefix + "web-app/security-constraint/auth-constraint",
+                         new SetAuthConstraintRule());
+        digester.addCallMethod(prefix + "web-app/security-constraint/auth-constraint/role-name",
+                               "addAuthRole", 0);
+        digester.addCallMethod(prefix + "web-app/security-constraint/display-name",
+                               "setDisplayName", 0);
+        digester.addCallMethod(prefix + "web-app/security-constraint/user-data-constraint/transport-guarantee",
+                               "setUserConstraint", 0);
+
+        digester.addObjectCreate(prefix + "web-app/security-constraint/web-resource-collection",
+                                 "org.apache.catalina.deploy.SecurityCollection");
+        digester.addSetNext(prefix + "web-app/security-constraint/web-resource-collection",
+                            "addCollection",
+                            "org.apache.catalina.deploy.SecurityCollection");
+        digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/http-method",
+                               "addMethod", 0);
+        digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/url-pattern",
+                               "addPattern", 0);
+        digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/web-resource-name",
+                               "setName", 0);
+
+        digester.addCallMethod(prefix + "web-app/security-role/role-name",
+                               "addSecurityRole", 0);
+
+        digester.addRule(prefix + "web-app/servlet",
+                         new WrapperCreateRule());
+        digester.addSetNext(prefix + "web-app/servlet",
+                            "addChild",
+                            "org.apache.catalina.Container");
+
+        digester.addCallMethod(prefix + "web-app/servlet/init-param",
+                               "addInitParameter", 2);
+        digester.addCallParam(prefix + "web-app/servlet/init-param/param-name",
+                              0);
+        digester.addCallParam(prefix + "web-app/servlet/init-param/param-value",
+                              1);
+
+        digester.addCallMethod(prefix + "web-app/servlet/jsp-file",
+                               "setJspFile", 0);
+        digester.addCallMethod(prefix + "web-app/servlet/load-on-startup",
+                               "setLoadOnStartupString", 0);
+        digester.addCallMethod(prefix + "web-app/servlet/run-as/role-name",
+                               "setRunAs", 0);
+
+        digester.addCallMethod(prefix + "web-app/servlet/security-role-ref",
+                               "addSecurityReference", 2);
+        digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-link", 1);
+        digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-name", 0);
+
+        digester.addCallMethod(prefix + "web-app/servlet/servlet-class",
+                              "setServletClass", 0);
+        digester.addCallMethod(prefix + "web-app/servlet/servlet-name",
+                              "setName", 0);
+
+        digester.addCallMethod(prefix + "web-app/servlet-mapping",
+                               "addServletMapping", 2);
+        digester.addCallParam(prefix + "web-app/servlet-mapping/servlet-name", 1);
+        digester.addCallParam(prefix + "web-app/servlet-mapping/url-pattern", 0);
+
+        digester.addRule(prefix + "web-app/session-config",
+                         sessionConfig);
+        
+        digester.addCallMethod(prefix + "web-app/session-config/session-timeout",
+                               "setSessionTimeout", 1,
+                               new Class[] { Integer.TYPE });
+        digester.addCallParam(prefix + "web-app/session-config/session-timeout", 0);
+
+        digester.addCallMethod(prefix + "web-app/taglib",
+                               "addTaglib", 2);
+        digester.addCallParam(prefix + "web-app/taglib/taglib-location", 1);
+        digester.addCallParam(prefix + "web-app/taglib/taglib-uri", 0);
+
+        digester.addCallMethod(prefix + "web-app/welcome-file-list/welcome-file",
+                               "addWelcomeFile", 0);
+
+        digester.addCallMethod(prefix + "web-app/locale-encoding-mapping-list/locale-encoding-mapping",
+                              "addLocaleEncodingMappingParameter", 2);
+        digester.addCallParam(prefix + "web-app/locale-encoding-mapping-list/locale-encoding-mapping/locale", 0);
+        digester.addCallParam(prefix + "web-app/locale-encoding-mapping-list/locale-encoding-mapping/encoding", 1);
+
+    }
+
+    /**
+     * Reset counter used for validating the web.xml file.
+     */
+    public void recycle(){
+        jspConfig.isJspConfigSet = false;
+        sessionConfig.isSessionConfigSet = false;
+        loginConfig.isLoginConfigSet = false;
+    }
+}
+
+
+// ----------------------------------------------------------- Private Classes
+
+
+/**
+ * Rule to check that the <code>login-config</code> is occuring 
+ * only 1 time within the web.xml
+ */
+final class SetLoginConfig extends Rule {
+    protected boolean isLoginConfigSet = false;
+    public SetLoginConfig() {
+    }
+
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+        if (isLoginConfigSet){
+            throw new IllegalArgumentException(
+            "<login-config> element is limited to 1 occurance");
+        }
+        isLoginConfigSet = true;
+    }
+
+}
+
+
+/**
+ * Rule to check that the <code>jsp-config</code> is occuring 
+ * only 1 time within the web.xml
+ */
+final class SetJspConfig extends Rule {
+    protected boolean isJspConfigSet = false;
+    public SetJspConfig() {
+    }
+
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+        if (isJspConfigSet){
+            throw new IllegalArgumentException(
+            "<jsp-config> element is limited to 1 occurance");
+        }
+        isJspConfigSet = true;
+    }
+
+}
+
+
+/**
+ * Rule to check that the <code>session-config</code> is occuring 
+ * only 1 time within the web.xml
+ */
+final class SetSessionConfig extends Rule {
+    protected boolean isSessionConfigSet = false;
+    public SetSessionConfig() {
+    }
+
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+        if (isSessionConfigSet){
+            throw new IllegalArgumentException(
+            "<session-config> element is limited to 1 occurance");
+        }
+        isSessionConfigSet = true;
+    }
+
+}
+
+/**
+ * A Rule that calls the <code>setAuthConstraint(true)</code> method of
+ * the top item on the stack, which must be of type
+ * <code>org.apache.catalina.deploy.SecurityConstraint</code>.
+ */
+
+final class SetAuthConstraintRule extends Rule {
+
+    public SetAuthConstraintRule() {
+    }
+
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+        SecurityConstraint securityConstraint =
+            (SecurityConstraint) digester.peek();
+        securityConstraint.setAuthConstraint(true);
+        if (digester.getLogger().isDebugEnabled()) {
+            digester.getLogger()
+               .debug("Calling SecurityConstraint.setAuthConstraint(true)");
+        }
+    }
+
+}
+
+
+/**
+ * Class that calls <code>setDistributable(true)</code> for the top object
+ * on the stack, which must be a <code>org.apache.catalina.Context</code>.
+ */
+
+final class SetDistributableRule extends Rule {
+
+    public SetDistributableRule() {
+    }
+
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+        Context context = (Context) digester.peek();
+        context.setDistributable(true);
+        if (digester.getLogger().isDebugEnabled()) {
+            digester.getLogger().debug
+               (context.getClass().getName() + ".setDistributable( true)");
+        }
+    }
+
+}
+
+
+/**
+ * Class that calls a property setter for the top object on the stack,
+ * passing the public ID of the entity we are currently processing.
+ */
+
+final class SetPublicIdRule extends Rule {
+
+    public SetPublicIdRule(String method) {
+        this.method = method;
+    }
+
+    private String method = null;
+
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+
+        Context context = (Context) digester.peek(digester.getCount() - 1);
+        Object top = digester.peek();
+        Class paramClasses[] = new Class[1];
+        paramClasses[0] = "String".getClass();
+        String paramValues[] = new String[1];
+        paramValues[0] = digester.getPublicId();
+
+        Method m = null;
+        try {
+            m = top.getClass().getMethod(method, paramClasses);
+        } catch (NoSuchMethodException e) {
+            digester.getLogger().error("Can't find method " + method + " in "
+                                       + top + " CLASS " + top.getClass());
+            return;
+        }
+
+        m.invoke(top, paramValues);
+        if (digester.getLogger().isDebugEnabled())
+            digester.getLogger().debug("" + top.getClass().getName() + "." 
+                                       + method + "(" + paramValues[0] + ")");
+
+    }
+
+}
+
+
+/**
+ * A Rule that calls the factory method on the specified Context to
+ * create the object that is to be added to the stack.
+ */
+
+final class WrapperCreateRule extends Rule {
+
+    public WrapperCreateRule() {
+    }
+
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+        Context context =
+            (Context) digester.peek(digester.getCount() - 1);
+        Wrapper wrapper = context.createWrapper();
+        digester.push(wrapper);
+        if (digester.getLogger().isDebugEnabled())
+            digester.getLogger().debug("new " + wrapper.getClass().getName());
+    }
+
+    public void end(String namespace, String name)
+        throws Exception {
+        Wrapper wrapper = (Wrapper) digester.pop();
+        if (digester.getLogger().isDebugEnabled())
+            digester.getLogger().debug("pop " + wrapper.getClass().getName());
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/startup/catalina.properties b/container/catalina/src/share/org/apache/catalina/startup/catalina.properties
new file mode 100644
index 0000000..417eb91
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/catalina.properties
@@ -0,0 +1,66 @@
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageAccess unless the
+# corresponding RuntimePermission ("accessClassInPackage."+package) has
+# been granted.
+package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.,sun.beans.
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageDefinition unless the
+# corresponding RuntimePermission ("defineClassInPackage."+package) has
+# been granted.
+#
+# by default, no packages are restricted for definition, and none of
+# the class loaders supplied with the JDK call checkPackageDefinition.
+#
+package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.
+
+#
+#
+# List of comma-separated paths defining the contents of the "common" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
+# If left as blank,the JVM system loader will be used as Catalina's "common" 
+# loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository
+common.loader=${catalina.home}/common/classes,${catalina.home}/common/i18n/*.jar,${catalina.home}/common/endorsed/*.jar,${catalina.home}/common/lib/*.jar
+
+#
+# List of comma-separated paths defining the contents of the "server" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
+# If left as blank, the "common" loader will be used as Catalina's "server" 
+# loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository
+server.loader=${catalina.home}/server/classes,${catalina.home}/server/lib/*.jar
+
+#
+# List of comma-separated paths defining the contents of the "shared" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
+# the "common" loader will be used as Catalina's "shared" loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository 
+# Please note that for single jars, e.g. bar.jar, you need the URL form
+# starting with file:.
+shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar
+
+#
+# String cache configuration.
+tomcat.util.buf.StringCache.byte.enabled=true
+#tomcat.util.buf.StringCache.char.enabled=true
+#tomcat.util.buf.StringCache.trainThreshold=500000
+#tomcat.util.buf.StringCache.cacheSize=5000
diff --git a/container/catalina/src/share/org/apache/catalina/startup/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/startup/mbeans-descriptors.xml
new file mode 100644
index 0000000..1d283c4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/startup/mbeans-descriptors.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean name="ContextConfig"
+         description="Startup event listener for a Context that configures the properties of that Context, and the associated defined servlets"
+         domain="Catalina"
+         group="Listener"
+         type="org.apache.catalina.startup.ContextConfig">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+  </mbean>
+
+  <mbean name="EngineConfig"
+         description="Startup event listener for a Engine that configures the properties of that Engine, and the associated defined contexts"
+         domain="Catalina"
+         group="Listener"
+         type="org.apache.catalina.startup.EngineConfig">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+  </mbean>
+
+
+  <mbean name="HostConfig"
+         description="Startup event listener for a Host that configures the properties of that Host, and the associated defined contexts"
+         domain="Catalina"
+         group="Listener"
+         type="org.apache.catalina.startup.HostConfig">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="configBaseName"
+               description="The base directory for Context configuration files"
+               type="java.lang.String"
+               writeable="false" />
+
+    <attribute name="configClass"
+               description="The Java class name of the Context configuration class we should use"
+               type="java.lang.String"/>
+
+    <attribute name="contextClass"
+               description="The Java class name of the Context implementation we should use"
+               type="java.lang.String"/>
+      
+    <operation name="check"
+               description="Check a web application name for updates"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="name"
+                 description="Application name"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation name="addServiced"
+               description="Add a web application name to the serviced list"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="name"
+                 description="Application name"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation name="isServiced"
+               description="Add a web application name to the serviced list"
+               impact="ACTION"
+               returnType="boolean">
+      <parameter name="name"
+                 description="Application name"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation name="manageApp"
+               description="Add a web application managed externally"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="context"
+                 description="Context to add"
+                 type="org.apache.catalina.Context" />
+    </operation>
+
+    <operation name="removeServiced"
+               description="Add a web application name to the serviced list"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="name"
+                 description="Application name"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation name="unmanageApp"
+               description="Remove a web application from checks"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="contextPath"
+                 description="The application path"
+                 type="java.lang.String" />
+    </operation>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/users/AbstractGroup.java b/container/catalina/src/share/org/apache/catalina/users/AbstractGroup.java
new file mode 100644
index 0000000..10212c2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/AbstractGroup.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.users;
+
+
+import java.util.Iterator;
+
+import org.apache.catalina.Group;
+import org.apache.catalina.Role;
+import org.apache.catalina.UserDatabase;
+
+
+/**
+ * <p>Convenience base class for {@link Group} implementations.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public abstract class AbstractGroup implements Group {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The description of this group.
+     */
+    protected String description = null;
+
+
+    /**
+     * The group name of this group.
+     */
+    protected String groupname = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the description of this group.
+     */
+    public String getDescription() {
+
+        return (this.description);
+
+    }
+
+
+    /**
+     * Set the description of this group.
+     *
+     * @param description The new description
+     */
+    public void setDescription(String description) {
+
+        this.description = description;
+
+    }
+
+
+    /**
+     * Return the group name of this group, which must be unique
+     * within the scope of a {@link UserDatabase}.
+     */
+    public String getGroupname() {
+
+        return (this.groupname);
+
+    }
+
+
+    /**
+     * Set the group name of this group, which must be unique
+     * within the scope of a {@link UserDatabase}.
+     *
+     * @param groupname The new group name
+     */
+    public void setGroupname(String groupname) {
+
+        this.groupname = groupname;
+
+    }
+
+
+    /**
+     * Return the set of {@link Role}s assigned specifically to this group.
+     */
+    public abstract Iterator getRoles();
+
+
+    /**
+     * Return the {@link UserDatabase} within which this Group is defined.
+     */
+    public abstract UserDatabase getUserDatabase();
+
+
+    /**
+     * Return an Iterator over the set of {@link org.apache.catalina.User}s that 
+     * are members of this group.
+     */
+    public abstract Iterator getUsers();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new {@link Role} to those assigned specifically to this group.
+     *
+     * @param role The new role
+     */
+    public abstract void addRole(Role role);
+
+
+    /**
+     * Is this group specifically assigned the specified {@link Role}?
+     *
+     * @param role The role to check
+     */
+    public abstract boolean isInRole(Role role);
+
+
+    /**
+     * Remove a {@link Role} from those assigned to this group.
+     *
+     * @param role The old role
+     */
+    public abstract void removeRole(Role role);
+
+
+    /**
+     * Remove all {@link Role}s from those assigned to this group.
+     */
+    public abstract void removeRoles();
+
+
+    // ------------------------------------------------------ Principal Methods
+
+
+    /**
+     * Make the principal name the same as the group name.
+     */
+    public String getName() {
+
+        return (getGroupname());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/users/AbstractRole.java b/container/catalina/src/share/org/apache/catalina/users/AbstractRole.java
new file mode 100644
index 0000000..f3ad2a3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/AbstractRole.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.users;
+
+
+import org.apache.catalina.Role;
+import org.apache.catalina.UserDatabase;
+
+
+/**
+ * <p>Convenience base class for {@link Role} implementations.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public abstract class AbstractRole implements Role {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The description of this Role.
+     */
+    protected String description = null;
+
+
+    /**
+     * The role name of this Role.
+     */
+    protected String rolename = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the description of this role.
+     */
+    public String getDescription() {
+
+        return (this.description);
+
+    }
+
+
+    /**
+     * Set the description of this role.
+     *
+     * @param description The new description
+     */
+    public void setDescription(String description) {
+
+        this.description = description;
+
+    }
+
+
+    /**
+     * Return the role name of this role, which must be unique
+     * within the scope of a {@link UserDatabase}.
+     */
+    public String getRolename() {
+
+        return (this.rolename);
+
+    }
+
+
+    /**
+     * Set the role name of this role, which must be unique
+     * within the scope of a {@link UserDatabase}.
+     *
+     * @param rolename The new role name
+     */
+    public void setRolename(String rolename) {
+
+        this.rolename = rolename;
+
+    }
+
+
+    /**
+     * Return the {@link UserDatabase} within which this Role is defined.
+     */
+    public abstract UserDatabase getUserDatabase();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // ------------------------------------------------------ Principal Methods
+
+
+    /**
+     * Make the principal name the same as the role name.
+     */
+    public String getName() {
+
+        return (getRolename());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/users/AbstractUser.java b/container/catalina/src/share/org/apache/catalina/users/AbstractUser.java
new file mode 100644
index 0000000..27beae2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/AbstractUser.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.users;
+
+
+import java.util.Iterator;
+import org.apache.catalina.Group;
+import org.apache.catalina.Role;
+import org.apache.catalina.User;
+
+
+/**
+ * <p>Convenience base class for {@link User} implementations.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public abstract class AbstractUser implements User {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The full name of this user.
+     */
+    protected String fullName = null;
+
+
+    /**
+     * The logon password of this user.
+     */
+    protected String password = null;
+
+
+    /**
+     * The logon username of this user.
+     */
+    protected String username = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the full name of this user.
+     */
+    public String getFullName() {
+
+        return (this.fullName);
+
+    }
+
+
+    /**
+     * Set the full name of this user.
+     *
+     * @param fullName The new full name
+     */
+    public void setFullName(String fullName) {
+
+        this.fullName = fullName;
+
+    }
+
+
+    /**
+     * Return the set of {@link Group}s to which this user belongs.
+     */
+    public abstract Iterator getGroups();
+
+
+    /**
+     * Return the logon password of this user, optionally prefixed with the
+     * identifier of an encoding scheme surrounded by curly braces, such as
+     * <code>{md5}xxxxx</code>.
+     */
+    public String getPassword() {
+
+        return (this.password);
+
+    }
+
+
+    /**
+     * Set the logon password of this user, optionally prefixed with the
+     * identifier of an encoding scheme surrounded by curly braces, such as
+     * <code>{md5}xxxxx</code>.
+     *
+     * @param password The new logon password
+     */
+    public void setPassword(String password) {
+
+        this.password = password;
+
+    }
+
+
+    /**
+     * Return the set of {@link Role}s assigned specifically to this user.
+     */
+    public abstract Iterator getRoles();
+
+
+    /**
+     * Return the logon username of this user, which must be unique
+     * within the scope of a {@link org.apache.catalina.UserDatabase}.
+     */
+    public String getUsername() {
+
+        return (this.username);
+
+    }
+
+
+    /**
+     * Set the logon username of this user, which must be unique within
+     * the scope of a {@link org.apache.catalina.UserDatabase}.
+     *
+     * @param username The new logon username
+     */
+    public void setUsername(String username) {
+
+        this.username = username;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new {@link Group} to those this user belongs to.
+     *
+     * @param group The new group
+     */
+    public abstract void addGroup(Group group);
+
+
+    /**
+     * Add a new {@link Role} to those assigned specifically to this user.
+     *
+     * @param role The new role
+     */
+    public abstract void addRole(Role role);
+
+
+    /**
+     * Is this user in the specified {@link Group}?
+     *
+     * @param group The group to check
+     */
+    public abstract boolean isInGroup(Group group);
+
+
+    /**
+     * Is this user specifically assigned the specified {@link Role}?  This
+     * method does <strong>NOT</strong> check for roles inherited based on
+     * {@link Group} membership.
+     *
+     * @param role The role to check
+     */
+    public abstract boolean isInRole(Role role);
+
+
+    /**
+     * Remove a {@link Group} from those this user belongs to.
+     *
+     * @param group The old group
+     */
+    public abstract void removeGroup(Group group);
+
+
+    /**
+     * Remove all {@link Group}s from those this user belongs to.
+     */
+    public abstract void removeGroups();
+
+
+    /**
+     * Remove a {@link Role} from those assigned to this user.
+     *
+     * @param role The old role
+     */
+    public abstract void removeRole(Role role);
+
+
+    /**
+     * Remove all {@link Role}s from those assigned to this user.
+     */
+    public abstract void removeRoles();
+
+
+    // ------------------------------------------------------ Principal Methods
+
+
+    /**
+     * Make the principal name the same as the group name.
+     */
+    public String getName() {
+
+        return (getUsername());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/users/Constants.java b/container/catalina/src/share/org/apache/catalina/users/Constants.java
new file mode 100644
index 0000000..a36bc0c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/Constants.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.users;
+
+
+/**
+ * Manifest constants for this Java package.
+ *
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.catalina.users";
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/users/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/users/LocalStrings.properties
new file mode 100644
index 0000000..4c8c0c5
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/LocalStrings.properties
@@ -0,0 +1,5 @@
+memoryUserDatabase.invalidGroup=Invalid group name {0}
+memoryUserDatabase.renameOld=Cannot rename original file to {0}
+memoryUserDatabase.renameNew=Cannot rename new file to {0}
+memoryUserDatabase.writeException=IOException writing to {0}
+memoryUserDatabase.notPersistable=User database is not persistable - no write permissions on directory
diff --git a/container/catalina/src/share/org/apache/catalina/users/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/users/LocalStrings_es.properties
new file mode 100644
index 0000000..121e6f9
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/LocalStrings_es.properties
@@ -0,0 +1,4 @@
+memoryUserDatabase.invalidGroup=Nombre de grupo {0} inválido
+memoryUserDatabase.renameOld=Imposible de renombrar el archivo original a {0}
+memoryUserDatabase.renameNew=Imposible de renombrar el archivo nuevo a {0}
+memoryUserDatabase.writeException=IOException durante la escritura hacia {0}
diff --git a/container/catalina/src/share/org/apache/catalina/users/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/users/LocalStrings_fr.properties
new file mode 100644
index 0000000..bb28728
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/LocalStrings_fr.properties
@@ -0,0 +1,4 @@
+memoryUserDatabase.invalidGroup=Nom de groupe invalide {0}
+memoryUserDatabase.renameOld=Impossible de renommer le fichier original en {0}
+memoryUserDatabase.renameNew=Impossible de renommer le nouveau fichier en {0}
+memoryUserDatabase.writeException=IOException lors de l''écriture vers {0}
diff --git a/container/catalina/src/share/org/apache/catalina/users/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/users/LocalStrings_ja.properties
new file mode 100644
index 0000000..2ae96e8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/LocalStrings_ja.properties
@@ -0,0 +1,4 @@
+memoryUserDatabase.invalidGroup=\u7121\u52b9\u306a\u30b0\u30eb\u30fc\u30d7\u540d {0}
+memoryUserDatabase.renameOld=\u5143\u306e\u30d5\u30a1\u30a4\u30eb\u540d\u3092 {0} \u306b\u5909\u66f4\u3067\u304d\u307e\u305b\u3093
+memoryUserDatabase.renameNew=\u65b0\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u540d\u3092 {0} \u306b\u5909\u66f4\u3067\u304d\u307e\u305b\u3093
+memoryUserDatabase.writeException={0} \u306b\u66f8\u304d\u8fbc\u307f\u4e2d\u306eIOException\u3067\u3059
diff --git a/container/catalina/src/share/org/apache/catalina/users/MemoryGroup.java b/container/catalina/src/share/org/apache/catalina/users/MemoryGroup.java
new file mode 100644
index 0000000..80ad2d4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/MemoryGroup.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.users;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.catalina.Role;
+import org.apache.catalina.UserDatabase;
+
+
+/**
+ * <p>Concrete implementation of {@link org.apache.catalina.Group} for the
+ * {@link MemoryUserDatabase} implementation of {@link UserDatabase}.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class MemoryGroup extends AbstractGroup {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Package-private constructor used by the factory method in
+     * {@link MemoryUserDatabase}.
+     *
+     * @param database The {@link MemoryUserDatabase} that owns this group
+     * @param groupname Group name of this group
+     * @param description Description of this group
+     */
+    MemoryGroup(MemoryUserDatabase database,
+                String groupname, String description) {
+
+        super();
+        this.database = database;
+        setGroupname(groupname);
+        setDescription(description);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The {@link MemoryUserDatabase} that owns this group.
+     */
+    protected MemoryUserDatabase database = null;
+
+
+    /**
+     * The set of {@link Role}s associated with this group.
+     */
+    protected ArrayList roles = new ArrayList();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the set of {@link Role}s assigned specifically to this group.
+     */
+    public Iterator getRoles() {
+
+        synchronized (roles) {
+            return (roles.iterator());
+        }
+
+    }
+
+
+    /**
+     * Return the {@link UserDatabase} within which this Group is defined.
+     */
+    public UserDatabase getUserDatabase() {
+
+        return (this.database);
+
+    }
+
+
+    /**
+     * Return the set of {@link org.apache.catalina.User}s that are members of this group.
+     */
+    public Iterator getUsers() {
+
+        ArrayList results = new ArrayList();
+        Iterator users = database.getUsers();
+        while (users.hasNext()) {
+            MemoryUser user = (MemoryUser) users.next();
+            if (user.isInGroup(this)) {
+                results.add(user);
+            }
+        }
+        return (results.iterator());
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new {@link Role} to those assigned specifically to this group.
+     *
+     * @param role The new role
+     */
+    public void addRole(Role role) {
+
+        synchronized (roles) {
+            if (!roles.contains(role)) {
+                roles.add(role);
+            }
+        }
+
+    }
+
+
+    /**
+     * Is this group specifically assigned the specified {@link Role}?
+     *
+     * @param role The role to check
+     */
+    public boolean isInRole(Role role) {
+
+        synchronized (roles) {
+            return (roles.contains(role));
+        }
+
+    }
+
+
+    /**
+     * Remove a {@link Role} from those assigned to this group.
+     *
+     * @param role The old role
+     */
+    public void removeRole(Role role) {
+
+        synchronized (roles) {
+            roles.remove(role);
+        }
+
+    }
+
+
+    /**
+     * Remove all {@link Role}s from those assigned to this group.
+     */
+    public void removeRoles() {
+
+        synchronized (roles) {
+            roles.clear();
+        }
+
+    }
+
+
+    /**
+     * <p>Return a String representation of this group in XML format.</p>
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("<group groupname=\"");
+        sb.append(groupname);
+        sb.append("\"");
+        if (description != null) {
+            sb.append(" description=\"");
+            sb.append(description);
+            sb.append("\"");
+        }
+        synchronized (roles) {
+            if (roles.size() > 0) {
+                sb.append(" roles=\"");
+                int n = 0;
+                Iterator values = roles.iterator();
+                while (values.hasNext()) {
+                    if (n > 0) {
+                        sb.append(',');
+                    }
+                    n++;
+                    sb.append((String) ((Role) values.next()).getRolename());
+                }
+                sb.append("\"");
+            }
+        }
+        sb.append("/>");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/users/MemoryRole.java b/container/catalina/src/share/org/apache/catalina/users/MemoryRole.java
new file mode 100644
index 0000000..af02422
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/MemoryRole.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.users;
+
+
+import org.apache.catalina.UserDatabase;
+
+
+/**
+ * <p>Concrete implementation of {@link org.apache.catalina.Role} for the
+ * {@link MemoryUserDatabase} implementation of {@link UserDatabase}.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class MemoryRole extends AbstractRole {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Package-private constructor used by the factory method in
+     * {@link MemoryUserDatabase}.
+     *
+     * @param database The {@link MemoryUserDatabase} that owns this role
+     * @param rolename Role name of this role
+     * @param description Description of this role
+     */
+    MemoryRole(MemoryUserDatabase database,
+               String rolename, String description) {
+
+        super();
+        this.database = database;
+        setRolename(rolename);
+        setDescription(description);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The {@link MemoryUserDatabase} that owns this role.
+     */
+    protected MemoryUserDatabase database = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the {@link UserDatabase} within which this role is defined.
+     */
+    public UserDatabase getUserDatabase() {
+
+        return (this.database);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Return a String representation of this role in XML format.</p>
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("<role rolename=\"");
+        sb.append(rolename);
+        sb.append("\"");
+        if (description != null) {
+            sb.append(" description=\"");
+            sb.append(description);
+            sb.append("\"");
+        }
+        sb.append("/>");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/users/MemoryUser.java b/container/catalina/src/share/org/apache/catalina/users/MemoryUser.java
new file mode 100644
index 0000000..32b2f17
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/MemoryUser.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.users;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.catalina.Group;
+import org.apache.catalina.Role;
+import org.apache.catalina.UserDatabase;
+import org.apache.catalina.util.RequestUtil;
+
+/**
+ * <p>Concrete implementation of {@link org.apache.catalina.User} for the
+ * {@link MemoryUserDatabase} implementation of {@link UserDatabase}.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class MemoryUser extends AbstractUser {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Package-private constructor used by the factory method in
+     * {@link MemoryUserDatabase}.
+     *
+     * @param database The {@link MemoryUserDatabase} that owns this user
+     * @param username Logon username of the new user
+     * @param password Logon password of the new user
+     * @param fullName Full name of the new user
+     */
+    MemoryUser(MemoryUserDatabase database, String username,
+               String password, String fullName) {
+
+        super();
+        this.database = database;
+        setUsername(username);
+        setPassword(password);
+        setFullName(fullName);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The {@link MemoryUserDatabase} that owns this user.
+     */
+    protected MemoryUserDatabase database = null;
+
+
+    /**
+     * The set of {@link Group}s that this user is a member of.
+     */
+    protected ArrayList groups = new ArrayList();
+
+
+    /**
+     * The set of {@link Role}s associated with this user.
+     */
+    protected ArrayList roles = new ArrayList();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the set of {@link Group}s to which this user belongs.
+     */
+    public Iterator getGroups() {
+
+        synchronized (groups) {
+            return (groups.iterator());
+        }
+
+    }
+
+
+    /**
+     * Return the set of {@link Role}s assigned specifically to this user.
+     */
+    public Iterator getRoles() {
+
+        synchronized (roles) {
+            return (roles.iterator());
+        }
+
+    }
+
+
+    /**
+     * Return the {@link UserDatabase} within which this User is defined.
+     */
+    public UserDatabase getUserDatabase() {
+
+        return (this.database);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new {@link Group} to those this user belongs to.
+     *
+     * @param group The new group
+     */
+    public void addGroup(Group group) {
+
+        synchronized (groups) {
+            if (!groups.contains(group)) {
+                groups.add(group);
+            }
+        }
+
+    }
+
+
+    /**
+     * Add a new {@link Role} to those assigned specifically to this user.
+     *
+     * @param role The new role
+     */
+    public void addRole(Role role) {
+
+        synchronized (roles) {
+            if (!roles.contains(role)) {
+                roles.add(role);
+            }
+        }
+
+    }
+
+
+    /**
+     * Is this user in the specified group?
+     *
+     * @param group The group to check
+     */
+    public boolean isInGroup(Group group) {
+
+        synchronized (groups) {
+            return (groups.contains(group));
+        }
+
+    }
+
+
+    /**
+     * Is this user specifically assigned the specified {@link Role}?  This
+     * method does <strong>NOT</strong> check for roles inherited based on
+     * {@link Group} membership.
+     *
+     * @param role The role to check
+     */
+    public boolean isInRole(Role role) {
+
+        synchronized (roles) {
+            return (roles.contains(role));
+        }
+
+    }
+
+
+    /**
+     * Remove a {@link Group} from those this user belongs to.
+     *
+     * @param group The old group
+     */
+    public void removeGroup(Group group) {
+
+        synchronized (groups) {
+            groups.remove(group);
+        }
+
+    }
+
+
+    /**
+     * Remove all {@link Group}s from those this user belongs to.
+     */
+    public void removeGroups() {
+
+        synchronized (groups) {
+            groups.clear();
+        }
+
+    }
+
+
+    /**
+     * Remove a {@link Role} from those assigned to this user.
+     *
+     * @param role The old role
+     */
+    public void removeRole(Role role) {
+
+        synchronized (roles) {
+            roles.remove(role);
+        }
+
+    }
+
+
+    /**
+     * Remove all {@link Role}s from those assigned to this user.
+     */
+    public void removeRoles() {
+
+        synchronized (roles) {
+            roles.clear();
+        }
+
+    }
+
+
+    /**
+     * <p>Return a String representation of this user in XML format.</p>
+     *
+     * <p><strong>IMPLEMENTATION NOTE</strong> - For backwards compatibility,
+     * the reader that processes this entry will accept either
+     * <code>username</code> or </code>name</code> for the username
+     * property.</p>
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("<user username=\"");
+        sb.append(RequestUtil.filter(username));
+        sb.append("\" password=\"");
+        sb.append(RequestUtil.filter(password));
+        sb.append("\"");
+        if (fullName != null) {
+            sb.append(" fullName=\"");
+            sb.append(RequestUtil.filter(fullName));
+            sb.append("\"");
+        }
+        synchronized (groups) {
+            if (groups.size() > 0) {
+                sb.append(" groups=\"");
+                int n = 0;
+                Iterator values = groups.iterator();
+                while (values.hasNext()) {
+                    if (n > 0) {
+                        sb.append(',');
+                    }
+                    n++;
+                    sb.append(RequestUtil.filter(((Group) values.next()).getGroupname()));
+                }
+                sb.append("\"");
+            }
+        }
+        synchronized (roles) {
+            if (roles.size() > 0) {
+                sb.append(" roles=\"");
+                int n = 0;
+                Iterator values = roles.iterator();
+                while (values.hasNext()) {
+                    if (n > 0) {
+                        sb.append(',');
+                    }
+                    n++;
+                    sb.append(RequestUtil.filter(((Role) values.next()).getRolename()));
+                }
+                sb.append("\"");
+            }
+        }
+        sb.append("/>");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/users/MemoryUserDatabase.java b/container/catalina/src/share/org/apache/catalina/users/MemoryUserDatabase.java
new file mode 100644
index 0000000..51d574f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/MemoryUserDatabase.java
@@ -0,0 +1,803 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.users;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import org.apache.catalina.Group;
+import org.apache.catalina.Role;
+import org.apache.catalina.User;
+import org.apache.catalina.UserDatabase;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.ObjectCreationFactory;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Concrete implementation of {@link UserDatabase} that loads all
+ * defined users, groups, and roles into an in-memory data structure,
+ * and uses a specified XML file for its persistent storage.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class MemoryUserDatabase implements UserDatabase {
+
+
+    private static Log log = LogFactory.getLog(MemoryUserDatabase.class);
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new instance with default values.
+     */
+    public MemoryUserDatabase() {
+
+        super();
+
+    }
+
+
+    /**
+     * Create a new instance with the specified values.
+     *
+     * @param id Unique global identifier of this user database
+     */
+    public MemoryUserDatabase(String id) {
+
+        super();
+        this.id = id;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of {@link Group}s defined in this database, keyed by
+     * group name.
+     */
+    protected HashMap groups = new HashMap();
+
+
+    /**
+     * The unique global identifier of this user database.
+     */
+    protected String id = null;
+
+
+    /**
+     * The relative (to <code>catalina.base</code>) or absolute pathname to
+     * the XML file in which we will save our persistent information.
+     */
+    protected String pathname = "conf/tomcat-users.xml";
+
+
+    /**
+     * The relative or absolute pathname to the file in which our old
+     * information is stored while renaming is in progress.
+     */
+    protected String pathnameOld = pathname + ".old";
+
+
+    /**
+     * The relative or absolute pathname ot the file in which we write
+     * our new information prior to renaming.
+     */
+    protected String pathnameNew = pathname + ".new";
+
+
+    /**
+     * A flag, indicating if the user database is read only.
+     */
+    protected boolean readonly = false;
+
+    /**
+     * The set of {@link Role}s defined in this database, keyed by
+     * role name.
+     */
+    protected HashMap roles = new HashMap();
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The set of {@link User}s defined in this database, keyed by
+     * user name.
+     */
+    protected HashMap users = new HashMap();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the set of {@link Group}s defined in this user database.
+     */
+    public Iterator getGroups() {
+
+        synchronized (groups) {
+            return (groups.values().iterator());
+        }
+
+    }
+
+
+    /**
+     * Return the unique global identifier of this user database.
+     */
+    public String getId() {
+
+        return (this.id);
+
+    }
+
+
+    /**
+     * Return the relative or absolute pathname to the persistent storage file.
+     */
+    public String getPathname() {
+
+        return (this.pathname);
+
+    }
+
+
+    /**
+     * Set the relative or absolute pathname to the persistent storage file.
+     *
+     * @param pathname The new pathname
+     */
+    public void setPathname(String pathname) {
+
+        this.pathname = pathname;
+        this.pathnameOld = pathname + ".old";
+        this.pathnameNew = pathname + ".new";
+
+    }
+
+
+    /**
+     * Returning the readonly status of the user database
+     */
+    public boolean getReadonly() {
+
+        return (this.readonly);
+
+    }
+
+
+    /**
+     * Setting the readonly status of the user database
+     *
+     * @param pathname The new pathname
+     */
+    public void setReadonly(boolean readonly) {
+
+        this.readonly = readonly;
+
+    }
+
+
+    /**
+     * Return the set of {@link Role}s defined in this user database.
+     */
+    public Iterator getRoles() {
+
+        synchronized (roles) {
+            return (roles.values().iterator());
+        }
+
+    }
+
+
+    /**
+     * Return the set of {@link User}s defined in this user database.
+     */
+    public Iterator getUsers() {
+
+        synchronized (users) {
+            return (users.values().iterator());
+        }
+
+    }
+
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Finalize access to this user database.
+     *
+     * @exception Exception if any exception is thrown during closing
+     */
+    public void close() throws Exception {
+
+        save();
+
+        synchronized (groups) {
+            synchronized (users) {
+                users.clear();
+                groups.clear();
+            }
+        }
+
+    }
+
+
+    /**
+     * Create and return a new {@link Group} defined in this user database.
+     *
+     * @param groupname The group name of the new group (must be unique)
+     * @param description The description of this group
+     */
+    public Group createGroup(String groupname, String description) {
+
+        MemoryGroup group = new MemoryGroup(this, groupname, description);
+        synchronized (groups) {
+            groups.put(group.getGroupname(), group);
+        }
+        return (group);
+
+    }
+
+
+    /**
+     * Create and return a new {@link Role} defined in this user database.
+     *
+     * @param rolename The role name of the new group (must be unique)
+     * @param description The description of this group
+     */
+    public Role createRole(String rolename, String description) {
+
+        MemoryRole role = new MemoryRole(this, rolename, description);
+        synchronized (roles) {
+            roles.put(role.getRolename(), role);
+        }
+        return (role);
+
+    }
+
+
+    /**
+     * Create and return a new {@link User} defined in this user database.
+     *
+     * @param username The logon username of the new user (must be unique)
+     * @param password The logon password of the new user
+     * @param fullName The full name of the new user
+     */
+    public User createUser(String username, String password,
+                           String fullName) {
+
+        MemoryUser user = new MemoryUser(this, username, password, fullName);
+        synchronized (users) {
+            users.put(user.getUsername(), user);
+        }
+        return (user);
+
+    }
+
+
+    /**
+     * Return the {@link Group} with the specified group name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param groupname Name of the group to return
+     */
+    public Group findGroup(String groupname) {
+
+        synchronized (groups) {
+            return ((Group) groups.get(groupname));
+        }
+
+    }
+
+
+    /**
+     * Return the {@link Role} with the specified role name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param rolename Name of the role to return
+     */
+    public Role findRole(String rolename) {
+
+        synchronized (roles) {
+            return ((Role) roles.get(rolename));
+        }
+
+    }
+
+
+    /**
+     * Return the {@link User} with the specified user name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param username Name of the user to return
+     */
+    public User findUser(String username) {
+
+        synchronized (users) {
+            return ((User) users.get(username));
+        }
+
+    }
+
+
+    /**
+     * Initialize access to this user database.
+     *
+     * @exception Exception if any exception is thrown during opening
+     */
+    public void open() throws Exception {
+
+        synchronized (groups) {
+            synchronized (users) {
+
+                // Erase any previous groups and users
+                users.clear();
+                groups.clear();
+                roles.clear();
+
+                // Construct a reader for the XML input file (if it exists)
+                File file = new File(pathname);
+                if (!file.isAbsolute()) {
+                    file = new File(System.getProperty("catalina.base"),
+                                    pathname);
+                }
+                if (!file.exists()) {
+                    return;
+                }
+                FileInputStream fis = new FileInputStream(file);
+
+                // Construct a digester to read the XML input file
+                Digester digester = new Digester();
+                digester.addFactoryCreate
+                    ("tomcat-users/group",
+                     new MemoryGroupCreationFactory(this));
+                digester.addFactoryCreate
+                    ("tomcat-users/role",
+                     new MemoryRoleCreationFactory(this));
+                digester.addFactoryCreate
+                    ("tomcat-users/user",
+                     new MemoryUserCreationFactory(this));
+
+                // Parse the XML input file to load this database
+                try {
+                    digester.parse(fis);
+                    fis.close();
+                } catch (Exception e) {
+                    try {
+                        fis.close();
+                    } catch (Throwable t) {
+                        ;
+                    }
+                    throw e;
+                }
+
+            }
+        }
+
+    }
+
+
+    /**
+     * Remove the specified {@link Group} from this user database.
+     *
+     * @param group The group to be removed
+     */
+    public void removeGroup(Group group) {
+
+        synchronized (groups) {
+            Iterator users = getUsers();
+            while (users.hasNext()) {
+                User user = (User) users.next();
+                user.removeGroup(group);
+            }
+            groups.remove(group.getGroupname());
+        }
+
+    }
+
+
+    /**
+     * Remove the specified {@link Role} from this user database.
+     *
+     * @param role The role to be removed
+     */
+    public void removeRole(Role role) {
+
+        synchronized (roles) {
+            Iterator groups = getGroups();
+            while (groups.hasNext()) {
+                Group group = (Group) groups.next();
+                group.removeRole(role);
+            }
+            Iterator users = getUsers();
+            while (users.hasNext()) {
+                User user = (User) users.next();
+                user.removeRole(role);
+            }
+            roles.remove(role.getRolename());
+        }
+
+    }
+
+
+    /**
+     * Remove the specified {@link User} from this user database.
+     *
+     * @param user The user to be removed
+     */
+    public void removeUser(User user) {
+
+        synchronized (users) {
+            users.remove(user.getUsername());
+        }
+
+    }
+
+
+    /**
+     * Check for permissions to save this user database
+     * to persistent storage location
+     *
+     */
+    public boolean isWriteable() {
+
+        File file = new File(pathname);
+        if (!file.isAbsolute()) {
+            file = new File(System.getProperty("catalina.base"),
+                            pathname);
+        }
+        File dir = file.getParentFile();
+        return dir.exists() && dir.isDirectory() && dir.canWrite();
+
+    }
+
+
+    /**
+     * Save any updated information to the persistent storage location for
+     * this user database.
+     *
+     * @exception Exception if any exception is thrown during saving
+     */
+    public void save() throws Exception {
+
+        if (getReadonly()) {
+            return;
+        }
+
+        if (!isWriteable()) {
+            log.warn(sm.getString("memoryUserDatabase.notPersistable"));
+            return;
+        }
+
+        // Write out contents to a temporary file
+        File fileNew = new File(pathnameNew);
+        if (!fileNew.isAbsolute()) {
+            fileNew =
+                new File(System.getProperty("catalina.base"), pathnameNew);
+        }
+        PrintWriter writer = null;
+        try {
+
+            // Configure our PrintWriter
+            FileOutputStream fos = new FileOutputStream(fileNew);
+            OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
+            writer = new PrintWriter(osw);
+
+            // Print the file prolog
+            writer.println("<?xml version='1.0' encoding='utf-8'?>");
+            writer.println("<tomcat-users>");
+
+            // Print entries for each defined role, group, and user
+            Iterator values = null;
+            values = getRoles();
+            while (values.hasNext()) {
+                writer.print("  ");
+                writer.println(values.next());
+            }
+            values = getGroups();
+            while (values.hasNext()) {
+                writer.print("  ");
+                writer.println(values.next());
+            }
+            values = getUsers();
+            while (values.hasNext()) {
+                writer.print("  ");
+                writer.println(values.next());
+            }
+
+            // Print the file epilog
+            writer.println("</tomcat-users>");
+
+            // Check for errors that occurred while printing
+            if (writer.checkError()) {
+                writer.close();
+                fileNew.delete();
+                throw new IOException
+                    (sm.getString("memoryUserDatabase.writeException",
+                                  fileNew.getAbsolutePath()));
+            }
+            writer.close();
+        } catch (IOException e) {
+            if (writer != null) {
+                writer.close();
+            }
+            fileNew.delete();
+            throw e;
+        }
+
+        // Perform the required renames to permanently save this file
+        File fileOld = new File(pathnameOld);
+        if (!fileOld.isAbsolute()) {
+            fileOld =
+                new File(System.getProperty("catalina.base"), pathnameOld);
+        }
+        fileOld.delete();
+        File fileOrig = new File(pathname);
+        if (!fileOrig.isAbsolute()) {
+            fileOrig =
+                new File(System.getProperty("catalina.base"), pathname);
+        }
+        if (fileOrig.exists()) {
+            fileOld.delete();
+            if (!fileOrig.renameTo(fileOld)) {
+                throw new IOException
+                    (sm.getString("memoryUserDatabase.renameOld",
+                                  fileOld.getAbsolutePath()));
+            }
+        }
+        if (!fileNew.renameTo(fileOrig)) {
+            if (fileOld.exists()) {
+                fileOld.renameTo(fileOrig);
+            }
+            throw new IOException
+                (sm.getString("memoryUserDatabase.renameNew",
+                              fileOrig.getAbsolutePath()));
+        }
+        fileOld.delete();
+
+    }
+
+
+    /**
+     * Return a String representation of this UserDatabase.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("MemoryUserDatabase[id=");
+        sb.append(this.id);
+        sb.append(",pathname=");
+        sb.append(pathname);
+        sb.append(",groupCount=");
+        sb.append(this.groups.size());
+        sb.append(",roleCount=");
+        sb.append(this.roles.size());
+        sb.append(",userCount=");
+        sb.append(this.users.size());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Return the <code>StringManager</code> for use in looking up messages.
+     */
+    StringManager getStringManager() {
+
+        return (sm);
+
+    }
+
+
+}
+
+
+
+/**
+ * Digester object creation factory for group instances.
+ */
+class MemoryGroupCreationFactory implements ObjectCreationFactory {
+
+    public MemoryGroupCreationFactory(MemoryUserDatabase database) {
+        this.database = database;
+    }
+
+    public Object createObject(Attributes attributes) {
+        String groupname = attributes.getValue("groupname");
+        if (groupname == null) {
+            groupname = attributes.getValue("name");
+        }
+        String description = attributes.getValue("description");
+        String roles = attributes.getValue("roles");
+        Group group = database.createGroup(groupname, description);
+        if (roles != null) {
+            while (roles.length() > 0) {
+                String rolename = null;
+                int comma = roles.indexOf(',');
+                if (comma >= 0) {
+                    rolename = roles.substring(0, comma).trim();
+                    roles = roles.substring(comma + 1);
+                } else {
+                    rolename = roles.trim();
+                    roles = "";
+                }
+                if (rolename.length() > 0) {
+                    Role role = database.findRole(rolename);
+                    if (role == null) {
+                        role = database.createRole(rolename, null);
+                    }
+                    group.addRole(role);
+                }
+            }
+        }
+        return (group);
+    }
+
+    private MemoryUserDatabase database = null;
+
+    private Digester digester = null;
+
+    public Digester getDigester() {
+        return (this.digester);
+    }
+
+    public void setDigester(Digester digester) {
+        this.digester = digester;
+    }
+
+}
+
+
+/**
+ * Digester object creation factory for role instances.
+ */
+class MemoryRoleCreationFactory implements ObjectCreationFactory {
+
+    public MemoryRoleCreationFactory(MemoryUserDatabase database) {
+        this.database = database;
+    }
+
+    public Object createObject(Attributes attributes) {
+        String rolename = attributes.getValue("rolename");
+        if (rolename == null) {
+            rolename = attributes.getValue("name");
+        }
+        String description = attributes.getValue("description");
+        Role role = database.createRole(rolename, description);
+        return (role);
+    }
+
+    private MemoryUserDatabase database = null;
+
+    private Digester digester = null;
+
+    public Digester getDigester() {
+        return (this.digester);
+    }
+
+    public void setDigester(Digester digester) {
+        this.digester = digester;
+    }
+
+}
+
+
+/**
+ * Digester object creation factory for user instances.
+ */
+class MemoryUserCreationFactory implements ObjectCreationFactory {
+
+    public MemoryUserCreationFactory(MemoryUserDatabase database) {
+        this.database = database;
+    }
+
+    public Object createObject(Attributes attributes) {
+        String username = attributes.getValue("username");
+        if (username == null) {
+            username = attributes.getValue("name");
+        }
+        String password = attributes.getValue("password");
+        String fullName = attributes.getValue("fullName");
+        if (fullName == null) {
+            fullName = attributes.getValue("fullname");
+        }
+        String groups = attributes.getValue("groups");
+        String roles = attributes.getValue("roles");
+        User user = database.createUser(username, password, fullName);
+        if (groups != null) {
+            while (groups.length() > 0) {
+                String groupname = null;
+                int comma = groups.indexOf(',');
+                if (comma >= 0) {
+                    groupname = groups.substring(0, comma).trim();
+                    groups = groups.substring(comma + 1);
+                } else {
+                    groupname = groups.trim();
+                    groups = "";
+                }
+                if (groupname.length() > 0) {
+                    Group group = database.findGroup(groupname);
+                    if (group == null) {
+                        group = database.createGroup(groupname, null);
+                    }
+                    user.addGroup(group);
+                }
+            }
+        }
+        if (roles != null) {
+            while (roles.length() > 0) {
+                String rolename = null;
+                int comma = roles.indexOf(',');
+                if (comma >= 0) {
+                    rolename = roles.substring(0, comma).trim();
+                    roles = roles.substring(comma + 1);
+                } else {
+                    rolename = roles.trim();
+                    roles = "";
+                }
+                if (rolename.length() > 0) {
+                    Role role = database.findRole(rolename);
+                    if (role == null) {
+                        role = database.createRole(rolename, null);
+                    }
+                    user.addRole(role);
+                }
+            }
+        }
+        return (user);
+    }
+
+    private MemoryUserDatabase database = null;
+
+    private Digester digester = null;
+
+    public Digester getDigester() {
+        return (this.digester);
+    }
+
+    public void setDigester(Digester digester) {
+        this.digester = digester;
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/users/MemoryUserDatabaseFactory.java b/container/catalina/src/share/org/apache/catalina/users/MemoryUserDatabaseFactory.java
new file mode 100644
index 0000000..3f974bf
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/MemoryUserDatabaseFactory.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.users;
+
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+
+
+/**
+ * <p>JNDI object creation factory for <code>MemoryUserDatabase</code>
+ * instances.  This makes it convenient to configure a user database
+ * in the global JNDI resources associated with this Catalina instance,
+ * and then link to that resource for web applications that administer
+ * the contents of the user database.</p>
+ *
+ * <p>The <code>MemoryUserDatabase</code> instance is configured based
+ * on the following parameter values:</p>
+ * <ul>
+ * <li><strong>pathname</strong> - Absolute or relative (to the directory
+ *     path specified by the <code>catalina.base</code> system property)
+ *     pathname to the XML file from which our user information is loaded,
+ *     and to which it is stored.  [conf/tomcat-users.xml]</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class MemoryUserDatabaseFactory implements ObjectFactory {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Create and return a new <code>MemoryUserDatabase</code> instance
+     * that has been configured according to the properties of the
+     * specified <code>Reference</code>.  If you instance can be created,
+     * return <code>null</code> instead.</p>
+     *
+     * @param obj The possibly null object containing location or
+     *  reference information that can be used in creating an object
+     * @param name The name of this object relative to <code>nameCtx</code>
+     * @param nameCtx The context relative to which the <code>name</code>
+     *  parameter is specified, or <code>null</code> if <code>name</code>
+     *  is relative to the default initial context
+     * @param environment The possibly null environment that is used in
+     *  creating this object
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws Exception {
+
+        // We only know how to deal with <code>javax.naming.Reference</code>s
+        // that specify a class name of "org.apache.catalina.UserDatabase"
+        if ((obj == null) || !(obj instanceof Reference)) {
+            return (null);
+        }
+        Reference ref = (Reference) obj;
+        if (!"org.apache.catalina.UserDatabase".equals(ref.getClassName())) {
+            return (null);
+        }
+
+        // Create and configure a MemoryUserDatabase instance based on the
+        // RefAddr values associated with this Reference
+        MemoryUserDatabase database = new MemoryUserDatabase(name.toString());
+        RefAddr ra = null;
+
+        ra = ref.get("pathname");
+        if (ra != null) {
+            database.setPathname(ra.getContent().toString());
+        }
+
+        ra = ref.get("readonly");
+        if (ra != null) {
+            database.setReadonly(Boolean.valueOf(ra.getContent().toString()).booleanValue());
+        }
+
+        // Return the configured database instance
+        database.open();
+        database.save();
+        return (database);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/users/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/users/mbeans-descriptors.xml
new file mode 100644
index 0000000..47b478c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/users/mbeans-descriptors.xml
@@ -0,0 +1,280 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean         name="JDBCUserDatabase"
+            className="org.apache.catalina.mbeans.JDBCUserDatabaseMBean"
+          description="JDBC based user and group database"
+               domain="Users"
+                group="UserDatabase"
+                 type="org.apache.catalina.users.JDBCUserDatabase">
+
+    <attribute   name="groups"
+          description="MBean Names of all defined groups"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="roles"
+          description="MBean Names of all defined roles"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="users"
+          description="MBean Names of all defined users"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <operation   name="createGroup"
+          description="Create new group and return MBean name"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="groupname"
+          description="Group name of the new group"
+                 type="java.lang.String"/>
+      <parameter name="description"
+          description="Description of the new group"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createRole"
+          description="Create new role and return MBean name"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="rolename"
+          description="Role name of the new role"
+                 type="java.lang.String"/>
+      <parameter name="description"
+          description="Description of the new role"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createUser"
+          description="Create new user and return MBean name"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="username"
+          description="User name of the new user"
+                 type="java.lang.String"/>
+      <parameter name="password"
+          description="Password of the new user"
+                 type="java.lang.String"/>
+      <parameter name="fullName"
+          description="Full name of the new user"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="findGroup"
+          description="Return MBean Name of the specified group (if any)"
+               impact="INFO"
+           returnType="java.lang.String">
+      <parameter name="groupname"
+          description="Group name of the requested group"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="findRole"
+          description="Return MBean Name of the specified role (if any)"
+               impact="INFO"
+           returnType="java.lang.String">
+      <parameter name="rolename"
+          description="Role name of the requested role"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="findUser"
+          description="Return MBean Name of the specified user (if any)"
+               impact="INFO"
+           returnType="java.lang.String">
+      <parameter name="username"
+          description="User name of the requested user"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeGroup"
+          description="Remove existing group (and all user memberships)"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="groupname"
+          description="Group name of the group to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRole"
+          description="Remove existing role"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="rolename"
+          description="Role name of the role to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeUser"
+          description="Remove existing user (and all group memberships)"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="username"
+          description="User name of the user to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="save"
+          description="Save current users and groups to persistent storage"
+               impact="ACTION"
+           returnType="void">
+    </operation>
+
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+
+  <mbean         name="MemoryUserDatabase"
+            className="org.apache.catalina.mbeans.MemoryUserDatabaseMBean"
+          description="In-memory user and group database"
+               domain="Users"
+                group="UserDatabase"
+                 type="org.apache.catalina.users.MemoryUserDatabase">
+
+    <attribute   name="encoding"
+          description="Character encoding to use when writing XML file"
+                 type="java.lang.String"/>
+
+    <attribute   name="groups"
+          description="MBean Names of all defined groups"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="pathname"
+          description="Relative or absolute pathname to database file"
+                 type="java.lang.String"/>
+
+    <attribute   name="roles"
+          description="MBean Names of all defined roles"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="users"
+          description="MBean Names of all defined users"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="readonly"
+          description="No persistant save of the user database"
+                 type="boolean"
+            writeable="false"/>
+
+    <attribute   name="writeable"
+          description="Check if user database is writeable"
+               impact="INFO"
+                   is="true"
+           writeable="false"/>
+
+    <operation   name="createGroup"
+          description="Create new group and return MBean name"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="groupname"
+          description="Group name of the new group"
+                 type="java.lang.String"/>
+      <parameter name="description"
+          description="Description of the new group"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createRole"
+          description="Create new role and return MBean name"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="rolename"
+          description="Role name of the new role"
+                 type="java.lang.String"/>
+      <parameter name="description"
+          description="Description of the new role"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createUser"
+          description="Create new user and return MBean name"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="username"
+          description="User name of the new user"
+                 type="java.lang.String"/>
+      <parameter name="password"
+          description="Password of the new user"
+                 type="java.lang.String"/>
+      <parameter name="fullName"
+          description="Full name of the new user"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="findGroup"
+          description="Return MBean Name of the specified group (if any)"
+               impact="INFO"
+           returnType="java.lang.String">
+      <parameter name="groupname"
+          description="Group name of the requested group"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="findRole"
+          description="Return MBean Name of the specified role (if any)"
+               impact="INFO"
+           returnType="java.lang.String">
+      <parameter name="rolename"
+          description="Role name of the requested role"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="findUser"
+          description="Return MBean Name of the specified user (if any)"
+               impact="INFO"
+           returnType="java.lang.String">
+      <parameter name="username"
+          description="User name of the requested user"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeGroup"
+          description="Remove existing group (and all user memberships)"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="groupname"
+          description="Group name of the group to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRole"
+          description="Remove existing role"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="rolename"
+          description="Role name of the role to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeUser"
+          description="Remove existing user (and all group memberships)"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="username"
+          description="User name of the user to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="save"
+          description="Save current users and groups to persistent storage"
+               impact="ACTION"
+           returnType="void">
+    </operation>
+
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/util/Base64.java b/container/catalina/src/share/org/apache/catalina/util/Base64.java
new file mode 100644
index 0000000..0c1bf60
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/Base64.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+
+/**
+ * This class provides encode/decode for RFC 2045 Base64 as defined by
+ * RFC 2045, N. Freed and N. Borenstein.  <a
+ * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>:
+ * Multipurpose Internet Mail Extensions (MIME) Part One: Format of
+ * Internet Message Bodies. Reference 1996
+ *
+ * @author Jeffrey Rodriguez
+ * @version $Id$
+ */
+public final class  Base64
+{
+    static private final int  BASELENGTH         = 255;
+    static private final int  LOOKUPLENGTH       = 64;
+    static private final int  TWENTYFOURBITGROUP = 24;
+    static private final int  EIGHTBIT           = 8;
+    static private final int  SIXTEENBIT         = 16;
+    static private final int  SIXBIT             = 6;
+    static private final int  FOURBYTE           = 4;
+    static private final int  SIGN               = -128;
+    static private final byte PAD                = (byte) '=';
+    static private byte [] base64Alphabet       = new byte[BASELENGTH];
+    static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
+    //static private final Log log = LogSource.getInstance("org.apache.commons.util.Base64");
+
+    static
+    {
+        for (int i = 0; i < BASELENGTH; i++ )
+        {
+            base64Alphabet[i] = -1;
+        }
+        for (int i = 'Z'; i >= 'A'; i--)
+        {
+            base64Alphabet[i] = (byte) (i - 'A');
+        }
+        for (int i = 'z'; i>= 'a'; i--)
+        {
+            base64Alphabet[i] = (byte) (i - 'a' + 26);
+        }
+        for (int i = '9'; i >= '0'; i--)
+        {
+            base64Alphabet[i] = (byte) (i - '0' + 52);
+        }
+
+        base64Alphabet['+']  = 62;
+        base64Alphabet['/']  = 63;
+
+        for (int i = 0; i <= 25; i++ )
+            lookUpBase64Alphabet[i] = (byte) ('A' + i);
+
+        for (int i = 26,  j = 0; i <= 51; i++, j++ )
+            lookUpBase64Alphabet[i] = (byte) ('a'+ j);
+
+        for (int i = 52,  j = 0; i <= 61; i++, j++ )
+            lookUpBase64Alphabet[i] = (byte) ('0' + j);
+
+        lookUpBase64Alphabet[62] = (byte) '+';
+        lookUpBase64Alphabet[63] = (byte) '/';
+    }
+
+    public static boolean isBase64( String isValidString )
+    {
+        return isArrayByteBase64(isValidString.getBytes());
+    }
+
+    public static boolean isBase64( byte octect )
+    {
+        //shall we ignore white space? JEFF??
+        return (octect == PAD || base64Alphabet[octect] != -1);
+    }
+
+    public static boolean isArrayByteBase64( byte[] arrayOctect )
+    {
+        int length = arrayOctect.length;
+        if (length == 0)
+        {
+            // shouldn't a 0 length array be valid base64 data?
+            // return false;
+            return true;
+        }
+        for (int i=0; i < length; i++)
+        {
+            if ( !Base64.isBase64(arrayOctect[i]) )
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Encodes hex octects into Base64.
+     *
+     * @param binaryData Array containing binary data to encode.
+     * @return Base64-encoded data.
+     */
+    public static byte[] encode( byte[] binaryData )
+    {
+        int      lengthDataBits    = binaryData.length*EIGHTBIT;
+        int      fewerThan24bits   = lengthDataBits%TWENTYFOURBITGROUP;
+        int      numberTriplets    = lengthDataBits/TWENTYFOURBITGROUP;
+        byte     encodedData[]     = null;
+
+
+        if (fewerThan24bits != 0)
+        {
+            //data not divisible by 24 bit
+            encodedData = new byte[ (numberTriplets + 1 ) * 4 ];
+        }
+        else
+        {
+            // 16 or 8 bit
+            encodedData = new byte[ numberTriplets * 4 ];
+        }
+
+        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+
+        int encodedIndex = 0;
+        int dataIndex   = 0;
+        int i           = 0;
+        //log.debug("number of triplets = " + numberTriplets);
+        for ( i = 0; i<numberTriplets; i++ )
+        {
+            dataIndex = i*3;
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            b3 = binaryData[dataIndex + 2];
+
+            //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
+
+            l  = (byte)(b2 & 0x0f);
+            k  = (byte)(b1 & 0x03);
+
+            encodedIndex = i * 4;
+            byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
+            byte val2 = ((b2 & SIGN)==0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
+            byte val3 = ((b3 & SIGN)==0)?(byte)(b3>>6):(byte)((b3)>>6^0xfc);
+
+            encodedData[encodedIndex]   = lookUpBase64Alphabet[ val1 ];
+            //log.debug( "val2 = " + val2 );
+            //log.debug( "k4   = " + (k<<4) );
+            //log.debug(  "vak  = " + (val2 | (k<<4)) );
+            encodedData[encodedIndex+1] =
+                lookUpBase64Alphabet[ val2 | ( k<<4 )];
+            encodedData[encodedIndex+2] =
+                lookUpBase64Alphabet[ (l <<2 ) | val3 ];
+            encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
+        }
+
+        // form integral number of 6-bit groups
+        dataIndex    = i*3;
+        encodedIndex = i*4;
+        if (fewerThan24bits == EIGHTBIT )
+        {
+            b1 = binaryData[dataIndex];
+            k = (byte) ( b1 &0x03 );
+            //log.debug("b1=" + b1);
+            //log.debug("b1<<2 = " + (b1>>2) );
+            byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ val1 ];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
+            encodedData[encodedIndex + 2] = PAD;
+            encodedData[encodedIndex + 3] = PAD;
+        }
+        else if (fewerThan24bits == SIXTEENBIT)
+        {
+
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex +1 ];
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 = ((b1 & SIGN) == 0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
+            byte val2 = ((b2 & SIGN) == 0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
+
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ val1 ];
+            encodedData[encodedIndex + 1] =
+                lookUpBase64Alphabet[ val2 | ( k<<4 )];
+            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
+            encodedData[encodedIndex + 3] = PAD;
+        }
+
+        return encodedData;
+    }
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param base64DataBC Byte array containing Base64 data
+     * @param decodedDataCC The decoded data chars
+     */
+    public static void decode( ByteChunk base64DataBC, CharChunk decodedDataCC)
+    {
+        int start = base64DataBC.getStart();
+        int end = base64DataBC.getEnd();
+        byte[] base64Data = base64DataBC.getBuffer();
+        
+        decodedDataCC.recycle();
+        
+        // handle the edge case, so we don't have to worry about it later
+        if(end - start == 0) { return; }
+
+        int      numberQuadruple    = (end - start)/FOURBYTE;
+        byte     b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;
+
+        // Throw away anything not in base64Data
+
+        int encodedIndex = 0;
+        int dataIndex = start;
+        char[] decodedData = null;
+        
+        {
+            // this sizes the output array properly - rlw
+            int lastData = end - start;
+            // ignore the '=' padding
+            while (base64Data[start+lastData-1] == PAD)
+            {
+                if (--lastData == 0)
+                {
+                    return;
+                }
+            }
+            decodedDataCC.allocate(lastData - numberQuadruple, -1);
+            decodedDataCC.setEnd(lastData - numberQuadruple);
+            decodedData = decodedDataCC.getBuffer();
+        }
+
+        for (int i = 0; i < numberQuadruple; i++)
+        {
+            dataIndex = start + i * 4;
+            marker0   = base64Data[dataIndex + 2];
+            marker1   = base64Data[dataIndex + 3];
+
+            b1 = base64Alphabet[base64Data[dataIndex]];
+            b2 = base64Alphabet[base64Data[dataIndex +1]];
+
+            if (marker0 != PAD && marker1 != PAD)
+            {
+                //No PAD e.g 3cQl
+                b3 = base64Alphabet[ marker0 ];
+                b4 = base64Alphabet[ marker1 ];
+
+                decodedData[encodedIndex]   = (char) ((  b1 <<2 | b2>>4 ) & 0xff);
+                decodedData[encodedIndex + 1] =
+                    (char) ((((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) ) & 0xff);
+                decodedData[encodedIndex + 2] = (char) (( b3<<6 | b4 ) & 0xff);
+            }
+            else if (marker0 == PAD)
+            {
+                //Two PAD e.g. 3c[Pad][Pad]
+                decodedData[encodedIndex]   = (char) ((  b1 <<2 | b2>>4 ) & 0xff);
+            }
+            else if (marker1 == PAD)
+            {
+                //One PAD e.g. 3cQ[Pad]
+                b3 = base64Alphabet[ marker0 ];
+
+                decodedData[encodedIndex]   = (char) ((  b1 <<2 | b2>>4 ) & 0xff);
+                decodedData[encodedIndex + 1] =
+                    (char) ((((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) ) & 0xff);
+            }
+            encodedIndex += 3;
+        }
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/CGIProcessEnvironment.java b/container/catalina/src/share/org/apache/catalina/util/CGIProcessEnvironment.java
new file mode 100644
index 0000000..6b941e3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/CGIProcessEnvironment.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+import java.io.File;
+import java.net.URLEncoder;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * Encapsulates the CGI Process' environment and rules to derive
+ * that environment from the servlet container and request information.
+ * @author   Martin Dengler [root@martindengler.com]
+ * @version  $Revision$, $Date$
+ * @since    Tomcat 4.0
+ */
+
+public class CGIProcessEnvironment extends ProcessEnvironment {
+    
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( CGIProcessEnvironment.class );
+    
+    /** cgi command's query parameters */
+    private Hashtable queryParameters = null;
+
+    /**
+     *  The CGI search path will start at
+     *    webAppRootDir + File.separator + cgiPathPrefix
+     *    (or webAppRootDir alone if cgiPathPrefix is
+     *    null)
+     */
+    private String cgiPathPrefix = null;
+
+
+    /**
+     * Creates a ProcessEnvironment and derives the necessary environment,
+     * working directory, command, etc.  The cgi path prefix is initialized
+     * to "" (the empty string).
+     *
+     * @param  req       HttpServletRequest for information provided by
+     *                   the Servlet API
+     * @param  context   ServletContext for information provided by
+     *                   the Servlet API
+     */
+    public CGIProcessEnvironment(HttpServletRequest req,
+        ServletContext context) {
+            this(req, context, "");
+    }
+
+
+
+    /**
+     * Creates a ProcessEnvironment and derives the necessary environment,
+     * working directory, command, etc.
+     * @param req             HttpServletRequest for information provided by
+     *                        the Servlet API
+     * @param context         ServletContext for information provided by
+     *                        the Servlet API
+     * @param cgiPathPrefix   subdirectory of webAppRootDir below which the
+     *                        web app's CGIs may be stored; can be null or "".
+     */
+    public CGIProcessEnvironment(HttpServletRequest req,
+        ServletContext context, String cgiPathPrefix) {
+            this(req, context, cgiPathPrefix, 0);
+    }
+
+
+
+    /**
+     * Creates a ProcessEnvironment and derives the necessary environment,
+     * working directory, command, etc.
+     * @param req             HttpServletRequest for information provided by
+     *                        the Servlet API
+     * @param context         ServletContext for information provided by
+     *                        the Servlet API
+     * @param  debug          int debug level (0 == none, 6 == lots)
+     */
+    public CGIProcessEnvironment(HttpServletRequest req,
+        ServletContext context, int debug) {
+            this(req, context, "", 0);
+    }
+
+
+
+
+    /**
+     * Creates a ProcessEnvironment and derives the necessary environment,
+     * working directory, command, etc.
+     * @param req             HttpServletRequest for information provided by
+     *                        the Servlet API
+     * @param context         ServletContext for information provided by
+     *                        the Servlet API
+     * @param cgiPathPrefix   subdirectory of webAppRootDir below which the
+     *                        web app's CGIs may be stored; can be null or "".
+     * @param  debug          int debug level (0 == none, 6 == lots)
+     */
+    public CGIProcessEnvironment(HttpServletRequest req,
+        ServletContext context, String cgiPathPrefix, int debug) {
+            super(req, context, debug);
+            this.cgiPathPrefix = cgiPathPrefix;
+            queryParameters = new Hashtable();
+            Enumeration paramNames = req.getParameterNames();
+            while (paramNames != null && paramNames.hasMoreElements()) {
+                String param = paramNames.nextElement().toString();
+                if (param != null) {
+                    queryParameters.put(param,
+                        URLEncoder.encode(req.getParameter(param)));
+                }
+            }
+            this.valid = deriveProcessEnvironment(req);
+    }
+
+
+
+    /**
+     * Constructs the CGI environment to be supplied to the invoked CGI
+     * script; relies heavliy on Servlet API methods and findCGI
+     * @param    req request associated with the CGI invokation
+     * @return   true if environment was set OK, false if there was a problem
+     *           and no environment was set
+     */
+    protected boolean deriveProcessEnvironment(HttpServletRequest req) {
+        /*
+         * This method is slightly ugly; c'est la vie.
+         * "You cannot stop [ugliness], you can only hope to contain [it]"
+         * (apologies to Marv Albert regarding MJ)
+         */
+
+        Hashtable envp;
+        super.deriveProcessEnvironment(req);
+        envp = getEnvironment();
+
+        String sPathInfoOrig = null;
+        String sPathTranslatedOrig = null;
+        String sPathInfoCGI = null;
+        String sPathTranslatedCGI = null;
+        String sCGIFullPath = null;
+        String sCGIScriptName = null;
+        String sCGIFullName = null;
+        String sCGIName = null;
+        String[] sCGINames;
+        sPathInfoOrig = this.pathInfo;
+        sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig;
+        sPathTranslatedOrig = req.getPathTranslated();
+        sPathTranslatedOrig = sPathTranslatedOrig == null ? "" :
+            sPathTranslatedOrig;
+            sCGINames =
+                findCGI(sPathInfoOrig, getWebAppRootDir(), getContextPath(),
+                getServletPath(), cgiPathPrefix);
+        sCGIFullPath = sCGINames[0];
+        sCGIScriptName = sCGINames[1];
+        sCGIFullName = sCGINames[2];
+        sCGIName = sCGINames[3];
+        if (sCGIFullPath == null || sCGIScriptName == null
+            || sCGIFullName == null || sCGIName == null) {
+                return false;
+        }
+        envp.put("SERVER_SOFTWARE", "TOMCAT");
+        envp.put("SERVER_NAME", nullsToBlanks(req.getServerName()));
+        envp.put("GATEWAY_INTERFACE", "CGI/1.1");
+        envp.put("SERVER_PROTOCOL", nullsToBlanks(req.getProtocol()));
+        int port = req.getServerPort();
+        Integer iPort = (port == 0 ? new Integer(-1) : new Integer(port));
+        envp.put("SERVER_PORT", iPort.toString());
+        envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod()));
+
+        /*-
+        * PATH_INFO should be determined by using sCGIFullName:
+        * 1) Let sCGIFullName not end in a "/" (see method findCGI)
+        * 2) Let sCGIFullName equal the pathInfo fragment which
+        *    corresponds to the actual cgi script.
+        * 3) Thus, PATH_INFO = request.getPathInfo().substring(
+        *                      sCGIFullName.length())
+        *
+        * (see method findCGI, where the real work is done)
+        *
+        */
+
+        if (pathInfo == null ||
+            (pathInfo.substring(sCGIFullName.length()).length() <= 0)) {
+                sPathInfoCGI = "";
+        } else {
+            sPathInfoCGI = pathInfo.substring(sCGIFullName.length());
+        }
+        envp.put("PATH_INFO", sPathInfoCGI);
+
+        /*-
+        * PATH_TRANSLATED must be determined after PATH_INFO (and the
+        * implied real cgi-script) has been taken into account.
+        *
+        * The following example demonstrates:
+        *
+        * servlet info   = /servlet/cgigw/dir1/dir2/cgi1/trans1/trans2
+        * cgifullpath    = /servlet/cgigw/dir1/dir2/cgi1
+        * path_info      = /trans1/trans2
+        * webAppRootDir  = servletContext.getRealPath("/")
+        *
+        * path_translated = servletContext.getRealPath("/trans1/trans2")
+        *
+        * That is, PATH_TRANSLATED = webAppRootDir + sPathInfoCGI
+        * (unless sPathInfoCGI is null or blank, then the CGI
+        * specification dictates that the PATH_TRANSLATED metavariable
+        * SHOULD NOT be defined.
+        *
+        */
+
+        if (sPathInfoCGI != null && !("".equals(sPathInfoCGI))) {
+            sPathTranslatedCGI = getContext().getRealPath(sPathInfoCGI);
+        } else {
+            sPathTranslatedCGI = null;
+        }
+        if (sPathTranslatedCGI == null || "".equals(sPathTranslatedCGI)) {
+            //NOOP
+        } else {
+            envp.put("PATH_TRANSLATED", nullsToBlanks(sPathTranslatedCGI));
+        }
+        envp.put("SCRIPT_NAME", nullsToBlanks(sCGIScriptName));
+        envp.put("QUERY_STRING", nullsToBlanks(req.getQueryString()));
+        envp.put("REMOTE_HOST", nullsToBlanks(req.getRemoteHost()));
+        envp.put("REMOTE_ADDR", nullsToBlanks(req.getRemoteAddr()));
+        envp.put("AUTH_TYPE", nullsToBlanks(req.getAuthType()));
+        envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser()));
+        envp.put("REMOTE_IDENT", ""); //not necessary for full compliance
+        envp.put("CONTENT_TYPE", nullsToBlanks(req.getContentType()));
+
+        /* Note CGI spec says CONTENT_LENGTH must be NULL ("") or undefined
+        * if there is no content, so we cannot put 0 or -1 in as per the
+        * Servlet API spec.
+        */
+
+        int contentLength = req.getContentLength();
+        String sContentLength = (contentLength <= 0 ? "" : (
+            new Integer(contentLength)).toString());
+        envp.put("CONTENT_LENGTH", sContentLength);
+        Enumeration headers = req.getHeaderNames();
+        String header = null;
+        while (headers.hasMoreElements()) {
+            header = null;
+            header = ((String)headers.nextElement()).toUpperCase();
+            //REMIND: rewrite multiple headers as if received as single
+            //REMIND: change character set
+            //REMIND: I forgot what the previous REMIND means
+            if ("AUTHORIZATION".equalsIgnoreCase(header)
+                || "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) {
+                    //NOOP per CGI specification section 11.2
+            } else if ("HOST".equalsIgnoreCase(header)) {
+                String host = req.getHeader(header);
+                envp.put("HTTP_" + header.replace('-', '_'),
+                    host.substring(0, host.indexOf(":")));
+            } else {
+                envp.put("HTTP_" + header.replace('-', '_'),
+                    req.getHeader(header));
+            }
+        }
+        command = sCGIFullPath;
+        workingDirectory = new File(command.substring(0,
+            command.lastIndexOf(File.separator)));
+        envp.put("X_TOMCAT_COMMAND_PATH", command); //for kicks
+        this.setEnvironment(envp);
+        return true;
+    }
+
+
+    /**
+     * Resolves core information about the cgi script. <p> Example URI:
+     * <PRE> /servlet/cgigateway/dir1/realCGIscript/pathinfo1 </PRE> <ul>
+     * <LI><b>path</b> = $CATALINA_HOME/mywebapp/dir1/realCGIscript
+     * <LI><b>scriptName</b> = /servlet/cgigateway/dir1/realCGIscript</LI>
+     * <LI><b>cgiName</b> = /dir1/realCGIscript
+     * <LI><b>name</b> = realCGIscript
+     * </ul>
+     * </p>
+     * <p>
+     * CGI search algorithm: search the real path below
+     * &lt;my-webapp-root&gt; and find the first non-directory in
+     * the getPathTranslated("/"), reading/searching from left-to-right.
+     * </p>
+     * <p>
+     * The CGI search path will start at
+     * webAppRootDir + File.separator + cgiPathPrefix (or webAppRootDir
+     * alone if cgiPathPrefix is null).
+     * </p>
+     * <p>
+     * cgiPathPrefix is usually set by the calling servlet to the servlet's
+     * cgiPathPrefix init parameter
+     * </p>
+     *
+     * @param pathInfo       String from HttpServletRequest.getPathInfo()
+     * @param webAppRootDir  String from context.getRealPath("/")
+     * @param contextPath    String as from HttpServletRequest.getContextPath()
+     * @param servletPath    String as from HttpServletRequest.getServletPath()
+     * @param cgiPathPrefix  subdirectory of webAppRootDir below which the
+     *                       web app's CGIs may be stored; can be null.
+     * @return
+     * <ul> <li> <code>path</code>  -    full file-system path to valid cgi
+     *                                   script, or null if no cgi was found
+     * <li> <code>scriptName</code> -    CGI variable SCRIPT_NAME; the full
+     *                                   URL path to valid cgi script or
+     *                                   null if no cgi was found
+     * <li> <code>cgiName</code>    -    servlet pathInfo fragment
+     *                                   corresponding to the cgi script
+     *                                   itself, or null if not found
+     * <li> <code>name</code>       -    simple name (no directories) of
+     *                                   the cgi script, or null if no cgi
+     *                                   was found
+     * </ul>
+     * @since Tomcat 4.0
+     */
+    protected String[] findCGI(String pathInfo, String webAppRootDir,
+        String contextPath, String servletPath, String cgiPathPrefix) {
+            String path = null;
+            String name = null;
+            String scriptname = null;
+            String cginame = null;
+            if ((webAppRootDir != null)
+                && (webAppRootDir.lastIndexOf("/")
+                == (webAppRootDir.length() - 1))) {
+                    //strip the trailing "/" from the webAppRootDir
+                    webAppRootDir =
+                        webAppRootDir.substring(0,
+                        (webAppRootDir.length() - 1));
+            }
+            if (cgiPathPrefix != null) {
+                webAppRootDir = webAppRootDir + File.separator
+                    + cgiPathPrefix;
+            }
+            
+            if (log.isDebugEnabled()) {
+                log.debug("findCGI: start = [" + webAppRootDir
+                    + "], pathInfo = [" + pathInfo + "] ");
+            }
+            File currentLocation = new File(webAppRootDir);
+            StringTokenizer dirWalker = new StringTokenizer(pathInfo, "/");
+            while (!currentLocation.isFile() && dirWalker.hasMoreElements()) {
+                currentLocation = new
+                    File(currentLocation, (String) dirWalker.nextElement());
+                if (log.isDebugEnabled())  {
+                    log.debug("findCGI: traversing to [" + currentLocation + "]");
+                }
+            }
+            if (!currentLocation.isFile()) {
+                return new String[] { null, null, null, null };
+            } else {
+                if (log.isDebugEnabled())  {
+                    log.debug("findCGI: FOUND cgi at [" + currentLocation + "]");
+                }
+                path = currentLocation.getAbsolutePath();
+                name = currentLocation.getName();
+                cginame = currentLocation.getParent()
+                    .substring(webAppRootDir.length())
+                    + File.separator + name;
+                    if (".".equals(contextPath)) {
+                        scriptname = servletPath + cginame;
+                } else {
+                    scriptname = contextPath + servletPath + cginame;
+                }
+            }
+            if (log.isDebugEnabled())  {
+                log.debug("findCGI calc: name=" + name + ", path=" + path
+                    + ", scriptname=" + scriptname + ", cginame=" + cginame);
+            }
+            return new String[] { path, scriptname, cginame, name };
+    }
+
+
+    /**
+     * Print important CGI environment information in an
+     * easy-to-read HTML table
+     * @return  HTML string containing CGI environment info
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("<TABLE border=2>");
+        sb.append("<tr><th colspan=2 bgcolor=grey>");
+        sb.append("ProcessEnvironment Info</th></tr>");
+        sb.append("<tr><td>Debug Level</td><td>");
+        sb.append(debug);
+        sb.append("</td></tr>");
+        sb.append("<tr><td>Validity:</td><td>");
+        sb.append(isValid());
+        sb.append("</td></tr>");
+        if (isValid()) {
+            Enumeration envk = env.keys();
+            while (envk.hasMoreElements()) {
+                String s = (String)envk.nextElement();
+                sb.append("<tr><td>");
+                sb.append(s);
+                sb.append("</td><td>");
+                sb.append(blanksToString((String)env.get(s),
+                    "[will be set to blank]"));
+                    sb.append("</td></tr>");
+            }
+        }
+        sb.append("<tr><td colspan=2><HR></td></tr>");
+        sb.append("<tr><td>Derived Command</td><td>");
+        sb.append(nullsToBlanks(command));
+        sb.append("</td></tr>");
+        sb.append("<tr><td>Working Directory</td><td>");
+        if (workingDirectory != null) {
+            sb.append(workingDirectory.toString());
+        }
+        sb.append("</td></tr>");
+        sb.append("<tr><td colspan=2>Query Params</td></tr>");
+        Enumeration paramk = queryParameters.keys();
+        while (paramk.hasMoreElements()) {
+            String s = paramk.nextElement().toString();
+            sb.append("<tr><td>");
+            sb.append(s);
+            sb.append("</td><td>");
+            sb.append(queryParameters.get(s).toString());
+            sb.append("</td></tr>");
+        }
+
+        sb.append("</TABLE><p>end.");
+        return sb.toString();
+    }
+
+
+    /**
+     * Gets process' derived query parameters
+     * @return   process' query parameters
+     */
+    public Hashtable getParameters() {
+        return queryParameters;
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/CharsetMapper.java b/container/catalina/src/share/org/apache/catalina/util/CharsetMapper.java
new file mode 100644
index 0000000..d0d2d44
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/CharsetMapper.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.Properties;
+
+
+
+/**
+ * Utility class that attempts to map from a Locale to the corresponding
+ * character set to be used for interpreting input text (or generating
+ * output text) when the Content-Type header does not include one.  You
+ * can customize the behavior of this class by modifying the mapping data
+ * it loads, or by subclassing it (to change the algorithm) and then using
+ * your own version for a particular web application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Date$ $Version$
+ */
+
+public class CharsetMapper {
+
+
+    // ---------------------------------------------------- Manifest Constants
+
+
+    /**
+     * Default properties resource name.
+     */
+    public static final String DEFAULT_RESOURCE =
+      "/org/apache/catalina/util/CharsetMapperDefault.properties";
+
+
+    // ---------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new CharsetMapper using the default properties resource.
+     */
+    public CharsetMapper() {
+        this(DEFAULT_RESOURCE);
+    }
+
+
+    /**
+     * Construct a new CharsetMapper using the specified properties resource.
+     *
+     * @param name Name of a properties resource to be loaded
+     *
+     * @exception IllegalArgumentException if the specified properties
+     *  resource could not be loaded for any reason.
+     */
+    public CharsetMapper(String name) {
+        try {
+            InputStream stream =
+              this.getClass().getResourceAsStream(name);
+            map.load(stream);
+            stream.close();
+        } catch (Throwable t) {
+            throw new IllegalArgumentException(t.toString());
+        }
+    }
+
+
+    // ---------------------------------------------------- Instance Variables
+
+
+    /**
+     * The mapping properties that have been initialized from the specified or
+     * default properties resource.
+     */
+    private Properties map = new Properties();
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Calculate the name of a character set to be assumed, given the specified
+     * Locale and the absence of a character set specified as part of the
+     * content type header.
+     *
+     * @param locale The locale for which to calculate a character set
+     */
+    public String getCharset(Locale locale) {
+        // Match full language_country_variant first, then language_country, 
+        // then language only
+        String charset = map.getProperty(locale.toString());
+        if (charset == null) {
+            charset = map.getProperty(locale.getLanguage() + "_" 
+                    + locale.getCountry());
+            if (charset == null) {
+                charset = map.getProperty(locale.getLanguage());
+            }
+        }
+        return (charset);
+    }
+
+    
+    /**
+     * The deployment descriptor can have a
+     * locale-encoding-mapping-list element which describes the
+     * webapp's desired mapping from locale to charset.  This method
+     * gets called when processing the web.xml file for a context
+     *
+     * @param locale The locale for a character set
+     * @param charset The charset to be associated with the locale
+     */
+    public void addCharsetMappingFromDeploymentDescriptor(String locale, String charset) {
+        map.put(locale, charset);
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/CharsetMapperDefault.properties b/container/catalina/src/share/org/apache/catalina/util/CharsetMapperDefault.properties
new file mode 100644
index 0000000..c73a8fc
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/CharsetMapperDefault.properties
@@ -0,0 +1,2 @@
+en=ISO-8859-1
+fr=ISO-8859-1
diff --git a/container/catalina/src/share/org/apache/catalina/util/CookieTools.java b/container/catalina/src/share/org/apache/catalina/util/CookieTools.java
new file mode 100644
index 0000000..5298d6d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/CookieTools.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+import java.text.*;
+import java.util.*;
+
+import javax.servlet.http.Cookie;
+
+// XXX use only one Date instance/request, reuse it.
+
+/**
+ * Cookie utils - generate cookie header, etc
+ *
+ * @author Original Author Unknown
+ * @author duncan@eng.sun.com
+ */
+public class CookieTools {
+
+    /** Return the header name to set the cookie, based on cookie
+     *  version
+     */
+    public static String getCookieHeaderName(Cookie cookie) {
+        int version = cookie.getVersion();
+
+        if (version == 1) {
+            return "Set-Cookie2";
+        } else {
+            return "Set-Cookie";
+        }
+    }
+
+    /** Return the header value used to set this cookie
+     *  @deprecated Use StringBuffer version
+     */
+    public static String getCookieHeaderValue(Cookie cookie) {
+        StringBuffer buf = new StringBuffer();
+        getCookieHeaderValue( cookie, buf );
+        return buf.toString();
+    }
+
+    /** Return the header value used to set this cookie
+     */
+    public static void getCookieHeaderValue(Cookie cookie, StringBuffer buf) {
+        int version = cookie.getVersion();
+
+        // this part is the same for all cookies
+
+        String name = cookie.getName();     // Avoid NPE on malformed cookies
+        if (name == null)
+            name = "";
+        String value = cookie.getValue();
+        if (value == null)
+            value = "";
+        
+        buf.append(name);
+        buf.append("=");
+        maybeQuote(version, buf, value);
+
+        // add version 1 specific information
+        if (version == 1) {
+            // Version=1 ... required
+            buf.append ("; Version=1");
+
+            // Comment=comment
+            if (cookie.getComment() != null) {
+                buf.append ("; Comment=");
+                maybeQuote (version, buf, cookie.getComment());
+            }
+        }
+
+        // add domain information, if present
+
+        if (cookie.getDomain() != null) {
+            buf.append("; Domain=");
+            maybeQuote (version, buf, cookie.getDomain());
+        }
+
+        // Max-Age=secs/Discard ... or use old "Expires" format
+        if (cookie.getMaxAge() >= 0) {
+            if (version == 0) {
+                buf.append ("; Expires=");
+                if (cookie.getMaxAge() == 0)
+                    DateTool.oldCookieFormat.format(new Date(10000), buf,
+                                                    new FieldPosition(0));
+                else
+                    DateTool.oldCookieFormat.format
+                        (new Date( System.currentTimeMillis() +
+                                   cookie.getMaxAge() *1000L), buf,
+                         new FieldPosition(0));
+            } else {
+                buf.append ("; Max-Age=");
+                buf.append (cookie.getMaxAge());
+            }
+        } else if (version == 1)
+          buf.append ("; Discard");
+
+        // Path=path
+        if (cookie.getPath() != null) {
+            buf.append ("; Path=");
+            maybeQuote (version, buf, cookie.getPath());
+        }
+
+        // Secure
+        if (cookie.getSecure()) {
+          buf.append ("; Secure");
+        }
+    }
+
+    static void maybeQuote (int version, StringBuffer buf,
+                                    String value)
+    {
+        if (version == 0 || isToken (value))
+            buf.append (value);
+        else {
+            buf.append ('"');
+            buf.append (value);
+            buf.append ('"');
+        }
+    }
+
+        //
+    // from RFC 2068, token special case characters
+    //
+    private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
+
+    /*
+     * Return true iff the string counts as an HTTP/1.1 "token".
+     */
+    private static boolean isToken (String value) {
+        int len = value.length ();
+
+        for (int i = 0; i < len; i++) {
+            char c = value.charAt (i);
+
+            if (c < 0x20 || c >= 0x7f || tspecials.indexOf (c) != -1)
+              return false;
+        }
+        return true;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/CustomObjectInputStream.java b/container/catalina/src/share/org/apache/catalina/util/CustomObjectInputStream.java
new file mode 100644
index 0000000..2df1ed7
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/CustomObjectInputStream.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Proxy;
+
+/**
+ * Custom subclass of <code>ObjectInputStream</code> that loads from the
+ * class loader for this web application.  This allows classes defined only
+ * with the web application to be found correctly.
+ *
+ * @author Craig R. McClanahan
+ * @author Bip Thelin
+ * @version $Revision$, $Date$
+ */
+
+public final class CustomObjectInputStream
+    extends ObjectInputStream {
+
+
+    /**
+     * The class loader we will use to resolve classes.
+     */
+    private ClassLoader classLoader = null;
+
+
+    /**
+     * Construct a new instance of CustomObjectInputStream
+     *
+     * @param stream The input stream we will read from
+     * @param classLoader The class loader used to instantiate objects
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public CustomObjectInputStream(InputStream stream,
+                                   ClassLoader classLoader)
+        throws IOException {
+
+        super(stream);
+        this.classLoader = classLoader;
+    }
+
+
+    /**
+     * Load the local class equivalent of the specified stream class
+     * description, by using the class loader assigned to this Context.
+     *
+     * @param classDesc Class description from the input stream
+     *
+     * @exception ClassNotFoundException if this class cannot be found
+     * @exception IOException if an input/output error occurs
+     */
+    public Class resolveClass(ObjectStreamClass classDesc)
+        throws ClassNotFoundException, IOException {
+        return Class.forName(classDesc.getName(), false, classLoader);
+    }
+
+
+    /**
+     * Return a proxy class that implements the interfaces named in a proxy
+     * class descriptor. Do this using the class loader assigned to this
+     * Context.
+     */
+    protected Class resolveProxyClass(String[] interfaces)
+        throws IOException, ClassNotFoundException {
+
+        Class[] cinterfaces = new Class[interfaces.length];
+        for (int i = 0; i < interfaces.length; i++)
+            cinterfaces[i] = classLoader.loadClass(interfaces[i]);
+
+        try {
+            return Proxy.getProxyClass(classLoader, cinterfaces);
+        } catch (IllegalArgumentException e) {
+            throw new ClassNotFoundException(null, e);
+        }
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/DOMWriter.java b/container/catalina/src/share/org/apache/catalina/util/DOMWriter.java
new file mode 100644
index 0000000..d350356
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/DOMWriter.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A sample DOM writer. This sample program illustrates how to
+ * traverse a DOM tree in order to print a document that is parsed.
+ */
+public class DOMWriter {
+
+   //
+   // Data
+   //
+
+   /** Default Encoding */
+   private static  String
+   PRINTWRITER_ENCODING = "UTF8";
+
+   private static String MIME2JAVA_ENCODINGS[] =
+    { "Default", "UTF-8", "US-ASCII", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4",
+      "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", "ISO-2022-JP",
+      "SHIFT_JIS", "EUC-JP","GB2312", "BIG5", "EUC-KR", "ISO-2022-KR", "KOI8-R", "EBCDIC-CP-US",
+      "EBCDIC-CP-CA", "EBCDIC-CP-NL", "EBCDIC-CP-DK", "EBCDIC-CP-NO", "EBCDIC-CP-FI", "EBCDIC-CP-SE",
+      "EBCDIC-CP-IT", "EBCDIC-CP-ES", "EBCDIC-CP-GB", "EBCDIC-CP-FR", "EBCDIC-CP-AR1",
+      "EBCDIC-CP-HE", "EBCDIC-CP-CH", "EBCDIC-CP-ROECE","EBCDIC-CP-YU",
+      "EBCDIC-CP-IS", "EBCDIC-CP-AR2", "UTF-16"
+    };
+
+   /** Output qualified names */
+   private boolean qualifiedNames = true;
+
+   /** Print writer. */
+   protected PrintWriter out;
+
+   /** Canonical output. */
+   protected boolean canonical;
+
+
+   public DOMWriter(String encoding, boolean canonical)
+   throws UnsupportedEncodingException {
+      out = new PrintWriter(new OutputStreamWriter(System.out, encoding));
+      this.canonical = canonical;
+   } // <init>(String,boolean)
+
+   //
+   // Constructors
+   //
+
+   /** Default constructor. */
+   public DOMWriter(boolean canonical) throws UnsupportedEncodingException {
+      this( getWriterEncoding(), canonical);
+   }
+
+    public DOMWriter(Writer writer, boolean canonical) {
+        out = new PrintWriter(writer);
+        this.canonical = canonical;
+    }
+
+   public boolean getQualifiedNames() {
+      return this.qualifiedNames;
+   }
+
+   public void setQualifiedNames(boolean qualifiedNames) {
+      this.qualifiedNames = qualifiedNames;
+   }
+
+   public static String getWriterEncoding( ) {
+      return (PRINTWRITER_ENCODING);
+   }// getWriterEncoding
+
+   public static void  setWriterEncoding( String encoding ) {
+      if( encoding.equalsIgnoreCase( "DEFAULT" ) )
+         PRINTWRITER_ENCODING  = "UTF8";
+      else if( encoding.equalsIgnoreCase( "UTF-16" ) )
+         PRINTWRITER_ENCODING  = "Unicode";
+      else
+         PRINTWRITER_ENCODING = MIME2Java.convert( encoding );
+   }// setWriterEncoding
+
+
+   public static boolean isValidJavaEncoding( String encoding ) {
+      for ( int i = 0; i < MIME2JAVA_ENCODINGS.length; i++ )
+         if ( encoding.equals( MIME2JAVA_ENCODINGS[i] ) )
+            return (true);
+
+      return (false);
+   }// isValidJavaEncoding
+
+
+   /** Prints the specified node, recursively. */
+   public void print(Node node) {
+
+      // is there anything to do?
+      if ( node == null ) {
+         return;
+      }
+
+      int type = node.getNodeType();
+      switch ( type ) {
+         // print document
+         case Node.DOCUMENT_NODE: {
+               if ( !canonical ) {
+                  String  Encoding = getWriterEncoding();
+                  if( Encoding.equalsIgnoreCase( "DEFAULT" ) )
+                     Encoding = "UTF-8";
+                  else if( Encoding.equalsIgnoreCase( "Unicode" ) )
+                     Encoding = "UTF-16";
+                  else
+                     Encoding = MIME2Java.reverse( Encoding );
+
+                  out.println("<?xml version=\"1.0\" encoding=\""+
+                           Encoding + "\"?>");
+               }
+               print(((Document)node).getDocumentElement());
+               out.flush();
+               break;
+            }
+
+            // print element with attributes
+         case Node.ELEMENT_NODE: {
+               out.print('<');
+               if (this.qualifiedNames) { 
+                  out.print(node.getNodeName());
+               } else {
+                  out.print(node.getLocalName());
+               }
+               Attr attrs[] = sortAttributes(node.getAttributes());
+               for ( int i = 0; i < attrs.length; i++ ) {
+                  Attr attr = attrs[i];
+                  out.print(' ');
+                  if (this.qualifiedNames) {
+                     out.print(attr.getNodeName());
+                  } else {
+                     out.print(attr.getLocalName());
+                  }
+                  
+                  out.print("=\"");
+                  out.print(normalize(attr.getNodeValue()));
+                  out.print('"');
+               }
+               out.print('>');
+               NodeList children = node.getChildNodes();
+               if ( children != null ) {
+                  int len = children.getLength();
+                  for ( int i = 0; i < len; i++ ) {
+                     print(children.item(i));
+                  }
+               }
+               break;
+            }
+
+            // handle entity reference nodes
+         case Node.ENTITY_REFERENCE_NODE: {
+               if ( canonical ) {
+                  NodeList children = node.getChildNodes();
+                  if ( children != null ) {
+                     int len = children.getLength();
+                     for ( int i = 0; i < len; i++ ) {
+                        print(children.item(i));
+                     }
+                  }
+               } else {
+                  out.print('&');
+                  if (this.qualifiedNames) {
+                     out.print(node.getNodeName());
+                  } else {
+                     out.print(node.getLocalName());
+                  }
+                  out.print(';');
+               }
+               break;
+            }
+
+            // print cdata sections
+         case Node.CDATA_SECTION_NODE: {
+               if ( canonical ) {
+                  out.print(normalize(node.getNodeValue()));
+               } else {
+                  out.print("<![CDATA[");
+                  out.print(node.getNodeValue());
+                  out.print("]]>");
+               }
+               break;
+            }
+
+            // print text
+         case Node.TEXT_NODE: {
+               out.print(normalize(node.getNodeValue()));
+               break;
+            }
+
+            // print processing instruction
+         case Node.PROCESSING_INSTRUCTION_NODE: {
+               out.print("<?");
+               if (this.qualifiedNames) {
+                  out.print(node.getNodeName());
+               } else {
+                  out.print(node.getLocalName());
+               }
+               
+               String data = node.getNodeValue();
+               if ( data != null && data.length() > 0 ) {
+                  out.print(' ');
+                  out.print(data);
+               }
+               out.print("?>");
+               break;
+            }
+      }
+
+      if ( type == Node.ELEMENT_NODE ) {
+         out.print("</");
+         if (this.qualifiedNames) {
+            out.print(node.getNodeName());
+         } else {
+            out.print(node.getLocalName());
+         }
+         out.print('>');
+      }
+
+      out.flush();
+
+   } // print(Node)
+
+   /** Returns a sorted list of attributes. */
+   protected Attr[] sortAttributes(NamedNodeMap attrs) {
+
+      int len = (attrs != null) ? attrs.getLength() : 0;
+      Attr array[] = new Attr[len];
+      for ( int i = 0; i < len; i++ ) {
+         array[i] = (Attr)attrs.item(i);
+      }
+      for ( int i = 0; i < len - 1; i++ ) {
+         String name = null;
+         if (this.qualifiedNames) {
+            name  = array[i].getNodeName();
+         } else {
+            name  = array[i].getLocalName();
+         }
+         int    index = i;
+         for ( int j = i + 1; j < len; j++ ) {
+            String curName = null;
+            if (this.qualifiedNames) {
+               curName = array[j].getNodeName();
+            } else {
+               curName = array[j].getLocalName();
+            }
+            if ( curName.compareTo(name) < 0 ) {
+               name  = curName;
+               index = j;
+            }
+         }
+         if ( index != i ) {
+            Attr temp    = array[i];
+            array[i]     = array[index];
+            array[index] = temp;
+         }
+      }
+
+      return (array);
+
+   } // sortAttributes(NamedNodeMap):Attr[]
+
+
+   /** Normalizes the given string. */
+   protected String normalize(String s) {
+      StringBuffer str = new StringBuffer();
+
+      int len = (s != null) ? s.length() : 0;
+      for ( int i = 0; i < len; i++ ) {
+         char ch = s.charAt(i);
+         switch ( ch ) {
+            case '<': {
+                  str.append("&lt;");
+                  break;
+               }
+            case '>': {
+                  str.append("&gt;");
+                  break;
+               }
+            case '&': {
+                  str.append("&amp;");
+                  break;
+               }
+            case '"': {
+                  str.append("&quot;");
+                  break;
+               }
+            case '\r':
+            case '\n': {
+                  if ( canonical ) {
+                     str.append("&#");
+                     str.append(Integer.toString(ch));
+                     str.append(';');
+                     break;
+                  }
+                  // else, default append char
+               }
+            default: {
+                  str.append(ch);
+               }
+         }
+      }
+
+      return (str.toString());
+
+   } // normalize(String):String
+
+   private static void printValidJavaEncoding() {
+      System.err.println( "    ENCODINGS:" );
+      System.err.print( "   " );
+      for( int i = 0;
+                     i < MIME2JAVA_ENCODINGS.length; i++) {
+         System.err.print( MIME2JAVA_ENCODINGS[i] + " " );
+      if( (i % 7 ) == 0 ){
+         System.err.println();
+         System.err.print( "   " );
+         }
+      }
+
+   } // printJavaEncoding()
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/DateTool.java b/container/catalina/src/share/org/apache/catalina/util/DateTool.java
new file mode 100644
index 0000000..b5b8487
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/DateTool.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ *  Common place for date utils.
+ *
+ * @author dac@eng.sun.com
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Costin Manolache
+ */
+public class DateTool {
+
+    private static StringManager sm =
+        StringManager.getManager("org.apache.catalina.util");
+
+    /**
+     * US locale - all HTTP dates are in english
+     */
+    public final static Locale LOCALE_US = Locale.US;
+
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    public final static TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
+
+    /**
+     * format for RFC 1123 date string -- "Sun, 06 Nov 1994 08:49:37 GMT"
+     */
+    public final static String RFC1123_PATTERN =
+        "EEE, dd MMM yyyyy HH:mm:ss z";
+
+    /** 
+     * Format for http response header date field
+     */
+    public static final String HTTP_RESPONSE_DATE_HEADER =
+        "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+    // format for RFC 1036 date string -- "Sunday, 06-Nov-94 08:49:37 GMT"
+    private final static String rfc1036Pattern =
+        "EEEEEEEEE, dd-MMM-yy HH:mm:ss z";
+
+    // format for C asctime() date string -- "Sun Nov  6 08:49:37 1994"
+    private final static String asctimePattern =
+        "EEE MMM d HH:mm:ss yyyyy";
+
+    /**
+     * Pattern used for old cookies
+     */
+    public final static String OLD_COOKIE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z";
+
+    /**
+     * DateFormat to be used to format dates
+     */
+    public final static DateFormat rfc1123Format =
+        new SimpleDateFormat(RFC1123_PATTERN, LOCALE_US);
+
+    /**
+     * DateFormat to be used to format old netscape cookies
+     */
+    public final static DateFormat oldCookieFormat =
+        new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US);
+
+    public final static DateFormat rfc1036Format =
+        new SimpleDateFormat(rfc1036Pattern, LOCALE_US);
+
+    public final static DateFormat asctimeFormat =
+        new SimpleDateFormat(asctimePattern, LOCALE_US);
+
+    static {
+        rfc1123Format.setTimeZone(GMT_ZONE);
+        oldCookieFormat.setTimeZone(GMT_ZONE);
+        rfc1036Format.setTimeZone(GMT_ZONE);
+        asctimeFormat.setTimeZone(GMT_ZONE);
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/Enumerator.java b/container/catalina/src/share/org/apache/catalina/util/Enumerator.java
new file mode 100644
index 0000000..622ffd7
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/Enumerator.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+
+/**
+ * Adapter class that wraps an <code>Enumeration</code> around a Java2
+ * collection classes object <code>Iterator</code> so that existing APIs
+ * returning Enumerations can easily run on top of the new collections.
+ * Constructors are provided to easliy create such wrappers.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class Enumerator implements Enumeration {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Return an Enumeration over the values of the specified Collection.
+     *
+     * @param collection Collection whose values should be enumerated
+     */
+    public Enumerator(Collection collection) {
+
+        this(collection.iterator());
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Collection.
+     *
+     * @param collection Collection whose values should be enumerated
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Collection collection, boolean clone) {
+
+        this(collection.iterator(), clone);
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values returned by the
+     * specified Iterator.
+     *
+     * @param iterator Iterator to be wrapped
+     */
+    public Enumerator(Iterator iterator) {
+
+        super();
+        this.iterator = iterator;
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values returned by the
+     * specified Iterator.
+     *
+     * @param iterator Iterator to be wrapped
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Iterator iterator, boolean clone) {
+
+        super();
+        if (!clone) {
+            this.iterator = iterator;
+        } else {
+            List list = new ArrayList();
+            while (iterator.hasNext()) {
+                list.add(iterator.next());
+            }
+            this.iterator = list.iterator();   
+        }
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Map.
+     *
+     * @param map Map whose values should be enumerated
+     */
+    public Enumerator(Map map) {
+
+        this(map.values().iterator());
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Map.
+     *
+     * @param map Map whose values should be enumerated
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Map map, boolean clone) {
+
+        this(map.values().iterator(), clone);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>Iterator</code> over which the <code>Enumeration</code>
+     * represented by this class actually operates.
+     */
+    private Iterator iterator = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Tests if this enumeration contains more elements.
+     *
+     * @return <code>true</code> if and only if this enumeration object
+     *  contains at least one more element to provide, <code>false</code>
+     *  otherwise
+     */
+    public boolean hasMoreElements() {
+
+        return (iterator.hasNext());
+
+    }
+
+
+    /**
+     * Returns the next element of this enumeration if this enumeration
+     * has at least one more element to provide.
+     *
+     * @return the next element of this enumeration
+     *
+     * @exception NoSuchElementException if no more elements exist
+     */
+    public Object nextElement() throws NoSuchElementException {
+
+        return (iterator.next());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/Extension.java b/container/catalina/src/share/org/apache/catalina/util/Extension.java
new file mode 100644
index 0000000..c3be065
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/Extension.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+import java.util.StringTokenizer;
+
+
+/**
+ * Utility class that represents either an available "Optional Package"
+ * (formerly known as "Standard Extension") as described in the manifest
+ * of a JAR file, or the requirement for such an optional package.  It is
+ * used to support the requirements of the Servlet Specification, version
+ * 2.3, related to providing shared extensions to all webapps.
+ * <p>
+ * In addition, static utility methods are available to scan a manifest
+ * and return an array of either available or required optional modules
+ * documented in that manifest.
+ * <p>
+ * For more information about optional packages, see the document
+ * <em>Optional Package Versioning</em> in the documentation bundle for your
+ * Java2 Standard Edition package, in file
+ * <code>guide/extensions/versioning.html</code>.
+ *
+ * @author Craig McClanahan
+ * @author Justyna Horwat
+ * @author Greg Murray
+ * @version $Revision$ $Date$
+ */
+
+public final class Extension {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The name of the optional package being made available, or required.
+     */
+    private String extensionName = null;
+    
+
+    public String getExtensionName() {
+        return (this.extensionName);
+    }
+
+    public void setExtensionName(String extensionName) {
+        this.extensionName = extensionName;
+    }
+
+    /**
+     * UniqueId created by combining the extension name and implementation
+     * version. 
+     */
+    public String getUniqueId() {
+        return this.extensionName + this.implementationVersion;
+    }
+
+    /**
+     * The URL from which the most recent version of this optional package
+     * can be obtained if it is not already installed.
+     */
+    private String implementationURL = null;
+
+    public String getImplementationURL() {
+        return (this.implementationURL);
+    }
+
+    public void setImplementationURL(String implementationURL) {
+        this.implementationURL = implementationURL;
+    }
+
+
+    /**
+     * The name of the company or organization that produced this
+     * implementation of this optional package.
+     */
+    private String implementationVendor = null;
+
+    public String getImplementationVendor() {
+        return (this.implementationVendor);
+    }
+
+    public void setImplementationVendor(String implementationVendor) {
+        this.implementationVendor = implementationVendor;
+    }
+
+
+    /**
+     * The unique identifier of the company that produced the optional
+     * package contained in this JAR file.
+     */
+    private String implementationVendorId = null;
+
+    public String getImplementationVendorId() {
+        return (this.implementationVendorId);
+    }
+
+    public void setImplementationVendorId(String implementationVendorId) {
+        this.implementationVendorId = implementationVendorId;
+    }
+
+
+    /**
+     * The version number (dotted decimal notation) for this implementation
+     * of the optional package.
+     */
+    private String implementationVersion = null;
+
+    public String getImplementationVersion() {
+        return (this.implementationVersion);
+    }
+
+    public void setImplementationVersion(String implementationVersion) {
+        this.implementationVersion = implementationVersion;
+    }
+
+
+    /**
+     * The name of the company or organization that originated the
+     * specification to which this optional package conforms.
+     */
+    private String specificationVendor = null;
+
+    public String getSpecificationVendor() {
+        return (this.specificationVendor);
+    }
+
+    public void setSpecificationVendor(String specificationVendor) {
+        this.specificationVendor = specificationVendor;
+    }
+
+
+    /**
+     * The version number (dotted decimal notation) of the specification
+     * to which this optional package conforms.
+     */
+    private String specificationVersion = null;
+
+    public String getSpecificationVersion() {
+        return (this.specificationVersion);
+    }
+
+    public void setSpecificationVersion(String specificationVersion) {
+        this.specificationVersion = specificationVersion;
+    }
+
+
+    /**
+     * fulfilled is true if all the required extension dependencies have been
+     * satisfied
+     */
+    private boolean fulfilled = false;
+
+    public void setFulfilled(boolean fulfilled) {
+        this.fulfilled = fulfilled;
+    }
+    
+    public boolean isFulfilled() {
+        return fulfilled;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Return <code>true</code> if the specified <code>Extension</code>
+     * (which represents an optional package required by this application)
+     * is satisfied by this <code>Extension</code> (which represents an
+     * optional package that is already installed.  Otherwise, return
+     * <code>false</code>.
+     *
+     * @param required Extension of the required optional package
+     */
+    public boolean isCompatibleWith(Extension required) {
+
+        // Extension Name must match
+        if (extensionName == null)
+            return (false);
+        if (!extensionName.equals(required.getExtensionName()))
+            return (false);
+
+        // Available specification version must be >= required
+        if (!isNewer(specificationVersion, required.getSpecificationVersion()))
+            return (false);
+
+        // Implementation Vendor ID must match
+        if (implementationVendorId == null)
+            return (false);
+        if (!implementationVendorId.equals(required.getImplementationVendorId()))
+            return (false);
+
+        // Implementation version must be >= required
+        if (!isNewer(implementationVersion, required.getImplementationVersion()))
+            return (false);
+
+        // This available optional package satisfies the requirements
+        return (true);
+
+    }
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("Extension[");
+        sb.append(extensionName);
+        if (implementationURL != null) {
+            sb.append(", implementationURL=");
+            sb.append(implementationURL);
+        }
+        if (implementationVendor != null) {
+            sb.append(", implementationVendor=");
+            sb.append(implementationVendor);
+        }
+        if (implementationVendorId != null) {
+            sb.append(", implementationVendorId=");
+            sb.append(implementationVendorId);
+        }
+        if (implementationVersion != null) {
+            sb.append(", implementationVersion=");
+            sb.append(implementationVersion);
+        }
+        if (specificationVendor != null) {
+            sb.append(", specificationVendor=");
+            sb.append(specificationVendor);
+        }
+        if (specificationVersion != null) {
+            sb.append(", specificationVersion=");
+            sb.append(specificationVersion);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+
+    /**
+     * Return <code>true</code> if the first version number is greater than
+     * or equal to the second; otherwise return <code>false</code>.
+     *
+     * @param first First version number (dotted decimal)
+     * @param second Second version number (dotted decimal)
+     *
+     * @exception NumberFormatException on a malformed version number
+     */
+    private boolean isNewer(String first, String second)
+        throws NumberFormatException {
+
+        if ((first == null) || (second == null))
+            return (false);
+        if (first.equals(second))
+            return (true);
+
+        StringTokenizer fTok = new StringTokenizer(first, ".", true);
+        StringTokenizer sTok = new StringTokenizer(second, ".", true);
+        int fVersion = 0;
+        int sVersion = 0;
+        while (fTok.hasMoreTokens() || sTok.hasMoreTokens()) {
+            if (fTok.hasMoreTokens())
+                fVersion = Integer.parseInt(fTok.nextToken());
+            else
+                fVersion = 0;
+            if (sTok.hasMoreTokens())
+                sVersion = Integer.parseInt(sTok.nextToken());
+            else
+                sVersion = 0;
+            if (fVersion < sVersion)
+                return (false);
+            else if (fVersion > sVersion)
+                return (true);
+            if (fTok.hasMoreTokens())   // Swallow the periods
+                fTok.nextToken();
+            if (sTok.hasMoreTokens())
+                sTok.nextToken();
+        }
+
+        return (true);  // Exact match
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/ExtensionValidator.java b/container/catalina/src/share/org/apache/catalina/util/ExtensionValidator.java
new file mode 100644
index 0000000..2fadee9
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/ExtensionValidator.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import javax.naming.Binding;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.naming.resources.Resource;
+
+
+/**
+ * Ensures that all extension dependies are resolved for a WEB application
+ * are met. This class builds a master list of extensions available to an
+ * applicaiton and then validates those extensions.
+ *
+ * See http://java.sun.com/j2se/1.4/docs/guide/extensions/spec.html for
+ * a detailed explanation of the extension mechanism in Java.
+ *
+ * @author Greg Murray
+ * @author Justyna Horwat
+ * @version $Revision$ $Date$
+ *
+ */
+public final class ExtensionValidator {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(ExtensionValidator.class);
+
+    /**
+     * The string resources for this package.
+     */
+    private static StringManager sm =
+        StringManager.getManager("org.apache.catalina.util");
+    
+    private static HashMap containerAvailableExtensions = null;
+    private static ArrayList containerManifestResources = new ArrayList();
+    private static ResourceBundle messages = null;
+
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    /**
+     *  This static initializer loads the container level extensions that are
+     *  available to all web applications. This method scans all extension 
+     *  directories available via the "java.ext.dirs" System property. 
+     *
+     *  The System Class-Path is also scanned for jar files that may contain 
+     *  available extensions.
+     */
+    static {
+
+        // check for container level optional packages
+        String systemClasspath = System.getProperty("java.class.path");
+
+        StringTokenizer strTok = new StringTokenizer(systemClasspath, 
+                                                     File.pathSeparator);
+
+        // build a list of jar files in the classpath
+        while (strTok.hasMoreTokens()) {
+            String classpathItem = strTok.nextToken();
+            if (classpathItem.toLowerCase().endsWith(".jar")) {
+                File item = new File(classpathItem);
+                if (item.exists()) {
+                    try {
+                        addSystemResource(item);
+                    } catch (IOException e) {
+                        log.error(sm.getString
+                                  ("extensionValidator.failload", item), e);
+                    }
+                }
+            }
+        }
+
+        // add specified folders to the list
+        addFolderList("java.ext.dirs");
+        addFolderList("catalina.ext.dirs");
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Runtime validation of a Web Applicaiton.
+     *
+     * This method uses JNDI to look up the resources located under a 
+     * <code>DirContext</code>. It locates Web Application MANIFEST.MF 
+     * file in the /META-INF/ directory of the application and all 
+     * MANIFEST.MF files in each JAR file located in the WEB-INF/lib 
+     * directory and creates an <code>ArrayList</code> of 
+     * <code>ManifestResorce<code> objects. These objects are then passed 
+     * to the validateManifestResources method for validation.
+     *
+     * @param dirContext The JNDI root of the Web Application
+     * @param context The context from which the Logger and path to the
+     *                application
+     *
+     * @return true if all required extensions satisfied
+     */
+    public static synchronized boolean validateApplication(
+                                           DirContext dirContext, 
+                                           StandardContext context)
+                    throws IOException {
+
+        String appName = context.getPath();
+        ArrayList appManifestResources = new ArrayList();
+        ManifestResource appManifestResource = null;
+        // If the application context is null it does not exist and 
+        // therefore is not valid
+        if (dirContext == null) return false;
+        // Find the Manifest for the Web Applicaiton
+        InputStream inputStream = null;
+        try {
+            NamingEnumeration wne = dirContext.listBindings("/META-INF/");
+            Binding binding = (Binding) wne.nextElement();
+            if (binding.getName().toUpperCase().equals("MANIFEST.MF")) {
+                Resource resource = (Resource)dirContext.lookup
+                                    ("/META-INF/" + binding.getName());
+                inputStream = resource.streamContent();
+                Manifest manifest = new Manifest(inputStream);
+                inputStream.close();
+                inputStream = null;
+                ManifestResource mre = new ManifestResource
+                    (sm.getString("extensionValidator.web-application-manifest"),
+                    manifest, ManifestResource.WAR);
+                appManifestResources.add(mre);
+            } 
+        } catch (NamingException nex) {
+            // Application does not contain a MANIFEST.MF file
+        } catch (NoSuchElementException nse) {
+            // Application does not contain a MANIFEST.MF file
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (Throwable t) {
+                    // Ignore
+                }
+            }
+        }
+
+        // Locate the Manifests for all bundled JARs
+        NamingEnumeration ne = null;
+        try {
+            if (dirContext != null) {
+                ne = dirContext.listBindings("WEB-INF/lib/");
+            }
+            while ((ne != null) && ne.hasMoreElements()) {
+                Binding binding = (Binding)ne.nextElement();
+                if (!binding.getName().toLowerCase().endsWith(".jar")) {
+                    continue;
+                }
+                Resource resource = (Resource)dirContext.lookup
+                                        ("/WEB-INF/lib/" + binding.getName());
+                Manifest jmanifest = getManifest(resource.streamContent());
+                if (jmanifest != null) {
+                    ManifestResource mre = new ManifestResource(
+                                                binding.getName(),
+                                                jmanifest, 
+                                                ManifestResource.APPLICATION);
+                    appManifestResources.add(mre);
+                }
+            }
+        } catch (NamingException nex) {
+            // Jump out of the check for this application because it 
+            // has no resources
+        }
+
+        return validateManifestResources(appName, appManifestResources);
+    }
+
+
+    /**
+     * Checks to see if the given system JAR file contains a MANIFEST, and adds
+     * it to the container's manifest resources.
+     *
+     * @param jarFile The system JAR whose manifest to add
+     */
+    public static void addSystemResource(File jarFile) throws IOException {
+        Manifest manifest = getManifest(new FileInputStream(jarFile));
+        if (manifest != null)  {
+            ManifestResource mre
+                = new ManifestResource(jarFile.getAbsolutePath(),
+                                       manifest,
+                                       ManifestResource.SYSTEM);
+            containerManifestResources.add(mre);
+        }
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Validates a <code>ArrayList</code> of <code>ManifestResource</code> 
+     * objects. This method requires an application name (which is the 
+     * context root of the application at runtime).  
+     *
+     * <code>false</false> is returned if the extension dependencies
+     * represented by any given <code>ManifestResource</code> objects 
+     * is not met.
+     *
+     * This method should also provide static validation of a Web Applicaiton 
+     * if provided with the necessary parameters.
+     *
+     * @param appName The name of the Application that will appear in the 
+     *                error messages
+     * @param resources A list of <code>ManifestResource</code> objects 
+     *                  to be validated.
+     *
+     * @return true if manifest resource file requirements are met
+     */
+    private static boolean validateManifestResources(String appName, 
+                                                     ArrayList resources) {
+        boolean passes = true;
+        int failureCount = 0;        
+        HashMap availableExtensions = null;
+
+        Iterator it = resources.iterator();
+        while (it.hasNext()) {
+            ManifestResource mre = (ManifestResource)it.next();
+            ArrayList requiredList = mre.getRequiredExtensions();
+            if (requiredList == null) {
+                continue;
+            }
+
+            // build the list of available extensions if necessary
+            if (availableExtensions == null) {
+                availableExtensions = buildAvailableExtensionsMap(resources);
+            }
+
+            // load the container level resource map if it has not been built
+            // yet
+            if (containerAvailableExtensions == null) {
+                containerAvailableExtensions
+                    = buildAvailableExtensionsMap(containerManifestResources);
+            }
+
+            // iterate through the list of required extensions
+            Iterator rit = requiredList.iterator();
+            while (rit.hasNext()) {
+                Extension requiredExt = (Extension)rit.next();
+                String extId = requiredExt.getUniqueId();
+                // check the applicaion itself for the extension
+                if (availableExtensions != null
+                                && availableExtensions.containsKey(extId)) {
+                   Extension targetExt = (Extension)
+                       availableExtensions.get(extId);
+                   if (targetExt.isCompatibleWith(requiredExt)) {
+                       requiredExt.setFulfilled(true);
+                   }
+                // check the container level list for the extension
+                } else if (containerAvailableExtensions != null
+                        && containerAvailableExtensions.containsKey(extId)) {
+                   Extension targetExt = (Extension)
+                       containerAvailableExtensions.get(extId);
+                   if (targetExt.isCompatibleWith(requiredExt)) {
+                       requiredExt.setFulfilled(true);
+                   }
+                } else {
+                    // Failure
+                    log.info(sm.getString(
+                        "extensionValidator.extension-not-found-error",
+                        appName, mre.getResourceName(),
+                        requiredExt.getExtensionName()));
+                    passes = false;
+                    failureCount++;
+                }
+            }
+        }
+
+        if (!passes) {
+            log.info(sm.getString(
+                     "extensionValidator.extension-validation-error", appName,
+                     failureCount + ""));
+        }
+
+        return passes;
+    }
+    
+   /* 
+    * Build this list of available extensions so that we do not have to 
+    * re-build this list every time we iterate through the list of required 
+    * extensions. All available extensions in all of the 
+    * <code>MainfestResource</code> objects will be added to a 
+    * <code>HashMap</code> which is returned on the first dependency list
+    * processing pass. 
+    *
+    * The key is the name + implementation version.
+    *
+    * NOTE: A list is built only if there is a dependency that needs 
+    * to be checked (performance optimization).
+    *
+    * @param resources A list of <code>ManifestResource</code> objects
+    *
+    * @return HashMap Map of available extensions
+    */
+    private static HashMap buildAvailableExtensionsMap(ArrayList resources) {
+
+        HashMap availableMap = null;
+
+        Iterator it = resources.iterator();
+        while (it.hasNext()) {
+            ManifestResource mre = (ManifestResource)it.next();
+            HashMap map = mre.getAvailableExtensions();
+            if (map != null) {
+                Iterator values = map.values().iterator();
+                while (values.hasNext()) {
+                    Extension ext = (Extension) values.next();
+                    if (availableMap == null) {
+                        availableMap = new HashMap();
+                        availableMap.put(ext.getUniqueId(), ext);
+                    } else if (!availableMap.containsKey(ext.getUniqueId())) {
+                        availableMap.put(ext.getUniqueId(), ext);
+                    }
+                }
+            }
+        }
+
+        return availableMap;
+    }
+    
+    /**
+     * Return the Manifest from a jar file or war file
+     *
+     * @param inStream Input stream to a WAR or JAR file
+     * @return The WAR's or JAR's manifest
+     */
+    private static Manifest getManifest(InputStream inStream)
+            throws IOException {
+
+        Manifest manifest = null;
+        JarInputStream jin = null;
+
+        try {
+            jin = new JarInputStream(inStream);
+            manifest = jin.getManifest();
+            jin.close();
+            jin = null;
+        } finally {
+            if (jin != null) {
+                try {
+                    jin.close();
+                } catch (Throwable t) {
+                    // Ignore
+                }
+            }
+        }
+
+        return manifest;
+    }
+
+
+    /**
+     * Add the JARs specified to the extension list.
+     */
+    private static void addFolderList(String property) {
+
+        // get the files in the extensions directory
+        String extensionsDir = System.getProperty(property);
+        if (extensionsDir != null) {
+            StringTokenizer extensionsTok
+                = new StringTokenizer(extensionsDir, File.pathSeparator);
+            while (extensionsTok.hasMoreTokens()) {
+                File targetDir = new File(extensionsTok.nextToken());
+                if (!targetDir.exists() || !targetDir.isDirectory()) {
+                    continue;
+                }
+                File[] files = targetDir.listFiles();
+                for (int i = 0; i < files.length; i++) {
+                    if (files[i].getName().toLowerCase().endsWith(".jar")) {
+                        try {
+                            addSystemResource(files[i]);
+                        } catch (IOException e) {
+                            log.error
+                                (sm.getString
+                                 ("extensionValidator.failload", files[i]), e);
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/FastDateFormat.java b/container/catalina/src/share/org/apache/catalina/util/FastDateFormat.java
new file mode 100644
index 0000000..f2a2c5c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/FastDateFormat.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.util.Date;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+
+/**
+ * Fast date formatter that caches recently formatted date information
+ * and uses it to avoid too-frequent calls to the underlying
+ * formatter.  Note: breaks fieldPosition param of format(Date,
+ * StringBuffer, FieldPosition).  If you care about the field
+ * position, call the underlying DateFormat directly.
+ *
+ * @author Stan Bailes
+ * @author Alex Chaffee
+ **/
+public class FastDateFormat extends DateFormat {
+    DateFormat    df;
+    long          lastSec = -1;
+    StringBuffer  sb      = new StringBuffer();
+    FieldPosition fp      = new FieldPosition(DateFormat.MILLISECOND_FIELD);
+
+    public FastDateFormat(DateFormat df) {
+        this.df = df;
+    }
+
+    public Date parse(String text, ParsePosition pos) {
+        return df.parse(text, pos);
+    }
+
+    /**
+     * Note: breaks functionality of fieldPosition param. Also:
+     * there's a bug in SimpleDateFormat with "S" and "SS", use "SSS"
+     * instead if you want a msec field.
+     **/
+    public StringBuffer format(Date date, StringBuffer toAppendTo,
+                               FieldPosition fieldPosition) {
+        long dt = date.getTime();
+        long ds = dt / 1000;
+        if (ds != lastSec) {
+            sb.setLength(0);
+            df.format(date, sb, fp);
+            lastSec = ds;
+        } else {
+            // munge current msec into existing string
+            int ms = (int)(dt % 1000);
+            int pos = fp.getEndIndex();
+            int begin = fp.getBeginIndex();
+            if (pos > 0) {
+                if (pos > begin)
+                    sb.setCharAt(--pos, Character.forDigit(ms % 10, 10));
+                ms /= 10;
+                if (pos > begin)
+                    sb.setCharAt(--pos, Character.forDigit(ms % 10, 10));
+                ms /= 10;
+                if (pos > begin)
+                    sb.setCharAt(--pos, Character.forDigit(ms % 10, 10));
+            }
+        }
+        toAppendTo.append(sb.toString());
+        return toAppendTo;
+    }
+
+    public static void main(String[] args) {
+        String format = "yyyy-MM-dd HH:mm:ss.SSS";
+        if (args.length > 0)
+            format = args[0];
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        FastDateFormat fdf = new FastDateFormat(sdf);
+        Date d = new Date();
+
+        d.setTime(1); System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+        d.setTime(20); System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+        d.setTime(500); System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+        d.setTime(543); System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+        d.setTime(999); System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+        d.setTime(1050); System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+        d.setTime(2543); System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+        d.setTime(12345); System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+        d.setTime(12340); System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+
+        final int reps = 100000;
+        {
+            long start = System.currentTimeMillis();
+            for (int i = 0; i < reps; i++) {
+                d.setTime(System.currentTimeMillis());
+                fdf.format(d);
+            }
+            long elap = System.currentTimeMillis() - start;
+            System.out.println("fast: " + elap + " elapsed");
+            System.out.println(fdf.format(d));
+        }
+        {
+            long start = System.currentTimeMillis();
+            for (int i = 0; i < reps; i++) {
+                d.setTime(System.currentTimeMillis());
+                sdf.format(d);
+            }
+            long elap = System.currentTimeMillis() - start;
+            System.out.println("slow: " + elap + " elapsed");
+            System.out.println(sdf.format(d));
+        }
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/HexUtils.java b/container/catalina/src/share/org/apache/catalina/util/HexUtils.java
new file mode 100644
index 0000000..a3d920f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/HexUtils.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Library of utility methods useful in dealing with converting byte arrays
+ * to and from strings of hexadecimal digits.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public final class HexUtils {
+    // Code from Ajp11, from Apache's JServ
+
+    // Table for HEX to DEC byte translation
+    public static final int[] DEC = {
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        00, 01, 02, 03, 04, 05, 06, 07,  8,  9, -1, -1, -1, -1, -1, -1,
+        -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    };
+
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static StringManager sm =
+        StringManager.getManager("org.apache.catalina.util");
+
+
+    /**
+     * Convert a String of hexadecimal digits into the corresponding
+     * byte array by encoding each two hexadecimal digits as a byte.
+     *
+     * @param digits Hexadecimal digits representation
+     *
+     * @exception IllegalArgumentException if an invalid hexadecimal digit
+     *  is found, or the input string contains an odd number of hexadecimal
+     *  digits
+     */
+    public static byte[] convert(String digits) {
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        for (int i = 0; i < digits.length(); i += 2) {
+            char c1 = digits.charAt(i);
+            if ((i+1) >= digits.length())
+                throw new IllegalArgumentException
+                    (sm.getString("hexUtil.odd"));
+            char c2 = digits.charAt(i + 1);
+            byte b = 0;
+            if ((c1 >= '0') && (c1 <= '9'))
+                b += ((c1 - '0') * 16);
+            else if ((c1 >= 'a') && (c1 <= 'f'))
+                b += ((c1 - 'a' + 10) * 16);
+            else if ((c1 >= 'A') && (c1 <= 'F'))
+                b += ((c1 - 'A' + 10) * 16);
+            else
+                throw new IllegalArgumentException
+                    (sm.getString("hexUtil.bad"));
+            if ((c2 >= '0') && (c2 <= '9'))
+                b += (c2 - '0');
+            else if ((c2 >= 'a') && (c2 <= 'f'))
+                b += (c2 - 'a' + 10);
+            else if ((c2 >= 'A') && (c2 <= 'F'))
+                b += (c2 - 'A' + 10);
+            else
+                throw new IllegalArgumentException
+                    (sm.getString("hexUtil.bad"));
+            baos.write(b);
+        }
+        return (baos.toByteArray());
+
+    }
+
+
+    /**
+     * Convert a byte array into a printable format containing a
+     * String of hexadecimal digit characters (two per byte).
+     *
+     * @param bytes Byte array representation
+     */
+    public static String convert(byte bytes[]) {
+
+        StringBuffer sb = new StringBuffer(bytes.length * 2);
+        for (int i = 0; i < bytes.length; i++) {
+            sb.append(convertDigit((int) (bytes[i] >> 4)));
+            sb.append(convertDigit((int) (bytes[i] & 0x0f)));
+        }
+        return (sb.toString());
+
+    }
+
+    /**
+     * Convert 4 hex digits to an int, and return the number of converted
+     * bytes.
+     *
+     * @param hex Byte array containing exactly four hexadecimal digits
+     *
+     * @exception IllegalArgumentException if an invalid hexadecimal digit
+     *  is included
+     */
+    public static int convert2Int( byte[] hex ) {
+        // Code from Ajp11, from Apache's JServ
+
+        // assert b.length==4
+        // assert valid data
+        int len;
+        if(hex.length < 4 ) return 0;
+        if( DEC[hex[0]]<0 )
+            throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+        len = DEC[hex[0]];
+        len = len << 4;
+        if( DEC[hex[1]]<0 )
+            throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+        len += DEC[hex[1]];
+        len = len << 4;
+        if( DEC[hex[2]]<0 )
+            throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+        len += DEC[hex[2]];
+        len = len << 4;
+        if( DEC[hex[3]]<0 )
+            throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+        len += DEC[hex[3]];
+        return len;
+    }
+
+
+
+    /**
+     * [Private] Convert the specified value (0 .. 15) to the corresponding
+     * hexadecimal digit.
+     *
+     * @param value Value to be converted
+     */
+    private static char convertDigit(int value) {
+
+        value &= 0x0f;
+        if (value >= 10)
+            return ((char) (value - 10 + 'a'));
+        else
+            return ((char) (value + '0'));
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/IOTools.java b/container/catalina/src/share/org/apache/catalina/util/IOTools.java
new file mode 100644
index 0000000..5d303f6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/IOTools.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.util;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+
+
+/**
+ * Contains commonly needed I/O-related methods 
+ *
+ * @author Dan Sandberg
+ */
+public class IOTools {
+    protected final static int DEFAULT_BUFFER_SIZE=4*1024; //4k
+
+    //Ensure non-instantiability
+    private IOTools() {
+    }
+
+     /**
+     * Read input from reader and write it to writer until there is no more
+     * input from reader.
+     *
+     * @param reader the reader to read from.
+     * @param writer the writer to write to.
+     * @param buf the char array to use as a bufferx
+     */
+    public static void flow( Reader reader, Writer writer, char[] buf ) 
+        throws IOException {
+        int numRead;
+        while ( (numRead = reader.read(buf) ) >= 0) {
+            writer.write(buf, 0, numRead);
+        }
+    }
+
+    /**
+     * @see #flow( Reader, Writer, char[] )
+     */
+    public static void flow( Reader reader, Writer writer ) 
+        throws IOException {
+        char[] buf = new char[DEFAULT_BUFFER_SIZE];
+        flow( reader, writer, buf );
+    }
+
+    /**
+     * Read input from input stream and write it to output stream 
+     * until there is no more input from input stream.
+     *
+     * @param is input stream the input stream to read from.
+     * @param os output stream the output stream to write to.
+     * @param buf the byte array to use as a buffer
+     */
+    public static void flow( InputStream is, OutputStream os, byte[] buf ) 
+        throws IOException {
+        int numRead;
+        while ( (numRead = is.read(buf) ) >= 0) {
+            os.write(buf, 0, numRead);
+        }
+    }  
+
+    /**
+     * @see #flow( java.io.InputStream, java.io.OutputStream, byte[] )
+     */ 
+    public static void flow( InputStream is, OutputStream os ) 
+        throws IOException {
+        byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
+        flow( is, os, buf );
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/InstanceSupport.java b/container/catalina/src/share/org/apache/catalina/util/InstanceSupport.java
new file mode 100644
index 0000000..ef285ca
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/InstanceSupport.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import org.apache.catalina.InstanceEvent;
+import org.apache.catalina.InstanceListener;
+import org.apache.catalina.Wrapper;
+
+
+/**
+ * Support class to assist in firing InstanceEvent notifications to
+ * registered InstanceListeners.
+ *
+ * @author Craig R. McClanahan
+ * @version $Id$
+ */
+
+public final class InstanceSupport {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new InstanceSupport object associated with the specified
+     * Instance component.
+     *
+     * @param wrapper The component that will be the source
+     *  of events that we fire
+     */
+    public InstanceSupport(Wrapper wrapper) {
+
+        super();
+        this.wrapper = wrapper;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of registered InstanceListeners for event notifications.
+     */
+    private InstanceListener listeners[] = new InstanceListener[0];
+
+
+    /**
+     * The source component for instance events that we will fire.
+     */
+    private Wrapper wrapper = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Wrapper with which we are associated.
+     */
+    public Wrapper getWrapper() {
+
+        return (this.wrapper);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addInstanceListener(InstanceListener listener) {
+
+      synchronized (listeners) {
+          InstanceListener results[] =
+            new InstanceListener[listeners.length + 1];
+          for (int i = 0; i < listeners.length; i++)
+              results[i] = listeners[i];
+          results[listeners.length] = listener;
+          listeners = results;
+      }
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param filter The relevant Filter for this event
+     */
+    public void fireInstanceEvent(String type, Filter filter) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, filter, type);
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param filter The relevant Filter for this event
+     * @param exception Exception that occurred
+     */
+    public void fireInstanceEvent(String type, Filter filter,
+                                  Throwable exception) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, filter, type,
+                                                exception);
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param filter The relevant Filter for this event
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     */
+    public void fireInstanceEvent(String type, Filter filter,
+                                  ServletRequest request,
+                                  ServletResponse response) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, filter, type,
+                                                request, response);
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param filter The relevant Filter for this event
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     * @param exception Exception that occurred
+     */
+    public void fireInstanceEvent(String type, Filter filter,
+                                  ServletRequest request,
+                                  ServletResponse response,
+                                  Throwable exception) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, filter, type,
+                                                request, response, exception);
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param servlet The relevant Servlet for this event
+     */
+    public void fireInstanceEvent(String type, Servlet servlet) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, servlet, type);
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param servlet The relevant Servlet for this event
+     * @param exception Exception that occurred
+     */
+    public void fireInstanceEvent(String type, Servlet servlet,
+                                  Throwable exception) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, servlet, type,
+                                                exception);
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param servlet The relevant Servlet for this event
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     */
+    public void fireInstanceEvent(String type, Servlet servlet,
+                                  ServletRequest request,
+                                  ServletResponse response) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, servlet, type,
+                                                request, response);
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param servlet The relevant Servlet for this event
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     * @param exception Exception that occurred
+     */
+    public void fireInstanceEvent(String type, Servlet servlet,
+                                  ServletRequest request,
+                                  ServletResponse response,
+                                  Throwable exception) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, servlet, type,
+                                                request, response, exception);
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeInstanceListener(InstanceListener listener) {
+
+        synchronized (listeners) {
+            int n = -1;
+            for (int i = 0; i < listeners.length; i++) {
+                if (listeners[i] == listener) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+            InstanceListener results[] =
+              new InstanceListener[listeners.length - 1];
+            int j = 0;
+            for (int i = 0; i < listeners.length; i++) {
+                if (i != n)
+                    results[j++] = listeners[i];
+            }
+            listeners = results;
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/LifecycleSupport.java b/container/catalina/src/share/org/apache/catalina/util/LifecycleSupport.java
new file mode 100644
index 0000000..0cf577b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/LifecycleSupport.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+
+
+/**
+ * Support class to assist in firing LifecycleEvent notifications to
+ * registered LifecycleListeners.
+ *
+ * @author Craig R. McClanahan
+ * @version $Id$
+ */
+
+public final class LifecycleSupport {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new LifecycleSupport object associated with the specified
+     * Lifecycle component.
+     *
+     * @param lifecycle The Lifecycle component that will be the source
+     *  of events that we fire
+     */
+    public LifecycleSupport(Lifecycle lifecycle) {
+
+        super();
+        this.lifecycle = lifecycle;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The source component for lifecycle events that we will fire.
+     */
+    private Lifecycle lifecycle = null;
+
+
+    /**
+     * The set of registered LifecycleListeners for event notifications.
+     */
+    private LifecycleListener listeners[] = new LifecycleListener[0];
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+      synchronized (listeners) {
+          LifecycleListener results[] =
+            new LifecycleListener[listeners.length + 1];
+          for (int i = 0; i < listeners.length; i++)
+              results[i] = listeners[i];
+          results[listeners.length] = listener;
+          listeners = results;
+      }
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return listeners;
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param data Event data
+     */
+    public void fireLifecycleEvent(String type, Object data) {
+
+        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
+        LifecycleListener interested[] = null;
+        synchronized (listeners) {
+            interested = (LifecycleListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].lifecycleEvent(event);
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        synchronized (listeners) {
+            int n = -1;
+            for (int i = 0; i < listeners.length; i++) {
+                if (listeners[i] == listener) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+            LifecycleListener results[] =
+              new LifecycleListener[listeners.length - 1];
+            int j = 0;
+            for (int i = 0; i < listeners.length; i++) {
+                if (i != n)
+                    results[j++] = listeners[i];
+            }
+            listeners = results;
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/util/LocalStrings.properties
new file mode 100644
index 0000000..e5a1229
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/LocalStrings.properties
@@ -0,0 +1,11 @@
+propertyMap.locked=No modifications are allowed to a locked ParameterMap
+resourceSet.locked=No modifications are allowed to a locked ResourceSet
+hexUtil.bad=Bad hexadecimal digit
+hexUtil.odd=Odd number of hexadecimal digits
+#Default Messages Utilized by the ExtensionValidator
+extensionValidator.web-application-manifest=Web Application Manifest
+extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: Required extension "{2}" not found.
+extensionValidator.extension-validation-error=ExtensionValidator[{0}]: Failure to find {1} required extension(s).
+extensionValidator.failload=Failure loading extension {0}
+SecurityUtil.doAsPrivilege=An exception occurs when running the PrivilegedExceptionAction block.
+
diff --git a/container/catalina/src/share/org/apache/catalina/util/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/util/LocalStrings_es.properties
new file mode 100644
index 0000000..3a251a5
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/LocalStrings_es.properties
@@ -0,0 +1,10 @@
+propertyMap.locked=No se permiten modificaciones en un ParameterMap bloqueado
+resourceSet.locked=No se permiten modificaciones en un ResourceSet bloqueado
+hexUtil.bad=Dígito hexadecimal incorrecto
+hexUtil.odd=Número de dígitos hexadecimales impar
+#Default Messages Utilized by the ExtensionValidator
+extensionValidator.web-application-manifest=Manifiesto de Aplicación Web
+extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: La extensión no encuentra el "{2}" requerido.
+extensionValidator.extension-validation-error=ExtensionValidator[{0}]: Imposible de hallar la(s) extension(es) {1} requerida(s).
+SecurityUtil.doAsPrivilege=Una excepción se ha producido durante la ejecución del bloque PrivilegedExceptionAction.
+
diff --git a/container/catalina/src/share/org/apache/catalina/util/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/util/LocalStrings_fr.properties
new file mode 100644
index 0000000..9276a8a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/LocalStrings_fr.properties
@@ -0,0 +1,10 @@
+propertyMap.locked=Aucune modification n''est authorisée sur un ParameterMap vérrouillé
+resourceSet.locked=Aucune modification n''est authorisée sur un ResourceSet vérrouillé
+hexUtil.bad=Mauvais digit hexadecimal
+hexUtil.odd=Nombre impair de digits hexadecimaux
+#Default Messages Utilized by the ExtensionValidator
+extensionValidator.web-application-manifest=Web Application Manifest
+extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: L''extension requise "{2}" est introuvable.
+extensionValidator.extension-validation-error=ExtensionValidator[{0}]: Impossible de trouver {1} extension(s) requise(s).
+SecurityUtil.doAsPrivilege=Une exception s''est produite lors de l''execution du bloc PrivilegedExceptionAction.
+
diff --git a/container/catalina/src/share/org/apache/catalina/util/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/util/LocalStrings_ja.properties
new file mode 100644
index 0000000..d235d64
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/LocalStrings_ja.properties
@@ -0,0 +1,11 @@
+propertyMap.locked=\u30ed\u30c3\u30af\u3055\u308c\u305fParameterMap\u306f\u5909\u66f4\u304c\u8a31\u3055\u308c\u307e\u305b\u3093
+resourceSet.locked=\u30ed\u30c3\u30af\u3055\u308c\u305fResourceSet\u306f\u5909\u66f4\u304c\u8a31\u3055\u308c\u307e\u305b\u3093
+hexUtil.bad=\u7121\u52b9\u306a16\u9032\u6570\u5024\u3067\u3059
+hexUtil.odd=\u5947\u6570\u6841\u306e16\u9032\u6570\u5024\u3067\u3059
+#Default Messages Utilized by the ExtensionValidator
+extensionValidator.web-application-manifest=Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30de\u30cb\u30d5\u30a7\u30b9\u30c8
+extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: \u5fc5\u8981\u306a\u62e1\u5f35 "{2}" \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002
+extensionValidator.extension-validation-error=ExtensionValidator[{0}]: \u5fc5\u8981\u306a\u62e1\u5f35 "{1}" \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002
+extensionValidator.failload=\u62e1\u5f35 {0} \u306e\u30ed\u30fc\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+SecurityUtil.doAsPrivilege=PrivilegedExceptionAction\u30d6\u30ed\u30c3\u30af\u3092\u5b9f\u884c\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
+
diff --git a/container/catalina/src/share/org/apache/catalina/util/MD5Encoder.java b/container/catalina/src/share/org/apache/catalina/util/MD5Encoder.java
new file mode 100644
index 0000000..5cb9ee6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/MD5Encoder.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+/**
+ * Encode an MD5 digest into a String.
+ * <p>
+ * The 128 bit MD5 hash is converted into a 32 character long String.
+ * Each character of the String is the hexadecimal representation of 4 bits
+ * of the digest.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public final class MD5Encoder {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    private static final char[] hexadecimal =
+    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+     'a', 'b', 'c', 'd', 'e', 'f'};
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Encodes the 128 bit (16 bytes) MD5 into a 32 character String.
+     *
+     * @param binaryData Array containing the digest
+     * @return Encoded MD5, or null if encoding failed
+     */
+    public String encode( byte[] binaryData ) {
+
+        if (binaryData.length != 16)
+            return null;
+
+        char[] buffer = new char[32];
+
+        for (int i=0; i<16; i++) {
+            int low = (int) (binaryData[i] & 0x0f);
+            int high = (int) ((binaryData[i] & 0xf0) >> 4);
+            buffer[i*2] = hexadecimal[high];
+            buffer[i*2 + 1] = hexadecimal[low];
+        }
+
+        return new String(buffer);
+
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/util/MIME2Java.java b/container/catalina/src/share/org/apache/catalina/util/MIME2Java.java
new file mode 100644
index 0000000..d15f17a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/MIME2Java.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.util.*;
+
+/**
+ * MIME2Java is a convenience class which handles conversions between MIME charset names
+ * and Java encoding names.
+ * <p>The supported XML encodings are the intersection of XML-supported code sets and those
+ * supported in JDK 1.1.
+ * <p>MIME charset names are used on <var>xmlEncoding</var> parameters to methods such
+ * as <code>TXDocument#setEncoding</code> and <code>DTD#setEncoding</code>.
+ * <p>Java encoding names are used on <var>encoding</var> parameters to
+ * methods such as <code>TXDocument#printWithFormat</code> and <code>DTD#printExternal</code>.
+ * <P>
+ * <TABLE BORDER="0" WIDTH="100%">
+ *  <TR>
+ *      <TD WIDTH="33%">
+ *          <P ALIGN="CENTER"><B>Common Name</B>
+ *      </TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER"><B>Use this name in XML files</B>
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER"><B>Name Type</B>
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER"><B>Xerces converts to this Java Encoder Name</B>
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">8 bit Unicode</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">UTF-8
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">UTF8
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 1</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-1
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-1
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 2</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-2
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-2
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 3</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-3
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-3
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 4</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-4
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-4
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Cyrillic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-5
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-5
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Arabic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-6
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-6
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Greek</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-7
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-7
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Hebrew</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-8
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-8
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 5</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-9
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-9
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: US</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-us
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Canada</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ca
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Netherlands</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-nl
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Denmark</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-dk
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp277
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Norway</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-no
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp277
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Finland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-fi
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp278
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Sweden</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-se
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp278
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Italy</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-it
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp280
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Spain, Latin America</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-es
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp284
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Great Britain</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-gb
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp285
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: France</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-fr
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp297
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Arabic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ar1
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp420
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Hebrew</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-he
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp424
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Switzerland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ch
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp500
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Roece</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-roece
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp870
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Yogoslavia</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-yu
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp870
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Iceland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-is
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp871
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Urdu</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ar2
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp918
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Chinese for PRC, mixed 1/2 byte</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">gb2312
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">GB2312
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Extended Unix Code, packed for Japanese</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">euc-jp
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">eucjis
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Japanese: iso-2022-jp</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">iso-2020-jp
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">JIS
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Japanese: Shift JIS</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">Shift_JIS
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">SJIS
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Chinese: Big5</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">Big5
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">Big5
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Extended Unix Code, packed for Korean</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">euc-kr
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">iso2022kr
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Cyrillic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">koi8-r
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">koi8-r
+ *      </TD>
+ *  </TR>
+ * </TABLE>
+ *
+ * @version $Revision$ $Date$
+ * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
+ */
+public class MIME2Java {
+
+    static private Hashtable s_enchash;
+    static private Hashtable s_revhash;
+
+    static {
+        s_enchash = new Hashtable();
+        //    <preferred MIME name>, <Java encoding name>
+        s_enchash.put("UTF-8", "UTF8");
+        s_enchash.put("US-ASCII",        "8859_1");    // ?
+        s_enchash.put("ISO-8859-1",      "8859_1");
+        s_enchash.put("ISO-8859-2",      "8859_2");
+        s_enchash.put("ISO-8859-3",      "8859_3");
+        s_enchash.put("ISO-8859-4",      "8859_4");
+        s_enchash.put("ISO-8859-5",      "8859_5");
+        s_enchash.put("ISO-8859-6",      "8859_6");
+        s_enchash.put("ISO-8859-7",      "8859_7");
+        s_enchash.put("ISO-8859-8",      "8859_8");
+        s_enchash.put("ISO-8859-9",      "8859_9");
+        s_enchash.put("ISO-2022-JP",     "JIS");
+        s_enchash.put("SHIFT_JIS",       "SJIS");
+        s_enchash.put("EUC-JP",          "EUCJIS");
+        s_enchash.put("GB2312",          "GB2312");
+        s_enchash.put("BIG5",            "Big5");
+        s_enchash.put("EUC-KR",          "KSC5601");
+        s_enchash.put("ISO-2022-KR",     "ISO2022KR");
+        s_enchash.put("KOI8-R",          "KOI8_R");
+
+        s_enchash.put("EBCDIC-CP-US",    "CP037");
+        s_enchash.put("EBCDIC-CP-CA",    "CP037");
+        s_enchash.put("EBCDIC-CP-NL",    "CP037");
+        s_enchash.put("EBCDIC-CP-DK",    "CP277");
+        s_enchash.put("EBCDIC-CP-NO",    "CP277");
+        s_enchash.put("EBCDIC-CP-FI",    "CP278");
+        s_enchash.put("EBCDIC-CP-SE",    "CP278");
+        s_enchash.put("EBCDIC-CP-IT",    "CP280");
+        s_enchash.put("EBCDIC-CP-ES",    "CP284");
+        s_enchash.put("EBCDIC-CP-GB",    "CP285");
+        s_enchash.put("EBCDIC-CP-FR",    "CP297");
+        s_enchash.put("EBCDIC-CP-AR1",   "CP420");
+        s_enchash.put("EBCDIC-CP-HE",    "CP424");
+        s_enchash.put("EBCDIC-CP-CH",    "CP500");
+        s_enchash.put("EBCDIC-CP-ROECE", "CP870");
+        s_enchash.put("EBCDIC-CP-YU",    "CP870");
+        s_enchash.put("EBCDIC-CP-IS",    "CP871");
+        s_enchash.put("EBCDIC-CP-AR2",   "CP918");
+
+                                                // j:CNS11643 -> EUC-TW?
+                                                // ISO-2022-CN? ISO-2022-CN-EXT?
+
+        s_revhash = new Hashtable();
+        //    <Java encoding name>, <preferred MIME name>
+        s_revhash.put("UTF8", "UTF-8");
+        //s_revhash.put("8859_1", "US-ASCII");    // ?
+        s_revhash.put("8859_1", "ISO-8859-1");
+        s_revhash.put("8859_2", "ISO-8859-2");
+        s_revhash.put("8859_3", "ISO-8859-3");
+        s_revhash.put("8859_4", "ISO-8859-4");
+        s_revhash.put("8859_5", "ISO-8859-5");
+        s_revhash.put("8859_6", "ISO-8859-6");
+        s_revhash.put("8859_7", "ISO-8859-7");
+        s_revhash.put("8859_8", "ISO-8859-8");
+        s_revhash.put("8859_9", "ISO-8859-9");
+        s_revhash.put("JIS", "ISO-2022-JP");
+        s_revhash.put("SJIS", "Shift_JIS");
+        s_revhash.put("EUCJIS", "EUC-JP");
+        s_revhash.put("GB2312", "GB2312");
+        s_revhash.put("BIG5", "Big5");
+        s_revhash.put("KSC5601", "EUC-KR");
+        s_revhash.put("ISO2022KR", "ISO-2022-KR");
+        s_revhash.put("KOI8_R", "KOI8-R");
+
+        s_revhash.put("CP037", "EBCDIC-CP-US");
+        s_revhash.put("CP037", "EBCDIC-CP-CA");
+        s_revhash.put("CP037", "EBCDIC-CP-NL");
+        s_revhash.put("CP277", "EBCDIC-CP-DK");
+        s_revhash.put("CP277", "EBCDIC-CP-NO");
+        s_revhash.put("CP278", "EBCDIC-CP-FI");
+        s_revhash.put("CP278", "EBCDIC-CP-SE");
+        s_revhash.put("CP280", "EBCDIC-CP-IT");
+        s_revhash.put("CP284", "EBCDIC-CP-ES");
+        s_revhash.put("CP285", "EBCDIC-CP-GB");
+        s_revhash.put("CP297", "EBCDIC-CP-FR");
+        s_revhash.put("CP420", "EBCDIC-CP-AR1");
+        s_revhash.put("CP424", "EBCDIC-CP-HE");
+        s_revhash.put("CP500", "EBCDIC-CP-CH");
+        s_revhash.put("CP870", "EBCDIC-CP-ROECE");
+        s_revhash.put("CP870", "EBCDIC-CP-YU");
+        s_revhash.put("CP871", "EBCDIC-CP-IS");
+        s_revhash.put("CP918", "EBCDIC-CP-AR2");
+    }
+
+    private MIME2Java() {
+    }
+
+    /**
+     * Convert a MIME charset name, also known as an XML encoding name, to a Java encoding name.
+     * @param   mimeCharsetName Case insensitive MIME charset name: <code>UTF-8, US-ASCII, ISO-8859-1,
+     *                          ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6,
+     *                          ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-2022-JP, Shift_JIS,
+     *                          EUC-JP, GB2312, Big5, EUC-KR, ISO-2022-KR, KOI8-R,
+     *                          EBCDIC-CP-US, EBCDIC-CP-CA, EBCDIC-CP-NL, EBCDIC-CP-DK,
+     *                          EBCDIC-CP-NO, EBCDIC-CP-FI, EBCDIC-CP-SE, EBCDIC-CP-IT,
+     *                          EBCDIC-CP-ES, EBCDIC-CP-GB, EBCDIC-CP-FR, EBCDIC-CP-AR1,
+     *                          EBCDIC-CP-HE, EBCDIC-CP-CH, EBCDIC-CP-ROECE, EBCDIC-CP-YU,
+     *                          EBCDIC-CP-IS and EBCDIC-CP-AR2</code>.
+     * @return                  Java encoding name, or <var>null</var> if <var>mimeCharsetName</var>
+     *                          is unknown.
+     * @see #reverse
+     */
+    public static String convert(String mimeCharsetName) {
+        return (String)s_enchash.get(mimeCharsetName.toUpperCase());
+    }
+
+    /**
+     * Convert a Java encoding name to MIME charset name.
+     * Available values of <i>encoding</i> are "UTF8", "8859_1", "8859_2", "8859_3", "8859_4",
+     * "8859_5", "8859_6", "8859_7", "8859_8", "8859_9", "JIS", "SJIS", "EUCJIS",
+     * "GB2312", "BIG5", "KSC5601", "ISO2022KR",  "KOI8_R", "CP037", "CP277", "CP278",
+     * "CP280", "CP284", "CP285", "CP297", "CP420", "CP424", "CP500", "CP870", "CP871" and "CP918".
+     * @param   encoding    Case insensitive Java encoding name: <code>UTF8, 8859_1, 8859_2, 8859_3,
+     *                      8859_4, 8859_5, 8859_6, 8859_7, 8859_8, 8859_9, JIS, SJIS, EUCJIS,
+     *                      GB2312, BIG5, KSC5601, ISO2022KR, KOI8_R, CP037, CP277, CP278,
+     *                      CP280, CP284, CP285, CP297, CP420, CP424, CP500, CP870, CP871
+     *                      and CP918</code>.
+     * @return              MIME charset name, or <var>null</var> if <var>encoding</var> is unknown.
+     * @see #convert
+     */
+    public static String reverse(String encoding) {
+        return (String)s_revhash.get(encoding.toUpperCase());
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/ManifestResource.java b/container/catalina/src/share/org/apache/catalina/util/ManifestResource.java
new file mode 100644
index 0000000..d628ac7
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/ManifestResource.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.util;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes;
+import java.util.ArrayList;
+
+/**
+ *  Representation of a Manifest file and its available extensions and
+ *  required extensions
+ *  
+ * @author Greg Murray
+ * @author Justyna Horwat
+ * @version $Revision$ $Date$
+ * 
+ */
+public class ManifestResource {
+    
+    // ------------------------------------------------------------- Properties
+
+    // These are the resource types for determining effect error messages
+    public static final int SYSTEM = 1;
+    public static final int WAR = 2;
+    public static final int APPLICATION = 3;
+    
+    private HashMap availableExtensions = null;
+    private ArrayList requiredExtensions = null;
+    
+    private String resourceName = null;
+    private int resourceType = -1;
+        
+    public ManifestResource(String resourceName, Manifest manifest, 
+                            int resourceType) {
+        this.resourceName = resourceName;
+        this.resourceType = resourceType;
+        processManifest(manifest);
+    }
+    
+    /**
+     * Gets the name of the resource
+     *
+     * @return The name of the resource
+     */
+    public String getResourceName() {
+        return resourceName;
+    }
+
+    /**
+     * Gets the map of available extensions
+     *
+     * @return Map of available extensions
+     */
+    public HashMap getAvailableExtensions() {
+        return availableExtensions;
+    }
+    
+    /**
+     * Gets the list of required extensions
+     *
+     * @return List of required extensions
+     */
+    public ArrayList getRequiredExtensions() {
+        return requiredExtensions;   
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Gets the number of available extensions
+     *
+     * @return The number of available extensions
+     */
+    public int getAvailableExtensionCount() {
+        return (availableExtensions != null) ? availableExtensions.size() : 0;
+    }
+    
+    /**
+     * Gets the number of required extensions
+     *
+     * @return The number of required extensions
+     */
+    public int getRequiredExtensionCount() {
+        return (requiredExtensions != null) ? requiredExtensions.size() : 0;
+    }
+    
+    /**
+     * Convienience method to check if this <code>ManifestResource</code>
+     * has an requires extensions.
+     *
+     * @return true if required extensions are present
+     */
+    public boolean requiresExtensions() {
+        return (requiredExtensions != null) ? true : false;
+    }
+    
+    /**
+     * Convienience method to check if this <code>ManifestResource</code>
+     * has an extension available.
+     *
+     * @param key extension identifier
+     *
+     * @return true if extension available
+     */
+    public boolean containsExtension(String key) {
+        return (availableExtensions != null) ?
+                availableExtensions.containsKey(key) : false;
+    }
+    
+    /**
+     * Returns <code>true</code> if all required extension dependencies
+     * have been meet for this <code>ManifestResource</code> object.
+     *
+     * @return boolean true if all extension dependencies have been satisfied
+     */
+    public boolean isFulfilled() {
+        if (requiredExtensions == null) {
+            return false;
+        }
+        Iterator it = requiredExtensions.iterator();
+        while (it.hasNext()) {
+            Extension ext = (Extension)it.next();
+            if (!ext.isFulfilled()) return false;            
+        }
+        return true;
+    }
+    
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ManifestResource[");
+        sb.append(resourceName);
+
+        sb.append(", isFulfilled=");
+        sb.append(isFulfilled() +"");
+        sb.append(", requiredExtensionCount =");
+        sb.append(getRequiredExtensionCount());
+        sb.append(", availableExtensionCount=");
+        sb.append(getAvailableExtensionCount());
+        switch (resourceType) {
+            case SYSTEM : sb.append(", resourceType=SYSTEM"); break;
+            case WAR : sb.append(", resourceType=WAR"); break;
+            case APPLICATION : sb.append(", resourceType=APPLICATION"); break;
+        }
+        sb.append("]");
+        return (sb.toString());
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+    private void processManifest(Manifest manifest) {
+        availableExtensions = getAvailableExtensions(manifest);
+        requiredExtensions = getRequiredExtensions(manifest);
+    }
+    
+    /**
+     * Return the set of <code>Extension</code> objects representing optional
+     * packages that are required by the application associated with the
+     * specified <code>Manifest</code>.
+     *
+     * @param manifest Manifest to be parsed
+     *
+     * @return List of required extensions, or null if the application
+     * does not require any extensions
+     */
+    private ArrayList getRequiredExtensions(Manifest manifest) {
+
+        Attributes attributes = manifest.getMainAttributes();
+        String names = attributes.getValue("Extension-List");
+        if (names == null)
+            return null;
+
+        ArrayList extensionList = new ArrayList();
+        names += " ";
+
+        while (true) {
+
+            int space = names.indexOf(' ');
+            if (space < 0)
+                break;
+            String name = names.substring(0, space).trim();
+            names = names.substring(space + 1);
+
+            String value =
+                attributes.getValue(name + "-Extension-Name");
+            if (value == null)
+                continue;
+            Extension extension = new Extension();
+            extension.setExtensionName(value);
+            extension.setImplementationURL
+                (attributes.getValue(name + "-Implementation-URL"));
+            extension.setImplementationVendorId
+                (attributes.getValue(name + "-Implementation-Vendor-Id"));
+            String version = attributes.getValue(name + "-Implementation-Version");
+            extension.setImplementationVersion(version);
+            extension.setSpecificationVersion
+                (attributes.getValue(name + "-Specification-Version"));
+            extensionList.add(extension);
+        }
+        return extensionList;
+    }
+    
+    /**
+     * Return the set of <code>Extension</code> objects representing optional
+     * packages that are bundled with the application associated with the
+     * specified <code>Manifest</code>.
+     *
+     * @param manifest Manifest to be parsed
+     *
+     * @return Map of available extensions, or null if the web application
+     * does not bundle any extensions
+     */
+    private HashMap getAvailableExtensions(Manifest manifest) {
+
+        Attributes attributes = manifest.getMainAttributes();
+        String name = attributes.getValue("Extension-Name");
+        if (name == null)
+            return null;
+
+        HashMap extensionMap = new HashMap();
+
+        Extension extension = new Extension();
+        extension.setExtensionName(name);
+        extension.setImplementationURL(
+            attributes.getValue("Implementation-URL"));
+        extension.setImplementationVendor(
+            attributes.getValue("Implementation-Vendor"));
+        extension.setImplementationVendorId(
+            attributes.getValue("Implementation-Vendor-Id"));
+        extension.setImplementationVersion(
+            attributes.getValue("Implementation-Version"));
+        extension.setSpecificationVersion(
+            attributes.getValue("Specification-Version"));
+
+        if (!extensionMap.containsKey(extension.getUniqueId())) {
+            extensionMap.put(extension.getUniqueId(), extension);
+        }
+
+        return extensionMap;
+    }
+    
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/ParameterMap.java b/container/catalina/src/share/org/apache/catalina/util/ParameterMap.java
new file mode 100644
index 0000000..135ab46
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/ParameterMap.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Extended implementation of <strong>HashMap</strong> that includes a
+ * <code>locked</code> property.  This class can be used to safely expose
+ * Catalina internal parameter map objects to user classes without having
+ * to clone them in order to avoid modifications.  When first created, a
+ * <code>ParmaeterMap</code> instance is not locked.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class ParameterMap extends HashMap {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new, empty map with the default initial capacity and
+     * load factor.
+     */
+    public ParameterMap() {
+
+        super();
+
+    }
+
+
+    /**
+     * Construct a new, empty map with the specified initial capacity and
+     * default load factor.
+     *
+     * @param initialCapacity The initial capacity of this map
+     */
+    public ParameterMap(int initialCapacity) {
+
+        super(initialCapacity);
+
+    }
+
+
+    /**
+     * Construct a new, empty map with the specified initial capacity and
+     * load factor.
+     *
+     * @param initialCapacity The initial capacity of this map
+     * @param loadFactor The load factor of this map
+     */
+    public ParameterMap(int initialCapacity, float loadFactor) {
+
+        super(initialCapacity, loadFactor);
+
+    }
+
+
+    /**
+     * Construct a new map with the same mappings as the given map.
+     *
+     * @param map Map whose contents are dupliated in the new map
+     */
+    public ParameterMap(Map map) {
+
+        super(map);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The current lock state of this parameter map.
+     */
+    private boolean locked = false;
+
+
+    /**
+     * Return the locked state of this parameter map.
+     */
+    public boolean isLocked() {
+
+        return (this.locked);
+
+    }
+
+
+    /**
+     * Set the locked state of this parameter map.
+     *
+     * @param locked The new locked state
+     */
+    public void setLocked(boolean locked) {
+
+        this.locked = locked;
+
+    }
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager("org.apache.catalina.util");
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+
+    /**
+     * Remove all mappings from this map.
+     *
+     * @exception IllegalStateException if this map is currently locked
+     */
+    public void clear() {
+
+        if (locked)
+            throw new IllegalStateException
+                (sm.getString("parameterMap.locked"));
+        super.clear();
+
+    }
+
+
+    /**
+     * Associate the specified value with the specified key in this map.  If
+     * the map previously contained a mapping for this key, the old value is
+     * replaced.
+     *
+     * @param key Key with which the specified value is to be associated
+     * @param value Value to be associated with the specified key
+     *
+     * @return The previous value associated with the specified key, or
+     *  <code>null</code> if there was no mapping for key
+     *
+     * @exception IllegalStateException if this map is currently locked
+     */
+    public Object put(Object key, Object value) {
+
+        if (locked)
+            throw new IllegalStateException
+                (sm.getString("parameterMap.locked"));
+        return (super.put(key, value));
+
+    }
+
+
+    /**
+     * Copy all of the mappings from the specified map to this one.  These
+     * mappings replace any mappings that this map had for any of the keys
+     * currently in the specified Map.
+     *
+     * @param map Mappings to be stored into this map
+     *
+     * @exception IllegalStateException if this map is currently locked
+     */
+    public void putAll(Map map) {
+
+        if (locked)
+            throw new IllegalStateException
+                (sm.getString("parameterMap.locked"));
+        super.putAll(map);
+
+    }
+
+
+    /**
+     * Remove the mapping for this key from the map if present.
+     *
+     * @param key Key whose mapping is to be removed from the map
+     *
+     * @return The previous value associated with the specified key, or
+     *  <code>null</code> if there was no mapping for that key
+     *
+     * @exception IllegalStateException if this map is currently locked
+     */
+    public Object remove(Object key) {
+
+        if (locked)
+            throw new IllegalStateException
+                (sm.getString("parameterMap.locked"));
+        return (super.remove(key));
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/ProcessEnvironment.java b/container/catalina/src/share/org/apache/catalina/util/ProcessEnvironment.java
new file mode 100644
index 0000000..32eeb31
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/ProcessEnvironment.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+
+
+/**
+ * Encapsulates the Process environment and rules to derive
+ * that environment from the servlet container and request information.
+ * @author   Martin Dengler [root@martindengler.com]
+ * @version  $Revision$, $Date$
+ * @since    Tomcat 4.0
+ */
+public class ProcessEnvironment {
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( ProcessEnvironment.class );
+    
+    /** context of the enclosing servlet */
+    private ServletContext context = null;
+
+    /** real file system directory of the enclosing servlet's web app */
+    private String webAppRootDir = null;
+
+    /** context path of enclosing servlet */
+    private String contextPath = null;
+
+    /** pathInfo for the current request */
+    protected String pathInfo = null;
+
+    /** servlet URI of the enclosing servlet */
+    private String servletPath = null;
+
+    /** derived process environment */
+    protected Hashtable env = null;
+
+    /** command to be invoked */
+    protected String command = null;
+
+    /** whether or not this object is valid or not */
+    protected boolean valid = false;
+
+    /** the debugging detail level for this instance. */
+    protected int debug = 0;
+
+    /** process' desired working directory */
+    protected File workingDirectory = null;
+
+
+    /**
+     * Creates a ProcessEnvironment and derives the necessary environment,
+     * working directory, command, etc.
+     * @param  req       HttpServletRequest for information provided by
+     *                   the Servlet API
+     * @param  context   ServletContext for information provided by
+     *                   the Servlet API
+     */
+    public ProcessEnvironment(HttpServletRequest req,
+        ServletContext context) {
+        this(req, context, 0);
+    }
+
+
+    /**
+     * Creates a ProcessEnvironment and derives the necessary environment,
+     * working directory, command, etc.
+     * @param  req       HttpServletRequest for information provided by
+     *                   the Servlet API
+     * @param  context   ServletContext for information provided by
+     *                   the Servlet API
+     * @param  debug     int debug level (0 == none, 4 == medium, 6 == lots)
+     */
+    public ProcessEnvironment(HttpServletRequest req,
+        ServletContext context, int debug) {
+            this.debug = debug;
+            setupFromContext(context);
+            setupFromRequest(req);
+            this.valid = deriveProcessEnvironment(req);
+            if (log.isDebugEnabled()) 
+                log.debug(this.getClass().getName() + "() ctor, debug level " + 
+                          debug);
+    }
+
+
+    /**
+     * Uses the ServletContext to set some process variables
+     * @param  context   ServletContext for information provided by
+     *                   the Servlet API
+     */
+    protected void setupFromContext(ServletContext context) {
+        this.context = context;
+        this.webAppRootDir = context.getRealPath("/");
+    }
+
+
+    /**
+     * Uses the HttpServletRequest to set most process variables
+     * @param  req   HttpServletRequest for information provided by
+     *               the Servlet API
+     */
+    protected void setupFromRequest(HttpServletRequest req) {
+        this.contextPath = req.getContextPath();
+        this.pathInfo = req.getPathInfo();
+        this.servletPath = req.getServletPath();
+    }
+
+
+    /**
+     * Print important process environment information in an
+     * easy-to-read HTML table
+     * @return  HTML string containing process environment info
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("<TABLE border=2>");
+        sb.append("<tr><th colspan=2 bgcolor=grey>");
+        sb.append("ProcessEnvironment Info</th></tr>");
+        sb.append("<tr><td>Debug Level</td><td>");
+        sb.append(debug);
+        sb.append("</td></tr>");
+        sb.append("<tr><td>Validity:</td><td>");
+        sb.append(isValid());
+        sb.append("</td></tr>");
+        if (isValid()) {
+            Enumeration envk = env.keys();
+            while (envk.hasMoreElements()) {
+                String s = (String)envk.nextElement();
+                sb.append("<tr><td>");
+                sb.append(s);
+                sb.append("</td><td>");
+                sb.append(blanksToString((String)env.get(s),
+                    "[will be set to blank]"));
+                    sb.append("</td></tr>");
+            }
+        }
+        sb.append("<tr><td colspan=2><HR></td></tr>");
+        sb.append("<tr><td>Derived Command</td><td>");
+        sb.append(nullsToBlanks(command));
+        sb.append("</td></tr>");
+        sb.append("<tr><td>Working Directory</td><td>");
+        if (workingDirectory != null) {
+            sb.append(workingDirectory.toString());
+        }
+        sb.append("</td></tr>");
+        sb.append("</TABLE><p>end.");
+        return sb.toString();
+    }
+
+
+    /**
+     * Gets derived command string
+     * @return  command string
+     */
+    public String getCommand() {
+        return command;
+    }
+
+
+    /**
+     * Sets the desired command string
+     * @param   command String command as desired
+     * @return  command string
+     */
+    protected String setCommand(String command) {
+        return command;
+    }
+
+
+    /**
+     * Gets this process' derived working directory
+     * @return  working directory
+     */
+    public File getWorkingDirectory() {
+        return workingDirectory;
+    }
+
+
+    /**
+     * Gets process' environment
+     * @return   process' environment
+     */
+    public Hashtable getEnvironment() {
+        return env;
+    }
+
+
+    /**
+     * Sets process' environment
+     * @param    env process' environment
+     * @return   Hashtable to which the process' environment was set
+     */
+    public Hashtable setEnvironment(Hashtable env) {
+        this.env = env;
+        return this.env;
+    }
+
+
+    /**
+     * Gets validity status
+     * @return   true if this environment is valid, false otherwise
+     */
+    public boolean isValid() {
+        return valid;
+    }
+
+
+    /**
+     * Converts null strings to blank strings ("")
+     * @param    s string to be converted if necessary
+     * @return   a non-null string, either the original or the empty string
+     *           ("") if the original was <code>null</code>
+     */
+    protected String nullsToBlanks(String s) {
+        return nullsToString(s, "");
+    }
+
+
+    /**
+     * Converts null strings to another string
+     * @param    couldBeNull string to be converted if necessary
+     * @param    subForNulls string to return instead of a null string
+     * @return   a non-null string, either the original or the substitute
+     *           string if the original was <code>null</code>
+     */
+    protected String nullsToString(String couldBeNull, String subForNulls) {
+        return (couldBeNull == null ? subForNulls : couldBeNull);
+    }
+
+
+    /**
+     * Converts blank strings to another string
+     * @param    couldBeBlank string to be converted if necessary
+     * @param    subForBlanks string to return instead of a blank string
+     * @return   a non-null string, either the original or the substitute
+     *           string if the original was <code>null</code> or empty ("")
+     */
+    protected String blanksToString(String couldBeBlank,
+        String subForBlanks) {
+            return (("".equals(couldBeBlank) || couldBeBlank == null) ?
+                subForBlanks : couldBeBlank);
+    }
+
+
+    /**
+     * Constructs the Process environment to be supplied to the invoked
+     * process.  Defines an environment no environment variables.
+     * <p>
+     * Should be overriden by subclasses to perform useful setup.
+     * </p>
+     *
+     * @param    req request associated with the
+     *           Process' invocation
+     * @return   true if environment was set OK, false if there was a problem
+     *           and no environment was set
+     */
+    protected boolean deriveProcessEnvironment(HttpServletRequest req) {
+
+        Hashtable envp = new Hashtable();
+        command = getCommand();
+        if (command != null) {
+            workingDirectory = new
+                File(command.substring(0,
+                command.lastIndexOf(File.separator)));
+                envp.put("X_TOMCAT_COMMAND_PATH", command); //for kicks
+        }
+        this.env = envp;
+        return true;
+    }
+
+
+    /**
+     * Gets the root directory of the web application to which this process\
+     * belongs
+     * @return  root directory
+     */
+    public String getWebAppRootDir() {
+        return webAppRootDir;
+    }
+
+
+    public String getContextPath(){
+            return contextPath;
+        }
+
+
+    public ServletContext getContext(){
+            return context;
+        }
+
+
+    public String getServletPath(){
+            return servletPath;
+        }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/ProcessHelper.java b/container/catalina/src/share/org/apache/catalina/util/ProcessHelper.java
new file mode 100644
index 0000000..eab5d8f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/ProcessHelper.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+     * Encapsulates the knowledge of how to run a CGI script, given the
+     * script's desired environment and (optionally) input/output streams
+     *
+     * <p>
+     *
+     * Exposes a <code>run</code> method used to actually invoke the
+     * CGI.
+     *
+     * </p>
+     * <p>
+     *
+     * The CGI environment and settings are derived from the information
+     * passed to the constuctor.
+     *
+     * </p>
+     * <p>
+     *
+     * The input and output streams can be set by the <code>setInput</code>
+     * and <code>setResponse</code> methods, respectively.
+     * </p>
+     *
+     * @author    Martin Dengler [root@martindengler.com]
+     * @version   $Revision$, $Date$
+     */
+public class ProcessHelper {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( ProcessHelper.class );
+    
+    /** script/command to be executed */
+    private String command = null;
+
+    /** environment used when invoking the cgi script */
+    private Hashtable env = null;
+
+    /** working directory used when invoking the cgi script */
+    private File wd = null;
+
+    /** query parameters to be passed to the invoked script */
+    private Hashtable params = null;
+
+    /** stdin to be passed to cgi script */
+    private InputStream stdin = null;
+
+    /** response object used to set headers & get output stream */
+    private HttpServletResponse response = null;
+
+    /** boolean tracking whether this object has enough info to run() */
+    private boolean readyToRun = false;
+
+    /** the debugging detail level for this instance. */
+    private int debug = 0;
+
+    /** the time in ms to wait for the client to send us CGI input data */
+    private int iClientInputTimeout;
+
+    /**
+     *  Creates a ProcessHelper and initializes its environment, working
+     *  directory, and query parameters.
+     *  <BR>
+     *  Input/output streams (optional) are set using the
+     *  <code>setInput</code> and <code>setResponse</code> methods,
+     *  respectively.
+     *
+     * @param  command  string full path to command to be executed
+     * @param  env      Hashtable with the desired script environment
+     * @param  wd       File with the script's desired working directory
+     * @param  params   Hashtable with the script's query parameters
+     */
+    public ProcessHelper(
+        String command,
+        Hashtable env,
+        File wd,
+        Hashtable params) {
+        this.command = command;
+        this.env = env;
+        this.wd = wd;
+        this.params = params;
+        updateReadyStatus();
+    }
+
+    /**
+     * Checks & sets ready status
+     */
+    protected void updateReadyStatus() {
+        if (command != null
+            && env != null
+            && wd != null
+            && params != null
+            && response != null) {
+            readyToRun = true;
+        } else {
+            readyToRun = false;
+        }
+    }
+
+    /**
+     * Gets ready status
+     *
+     * @return   false if not ready (<code>run</code> will throw
+     *           an exception), true if ready
+     */
+    public boolean isReady() {
+        return readyToRun;
+    }
+
+    /**
+     * Sets HttpServletResponse object used to set headers and send
+     * output to
+     *
+     * @param  response   HttpServletResponse to be used
+     *
+     */
+    public void setResponse(HttpServletResponse response) {
+        this.response = response;
+        updateReadyStatus();
+    }
+
+    /**
+     * Sets standard input to be passed on to the invoked cgi script
+     *
+     * @param  stdin   InputStream to be used
+     *
+     */
+    public void setInput(InputStream stdin) {
+        this.stdin = stdin;
+        updateReadyStatus();
+    }
+
+    /**
+     * Converts a Hashtable to a String array by converting each
+     * key/value pair in the Hashtable to a String in the form
+     * "key=value" (hashkey + "=" + hash.get(hashkey).toString())
+     *
+     * @param  h   Hashtable to convert
+     *
+     * @return     converted string array
+     *
+     * @exception  NullPointerException   if a hash key has a null value
+     *
+     */
+    private String[] hashToStringArray(Hashtable h)
+        throws NullPointerException {
+        Vector v = new Vector();
+        Enumeration e = h.keys();
+        while (e.hasMoreElements()) {
+            String k = e.nextElement().toString();
+            v.add(k + "=" + h.get(k));
+        }
+        String[] strArr = new String[v.size()];
+        v.copyInto(strArr);
+        return strArr;
+    }
+
+    /**
+     * Executes a process script with the desired environment, current working
+     * directory, and input/output streams
+     *
+     * <p>
+     * This implements the following CGI specification recommedations:
+     * <UL>
+     * <LI> Servers SHOULD provide the "<code>query</code>" component of
+     *      the script-URI as command-line arguments to scripts if it
+     *      does not contain any unencoded "=" characters and the
+     *      command-line arguments can be generated in an unambiguous
+     *      manner.
+     * <LI> Servers SHOULD set the AUTH_TYPE metavariable to the value
+     *      of the "<code>auth-scheme</code>" token of the
+     *      "<code>Authorization</code>" if it was supplied as part of the
+     *      request header.  See <code>getCGIEnvironment</code> method.
+     * <LI> Where applicable, servers SHOULD set the current working
+     *      directory to the directory in which the script is located
+     *      before invoking it.
+     * <LI> Server implementations SHOULD define their behavior for the
+     *      following cases:
+     *     <ul>
+     *     <LI> <u>Allowed characters in pathInfo</u>:  This implementation
+     *             does not allow ASCII NUL nor any character which cannot
+     *             be URL-encoded according to internet standards;
+     *     <LI> <u>Allowed characters in path segments</u>: This
+     *             implementation does not allow non-terminal NULL
+     *             segments in the the path -- IOExceptions may be thrown;
+     *     <LI> <u>"<code>.</code>" and "<code>..</code>" path
+     *             segments</u>:
+     *             This implementation does not allow "<code>.</code>" and
+     *             "<code>..</code>" in the the path, and such characters
+     *             will result in an IOException being thrown;
+     *     <LI> <u>Implementation limitations</u>: This implementation
+     *             does not impose any limitations except as documented
+     *             above.  This implementation may be limited by the
+     *             servlet container used to house this implementation.
+     *             In particular, all the primary CGI variable values
+     *             are derived either directly or indirectly from the
+     *             container's implementation of the Servlet API methods.
+     *     </ul>
+     * </UL>
+     * </p>
+     *
+     * For more information, see java.lang.Runtime#exec(String command, 
+     * String[] envp, File dir)
+     *
+     * @exception IOException if problems during reading/writing occur
+     *
+     */
+    public void run() throws IOException {
+
+        /*
+         * REMIND:  this method feels too big; should it be re-written?
+         */
+
+        if (!isReady()) {
+            throw new IOException(
+                this.getClass().getName() + ": not ready to run.");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("runCGI(envp=[" + env + "], command=" + command + ")");
+        }
+
+        if ((command.indexOf(File.separator + "." + File.separator) >= 0)
+            || (command.indexOf(File.separator + "..") >= 0)
+            || (command.indexOf(".." + File.separator) >= 0)) {
+            throw new IOException(
+                this.getClass().getName()
+                    + "Illegal Character in CGI command "
+                    + "path ('.' or '..') detected.  Not "
+                    + "running CGI ["
+                    + command
+                    + "].");
+        }
+
+        /* original content/structure of this section taken from
+         * http://developer.java.sun.com/developer/
+         *                               bugParade/bugs/4216884.html
+         * with major modifications by Martin Dengler
+         */
+        Runtime rt = null;
+        BufferedReader commandsStdOut = null;
+        BufferedReader commandsStdErr = null;
+        BufferedOutputStream commandsStdIn = null;
+        Process proc = null;
+        byte[] bBuf = new byte[1024];
+        char[] cBuf = new char[1024];
+        int bufRead = -1;
+
+        //create query arguments
+        Enumeration paramNames = params.keys();
+        StringBuffer cmdAndArgs = new StringBuffer(command);
+        if (paramNames != null && paramNames.hasMoreElements()) {
+            cmdAndArgs.append(" ");
+            while (paramNames.hasMoreElements()) {
+                String k = (String) paramNames.nextElement();
+                String v = params.get(k).toString();
+                if ((k.indexOf("=") < 0) && (v.indexOf("=") < 0)) {
+                    cmdAndArgs.append(k);
+                    cmdAndArgs.append("=");
+                    v = java.net.URLEncoder.encode(v);
+                    cmdAndArgs.append(v);
+                    cmdAndArgs.append(" ");
+                }
+            }
+        }
+
+        String postIn = getPostInput(params);
+        int contentLength =
+            (postIn.length() + System.getProperty("line.separator").length());
+        if ("POST".equals(env.get("REQUEST_METHOD"))) {
+            env.put("CONTENT_LENGTH", new Integer(contentLength));
+        }
+
+        rt = Runtime.getRuntime();
+        proc = rt.exec(cmdAndArgs.toString(), hashToStringArray(env), wd);
+
+        /*
+         * provide input to cgi
+         * First  -- parameters
+         * Second -- any remaining input
+         */
+        commandsStdIn = new BufferedOutputStream(proc.getOutputStream());
+        if (log.isDebugEnabled()) {
+            log.debug("runCGI stdin=[" + stdin + "], qs=" + env.get("QUERY_STRING"));
+        }
+        if ("POST".equals(env.get("REQUEST_METHOD"))) {
+            if (log.isDebugEnabled()) {
+                log.debug("runCGI: writing ---------------\n");
+                log.debug(postIn);
+                log.debug(
+                    "runCGI: new content_length="
+                        + contentLength
+                        + "---------------\n");
+            }
+            commandsStdIn.write(postIn.getBytes());
+        }
+        if (stdin != null) {
+            //REMIND: document this
+            /* assume if nothing is available after a time, that nothing is
+             * coming...
+             */
+            if (stdin.available() <= 0) {
+                if (log.isDebugEnabled()) {
+                    log.debug(
+                        "runCGI stdin is NOT available ["
+                            + stdin.available()
+                            + "]");
+                }
+                try {
+                    Thread.sleep(iClientInputTimeout);
+                } catch (InterruptedException ignored) {
+                }
+            }
+            if (stdin.available() > 0) {
+                if (log.isDebugEnabled()) {
+                    log.debug(
+                        "runCGI stdin IS available ["
+                            + stdin.available()
+                            + "]");
+                }
+                bBuf = new byte[1024];
+                bufRead = -1;
+                try {
+                    while ((bufRead = stdin.read(bBuf)) != -1) {
+                        if (log.isDebugEnabled()) {
+                            log.debug(
+                                "runCGI: read ["
+                                    + bufRead
+                                    + "] bytes from stdin");
+                        }
+                        commandsStdIn.write(bBuf, 0, bufRead);
+                    }
+                    if (log.isDebugEnabled()) {
+                        log.debug("runCGI: DONE READING from stdin");
+                    }
+                } catch (IOException ioe) {
+                    log.error("runCGI: couldn't write all bytes.", ioe);
+                }
+            }
+        }
+        commandsStdIn.flush();
+        commandsStdIn.close();
+
+        /* we want to wait for the process to exit,  Process.waitFor()
+         * is useless in our situation; see
+         * http://developer.java.sun.com/developer/
+         *                               bugParade/bugs/4223650.html
+         */
+
+        boolean isRunning = true;
+        commandsStdOut =
+            new BufferedReader(new InputStreamReader(proc.getInputStream()));
+        commandsStdErr =
+            new BufferedReader(new InputStreamReader(proc.getErrorStream()));
+        BufferedWriter servletContainerStdout = null;
+
+        try {
+            if (response.getOutputStream() != null) {
+                servletContainerStdout =
+                    new BufferedWriter(
+                        new OutputStreamWriter(response.getOutputStream()));
+            }
+        } catch (IOException ignored) {
+            //NOOP: no output will be written
+        }
+
+        while (isRunning) {
+
+            try {
+                //read stderr first
+                cBuf = new char[1024];
+                while ((bufRead = commandsStdErr.read(cBuf)) != -1) {
+                    if (servletContainerStdout != null) {
+                        servletContainerStdout.write(cBuf, 0, bufRead);
+                    }
+                }
+
+                //set headers
+                String line = null;
+                while (((line = commandsStdOut.readLine()) != null)
+                    && !("".equals(line))) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("runCGI: addHeader(\"" + line + "\")");
+                    }
+                    if (line.startsWith("HTTP")) {
+                        //TODO: should set status codes (NPH support)
+                        /*
+                         * response.setStatus(getStatusCode(line));
+                         */
+                    } else {
+                        response.addHeader(
+                            line.substring(0, line.indexOf(":")).trim(),
+                            line.substring(line.indexOf(":") + 1).trim());
+                    }
+                }
+
+                //write output
+                cBuf = new char[1024];
+                while ((bufRead = commandsStdOut.read(cBuf)) != -1) {
+                    if (servletContainerStdout != null) {
+                        if (log.isDebugEnabled()) {
+                            log.debug("runCGI: write(\"" + new String(cBuf) + "\")");
+                        }
+                        servletContainerStdout.write(cBuf, 0, bufRead);
+                    }
+                }
+
+                if (servletContainerStdout != null) {
+                    servletContainerStdout.flush();
+                }
+
+                proc.exitValue(); // Throws exception if alive
+
+                isRunning = false;
+
+            } catch (IllegalThreadStateException e) {
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException ignored) {
+                }
+            }
+        } //replacement for Process.waitFor()
+
+    }
+
+    /**
+     * Gets a string for input to a POST cgi script
+     *
+     * @param  params   Hashtable of query parameters to be passed to
+     *                  the CGI script
+     * @return          for use as input to the CGI script
+     */
+
+    protected String getPostInput(Hashtable params) {
+        String lineSeparator = System.getProperty("line.separator");
+        Enumeration paramNames = params.keys();
+        StringBuffer postInput = new StringBuffer("");
+        StringBuffer qs = new StringBuffer("");
+        if (paramNames != null && paramNames.hasMoreElements()) {
+            while (paramNames.hasMoreElements()) {
+                String k = (String) paramNames.nextElement();
+                String v = params.get(k).toString();
+                if ((k.indexOf("=") < 0) && (v.indexOf("=") < 0)) {
+                    postInput.append(k);
+                    qs.append(k);
+                    postInput.append("=");
+                    qs.append("=");
+                    postInput.append(v);
+                    qs.append(v);
+                    postInput.append(lineSeparator);
+                    qs.append("&");
+                }
+            }
+        }
+        qs.append(lineSeparator);
+        return qs.append(postInput).toString();
+    }
+
+    public int getIClientInputTimeout() {
+        return iClientInputTimeout;
+    }
+
+    public void setIClientInputTimeout(int iClientInputTimeout) {
+        this.iClientInputTimeout = iClientInputTimeout;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/Queue.java b/container/catalina/src/share/org/apache/catalina/util/Queue.java
new file mode 100644
index 0000000..fc4320a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/Queue.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.util;
+
+import java.util.Vector;
+
+/**
+ * A simple FIFO queue class which causes the calling thread to wait
+ * if the queue is empty and notifies threads that are waiting when it
+ * is not empty.
+ *
+ * @author Anil V (akv@eng.sun.com)
+ */
+public class Queue {
+    private Vector vector = new Vector();
+
+    /**
+     * Put the object into the queue.
+     *
+     * @param   object          the object to be appended to the
+     *                          queue.
+     */
+    public synchronized void put(Object object) {
+        vector.addElement(object);
+        notify();
+    }
+
+    /**
+     * Pull the first object out of the queue. Wait if the queue is
+     * empty.
+     */
+    public synchronized Object pull() {
+        while (isEmpty())
+            try {
+                wait();
+            } catch (InterruptedException ex) {
+            }
+        return get();
+    }
+
+    /**
+     * Get the first object out of the queue. Return null if the queue
+     * is empty.
+     */
+    public synchronized Object get() {
+        Object object = peek();
+        if (object != null)
+            vector.removeElementAt(0);
+        return object;
+    }
+
+    /**
+     * Peek to see if something is available.
+     */
+    public Object peek() {
+        if (isEmpty())
+            return null;
+        return vector.elementAt(0);
+    }
+
+    /**
+     * Is the queue empty?
+     */
+    public boolean isEmpty() {
+        return vector.isEmpty();
+    }
+
+    /**
+     * How many elements are there in this queue?
+     */
+    public int size() {
+        return vector.size();
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/RequestUtil.java b/container/catalina/src/share/org/apache/catalina/util/RequestUtil.java
new file mode 100644
index 0000000..bb0fef9
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/RequestUtil.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TimeZone;
+
+import javax.servlet.http.Cookie;
+
+
+/**
+ * General purpose request parsing and encoding utility methods.
+ *
+ * @author Craig R. McClanahan
+ * @author Tim Tye
+ * @version $Revision$ $Date$
+ */
+
+public final class RequestUtil {
+
+
+    /**
+     * The DateFormat to use for generating readable dates in cookies.
+     */
+    private static SimpleDateFormat format =
+        new SimpleDateFormat(" EEEE, dd-MMM-yy kk:mm:ss zz");
+
+    static {
+        format.setTimeZone(TimeZone.getTimeZone("GMT"));
+    }
+
+
+    /**
+     * Encode a cookie as per RFC 2109.  The resulting string can be used
+     * as the value for a <code>Set-Cookie</code> header.
+     *
+     * @param cookie The cookie to encode.
+     * @return A string following RFC 2109.
+     */
+    public static String encodeCookie(Cookie cookie) {
+
+        StringBuffer buf = new StringBuffer( cookie.getName() );
+        buf.append("=");
+        buf.append(cookie.getValue());
+
+        if (cookie.getComment() != null) {
+            buf.append("; Comment=\"");
+            buf.append(cookie.getComment());
+            buf.append("\"");
+        }
+
+        if (cookie.getDomain() != null) {
+            buf.append("; Domain=\"");
+            buf.append(cookie.getDomain());
+            buf.append("\"");
+        }
+
+        long age = cookie.getMaxAge();
+        if (cookie.getMaxAge() >= 0) {
+            buf.append("; Max-Age=\"");
+            buf.append(cookie.getMaxAge());
+            buf.append("\"");
+        }
+
+        if (cookie.getPath() != null) {
+            buf.append("; Path=\"");
+            buf.append(cookie.getPath());
+            buf.append("\"");
+        }
+
+        if (cookie.getSecure()) {
+            buf.append("; Secure");
+        }
+
+        if (cookie.getVersion() > 0) {
+            buf.append("; Version=\"");
+            buf.append(cookie.getVersion());
+            buf.append("\"");
+        }
+
+        return (buf.toString());
+    }
+
+
+    /**
+     * Filter the specified message string for characters that are sensitive
+     * in HTML.  This avoids potential attacks caused by including JavaScript
+     * codes in the request URL that is often reported in error messages.
+     *
+     * @param message The message string to be filtered
+     */
+    public static String filter(String message) {
+
+        if (message == null)
+            return (null);
+
+        char content[] = new char[message.length()];
+        message.getChars(0, message.length(), content, 0);
+        StringBuffer result = new StringBuffer(content.length + 50);
+        for (int i = 0; i < content.length; i++) {
+            switch (content[i]) {
+            case '<':
+                result.append("&lt;");
+                break;
+            case '>':
+                result.append("&gt;");
+                break;
+            case '&':
+                result.append("&amp;");
+                break;
+            case '"':
+                result.append("&quot;");
+                break;
+            default:
+                result.append(content[i]);
+            }
+        }
+        return (result.toString());
+
+    }
+
+
+    /**
+     * Normalize a relative URI path that may have relative values ("/./",
+     * "/../", and so on ) it it.  <strong>WARNING</strong> - This method is
+     * useful only for normalizing application-generated paths.  It does not
+     * try to perform security checks for malicious input.
+     *
+     * @param path Relative path to be normalized
+     */
+    public static String normalize(String path) {
+
+        if (path == null)
+            return null;
+
+        // Create a place for the normalized path
+        String normalized = path;
+
+        if (normalized.equals("/."))
+            return "/";
+
+        // Add a leading "/" if necessary
+        if (!normalized.startsWith("/"))
+            normalized = "/" + normalized;
+
+        // Resolve occurrences of "//" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("//");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 1);
+        }
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/./");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 2);
+        }
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/../");
+            if (index < 0)
+                break;
+            if (index == 0)
+                return (null);  // Trying to go outside our context
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            normalized = normalized.substring(0, index2) +
+                normalized.substring(index + 3);
+        }
+
+        // Return the normalized path that we have completed
+        return (normalized);
+
+    }
+
+
+    /**
+     * Parse the character encoding from the specified content type header.
+     * If the content type is null, or there is no explicit character encoding,
+     * <code>null</code> is returned.
+     *
+     * @param contentType a content type header
+     */
+    public static String parseCharacterEncoding(String contentType) {
+
+        if (contentType == null)
+            return (null);
+        int start = contentType.indexOf("charset=");
+        if (start < 0)
+            return (null);
+        String encoding = contentType.substring(start + 8);
+        int end = encoding.indexOf(';');
+        if (end >= 0)
+            encoding = encoding.substring(0, end);
+        encoding = encoding.trim();
+        if ((encoding.length() > 2) && (encoding.startsWith("\""))
+            && (encoding.endsWith("\"")))
+            encoding = encoding.substring(1, encoding.length() - 1);
+        return (encoding.trim());
+
+    }
+
+
+    /**
+     * Parse a cookie header into an array of cookies according to RFC 2109.
+     *
+     * @param header Value of an HTTP "Cookie" header
+     */
+    public static Cookie[] parseCookieHeader(String header) {
+
+        if ((header == null) || (header.length() < 1))
+            return (new Cookie[0]);
+
+        ArrayList cookies = new ArrayList();
+        while (header.length() > 0) {
+            int semicolon = header.indexOf(';');
+            if (semicolon < 0)
+                semicolon = header.length();
+            if (semicolon == 0)
+                break;
+            String token = header.substring(0, semicolon);
+            if (semicolon < header.length())
+                header = header.substring(semicolon + 1);
+            else
+                header = "";
+            try {
+                int equals = token.indexOf('=');
+                if (equals > 0) {
+                    String name = token.substring(0, equals).trim();
+                    String value = token.substring(equals+1).trim();
+                    cookies.add(new Cookie(name, value));
+                }
+            } catch (Throwable e) {
+                ;
+            }
+        }
+
+        return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
+
+    }
+
+
+    /**
+     * Append request parameters from the specified String to the specified
+     * Map.  It is presumed that the specified Map is not accessed from any
+     * other thread, so no synchronization is performed.
+     * <p>
+     * <strong>IMPLEMENTATION NOTE</strong>:  URL decoding is performed
+     * individually on the parsed name and value elements, rather than on
+     * the entire query string ahead of time, to properly deal with the case
+     * where the name or value includes an encoded "=" or "&" character
+     * that would otherwise be interpreted as a delimiter.
+     *
+     * @param map Map that accumulates the resulting parameters
+     * @param data Input string containing request parameters
+     *
+     * @exception IllegalArgumentException if the data is malformed
+     */
+    public static void parseParameters(Map map, String data, String encoding)
+        throws UnsupportedEncodingException {
+
+        if ((data != null) && (data.length() > 0)) {
+
+            // use the specified encoding to extract bytes out of the
+            // given string so that the encoding is not lost. If an
+            // encoding is not specified, let it use platform default
+            byte[] bytes = null;
+            try {
+                if (encoding == null) {
+                    bytes = data.getBytes();
+                } else {
+                    bytes = data.getBytes(encoding);
+                }
+            } catch (UnsupportedEncodingException uee) {
+            }
+
+            parseParameters(map, bytes, encoding);
+        }
+
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded String.
+     * When the byte array is converted to a string, the system default
+     * character encoding is used...  This may be different than some other
+     * servers.
+     *
+     * @param str The url-encoded string
+     *
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String URLDecode(String str) {
+
+        return URLDecode(str, null);
+
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded String.
+     *
+     * @param str The url-encoded string
+     * @param enc The encoding to use; if null, the default encoding is used
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String URLDecode(String str, String enc) {
+
+        if (str == null)
+            return (null);
+
+        // use the specified encoding to extract bytes out of the
+        // given string so that the encoding is not lost. If an
+        // encoding is not specified, let it use platform default
+        byte[] bytes = null;
+        try {
+            if (enc == null) {
+                bytes = str.getBytes();
+            } else {
+                bytes = str.getBytes(enc);
+            }
+        } catch (UnsupportedEncodingException uee) {}
+
+        return URLDecode(bytes, enc);
+
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded byte array.
+     *
+     * @param bytes The url-encoded byte array
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String URLDecode(byte[] bytes) {
+        return URLDecode(bytes, null);
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded byte array.
+     *
+     * @param bytes The url-encoded byte array
+     * @param enc The encoding to use; if null, the default encoding is used
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String URLDecode(byte[] bytes, String enc) {
+
+        if (bytes == null)
+            return (null);
+
+        int len = bytes.length;
+        int ix = 0;
+        int ox = 0;
+        while (ix < len) {
+            byte b = bytes[ix++];     // Get byte to test
+            if (b == '+') {
+                b = (byte)' ';
+            } else if (b == '%') {
+                b = (byte) ((convertHexDigit(bytes[ix++]) << 4)
+                            + convertHexDigit(bytes[ix++]));
+            }
+            bytes[ox++] = b;
+        }
+        if (enc != null) {
+            try {
+                return new String(bytes, 0, ox, enc);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return new String(bytes, 0, ox);
+
+    }
+
+
+    /**
+     * Convert a byte character value to hexidecimal digit value.
+     *
+     * @param b the character value byte
+     */
+    private static byte convertHexDigit( byte b ) {
+        if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
+        if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
+        if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
+        return 0;
+    }
+
+
+    /**
+     * Put name and value pair in map.  When name already exist, add value
+     * to array of values.
+     *
+     * @param map The map to populate
+     * @param name The parameter name
+     * @param value The parameter value
+     */
+    private static void putMapEntry( Map map, String name, String value) {
+        String[] newValues = null;
+        String[] oldValues = (String[]) map.get(name);
+        if (oldValues == null) {
+            newValues = new String[1];
+            newValues[0] = value;
+        } else {
+            newValues = new String[oldValues.length + 1];
+            System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
+            newValues[oldValues.length] = value;
+        }
+        map.put(name, newValues);
+    }
+
+
+    /**
+     * Append request parameters from the specified String to the specified
+     * Map.  It is presumed that the specified Map is not accessed from any
+     * other thread, so no synchronization is performed.
+     * <p>
+     * <strong>IMPLEMENTATION NOTE</strong>:  URL decoding is performed
+     * individually on the parsed name and value elements, rather than on
+     * the entire query string ahead of time, to properly deal with the case
+     * where the name or value includes an encoded "=" or "&" character
+     * that would otherwise be interpreted as a delimiter.
+     *
+     * NOTE: byte array data is modified by this method.  Caller beware.
+     *
+     * @param map Map that accumulates the resulting parameters
+     * @param data Input string containing request parameters
+     * @param encoding Encoding to use for converting hex
+     *
+     * @exception UnsupportedEncodingException if the data is malformed
+     */
+    public static void parseParameters(Map map, byte[] data, String encoding)
+        throws UnsupportedEncodingException {
+
+        if (data != null && data.length > 0) {
+            int    pos = 0;
+            int    ix = 0;
+            int    ox = 0;
+            String key = null;
+            String value = null;
+            while (ix < data.length) {
+                byte c = data[ix++];
+                switch ((char) c) {
+                case '&':
+                    value = new String(data, 0, ox, encoding);
+                    if (key != null) {
+                        putMapEntry(map, key, value);
+                        key = null;
+                    }
+                    ox = 0;
+                    break;
+                case '=':
+                    if (key == null) {
+                        key = new String(data, 0, ox, encoding);
+                        ox = 0;
+                    } else {
+                        data[ox++] = c;
+                    }                   
+                    break;  
+                case '+':
+                    data[ox++] = (byte)' ';
+                    break;
+                case '%':
+                    data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4)
+                                    + convertHexDigit(data[ix++]));
+                    break;
+                default:
+                    data[ox++] = c;
+                }
+            }
+            //The last value does not end in '&'.  So save it now.
+            if (key != null) {
+                value = new String(data, 0, ox, encoding);
+                putMapEntry(map, key, value);
+            }
+        }
+
+    }
+
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/ResourceSet.java b/container/catalina/src/share/org/apache/catalina/util/ResourceSet.java
new file mode 100644
index 0000000..2dcf124
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/ResourceSet.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+import java.util.Collection;
+import java.util.HashSet;
+
+
+/**
+ * Extended implementation of <strong>HashSet</strong> that includes a
+ * <code>locked</code> property.  This class can be used to safely expose
+ * resource path sets to user classes without having to clone them in order
+ * to avoid modifications.  When first created, a <code>ResourceMap</code>
+ * is not locked.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class ResourceSet extends HashSet {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new, empty set with the default initial capacity and
+     * load factor.
+     */
+    public ResourceSet() {
+
+        super();
+
+    }
+
+
+    /**
+     * Construct a new, empty set with the specified initial capacity and
+     * default load factor.
+     *
+     * @param initialCapacity The initial capacity of this set
+     */
+    public ResourceSet(int initialCapacity) {
+
+        super(initialCapacity);
+
+    }
+
+
+    /**
+     * Construct a new, empty set with the specified initial capacity and
+     * load factor.
+     *
+     * @param initialCapacity The initial capacity of this set
+     * @param loadFactor The load factor of this set
+     */
+    public ResourceSet(int initialCapacity, float loadFactor) {
+
+        super(initialCapacity, loadFactor);
+
+    }
+
+
+    /**
+     * Construct a new set with the same contents as the existing collection.
+     *
+     * @param coll The collection whose contents we should copy
+     */
+    public ResourceSet(Collection coll) {
+
+        super(coll);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The current lock state of this parameter map.
+     */
+    private boolean locked = false;
+
+
+    /**
+     * Return the locked state of this parameter map.
+     */
+    public boolean isLocked() {
+
+        return (this.locked);
+
+    }
+
+
+    /**
+     * Set the locked state of this parameter map.
+     *
+     * @param locked The new locked state
+     */
+    public void setLocked(boolean locked) {
+
+        this.locked = locked;
+
+    }
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager("org.apache.catalina.util");
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add the specified element to this set if it is not already present.
+     * Return <code>true</code> if the element was added.
+     *
+     * @param o The object to be added
+     *
+     * @exception IllegalStateException if this ResourceSet is locked
+     */
+    public boolean add(Object o) {
+
+        if (locked)
+            throw new IllegalStateException
+              (sm.getString("resourceSet.locked"));
+        return (super.add(o));
+
+    }
+
+
+    /**
+     * Remove all of the elements from this set.
+     *
+     * @exception IllegalStateException if this ResourceSet is locked
+     */
+    public void clear() {
+
+        if (locked)
+            throw new IllegalStateException
+              (sm.getString("resourceSet.locked"));
+        super.clear();
+
+    }
+
+
+    /**
+     * Remove the given element from this set if it is present.
+     * Return <code>true</code> if the element was removed.
+     *
+     * @param o The object to be removed
+     *
+     * @exception IllegalStateException if this ResourceSet is locked
+     */
+    public boolean remove(Object o) {
+
+        if (locked)
+            throw new IllegalStateException
+              (sm.getString("resourceSet.locked"));
+        return (super.remove(o));
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/SchemaResolver.java b/container/catalina/src/share/org/apache/catalina/util/SchemaResolver.java
new file mode 100644
index 0000000..1a66de3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/SchemaResolver.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.util;
+
+
+import java.util.HashMap;
+
+import org.apache.tomcat.util.digester.Digester;
+import org.xml.sax.InputSource;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.SAXException;
+
+/**
+ * This class implements a local SAX's <code>EntityResolver</code>. All
+ * DTDs and schemas used to validate the web.xml file will re-directed
+ * to a local file stored in the servlet-api.jar and jsp-api.jar.
+ *
+ * @author Jean-Francois Arcand
+ */
+public class SchemaResolver implements EntityResolver {
+
+    /**
+     * The disgester instance for which this class is the entity resolver.
+     */
+    protected Digester digester;
+
+
+    /**
+     * The URLs of dtds and schemas that have been registered, keyed by the
+     * public identifier that corresponds.
+     */
+    protected HashMap entityValidator = new HashMap();
+
+
+    /**
+     * The public identifier of the DTD we are currently parsing under
+     * (if any).
+     */
+    protected String publicId = null;
+
+
+    /**
+     * Extension to make the difference between DTD and Schema.
+     */
+    protected String schemaExtension = "xsd";
+
+
+    /**
+     * Create a new <code>EntityResolver</code> that will redirect
+     * all remote dtds and schema to a locat destination.
+     * @param digester The digester instance.
+     */
+    public SchemaResolver(Digester digester) {
+        this.digester = digester;
+    }
+
+
+    /**
+     * Register the specified DTD/Schema URL for the specified public
+     * identifier. This must be called before the first call to
+     * <code>parse()</code>.
+     *
+     * When adding a schema file (*.xsd), only the name of the file
+     * will get added. If two schemas with the same name are added,
+     * only the last one will be stored.
+     *
+     * @param publicId Public identifier of the DTD to be resolved
+     * @param entityURL The URL to use for reading this DTD
+     */
+     public void register(String publicId, String entityURL) {
+         String key = publicId;
+         if (publicId.indexOf(schemaExtension) != -1)
+             key = publicId.substring(publicId.lastIndexOf('/')+1);
+         entityValidator.put(key, entityURL);
+     }
+
+
+    /**
+     * Resolve the requested external entity.
+     *
+     * @param publicId The public identifier of the entity being referenced
+     * @param systemId The system identifier of the entity being referenced
+     *
+     * @exception SAXException if a parsing exception occurs
+     *
+     */
+    public InputSource resolveEntity(String publicId, String systemId)
+        throws SAXException {
+
+        if (publicId != null) {
+            this.publicId = publicId;
+            digester.setPublicId(publicId);
+        }
+
+        // Has this system identifier been registered?
+        String entityURL = null;
+        if (publicId != null) {
+            entityURL = (String) entityValidator.get(publicId);
+        }
+
+        // Redirect the schema location to a local destination
+        String key = null;
+        if (entityURL == null && systemId != null) {
+            key = systemId.substring(systemId.lastIndexOf('/')+1);
+            entityURL = (String)entityValidator.get(key);
+        }
+
+        if (entityURL == null) {
+           return (null);
+        }
+
+        try {
+            return (new InputSource(entityURL));
+        } catch (Exception e) {
+            throw new SAXException(e);
+        }
+
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/ServerInfo.java b/container/catalina/src/share/org/apache/catalina/util/ServerInfo.java
new file mode 100644
index 0000000..e5ba1fd
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/ServerInfo.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+import java.io.InputStream;
+import java.util.Properties;
+
+
+/**
+ * Simple utility module to make it easy to plug in the server identifier
+ * when integrating Tomcat.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ServerInfo {
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The server information String with which we identify ourselves.
+     */
+    private static String serverInfo = null;
+
+    /**
+     * The server built String.
+     */
+    private static String serverBuilt = null;
+
+    /**
+     * The server's version number String.
+     */
+    private static String serverNumber = null;
+
+    static {
+
+        try {
+            InputStream is = ServerInfo.class.getResourceAsStream
+                ("/org/apache/catalina/util/ServerInfo.properties");
+            Properties props = new Properties();
+            props.load(is);
+            is.close();
+            serverInfo = props.getProperty("server.info");
+            serverBuilt = props.getProperty("server.built");
+            serverNumber = props.getProperty("server.number");
+        } catch (Throwable t) {
+            ;
+        }
+        if (serverInfo == null)
+            serverInfo = "Apache Tomcat";
+        if (serverBuilt == null)
+            serverBuilt = "unknown";
+        if (serverNumber == null)
+            serverNumber = "5.5.0.0";
+        
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the server identification for this version of Tomcat.
+     */
+    public static String getServerInfo() {
+
+        return (serverInfo);
+
+    }
+
+    /**
+     * Return the server built time for this version of Tomcat.
+     */
+    public static String getServerBuilt() {
+
+        return (serverBuilt);
+
+    }
+
+    /**
+     * Return the server's version number.
+     */
+    public static String getServerNumber() {
+
+        return (serverNumber);
+
+    }
+
+    public static void main(String args[]) {
+        System.out.println("Server version: " + getServerInfo());
+        System.out.println("Server built:   " + getServerBuilt());
+        System.out.println("Server number:  " + getServerNumber());
+        System.out.println("OS Name:        " +
+                           System.getProperty("os.name"));
+        System.out.println("OS Version:     " +
+                           System.getProperty("os.version"));
+        System.out.println("Architecture:   " +
+                           System.getProperty("os.arch"));
+        System.out.println("JVM Version:    " +
+                           System.getProperty("java.runtime.version"));
+        System.out.println("JVM Vendor:     " +
+                           System.getProperty("java.vm.vendor"));                        
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/ServerInfo.properties b/container/catalina/src/share/org/apache/catalina/util/ServerInfo.properties
new file mode 100644
index 0000000..924afb1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/ServerInfo.properties
@@ -0,0 +1,3 @@
+server.info=Apache Tomcat/@VERSION@
+server.number=@VERSION_NUMBER@
+server.built=@VERSION_BUILT@
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/catalina/util/Strftime.java b/container/catalina/src/share/org/apache/catalina/util/Strftime.java
new file mode 100644
index 0000000..d269771
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/Strftime.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Properties;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Converts dates to strings using the same format specifiers as strftime
+ *
+ * Note: This does not mimic strftime perfectly.  Certain strftime commands, 
+ *       are not supported, and will convert as if they were literals.
+ *
+ *       Certain complicated commands, like those dealing with the week of the year
+ *       probably don't have exactly the same behavior as strftime.
+ *
+ *       These limitations are due to use SimpleDateTime.  If the conversion was done
+ *       manually, all these limitations could be eliminated.
+ *
+ *       The interface looks like a subset of DateFormat.  Maybe someday someone will make this class
+ *       extend DateFormat.
+ *
+ * @author Bip Thelin
+ * @author Dan Sandberg
+ * @version $Revision$, $Date$
+ */
+public class Strftime {
+    protected static Properties translate;
+    protected SimpleDateFormat simpleDateFormat;
+
+    /**
+     * Initialize our pattern translation
+     */
+    static {
+        translate = new Properties();
+        translate.put("a","EEE");
+        translate.put("A","EEEE");
+        translate.put("b","MMM");
+        translate.put("B","MMMM");
+        translate.put("c","EEE MMM d HH:mm:ss yyyy");
+
+        //There's no way to specify the century in SimpleDateFormat.  We don't want to hard-code
+        //20 since this could be wrong for the pre-2000 files.
+        //translate.put("C", "20");
+        translate.put("d","dd");
+        translate.put("D","MM/dd/yy");
+        translate.put("e","dd"); //will show as '03' instead of ' 3'
+        translate.put("F","yyyy-MM-dd");
+        translate.put("g","yy");
+        translate.put("G","yyyy");
+        translate.put("H","HH");
+        translate.put("h","MMM");
+        translate.put("I","hh");
+        translate.put("j","DDD");
+        translate.put("k","HH"); //will show as '07' instead of ' 7'
+        translate.put("l","hh"); //will show as '07' instead of ' 7'
+        translate.put("m","MM");
+        translate.put("M","mm");
+        translate.put("n","\n");
+        translate.put("p","a");
+        translate.put("P","a");  //will show as pm instead of PM
+        translate.put("r","hh:mm:ss a");
+        translate.put("R","HH:mm");
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("s","seconds since ecpoch");
+        translate.put("S","ss");
+        translate.put("t","\t");
+        translate.put("T","HH:mm:ss");
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("u","day of week ( 1-7 )");
+
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("U","week in year with first sunday as first day...");
+
+        translate.put("V","ww"); //I'm not sure this is always exactly the same
+
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("W","week in year with first monday as first day...");
+
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("w","E");
+        translate.put("X","HH:mm:ss");
+        translate.put("x","MM/dd/yy");
+        translate.put("y","yy");
+        translate.put("Y","yyyy");
+        translate.put("Z","z");
+        translate.put("z","Z");
+        translate.put("%","%");
+    }
+
+
+    /**
+     * Create an instance of this date formatting class
+     *
+     * @see #Strftime( String, Locale )
+     */
+    public Strftime( String origFormat ) {
+        String convertedFormat = convertDateFormat( origFormat );
+        simpleDateFormat = new SimpleDateFormat( convertedFormat );
+    }
+
+    /**
+     * Create an instance of this date formatting class
+     * 
+     * @param origFormat the strftime-style formatting string
+     * @param locale the locale to use for locale-specific conversions
+     */
+    public Strftime( String origFormat, Locale locale ) {
+        String convertedFormat = convertDateFormat( origFormat );
+        simpleDateFormat = new SimpleDateFormat( convertedFormat, locale );
+    }
+
+    /**
+     * Format the date according to the strftime-style string given in the constructor.
+     *
+     * @param date the date to format
+     * @return the formatted date
+     */
+    public String format( Date date ) {
+        return simpleDateFormat.format( date );
+    }
+
+    /**
+     * Get the timezone used for formatting conversions
+     *
+     * @return the timezone
+     */
+    public TimeZone getTimeZone() {
+        return simpleDateFormat.getTimeZone();
+    }
+
+    /**
+     * Change the timezone used to format dates
+     *
+     * @see SimpleDateFormat#setTimeZone
+     */
+    public void setTimeZone( TimeZone timeZone ) {
+        simpleDateFormat.setTimeZone( timeZone );
+    }
+
+    /**
+     * Search the provided pattern and get the C standard
+     * Date/Time formatting rules and convert them to the
+     * Java equivalent.
+     *
+     * @param pattern The pattern to search
+     * @return The modified pattern
+     */
+    protected String convertDateFormat( String pattern ) {
+        boolean inside = false;
+        boolean mark = false;
+        boolean modifiedCommand = false;
+
+        StringBuffer buf = new StringBuffer();
+
+        for(int i = 0; i < pattern.length(); i++) {
+            char c = pattern.charAt(i);
+
+            if ( c=='%' && !mark ) {
+                mark=true;
+            } else {
+                if ( mark ) {
+                    if ( modifiedCommand ) {
+                        //don't do anything--we just wanted to skip a char
+                        modifiedCommand = false;
+                        mark = false;
+                    } else {
+                        inside = translateCommand( buf, pattern, i, inside );
+                        //It's a modifier code
+                        if ( c=='O' || c=='E' ) {
+                            modifiedCommand = true;
+                        } else {
+                            mark=false;
+                        }
+                    }
+                } else {
+                    if ( !inside && c != ' ' ) {
+                        //We start a literal, which we need to quote
+                        buf.append("'");
+                        inside = true;
+                    }
+                    
+                    buf.append(c);
+                }
+            }
+        }
+
+        if ( buf.length() > 0 ) {
+            char lastChar = buf.charAt( buf.length() - 1 );
+
+            if( lastChar!='\'' && inside ) {
+                buf.append('\'');
+            }
+        }
+        return buf.toString();
+    }
+
+    protected String quote( String str, boolean insideQuotes ) {
+        String retVal = str;
+        if ( !insideQuotes ) {
+            retVal = '\'' + retVal + '\'';
+        }
+        return retVal;
+    }
+
+    /**
+     * Try to get the Java Date/Time formatting associated with
+     * the C standard provided.
+     *
+     * @param buf The buffer
+     * @param pattern The date/time pattern
+     * @param index The char index
+     * @param oldInside Flag value
+     * @return True if new is inside buffer
+     */
+    protected boolean translateCommand( StringBuffer buf, String pattern, int index, boolean oldInside ) {
+        char firstChar = pattern.charAt( index );
+        boolean newInside = oldInside;
+
+        //O and E are modifiers, they mean to present an alternative representation of the next char
+        //we just handle the next char as if the O or E wasn't there
+        if ( firstChar == 'O' || firstChar == 'E' ) {
+            if ( index + 1 < pattern.length() ) {               
+                newInside = translateCommand( buf, pattern, index + 1, oldInside );
+            } else {
+                buf.append( quote("%" + firstChar, oldInside ) );
+            }
+        } else {
+            String command = translate.getProperty( String.valueOf( firstChar ) );
+            
+            //If we don't find a format, treat it as a literal--That's what apache does
+            if ( command == null ) {
+                buf.append( quote( "%" + firstChar, oldInside ) );
+            } else {
+                //If we were inside quotes, close the quotes
+                if ( oldInside ) {
+                    buf.append( '\'' );
+                }
+                buf.append( command );
+                newInside = false;
+            }
+        }
+        return newInside;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/StringManager.java b/container/catalina/src/share/org/apache/catalina/util/StringManager.java
new file mode 100644
index 0000000..3596534
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/StringManager.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+import java.text.MessageFormat;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.net.URLClassLoader;
+
+/**
+ * An internationalization / localization helper class which reduces
+ * the bother of handling ResourceBundles and takes care of the
+ * common cases of message formating which otherwise require the
+ * creation of Object arrays and such.
+ *
+ * <p>The StringManager operates on a package basis. One StringManager
+ * per package can be created and accessed via the getManager method
+ * call.
+ *
+ * <p>The StringManager will look for a ResourceBundle named by
+ * the package name given plus the suffix of "LocalStrings". In
+ * practice, this means that the localized information will be contained
+ * in a LocalStrings.properties file located in the package
+ * directory of the classpath.
+ *
+ * <p>Please see the documentation for java.util.ResourceBundle for
+ * more information.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ */
+
+public class StringManager {
+
+    /**
+     * The ResourceBundle for this StringManager.
+     */
+
+    private ResourceBundle bundle;
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( StringManager.class );
+    
+    /**
+     * Creates a new StringManager for a given package. This is a
+     * private method and all access to it is arbitrated by the
+     * static getManager method call so that only one StringManager
+     * per package will be created.
+     *
+     * @param packageName Name of package to create StringManager for.
+     */
+
+    private StringManager(String packageName) {
+        String bundleName = packageName + ".LocalStrings";
+        try {
+            bundle = ResourceBundle.getBundle(bundleName);
+            return;
+        } catch( MissingResourceException ex ) {
+            // Try from the current loader ( that's the case for trusted apps )
+            ClassLoader cl=Thread.currentThread().getContextClassLoader();
+            if( cl != null ) {
+                try {
+                    bundle=ResourceBundle.getBundle(bundleName, Locale.getDefault(), cl);
+                    return;
+                } catch(MissingResourceException ex2) {
+                }
+            }
+            if( cl==null )
+                cl=this.getClass().getClassLoader();
+
+            if (log.isDebugEnabled())
+                log.debug("Can't find resource " + bundleName +
+                    " " + cl);
+            if( cl instanceof URLClassLoader ) {
+                if (log.isDebugEnabled()) 
+                    log.debug( ((URLClassLoader)cl).getURLs());
+            }
+        }
+    }
+
+    /**
+     * Get a string from the underlying resource bundle.
+     *
+     * @param key The resource name
+     */
+    public String getString(String key) {
+        return MessageFormat.format(getStringInternal(key), null);
+    }
+
+
+    protected String getStringInternal(String key) {
+        if (key == null) {
+            String msg = "key is null";
+
+            throw new NullPointerException(msg);
+        }
+
+        String str = null;
+
+        if( bundle==null )
+            return key;
+        try {
+            str = bundle.getString(key);
+        } catch (MissingResourceException mre) {
+            str = "Cannot find message associated with key '" + key + "'";
+        }
+
+        return str;
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format
+     * it with the given set of arguments.
+     *
+     * @param key The resource name
+     * @param args Formatting directives
+     */
+
+    public String getString(String key, Object[] args) {
+        String iString = null;
+        String value = getStringInternal(key);
+
+        // this check for the runtime exception is some pre 1.1.6
+        // VM's don't do an automatic toString() on the passed in
+        // objects and barf out
+
+        try {
+            // ensure the arguments are not null so pre 1.2 VM's don't barf
+            Object nonNullArgs[] = args;
+            for (int i=0; i<args.length; i++) {
+                if (args[i] == null) {
+                    if (nonNullArgs==args) nonNullArgs=(Object[])args.clone();
+                    nonNullArgs[i] = "null";
+                }
+            }
+
+            iString = MessageFormat.format(value, nonNullArgs);
+        } catch (IllegalArgumentException iae) {
+            StringBuffer buf = new StringBuffer();
+            buf.append(value);
+            for (int i = 0; i < args.length; i++) {
+                buf.append(" arg[" + i + "]=" + args[i]);
+            }
+            iString = buf.toString();
+        }
+        return iString;
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object argument. This argument can of course be
+     * a String object.
+     *
+     * @param key The resource name
+     * @param arg Formatting directive
+     */
+
+    public String getString(String key, Object arg) {
+        Object[] args = new Object[] {arg};
+        return getString(key, args);
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object arguments. These arguments can of course
+     * be String objects.
+     *
+     * @param key The resource name
+     * @param arg1 Formatting directive
+     * @param arg2 Formatting directive
+     */
+
+    public String getString(String key, Object arg1, Object arg2) {
+        Object[] args = new Object[] {arg1, arg2};
+        return getString(key, args);
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object arguments. These arguments can of course
+     * be String objects.
+     *
+     * @param key The resource name
+     * @param arg1 Formatting directive
+     * @param arg2 Formatting directive
+     * @param arg3 Formatting directive
+     */
+
+    public String getString(String key, Object arg1, Object arg2,
+                            Object arg3) {
+        Object[] args = new Object[] {arg1, arg2, arg3};
+        return getString(key, args);
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object arguments. These arguments can of course
+     * be String objects.
+     *
+     * @param key The resource name
+     * @param arg1 Formatting directive
+     * @param arg2 Formatting directive
+     * @param arg3 Formatting directive
+     * @param arg4 Formatting directive
+     */
+
+    public String getString(String key, Object arg1, Object arg2,
+                            Object arg3, Object arg4) {
+        Object[] args = new Object[] {arg1, arg2, arg3, arg4};
+        return getString(key, args);
+    }
+    // --------------------------------------------------------------
+    // STATIC SUPPORT METHODS
+    // --------------------------------------------------------------
+
+    private static Hashtable managers = new Hashtable();
+
+    /**
+     * Get the StringManager for a particular package. If a manager for
+     * a package already exists, it will be reused, else a new
+     * StringManager will be created and returned.
+     *
+     * @param packageName The package name
+     */
+
+    public synchronized static StringManager getManager(String packageName) {
+        StringManager mgr = (StringManager)managers.get(packageName);
+
+        if (mgr == null) {
+            mgr = new StringManager(packageName);
+            managers.put(packageName, mgr);
+        }
+        return mgr;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/StringParser.java b/container/catalina/src/share/org/apache/catalina/util/StringParser.java
new file mode 100644
index 0000000..b7ecb7b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/StringParser.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+/**
+ * Utility class for string parsing that is higher performance than
+ * StringParser for simple delimited text cases.  Parsing is performed
+ * by setting the string, and then using the <code>findXxxx()</code> and
+ * <code>skipXxxx()</code> families of methods to remember significant
+ * offsets.  To retrieve the parsed substrings, call the <code>extract()</code>
+ * method with the appropriate saved offset values.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class StringParser {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a string parser with no preset string to be parsed.
+     */
+    public StringParser() {
+
+        this(null);
+
+    }
+
+
+    /**
+     * Construct a string parser that is initialized to parse the specified
+     * string.
+     *
+     * @param string The string to be parsed
+     */
+    public StringParser(String string) {
+
+        super();
+        setString(string);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The characters of the current string, as a character array.  Stored
+     * when the string is first specified to speed up access to characters
+     * being compared during parsing.
+     */
+    private char chars[] = null;
+
+
+    /**
+     * The zero-relative index of the current point at which we are
+     * positioned within the string being parsed.  <strong>NOTE</strong>:
+     * the value of this index can be one larger than the index of the last
+     * character of the string (i.e. equal to the string length) if you
+     * parse off the end of the string.  This value is useful for extracting
+     * substrings that include the end of the string.
+     */
+    private int index = 0;
+
+
+    /**
+     * The length of the String we are currently parsing.  Stored when the
+     * string is first specified to avoid repeated recalculations.
+     */
+    private int length = 0;
+
+
+    /**
+     * The String we are currently parsing.
+     */
+    private String string = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the zero-relative index of our current parsing position
+     * within the string being parsed.
+     */
+    public int getIndex() {
+
+        return (this.index);
+
+    }
+
+
+    /**
+     * Return the length of the string we are parsing.
+     */
+    public int getLength() {
+
+        return (this.length);
+
+    }
+
+
+    /**
+     * Return the String we are currently parsing.
+     */
+    public String getString() {
+
+        return (this.string);
+
+    }
+
+
+    /**
+     * Set the String we are currently parsing.  The parser state is also reset
+     * to begin at the start of this string.
+     *
+     * @param string The string to be parsed.
+     */
+    public void setString(String string) {
+
+        this.string = string;
+        if (string != null) {
+            this.length = string.length();
+            chars = this.string.toCharArray();
+        } else {
+            this.length = 0;
+            chars = new char[0];
+        }
+        reset();
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Advance the current parsing position by one, if we are not already
+     * past the end of the string.
+     */
+    public void advance() {
+
+        if (index < length)
+            index++;
+
+    }
+
+
+    /**
+     * Extract and return a substring that starts at the specified position,
+     * and extends to the end of the string being parsed.  If this is not
+     * possible, a zero-length string is returned.
+     *
+     * @param start Starting index, zero relative, inclusive
+     */
+    public String extract(int start) {
+
+        if ((start < 0) || (start >= length))
+            return ("");
+        else
+            return (string.substring(start));
+
+    }
+
+
+    /**
+     * Extract and return a substring that starts at the specified position,
+     * and ends at the character before the specified position.  If this is
+     * not possible, a zero-length string is returned.
+     *
+     * @param start Starting index, zero relative, inclusive
+     * @param end Ending index, zero relative, exclusive
+     */
+    public String extract(int start, int end) {
+
+        if ((start < 0) || (start >= end) || (end > length))
+            return ("");
+        else
+            return (string.substring(start, end));
+
+    }
+
+
+    /**
+     * Return the index of the next occurrence of the specified character,
+     * or the index of the character after the last position of the string
+     * if no more occurrences of this character are found.  The current
+     * parsing position is updated to the returned value.
+     *
+     * @param ch Character to be found
+     */
+    public int findChar(char ch) {
+
+        while ((index < length) && (ch != chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Return the index of the next occurrence of a non-whitespace character,
+     * or the index of the character after the last position of the string
+     * if no more non-whitespace characters are found.  The current
+     * parsing position is updated to the returned value.
+     */
+    public int findText() {
+
+        while ((index < length) && isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Return the index of the next occurrence of a whitespace character,
+     * or the index of the character after the last position of the string
+     * if no more whitespace characters are found.  The current parsing
+     * position is updated to the returned value.
+     */
+    public int findWhite() {
+
+        while ((index < length) && !isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Reset the current state of the parser to the beginning of the
+     * current string being parsed.
+     */
+    public void reset() {
+
+        index = 0;
+
+    }
+
+
+    /**
+     * Advance the current parsing position while it is pointing at the
+     * specified character, or until it moves past the end of the string.
+     * Return the final value.
+     *
+     * @param ch Character to be skipped
+     */
+    public int skipChar(char ch) {
+
+        while ((index < length) && (ch == chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Advance the current parsing position while it is pointing at a
+     * non-whitespace character, or until it moves past the end of the string.
+     * Return the final value.
+     */
+    public int skipText() {
+
+        while ((index < length) && !isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Advance the current parsing position while it is pointing at a
+     * whitespace character, or until it moves past the end of the string.
+     * Return the final value.
+     */
+    public int skipWhite() {
+
+        while ((index < length) && isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Is the specified character considered to be whitespace?
+     *
+     * @param ch Character to be checked
+     */
+    protected boolean isWhite(char ch) {
+
+        if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
+            return (true);
+        else
+            return (false);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/TomcatCSS.java b/container/catalina/src/share/org/apache/catalina/util/TomcatCSS.java
new file mode 100644
index 0000000..7d66b19
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/TomcatCSS.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+public class TomcatCSS {
+
+    public static final String TOMCAT_CSS =
+        "H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} " +
+        "H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} " +
+        "H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} " +
+        "BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} " +
+        "B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} " +
+        "P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}" +
+        "A {color : black;}" +
+        "A.name {color : black;}" +
+        "HR {color : #525D76;}";
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/catalina/util/URL.java b/container/catalina/src/share/org/apache/catalina/util/URL.java
new file mode 100644
index 0000000..d52552e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/URL.java
@@ -0,0 +1,707 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.util;
+
+
+import java.io.Serializable;
+import java.net.MalformedURLException;
+
+
+/**
+ * <p><strong>URL</strong> is designed to provide public APIs for parsing
+ * and synthesizing Uniform Resource Locators as similar as possible to the
+ * APIs of <code>java.net.URL</code>, but without the ability to open a
+ * stream or connection.  One of the consequences of this is that you can
+ * construct URLs for protocols for which a URLStreamHandler is not
+ * available (such as an "https" URL when JSSE is not installed).</p>
+ *
+ * <p><strong>WARNING</strong> - This class assumes that the string
+ * representation of a URL conforms to the <code>spec</code> argument
+ * as described in RFC 2396 "Uniform Resource Identifiers: Generic Syntax":
+ * <pre>
+ *   &lt;scheme&gt;//&lt;authority&gt;&lt;path&gt;?&lt;query&gt;#&lt;fragment&gt;
+ * </pre></p>
+ *
+ * <p><strong>FIXME</strong> - This class really ought to end up in a Commons
+ * package someplace.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class URL implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a URL object from the specified String representation.
+     *
+     * @param spec String representation of the URL
+     *
+     * @exception MalformedURLException if the string representation
+     *  cannot be parsed successfully
+     */
+    public URL(String spec) throws MalformedURLException {
+
+        this(null, spec);
+
+    }
+
+
+    /**
+     * Create a URL object by parsing a string representation relative
+     * to a specified context.  Based on logic from JDK 1.3.1's
+     * <code>java.net.URL</code>.
+     *
+     * @param context URL against which the relative representation
+     *  is resolved
+     * @param spec String representation of the URL (usually relative)
+     *
+     * @exception MalformedURLException if the string representation
+     *  cannot be parsed successfully
+     */
+    public URL(URL context, String spec) throws MalformedURLException {
+
+        String original = spec;
+        int i, limit, c;
+        int start = 0;
+        String newProtocol = null;
+        boolean aRef = false;
+
+        try {
+
+            // Eliminate leading and trailing whitespace
+            limit = spec.length();
+            while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
+                limit--;
+            }
+            while ((start < limit) && (spec.charAt(start) <= ' ')) {
+                start++;
+            }
+
+            // If the string representation starts with "url:", skip it
+            if (spec.regionMatches(true, start, "url:", 0, 4)) {
+                start += 4;
+            }
+
+            // Is this a ref relative to the context URL?
+            if ((start < spec.length()) && (spec.charAt(start) == '#')) {
+                aRef = true;
+            }
+
+            // Parse out the new protocol
+            for (i = start; !aRef && (i < limit) &&
+                     ((c = spec.charAt(i)) != '/'); i++) {
+                if (c == ':') {
+                    String s = spec.substring(start, i).toLowerCase();
+                    // Assume all protocols are valid
+                    newProtocol = s;
+                    start = i + 1;
+                    break;
+                }
+            }
+
+            // Only use our context if the protocols match
+            protocol = newProtocol;
+            if ((context != null) && ((newProtocol == null) ||
+                 newProtocol.equalsIgnoreCase(context.getProtocol()))) {
+                // If the context is a hierarchical URL scheme and the spec
+                // contains a matching scheme then maintain backwards
+                // compatibility and treat it as if the spec didn't contain
+                // the scheme; see 5.2.3 of RFC2396
+                if ((context.getPath() != null) &&
+                    (context.getPath().startsWith("/")))
+                    newProtocol = null;
+                if (newProtocol == null) {
+                    protocol = context.getProtocol();
+                    authority = context.getAuthority();
+                    userInfo = context.getUserInfo();
+                    host = context.getHost();
+                    port = context.getPort();
+                    file = context.getFile();
+                    int question = file.lastIndexOf("?");
+                    if (question < 0)
+                        path = file;
+                    else
+                        path = file.substring(0, question);
+                }
+            }
+
+            if (protocol == null)
+                throw new MalformedURLException("no protocol: " + original);
+
+            // Parse out any ref portion of the spec
+            i = spec.indexOf('#', start);
+            if (i >= 0) {
+                ref = spec.substring(i + 1, limit);
+                limit = i;
+            }
+
+            // Parse the remainder of the spec in a protocol-specific fashion
+            parse(spec, start, limit);
+            if (context != null)
+                normalize();
+
+
+        } catch (MalformedURLException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new MalformedURLException(e.toString());
+        }
+
+    }
+
+
+
+
+
+    /**
+     * Create a URL object from the specified components.  The default port
+     * number for the specified protocol will be used.
+     *
+     * @param protocol Name of the protocol to use
+     * @param host Name of the host addressed by this protocol
+     * @param file Filename on the specified host
+     *
+     * @exception MalformedURLException is never thrown, but present for
+     *  compatible APIs
+     */
+    public URL(String protocol, String host, String file)
+        throws MalformedURLException {
+
+        this(protocol, host, -1, file);
+
+    }
+
+
+    /**
+     * Create a URL object from the specified components.  Specifying a port
+     * number of -1 indicates that the URL should use the default port for
+     * that protocol.  Based on logic from JDK 1.3.1's
+     * <code>java.net.URL</code>.
+     *
+     * @param protocol Name of the protocol to use
+     * @param host Name of the host addressed by this protocol
+     * @param port Port number, or -1 for the default port for this protocol
+     * @param file Filename on the specified host
+     *
+     * @exception MalformedURLException is never thrown, but present for
+     *  compatible APIs
+     */
+    public URL(String protocol, String host, int port, String file)
+        throws MalformedURLException {
+
+        this.protocol = protocol;
+        this.host = host;
+        this.port = port;
+
+        int hash = file.indexOf('#');
+        this.file = hash < 0 ? file : file.substring(0, hash);
+        this.ref = hash < 0 ? null : file.substring(hash + 1);
+        int question = file.lastIndexOf('?');
+        if (question >= 0) {
+            query = file.substring(question + 1);
+            path = file.substring(0, question);
+        } else
+            path = file;
+
+        if ((host != null) && (host.length() > 0))
+            authority = (port == -1) ? host : host + ":" + port;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The authority part of the URL.
+     */
+    private String authority = null;
+
+
+    /**
+     * The filename part of the URL.
+     */
+    private String file = null;
+
+
+    /**
+     * The host name part of the URL.
+     */
+    private String host = null;
+
+
+    /**
+     * The path part of the URL.
+     */
+    private String path = null;
+
+
+    /**
+     * The port number part of the URL.
+     */
+    private int port = -1;
+
+
+    /**
+     * The protocol name part of the URL.
+     */
+    private String protocol = null;
+
+
+    /**
+     * The query part of the URL.
+     */
+    private String query = null;
+
+
+    /**
+     * The reference part of the URL.
+     */
+    private String ref = null;
+
+
+    /**
+     * The user info part of the URL.
+     */
+    private String userInfo = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Compare two URLs for equality.  The result is <code>true</code> if and
+     * only if the argument is not null, and is a <code>URL</code> object
+     * that represents the same <code>URL</code> as this object.  Two
+     * <code>URLs</code> are equal if they have the same protocol and
+     * reference the same host, the same port number on the host,
+     * and the same file and anchor on the host.
+     *
+     * @param obj The URL to compare against
+     */
+    public boolean equals(Object obj) {
+
+        if (obj == null)
+            return (false);
+        if (!(obj instanceof URL))
+            return (false);
+        URL other = (URL) obj;
+        if (!sameFile(other))
+            return (false);
+        return (compare(ref, other.getRef()));
+
+    }
+
+
+    /**
+     * Return the authority part of the URL.
+     */
+    public String getAuthority() {
+
+        return (this.authority);
+
+    }
+
+
+    /**
+     * Return the filename part of the URL.  <strong>NOTE</strong> - For
+     * compatibility with <code>java.net.URL</code>, this value includes
+     * the query string if there was one.  For just the path portion,
+     * call <code>getPath()</code> instead.
+     */
+    public String getFile() {
+
+        if (file == null)
+            return ("");
+        return (this.file);
+
+    }
+
+
+    /**
+     * Return the host name part of the URL.
+     */
+    public String getHost() {
+
+        return (this.host);
+
+    }
+
+
+    /**
+     * Return the path part of the URL.
+     */
+    public String getPath() {
+
+        if (this.path == null)
+            return ("");
+        return (this.path);
+
+    }
+
+
+    /**
+     * Return the port number part of the URL.
+     */
+    public int getPort() {
+
+        return (this.port);
+
+    }
+
+
+    /**
+     * Return the protocol name part of the URL.
+     */
+    public String getProtocol() {
+
+        return (this.protocol);
+
+    }
+
+
+    /**
+     * Return the query part of the URL.
+     */
+    public String getQuery() {
+
+        return (this.query);
+
+    }
+
+
+    /**
+     * Return the reference part of the URL.
+     */
+    public String getRef() {
+
+        return (this.ref);
+
+    }
+
+
+    /**
+     * Return the user info part of the URL.
+     */
+    public String getUserInfo() {
+
+        return (this.userInfo);
+
+    }
+
+
+    /**
+     * Normalize the <code>path</code> (and therefore <code>file</code>)
+     * portions of this URL.
+     * <p>
+     * <strong>NOTE</strong> - This method is not part of the public API
+     * of <code>java.net.URL</code>, but is provided as a value added
+     * service of this implementation.
+     *
+     * @exception MalformedURLException if a normalization error occurs,
+     *  such as trying to move about the hierarchical root
+     */
+    public void normalize() throws MalformedURLException {
+
+        // Special case for null path
+        if (path == null) {
+            if (query != null)
+                file = "?" + query;
+            else
+                file = "";
+            return;
+        }
+
+        // Create a place for the normalized path
+        String normalized = path;
+        if (normalized.equals("/.")) {
+            path = "/";
+            if (query != null)
+                file = path + "?" + query;
+            else
+                file = path;
+            return;
+        }
+
+        // Normalize the slashes and add leading slash if necessary
+        if (normalized.indexOf('\\') >= 0)
+            normalized = normalized.replace('\\', '/');
+        if (!normalized.startsWith("/"))
+            normalized = "/" + normalized;
+
+        // Resolve occurrences of "//" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("//");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 1);
+        }
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/./");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 2);
+        }
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/../");
+            if (index < 0)
+                break;
+            if (index == 0)
+                throw new MalformedURLException
+                    ("Invalid relative URL reference");
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            normalized = normalized.substring(0, index2) +
+                normalized.substring(index + 3);
+        }
+
+        // Resolve occurrences of "/." at the end of the normalized path
+        if (normalized.endsWith("/."))
+            normalized = normalized.substring(0, normalized.length() - 1);
+
+        // Resolve occurrences of "/.." at the end of the normalized path
+        if (normalized.endsWith("/..")) {
+            int index = normalized.length() - 3;
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            if (index2 < 0)
+                throw new MalformedURLException
+                    ("Invalid relative URL reference");
+            normalized = normalized.substring(0, index2 + 1);
+        }
+
+        // Return the normalized path that we have completed
+        path = normalized;
+        if (query != null)
+            file = path + "?" + query;
+        else
+            file = path;
+
+    }
+
+
+    /**
+     * Compare two URLs, excluding the "ref" fields.  Returns <code>true</code>
+     * if this <code>URL</code> and the <code>other</code> argument both refer
+     * to the same resource.  The two <code>URLs</code> might not both contain
+     * the same anchor.
+     */
+    public boolean sameFile(URL other) {
+
+        if (!compare(protocol, other.getProtocol()))
+            return (false);
+        if (!compare(host, other.getHost()))
+            return (false);
+        if (port != other.getPort())
+            return (false);
+        if (!compare(file, other.getFile()))
+            return (false);
+        return (true);
+
+    }
+
+
+    /**
+     * Return a string representation of this URL.  This follow the rules in
+     * RFC 2396, Section 5.2, Step 7.
+     */
+    public String toExternalForm() {
+
+        StringBuffer sb = new StringBuffer();
+        if (protocol != null) {
+            sb.append(protocol);
+            sb.append(":");
+        }
+        if (authority != null) {
+            sb.append("//");
+            sb.append(authority);
+        }
+        if (path != null)
+            sb.append(path);
+        if (query != null) {
+            sb.append('?');
+            sb.append(query);
+        }
+        if (ref != null) {
+            sb.append('#');
+            sb.append(ref);
+        }
+        return (sb.toString());
+
+    }
+
+
+    /**
+     * Return a string representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("URL[");
+        sb.append("authority=");
+        sb.append(authority);
+        sb.append(", file=");
+        sb.append(file);
+        sb.append(", host=");
+        sb.append(host);
+        sb.append(", port=");
+        sb.append(port);
+        sb.append(", protocol=");
+        sb.append(protocol);
+        sb.append(", query=");
+        sb.append(query);
+        sb.append(", ref=");
+        sb.append(ref);
+        sb.append(", userInfo=");
+        sb.append(userInfo);
+        sb.append("]");
+        return (sb.toString());
+
+        //        return (toExternalForm());
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Compare to String values for equality, taking appropriate care if one
+     * or both of the values are <code>null</code>.
+     *
+     * @param first First string
+     * @param second Second string
+     */
+    private boolean compare(String first, String second) {
+
+        if (first == null) {
+            if (second == null)
+                return (true);
+            else
+                return (false);
+        } else {
+            if (second == null)
+                return (false);
+            else
+                return (first.equals(second));
+        }
+
+    }
+
+
+    /**
+     * Parse the specified portion of the string representation of a URL,
+     * assuming that it has a format similar to that for <code>http</code>.
+     *
+     * <p><strong>FIXME</strong> - This algorithm can undoubtedly be optimized
+     * for performance.  However, that needs to wait until after sufficient
+     * unit tests are implemented to guarantee correct behavior with no
+     * regressions.</p>
+     *
+     * @param spec String representation being parsed
+     * @param start Starting offset, which will be just after the ':' (if
+     *  there is one) that determined the protocol name
+     * @param limit Ending position, which will be the position of the '#'
+     *  (if there is one) that delimited the anchor
+     *
+     * @exception MalformedURLException if a parsing error occurs
+     */
+    private void parse(String spec, int start, int limit)
+        throws MalformedURLException {
+
+        // Trim the query string (if any) off the tail end
+        int question = spec.lastIndexOf('?', limit - 1);
+        if ((question >= 0) && (question < limit)) {
+            query = spec.substring(question + 1, limit);
+            limit = question;
+        } else {
+            query = null;
+        }
+
+        // Parse the authority section
+        if (spec.indexOf("//", start) == start) {
+            int pathStart = spec.indexOf("/", start + 2);
+            if ((pathStart >= 0) && (pathStart < limit)) {
+                authority = spec.substring(start + 2, pathStart);
+                start = pathStart;
+            } else {
+                authority = spec.substring(start + 2, limit);
+                start = limit;
+            }
+            if (authority.length() > 0) {
+                int at = authority.indexOf('@');
+                if( at >= 0 ) {
+                    userInfo = authority.substring(0,at);
+                }
+                int colon = authority.indexOf(':',at+1);
+                if (colon >= 0) {
+                    try {
+                        port =
+                            Integer.parseInt(authority.substring(colon + 1));
+                    } catch (NumberFormatException e) {
+                        throw new MalformedURLException(e.toString());
+                    }
+                    host = authority.substring(at+1, colon);
+                } else {
+                    host = authority.substring(at+1);
+                    port = -1;
+                }
+            }
+        }
+
+        // Parse the path section
+        if (spec.indexOf("/", start) == start) {     // Absolute path
+            path = spec.substring(start, limit);
+            if (query != null)
+                file = path + "?" + query;
+            else
+                file = path;
+            return;
+        }
+
+        // Resolve relative path against our context's file
+        if (path == null) {
+            if (query != null)
+                file = "?" + query;
+            else
+                file = null;
+            return;
+        }
+        if (!path.startsWith("/"))
+            throw new MalformedURLException
+                ("Base path does not start with '/'");
+        if (!path.endsWith("/"))
+            path += "/../";
+        path += spec.substring(start, limit);
+        if (query != null)
+            file = path + "?" + query;
+        else
+            file = path;
+        return;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/URLEncoder.java b/container/catalina/src/share/org/apache/catalina/util/URLEncoder.java
new file mode 100644
index 0000000..8d23dbf
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/URLEncoder.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.BitSet;
+
+/**
+ *
+ * This class is very similar to the java.net.URLEncoder class.
+ *
+ * Unfortunately, with java.net.URLEncoder there is no way to specify to the 
+ * java.net.URLEncoder which characters should NOT be encoded.
+ *
+ * This code was moved from DefaultServlet.java
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ */
+public class URLEncoder {
+    protected static final char[] hexadecimal =
+    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+     'A', 'B', 'C', 'D', 'E', 'F'};
+
+    //Array containing the safe characters set.
+    protected BitSet safeCharacters = new BitSet(256);
+
+    public URLEncoder() {
+        for (char i = 'a'; i <= 'z'; i++) {
+            addSafeCharacter(i);
+        }
+        for (char i = 'A'; i <= 'Z'; i++) {
+            addSafeCharacter(i);
+        }
+        for (char i = '0'; i <= '9'; i++) {
+            addSafeCharacter(i);
+        }
+    }
+
+    public void addSafeCharacter( char c ) {
+	safeCharacters.set( c );
+    }
+
+    public String encode( String path ) {
+        int maxBytesPerChar = 10;
+        int caseDiff = ('a' - 'A');
+        StringBuffer rewrittenPath = new StringBuffer(path.length());
+        ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
+        OutputStreamWriter writer = null;
+        try {
+            writer = new OutputStreamWriter(buf, "UTF8");
+        } catch (Exception e) {
+            e.printStackTrace();
+            writer = new OutputStreamWriter(buf);
+        }
+
+        for (int i = 0; i < path.length(); i++) {
+            int c = (int) path.charAt(i);
+            if (safeCharacters.get(c)) {
+                rewrittenPath.append((char)c);
+            } else {
+                // convert to external encoding before hex conversion
+                try {
+                    writer.write((char)c);
+                    writer.flush();
+                } catch(IOException e) {
+                    buf.reset();
+                    continue;
+                }
+                byte[] ba = buf.toByteArray();
+                for (int j = 0; j < ba.length; j++) {
+                    // Converting each byte in the buffer
+                    byte toEncode = ba[j];
+                    rewrittenPath.append('%');
+                    int low = (int) (toEncode & 0x0f);
+                    int high = (int) ((toEncode & 0xf0) >> 4);
+                    rewrittenPath.append(hexadecimal[high]);
+                    rewrittenPath.append(hexadecimal[low]);
+                }
+                buf.reset();
+            }
+        }
+        return rewrittenPath.toString();
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/util/XMLWriter.java b/container/catalina/src/share/org/apache/catalina/util/XMLWriter.java
new file mode 100644
index 0000000..f1a71a6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/util/XMLWriter.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * XMLWriter helper class.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ */
+public class XMLWriter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Opening tag.
+     */
+    public static final int OPENING = 0;
+
+
+    /**
+     * Closing tag.
+     */
+    public static final int CLOSING = 1;
+
+
+    /**
+     * Element with no content.
+     */
+    public static final int NO_CONTENT = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Buffer.
+     */
+    protected StringBuffer buffer = new StringBuffer();
+
+
+    /**
+     * Writer.
+     */
+    protected Writer writer = null;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructor.
+     */
+    public XMLWriter() {
+    }
+
+
+    /**
+     * Constructor.
+     */
+    public XMLWriter(Writer writer) {
+        this.writer = writer;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieve generated XML.
+     *
+     * @return String containing the generated XML
+     */
+    public String toString() {
+        return buffer.toString();
+    }
+
+
+    /**
+     * Write property to the XML.
+     *
+     * @param namespace Namespace
+     * @param namespaceInfo Namespace info
+     * @param name Property name
+     * @param value Property value
+     */
+    public void writeProperty(String namespace, String namespaceInfo,
+                              String name, String value) {
+        writeElement(namespace, namespaceInfo, name, OPENING);
+        buffer.append(value);
+        writeElement(namespace, namespaceInfo, name, CLOSING);
+
+    }
+
+
+    /**
+     * Write property to the XML.
+     *
+     * @param namespace Namespace
+     * @param name Property name
+     * @param value Property value
+     */
+    public void writeProperty(String namespace, String name, String value) {
+        writeElement(namespace, name, OPENING);
+        buffer.append(value);
+        writeElement(namespace, name, CLOSING);
+    }
+
+
+    /**
+     * Write property to the XML.
+     *
+     * @param namespace Namespace
+     * @param name Property name
+     */
+    public void writeProperty(String namespace, String name) {
+        writeElement(namespace, name, NO_CONTENT);
+    }
+
+
+    /**
+     * Write an element.
+     *
+     * @param name Element name
+     * @param namespace Namespace abbreviation
+     * @param type Element type
+     */
+    public void writeElement(String namespace, String name, int type) {
+        writeElement(namespace, null, name, type);
+    }
+
+
+    /**
+     * Write an element.
+     *
+     * @param namespace Namespace abbreviation
+     * @param namespaceInfo Namespace info
+     * @param name Element name
+     * @param type Element type
+     */
+    public void writeElement(String namespace, String namespaceInfo,
+                             String name, int type) {
+        if ((namespace != null) && (namespace.length() > 0)) {
+            switch (type) {
+            case OPENING:
+                if (namespaceInfo != null) {
+                    buffer.append("<" + namespace + ":" + name + " xmlns:"
+                                  + namespace + "=\""
+                                  + namespaceInfo + "\">");
+                } else {
+                    buffer.append("<" + namespace + ":" + name + ">");
+                }
+                break;
+            case CLOSING:
+                buffer.append("</" + namespace + ":" + name + ">\n");
+                break;
+            case NO_CONTENT:
+            default:
+                if (namespaceInfo != null) {
+                    buffer.append("<" + namespace + ":" + name + " xmlns:"
+                                  + namespace + "=\""
+                                  + namespaceInfo + "\"/>");
+                } else {
+                    buffer.append("<" + namespace + ":" + name + "/>");
+                }
+                break;
+            }
+        } else {
+            switch (type) {
+            case OPENING:
+                buffer.append("<" + name + ">");
+                break;
+            case CLOSING:
+                buffer.append("</" + name + ">\n");
+                break;
+            case NO_CONTENT:
+            default:
+                buffer.append("<" + name + "/>");
+                break;
+            }
+        }
+    }
+
+
+    /**
+     * Write text.
+     *
+     * @param text Text to append
+     */
+    public void writeText(String text) {
+        buffer.append(text);
+    }
+
+
+    /**
+     * Write data.
+     *
+     * @param data Data to append
+     */
+    public void writeData(String data) {
+        buffer.append("<![CDATA[" + data + "]]>");
+    }
+
+
+    /**
+     * Write XML Header.
+     */
+    public void writeXMLHeader() {
+        buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    }
+
+
+    /**
+     * Send data and reinitializes buffer.
+     */
+    public void sendData()
+        throws IOException {
+        if (writer != null) {
+            writer.write(buffer.toString());
+            buffer = new StringBuffer();
+        }
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/AccessLogValve.java b/container/catalina/src/share/org/apache/catalina/valves/AccessLogValve.java
new file mode 100644
index 0000000..0af939d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/AccessLogValve.java
@@ -0,0 +1,1131 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * <p>Implementation of the <b>Valve</b> interface that generates a web server
+ * access log with the detailed line contents matching a configurable pattern.
+ * The syntax of the available patterns is similar to that supported by the
+ * Apache <code>mod_log_config</code> module.  As an additional feature,
+ * automatic rollover of log files when the date changes is also supported.</p>
+ *
+ * <p>Patterns for the logged message may include constant text or any of the
+ * following replacement strings, for which the corresponding information
+ * from the specified Response is substituted:</p>
+ * <ul>
+ * <li><b>%a</b> - Remote IP address
+ * <li><b>%A</b> - Local IP address
+ * <li><b>%b</b> - Bytes sent, excluding HTTP headers, or '-' if no bytes
+ *     were sent
+ * <li><b>%B</b> - Bytes sent, excluding HTTP headers
+ * <li><b>%h</b> - Remote host name
+ * <li><b>%H</b> - Request protocol
+ * <li><b>%l</b> - Remote logical username from identd (always returns '-')
+ * <li><b>%m</b> - Request method
+ * <li><b>%p</b> - Local port
+ * <li><b>%q</b> - Query string (prepended with a '?' if it exists, otherwise
+ *     an empty string
+ * <li><b>%r</b> - First line of the request
+ * <li><b>%s</b> - HTTP status code of the response
+ * <li><b>%S</b> - User session ID
+ * <li><b>%t</b> - Date and time, in Common Log Format format
+ * <li><b>%u</b> - Remote user that was authenticated
+ * <li><b>%U</b> - Requested URL path
+ * <li><b>%v</b> - Local server name
+ * <li><b>%D</b> - Time taken to process the request, in millis
+ * <li><b>%T</b> - Time taken to process the request, in seconds
+ * </ul>
+ * <p>In addition, the caller can specify one of the following aliases for
+ * commonly utilized patterns:</p>
+ * <ul>
+ * <li><b>common</b> - <code>%h %l %u %t "%r" %s %b</code>
+ * <li><b>combined</b> -
+ *   <code>%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"</code>
+ * </ul>
+ *
+ * <p>
+ * There is also support to write information from the cookie, incoming
+ * header, the Session or something else in the ServletRequest.<br>
+ * It is modeled after the apache syntax:
+ * <ul>
+ * <li><code>%{xxx}i</code> for incoming headers
+ * <li><code>%{xxx}c</code> for a specific cookie
+ * <li><code>%{xxx}r</code> xxx is an attribute in the ServletRequest
+ * <li><code>%{xxx}s</code> xxx is an attribute in the HttpSession
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * Conditional logging is also supported. This can be done with the
+ * <code>condition</code> property.
+ * If the value returned from ServletRequest.getAttribute(condition)
+ * yields a non-null value. The logging will be skipped.
+ * </p>
+ *
+ * @author Craig R. McClanahan
+ * @author Jason Brittain
+ * @version $Revision$ $Date$
+ */
+
+public class AccessLogValve
+    extends ValveBase
+    implements Lifecycle {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class with default property values.
+     */
+    public AccessLogValve() {
+
+        super();
+        setPattern("common");
+
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The as-of date for the currently open log file, or a zero-length
+     * string if there is no open log file.
+     */
+    private String dateStamp = "";
+
+
+    /**
+     * The directory in which log files are created.
+     */
+    private String directory = "logs";
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.valves.AccessLogValve/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The set of month abbreviations for log messages.
+     */
+    protected static final String months[] =
+    { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+
+    /**
+     * If the current log pattern is the same as the common access log
+     * format pattern, then we'll set this variable to true and log in
+     * a more optimal and hard-coded way.
+     */
+    private boolean common = false;
+
+
+    /**
+     * For the combined format (common, plus useragent and referer), we do
+     * the same
+     */
+    private boolean combined = false;
+
+
+    /**
+     * The pattern used to format our access log lines.
+     */
+    private String pattern = null;
+
+
+    /**
+     * The prefix that is added to log file filenames.
+     */
+    private String prefix = "access_log.";
+
+
+    /**
+     * Should we rotate our log file? Default is true (like old behavior)
+     */
+    private boolean rotatable = true;
+
+
+    /**
+     * The string manager for this package.
+     */
+    private StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The suffix that is added to log file filenames.
+     */
+    private String suffix = "";
+
+
+    /**
+     * The PrintWriter to which we are currently logging, if any.
+     */
+    private PrintWriter writer = null;
+
+
+    /**
+     * A date formatter to format a Date into a date in the format
+     * "yyyy-MM-dd".
+     */
+    private SimpleDateFormat dateFormatter = null;
+
+
+    /**
+     * A date formatter to format Dates into a day string in the format
+     * "dd".
+     */
+    private SimpleDateFormat dayFormatter = null;
+
+
+    /**
+     * A date formatter to format a Date into a month string in the format
+     * "MM".
+     */
+    private SimpleDateFormat monthFormatter = null;
+
+
+    /**
+     * Time taken formatter for 3 decimal places.
+     */
+     private DecimalFormat timeTakenFormatter = null;
+
+
+    /**
+     * A date formatter to format a Date into a year string in the format
+     * "yyyy".
+     */
+    private SimpleDateFormat yearFormatter = null;
+
+
+    /**
+     * A date formatter to format a Date into a time in the format
+     * "kk:mm:ss" (kk is a 24-hour representation of the hour).
+     */
+    private SimpleDateFormat timeFormatter = null;
+
+
+    /**
+     * The system timezone.
+     */
+    private TimeZone timezone = null;
+
+    
+    /**
+     * The time zone offset relative to GMT in text form when daylight saving
+     * is not in operation.
+     */
+    private String timeZoneNoDST = null;
+
+
+    /**
+     * The time zone offset relative to GMT in text form when daylight saving
+     * is in operation.
+     */
+    private String timeZoneDST = null;
+    
+    
+    /**
+     * The system time when we last updated the Date that this valve
+     * uses for log lines.
+     */
+    private Date currentDate = null;
+
+
+    /**
+     * When formatting log lines, we often use strings like this one (" ").
+     */
+    private String space = " ";
+
+
+    /**
+     * Resolve hosts.
+     */
+    private boolean resolveHosts = false;
+
+
+    /**
+     * Instant when the log daily rotation was last checked.
+     */
+    private long rotationLastChecked = 0L;
+
+
+    /**
+     * Are we doing conditional logging. default false.
+     */
+    private String condition = null;
+
+
+    /**
+     * Date format to place in log file name. Use at your own risk!
+     */
+    private String fileDateFormat = null;
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the directory in which we create log files.
+     */
+    public String getDirectory() {
+
+        return (directory);
+
+    }
+
+
+    /**
+     * Set the directory in which we create log files.
+     *
+     * @param directory The new log file directory
+     */
+    public void setDirectory(String directory) {
+
+        this.directory = directory;
+
+    }
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the format pattern.
+     */
+    public String getPattern() {
+
+        return (this.pattern);
+
+    }
+
+
+    /**
+     * Set the format pattern, first translating any recognized alias.
+     *
+     * @param pattern The new pattern
+     */
+    public void setPattern(String pattern) {
+
+        if (pattern == null)
+            pattern = "";
+        if (pattern.equals(Constants.AccessLog.COMMON_ALIAS))
+            pattern = Constants.AccessLog.COMMON_PATTERN;
+        if (pattern.equals(Constants.AccessLog.COMBINED_ALIAS))
+            pattern = Constants.AccessLog.COMBINED_PATTERN;
+        this.pattern = pattern;
+
+        if (this.pattern.equals(Constants.AccessLog.COMMON_PATTERN))
+            common = true;
+        else
+            common = false;
+
+        if (this.pattern.equals(Constants.AccessLog.COMBINED_PATTERN))
+            combined = true;
+        else
+            combined = false;
+
+    }
+
+
+    /**
+     * Return the log file prefix.
+     */
+    public String getPrefix() {
+
+        return (prefix);
+
+    }
+
+
+    /**
+     * Set the log file prefix.
+     *
+     * @param prefix The new log file prefix
+     */
+    public void setPrefix(String prefix) {
+
+        this.prefix = prefix;
+
+    }
+
+
+    /**
+     * Should we rotate the logs
+     */
+    public boolean isRotatable() {
+
+        return rotatable;
+
+    }
+
+
+    /**
+     * Set the value is we should we rotate the logs
+     *
+     * @param rotatable true is we should rotate.
+     */
+    public void setRotatable(boolean rotatable) {
+
+        this.rotatable = rotatable;
+
+    }
+
+
+    /**
+     * Return the log file suffix.
+     */
+    public String getSuffix() {
+
+        return (suffix);
+
+    }
+
+
+    /**
+     * Set the log file suffix.
+     *
+     * @param suffix The new log file suffix
+     */
+    public void setSuffix(String suffix) {
+
+        this.suffix = suffix;
+
+    }
+
+
+    /**
+     * Set the resolve hosts flag.
+     *
+     * @param resolveHosts The new resolve hosts value
+     */
+    public void setResolveHosts(boolean resolveHosts) {
+
+        this.resolveHosts = resolveHosts;
+
+    }
+
+
+    /**
+     * Get the value of the resolve hosts flag.
+     */
+    public boolean isResolveHosts() {
+
+        return resolveHosts;
+
+    }
+
+
+    /**
+     * Return whether the attribute name to look for when
+     * performing conditional loggging. If null, every
+     * request is logged.
+     */
+    public String getCondition() {
+
+        return condition;
+
+    }
+
+
+    /**
+     * Set the ServletRequest.attribute to look for to perform
+     * conditional logging. Set to null to log everything.
+     *
+     * @param condition Set to null to log everything
+     */
+    public void setCondition(String condition) {
+
+        this.condition = condition;
+
+    }
+
+    /**
+     *  Return the date format date based log rotation.
+     */
+    public String getFileDateFormat() {
+        return fileDateFormat;
+    }
+
+
+    /**
+     *  Set the date format date based log rotation.
+     */
+    public void setFileDateFormat(String fileDateFormat) {
+        this.fileDateFormat =  fileDateFormat;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Log a message summarizing the specified request and response, according
+     * to the format specified by the <code>pattern</code> property.
+     *
+     * @param request Request being processed
+     * @param response Response being processed
+     *
+     * @exception IOException if an input/output error has occurred
+     * @exception ServletException if a servlet error has occurred
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // Pass this request on to the next valve in our pipeline
+        long t1=System.currentTimeMillis();
+
+        getNext().invoke(request, response);
+
+        long t2=System.currentTimeMillis();
+        long time=t2-t1;
+
+        if (condition!=null &&
+                null!=request.getRequest().getAttribute(condition)) {
+            return;
+        }
+
+
+        Date date = getDate();
+        StringBuffer result = new StringBuffer();
+
+        // Check to see if we should log using the "common" access log pattern
+        if (common || combined) {
+            String value = null;
+
+            if (isResolveHosts())
+                result.append(request.getRemoteHost());
+            else
+                result.append(request.getRemoteAddr());
+
+            result.append(" - ");
+
+            value = request.getRemoteUser();
+            if (value == null)
+                result.append("- ");
+            else {
+                result.append(value);
+                result.append(space);
+            }
+
+            result.append("[");
+            result.append(dayFormatter.format(date));           // Day
+            result.append('/');
+            result.append(lookup(monthFormatter.format(date))); // Month
+            result.append('/');
+            result.append(yearFormatter.format(date));          // Year
+            result.append(':');
+            result.append(timeFormatter.format(date));          // Time
+            result.append(space);
+            result.append(getTimeZone(date));                   // Time Zone
+            result.append("] \"");
+
+            result.append(request.getMethod());
+            result.append(space);
+            result.append(request.getRequestURI());
+            if (request.getQueryString() != null) {
+                result.append('?');
+                result.append(request.getQueryString());
+            }
+            result.append(space);
+            result.append(request.getProtocol());
+            result.append("\" ");
+
+            result.append(response.getStatus());
+
+            result.append(space);
+
+            int length = response.getContentCount();
+
+            if (length <= 0)
+                value = "-";
+            else
+                value = "" + length;
+            result.append(value);
+
+            if (combined) {
+                result.append(space);
+                result.append("\"");
+                String referer = request.getHeader("referer");
+                if(referer != null)
+                    result.append(referer);
+                else
+                    result.append("-");
+                result.append("\"");
+
+                result.append(space);
+                result.append("\"");
+                String ua = request.getHeader("user-agent");
+                if(ua != null)
+                    result.append(ua);
+                else
+                    result.append("-");
+                result.append("\"");
+            }
+
+        } else {
+            // Generate a message based on the defined pattern
+            boolean replace = false;
+            for (int i = 0; i < pattern.length(); i++) {
+                char ch = pattern.charAt(i);
+                if (replace) {
+                    /* For code that processes {, the behavior will be ... if I
+                     * do not enounter a closing } - then I ignore the {
+                     */
+                    if ('{' == ch){
+                        StringBuffer name = new StringBuffer();
+                        int j = i + 1;
+                        for(;j < pattern.length() && '}' != pattern.charAt(j); j++) {
+                            name.append(pattern.charAt(j));
+                        }
+                        if (j+1 < pattern.length()) {
+                            /* the +1 was to account for } which we increment now */
+                            j++;
+                            result.append(replace(name.toString(),
+                                                pattern.charAt(j),
+                                                request,
+                                                response));
+                            i=j; /*Since we walked more than one character*/
+                        } else {
+                            //D'oh - end of string - pretend we never did this
+                            //and do processing the "old way"
+                            result.append(replace(ch, date, request, response, time));
+                        }
+                    } else {
+                        result.append(replace(ch, date, request, response,time ));
+                    }
+                    replace = false;
+                } else if (ch == '%') {
+                    replace = true;
+                } else {
+                    result.append(ch);
+                }
+            }
+        }
+        log(result.toString(), date);
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Close the currently open log file (if any)
+     */
+    private synchronized void close() {
+
+        if (writer == null)
+            return;
+        writer.flush();
+        writer.close();
+        writer = null;
+        dateStamp = "";
+
+    }
+
+
+    /**
+     * Log the specified message to the log file, switching files if the date
+     * has changed since the previous log call.
+     *
+     * @param message Message to be logged
+     * @param date the current Date object (so this method doesn't need to
+     *        create a new one)
+     */
+    public void log(String message, Date date) {
+
+        if (rotatable){
+            // Only do a logfile switch check once a second, max.
+            long systime = System.currentTimeMillis();
+            if ((systime - rotationLastChecked) > 1000) {
+
+                // We need a new currentDate
+                currentDate = new Date(systime);
+                rotationLastChecked = systime;
+
+                // Check for a change of date
+                String tsDate = dateFormatter.format(currentDate);
+
+                // If the date has changed, switch log files
+                if (!dateStamp.equals(tsDate)) {
+                    synchronized (this) {
+                        if (!dateStamp.equals(tsDate)) {
+                            close();
+                            dateStamp = tsDate;
+                            open();
+                        }
+                    }
+                }
+
+            }
+        }
+
+        // Log this message
+        if (writer != null) {
+            writer.println(message);
+        }
+
+    }
+
+
+    /**
+     * Return the month abbreviation for the specified month, which must
+     * be a two-digit String.
+     *
+     * @param month Month number ("01" .. "12").
+     */
+    private String lookup(String month) {
+
+        int index;
+        try {
+            index = Integer.parseInt(month) - 1;
+        } catch (Throwable t) {
+            index = 0;  // Can not happen, in theory
+        }
+        return (months[index]);
+
+    }
+
+
+    /**
+     * Open the new log file for the date specified by <code>dateStamp</code>.
+     */
+    private synchronized void open() {
+
+        // Create the directory if necessary
+        File dir = new File(directory);
+        if (!dir.isAbsolute())
+            dir = new File(System.getProperty("catalina.base"), directory);
+        dir.mkdirs();
+
+        // Open the current log file
+        try {
+            String pathname;
+            // If no rotate - no need for dateStamp in fileName
+            if (rotatable){
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + dateStamp + suffix;
+            } else {
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + suffix;
+            }
+            writer = new PrintWriter(new FileWriter(pathname, true), true);
+        } catch (IOException e) {
+            writer = null;
+        }
+
+    }
+
+
+    /**
+     * Return the replacement text for the specified pattern character.
+     *
+     * @param pattern Pattern character identifying the desired text
+     * @param date the current Date so that this method doesn't need to
+     *        create one
+     * @param request Request being processed
+     * @param response Response being processed
+     */
+    private String replace(char pattern, Date date, Request request,
+                           Response response, long time) {
+
+        String value = null;
+
+        if (pattern == 'a') {
+            value = request.getRemoteAddr();
+        } else if (pattern == 'A') {
+            try {
+                value = InetAddress.getLocalHost().getHostAddress();
+            } catch(Throwable e){
+                value = "127.0.0.1";
+            }
+        } else if (pattern == 'b') {
+            int length = response.getContentCount();
+            if (length <= 0)
+                value = "-";
+            else
+                value = "" + length;
+        } else if (pattern == 'B') {
+            value = "" + response.getContentLength();
+        } else if (pattern == 'h') {
+            value = request.getRemoteHost();
+        } else if (pattern == 'H') {
+            value = request.getProtocol();
+        } else if (pattern == 'l') {
+            value = "-";
+        } else if (pattern == 'm') {
+            if (request != null)
+                value = request.getMethod();
+            else
+                value = "";
+        } else if (pattern == 'p') {
+            value = "" + request.getServerPort();
+        } else if (pattern == 'D') {
+                    value = "" + time;
+        } else if (pattern == 'q') {
+            String query = null;
+            if (request != null)
+                query = request.getQueryString();
+            if (query != null)
+                value = "?" + query;
+            else
+                value = "";
+        } else if (pattern == 'r') {
+            StringBuffer sb = new StringBuffer();
+            if (request != null) {
+                sb.append(request.getMethod());
+                sb.append(space);
+                sb.append(request.getRequestURI());
+                if (request.getQueryString() != null) {
+                    sb.append('?');
+                    sb.append(request.getQueryString());
+                }
+                sb.append(space);
+                sb.append(request.getProtocol());
+            } else {
+                sb.append("- - ");
+                sb.append(request.getProtocol());
+            }
+            value = sb.toString();
+        } else if (pattern == 'S') {
+            if (request != null)
+                if (request.getSession(false) != null)
+                    value = request.getSessionInternal(false).getIdInternal();
+                else value = "-";
+            else
+                value = "-";
+        } else if (pattern == 's') {
+            if (response != null)
+                value = "" + response.getStatus();
+            else
+                value = "-";
+        } else if (pattern == 't') {
+            StringBuffer temp = new StringBuffer("[");
+            temp.append(dayFormatter.format(date));             // Day
+            temp.append('/');
+            temp.append(lookup(monthFormatter.format(date)));   // Month
+            temp.append('/');
+            temp.append(yearFormatter.format(date));            // Year
+            temp.append(':');
+            temp.append(timeFormatter.format(date));            // Time
+            temp.append(' ');
+            temp.append(getTimeZone(date));                     // Timezone
+            temp.append(']');
+            value = temp.toString();
+        } else if (pattern == 'T') {
+            value = timeTakenFormatter.format(time/1000d);
+        } else if (pattern == 'u') {
+            if (request != null)
+                value = request.getRemoteUser();
+            if (value == null)
+                value = "-";
+        } else if (pattern == 'U') {
+            if (request != null)
+                value = request.getRequestURI();
+            else
+                value = "-";
+        } else if (pattern == 'v') {
+            value = request.getServerName();
+        } else {
+            value = "???" + pattern + "???";
+        }
+
+        if (value == null)
+            return ("");
+        else
+            return (value);
+
+    }
+
+
+    /**
+     * Return the replacement text for the specified "header/parameter".
+     *
+     * @param header The header/parameter to get
+     * @param type Where to get it from i=input,c=cookie,r=ServletRequest,s=Session
+     * @param request Request being processed
+     * @param response Response being processed
+     */
+    private String replace(String header, char type, Request request,
+                           Response response) {
+
+        Object value = null;
+
+        switch (type) {
+            case 'i':
+                if (null != request)
+                    value = request.getHeader(header);
+                else
+                    value= "??";
+                break;
+/*
+            // Someone please make me work
+            case 'o':
+                break;
+*/
+            case 'c':
+                 Cookie[] c = request.getCookies();
+                 for (int i=0; c != null && i < c.length; i++){
+                     if (header.equals(c[i].getName())){
+                         value = c[i].getValue();
+                         break;
+                     }
+                 }
+                break;
+            case 'r':
+                if (null != request)
+                    value = request.getAttribute(header);
+                else
+                    value= "??";
+                break;
+            case 's':
+                if (null != request) {
+                    HttpSession sess = request.getSession(false);
+                    if (null != sess)
+                        value = sess.getAttribute(header);
+                }
+               break;
+            default:
+                value = "???";
+        }
+
+        /* try catch in case toString() barfs */
+        try {
+            if (value!=null)
+                if (value instanceof String)
+                    return (String)value;
+                else
+                    return value.toString();
+            else
+               return "-";
+        } catch(Throwable e) {
+            return "-";
+        }
+    }
+
+
+    /**
+     * This method returns a Date object that is accurate to within one
+     * second.  If a thread calls this method to get a Date and it's been
+     * less than 1 second since a new Date was created, this method
+     * simply gives out the same Date again so that the system doesn't
+     * spend time creating Date objects unnecessarily.
+     *
+     * @return Date
+     */
+    private Date getDate() {
+        if(currentDate == null) {
+        currentDate = new Date();
+        } else {
+          // Only create a new Date once per second, max.
+          long systime = System.currentTimeMillis();
+          if ((systime - currentDate.getTime()) > 1000) {
+              currentDate = new Date(systime);
+          }
+    }
+
+        return currentDate;
+    }
+
+
+    private String getTimeZone(Date date) {
+        if (timezone.inDaylightTime(date)) {
+            return timeZoneDST;
+        } else {
+            return timeZoneNoDST;
+        }
+    }
+    
+    
+    private String calculateTimeZoneOffset(long offset) {
+        StringBuffer tz = new StringBuffer();
+        if ((offset<0))  {
+            tz.append("-");
+            offset = -offset;
+        } else {
+            tz.append("+");
+        }
+
+        long hourOffset = offset/(1000*60*60);
+        long minuteOffset = (offset/(1000*60)) % 60;
+
+        if (hourOffset<10)
+            tz.append("0");
+        tz.append(hourOffset);
+
+        if (minuteOffset<10)
+            tz.append("0");
+        tz.append(minuteOffset);
+
+        return tz.toString();
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (sm.getString("accessLogValve.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Initialize the timeZone, Date formatters, and currentDate
+        timezone = TimeZone.getDefault();
+        timeZoneNoDST = calculateTimeZoneOffset(timezone.getRawOffset());
+        Calendar calendar = Calendar.getInstance(timezone);
+        int offset = calendar.get(Calendar.DST_OFFSET);
+        timeZoneDST = calculateTimeZoneOffset(timezone.getRawOffset()+offset);
+        
+        if (fileDateFormat==null || fileDateFormat.length()==0)
+            fileDateFormat = "yyyy-MM-dd";
+        dateFormatter = new SimpleDateFormat(fileDateFormat);
+        dateFormatter.setTimeZone(timezone);
+        dayFormatter = new SimpleDateFormat("dd");
+        dayFormatter.setTimeZone(timezone);
+        monthFormatter = new SimpleDateFormat("MM");
+        monthFormatter.setTimeZone(timezone);
+        yearFormatter = new SimpleDateFormat("yyyy");
+        yearFormatter.setTimeZone(timezone);
+        timeFormatter = new SimpleDateFormat("HH:mm:ss");
+        timeFormatter.setTimeZone(timezone);
+        currentDate = new Date();
+        dateStamp = dateFormatter.format(currentDate);
+        timeTakenFormatter = new DecimalFormat("0.000");
+
+        open();
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("accessLogValve.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        close();
+
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/Constants.java b/container/catalina/src/share/org/apache/catalina/valves/Constants.java
new file mode 100644
index 0000000..0864325
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/Constants.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.valves</code>
+ * package.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.catalina.valves";
+
+    // Constants for the AccessLogValve class
+    public static final class AccessLog {
+        public static final String COMMON_ALIAS = "common";
+        public static final String COMMON_PATTERN = "%h %l %u %t \"%r\" %s %b";
+        public static final String COMBINED_ALIAS = "combined";
+        public static final String COMBINED_PATTERN = "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\"";
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/ErrorReportValve.java b/container/catalina/src/share/org/apache/catalina/valves/ErrorReportValve.java
new file mode 100644
index 0000000..c67db64
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/ErrorReportValve.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.IOException;
+import java.io.Writer;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * <p>Implementation of a Valve that outputs HTML error pages.</p>
+ *
+ * <p>This Valve should be attached at the Host level, although it will work
+ * if attached to a Context.</p>
+ *
+ * <p>HTML code from the Cocoon 2 project.</p>
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @author <a href="mailto:nicolaken@supereva.it">Nicola Ken Barozzi</a> Aisa
+ * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
+ * @author Yoav Shapira
+ * @version $Revision$ $Date$
+ */
+
+public class ErrorReportValve
+    extends ValveBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.ErrorReportValve/1.0";
+
+
+    /**
+     * The StringManager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Invoke the next Valve in the sequence. When the invoke returns, check
+     * the response state, and output an error report is necessary.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // Perform the request
+        getNext().invoke(request, response);
+
+        ServletRequest sreq = (ServletRequest) request;
+        Throwable throwable =
+            (Throwable) sreq.getAttribute(Globals.EXCEPTION_ATTR);
+
+        ServletResponse sresp = (ServletResponse) response;
+        if (sresp.isCommitted()) {
+            return;
+        }
+
+        if (throwable != null) {
+
+            // The response is an error
+            response.setError();
+
+            // Reset the response (if possible)
+            try {
+                sresp.reset();
+            } catch (IllegalStateException e) {
+                ;
+            }
+
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+
+        }
+
+        response.setSuspended(false);
+
+        try {
+            report(request, response, throwable);
+        } catch (Throwable tt) {
+            ;
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Prints out an error report.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     * @param throwable The exception that occurred (which possibly wraps
+     *  a root cause exception
+     */
+    protected void report(Request request, Response response,
+                          Throwable throwable)
+        throws IOException {
+
+        // Do nothing on non-HTTP responses
+        int statusCode = response.getStatus();
+
+        // Do nothing on a 1xx, 2xx and 3xx status
+        // Do nothing if anything has been written already
+        if ((statusCode < 400) || (response.getContentCount() > 0))
+            return;
+
+        Throwable rootCause = null;
+
+        if (throwable != null) {
+
+            if (throwable instanceof ServletException)
+                rootCause = ((ServletException) throwable).getRootCause();
+
+        }
+
+        String message = RequestUtil.filter(response.getMessage());
+        if (message == null)
+            message = "";
+
+        // Do nothing if there is no report for the specified status code
+        String report = null;
+        try {
+            report = sm.getString("http." + statusCode, message);
+        } catch (Throwable t) {
+            ;
+        }
+        if (report == null)
+            return;
+
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("<html><head><title>");
+        sb.append(ServerInfo.getServerInfo()).append(" - ");
+        sb.append(sm.getString("errorReportValve.errorReport"));
+        sb.append("</title>");
+        sb.append("<style><!--");
+        sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
+        sb.append("--></style> ");
+        sb.append("</head><body>");
+        sb.append("<h1>");
+        sb.append(sm.getString("errorReportValve.statusHeader",
+                               "" + statusCode, message)).append("</h1>");
+        sb.append("<HR size=\"1\" noshade=\"noshade\">");
+        sb.append("<p><b>type</b> ");
+        if (throwable != null) {
+            sb.append(sm.getString("errorReportValve.exceptionReport"));
+        } else {
+            sb.append(sm.getString("errorReportValve.statusReport"));
+        }
+        sb.append("</p>");
+        sb.append("<p><b>");
+        sb.append(sm.getString("errorReportValve.message"));
+        sb.append("</b> <u>");
+        sb.append(message).append("</u></p>");
+        sb.append("<p><b>");
+        sb.append(sm.getString("errorReportValve.description"));
+        sb.append("</b> <u>");
+        sb.append(report);
+        sb.append("</u></p>");
+
+        if (throwable != null) {
+
+            String stackTrace = JdkCompat.getJdkCompat()
+                .getPartialServletStackTrace(throwable);
+            sb.append("<p><b>");
+            sb.append(sm.getString("errorReportValve.exception"));
+            sb.append("</b> <pre>");
+            sb.append(RequestUtil.filter(stackTrace));
+            sb.append("</pre></p>");
+
+            while (rootCause != null) {
+                stackTrace = JdkCompat.getJdkCompat()
+                    .getPartialServletStackTrace(rootCause);
+                sb.append("<p><b>");
+                sb.append(sm.getString("errorReportValve.rootCause"));
+                sb.append("</b> <pre>");
+                sb.append(RequestUtil.filter(stackTrace));
+                sb.append("</pre></p>");
+                // In case root cause is somehow heavily nested
+                try {
+                    rootCause = (Throwable)IntrospectionUtils.getProperty
+                                                (rootCause, "rootCause");
+                } catch (ClassCastException e) {
+                    rootCause = null;
+                }
+            }
+
+            sb.append("<p><b>");
+            sb.append(sm.getString("errorReportValve.note"));
+            sb.append("</b> <u>");
+            sb.append(sm.getString("errorReportValve.rootCauseInLogs",
+                                   ServerInfo.getServerInfo()));
+            sb.append("</u></p>");
+
+        }
+
+        sb.append("<HR size=\"1\" noshade=\"noshade\">");
+        sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
+        sb.append("</body></html>");
+
+        try {
+            try {
+                response.setContentType("text/html");
+                response.setCharacterEncoding("utf-8");
+            } catch (Throwable t) {
+                if (container.getLogger().isDebugEnabled())
+                    container.getLogger().debug("status.setContentType", t);
+            }
+            Writer writer = response.getReporter();
+            if (writer != null) {
+                // If writer is null, it's an indication that the response has
+                // been hard committed already, which should never happen
+                writer.write(sb.toString());
+            }
+        } catch (IOException e) {
+            ;
+        } catch (IllegalStateException e) {
+            ;
+        }
+        
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/ExtendedAccessLogValve.java b/container/catalina/src/share/org/apache/catalina/valves/ExtendedAccessLogValve.java
new file mode 100755
index 0000000..721bd81
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/ExtendedAccessLogValve.java
@@ -0,0 +1,1429 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.URLEncoder;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.TimeZone;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+
+/**
+ * An implementation of the W3c Extended Log File Format. See
+ * http://www.w3.org/TR/WD-logfile.html for more information about the format.
+ *
+ * The following fields are supported:
+ * <ul>
+ * <li><code>c-dns</code>:  Client hostname</li>
+ * <li><code>c-ip</code>:  Client ip address</li>
+ * <li><code>bytes</code>:  bytes served</li>
+ * <li><code>cs-method</code>:  request method</li>
+ * <li><code>cs-uri</code>:  The full uri requested</li>
+ * <li><code>cs-uri-query</code>:  The query string</li>
+ * <li><code>cs-uri-stem</code>:  The uri without query string</li>
+ * <li><code>date</code>:  The date in yyyy-mm-dd  format for GMT</li>
+ * <li><code>s-dns</code>: The server dns entry </li>
+ * <li><code>s-ip</code>:  The server ip address</li>
+ * <li><code>cs(XXX)</code>:  The value of header XXX from client to server</li>
+ * <li><code>sc(XXX)</code>: The value of header XXX from server to client </li>
+ * <li><code>sc-status</code>:  The status code</li>
+ * <li><code>time</code>:  Time the request was served</li>
+ * <li><code>time-taken</code>:  Time (in seconds) taken to serve the request</li>
+ * <li><code>x-A(XXX)</code>: Pull XXX attribute from the servlet context </li>
+ * <li><code>x-C(XXX)</code>: Pull the first cookie of the name XXX </li>
+ * <li><code>x-R(XXX)</code>: Pull XXX attribute from the servlet request </li>
+ * <li><code>x-S(XXX)</code>: Pull XXX attribute from the session </li>
+ * <li><code>x-P(...)</code>:  Call request.getParameter(...)
+ *                             and URLencode it. Helpful to capture
+ *                             certain POST parameters.
+ * </li>
+ * <li>For any of the x-H(...) the following method will be called from the
+ *                HttpServletRequestObject </li>
+ * <li><code>x-H(authType)</code>: getAuthType </li>
+ * <li><code>x-H(characterEncoding)</code>: getCharacterEncoding </li>
+ * <li><code>x-H(contentLength)</code>: getContentLength </li>
+ * <li><code>x-H(locale)</code>:  getLocale</li>
+ * <li><code>x-H(protocol)</code>: getProtocol </li>
+ * <li><code>x-H(remoteUser)</code>:  getRemoteUser</li>
+ * <li><code>x-H(requestedSessionId)</code>: getGequestedSessionId</li>
+ * <li><code>x-H(requestedSessionIdFromCookie)</code>:
+ *                  isRequestedSessionIdFromCookie </li>
+ * <li><code>x-H(requestedSessionIdValid)</code>:
+ *                  isRequestedSessionIdValid</li>
+ * <li><code>x-H(scheme)</code>:  getScheme</li>
+ * <li><code>x-H(secure)</code>:  isSecure</li>
+ * </ul>
+ *
+ *
+ *
+ * <p>
+ * Log rotation can be on or off. This is dictated by the rotatable
+ * property.
+ * </p>
+ *
+ * <p>
+ * For UNIX users, another field called <code>checkExists</code>is also
+ * available. If set to true, the log file's existence will be checked before
+ * each logging. This way an external log rotator can move the file
+ * somewhere and tomcat will start with a new file.
+ * </p>
+ *
+ * <p>
+ * For JMX junkies, a public method called </code>rotate</code> has
+ * been made available to allow you to tell this instance to move
+ * the existing log file to somewhere else start writing a new log file.
+ * </p>
+ *
+ * <p>
+ * Conditional logging is also supported. This can be done with the
+ * <code>condition</code> property.
+ * If the value returned from ServletRequest.getAttribute(condition)
+ * yields a non-null value. The logging will be skipped.
+ * </p>
+ *
+ * <p>
+ * For extended attributes coming from a getAttribute() call,
+ * it is you responsibility to ensure there are no newline or
+ * control characters.
+ * </p>
+ *
+ *
+ * @author Tim Funk
+ * @version $Revision$ $Date$
+ */
+
+public class ExtendedAccessLogValve
+    extends ValveBase
+    implements Lifecycle {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class with default property values.
+     */
+    public ExtendedAccessLogValve() {
+
+        super();
+
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+    private static Log log = LogFactory.getLog(ExtendedAccessLogValve.class);
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.valves.ExtendedAccessLogValve/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+
+    /**
+     * The string manager for this package.
+     */
+    private StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The as-of date for the currently open log file, or a zero-length
+     * string if there is no open log file.
+     */
+    private String dateStamp = "";
+
+
+    /**
+     * The PrintWriter to which we are currently logging, if any.
+     */
+    private PrintWriter writer = null;
+
+
+    /**
+     * The formatter for the date contained in the file name.
+     */
+    private SimpleDateFormat fileDateFormatter = null;
+
+
+    /**
+     * A date formatter to format a Date into a date in the format
+     * "yyyy-MM-dd".
+     */
+    private SimpleDateFormat dateFormatter = null;
+
+
+    /**
+     * A date formatter to format a Date into a time in the format
+     * "kk:mm:ss" (kk is a 24-hour representation of the hour).
+     */
+    private SimpleDateFormat timeFormatter = null;
+
+
+    /**
+     * Time taken formatter for 3 decimal places.
+     */
+     private DecimalFormat timeTakenFormatter = null;
+
+
+    /**
+     * My ip address. Look it up once and remember it. Dump this if we can
+     * determine another reliable way to get server ip address since this
+     * server may have many ip's.
+     */
+    private String myIpAddress = null;
+
+
+    /**
+     * My dns name. Look it up once and remember it. Dump this if we can
+     * determine another reliable way to get server name address since this
+     * server may have many ip's.
+     */
+    private String myDNSName = null;
+
+
+    /**
+     * Holder for all of the fields to log after the pattern is decoded.
+     */
+    private FieldInfo[] fieldInfos;
+
+
+    /**
+     * The current log file we are writing to. Helpful when checkExists
+     * is true.
+     */
+    private File currentLogFile = null;
+
+
+
+    /**
+     * The system time when we last updated the Date that this valve
+     * uses for log lines.
+     */
+    private Date currentDate = null;
+
+
+    /**
+     * Instant when the log daily rotation was last checked.
+     */
+    private long rotationLastChecked = 0L;
+
+
+    /**
+     * The directory in which log files are created.
+     */
+    private String directory = "logs";
+
+
+    /**
+     * The pattern used to format our access log lines.
+     */
+    private String pattern = null;
+
+
+    /**
+     * The prefix that is added to log file filenames.
+     */
+    private String prefix = "access_log.";
+
+
+    /**
+     * Should we rotate our log file? Default is true (like old behavior)
+     */
+    private boolean rotatable = true;
+
+
+    /**
+     * The suffix that is added to log file filenames.
+     */
+    private String suffix = "";
+
+
+    /**
+     * Are we doing conditional logging. default false.
+     */
+    private String condition = null;
+
+
+    /**
+     * Do we check for log file existence? Helpful if an external
+     * agent renames the log file so we can automagically recreate it.
+     */
+    private boolean checkExists = false;
+
+
+    /**
+     * Date format to place in log file name. Use at your own risk!
+     */
+    private String fileDateFormat = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the directory in which we create log files.
+     */
+    public String getDirectory() {
+
+        return (directory);
+
+    }
+
+
+    /**
+     * Set the directory in which we create log files.
+     *
+     * @param directory The new log file directory
+     */
+    public void setDirectory(String directory) {
+
+        this.directory = directory;
+
+    }
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the format pattern.
+     */
+    public String getPattern() {
+
+        return (this.pattern);
+
+    }
+
+
+    /**
+     * Set the format pattern, first translating any recognized alias.
+     *
+     * @param pattern The new pattern pattern
+     */
+    public void setPattern(String pattern) {
+
+        FieldInfo[] f= decodePattern(pattern);
+        if (f!=null) {
+            this.pattern = pattern;
+            this.fieldInfos = f;
+        }
+    }
+
+
+    /**
+     * Return the log file prefix.
+     */
+    public String getPrefix() {
+
+        return (prefix);
+
+    }
+
+
+    /**
+     * Set the log file prefix.
+     *
+     * @param prefix The new log file prefix
+     */
+    public void setPrefix(String prefix) {
+
+        this.prefix = prefix;
+
+    }
+
+
+    /**
+     * Return true if logs are automatically rotated.
+     */
+    public boolean isRotatable() {
+
+        return rotatable;
+
+    }
+
+
+    /**
+     * Set the value is we should we rotate the logs
+     *
+     * @param rotatable true is we should rotate.
+     */
+    public void setRotatable(boolean rotatable) {
+
+        this.rotatable = rotatable;
+
+    }
+
+
+    /**
+     * Return the log file suffix.
+     */
+    public String getSuffix() {
+
+        return (suffix);
+
+    }
+
+
+    /**
+     * Set the log file suffix.
+     *
+     * @param suffix The new log file suffix
+     */
+    public void setSuffix(String suffix) {
+
+        this.suffix = suffix;
+
+    }
+
+
+
+    /**
+     * Return whether the attribute name to look for when
+     * performing conditional loggging. If null, every
+     * request is logged.
+     */
+    public String getCondition() {
+
+        return condition;
+
+    }
+
+
+    /**
+     * Set the ServletRequest.attribute to look for to perform
+     * conditional logging. Set to null to log everything.
+     *
+     * @param condition Set to null to log everything
+     */
+    public void setCondition(String condition) {
+
+        this.condition = condition;
+
+    }
+
+
+
+    /**
+     * Check for file existence before logging.
+     */
+    public boolean isCheckExists() {
+
+        return checkExists;
+
+    }
+
+
+    /**
+     * Set whether to check for log file existence before logging.
+     *
+     * @param checkExists true meaning to check for file existence.
+     */
+    public void setCheckExists(boolean checkExists) {
+
+        this.checkExists = checkExists;
+
+    }
+
+
+    /**
+     *  Return the date format date based log rotation.
+     */
+    public String getFileDateFormat() {
+        return fileDateFormat;
+    }
+
+
+    /**
+     *  Set the date format date based log rotation.
+     */
+    public void setFileDateFormat(String fileDateFormat) {
+        this.fileDateFormat =  fileDateFormat;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Log a message summarizing the specified request and response, according
+     * to the format specified by the <code>pattern</code> property.
+     *
+     * @param request Request being processed
+     * @param response Response being processed
+     *
+     * @exception IOException if an input/output error has occurred
+     * @exception ServletException if a servlet error has occurred
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // Pass this request on to the next valve in our pipeline
+        long endTime;
+        long runTime;
+        long startTime=System.currentTimeMillis();
+
+        getNext().invoke(request, response);
+
+        endTime = System.currentTimeMillis();
+        runTime = endTime-startTime;
+
+        if (fieldInfos==null || condition!=null &&
+              null!=request.getRequest().getAttribute(condition)) {
+            return;
+        }
+
+
+        Date date = getDate(endTime);
+        StringBuffer result = new StringBuffer();
+
+        for (int i=0; fieldInfos!=null && i<fieldInfos.length; i++) {
+            switch(fieldInfos[i].type) {
+                case FieldInfo.DATA_CLIENT:
+                    if (FieldInfo.FIELD_IP==fieldInfos[i].location)
+                        result.append(request.getRequest().getRemoteAddr());
+                    else if (FieldInfo.FIELD_DNS==fieldInfos[i].location)
+                        result.append(request.getRequest().getRemoteHost());
+                    else
+                        result.append("?WTF?"); /* This should never happen! */
+                    break;
+                case FieldInfo.DATA_SERVER:
+                    if (FieldInfo.FIELD_IP==fieldInfos[i].location)
+                        result.append(myIpAddress);
+                    else if (FieldInfo.FIELD_DNS==fieldInfos[i].location)
+                        result.append(myDNSName);
+                    else
+                        result.append("?WTF?"); /* This should never happen! */
+                    break;
+                case FieldInfo.DATA_REMOTE:
+                    result.append('?'); /* I don't know how to handle these! */
+                    break;
+                case FieldInfo.DATA_CLIENT_TO_SERVER:
+                    result.append(getClientToServer(fieldInfos[i], request));
+                    break;
+                case FieldInfo.DATA_SERVER_TO_CLIENT:
+                    result.append(getServerToClient(fieldInfos[i], response));
+                    break;
+                case FieldInfo.DATA_SERVER_TO_RSERVER:
+                    result.append('-');
+                    break;
+                case FieldInfo.DATA_RSERVER_TO_SERVER:
+                    result.append('-');
+                    break;
+                case FieldInfo.DATA_APP_SPECIFIC:
+                    result.append(getAppSpecific(fieldInfos[i], request));
+                    break;
+                case FieldInfo.DATA_SPECIAL:
+                    if (FieldInfo.SPECIAL_DATE==fieldInfos[i].location)
+                        result.append(dateFormatter.format(date));
+                    else if (FieldInfo.SPECIAL_TIME_TAKEN==fieldInfos[i].location)
+                        result.append(timeTakenFormatter.format(runTime/1000d));
+                    else if (FieldInfo.SPECIAL_TIME==fieldInfos[i].location)
+                        result.append(timeFormatter.format(date));
+                    else if (FieldInfo.SPECIAL_BYTES==fieldInfos[i].location) {
+                        int length = response.getContentCount();
+                        if (length > 0)
+                            result.append(length);
+                        else
+                            result.append("-");
+                    } else if (FieldInfo.SPECIAL_CACHED==fieldInfos[i].location)
+                        result.append('-'); /* I don't know how to evaluate this! */
+                    else
+                        result.append("?WTF?"); /* This should never happen! */
+                    break;
+                default:
+                    result.append("?WTF?"); /* This should never happen! */
+            }
+
+            if (fieldInfos[i].postWhiteSpace!=null) {
+                result.append(fieldInfos[i].postWhiteSpace);
+            }
+        }
+        log(result.toString(), date);
+
+    }
+
+
+    /**
+     * Rename the existing log file to something else. Then open the
+     * old log file name up once again. Intended to be called by a JMX
+     * agent.
+     *
+     *
+     * @param newFileName The file name to move the log file entry to
+     * @return true if a file was rotated with no error
+     */
+    public synchronized boolean rotate(String newFileName) {
+
+        if (currentLogFile!=null) {
+            File holder = currentLogFile;
+            close();
+            try {
+                holder.renameTo(new File(newFileName));
+            } catch(Throwable e){
+                log.error("rotate failed", e);
+            }
+
+            /* Make sure date is correct */
+            currentDate = new Date(System.currentTimeMillis());
+            dateStamp = fileDateFormatter.format(currentDate);
+
+            open();
+            return true;
+        } else {
+            return false;
+        }
+
+    }
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     *  Return the client to server data.
+     *  @param fieldInfo The field to decode.
+     *  @param request The object we pull data from.
+     *  @return The appropriate value.
+     */
+     private String getClientToServer(FieldInfo fieldInfo, Request request) {
+
+        switch(fieldInfo.location) {
+            case FieldInfo.FIELD_METHOD:
+                return request.getMethod();
+            case FieldInfo.FIELD_URI:
+                if (null==request.getQueryString())
+                    return request.getRequestURI();
+                else
+                    return request.getRequestURI() + "?" + request.getQueryString();
+            case FieldInfo.FIELD_URI_STEM:
+                return request.getRequestURI();
+            case FieldInfo.FIELD_URI_QUERY:
+                if (null==request.getQueryString())
+                    return "-";
+                return request.getQueryString();
+            case FieldInfo.FIELD_HEADER:
+                return wrap(request.getHeader(fieldInfo.value));
+            default:
+                ;
+        }
+
+        return "-";
+
+    }
+
+
+    /**
+     *  Return the server to client data.
+     *  @param fieldInfo The field to decode.
+     *  @param response The object we pull data from.
+     *  @return The appropriate value.
+     */
+    private String getServerToClient(FieldInfo fieldInfo, Response response) {
+        switch(fieldInfo.location) {
+            case FieldInfo.FIELD_STATUS:
+                return "" + response.getStatus();
+            case FieldInfo.FIELD_COMMENT:
+                return "?"; /* Not coded yet*/
+            case FieldInfo.FIELD_HEADER:
+                return wrap(response.getHeader(fieldInfo.value));
+            default:
+                ;
+        }
+
+        return "-";
+
+    }
+
+
+    /**
+     * Get app specific data.
+     * @param fieldInfo The field to decode
+     * @param request Where we will pull the data from.
+     * @return The appropriate value
+     */
+    private String getAppSpecific(FieldInfo fieldInfo, Request request) {
+
+        switch(fieldInfo.xType) {
+            case FieldInfo.X_PARAMETER:
+                return wrap(urlEncode(request.getParameter(fieldInfo.value)));
+            case FieldInfo.X_REQUEST:
+                return wrap(request.getAttribute(fieldInfo.value));
+            case FieldInfo.X_SESSION:
+                HttpSession session = null;
+                if (request!=null){
+                    session = request.getSession(false);
+                    if (session!=null)
+                        return wrap(session.getAttribute(fieldInfo.value));
+                }
+                break;
+            case FieldInfo.X_COOKIE:
+                Cookie[] c = request.getCookies();
+                for (int i=0; c != null && i < c.length; i++){
+                    if (fieldInfo.value.equals(c[i].getName())){
+                        return wrap(c[i].getValue());
+                    }
+                 }
+            case FieldInfo.X_APP:
+                return wrap(request.getContext().getServletContext()
+                                .getAttribute(fieldInfo.value));
+            case FieldInfo.X_SERVLET_REQUEST:
+                if (fieldInfo.location==FieldInfo.X_LOC_AUTHTYPE) {
+                    return wrap(request.getAuthType());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_REMOTEUSER) {
+                    return wrap(request.getRemoteUser());
+                } else if (fieldInfo.location==
+                            FieldInfo.X_LOC_REQUESTEDSESSIONID) {
+                    return wrap(request.getRequestedSessionId());
+                } else if (fieldInfo.location==
+                            FieldInfo.X_LOC_REQUESTEDSESSIONIDFROMCOOKIE) {
+                    return wrap(""+request.isRequestedSessionIdFromCookie());
+                } else if (fieldInfo.location==
+                            FieldInfo.X_LOC_REQUESTEDSESSIONIDVALID) {
+                    return wrap(""+request.isRequestedSessionIdValid());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_CONTENTLENGTH) {
+                    return wrap(""+request.getContentLength());
+                } else if (fieldInfo.location==
+                            FieldInfo.X_LOC_CHARACTERENCODING) {
+                    return wrap(request.getCharacterEncoding());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_LOCALE) {
+                    return wrap(request.getLocale());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_PROTOCOL) {
+                    return wrap(request.getProtocol());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_SCHEME) {
+                    return wrap(request.getScheme());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_SECURE) {
+                    return wrap(""+request.isSecure());
+                }
+                break;
+            default:
+                ;
+        }
+
+        return "-";
+
+    }
+
+
+    /**
+     *  urlEncode the given string. If null or empty, return null.
+     */
+    private String urlEncode(String value) {
+        if (null==value || value.length()==0) {
+            return null;
+        }
+        return URLEncoder.encode(value);
+    }
+
+
+    /**
+     *  Wrap the incoming value into quotes and escape any inner
+     *  quotes with double quotes.
+     *
+     *  @param value - The value to wrap quotes around
+     *  @return '-' if empty of null. Otherwise, toString() will
+     *     be called on the object and the value will be wrapped
+     *     in quotes and any quotes will be escaped with 2
+     *     sets of quotes.
+     */
+    private String wrap(Object value) {
+
+        String svalue;
+        // Does the value contain a " ? If so must encode it
+        if (value==null || "-".equals(value))
+            return "-";
+
+
+        try {
+            svalue = value.toString();
+            if ("".equals(svalue))
+                return "-";
+        } catch(Throwable e){
+            /* Log error */
+            return "-";
+        }
+
+        /* Wrap all quotes in double quotes. */
+        StringBuffer buffer = new StringBuffer(svalue.length()+2);
+        buffer.append('"');
+        int i=0;
+        while (i<svalue.length()) {
+            int j = svalue.indexOf('"', i);
+            if (j==-1) {
+                buffer.append(svalue.substring(i));
+                i=svalue.length();
+            } else {
+                buffer.append(svalue.substring(i, j+1));
+                buffer.append('"');
+                i=j+2;
+            }
+        }
+
+        buffer.append('"');
+        return buffer.toString();
+
+    }
+
+
+    /**
+     * Close the currently open log file (if any)
+     */
+    private synchronized void close() {
+
+        if (writer == null)
+            return;
+        writer.flush();
+        writer.close();
+        writer = null;
+        currentLogFile = null;
+
+    }
+
+
+    /**
+     * Log the specified message to the log file, switching files if the date
+     * has changed since the previous log call.
+     *
+     * @param message Message to be logged
+     * @param date the current Date object (so this method doesn't need to
+     *        create a new one)
+     */
+    private void log(String message, Date date) {
+
+        if (rotatable){
+            // Only do a logfile switch check once a second, max.
+            long systime = System.currentTimeMillis();
+            if ((systime - rotationLastChecked) > 1000) {
+
+                // We need a new currentDate
+                currentDate = new Date(systime);
+                rotationLastChecked = systime;
+
+                // Check for a change of date
+                String tsDate = fileDateFormatter.format(currentDate);
+
+                // If the date has changed, switch log files
+                if (!dateStamp.equals(tsDate)) {
+                    synchronized (this) {
+                        if (!dateStamp.equals(tsDate)) {
+                            close();
+                            dateStamp = tsDate;
+                            open();
+                        }
+                    }
+                }
+            }
+        }
+
+        /* In case something external rotated the file instead */
+        if (checkExists){
+            synchronized (this) {
+                if (currentLogFile!=null && !currentLogFile.exists()) {
+                    try {
+                        close();
+                    } catch (Throwable e){
+                        log.info("at least this wasn't swallowed", e);
+                    }
+
+                    /* Make sure date is correct */
+                    currentDate = new Date(System.currentTimeMillis());
+                    dateStamp = fileDateFormatter.format(currentDate);
+
+                    open();
+                }
+            }
+        }
+
+        // Log this message
+        if (writer != null) {
+            writer.println(message);
+        }
+
+    }
+
+
+    /**
+     * Open the new log file for the date specified by <code>dateStamp</code>.
+     */
+    private synchronized void open() {
+
+        // Create the directory if necessary
+        File dir = new File(directory);
+        if (!dir.isAbsolute())
+            dir = new File(System.getProperty("catalina.base"), directory);
+        dir.mkdirs();
+
+        // Open the current log file
+        try {
+            String pathname;
+
+            // If no rotate - no need for dateStamp in fileName
+            if (rotatable){
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + dateStamp + suffix;
+            } else {
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + suffix;
+            }
+
+            currentLogFile = new File(pathname);
+            writer = new PrintWriter(new FileWriter(pathname, true), true);
+            if (currentLogFile.length()==0) {
+                writer.println("#Fields: " + pattern);
+                writer.println("#Version: 1.0");
+                writer.println("#Software: " + ServerInfo.getServerInfo());
+            }
+
+
+        } catch (IOException e) {
+            writer = null;
+            currentLogFile = null;
+        }
+
+    }
+
+
+    /**
+     * This method returns a Date object that is accurate to within one
+     * second.  If a thread calls this method to get a Date and it's been
+     * less than 1 second since a new Date was created, this method
+     * simply gives out the same Date again so that the system doesn't
+     * spend time creating Date objects unnecessarily.
+     */
+    private Date getDate(long systime) {
+        /* Avoid extra call to System.currentTimeMillis(); */
+        if (0==systime) {
+            systime = System.currentTimeMillis();
+        }
+
+        // Only create a new Date once per second, max.
+        if ((systime - currentDate.getTime()) > 1000) {
+            currentDate.setTime(systime);
+        }
+
+        return currentDate;
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (sm.getString("extendedAccessLogValve.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Initialize the timeZone, Date formatters, and currentDate
+        TimeZone tz = TimeZone.getTimeZone("GMT");
+        dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
+        dateFormatter.setTimeZone(tz);
+        timeFormatter = new SimpleDateFormat("HH:mm:ss");
+        timeFormatter.setTimeZone(tz);
+        currentDate = new Date(System.currentTimeMillis());
+        if (fileDateFormat==null || fileDateFormat.length()==0)
+            fileDateFormat = "yyyy-MM-dd";
+        fileDateFormatter = new SimpleDateFormat(fileDateFormat);
+        dateStamp = fileDateFormatter.format(currentDate);
+        timeTakenFormatter = new DecimalFormat("0.000");
+
+        /* Everybody say ick ... ick */
+        try {
+            InetAddress inetAddress = InetAddress.getLocalHost();
+            myIpAddress = inetAddress.getHostAddress();
+            myDNSName = inetAddress.getHostName();
+        } catch(Throwable e){
+            myIpAddress="127.0.0.1";
+            myDNSName="localhost";
+        }
+
+        open();
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("extendedAccessLogValve.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        close();
+
+    }
+
+
+    /**
+     * Decode the given pattern. Is public so a pattern may
+     * allows to be validated.
+     * @param fields The pattern to decode
+     * @return null on error.  Otherwise array of decoded fields
+     */
+    public FieldInfo[] decodePattern(String fields) {
+
+        if (log.isDebugEnabled())
+            log.debug("decodePattern, fields=" + fields);
+
+        LinkedList list = new LinkedList();
+
+        //Ignore leading whitespace.
+        int i=0;
+        for (;i<fields.length() && Character.isWhitespace(fields.charAt(i));i++);
+
+        if (i>=fields.length()) {
+            log.info("fields was just empty or whitespace");
+            return null;
+        }
+
+        int j;
+        while(i<fields.length()) {
+            if (log.isDebugEnabled())
+                log.debug("fields.substring(i)=" + fields.substring(i));
+
+            FieldInfo currentFieldInfo = new FieldInfo();
+
+
+            if (fields.startsWith("date",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_DATE;
+                i+="date".length();
+            } else if (fields.startsWith("time-taken",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_TIME_TAKEN;
+                i+="time-taken".length();
+            } else if (fields.startsWith("time",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_TIME;
+                i+="time".length();
+            } else if (fields.startsWith("bytes",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_BYTES;
+                i+="bytes".length();
+            } else if (fields.startsWith("cached",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_CACHED;
+                i+="cached".length();
+            } else if (fields.startsWith("c-ip",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_CLIENT;
+                currentFieldInfo.location = FieldInfo.FIELD_IP;
+                i+="c-ip".length();
+            } else if (fields.startsWith("c-dns",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_CLIENT;
+                currentFieldInfo.location = FieldInfo.FIELD_DNS;
+                i+="c-dns".length();
+            } else if (fields.startsWith("s-ip",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SERVER;
+                currentFieldInfo.location = FieldInfo.FIELD_IP;
+                i+="s-ip".length();
+            } else if (fields.startsWith("s-dns",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SERVER;
+                currentFieldInfo.location = FieldInfo.FIELD_DNS;
+                i+="s-dns".length();
+            } else if (fields.startsWith("cs",i)) {
+                i = decode(fields, i+2, currentFieldInfo,
+                            FieldInfo.DATA_CLIENT_TO_SERVER);
+                if (i<0)
+                    return null;
+            } else if (fields.startsWith("sc",i)) {
+                i = decode(fields, i+2, currentFieldInfo,
+                            FieldInfo.DATA_SERVER_TO_CLIENT);
+                if (i<0)
+                    return null;
+            } else if (fields.startsWith("sr",i)) {
+                i = decode(fields, i+2, currentFieldInfo,
+                            FieldInfo.DATA_SERVER_TO_RSERVER);
+                if (i<0)
+                    return null;
+            } else if (fields.startsWith("rs",i)) {
+                i = decode(fields, i+2, currentFieldInfo,
+                            FieldInfo.DATA_RSERVER_TO_SERVER);
+                if (i<0)
+                    return null;
+            } else if (fields.startsWith("x",i)) {
+                i = decodeAppSpecific(fields, i, currentFieldInfo);
+            } else {
+                // Unable to decode ...
+                log.error("unable to decode with rest of chars being: " +
+                            fields.substring(i));
+                return null;
+            }
+
+            // By this point we should have the field, get the whitespace
+            j=i;
+            for (;j<fields.length() && Character.isWhitespace(fields.charAt(j));j++);
+
+            if (j>=fields.length()) {
+                if (j==i) {
+                    // Special case - end of string
+                    currentFieldInfo.postWhiteSpace = "";
+                } else {
+                    currentFieldInfo.postWhiteSpace = fields.substring(i);
+                    i=j;
+                }
+            } else {
+                currentFieldInfo.postWhiteSpace = fields.substring(i,j);
+                i=j;
+            }
+
+            list.add(currentFieldInfo);
+        }
+
+        i=0;
+        FieldInfo[] f = new FieldInfo[list.size()];
+        for (Iterator k = list.iterator(); k.hasNext();)
+             f[i++] = (FieldInfo)k.next();
+
+        if (log.isDebugEnabled())
+            log.debug("finished decoding with length of: " + i);
+
+        return f;
+    }
+
+    /**
+     * Decode the cs or sc fields.
+     * Returns negative on error.
+     *
+     * @param fields The pattern to decode
+     * @param i The string index where we are decoding.
+     * @param fieldInfo Where to store the results
+     * @param type The type we are decoding.
+     * @return -1 on error. Otherwise the new String index.
+     */
+    private int decode(String fields, int i, FieldInfo fieldInfo, short type) {
+
+        if (fields.startsWith("-status",i)) {
+            fieldInfo.location = FieldInfo.FIELD_STATUS;
+            i+="-status".length();
+        } else if (fields.startsWith("-comment",i)) {
+            fieldInfo.location = FieldInfo.FIELD_COMMENT;
+            i+="-comment".length();
+        } else if (fields.startsWith("-uri-query",i)) {
+            fieldInfo.location = FieldInfo.FIELD_URI_QUERY;
+            i+="-uri-query".length();
+        } else if (fields.startsWith("-uri-stem",i)) {
+            fieldInfo.location = FieldInfo.FIELD_URI_STEM;
+            i+="-uri-stem".length();
+        } else if (fields.startsWith("-uri",i)) {
+            fieldInfo.location = FieldInfo.FIELD_URI;
+            i+="-uri".length();
+        } else if (fields.startsWith("-method",i)) {
+            fieldInfo.location = FieldInfo.FIELD_METHOD;
+            i+="-method".length();
+        } else if (fields.startsWith("(",i)) {
+            fieldInfo.location = FieldInfo.FIELD_HEADER;
+            i++;                                  /* Move past the ( */
+            int j = fields.indexOf(')', i);
+            if (j==-1) {                          /* Not found */
+                log.error("No closing ) found for in decode");
+                return -1;
+            }
+            fieldInfo.value = fields.substring(i,j);
+            i=j+1;                                // Move pointer past ) */
+        } else {
+            log.error("The next characters couldn't be decoded: " + fields.substring(i));
+            return -1;
+        }
+
+        fieldInfo.type = type;
+        return i;
+
+    }
+
+
+    /**
+      * Decode app specific log entry.
+      *
+      * Special fields are of the form:
+      * x-C(...) - For cookie
+      * x-A(...) - Value in servletContext
+      * x-S(...) - Value in session
+      * x-R(...) - Value in servletRequest
+      * @param fields The pattern to decode
+      * @param i The string index where we are decoding.
+      * @param fieldInfo Where to store the results
+      * @return -1 on error. Otherwise the new String index.
+      */
+    private int decodeAppSpecific(String fields, int i, FieldInfo fieldInfo) {
+
+        fieldInfo.type = FieldInfo.DATA_APP_SPECIFIC;
+        /* Move past 'x-' */
+        i+=2;
+
+        if (i>=fields.length()) {
+            log.error("End of line reached before decoding x- param");
+            return -1;
+        }
+
+        switch(fields.charAt(i)) {
+            case 'A':
+                fieldInfo.xType = FieldInfo.X_APP;
+                break;
+            case 'C':
+                fieldInfo.xType = FieldInfo.X_COOKIE;
+                break;
+            case 'R':
+                fieldInfo.xType = FieldInfo.X_REQUEST;
+                break;
+            case 'S':
+                fieldInfo.xType = FieldInfo.X_SESSION;
+                break;
+            case 'H':
+                fieldInfo.xType = FieldInfo.X_SERVLET_REQUEST;
+                break;
+            case 'P':
+                fieldInfo.xType = FieldInfo.X_PARAMETER;
+                break;
+            default:
+                return -1;
+        }
+
+        /* test that next char is a ( */
+        if (i+1!=fields.indexOf('(',i)) {
+            log.error("x param in wrong format. Needs to be 'x-#(...)' read the docs!");
+            return -1;
+        }
+        i+=2; /* Move inside of the () */
+
+        /* Look for ending ) and return error if not found. */
+        int j = fields.indexOf(')',i);
+        if (j==-1) {
+            log.error("x param in wrong format. No closing ')'!");
+            return -1;
+        }
+
+        fieldInfo.value = fields.substring(i,j);
+
+        if (fieldInfo.xType == FieldInfo.X_SERVLET_REQUEST) {
+            if ("authType".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_AUTHTYPE;
+            } else if ("remoteUser".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_REMOTEUSER;
+            } else if ("requestedSessionId".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONID;
+            } else if ("requestedSessionIdFromCookie".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONIDFROMCOOKIE;
+            } else if ("requestedSessionIdValid".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONIDVALID;
+            } else if ("contentLength".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_CONTENTLENGTH;
+            } else if ("characterEncoding".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_CHARACTERENCODING;
+            } else if ("locale".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_LOCALE;
+            } else if ("protocol".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_PROTOCOL;
+            } else if ("scheme".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_SCHEME;
+            } else if ("secure".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_SECURE;
+            } else {
+                log.error("x param for servlet request, couldn't decode value: " +
+                            fieldInfo.location);
+                return -1;
+            }
+        }
+
+        return j+1;
+
+    }
+
+
+}
+
+/**
+ * A simple helper for decoding the pattern.
+ */
+class FieldInfo {
+    /*
+       The goal of the constants listed below is to make the construction of the log
+       entry as quick as possible via numerci decodings of the methods to call instead
+       of performing many String comparisons on each logging request.
+    */
+
+    /* Where the data is located. */
+    static final short DATA_CLIENT = 0;
+    static final short DATA_SERVER = 1;
+    static final short DATA_REMOTE = 2;
+    static final short DATA_CLIENT_TO_SERVER = 3;
+    static final short DATA_SERVER_TO_CLIENT = 4;
+    static final short DATA_SERVER_TO_RSERVER = 5; /* Here to honor the spec. */
+    static final short DATA_RSERVER_TO_SERVER = 6; /* Here to honor the spec. */
+    static final short DATA_APP_SPECIFIC = 7;
+    static final short DATA_SPECIAL = 8;
+
+    /* The type of special fields. */
+    static final short SPECIAL_DATE         = 1;
+    static final short SPECIAL_TIME_TAKEN   = 2;
+    static final short SPECIAL_TIME         = 3;
+    static final short SPECIAL_BYTES        = 4;
+    static final short SPECIAL_CACHED       = 5;
+
+    /* Where to pull the data for prefixed values */
+    static final short FIELD_IP            = 1;
+    static final short FIELD_DNS           = 2;
+    static final short FIELD_STATUS        = 3;
+    static final short FIELD_COMMENT       = 4;
+    static final short FIELD_METHOD        = 5;
+    static final short FIELD_URI           = 6;
+    static final short FIELD_URI_STEM      = 7;
+    static final short FIELD_URI_QUERY     = 8;
+    static final short FIELD_HEADER        = 9;
+
+
+    /* Application Specific parameters */
+    static final short X_REQUEST = 1; /* For x app specific */
+    static final short X_SESSION = 2; /* For x app specific */
+    static final short X_COOKIE  = 3; /* For x app specific */
+    static final short X_APP     = 4; /* For x app specific */
+    static final short X_SERVLET_REQUEST = 5; /* For x app specific */
+    static final short X_PARAMETER = 6; /* For x app specific */
+
+    static final short X_LOC_AUTHTYPE                       = 1;
+    static final short X_LOC_REMOTEUSER                     = 2;
+    static final short X_LOC_REQUESTEDSESSIONID             = 3;
+    static final short X_LOC_REQUESTEDSESSIONIDFROMCOOKIE   = 4;
+    static final short X_LOC_REQUESTEDSESSIONIDVALID        = 5;
+    static final short X_LOC_CONTENTLENGTH                  = 6;
+    static final short X_LOC_CHARACTERENCODING              = 7;
+    static final short X_LOC_LOCALE                         = 8;
+    static final short X_LOC_PROTOCOL                       = 9;
+    static final short X_LOC_SCHEME                         = 10;
+    static final short X_LOC_SECURE                         = 11;
+
+
+
+    /** The field type */
+    short type;
+
+    /** Where to pull the data from? Icky variable name. */
+    short location;
+
+    /** The x- specific place to pull the data from. */
+    short xType;
+
+    /** The field value if needed. Needed for headers and app specific. */
+    String value;
+
+    /** Any white space after this field? Put it here. */
+    String postWhiteSpace = null;
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/FastCommonAccessLogValve.java b/container/catalina/src/share/org/apache/catalina/valves/FastCommonAccessLogValve.java
new file mode 100644
index 0000000..ce49fe5
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/FastCommonAccessLogValve.java
@@ -0,0 +1,855 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * <p>Implementation of the <b>Valve</b> interface that generates a web server
+ * access log with the detailed line contents matching either the common or
+ * combined patterns.  As an additional feature, automatic rollover of log files
+ * when the date changes is also supported.</p>
+ * <p>
+ * Conditional logging is also supported. This can be done with the
+ * <code>condition</code> property.
+ * If the value returned from ServletRequest.getAttribute(condition)
+ * yields a non-null value. The logging will be skipped.
+ * </p>
+ *
+ * @author Craig R. McClanahan
+ * @author Jason Brittain
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public final class FastCommonAccessLogValve
+    extends ValveBase
+    implements Lifecycle {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class with default property values.
+     */
+    public FastCommonAccessLogValve() {
+
+        super();
+        setPattern("common");
+
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The as-of date for the currently open log file, or a zero-length
+     * string if there is no open log file.
+     */
+    private String dateStamp = "";
+
+
+    /**
+     * The directory in which log files are created.
+     */
+    private String directory = "logs";
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.valves.FastCommonAccessLogValve/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The set of month abbreviations for log messages.
+     */
+    protected static final String months[] =
+    { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+
+    /**
+     * If the current log pattern is the same as the common access log
+     * format pattern, then we'll set this variable to true and log in
+     * a more optimal and hard-coded way.
+     */
+    private boolean common = false;
+
+
+    /**
+     * For the combined format (common, plus useragent and referer), we do
+     * the same
+     */
+    private boolean combined = false;
+
+
+    /**
+     * The pattern used to format our access log lines.
+     */
+    private String pattern = null;
+
+
+    /**
+     * The prefix that is added to log file filenames.
+     */
+    private String prefix = "access_log.";
+
+
+    /**
+     * Should we rotate our log file? Default is true (like old behavior)
+     */
+    private boolean rotatable = true;
+
+
+    /**
+     * The string manager for this package.
+     */
+    private StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The suffix that is added to log file filenames.
+     */
+    private String suffix = "";
+
+
+    /**
+     * The PrintWriter to which we are currently logging, if any.
+     */
+    private PrintWriter writer = null;
+
+
+    /**
+     * A date formatter to format a Date into a date in the format
+     * "yyyy-MM-dd".
+     */
+    private SimpleDateFormat dateFormatter = null;
+
+
+    /**
+     * A date formatter to format Dates into a day string in the format
+     * "dd".
+     */
+    private SimpleDateFormat dayFormatter = null;
+
+
+    /**
+     * A date formatter to format a Date into a month string in the format
+     * "MM".
+     */
+    private SimpleDateFormat monthFormatter = null;
+
+
+    /**
+     * A date formatter to format a Date into a year string in the format
+     * "yyyy".
+     */
+    private SimpleDateFormat yearFormatter = null;
+
+
+    /**
+     * A date formatter to format a Date into a time in the format
+     * "kk:mm:ss" (kk is a 24-hour representation of the hour).
+     */
+    private SimpleDateFormat timeFormatter = null;
+
+
+    /**
+     * The system timezone.
+     */
+    private TimeZone timezone = null;
+
+    
+    /**
+     * The time zone offset relative to GMT in text form when daylight saving
+     * is not in operation.
+     */
+    private String timeZoneNoDST = null;
+ 
+    /**
+     * The time zone offset relative to GMT in text form when daylight saving
+     * is in operation.
+     */
+    private String timeZoneDST = null;
+
+
+    /**
+     * The system time when we last updated the Date that this valve
+     * uses for log lines.
+     */
+    private String currentDateString = null;
+    
+    
+    /**
+     * The instant where the date string was last updated.
+     */
+    private long currentDate = 0L;
+
+
+    /**
+     * When formatting log lines, we often use strings like this one (" ").
+     */
+    private String space = " ";
+
+
+    /**
+     * Resolve hosts.
+     */
+    private boolean resolveHosts = false;
+
+
+    /**
+     * Instant when the log daily rotation was last checked.
+     */
+    private long rotationLastChecked = 0L;
+
+
+    /**
+     * Are we doing conditional logging. default false.
+     */
+    private String condition = null;
+
+
+    /**
+     * Date format to place in log file name. Use at your own risk!
+     */
+    private String fileDateFormat = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the directory in which we create log files.
+     */
+    public String getDirectory() {
+
+        return (directory);
+
+    }
+
+
+    /**
+     * Set the directory in which we create log files.
+     *
+     * @param directory The new log file directory
+     */
+    public void setDirectory(String directory) {
+
+        this.directory = directory;
+
+    }
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the format pattern.
+     */
+    public String getPattern() {
+
+        return (this.pattern);
+
+    }
+
+
+    /**
+     * Set the format pattern, first translating any recognized alias.
+     *
+     * @param pattern The new pattern
+     */
+    public void setPattern(String pattern) {
+
+        if (pattern == null)
+            pattern = "";
+        if (pattern.equals(Constants.AccessLog.COMMON_ALIAS))
+            pattern = Constants.AccessLog.COMMON_PATTERN;
+        if (pattern.equals(Constants.AccessLog.COMBINED_ALIAS))
+            pattern = Constants.AccessLog.COMBINED_PATTERN;
+        this.pattern = pattern;
+
+        if (this.pattern.equals(Constants.AccessLog.COMBINED_PATTERN))
+            combined = true;
+        else
+            combined = false;
+
+    }
+
+
+    /**
+     * Return the log file prefix.
+     */
+    public String getPrefix() {
+
+        return (prefix);
+
+    }
+
+
+    /**
+     * Set the log file prefix.
+     *
+     * @param prefix The new log file prefix
+     */
+    public void setPrefix(String prefix) {
+
+        this.prefix = prefix;
+
+    }
+
+
+    /**
+     * Should we rotate the logs
+     */
+    public boolean isRotatable() {
+
+        return rotatable;
+
+    }
+
+
+    /**
+     * Set the value is we should we rotate the logs
+     *
+     * @param rotatable true is we should rotate.
+     */
+    public void setRotatable(boolean rotatable) {
+
+        this.rotatable = rotatable;
+
+    }
+
+
+    /**
+     * Return the log file suffix.
+     */
+    public String getSuffix() {
+
+        return (suffix);
+
+    }
+
+
+    /**
+     * Set the log file suffix.
+     *
+     * @param suffix The new log file suffix
+     */
+    public void setSuffix(String suffix) {
+
+        this.suffix = suffix;
+
+    }
+
+
+    /**
+     * Set the resolve hosts flag.
+     *
+     * @param resolveHosts The new resolve hosts value
+     */
+    public void setResolveHosts(boolean resolveHosts) {
+
+        this.resolveHosts = resolveHosts;
+
+    }
+
+
+    /**
+     * Get the value of the resolve hosts flag.
+     */
+    public boolean isResolveHosts() {
+
+        return resolveHosts;
+
+    }
+
+
+    /**
+     * Return whether the attribute name to look for when
+     * performing conditional loggging. If null, every
+     * request is logged.
+     */
+    public String getCondition() {
+
+        return condition;
+
+    }
+
+
+    /**
+     * Set the ServletRequest.attribute to look for to perform
+     * conditional logging. Set to null to log everything.
+     *
+     * @param condition Set to null to log everything
+     */
+    public void setCondition(String condition) {
+
+        this.condition = condition;
+
+    }
+
+    /**
+     *  Return the date format date based log rotation.
+     */
+    public String getFileDateFormat() {
+        return fileDateFormat;
+    }
+
+
+    /**
+     *  Set the date format date based log rotation.
+     */
+    public void setFileDateFormat(String fileDateFormat) {
+        this.fileDateFormat =  fileDateFormat;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess() {
+        if (writer != null)
+            writer.flush();
+    }
+
+
+    /**
+     * Log a message summarizing the specified request and response, according
+     * to the format specified by the <code>pattern</code> property.
+     *
+     * @param request Request being processed
+     * @param response Response being processed
+     *
+     * @exception IOException if an input/output error has occurred
+     * @exception ServletException if a servlet error has occurred
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // Pass this request on to the next valve in our pipeline
+        getNext().invoke(request, response);
+
+        if (condition!=null &&
+                null!=request.getRequest().getAttribute(condition)) {
+            return;
+        }
+
+        StringBuffer result = new StringBuffer();
+
+        // Check to see if we should log using the "common" access log pattern
+        String value = null;
+        
+        if (isResolveHosts())
+            result.append(request.getRemoteHost());
+        else
+            result.append(request.getRemoteAddr());
+        
+        result.append(" - ");
+        
+        value = request.getRemoteUser();
+        if (value == null)
+            result.append("- ");
+        else {
+            result.append(value);
+            result.append(space);
+        }
+        
+        result.append(getCurrentDateString());
+        
+        result.append(request.getMethod());
+        result.append(space);
+        result.append(request.getRequestURI());
+        if (request.getQueryString() != null) {
+            result.append('?');
+            result.append(request.getQueryString());
+        }
+        result.append(space);
+        result.append(request.getProtocol());
+        result.append("\" ");
+        
+        result.append(response.getStatus());
+        
+        result.append(space);
+        
+        int length = response.getContentCount();
+        
+        if (length <= 0)
+            value = "-";
+        else
+            value = "" + length;
+        result.append(value);
+        
+        if (combined) {
+            result.append(space);
+            result.append("\"");
+            String referer = request.getHeader("referer");
+            if(referer != null)
+                result.append(referer);
+            else
+                result.append("-");
+            result.append("\"");
+            
+            result.append(space);
+            result.append("\"");
+            String ua = request.getHeader("user-agent");
+            if(ua != null)
+                result.append(ua);
+            else
+                result.append("-");
+            result.append("\"");
+        }
+        
+        log(result.toString());
+        
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Close the currently open log file (if any)
+     */
+    private synchronized void close() {
+
+        if (writer == null)
+            return;
+        writer.flush();
+        writer.close();
+        writer = null;
+        dateStamp = "";
+
+    }
+
+
+    /**
+     * Log the specified message to the log file, switching files if the date
+     * has changed since the previous log call.
+     *
+     * @param message Message to be logged
+     */
+    public void log(String message) {
+
+        // Log this message
+        if (writer != null) {
+            writer.println(message);
+        }
+
+    }
+
+
+    /**
+     * Return the month abbreviation for the specified month, which must
+     * be a two-digit String.
+     *
+     * @param month Month number ("01" .. "12").
+     */
+    private String lookup(String month) {
+
+        int index;
+        try {
+            index = Integer.parseInt(month) - 1;
+        } catch (Throwable t) {
+            index = 0;  // Can not happen, in theory
+        }
+        return (months[index]);
+
+    }
+
+
+    /**
+     * Open the new log file for the date specified by <code>dateStamp</code>.
+     */
+    private synchronized void open() {
+
+        // Create the directory if necessary
+        File dir = new File(directory);
+        if (!dir.isAbsolute())
+            dir = new File(System.getProperty("catalina.base"), directory);
+        dir.mkdirs();
+
+        // Open the current log file
+        try {
+            String pathname;
+            // If no rotate - no need for dateStamp in fileName
+            if (rotatable){
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + dateStamp + suffix;
+            } else {
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + suffix;
+            }
+            writer = new PrintWriter(new BufferedWriter
+                    (new FileWriter(pathname, true), 128000), false);
+        } catch (IOException e) {
+            writer = null;
+        }
+
+    }
+
+
+    /**
+     * This method returns a Date object that is accurate to within one
+     * second.  If a thread calls this method to get a Date and it's been
+     * less than 1 second since a new Date was created, this method
+     * simply gives out the same Date again so that the system doesn't
+     * spend time creating Date objects unnecessarily.
+     *
+     * @return Date
+     */
+    private String getCurrentDateString() {
+        // Only create a new Date once per second, max.
+        long systime = System.currentTimeMillis();
+        if ((systime - currentDate) > 1000) {
+            synchronized (this) {
+                // We don't care about being exact here: if an entry does get
+                // logged as having happened during the previous second
+                // it will not make any difference
+                if ((systime - currentDate) > 1000) {
+
+                    // Format the new date
+                    Date date = new Date();
+                    StringBuffer result = new StringBuffer(32);
+                    result.append("[");
+                    // Day
+                    result.append(dayFormatter.format(date));
+                    result.append('/');
+                    // Month
+                    result.append(lookup(monthFormatter.format(date)));
+                    result.append('/');
+                    // Year
+                    result.append(yearFormatter.format(date));
+                    result.append(':');
+                    // Time
+                    result.append(timeFormatter.format(date));
+                    result.append(space);
+                    // Time zone
+                    result.append(getTimeZone(date));
+                    result.append("] \"");
+                    
+                    // Check for log rotation
+                    if (rotatable) {
+                        // Check for a change of date
+                        String tsDate = dateFormatter.format(date);
+                        // If the date has changed, switch log files
+                        if (!dateStamp.equals(tsDate)) {
+                            synchronized (this) {
+                                if (!dateStamp.equals(tsDate)) {
+                                    close();
+                                    dateStamp = tsDate;
+                                    open();
+                                }
+                            }
+                        }
+                    }
+                    
+                    currentDateString = result.toString();
+                    currentDate = date.getTime();
+                }
+            }
+        }
+        return currentDateString;
+    }
+
+
+    private String getTimeZone(Date date) {
+        if (timezone.inDaylightTime(date)) {
+            return timeZoneDST;
+        } else {
+            return timeZoneNoDST;
+        }
+    }
+    
+    
+    private String calculateTimeZoneOffset(long offset) {
+        StringBuffer tz = new StringBuffer();
+        if ((offset<0))  {
+            tz.append("-");
+            offset = -offset;
+        } else {
+            tz.append("+");
+        }
+
+        long hourOffset = offset/(1000*60*60);
+        long minuteOffset = (offset/(1000*60)) % 60;
+
+        if (hourOffset<10)
+            tz.append("0");
+        tz.append(hourOffset);
+
+        if (minuteOffset<10)
+            tz.append("0");
+        tz.append(minuteOffset);
+
+        return tz.toString();
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (sm.getString("accessLogValve.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Initialize the timeZone, Date formatters, and currentDate
+        timezone = TimeZone.getDefault();
+        timeZoneNoDST = calculateTimeZoneOffset(timezone.getRawOffset());
+        Calendar calendar = Calendar.getInstance(timezone);
+        int offset = calendar.get(Calendar.DST_OFFSET);
+        timeZoneDST = calculateTimeZoneOffset(timezone.getRawOffset()+offset);
+        
+        if (fileDateFormat==null || fileDateFormat.length()==0)
+            fileDateFormat = "yyyy-MM-dd";
+        dateFormatter = new SimpleDateFormat(fileDateFormat);
+        dateFormatter.setTimeZone(timezone);
+        dayFormatter = new SimpleDateFormat("dd");
+        dayFormatter.setTimeZone(timezone);
+        monthFormatter = new SimpleDateFormat("MM");
+        monthFormatter.setTimeZone(timezone);
+        yearFormatter = new SimpleDateFormat("yyyy");
+        yearFormatter.setTimeZone(timezone);
+        timeFormatter = new SimpleDateFormat("HH:mm:ss");
+        timeFormatter.setTimeZone(timezone);
+        currentDateString = getCurrentDateString();
+        dateStamp = dateFormatter.format(new Date());
+
+        open();
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("accessLogValve.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        close();
+
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/JDBCAccessLogValve.java b/container/catalina/src/share/org/apache/catalina/valves/JDBCAccessLogValve.java
new file mode 100644
index 0000000..f3c15e8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/JDBCAccessLogValve.java
@@ -0,0 +1,681 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Properties;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * <p>
+ * This Tomcat extension logs server access directly to a database, and can 
+ * be used instead of the regular file-based access log implemented in 
+ * AccessLogValve.
+ * To use, copy into the server/classes directory of the Tomcat installation
+ * and configure in server.xml as:
+ * <pre>
+ * 		&lt;Valve className="AccessLogDBValve"
+ *        	driverName="<i>your_jdbc_driver</i>"
+ *        	connectionURL="<i>your_jdbc_url</i>"
+ *        	pattern="combined" resolveHosts="false"
+ * 		/&gt;
+ * </pre>
+ * </p>
+ * <p>
+ * Many parameters can be configured, such as the database connection (with
+ * <code>driverName</code> and <code>connectionURL</code>),
+ * the table name (<code>tableName</code>)
+ * and the field names (corresponding to the get/set method names).
+ * The same options as AccessLogValve are supported, such as
+ * <code>resolveHosts</code> and <code>pattern</code> ("common" or "combined" 
+ * only).
+ * </p>
+ * <p>
+ * When Tomcat is started, a database connection (with autoReconnect option)
+ * is created and used for all the log activity. When Tomcat is shutdown, the
+ * database connection is closed.
+ * This logger can be used at the level of the Engine context (being shared
+ * by all the defined hosts) or the Host context (one instance of the logger 
+ * per host, possibly using different databases).
+ * </p>
+ * <p>
+ * The database table can be created with the following command:
+ * </p>
+ * <pre>
+ * CREATE TABLE access (
+ * id INT UNSIGNED AUTO_INCREMENT NOT NULL,
+ * ts TIMESTAMP NOT NULL,
+ * remoteHost CHAR(15) NOT NULL,
+ * user CHAR(15),
+ * timestamp TIMESTAMP NOT NULL,
+ * virtualHost VARCHAR(64) NOT NULL,
+ * method VARCHAR(8) NOT NULL,
+ * query VARCHAR(255) NOT NULL,
+ * status SMALLINT UNSIGNED NOT NULL,
+ * bytes INT UNSIGNED NOT NULL,
+ * referer VARCHAR(128),
+ * userAgent VARCHAR(128),
+ * PRIMARY KEY (id),
+ * INDEX (ts),
+ * INDEX (remoteHost),
+ * INDEX (virtualHost),
+ * INDEX (query),
+ * INDEX (userAgent)
+ * );
+ * </pre>
+ * <p>
+ * If the table is created as above, its name and the field names don't need 
+ * to be defined.
+ * </p>
+ * <p>
+ * If the request method is "common", only these fields are used:
+ * <code>remoteHost, user, timeStamp, query, status, bytes</code>
+ * </p>
+ * <p>
+ * <i>TO DO: provide option for excluding logging of certain MIME types.</i>
+ * </p>
+ * 
+ * @author Andre de Jesus
+ * @author Peter Rossbach
+ */
+
+public final class JDBCAccessLogValve 
+    extends ValveBase 
+    implements Lifecycle {
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Class constructor. Initializes the fields with the default values.
+     * The defaults are:
+     * <pre>
+     * 		driverName = null;
+     * 		connectionURL = null;
+     * 		tableName = "access";
+     * 		remoteHostField = "remoteHost";
+     * 		userField = "user";
+     * 		timestampField = "timestamp";
+     * 		virtualHostField = "virtualHost";
+     * 		methodField = "method";
+     * 		queryField = "query";
+     * 		statusField = "status";
+     * 		bytesField = "bytes";
+     * 		refererField = "referer";
+     * 		userAgentField = "userAgent";
+     * 		pattern = "common";
+     * 		resolveHosts = false;
+     * </pre>
+     */
+    public JDBCAccessLogValve() {
+        super();
+        driverName = null;
+        connectionURL = null;
+        tableName = "access";
+        remoteHostField = "remoteHost";
+        userField = "user";
+        timestampField = "timestamp";
+        virtualHostField = "virtualHost";
+        methodField = "method";
+        queryField = "query";
+        statusField = "status";
+        bytesField = "bytes";
+        refererField = "referer";
+        userAgentField = "userAgent";
+        pattern = "common";
+        resolveHosts = false;
+        conn = null;
+        ps = null;
+        currentTimeMillis = new java.util.Date().getTime();
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+   /**
+     * The connection username to use when trying to connect to the database.
+     */
+    protected String connectionName = null;
+
+
+    /**
+     * The connection URL to use when trying to connect to the database.
+     */
+    protected String connectionPassword = null;
+
+   /**
+     * Instance of the JDBC Driver class we use as a connection factory.
+     */
+    protected Driver driver = null;
+
+
+    private String driverName;
+    private String connectionURL;
+    private String tableName;
+    private String remoteHostField;
+    private String userField;
+    private String timestampField;
+    private String virtualHostField;
+    private String methodField;
+    private String queryField;
+    private String statusField;
+    private String bytesField;
+    private String refererField;
+    private String userAgentField;
+    private String pattern;
+    private boolean resolveHosts;
+
+
+    private Connection conn;
+    private PreparedStatement ps;
+
+
+    private long currentTimeMillis;
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static String info = 
+        "org.apache.catalina.valves.JDBCAccessLogValve/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The string manager for this package.
+     */
+    private StringManager sm = StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+
+    // ------------------------------------------------------------- Properties
+ 
+    /**
+     * Return the username to use to connect to the database.
+     *
+     */
+    public String getConnectionName() {
+        return connectionName;
+    }
+
+    /**
+     * Set the username to use to connect to the database.
+     *
+     * @param connectionName Username
+     */
+    public void setConnectionName(String connectionName) {
+        this.connectionName = connectionName;
+    }
+
+    /**
+     * Sets the database driver name.
+     * 
+     * @param driverName The complete name of the database driver class.
+     */
+    public void setDriverName(String driverName) {
+        this.driverName = driverName;
+    }
+
+   /**
+     * Return the password to use to connect to the database.
+     *
+     */
+    public String getConnectionPassword() {
+        return connectionPassword;
+    }
+
+    /**
+     * Set the password to use to connect to the database.
+     *
+     * @param connectionPassword User password
+     */
+    public void setConnectionPassword(String connectionPassword) {
+        this.connectionPassword = connectionPassword;
+    }
+
+    /**
+     * Sets the JDBC URL for the database where the log is stored.
+     * 
+     * @param connectionURL The JDBC URL of the database.
+     */
+    public void setConnectionURL(String connectionURL) {
+        this.connectionURL = connectionURL;
+    }
+
+
+    /**
+     * Sets the name of the table where the logs are stored.
+     * 
+     * @param tableName The name of the table.
+     */
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+
+    /**
+     * Sets the name of the field containing the remote host.
+     * 
+     * @param remoteHostField The name of the remote host field.
+     */
+    public void setRemoteHostField(String remoteHostField) {
+        this.remoteHostField = remoteHostField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the remote user name.
+     * 
+     * @param userField The name of the remote user field.
+     */
+    public void setUserField(String userField) {
+        this.userField = userField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the server-determined timestamp.
+     * 
+     * @param timestampField The name of the server-determined timestamp field.
+     */
+    public void setTimestampField(String timestampField) {
+        this.timestampField = timestampField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the virtual host information 
+     * (this is in fact the server name).
+     * 
+     * @param virtualHostField The name of the virtual host field.
+     */
+    public void setVirtualHostField(String virtualHostField) {
+        this.virtualHostField = virtualHostField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the HTTP request method.
+     * 
+     * @param methodField The name of the HTTP request method field.
+     */
+    public void setMethodField(String methodField) {
+        this.methodField = methodField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the URL part of the HTTP query.
+     * 
+     * @param queryField The name of the field containing the URL part of 
+     * the HTTP query.
+     */
+    public void setQueryField(String queryField) {
+        this.queryField = queryField;
+    }
+
+
+  /**
+   * Sets the name of the field containing the HTTP response status code.
+   * 
+   * @param statusField The name of the HTTP response status code field.
+   */  
+    public void setStatusField(String statusField) {
+        this.statusField = statusField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the number of bytes returned.
+     * 
+     * @param bytesField The name of the returned bytes field.
+     */
+    public void setBytesField(String bytesField) {
+        this.bytesField = bytesField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the referer.
+     * 
+     * @param refererField The referer field name.
+     */
+    public void setRefererField(String refererField) {
+        this.refererField = refererField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the user agent.
+     * 
+     * @param userAgentField The name of the user agent field.
+     */
+    public void setUserAgentField(String userAgentField) {
+        this.userAgentField = userAgentField;
+    }
+
+
+    /**
+     * Sets the logging pattern. The patterns supported correspond to the 
+     * file-based "common" and "combined". These are translated into the use 
+     * of tables containing either set of fields.
+     * <P><I>TO DO: more flexible field choices.</I></P>
+     * 
+     * @param pattern The name of the logging pattern.
+     */
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+
+    /**
+     * Determines whether IP host name resolution is done.
+     * 
+     * @param resolveHosts "true" or "false", if host IP resolution 
+     * is desired or not.
+     */
+    public void setResolveHosts(String resolveHosts) {
+        this.resolveHosts = new Boolean(resolveHosts).booleanValue();
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * This method is invoked by Tomcat on each query.
+     * 
+     * @param request The Request object.
+     * @param response The Response object.
+     *
+     * @exception IOException Should not be thrown.
+     * @exception ServletException Database SQLException is wrapped 
+     * in a ServletException.
+     */    
+    public void invoke(Request request, Response response) 
+        throws IOException, ServletException {
+
+        getNext().invoke(request, response);
+
+        String remoteHost = "";
+        if(resolveHosts)
+            remoteHost = request.getRemoteHost();
+        else
+            remoteHost = request.getRemoteAddr();
+        String user = "";
+        if(request != null)
+            user = request.getRemoteUser();
+        String query="";
+        if(request != null)
+            query = request.getRequestURI();
+        int bytes = response.getContentCount();
+        if(bytes < 0)
+            bytes = 0;
+        int status = response.getStatus();
+        if (pattern.equals("combined")) {
+                String virtualHost = "";
+                if(request != null)
+                    virtualHost = request.getServerName();
+                String method = "";
+                if(request != null)
+                    method = request.getMethod();
+                String referer = "";
+                if(request != null)
+                    referer = request.getHeader("referer");
+                String userAgent = "";
+                if(request != null)
+                    userAgent = request.getHeader("user-agent");
+        }
+        synchronized (this) {
+          int numberOfTries = 2;
+          while (numberOfTries>0) {
+            try {
+                open();
+    
+                ps.setString(1, remoteHost);
+                ps.setString(2, user);
+                ps.setTimestamp(3, new Timestamp(getCurrentTimeMillis()));
+                ps.setString(4, query);
+                ps.setInt(5, status);
+                ps.setInt(6, bytes);
+                if (pattern.equals("combined")) {
+     
+                      String virtualHost = "";
+                      if(request != null)
+                         virtualHost = request.getServerName();
+                      String method = "";
+                      if(request != null)
+                         method = request.getMethod();
+                      String referer = "";
+                      if(request != null)
+                         referer = request.getHeader("referer");
+                      String userAgent = "";
+                      if(request != null)
+                         userAgent = request.getHeader("user-agent");
+                      ps.setString(7, virtualHost);
+                      ps.setString(8, method);
+                      ps.setString(9, referer);
+                      ps.setString(10, userAgent);
+                }
+                ps.executeUpdate();
+                return;
+              } catch (SQLException e) {
+                // Log the problem for posterity
+                  container.getLogger().error(sm.getString("jdbcAccessLogValve.exception"), e);
+
+                // Close the connection so that it gets reopened next time
+                if (conn != null)
+                    close();
+              }
+    	      numberOfTries--;        
+           }
+        }
+
+    }	
+
+
+    /**
+     * Adds a Lifecycle listener.
+     * 
+     * @param listener The listener to add.
+     */  
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Removes a Lifecycle listener.
+     * 
+     * @param listener The listener to remove.
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+    /**
+     * Open (if necessary) and return a database connection for use by
+     * this AccessLogValve.
+     *
+     * @exception SQLException if a database error occurs
+     */
+    protected void open() throws SQLException {
+
+        // Do nothing if there is a database connection already open
+        if (conn != null)
+            return ;
+
+        // Instantiate our database driver if necessary
+        if (driver == null) {
+            try {
+                Class clazz = Class.forName(driverName);
+                driver = (Driver) clazz.newInstance();
+            } catch (Throwable e) {
+                throw new SQLException(e.getMessage());
+            }
+        }
+
+        // Open a new connection
+        Properties props = new Properties();
+        props.put("autoReconnect", "true");
+        if (connectionName != null)
+            props.put("user", connectionName);
+        if (connectionPassword != null)
+            props.put("password", connectionPassword);
+        conn = driver.connect(connectionURL, props);
+        conn.setAutoCommit(true);
+        if (pattern.equals("common")) {
+                ps = conn.prepareStatement
+                    ("INSERT INTO " + tableName + " (" 
+                     + remoteHostField + ", " + userField + ", "
+                     + timestampField +", " + queryField + ", "
+                     + statusField + ", " + bytesField 
+                     + ") VALUES(?, ?, ?, ?, ?, ?)");
+        } else if (pattern.equals("combined")) {
+                ps = conn.prepareStatement
+                    ("INSERT INTO " + tableName + " (" 
+                     + remoteHostField + ", " + userField + ", "
+                     + timestampField + ", " + queryField + ", " 
+                     + statusField + ", " + bytesField + ", " 
+                     + virtualHostField + ", " + methodField + ", "
+                     + refererField + ", " + userAgentField
+                     + ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+        }
+    }
+
+    /**
+     * Close the specified database connection.
+     */
+    protected void close() {
+
+        // Do nothing if the database connection is already closed
+        if (conn == null)
+            return;
+
+        // Close our prepared statements (if any)
+        try {
+            ps.close();
+        } catch (Throwable f) {
+            ;
+        }
+        this.ps = null;
+
+
+
+        // Close this database connection, and log any errors
+        try {
+            conn.close();
+        } catch (SQLException e) {
+            container.getLogger().error(sm.getString("jdbcAccessLogValeve.close"), e); // Just log it here            
+        } finally {
+           this.conn = null;
+        }
+
+    }
+    /**
+     * Invoked by Tomcat on startup. The database connection is set here.
+     * 
+     * @exception LifecycleException Can be thrown on lifecycle 
+     * inconsistencies or on database errors (as a wrapped SQLException).
+     */
+    public void start() throws LifecycleException {
+
+        if (started)
+            throw new LifecycleException
+                (sm.getString("accessLogValve.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        try {
+            open() ;        
+        } catch (SQLException e) {
+            throw new LifecycleException(e);
+        }
+
+    }
+
+
+    /**
+     * Invoked by tomcat on shutdown. The database connection is closed here.
+     * 
+     * @exception LifecycleException Can be thrown on lifecycle 
+     * inconsistencies or on database errors (as a wrapped SQLException).
+     */
+    public void stop() throws LifecycleException {
+
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("accessLogValve.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+        
+        close() ;
+    	
+    }
+
+
+    public long getCurrentTimeMillis() {
+        long systime  =  System.currentTimeMillis();
+        if ((systime - currentTimeMillis) > 1000) {
+            currentTimeMillis  =  new java.util.Date(systime).getTime();
+        }
+        return currentTimeMillis;
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/LocalStrings.properties b/container/catalina/src/share/org/apache/catalina/valves/LocalStrings.properties
new file mode 100644
index 0000000..c3d4203
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/LocalStrings.properties
@@ -0,0 +1,71 @@
+accessLogValve.alreadyStarted=Access Logger has already been started
+accessLogValve.notStarted=Access Logger has not yet been started
+semaphoreValve.alreadyStarted=Semaphore valve has already been started
+semaphoreValve.notStarted=Semaphore valve has not yet been started
+certificatesValve.alreadyStarted=Certificates Valve has already been started
+certificatesValve.notStarted=Certificates Valve has not yet been started
+interceptorValve.alreadyStarted=Interceptor Valve has already been started
+interceptorValve.notStarted=Interceptor Valve has not yet been started
+requestFilterValve.next=No ''next'' valve has been configured
+requestFilterValve.syntax=Syntax error in request filter pattern {0}
+requestListenerValve.requestInit=Exception sending request initialized lifecycle event to listener instance of class {0}
+requestListenerValve.requestDestroy=Exception sending request destroyed lifecycle event to listener instance of class {0}
+valveBase.noNext=Configuration error: No ''next'' valve configured
+jdbcAccessLogValve.exception=Exception performing insert access entry
+jdbcAccessLogValve.close=Exception closing database connection
+
+# Error report valve
+errorReportValve.errorReport=Error report
+errorReportValve.statusHeader=HTTP Status {0} - {1}
+errorReportValve.exceptionReport=Exception report
+errorReportValve.statusReport=Status report
+errorReportValve.message=message
+errorReportValve.description=description
+errorReportValve.exception=exception
+errorReportValve.rootCause=root cause
+errorReportValve.note=note
+errorReportValve.rootCauseInLogs=The full stack trace of the root cause is available in the {0} logs.
+
+# HTTP status reports
+http.100=The client may continue ({0}).
+http.101=The server is switching protocols according to the "Upgrade" header ({0}).
+http.201=The request succeeded and a new resource ({0}) has been created on the server.
+http.202=This request was accepted for processing, but has not been completed ({0}).
+http.203=The meta information presented by the client did not originate from the server ({0}).
+http.204=The request succeeded but there is no information to return ({0}).
+http.205=The client should reset the document view which caused this request to be sent ({0}).
+http.206=The server has fulfilled a partial GET request for this resource ({0}).
+http.207=Multiple status values have been returned ({0}).
+http.300=The requested resource ({0}) corresponds to any one of a set of representations, each with its own specific location.
+http.301=The requested resource ({0}) has moved permanently to a new location.
+http.302=The requested resource ({0}) has moved temporarily to a new location.
+http.303=The response to this request can be found under a different URI ({0}).
+http.304=The requested resource ({0}) is available and has not been modified.
+http.305=The requested resource ({0}) must be accessed through the proxy given by the "Location" header.
+http.400=The request sent by the client was syntactically incorrect ({0}).
+http.401=This request requires HTTP authentication ({0}).
+http.402=Payment is required for access to this resource ({0}).
+http.403=Access to the specified resource ({0}) has been forbidden.
+http.404=The requested resource ({0}) is not available.
+http.405=The specified HTTP method is not allowed for the requested resource ({0}).
+http.406=The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers ({0}).
+http.407=The client must first authenticate itself with the proxy ({0}).
+http.408=The client did not produce a request within the time that the server was prepared to wait ({0}).
+http.409=The request could not be completed due to a conflict with the current state of the resource ({0}).
+http.410=The requested resource ({0}) is no longer available, and no forwarding address is known.
+http.411=This request cannot be handled without a defined content length ({0}).
+http.412=A specified precondition has failed for this request ({0}).
+http.413=The request entity is larger than the server is willing or able to process.
+http.414=The server refused this request because the request URI was too long ({0}).
+http.415=The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ({0}).
+http.416=The requested byte range cannot be satisfied ({0}).
+http.417=The expectation given in the "Expect" request header ({0}) could not be fulfilled.
+http.422=The server understood the content type and syntax of the request but was unable to process the contained instructions ({0}).
+http.423=The source or destination resource of a method is locked ({0}).
+http.500=The server encountered an internal error ({0}) that prevented it from fulfilling this request.
+http.501=The server does not support the functionality needed to fulfill this request ({0}).
+http.502=This server received an invalid response from a server it consulted when acting as a proxy or gateway ({0}).
+http.503=The requested service ({0}) is not currently available.
+http.504=The server received a timeout from an upstream server while acting as a gateway or proxy ({0}).
+http.505=The server does not support the requested HTTP protocol version ({0}).
+http.507=The resource does not have sufficient space to record the state of the resource after execution of this method ({0}).
diff --git a/container/catalina/src/share/org/apache/catalina/valves/LocalStrings_es.properties b/container/catalina/src/share/org/apache/catalina/valves/LocalStrings_es.properties
new file mode 100644
index 0000000..6373fba
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/LocalStrings_es.properties
@@ -0,0 +1,69 @@
+accessLogValve.alreadyStarted=El Registrador de accesos ya se había iniciado
+accessLogValve.notStarted=El Registrador de accesos no se ha iniciado
+certificatesValve.alreadyStarted=La válvula de certificados ya se había iniciado
+certificatesValve.notStarted=La válvula de certificados no se ha iniciado
+interceptorValve.alreadyStarted=La válvula interceptora ya se había iniciado
+interceptorValve.notStarted=La válvula interceptora no se ha iniciado
+requestFilterValve.next=No hay ''siguiente'' válvula configurada
+requestFilterValve.syntax=Error de sintáxis en petición de filtro patrón {0}
+requestListenerValve.requestInit=Una excepción durante el envío de requerimiento ha iniciado un evento de ciclo de vida (lifecycle event) para la instancia de clase a la escucha (listener) {0}
+requestListenerValve.requestDestroy=Una excepción durante el envío de requerimiento ha destruído un evento de ciclo de vida (lifecycle event) para la instancia de clase a la escucha (listener) {0}
+valveBase.noNext=Error de configuración: No hay ''siguiente'' válvula configurada
+jdbcAccessLogValve.exception=Excepción realizando entrada de acceso a inserción
+jdbcAccessLogValve.close=Excepción cerrando conexión a base de datos
+
+# Error report valve
+errorReportValve.errorReport=Informe de Error
+errorReportValve.statusHeader=Estado HTTP {0} - {1}
+errorReportValve.exceptionReport=Informe de Excepción
+errorReportValve.statusReport=Informe de estado
+errorReportValve.message=mensaje
+errorReportValve.description=descripción
+errorReportValve.exception=excepción
+errorReportValve.rootCause=causa raíz
+errorReportValve.note=nota
+errorReportValve.rootCauseInLogs=La traza completa de la causa de este error se encuentra en los archivos de diario de {0}.
+
+# HTTP status reports
+http.100=El cliente puede continuar ({0}).
+http.101=El servidor está conmutando protocolos con arreglo a la cabecera "Upgrade" ({0}).
+http.201=El requerimiento tuvo éxito y un nuevo recurso ({0}) ha sido creado en el servidor.
+http.202=Este requerimiento ha sido aceptado para ser procesado, pero no ha sido completado ({0}).
+http.203=La información meta presentada por el cliente no se originó desde el servidor ({0}).
+http.204=El requerimiento tuvo éxito pero no hay información que devolver ({0}).
+http.205=El cliente no debería de limpiar la vista del documento que causó que este requerimiento fuera enviado ({0}).
+http.206=El servidor ha rellenado paciálmente un requerimiento GET para este recurso ({0}).
+http.207=Se han devuelto valores múltiples de estado ({0}).
+http.300=El recurso requerido ({0}) corresponde a una cualquiera de un conjunto de representaciones, cada una con su propia localización específica.
+http.301=El recurso requerido ({0}) ha sido movido permanéntemente a una nueva localización.
+http.302=El recurso requerido ({0}) ha sido movido temporálmente a una nueva localización.
+http.303=La respuesta a este requerimiento se puede hallar bajo una URI diferente ({0}).
+http.304=El recurso requerido ({0}) está disponible y no ha sido modificado.
+http.305=El recurso requerido ({0}) debe de ser accedido a través del apoderado (proxy) dado mediante la cabecera "Location".
+http.400=El requerimiento enviado por el cliente era sintácticamente incorrecto ({0}).
+http.401=Este requerimiento requiere autenticación HTTP ({0}).
+http.402=Se requiere pago para acceder a este recurso ({0}).
+http.403=El acceso al recurso especificado ({0}) ha sido prohibido.
+http.404=El recurso requerido ({0}) no está disponible.
+http.405=El método HTTP especificado no está permitido para el recurso requerido ({0}).
+http.406=El recurso identificado por este requerimiento sólo es capaz de generar respuestas con características no aceptables con arreglo a las cabeceras "accept" de requerimiento ({0}).
+http.407=El cliente debe de ser primero autenticado en el apoderado ({0}).
+http.408=El cliente no produjo un requerimiento dentro del tiempo en que el servidor estaba preparado esperando ({0}).
+http.409=El requerimiento no pudo ser completado debido a un conflicto con el estado actual del recurso ({0}).
+http.410=El recurso requerido ({0}) ya no está disponible y no se conoce dirección de reenvío.
+http.411=Este requerimiento no puede ser manejado sin un tamaño definido de contenido ({0}).
+http.412=Una precondición especificada ha fallado para este requerimiento ({0}).
+http.413=La entidad de requerimiento es mayor de lo que el servidor quiere o puede procesar.
+http.414=El servidor rechazó este requerimiento porque la URI requerida era demasiado larga ({0}).
+http.415=El servidor rechazó este requerimiento porque la entidad requerida se encuentra en un formato no soportado por el recurso requerido para el método requerido ({0}).
+http.416=El rango de byte requerido no puede ser satisfecho ({0}).
+http.417=Lo que se espera dado por la cabecera "Expect" de requerimiento ({0}) no pudo ser completado.
+http.422=El servidor entendió el tipo de contenido y la sintáxis del requerimiento pero no pudo procesar las instrucciones contenidas ({0}).
+http.423=La fuente o recurso de destino de un método está bloqueada ({0}).
+http.500=El servidor encontró un error interno ({0}) que hizo que no pudiera rellenar este requerimiento.
+http.501=El servidor no soporta la funcionalidad necesaria para rellenar este requerimiento ({0}).
+http.502=Este servidor recibió una respuesta inválida desde un servidor que consultó cuando actuaba como apoderado o pasarela ({0}).
+http.503=El servicio requerido ({0}) no está disponible en este momento.
+http.504=El servidor recibió un Tiempo Agotado desde un servidor superior cuando actuaba como pasarela o apoderado ({0}).
+http.505=El servidor no soporta la versión de protocolo HTTP requerida ({0}).
+http.507=El recurso no tiene espacio suficiente para registrar el estado del recurso tras la ejecución de este método ({0}).
diff --git a/container/catalina/src/share/org/apache/catalina/valves/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/catalina/valves/LocalStrings_fr.properties
new file mode 100644
index 0000000..4ca7969
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/LocalStrings_fr.properties
@@ -0,0 +1,67 @@
+accessLogValve.alreadyStarted=Le traceur d''accès a déjà été démarré
+accessLogValve.notStarted=Le traceur d''accès n''a pas encore été démarré
+certificatesValve.alreadyStarted=La Valve de Certificats a déjà été démarrée
+certificatesValve.notStarted=La Valve de Certificats n''a pas encore été démarrée
+interceptorValve.alreadyStarted=La Valve d''Interception a déjà été démarré
+interceptorValve.notStarted=La Valve d''Interception n''a pas encore été démarrée
+requestFilterValve.next=Aucune Valve ''suivante'' n''a été configurée
+requestFilterValve.syntax=Erreur de synthaxe dans le pattern de filtre de requête {0}
+requestListenerValve.requestInit=Une exception lors de l''envoi de requête a initié un évènement cycle de vie (lifecycle event) pour l''instance de classe à l''écoute (listener) {0}
+requestListenerValve.requestDestroy=Une exception lors de l''envoi de requête a détruit un évènement cycle de vie (lifecycle event) pour l''instance de classe à l''écoute (listener) {0}
+valveBase.noNext=Erreur de configuration error: Aucune Valve ''suivante'' n''a été configurée
+
+# Error report valve
+errorReportValve.errorReport=Rapport d''erreur
+errorReportValve.statusHeader=Etat HTTP {0} - {1}
+errorReportValve.exceptionReport=Rapport d''exception
+errorReportValve.statusReport=Rapport d''état
+errorReportValve.message=message
+errorReportValve.description=description
+errorReportValve.exception=exception
+errorReportValve.rootCause=cause mère
+errorReportValve.note=note
+errorReportValve.rootCauseInLogs=La trace complète de la cause mère de cette erreur est disponible dans les fichiers journaux de {0}.
+
+# HTTP status reports
+http.100=Le client peut continuer ({0}).
+http.101=Le serveur change de protocoles suivant la directive "Upgrade" de l''entête ({0}).
+http.201=La requête a réussi et une nouvelle ressource ({0}) a été créee sur le serveur.
+http.202=La requête a été accepté pour traitement, mais n''a pas été terminée ({0}).
+http.203=L''information meta présentée par le client n''a pas pour origine ce server ({0}).
+http.204=La requête a réussi mais il n''y a aucune information à retourner ({0}).
+http.205=Le client doit remettre à zéro la vue de document qui a causée l''envoi de cette requête ({0}).
+http.206=Le serveur a satisfait une requête GET partielle pour cette ressource ({0}).
+http.207=Plusieurs valeurs d''états ont été retournées ({0}).
+http.300=La ressource demandée ({0}) correspond à plusieurs représentations, chacune avec sa propre localisation.
+http.301=La ressource demandée ({0}) a été déplacée de façon permanente vers une nouvelle localisation.
+http.302=La ressource demandée ({0}) a été déplacée de façon temporaire vers une nouvelle localisation.
+http.303=La réponse à cette requête peut être trouvée à une URI différente ({0}).
+http.304=La ressource demandée ({0}) est disponible et n''a pas été modifiée.
+http.305=La ressource demandée ({0}) doit être accédée au travers du relais indiqué par la directive "Location" de l''entête.
+http.400=La requête envoyée par le client était syntaxiquement incorrecte ({0}).
+http.401=La requête nécessite une authentification HTTP ({0}).
+http.402=Un paiement est demandé pour accéder à cette ressource ({0}).
+http.403=L''accès à la ressource demandée ({0}) a été interdit.
+http.404=La ressource demandée ({0}) n''est pas disponible.
+http.405=La méthode HTTP spécifiée n''est pas autorisée pour la ressource demandée ({0}).
+http.406=La ressource identifiée par cette requête n''est capable de générer des réponses qu''avec des caractéristiques incompatible avec la directive "accept" présente dans l''entête de requête ({0}).
+http.407=Le client doit d''abord s''authentifier auprès du relais ({0}).
+http.408=Le client n''a pas produit de requête pendant le temps d''attente du serveur ({0}).
+http.409=La requête ne peut être finalisée suite à un conflit lié à l''état de la ressource ({0}).
+http.410=La ressource demandée ({0}) n''est pas disponible, et aucune addresse de rebond (forwarding) n''est connue.
+http.411=La requête ne peut être traitée sans définition d''une taille de contenu (content length) ({0}).
+http.412=Une condition préalable demandée a échouée pour cette requête ({0}).
+http.413=L''entité de requête est plus importante que ce que le serveur veut ou peut traiter.
+http.414=Le serveur a refusé cette requête car URI de requête est trop longue ({0}).
+http.415=Le serveur a refusé cette requête car l''entité de requête est dans un format non supporté par la ressource demandée avec la méthode spécifiée ({0}).
+http.416=La place d''octets (byte range) ne peut être satisfaite ({0}).
+http.417=L''attente indiqué dans la directive "Expect" de l''entête de requête ({0}) ne peut être satisfaite.
+http.422=Le serveur a compris le type de contenu (content type) ainsi que la synthaxe de la requête mais a été incapable de traiter les instructions contenues ({0}).
+http.423=La ressource source ou destination de la méthode est vérrouillée ({0}).
+http.500=Le serveur a rencontré une erreur interne ({0}) qui l''a empêché de satisfaire la requête.
+http.501=Le serveur ne supporte pas la fonctionnalité demandée pour satisfaire cette requête ({0}).
+http.502=Le serveur a reçu une réponse invalide d''un serveur qu''il consultait en tant que relais ou passerelle ({0}).
+http.503=Le service demandé ({0}) n''est pas disponible actuellement.
+http.504=Le serveur a reçu un dépassement de delais (timeout) d''un serveur amont qu''il consultait en tant que relais ou passerelle ({0}).
+http.505=Le serveur ne supporte pas la version de protocole HTTP demandé ({0}).
+http.507=La ressource n''a pas assez d''espace pour enregistrer l''état de la ressource après exécution de cette méthode ({0}).
diff --git a/container/catalina/src/share/org/apache/catalina/valves/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/catalina/valves/LocalStrings_ja.properties
new file mode 100644
index 0000000..dff4096
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/LocalStrings_ja.properties
@@ -0,0 +1,24 @@
+accessLogValve.alreadyStarted=\u30a2\u30af\u30bb\u30b9\u30ed\u30ac\u30fc\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+accessLogValve.notStarted=\u30a2\u30af\u30bb\u30b9\u30ed\u30ac\u30fc\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+certificatesValve.alreadyStarted=\u8a8d\u8a3c\u30d0\u30eb\u30d6\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+certificatesValve.notStarted=\u8a8d\u8a3c\u30d0\u30eb\u30d6\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+interceptorValve.alreadyStarted=\u30a4\u30f3\u30bf\u30fc\u30bb\u30d7\u30bf\u30d0\u30eb\u30d6\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+interceptorValve.notStarted=\u30a4\u30f3\u30bf\u30fc\u30bb\u30d7\u30bf\u30d0\u30eb\u30d6\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+requestFilterValve.next=\u6b21\u306e\u30d0\u30eb\u30d6\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+requestFilterValve.syntax=\u30ea\u30af\u30a8\u30b9\u30c8\u30d5\u30a3\u30eb\u30bf\u30d1\u30bf\u30fc\u30f3 {0} \u306b\u69cb\u6587\u30a8\u30e9\u30fc\u304c\u3042\u308a\u307e\u3059
+requestListenerValve.requestInit=\u30af\u30e9\u30b9 {0} \u306e\u30ea\u30b9\u30ca\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u521d\u671f\u5316\u3059\u308b\u30e9\u30a4\u30d5\u30b5\u30a4\u30af\u30eb\u30a4\u30d9\u30f3\u30c8\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u9001\u4fe1\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+requestListenerValve.requestDestroy=\u30af\u30e9\u30b9 {0} \u306e\u30ea\u30b9\u30ca\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u5ec3\u68c4\u3059\u308b\u30e9\u30a4\u30d5\u30b5\u30a4\u30af\u30eb\u30a4\u30d9\u30f3\u30c8\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u9001\u4fe1\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+jdbcAccessLogValve.exception=\u30a2\u30af\u30bb\u30b9\u30a8\u30f3\u30c8\u30ea\u306e\u633f\u5165\u3092\u5b9f\u884c\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+jdbcAccessLogValve.close=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u3092\u30af\u30ed\u30fc\u30ba\u4e2d\u306e\u4f8b\u5916\u3067\u3059
+
+# Error report valve
+valveBase.noNext=\u8a2d\u5b9a\u30a8\u30e9\u30fc: \u6b21\u306e\u30d0\u30eb\u30d6\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+errorReportValve.statusHeader=HTTP\u30b9\u30c6\u30fc\u30bf\u30b9 {0} - {1}
+errorReportValve.exceptionReport=\u4f8b\u5916\u30ec\u30dd\u30fc\u30c8
+errorReportValve.statusReport=\u30b9\u30c6\u30fc\u30bf\u30b9\u30ec\u30dd\u30fc\u30c8
+errorReportValve.message=\u30e1\u30c3\u30bb\u30fc\u30b8
+errorReportValve.description=\u8aac\u660e
+errorReportValve.exception=\u4f8b\u5916
+errorReportValve.rootCause=\u539f\u56e0
+errorReportValve.note=\u6ce8\u610f
+errorReportValve.rootCauseInLogs=\u539f\u56e0\u306e\u3059\u3079\u3066\u306e\u30b9\u30bf\u30c3\u30af\u30c8\u30ec\u30fc\u30b9\u306f\u3001{0}\u306e\u30ed\u30b0\u306b\u8a18\u9332\u3055\u308c\u3066\u3044\u307e\u3059
diff --git a/container/catalina/src/share/org/apache/catalina/valves/PersistentValve.java b/container/catalina/src/share/org/apache/catalina/valves/PersistentValve.java
new file mode 100644
index 0000000..e0c39c2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/PersistentValve.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.Store;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.session.PersistentManager;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardHost</code> container implementation.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>: To work correctly it requires a  PersistentManager.
+ *
+ * @author Jean-Frederic Clere
+ * @version $Revision$ $Date$
+ */
+
+public class PersistentValve
+    extends ValveBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.PersistentValve/1.0";
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Select the appropriate child Context to process this request,
+     * based on the specified request URI.  If no matching Context can
+     * be found, return an appropriate HTTP error.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // Select the Context to be used for this Request
+        StandardHost host = (StandardHost) getContainer();
+        Context context = request.getContext();
+        if (context == null) {
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 sm.getString("standardHost.noContext"));
+            return;
+        }
+
+        // Bind the context CL to the current thread
+        Thread.currentThread().setContextClassLoader
+            (context.getLoader().getClassLoader());
+
+        // Update the session last access time for our session (if any)
+        String sessionId = request.getRequestedSessionId();
+        Manager manager = context.getManager();
+        if (sessionId != null && manager != null) {
+            if (manager instanceof PersistentManager) {
+                Store store = ((PersistentManager) manager).getStore();
+                if (store != null) {
+                    Session session = null;
+                    try {
+                        session = store.load(sessionId);
+                    } catch (Exception e) {
+                        container.getLogger().error("deserializeError");
+                    }
+                    if (session != null) {
+                        if (!session.isValid() ||
+                            isSessionStale(session, System.currentTimeMillis())) {
+                            if (container.getLogger().isDebugEnabled())
+                                container.getLogger().debug("session swapped in is invalid or expired");
+                            session.expire();
+                            store.remove(sessionId);
+                        } else {
+                            session.setManager(manager);
+                            // session.setId(sessionId); Only if new ???
+                            manager.add(session);
+                            // ((StandardSession)session).activate();
+                            session.access();
+                        }
+                    }
+                }
+            }
+        }
+        if (container.getLogger().isDebugEnabled())
+            container.getLogger().debug("sessionId: " + sessionId);
+
+        // Ask the next valve to process the request.
+        getNext().invoke(request, response);
+
+        // Read the sessionid after the response.
+        // HttpSession hsess = hreq.getSession(false);
+        Session hsess;
+        try {
+            hsess = request.getSessionInternal();
+        } catch (Exception ex) {
+            hsess = null;
+        }
+        String newsessionId = null;
+        if (hsess!=null)
+            newsessionId = hsess.getIdInternal();
+
+        if (container.getLogger().isDebugEnabled())
+            container.getLogger().debug("newsessionId: " + newsessionId);
+        if (newsessionId!=null) {
+            /* store the session in the store and remove it from the manager */
+            if (manager instanceof PersistentManager) {
+                Session session = manager.findSession(newsessionId);
+                Store store = ((PersistentManager) manager).getStore();
+                if (store != null && session!=null &&
+                    session.isValid() &&
+                    !isSessionStale(session, System.currentTimeMillis())) {
+                    // ((StandardSession)session).passivate();
+                    store.save(session);
+                    ((PersistentManager) manager).removeSuper(session);
+                    session.recycle();
+                } else {
+                    if (container.getLogger().isDebugEnabled())
+                        container.getLogger().debug("newsessionId store: " + store + " session: " +
+                                session + " valid: " + session.isValid() +
+                                " Staled: " +
+                                isSessionStale(session, System.currentTimeMillis()));
+
+                }
+            } else {
+                if (container.getLogger().isDebugEnabled())
+                    container.getLogger().debug("newsessionId Manager: " + manager);
+            }
+        }
+    }
+
+    /**
+     * Indicate whether the session has been idle for longer
+     * than its expiration date as of the supplied time.
+     *
+     * FIXME: Probably belongs in the Session class.
+     */
+    protected boolean isSessionStale(Session session, long timeNow) {
+ 
+        int maxInactiveInterval = session.getMaxInactiveInterval();
+        if (maxInactiveInterval >= 0) {
+            int timeIdle = // Truncate, do not round up
+                (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+            if (timeIdle >= maxInactiveInterval)
+                return true;
+        }
+ 
+        return false;
+ 
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/RemoteAddrValve.java b/container/catalina/src/share/org/apache/catalina/valves/RemoteAddrValve.java
new file mode 100644
index 0000000..8829825
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/RemoteAddrValve.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+
+/**
+ * Concrete implementation of <code>RequestFilterValve</code> that filters
+ * based on the string representation of the remote client's IP address.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class RemoteAddrValve
+    extends RequestFilterValve {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.RemoteAddrValve/1.0";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Extract the desired request property, and pass it (along with the
+     * specified request and response objects) to the protected
+     * <code>process()</code> method to perform the actual filtering.
+     * This method must be implemented by a concrete subclass.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        process(request.getRequest().getRemoteAddr(), request, response);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/RemoteHostValve.java b/container/catalina/src/share/org/apache/catalina/valves/RemoteHostValve.java
new file mode 100644
index 0000000..adbf328
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/RemoteHostValve.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+
+/**
+ * Concrete implementation of <code>RequestFilterValve</code> that filters
+ * based on the string representation of the remote client's IP address.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class RemoteHostValve
+    extends RequestFilterValve {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.RemoteHostValve/1.0";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Extract the desired request property, and pass it (along with the
+     * specified request and response objects) to the protected
+     * <code>process()</code> method to perform the actual filtering.
+     * This method must be implemented by a concrete subclass.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        process(request.getRequest().getRemoteHost(), request, response);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/RequestDumperValve.java b/container/catalina/src/share/org/apache/catalina/valves/RequestDumperValve.java
new file mode 100644
index 0000000..bc97c53
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/RequestDumperValve.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+
+
+/**
+ * <p>Implementation of a Valve that logs interesting contents from the
+ * specified Request (before processing) and the corresponding Response
+ * (after processing).  It is especially useful in debugging problems
+ * related to headers and cookies.</p>
+ *
+ * <p>This Valve may be attached to any Container, depending on the granularity
+ * of the logging you wish to perform.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class RequestDumperValve
+    extends ValveBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.RequestDumperValve/1.0";
+
+
+    /**
+     * The StringManager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Log the interesting request parameters, invoke the next Valve in the
+     * sequence, and log the interesting response parameters.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        Log log = container.getLogger();
+        
+        // Log pre-service information
+        log.info("REQUEST URI       =" + request.getRequestURI());
+        log.info("          authType=" + request.getAuthType());
+        log.info(" characterEncoding=" + request.getCharacterEncoding());
+        log.info("     contentLength=" + request.getContentLength());
+        log.info("       contentType=" + request.getContentType());
+        log.info("       contextPath=" + request.getContextPath());
+        Cookie cookies[] = request.getCookies();
+        if (cookies != null) {
+            for (int i = 0; i < cookies.length; i++)
+                log.info("            cookie=" + cookies[i].getName() + "=" +
+                    cookies[i].getValue());
+        }
+        Enumeration hnames = request.getHeaderNames();
+        while (hnames.hasMoreElements()) {
+            String hname = (String) hnames.nextElement();
+            Enumeration hvalues = request.getHeaders(hname);
+            while (hvalues.hasMoreElements()) {
+                String hvalue = (String) hvalues.nextElement();
+                log.info("            header=" + hname + "=" + hvalue);
+            }
+        }
+        log.info("            locale=" + request.getLocale());
+        log.info("            method=" + request.getMethod());
+        Enumeration pnames = request.getParameterNames();
+        while (pnames.hasMoreElements()) {
+            String pname = (String) pnames.nextElement();
+            String pvalues[] = request.getParameterValues(pname);
+            StringBuffer result = new StringBuffer(pname);
+            result.append('=');
+            for (int i = 0; i < pvalues.length; i++) {
+                if (i > 0)
+                    result.append(", ");
+                result.append(pvalues[i]);
+            }
+            log.info("         parameter=" + result.toString());
+        }
+        log.info("          pathInfo=" + request.getPathInfo());
+        log.info("          protocol=" + request.getProtocol());
+        log.info("       queryString=" + request.getQueryString());
+        log.info("        remoteAddr=" + request.getRemoteAddr());
+        log.info("        remoteHost=" + request.getRemoteHost());
+        log.info("        remoteUser=" + request.getRemoteUser());
+        log.info("requestedSessionId=" + request.getRequestedSessionId());
+        log.info("            scheme=" + request.getScheme());
+        log.info("        serverName=" + request.getServerName());
+        log.info("        serverPort=" + request.getServerPort());
+        log.info("       servletPath=" + request.getServletPath());
+        log.info("          isSecure=" + request.isSecure());
+        log.info("---------------------------------------------------------------");
+
+        // Perform the request
+        getNext().invoke(request, response);
+
+        // Log post-service information
+        log.info("---------------------------------------------------------------");
+        log.info("          authType=" + request.getAuthType());
+        log.info("     contentLength=" + response.getContentLength());
+        log.info("       contentType=" + response.getContentType());
+        Cookie rcookies[] = response.getCookies();
+        for (int i = 0; i < rcookies.length; i++) {
+            log.info("            cookie=" + rcookies[i].getName() + "=" +
+                rcookies[i].getValue() + "; domain=" +
+                rcookies[i].getDomain() + "; path=" + rcookies[i].getPath());
+        }
+        String rhnames[] = response.getHeaderNames();
+        for (int i = 0; i < rhnames.length; i++) {
+            String rhvalues[] = response.getHeaderValues(rhnames[i]);
+            for (int j = 0; j < rhvalues.length; j++)
+                log.info("            header=" + rhnames[i] + "=" + rhvalues[j]);
+        }
+        log.info("           message=" + response.getMessage());
+        log.info("        remoteUser=" + request.getRemoteUser());
+        log.info("            status=" + response.getStatus());
+        log.info("===============================================================");
+
+    }
+
+
+    /**
+     * Return a String rendering of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("RequestDumperValve[");
+        if (container != null)
+            sb.append(container.getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/RequestFilterValve.java b/container/catalina/src/share/org/apache/catalina/valves/RequestFilterValve.java
new file mode 100644
index 0000000..27aa6d6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/RequestFilterValve.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+/**
+ * Implementation of a Valve that performs filtering based on comparing the
+ * appropriate request property (selected based on which subclass you choose
+ * to configure into your Container's pipeline) against a set of regular
+ * expressions configured for this Valve.
+ * <p>
+ * This valve is configured by setting the <code>allow</code> and/or
+ * <code>deny</code> properties to a comma-delimited list of regular
+ * expressions (in the syntax supported by the jakarta-regexp library) to
+ * which the appropriate request property will be compared.  Evaluation
+ * proceeds as follows:
+ * <ul>
+ * <li>The subclass extracts the request property to be filtered, and
+ *     calls the common <code>process()</code> method.
+ * <li>If there are any deny expressions configured, the property will
+ *     be compared to each such expression.  If a match is found, this
+ *     request will be rejected with a "Forbidden" HTTP response.</li>
+ * <li>If there are any allow expressions configured, the property will
+ *     be compared to each such expression.  If a match is found, this
+ *     request will be allowed to pass through to the next Valve in the
+ *     current pipeline.</li>
+ * <li>If one or more deny expressions was specified but no allow expressions,
+ *     allow this request to pass through (because none of the deny
+ *     expressions matched it).
+ * <li>The request will be rejected with a "Forbidden" HTTP response.</li>
+ * </ul>
+ * <p>
+ * This Valve may be attached to any Container, depending on the granularity
+ * of the filtering you wish to perform.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public abstract class RequestFilterValve
+    extends ValveBase {
+
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * JDK compatibility support
+     */
+    private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.RequestFilterValve/1.0";
+
+
+    /**
+     * The StringManager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The comma-delimited set of <code>allow</code> expressions.
+     */
+    protected String allow = null;
+
+
+    /**
+     * The set of <code>allow</code> regular expressions we will evaluate.
+     */
+    protected Pattern allows[] = new Pattern[0];
+
+
+    /**
+     * The set of <code>deny</code> regular expressions we will evaluate.
+     */
+    protected Pattern denies[] = new Pattern[0];
+
+
+    /**
+     * The comma-delimited set of <code>deny</code> expressions.
+     */
+    protected String deny = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return a comma-delimited set of the <code>allow</code> expressions
+     * configured for this Valve, if any; otherwise, return <code>null</code>.
+     */
+    public String getAllow() {
+
+        return (this.allow);
+
+    }
+
+
+    /**
+     * Set the comma-delimited set of the <code>allow</code> expressions
+     * configured for this Valve, if any.
+     *
+     * @param allow The new set of allow expressions
+     */
+    public void setAllow(String allow) {
+
+        this.allow = allow;
+        allows = precalculate(allow);
+
+    }
+
+
+    /**
+     * Return a comma-delimited set of the <code>deny</code> expressions
+     * configured for this Valve, if any; otherwise, return <code>null</code>.
+     */
+    public String getDeny() {
+
+        return (this.deny);
+
+    }
+
+
+    /**
+     * Set the comma-delimited set of the <code>deny</code> expressions
+     * configured for this Valve, if any.
+     *
+     * @param deny The new set of deny expressions
+     */
+    public void setDeny(String deny) {
+
+        this.deny = deny;
+        denies = precalculate(deny);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Extract the desired request property, and pass it (along with the
+     * specified request and response objects) to the protected
+     * <code>process()</code> method to perform the actual filtering.
+     * This method must be implemented by a concrete subclass.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public abstract void invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return an array of regular expression objects initialized from the
+     * specified argument, which must be <code>null</code> or a comma-delimited
+     * list of regular expression patterns.
+     *
+     * @param list The comma-separated list of patterns
+     *
+     * @exception IllegalArgumentException if one of the patterns has
+     *  invalid syntax
+     */
+    protected Pattern[] precalculate(String list) {
+
+        if (list == null)
+            return (new Pattern[0]);
+        list = list.trim();
+        if (list.length() < 1)
+            return (new Pattern[0]);
+        list += ",";
+
+        ArrayList reList = new ArrayList();
+        while (list.length() > 0) {
+            int comma = list.indexOf(',');
+            if (comma < 0)
+                break;
+            String pattern = list.substring(0, comma).trim();
+            try {
+                reList.add(Pattern.compile(pattern));
+            } catch (PatternSyntaxException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    (sm.getString("requestFilterValve.syntax", pattern));
+                jdkCompat.chainException(iae, e);
+                throw iae;
+            }
+            list = list.substring(comma + 1);
+        }
+
+        Pattern reArray[] = new Pattern[reList.size()];
+        return ((Pattern[]) reList.toArray(reArray));
+
+    }
+
+
+    /**
+     * Perform the filtering that has been configured for this Valve, matching
+     * against the specified request property.
+     *
+     * @param property The request property on which to filter
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be processed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    protected void process(String property,
+                           Request request, Response response)
+        throws IOException, ServletException {
+
+        // Check the deny patterns, if any
+        for (int i = 0; i < denies.length; i++) {
+            if (denies[i].matcher(property).matches()) {
+                response.sendError(HttpServletResponse.SC_FORBIDDEN);
+                return;
+            }
+        }
+
+        // Check the allow patterns, if any
+        for (int i = 0; i < allows.length; i++) {
+            if (allows[i].matcher(property).matches()) {
+                getNext().invoke(request, response);
+                return;
+            }
+        }
+
+        // Allow if denies specified but not allows
+        if ((denies.length > 0) && (allows.length == 0)) {
+            getNext().invoke(request, response);
+            return;
+        }
+
+        // Deny this request
+        response.sendError(HttpServletResponse.SC_FORBIDDEN);
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/SemaphoreValve.java b/container/catalina/src/share/org/apache/catalina/valves/SemaphoreValve.java
new file mode 100644
index 0000000..cf5650c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/SemaphoreValve.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 1999-2001,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.IOException;
+import java.util.concurrent.Semaphore;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * <p>Implementation of a Valve that limits concurrency.</p>
+ *
+ * <p>This Valve may be attached to any Container, depending on the granularity
+ * of the concurrency control you wish to perform.</p>
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class SemaphoreValve
+    extends ValveBase
+    implements Lifecycle {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.SemaphoreValve/1.0";
+
+
+    /**
+     * The string manager for this package.
+     */
+    private StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Semaphore.
+     */
+    protected Semaphore semaphore = null;
+    
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+    
+    /**
+     * Concurrency level of the semaphore.
+     */
+    protected int concurrency = 10;
+    public int getConcurrency() { return concurrency; }
+    public void setConcurrency(int concurrency) { this.concurrency = concurrency; }
+    
+
+    /**
+     * Fairness of the semaphore.
+     */
+    protected boolean fairness = false;
+    public boolean getFairness() { return fairness; }
+    public void setFairness(boolean fairness) { this.fairness = fairness; }
+    
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (sm.getString("semaphoreValve.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        semaphore = new Semaphore(concurrency, fairness);
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("semaphoreValve.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        semaphore = null;
+
+    }
+
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+
+    /**
+     * Do concurrency control on the request using the semaphore.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        try {
+            semaphore.acquireUninterruptibly();
+            // Perform the request
+            getNext().invoke(request, response);
+        } finally {
+            semaphore.release();
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/ValveBase.java b/container/catalina/src/share/org/apache/catalina/valves/ValveBase.java
new file mode 100644
index 0000000..67fd84c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/ValveBase.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.valves;
+
+
+import java.io.IOException;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Contained;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Valve;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Convenience base class for implementations of the <b>Valve</b> interface.
+ * A subclass <strong>MUST</strong> implement an <code>invoke()</code>
+ * method to provide the required functionality, and <strong>MAY</strong>
+ * implement the <code>Lifecycle</code> interface to provide configuration
+ * management and lifecycle support.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public abstract class ValveBase
+    implements Contained, Valve, MBeanRegistration {
+    private static Log log = LogFactory.getLog(ValveBase.class);
+
+    //------------------------------------------------------ Instance Variables
+
+
+    /**
+     * The Container whose pipeline this Valve is a component of.
+     */
+    protected Container container = null;
+
+
+    /**
+     * Container log
+     */
+    protected Log containerLog = null;
+
+
+    /**
+     * Descriptive information about this Valve implementation.  This value
+     * should be overridden by subclasses.
+     */
+    protected static String info =
+        "org.apache.catalina.core.ValveBase/1.0";
+
+
+    /**
+     * The next Valve in the pipeline this Valve is a component of.
+     */
+    protected Valve next = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected final static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    //-------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Container with which this Valve is associated, if any.
+     */
+    public Container getContainer() {
+
+        return (container);
+
+    }
+
+
+    /**
+     * Set the Container with which this Valve is associated, if any.
+     *
+     * @param container The new associated container
+     */
+    public void setContainer(Container container) {
+
+        this.container = container;
+
+    }
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the next Valve in this pipeline, or <code>null</code> if this
+     * is the last Valve in the pipeline.
+     */
+    public Valve getNext() {
+
+        return (next);
+
+    }
+
+
+    /**
+     * Set the Valve that follows this one in the pipeline it is part of.
+     *
+     * @param valve The new next valve
+     */
+    public void setNext(Valve valve) {
+
+        this.next = valve;
+
+    }
+
+
+    //---------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess() {
+    }
+
+
+    /**
+     * The implementation-specific logic represented by this Valve.  See the
+     * Valve description for the normal design patterns for this method.
+     * <p>
+     * This method <strong>MUST</strong> be provided by a subclass.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public abstract void invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+
+    /**
+     * Return a String rendering of this object.
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer(this.getClass().getName());
+        sb.append("[");
+        if (container != null)
+            sb.append(container.getName());
+        sb.append("]");
+        return (sb.toString());
+    }
+
+
+    // -------------------- JMX and Registration  --------------------
+    protected String domain;
+    protected ObjectName oname;
+    protected MBeanServer mserver;
+    protected ObjectName controller;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public void setObjectName(ObjectName oname) {
+        this.oname = oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+
+
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+
+    public ObjectName getController() {
+        return controller;
+    }
+
+    public void setController(ObjectName controller) {
+        this.controller = controller;
+    }
+
+    /** From the name, extract the parent object name
+     *
+     * @param valveName The valve name
+     * @return ObjectName The parent name
+     */
+    public ObjectName getParentName( ObjectName valveName ) {
+
+        return null;
+    }
+
+    public ObjectName createObjectName(String domain, ObjectName parent)
+            throws MalformedObjectNameException
+    {
+        Container container=this.getContainer();
+        if( container == null || ! (container instanceof ContainerBase) )
+            return null;
+        this.containerLog = container.getLogger();
+        ContainerBase containerBase=(ContainerBase)container;
+        Pipeline pipe=containerBase.getPipeline();
+        Valve valves[]=pipe.getValves();
+
+        /* Compute the "parent name" part */
+        String parentName="";
+        if (container instanceof Engine) {
+        } else if (container instanceof Host) {
+            parentName=",host=" +container.getName();
+        } else if (container instanceof Context) {
+                    String path = ((Context)container).getPath();
+                    if (path.length() < 1) {
+                        path = "/";
+                    }
+                    Host host = (Host) container.getParent();
+                    parentName=",path=" + path + ",host=" +
+                            host.getName();
+        } else if (container instanceof Wrapper) {
+            Context ctx = (Context) container.getParent();
+            String path = ctx.getPath();
+            if (path.length() < 1) {
+                path = "/";
+            }
+            Host host = (Host) ctx.getParent();
+            parentName=",servlet=" + container.getName() +
+                    ",path=" + path + ",host=" + host.getName();
+        }
+        log.debug("valve parent=" + parentName + " " + parent);
+
+        String className=this.getClass().getName();
+        int period = className.lastIndexOf('.');
+        if (period >= 0)
+            className = className.substring(period + 1);
+
+        int seq=0;
+        for( int i=0; i<valves.length; i++ ) {
+            // Find other valves with the same name
+            if (valves[i] == this) {
+                break;
+            }
+            if( valves[i]!=null &&
+                    valves[i].getClass() == this.getClass() ) {
+                log.debug("Duplicate " + valves[i] + " " + this + " " + container);
+                seq++;
+            }
+        }
+        String ext="";
+        if( seq > 0 ) {
+            ext=",seq=" + seq;
+        }
+
+        ObjectName objectName = 
+            new ObjectName( domain + ":type=Valve,name=" + className + ext + parentName);
+        log.debug("valve objectname = "+objectName);
+        return objectName;
+    }
+
+    // -------------------- JMX data  --------------------
+
+    public ObjectName getContainerName() {
+        if( container== null) return null;
+        return ((ContainerBase)container).getJmxName();
+    }
+}
diff --git a/container/catalina/src/share/org/apache/catalina/valves/mbeans-descriptors.xml b/container/catalina/src/share/org/apache/catalina/valves/mbeans-descriptors.xml
new file mode 100644
index 0000000..2c1d3db
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/mbeans-descriptors.xml
@@ -0,0 +1,348 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean name="AccessLogValve"
+         description="Valve that generates a web server access log"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.AccessLogValve">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute name="directory"
+               description="The directory in which log files are created"
+               type="java.lang.String"/>
+
+    <attribute   name="pattern"
+               description="The pattern used to format our access log lines"
+               type="java.lang.String"/>
+
+    <attribute name="prefix"
+               description="The prefix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="resolveHosts"
+               description="Resolve hosts"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="rotatable"
+               description="Flag to indicate automatic log rotation."
+               is="true"
+               type="boolean"/>
+
+    <attribute name="suffix"
+               description="The suffix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="condition"
+               description="The value to look for conditional logging."
+               type="java.lang.String"/>
+
+    <attribute name="fileDateFormat"
+               description="The format for the date date based log rotation."
+               type="java.lang.String"/>
+  </mbean>
+
+  <mbean name="ByteBufferAccessLogValve"
+           description="Valve that generates asynchronously web server access log"
+           domain="Catalina"
+           group="Valve"
+           type="org.apache.catalina.valves.ByteBufferAccessLogValve">
+  
+      <attribute name="className"
+                 description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+                 writeable="false"/>
+  
+      <attribute name="containerName"
+                 description="Object name of the container"
+                 type="javax.management.ObjectName"/>
+  
+      <attribute name="directory"
+                 description="The directory in which log files are created"
+                 type="java.lang.String"/>
+  
+      <attribute   name="pattern"
+                 description="The pattern used to format our access log lines, which must be either common or combined"
+                 type="java.lang.String"/>
+  
+      <attribute name="prefix"
+                 description="The prefix that is added to log file filenames"
+                 type="java.lang.String"/>
+  
+      <attribute name="resolveHosts"
+                 description="Resolve hosts"
+                 is="true"
+                 type="boolean"/>
+  
+      <attribute name="rotatable"
+                 description="Flag to indicate automatic log rotation."
+                 is="true"
+                 type="boolean"/>
+  
+      <attribute name="suffix"
+                 description="The suffix that is added to log file filenames"
+                 type="java.lang.String"/>
+  
+      <attribute name="condition"
+                 description="The value to look for conditional logging."
+                 type="java.lang.String"/>
+  
+      <attribute name="fileDateFormat"
+                 description="The format for the date date based log rotation."
+                 type="java.lang.String"/>
+                 
+      <attribute name="byteBufferSize"
+                 description="The size of the byte buffer used to store the log."
+                 type="java.lang.String"/>
+    </mbean>
+  
+  
+  
+  <mbean name="ErrorReportValve"
+         description="Implementation of a Valve that outputs HTML error pages"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.ErrorReportValve">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+  </mbean>
+
+  <mbean name="ExtendedAccessLogValve"
+         description="Valve that generates a web server access log"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.ExtendedAccessLogValve">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute name="directory"
+               description="The directory in which log files are created"
+               type="java.lang.String"/>
+
+    <attribute   name="pattern"
+               description="The pattern used to format our access log lines"
+               type="java.lang.String"/>
+
+    <attribute name="prefix"
+               description="The prefix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="rotatable"
+               description="Rotate log"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="condition"
+               description="The value to look for conditional logging."
+               type="java.lang.String"/>
+
+    <attribute name="checkExists"
+               description="Check for file existence before each logging."
+               is="true"
+               type="boolean"/>
+
+    <attribute name="suffix"
+               description="The suffix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="fileDateFormat"
+               description="The format for the date date based log rotation."
+               type="java.lang.String"/>
+
+    <operation name="rotate"
+               description="Move the existing log file to a new name"
+               impact="ACTION"
+               returnType="boolean">
+      <parameter name="newFileName"
+                 description="File name to move the log file to."
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+
+  <mbean name="FastCommonAccessLogValve"
+         description="Valve that generates a web server access log"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.FastCommonAccessLogValve">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute name="directory"
+               description="The directory in which log files are created"
+               type="java.lang.String"/>
+
+    <attribute   name="pattern"
+               description="The pattern used to format our access log lines, which must be either common or combined"
+               type="java.lang.String"/>
+
+    <attribute name="prefix"
+               description="The prefix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="resolveHosts"
+               description="Resolve hosts"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="rotatable"
+               description="Flag to indicate automatic log rotation."
+               is="true"
+               type="boolean"/>
+
+    <attribute name="suffix"
+               description="The suffix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="condition"
+               description="The value to look for conditional logging."
+               type="java.lang.String"/>
+
+    <attribute name="fileDateFormat"
+               description="The format for the date date based log rotation."
+               type="java.lang.String"/>
+  </mbean>
+
+  <mbean name="SemaphoreValve"
+         description="Valve that does concurrency control"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.SemaphoreValve">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute name="concurrency"
+               description="Desired concurrency level"
+               type="int"/>
+
+    <attribute name="fairness"
+               description="Use a fair semaphore"
+               type="boolean"/>
+
+  </mbean>
+
+  <mbean name="RemoteAddrValve"
+         description="Concrete implementation of RequestFilterValve that  filters based on the string representation of the remote client's IP address"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.RemoteAddrValve">
+
+    <attribute name="allow"
+               description="The comma-delimited set of allow expressions"
+               type="java.lang.String"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute   name="deny"
+               description="The comma-delimited set of deny expressions"
+               type="java.lang.String"/>
+
+  </mbean>
+
+  <mbean name="RemoteHostValve"
+         description="Concrete implementation of RequestFilterValve that
+         filters based on the string representation of the remote
+         client's host name"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.RemoteHostValve">
+
+    <attribute   name="allow"
+               description="The comma-delimited set of allow expressions"
+               type="java.lang.String"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute   name="deny"
+               description="The comma-delimited set of deny expressions"
+               type="java.lang.String"/>
+
+  </mbean>
+
+  <mbean name="RequestDumperValve"
+         description="Implementation of a Valve that logs interesting contents from the specified Request and the corresponding Response"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.RequestDumperValve">
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+  </mbean>
+
+  <mbean name="RequestListenerValve"
+         description="Valve that handles request initialization and destroy events"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.RequestListenerValve">
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/container/catalina/src/share/org/apache/catalina/valves/package.html b/container/catalina/src/share/org/apache/catalina/valves/package.html
new file mode 100644
index 0000000..23cd0ac
--- /dev/null
+++ b/container/catalina/src/share/org/apache/catalina/valves/package.html
@@ -0,0 +1,12 @@
+<body>
+
+<p>This package contains a variety of small Valve implementations that do
+not warrant being packaged separately.  In addition, there is a convenience
+base class (<code>ValveBase</code>) that supports the usual mechanisms for
+including custom Valves into the corresponding Pipeline.</p>
+
+<p>Other packages that include Valves include
+<code>org.apache.tomcat.logger</code> and
+<code>org.apache.tomcat.security</code>.</p>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/naming/Constants.java b/container/catalina/src/share/org/apache/naming/Constants.java
new file mode 100644
index 0000000..127c8e6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/Constants.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+
+/**
+ * Static constants for this package.
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.naming";
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/ContextAccessController.java b/container/catalina/src/share/org/apache/naming/ContextAccessController.java
new file mode 100644
index 0000000..fb50f43
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/ContextAccessController.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.util.Hashtable;
+
+/**
+ * Handles the access control on the JNDI contexts.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ContextAccessController {
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Catalina context names on which writing is not allowed.
+     */
+    private static Hashtable readOnlyContexts = new Hashtable();
+
+
+    /**
+     * Security tokens repository.
+     */
+    private static Hashtable securityTokens = new Hashtable();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Set a security token for a context. Can be set only once.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void setSecurityToken(Object name, Object token) {
+        if ((!securityTokens.containsKey(name)) && (token != null)) {
+            securityTokens.put(name, token);
+        }
+    }
+
+
+    /**
+     * Remove a security token for a context.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void unsetSecurityToken(Object name, Object token) {
+        if (checkSecurityToken(name, token)) {
+            securityTokens.remove(name);
+        }
+    }
+
+
+    /**
+     * Check a submitted security token. The submitted token must be equal to
+     * the token present in the repository. If no token is present for the 
+     * context, then returns true.
+     * 
+     * @param name Name of the context
+     * @param token Submitted security token
+     */
+    public static boolean checkSecurityToken
+        (Object name, Object token) {
+        Object refToken = securityTokens.get(name);
+        if (refToken == null)
+            return (true);
+        if ((refToken != null) && (refToken.equals(token)))
+            return (true);
+        return (false);
+    }
+
+
+    /**
+     * Allow writing to a context.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void setWritable(Object name, Object token) {
+        if (checkSecurityToken(name, token))
+            readOnlyContexts.remove(name);
+    }
+
+
+    /**
+     * Set whether or not a context is writable.
+     * 
+     * @param name Name of the context
+     */
+    public static void setReadOnly(Object name) {
+        readOnlyContexts.put(name, name);
+    }
+
+
+    /**
+     * Returns if a context is writable.
+     * 
+     * @param name Name of the context
+     */
+    public static boolean isWritable(Object name) {
+        return !(readOnlyContexts.containsKey(name));
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/ContextBindings.java b/container/catalina/src/share/org/apache/naming/ContextBindings.java
new file mode 100644
index 0000000..68b3bd3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/ContextBindings.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.util.Hashtable;
+import javax.naming.NamingException;
+import javax.naming.Context;
+
+/**
+ * Handles the associations :
+ * <ul>
+ * <li>Catalina context name with the NamingContext</li>
+ * <li>Calling thread with the NamingContext</li>
+ * </ul>
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ContextBindings {
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Bindings name - naming context. Keyed by name.
+     */
+    private static Hashtable contextNameBindings = new Hashtable();
+
+
+    /**
+     * Bindings thread - naming context. Keyed by thread id.
+     */
+    private static Hashtable threadBindings = new Hashtable();
+
+
+    /**
+     * Bindings thread - name. Keyed by thread id.
+     */
+    private static Hashtable threadNameBindings = new Hashtable();
+
+
+    /**
+     * Bindings class loader - naming context. Keyed by CL id.
+     */
+    private static Hashtable clBindings = new Hashtable();
+
+
+    /**
+     * Bindings class loader - name. Keyed by CL id.
+     */
+    private static Hashtable clNameBindings = new Hashtable();
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm = 
+        StringManager.getManager(Constants.Package);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Binds a context name.
+     * 
+     * @param name Name of the context
+     * @param context Associated naming context instance
+     */
+    public static void bindContext(Object name, Context context) {
+        bindContext(name, context, null);
+    }
+
+
+    /**
+     * Binds a context name.
+     * 
+     * @param name Name of the context
+     * @param context Associated naming context instance
+     * @param token Security token
+     */
+    public static void bindContext(Object name, Context context, 
+                                   Object token) {
+        if (ContextAccessController.checkSecurityToken(name, token))
+            contextNameBindings.put(name, context);
+    }
+
+
+    /**
+     * Unbind context name.
+     * 
+     * @param name Name of the context
+     */
+    public static void unbindContext(Object name) {
+        unbindContext(name, null);
+    }
+
+
+    /**
+     * Unbind context name.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void unbindContext(Object name, Object token) {
+        if (ContextAccessController.checkSecurityToken(name, token))
+            contextNameBindings.remove(name);
+    }
+
+
+    /**
+     * Retrieve a naming context.
+     * 
+     * @param name Name of the context
+     */
+    static Context getContext(Object name) {
+        return (Context) contextNameBindings.get(name);
+    }
+
+
+    /**
+     * Binds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     */
+    public static void bindThread(Object name) 
+        throws NamingException {
+        bindThread(name, null);
+    }
+
+
+    /**
+     * Binds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void bindThread(Object name, Object token) 
+        throws NamingException {
+        if (ContextAccessController.checkSecurityToken(name, token)) {
+            Context context = (Context) contextNameBindings.get(name);
+            if (context == null)
+                throw new NamingException
+                    (sm.getString("contextBindings.unknownContext", name));
+            threadBindings.put(Thread.currentThread(), context);
+            threadNameBindings.put(Thread.currentThread(), name);
+        }
+    }
+
+
+    /**
+     * Unbinds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     */
+    public static void unbindThread(Object name) {
+        unbindThread(name, null);
+    }
+
+
+    /**
+     * Unbinds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void unbindThread(Object name, Object token) {
+        if (ContextAccessController.checkSecurityToken(name, token)) {
+            threadBindings.remove(Thread.currentThread());
+            threadNameBindings.remove(Thread.currentThread());
+        }
+    }
+
+
+    /**
+     * Retrieves the naming context bound to a thread.
+     */
+    public static Context getThread()
+        throws NamingException {
+        Context context = 
+            (Context) threadBindings.get(Thread.currentThread());
+        if (context == null)
+            throw new NamingException
+                (sm.getString("contextBindings.noContextBoundToThread"));
+        return context;
+    }
+
+
+    /**
+     * Retrieves the naming context name bound to a thread.
+     */
+    static Object getThreadName()
+        throws NamingException {
+        Object name = threadNameBindings.get(Thread.currentThread());
+        if (name == null)
+            throw new NamingException
+                (sm.getString("contextBindings.noContextBoundToThread"));
+        return name;
+    }
+
+
+    /**
+     * Tests if current thread is bound to a context.
+     */
+    public static boolean isThreadBound() {
+        return (threadBindings.containsKey(Thread.currentThread()));
+    }
+
+
+    /**
+     * Binds a naming context to a class loader.
+     * 
+     * @param name Name of the context
+     */
+    public static void bindClassLoader(Object name) 
+        throws NamingException {
+        bindClassLoader(name, null);
+    }
+
+
+    /**
+     * Binds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void bindClassLoader(Object name, Object token) 
+        throws NamingException {
+        bindClassLoader
+            (name, token, Thread.currentThread().getContextClassLoader());
+    }
+
+
+    /**
+     * Binds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void bindClassLoader(Object name, Object token, 
+                                       ClassLoader classLoader) 
+        throws NamingException {
+        if (ContextAccessController.checkSecurityToken(name, token)) {
+            Context context = (Context) contextNameBindings.get(name);
+            if (context == null)
+                throw new NamingException
+                    (sm.getString("contextBindings.unknownContext", name));
+            clBindings.put(classLoader, context);
+            clNameBindings.put(classLoader, name);
+        }
+    }
+
+
+    /**
+     * Unbinds a naming context to a class loader.
+     * 
+     * @param name Name of the context
+     */
+    public static void unbindClassLoader(Object name) {
+        unbindClassLoader(name, null);
+    }
+
+
+    /**
+     * Unbinds a naming context to a class loader.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void unbindClassLoader(Object name, Object token) {
+        unbindClassLoader(name, token, 
+                          Thread.currentThread().getContextClassLoader());
+    }
+
+
+    /**
+     * Unbinds a naming context to a class loader.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void unbindClassLoader(Object name, Object token, 
+                                         ClassLoader classLoader) {
+        if (ContextAccessController.checkSecurityToken(name, token)) {
+            Object n = clNameBindings.get(classLoader);
+            if ((n==null) || !(n.equals(name))) {
+                return;
+            }
+            clBindings.remove(classLoader);
+            clNameBindings.remove(classLoader);
+        }
+    }
+
+
+    /**
+     * Retrieves the naming context bound to a class loader.
+     */
+    public static Context getClassLoader()
+        throws NamingException {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        Context context = null;
+        do {
+            context = (Context) clBindings.get(cl);
+            if (context != null) {
+                return context;
+            }
+        } while ((cl = cl.getParent()) != null);
+        throw new NamingException
+            (sm.getString("contextBindings.noContextBoundToCL"));
+    }
+
+
+    /**
+     * Retrieves the naming context name bound to a class loader.
+     */
+    static Object getClassLoaderName()
+        throws NamingException {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        Object name = null;
+        do {
+            name = clNameBindings.get(cl);
+            if (name != null) {
+                return name;
+            }
+        } while ((cl = cl.getParent()) != null);
+        throw new NamingException
+            (sm.getString("contextBindings.noContextBoundToCL"));
+    }
+
+
+    /**
+     * Tests if current class loader is bound to a context.
+     */
+    public static boolean isClassLoaderBound() {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        do {
+            if (clBindings.containsKey(cl)) {
+                return true;
+            }
+        } while ((cl = cl.getParent()) != null);
+        return false;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/EjbRef.java b/container/catalina/src/share/org/apache/naming/EjbRef.java
new file mode 100644
index 0000000..5758cb8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/EjbRef.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import javax.naming.Context;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+
+/**
+ * Represents a reference address to an EJB.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class EjbRef
+    extends Reference {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Default factory for this reference.
+     */
+    public static final String DEFAULT_FACTORY = 
+        org.apache.naming.factory.Constants.DEFAULT_EJB_FACTORY;
+
+
+    /**
+     * EJB type address type.
+     */
+    public static final String TYPE = "type";
+
+
+    /**
+     * Remote interface classname address type.
+     */
+    public static final String REMOTE = "remote";
+
+
+    /**
+     * Link address type.
+     */
+    public static final String LINK = "link";
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * EJB Reference.
+     * 
+     * @param ejbType EJB type
+     * @param home Home interface classname
+     * @param remote Remote interface classname
+     * @param link EJB link
+     */
+    public EjbRef(String ejbType, String home, String remote, String link) {
+        this(ejbType, home, remote, link, null, null);
+    }
+
+
+    /**
+     * EJB Reference.
+     * 
+     * @param ejbType EJB type
+     * @param home Home interface classname
+     * @param remote Remote interface classname
+     * @param link EJB link
+     */
+    public EjbRef(String ejbType, String home, String remote, String link,
+                  String factory, String factoryLocation) {
+        super(home, factory, factoryLocation);
+        StringRefAddr refAddr = null;
+        if (ejbType != null) {
+            refAddr = new StringRefAddr(TYPE, ejbType);
+            add(refAddr);
+        }
+        if (remote != null) {
+            refAddr = new StringRefAddr(REMOTE, remote);
+            add(refAddr);
+        }
+        if (link != null) {
+            refAddr = new StringRefAddr(LINK, link);
+            add(refAddr);
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // -------------------------------------------------------- RefAddr Methods
+
+
+    // ------------------------------------------------------ Reference Methods
+
+
+    /**
+     * Retrieves the class name of the factory of the object to which this 
+     * reference refers.
+     */
+    public String getFactoryClassName() {
+        String factory = super.getFactoryClassName();
+        if (factory != null) {
+            return factory;
+        } else {
+            factory = System.getProperty(Context.OBJECT_FACTORIES);
+            if (factory != null) {
+                return null;
+            } else {
+                return DEFAULT_FACTORY;
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/JndiPermission.java b/container/catalina/src/share/org/apache/naming/JndiPermission.java
new file mode 100644
index 0000000..834e811
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/JndiPermission.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.security.BasicPermission;
+
+/**
+ * Java SecurityManager Permission class for JNDI name based file resources
+ * <p>
+ * The JndiPermission extends the BasicPermission.
+ * The permission name is a full or partial jndi resource name.
+ * An * can be used at the end of the name to match all named
+ * resources that start with name.  There are no actions.</p>
+ * <p>
+ * Example that grants permission to read all JNDI file based resources:
+ * <li> permission org.apache.naming.JndiPermission "*";</li>
+ * </p>
+ *
+ * @author Glenn Nielsen
+ * @version $Revision$ $Date$
+ */
+
+public final class JndiPermission extends BasicPermission {
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Creates a new JndiPermission with no actions
+     *
+     * @param name - JNDI resource path name
+     */
+    public JndiPermission(String name) {
+        super(name);
+    }
+
+    /**
+     * Creates a new JndiPermission with actions
+     *
+     * @param name - JNDI resource path name
+     * @param actions - JNDI actions (none defined)
+     */
+    public JndiPermission(String name, String actions) {
+        super(name,actions);
+    }
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/LocalStrings.properties b/container/catalina/src/share/org/apache/naming/LocalStrings.properties
new file mode 100644
index 0000000..254f572
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/LocalStrings.properties
@@ -0,0 +1,11 @@
+contextBindings.unknownContext=Unknown context name : {0}
+contextBindings.noContextBoundToThread=No naming context bound to this thread
+contextBindings.noContextBoundToCL=No naming context bound to this class loader
+selectorContext.noJavaUrl=This context must be accessed throught a java: URL
+namingContext.contextExpected=Name is not bound to a Context
+namingContext.failResolvingReference=Unexpected exception resolving reference
+namingContext.nameNotBound=Name {0} is not bound in this Context
+namingContext.readOnly=Context is read only
+namingContext.invalidName=Name is not valid
+namingContext.alreadyBound=Name {0} is already bound in this Context
+namingContext.noAbsoluteName=Can''t generate an absolute name for this namespace
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/naming/LocalStrings_es.properties b/container/catalina/src/share/org/apache/naming/LocalStrings_es.properties
new file mode 100644
index 0000000..ec2c2b6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/LocalStrings_es.properties
@@ -0,0 +1,16 @@
+# $Id$
+
+# language es
+
+# package org.apache.naming
+
+
+contextBindings.unknownContext=Contexto {0} desconocido 
+contextBindings.noContextBoundToThread=No hay contexto de nombres asociado a este hilo
+selectorContext.noJavaUrl=Este contexto debe de ser accedido a traves de una URL de tipo java:
+namingContext.contextExpected=El nombre no esta asociado a ningun Contexto
+namingContext.nameNotBound=El nombre {0} no este asociado a este contexto
+namingContext.readOnly=El contexto es de solo lectura
+namingContext.invalidName=Nombre no valido
+namingContext.noAbsoluteName=No se puede generar un nombre absoluto para este espacio de nombres
+namingContext.alreadyBound=El nombre {0} este ya asociado en este Contexto
diff --git a/container/catalina/src/share/org/apache/naming/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/naming/LocalStrings_fr.properties
new file mode 100644
index 0000000..911a602
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/LocalStrings_fr.properties
@@ -0,0 +1,11 @@
+contextBindings.unknownContext=Nom de Contexte inconnu : {0}
+contextBindings.noContextBoundToThread=Aucun Contexte de nommage lié à ce thread
+contextBindings.noContextBoundToCL=Aucun Contexte de nommage lié à ce chargeur de classes
+selectorContext.noJavaUrl=Ce Contexte doit être accédé par une java: URL
+namingContext.contextExpected=Le Nom n''est pas lié à un Contexte
+namingContext.failResolvingReference=Une erreur s est produite durant la résolution de la référence
+namingContext.nameNotBound=Le Nom {0} n''est pas lié à ce Contexte
+namingContext.readOnly=Le Contexte est en lecture seule
+namingContext.invalidName=Le Nom est invalide
+namingContext.alreadyBound=Le Nom {0} est déjà lié à ce Contexte
+namingContext.noAbsoluteName=Impossible de générer un nom absolu pour cet espace de nommage (namespace)
\ No newline at end of file
diff --git a/container/catalina/src/share/org/apache/naming/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/naming/LocalStrings_ja.properties
new file mode 100644
index 0000000..7d07efd
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/LocalStrings_ja.properties
@@ -0,0 +1,11 @@
+contextBindings.unknownContext=\u672a\u77e5\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u540d\u3067\u3059: {0}
+contextBindings.noContextBoundToThread=\u540d\u524d\u4ed8\u3051\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306f\u3053\u306e\u30b9\u30ec\u30c3\u30c9\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+contextBindings.noContextBoundToCL=\u540d\u524d\u4ed8\u3051\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306f\u3053\u306e\u30af\u30e9\u30b9\u30ed\u30fc\u30c0\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+selectorContext.noJavaUrl=\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u306fjava: URL\u3092\u7528\u3044\u3066\u30a2\u30af\u30bb\u30b9\u3055\u308c\u306d\u3070\u3044\u3051\u307e\u305b\u3093
+namingContext.contextExpected=\u540d\u524d\u304c\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+namingContext.failResolvingReference=\u53c2\u7167\u306e\u89e3\u6c7a\u4e2d\u306b\u4e88\u6e2c\u3057\u306a\u3044\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+namingContext.nameNotBound=\u540d\u524d {0} \u306f\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+namingContext.readOnly=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306f\u30ea\u30fc\u30c9\u30aa\u30f3\u30ea\u30fc\u3067\u3059
+namingContext.invalidName=\u540d\u524d\u306f\u7121\u52b9\u3067\u3059
+namingContext.alreadyBound=\u540d\u524d {0} \u306f\u65e2\u306b\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u3059
+namingContext.noAbsoluteName=\u3053\u306e\u540d\u524d\u7a7a\u9593\u306b\u7d76\u5bfe\u540d\u3092\u751f\u6210\u3067\u304d\u307e\u305b\u3093
diff --git a/container/catalina/src/share/org/apache/naming/NameParserImpl.java b/container/catalina/src/share/org/apache/naming/NameParserImpl.java
new file mode 100644
index 0000000..6616e42
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/NameParserImpl.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import javax.naming.NameParser;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.CompositeName;
+
+/**
+ * Parses names.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class NameParserImpl 
+    implements NameParser {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ----------------------------------------------------- NameParser Methods
+
+
+    /**
+     * Parses a name into its components.
+     * 
+     * @param name The non-null string name to parse
+     * @return A non-null parsed form of the name using the naming convention 
+     * of this parser.
+     */
+    public Name parse(String name)
+        throws NamingException {
+        return new CompositeName(name);
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/NamingContext.java b/container/catalina/src/share/org/apache/naming/NamingContext.java
new file mode 100644
index 0000000..0c72267
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/NamingContext.java
@@ -0,0 +1,907 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.LinkRef;
+import javax.naming.CompositeName;
+import javax.naming.NameParser;
+import javax.naming.Referenceable;
+import javax.naming.Reference;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NotContextException;
+import javax.naming.InitialContext;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.spi.NamingManager;
+
+/**
+ * Catalina JNDI Context implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+public class NamingContext implements Context {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Name parser for this context.
+     */
+    protected static final NameParser nameParser = new NameParserImpl();
+
+
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(NamingContext.class);
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Builds a naming context using the given environment.
+     */
+    public NamingContext(Hashtable env, String name) 
+        throws NamingException {
+        this.bindings = new HashMap();
+        this.env = new Hashtable();
+        // FIXME ? Could be put in the environment ?
+        this.name = name;
+        // Populating the environment hashtable
+        if (env != null ) {
+            Enumeration envEntries = env.keys();
+            while (envEntries.hasMoreElements()) {
+                String entryName = (String) envEntries.nextElement();
+                addToEnvironment(entryName, env.get(entryName));
+            }
+        }
+    }
+
+
+    /**
+     * Builds a naming context using the given environment.
+     */
+    public NamingContext(Hashtable env, String name, HashMap bindings) 
+        throws NamingException {
+        this(env, name);
+        this.bindings = bindings;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Environment.
+     */
+    protected Hashtable env;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Bindings in this Context.
+     */
+    protected HashMap bindings;
+
+
+    /**
+     * Name of the associated Catalina Context.
+     */
+    protected String name;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Retrieves the named object. If name is empty, returns a new instance 
+     * of this context (which represents the same naming context as this 
+     * context, but its environment may be modified independently and it may 
+     * be accessed concurrently).
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(Name name)
+        throws NamingException {
+        return lookup(name, true);
+    }
+
+
+    /**
+     * Retrieves the named object.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(String name)
+        throws NamingException {
+        return lookup(new CompositeName(name), true);
+    }
+
+
+    /**
+     * Binds a name to an object. All intermediate contexts and the target 
+     * context (that named by all but terminal atomic component of the name) 
+     * must already exist.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj)
+        throws NamingException {
+        bind(name, obj, false);
+    }
+
+
+    /**
+     * Binds a name to an object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj)
+        throws NamingException {
+        bind(new CompositeName(name), obj);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding. All 
+     * intermediate contexts and the target context (that named by all but 
+     * terminal atomic component of the name) must already exist.
+     * <p>
+     * If the object is a DirContext, any existing attributes associated with 
+     * the name are replaced with those of the object. Otherwise, any 
+     * existing attributes associated with the name remain unchanged.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj)
+        throws NamingException {
+        bind(name, obj, true);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj)
+        throws NamingException {
+        rebind(new CompositeName(name), obj);
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(Name name)
+        throws NamingException {
+        checkWritable();
+        
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        if (name.isEmpty())
+            throw new NamingException
+                (sm.getString("namingContext.invalidName"));
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (entry == null) {
+            throw new NameNotFoundException
+                (sm.getString("namingContext.nameNotBound", name.get(0)));
+        }
+        
+        if (name.size() > 1) {
+            if (entry.type == NamingEntry.CONTEXT) {
+                ((Context) entry.value).unbind(name.getSuffix(1));
+            } else {
+                throw new NamingException
+                    (sm.getString("namingContext.contextExpected"));
+            }
+        } else {
+            bindings.remove(name.get(0));
+        }
+        
+    }
+
+
+    /**
+     * Unbinds the named object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(String name)
+        throws NamingException {
+        unbind(new CompositeName(name));
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name. Both names are relative to this context. Any attributes 
+     * associated with the old name become associated with the new name. 
+     * Intermediate contexts of the old name are not changed.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(Name oldName, Name newName)
+        throws NamingException {
+        Object value = lookup(oldName);
+        bind(newName, value);
+        unbind(oldName);
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(String oldName, String newName)
+        throws NamingException {
+        rename(new CompositeName(oldName), new CompositeName(newName));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name name)
+        throws NamingException {
+        // Removing empty parts
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        if (name.isEmpty()) {
+            return new NamingContextEnumeration(bindings.values().iterator());
+        }
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (entry == null) {
+            throw new NameNotFoundException
+                (sm.getString("namingContext.nameNotBound", name.get(0)));
+        }
+        
+        if (entry.type != NamingEntry.CONTEXT) {
+            throw new NamingException
+                (sm.getString("namingContext.contextExpected"));
+        }
+        return ((Context) entry.value).list(name.getSuffix(1));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(String name)
+        throws NamingException {
+        return list(new CompositeName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+        throws NamingException {
+        // Removing empty parts
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        if (name.isEmpty()) {
+            return new NamingContextBindingsEnumeration(bindings.values().iterator());
+        }
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (entry == null) {
+            throw new NameNotFoundException
+                (sm.getString("namingContext.nameNotBound", name.get(0)));
+        }
+        
+        if (entry.type != NamingEntry.CONTEXT) {
+            throw new NamingException
+                (sm.getString("namingContext.contextExpected"));
+        }
+        return ((Context) entry.value).listBindings(name.getSuffix(1));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(String name)
+        throws NamingException {
+        return listBindings(new CompositeName(name));
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any 
+     * attributes associated with the name are also removed. Intermediate 
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * In a federated naming system, a context from one naming system may be 
+     * bound to a name in another. One can subsequently look up and perform 
+     * operations on the foreign context using a composite name. However, an 
+     * attempt destroy the context using this composite name will fail with 
+     * NotContextException, because the foreign context is not a "subcontext" 
+     * of the context in which it is bound. Instead, use unbind() to remove 
+     * the binding of the foreign context. Destroying the foreign context 
+     * requires that the destroySubcontext() be performed on a context from 
+     * the foreign context's "native" naming system.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(Name name)
+        throws NamingException {
+        
+        checkWritable();
+        
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        if (name.isEmpty())
+            throw new NamingException
+                (sm.getString("namingContext.invalidName"));
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (entry == null) {
+            throw new NameNotFoundException
+                (sm.getString("namingContext.nameNotBound", name.get(0)));
+        }
+        
+        if (name.size() > 1) {
+            if (entry.type == NamingEntry.CONTEXT) {
+                ((Context) entry.value).unbind(name.getSuffix(1));
+            } else {
+                throw new NamingException
+                    (sm.getString("namingContext.contextExpected"));
+            }
+        } else {
+            if (entry.type == NamingEntry.CONTEXT) {
+                ((Context) entry.value).close();
+                bindings.remove(name.get(0));
+            } else {
+                throw new NotContextException
+                    (sm.getString("namingContext.contextExpected"));
+            }
+        }
+        
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(String name)
+        throws NamingException {
+        destroySubcontext(new CompositeName(name));
+    }
+
+
+    /**
+     * Creates and binds a new context. Creates a new context with the given 
+     * name and binds it in the target context (that named by all but 
+     * terminal atomic component of the name). All intermediate contexts and 
+     * the target context must already exist.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(Name name)
+        throws NamingException {
+        checkWritable();
+        
+        Context newContext = new NamingContext(env, this.name);
+        bind(name, newContext);
+        
+        return newContext;
+    }
+
+
+    /**
+     * Creates and binds a new context.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(String name)
+        throws NamingException {
+        return createSubcontext(new CompositeName(name));
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name. If the object bound to name is not a 
+     * link, returns the object itself.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(Name name)
+        throws NamingException {
+        return lookup(name, false);
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(String name)
+        throws NamingException {
+        return lookup(new CompositeName(name), false);
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context. In a 
+     * federation of namespaces, different naming systems will parse names 
+     * differently. This method allows an application to get a parser for 
+     * parsing names into their atomic components using the naming convention 
+     * of a particular naming system. Within any single naming system, 
+     * NameParser objects returned by this method must be equal (using the 
+     * equals() test).
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(Name name)
+        throws NamingException {
+
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        if (name.isEmpty())
+            return nameParser;
+
+        if (name.size() > 1) {
+            Object obj = bindings.get(name.get(0));
+            if (obj instanceof Context) {
+                return ((Context) obj).getNameParser(name.getSuffix(1));
+            } else {
+                throw new NotContextException
+                    (sm.getString("namingContext.contextExpected"));
+            }
+        }
+
+        return nameParser;
+
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context.
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(String name)
+        throws NamingException {
+        return getNameParser(new CompositeName(name));
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * <p>
+     * Given a name (name) relative to this context, and the name (prefix) 
+     * of this context relative to one of its ancestors, this method returns 
+     * the composition of the two names using the syntax appropriate for the 
+     * naming system(s) involved. That is, if name names an object relative 
+     * to this context, the result is the name of the same object, but 
+     * relative to the ancestor context. None of the names may be null.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Name composeName(Name name, Name prefix)
+        throws NamingException {
+        prefix = (Name) prefix.clone();
+        return prefix.addAll(name);
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String composeName(String name, String prefix)
+        throws NamingException {
+        return prefix + "/" + name;
+    }
+
+
+    /**
+     * Adds a new environment property to the environment of this context. If 
+     * the property already exists, its value is overwritten.
+     * 
+     * @param propName the name of the environment property to add; may not 
+     * be null
+     * @param propVal the value of the property to add; may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object addToEnvironment(String propName, Object propVal)
+        throws NamingException {
+        return env.put(propName, propVal);
+    }
+
+
+    /**
+     * Removes an environment property from the environment of this context. 
+     * 
+     * @param propName the name of the environment property to remove; 
+     * may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object removeFromEnvironment(String propName)
+        throws NamingException {
+        return env.remove(propName);
+    }
+
+
+    /**
+     * Retrieves the environment in effect for this context. See class 
+     * description for more details on environment properties. 
+     * The caller should not make any changes to the object returned: their 
+     * effect on the context is undefined. The environment of this context 
+     * may be changed using addToEnvironment() and removeFromEnvironment().
+     * 
+     * @return the environment of this context; never null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Hashtable getEnvironment()
+        throws NamingException {
+        return env;
+    }
+
+
+    /**
+     * Closes this context. This method releases this context's resources 
+     * immediately, instead of waiting for them to be released automatically 
+     * by the garbage collector.
+     * This method is idempotent: invoking it on a context that has already 
+     * been closed has no effect. Invoking any other method on a closed 
+     * context is not allowed, and results in undefined behaviour.
+     * 
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void close()
+        throws NamingException {
+        env.clear();
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in 
+     * their respective namespaces. For example, an LDAP entry has a 
+     * distinguished name, and a DNS record has a fully qualified name. This 
+     * method allows the client application to retrieve this name. The string 
+     * returned by this method is not a JNDI composite name and should not be 
+     * passed directly to context methods. In naming systems for which the 
+     * notion of full name does not make sense, 
+     * OperationNotSupportedException is thrown.
+     * 
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does 
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+        throws NamingException {
+        throw  new OperationNotSupportedException
+            (sm.getString("namingContext.noAbsoluteName"));
+        //FIXME ?
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Retrieves the named object.
+     * 
+     * @param name the name of the object to look up
+     * @param resolveLinks If true, the links will be resolved
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    protected Object lookup(Name name, boolean resolveLinks)
+        throws NamingException {
+
+        // Removing empty parts
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        if (name.isEmpty()) {
+            // If name is empty, a newly allocated naming context is returned
+            return new NamingContext(env, this.name, bindings);
+        }
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (entry == null) {
+            throw new NameNotFoundException
+                (sm.getString("namingContext.nameNotBound", name.get(0)));
+        }
+        
+        if (name.size() > 1) {
+            // If the size of the name is greater that 1, then we go through a
+            // number of subcontexts.
+            if (entry.type != NamingEntry.CONTEXT) {
+                throw new NamingException
+                    (sm.getString("namingContext.contextExpected"));
+            }
+            return ((Context) entry.value).lookup(name.getSuffix(1));
+        } else {
+            if ((resolveLinks) && (entry.type == NamingEntry.LINK_REF)) {
+                String link = ((LinkRef) entry.value).getLinkName();
+                if (link.startsWith(".")) {
+                    // Link relative to this context
+                    return lookup(link.substring(1));
+                } else {
+                    return (new InitialContext(env)).lookup(link);
+                }
+            } else if (entry.type == NamingEntry.REFERENCE) {
+                try {
+                    Object obj = NamingManager.getObjectInstance
+                        (entry.value, name, this, env);
+                    if (obj != null) {
+                        entry.value = obj;
+                        entry.type = NamingEntry.ENTRY;
+                    }
+                    return obj;
+                } catch (NamingException e) {
+                    throw e;
+                } catch (Exception e) {
+                    log.warn(sm.getString
+                             ("namingContext.failResolvingReference"), e);
+                    throw new NamingException(e.getMessage());
+                }
+            } else {
+                return entry.value;
+            }
+        }
+        
+    }
+
+
+    /**
+     * Binds a name to an object. All intermediate contexts and the target 
+     * context (that named by all but terminal atomic component of the name) 
+     * must already exist.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param rebind if true, then perform a rebind (ie, overwrite)
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    protected void bind(Name name, Object obj, boolean rebind)
+        throws NamingException {
+        
+        checkWritable();
+        
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        if (name.isEmpty())
+            throw new NamingException
+                (sm.getString("namingContext.invalidName"));
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (name.size() > 1) {
+            if (entry == null) {
+                throw new NameNotFoundException
+                    (sm.getString("namingContext.nameNotBound", name.get(0)));
+            }
+            if (entry.type == NamingEntry.CONTEXT) {
+                if (rebind) {
+                    ((Context) entry.value).rebind(name.getSuffix(1), obj);
+                } else {
+                    ((Context) entry.value).bind(name.getSuffix(1), obj);
+                }
+            } else {
+                throw new NamingException
+                    (sm.getString("namingContext.contextExpected"));
+            }
+        } else {
+            if ((!rebind) && (entry != null)) {
+                throw new NameAlreadyBoundException
+                    (sm.getString("namingContext.alreadyBound", name.get(0)));
+            } else {
+                // Getting the type of the object and wrapping it within a new
+                // NamingEntry
+                Object toBind = 
+                    NamingManager.getStateToBind(obj, name, this, env);
+                if (toBind instanceof Context) {
+                    entry = new NamingEntry(name.get(0), toBind, 
+                                            NamingEntry.CONTEXT);
+                } else if (toBind instanceof LinkRef) {
+                    entry = new NamingEntry(name.get(0), toBind, 
+                                            NamingEntry.LINK_REF);
+                } else if (toBind instanceof Reference) {
+                    entry = new NamingEntry(name.get(0), toBind, 
+                                            NamingEntry.REFERENCE);
+                } else if (toBind instanceof Referenceable) {
+                    toBind = ((Referenceable) toBind).getReference();
+                    entry = new NamingEntry(name.get(0), toBind, 
+                                            NamingEntry.REFERENCE);
+                } else {
+                    entry = new NamingEntry(name.get(0), toBind, 
+                                            NamingEntry.ENTRY);
+                }
+                bindings.put(name.get(0), entry);
+            }
+        }
+        
+    }
+
+
+    /**
+     * Returns true if writing is allowed on this context.
+     */
+    protected boolean isWritable() {
+        return ContextAccessController.isWritable(name);
+    }
+
+
+    /**
+     * Throws a naming exception is Context is not writable.
+     */
+    protected void checkWritable() 
+        throws NamingException {
+        if (!isWritable())
+            throw new NamingException(sm.getString("namingContext.readOnly"));
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/NamingContextBindingsEnumeration.java b/container/catalina/src/share/org/apache/naming/NamingContextBindingsEnumeration.java
new file mode 100644
index 0000000..95c1c4a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/NamingContextBindingsEnumeration.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.util.Iterator;
+
+import javax.naming.Binding;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+/**
+ * Naming enumeration implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class NamingContextBindingsEnumeration 
+    implements NamingEnumeration {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public NamingContextBindingsEnumeration(Iterator entries) {
+    	iterator = entries;
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Underlying enumeration.
+     */
+    protected Iterator iterator;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieves the next element in the enumeration.
+     */
+    public Object next()
+        throws NamingException {
+        return nextElement();
+    }
+
+
+    /**
+     * Determines whether there are any more elements in the enumeration.
+     */
+    public boolean hasMore()
+        throws NamingException {
+        return iterator.hasNext();
+    }
+
+
+    /**
+     * Closes this enumeration.
+     */
+    public void close()
+        throws NamingException {
+    }
+
+
+    public boolean hasMoreElements() {
+        return iterator.hasNext();
+    }
+
+
+    public Object nextElement() {
+        NamingEntry entry = (NamingEntry) iterator.next();
+        return new Binding(entry.name, entry.value.getClass().getName(), 
+                           entry.value, true);
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/NamingContextEnumeration.java b/container/catalina/src/share/org/apache/naming/NamingContextEnumeration.java
new file mode 100644
index 0000000..77b3de4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/NamingContextEnumeration.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.util.Iterator;
+
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+/**
+ * Naming enumeration implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class NamingContextEnumeration 
+    implements NamingEnumeration {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public NamingContextEnumeration(Iterator entries) {
+    	iterator = entries;
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Underlying enumeration.
+     */
+    protected Iterator iterator;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieves the next element in the enumeration.
+     */
+    public Object next()
+        throws NamingException {
+        return nextElement();
+    }
+
+
+    /**
+     * Determines whether there are any more elements in the enumeration.
+     */
+    public boolean hasMore()
+        throws NamingException {
+        return iterator.hasNext();
+    }
+
+
+    /**
+     * Closes this enumeration.
+     */
+    public void close()
+        throws NamingException {
+    }
+
+
+    public boolean hasMoreElements() {
+        return iterator.hasNext();
+    }
+
+
+    public Object nextElement() {
+        NamingEntry entry = (NamingEntry) iterator.next();
+        return new NameClassPair(entry.name, entry.value.getClass().getName());
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/NamingEntry.java b/container/catalina/src/share/org/apache/naming/NamingEntry.java
new file mode 100644
index 0000000..2428286
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/NamingEntry.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+
+/**
+ * Represents a binding in a NamingContext.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class NamingEntry {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final int ENTRY = 0;
+    public static final int LINK_REF = 1;
+    public static final int REFERENCE = 2;
+    
+    public static final int CONTEXT = 10;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public NamingEntry(String name, Object value, int type) {
+        this.name = name;
+        this.value = value;
+        this.type = type;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The type instance variable is used to avoid unsing RTTI when doing
+     * lookups.
+     */
+    public int type;
+    public String name;
+    public Object value;
+
+
+    // --------------------------------------------------------- Object Methods
+
+
+    public boolean equals(Object obj) {
+        if ((obj != null) && (obj instanceof NamingEntry)) {
+            return name.equals(((NamingEntry) obj).name);
+        } else {
+            return false;
+        }
+    }
+
+
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/NamingService.java b/container/catalina/src/share/org/apache/naming/NamingService.java
new file mode 100644
index 0000000..12ce624
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/NamingService.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming;
+
+import javax.naming.Context;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.ObjectName;
+import javax.management.MBeanServer;
+import javax.management.MBeanRegistration;
+import javax.management.AttributeChangeNotification;
+import javax.management.Notification;
+
+/**
+ * Implementation of the NamingService JMX MBean.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+
+public final class NamingService
+    extends NotificationBroadcasterSupport
+    implements NamingServiceMBean, MBeanRegistration {
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * Status of the Slide domain.
+     */
+    private int state = STOPPED;
+    
+    
+    /**
+     * Notification sequence number.
+     */
+    private long sequenceNumber = 0;
+    
+    
+    /**
+     * Old URL packages value.
+     */
+    private String oldUrlValue = "";
+    
+    
+    /**
+     * Old initial context value.
+     */
+    private String oldIcValue = "";
+    
+    
+    // ---------------------------------------------- MBeanRegistration Methods
+    
+    
+    public ObjectName preRegister(MBeanServer server, ObjectName name)
+        throws Exception {
+        return new ObjectName(OBJECT_NAME);
+    }
+    
+    
+    public void postRegister(Boolean registrationDone) {
+        if (!registrationDone.booleanValue())
+            destroy();
+    }
+    
+    
+    public void preDeregister()
+        throws Exception {
+    }
+    
+    
+    public void postDeregister() {
+        destroy();
+    }
+    
+    
+    // ----------------------------------------------------- SlideMBean Methods
+    
+    
+    /**
+     * Retruns the Catalina component name.
+     */
+    public String getName() {
+        return NAME;
+    }
+    
+    
+    /**
+     * Returns the state.
+     */
+    public int getState() {
+        return state;
+    }
+    
+    
+    /**
+     * Returns a String representation of the state.
+     */
+    public String getStateString() {
+        return states[state];
+    }
+    
+    
+    /**
+     * Start the servlet container.
+     */
+    public void start()
+        throws Exception {
+        
+        Notification notification = null;
+        
+        if (state != STOPPED)
+            return;
+        
+        state = STARTING;
+        
+        // Notifying the MBEan server that we're starting
+        
+        notification = new AttributeChangeNotification
+            (this, sequenceNumber++, System.currentTimeMillis(), 
+             "Starting " + NAME, "State", "java.lang.Integer", 
+             new Integer(STOPPED), new Integer(STARTING));
+        sendNotification(notification);
+        
+        try {
+            
+            String value = "org.apache.naming";
+            String oldValue = System.getProperty(Context.URL_PKG_PREFIXES);
+            if (oldValue != null) {
+                oldUrlValue = oldValue;
+                value = oldValue + ":" + value;
+            }
+            System.setProperty(Context.URL_PKG_PREFIXES, value);
+            
+            oldValue = System.getProperty(Context.INITIAL_CONTEXT_FACTORY);
+            if ((oldValue != null) && (oldValue.length() > 0)) {
+                oldIcValue = oldValue;
+            } else {
+                System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
+                                   Constants.Package 
+                                   + ".java.javaURLContextFactory");
+            }
+            
+        } catch (Throwable t) {
+            state = STOPPED;
+            notification = new AttributeChangeNotification
+                (this, sequenceNumber++, System.currentTimeMillis(), 
+                 "Stopped " + NAME, "State", "java.lang.Integer", 
+                 new Integer(STARTING), new Integer(STOPPED));
+            sendNotification(notification);
+        }
+        
+        state = STARTED;
+        notification = new AttributeChangeNotification
+            (this, sequenceNumber++, System.currentTimeMillis(), 
+             "Started " + NAME, "State", "java.lang.Integer", 
+             new Integer(STARTING), new Integer(STARTED));
+        sendNotification(notification);
+        
+    }
+    
+    
+    /**
+     * Stop the servlet container.
+     */
+    public void stop() {
+        
+        Notification notification = null;
+        
+        if (state != STARTED)
+            return;
+        
+        state = STOPPING;
+        
+        notification = new AttributeChangeNotification
+            (this, sequenceNumber++, System.currentTimeMillis(), 
+             "Stopping " + NAME, "State", "java.lang.Integer", 
+             new Integer(STARTED), new Integer(STOPPING));
+        sendNotification(notification);
+        
+        try {
+            
+            System.setProperty(Context.URL_PKG_PREFIXES, oldUrlValue);
+            System.setProperty(Context.INITIAL_CONTEXT_FACTORY, oldIcValue);
+            
+        } catch (Throwable t) {
+            
+            // FIXME
+            t.printStackTrace();
+            
+        }
+        
+        state = STOPPED;
+        
+        notification = new AttributeChangeNotification
+            (this, sequenceNumber++, System.currentTimeMillis(), 
+             "Stopped " + NAME, "State", "java.lang.Integer", 
+             new Integer(STOPPING), new Integer(STOPPED));
+        sendNotification(notification);
+        
+    }
+    
+    
+    /**
+     * Destroy servlet container (if any is running).
+     */
+    public void destroy() {
+        
+        if (getState() != STOPPED)
+            stop();
+        
+    }
+    
+    
+}
diff --git a/container/catalina/src/share/org/apache/naming/NamingServiceMBean.java b/container/catalina/src/share/org/apache/naming/NamingServiceMBean.java
new file mode 100644
index 0000000..72a5785
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/NamingServiceMBean.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming;
+
+/**
+ * Naming MBean interface.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+
+public interface NamingServiceMBean {
+    
+    
+    // -------------------------------------------------------------- Constants
+    
+    
+    /**
+     * Status constants.
+     */
+    public static final String[] states = 
+    {"Stopped", "Stopping", "Starting", "Started"};
+    
+    
+    public static final int STOPPED  = 0;
+    public static final int STOPPING = 1;
+    public static final int STARTING = 2;
+    public static final int STARTED  = 3;
+    
+    
+    /**
+     * Component name.
+     */
+    public static final String NAME = "Apache JNDI Naming Service";
+    
+    
+    /**
+     * Object name.
+     */
+    public static final String OBJECT_NAME = ":service=Naming";
+    
+    
+    // ------------------------------------------------------ Interface Methods
+    
+    
+    /**
+     * Retruns the JNDI component name.
+     */
+    public String getName();
+    
+    
+    /**
+     * Returns the state.
+     */
+    public int getState();
+    
+    
+    /**
+     * Returns a String representation of the state.
+     */
+    public String getStateString();
+    
+    
+    /**
+     * Start the servlet container.
+     */
+    public void start()
+        throws Exception;
+    
+    
+    /**
+     * Stop the servlet container.
+     */
+    public void stop();
+    
+    
+    /**
+     * Destroy servlet container (if any is running).
+     */
+    public void destroy();
+    
+    
+}
diff --git a/container/catalina/src/share/org/apache/naming/ResourceEnvRef.java b/container/catalina/src/share/org/apache/naming/ResourceEnvRef.java
new file mode 100644
index 0000000..dee01c3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/ResourceEnvRef.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import javax.naming.Context;
+import javax.naming.Reference;
+
+/**
+ * Represents a reference address to a resource environment.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ResourceEnvRef
+    extends Reference {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Default factory for this reference.
+     */
+    public static final String DEFAULT_FACTORY = 
+        org.apache.naming.factory.Constants.DEFAULT_RESOURCE_ENV_FACTORY;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Resource env reference.
+     * 
+     * @param resourceType Type
+     */
+    public ResourceEnvRef(String resourceType) {
+        super(resourceType);
+    }
+
+
+    /**
+     * Resource env reference.
+     * 
+     * @param resourceType Type
+     * @param factory The factory class
+     * @param factoryLocation The factory location
+     */
+    public ResourceEnvRef(String resourceType, String factory,
+                          String factoryLocation) {
+        super(resourceType, factory, factoryLocation);
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------ Reference Methods
+
+
+    /**
+     * Retrieves the class name of the factory of the object to which this 
+     * reference refers.
+     */
+    public String getFactoryClassName() {
+        String factory = super.getFactoryClassName();
+        if (factory != null) {
+            return factory;
+        } else {
+            factory = System.getProperty(Context.OBJECT_FACTORIES);
+            if (factory != null) {
+                return null;
+            } else {
+                return DEFAULT_FACTORY;
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/ResourceLinkRef.java b/container/catalina/src/share/org/apache/naming/ResourceLinkRef.java
new file mode 100644
index 0000000..77e9ce1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/ResourceLinkRef.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import javax.naming.Context;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+
+/**
+ * Represents a reference address to a resource.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ResourceLinkRef
+    extends Reference {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Default factory for this reference.
+     */
+    public static final String DEFAULT_FACTORY = 
+        org.apache.naming.factory.Constants.DEFAULT_RESOURCE_LINK_FACTORY;
+
+
+    /**
+     * Description address type.
+     */
+    public static final String GLOBALNAME = "globalName";
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * ResourceLink Reference.
+     * 
+     * @param resourceClass Resource class
+     * @param globalName Global name
+     */
+    public ResourceLinkRef(String resourceClass, String globalName) {
+        this(resourceClass, globalName, null, null);
+    }
+
+
+    /**
+     * ResourceLink Reference.
+     * 
+     * @param resourceClass Resource class
+     * @param globalName Global name
+     */
+    public ResourceLinkRef(String resourceClass, String globalName, 
+                           String factory, String factoryLocation) {
+        super(resourceClass, factory, factoryLocation);
+        StringRefAddr refAddr = null;
+        if (globalName != null) {
+            refAddr = new StringRefAddr(GLOBALNAME, globalName);
+            add(refAddr);
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------ Reference Methods
+
+
+    /**
+     * Retrieves the class name of the factory of the object to which this 
+     * reference refers.
+     */
+    public String getFactoryClassName() {
+        String factory = super.getFactoryClassName();
+        if (factory != null) {
+            return factory;
+        } else {
+            factory = System.getProperty(Context.OBJECT_FACTORIES);
+            if (factory != null) {
+                return null;
+            } else {
+                return DEFAULT_FACTORY;
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/ResourceRef.java b/container/catalina/src/share/org/apache/naming/ResourceRef.java
new file mode 100644
index 0000000..d9d6d0b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/ResourceRef.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.util.Enumeration;
+
+import javax.naming.Context;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+
+/**
+ * Represents a reference address to a resource.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ResourceRef
+    extends Reference {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Default factory for this reference.
+     */
+    public static final String DEFAULT_FACTORY = 
+        org.apache.naming.factory.Constants.DEFAULT_RESOURCE_FACTORY;
+
+
+    /**
+     * Description address type.
+     */
+    public static final String DESCRIPTION = "description";
+
+
+    /**
+     * Scope address type.
+     */
+    public static final String SCOPE = "scope";
+
+
+    /**
+     * Auth address type.
+     */
+    public static final String AUTH = "auth";
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Resource Reference.
+     * 
+     * @param resourceClass Resource class
+     * @param scope Resource scope
+     * @param auth Resource authetication
+     */
+    public ResourceRef(String resourceClass, String description, 
+                       String scope, String auth) {
+        this(resourceClass, description, scope, auth, null, null);
+    }
+
+
+    /**
+     * Resource Reference.
+     * 
+     * @param resourceClass Resource class
+     * @param scope Resource scope
+     * @param auth Resource authetication
+     */
+    public ResourceRef(String resourceClass, String description, 
+                       String scope, String auth, String factory,
+                       String factoryLocation) {
+        super(resourceClass, factory, factoryLocation);
+        StringRefAddr refAddr = null;
+        if (description != null) {
+            refAddr = new StringRefAddr(DESCRIPTION, description);
+            add(refAddr);
+        }
+        if (scope != null) {
+            refAddr = new StringRefAddr(SCOPE, scope);
+            add(refAddr);
+        }
+        if (auth != null) {
+            refAddr = new StringRefAddr(AUTH, auth);
+            add(refAddr);
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------ Reference Methods
+
+
+    /**
+     * Retrieves the class name of the factory of the object to which this 
+     * reference refers.
+     */
+    public String getFactoryClassName() {
+        String factory = super.getFactoryClassName();
+        if (factory != null) {
+            return factory;
+        } else {
+            factory = System.getProperty(Context.OBJECT_FACTORIES);
+            if (factory != null) {
+                return null;
+            } else {
+                return DEFAULT_FACTORY;
+            }
+        }
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String rendering of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ResourceRef[");
+        sb.append("className=");
+        sb.append(getClassName());
+        sb.append(",factoryClassLocation=");
+        sb.append(getFactoryClassLocation());
+        sb.append(",factoryClassName=");
+        sb.append(getFactoryClassName());
+        Enumeration refAddrs = getAll();
+        while (refAddrs.hasMoreElements()) {
+            RefAddr refAddr = (RefAddr) refAddrs.nextElement();
+            sb.append(",{type=");
+            sb.append(refAddr.getType());
+            sb.append(",content=");
+            sb.append(refAddr.getContent());
+            sb.append("}");
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/SelectorContext.java b/container/catalina/src/share/org/apache/naming/SelectorContext.java
new file mode 100644
index 0000000..38b2e77
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/SelectorContext.java
@@ -0,0 +1,694 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.util.Hashtable;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+/**
+ * Catalina JNDI Context implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class SelectorContext implements Context {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Namespace URL.
+     */
+    public static final String prefix = "java:";
+
+
+    /**
+     * Namespace URL length.
+     */
+    public static final int prefixLength = prefix.length();
+
+
+    /**
+     * Initial context prefix.
+     */
+    public static final String IC_PREFIX = "IC_";
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Builds a Catalina selector context using the given environment.
+     */
+    public SelectorContext(Hashtable env) {
+        this.env = env;
+    }
+
+
+    /**
+     * Builds a Catalina selector context using the given environment.
+     */
+    public SelectorContext(Hashtable env, boolean initialContext) {
+        this(env);
+        this.initialContext = initialContext;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Environment.
+     */
+    protected Hashtable env;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Request for an initial context.
+     */
+    protected boolean initialContext = false;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Retrieves the named object. If name is empty, returns a new instance 
+     * of this context (which represents the same naming context as this 
+     * context, but its environment may be modified independently and it may 
+     * be accessed concurrently).
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(Name name)
+        throws NamingException {
+        // Strip the URL header
+        // Find the appropriate NamingContext according to the current bindings
+        // Execute the lookup on that context
+        return getBoundContext().lookup(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the named object.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(String name)
+        throws NamingException {
+        // Strip the URL header
+        // Find the appropriate NamingContext according to the current bindings
+        // Execute the lookup on that context
+        return getBoundContext().lookup(parseName(name));
+    }
+
+
+    /**
+     * Binds a name to an object. All intermediate contexts and the target 
+     * context (that named by all but terminal atomic component of the name) 
+     * must already exist.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj)
+        throws NamingException {
+        getBoundContext().bind(parseName(name), obj);
+    }
+
+
+    /**
+     * Binds a name to an object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj)
+        throws NamingException {
+        getBoundContext().bind(parseName(name), obj);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding. All 
+     * intermediate contexts and the target context (that named by all but 
+     * terminal atomic component of the name) must already exist.
+     * <p>
+     * If the object is a DirContext, any existing attributes associated with 
+     * the name are replaced with those of the object. Otherwise, any 
+     * existing attributes associated with the name remain unchanged.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj)
+        throws NamingException {
+        getBoundContext().rebind(parseName(name), obj);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj)
+        throws NamingException {
+        getBoundContext().rebind(parseName(name), obj);
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(Name name)
+        throws NamingException {
+        getBoundContext().unbind(parseName(name));
+    }
+
+
+    /**
+     * Unbinds the named object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(String name)
+        throws NamingException {
+        getBoundContext().unbind(parseName(name));
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name. Both names are relative to this context. Any attributes 
+     * associated with the old name become associated with the new name. 
+     * Intermediate contexts of the old name are not changed.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(Name oldName, Name newName)
+        throws NamingException {
+        getBoundContext().rename(parseName(oldName), parseName(newName));
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(String oldName, String newName)
+        throws NamingException {
+        getBoundContext().rename(parseName(oldName), parseName(newName));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name name)
+        throws NamingException {
+        return getBoundContext().list(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(String name)
+        throws NamingException {
+        return getBoundContext().list(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+        throws NamingException {
+        return getBoundContext().listBindings(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(String name)
+        throws NamingException {
+        return getBoundContext().listBindings(parseName(name));
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any 
+     * attributes associated with the name are also removed. Intermediate 
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * In a federated naming system, a context from one naming system may be 
+     * bound to a name in another. One can subsequently look up and perform 
+     * operations on the foreign context using a composite name. However, an 
+     * attempt destroy the context using this composite name will fail with 
+     * NotContextException, because the foreign context is not a "subcontext" 
+     * of the context in which it is bound. Instead, use unbind() to remove 
+     * the binding of the foreign context. Destroying the foreign context 
+     * requires that the destroySubcontext() be performed on a context from 
+     * the foreign context's "native" naming system.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(Name name)
+        throws NamingException {
+        getBoundContext().destroySubcontext(parseName(name));
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(String name)
+        throws NamingException {
+        getBoundContext().destroySubcontext(parseName(name));
+    }
+
+
+    /**
+     * Creates and binds a new context. Creates a new context with the given 
+     * name and binds it in the target context (that named by all but 
+     * terminal atomic component of the name). All intermediate contexts and 
+     * the target context must already exist.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(Name name)
+        throws NamingException {
+        return getBoundContext().createSubcontext(parseName(name));
+    }
+
+
+    /**
+     * Creates and binds a new context.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(String name)
+        throws NamingException {
+        return getBoundContext().createSubcontext(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name. If the object bound to name is not a 
+     * link, returns the object itself.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(Name name)
+        throws NamingException {
+        return getBoundContext().lookupLink(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(String name)
+        throws NamingException {
+        return getBoundContext().lookupLink(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context. In a 
+     * federation of namespaces, different naming systems will parse names 
+     * differently. This method allows an application to get a parser for 
+     * parsing names into their atomic components using the naming convention 
+     * of a particular naming system. Within any single naming system, 
+     * NameParser objects returned by this method must be equal (using the 
+     * equals() test).
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(Name name)
+        throws NamingException {
+        return getBoundContext().getNameParser(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context.
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(String name)
+        throws NamingException {
+        return getBoundContext().getNameParser(parseName(name));
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * <p>
+     * Given a name (name) relative to this context, and the name (prefix) 
+     * of this context relative to one of its ancestors, this method returns 
+     * the composition of the two names using the syntax appropriate for the 
+     * naming system(s) involved. That is, if name names an object relative 
+     * to this context, the result is the name of the same object, but 
+     * relative to the ancestor context. None of the names may be null.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Name composeName(Name name, Name prefix)
+        throws NamingException {
+        prefix = (Name) prefix.clone();
+        return prefix.addAll(name);
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String composeName(String name, String prefix)
+        throws NamingException {
+        return prefix + "/" + name;
+    }
+
+
+    /**
+     * Adds a new environment property to the environment of this context. If 
+     * the property already exists, its value is overwritten.
+     * 
+     * @param propName the name of the environment property to add; may not 
+     * be null
+     * @param propVal the value of the property to add; may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object addToEnvironment(String propName, Object propVal)
+        throws NamingException {
+        return getBoundContext().addToEnvironment(propName, propVal);
+    }
+
+
+    /**
+     * Removes an environment property from the environment of this context. 
+     * 
+     * @param propName the name of the environment property to remove; 
+     * may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object removeFromEnvironment(String propName)
+        throws NamingException {
+        return getBoundContext().removeFromEnvironment(propName);
+    }
+
+
+    /**
+     * Retrieves the environment in effect for this context. See class 
+     * description for more details on environment properties. 
+     * The caller should not make any changes to the object returned: their 
+     * effect on the context is undefined. The environment of this context 
+     * may be changed using addToEnvironment() and removeFromEnvironment().
+     * 
+     * @return the environment of this context; never null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Hashtable getEnvironment()
+        throws NamingException {
+        return getBoundContext().getEnvironment();
+    }
+
+
+    /**
+     * Closes this context. This method releases this context's resources 
+     * immediately, instead of waiting for them to be released automatically 
+     * by the garbage collector.
+     * This method is idempotent: invoking it on a context that has already 
+     * been closed has no effect. Invoking any other method on a closed 
+     * context is not allowed, and results in undefined behaviour.
+     * 
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void close()
+        throws NamingException {
+        getBoundContext().close();
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in 
+     * their respective namespaces. For example, an LDAP entry has a 
+     * distinguished name, and a DNS record has a fully qualified name. This 
+     * method allows the client application to retrieve this name. The string 
+     * returned by this method is not a JNDI composite name and should not be 
+     * passed directly to context methods. In naming systems for which the 
+     * notion of full name does not make sense, 
+     * OperationNotSupportedException is thrown.
+     * 
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does 
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+        throws NamingException {
+        return prefix;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Get the bound context.
+     */
+    protected Context getBoundContext()
+        throws NamingException {
+
+        if (initialContext) {
+            String ICName = IC_PREFIX;
+            if (ContextBindings.isThreadBound()) {
+                ICName += ContextBindings.getThreadName();
+            } else if (ContextBindings.isClassLoaderBound()) {
+                ICName += ContextBindings.getClassLoaderName();
+            }
+            Context initialContext = ContextBindings.getContext(ICName);
+            if (initialContext == null) {
+                // Allocating a new context and binding it to the appropriate 
+                // name
+                initialContext = new NamingContext(env, ICName);
+                ContextBindings.bindContext(ICName, initialContext);
+            }
+            return initialContext;
+        } else {
+            if (ContextBindings.isThreadBound()) {
+                return ContextBindings.getThread();
+            } else {
+                return ContextBindings.getClassLoader();
+            }
+        }
+
+    }
+
+
+    /**
+     * Strips the URL header.
+     * 
+     * @return the parsed name
+     * @exception NamingException if there is no "java:" header or if no 
+     * naming context has been bound to this thread
+     */
+    protected String parseName(String name) 
+        throws NamingException {
+        
+        if ((!initialContext) && (name.startsWith(prefix))) {
+            return (name.substring(prefixLength));
+        } else {
+            if (initialContext) {
+                return (name);
+            } else {
+                throw new NamingException
+                    (sm.getString("selectorContext.noJavaUrl"));
+            }
+        }
+        
+    }
+
+
+    /**
+     * Strips the URL header.
+     * 
+     * @return the parsed name
+     * @exception NamingException if there is no "java:" header or if no 
+     * naming context has been bound to this thread
+     */
+    protected Name parseName(Name name) 
+        throws NamingException {
+
+        if ((!initialContext) && (!name.isEmpty()) 
+            && (name.get(0).equals(prefix))) {
+            return (name.getSuffix(1));
+        } else {
+            if (initialContext) {
+                return (name);
+            } else {
+                throw new NamingException
+                    (sm.getString("selectorContext.noJavaUrl"));
+            }
+        }
+
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/StringManager.java b/container/catalina/src/share/org/apache/naming/StringManager.java
new file mode 100644
index 0000000..82da0b3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/StringManager.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.text.MessageFormat;
+import java.util.Hashtable;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An internationalization / localization helper class which reduces
+ * the bother of handling ResourceBundles and takes care of the
+ * common cases of message formating which otherwise require the
+ * creation of Object arrays and such.
+ *
+ * <p>The StringManager operates on a package basis. One StringManager
+ * per package can be created and accessed via the getManager method
+ * call.
+ *
+ * <p>The StringManager will look for a ResourceBundle named by
+ * the package name given plus the suffix of "LocalStrings". In
+ * practice, this means that the localized information will be contained
+ * in a LocalStrings.properties file located in the package
+ * directory of the classpath.
+ *
+ * <p>Please see the documentation for java.util.ResourceBundle for
+ * more information.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ */
+
+public class StringManager {
+
+    /**
+     * The ResourceBundle for this StringManager.
+     */
+    
+    private ResourceBundle bundle;
+
+    /**
+     * Creates a new StringManager for a given package. This is a
+     * private method and all access to it is arbitrated by the
+     * static getManager method call so that only one StringManager
+     * per package will be created.
+     *
+     * @param packageName Name of package to create StringManager for.
+     */
+
+    private StringManager(String packageName) {
+	String bundleName = packageName + ".LocalStrings";
+	bundle = ResourceBundle.getBundle(bundleName);
+    }
+
+    /**
+     * Get a string from the underlying resource bundle.
+     *
+     * @param key 
+     */
+    
+    public String getString(String key) {
+        if (key == null) {
+            String msg = "key is null";
+
+            throw new NullPointerException(msg);
+        }
+
+        String str = null;
+
+        try {
+	    str = bundle.getString(key);
+        } catch (MissingResourceException mre) {
+            str = "Cannot find message associated with key '" + key + "'";
+        }
+
+        return str;
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format
+     * it with the given set of arguments.
+     *
+     * @param key
+     * @param args
+     */
+
+    public String getString(String key, Object[] args) {
+	String iString = null;
+        String value = getString(key);
+
+	// this check for the runtime exception is some pre 1.1.6
+	// VM's don't do an automatic toString() on the passed in
+	// objects and barf out
+	
+	try {
+            // ensure the arguments are not null so pre 1.2 VM's don't barf
+            Object nonNullArgs[] = args;
+            for (int i=0; i<args.length; i++) {
+		if (args[i] == null) {
+		    if (nonNullArgs==args) nonNullArgs=(Object[])args.clone();
+		    nonNullArgs[i] = "null";
+		}
+	    }
+ 
+            iString = MessageFormat.format(value, nonNullArgs);
+	} catch (IllegalArgumentException iae) {
+	    StringBuffer buf = new StringBuffer();
+	    buf.append(value);
+	    for (int i = 0; i < args.length; i++) {
+		buf.append(" arg[" + i + "]=" + args[i]);
+	    }
+	    iString = buf.toString();
+	}
+	return iString;
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object argument. This argument can of course be
+     * a String object.
+     *
+     * @param key
+     * @param arg
+     */
+
+    public String getString(String key, Object arg) {
+	Object[] args = new Object[] {arg};
+	return getString(key, args);
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object arguments. These arguments can of course
+     * be String objects.
+     *
+     * @param key
+     * @param arg1
+     * @param arg2
+     */
+
+    public String getString(String key, Object arg1, Object arg2) {
+	Object[] args = new Object[] {arg1, arg2};
+	return getString(key, args);
+    }
+    
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object arguments. These arguments can of course
+     * be String objects.
+     *
+     * @param key
+     * @param arg1
+     * @param arg2
+     * @param arg3
+     */
+
+    public String getString(String key, Object arg1, Object arg2,
+			    Object arg3) {
+	Object[] args = new Object[] {arg1, arg2, arg3};
+	return getString(key, args);
+    }
+    
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object arguments. These arguments can of course
+     * be String objects.
+     *
+     * @param key
+     * @param arg1
+     * @param arg2
+     * @param arg3
+     * @param arg4
+     */
+
+    public String getString(String key, Object arg1, Object arg2,
+			    Object arg3, Object arg4) {
+	Object[] args = new Object[] {arg1, arg2, arg3, arg4};
+	return getString(key, args);
+    }   
+    // --------------------------------------------------------------
+    // STATIC SUPPORT METHODS
+    // --------------------------------------------------------------
+
+    private static Hashtable managers = new Hashtable();
+
+    /**
+     * Get the StringManager for a particular package. If a manager for
+     * a package already exists, it will be reused, else a new
+     * StringManager will be created and returned.
+     *
+     * @param packageName
+     */
+
+    public synchronized static StringManager getManager(String packageName) {
+	StringManager mgr = (StringManager)managers.get(packageName);
+	if (mgr == null) {
+	    mgr = new StringManager(packageName);
+	    managers.put(packageName, mgr);
+	}
+	return mgr;
+    }
+}
diff --git a/container/catalina/src/share/org/apache/naming/TransactionRef.java b/container/catalina/src/share/org/apache/naming/TransactionRef.java
new file mode 100644
index 0000000..0b1c10e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/TransactionRef.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import javax.naming.Context;
+import javax.naming.Reference;
+
+/**
+ * Represents a reference address to a transaction.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class TransactionRef
+    extends Reference {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Default factory for this reference.
+     */
+    public static final String DEFAULT_FACTORY = 
+        org.apache.naming.factory.Constants.DEFAULT_TRANSACTION_FACTORY;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Resource Reference.
+     */
+    public TransactionRef() {
+        this(null, null);
+    }
+
+
+    /**
+     * Resource Reference.
+     *
+     * @param factory The factory class
+     * @param factoryLocation The factory location
+     */
+    public TransactionRef(String factory, String factoryLocation) {
+        super("javax.transaction.UserTransaction", factory, factoryLocation);
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------ Reference Methods
+
+
+    /**
+     * Retrieves the class name of the factory of the object to which this 
+     * reference refers.
+     */
+    public String getFactoryClassName() {
+        String factory = super.getFactoryClassName();
+        if (factory != null) {
+            return factory;
+        } else {
+            factory = System.getProperty(Context.OBJECT_FACTORIES);
+            if (factory != null) {
+                return null;
+            } else {
+                return DEFAULT_FACTORY;
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/factory/BeanFactory.java b/container/catalina/src/share/org/apache/naming/factory/BeanFactory.java
new file mode 100644
index 0000000..45938e3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/BeanFactory.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.naming.factory;
+
+import java.util.Hashtable;
+import java.util.Enumeration;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.RefAddr;
+import javax.naming.spi.ObjectFactory;
+import org.apache.naming.ResourceRef;
+
+import java.beans.Introspector;
+import java.beans.BeanInfo;
+import java.beans.PropertyDescriptor;
+
+import java.lang.reflect.Method;
+
+/**
+ * Object factory for any Resource conforming to the JavaBean spec.
+ * 
+ * <p>This factory can be configured in a <code>&lt;DefaultContext&gt;</code>
+ * or <code>&lt;Context&gt;</code> element in your <code>conf/server.xml</code>
+ * configuration file.  An example of factory configuration is:</p>
+ * <pre>
+ * &lt;Resource name="jdbc/myDataSource" auth="SERVLET"
+ *   type="oracle.jdbc.pool.OracleConnectionCacheImpl"/&gt;
+ * &lt;ResourceParams name="jdbc/myDataSource"&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;factory&lt;/name&gt;
+ *     &lt;value&gt;org.apache.naming.factory.BeanFactory&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;driverType&lt;/name&gt;
+ *     &lt;value&gt;thin&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;serverName&lt;/name&gt;
+ *     &lt;value&gt;hue&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;networkProtocol&lt;/name&gt;
+ *     &lt;value&gt;tcp&lt;/value&gt;
+ *   &lt;/parameter&gt; 
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;databaseName&lt;/name&gt;
+ *     &lt;value&gt;XXXX&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;portNumber&lt;/name&gt;
+ *     &lt;value&gt;NNNN&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;user&lt;/name&gt;
+ *     &lt;value&gt;XXXX&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;password&lt;/name&gt;
+ *     &lt;value&gt;XXXX&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;maxLimit&lt;/name&gt;
+ *     &lt;value&gt;5&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ * &lt;/ResourceParams&gt;
+ * </pre>
+ *
+ * @author <a href="mailto:aner at ncstech.com">Aner Perez</a>
+ */
+public class BeanFactory
+    implements ObjectFactory {
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------- ObjectFactory Methods
+
+
+    /**
+     * Create a new Bean instance.
+     * 
+     * @param obj The reference object describing the Bean
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws NamingException {
+
+        if (obj instanceof ResourceRef) {
+
+            try {
+                
+                Reference ref = (Reference) obj;
+                String beanClassName = ref.getClassName();
+                Class beanClass = null;
+                ClassLoader tcl = 
+                    Thread.currentThread().getContextClassLoader();
+                if (tcl != null) {
+                    try {
+                        beanClass = tcl.loadClass(beanClassName);
+                    } catch(ClassNotFoundException e) {
+                    }
+                } else {
+                    try {
+                        beanClass = Class.forName(beanClassName);
+                    } catch(ClassNotFoundException e) {
+                        e.printStackTrace();
+                    }
+                }
+                if (beanClass == null) {
+                    throw new NamingException
+                        ("Class not found: " + beanClassName);
+                }
+                
+                BeanInfo bi = Introspector.getBeanInfo(beanClass);
+                PropertyDescriptor[] pda = bi.getPropertyDescriptors();
+                
+                Object bean = beanClass.newInstance();
+                
+                Enumeration e = ref.getAll();
+                while (e.hasMoreElements()) {
+                    
+                    RefAddr ra = (RefAddr) e.nextElement();
+                    String propName = ra.getType();
+                    
+                    if (propName.equals(Constants.FACTORY) ||
+                        propName.equals("scope") || propName.equals("auth")) {
+                        continue;
+                    }
+                    
+                    String value = (String)ra.getContent();
+                    
+                    Object[] valueArray = new Object[1];
+                    
+                    int i = 0;
+                    for (i = 0; i<pda.length; i++) {
+
+                        if (pda[i].getName().equals(propName)) {
+
+                            Class propType = pda[i].getPropertyType();
+
+                            if (propType.equals(String.class)) {
+                                valueArray[0] = value;
+                            } else if (propType.equals(Character.class) 
+                                       || propType.equals(char.class)) {
+                                valueArray[0] = new Character(value.charAt(0));
+                            } else if (propType.equals(Byte.class) 
+                                       || propType.equals(byte.class)) {
+                                valueArray[0] = new Byte(value);
+                            } else if (propType.equals(Short.class) 
+                                       || propType.equals(short.class)) {
+                                valueArray[0] = new Short(value);
+                            } else if (propType.equals(Integer.class) 
+                                       || propType.equals(int.class)) {
+                                valueArray[0] = new Integer(value);
+                            } else if (propType.equals(Long.class) 
+                                       || propType.equals(long.class)) {
+                                valueArray[0] = new Long(value);
+                            } else if (propType.equals(Float.class) 
+                                       || propType.equals(float.class)) {
+                                valueArray[0] = new Float(value);
+                            } else if (propType.equals(Double.class) 
+                                       || propType.equals(double.class)) {
+                                valueArray[0] = new Double(value);
+                            } else if (propType.equals(Boolean.class)
+                                       || propType.equals(boolean.class)) {
+                                valueArray[0] = new Boolean(value);
+                            } else {
+                                throw new NamingException
+                                    ("String conversion for property type '"
+                                     + propType.getName() + "' not available");
+                            }
+                            
+                            Method setProp = pda[i].getWriteMethod();
+                            if (setProp != null) {
+                                setProp.invoke(bean, valueArray);
+                            } else {
+                                throw new NamingException
+                                    ("Write not allowed for property: " 
+                                     + propName);
+                            }
+
+                            break;
+
+                        }
+
+                    }
+
+                    if (i == pda.length) {
+                        throw new NamingException
+                            ("No set method found for property: " + propName);
+                    }
+
+                }
+
+                return bean;
+
+            } catch (java.beans.IntrospectionException ie) {
+                NamingException ne = new NamingException(ie.getMessage());
+                ne.setRootCause(ie);
+                throw ne;
+            } catch (java.lang.IllegalAccessException iae) {
+                NamingException ne = new NamingException(iae.getMessage());
+                ne.setRootCause(iae);
+                throw ne;
+            } catch (java.lang.InstantiationException ie2) {
+                NamingException ne = new NamingException(ie2.getMessage());
+                ne.setRootCause(ie2);
+                throw ne;
+            } catch (java.lang.reflect.InvocationTargetException ite) {
+                NamingException ne = new NamingException(ite.getMessage());
+                ne.setRootCause(ite);
+                throw ne;
+            }
+
+        } else {
+            return null;
+        }
+
+    }
+}
diff --git a/container/catalina/src/share/org/apache/naming/factory/Constants.java b/container/catalina/src/share/org/apache/naming/factory/Constants.java
new file mode 100644
index 0000000..2ceeb0c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/Constants.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.factory;
+
+
+/**
+ * Static constants for this package.
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.naming.factory";
+
+    public static final String DEFAULT_RESOURCE_FACTORY = 
+        Package + ".ResourceFactory";
+
+    public static final String DEFAULT_RESOURCE_LINK_FACTORY = 
+        Package + ".ResourceLinkFactory";
+
+    public static final String DEFAULT_TRANSACTION_FACTORY = 
+        Package + ".TransactionFactory";
+
+    public static final String DEFAULT_RESOURCE_ENV_FACTORY = 
+        Package + ".ResourceEnvFactory";
+
+    public static final String DEFAULT_EJB_FACTORY = 
+        Package + ".EjbFactory";
+
+    public static final String DBCP_DATASOURCE_FACTORY = 
+        "org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory";
+
+    public static final String OPENEJB_EJB_FACTORY = 
+        Package + ".OpenEjbFactory";
+
+    public static final String OBJECT_FACTORIES = "";
+
+    public static final String FACTORY = "factory";
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/factory/EjbFactory.java b/container/catalina/src/share/org/apache/naming/factory/EjbFactory.java
new file mode 100644
index 0000000..1904d75
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/EjbFactory.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.factory;
+
+import java.util.Hashtable;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.RefAddr;
+import javax.naming.spi.ObjectFactory;
+import org.apache.naming.EjbRef;
+
+/**
+ * Object factory for EJBs.
+ * 
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class EjbFactory
+    implements ObjectFactory {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------- ObjectFactory Methods
+
+
+    /**
+     * Crete a new EJB instance.
+     * 
+     * @param obj The reference object describing the DataSource
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws Exception {
+        
+        if (obj instanceof EjbRef) {
+            Reference ref = (Reference) obj;
+
+            // If ejb-link has been specified, resolving the link using JNDI
+            RefAddr linkRefAddr = ref.get(EjbRef.LINK);
+            if (linkRefAddr != null) {
+                // Retrieving the EJB link
+                String ejbLink = linkRefAddr.getContent().toString();
+                Object beanObj = (new InitialContext()).lookup(ejbLink);
+                // Load home interface and checking if bean correctly
+                // implements specified home interface
+                /*
+                String homeClassName = ref.getClassName();
+                try {
+                    Class home = Class.forName(homeClassName);
+                    if (home.isInstance(beanObj)) {
+                        System.out.println("Bean of type " 
+                                           + beanObj.getClass().getName() 
+                                           + " implements home interface " 
+                                           + home.getName());
+                    } else {
+                        System.out.println("Bean of type " 
+                                           + beanObj.getClass().getName() 
+                                           + " doesn't implement home interface " 
+                                           + home.getName());
+                        throw new NamingException
+                            ("Bean of type " + beanObj.getClass().getName() 
+                             + " doesn't implement home interface " 
+                             + home.getName());
+                    }
+                } catch (ClassNotFoundException e) {
+                    System.out.println("Couldn't load home interface "
+                                       + homeClassName);
+                }
+                */
+                return beanObj;
+            }
+            
+            ObjectFactory factory = null;
+            RefAddr factoryRefAddr = ref.get(Constants.FACTORY);
+            if (factoryRefAddr != null) {
+                // Using the specified factory
+                String factoryClassName = 
+                    factoryRefAddr.getContent().toString();
+                // Loading factory
+                ClassLoader tcl = 
+                    Thread.currentThread().getContextClassLoader();
+                Class factoryClass = null;
+                if (tcl != null) {
+                    try {
+                        factoryClass = tcl.loadClass(factoryClassName);
+                    } catch(ClassNotFoundException e) {
+                    }
+                } else {
+                    try {
+                        factoryClass = Class.forName(factoryClassName);
+                    } catch(ClassNotFoundException e) {
+                    }
+                }
+                if (factoryClass != null) {
+                    try {
+                        factory = (ObjectFactory) factoryClass.newInstance();
+                    } catch(Throwable t) {
+                    }
+                }
+            } else {
+                String javaxEjbFactoryClassName =
+                    System.getProperty("javax.ejb.Factory",
+                                       Constants.OPENEJB_EJB_FACTORY);
+                try {
+                    factory = (ObjectFactory)
+                        Class.forName(javaxEjbFactoryClassName).newInstance();
+                } catch(Throwable t) {
+                }
+            }
+
+            if (factory != null) {
+                return factory.getObjectInstance
+                    (obj, name, nameCtx, environment);
+            } else {
+                throw new NamingException
+                    ("Cannot create resource instance");
+            }
+
+        }
+
+        return null;
+
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/factory/MailSessionFactory.java b/container/catalina/src/share/org/apache/naming/factory/MailSessionFactory.java
new file mode 100644
index 0000000..90a8226
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/MailSessionFactory.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.naming.factory;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import javax.mail.Authenticator;
+import javax.mail.PasswordAuthentication;
+import javax.mail.Session;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+
+/**
+ * <p>Factory class that creates a JNDI named JavaMail Session factory,
+ * which can be used for managing inbound and outbound electronic mail
+ * messages via JavaMail APIs.  All messaging environment properties
+ * described in the JavaMail Specification may be passed to the Session
+ * factory; however the following properties are the most commonly used:</p>
+ * <ul>
+ * <li>
+ * <li><strong>mail.smtp.host</strong> - Hostname for outbound transport
+ *     connections.  Defaults to <code>localhost</code> if not specified.</li>
+ * </ul>
+ *
+ * <p>This factory can be configured in a <code>&lt;DefaultContext&gt;</code>
+ * or <code>&lt;Context&gt;</code> element in your <code>conf/server.xml</code>
+ * configuration file.  An example of factory configuration is:</p>
+ * <pre>
+ * &lt;Resource name="mail/smtp" auth="CONTAINER"
+ *           type="javax.mail.Session"/&gt;
+ * &lt;ResourceParams name="mail/smtp"&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;factory&lt;/name&gt;
+ *     &lt;value&gt;org.apache.naming.factory.MailSessionFactory&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ *   &lt;parameter&gt;
+ *     &lt;name&gt;mail.smtp.host&lt;/name&gt;
+ *     &lt;value&gt;mail.mycompany.com&lt;/value&gt;
+ *   &lt;/parameter&gt;
+ * &lt;/ResourceParams&gt;
+ * </pre>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class MailSessionFactory implements ObjectFactory {
+
+
+    /**
+     * The Java type for which this factory knows how to create objects.
+     */
+    protected static final String factoryType = "javax.mail.Session";
+
+
+    /**
+     * Create and return an object instance based on the specified
+     * characteristics.
+     *
+     * @param refObj Reference information containing our parameters, or null
+     *  if there are no parameters
+     * @param name The name of this object, relative to context, or null
+     *  if there is no name
+     * @param context The context to which name is relative, or null if name
+     *  is relative to the default initial context
+     * @param env Environment variables, or null if there are none
+     *
+     * @exception Exception if an error occurs during object creation
+     */
+    public Object getObjectInstance(Object refObj, Name name, Context context,
+				    Hashtable env) throws Exception 
+    {
+
+        // Return null if we cannot create an object of the requested type
+	final Reference ref = (Reference) refObj;
+        if (!ref.getClassName().equals(factoryType))
+            return (null);
+
+        // Create a new Session inside a doPrivileged block, so that JavaMail
+        // can read its default properties without throwing Security
+        // exceptions.
+        //
+        // Bugzilla 31288, 33077: add support for authentication.
+        return AccessController.doPrivileged( new PrivilegedAction() {
+		public Object run() {
+
+                    // Create the JavaMail properties we will use
+                    Properties props = new Properties();
+                    props.put("mail.transport.protocol", "smtp");
+                    props.put("mail.smtp.host", "localhost");
+
+                    String password = null;
+
+                    Enumeration attrs = ref.getAll();
+                    while (attrs.hasMoreElements()) {
+                        RefAddr attr = (RefAddr) attrs.nextElement();
+                        if ("factory".equals(attr.getType())) {
+                            continue;
+                        }
+
+                        if ("password".equals(attr.getType())) {
+                            password = (String) attr.getContent();
+                            continue;
+                        }
+
+                        props.put(attr.getType(), (String) attr.getContent());
+                    }
+
+                    Authenticator auth = null;
+                    if (password != null) {
+                        String user = props.getProperty("mail.smtp.user");
+                        if(user == null) {
+                            user = props.getProperty("mail.user");
+                        }
+                        
+                        if(user != null) {
+                            final PasswordAuthentication pa = new PasswordAuthentication(user, password);
+                            auth = new Authenticator() {
+                                    protected PasswordAuthentication getPasswordAuthentication() {
+                                        return pa;
+                                    }
+                                };
+                        }
+                    }
+
+                    // Create and return the new Session object
+                    Session session = Session.getInstance(props, auth);
+                    return (session);
+
+		}
+	    } );
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/factory/OpenEjbFactory.java b/container/catalina/src/share/org/apache/naming/factory/OpenEjbFactory.java
new file mode 100644
index 0000000..a7eac8f
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/OpenEjbFactory.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.factory;
+
+import org.apache.naming.EjbRef;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.Name;
+import javax.naming.Reference;
+import javax.naming.RefAddr;
+import javax.naming.spi.ObjectFactory;
+import java.util.Hashtable;
+import java.util.Properties;
+
+/**
+ * Object factory for EJBs.
+ * 
+ * @author Jacek Laskowski
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+public class OpenEjbFactory implements ObjectFactory {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final String DEFAULT_OPENEJB_FACTORY = 
+        "org.openejb.client.LocalInitialContextFactory";
+
+
+    // -------------------------------------------------- ObjectFactory Methods
+
+
+    /**
+     * Crete a new EJB instance using OpenEJB.
+     * 
+     * @param obj The reference object describing the DataSource
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws Exception {
+
+        Object beanObj = null;
+
+        if (obj instanceof EjbRef) {
+
+            Reference ref = (Reference) obj;
+
+            String factory = DEFAULT_OPENEJB_FACTORY;
+            RefAddr factoryRefAddr = ref.get("openejb.factory");
+            if (factoryRefAddr != null) {
+                // Retrieving the OpenEJB factory
+                factory = factoryRefAddr.getContent().toString();
+            }
+
+            Properties env = new Properties();
+            env.put(Context.INITIAL_CONTEXT_FACTORY, factory);
+
+            RefAddr linkRefAddr = ref.get("openejb.link");
+            if (linkRefAddr != null) {
+                String ejbLink = linkRefAddr.getContent().toString();
+                beanObj = (new InitialContext(env)).lookup(ejbLink);
+            }
+
+        }
+
+        return beanObj;
+
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/factory/ResourceEnvFactory.java b/container/catalina/src/share/org/apache/naming/factory/ResourceEnvFactory.java
new file mode 100644
index 0000000..eb4231e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/ResourceEnvFactory.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.factory;
+
+import java.util.Hashtable;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.RefAddr;
+import javax.naming.spi.ObjectFactory;
+import org.apache.naming.ResourceEnvRef;
+
+/**
+ * Object factory for Resources env.
+ * 
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ResourceEnvFactory
+    implements ObjectFactory {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------- ObjectFactory Methods
+
+
+    /**
+     * Crete a new Resource env instance.
+     * 
+     * @param obj The reference object describing the DataSource
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws Exception {
+        
+        if (obj instanceof ResourceEnvRef) {
+            Reference ref = (Reference) obj;
+            ObjectFactory factory = null;
+            RefAddr factoryRefAddr = ref.get(Constants.FACTORY);
+            if (factoryRefAddr != null) {
+                // Using the specified factory
+                String factoryClassName = 
+                    factoryRefAddr.getContent().toString();
+                // Loading factory
+                ClassLoader tcl = 
+                    Thread.currentThread().getContextClassLoader();
+                Class factoryClass = null;
+                if (tcl != null) {
+                    try {
+                        factoryClass = tcl.loadClass(factoryClassName);
+                    } catch(ClassNotFoundException e) {
+                    }
+                } else {
+                    try {
+                        factoryClass = Class.forName(factoryClassName);
+                    } catch(ClassNotFoundException e) {
+                    }
+                }
+                if (factoryClass != null) {
+                    try {
+                        factory = (ObjectFactory) factoryClass.newInstance();
+                    } catch(Throwable t) {
+                    }
+                }
+            }
+            // Note: No defaults here
+            if (factory != null) {
+                return factory.getObjectInstance
+                    (obj, name, nameCtx, environment);
+            } else {
+                throw new NamingException
+                    ("Cannot create resource instance");
+            }
+        }
+
+        return null;
+
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/factory/ResourceFactory.java b/container/catalina/src/share/org/apache/naming/factory/ResourceFactory.java
new file mode 100644
index 0000000..d432f15
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/ResourceFactory.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.factory;
+
+import java.util.Hashtable;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.RefAddr;
+import javax.naming.spi.ObjectFactory;
+import org.apache.naming.ResourceRef;
+
+/**
+ * Object factory for Resources.
+ * 
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ResourceFactory
+    implements ObjectFactory {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------- ObjectFactory Methods
+
+
+    /**
+     * Crete a new DataSource instance.
+     * 
+     * @param obj The reference object describing the DataSource
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws Exception {
+        
+        if (obj instanceof ResourceRef) {
+            Reference ref = (Reference) obj;
+            ObjectFactory factory = null;
+            RefAddr factoryRefAddr = ref.get(Constants.FACTORY);
+            if (factoryRefAddr != null) {
+                // Using the specified factory
+                String factoryClassName = 
+                    factoryRefAddr.getContent().toString();
+                // Loading factory
+                ClassLoader tcl = 
+                    Thread.currentThread().getContextClassLoader();
+                Class factoryClass = null;
+                if (tcl != null) {
+                    try {
+                        factoryClass = tcl.loadClass(factoryClassName);
+                    } catch(ClassNotFoundException e) {
+                        throw new NamingException(
+                            "Could not create resource factory, ClassNotFoundException:" +
+                            e.getMessage());
+                    }
+                } else {
+                    try {
+                        factoryClass = Class.forName(factoryClassName);
+                    } catch(ClassNotFoundException e) {
+                        throw new NamingException(
+                            "Could not create resource factory, ClassNotFoundException:" +
+                            e.getMessage());
+                    }
+                }
+                if (factoryClass != null) {
+                    try {
+                        factory = (ObjectFactory) factoryClass.newInstance();
+                    } catch(Throwable t) {
+                        if( t instanceof NamingException)
+                            throw (NamingException)t;
+                        throw new NamingException(
+                            "Could not create resource factory instance, " +
+                            t.getMessage());
+                    }
+                }
+            } else {
+                if (ref.getClassName().equals("javax.sql.DataSource")) {
+                    String javaxSqlDataSourceFactoryClassName =
+                        System.getProperty("javax.sql.DataSource.Factory",
+                                           Constants.DBCP_DATASOURCE_FACTORY);
+                    try {
+                        factory = (ObjectFactory) 
+                            Class.forName(javaxSqlDataSourceFactoryClassName)
+                            .newInstance();
+                    } catch(Throwable t) {
+
+                    }
+                } else if (ref.getClassName().equals("javax.mail.Session")) {
+                    String javaxMailSessionFactoryClassName =
+                        System.getProperty("javax.mail.Session.Factory",
+                                           "org.apache.naming.factory.MailSessionFactory");
+                    try {
+                        factory = (ObjectFactory) 
+                            Class.forName(javaxMailSessionFactoryClassName)
+                            .newInstance();
+                    } catch(Throwable t) {
+                    }
+                }
+            }
+            if (factory != null) {
+                return factory.getObjectInstance
+                    (obj, name, nameCtx, environment);
+            } else {
+                throw new NamingException
+                    ("Cannot create resource instance");
+            }
+        }
+        
+        return null;
+
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/factory/ResourceLinkFactory.java b/container/catalina/src/share/org/apache/naming/factory/ResourceLinkFactory.java
new file mode 100644
index 0000000..ecdef40
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/ResourceLinkFactory.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.factory;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+
+import org.apache.naming.ResourceLinkRef;
+
+
+/**
+ * <p>Object factory for resource links.</p>
+ * 
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ResourceLinkFactory
+    implements ObjectFactory {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * Global naming context.
+     */
+    private static Context globalContext = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Set the global context (note: can only be used once).
+     * 
+     * @param newGlobalContext new global context value
+     */
+    public static void setGlobalContext(Context newGlobalContext) {
+        if (globalContext != null)
+            return;
+        globalContext = newGlobalContext;
+    }
+
+
+    // -------------------------------------------------- ObjectFactory Methods
+
+
+    /**
+     * Create a new DataSource instance.
+     * 
+     * @param obj The reference object describing the DataSource
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws NamingException {
+        
+        if (!(obj instanceof ResourceLinkRef))
+            return null;
+
+        // Can we process this request?
+        Reference ref = (Reference) obj;
+
+        String type = ref.getClassName();
+
+        // Read the global ref addr
+        String globalName = null;
+        RefAddr refAddr = ref.get(ResourceLinkRef.GLOBALNAME);
+        if (refAddr != null) {
+            globalName = refAddr.getContent().toString();
+            Object result = null;
+            result = globalContext.lookup(globalName);
+            // FIXME: Check type
+            return result;
+        }
+
+        return (null);
+
+        
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/factory/SendMailFactory.java b/container/catalina/src/share/org/apache/naming/factory/SendMailFactory.java
new file mode 100644
index 0000000..b0fec48
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/SendMailFactory.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.naming.factory;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Enumeration;
+import javax.mail.Session;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimePart;
+import javax.mail.internet.MimePartDataSource;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.Reference;
+import javax.naming.RefAddr;
+import javax.naming.spi.ObjectFactory;
+
+/**
+ * Factory class that creates a JNDI named javamail MimePartDataSource
+ * object which can be used for sending email using SMTP.
+ * <p>
+ * Can be configured in the DefaultContext or Context scope
+ * of your server.xml configuration file.
+ * <p>
+ * Example:
+ * <p>
+ * <pre>
+ * &lt;Resource name="mail/send" auth="CONTAINER"
+ *           type="javax.mail.internet.MimePartDataSource"/>
+ * &lt;ResourceParams name="mail/send">
+ *   &lt;parameter>&lt;name>factory&lt;/name>
+ *     &lt;value>org.apache.naming.factory.SendMailFactory&lt;/value>
+ *   &lt;/parameter>
+ *   &lt;parameter>&lt;name>mail.smtp.host&lt;/name>
+ *     &lt;value>your.smtp.host&lt;/value>
+ *   &lt;/parameter>
+ *   &lt;parameter>&lt;name>mail.smtp.user&lt;/name>
+ *     &lt;value>someuser&lt;/value>
+ *   &lt;/parameter>
+ *   &lt;parameter>&lt;name>mail.from&lt;/name>
+ *     &lt;value>someuser@some.host&lt;/value>
+ *   &lt;/parameter>
+ *   &lt;parameter>&lt;name>mail.smtp.sendpartial&lt;/name>
+ *     &lt;value>true&lt;/value>
+ *   &lt;/parameter>
+ *  &lt;parameter>&lt;name>mail.smtp.dsn.notify&lt;/name>
+ *     &lt;value>FAILURE&lt;/value>
+ *   &lt;/parameter>
+ *   &lt;parameter>&lt;name>mail.smtp.dsn.ret&lt;/name>
+ *     &lt;value>FULL&lt;/value>
+ *   &lt;/parameter>
+ * &lt;/ResourceParams>
+ * </pre>
+ *
+ * @author Glenn Nielsen Rich Catlett
+ */
+
+public class SendMailFactory implements ObjectFactory 
+{
+    // The class name for the javamail MimeMessageDataSource
+    protected final String DataSourceClassName = 
+	"javax.mail.internet.MimePartDataSource";
+
+    public Object getObjectInstance(Object RefObj, Name Nm, Context Ctx,
+				    Hashtable Env) throws Exception 
+    {
+	final Reference Ref = (Reference)RefObj;
+
+	// Creation of the DataSource is wrapped inside a doPrivileged
+	// so that javamail can read its default properties without
+	// throwing Security Exceptions
+	if (Ref.getClassName().equals(DataSourceClassName)) {
+	    return AccessController.doPrivileged( new PrivilegedAction()
+	    {
+		public Object run() {
+        	    // set up the smtp session that will send the message
+	            Properties props = new Properties();
+		    // enumeration of all refaddr
+		    Enumeration list = Ref.getAll();
+		    // current refaddr to be set
+		    RefAddr refaddr;
+	            // set transport to smtp
+	            props.put("mail.transport.protocol", "smtp");
+
+		    while (list.hasMoreElements()) {
+			refaddr = (RefAddr)list.nextElement();
+
+			// set property
+			props.put(refaddr.getType(), (String)refaddr.getContent());
+		    }
+		    MimeMessage message = new MimeMessage(
+			Session.getInstance(props));
+		    try {
+			String from = (String)Ref.get("mail.from").getContent();
+		        message.setFrom(new InternetAddress(from));
+		        message.setSubject("");
+		    } catch (Exception e) {}
+		    MimePartDataSource mds = new MimePartDataSource(
+			(MimePart)message);
+		    return mds;
+		}
+	    } );
+	}
+	else { // We can't create an instance of the DataSource
+	    return null;
+	}
+    }
+}
diff --git a/container/catalina/src/share/org/apache/naming/factory/TransactionFactory.java b/container/catalina/src/share/org/apache/naming/factory/TransactionFactory.java
new file mode 100644
index 0000000..c4a70d4
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/TransactionFactory.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.factory;
+
+import java.util.Hashtable;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.RefAddr;
+import javax.naming.spi.ObjectFactory;
+import org.apache.naming.TransactionRef;
+
+/**
+ * Object factory for User trasactions.
+ * 
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class TransactionFactory
+    implements ObjectFactory {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------- ObjectFactory Methods
+
+
+    /**
+     * Crete a new User transaction instance.
+     * 
+     * @param obj The reference object describing the DataSource
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws Exception {
+        
+        if (obj instanceof TransactionRef) {
+            Reference ref = (Reference) obj;
+            ObjectFactory factory = null;
+            RefAddr factoryRefAddr = ref.get(Constants.FACTORY);
+            if (factoryRefAddr != null) {
+                // Using the specified factory
+                String factoryClassName = 
+                    factoryRefAddr.getContent().toString();
+                // Loading factory
+                ClassLoader tcl = 
+                    Thread.currentThread().getContextClassLoader();
+                Class factoryClass = null;
+                if (tcl != null) {
+                    try {
+                        factoryClass = tcl.loadClass(factoryClassName);
+                    } catch(ClassNotFoundException e) {
+                    }
+                } else {
+                    try {
+                        factoryClass = Class.forName(factoryClassName);
+                    } catch(ClassNotFoundException e) {
+                    }
+                }
+                if (factoryClass != null) {
+                    try {
+                        factory = (ObjectFactory) factoryClass.newInstance();
+                    } catch(Throwable t) {
+                    }
+                }
+            }
+            if (factory != null) {
+                return factory.getObjectInstance
+                    (obj, name, nameCtx, environment);
+            } else {
+                throw new NamingException
+                    ("Cannot create resource instance");
+            }
+            
+        }
+        
+        return null;
+        
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/factory/package.html b/container/catalina/src/share/org/apache/naming/factory/package.html
new file mode 100644
index 0000000..b18cb07
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/factory/package.html
@@ -0,0 +1,7 @@
+<body>
+
+<p>This package contains object factories used by the naming service.</p>
+
+<p></p>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/naming/java/javaURLContextFactory.java b/container/catalina/src/share/org/apache/naming/java/javaURLContextFactory.java
new file mode 100644
index 0000000..d65869b
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/java/javaURLContextFactory.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.java;
+
+import java.util.Hashtable;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.ObjectFactory;
+import javax.naming.spi.InitialContextFactory;
+import org.apache.naming.SelectorContext;
+import org.apache.naming.NamingContext;
+import org.apache.naming.ContextBindings;
+
+/**
+ * Context factory for the "java:" namespace.
+ * <p>
+ * <b>Important note</b> : This factory MUST be associated with the "java" URL
+ * prefix, which can be done by either :
+ * <ul>
+ * <li>Adding a 
+ * java.naming.factory.url.pkgs=org.apache.catalina.util.naming property
+ * to the JNDI properties file</li>
+ * <li>Setting an environment variable named Context.URL_PKG_PREFIXES with 
+ * its value including the org.apache.catalina.util.naming package name. 
+ * More detail about this can be found in the JNDI documentation : 
+ * {@link javax.naming.spi.NamingManager#getURLContext(java.lang.String, java.util.Hashtable)}.</li>
+ * </ul>
+ * 
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class javaURLContextFactory
+    implements ObjectFactory, InitialContextFactory {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String MAIN = "initialContext";
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Initial context.
+     */
+    protected static Context initialContext = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------- ObjectFactory Methods
+
+
+    /**
+     * Crete a new Context's instance.
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws NamingException {
+        if ((ContextBindings.isThreadBound()) || 
+            (ContextBindings.isClassLoaderBound())) {
+            return new SelectorContext(environment);
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Get a new (writable) initial context.
+     */
+    public Context getInitialContext(Hashtable environment)
+        throws NamingException {
+        if (ContextBindings.isThreadBound() || 
+            (ContextBindings.isClassLoaderBound())) {
+            // Redirect the request to the bound initial context
+            return new SelectorContext(environment, true);
+        } else {
+            // If the thread is not bound, return a shared writable context
+            if (initialContext == null)
+                initialContext = new NamingContext(environment, MAIN);
+            return initialContext;
+        }
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/java/package.html b/container/catalina/src/share/org/apache/naming/java/package.html
new file mode 100644
index 0000000..900f764
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/java/package.html
@@ -0,0 +1,7 @@
+<body>
+
+<p>This package contains the URL context factory for the "java" namespace.</p>
+
+<p></p>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/naming/package.html b/container/catalina/src/share/org/apache/naming/package.html
new file mode 100644
index 0000000..7d5f9eb
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/package.html
@@ -0,0 +1,7 @@
+<body>
+
+<p>This package contains a memory based naming service provider.</p>
+
+<p></p>
+
+</body>
diff --git a/container/catalina/src/share/org/apache/naming/resources/BaseDirContext.java b/container/catalina/src/share/org/apache/naming/resources/BaseDirContext.java
new file mode 100644
index 0000000..66d05a5
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/BaseDirContext.java
@@ -0,0 +1,1208 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.resources;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+import org.apache.naming.NameParserImpl;
+import org.apache.naming.StringManager;
+
+/**
+ * Directory Context implementation helper class.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public abstract class BaseDirContext implements DirContext {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Builds a base directory context.
+     */
+    public BaseDirContext() {
+        this.env = new Hashtable();
+    }
+
+
+    /**
+     * Builds a base directory context using the given environment.
+     */
+    public BaseDirContext(Hashtable env) {
+        this.env = env;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The document base path.
+     */
+    protected String docBase = null;
+
+
+    /**
+     * Environment.
+     */
+    protected Hashtable env;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Name parser for this context.
+     */
+    protected final NameParser nameParser = new NameParserImpl();
+
+
+    /**
+     * Cached.
+     */
+    protected boolean cached = true;
+
+
+    /**
+     * Cache TTL.
+     */
+    protected int cacheTTL = 5000; // 5s
+
+
+    /**
+     * Max size of resources which will have their content cached.
+     */
+    protected int cacheMaxSize = 10240; // 10 MB
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the document root for this component.
+     */
+    public String getDocBase() {
+        return (this.docBase);
+    }
+
+
+    /**
+     * Set the document root for this component.
+     *
+     * @param docBase The new document root
+     *
+     * @exception IllegalArgumentException if the specified value is not
+     *  supported by this implementation
+     * @exception IllegalArgumentException if this would create a
+     *  malformed URL
+     */
+    public void setDocBase(String docBase) {
+
+        // Validate the format of the proposed document root
+        if (docBase == null)
+            throw new IllegalArgumentException
+                (sm.getString("resources.null"));
+
+        // Change the document root property
+        this.docBase = docBase;
+
+    }
+
+
+    /**
+     * Set cached.
+     */
+    public void setCached(boolean cached) {
+        this.cached = cached;
+    }
+
+
+    /**
+     * Is cached ?
+     */
+    public boolean isCached() {
+        return cached;
+    }
+
+
+    /**
+     * Set cache TTL.
+     */
+    public void setCacheTTL(int cacheTTL) {
+        this.cacheTTL = cacheTTL;
+    }
+
+
+    /**
+     * Get cache TTL.
+     */
+    public int getCacheTTL() {
+        return cacheTTL;
+    }
+
+
+    /**
+     * Return the maximum size of the cache in KB.
+     */
+    public int getCacheMaxSize() {
+        return cacheMaxSize;
+    }
+
+
+    /**
+     * Set the maximum size of the cache in KB.
+     */
+    public void setCacheMaxSize(int cacheMaxSize) {
+        this.cacheMaxSize = cacheMaxSize;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Allocate resources for this directory context.
+     */
+    public void allocate() {
+        ; // No action taken by the default implementation
+    }
+
+
+    /**
+     * Release any resources allocated for this directory context.
+     */
+    public void release() {
+        ; // No action taken by the default implementation
+    }
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Retrieves the named object. If name is empty, returns a new instance 
+     * of this context (which represents the same naming context as this 
+     * context, but its environment may be modified independently and it may 
+     * be accessed concurrently).
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(Name name)
+        throws NamingException {
+        return lookup(name.toString());
+    }
+
+
+    /**
+     * Retrieves the named object.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract Object lookup(String name)
+        throws NamingException;
+
+
+    /**
+     * Binds a name to an object. All intermediate contexts and the target 
+     * context (that named by all but terminal atomic component of the name) 
+     * must already exist.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj)
+        throws NamingException {
+        bind(name.toString(), obj);
+    }
+
+
+    /**
+     * Binds a name to an object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj)
+        throws NamingException {
+        bind(name, obj, null);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding. All 
+     * intermediate contexts and the target context (that named by all but 
+     * terminal atomic component of the name) must already exist.
+     * <p>
+     * If the object is a DirContext, any existing attributes associated with 
+     * the name are replaced with those of the object. Otherwise, any 
+     * existing attributes associated with the name remain unchanged.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj)
+        throws NamingException {
+        rebind(name.toString(), obj);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj)
+        throws NamingException {
+        rebind(name, obj, null);
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(Name name)
+        throws NamingException {
+        unbind(name.toString());
+    }
+
+
+    /**
+     * Unbinds the named object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract void unbind(String name)
+        throws NamingException;
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name. Both names are relative to this context. Any attributes 
+     * associated with the old name become associated with the new name. 
+     * Intermediate contexts of the old name are not changed.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(Name oldName, Name newName)
+        throws NamingException {
+        rename(oldName.toString(), newName.toString());
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract void rename(String oldName, String newName)
+        throws NamingException;
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name name)
+        throws NamingException {
+        return list(name.toString());
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract NamingEnumeration list(String name)
+        throws NamingException;
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+        throws NamingException {
+        return listBindings(name.toString());
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract NamingEnumeration listBindings(String name)
+        throws NamingException;
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any 
+     * attributes associated with the name are also removed. Intermediate 
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * In a federated naming system, a context from one naming system may be 
+     * bound to a name in another. One can subsequently look up and perform 
+     * operations on the foreign context using a composite name. However, an 
+     * attempt destroy the context using this composite name will fail with 
+     * NotContextException, because the foreign context is not a "subcontext" 
+     * of the context in which it is bound. Instead, use unbind() to remove 
+     * the binding of the foreign context. Destroying the foreign context 
+     * requires that the destroySubcontext() be performed on a context from 
+     * the foreign context's "native" naming system.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(Name name)
+        throws NamingException {
+        destroySubcontext(name.toString());
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public abstract void destroySubcontext(String name)
+        throws NamingException;
+
+
+    /**
+     * Creates and binds a new context. Creates a new context with the given 
+     * name and binds it in the target context (that named by all but 
+     * terminal atomic component of the name). All intermediate contexts and 
+     * the target context must already exist.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(Name name)
+        throws NamingException {
+        return createSubcontext(name.toString());
+    }
+
+
+    /**
+     * Creates and binds a new context.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(String name)
+        throws NamingException {
+        return createSubcontext(name, null);
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name. If the object bound to name is not a 
+     * link, returns the object itself.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(Name name)
+        throws NamingException {
+        return lookupLink(name.toString());
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract Object lookupLink(String name)
+        throws NamingException;
+
+
+    /**
+     * Retrieves the parser associated with the named context. In a 
+     * federation of namespaces, different naming systems will parse names 
+     * differently. This method allows an application to get a parser for 
+     * parsing names into their atomic components using the naming convention 
+     * of a particular naming system. Within any single naming system, 
+     * NameParser objects returned by this method must be equal (using the 
+     * equals() test).
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(Name name)
+        throws NamingException {
+        return new NameParserImpl();
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context.
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(String name)
+        throws NamingException {
+        return new NameParserImpl();
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * <p>
+     * Given a name (name) relative to this context, and the name (prefix) 
+     * of this context relative to one of its ancestors, this method returns 
+     * the composition of the two names using the syntax appropriate for the 
+     * naming system(s) involved. That is, if name names an object relative 
+     * to this context, the result is the name of the same object, but 
+     * relative to the ancestor context. None of the names may be null.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Name composeName(Name name, Name prefix)
+        throws NamingException {
+        prefix = (Name) prefix.clone();
+        return prefix.addAll(name);
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String composeName(String name, String prefix)
+        throws NamingException {
+        return prefix + "/" + name;
+    }
+
+
+    /**
+     * Adds a new environment property to the environment of this context. If 
+     * the property already exists, its value is overwritten.
+     * 
+     * @param propName the name of the environment property to add; may not 
+     * be null
+     * @param propVal the value of the property to add; may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object addToEnvironment(String propName, Object propVal)
+        throws NamingException {
+        return env.put(propName, propVal);
+    }
+
+
+    /**
+     * Removes an environment property from the environment of this context. 
+     * 
+     * @param propName the name of the environment property to remove; 
+     * may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object removeFromEnvironment(String propName)
+        throws NamingException {
+        return env.remove(propName);
+    }
+
+
+    /**
+     * Retrieves the environment in effect for this context. See class 
+     * description for more details on environment properties. 
+     * The caller should not make any changes to the object returned: their 
+     * effect on the context is undefined. The environment of this context 
+     * may be changed using addToEnvironment() and removeFromEnvironment().
+     * 
+     * @return the environment of this context; never null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Hashtable getEnvironment()
+        throws NamingException {
+        return env;
+    }
+
+
+    /**
+     * Closes this context. This method releases this context's resources 
+     * immediately, instead of waiting for them to be released automatically 
+     * by the garbage collector.
+     * This method is idempotent: invoking it on a context that has already 
+     * been closed has no effect. Invoking any other method on a closed 
+     * context is not allowed, and results in undefined behaviour.
+     * 
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void close()
+        throws NamingException {
+        env.clear();
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in 
+     * their respective namespaces. For example, an LDAP entry has a 
+     * distinguished name, and a DNS record has a fully qualified name. This 
+     * method allows the client application to retrieve this name. The string 
+     * returned by this method is not a JNDI composite name and should not be 
+     * passed directly to context methods. In naming systems for which the 
+     * notion of full name does not make sense, 
+     * OperationNotSupportedException is thrown.
+     * 
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does 
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract String getNameInNamespace()
+        throws NamingException;
+
+
+    // ----------------------------------------------------- DirContext Methods
+
+
+    /**
+     * Retrieves all of the attributes associated with a named object. 
+     * 
+     * @return the set of attributes associated with name. 
+     * Returns an empty attribute set if name has no attributes; never null.
+     * @param name the name of the object from which to retrieve attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name name)
+        throws NamingException {
+        return getAttributes(name.toString());
+    }
+
+
+    /**
+     * Retrieves all of the attributes associated with a named object.
+     * 
+     * @return the set of attributes associated with name
+     * @param name the name of the object from which to retrieve attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(String name)
+        throws NamingException {
+        return getAttributes(name, null);
+    }
+
+
+    /**
+     * Retrieves selected attributes associated with a named object. 
+     * See the class description regarding attribute models, attribute type 
+     * names, and operational attributes.
+     * 
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null 
+     * indicates that all attributes should be retrieved; an empty array 
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name name, String[] attrIds)
+        throws NamingException {
+        return getAttributes(name.toString(), attrIds);
+    }
+    
+    
+    /**
+     * Retrieves selected attributes associated with a named object.
+     * 
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null 
+     * indicates that all attributes should be retrieved; an empty array 
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract Attributes getAttributes(String name, String[] attrIds)
+        throws NamingException;
+
+
+    /**
+     * Modifies the attributes associated with a named object. The order of 
+     * the modifications is not specified. Where possible, the modifications 
+     * are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE, 
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not 
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(Name name, int mod_op, Attributes attrs)
+        throws NamingException {
+        modifyAttributes(name.toString(), mod_op, attrs);
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE, 
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not 
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract void modifyAttributes
+        (String name, int mod_op, Attributes attrs)
+        throws NamingException;
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an 
+     * ordered list of modifications. The modifications are performed in the 
+     * order specified. Each modification specifies a modification operation 
+     * code and an attribute on which to operate. Where possible, the 
+     * modifications are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may 
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(Name name, ModificationItem[] mods)
+        throws NamingException {
+        modifyAttributes(name.toString(), mods);
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an 
+     * ordered list of modifications.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may 
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract void modifyAttributes(String name, ModificationItem[] mods)
+        throws NamingException;
+
+
+    /**
+     * Binds a name to an object, along with associated attributes. If attrs 
+     * is null, the resulting binding will have the attributes associated 
+     * with obj if obj is a DirContext, and no attributes otherwise. If attrs 
+     * is non-null, the resulting binding will have attrs as its attributes; 
+     * any attributes associated with obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj, Attributes attrs)
+        throws NamingException {
+        bind(name.toString(), obj, attrs);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract void bind(String name, Object obj, Attributes attrs)
+        throws NamingException;
+
+
+    /**
+     * Binds a name to an object, along with associated attributes, 
+     * overwriting any existing binding. If attrs is null and obj is a 
+     * DirContext, the attributes from obj are used. If attrs is null and obj 
+     * is not a DirContext, any existing attributes associated with the object
+     * already bound in the directory remain unchanged. If attrs is non-null, 
+     * any existing attributes associated with the object already bound in 
+     * the directory are removed and attrs is associated with the named 
+     * object. If obj is a DirContext and attrs is non-null, the attributes 
+     * of obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj, Attributes attrs)
+        throws NamingException {
+        rebind(name.toString(), obj, attrs);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes, 
+     * overwriting any existing binding.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract void rebind(String name, Object obj, Attributes attrs)
+        throws NamingException;
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes. 
+     * This method creates a new subcontext with the given name, binds it in 
+     * the target context (that named by all but terminal atomic component of 
+     * the name), and associates the supplied attributes with the newly 
+     * created object. All intermediate and target contexts must already 
+     * exist. If attrs is null, this method is equivalent to 
+     * Context.createSubcontext().
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all 
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(Name name, Attributes attrs)
+        throws NamingException {
+        return createSubcontext(name.toString(), attrs);
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all 
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract DirContext createSubcontext(String name, Attributes attrs)
+        throws NamingException;
+
+
+    /**
+     * Retrieves the schema associated with the named object. The schema 
+     * describes rules regarding the structure of the namespace and the 
+     * attributes stored within it. The schema specifies what types of 
+     * objects can be added to the directory and where they can be added; 
+     * what mandatory and optional attributes an object can have. The range 
+     * of support for schemas is directory-specific.
+     * 
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(Name name)
+        throws NamingException {
+        return getSchema(name.toString());
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object.
+     * 
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract DirContext getSchema(String name)
+        throws NamingException;
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named 
+     * object's class definitions.
+     * 
+     * @param name the name of the object whose object class definition is to 
+     * be retrieved
+     * @return the DirContext containing the named object's class 
+     * definitions; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(Name name)
+        throws NamingException {
+        return getSchemaClassDefinition(name.toString());
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named 
+     * object's class definitions.
+     * 
+     * @param name the name of the object whose object class definition is to 
+     * be retrieved
+     * @return the DirContext containing the named object's class 
+     * definitions; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract DirContext getSchemaClassDefinition(String name)
+        throws NamingException;
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes, and retrieves selected attributes. The search is 
+     * performed using the default SearchControls settings.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates 
+     * that all attributes are to be returned; an empty array indicates that 
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+        throws NamingException {
+        return search(name.toString(), matchingAttributes, attributesToReturn);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes, and retrieves selected attributes.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates 
+     * that all attributes are to be returned; an empty array indicates that 
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract NamingEnumeration search
+        (String name, Attributes matchingAttributes,
+         String[] attributesToReturn)
+        throws NamingException;
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes. This method returns all the attributes of such objects. 
+     * It is equivalent to supplying null as the atributesToReturn parameter 
+     * to the method search(Name, Attributes, String[]).
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, Attributes matchingAttributes)
+        throws NamingException {
+        return search(name.toString(), matchingAttributes);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract NamingEnumeration search
+        (String name, Attributes matchingAttributes)
+        throws NamingException;
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be 
+     * null
+     * @param cons the search controls that control the search. If null, 
+     * the default search controls are used (equivalent to 
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy 
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter specified 
+     * is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls 
+     * contain invalid settings
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search
+        (Name name, String filter, SearchControls cons)
+        throws NamingException {
+        return search(name.toString(), filter, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be 
+     * null
+     * @param cons the search controls that control the search. If null, 
+     * the default search controls are used (equivalent to 
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy 
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter 
+     * specified is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls 
+     * contain invalid settings
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract NamingEnumeration search(String name, String filter, 
+                                             SearchControls cons)
+        throws NamingException;
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search. 
+     * The expression may contain variables of the form "{i}" where i is a 
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the 
+     * variables in filterExpr. The value of filterArgs[i] will replace each 
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the 
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the 
+     * filter; never null
+     * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i} 
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid 
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs 
+     * represents an invalid search filter
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, String filterExpr, 
+                                    Object[] filterArgs, SearchControls cons)
+        throws NamingException {
+        return search(name.toString(), filterExpr, filterArgs, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search. 
+     * The expression may contain variables of the form "{i}" where i is a 
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the 
+     * variables in filterExpr. The value of filterArgs[i] will replace each 
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the 
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the 
+     * filter; never null
+     * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i} 
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid 
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs 
+     * represents an invalid search filter
+     * @exception NamingException if a naming exception is encountered
+     */
+    public abstract NamingEnumeration search
+        (String name, String filterExpr, 
+         Object[] filterArgs, SearchControls cons)
+        throws NamingException;
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/resources/CacheEntry.java b/container/catalina/src/share/org/apache/naming/resources/CacheEntry.java
new file mode 100644
index 0000000..abac861
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/CacheEntry.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming.resources;
+
+import javax.naming.directory.DirContext;
+
+/**
+ * Implements a cache entry.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class CacheEntry {
+    
+    
+    // ------------------------------------------------- Instance Variables
+
+
+    public long timestamp = -1;
+    public String name = null;
+    public ResourceAttributes attributes = null;
+    public Resource resource = null;
+    public DirContext context = null;
+    public boolean exists = true;
+    public long accessCount = 0;
+    public int size = 1;
+
+
+    // ----------------------------------------------------- Public Methods
+
+
+    public void recycle() {
+        timestamp = -1;
+        name = null;
+        attributes = null;
+        resource = null;
+        context = null;
+        exists = true;
+        accessCount = 0;
+        size = 1;
+    }
+
+
+    public String toString() {
+        return ("Cache entry: " + name + "\n"
+                + "Exists: " + exists + "\n"
+                + "Attributes: " + attributes + "\n"
+                + "Resource: " + resource + "\n"
+                + "Context: " + context);
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/Constants.java b/container/catalina/src/share/org/apache/naming/resources/Constants.java
new file mode 100644
index 0000000..a505a22
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/Constants.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.resources;
+
+
+/**
+ * Static constants for this package.
+ */
+
+public final class Constants {
+
+    public static final String PROTOCOL_HANDLER_VARIABLE = 
+        "java.protocol.handler.pkgs";
+
+    public static final String Package = "org.apache.naming.resources";
+
+    // Default namespace name
+    public static final String DEFAULT_NAMESPACE = "DAV:";
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/DirContextURLConnection.java b/container/catalina/src/share/org/apache/naming/resources/DirContextURLConnection.java
new file mode 100644
index 0000000..2a39cdc
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/DirContextURLConnection.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming.resources;
+
+import java.net.URL;
+import java.net.URLConnection;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+import java.security.Permission;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Vector;
+import javax.naming.NamingException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NameClassPair;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import org.apache.naming.JndiPermission;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.ResourceAttributes;
+
+/**
+ * Connection to a JNDI directory context.
+ * <p/>
+ * Note: All the object attribute names are the WebDAV names, not the HTTP 
+ * names, so this class overrides some methods from URLConnection to do the
+ * queries using the right names. Content handler is also not used; the 
+ * content is directly returned.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class DirContextURLConnection 
+    extends URLConnection {
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    
+    public DirContextURLConnection(DirContext context, URL url) {
+        super(url);
+        if (context == null)
+            throw new IllegalArgumentException
+                ("Directory context can't be null");
+        if (System.getSecurityManager() != null) {
+            this.permission = new JndiPermission(url.toString());
+	}
+        this.context = context;
+    }
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * Directory context.
+     */
+    protected DirContext context;
+    
+    
+    /**
+     * Associated resource.
+     */
+    protected Resource resource;
+    
+    
+    /**
+     * Associated DirContext.
+     */
+    protected DirContext collection;
+    
+    
+    /**
+     * Other unknown object.
+     */
+    protected Object object;
+    
+    
+    /**
+     * Attributes.
+     */
+    protected Attributes attributes;
+    
+    
+    /**
+     * Date.
+     */
+    protected long date;
+    
+    
+    /**
+     * Permission
+     */
+    protected Permission permission;
+
+
+    // ------------------------------------------------------------- Properties
+    
+    
+    /**
+     * Connect to the DirContext, and retrive the bound object, as well as
+     * its attributes. If no object is bound with the name specified in the
+     * URL, then an IOException is thrown.
+     * 
+     * @throws IOException Object not found
+     */
+    public void connect()
+        throws IOException {
+        
+        if (!connected) {
+            
+            try {
+                date = System.currentTimeMillis();
+                String path = getURL().getFile();
+                if (context instanceof ProxyDirContext) {
+                    ProxyDirContext proxyDirContext = 
+                        (ProxyDirContext) context;
+                    String hostName = proxyDirContext.getHostName();
+                    String contextName = proxyDirContext.getContextName();
+                    if (hostName != null) {
+                        if (!path.startsWith("/" + hostName + "/"))
+                            return;
+                        path = path.substring(hostName.length()+ 1);
+                    }
+                    if (contextName != null) {
+                        if (!path.startsWith(contextName + "/")) {
+                            return;
+                        } else {
+                            path = path.substring(contextName.length());
+                        }
+                    }
+                }
+                object = context.lookup(path);
+                attributes = context.getAttributes(path);
+                if (object instanceof Resource)
+                    resource = (Resource) object;
+                if (object instanceof DirContext)
+                    collection = (DirContext) object;
+            } catch (NamingException e) {
+                // Object not found
+            }
+            
+            connected = true;
+            
+        }
+        
+    }
+    
+    
+    /**
+     * Return the content length value.
+     */
+    public int getContentLength() {
+        return getHeaderFieldInt(ResourceAttributes.CONTENT_LENGTH, -1);
+    }
+    
+    
+    /**
+     * Return the content type value.
+     */
+    public String getContentType() {
+        return getHeaderField(ResourceAttributes.CONTENT_TYPE);
+    }
+    
+    
+    /**
+     * Return the last modified date.
+     */
+    public long getDate() {
+        return date;
+    }
+    
+    
+    /**
+     * Return the last modified date.
+     */
+    public long getLastModified() {
+
+        if (!connected) {
+            // Try to connect (silently)
+            try {
+                connect();
+            } catch (IOException e) {
+            }
+        }
+
+        if (attributes == null)
+            return 0;
+
+        Attribute lastModified = 
+            attributes.get(ResourceAttributes.LAST_MODIFIED);
+        if (lastModified != null) {
+            try {
+                Date lmDate = (Date) lastModified.get();
+                return lmDate.getTime();
+            } catch (Exception e) {
+            }
+        }
+
+        return 0;
+    }
+    
+    
+    /**
+     * Returns the name of the specified header field.
+     */
+    public String getHeaderField(String name) {
+
+        if (!connected) {
+            // Try to connect (silently)
+            try {
+                connect();
+            } catch (IOException e) {
+            }
+        }
+        
+        if (attributes == null)
+            return (null);
+
+        Attribute attribute = attributes.get(name);
+        try {
+            return attribute.get().toString();
+        } catch (Exception e) {
+            // Shouldn't happen, unless the attribute has no value
+        }
+
+        return (null);
+        
+    }
+    
+    
+    /**
+     * Get object content.
+     */
+    public Object getContent()
+        throws IOException {
+        
+        if (!connected)
+            connect();
+        
+        if (resource != null)
+            return getInputStream();
+        if (collection != null)
+            return collection;
+        if (object != null)
+            return object;
+        
+        throw new FileNotFoundException();
+        
+    }
+    
+    
+    /**
+     * Get object content.
+     */
+    public Object getContent(Class[] classes)
+        throws IOException {
+        
+        Object object = getContent();
+        
+        for (int i = 0; i < classes.length; i++) {
+            if (classes[i].isInstance(object))
+                return object;
+        }
+        
+        return null;
+        
+    }
+    
+    
+    /**
+     * Get input stream.
+     */
+    public InputStream getInputStream() 
+        throws IOException {
+        
+        if (!connected)
+            connect();
+        
+        if (resource == null) {
+            throw new FileNotFoundException();
+        } else {
+            // Reopen resource
+            try {
+                resource = (Resource) context.lookup(getURL().getFile());
+            } catch (NamingException e) {
+            }
+        }
+        
+        return (resource.streamContent());
+        
+    }
+    
+    
+    /**
+     * Get the Permission for this URL
+     */
+    public Permission getPermission() {
+
+        return permission;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * List children of this collection. The names given are relative to this
+     * URI's path. The full uri of the children is then : path + "/" + name.
+     */
+    public Enumeration list()
+        throws IOException {
+        
+        if (!connected) {
+            connect();
+        }
+        
+        if ((resource == null) && (collection == null)) {
+            throw new FileNotFoundException();
+        }
+        
+        Vector result = new Vector();
+        
+        if (collection != null) {
+            try {
+                NamingEnumeration enumeration = context.list(getURL().getFile());
+                while (enumeration.hasMoreElements()) {
+                    NameClassPair ncp = (NameClassPair) enumeration.nextElement();
+                    result.addElement(ncp.getName());
+                }
+            } catch (NamingException e) {
+                // Unexpected exception
+                throw new FileNotFoundException();
+            }
+        }
+        
+        return result.elements();
+        
+    }
+    
+    
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/DirContextURLStreamHandler.java b/container/catalina/src/share/org/apache/naming/resources/DirContextURLStreamHandler.java
new file mode 100644
index 0000000..41a53c1
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/DirContextURLStreamHandler.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming.resources;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.util.Hashtable;
+
+import javax.naming.directory.DirContext;
+
+/**
+ * Stream handler to a JNDI directory context.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class DirContextURLStreamHandler 
+    extends URLStreamHandler {
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    
+    public DirContextURLStreamHandler() {
+    }
+    
+    
+    public DirContextURLStreamHandler(DirContext context) {
+        this.context = context;
+    }
+    
+    
+    // -------------------------------------------------------------- Variables
+    
+    
+    /**
+     * Bindings class loader - directory context. Keyed by CL id.
+     */
+    private static Hashtable clBindings = new Hashtable();
+    
+    
+    /**
+     * Bindings thread - directory context. Keyed by thread id.
+     */
+    private static Hashtable threadBindings = new Hashtable();
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * Directory context.
+     */
+    protected DirContext context = null;
+    
+    
+    // ------------------------------------------------------------- Properties
+    
+    
+    // ----------------------------------------------- URLStreamHandler Methods
+    
+    
+    /**
+     * Opens a connection to the object referenced by the <code>URL</code> 
+     * argument.
+     */
+    protected URLConnection openConnection(URL u) 
+        throws IOException {
+        DirContext currentContext = this.context;
+        if (currentContext == null)
+            currentContext = get();
+        return new DirContextURLConnection(currentContext, u);
+    }
+    
+    
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Set the java.protocol.handler.pkgs system property.
+     */
+    public static void setProtocolHandler() {
+        String value = System.getProperty(Constants.PROTOCOL_HANDLER_VARIABLE);
+        if (value == null) {
+            value = Constants.Package;
+            System.setProperty(Constants.PROTOCOL_HANDLER_VARIABLE, value);
+        } else if (value.indexOf(Constants.Package) == -1) {
+            value += "|" + Constants.Package;
+            System.setProperty(Constants.PROTOCOL_HANDLER_VARIABLE, value);
+        }
+    }
+    
+    
+    /**
+     * Returns true if the thread or the context class loader of the current 
+     * thread is bound.
+     */
+    public static boolean isBound() {
+        return (clBindings.containsKey
+                (Thread.currentThread().getContextClassLoader()))
+            || (threadBindings.containsKey(Thread.currentThread()));
+    }
+    
+    
+    /**
+     * Binds a directory context to a class loader.
+     */
+    public static void bind(DirContext dirContext) {
+        ClassLoader currentCL = 
+            Thread.currentThread().getContextClassLoader();
+        if (currentCL != null)
+            clBindings.put(currentCL, dirContext);
+    }
+    
+    
+    /**
+     * Unbinds a directory context to a class loader.
+     */
+    public static void unbind() {
+        ClassLoader currentCL = 
+            Thread.currentThread().getContextClassLoader();
+        if (currentCL != null)
+            clBindings.remove(currentCL);
+    }
+    
+    
+    /**
+     * Binds a directory context to a thread.
+     */
+    public static void bindThread(DirContext dirContext) {
+        threadBindings.put(Thread.currentThread(), dirContext);
+    }
+    
+    
+    /**
+     * Unbinds a directory context to a thread.
+     */
+    public static void unbindThread() {
+        threadBindings.remove(Thread.currentThread());
+    }
+    
+    
+    /**
+     * Get the bound context.
+     */
+    public static DirContext get() {
+
+        DirContext result = null;
+
+        Thread currentThread = Thread.currentThread();
+        ClassLoader currentCL = currentThread.getContextClassLoader();
+
+        // Checking CL binding
+        result = (DirContext) clBindings.get(currentCL);
+        if (result != null)
+            return result;
+
+        // Checking thread biding
+        result = (DirContext) threadBindings.get(currentThread);
+
+        // Checking parent CL binding
+        currentCL = currentCL.getParent();
+        while (currentCL != null) {
+            result = (DirContext) clBindings.get(currentCL);
+            if (result != null)
+                return result;
+            currentCL = currentCL.getParent();
+        }
+
+        if (result == null)
+            throw new IllegalStateException("Illegal class loader binding");
+
+        return result;
+
+    }
+    
+    
+    /**
+     * Binds a directory context to a class loader.
+     */
+    public static void bind(ClassLoader cl, DirContext dirContext) {
+        clBindings.put(cl, dirContext);
+    }
+    
+    
+    /**
+     * Unbinds a directory context to a class loader.
+     */
+    public static void unbind(ClassLoader cl) {
+        clBindings.remove(cl);
+    }
+    
+    
+    /**
+     * Get the bound context.
+     */
+    public static DirContext get(ClassLoader cl) {
+        return (DirContext) clBindings.get(cl);
+    }
+    
+    
+    /**
+     * Get the bound context.
+     */
+    public static DirContext get(Thread thread) {
+        return (DirContext) threadBindings.get(thread);
+    }
+    
+    
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/DirContextURLStreamHandlerFactory.java b/container/catalina/src/share/org/apache/naming/resources/DirContextURLStreamHandlerFactory.java
new file mode 100644
index 0000000..3239f41
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/DirContextURLStreamHandlerFactory.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming.resources;
+
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+
+/**
+ * Factory for Stream handlers to a JNDI directory context.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class DirContextURLStreamHandlerFactory 
+    implements URLStreamHandlerFactory {
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    
+    public DirContextURLStreamHandlerFactory() {
+    }
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    // ------------------------------------------------------------- Properties
+    
+    
+    // ---------------------------------------- URLStreamHandlerFactory Methods
+    
+    
+    /**
+     * Creates a new URLStreamHandler instance with the specified protocol.
+     * Will return null if the protocol is not <code>jndi</code>.
+     * 
+     * @param protocol the protocol (must be "jndi" here)
+     * @return a URLStreamHandler for the jndi protocol, or null if the 
+     * protocol is not JNDI
+     */
+    public URLStreamHandler createURLStreamHandler(String protocol) {
+        if (protocol.equals("jndi")) {
+            return new DirContextURLStreamHandler();
+        } else {
+            return null;
+        }
+    }
+    
+    
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/FileDirContext.java b/container/catalina/src/share/org/apache/naming/resources/FileDirContext.java
new file mode 100644
index 0000000..5a172e3
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/FileDirContext.java
@@ -0,0 +1,1134 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.naming.resources;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Hashtable;
+
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+import org.apache.naming.NamingContextBindingsEnumeration;
+import org.apache.naming.NamingContextEnumeration;
+import org.apache.naming.NamingEntry;
+
+/**
+ * Filesystem Directory Context implementation helper class.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class FileDirContext extends BaseDirContext {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( FileDirContext.class );
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    protected static final int BUFFER_SIZE = 2048;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Builds a file directory context using the given environment.
+     */
+    public FileDirContext() {
+        super();
+    }
+
+
+    /**
+     * Builds a file directory context using the given environment.
+     */
+    public FileDirContext(Hashtable env) {
+        super(env);
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The document base directory.
+     */
+    protected File base = null;
+
+
+    /**
+     * Absolute normalized filename of the base.
+     */
+    protected String absoluteBase = null;
+
+
+    /**
+     * Case sensitivity.
+     */
+    protected boolean caseSensitive = true;
+
+
+    /**
+     * Allow linking.
+     */
+    protected boolean allowLinking = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the document root.
+     *
+     * @param docBase The new document root
+     *
+     * @exception IllegalArgumentException if the specified value is not
+     *  supported by this implementation
+     * @exception IllegalArgumentException if this would create a
+     *  malformed URL
+     */
+    public void setDocBase(String docBase) {
+
+    // Validate the format of the proposed document root
+    if (docBase == null)
+        throw new IllegalArgumentException
+        (sm.getString("resources.null"));
+
+    // Calculate a File object referencing this document base directory
+    base = new File(docBase);
+        try {
+            base = base.getCanonicalFile();
+        } catch (IOException e) {
+            // Ignore
+        }
+
+    // Validate that the document base is an existing directory
+    if (!base.exists() || !base.isDirectory() || !base.canRead())
+        throw new IllegalArgumentException
+        (sm.getString("fileResources.base", docBase));
+        this.absoluteBase = base.getAbsolutePath();
+        super.setDocBase(docBase);
+
+    }
+
+
+    /**
+     * Set case sensitivity.
+     */
+    public void setCaseSensitive(boolean caseSensitive) {
+        this.caseSensitive = caseSensitive;
+    }
+
+
+    /**
+     * Is case sensitive ?
+     */
+    public boolean isCaseSensitive() {
+        return caseSensitive;
+    }
+
+
+    /**
+     * Set allow linking.
+     */
+    public void setAllowLinking(boolean allowLinking) {
+        this.allowLinking = allowLinking;
+    }
+
+
+    /**
+     * Is linking allowed.
+     */
+    public boolean getAllowLinking() {
+        return allowLinking;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Release any resources allocated for this directory context.
+     */
+    public void release() {
+
+        caseSensitive = true;
+        allowLinking = false;
+        absoluteBase = null;
+        base = null;
+        super.release();
+
+    }
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Retrieves the named object.
+     *
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(String name)
+        throws NamingException {
+        Object result = null;
+        File file = file(name);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+
+        if (file.isDirectory()) {
+            FileDirContext tempContext = new FileDirContext(env);
+            tempContext.setDocBase(file.getPath());
+            tempContext.setAllowLinking(getAllowLinking());
+            tempContext.setCaseSensitive(isCaseSensitive());
+            result = tempContext;
+        } else {
+            result = new FileResource(file);
+        }
+
+        return result;
+
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name
+     * from the target context--that named by all but the terminal atomic
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic
+     * name is not bound in the target context, but throws
+     * NameNotFoundException if any of the intermediate contexts do not exist.
+     *
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(String name)
+        throws NamingException {
+
+        File file = file(name);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+
+        if (!file.delete())
+            throw new NamingException
+                (sm.getString("resources.unbindFailed", name));
+
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the
+     * old name. Both names are relative to this context. Any attributes
+     * associated with the old name become associated with the new name.
+     * Intermediate contexts of the old name are not changed.
+     *
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(String oldName, String newName)
+        throws NamingException {
+
+        File file = file(oldName);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", oldName));
+
+        File newFile = new File(base, newName);
+
+        file.renameTo(newFile);
+
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class
+     * names of objects bound to them. The contents of any subcontexts are
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on
+     * an enumeration previously returned is undefined.
+     *
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(String name)
+        throws NamingException {
+
+        File file = file(name);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+
+        return new NamingContextEnumeration(list(file).iterator());
+
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the
+     * objects bound to them. The contents of any subcontexts are not
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on
+     * an enumeration previously returned is undefined.
+     *
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context.
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(String name)
+        throws NamingException {
+
+        File file = file(name);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+
+        return new NamingContextBindingsEnumeration(list(file).iterator());
+
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any
+     * attributes associated with the name are also removed. Intermediate
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic
+     * name is not bound in the target context, but throws
+     * NameNotFoundException if any of the intermediate contexts do not exist.
+     *
+     * In a federated naming system, a context from one naming system may be
+     * bound to a name in another. One can subsequently look up and perform
+     * operations on the foreign context using a composite name. However, an
+     * attempt destroy the context using this composite name will fail with
+     * NotContextException, because the foreign context is not a "subcontext"
+     * of the context in which it is bound. Instead, use unbind() to remove
+     * the binding of the foreign context. Destroying the foreign context
+     * requires that the destroySubcontext() be performed on a context from
+     * the foreign context's "native" naming system.
+     *
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not
+     * exist
+     * @exception NotContextException if the name is bound but does not name
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(String name)
+        throws NamingException {
+        unbind(name);
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal
+     * atomic component of the name. If the object bound to name is not a
+     * link, returns the object itself.
+     *
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(String name)
+        throws NamingException {
+        // Note : Links are not supported
+        return lookup(name);
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in
+     * their respective namespaces. For example, an LDAP entry has a
+     * distinguished name, and a DNS record has a fully qualified name. This
+     * method allows the client application to retrieve this name. The string
+     * returned by this method is not a JNDI composite name and should not be
+     * passed directly to context methods. In naming systems for which the
+     * notion of full name does not make sense,
+     * OperationNotSupportedException is thrown.
+     *
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+        throws NamingException {
+        return docBase;
+    }
+
+
+    // ----------------------------------------------------- DirContext Methods
+
+
+    /**
+     * Retrieves selected attributes associated with a named object.
+     * See the class description regarding attribute models, attribute type
+     * names, and operational attributes.
+     *
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null
+     * indicates that all attributes should be retrieved; an empty array
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(String name, String[] attrIds)
+        throws NamingException {
+
+        // Building attribute list
+        File file = file(name);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+
+        return new FileResourceAttributes(file);
+
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object. The order of
+     * the modifications is not specified. Where possible, the modifications
+     * are performed atomically.
+     *
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE,
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, int mod_op, Attributes attrs)
+        throws NamingException {
+
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an
+     * ordered list of modifications. The modifications are performed in the
+     * order specified. Each modification specifies a modification operation
+     * code and an attribute on which to operate. Where possible, the
+     * modifications are performed atomically.
+     *
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, ModificationItem[] mods)
+        throws NamingException {
+
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes. If attrs
+     * is null, the resulting binding will have the attributes associated
+     * with obj if obj is a DirContext, and no attributes otherwise. If attrs
+     * is non-null, the resulting binding will have attrs as its attributes;
+     * any attributes associated with obj are ignored.
+     *
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+
+        // Note: No custom attributes allowed
+
+        File file = new File(base, name);
+        if (file.exists())
+            throw new NameAlreadyBoundException
+                (sm.getString("resources.alreadyBound", name));
+
+        rebind(name, obj, attrs);
+
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes,
+     * overwriting any existing binding. If attrs is null and obj is a
+     * DirContext, the attributes from obj are used. If attrs is null and obj
+     * is not a DirContext, any existing attributes associated with the object
+     * already bound in the directory remain unchanged. If attrs is non-null,
+     * any existing attributes associated with the object already bound in
+     * the directory are removed and attrs is associated with the named
+     * object. If obj is a DirContext and attrs is non-null, the attributes
+     * of obj are ignored.
+     *
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+
+        // Note: No custom attributes allowed
+        // Check obj type
+
+        File file = new File(base, name);
+
+        InputStream is = null;
+        if (obj instanceof Resource) {
+            try {
+                is = ((Resource) obj).streamContent();
+            } catch (IOException e) {
+            }
+        } else if (obj instanceof InputStream) {
+            is = (InputStream) obj;
+        } else if (obj instanceof DirContext) {
+            if (file.exists()) {
+                if (!file.delete())
+                    throw new NamingException
+                        (sm.getString("resources.bindFailed", name));
+            }
+            if (!file.mkdir())
+                throw new NamingException
+                    (sm.getString("resources.bindFailed", name));
+        }
+        if (is == null)
+            throw new NamingException
+                (sm.getString("resources.bindFailed", name));
+
+        // Open os
+
+        try {
+            FileOutputStream os = null;
+            byte buffer[] = new byte[BUFFER_SIZE];
+            int len = -1;
+            try {
+                os = new FileOutputStream(file);
+                while (true) {
+                    len = is.read(buffer);
+                    if (len == -1)
+                        break;
+                    os.write(buffer, 0, len);
+                }
+            } finally {
+                if (os != null)
+                    os.close();
+                is.close();
+            }
+        } catch (IOException e) {
+            throw new NamingException
+                (sm.getString("resources.bindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes.
+     * This method creates a new subcontext with the given name, binds it in
+     * the target context (that named by all but terminal atomic component of
+     * the name), and associates the supplied attributes with the newly
+     * created object. All intermediate and target contexts must already
+     * exist. If attrs is null, this method is equivalent to
+     * Context.createSubcontext().
+     *
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(String name, Attributes attrs)
+        throws NamingException {
+
+        File file = new File(base, name);
+        if (file.exists())
+            throw new NameAlreadyBoundException
+                (sm.getString("resources.alreadyBound", name));
+        if (!file.mkdir())
+            throw new NamingException
+                (sm.getString("resources.bindFailed", name));
+        return (DirContext) lookup(name);
+
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object. The schema
+     * describes rules regarding the structure of the namespace and the
+     * attributes stored within it. The schema specifies what types of
+     * objects can be added to the directory and where they can be added;
+     * what mandatory and optional attributes an object can have. The range
+     * of support for schemas is directory-specific.
+     *
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named
+     * object's class definitions.
+     *
+     * @param name the name of the object whose object class definition is to
+     * be retrieved
+     * @return the DirContext containing the named object's class
+     * definitions; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set
+     * of attributes, and retrieves selected attributes. The search is
+     * performed using the default SearchControls settings.
+     *
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates
+     * that all attributes are to be returned; an empty array indicates that
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each
+     * SearchResult contains the attributes identified by attributesToReturn
+     * and the name of the corresponding object, named relative to the
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+        throws NamingException {
+        return null;
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set
+     * of attributes. This method returns all the attributes of such objects.
+     * It is equivalent to supplying null as the atributesToReturn parameter
+     * to the method search(Name, Attributes, String[]).
+     *
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each
+     * SearchResult contains the attributes identified by attributesToReturn
+     * and the name of the corresponding object, named relative to the
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes)
+        throws NamingException {
+        return null;
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be
+     * null
+     * @param cons the search controls that control the search. If null,
+     * the default search controls are used (equivalent to
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter specified
+     * is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls
+     * contain invalid settings
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filter,
+                                    SearchControls cons)
+        throws NamingException {
+        return null;
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search.
+     * The expression may contain variables of the form "{i}" where i is a
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the
+     * variables in filterExpr. The value of filterArgs[i] will replace each
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the
+     * filter; never null
+     * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i}
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs
+     * represents an invalid search filter
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filterExpr,
+                                    Object[] filterArgs, SearchControls cons)
+        throws NamingException {
+        return null;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return a context-relative path, beginning with a "/", that represents
+     * the canonical version of the specified path after ".." and "." elements
+     * are resolved out.  If the specified path attempts to go outside the
+     * boundaries of the current context (i.e. too many ".." path elements
+     * are present), return <code>null</code> instead.
+     *
+     * @param path Path to be normalized
+     */
+    protected String normalize(String path) {
+
+    String normalized = path;
+
+    // Normalize the slashes and add leading slash if necessary
+    if (normalized.indexOf('\\') >= 0)
+        normalized = normalized.replace('\\', '/');
+    if (!normalized.startsWith("/"))
+        normalized = "/" + normalized;
+
+    // Resolve occurrences of "//" in the normalized path
+    while (true) {
+        int index = normalized.indexOf("//");
+        if (index < 0)
+        break;
+        normalized = normalized.substring(0, index) +
+        normalized.substring(index + 1);
+    }
+
+    // Resolve occurrences of "/./" in the normalized path
+    while (true) {
+        int index = normalized.indexOf("/./");
+        if (index < 0)
+        break;
+        normalized = normalized.substring(0, index) +
+        normalized.substring(index + 2);
+    }
+
+    // Resolve occurrences of "/../" in the normalized path
+    while (true) {
+        int index = normalized.indexOf("/../");
+        if (index < 0)
+        break;
+        if (index == 0)
+        return (null);  // Trying to go outside our context
+        int index2 = normalized.lastIndexOf('/', index - 1);
+        normalized = normalized.substring(0, index2) +
+        normalized.substring(index + 3);
+    }
+
+    // Return the normalized path that we have completed
+    return (normalized);
+
+    }
+
+
+    /**
+     * Return a File object representing the specified normalized
+     * context-relative path if it exists and is readable.  Otherwise,
+     * return <code>null</code>.
+     *
+     * @param name Normalized context-relative path (with leading '/')
+     */
+    protected File file(String name) {
+
+        File file = new File(base, name);
+        if (file.exists() && file.canRead()) {
+
+        	if (allowLinking)
+        		return file;
+        	
+            // Check that this file belongs to our root path
+            String canPath = null;
+            try {
+                canPath = file.getCanonicalPath();
+            } catch (IOException e) {
+            }
+            if (canPath == null)
+                return null;
+
+            // Check to see if going outside of the web application root
+            if (!canPath.startsWith(absoluteBase)) {
+                return null;
+            }
+
+            // Case sensitivity check
+            if (caseSensitive) {
+                String fileAbsPath = file.getAbsolutePath();
+                if (fileAbsPath.endsWith("."))
+                    fileAbsPath = fileAbsPath + "/";
+                String absPath = normalize(fileAbsPath);
+                if (canPath != null)
+                    canPath = normalize(canPath);
+                if ((absoluteBase.length() < absPath.length())
+                    && (absoluteBase.length() < canPath.length())) {
+                    absPath = absPath.substring(absoluteBase.length() + 1);
+                    if ((canPath == null) || (absPath == null))
+                        return null;
+                    if (absPath.equals(""))
+                        absPath = "/";
+                    canPath = canPath.substring(absoluteBase.length() + 1);
+                    if (canPath.equals(""))
+                        canPath = "/";
+                    if (!canPath.equals(absPath))
+                        return null;
+                }
+            }
+
+        } else {
+            return null;
+        }
+        return file;
+
+    }
+
+
+    /**
+     * List the resources which are members of a collection.
+     *
+     * @param file Collection
+     * @return Vector containg NamingEntry objects
+     */
+    protected ArrayList list(File file) {
+
+        ArrayList entries = new ArrayList();
+        if (!file.isDirectory())
+            return entries;
+        String[] names = file.list();
+        if (names==null) {
+            /* Some IO error occurred such as bad file permissions.
+               Prevent a NPE with Arrays.sort(names) */
+            log.warn(sm.getString("fileResources.listingNull",
+                                  file.getAbsolutePath()));
+            return entries;
+        }
+
+        Arrays.sort(names);             // Sort alphabetically
+        if (names == null)
+            return entries;
+        NamingEntry entry = null;
+
+        for (int i = 0; i < names.length; i++) {
+
+            File currentFile = new File(file, names[i]);
+            Object object = null;
+            if (currentFile.isDirectory()) {
+                FileDirContext tempContext = new FileDirContext(env);
+                tempContext.setDocBase(file.getPath());
+                tempContext.setAllowLinking(getAllowLinking());
+                tempContext.setCaseSensitive(isCaseSensitive());
+                object = tempContext;
+            } else {
+                object = new FileResource(currentFile);
+            }
+            entry = new NamingEntry(names[i], object, NamingEntry.ENTRY);
+            entries.add(entry);
+
+        }
+
+        return entries;
+
+    }
+
+
+    // ----------------------------------------------- FileResource Inner Class
+
+
+    /**
+     * This specialized resource implementation avoids opening the IputStream
+     * to the file right away (which would put a lock on the file).
+     */
+    protected class FileResource extends Resource {
+
+
+        // -------------------------------------------------------- Constructor
+
+
+        public FileResource(File file) {
+            this.file = file;
+        }
+
+
+        // --------------------------------------------------- Member Variables
+
+
+        /**
+         * Associated file object.
+         */
+        protected File file;
+
+
+        /**
+         * File length.
+         */
+        protected long length = -1L;
+
+
+        // --------------------------------------------------- Resource Methods
+
+
+        /**
+         * Content accessor.
+         *
+         * @return InputStream
+         */
+        public InputStream streamContent()
+            throws IOException {
+            if (binaryContent == null) {
+                inputStream = new FileInputStream(file);
+            }
+            return super.streamContent();
+        }
+
+
+    }
+
+
+    // ------------------------------------- FileResourceAttributes Inner Class
+
+
+    /**
+     * This specialized resource attribute implementation does some lazy
+     * reading (to speed up simple checks, like checking the last modified
+     * date).
+     */
+    protected class FileResourceAttributes extends ResourceAttributes {
+
+
+        // -------------------------------------------------------- Constructor
+
+
+        public FileResourceAttributes(File file) {
+            this.file = file;
+        }
+
+        // --------------------------------------------------- Member Variables
+
+
+        protected File file;
+
+
+        protected boolean accessed = false;
+
+
+        protected String canonicalPath = null;
+
+
+        // ----------------------------------------- ResourceAttributes Methods
+
+
+        /**
+         * Is collection.
+         */
+        public boolean isCollection() {
+            if (!accessed) {
+                collection = file.isDirectory();
+                accessed = true;
+            }
+            return super.isCollection();
+        }
+
+
+        /**
+         * Get content length.
+         *
+         * @return content length value
+         */
+        public long getContentLength() {
+            if (contentLength != -1L)
+                return contentLength;
+            contentLength = file.length();
+            return contentLength;
+        }
+
+
+        /**
+         * Get creation time.
+         *
+         * @return creation time value
+         */
+        public long getCreation() {
+            if (creation != -1L)
+                return creation;
+            creation = file.lastModified();
+            return creation;
+        }
+
+
+        /**
+         * Get creation date.
+         *
+         * @return Creation date value
+         */
+        public Date getCreationDate() {
+            if (creation == -1L) {
+                creation = file.lastModified();
+            }
+            return super.getCreationDate();
+        }
+
+
+        /**
+         * Get last modified time.
+         *
+         * @return lastModified time value
+         */
+        public long getLastModified() {
+            if (lastModified != -1L)
+                return lastModified;
+            lastModified = file.lastModified();
+            return lastModified;
+        }
+
+
+        /**
+         * Get lastModified date.
+         *
+         * @return LastModified date value
+         */
+        public Date getLastModifiedDate() {
+            if (lastModified == -1L) {
+                lastModified = file.lastModified();
+            }
+            return super.getLastModifiedDate();
+        }
+
+
+        /**
+         * Get name.
+         *
+         * @return Name value
+         */
+        public String getName() {
+            if (name == null)
+                name = file.getName();
+            return name;
+        }
+
+
+        /**
+         * Get resource type.
+         *
+         * @return String resource type
+         */
+        public String getResourceType() {
+            if (!accessed) {
+                collection = file.isDirectory();
+                accessed = true;
+            }
+            return super.getResourceType();
+        }
+
+        
+        /**
+         * Get canonical path.
+         * 
+         * @return String the file's canonical path
+         */
+        public String getCanonicalPath() {
+            if (canonicalPath == null) {
+                try {
+                    canonicalPath = file.getCanonicalPath();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+            return canonicalPath;
+        }
+        
+
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/resources/ImmutableNameNotFoundException.java b/container/catalina/src/share/org/apache/naming/resources/ImmutableNameNotFoundException.java
new file mode 100644
index 0000000..3def3d2
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/ImmutableNameNotFoundException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming.resources;
+
+import javax.naming.Name;
+import javax.naming.NameNotFoundException;
+
+/**
+ * Immutable exception to avoid useless object creation by the proxy context.
+ * This should be used only by the proxy context. Actual contexts should return
+ * properly populated exceptions.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class ImmutableNameNotFoundException
+    extends NameNotFoundException {
+
+    public void appendRemainingComponent(String name) {}
+    public void appendRemainingName(Name name) {}
+    public void setRemainingName(Name name) {}
+    public void setResolverName(Name name) {}
+    public void setRootCause(Throwable e) {}
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/LocalStrings.properties b/container/catalina/src/share/org/apache/naming/resources/LocalStrings.properties
new file mode 100644
index 0000000..36aa5f8
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/LocalStrings.properties
@@ -0,0 +1,21 @@
+fileResources.base=Document base {0} does not exist or is not a readable directory
+fileResources.listingNull=Could not get dir listing for {0}
+warResources.notWar=Doc base must point to a WAR file
+warResources.invalidWar=Invalid or unreadable WAR file : {0}
+jarResources.syntax=Document base {0} must start with ''jar:'' and end with ''!/''
+resources.alreadyStarted=Resources has already been started
+resources.connect=Cannot connect to document base {0}
+resources.input=Cannot create input stream for resource {0}
+resources.notStarted=Resources has not yet been started
+resources.null=Document base cannot be null
+resources.notFound=Resource {0} not found
+resources.path=Context relative path {0} must start with ''/''
+resources.alreadyBound=Name {0} is already bound in this Context
+resources.bindFailed=Bind failed: {0}
+resources.unbindFailed=Unbind failed: {0}
+standardResources.alreadyStarted=Resources has already been started
+standardResources.directory=File base {0} is not a directory
+standardResources.exists=File base {0} does not exist
+standardResources.notStarted=Resources has not yet been started
+standardResources.null=Document base cannot be null
+standardResources.slash=Document base {0} must not end with a slash
diff --git a/container/catalina/src/share/org/apache/naming/resources/LocalStrings_es.properties b/container/catalina/src/share/org/apache/naming/resources/LocalStrings_es.properties
new file mode 100644
index 0000000..4e4aa3c
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/LocalStrings_es.properties
@@ -0,0 +1,20 @@
+fileResources.base=El Documento base {0} no existe o no es un directorio legible
+warResources.notWar=Doc base debe de apuntar a un archivo WAR
+warResources.invalidWar=Archivo WAR inválido o ilegible: {0}
+jarResources.syntax=Documento base {0} debe de empezar con ''jar:'' y acabar con ''!/''
+resources.alreadyStarted=Ya han sido arrancados los Recursos
+resources.connect=No puedo conectar a documento base {0}
+resources.input=No puedo crear flujo (stream) de entrada para recurso {0}
+resources.notStarted=Aún no han sido arrancados los Recursos
+resources.null=El Documento base no puede ser nulo
+resources.notFound=Recurso {0} no hallado
+resources.path=Trayectoria relativa a contexto {0} debe de comenzar con ''/''
+resources.alreadyBound=El Nombre {0} ya ha sido cambiado (bound) en este Contexto
+resources.bindFailed=Falló el Cambio (Bind): {0}
+resources.unbindFailed=Falló el Descambio (Unbind): {0}
+standardResources.alreadyStarted=Ya han sido arrancados los Recursos
+standardResources.directory=El archivo base {0} no es un directorio
+standardResources.exists=El archivo base {0} no existe
+standardResources.notStarted=Aún no han sido arrancados los Recursos
+standardResources.null=El Documento base no puede ser nulo
+standardResources.slash=El Documento base {0} no debe de terminar con una barra
diff --git a/container/catalina/src/share/org/apache/naming/resources/LocalStrings_fr.properties b/container/catalina/src/share/org/apache/naming/resources/LocalStrings_fr.properties
new file mode 100644
index 0000000..06ee064
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/LocalStrings_fr.properties
@@ -0,0 +1,20 @@
+fileResources.base=Le document base {0} n''existe pas ou n''est pas un répertoire lisible
+warResources.notWar=Doc base doit pointé vers un fichier WAR
+warResources.invalidWar=Fichier WAR invalide ou illisible  : {0}
+jarResources.syntax=Le document base {0} doit commencé par ''jar:'' et finir avec ''!/''
+resources.alreadyStarted=Les Ressources ont déjà été démarrées
+resources.connect=Impossible de se connecter au document base {0}
+resources.input=Impossible de créer l''input stream pour la ressource {0}
+resources.notStarted=Les ressources n''ont pas encore été démarrées
+resources.null=Le document base ne peut être nul
+resources.notFound=La ressource {0} est introuvable
+resources.path=Le chemin relatif de context {0} doit commencé par ''/''
+resources.alreadyBound=Le nom {0} est déjà référencé par ce contexte
+resources.bindFailed=Le liage a échoué: {0}
+resources.unbindFailed=Le déliage a échoué: {0}
+standardResources.alreadyStarted=Les ressources ont déja été démarrées
+standardResources.directory=Le file base {0} n''est pas un répertoire
+standardResources.exists=Le file base {0} n''existe pas
+standardResources.notStarted=Les ressources n''ont pas encore été démarrées
+standardResources.null=Le document base ne peut être nul
+standardResources.slash=Le document base {0} ne doit pas se terminer par un ''/''
diff --git a/container/catalina/src/share/org/apache/naming/resources/LocalStrings_ja.properties b/container/catalina/src/share/org/apache/naming/resources/LocalStrings_ja.properties
new file mode 100644
index 0000000..a991455
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/LocalStrings_ja.properties
@@ -0,0 +1,21 @@
+fileResources.base=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9 {0} \u304c\u5b58\u5728\u3057\u306a\u3044\u3001\u53c8\u306f\u8aad\u3081\u306a\u3044\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3067\u3059
+fileResources.listingNull={0} \u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306e\u30ea\u30b9\u30c8\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093
+warResources.notWar=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u306fWAR\u30d5\u30a1\u30a4\u30eb\u3092\u793a\u3055\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+warResources.invalidWar=\u7121\u52b9\u53c8\u306f\u8aad\u3081\u306a\u3044WAR\u30d5\u30a1\u30a4\u30eb\u3067\u3059 : {0}
+jarResources.syntax=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9 {0} \u306f''jar:''\u3067\u59cb\u307e\u308a\u3001''!/''\u3067\u7d42\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+resources.alreadyStarted=\u30ea\u30bd\u30fc\u30b9\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+resources.connect=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9 {0} \u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093
+resources.input=\u30ea\u30bd\u30fc\u30b9 {0} \u306b\u5165\u529b\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093
+resources.notStarted=\u30ea\u30bd\u30fc\u30b9\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+resources.null=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u306fnull\u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+resources.notFound=\u30ea\u30bd\u30fc\u30b9 {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+resources.path=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u76f8\u5bfe\u30d1\u30b9 {0} \u306f''/''\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+resources.alreadyBound=\u540d\u524d {0} \u306f\u65e2\u306b\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u3059
+resources.bindFailed=\u30d0\u30a4\u30f3\u30c9\u304c\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+resources.unbindFailed=\u30a2\u30f3\u30d0\u30a4\u30f3\u30c9\u304c\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+standardResources.alreadyStarted=\u30ea\u30bd\u30fc\u30b9\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardResources.directory=\u30d5\u30a1\u30a4\u30eb\u30d9\u30fc\u30b9 {0} \u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+standardResources.exists=\u30d5\u30a1\u30a4\u30eb\u30d9\u30fc\u30b9 {0} \u306f\u5b58\u5728\u3057\u307e\u305b\u3093
+standardResources.notStarted=\u30ea\u30bd\u30fc\u30b9\u304c\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardResources.null=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u306fnull\u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+standardResources.slash=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9 {0} \u306f\u30b9\u30e9\u30c3\u30b7\u30e5\u3067\u7d42\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
diff --git a/container/catalina/src/share/org/apache/naming/resources/ProxyDirContext.java b/container/catalina/src/share/org/apache/naming/resources/ProxyDirContext.java
new file mode 100644
index 0000000..b91a8f9
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/ProxyDirContext.java
@@ -0,0 +1,1620 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.resources;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameNotFoundException;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+import org.apache.naming.StringManager;
+
+/**
+ * Proxy Directory Context implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ProxyDirContext implements DirContext {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String CONTEXT = "context";
+    public static final String HOST = "host";
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Builds a proxy directory context using the given environment.
+     */
+    public ProxyDirContext(Hashtable env, DirContext dirContext) {
+        this.env = env;
+        this.dirContext = dirContext;
+        if (dirContext instanceof BaseDirContext) {
+            // Initialize parameters based on the associated dir context, like
+            // the caching policy.
+            BaseDirContext baseDirContext = (BaseDirContext) dirContext;
+            if (baseDirContext.isCached()) {
+                try {
+                    cache = (ResourceCache) 
+                        Class.forName(cacheClassName).newInstance();
+                } catch (Exception e) {
+                    //FIXME
+                    e.printStackTrace();
+                }
+                cache.setCacheMaxSize(baseDirContext.getCacheMaxSize());
+                cacheTTL = baseDirContext.getCacheTTL();
+                cacheObjectMaxSize = baseDirContext.getCacheMaxSize() / 20;
+            }
+        }
+        hostName = (String) env.get(HOST);
+        contextName = (String) env.get(CONTEXT);
+    }
+
+
+    /**
+     * Builds a clone of this proxy dir context, wrapping the given directory
+     * context, and sharing the same cache.
+     */
+    // TODO: Refactor using the proxy field
+    /*
+    protected ProxyDirContext(ProxyDirContext proxyDirContext, 
+                              DirContext dirContext, String vPath) {
+        this.env = proxyDirContext.env;
+        this.dirContext = dirContext;
+        this.vPath = vPath;
+        this.cache = proxyDirContext.cache;
+        this.cacheMaxSize = proxyDirContext.cacheMaxSize;
+        this.cacheSize = proxyDirContext.cacheSize;
+        this.cacheTTL = proxyDirContext.cacheTTL;
+        this.cacheObjectMaxSize = proxyDirContext.cacheObjectMaxSize;
+        this.notFoundCache = proxyDirContext.notFoundCache;
+        this.hostName = proxyDirContext.hostName;
+        this.contextName = proxyDirContext.contextName;
+    }
+    */
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Proxy DirContext (either this or the real proxy).
+     */
+    protected ProxyDirContext proxy = this;
+
+
+    /**
+     * Environment.
+     */
+    protected Hashtable env;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Associated DirContext.
+     */
+    protected DirContext dirContext;
+
+
+    /**
+     * Virtual path.
+     */
+    protected String vPath = null;
+
+
+    /**
+     * Host name.
+     */
+    protected String hostName;
+
+
+    /**
+     * Context name.
+     */
+    protected String contextName;
+
+
+    /**
+     * Cache class.
+     */
+    protected String cacheClassName = 
+        "org.apache.naming.resources.ResourceCache";
+
+
+    /**
+     * Cache.
+     */
+    protected ResourceCache cache = null;
+
+
+    /**
+     * Cache TTL.
+     */
+    protected int cacheTTL = 5000; // 5s
+
+
+    /**
+     * Max size of resources which will have their content cached.
+     */
+    protected int cacheObjectMaxSize = 512; // 512 KB
+
+
+    /**
+     * Immutable name not found exception.
+     */
+    protected NameNotFoundException notFoundException =
+        new ImmutableNameNotFoundException();
+
+
+    /**
+     * Non cacheable resources.
+     */
+    protected String[] nonCacheable = { "/WEB-INF/lib/", "/WEB-INF/classes/" };
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Get the cache used for this context.
+     */
+    public ResourceCache getCache() {
+        return cache;
+    }
+
+
+    /**
+     * Return the actual directory context we are wrapping.
+     */
+    public DirContext getDirContext() {
+        return this.dirContext;
+    }
+
+
+    /**
+     * Return the document root for this component.
+     */
+    public String getDocBase() {
+        if (dirContext instanceof BaseDirContext)
+            return ((BaseDirContext) dirContext).getDocBase();
+        else
+            return "";
+    }
+
+
+    /**
+     * Return the host name.
+     */
+    public String getHostName() {
+        return this.hostName;
+    }
+
+
+    /**
+     * Return the context name.
+     */
+    public String getContextName() {
+        return this.contextName;
+    }
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Retrieves the named object. If name is empty, returns a new instance 
+     * of this context (which represents the same naming context as this 
+     * context, but its environment may be modified independently and it may 
+     * be accessed concurrently).
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(Name name)
+        throws NamingException {
+        CacheEntry entry = cacheLookup(name.toString());
+        if (entry != null) {
+            if (!entry.exists) {
+                throw notFoundException;
+            }
+            if (entry.resource != null) {
+                // Check content caching.
+                return entry.resource;
+            } else {
+                return entry.context;
+            }
+        }
+        Object object = dirContext.lookup(parseName(name));
+        if (object instanceof InputStream)
+            return new Resource((InputStream) object);
+        else
+            return object;
+    }
+
+
+    /**
+     * Retrieves the named object.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(String name)
+        throws NamingException {
+        CacheEntry entry = cacheLookup(name);
+        if (entry != null) {
+            if (!entry.exists) {
+                throw notFoundException;
+            }
+            if (entry.resource != null) {
+                return entry.resource;
+            } else {
+                return entry.context;
+            }
+        }
+        Object object = dirContext.lookup(parseName(name));
+        if (object instanceof InputStream) {
+            return new Resource((InputStream) object);
+        } else if (object instanceof DirContext) {
+            return object;
+        } else if (object instanceof Resource) {
+            return object;
+        } else {
+            return new Resource(new ByteArrayInputStream
+                (object.toString().getBytes()));
+        }
+    }
+
+
+    /**
+     * Binds a name to an object. All intermediate contexts and the target 
+     * context (that named by all but terminal atomic component of the name) 
+     * must already exist.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj)
+        throws NamingException {
+        dirContext.bind(parseName(name), obj);
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Binds a name to an object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj)
+        throws NamingException {
+        dirContext.bind(parseName(name), obj);
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding. All 
+     * intermediate contexts and the target context (that named by all but 
+     * terminal atomic component of the name) must already exist.
+     * <p>
+     * If the object is a DirContext, any existing attributes associated with 
+     * the name are replaced with those of the object. Otherwise, any 
+     * existing attributes associated with the name remain unchanged.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj)
+        throws NamingException {
+        dirContext.rebind(parseName(name), obj);
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj)
+        throws NamingException {
+        dirContext.rebind(parseName(name), obj);
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(Name name)
+        throws NamingException {
+        dirContext.unbind(parseName(name));
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Unbinds the named object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(String name)
+        throws NamingException {
+        dirContext.unbind(parseName(name));
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name. Both names are relative to this context. Any attributes 
+     * associated with the old name become associated with the new name. 
+     * Intermediate contexts of the old name are not changed.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(Name oldName, Name newName)
+        throws NamingException {
+        dirContext.rename(parseName(oldName), parseName(newName));
+        cacheUnload(oldName.toString());
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(String oldName, String newName)
+        throws NamingException {
+        dirContext.rename(parseName(oldName), parseName(newName));
+        cacheUnload(oldName);
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name name)
+        throws NamingException {
+        return dirContext.list(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(String name)
+        throws NamingException {
+        return dirContext.list(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+        throws NamingException {
+        return dirContext.listBindings(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(String name)
+        throws NamingException {
+        return dirContext.listBindings(parseName(name));
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any 
+     * attributes associated with the name are also removed. Intermediate 
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * In a federated naming system, a context from one naming system may be 
+     * bound to a name in another. One can subsequently look up and perform 
+     * operations on the foreign context using a composite name. However, an 
+     * attempt destroy the context using this composite name will fail with 
+     * NotContextException, because the foreign context is not a "subcontext" 
+     * of the context in which it is bound. Instead, use unbind() to remove 
+     * the binding of the foreign context. Destroying the foreign context 
+     * requires that the destroySubcontext() be performed on a context from 
+     * the foreign context's "native" naming system.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(Name name)
+        throws NamingException {
+        dirContext.destroySubcontext(parseName(name));
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(String name)
+        throws NamingException {
+        dirContext.destroySubcontext(parseName(name));
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Creates and binds a new context. Creates a new context with the given 
+     * name and binds it in the target context (that named by all but 
+     * terminal atomic component of the name). All intermediate contexts and 
+     * the target context must already exist.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(Name name)
+        throws NamingException {
+        Context context = dirContext.createSubcontext(parseName(name));
+        cacheUnload(name.toString());
+        return context;
+    }
+
+
+    /**
+     * Creates and binds a new context.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(String name)
+        throws NamingException {
+        Context context = dirContext.createSubcontext(parseName(name));
+        cacheUnload(name);
+        return context;
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name. If the object bound to name is not a 
+     * link, returns the object itself.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(Name name)
+        throws NamingException {
+        return dirContext.lookupLink(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(String name)
+        throws NamingException {
+        return dirContext.lookupLink(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context. In a 
+     * federation of namespaces, different naming systems will parse names 
+     * differently. This method allows an application to get a parser for 
+     * parsing names into their atomic components using the naming convention 
+     * of a particular naming system. Within any single naming system, 
+     * NameParser objects returned by this method must be equal (using the 
+     * equals() test).
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(Name name)
+        throws NamingException {
+        return dirContext.getNameParser(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context.
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(String name)
+        throws NamingException {
+        return dirContext.getNameParser(parseName(name));
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * <p>
+     * Given a name (name) relative to this context, and the name (prefix) 
+     * of this context relative to one of its ancestors, this method returns 
+     * the composition of the two names using the syntax appropriate for the 
+     * naming system(s) involved. That is, if name names an object relative 
+     * to this context, the result is the name of the same object, but 
+     * relative to the ancestor context. None of the names may be null.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Name composeName(Name name, Name prefix)
+        throws NamingException {
+        prefix = (Name) prefix.clone();
+        return prefix.addAll(name);
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String composeName(String name, String prefix)
+        throws NamingException {
+        return prefix + "/" + name;
+    }
+
+
+    /**
+     * Adds a new environment property to the environment of this context. If 
+     * the property already exists, its value is overwritten.
+     * 
+     * @param propName the name of the environment property to add; may not 
+     * be null
+     * @param propVal the value of the property to add; may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object addToEnvironment(String propName, Object propVal)
+        throws NamingException {
+        return dirContext.addToEnvironment(propName, propVal);
+    }
+
+
+    /**
+     * Removes an environment property from the environment of this context. 
+     * 
+     * @param propName the name of the environment property to remove; 
+     * may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object removeFromEnvironment(String propName)
+        throws NamingException {
+        return dirContext.removeFromEnvironment(propName);
+    }
+
+
+    /**
+     * Retrieves the environment in effect for this context. See class 
+     * description for more details on environment properties. 
+     * The caller should not make any changes to the object returned: their 
+     * effect on the context is undefined. The environment of this context 
+     * may be changed using addToEnvironment() and removeFromEnvironment().
+     * 
+     * @return the environment of this context; never null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Hashtable getEnvironment()
+        throws NamingException {
+        return dirContext.getEnvironment();
+    }
+
+
+    /**
+     * Closes this context. This method releases this context's resources 
+     * immediately, instead of waiting for them to be released automatically 
+     * by the garbage collector.
+     * This method is idempotent: invoking it on a context that has already 
+     * been closed has no effect. Invoking any other method on a closed 
+     * context is not allowed, and results in undefined behaviour.
+     * 
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void close()
+        throws NamingException {
+        dirContext.close();
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in 
+     * their respective namespaces. For example, an LDAP entry has a 
+     * distinguished name, and a DNS record has a fully qualified name. This 
+     * method allows the client application to retrieve this name. The string 
+     * returned by this method is not a JNDI composite name and should not be 
+     * passed directly to context methods. In naming systems for which the 
+     * notion of full name does not make sense, 
+     * OperationNotSupportedException is thrown.
+     * 
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does 
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+        throws NamingException {
+        return dirContext.getNameInNamespace();
+    }
+
+
+    // ----------------------------------------------------- DirContext Methods
+
+
+    /**
+     * Retrieves all of the attributes associated with a named object. 
+     * 
+     * @return the set of attributes associated with name. 
+     * Returns an empty attribute set if name has no attributes; never null.
+     * @param name the name of the object from which to retrieve attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name name)
+        throws NamingException {
+        CacheEntry entry = cacheLookup(name.toString());
+        if (entry != null) {
+            if (!entry.exists) {
+                throw notFoundException;
+            }
+            return entry.attributes;
+        }
+        Attributes attributes = dirContext.getAttributes(parseName(name));
+        if (!(attributes instanceof ResourceAttributes)) {
+            attributes = new ResourceAttributes(attributes);
+        }
+        return attributes;
+    }
+
+
+    /**
+     * Retrieves all of the attributes associated with a named object.
+     * 
+     * @return the set of attributes associated with name
+     * @param name the name of the object from which to retrieve attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(String name)
+        throws NamingException {
+        CacheEntry entry = cacheLookup(name);
+        if (entry != null) {
+            if (!entry.exists) {
+                throw notFoundException;
+            }
+            return entry.attributes;
+        }
+        Attributes attributes = dirContext.getAttributes(parseName(name));
+        if (!(attributes instanceof ResourceAttributes)) {
+            attributes = new ResourceAttributes(attributes);
+        }
+        return attributes;
+    }
+
+
+    /**
+     * Retrieves selected attributes associated with a named object. 
+     * See the class description regarding attribute models, attribute type 
+     * names, and operational attributes.
+     * 
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null 
+     * indicates that all attributes should be retrieved; an empty array 
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name name, String[] attrIds)
+        throws NamingException {
+        Attributes attributes = 
+            dirContext.getAttributes(parseName(name), attrIds);
+        if (!(attributes instanceof ResourceAttributes)) {
+            attributes = new ResourceAttributes(attributes);
+        }
+        return attributes;
+    }
+
+
+    /**
+     * Retrieves selected attributes associated with a named object.
+     * 
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null 
+     * indicates that all attributes should be retrieved; an empty array 
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+     public Attributes getAttributes(String name, String[] attrIds)
+         throws NamingException {
+        Attributes attributes = 
+            dirContext.getAttributes(parseName(name), attrIds);
+        if (!(attributes instanceof ResourceAttributes)) {
+            attributes = new ResourceAttributes(attributes);
+        }
+        return attributes;
+     }
+
+
+    /**
+     * Modifies the attributes associated with a named object. The order of 
+     * the modifications is not specified. Where possible, the modifications 
+     * are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE, 
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not 
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(Name name, int mod_op, Attributes attrs)
+        throws NamingException {
+        dirContext.modifyAttributes(parseName(name), mod_op, attrs);
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE, 
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not 
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, int mod_op, Attributes attrs)
+        throws NamingException {
+        dirContext.modifyAttributes(parseName(name), mod_op, attrs);
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an 
+     * ordered list of modifications. The modifications are performed in the 
+     * order specified. Each modification specifies a modification operation 
+     * code and an attribute on which to operate. Where possible, the 
+     * modifications are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may 
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(Name name, ModificationItem[] mods)
+        throws NamingException {
+        dirContext.modifyAttributes(parseName(name), mods);
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an 
+     * ordered list of modifications.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may 
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, ModificationItem[] mods)
+        throws NamingException {
+        dirContext.modifyAttributes(parseName(name), mods);
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes. If attrs 
+     * is null, the resulting binding will have the attributes associated 
+     * with obj if obj is a DirContext, and no attributes otherwise. If attrs 
+     * is non-null, the resulting binding will have attrs as its attributes; 
+     * any attributes associated with obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj, Attributes attrs)
+        throws NamingException {
+        dirContext.bind(parseName(name), obj, attrs);
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+        dirContext.bind(parseName(name), obj, attrs);
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes, 
+     * overwriting any existing binding. If attrs is null and obj is a 
+     * DirContext, the attributes from obj are used. If attrs is null and obj 
+     * is not a DirContext, any existing attributes associated with the object
+     * already bound in the directory remain unchanged. If attrs is non-null, 
+     * any existing attributes associated with the object already bound in 
+     * the directory are removed and attrs is associated with the named 
+     * object. If obj is a DirContext and attrs is non-null, the attributes 
+     * of obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj, Attributes attrs)
+        throws NamingException {
+        dirContext.rebind(parseName(name), obj, attrs);
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes, 
+     * overwriting any existing binding.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+        dirContext.rebind(parseName(name), obj, attrs);
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes. 
+     * This method creates a new subcontext with the given name, binds it in 
+     * the target context (that named by all but terminal atomic component of 
+     * the name), and associates the supplied attributes with the newly 
+     * created object. All intermediate and target contexts must already 
+     * exist. If attrs is null, this method is equivalent to 
+     * Context.createSubcontext().
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all 
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(Name name, Attributes attrs)
+        throws NamingException {
+        DirContext context = 
+            dirContext.createSubcontext(parseName(name), attrs);
+        cacheUnload(name.toString());
+        return context;
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all 
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(String name, Attributes attrs)
+        throws NamingException {
+        DirContext context = 
+            dirContext.createSubcontext(parseName(name), attrs);
+        cacheUnload(name);
+        return context;
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object. The schema 
+     * describes rules regarding the structure of the namespace and the 
+     * attributes stored within it. The schema specifies what types of 
+     * objects can be added to the directory and where they can be added; 
+     * what mandatory and optional attributes an object can have. The range 
+     * of support for schemas is directory-specific.
+     * 
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(Name name)
+        throws NamingException {
+        return dirContext.getSchema(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object.
+     * 
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(String name)
+        throws NamingException {
+        return dirContext.getSchema(parseName(name));
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named 
+     * object's class definitions.
+     * 
+     * @param name the name of the object whose object class definition is to 
+     * be retrieved
+     * @return the DirContext containing the named object's class 
+     * definitions; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(Name name)
+        throws NamingException {
+        return dirContext.getSchemaClassDefinition(parseName(name));
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named 
+     * object's class definitions.
+     * 
+     * @param name the name of the object whose object class definition is to 
+     * be retrieved
+     * @return the DirContext containing the named object's class 
+     * definitions; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(String name)
+        throws NamingException {
+        return dirContext.getSchemaClassDefinition(parseName(name));
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes, and retrieves selected attributes. The search is 
+     * performed using the default SearchControls settings.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates 
+     * that all attributes are to be returned; an empty array indicates that 
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+        throws NamingException {
+        return dirContext.search(parseName(name), matchingAttributes, 
+                                 attributesToReturn);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes, and retrieves selected attributes.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates 
+     * that all attributes are to be returned; an empty array indicates that 
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+        throws NamingException {
+        return dirContext.search(parseName(name), matchingAttributes, 
+                                 attributesToReturn);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes. This method returns all the attributes of such objects. 
+     * It is equivalent to supplying null as the atributesToReturn parameter 
+     * to the method search(Name, Attributes, String[]).
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, Attributes matchingAttributes)
+        throws NamingException {
+        return dirContext.search(parseName(name), matchingAttributes);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes)
+        throws NamingException {
+        return dirContext.search(parseName(name), matchingAttributes);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be 
+     * null
+     * @param cons the search controls that control the search. If null, 
+     * the default search controls are used (equivalent to 
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy 
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter specified 
+     * is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls 
+     * contain invalid settings
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, String filter, 
+                                    SearchControls cons)
+        throws NamingException {
+        return dirContext.search(parseName(name), filter, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be 
+     * null
+     * @param cons the search controls that control the search. If null, 
+     * the default search controls are used (equivalent to 
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy 
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter 
+     * specified is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls 
+     * contain invalid settings
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filter, 
+                                    SearchControls cons)
+        throws NamingException {
+        return dirContext.search(parseName(name), filter, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search. 
+     * The expression may contain variables of the form "{i}" where i is a 
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the 
+     * variables in filterExpr. The value of filterArgs[i] will replace each 
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the 
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the 
+     * filter; never null
+     * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i} 
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid 
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs 
+     * represents an invalid search filter
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, String filterExpr,
+                                    Object[] filterArgs, SearchControls cons)
+        throws NamingException {
+        return dirContext.search(parseName(name), filterExpr, filterArgs, 
+                                 cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search. 
+     * The expression may contain variables of the form "{i}" where i is a 
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the 
+     * variables in filterExpr. The value of filterArgs[i] will replace each 
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the 
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the 
+     * filter; never null
+     * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i} 
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid 
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs 
+     * represents an invalid search filter
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filterExpr,
+                                    Object[] filterArgs, SearchControls cons)
+        throws NamingException {
+        return dirContext.search(parseName(name), filterExpr, filterArgs, 
+                                 cons);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieves the named object as a cache entry, without any exception.
+     * 
+     * @param name the name of the object to look up
+     * @return the cache entry bound to name
+     */
+    public CacheEntry lookupCache(String name) {
+        CacheEntry entry = cacheLookup(name);
+        if (entry == null) {
+            entry = new CacheEntry();
+            entry.name = name;
+            try {
+                Object object = dirContext.lookup(parseName(name));
+                if (object instanceof InputStream) {
+                    entry.resource = new Resource((InputStream) object);
+                } else if (object instanceof DirContext) {
+                    entry.context = (DirContext) object;
+                } else if (object instanceof Resource) {
+                    entry.resource = (Resource) object;
+                } else {
+                    entry.resource = new Resource(new ByteArrayInputStream
+                        (object.toString().getBytes()));
+                }
+                Attributes attributes = dirContext.getAttributes(parseName(name));
+                if (!(attributes instanceof ResourceAttributes)) {
+                    attributes = new ResourceAttributes(attributes);
+                }
+                entry.attributes = (ResourceAttributes) attributes;
+            } catch (NamingException e) {
+                entry.exists = false;
+            }
+        }
+        return entry;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Parses a name.
+     * 
+     * @return the parsed name
+     */
+    protected String parseName(String name) 
+        throws NamingException {
+        return name;
+    }
+
+
+    /**
+     * Parses a name.
+     * 
+     * @return the parsed name
+     */
+    protected Name parseName(Name name) 
+        throws NamingException {
+        return name;
+    }
+
+
+    /**
+     * Lookup in cache.
+     */
+    protected CacheEntry cacheLookup(String name) {
+        if (cache == null)
+            return (null);
+        if (name == null)
+            name = "";
+        for (int i = 0; i < nonCacheable.length; i++) {
+            if (name.startsWith(nonCacheable[i])) {
+                return (null);
+            }
+        }
+        CacheEntry cacheEntry = cache.lookup(name);
+        if (cacheEntry == null) {
+            cacheEntry = new CacheEntry();
+            cacheEntry.name = name;
+            // Load entry
+            cacheLoad(cacheEntry);
+        } else {
+            if (!validate(cacheEntry)) {
+                if (!revalidate(cacheEntry)) {
+                    cacheUnload(cacheEntry.name);
+                    return (null);
+                } else {
+                    cacheEntry.timestamp = 
+                        System.currentTimeMillis() + cacheTTL;
+                }
+            }
+            cacheEntry.accessCount++;
+        }
+        return (cacheEntry);
+    }
+
+
+    /**
+     * Validate entry.
+     */
+    protected boolean validate(CacheEntry entry) {
+        if (((!entry.exists)
+             || (entry.context != null)
+             || ((entry.resource != null) 
+                 && (entry.resource.getContent() != null)))
+            && (System.currentTimeMillis() < entry.timestamp)) {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Revalidate entry.
+     */
+    protected boolean revalidate(CacheEntry entry) {
+        // Get the attributes at the given path, and check the last 
+        // modification date
+        if (!entry.exists)
+            return false;
+        if (entry.attributes == null)
+            return false;
+        long lastModified = entry.attributes.getLastModified();
+        long contentLength = entry.attributes.getContentLength();
+        if (lastModified <= 0)
+            return false;
+        try {
+            Attributes tempAttributes = dirContext.getAttributes(entry.name);
+            ResourceAttributes attributes = null;
+            if (!(tempAttributes instanceof ResourceAttributes)) {
+                attributes = new ResourceAttributes(tempAttributes);
+            } else {
+                attributes = (ResourceAttributes) tempAttributes;
+            }
+            long lastModified2 = attributes.getLastModified();
+            long contentLength2 = attributes.getContentLength();
+            return (lastModified == lastModified2) 
+                && (contentLength == contentLength2);
+        } catch (NamingException e) {
+            return false;
+        }
+    }
+
+
+    /**
+     * Load entry into cache.
+     */
+    protected void cacheLoad(CacheEntry entry) {
+
+        String name = entry.name;
+
+        // Retrieve missing info
+        boolean exists = true;
+
+        // Retrieving attributes
+        if (entry.attributes == null) {
+            try {
+                Attributes attributes = dirContext.getAttributes(entry.name);
+                if (!(attributes instanceof ResourceAttributes)) {
+                    entry.attributes = 
+                        new ResourceAttributes(attributes);
+                } else {
+                    entry.attributes = (ResourceAttributes) attributes;
+                }
+            } catch (NamingException e) {
+                exists = false;
+            }
+        }
+
+        // Retriving object
+        if ((exists) && (entry.resource == null) && (entry.context == null)) {
+            try {
+                Object object = dirContext.lookup(name);
+                if (object instanceof InputStream) {
+                    entry.resource = new Resource((InputStream) object);
+                } else if (object instanceof DirContext) {
+                    entry.context = (DirContext) object;
+                } else if (object instanceof Resource) {
+                    entry.resource = (Resource) object;
+                } else {
+                    entry.resource = new Resource(new ByteArrayInputStream
+                        (object.toString().getBytes()));
+                }
+            } catch (NamingException e) {
+                exists = false;
+            }
+        }
+
+        // Load object content
+        if ((exists) && (entry.resource != null) 
+            && (entry.resource.getContent() == null) 
+            && (entry.attributes.getContentLength() >= 0)
+            && (entry.attributes.getContentLength() < 
+                (cacheObjectMaxSize * 1024))) {
+            int length = (int) entry.attributes.getContentLength();
+            // The entry size is 1 + the resource size in KB, if it will be 
+            // cached
+            entry.size += (entry.attributes.getContentLength() / 1024);
+            InputStream is = null;
+            try {
+                is = entry.resource.streamContent();
+                int pos = 0;
+                byte[] b = new byte[length];
+                while (pos < length) {
+                    int n = is.read(b, pos, length - pos);
+                    if (n < 0)
+                        break;
+                    pos = pos + n;
+                }
+                entry.resource.setContent(b);
+            } catch (IOException e) {
+                ; // Ignore
+            } finally {
+                try {
+                    if (is != null)
+                        is.close();
+                } catch (IOException e) {
+                    ; // Ignore
+                }
+            }
+        }
+
+        // Set existence flag
+        entry.exists = exists;
+
+        // Set timestamp
+        entry.timestamp = System.currentTimeMillis() + cacheTTL;
+
+        // Add new entry to cache
+        synchronized (cache) {
+            // Check cache size, and remove elements if too big
+            if (cache.allocate(entry.size)) {
+                cache.load(entry);
+            }
+        }
+
+    }
+
+
+    /**
+     * Remove entry from cache.
+     */
+    protected boolean cacheUnload(String name) {
+        if (cache == null)
+            return false;
+        synchronized (cache) {
+            return cache.unload(name);
+        }
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/resources/RecyclableNamingEnumeration.java b/container/catalina/src/share/org/apache/naming/resources/RecyclableNamingEnumeration.java
new file mode 100644
index 0000000..60aec8d
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/RecyclableNamingEnumeration.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.resources;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+/**
+ * Naming enumeration implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class RecyclableNamingEnumeration 
+    implements NamingEnumeration {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public RecyclableNamingEnumeration(Vector entries) {
+        this.entries = entries;
+        recycle();
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Entries.
+     */
+    protected Vector entries;
+
+
+    /**
+     * Underlying enumeration.
+     */
+    protected Enumeration enumeration;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieves the next element in the enumeration.
+     */
+    public Object next()
+        throws NamingException {
+        return nextElement();
+    }
+
+
+    /**
+     * Determines whether there are any more elements in the enumeration.
+     */
+    public boolean hasMore()
+        throws NamingException {
+        return enumeration.hasMoreElements();
+    }
+
+
+    /**
+     * Closes this enumeration.
+     */
+    public void close()
+        throws NamingException {
+    }
+
+
+    public boolean hasMoreElements() {
+        return enumeration.hasMoreElements();
+    }
+
+
+    public Object nextElement() {
+        return enumeration.nextElement();
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Recycle.
+     */
+    void recycle() {
+    	enumeration = entries.elements();
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/resources/Resource.java b/container/catalina/src/share/org/apache/naming/resources/Resource.java
new file mode 100644
index 0000000..923e3b7
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/Resource.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming.resources;
+
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * Encapsultes the contents of a resource.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class Resource {
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    
+    public Resource() {
+    }
+    
+    
+    public Resource(InputStream inputStream) {
+        setContent(inputStream);
+    }
+    
+    
+    public Resource(byte[] binaryContent) {
+        setContent(binaryContent);
+    }
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * Binary content.
+     */
+    protected byte[] binaryContent = null;
+    
+    
+    /**
+     * Input stream.
+     */
+    protected InputStream inputStream = null;
+    
+    
+    // ------------------------------------------------------------- Properties
+    
+    
+    /**
+     * Content accessor.
+     * 
+     * @return InputStream
+     */
+    public InputStream streamContent()
+        throws IOException {
+        if (binaryContent != null) {
+            return new ByteArrayInputStream(binaryContent);
+        }
+        return inputStream;
+    }
+    
+    
+    /**
+     * Content accessor.
+     * 
+     * @return binary content
+     */
+    public byte[] getContent() {
+        return binaryContent;
+    }
+    
+    
+    /**
+     * Content mutator.
+     * 
+     * @param inputStream New input stream
+     */
+    public void setContent(InputStream inputStream) {
+        this.inputStream = inputStream;
+    }
+    
+    
+    /**
+     * Content mutator.
+     * 
+     * @param binaryContent New bin content
+     */
+    public void setContent(byte[] binaryContent) {
+        this.binaryContent = binaryContent;
+    }
+    
+    
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/ResourceAttributes.java b/container/catalina/src/share/org/apache/naming/resources/ResourceAttributes.java
new file mode 100644
index 0000000..310fee0
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/ResourceAttributes.java
@@ -0,0 +1,915 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming.resources;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.Vector;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+
+/**
+ * Attributes implementation.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class ResourceAttributes implements Attributes {
+    
+    
+    // -------------------------------------------------------------- Constants
+    
+    
+    // Default attribute names
+    
+    /**
+     * Creation date.
+     */
+    public static final String CREATION_DATE = "creationdate";
+    
+    
+    /**
+     * Creation date.
+     */
+    public static final String ALTERNATE_CREATION_DATE = "creation-date";
+    
+    
+    /**
+     * Last modification date.
+     */
+    public static final String LAST_MODIFIED = "getlastmodified";
+    
+    
+    /**
+     * Last modification date.
+     */
+    public static final String ALTERNATE_LAST_MODIFIED = "last-modified";
+    
+    
+    /**
+     * Name.
+     */
+    public static final String NAME = "displayname";
+    
+    
+    /**
+     * Type.
+     */
+    public static final String TYPE = "resourcetype";
+    
+    
+    /**
+     * Type.
+     */
+    public static final String ALTERNATE_TYPE = "content-type";
+    
+    
+    /**
+     * Source.
+     */
+    public static final String SOURCE = "source";
+    
+    
+    /**
+     * MIME type of the content.
+     */
+    public static final String CONTENT_TYPE = "getcontenttype";
+    
+    
+    /**
+     * Content language.
+     */
+    public static final String CONTENT_LANGUAGE = "getcontentlanguage";
+    
+    
+    /**
+     * Content length.
+     */
+    public static final String CONTENT_LENGTH = "getcontentlength";
+    
+    
+    /**
+     * Content length.
+     */
+    public static final String ALTERNATE_CONTENT_LENGTH = "content-length";
+    
+    
+    /**
+     * ETag.
+     */
+    public static final String ETAG = "getetag";
+    
+    
+    /**
+     * Collection type.
+     */
+    public static final String COLLECTION_TYPE = "<collection/>";
+    
+    
+    /**
+     * HTTP date format.
+     */
+    protected static final SimpleDateFormat format =
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+    
+    
+    /**
+     * Date formats using for Date parsing.
+     */
+    protected static final SimpleDateFormat formats[] = {
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+    };
+    
+    
+    protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
+
+
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    static {
+
+        format.setTimeZone(gmtZone);
+
+        formats[0].setTimeZone(gmtZone);
+        formats[1].setTimeZone(gmtZone);
+        formats[2].setTimeZone(gmtZone);
+
+    }
+
+
+    // ----------------------------------------------------------- Constructors
+    
+    
+    /**
+     * Default constructor.
+     */
+    public ResourceAttributes() {
+    }
+    
+    
+    /**
+     * Merges with another attribute set.
+     */
+    public ResourceAttributes(Attributes attributes) {
+        this.attributes = attributes;
+    }
+    
+    
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Collection flag.
+     */
+    protected boolean collection = false;
+
+
+    /**
+     * Content length.
+     */
+    protected long contentLength = -1;
+
+
+    /**
+     * Creation time.
+     */
+    protected long creation = -1;
+
+
+    /**
+     * Creation date.
+     */
+    protected Date creationDate = null;
+
+
+    /**
+     * Last modified time.
+     */
+    protected long lastModified = -1;
+
+
+    /**
+     * Last modified date.
+     */
+    protected Date lastModifiedDate = null;
+
+    
+    /**
+     * Last modified date in HTTP format.
+     */
+    protected String lastModifiedHttp = null;
+    
+
+    /**
+     * MIME type.
+     */
+    protected String mimeType = null;
+    
+
+    /**
+     * Name.
+     */
+    protected String name = null;
+
+
+    /**
+     * Weak ETag.
+     */
+    protected String weakETag = null;
+
+
+    /**
+     * Strong ETag.
+     */
+    protected String strongETag = null;
+
+
+    /**
+     * External attributes.
+     */
+    protected Attributes attributes = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Is collection.
+     */
+    public boolean isCollection() {
+        if (attributes != null) {
+            return (getResourceType().equals(COLLECTION_TYPE));
+        } else {
+            return (collection);
+        }
+    }
+    
+    
+    /**
+     * Set collection flag.
+     *
+     * @param collection New flag value
+     */
+    public void setCollection(boolean collection) {
+        this.collection = collection;
+        if (attributes != null) {
+            String value = "";
+            if (collection)
+                value = COLLECTION_TYPE;
+            attributes.put(TYPE, value);
+        }
+    }
+    
+    
+    /**
+     * Get content length.
+     * 
+     * @return content length value
+     */
+    public long getContentLength() {
+        if (contentLength != -1L)
+            return contentLength;
+        if (attributes != null) {
+            Attribute attribute = attributes.get(CONTENT_LENGTH);
+            if (attribute != null) {
+                try {
+                    Object value = attribute.get();
+                    if (value instanceof Long) {
+                        contentLength = ((Long) value).longValue();
+                    } else {
+                        try {
+                            contentLength = Long.parseLong(value.toString());
+                        } catch (NumberFormatException e) {
+                            ; // Ignore
+                        }
+                    }
+                } catch (NamingException e) {
+                    ; // No value for the attribute
+                }
+            }
+        }
+        return contentLength;
+    }
+    
+    
+    /**
+     * Set content length.
+     * 
+     * @param contentLength New content length value
+     */
+    public void setContentLength(long contentLength) {
+        this.contentLength = contentLength;
+        if (attributes != null)
+            attributes.put(CONTENT_LENGTH, new Long(contentLength));
+    }
+    
+    
+    /**
+     * Get creation time.
+     * 
+     * @return creation time value
+     */
+    public long getCreation() {
+        if (creation != -1L)
+            return creation;
+        if (creationDate != null)
+            return creationDate.getTime();
+        if (attributes != null) {
+            Attribute attribute = attributes.get(CREATION_DATE);
+            if (attribute != null) {
+                try {
+                    Object value = attribute.get();
+                    if (value instanceof Long) {
+                        creation = ((Long) value).longValue();
+                    } else if (value instanceof Date) {
+                        creation = ((Date) value).getTime();
+                        creationDate = (Date) value;
+                    } else {
+                        String creationDateValue = value.toString();
+                        Date result = null;
+                        // Parsing the HTTP Date
+                        for (int i = 0; (result == null) && 
+                                 (i < formats.length); i++) {
+                            try {
+                                result = formats[i].parse(creationDateValue);
+                            } catch (ParseException e) {
+                                ;
+                            }
+                        }
+                        if (result != null) {
+                            creation = result.getTime();
+                            creationDate = result;
+                        }
+                    }
+                } catch (NamingException e) {
+                    ; // No value for the attribute
+                }
+            }
+        }
+        return creation;
+    }
+    
+    
+    /**
+     * Set creation.
+     * 
+     * @param creation New creation value
+     */
+    public void setCreation(long creation) {
+        this.creation = creation;
+        this.creationDate = null;
+        if (attributes != null)
+            attributes.put(CREATION_DATE, new Date(creation));
+    }
+    
+    
+    /**
+     * Get creation date.
+     * 
+     * @return Creation date value
+     */
+    public Date getCreationDate() {
+        if (creationDate != null)
+            return creationDate;
+        if (creation != -1L) {
+            creationDate = new Date(creation);
+            return creationDate;
+        }
+        if (attributes != null) {
+            Attribute attribute = attributes.get(CREATION_DATE);
+            if (attribute != null) {
+                try {
+                    Object value = attribute.get();
+                    if (value instanceof Long) {
+                        creation = ((Long) value).longValue();
+                        creationDate = new Date(creation);
+                    } else if (value instanceof Date) {
+                        creation = ((Date) value).getTime();
+                        creationDate = (Date) value;
+                    } else {
+                        String creationDateValue = value.toString();
+                        Date result = null;
+                        // Parsing the HTTP Date
+                        for (int i = 0; (result == null) && 
+                                 (i < formats.length); i++) {
+                            try {
+                                result = formats[i].parse(creationDateValue);
+                            } catch (ParseException e) {
+                                ;
+                            }
+                        }
+                        if (result != null) {
+                            creation = result.getTime();
+                            creationDate = result;
+                        }
+                    }
+                } catch (NamingException e) {
+                    ; // No value for the attribute
+                }
+            }
+        }
+        return creationDate;
+    }
+    
+    
+    /**
+     * Creation date mutator.
+     * 
+     * @param creationDate New creation date
+     */
+    public void setCreationDate(Date creationDate) {
+        this.creation = creationDate.getTime();
+        this.creationDate = creationDate;
+        if (attributes != null)
+            attributes.put(CREATION_DATE, creationDate);
+    }
+    
+    
+    /**
+     * Get last modified time.
+     * 
+     * @return lastModified time value
+     */
+    public long getLastModified() {
+        if (lastModified != -1L)
+            return lastModified;
+        if (lastModifiedDate != null)
+            return lastModifiedDate.getTime();
+        if (attributes != null) {
+            Attribute attribute = attributes.get(LAST_MODIFIED);
+            if (attribute != null) {
+                try {
+                    Object value = attribute.get();
+                    if (value instanceof Long) {
+                        lastModified = ((Long) value).longValue();
+                    } else if (value instanceof Date) {
+                        lastModified = ((Date) value).getTime();
+                        lastModifiedDate = (Date) value;
+                    } else {
+                        String lastModifiedDateValue = value.toString();
+                        Date result = null;
+                        // Parsing the HTTP Date
+                        for (int i = 0; (result == null) && 
+                                 (i < formats.length); i++) {
+                            try {
+                                result = 
+                                    formats[i].parse(lastModifiedDateValue);
+                            } catch (ParseException e) {
+                                ;
+                            }
+                        }
+                        if (result != null) {
+                            lastModified = result.getTime();
+                            lastModifiedDate = result;
+                        }
+                    }
+                } catch (NamingException e) {
+                    ; // No value for the attribute
+                }
+            }
+        }
+        return lastModified;
+    }
+    
+    
+    /**
+     * Set last modified.
+     * 
+     * @param lastModified New last modified value
+     */
+    public void setLastModified(long lastModified) {
+        this.lastModified = lastModified;
+        this.lastModifiedDate = null;
+        if (attributes != null)
+            attributes.put(LAST_MODIFIED, new Date(lastModified));
+    }
+    
+    
+    /**
+     * Set last modified date.
+     * 
+     * @param lastModified New last modified date value
+     * @deprecated
+     */
+    public void setLastModified(Date lastModified) {
+        setLastModifiedDate(lastModified);
+    }
+
+
+    /**
+     * Get lastModified date.
+     * 
+     * @return LastModified date value
+     */
+    public Date getLastModifiedDate() {
+        if (lastModifiedDate != null)
+            return lastModifiedDate;
+        if (lastModified != -1L) {
+            lastModifiedDate = new Date(lastModified);
+            return lastModifiedDate;
+        }
+        if (attributes != null) {
+            Attribute attribute = attributes.get(LAST_MODIFIED);
+            if (attribute != null) {
+                try {
+                    Object value = attribute.get();
+                    if (value instanceof Long) {
+                        lastModified = ((Long) value).longValue();
+                        lastModifiedDate = new Date(lastModified);
+                    } else if (value instanceof Date) {
+                        lastModified = ((Date) value).getTime();
+                        lastModifiedDate = (Date) value;
+                    } else {
+                        String lastModifiedDateValue = value.toString();
+                        Date result = null;
+                        // Parsing the HTTP Date
+                        for (int i = 0; (result == null) && 
+                                 (i < formats.length); i++) {
+                            try {
+                                result = 
+                                    formats[i].parse(lastModifiedDateValue);
+                            } catch (ParseException e) {
+                                ;
+                            }
+                        }
+                        if (result != null) {
+                            lastModified = result.getTime();
+                            lastModifiedDate = result;
+                        }
+                    }
+                } catch (NamingException e) {
+                    ; // No value for the attribute
+                }
+            }
+        }
+        return lastModifiedDate;
+    }
+    
+    
+    /**
+     * Last modified date mutator.
+     * 
+     * @param lastModifiedDate New last modified date
+     */
+    public void setLastModifiedDate(Date lastModifiedDate) {
+        this.lastModified = lastModifiedDate.getTime();
+        this.lastModifiedDate = lastModifiedDate;
+        if (attributes != null)
+            attributes.put(LAST_MODIFIED, lastModifiedDate);
+    }
+    
+    
+    /**
+     * @return Returns the lastModifiedHttp.
+     */
+    public String getLastModifiedHttp() {
+        if (lastModifiedHttp != null)
+            return lastModifiedHttp;
+        Date modifiedDate = getLastModifiedDate();
+        if (modifiedDate == null) {
+            modifiedDate = getCreationDate();
+        }
+        if (modifiedDate == null) {
+            modifiedDate = new Date();
+        }
+        synchronized (format) {
+            lastModifiedHttp = format.format(modifiedDate);
+        }
+        return lastModifiedHttp;
+    }
+    
+    
+    /**
+     * @param lastModifiedHttp The lastModifiedHttp to set.
+     */
+    public void setLastModifiedHttp(String lastModifiedHttp) {
+        this.lastModifiedHttp = lastModifiedHttp;
+    }
+    
+    
+    /**
+     * @return Returns the mimeType.
+     */
+    public String getMimeType() {
+        return mimeType;
+    }
+    
+    
+    /**
+     * @param mimeType The mimeType to set.
+     */
+    public void setMimeType(String mimeType) {
+        this.mimeType = mimeType;
+    }
+
+    
+    /**
+     * Get name.
+     * 
+     * @return Name value
+     */
+    public String getName() {
+        if (name != null)
+            return name;
+        if (attributes != null) {
+            Attribute attribute = attributes.get(NAME);
+            if (attribute != null) {
+                try {
+                    name = attribute.get().toString();
+                } catch (NamingException e) {
+                    ; // No value for the attribute
+                }
+            }
+        }
+        return name;
+    }
+
+
+    /**
+     * Set name.
+     * 
+     * @param name New name value
+     */
+    public void setName(String name) {
+        this.name = name;
+        if (attributes != null)
+            attributes.put(NAME, name);
+    }
+    
+    
+    /**
+     * Get resource type.
+     * 
+     * @return String resource type
+     */
+    public String getResourceType() {
+        String result = null;
+        if (attributes != null) {
+            Attribute attribute = attributes.get(TYPE);
+            if (attribute != null) {
+                try {
+                    result = attribute.get().toString();
+                } catch (NamingException e) {
+                    ; // No value for the attribute
+                }
+            }
+        }
+        if (result == null) {
+            if (collection)
+                result = COLLECTION_TYPE;
+            else
+                result = "";
+        }
+        return result;
+    }
+    
+    
+    /**
+     * Type mutator.
+     * 
+     * @param resourceType New resource type
+     */
+    public void setResourceType(String resourceType) {
+        collection = resourceType.equals(COLLECTION_TYPE);
+        if (attributes != null)
+            attributes.put(TYPE, resourceType);
+    }
+
+
+    /**
+     * Get ETag.
+     * 
+     * @return Weak ETag
+     */
+    public String getETag() {
+        return getETag(false);
+    }
+
+
+    /**
+     * Get ETag.
+     * 
+     * @param strong If true, the strong ETag will be returned
+     * @return ETag
+     */
+    public String getETag(boolean strong) {
+        String result = null;
+        if (attributes != null) {
+            Attribute attribute = attributes.get(ETAG);
+            if (attribute != null) {
+                try {
+                    result = attribute.get().toString();
+                } catch (NamingException e) {
+                    ; // No value for the attribute
+                }
+            }
+        }
+        if (strong) {
+            // The strong ETag must always be calculated by the resources
+            result = strongETag;
+        } else {
+            // The weakETag is contentLenght + lastModified
+            if (weakETag == null) {
+                weakETag = "W/\"" + getContentLength() + "-" 
+                    + getLastModified() + "\"";
+            }
+            result = weakETag;
+        }
+        return result;
+    }
+
+
+    /**
+     * Set strong ETag.
+     */
+    public void setETag(String eTag) {
+        this.strongETag = eTag;
+        if (attributes != null)
+            attributes.put(ETAG, eTag);
+    }
+
+    
+    /**
+     * Return the canonical path of the resource, to possibly be used for 
+     * direct file serving. Implementations which support this should override
+     * it to return the file path.
+     * 
+     * @return The canonical path of the resource
+     */
+    public String getCanonicalPath() {
+        return null;
+    }
+    
+    
+    // ----------------------------------------------------- Attributes Methods
+
+
+    /**
+     * Get attribute.
+     */
+    public Attribute get(String attrID) {
+        if (attributes == null) {
+            if (attrID.equals(CREATION_DATE)) {
+                return new BasicAttribute(CREATION_DATE, getCreationDate());
+            } else if (attrID.equals(ALTERNATE_CREATION_DATE)) {
+                return new BasicAttribute(ALTERNATE_CREATION_DATE, 
+                                          getCreationDate());
+            } else if (attrID.equals(LAST_MODIFIED)) {
+                return new BasicAttribute(LAST_MODIFIED, 
+                                          getLastModifiedDate());
+            } else if (attrID.equals(ALTERNATE_LAST_MODIFIED)) {
+                return new BasicAttribute(ALTERNATE_LAST_MODIFIED,
+                                          getLastModifiedDate());
+            } else if (attrID.equals(NAME)) {
+                return new BasicAttribute(NAME, getName());
+            } else if (attrID.equals(TYPE)) {
+                return new BasicAttribute(TYPE, getResourceType());
+            } else if (attrID.equals(ALTERNATE_TYPE)) {
+                return new BasicAttribute(ALTERNATE_TYPE, getResourceType());
+            } else if (attrID.equals(CONTENT_LENGTH)) {
+                return new BasicAttribute(CONTENT_LENGTH, 
+                                          new Long(getContentLength()));
+            } else if (attrID.equals(ALTERNATE_CONTENT_LENGTH)) {
+                return new BasicAttribute(ALTERNATE_CONTENT_LENGTH, 
+                                          new Long(getContentLength()));
+            }
+        } else {
+            return attributes.get(attrID);
+        }
+        return null;
+    }
+    
+    
+    /**
+     * Put attribute.
+     */
+    public Attribute put(Attribute attribute) {
+        if (attributes == null) {
+            try {
+                return put(attribute.getID(), attribute.get());
+            } catch (NamingException e) {
+                return null;
+            }
+        } else {
+            return attributes.put(attribute);
+        }
+    }
+    
+    
+    /**
+     * Put attribute.
+     */
+    public Attribute put(String attrID, Object val) {
+        if (attributes == null) {
+            return null; // No reason to implement this
+        } else {
+            return attributes.put(attrID, val);
+        }
+    }
+    
+    
+    /**
+     * Remove attribute.
+     */
+    public Attribute remove(String attrID) {
+        if (attributes == null) {
+            return null; // No reason to implement this
+        } else {
+            return attributes.remove(attrID);
+        }
+    }
+    
+    
+    /**
+     * Get all attributes.
+     */
+    public NamingEnumeration getAll() {
+        if (attributes == null) {
+            Vector attributes = new Vector();
+            attributes.addElement(new BasicAttribute
+                                  (CREATION_DATE, getCreationDate()));
+            attributes.addElement(new BasicAttribute
+                                  (LAST_MODIFIED, getLastModifiedDate()));
+            attributes.addElement(new BasicAttribute(NAME, getName()));
+            attributes.addElement(new BasicAttribute(TYPE, getResourceType()));
+            attributes.addElement
+                (new BasicAttribute(CONTENT_LENGTH, 
+                                    new Long(getContentLength())));
+            return new RecyclableNamingEnumeration(attributes);
+        } else {
+            return attributes.getAll();
+        }
+    }
+    
+    
+    /**
+     * Get all attribute IDs.
+     */
+    public NamingEnumeration getIDs() {
+        if (attributes == null) {
+            Vector attributeIDs = new Vector();
+            attributeIDs.addElement(CREATION_DATE);
+            attributeIDs.addElement(LAST_MODIFIED);
+            attributeIDs.addElement(NAME);
+            attributeIDs.addElement(TYPE);
+            attributeIDs.addElement(CONTENT_LENGTH);
+            return new RecyclableNamingEnumeration(attributeIDs);
+        } else {
+            return attributes.getIDs();
+        }
+    }
+    
+    
+    /**
+     * Retrieves the number of attributes in the attribute set.
+     */
+    public int size() {
+        if (attributes == null) {
+            return 5;
+        } else {
+            return attributes.size();
+        }
+    }
+    
+    
+    /**
+     * Clone the attributes object (WARNING: fake cloning).
+     */
+    public Object clone() {
+        return this;
+    }
+    
+    
+    /**
+     * Case sensitivity.
+     */
+    public boolean isCaseIgnored() {
+        return false;
+    }
+    
+    
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/ResourceCache.java b/container/catalina/src/share/org/apache/naming/resources/ResourceCache.java
new file mode 100644
index 0000000..6571cd6
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/ResourceCache.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming.resources;
+
+import java.util.HashMap;
+
+/**
+ * Implements a special purpose cache.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class ResourceCache {
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    
+    public ResourceCache() {
+    }
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * Cache.
+     * Path -> Cache entry.
+     */
+    protected CacheEntry[] cache = new CacheEntry[0];
+
+
+    /**
+     * Not found cache.
+     */
+    protected HashMap notFoundCache = new HashMap();
+
+
+    /**
+     * Max size of resources which will have their content cached.
+     */
+    protected int cacheMaxSize = 10240; // 10 MB
+
+
+    /**
+     * Max amount of removals during a make space.
+     */
+    protected int maxAllocateIterations = 20;
+
+
+    /**
+     * Entry hit ratio at which an entry will never be removed from the cache.
+     * Compared with entry.access / hitsCount
+     */
+    protected long desiredEntryAccessRatio = 3;
+
+
+    /**
+     * Spare amount of not found entries.
+     */
+    protected int spareNotFoundEntries = 500;
+
+
+    /**
+     * Current cache size in KB.
+     */
+    protected int cacheSize = 0;
+
+
+    /**
+     * Number of accesses to the cache.
+     */
+    protected long accessCount = 0;
+
+
+    /**
+     * Number of cache hits.
+     */
+    protected long hitsCount = 0;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the access count.
+     * Note: Update is not synced, so the number may not be completely 
+     * accurate.
+     */
+    public long getAccessCount() {
+        return accessCount;
+    }
+
+
+    /**
+     * Return the maximum size of the cache in KB.
+     */
+    public int getCacheMaxSize() {
+        return cacheMaxSize;
+    }
+
+
+    /**
+     * Set the maximum size of the cache in KB.
+     */
+    public void setCacheMaxSize(int cacheMaxSize) {
+        this.cacheMaxSize = cacheMaxSize;
+    }
+
+
+    /**
+     * Return the current cache size in KB.
+     */
+    public int getCacheSize() {
+        return cacheSize;
+    }
+
+
+    /**
+     * Return desired entry access ratio.
+     */
+    public long getDesiredEntryAccessRatio() {
+        return desiredEntryAccessRatio;
+    }
+
+
+    /**
+     * Set the desired entry access ratio.
+     */
+    public void setDesiredEntryAccessRatio(long desiredEntryAccessRatio) {
+        this.desiredEntryAccessRatio = desiredEntryAccessRatio;
+    }
+
+
+    /**
+     * Return the number of cache hits.
+     * Note: Update is not synced, so the number may not be completely 
+     * accurate.
+     */
+    public long getHitsCount() {
+        return hitsCount;
+    }
+
+
+    /**
+     * Return the maximum amount of iterations during a space allocation.
+     */
+    public int getMaxAllocateIterations() {
+        return maxAllocateIterations;
+    }
+
+
+    /**
+     * Set the maximum amount of iterations during a space allocation.
+     */
+    public void setMaxAllocateIterations(int maxAllocateIterations) {
+        this.maxAllocateIterations = maxAllocateIterations;
+    }
+
+
+    /**
+     * Return the amount of spare not found entries.
+     */
+    public int getSpareNotFoundEntries() {
+        return spareNotFoundEntries;
+    }
+
+
+    /**
+     * Set the amount of spare not found entries.
+     */
+    public void setSpareNotFoundEntries(int spareNotFoundEntries) {
+        this.spareNotFoundEntries = spareNotFoundEntries;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    public boolean allocate(int space) {
+
+        int toFree = space - (cacheMaxSize - cacheSize);
+
+        if (toFree <= 0) {
+            return true;
+        }
+
+        // Increase the amount to free so that allocate won't have to run right
+        // away again
+        toFree += (cacheMaxSize / 20);
+
+        int size = notFoundCache.size();
+        if (size > spareNotFoundEntries) {
+            notFoundCache.clear();
+            cacheSize -= size;
+            toFree -= size;
+        }
+
+        if (toFree <= 0) {
+            return true;
+        }
+
+        int attempts = 0;
+        int entriesFound = 0;
+        long totalSpace = 0;
+        int[] toRemove = new int[maxAllocateIterations];
+        while (toFree > 0) {
+            if (attempts == maxAllocateIterations) {
+                // Give up, no changes are made to the current cache
+                return false;
+            }
+            if (toFree > 0) {
+                // Randomly select an entry in the array
+                int entryPos = -1;
+                boolean unique = false;
+                int count = 0;
+                while (!unique) {
+                    unique = true;
+                    entryPos = (int) Math.floor(Math.random() 
+                                                * (cache.length - 1));
+                    // Guarantee uniqueness
+                    for (int i = 0; i < entriesFound; i++) {
+                        if (toRemove[i] == entryPos) {
+                            unique = false;
+                        }
+                    }
+                }
+                long entryAccessRatio = 
+                    ((cache[entryPos].accessCount * 100) / accessCount);
+                if (entryAccessRatio < desiredEntryAccessRatio) {
+                    toRemove[entriesFound] = entryPos;
+                    totalSpace += cache[entryPos].size;
+                    toFree -= cache[entryPos].size;
+                    entriesFound++;
+                }
+            }
+            attempts++;
+        }
+
+        // Now remove the selected entries
+        java.util.Arrays.sort(toRemove, 0, entriesFound);
+        CacheEntry[] newCache = new CacheEntry[cache.length - entriesFound];
+        int pos = 0;
+        int n = -1;
+        if (entriesFound > 0) {
+            n = toRemove[0];
+            for (int i = 0; i < cache.length; i++) {
+                if (i == n) {
+                    if ((pos + 1) < entriesFound) {
+                        n = toRemove[pos + 1];
+                        pos++;
+                    } else {
+                        pos++;
+                        n = -1;
+                    }
+                } else {
+                    newCache[i - pos] = cache[i];
+                }
+            }
+        }
+        cache = newCache;
+        cacheSize -= totalSpace;
+
+        return true;
+
+    }
+
+
+    public CacheEntry lookup(String name) {
+
+        CacheEntry cacheEntry = null;
+        accessCount++;
+        int pos = find(cache, name);
+        if ((pos != -1) && (name.equals(cache[pos].name))) {
+            cacheEntry = cache[pos];
+        }
+        if (cacheEntry == null) {
+            try {
+                cacheEntry = (CacheEntry) notFoundCache.get(name);
+            } catch (Exception e) {
+                // Ignore: the reliability of this lookup is not critical
+            }
+        }
+        if (cacheEntry != null) {
+            hitsCount++;
+        }
+        return cacheEntry;
+
+    }
+
+
+    public void load(CacheEntry entry) {
+        if (entry.exists) {
+            insertCache(entry);
+        } else {
+            notFoundCache.put(entry.name, entry);
+        }
+        cacheSize += entry.size;
+    }
+
+
+    public boolean unload(String name) {
+        CacheEntry removedEntry = removeCache(name);
+        if (removedEntry != null) {
+            cacheSize -= removedEntry.size;
+            return true;
+        } else if (notFoundCache.remove(name) != null) {
+            cacheSize--;
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    private static final int find(CacheEntry[] map, String name) {
+
+        int a = 0;
+        int b = map.length - 1;
+
+        // Special cases: -1 and 0
+        if (b == -1) {
+            return -1;
+        }
+        if (name.compareTo(map[0].name) < 0) {
+            return -1;
+        }
+        if (b == 0) {
+            return 0;
+        }
+
+        int i = 0;
+        while (true) {
+            i = (b + a) / 2;
+            int result = name.compareTo(map[i].name);
+            if (result > 0) {
+                a = i;
+            } else if (result == 0) {
+                return i;
+            } else {
+                b = i;
+            }
+            if ((b - a) == 1) {
+                int result2 = name.compareTo(map[b].name);
+                if (result2 < 0) {
+                    return a;
+                } else {
+                    return b;
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Insert into the right place in a sorted MapElement array, and prevent
+     * duplicates.
+     */
+    private final boolean insertCache(CacheEntry newElement) {
+        CacheEntry[] oldCache = cache;
+        int pos = find(oldCache, newElement.name);
+        if ((pos != -1) && (newElement.name.equals(oldCache[pos].name))) {
+            return false;
+        }
+        CacheEntry[] newCache = new CacheEntry[cache.length + 1];
+        System.arraycopy(oldCache, 0, newCache, 0, pos + 1);
+        newCache[pos + 1] = newElement;
+        System.arraycopy
+            (oldCache, pos + 1, newCache, pos + 2, oldCache.length - pos - 1);
+        cache = newCache;
+        return true;
+    }
+
+
+    /**
+     * Insert into the right place in a sorted MapElement array.
+     */
+    private final CacheEntry removeCache(String name) {
+        CacheEntry[] oldCache = cache;
+        int pos = find(oldCache, name);
+        if ((pos != -1) && (name.equals(oldCache[pos].name))) {
+            CacheEntry[] newCache = new CacheEntry[cache.length - 1];
+            System.arraycopy(oldCache, 0, newCache, 0, pos);
+            System.arraycopy(oldCache, pos + 1, newCache, pos, 
+                             oldCache.length - pos - 1);
+            cache = newCache;
+            return oldCache[pos];
+        }
+        return null;
+    }
+
+
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/WARDirContext.java b/container/catalina/src/share/org/apache/naming/resources/WARDirContext.java
new file mode 100644
index 0000000..39fd951
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/WARDirContext.java
@@ -0,0 +1,951 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+import javax.naming.CompositeName;
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+import org.apache.naming.NamingContextBindingsEnumeration;
+import org.apache.naming.NamingContextEnumeration;
+import org.apache.naming.NamingEntry;
+
+/**
+ * WAR Directory Context implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class WARDirContext extends BaseDirContext {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( WARDirContext.class );
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Builds a WAR directory context using the given environment.
+     */
+    public WARDirContext() {
+        super();
+    }
+
+
+    /**
+     * Builds a WAR directory context using the given environment.
+     */
+    public WARDirContext(Hashtable env) {
+        super(env);
+    }
+
+
+    /**
+     * Constructor used for returning fake subcontexts.
+     */
+    protected WARDirContext(ZipFile base, Entry entries) {
+        this.base = base;
+        this.entries = entries;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The WAR file.
+     */
+    protected ZipFile base = null;
+
+
+    /**
+     * WAR entries.
+     */
+    protected Entry entries = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the document root.
+     * 
+     * @param docBase The new document root
+     * 
+     * @exception IllegalArgumentException if the specified value is not
+     *  supported by this implementation
+     * @exception IllegalArgumentException if this would create a
+     *  malformed URL
+     */
+    public void setDocBase(String docBase) {
+
+	// Validate the format of the proposed document root
+	if (docBase == null)
+	    throw new IllegalArgumentException
+		(sm.getString("resources.null"));
+	if (!(docBase.endsWith(".war")))
+	    throw new IllegalArgumentException
+		(sm.getString("warResources.notWar"));
+
+	// Calculate a File object referencing this document base directory
+	File base = new File(docBase);
+
+	// Validate that the document base is an existing directory
+	if (!base.exists() || !base.canRead() || base.isDirectory())
+	    throw new IllegalArgumentException
+		(sm.getString("warResources.invalidWar", docBase));
+        try {
+            this.base = new ZipFile(base);
+        } catch (Exception e) {
+	    throw new IllegalArgumentException
+		(sm.getString("warResources.invalidWar", e.getMessage()));
+        }
+        super.setDocBase(docBase);
+
+        loadEntries();
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Release any resources allocated for this directory context.
+     */
+    public void release() {
+
+        entries = null;
+        if (base != null) {
+            try {
+                base.close();
+            } catch (IOException e) {
+                log.warn
+                    ("Exception closing WAR File " + base.getName(), e);
+            }
+        }
+        base = null;
+        super.release();
+
+    }
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Retrieves the named object.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(String name)
+        throws NamingException {
+        return lookup(new CompositeName(name));
+    }
+
+
+    /**
+     * Retrieves the named object. If name is empty, returns a new instance 
+     * of this context (which represents the same naming context as this 
+     * context, but its environment may be modified independently and it may 
+     * be accessed concurrently).
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(Name name)
+        throws NamingException {
+        if (name.isEmpty())
+            return this;
+        Entry entry = treeLookup(name);
+        if (entry == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+        ZipEntry zipEntry = entry.getEntry();
+        if (zipEntry.isDirectory())
+            return new WARDirContext(base, entry);
+        else
+            return new WARResource(entry.getEntry());
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name. Both names are relative to this context. Any attributes 
+     * associated with the old name become associated with the new name. 
+     * Intermediate contexts of the old name are not changed.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(String oldName, String newName)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(String name)
+        throws NamingException {
+        return list(new CompositeName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name name)
+        throws NamingException {
+        if (name.isEmpty())
+            return new NamingContextEnumeration(list(entries).iterator());
+        Entry entry = treeLookup(name);
+        if (entry == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+        return new NamingContextEnumeration(list(entry).iterator());
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(String name)
+        throws NamingException {
+        return listBindings(new CompositeName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+        throws NamingException {
+        if (name.isEmpty())
+            return new NamingContextBindingsEnumeration(list(entries).iterator());
+        Entry entry = treeLookup(name);
+        if (entry == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+        return new NamingContextBindingsEnumeration(list(entry).iterator());
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any 
+     * attributes associated with the name are also removed. Intermediate 
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * In a federated naming system, a context from one naming system may be 
+     * bound to a name in another. One can subsequently look up and perform 
+     * operations on the foreign context using a composite name. However, an 
+     * attempt destroy the context using this composite name will fail with 
+     * NotContextException, because the foreign context is not a "subcontext" 
+     * of the context in which it is bound. Instead, use unbind() to remove 
+     * the binding of the foreign context. Destroying the foreign context 
+     * requires that the destroySubcontext() be performed on a context from 
+     * the foreign context's "native" naming system.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name. If the object bound to name is not a 
+     * link, returns the object itself.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(String name)
+        throws NamingException {
+        // Note : Links are not supported
+        return lookup(name);
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in 
+     * their respective namespaces. For example, an LDAP entry has a 
+     * distinguished name, and a DNS record has a fully qualified name. This 
+     * method allows the client application to retrieve this name. The string 
+     * returned by this method is not a JNDI composite name and should not be 
+     * passed directly to context methods. In naming systems for which the 
+     * notion of full name does not make sense, 
+     * OperationNotSupportedException is thrown.
+     * 
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does 
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+        throws NamingException {
+        return docBase;
+    }
+
+
+    // ----------------------------------------------------- DirContext Methods
+
+
+    /**
+     * Retrieves selected attributes associated with a named object. 
+     * See the class description regarding attribute models, attribute type 
+     * names, and operational attributes.
+     * 
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null 
+     * indicates that all attributes should be retrieved; an empty array 
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(String name, String[] attrIds)
+        throws NamingException {
+        return getAttributes(new CompositeName(name), attrIds);
+    }
+
+
+    /**
+     * Retrieves all of the attributes associated with a named object. 
+     * 
+     * @return the set of attributes associated with name. 
+     * Returns an empty attribute set if name has no attributes; never null.
+     * @param name the name of the object from which to retrieve attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name name, String[] attrIds)
+        throws NamingException {
+        
+        Entry entry = null;
+        if (name.isEmpty())
+            entry = entries;
+        else
+            entry = treeLookup(name);
+        if (entry == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+        
+        ZipEntry zipEntry = entry.getEntry();
+
+        ResourceAttributes attrs = new ResourceAttributes();
+        attrs.setCreationDate(new Date(zipEntry.getTime()));
+        attrs.setName(entry.getName());
+        if (!zipEntry.isDirectory())
+            attrs.setResourceType("");
+        attrs.setContentLength(zipEntry.getSize());
+        attrs.setLastModified(zipEntry.getTime());
+        
+        return attrs;
+        
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object. The order of 
+     * the modifications is not specified. Where possible, the modifications 
+     * are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE, 
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not 
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, int mod_op, Attributes attrs)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an 
+     * ordered list of modifications. The modifications are performed in the 
+     * order specified. Each modification specifies a modification operation 
+     * code and an attribute on which to operate. Where possible, the 
+     * modifications are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may 
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, ModificationItem[] mods)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes. If attrs 
+     * is null, the resulting binding will have the attributes associated 
+     * with obj if obj is a DirContext, and no attributes otherwise. If attrs 
+     * is non-null, the resulting binding will have attrs as its attributes; 
+     * any attributes associated with obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes, 
+     * overwriting any existing binding. If attrs is null and obj is a 
+     * DirContext, the attributes from obj are used. If attrs is null and obj 
+     * is not a DirContext, any existing attributes associated with the object
+     * already bound in the directory remain unchanged. If attrs is non-null, 
+     * any existing attributes associated with the object already bound in 
+     * the directory are removed and attrs is associated with the named 
+     * object. If obj is a DirContext and attrs is non-null, the attributes 
+     * of obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes. 
+     * This method creates a new subcontext with the given name, binds it in 
+     * the target context (that named by all but terminal atomic component of 
+     * the name), and associates the supplied attributes with the newly 
+     * created object. All intermediate and target contexts must already 
+     * exist. If attrs is null, this method is equivalent to 
+     * Context.createSubcontext().
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all 
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(String name, Attributes attrs)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object. The schema 
+     * describes rules regarding the structure of the namespace and the 
+     * attributes stored within it. The schema specifies what types of 
+     * objects can be added to the directory and where they can be added; 
+     * what mandatory and optional attributes an object can have. The range 
+     * of support for schemas is directory-specific.
+     * 
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named 
+     * object's class definitions.
+     * 
+     * @param name the name of the object whose object class definition is to 
+     * be retrieved
+     * @return the DirContext containing the named object's class 
+     * definitions; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes, and retrieves selected attributes. The search is 
+     * performed using the default SearchControls settings.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates 
+     * that all attributes are to be returned; an empty array indicates that 
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes. This method returns all the attributes of such objects. 
+     * It is equivalent to supplying null as the atributesToReturn parameter 
+     * to the method search(Name, Attributes, String[]).
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be 
+     * null
+     * @param cons the search controls that control the search. If null, 
+     * the default search controls are used (equivalent to 
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy 
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter specified 
+     * is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls 
+     * contain invalid settings
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filter, 
+                                    SearchControls cons)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search. 
+     * The expression may contain variables of the form "{i}" where i is a 
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the 
+     * variables in filterExpr. The value of filterArgs[i] will replace each 
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the 
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the 
+     * filter; never null
+     * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i} 
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid 
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs 
+     * represents an invalid search filter
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filterExpr, 
+                                    Object[] filterArgs, SearchControls cons)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Normalize the name of an entry read from the Zip.
+     */
+    protected String normalize(ZipEntry entry) {
+
+        String result = "/" + entry.getName();
+        if (entry.isDirectory()) {
+            result = result.substring(0, result.length() - 1);
+        }
+        return result;
+
+    }
+
+
+    /**
+     * Constructs a tree of the entries contained in a WAR file.
+     */
+    protected void loadEntries() {
+
+        try {
+
+            Enumeration entryList = base.entries();
+            entries = new Entry("/", new ZipEntry("/"));
+            
+            while (entryList.hasMoreElements()) {
+                
+                ZipEntry entry = (ZipEntry) entryList.nextElement();
+                String name = normalize(entry);
+                int pos = name.lastIndexOf('/');
+                // Check that parent entries exist and, if not, create them.
+                // This fixes a bug for war files that don't record separate
+                // zip entries for the directories.
+                int currentPos = -1;
+                int lastPos = 0;
+                while ((currentPos = name.indexOf('/', lastPos)) != -1) {
+                    Name parentName = new CompositeName(name.substring(0, lastPos));
+                    Name childName = new CompositeName(name.substring(0, currentPos));
+                    String entryName = name.substring(lastPos, currentPos);
+                    // Parent should have been created in last cycle through
+                    // this loop
+                    Entry parent = treeLookup(parentName);
+                    Entry child = treeLookup(childName);
+                    if (child == null) {
+                        // Create a new entry for missing entry and strip off
+                        // the leading '/' character and appended on by the
+                        // normalize method and add '/' character to end to
+                        // signify that it is a directory entry
+                        String zipName = name.substring(1, currentPos) + "/";
+                        child = new Entry(entryName, new ZipEntry(zipName));
+                        if (parent != null)
+                            parent.addChild(child);
+                    }
+                    // Increment lastPos
+                    lastPos = currentPos + 1;
+                }
+                String entryName = name.substring(pos + 1, name.length());
+                Name compositeName = new CompositeName(name.substring(0, pos));
+                Entry parent = treeLookup(compositeName);
+                Entry child = new Entry(entryName, entry);
+                if (parent != null)
+                    parent.addChild(child);
+                
+            }
+
+        } catch (Exception e) {
+        }
+
+    }
+
+
+    /**
+     * Entry tree lookup.
+     */
+    protected Entry treeLookup(Name name) {
+        if (name.isEmpty())
+            return entries;
+        Entry currentEntry = entries;
+        for (int i = 0; i < name.size(); i++) {
+            if (name.get(i).length() == 0)
+                continue;
+            currentEntry = currentEntry.getChild(name.get(i));
+            if (currentEntry == null)
+                return null;
+        }
+        return currentEntry;
+    }
+
+
+    /**
+     * List children as objects.
+     */
+    protected ArrayList list(Entry entry) {
+        
+        ArrayList entries = new ArrayList();
+        Entry[] children = entry.getChildren();
+        Arrays.sort(children);
+        NamingEntry namingEntry = null;
+        
+        for (int i = 0; i < children.length; i++) {
+            ZipEntry current = children[i].getEntry();
+            Object object = null;
+            if (current.isDirectory()) {
+                object = new WARDirContext(base, children[i]);
+            } else {
+                object = new WARResource(current);
+            }
+            namingEntry = new NamingEntry
+                (children[i].getName(), object, NamingEntry.ENTRY);
+            entries.add(namingEntry);
+        }
+        
+        return entries;
+        
+    }
+
+
+    // ---------------------------------------------------- Entries Inner Class
+
+
+    /**
+     * Entries structure.
+     */
+    protected class Entry implements Comparable {
+
+
+        // -------------------------------------------------------- Constructor
+        
+        
+        public Entry(String name, ZipEntry entry) {
+            this.name = name;
+            this.entry = entry;
+        }
+        
+        
+        // --------------------------------------------------- Member Variables
+        
+        
+        protected String name = null;
+        
+        
+        protected ZipEntry entry = null;
+        
+        
+        protected Entry children[] = new Entry[0];
+        
+        
+        // ----------------------------------------------------- Public Methods
+        
+        
+        public int compareTo(Object o) {
+            if (!(o instanceof Entry))
+                return (+1);
+            return (name.compareTo(((Entry) o).getName()));
+        }
+
+        public ZipEntry getEntry() {
+            return entry;
+        }
+        
+        
+        public String getName() {
+            return name;
+        }
+        
+        
+        public void addChild(Entry entry) {
+            Entry[] newChildren = new Entry[children.length + 1];
+            for (int i = 0; i < children.length; i++)
+                newChildren[i] = children[i];
+            newChildren[children.length] = entry;
+            children = newChildren;
+        }
+
+
+        public Entry[] getChildren() {
+            return children;
+        }
+
+
+        public Entry getChild(String name) {
+            for (int i = 0; i < children.length; i++) {
+                if (children[i].name.equals(name)) {
+                    return children[i];
+                }
+            }
+            return null;
+        }
+
+
+    }
+
+
+    // ------------------------------------------------ WARResource Inner Class
+
+
+    /**
+     * This specialized resource implementation avoids opening the IputStream
+     * to the WAR right away.
+     */
+    protected class WARResource extends Resource {
+        
+        
+        // -------------------------------------------------------- Constructor
+        
+        
+        public WARResource(ZipEntry entry) {
+            this.entry = entry;
+        }
+        
+        
+        // --------------------------------------------------- Member Variables
+        
+        
+        protected ZipEntry entry;
+        
+        
+        // ----------------------------------------------------- Public Methods
+        
+        
+        /**
+         * Content accessor.
+         * 
+         * @return InputStream
+         */
+        public InputStream streamContent()
+            throws IOException {
+            try {
+                if (binaryContent == null) {
+                    inputStream = base.getInputStream(entry);
+                }
+            } catch (ZipException e) {
+                throw new IOException(e.getMessage());
+            }
+            return super.streamContent();
+        }
+        
+        
+    }
+
+
+}
+
diff --git a/container/catalina/src/share/org/apache/naming/resources/jndi/Handler.java b/container/catalina/src/share/org/apache/naming/resources/jndi/Handler.java
new file mode 100644
index 0000000..203987e
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/jndi/Handler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.naming.resources.jndi;
+
+import org.apache.naming.resources.DirContextURLStreamHandler;
+
+/**
+ * Stream handler to a JNDI directory context.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class Handler 
+    extends DirContextURLStreamHandler {
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    
+    public Handler() {
+    }
+    
+    
+}
diff --git a/container/catalina/src/share/org/apache/naming/resources/package.html b/container/catalina/src/share/org/apache/naming/resources/package.html
new file mode 100644
index 0000000..02dc56a
--- /dev/null
+++ b/container/catalina/src/share/org/apache/naming/resources/package.html
@@ -0,0 +1,15 @@
+<body>
+
+<p>This package contains the resources directory context implemetation.
+This includes :
+<ul>
+<li>A CacheDirContext which handles caching and acts as a proxy for the real 
+resources context</li>
+<li>A FileDirContext, an implementation of DirContext to access the 
+filesystem</li>
+</ul>
+</p>
+
+<p></p>
+
+</body>
diff --git a/container/catalina/src/temp/README.txt b/container/catalina/src/temp/README.txt
new file mode 100644
index 0000000..6816c1d
--- /dev/null
+++ b/container/catalina/src/temp/README.txt
@@ -0,0 +1,5 @@
+This temp directory is used by the JVM for temporary file storage.
+The JVM is configured to use this as its java.io.tmpdir in the
+catalina.sh and catalina.bat scripts.  Tomcat is configured to use
+this temporary directory rather than its default for security reasons.
+The temp directory must exist for Tomcat to work correctly.
diff --git a/container/catalina/src/test/org/apache/catalina/util/CookieToolsTestCase.java b/container/catalina/src/test/org/apache/catalina/util/CookieToolsTestCase.java
new file mode 100644
index 0000000..58c0bef
--- /dev/null
+++ b/container/catalina/src/test/org/apache/catalina/util/CookieToolsTestCase.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.catalina.util;
+
+import javax.servlet.http.Cookie;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Unit tests for the <code>CookieTools</code> class.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class CookieToolsTestCase extends TestCase {
+
+
+    public static void main(String args[]) {
+        System.out.println("TestCase started");
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * A "version 0" cookie.
+     */
+    protected Cookie version0 = null;
+
+
+    /**
+     * A "version 1" cookie.
+     */
+    protected Cookie version1 = null;
+
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this test case.
+     *
+     * @param name Name of the test case
+     */
+    public CookieToolsTestCase(String name) {
+
+        super(name);
+
+    }
+
+
+    // --------------------------------------------------- Overall Test Methods
+
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() {
+
+        version0 = new Cookie("Version 0 Name", "Version 0 Value");
+        version0.setComment("Version 0 Comment");
+        version0.setDomain("localhost");
+        version0.setPath("/version0");
+        version0.setVersion(0);
+
+        version1 = new Cookie("Version 1 Name", "Version 1 Value");
+        version1.setComment("Version 1 Comment");
+        version1.setDomain("localhost");
+        version1.setPath("/version1");
+        version1.setVersion(1);
+
+    }
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() {
+
+        return (new TestSuite(CookieToolsTestCase.class));
+
+    }
+
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+
+        version0 = null;
+        version1 = null;
+
+    }
+
+
+    // ------------------------------------------------ Individual Test Methods
+
+
+    /**
+     * Check the value returned by <code>getCookieHeaderName()</code>.
+     */
+    public void testGetCookieHeaderName() {
+
+        assertEquals("Version 0 cookie header name", "Set-Cookie",
+                     CookieTools.getCookieHeaderName(version0));
+        assertEquals("Version 1 cookie header name", "Set-Cookie2",
+                     CookieTools.getCookieHeaderName(version1));
+
+    }
+
+
+    /**
+     * Check the value returned by <code>getCookieHeaderValue()</code>
+     */
+    public void testGetCookieHeaderValue() {
+
+        StringBuffer sb = null;
+
+        sb = new StringBuffer();
+        CookieTools.getCookieHeaderValue(version0, sb);
+        assertEquals("Version 0 cookie header value",
+                     "Version 0 Name=Version 0 Value;Domain=localhost;Path=/version0",
+                     sb.toString());
+
+        sb = new StringBuffer();
+        CookieTools.getCookieHeaderValue(version1, sb);
+        assertEquals("Version 1 cookie header value",
+                     "Version 1 Name=\"Version 1 Value\";Version=1;Comment=\"Version 1 Comment\";Domain=localhost;Discard;Path=\"/version1\"",
+                     sb.toString());
+
+    }
+
+
+}
diff --git a/container/catalina/src/test/org/apache/catalina/util/URLTestCase.java b/container/catalina/src/test/org/apache/catalina/util/URLTestCase.java
new file mode 100644
index 0000000..25bae80
--- /dev/null
+++ b/container/catalina/src/test/org/apache/catalina/util/URLTestCase.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.catalina.util;
+
+
+import java.net.MalformedURLException;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Unit tests for the <code>org.apache.catalina.util.URL</code> class.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class URLTestCase extends TestCase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this test case.
+     *
+     * @param name Name of the test case
+     */
+    public URLTestCase(String name) {
+
+        super(name);
+
+    }
+
+
+    // --------------------------------------------------- Overall Test Methods
+
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() {
+
+        ; // No action required
+
+    }
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() {
+
+        return (new TestSuite(URLTestCase.class));
+
+    }
+
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+
+        ; // No action required
+
+    }
+
+
+    // ------------------------------------------------ Individual Test Methods
+
+
+    /**
+     * Negative tests for absolute URL strings in various patterns.  Each of
+     * these should throw <code>MalformedURLException</code>.
+     */
+    public void testNegativeAbsolute() {
+
+        negative("index.html");
+        negative("index.html#ref");
+        negative("index.html?name=value");
+        negative("index.html?name=value#ref");
+
+        negative("/index.html");
+        negative("/index.html#ref");
+        negative("/index.html?name=value");
+        negative("/index.html?name=value#ref");
+
+    }
+
+
+    /**
+     * Negative tests for <code>normalize()</code>.  Attempts to normalize
+     * these legal URLs should throw <code>MalformedURLException</code>.
+     */
+    public void testNegativeNormalize() {
+
+        normalize("http://localhost/..");
+        normalize("http://localhost/..#ref");
+        normalize("http://localhost/..?name=value");
+        normalize("http://localhost/..?name=value#ref");
+
+        normalize("http://localhost:8080/..");
+        normalize("http://localhost:8080/..#ref");
+        normalize("http://localhost:8080/..?name=value");
+        normalize("http://localhost:8080/..?name=value#ref");
+ 
+        normalize("http://localhost/../");
+        normalize("http://localhost/../#ref");
+        normalize("http://localhost/../?name=value");
+        normalize("http://localhost/../?name=value#ref");
+
+        normalize("http://localhost:8080/../");
+        normalize("http://localhost:8080/../#ref");
+        normalize("http://localhost:8080/../?name=value");
+        normalize("http://localhost:8080/../?name=value#ref");
+ 
+        normalize("http://localhost/index.html/../../foo.html");
+        normalize("http://localhost/index.html/../../foo.html#ref");
+        normalize("http://localhost/index.html/../../foo.html?name=value");
+        normalize("http://localhost/index.html/../../foo.html?name=value#ref");
+
+        normalize("http://localhost:8080/index.html/../../foo.html");
+        normalize("http://localhost:8080/index.html/../../foo.html#ref");
+        normalize("http://localhost:8080/index.html/../../foo.html?name=value");
+        normalize("http://localhost:8080/index.html/../../foo.html?name=value#ref");
+ 
+    }
+
+
+    /**
+     * Negative tests for relative URL strings in various patterns.  Each of
+     * these should throw <code>MalformedURLException</code>.
+     */
+    public void testNegativeRelative() {
+
+        // Commented out because java.net.URL ignores extraneous "../"
+        //        negative("http://a/b/c/d;p?q", "../../../g");
+        //        negative("http://a/b/c/d;p?q", "/../g");
+
+    }
+
+
+    /**
+     * Positive tests for absolute URL strings in various patterns.
+     */
+    public void testPositiveAbsolute() {
+
+        positive("http://a/b/c/d;p?q");
+
+        positive("http://localhost/index.html");
+        positive("http://localhost/index.html#ref");
+        positive("http://localhost/index.html?name=value");
+        positive("http://localhost/index.html?name=value#ref");
+
+        positive("http://localhost:8080/index.html");
+        positive("http://localhost:8080/index.html#ref");
+        positive("http://localhost:8080/index.html?name=value");
+        positive("http://localhost:8080/index.html?name=value#ref");
+
+        positive("http://localhost/index.html/.");
+        positive("http://localhost/index.html/.#ref");
+        positive("http://localhost/index.html/.?name=value");
+        positive("http://localhost/index.html/.?name=value#ref");
+
+        positive("http://localhost:8080/index.html/.");
+        positive("http://localhost:8080/index.html/.#ref");
+        positive("http://localhost:8080/index.html/.?name=value");
+        positive("http://localhost:8080/index.html/.?name=value#ref");
+
+        positive("http://localhost/index.html/foo/..");
+        positive("http://localhost/index.html/foo/..#ref");
+        positive("http://localhost/index.html/foo/..?name=value");
+        positive("http://localhost/index.html/foo/..?name=value#ref");
+
+        positive("http://localhost:8080/index.html/foo/..");
+        positive("http://localhost:8080/index.html/foo/..#ref");
+        positive("http://localhost:8080/index.html/foo/..?name=value");
+        positive("http://localhost:8080/index.html/foo/..?name=value#ref");
+
+        positive("http://localhost/index.html/../foo.html");
+        positive("http://localhost/index.html/../foo.html#ref");
+        positive("http://localhost/index.html/../foo.html?name=value");
+        positive("http://localhost/index.html/../foo.html?name=value#ref");
+
+        positive("http://localhost:8080/index.html/../foo.html");
+        positive("http://localhost:8080/index.html/../foo.html#ref");
+        positive("http://localhost:8080/index.html/../foo.html?name=value");
+        positive("http://localhost:8080/index.html/../foo.html?name=value#ref");
+
+        positive("http://localhost");
+        positive("http://localhost#ref");
+        positive("http://localhost?name=value");
+        positive("http://localhost?name=value#ref");
+
+        positive("http://localhost:8080");
+        positive("http://localhost:8080#ref");
+        positive("http://localhost:8080?name=value");
+        positive("http://localhost:8080?name=value#ref");
+
+        positive("http://localhost/");
+        positive("http://localhost/#ref");
+        positive("http://localhost/?name=value");
+        positive("http://localhost/?name=value#ref");
+
+        positive("http://localhost:8080/");
+        positive("http://localhost:8080/#ref");
+        positive("http://localhost:8080/?name=value");
+        positive("http://localhost:8080/?name=value#ref");
+
+    }
+
+
+    /**
+     * Positive tests for normalizing absolute URL strings in various patterns.
+     */
+    public void testPositiveNormalize() {
+
+        normalize("http://a/b/c/d;p?q",
+                  "http://a/b/c/d;p?q");
+
+        normalize("http://localhost/index.html",
+                  "http://localhost/index.html");
+        normalize("http://localhost/index.html#ref",
+                  "http://localhost/index.html#ref");
+        normalize("http://localhost/index.html?name=value",
+                  "http://localhost/index.html?name=value");
+        normalize("http://localhost/index.html?name=value#ref",
+                  "http://localhost/index.html?name=value#ref");
+
+        normalize("http://localhost:8080/index.html",
+                  "http://localhost:8080/index.html");
+        normalize("http://localhost:8080/index.html#ref",
+                  "http://localhost:8080/index.html#ref");
+        normalize("http://localhost:8080/index.html?name=value",
+                  "http://localhost:8080/index.html?name=value");
+        normalize("http://localhost:8080/index.html?name=value#ref",
+                  "http://localhost:8080/index.html?name=value#ref");
+
+        normalize("http://localhost/./index.html",
+                  "http://localhost/index.html");
+        normalize("http://localhost/./index.html#ref",
+                  "http://localhost/index.html#ref");
+        normalize("http://localhost/./index.html?name=value",
+                  "http://localhost/index.html?name=value");
+        normalize("http://localhost/./index.html?name=value#ref",
+                  "http://localhost/index.html?name=value#ref");
+
+        normalize("http://localhost:8080/./index.html",
+                  "http://localhost:8080/index.html");
+        normalize("http://localhost:8080/./index.html#ref",
+                  "http://localhost:8080/index.html#ref");
+        normalize("http://localhost:8080/./index.html?name=value",
+                  "http://localhost:8080/index.html?name=value");
+        normalize("http://localhost:8080/./index.html?name=value#ref",
+                  "http://localhost:8080/index.html?name=value#ref");
+
+        normalize("http://localhost/index.html/.",
+                  "http://localhost/index.html/");
+        normalize("http://localhost/index.html/.#ref",
+                  "http://localhost/index.html/#ref");
+        normalize("http://localhost/index.html/.?name=value",
+                  "http://localhost/index.html/?name=value");
+        normalize("http://localhost/index.html/.?name=value#ref",
+                  "http://localhost/index.html/?name=value#ref");
+
+        normalize("http://localhost:8080/index.html/.",
+                  "http://localhost:8080/index.html/");
+        normalize("http://localhost:8080/index.html/.#ref",
+                  "http://localhost:8080/index.html/#ref");
+        normalize("http://localhost:8080/index.html/.?name=value",
+                  "http://localhost:8080/index.html/?name=value");
+        normalize("http://localhost:8080/index.html/.?name=value#ref",
+                  "http://localhost:8080/index.html/?name=value#ref");
+
+        normalize("http://localhost/index.html/./",
+                  "http://localhost/index.html/");
+        normalize("http://localhost/index.html/./#ref",
+                  "http://localhost/index.html/#ref");
+        normalize("http://localhost/index.html/./?name=value",
+                  "http://localhost/index.html/?name=value");
+        normalize("http://localhost/index.html/./?name=value#ref",
+                  "http://localhost/index.html/?name=value#ref");
+
+        normalize("http://localhost:8080/index.html/./",
+                  "http://localhost:8080/index.html/");
+        normalize("http://localhost:8080/index.html/./#ref",
+                  "http://localhost:8080/index.html/#ref");
+        normalize("http://localhost:8080/index.html/./?name=value",
+                  "http://localhost:8080/index.html/?name=value");
+        normalize("http://localhost:8080/index.html/./?name=value#ref",
+                  "http://localhost:8080/index.html/?name=value#ref");
+
+        normalize("http://localhost/foo.html/../index.html",
+                  "http://localhost/index.html");
+        normalize("http://localhost/foo.html/../index.html#ref",
+                  "http://localhost/index.html#ref");
+        normalize("http://localhost/foo.html/../index.html?name=value",
+                  "http://localhost/index.html?name=value");
+        normalize("http://localhost/foo.html/../index.html?name=value#ref",
+                  "http://localhost/index.html?name=value#ref");
+
+        normalize("http://localhost:8080/foo.html/../index.html",
+                  "http://localhost:8080/index.html");
+        normalize("http://localhost:8080/foo.html/../index.html#ref",
+                  "http://localhost:8080/index.html#ref");
+        normalize("http://localhost:8080/foo.html/../index.html?name=value",
+                  "http://localhost:8080/index.html?name=value");
+        normalize("http://localhost:8080/foo.html/../index.html?name=value#ref",
+                  "http://localhost:8080/index.html?name=value#ref");
+
+        normalize("http://localhost/index.html/foo.html/..",
+                  "http://localhost/index.html/");
+        normalize("http://localhost/index.html/foo.html/..#ref",
+                  "http://localhost/index.html/#ref");
+        normalize("http://localhost/index.html/foo.html/..?name=value",
+                  "http://localhost/index.html/?name=value");
+        normalize("http://localhost/index.html/foo.html/..?name=value#ref",
+                  "http://localhost/index.html/?name=value#ref");
+
+        normalize("http://localhost:8080/index.html/foo.html/..",
+                  "http://localhost:8080/index.html/");
+        normalize("http://localhost:8080/index.html/foo.html/..#ref",
+                  "http://localhost:8080/index.html/#ref");
+        normalize("http://localhost:8080/index.html/foo.html/..?name=value",
+                  "http://localhost:8080/index.html/?name=value");
+        normalize("http://localhost:8080/index.html/foo.html/..?name=value#ref",
+                  "http://localhost:8080/index.html/?name=value#ref");
+
+        normalize("http://localhost/index.html/foo.html/../",
+                  "http://localhost/index.html/");
+        normalize("http://localhost/index.html/foo.html/../#ref",
+                  "http://localhost/index.html/#ref");
+        normalize("http://localhost/index.html/foo.html/../?name=value",
+                  "http://localhost/index.html/?name=value");
+        normalize("http://localhost/index.html/foo.html/../?name=value#ref",
+                  "http://localhost/index.html/?name=value#ref");
+
+        normalize("http://localhost:8080/index.html/foo.html/../",
+                  "http://localhost:8080/index.html/");
+        normalize("http://localhost:8080/index.html/foo.html/../#ref",
+                  "http://localhost:8080/index.html/#ref");
+        normalize("http://localhost:8080/index.html/foo.html/../?name=value",
+                  "http://localhost:8080/index.html/?name=value");
+        normalize("http://localhost:8080/index.html/foo.html/../?name=value#ref",
+                  "http://localhost:8080/index.html/?name=value#ref");
+
+    }
+
+
+    /**
+     * Positive tests for relative URL strings in various patterns.
+     */
+    public void testPositiveRelative() {
+
+        // Test cases based on RFC 2396, Appendix C
+        positive("http://a/b/c/d;p?q", "http:h");
+        positive("http://a/b/c/d;p?q", "g");
+        positive("http://a/b/c/d;p?q", "./g");
+        positive("http://a/b/c/d;p?q", "g/");
+        positive("http://a/b/c/d;p?q", "/g");
+        //        positive("http://a/b/c/d;p?q", "//g");
+        positive("http://a/b/c/d;p?q", "?y");
+        positive("http://a/b/c/d;p?q", "g?y");
+        //        positive("http://a/b/c/d;p?q", "#s");
+        positive("http://a/b/c/d;p?q", "g#s");
+        positive("http://a/b/c/d;p?q", "g?y#s");
+        positive("http://a/b/c/d;p?q", ";x");
+        positive("http://a/b/c/d;p?q", "g;x");
+        positive("http://a/b/c/d;p?q", "g;x?y#s");
+        positive("http://a/b/c/d;p?q", ".");
+        positive("http://a/b/c/d;p?q", "./");
+        positive("http://a/b/c/d;p?q", "..");
+        positive("http://a/b/c/d;p?q", "../");
+        positive("http://a/b/c/d;p?q", "../g");
+        positive("http://a/b/c/d;p?q", "../..");
+        positive("http://a/b/c/d;p?q", "../../");
+        positive("http://a/b/c/d;p?q", "../../g");
+        // Commented because java.net.URL doesn't normalize out the "/./"????
+        //        positive("http://a/b/c/d;p?q", "/./g");
+        positive("http://a/b/c/d;p?q", "g.");
+        positive("http://a/b/c/d;p?q", ".g");
+        positive("http://a/b/c/d;p?q", "g..");
+        positive("http://a/b/c/d;p?q", "..g");
+        positive("http://a/b/c/d;p?q", "./../g");
+        positive("http://a/b/c/d;p?q", "./g/.");
+        positive("http://a/b/c/d;p?q", "g/./h");
+        positive("http://a/b/c/d;p?q", "g/../h");
+        positive("http://a/b/c/d;p?q", "g;x=1/./y");
+        positive("http://a/b/c/d;p?q", "g;x=1/../y");
+        positive("http://a/b/c/d;p?q", "g?y/./x");
+        positive("http://a/b/c/d;p?q", "g?y/../x");
+        positive("http://a/b/c/d;p?q", "g#s/./x");
+        positive("http://a/b/c/d;p?q", "g#s/../x");
+        positive("http://a/b", "c");
+        positive("http://a/b/", "c");
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Check that both our URL class and <code>java.net.URL</code> throw
+     * <code>MalformedURLException</code> on an absolute URL specification.
+     *
+     * @param spec Absolute URL specification to be checked
+     */
+    private void negative(String spec) {
+
+        try {
+            java.net.URL url = new java.net.URL(spec);
+            fail(spec + " should have failed on java.net.URL " +
+                 "but returned " + url.toExternalForm());
+        } catch (MalformedURLException e) {
+            ; // Expected response
+        }
+
+        try {
+            URL url = new URL(spec);
+            fail(spec + " should have failed on tested URL " +
+                 "but returned " + url.toExternalForm());
+        } catch (MalformedURLException e) {
+            ; // Expected response
+        }
+
+    }
+
+
+    /**
+     * Check that both our URL class and <code>java.net.URL</code> throw
+     * <code>MalformedURLException</code> on an absolute URL specification
+     * plus the corresponding relative URL specification.
+     *
+     * @param abs Absolute URL specification to be checked
+     * @param rel Relative URL specification to be checked
+     */
+    private void negative(String abs, String rel) {
+
+        java.net.URL baseNet = null;
+        URL baseUrl = null;
+
+        try {
+            baseNet = new java.net.URL(abs);
+        } catch (MalformedURLException e) {
+            fail(abs + " net URL threw " + e);
+        }
+
+        try {
+            baseUrl = new URL(abs);
+        } catch (MalformedURLException e) {
+            fail(abs + " url URL threw " + e);
+        }
+
+        try {
+            java.net.URL url = new java.net.URL(baseNet, rel);
+            fail(rel + " should have failed on java.net.URL " +
+                 "but returned " + url.toExternalForm());
+        } catch (MalformedURLException e) {
+            ; // Expected response
+        }
+
+        try {
+            URL url = new URL(baseUrl, rel);
+            fail(rel + " should have failed on tested URL " +
+                 "but returned " + url.toExternalForm());
+        } catch (MalformedURLException e) {
+            ; // Expected response
+        }
+
+    }
+
+
+    /**
+     * Attempts to normalize the specified URL should throw
+     * MalformedURLException.
+     *
+     * @param spec Unnormalized version of the URL specification
+     */
+    private void normalize(String spec) {
+
+        URL url = null;
+        try {
+            url = new URL(spec);
+        } catch (Throwable t) {
+            fail(spec + " should not have thrown " + t);
+        }
+
+        try {
+            url.normalize();
+            fail(spec + ".normalize() should have thrown MUE");
+        } catch (MalformedURLException e) {
+            ; // Expected result
+        }
+
+    }
+
+
+    /**
+     * It should be possible to normalize the specified URL into the
+     * specified normalized form.
+     *
+     * @param spec Unnormalized version of the URL specification
+     * @param norm Normalized version of the URL specification
+     */
+    private void normalize(String spec, String norm) {
+
+        try {
+            URL url = new URL(spec);
+            url.normalize();
+            assertEquals(spec + ".normalize()", norm, url.toExternalForm());
+        } catch (Throwable t) {
+            fail(spec + ".normalize() threw " + t);
+        }
+
+    }
+
+
+    /**
+     * Check the details of our URL class against <code>java.net.URL</code>
+     * for an absolute URL specification.
+     *
+     * @param spec Absolute URL specification to be checked
+     */
+    private void positive(String spec) {
+
+        // Compare results with what java.net.URL returns
+        try {
+            URL url = new URL(spec);
+            java.net.URL net = new java.net.URL(spec);
+            assertEquals(spec + " toExternalForm()",
+                         net.toExternalForm(),
+                         url.toExternalForm());
+            assertEquals(spec + ".getAuthority()",
+                         net.getAuthority(),
+                         url.getAuthority());
+            assertEquals(spec + ".getFile()",
+                         net.getFile(),
+                         url.getFile());
+            assertEquals(spec + ".getHost()",
+                         net.getHost(),
+                         url.getHost());
+            assertEquals(spec + ".getPath()",
+                         net.getPath(),
+                         url.getPath());
+            assertEquals(spec + ".getPort()",
+                         net.getPort(),
+                         url.getPort());
+            assertEquals(spec + ".getProtocol()",
+                         net.getProtocol(),
+                         url.getProtocol());
+            assertEquals(spec + ".getQuery()",
+                         net.getQuery(),
+                         url.getQuery());
+            assertEquals(spec + ".getRef()",
+                         net.getRef(),
+                         url.getRef());
+            assertEquals(spec + ".getUserInfo()",
+                         net.getUserInfo(),
+                         url.getUserInfo());
+        } catch (Throwable t) {
+            fail(spec + " positive test threw " + t);
+        }
+
+    }
+
+
+    /**
+     * Check the details of our URL class against <code>java.net.URL</code>
+     * for a relative URL specification.
+     *
+     * @param abs Absolute URL specification for base reference
+     * @param rel Relative URL specification to resolve
+     */
+    private void positive(String abs, String rel) {
+
+        // Compare results with what java.net.URL returns
+        try {
+            URL urlBase = new URL(abs);
+            java.net.URL netBase = new java.net.URL(abs);
+            URL url = new URL(urlBase, rel);
+            java.net.URL net = new java.net.URL(netBase, rel);
+            assertEquals(rel + " toExternalForm()",
+                         net.toExternalForm(),
+                         url.toExternalForm());
+            assertEquals(rel + ".getAuthority()",
+                         net.getAuthority(),
+                         url.getAuthority());
+            assertEquals(rel + ".getFile()",
+                         net.getFile(),
+                         url.getFile());
+            assertEquals(rel + ".getHost()",
+                         net.getHost(),
+                         url.getHost());
+            assertEquals(rel + ".getPath()",
+                         net.getPath(),
+                         url.getPath());
+            assertEquals(rel + ".getPort()",
+                         net.getPort(),
+                         url.getPort());
+            assertEquals(rel + ".getProtocol()",
+                         net.getProtocol(),
+                         url.getProtocol());
+            assertEquals(rel + ".getQuery()",
+                         net.getQuery(),
+                         url.getQuery());
+            assertEquals(rel + ".getRef()",
+                         net.getRef(),
+                         url.getRef());
+            assertEquals(rel + ".getUserInfo()",
+                         net.getUserInfo(),
+                         url.getUserInfo());
+        } catch (Throwable t) {
+            fail(rel + " positive test threw " + t);
+        }
+
+    }
+
+
+}
diff --git a/container/catalina/src/test/org/apache/naming/resources/BaseDirContextTestCase.java b/container/catalina/src/test/org/apache/naming/resources/BaseDirContextTestCase.java
new file mode 100644
index 0000000..1ab4aca
--- /dev/null
+++ b/container/catalina/src/test/org/apache/naming/resources/BaseDirContextTestCase.java
@@ -0,0 +1,541 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.resources;
+
+import java.util.Date;
+
+import javax.naming.Binding;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * <p>Basic unit tests for the <code>javax.naming.directory.DirContext</code>
+ * implementations.  This class must be subclassed for each individual
+ * <code>DirContext</code> implementation.</p>
+ *
+ * <p><strong>WARNING</strong>:  These tests make certain assumptions that
+ * can generate "false negative" results if they are violated:</p>
+ * <ul>
+ * <li>The pathname of a directory (or WAR file) containing the static
+ *     resources of the <code>/examples</code> web application shipped
+ *     with Tomcat is passed to our test class as a system property
+ *     named <code>doc.base</code>.</li>
+ * <li>The entry names that can be found in the top-level DirContext of
+ *     the static resources are enumerated in the <code>topLevelNames</code>
+ *     variable.</li>
+ * <li>The entry names that can be found in the WEB-INF DirContext of
+ *     the static resources are enumerated in the <code>webInfNames</code>
+ *     variable.</li>
+ * <li>The entry names in either the top-level or WEB-INF DirContext contexts
+ *     that should themselves be <code>DirContext</code> implementations (i.e.
+ *     "subdirectories" in the static resources for this web application)
+ *     are enumerated in the <code>dirContextNames</code> variable.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public abstract class BaseDirContextTestCase extends TestCase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * A directory context implementation to be tested.
+     */
+    protected DirContext context = null;
+
+
+    /**
+     * The pathname of the document base directory for this directory context.
+     */
+    protected String docBase = System.getProperty("doc.base");
+
+
+    /**
+     * Entry names that must be DirContexts.  Names not on this list are
+     * assumed to be Resources.
+     */
+    protected static final String dirContextNames[] =
+    { "classes", "images", "jsp", "lib", "META-INF", "WEB-INF" };
+
+
+    /**
+     * The set of names that should be present in the top-level
+     * directory context.
+     */
+    protected static final String topLevelNames[] =
+    { "index.jsp", "jakarta-banner.gif", "tomcat.gif", "tomcat-power.gif", "META-INF", "WEB-INF" };
+
+    /**
+     * The set of names that should be present in the WEB-INF
+     * directory context.
+     */
+    protected static final String webInfNames[] =
+    { "classes", "jsp", "lib", "web.xml" };
+
+
+    /**
+     * The set of names that should be attributes of WEB-INF.
+     */
+    protected static final String webInfAttrs[] =
+    { "creationdate", "displayname", "getcontentlength", "getlastmodified",
+      "resourcetype" };
+
+
+    /**
+     * The set of names that should be attributes of WEB-INF/web.xml.
+     */
+    protected static final String webXmlAttrs[] =
+    { "creationdate", "displayname", "getcontentlength", "getlastmodified",
+      "resourcetype" };
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this test case.
+     *
+     * @param name Name of the test case
+     */
+    public BaseDirContextTestCase(String name) {
+
+        super(name);
+
+    }
+
+
+    // --------------------------------------------------- Overall Test Methods
+
+
+    /**
+     * Set up instance variables required by this test case.  This method
+     * <strong>MUST</strong> be implemented by a subclass.
+     */
+    public abstract void setUp();
+
+
+    /**
+     * Return the tests included in this test suite.  This method
+     * <strong>MUST</strong> be implemented by a subclass.
+     */
+    // public abstract static Test suite();
+
+
+    /**
+     * Tear down instance variables required by this test case.  This method
+     * <strong>MUST</strong> be implemented by a subclass.
+     */
+    public abstract void tearDown();
+
+
+    // ------------------------------------------------ Individual Test Methods
+
+
+    /**
+     * Test the attributes returned for the <code>WEB-INF</code> entry.
+     */
+    public abstract void testGetAttributesWebInf();
+
+
+    /**
+     * Test the attributes returned for the <code>WEB-INF/web.xml</code>
+     * entry.
+     */
+    public abstract void testGetAttributesWebXml();
+
+
+    /**
+     * We should be able to list the contents of the top-level context itself,
+     * and locate some entries we know are there.
+     */
+    public void testListTopLevel() {
+
+        try {
+            checkList(context.list(""), topLevelNames);
+        } catch (NamingException e) {
+            fail("NamingException: " + e);
+        }
+
+    }
+
+
+    /**
+     * We should be able to list the contents of the WEB-INF entry,
+     * and locate some entries we know are there.
+     */
+    public void testListWebInfDirect() {
+
+        try {
+
+            // Look up the WEB-INF entry
+            Object webInfEntry = context.lookup("WEB-INF");
+            assertNotNull("Found WEB-INF entry", webInfEntry);
+            assertTrue("WEB-INF entry is a DirContext",
+                   webInfEntry instanceof DirContext);
+            DirContext webInfContext = (DirContext) webInfEntry;
+
+            // Check the contents of the WEB-INF context directly
+            checkList(webInfContext.list(""), webInfNames);
+
+        } catch (NamingException e) {
+            fail("NamingException: " + e);
+        }
+
+
+    }
+
+
+    /**
+     * We should be able to list the contents of the WEB-INF entry,
+     * and locate some entries we know are there.
+     */
+    public void testListWebInfIndirect() {
+
+        try {
+            checkList(context.list("WEB-INF"), webInfNames);
+        } catch (NamingException e) {
+            fail("NamingException: " + e);
+        }
+
+    }
+
+
+    /**
+     * We should be able to list the bindings of the top-level context itself,
+     * and locate some entries we know are there.
+     */
+    public void testListBindingsTopLevel() {
+
+        try {
+            checkListBindings(context.listBindings(""), topLevelNames);
+        } catch (NamingException e) {
+            fail("NamingException: " + e);
+        }
+
+    }
+
+
+    /**
+     * We should be able to list the bindings of the WEB-INF entry,
+     * and locate some entries we know are there.
+     */
+    public void testListBindingsWebInfDirect() {
+
+        try {
+
+            // Look up the WEB-INF entry
+            Object webInfEntry = context.lookup("WEB-INF");
+            assertNotNull("Found WEB-INF entry", webInfEntry);
+            assertTrue("WEB-INF entry is a DirContext",
+                   webInfEntry instanceof DirContext);
+            DirContext webInfContext = (DirContext) webInfEntry;
+
+            // Check the bindings of the WEB-INF context directly
+            checkListBindings(webInfContext.listBindings(""), webInfNames);
+
+        } catch (NamingException e) {
+            fail("NamingException: " + e);
+        }
+
+
+    }
+
+
+    /**
+     * We should be able to list the bindings of the WEB-INF entry,
+     * and locate some entries we know are there.
+     */
+    public void testListBindingsWebInfIndirect() {
+
+        try {
+            checkListBindings(context.listBindings("WEB-INF"), webInfNames);
+        } catch (NamingException e) {
+            fail("NamingException: " + e);
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Support Methods
+
+
+    /**
+     * Check the results of a list() call against the specified entry list.
+     *
+     * @param enum The naming enumeration we are checking
+     * @param list The list of entry names we are expecting
+     *
+     * @exception NamingException if a naming exception occurs
+     */
+    protected void checkList(NamingEnumeration ne, String list[])
+        throws NamingException {
+
+        String contextClassName = context.getClass().getName();
+
+        assertNotNull("NamingEnumeration is not null", ne);
+        while (ne.hasMore()) {
+
+            Object next = ne.next();
+            assertTrue("list() returns NameClassPair instances",
+                   next instanceof NameClassPair);
+            NameClassPair ncp = (NameClassPair) next;
+
+            assertTrue("Name '" + ncp.getName() + "' is expected",
+                   isListed(ncp.getName(), list));
+
+            if (isDirContext(ncp.getName())) {
+                assertTrue("Class '" + ncp.getClassName() + "' is '" +
+                       contextClassName + "'",
+                       contextClassName.equals(ncp.getClassName()));
+            }
+
+            assertTrue("Relative is 'true'", ncp.isRelative());
+
+        }
+
+    }
+
+
+    /**
+     * Check the results of a listBindings() call against the
+     * specified entry list.
+     *
+     * @param enum The naming enumeration we are checking
+     * @param list The list of entry names we are expecting
+     *
+     * @exception NamingException if a naming exception occurs
+     */
+    protected void checkListBindings(NamingEnumeration ne, String list[])
+        throws NamingException {
+
+        String contextClassName = context.getClass().getName();
+
+        assertNotNull("NamingEnumeration is not null", ne);
+        while (ne.hasMore()) {
+
+            Object next = ne.next();
+            assertTrue("listBindings() returns Binding instances",
+                   next instanceof Binding);
+            Binding b = (Binding) next;
+
+            assertTrue("Name '" + b.getName() + "' is expected",
+                   isListed(b.getName(), list));
+
+            if (isDirContext(b.getName())) {
+                assertTrue("Class '" + b.getClassName() + "' is '" +
+                       contextClassName + "'",
+                       contextClassName.equals(b.getClassName()));
+            }
+
+            assertTrue("Relative is 'true'", b.isRelative());
+
+            Object object = b.getObject();
+            assertNotNull("Name '" + b.getName() + "' has a non-null object",
+                          object);
+            if(isDirContext(b.getName())) {
+            	assertTrue("Entry '" + b.getName() + "' is a DirContext",
+                        object instanceof DirContext);          	
+            }  else {
+            	assertTrue("Entry '" + b.getName() + "' is a Resource",
+                       object instanceof Resource); 
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Check the attributes associated with a WEB-INF entry.
+     *
+     * @param attrs The attributes for this entry
+     * @param creationDate The creation date for this entry
+     * @param contentLength The content length for this entry
+     * @param displayName The display name for this entry
+     * @param lastModifiedDate The last modified date for this entry
+     */
+    protected void checkWebInfAttributes(Attributes attrs,
+                                         Date creationDate,
+                                         long contentLength,
+                                         String displayName,
+                                         Date lastModifiedDate)
+        throws NamingException {
+
+        assertNotNull("getAttributes() returned non-null", attrs);
+
+        NamingEnumeration ne = attrs.getAll();
+        assertNotNull("getAll() returned non-null", ne);
+        while (ne.hasMore()) {
+
+            Object next = ne.next();
+            assertTrue("getAll() returns Attribute instances",
+                   next instanceof Attribute);
+            Attribute attr = (Attribute) next;
+            String name = attr.getID();
+            int index = getIndex(name, webInfAttrs);
+            assertTrue("WEB-INF attribute '" + name + "' is expected",
+                   index >= 0);
+            Object value = attr.get();
+            assertNotNull("get() returned non-null", value);
+
+            if (name.equals("creationdate")) {
+                assertTrue("Creation date is a date",
+                       value instanceof Date);
+                assertTrue("Creation date equals " + creationDate,
+                       creationDate.equals((Date) value));
+            } else if (name.equals("displayname")) {
+                assertTrue("Display name is a string",
+                       value instanceof String);
+                assertTrue("Display name equals " + displayName,
+                       displayName.equals((String) value));
+            } else if (name.equals("getcontentlength")) {
+                assertTrue("Content length is a long",
+                       value instanceof Long);
+                assertTrue("Content length equals " + contentLength,
+                       contentLength == ((Long) value).longValue());
+            } else if (name.equals("getlastmodified")) {
+                assertTrue("Last modified date is a date",
+                       value instanceof Date);
+                assertTrue("Last modified date is " + lastModifiedDate,
+                       lastModifiedDate.equals((Date) value));
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Check the attributes associated with a WEB-INF/web.xml entry.
+     *
+     * @param attrs The attributes for this entry
+     * @param creationDate The creation date for this entry
+     * @param contentLength The content length for this entry
+     * @param displayName The display name for this entry
+     * @param lastModifiedDate The last modified date for this entry
+     */
+    protected void checkWebXmlAttributes(Attributes attrs,
+                                         Date creationDate,
+                                         long contentLength,
+                                         String displayName,
+                                         Date lastModifiedDate)
+        throws NamingException {
+
+        assertNotNull("getAttributes() returned non-null", attrs);
+
+        NamingEnumeration ne = attrs.getAll();
+        assertNotNull("getAll() returned non-null", ne);
+        while (ne.hasMore()) {
+
+            Object next = ne.next();
+            assertTrue("getAll() returns Attribute instances",
+                   next instanceof Attribute);
+            Attribute attr = (Attribute) next;
+            String name = attr.getID();
+            int index = getIndex(name, webXmlAttrs);
+            assertTrue("WEB-INF/web.xml attribute '" + name + "' is expected",
+                   index >= 0);
+            Object value = attr.get();
+            assertNotNull("get() returned non-null", value);
+
+            if (name.equals("creationdate")) {
+                assertTrue("Creation date is a date",
+                       value instanceof Date);
+                assertTrue("Creation date equals " + creationDate,
+                       creationDate.equals((Date) value));
+            } else if (name.equals("displayname")) {
+                assertTrue("Display name is a string",
+                       value instanceof String);
+                assertTrue("Display name equals " + displayName,
+                       displayName.equals((String) value));
+            } else if (name.equals("getcontentlength")) {
+                assertTrue("Content length is a long",
+                       value instanceof Long);
+                assertTrue("Content length equals " + contentLength,
+                       contentLength == ((Long) value).longValue());
+            } else if (name.equals("getlastmodified")) {
+                assertTrue("Last modified date is a date",
+                       value instanceof Date);
+                assertTrue("Last modified date is " + lastModifiedDate,
+                       lastModifiedDate.equals((Date) value));
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Return the index of the specified name in the specified list, or
+     * -1 if the name is not present.
+     *
+     * @param name Name to be looked up
+     * @param list List of names to be checked
+     */
+    protected int getIndex(String name, String list[]) {
+
+        for (int i = 0; i < list.length; i++) {
+            if (name.equals(list[i]))
+                return (i);
+        }
+        return (-1);
+
+    }
+
+
+    /**
+     * Is an entry of the specified name supposed to be a DirContext?
+     *
+     * @param name Name to be checked
+     */
+    protected boolean isDirContext(String name) {
+
+        return (isListed(name, dirContextNames));
+
+    }
+
+
+    /**
+     * Returns <code>true</code> if the specified name is found in the
+     * specified list.
+     *
+     * @param name Name to be looked up
+     * @param list List to be looked up into
+     */
+    protected boolean isListed(String name, String list[]) {
+
+        return (getIndex(name, list) >= 0);
+
+    }
+
+
+}
diff --git a/container/catalina/src/test/org/apache/naming/resources/FileDirContextTestCase.java b/container/catalina/src/test/org/apache/naming/resources/FileDirContextTestCase.java
new file mode 100644
index 0000000..6dfeec8
--- /dev/null
+++ b/container/catalina/src/test/org/apache/naming/resources/FileDirContextTestCase.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.resources;
+
+import java.io.File;
+
+import java.util.Date;
+
+import javax.naming.NamingException;
+
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Unit tests for <code>org.apache.naming.resources.FileDirContext</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class FileDirContextTestCase extends BaseDirContextTestCase {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this test case.
+     *
+     * @param name Name of the test case
+     */
+    public FileDirContextTestCase(String name) {
+
+        super(name);
+
+    }
+
+
+    // --------------------------------------------------- Overall Test Methods
+
+
+    /**
+     * Set up instance variables required by this test case.  This method
+     * <strong>MUST</strong> be implemented by a subclass.
+     */
+    public void setUp() {
+
+        context = new FileDirContext();
+        ((FileDirContext) context).setDocBase(docBase);
+
+    }
+
+
+    /**
+     * Return the tests included in this test suite.  This method
+     * <strong>MUST</strong> be implemented by a subclass.
+     */
+    public static Test suite() {
+
+        return (new TestSuite(FileDirContextTestCase.class));
+
+    }
+
+
+    /**
+     * Tear down instance variables required by this test case.  This method
+     * <strong>MUST</strong> be implemented by a subclass.
+     */
+    public void tearDown() {
+
+        context = null;
+
+    }
+
+
+    // ------------------------------------------------ Individual Test Methods
+
+
+    /**
+     * Test the attributes returned for the <code>WEB-INF</code> entry.
+     */
+    public void testGetAttributesWebInf() {
+
+        try {
+
+            // Identify a local file object for WEB-INF
+            File docBaseFile = new File(docBase);
+            File webInfFile = new File(docBaseFile, "WEB-INF");
+
+            // Look up the attributes for the WEB-INF entry
+            Attributes attributes = context.getAttributes("WEB-INF");
+
+            // Enumerate and check the attributes for this entry
+            checkWebInfAttributes(attributes,
+                                  new Date(webInfFile.lastModified()),
+                                  webInfFile.length(),
+                                  "WEB-INF",
+                                  new Date(webInfFile.lastModified()));
+
+        } catch (NamingException e) {
+
+            fail("NamingException: " + e);
+
+        }
+
+    }
+
+
+    /**
+     * Test the attributes returned for the <code>WEB-INF/web.xml</code>
+     * entry.
+     */
+    public void testGetAttributesWebXml() {
+
+        try {
+
+            // Identify a local file object for WEB-INF/web.xml
+            File docBaseFile = new File(docBase);
+            File webInfFile = new File(docBaseFile, "WEB-INF");
+            File webXmlFile = new File(webInfFile, "web.xml");
+
+            // Look up the attributes for the WEB-INF entry
+            Attributes attributes = context.getAttributes("WEB-INF/web.xml");
+
+            // Enumerate and check the attributes for this entry
+            checkWebXmlAttributes(attributes,
+                                  new Date(webXmlFile.lastModified()),
+                                  webXmlFile.length(),
+                                  "web.xml",
+                                  new Date(webXmlFile.lastModified()));
+
+        } catch (NamingException e) {
+
+            fail("NamingException:  " + e);
+
+        }
+
+    }
+
+
+}
+
+
+
diff --git a/container/catalina/src/test/org/apache/naming/resources/WARDirContextTestCase.java b/container/catalina/src/test/org/apache/naming/resources/WARDirContextTestCase.java
new file mode 100644
index 0000000..5790806
--- /dev/null
+++ b/container/catalina/src/test/org/apache/naming/resources/WARDirContextTestCase.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming.resources;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Date;
+
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import javax.naming.NamingException;
+
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Unit tests for <code>org.apache.naming.resources.WARDirContext</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class WARDirContextTestCase extends BaseDirContextTestCase {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this test case.
+     *
+     * @param name Name of the test case
+     */
+    public WARDirContextTestCase(String name) {
+
+        super(name);
+
+    }
+
+
+    // --------------------------------------------------- Overall Test Methods
+
+
+    /**
+     * Set up instance variables required by this test case.  This method
+     * <strong>MUST</strong> be implemented by a subclass.
+     */
+    public void setUp() {
+
+        context = new WARDirContext();
+        ((WARDirContext) context).setDocBase(docBase);
+
+    }
+
+
+    /**
+     * Return the tests included in this test suite.  This method
+     * <strong>MUST</strong> be implemented by a subclass.
+     */
+    public static Test suite() {
+
+        return (new TestSuite(WARDirContextTestCase.class));
+
+    }
+
+
+    /**
+     * Tear down instance variables required by this test case.  This method
+     * <strong>MUST</strong> be implemented by a subclass.
+     */
+    public void tearDown() {
+
+        context = null;
+
+    }
+
+
+    // ------------------------------------------------ Individual Test Methods
+
+
+    /**
+     * Test the attributes returned for the <code>WEB-INF</code> entry.
+     */
+    public void testGetAttributesWebInf() {
+
+        try {
+
+            // Look up the attributes of this WAR file entry
+            JarFile jarFile = new JarFile(docBase);
+            assertNotNull("Created JarFile for " + docBase, jarFile);
+            JarEntry jarEntry =
+                (JarEntry) jarFile.getEntry("WEB-INF");
+            assertNotNull("Created JarEntry for WEB-INF", jarEntry);
+
+            // Look up the attributes for the WEB-INF entry
+            Attributes attributes = context.getAttributes("WEB-INF");
+
+            // Enumerate and check the attributes for this entry
+            checkWebInfAttributes(attributes,
+                                  new Date(jarEntry.getTime()),
+                                  jarEntry.getSize(),
+                                  "WEB-INF",
+                                  new Date(jarEntry.getTime()));
+
+        } catch (IOException e) {
+
+            fail("IOException: " + e);
+
+        } catch (NamingException e) {
+
+            fail("NamingException: " + e);
+
+        }
+
+    }
+
+
+    /**
+     * Test the attributes returned for the <code>WEB-INF/web.xml</code>
+     * entry.
+     */
+    public void testGetAttributesWebXml() {
+
+        try {
+
+            // Look up the attributes of this WAR file entry
+            JarFile jarFile = new JarFile(docBase);
+            assertNotNull("Created JarFile for " + docBase, jarFile);
+            JarEntry jarEntry =
+                (JarEntry) jarFile.getEntry("WEB-INF/web.xml");
+            assertNotNull("Created JarEntry for WEB-INF/web.xml", jarEntry);
+
+            // Look up the attributes for the WEB-INF/web.xml entry
+            Attributes attributes = context.getAttributes("WEB-INF/web.xml");
+
+            // Enumerate and check the attributes for this entry
+            checkWebXmlAttributes(attributes,
+                                  new Date(jarEntry.getTime()),
+                                  jarEntry.getSize(),
+                                  "web.xml",
+                                  new Date(jarEntry.getTime()));
+
+        } catch (IOException e) {
+
+            fail("IOException: " + e);
+
+        } catch (NamingException e) {
+
+            fail("NamingException:  " + e);
+
+        }
+
+    }
+
+
+}
diff --git a/container/etc/mx4j.license b/container/etc/mx4j.license
new file mode 100644
index 0000000..281ebef
--- /dev/null
+++ b/container/etc/mx4j.license
@@ -0,0 +1,51 @@
+/* ====================================================================
+ * The MX4J License, Version 1.0
+ *
+ * Copyright (c) 2001 MX4J.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        MX4J project (http://mx4j.sourceforge.net)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "MX4J" and "mx4j" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact biorn_steedom@users.sourceforge.net
+ *
+ * 5. Products derived from this software may not be called "MX4J",
+ *    nor may "MX4J" appear in their name, without prior written
+ *    permission of Simone Bordet.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL CARLOS QUIROZ OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of MX4J.  For more information on
+ * MX4J, please see
+ * <http://mx4j.sourceforge.net>.
+ */
diff --git a/container/modules/build.xml b/container/modules/build.xml
new file mode 100644
index 0000000..78c673e
--- /dev/null
+++ b/container/modules/build.xml
@@ -0,0 +1,75 @@
+<project name="Modules" default="dist" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <!--property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="${user.home}/build.properties"/-->
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <available property="jdk1.4.present" classname="java.nio.Buffer" />
+  </target>
+
+
+  <!-- =================== BUILD: Compile Subprojects ===================== -->
+  <!-- Add a new target for each module subproject -->
+
+  <target name="cluster" if="jdk1.4.present">
+    <ant dir="${basedir}/cluster" target="dist"/>
+  </target>
+
+  <target name="config" >
+    <ant dir="${basedir}/storeconfig" target="dist"/>
+  </target>
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <!-- Update the depends list for each subproject -->
+  <target name="build" depends="build-prepare,cluster,config"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,dist"/>
+
+
+  <!-- ================= DEPLOY: Deploy Webapps Projects ================== -->
+  <target name="deploy" depends="dist"
+   description="Build and deploy Modules component">
+
+  </target>
+
+
+  <!-- ================= DIST: Create Distribution Files ================== -->
+  <target name="dist" depends="build"/>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+  <!-- ===================== TEST: Compile Unit Tests ===================== -->
+  <target name="build-tests" depends="dist" if="junit.present">
+  </target>
+
+
+  <!-- ===================== TEST: Execute Unit Tests ===================== -->
+  <target name="test" if="junit.present"
+   description="Run all unit test cases">
+  </target>
+
+
+</project>
diff --git a/container/modules/cluster/build.xml b/container/modules/cluster/build.xml
new file mode 100644
index 0000000..758ec8e
--- /dev/null
+++ b/container/modules/cluster/build.xml
@@ -0,0 +1,154 @@
+<project name="Cluster" default="dist" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <property file="../../../build.properties" />
+  <property file="../../../jakarta-tomcat-5/build.properties.default" />
+
+  <!-- Build Defaults -->
+  <property name="catalina.home"  location="../.."/>
+  <property name="catalina.build" location="../../../jakarta-tomcat-5/build"/>
+  <property name="cluster.build"  value="${catalina.home}/modules/cluster/build"/>
+  <property name="cluster.dist"   value="${catalina.home}/modules/cluster/dist"/>
+
+    <!-- Construct Catalina classpath -->
+  <path id="cluster.classpath">
+    <pathelement location="${catalina.build}/server/lib/catalina.jar"/>
+    <pathelement location="${catalina.build}/server/lib/tomcat-util.jar"/>
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${catalina.build}/common/lib/servlet-api.jar"/>
+  </path>
+
+    <!-- Source path -->
+  <path id="javadoc.sourcepath">
+    <pathelement location="src/share"/>
+  </path>
+
+
+  <!-- =================== BUILD: Set compile flags ======================= -->
+  <target name="flags">
+    <!-- JDK flags -->
+    <available property="jdk.1.2.present" classname="java.util.HashMap" />
+    <available property="jdk.1.3.present" 
+     classname="java.lang.reflect.Proxy" />
+    <available property="jdk.1.4.present" classname="java.nio.Buffer" />
+  </target>
+
+
+  <!-- =================== BUILD: Set compile flags ======================= -->
+  <target name="flags.display" depends="flags" unless="flags.hide">
+
+    <echo message="--- Build environment for Catalina ---" />
+
+    <echo message="If ${property_name} is displayed, then the property is not set)" />
+
+    <echo message="--- Build options ---" />
+    <echo message="full.dist=${full.dist}" />
+    <echo message="build.sysclasspath=${build.sysclasspath}" />
+    <echo message="compile.debug=${compile.debug}" />
+    <echo message="compile.deprecation=${compile.deprecation}" />
+    <echo message="compile.optimize=${compile.optimize}" />
+
+    <echo message="--- Ant Flags ---" />
+    <echo message="&lt;style&gt; task available (required)=${style.available}" />
+
+    <echo message="--- JDK ---" />
+    <echo message="jdk.1.2.present=${jdk.1.2.present}" />
+    <echo message="jdk.1.3.present=${jdk.1.3.present}" />
+    <echo message="jdk.1.4.present=${jdk.1.4.present}" />
+
+  </target>
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${catalina.build}"/>
+    <mkdir dir="${catalina.build}/classes"/>
+    <mkdir dir="${cluster.dist}"/>
+  </target>
+
+
+
+
+  <!-- ================ BUILD: Compile Catalina Components ================ -->
+  
+  <target name="build-catalina-cluster" depends="build-prepare">
+    <!-- Compile internal server components -->
+    <javac srcdir="${basedir}/src/share" destdir="${catalina.build}/classes"
+           debug="${compile.debug}" deprecation="${compile.deprecation}"
+           optimize="${compile.optimize}"
+           excludes="**/CVS/**"  	   
+    	>
+        <classpath refid="cluster.classpath" />
+    </javac>
+    <copy file="${basedir}/src/share/org/apache/catalina/cluster/LocalStrings.properties"
+    	  tofile="${catalina.build}/classes/org/apache/catalina/cluster/LocalStrings.properties"/>
+    <copy file="${basedir}/src/share/org/apache/catalina/cluster/session/LocalStrings.properties"
+    	  tofile="${catalina.build}/classes/org/apache/catalina/cluster/session/LocalStrings.properties"/>
+    <copy file="${basedir}/src/share/org/apache/catalina/cluster/tcp/LocalStrings.properties"
+    	  tofile="${catalina.build}/classes/org/apache/catalina/cluster/tcp/LocalStrings.properties"/>
+    <copy file="${basedir}/src/share/org/apache/catalina/cluster/tcp/DataSenders.properties"
+    	  tofile="${catalina.build}/classes/org/apache/catalina/cluster/tcp/DataSenders.properties"/>
+    <copy file="${basedir}/src/share/org/apache/catalina/cluster/session/mbeans-descriptors.xml"
+    	  tofile="${catalina.build}/classes/org/apache/catalina/cluster/session/mbeans-descriptors.xml"/>
+    <copy file="${basedir}/src/share/org/apache/catalina/cluster/tcp/mbeans-descriptors.xml"
+    	  tofile="${catalina.build}/classes/org/apache/catalina/cluster/tcp/mbeans-descriptors.xml"/>
+    <copy file="${basedir}/src/share/org/apache/catalina/cluster/mcast/LocalStrings.properties"
+    	  tofile="${catalina.build}/classes/org/apache/catalina/cluster/mcast/LocalStrings.properties"/>
+    <copy file="${basedir}/src/share/org/apache/catalina/cluster/mcast/mbeans-descriptors.xml"
+     	  tofile="${catalina.build}/classes/org/apache/catalina/cluster/mcast/mbeans-descriptors.xml"/>
+   </target>
+
+
+  <!-- ================ BUILD: Create Catalina Javadocs =================== -->
+  <target name="javadoc">
+    <delete dir="${catalina.build}/javadoc"/>
+    <mkdir dir="${catalina.build}/javadoc"/>
+    <javadoc packagenames="org.apache.catalina.*,org.apache.naming.*"
+      classpathref="catalina.classpath"
+      sourcepathref="javadoc.sourcepath"
+      destdir="${catalina.build}/javadoc"
+      author="true"
+      version="true"
+      windowtitle="Catalina Internal API Documentation"
+      doctitle="Catalina API"
+      bottom="Copyright &#169; 2000-2002 Apache Software Foundation.  All Rights Reserved."
+    />
+  </target>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${catalina.build}"/>
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+
+
+
+
+  <!-- ================ DIST: Create Distribution ========================= -->
+  <target name="dist" depends="build-catalina-cluster">
+    
+    <jar destfile="${cluster.dist}/catalina-cluster.jar"
+         basedir="${catalina.build}/classes">
+       <include name="org/apache/catalina/cluster/**" />
+       <exclude name="**/package.html" />
+       <exclude name="**/LocalStrings_*" />
+    </jar>
+  </target>
+
+  <target name="copy" depends="dist" >
+     <copy file="${cluster.dist}/catalina-cluster.jar" todir="${catalina.build}/server/lib" />
+  </target>
+  
+  <!-- ======================== DIST: Clean Directory ===================== -->
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+
+
+</project>
diff --git a/container/modules/cluster/etc/cluster-server.xml b/container/modules/cluster/etc/cluster-server.xml
new file mode 100644
index 0000000..4bb5ed0
--- /dev/null
+++ b/container/modules/cluster/etc/cluster-server.xml
@@ -0,0 +1,368 @@
+<!-- Example Server Configuration File  - Search for Cluster-->
+<!-- Note that component elements are nested corresponding to their
+     parent-child relationships with each other -->
+
+<!-- A "Server" is a singleton element that represents the entire JVM,
+     which may contain one or more "Service" instances.  The Server
+     listens for a shutdown command on the indicated port.
+
+     Note:  A "Server" is not itself a "Container", so you may not
+     define subcomponents such as "Valves" or "Loggers" at this level.
+ -->
+
+<Server port="8005" shutdown="SHUTDOWN" debug="0">
+
+
+  <!-- Comment these entries out to disable JMX MBeans support -->
+  <!-- You may also configure custom components (e.g. Valves/Realms) by 
+       including your own mbean-descriptor file(s), and setting the 
+       "descriptors" attribute to point to a ';' seperated list of paths
+       (in the ClassLoader sense) of files to add to the default list.
+       e.g. descriptors="/com/myfirm/mypackage/mbean-descriptor.xml"
+  -->
+  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener"
+            debug="0"/>
+  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"
+            debug="0"/>
+  
+  <!-- Global JNDI resources -->
+  <GlobalNamingResources>
+
+    <!-- Test entry for demonstration purposes -->
+    <Environment name="simpleValue" type="java.lang.Integer" value="30"/>
+
+    <!-- Editable user database that can also be used by
+         UserDatabaseRealm to authenticate users -->
+    <Resource name="UserDatabase" auth="Container"
+              type="org.apache.catalina.UserDatabase"
+       description="User database that can be updated and saved">
+    </Resource>
+    <ResourceParams name="UserDatabase">
+      <parameter>
+        <name>factory</name>
+        <value>org.apache.catalina.users.MemoryUserDatabaseFactory</value>
+      </parameter>
+      <parameter>
+        <name>pathname</name>
+        <value>conf/tomcat-users.xml</value>
+      </parameter>
+    </ResourceParams>
+
+  </GlobalNamingResources>
+
+  <!-- A "Service" is a collection of one or more "Connectors" that share
+       a single "Container" (and therefore the web applications visible
+       within that Container).  Normally, that Container is an "Engine",
+       but this is not required.
+
+       Note:  A "Service" is not itself a "Container", so you may not
+       define subcomponents such as "Valves" or "Loggers" at this level.
+   -->
+
+  <!-- Define the Tomcat Stand-Alone Service -->
+  <Service name="Tomcat-Standalone">
+
+    <!-- A "Connector" represents an endpoint by which requests are received
+         and responses are returned.  Each Connector passes requests on to the
+         associated "Container" (normally an Engine) for processing.
+
+         By default, a non-SSL HTTP/1.1 Connector is established on port 8080.
+         You can also enable an SSL HTTP/1.1 Connector on port 8443 by
+         following the instructions below and uncommenting the second Connector
+         entry.  SSL support requires the following steps (see the SSL Config
+         HOWTO in the Tomcat 5 documentation bundle for more detailed
+         instructions):
+         * If your JDK version 1.3 or prior, download and install JSSE 1.0.2 or
+           later, and put the JAR files into "$JAVA_HOME/jre/lib/ext".
+         * Execute:
+             %JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA (Windows)
+             $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA  (Unix)
+           with a password value of "changeit" for both the certificate and
+           the keystore itself.
+
+         By default, DNS lookups are enabled when a web application calls
+         request.getRemoteHost().  This can have an adverse impact on
+         performance, so you can disable it by setting the
+         "enableLookups" attribute to "false".  When DNS lookups are disabled,
+         request.getRemoteHost() will return the String version of the
+         IP address of the remote client.
+    -->
+
+    <!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8080 -->
+    <Connector className="org.apache.coyote.tomcat5.CoyoteConnector"
+               port="8080" minProcessors="5" maxProcessors="100"
+               enableLookups="true" redirectPort="8443" acceptCount="100"
+               debug="0" connectionTimeout="20000" 
+               disableUploadTimeout="true" />
+    <!-- Note : To disable connection timeouts, set connectionTimeout value
+     to -1 -->
+
+    <!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 -->
+    <!--
+    <Connector className="org.apache.coyote.tomcat5.CoyoteConnector"
+               port="8443" minProcessors="5" maxProcessors="75"
+               enableLookups="true" disableUploadTimeout="true"
+           acceptCount="100" debug="0" scheme="https" secure="true">
+      <Factory className="org.apache.coyote.tomcat5.CoyoteServerSocketFactory"
+               clientAuth="false" protocol="TLS" />
+    </Connector>
+    -->
+
+    <!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 -->
+    <Connector className="org.apache.coyote.tomcat5.CoyoteConnector"
+               port="8009" minProcessors="5" maxProcessors="75"
+               enableLookups="true" redirectPort="8443"
+               acceptCount="10" debug="0"
+               protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"/>
+
+    <!-- Define a Proxied HTTP/1.1 Connector on port 8082 -->
+    <!-- See proxy documentation for more information about using this. -->
+    <!--
+    <Connector className="org.apache.coyote.tomcat5.CoyoteConnector"
+               port="8082" minProcessors="5" maxProcessors="75"
+               enableLookups="true"
+               acceptCount="100" debug="0" connectionTimeout="20000"
+               proxyPort="80" disableUploadTimeout="true" />
+    -->
+
+    <!-- An Engine represents the entry point (within Catalina) that processes
+         every request.  The Engine implementation for Tomcat stand alone
+         analyzes the HTTP headers included with the request, and passes them
+         on to the appropriate Host (virtual host). -->
+
+    <!-- You should set jvmRoute to support load-balancing via JK/JK2 ie :
+    <Engine name="Standalone" defaultHost="localhost" debug="0" jmvRoute="jvm1">         
+    --> 
+         
+    <!-- Define the top level container in our container hierarchy -->
+    <Engine name="Standalone" defaultHost="localhost" debug="0">
+
+      <!-- The request dumper valve dumps useful debugging information about
+           the request headers and cookies that were received, and the response
+           headers and cookies that were sent, for all requests received by
+           this instance of Tomcat.  If you care only about requests to a
+           particular virtual host, or a particular application, nest this
+           element inside the corresponding <Host> or <Context> entry instead.
+
+           For a similar mechanism that is portable to all Servlet 2.4
+           containers, check out the "RequestDumperFilter" Filter in the
+           example application (the source for this filter may be found in
+           "$CATALINA_HOME/webapps/examples/WEB-INF/classes/filters").
+
+           Request dumping is disabled by default.  Uncomment the following
+           element to enable it. -->
+      <!--
+      <Valve className="org.apache.catalina.valves.RequestDumperValve"/>
+      -->
+
+      <!-- Global logger unless overridden at lower levels -->
+      <Logger className="org.apache.catalina.logger.FileLogger"
+              prefix="catalina_log." suffix=".txt"
+              timestamp="true"/>
+
+      <!-- Because this Realm is here, an instance will be shared globally -->
+
+      <!-- This Realm uses the UserDatabase configured in the global JNDI
+           resources under the key "UserDatabase".  Any edits
+           that are performed against this UserDatabase are immediately
+           available for use by the Realm.  -->
+      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
+                 debug="0" resourceName="UserDatabase"/>
+
+      <!-- Comment out the old realm but leave here for now in case we
+           need to go back quickly -->
+      <!--
+      <Realm className="org.apache.catalina.realm.MemoryRealm" />
+      -->
+
+      <!-- Replace the above Realm with one of the following to get a Realm
+           stored in a database and accessed via JDBC -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
+             driverName="org.gjt.mm.mysql.Driver"
+          connectionURL="jdbc:mysql://localhost/authority"
+         connectionName="test" connectionPassword="test"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
+             driverName="oracle.jdbc.driver.OracleDriver"
+          connectionURL="jdbc:oracle:thin:@ntserver:1521:ORCL"
+         connectionName="scott" connectionPassword="tiger"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
+             driverName="sun.jdbc.odbc.JdbcOdbcDriver"
+          connectionURL="jdbc:odbc:CATALINA"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!-- Define the default virtual host -->
+      <Host name="localhost" debug="0" appBase="webapps"
+       unpackWARs="true" autoDeploy="true">
+       
+        
+        <!-- Defines a cluster for this node,
+             By defining this element, means that every manager will be changed.
+             So when running a cluster, only make sure that you have webapps in there
+             that need to be clustered and remove the other ones.
+             A cluster has the following parameters:
+             
+             className = the fully qualified name of the cluster class
+             
+             name = a descriptive name for your cluster, can be anything
+             
+             debug = the debug level, higher means more output
+             
+             mcastAddr = the multicast address, has to be the same for all the nodes
+             
+             mcastPort = the multicast port, has to be the same for all the nodes
+             
+             mcastFrequency = the number of milliseconds in between sending a "I'm alive" heartbeat
+             
+             mcastDropTime = the number a milliseconds before a node is considered "dead" if no heartbeat is received
+             
+             tcpThreadCount = the number of threads to handle incoming replication requests, optimal would be the same amount of threads as nodes 
+
+             tcpListenAddress = the listen address (bind address) for TCP cluster request on this host, 
+                                in case of multiple ethernet cards.
+                                auto means that address becomes
+                                InetAddress.getLocalHost().getHostAddress()
+
+             tcpListenPort = the tcp listen port
+             
+             tcpSelectorTimeout = the timeout (ms) for the Selector.select() method in case the OS
+                                  has a wakup bug in java.nio. Set to 0 for no timeout
+
+             printToScreen = true means that managers will also print to std.out
+
+             expireSessionsOnShutdown = true means that 
+
+             useDirtyFlag = true means that we only replicate a session after setAttribute,removeAttribute has been called.
+                            false means to replicate the session after each request.
+                            false means that replication would work for the following piece of code:
+                            <%
+                            HashMap map = new HashMap();
+                            session.setAttribute("map",map);
+                            map.put("key","value");
+                            %>
+        -->             
+             
+            
+        <Cluster  className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
+                  name="FilipsCluster"
+                  debug="10"
+                  serviceclass="org.apache.catalina.cluster.mcast.McastService"
+                  mcastAddr="228.0.0.4"
+                  mcastPort="45564"
+                  mcastFrequency="500"
+                  mcastDropTime="3000"
+                  tcpThreadCount="2"
+                  tcpListenAddress="auto"
+                  tcpListenPort="4001"
+                  tcpSelectorTimeout="100"
+                  printToScreen="false"
+                  expireSessionsOnShutdown="false"
+                  useDirtyFlag="true"
+        />
+        
+        <!--
+            When configuring for clustering, you also add in a valve to catch all the requests
+            coming in, at the end of the request, the session may or may not be replicated.
+            A session is replicated if and only if all the conditions are met:
+            1. useDirtyFlag is true or setAttribute or removeAttribute has been called AND
+            2. a session exists (has been created)
+            3. the request is not trapped by the "filter" attribute
+            
+            The filter attribute is to filter out requests that could not modify the session,
+            hence we don't replicate the session after the end of this request.
+            The filter is negative, ie, anything you put in the filter, you mean to filter out,
+            ie, no replication will be done on requests that match one of the filters.
+            The filter attribute is delimited by ;, so you can't escape out ; even if you wanted to.
+            
+            filter=".*\.gif;.*\.js;" means that we will not replicate the session after requests with the URI
+            ending with .gif and .js are intercepted.
+        -->
+            
+        <Valve className="org.apache.catalina.cluster.tcp.ReplicationValve"
+               filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"/>
+
+      <!-- Add the following attributes if you want to turn XML validation
+           on. Remember to comment the Host element above. 
+           
+           Note: XML Schema validationn will works with Xerces 2.0.1 or 
+           Xerces 2.1. Xerces 2.0.2 and Xerces 2.2 have bugs that prevent 
+           their use with Tomcat-->
+      <!--
+      <Host name="localhost" debug="0" appBase="webapps"
+       unpackWARs="true" autoDeploy="true"
+       xmlValidation="true" xmlNamespaceAware="true">
+      -->
+
+        <!-- Uncomment this to cluster this host using JavaGroups. The
+             protocol attribute can be used to configure the JavaGroups
+             network stack (the defaults are used if it's not specified). -->
+        <!--
+        <Cluster className="org.apache.catalina.cluster.JGCluster"/>
+        -->
+
+        <!-- Normally, users must authenticate themselves to each web app
+             individually.  Uncomment the following entry if you would like
+             a user to be authenticated the first time they encounter a
+             resource protected by a security constraint, and then have that
+             user identity maintained across *all* web applications contained
+             in this virtual host. -->
+        <!--
+        <Valve className="org.apache.catalina.authenticator.SingleSignOn"
+                   debug="0"/>
+        -->
+        
+        <!-- Access log processes all requests for this virtual host.  By
+             default, log files are created in the "logs" directory relative to
+             $CATALINA_HOME.  If you wish, you can specify a different
+             directory with the "directory" attribute.  Specify either a relative
+             (to $CATALINA_HOME) or absolute path to the desired directory.
+        -->
+        <!--
+        <Valve className="org.apache.catalina.valves.AccessLogValve"
+                 directory="logs"  prefix="localhost_access_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/>
+        -->
+
+        <!-- Logger shared by all Contexts related to this virtual host.  By
+             default (when using FileLogger), log files are created in the "logs"
+             directory relative to $CATALINA_HOME.  If you wish, you can specify
+             a different directory with the "directory" attribute.  Specify either a
+             relative (to $CATALINA_HOME) or absolute path to the desired
+             directory.-->
+        <Logger className="org.apache.catalina.logger.FileLogger"
+                 directory="logs"  prefix="localhost_log." suffix=".txt"
+            timestamp="true"/>
+
+        <!-- Define properties for each web application.  This is only needed
+             if you want to set non-default properties, or have web application
+             document roots in places other than the virtual host's appBase
+             directory.  -->
+
+        <!-- Tomcat Root Context -->
+        <!--
+        <Context path="/reptest" docBase="reptest" debug="0">
+            
+        </Context>
+        -->
+        
+
+      </Host>
+
+    </Engine>
+
+  </Service>
+
+</Server>
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/CatalinaCluster.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/CatalinaCluster.java
new file mode 100644
index 0000000..2cc698c
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/CatalinaCluster.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster;
+
+import java.util.Map;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Valve;
+import org.apache.commons.logging.Log;
+
+/**
+ * A <b>CatalinaCluster</b> interface allows to plug in and out the 
+ * different cluster implementations
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+public interface CatalinaCluster extends Cluster {
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Descriptive information about this component implementation.
+     */
+    public String info = "CatalinaCluster/2.0";
+    
+    /**
+     * Start the cluster, the owning container will invoke this
+     * @throws Exception - if failure to start cluster
+     */
+    public void start() throws Exception;
+    
+    /**
+     * Stops the cluster, the owning container will invoke this
+     * @throws LifecycleException
+     */
+    public void stop() throws LifecycleException;
+    
+    /**
+     * Returns the associates logger with this cluster.
+     *
+     * @return Log
+     */
+    public Log getLogger();
+    
+    /**
+     * receive a message to all the members in the cluster.
+     *
+     * @param msg ClusterMessage
+     */
+    public void receive(ClusterMessage msg);
+ 
+    /**
+     * Sends a message to all the members in the cluster
+     * @param msg ClusterMessage
+     */
+    public void send(ClusterMessage msg);
+    
+    /**
+     * Sends a message to a specific member in the cluster.
+     *
+     * @param msg ClusterMessage
+     * @param dest Member
+     */
+    public void send(ClusterMessage msg, Member dest);
+    
+    /**
+     * Sends a message to a all members at local cluster domain
+     *
+     * @param msg ClusterMessage
+     */
+    public void sendClusterDomain(ClusterMessage msg);
+
+    /**
+     * Returns all the members currently participating in the cluster.
+     *
+     * @return Member[]
+     */
+    public Member[] getMembers();
+    
+    /**
+     * Return the member that represents this node.
+     *
+     * @return Member
+     */
+    public Member getLocalMember();
+    
+    public void setClusterSender(ClusterSender sender);
+    
+    public ClusterSender getClusterSender();
+    
+    public void setClusterReceiver(ClusterReceiver receiver);
+    
+    public ClusterReceiver getClusterReceiver();
+    
+    public void setMembershipService(MembershipService service);
+    
+    public MembershipService getMembershipService();
+    
+    public void addValve(Valve valve);
+    
+    public void addClusterListener(MessageListener listener);
+    
+    public void removeClusterListener(MessageListener listener);
+    
+    public void setClusterDeployer(ClusterDeployer deployer);
+    
+    public ClusterDeployer getClusterDeployer();
+    
+    /**
+     * @return The map of managers
+     */
+    public Map getManagers();
+
+    public Manager getManager(String name);
+    public void removeManager(String name,Manager manager);
+    public void addManager(String name,Manager manager);
+    public Valve[] getValves();
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterDeployer.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterDeployer.java
new file mode 100644
index 0000000..b1c7281
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterDeployer.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster;
+
+/**
+ * A <b>ClusterDeployer</b> interface allows to plug in and out the
+ * different deployment implementations
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+import org.apache.catalina.LifecycleException;
+import java.io.IOException;
+import java.net.URL;
+
+public interface ClusterDeployer extends MessageListener {
+    /**
+     * Descriptive information about this component implementation.
+     */
+    public String info = "ClusterDeployer/1.0";
+    /**
+     * Start the cluster deployer, the owning container will invoke this
+     * @throws Exception - if failure to start cluster
+     */
+    public void start() throws Exception;
+
+    /**
+     * Stops the cluster deployer, the owning container will invoke this
+     * @throws LifecycleException
+     */
+    public void stop() throws LifecycleException;
+
+    /**
+     * Sets the deployer for this cluster deployer to use.
+     * @param deployer Deployer
+     */
+    // FIXME
+    //public void setDeployer(Deployer deployer);
+
+    /**
+     * Install a new web application, whose web application archive is at the
+     * specified URL, into this container and all the other
+     * members of the cluster with the specified context path.
+     * A context path of "" (the empty string) should be used for the root
+     * application for this container.  Otherwise, the context path must
+     * start with a slash.
+     * <p>
+     * If this application is successfully installed locally, 
+     * a ContainerEvent of type
+     * <code>INSTALL_EVENT</code> will be sent to all registered listeners,
+     * with the newly created <code>Context</code> as an argument.
+     *
+     * @param contextPath The context path to which this application should
+     *  be installed (must be unique)
+     * @param war A URL of type "jar:" that points to a WAR file, or type
+     *  "file:" that points to an unpacked directory structure containing
+     *  the web application to be installed
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalStateException if the specified context path
+     *  is already attached to an existing web application
+     * @exception IOException if an input/output error was encountered
+     *  during installation
+     */
+    public void install(String contextPath, URL war) throws IOException;
+
+    /**
+     * Remove an existing web application, attached to the specified context
+     * path.  If this application is successfully removed, a
+     * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
+     * registered listeners, with the removed <code>Context</code> as
+     * an argument. Deletes the web application war file and/or directory
+     * if they exist in the Host's appBase.
+     *
+     * @param contextPath The context path of the application to be removed
+     * @param undeploy boolean flag to remove web application from server
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  removal
+     */
+    public void remove(String contextPath, boolean undeploy) throws IOException;
+
+    /**
+     * call from container Background Process
+     */
+    public void backgroundProcess();
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterManager.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterManager.java
new file mode 100644
index 0000000..83e580c
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterManager.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster;
+
+
+import org.apache.catalina.Manager;
+
+
+/**
+ * The common interface used by all cluster manager.
+ * This is so that we can have a more pluggable way
+ * of swapping session managers for different algorithms.
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ */
+public interface ClusterManager extends Manager {
+
+   /**
+    * A message was received from another node, this
+    * is the callback method to implement if you are interested in
+    * receiving replication messages.
+    * @param msg - the message received.
+    */
+   public void messageDataReceived(ClusterMessage msg);
+
+   /**
+    * When the request has been completed, the replication valve
+    * will notify the manager, and the manager will decide whether
+    * any replication is needed or not.
+    * If there is a need for replication, the manager will
+    * create a session message and that will be replicated.
+    * The cluster determines where it gets sent.
+    * @param sessionId - the sessionId that just completed.
+    * @return a SessionMessage to be sent.
+    */
+   public ClusterMessage requestCompleted(String sessionId);
+
+   /**
+    * When the manager expires session not tied to a request.
+    * The cluster will periodically ask for a list of sessions
+    * that should expire and that should be sent across the wire.
+    * @return String[] The invalidated sessions
+    */
+   public String[] getInvalidatedSessions();
+   
+   /**
+    * Return the name of the manager, at host /context name and at engine hostname+/context.
+    * @return String
+    * @since 5.5.10
+    */
+   public String getName();
+   
+   /**
+    * Set the name of the manager, at host /context name and at engine hostname+/context
+    * @param name
+    * @since 5.5.10
+    */
+   public void setName(String name);
+         
+   public CatalinaCluster getCluster();
+
+   public void setCluster(CatalinaCluster cluster);
+   
+   /**
+    * @return Manager send only to same cluster domain.
+    * @since 5.5.10
+    */
+   public boolean isSendClusterDomainOnly();
+
+   /**
+    * @param sendClusterDomainOnly Flag value.
+    * @since 5.5.10
+    */
+   public void setSendClusterDomainOnly(boolean sendClusterDomainOnly);
+
+   /**
+    * @param mode The mode
+    * @since 5.5.10
+    */
+   public void setDefaultMode(boolean mode);
+
+   /**
+    * @since 5.5.10
+    */
+   public boolean isDefaultMode();
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterMessage.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterMessage.java
new file mode 100644
index 0000000..162a4c3
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterMessage.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster;
+
+import java.io.Serializable;
+
+/**
+ * @author Peter Rossbach
+ */
+public interface ClusterMessage extends Serializable {
+    
+    public final static int FLAG_FORBIDDEN = 0 ;
+    public final static int FLAG_ALLOWED = 1 ;
+    public final static int FLAG_DEFAULT = 2 ;
+    
+    /**
+     * Get the address that this message originated from.  This would be set
+     * if the message was being relayed from a host other than the one
+     * that originally sent it.
+     */
+    public Member getAddress();
+
+    /**
+     * Called by the cluster before sending it to the other
+     * nodes.
+     *
+     * @param member Member
+     */
+    public void setAddress(Member member);
+
+    /**
+     * Timestamp message.
+     *
+     * @return long
+     */
+    public long getTimestamp();
+
+    /**
+     * Called by the cluster before sending out
+     * the message.
+     *
+     * @param timestamp The timestamp
+     */
+    public void setTimestamp(long timestamp);
+
+    /**
+     * Each message must have a unique ID, in case of using async replication,
+     * and a smart queue, this id is used to replace messages not yet sent.
+     *
+     * @return String
+     */
+    public String getUniqueId();
+
+    /**
+     * Each message can made the desicion that resend is allowed or not or handle by default.
+     * @return 0 Forbidden, 1 allowed, 2 default
+     * @since 5.5.10
+     */
+    public int getResend();
+    
+    /**
+     * set desicion that resend is allowed or not or handle by default.
+     *
+     * @param resend 0 Forbidden, 1 allowed, 2 default
+     * @since 5.5.10
+     */
+    public void setResend(int resend) ;
+
+    /**
+     * Each message can made the desicion that compress is allowed or not or handle by default.
+     *
+     * @return 0 Forbidden, 1 allowed, 2 default
+     * @since 5.5.10
+     */
+    public int getCompress();
+    
+    /**
+     * set desicion that compress is allowed or not or handle by default.
+     *
+     * @param compress 0 Forbidden, 1 allowed, 2 default
+     * @since 5.5.10
+     */
+    public void setCompress(int compress) ;
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterReceiver.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterReceiver.java
new file mode 100644
index 0000000..e8f7b0f
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterReceiver.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster;
+
+
+/**
+ * Cluster Receiver Interface
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public interface ClusterReceiver {
+    /**
+     * Start message listing
+     * @throws java.io.IOException
+     */
+    public void start() throws java.io.IOException;
+
+    /**
+     * Stop message listing 
+     */
+    public void stop();
+
+    /**
+     * set callback.
+     *
+     * @param cluster The cluster
+     */
+    public void setCatalinaCluster(CatalinaCluster cluster);
+    
+    /**
+     * get Callback.
+     *
+     * @return The cluster
+     */
+    public CatalinaCluster getCatalinaCluster();
+    
+    /**
+     * Send Ack to sender or not.
+     *
+     * @return The flag value
+     */
+    public boolean isSendAck();
+    
+    /**
+     * set ack mode
+     * @param isSendAck
+     */
+    public void setSendAck(boolean isSendAck);
+    
+    public boolean isCompress() ;
+    public void setCompress(boolean compress);
+
+    /**
+     * get the listing ip interface
+     * @return The host
+     */
+    public String getHost();
+    
+    
+    /**
+     * get the listing ip port
+     * @return The port
+     */
+    public int getPort();
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterSender.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterSender.java
new file mode 100644
index 0000000..7f5157a
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterSender.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster;
+
+import org.apache.catalina.cluster.tcp.SimpleTcpCluster;
+
+
+/**
+ * 
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ *
+ */
+public interface ClusterSender
+{
+
+    public void add(Member member);
+
+    public void remove(Member member);
+
+    public void start() throws java.io.IOException;
+
+    public void stop();
+
+    public void backgroundProcess() ;
+
+    public void sendMessage(ClusterMessage message, Member member) throws java.io.IOException;
+
+    public void sendMessage(ClusterMessage message) throws java.io.IOException;
+    
+    public void sendMessageClusterDomain(ClusterMessage message) throws java.io.IOException;
+
+    public boolean isWaitForAck();
+    public void setWaitForAck(boolean isWaitForAck);
+
+    public boolean isCompress() ;
+    public void setCompress(boolean compress);
+
+    /**
+     * @param cluster
+     */
+    public void setCatalinaCluster(SimpleTcpCluster cluster);
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterSession.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterSession.java
new file mode 100644
index 0000000..89f98a8
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterSession.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.cluster;
+
+import org.apache.catalina.Session;
+import javax.servlet.http.HttpSession;
+
+public interface ClusterSession extends Session, HttpSession {
+   /**
+    * returns true if this session is the primary session, if that is the
+    * case, the manager can expire it upon timeout.
+    * @return True if this session is primary
+    */
+   public boolean isPrimarySession();
+
+   /**
+    * Sets whether this is the primary session or not.
+    * @param primarySession Flag value
+    */
+   public void setPrimarySession(boolean primarySession);
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterValve.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterValve.java
new file mode 100644
index 0000000..64b9e8e
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/ClusterValve.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster;
+
+/**
+ * Cluster Valve Interface to mark all Cluster Valves 
+ * Only those Valve can'be configured as Cluster Valves
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public interface ClusterValve {
+    /**
+     * Returns the cluster the cluster deployer is associated with
+     * @return CatalinaCluster
+     */
+    public CatalinaCluster getCluster();
+
+    /**
+     * Associates the cluster deployer with a cluster
+     * @param cluster CatalinaCluster
+     */
+    public void setCluster(CatalinaCluster cluster);
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/Constants.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/Constants.java
new file mode 100644
index 0000000..b2ec9c4
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/Constants.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.cluster;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.cluster</code>
+ * package.
+ *
+ * @author Bip Thelin
+ * @version $Revision$, $Date$
+ */
+
+public final class Constants {
+    public static final String Package = "org.apache.catalina.cluster";
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/LocalStrings.properties b/container/modules/cluster/src/share/org/apache/catalina/cluster/LocalStrings.properties
new file mode 100644
index 0000000..b179189
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/LocalStrings.properties
@@ -0,0 +1 @@
+cluster.mbean.register.allready=MBean {0} allready registered!
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/Member.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/Member.java
new file mode 100644
index 0000000..eb43846
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/Member.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster;
+
+/**
+ * The Member interface, defines a member in the Cluster.
+ * A member is a Tomcat process that participates in session replication.<BR>
+ * Each member can carry a set of properties, defined by the actual implementation.<BR>
+ * For TCP replication has been targeted for the first release, the hostname and listen port
+ * of the member is defined as hardcoded stuff.<BR>
+ * The Member interface together with MembershipListener, MembershipService are interfaces used to
+ * switch out the service used to establish membership in between the cluster nodes.
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+
+public interface Member {
+    /**
+     * Return implementation specific properties about this cluster node.
+     */
+    public java.util.HashMap getMemberProperties();
+    /**
+     * Returns the name of this node, should be unique within the cluster.
+     */
+    public String getName();
+  
+    /**
+     * Returns the name of the cluster domain from this node
+     */
+    public String getDomain();
+    
+    /**
+     * Returns the TCP listen host for the TCP implementation
+     */
+    public String getHost();
+    /**
+     * Returns the TCP listen portfor the TCP implementation
+     */
+    public int getPort();
+
+    /**
+     * Contains information on how long this member has been online.
+     * The result is the number of milli seconds this member has been
+     * broadcasting its membership to the cluster.
+     * @return nr of milliseconds since this member started.
+     */
+    public long getMemberAliveTime();
+    
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/MembershipListener.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/MembershipListener.java
new file mode 100644
index 0000000..5db9c7f
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/MembershipListener.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster;
+
+/**
+ * The MembershipListener interface is used as a callback to the
+ * membership service. It has two methods that will notify the listener
+ * when a member has joined the cluster and when a member has disappeared (crashed)
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+
+public interface MembershipListener {
+    public void memberAdded(Member member);
+    public void memberDisappeared(Member member);
+
+}
\ No newline at end of file
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/MembershipService.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/MembershipService.java
new file mode 100644
index 0000000..68d430b
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/MembershipService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster;
+
+import org.apache.catalina.cluster.tcp.SimpleTcpCluster;
+
+/**
+ * The membership service helps the cluster determine the membership
+ * logic in the cluster.
+ * @author Peter Rossbach
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+
+public interface MembershipService {
+
+    /**
+     * Sets the properties for the membership service. This must be called before
+     * the <code>start()</code> method is called.
+     * The properties are implementation specific.
+     * @param properties - to be used to configure the membership service.
+     */
+    public void setProperties(java.util.Properties properties);
+    /**
+     * Returns the properties for the configuration used.
+     */
+    public java.util.Properties getProperties();
+    /**
+     * Starts the membership service. If a membership listeners is added
+     * the listener will start to receive membership events.
+     * Performs a start level 1 and 2
+     * @throws java.lang.Exception if the service fails to start.
+     */
+    public void start() throws java.lang.Exception;
+
+    /**
+     * Starts the membership service. If a membership listeners is added
+     * the listener will start to receive membership events.
+     * @param level - level 1 starts listening for members, level 2 
+     * starts broad casting the server
+     * @throws java.lang.Exception if the service fails to start.
+     */
+    public void start(int level) throws java.lang.Exception;
+
+
+    /**
+     * Stops the membership service
+     */
+    public void stop();
+    /**
+     * Returns a list of all the members in the cluster.
+     */
+    public Member[] getMembers();
+    /**
+     * Returns the member object that defines this member
+     */
+    public Member getLocalMember();
+
+    /**
+     * Return all members by name
+     */
+    public String[] getMembersByName() ; 
+    
+    /**
+     * Return the member by name
+     */
+    public Member findMemberByName(String name) ;
+
+    /**
+     * Sets the local member properties for broadcasting
+     */
+    public void setLocalMemberProperties(String listenHost, int listenPort);
+    /**
+     * Sets the membership listener, only one listener can be added.
+     * If you call this method twice, the last listener will be used.
+     * @param listener The listener
+     */
+    public void addMembershipListener(MembershipListener listener);
+    /**
+     * removes the membership listener.
+     */
+    public void removeMembershipListener();
+
+    /**
+     * @param cluster
+     */
+    public void setCatalinaCluster(SimpleTcpCluster cluster);
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/MessageListener.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/MessageListener.java
new file mode 100644
index 0000000..a288864
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/MessageListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster;
+
+public interface MessageListener {
+    
+    public void messageReceived(ClusterMessage msg);
+    
+    public boolean accept(ClusterMessage msg);
+    
+    public boolean equals(Object listener);
+    
+    public int hashCode();
+    
+    /**
+     * Returns the cluster the cluster deployer is associated with
+     * @return CatalinaCluster
+     */
+    public CatalinaCluster getCluster();
+
+    /**
+     * Associates the cluster deployer with a cluster
+     * @param cluster CatalinaCluster
+     */
+    public void setCluster(CatalinaCluster cluster);
+
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FarmWarDeployer.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FarmWarDeployer.java
new file mode 100644
index 0000000..4d13063
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FarmWarDeployer.java
@@ -0,0 +1,716 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.deploy;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashMap;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterDeployer;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.Member;
+import org.apache.commons.modeler.Registry;
+
+/**
+ * <p>
+ * A farm war deployer is a class that is able to deploy/undeploy web
+ * applications in WAR form within the cluster.
+ * </p>
+ * Any host can act as the admin, and will have three directories
+ * <ul>
+ * <li>deployDir - the directory where we watch for changes</li>
+ * <li>applicationDir - the directory where we install applications</li>
+ * <li>tempDir - a temporaryDirectory to store binary data when downloading a
+ * war from the cluster</li>
+ * </ul>
+ * Currently we only support deployment of WAR files since they are easier to
+ * send across the wire.
+ * 
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version 1.1
+ */
+public class FarmWarDeployer implements ClusterDeployer, FileChangeListener {
+    /*--Static Variables----------------------------------------*/
+    public static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(FarmWarDeployer.class);
+
+    /*--Instance Variables--------------------------------------*/
+    protected CatalinaCluster cluster = null;
+
+    protected boolean started = false; //default 5 seconds
+
+    protected HashMap fileFactories = new HashMap();
+
+    protected String deployDir;
+
+    protected String tempDir;
+
+    protected String watchDir;
+
+    protected boolean watchEnabled = false;
+
+    protected WarWatcher watcher = null;
+
+    /**
+     * Iteration count for background processing.
+     */
+    private int count = 0;
+
+    /**
+     * Frequency of the Farm watchDir check. Cluster wide deployment will be
+     * done once for the specified amount of backgrondProcess calls (ie, the
+     * lower the amount, the most often the checks will occur).
+     */
+    protected int processDeployFrequency = 2;
+
+    /**
+     * Path where context descriptors should be deployed.
+     */
+    protected File configBase = null;
+
+    /**
+     * The associated host.
+     */
+    protected Host host = null;
+
+    /**
+     * The host appBase.
+     */
+    protected File appBase = null;
+
+    /**
+     * MBean server.
+     */
+    protected MBeanServer mBeanServer = null;
+
+    /**
+     * The associated deployer ObjectName.
+     */
+    protected ObjectName oname = null;
+
+    /*--Constructor---------------------------------------------*/
+    public FarmWarDeployer() {
+    }
+
+    /*--Logic---------------------------------------------------*/
+    public void start() throws Exception {
+        if (started)
+            return;
+        getCluster().addClusterListener(this);
+        if (watchEnabled) {
+            watcher = new WarWatcher(this, new File(getWatchDir()));
+            if (log.isInfoEnabled())
+                log.info("Cluster deployment is watching " + getWatchDir()
+                        + " for changes.");
+        } //end if
+
+        // Check to correct engine and host setup
+        host = (Host) getCluster().getContainer();
+        Engine engine = (Engine) host.getParent();
+        try {
+            oname = new ObjectName(engine.getName() + ":type=Deployer,host="
+                    + host.getName());
+        } catch (Exception e) {
+            log.error("Can't construct MBean object name" + e);
+        }
+        configBase = new File(System.getProperty("catalina.base"), "conf");
+        if (engine != null) {
+            configBase = new File(configBase, engine.getName());
+        }
+        if (host != null) {
+            configBase = new File(configBase, host.getName());
+        }
+
+        // Retrieve the MBean server
+        mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+
+        started = true;
+        count = 0;
+        if (log.isInfoEnabled())
+            log.info("Cluster FarmWarDeployer started.");
+    }
+
+    /*
+     * stop cluster wide deployments
+     * 
+     * @see org.apache.catalina.cluster.ClusterDeployer#stop()
+     */
+    public void stop() throws LifecycleException {
+        started = false;
+        getCluster().removeClusterListener(this);
+        count = 0;
+        if (watcher != null) {
+            watcher.clear();
+            watcher = null;
+
+        }
+        if (log.isInfoEnabled())
+            log.info("Cluster FarmWarDeployer stopped.");
+    }
+
+    public void cleanDeployDir() {
+        throw new java.lang.UnsupportedOperationException(
+                "Method cleanDeployDir() not yet implemented.");
+    }
+
+    /**
+     * Callback from the cluster, when a message is received, The cluster will
+     * broadcast it invoking the messageReceived on the receiver.
+     * 
+     * @param msg
+     *            ClusterMessage - the message received from the cluster
+     */
+    public void messageReceived(ClusterMessage msg) {
+        try {
+            if (msg instanceof FileMessage && msg != null) {
+                FileMessage fmsg = (FileMessage) msg;
+                if (log.isDebugEnabled())
+                    log.debug("receive cluster deployment [ path: "
+                            + fmsg.getContextPath() + " war:  "
+                            + fmsg.getFileName() + " ]");
+                FileMessageFactory factory = getFactory(fmsg);
+                // TODO correct second try after app is in service!
+                if (factory.writeMessage(fmsg)) {
+                    //last message received war file is completed
+                    String name = factory.getFile().getName();
+                    if (!name.endsWith(".war"))
+                        name = name + ".war";
+                    File deployable = new File(getDeployDir(), name);
+                    try {
+                        String path = fmsg.getContextPath();
+                        if (!isServiced(path)) {
+                            addServiced(path);
+                            try {
+                                remove(path);
+                                factory.getFile().renameTo(deployable);
+                                check(path);
+                            } finally {
+                                removeServiced(path);
+                            }
+                            if (log.isDebugEnabled())
+                                log.debug("deployment from " + path
+                                        + " finished.");
+                        } else
+                            log.error("Application " + path
+                                    + " in used. touch war file " + name
+                                    + " again!");
+                    } catch (Exception ex) {
+                        log.error(ex);
+                    } finally {
+                        removeFactory(fmsg);
+                    }
+                } //end if
+            } else if (msg instanceof UndeployMessage && msg != null) {
+                try {
+                    UndeployMessage umsg = (UndeployMessage) msg;
+                    String path = umsg.getContextPath();
+                    if (log.isDebugEnabled())
+                        log.debug("receive cluster undeployment from " + path);
+                    if (!isServiced(path)) {
+                        addServiced(path);
+                        try {
+                            remove(path);
+                        } finally {
+                            removeServiced(path);
+                        }
+                        if (log.isDebugEnabled())
+                            log.debug("undeployment from " + path
+                                    + " finished.");
+                    } else
+                        log
+                                .error("Application "
+                                        + path
+                                        + " in used. Sorry not remove from backup cluster nodes!");
+                } catch (Exception ex) {
+                    log.error(ex);
+                }
+            } //end if
+        } catch (java.io.IOException x) {
+            log.error("Unable to read farm deploy file message.", x);
+        }
+    }
+
+    /**
+     * create factory for all transported war files
+     * 
+     * @param msg
+     * @return Factory for all app message (war files)
+     * @throws java.io.FileNotFoundException
+     * @throws java.io.IOException
+     */
+    public synchronized FileMessageFactory getFactory(FileMessage msg)
+            throws java.io.FileNotFoundException, java.io.IOException {
+        File tmpFile = new File(msg.getFileName());
+        File writeToFile = new File(getTempDir(), tmpFile.getName());
+        FileMessageFactory factory = (FileMessageFactory) fileFactories.get(msg
+                .getFileName());
+        if (factory == null) {
+            factory = FileMessageFactory.getInstance(writeToFile, true);
+            fileFactories.put(msg.getFileName(), factory);
+        }
+        return factory;
+    }
+
+    /**
+     * Remove file (war) from messages)
+     * 
+     * @param msg
+     */
+    public void removeFactory(FileMessage msg) {
+        fileFactories.remove(msg.getFileName());
+    }
+
+    /**
+     * Before the cluster invokes messageReceived the cluster will ask the
+     * receiver to accept or decline the message, In the future, when messages
+     * get big, the accept method will only take a message header
+     * 
+     * @param msg
+     *            ClusterMessage
+     * @return boolean - returns true to indicate that messageReceived should be
+     *         invoked. If false is returned, the messageReceived method will
+     *         not be invoked.
+     */
+    public boolean accept(ClusterMessage msg) {
+        return (msg instanceof FileMessage) || (msg instanceof UndeployMessage);
+    }
+
+    /**
+     * Install a new web application, whose web application archive is at the
+     * specified URL, into this container and all the other members of the
+     * cluster with the specified context path. A context path of "" (the empty
+     * string) should be used for the root application for this container.
+     * Otherwise, the context path must start with a slash.
+     * <p>
+     * If this application is successfully installed locally, a ContainerEvent
+     * of type <code>INSTALL_EVENT</code> will be sent to all registered
+     * listeners, with the newly created <code>Context</code> as an argument.
+     * 
+     * @param contextPath
+     *            The context path to which this application should be installed
+     *            (must be unique)
+     * @param war
+     *            A URL of type "jar:" that points to a WAR file, or type
+     *            "file:" that points to an unpacked directory structure
+     *            containing the web application to be installed
+     * 
+     * @exception IllegalArgumentException
+     *                if the specified context path is malformed (it must be ""
+     *                or start with a slash)
+     * @exception IllegalStateException
+     *                if the specified context path is already attached to an
+     *                existing web application
+     * @exception IOException
+     *                if an input/output error was encountered during
+     *                installation
+     */
+    public void install(String contextPath, URL war) throws IOException {
+        Member[] members = getCluster().getMembers();
+        Member localMember = getCluster().getLocalMember();
+        FileMessageFactory factory = FileMessageFactory.getInstance(new File(
+                war.getFile()), false);
+        FileMessage msg = new FileMessage(localMember, war.getFile(),
+                contextPath);
+        if(log.isDebugEnabled())
+            log.debug("Send cluster war deployment [ path:"
+                    + contextPath + " war: " + war + " ] started.");
+        msg = factory.readMessage(msg);
+        while (msg != null) {
+            for (int i = 0; i < members.length; i++) {
+                if (log.isDebugEnabled())
+                    log.debug("Send cluster war fragment [ path: "
+                            + contextPath + " war: " + war + " to: " +  members[i] + " ]");
+                getCluster().send(msg, members[i]);
+            } //for
+            msg = factory.readMessage(msg);
+        } //while
+        if(log.isDebugEnabled())
+            log.debug("Send cluster war deployment [ path: "
+                    + contextPath + " war: " + war + " ] finished.");
+    }
+
+    /**
+     * Remove an existing web application, attached to the specified context
+     * path. If this application is successfully removed, a ContainerEvent of
+     * type <code>REMOVE_EVENT</code> will be sent to all registered
+     * listeners, with the removed <code>Context</code> as an argument.
+     * Deletes the web application war file and/or directory if they exist in
+     * the Host's appBase.
+     * 
+     * @param contextPath
+     *            The context path of the application to be removed
+     * @param undeploy
+     *            boolean flag to remove web application from server
+     * 
+     * @exception IllegalArgumentException
+     *                if the specified context path is malformed (it must be ""
+     *                or start with a slash)
+     * @exception IllegalArgumentException
+     *                if the specified context path does not identify a
+     *                currently installed web application
+     * @exception IOException
+     *                if an input/output error occurs during removal
+     */
+    public void remove(String contextPath, boolean undeploy) throws IOException {
+        if (log.isInfoEnabled())
+            log.info("Cluster wide remove of web app " + contextPath);
+        // send to other nodes
+        Member[] members = getCluster().getMembers();
+        Member localMember = getCluster().getLocalMember();
+        UndeployMessage msg = new UndeployMessage(localMember, System
+                .currentTimeMillis(), "Undeploy:" + contextPath + ":"
+                + System.currentTimeMillis(), contextPath, undeploy);
+        if (log.isDebugEnabled())
+            log.debug("Send cluster wide undeployment from "
+                    + contextPath );
+        cluster.send(msg);
+        // remove locally
+        if (undeploy) {
+            try {
+                if (!isServiced(contextPath)) {
+                    addServiced(contextPath);
+                    try {
+                        remove(contextPath);
+                    } finally {
+                        removeServiced(contextPath);
+                    }
+                } else
+                    log.error("Local remove from " + contextPath
+                            + "failed, other manager has app in service!");
+
+            } catch (Exception ex) {
+                log.error("local remove from " + contextPath + " failed", ex);
+            }
+        }
+
+    }
+
+    /*
+     * Modifcation from watchDir war detected!
+     * 
+     * @see org.apache.catalina.cluster.deploy.FileChangeListener#fileModified(java.io.File)
+     */
+    public void fileModified(File newWar) {
+        try {
+            File deployWar = new File(getDeployDir(), newWar.getName());
+            copy(newWar, deployWar);
+            String contextName = "/"
+                    + deployWar.getName().substring(0,
+                            deployWar.getName().lastIndexOf(".war"));
+            if (log.isInfoEnabled())
+                log.info("Installing webapp[" + contextName + "] from "
+                        + deployWar.getAbsolutePath());
+            try {
+                remove(contextName, false);
+            } catch (Exception x) {
+                log.error("No removal", x);
+            }
+            install(contextName, deployWar.toURL());
+        } catch (Exception x) {
+            log.error("Unable to install WAR file", x);
+        }
+    }
+
+    /*
+     * War remvoe from watchDir
+     * 
+     * @see org.apache.catalina.cluster.deploy.FileChangeListener#fileRemoved(java.io.File)
+     */
+    public void fileRemoved(File removeWar) {
+        try {
+            String contextName = "/"
+                    + removeWar.getName().substring(0,
+                            removeWar.getName().lastIndexOf(".war"));
+            if (log.isInfoEnabled())
+                log.info("Removing webapp[" + contextName + "]");
+            remove(contextName, true);
+        } catch (Exception x) {
+            log.error("Unable to remove WAR file", x);
+        }
+    }
+
+    /**
+     * Given a context path, get the config file name.
+     */
+    protected String getConfigFile(String path) {
+        String basename = null;
+        if (path.equals("")) {
+            basename = "ROOT";
+        } else {
+            basename = path.substring(1).replace('/', '#');
+        }
+        return (basename);
+    }
+
+    /**
+     * Given a context path, get the config file name.
+     */
+    protected String getDocBase(String path) {
+        String basename = null;
+        if (path.equals("")) {
+            basename = "ROOT";
+        } else {
+            basename = path.substring(1);
+        }
+        return (basename);
+    }
+
+    /**
+     * Return a File object representing the "application root" directory for
+     * our associated Host.
+     */
+    protected File getAppBase() {
+
+        if (appBase != null) {
+            return appBase;
+        }
+
+        File file = new File(host.getAppBase());
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"), host
+                    .getAppBase());
+        try {
+            appBase = file.getCanonicalFile();
+        } catch (IOException e) {
+            appBase = file;
+        }
+        return (appBase);
+
+    }
+
+    /**
+     * Invoke the remove method on the deployer.
+     */
+    protected void remove(String path) throws Exception {
+        // TODO Handle remove also work dir content !
+        // Stop the context first to be nicer
+        Context context = (Context) host.findChild(path);
+        if (context != null) {
+            if(log.isDebugEnabled())
+                log.debug("Undeploy local context " +path );
+            ((Lifecycle) context).stop();
+            File war = new File(getAppBase(), getDocBase(path) + ".war");
+            File dir = new File(getAppBase(), getDocBase(path));
+            File xml = new File(configBase, getConfigFile(path) + ".xml");
+            if (war.exists()) {
+                war.delete();
+            } else if (dir.exists()) {
+                undeployDir(dir);
+            } else {
+                xml.delete();
+            }
+            // Perform new deployment and remove internal HostConfig state
+            check(path);
+        }
+
+    }
+
+    /**
+     * Delete the specified directory, including all of its contents and
+     * subdirectories recursively.
+     * 
+     * @param dir
+     *            File object representing the directory to be deleted
+     */
+    protected void undeployDir(File dir) {
+
+        String files[] = dir.list();
+        if (files == null) {
+            files = new String[0];
+        }
+        for (int i = 0; i < files.length; i++) {
+            File file = new File(dir, files[i]);
+            if (file.isDirectory()) {
+                undeployDir(file);
+            } else {
+                file.delete();
+            }
+        }
+        dir.delete();
+
+    }
+
+    /*
+     * Call watcher to check for deploy changes
+     * 
+     * @see org.apache.catalina.cluster.ClusterDeployer#backgroundProcess()
+     */
+    public void backgroundProcess() {
+        if (started) {
+            count = (count + 1) % processDeployFrequency;
+            if (count == 0 && watchEnabled) {
+                watcher.check();
+            }
+        }
+
+    }
+
+    /*--Deployer Operations ------------------------------------*/
+
+    /**
+     * Invoke the check method on the deployer.
+     */
+    protected void check(String name) throws Exception {
+        String[] params = { name };
+        String[] signature = { "java.lang.String" };
+        mBeanServer.invoke(oname, "check", params, signature);
+    }
+
+    /**
+     * Invoke the check method on the deployer.
+     */
+    protected boolean isServiced(String name) throws Exception {
+        String[] params = { name };
+        String[] signature = { "java.lang.String" };
+        Boolean result = (Boolean) mBeanServer.invoke(oname, "isServiced",
+                params, signature);
+        return result.booleanValue();
+    }
+
+    /**
+     * Invoke the check method on the deployer.
+     */
+    protected void addServiced(String name) throws Exception {
+        String[] params = { name };
+        String[] signature = { "java.lang.String" };
+        mBeanServer.invoke(oname, "addServiced", params, signature);
+    }
+
+    /**
+     * Invoke the check method on the deployer.
+     */
+    protected void removeServiced(String name) throws Exception {
+        String[] params = { name };
+        String[] signature = { "java.lang.String" };
+        mBeanServer.invoke(oname, "removeServiced", params, signature);
+    }
+
+    /*--Instance Getters/Setters--------------------------------*/
+    public CatalinaCluster getCluster() {
+        return cluster;
+    }
+
+    public void setCluster(CatalinaCluster cluster) {
+        this.cluster = cluster;
+    }
+
+    public boolean equals(Object listener) {
+        return super.equals(listener);
+    }
+
+    public int hashCode() {
+        return super.hashCode();
+    }
+
+    public String getDeployDir() {
+        return deployDir;
+    }
+
+    public void setDeployDir(String deployDir) {
+        this.deployDir = deployDir;
+    }
+
+    public String getTempDir() {
+        return tempDir;
+    }
+
+    public void setTempDir(String tempDir) {
+        this.tempDir = tempDir;
+    }
+
+    public String getWatchDir() {
+        return watchDir;
+    }
+
+    public void setWatchDir(String watchDir) {
+        this.watchDir = watchDir;
+    }
+
+    public boolean isWatchEnabled() {
+        return watchEnabled;
+    }
+
+    public boolean getWatchEnabled() {
+        return watchEnabled;
+    }
+
+    public void setWatchEnabled(boolean watchEnabled) {
+        this.watchEnabled = watchEnabled;
+    }
+
+    /**
+     * Return the frequency of watcher checks.
+     */
+    public int getProcessDeployFrequency() {
+
+        return (this.processDeployFrequency);
+
+    }
+
+    /**
+     * Set the watcher checks frequency.
+     * 
+     * @param processExpiresFrequency
+     *            the new manager checks frequency
+     */
+    public void setProcessDeployFrequency(int processExpiresFrequency) {
+
+        if (processExpiresFrequency <= 0) {
+            return;
+        }
+        this.processDeployFrequency = processExpiresFrequency;
+    }
+
+    /**
+     * Copy a file to the specified temp directory. This is required only
+     * because Jasper depends on it.
+     */
+    protected boolean copy(File from, File to) {
+        try {
+            if (!to.exists())
+                to.createNewFile();
+            java.io.FileInputStream is = new java.io.FileInputStream(from);
+            java.io.FileOutputStream os = new java.io.FileOutputStream(to,
+                    false);
+            byte[] buf = new byte[4096];
+            while (true) {
+                int len = is.read(buf);
+                if (len < 0)
+                    break;
+                os.write(buf, 0, len);
+            }
+            is.close();
+            os.close();
+        } catch (IOException e) {
+            log.error("Unable to copy file from:" + from + " to:" + to, e);
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileChangeListener.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileChangeListener.java
new file mode 100644
index 0000000..a1bcaf3
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileChangeListener.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.deploy;
+import java.io.File;
+
+public interface FileChangeListener {
+    public void fileModified(File f);
+    public void fileRemoved(File f);
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileMessage.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileMessage.java
new file mode 100644
index 0000000..95afcf6
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileMessage.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.deploy;
+
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.Member;
+import java.io.Serializable;
+
+/**
+ * Contains the data for a file being transferred over TCP, this is 
+ * essentially a fragment of a file, read and written by the FileMessageFactory
+ * @author Filip Hanik
+ * @version 1.0
+ */
+
+public class FileMessage implements ClusterMessage, Serializable {
+    private int messageNumber;
+    private byte[] data;
+    private int dataLength;
+    private org.apache.catalina.cluster.Member address;
+    
+    private long timestamp;
+    private long totalLength;
+    private long totalNrOfMsgs;
+    private String fileName;
+    private String contextPath;
+    private int resend = ClusterMessage.FLAG_FORBIDDEN;
+    private int compress = ClusterMessage.FLAG_DEFAULT ;
+    
+    public FileMessage(Member source,
+                       String fileName,
+                       String contextPath) {
+        this.address=source;
+        this.fileName=fileName;
+        this.contextPath=contextPath;
+    }
+    
+    /*
+    public void writeExternal(ObjectOutput out) throws IOException {
+                   
+    }
+    
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+                  
+    }
+    */
+   
+    public int getMessageNumber() {
+        return messageNumber;
+    }
+    public void setMessageNumber(int messageNumber) {
+        this.messageNumber = messageNumber;
+    }
+    public long getTotalNrOfMsgs() {
+        return totalNrOfMsgs;
+    }
+    public void setTotalNrOfMsgs(long totalNrOfMsgs) {
+        this.totalNrOfMsgs = totalNrOfMsgs;
+    }
+    public byte[] getData() {
+        return data;
+    }
+    public void setData(byte[] data, int length) {
+        this.data = data;
+        this.dataLength = length;
+    }
+    public int getDataLength() {
+        return dataLength;
+    }
+    public void setDataLength(int dataLength) {
+        this.dataLength = dataLength;
+    }
+    public long getTotalLength() {
+        return totalLength;
+    }
+    public void setTotalLength(long totalLength) {
+        this.totalLength = totalLength;
+    }
+    public org.apache.catalina.cluster.Member getAddress() {
+        return address;
+    }
+    public void setAddress(org.apache.catalina.cluster.Member address) {
+        this.address = address;
+    }
+    public String getUniqueId() {
+        StringBuffer result = new StringBuffer(getFileName());
+        result.append("#-#");
+        result.append(getMessageNumber());
+        result.append("#-#");
+        result.append(System.currentTimeMillis());
+        return result.toString();
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+    public String getFileName() {
+        return fileName;
+    }
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+    public String getContextPath() {
+        return contextPath;
+    }
+
+    /**
+     * @return Returns the compress.
+     * @since 5.5.10 
+     */
+    public int getCompress() {
+        return compress;
+    }
+    /**
+     * @param compress The compress to set.
+     * @since 5.5.10
+     */
+    public void setCompress(int compress) {
+        this.compress = compress;
+    }
+    /**
+     * @return Returns the resend.
+     * @since 5.5.10
+     */
+    public int getResend() {
+        return resend;
+    }
+    /**
+     * @param resend The resend to set.
+     * @since 5.5.10
+     */
+    public void setResend(int resend) {
+        this.resend = resend;
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileMessageFactory.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileMessageFactory.java
new file mode 100644
index 0000000..001afec
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileMessageFactory.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.deploy;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+
+/**
+ * This factory is used to read files and write files by splitting them up into
+ * smaller messages. So that entire files don't have to be read into memory.
+ * <BR>
+ * The factory can be used as a reader or writer but not both at the same time.
+ * When done reading or writing the factory will close the input or output
+ * streams and mark the factory as closed. It is not possible to use it after
+ * that. <BR>
+ * To force a cleanup, call cleanup() from the calling object. <BR>
+ * This class is not thread safe.
+ * 
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class FileMessageFactory {
+    /*--Static Variables----------------------------------------*/
+    public static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(FileMessageFactory.class);
+
+    /**
+     * The number of bytes that we read from file
+     */
+    public static final int READ_SIZE = 1024 * 10; //10kb
+
+    /**
+     * The file that we are reading/writing
+     */
+    protected File file = null;
+
+    /**
+     * True means that we are writing with this factory. False means that we are
+     * reading with this factory
+     */
+    protected boolean openForWrite;
+
+    /**
+     * Once the factory is used, it can not be reused.
+     */
+    protected boolean closed = false;
+
+    /**
+     * When openForWrite=false, the input stream is held by this variable
+     */
+    protected FileInputStream in;
+
+    /**
+     * When openForWrite=true, the output stream is held by this variable
+     */
+    protected FileOutputStream out;
+
+    /**
+     * The number of messages we have read or written
+     */
+    protected int nrOfMessagesProcessed = 0;
+
+    /**
+     * The total size of the file
+     */
+    protected long size = 0;
+
+    /**
+     * The total number of packets that we split this file into
+     */
+    protected long totalNrOfMessages = 0;
+
+    /**
+     * The bytes that we hold the data in, not thread safe.
+     */
+    protected byte[] data = new byte[READ_SIZE];
+
+    /**
+     * Private constructor, either instantiates a factory to read or write. <BR>
+     * When openForWrite==true, then a the file, f, will be created and an
+     * output stream is opened to write to it. <BR>
+     * When openForWrite==false, an input stream is opened, the file has to
+     * exist.
+     * 
+     * @param f
+     *            File - the file to be read/written
+     * @param openForWrite
+     *            boolean - true means we are writing to the file, false means
+     *            we are reading from the file
+     * @throws FileNotFoundException -
+     *             if the file to be read doesn't exist
+     * @throws IOException -
+     *             if the system fails to open input/output streams to the file
+     *             or if it fails to create the file to be written to.
+     */
+    private FileMessageFactory(File f, boolean openForWrite)
+            throws FileNotFoundException, IOException {
+        this.file = f;
+        this.openForWrite = openForWrite;
+        if (log.isDebugEnabled())
+            log.debug("open file " + f + " write " + openForWrite);
+        if (openForWrite) {
+            if (!file.exists())
+                file.createNewFile();
+            out = new FileOutputStream(f);
+        } else {
+            size = file.length();
+            totalNrOfMessages = (size / READ_SIZE) + 1;
+            in = new FileInputStream(f);
+        }//end if
+
+    }
+
+    /**
+     * Creates a factory to read or write from a file. When opening for read,
+     * the readMessage can be invoked, and when opening for write the
+     * writeMessage can be invoked.
+     * 
+     * @param f
+     *            File - the file to be read or written
+     * @param openForWrite
+     *            boolean - true, means we are writing to the file, false means
+     *            we are reading from it
+     * @throws FileNotFoundException -
+     *             if the file to be read doesn't exist
+     * @throws IOException -
+     *             if it fails to create the file that is to be written
+     * @return FileMessageFactory
+     */
+    public static FileMessageFactory getInstance(File f, boolean openForWrite)
+            throws FileNotFoundException, IOException {
+        return new FileMessageFactory(f, openForWrite);
+    }
+
+    /**
+     * Reads file data into the file message and sets the size, totalLength,
+     * totalNrOfMsgs and the message number <BR>
+     * If EOF is reached, the factory returns null, and closes itself, otherwise
+     * the same message is returned as was passed in. This makes sure that not
+     * more memory is ever used. To remember, neither the file message or the
+     * factory are thread safe. dont hand off the message to one thread and read
+     * the same with another.
+     * 
+     * @param f
+     *            FileMessage - the message to be populated with file data
+     * @throws IllegalArgumentException -
+     *             if the factory is for writing or is closed
+     * @throws IOException -
+     *             if a file read exception occurs
+     * @return FileMessage - returns the same message passed in as a parameter,
+     *         or null if EOF
+     */
+    public FileMessage readMessage(FileMessage f)
+            throws IllegalArgumentException, IOException {
+        checkState(false);
+        int length = in.read(data);
+        if (length == -1) {
+            cleanup();
+            return null;
+        } else {
+            f.setData(data, length);
+            f.setTotalLength(size);
+            f.setTotalNrOfMsgs(totalNrOfMessages);
+            f.setMessageNumber(++nrOfMessagesProcessed);
+            return f;
+        }//end if
+    }
+
+    /**
+     * Writes a message to file. If (msg.getMessageNumber() ==
+     * msg.getTotalNrOfMsgs()) the output stream will be closed after writing.
+     * 
+     * @param msg
+     *            FileMessage - message containing data to be written
+     * @throws IllegalArgumentException -
+     *             if the factory is opened for read or closed
+     * @throws IOException -
+     *             if a file write error occurs
+     * @return returns true if the file is complete and outputstream is closed,
+     *         false otherwise.
+     */
+    public boolean writeMessage(FileMessage msg)
+            throws IllegalArgumentException, IOException {
+        if (!openForWrite)
+            throw new IllegalArgumentException(
+                    "Can't write message, this factory is reading.");
+        if (log.isDebugEnabled())
+            log.debug("Message " + msg + " data " + msg.getData()
+                    + " data length " + msg.getDataLength() + " out " + out);
+        if (out != null) {
+            out.write(msg.getData(), 0, msg.getDataLength());
+            nrOfMessagesProcessed++;
+            out.flush();
+            if (msg.getMessageNumber() == msg.getTotalNrOfMsgs()) {
+                out.close();
+                cleanup();
+                return true;
+            }//end if
+        } else {
+            if (log.isWarnEnabled())
+                log.warn("Receive Message again -- Sender ActTimeout to short [ path: "
+                                + msg.getContextPath()
+                                + " war: "
+                                + msg.getFileName()
+                                + " data: "
+                                + msg.getData()
+                                + " data length: " + msg.getDataLength() + " ]");
+        }
+        return false;
+    }//writeMessage
+
+    /**
+     * Closes the factory, its streams and sets all its references to null
+     */
+    public void cleanup() {
+        if (in != null)
+            try {
+                in.close();
+            } catch (Exception ignore) {
+            }
+        if (out != null)
+            try {
+                out.close();
+            } catch (Exception ignore) {
+            }
+        in = null;
+        out = null;
+        size = 0;
+        closed = true;
+        data = null;
+        nrOfMessagesProcessed = 0;
+        totalNrOfMessages = 0;
+    }
+
+    /**
+     * Check to make sure the factory is able to perform the function it is
+     * asked to do. Invoked by readMessage/writeMessage before those methods
+     * proceed.
+     * 
+     * @param openForWrite
+     *            boolean
+     * @throws IllegalArgumentException
+     */
+    protected void checkState(boolean openForWrite)
+            throws IllegalArgumentException {
+        if (this.openForWrite != openForWrite) {
+            cleanup();
+            if (openForWrite)
+                throw new IllegalArgumentException(
+                        "Can't write message, this factory is reading.");
+            else
+                throw new IllegalArgumentException(
+                        "Can't read message, this factory is writing.");
+        }
+        if (this.closed) {
+            cleanup();
+            throw new IllegalArgumentException("Factory has been closed.");
+        }
+    }
+
+    /**
+     * Example usage.
+     * 
+     * @param args
+     *            String[], args[0] - read from filename, args[1] write to
+     *            filename
+     * @throws Exception
+     */
+    public static void main(String[] args) throws Exception {
+
+        System.out
+                .println("Usage: FileMessageFactory fileToBeRead fileToBeWritten");
+        System.out
+                .println("Usage: This will make a copy of the file on the local file system");
+        FileMessageFactory read = getInstance(new File(args[0]), false);
+        FileMessageFactory write = getInstance(new File(args[1]), true);
+        FileMessage msg = new FileMessage(null, args[0], args[0]);
+        msg = read.readMessage(msg);
+        System.out.println("Expecting to write " + msg.getTotalNrOfMsgs()
+                + " messages.");
+        int cnt = 0;
+        while (msg != null) {
+            write.writeMessage(msg);
+            cnt++;
+            msg = read.readMessage(msg);
+        }//while
+        System.out.println("Actually wrote " + cnt + " messages.");
+    }///main
+
+    public File getFile() {
+        return file;
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/UndeployMessage.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/UndeployMessage.java
new file mode 100644
index 0000000..11e08ba
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/UndeployMessage.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.deploy;
+
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.Member;
+import java.io.Serializable;
+public class UndeployMessage implements ClusterMessage,Serializable {
+    private Member address;
+    private long timestamp;
+    private String uniqueId;
+    private String contextPath;
+    private boolean undeploy;
+    private int resend = ClusterMessage.FLAG_DEFAULT ;
+    private int compress = ClusterMessage.FLAG_DEFAULT ;
+
+    public UndeployMessage() {} //for serialization
+    public UndeployMessage(Member address,
+                           long timestamp,
+                           String uniqueId,
+                           String contextPath,
+                           boolean undeploy) {
+        this.address  = address;
+        this.timestamp= timestamp;
+        this.undeploy = undeploy;
+        this.uniqueId = uniqueId;
+        this.undeploy = undeploy;
+        this.contextPath = contextPath;
+    }
+
+    public Member getAddress() {
+        return address;
+    }
+
+    public void setAddress(Member address) {
+        this.address = address;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public String getUniqueId() {
+        return uniqueId;
+    }
+
+    public void setUniqueId(String uniqueId) {
+        this.uniqueId = uniqueId;
+    }
+
+    public String getContextPath() {
+        return contextPath;
+    }
+
+    public void setContextPath(String contextPath) {
+        this.contextPath = contextPath;
+    }
+
+    public boolean getUndeploy() {
+        return undeploy;
+    }
+
+    public void setUndeploy(boolean undeploy) {
+        this.undeploy = undeploy;
+    }
+    /**
+     * @return Returns the compress.
+     * @since 5.5.10 
+     */
+    public int getCompress() {
+        return compress;
+    }
+    /**
+     * @param compress The compress to set.
+     * @since 5.5.10
+     */
+    public void setCompress(int compress) {
+        this.compress = compress;
+    }
+    /**
+     * @return Returns the resend.
+     * @since 5.5.10
+     */
+    public int getResend() {
+        return resend;
+    }
+    /**
+     * @param resend The resend to set.
+     * @since 5.5.10
+     */
+    public void setResend(int resend) {
+        this.resend = resend;
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/WarWatcher.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/WarWatcher.java
new file mode 100644
index 0000000..bd1246c
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/deploy/WarWatcher.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.deploy;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+
+/**
+ * <p>
+ * The <b>WarWatcher </b> watches the deployDir for changes made to the
+ * directory (adding new WAR files->deploy or remove WAR files->undeploy) And
+ * notifies a listener of the changes made
+ * </p>
+ * 
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version 1.1
+ */
+
+public class WarWatcher {
+
+    /*--Static Variables----------------------------------------*/
+    public static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(WarWatcher.class);
+
+    /*--Instance Variables--------------------------------------*/
+    /**
+     * Directory to watch for war files
+     */
+    protected File watchDir = null;
+
+    /**
+     * Parent to be notified of changes
+     */
+    protected FileChangeListener listener = null;
+
+    /**
+     * Currently deployed files
+     */
+    protected Map currentStatus = new HashMap();
+
+    /*--Constructor---------------------------------------------*/
+
+    public WarWatcher() {
+    }
+
+    public WarWatcher(FileChangeListener listener, File watchDir) {
+        this.listener = listener;
+        this.watchDir = watchDir;
+    }
+
+    /*--Logic---------------------------------------------------*/
+
+    /**
+     * check for modification and send notifcation to listener
+     */
+    public void check() {
+        if (log.isInfoEnabled())
+            log.info("check cluster wars at " + watchDir);
+        File[] list = watchDir.listFiles(new WarFilter());
+        if (list == null)
+            list = new File[0];
+        //first make sure all the files are listed in our current status
+        for (int i = 0; i < list.length; i++) {
+            addWarInfo(list[i]);
+        }//for
+
+        //check all the status codes and update the FarmDeployer
+        for (Iterator i = currentStatus.entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            WarInfo info = (WarInfo) entry.getValue();
+            int check = info.check();
+            if (check == 1) {
+                listener.fileModified(info.getWar());
+            } else if (check == -1) {
+                listener.fileRemoved(info.getWar());
+                //no need to keep in memory
+                currentStatus.remove(info.getWar());
+            }//end if
+        }//for
+
+    }
+
+    /**
+     * add cluster war to the watcher state
+     * @param warfile
+     */
+    protected void addWarInfo(File warfile) {
+        WarInfo info = (WarInfo) currentStatus.get(warfile.getAbsolutePath());
+        if (info == null) {
+            info = new WarInfo(warfile);
+            info.setLastState(-1); //assume file is non existent
+            currentStatus.put(warfile.getAbsolutePath(), info);
+        }
+    }
+
+    /**
+     * clear watcher state
+     */
+    public void clear() {
+        currentStatus.clear();
+    }
+
+    /**
+     * @return Returns the watchDir.
+     */
+    public File getWatchDir() {
+        return watchDir;
+    }
+
+    /**
+     * @param watchDir
+     *            The watchDir to set.
+     */
+    public void setWatchDir(File watchDir) {
+        this.watchDir = watchDir;
+    }
+
+    /**
+     * @return Returns the listener.
+     */
+    public FileChangeListener getListener() {
+        return listener;
+    }
+
+    /**
+     * @param listener
+     *            The listener to set.
+     */
+    public void setListener(FileChangeListener listener) {
+        this.listener = listener;
+    }
+
+    /*--Inner classes-------------------------------------------*/
+
+    /**
+     * File name filter for war files
+     */
+    protected class WarFilter implements java.io.FilenameFilter {
+        public boolean accept(File path, String name) {
+            if (name == null)
+                return false;
+            return name.endsWith(".war");
+        }
+    }
+
+    /**
+     * File information on existing WAR files
+     */
+    protected class WarInfo {
+        protected File war = null;
+
+        protected long lastChecked = 0;
+
+        protected long lastState = 0;
+
+        public WarInfo(File war) {
+            this.war = war;
+            this.lastChecked = war.lastModified();
+            if (!war.exists())
+                lastState = -1;
+        }
+
+        public boolean modified() {
+            return war.exists() && war.lastModified() > lastChecked;
+        }
+
+        public boolean exists() {
+            return war.exists();
+        }
+
+        /**
+         * Returns 1 if the file has been added/modified, 0 if the file is
+         * unchanged and -1 if the file has been removed
+         * 
+         * @return int 1=file added; 0=unchanged; -1=file removed
+         */
+        public int check() {
+            //file unchanged by default
+            int result = 0;
+
+            if (modified()) {
+                //file has changed - timestamp
+                result = 1;
+                lastState = result;
+            } else if ((!exists()) && (!(lastState == -1))) {
+                //file was removed
+                result = -1;
+                lastState = result;
+            } else if ((lastState == -1) && exists()) {
+                //file was added
+                result = 1;
+                lastState = result;
+            }
+            this.lastChecked = System.currentTimeMillis();
+            return result;
+        }
+
+        public File getWar() {
+            return war;
+        }
+
+        public int hashCode() {
+            return war.getAbsolutePath().hashCode();
+        }
+
+        public boolean equals(Object other) {
+            if (other instanceof WarInfo) {
+                WarInfo wo = (WarInfo) other;
+                return wo.getWar().equals(getWar());
+            } else {
+                return false;
+            }
+        }
+
+        protected void setLastState(int lastState) {
+            this.lastState = lastState;
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/io/ListenCallback.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/io/ListenCallback.java
new file mode 100644
index 0000000..9d70c3f
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/io/ListenCallback.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.io;
+
+import org.apache.catalina.cluster.tcp.ClusterData ;
+
+/**
+ * The listen callback interface is used by the replication system
+ * when data has been received. The interface does not care about
+ * objects and marshalling and just passes the bytes straight through.
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public interface ListenCallback
+{
+    /**
+     * This method is invoked on the callback object to notify it that new data has
+     * been received from one of the cluster nodes.
+     * @param data - the message bytes received from the cluster/replication system
+     */
+     public void messageDataReceived(ClusterData data);
+     
+    /** receiver must be send ack
+      */
+     public boolean isSendAck() ;
+     
+     /** send ack
+      *
+      */
+     public void sendAck() throws java.io.IOException ;
+
+}
\ No newline at end of file
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/io/ObjectReader.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/io/ObjectReader.java
new file mode 100644
index 0000000..3c027c0
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/io/ObjectReader.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster.io;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+
+import org.apache.catalina.cluster.tcp.ClusterData;
+
+/**
+ * The object reader object is an object used in conjunction with
+ * java.nio TCP messages. This object stores the message bytes in a
+ * <code>XByteBuffer</code> until a full package has been received.
+ * When a full package has been received, the append method will call messageDataReceived
+ * on the callback object associated with this object reader.<BR>
+ * This object uses an XByteBuffer which is an extendable object buffer that also allows
+ * for message encoding and decoding.
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public class ObjectReader {
+
+    private SocketChannel channel;
+
+    private Selector selector;
+
+    private ListenCallback callback;
+
+    private XByteBuffer buffer;
+
+    /**
+     * Create XByteBuffer and store parameter
+     * @param channel
+     * @param selector
+     * @param callback
+     */
+    public ObjectReader(SocketChannel channel, Selector selector, ListenCallback callback) {
+        this.channel = channel;
+        this.selector = selector;
+        this.callback = callback;
+        this.buffer = new XByteBuffer();
+    }
+
+    /**
+     * get the current SimpleTcpCluster
+     * @return Returns the callback.
+     */
+    public ListenCallback getCallback() {
+        return callback;
+    }
+
+    /**
+     * Get underlying NIO channel
+     * @return The socket
+     */
+    public SocketChannel getChannel() {
+        return this.channel;
+    }
+
+    /**
+     * Append new bytes to buffer. 
+     * @see XByteBuffer#countPackages()
+     * @param data new transfer buffer
+     * @param off offset
+     * @param len length in buffer
+     * @return number of messages that sended to callback
+     * @throws java.io.IOException
+     */
+     public int append(byte[] data,int off,int len) throws java.io.IOException {
+        buffer.append(data,off,len);
+        int pkgCnt = buffer.countPackages();
+        return pkgCnt;
+    }
+
+    /**
+     * Send buffer to cluster listener (callback).
+     * Is message complete receiver send message to callback?
+     *
+     * @see org.apache.catalina.cluster.tcp.ClusterReceiverBase#messageDataReceived(ClusterData)
+     * @see XByteBuffer#doesPackageExist()
+     * @see XByteBuffer#extractPackage(boolean)
+     *
+     * @return number of received packages/messages
+     * @throws java.io.IOException
+     */
+    public int execute() throws java.io.IOException {
+        int pkgCnt = 0;
+        boolean pkgExists = buffer.doesPackageExist();
+        while ( pkgExists ) {
+            ClusterData data = buffer.extractPackage(true);
+            getCallback().messageDataReceived(data);
+            pkgCnt++;
+            pkgExists = buffer.doesPackageExist();
+        }
+        return pkgCnt;
+    }
+    
+    /**
+     * Write Ack to sender
+     * @param buf
+     * @return The bytes written count
+     * @throws java.io.IOException
+     */
+    public int write(ByteBuffer buf) throws java.io.IOException {
+        return getChannel().write(buf);
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/io/SocketObjectReader.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/io/SocketObjectReader.java
new file mode 100644
index 0000000..8542c44
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/io/SocketObjectReader.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.io;
+
+
+import java.net.Socket;
+
+import org.apache.catalina.cluster.tcp.ClusterData;
+
+/**
+ * The object reader object is an object used in conjunction with
+ * java.nio TCP messages. This object stores the message bytes in a
+ * <code>XByteBuffer</code> until a full package has been received.
+ * When a full package has been received, the append method will call messageDataReceived
+ * on the callback object associated with this object reader.<BR>
+ * This object uses an XByteBuffer which is an extendable object buffer that also allows
+ * for message encoding and decoding.
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ * @since 5.5.10
+ */
+public class SocketObjectReader
+{
+    private Socket socket;
+    private ListenCallback callback;
+    private XByteBuffer buffer;
+
+    /**
+     * use this socket and callback to receive messages
+     * @param socket listener socket
+     * @param callback ClusterReceiverBase listener
+     */
+    public SocketObjectReader( Socket socket,
+                               ListenCallback callback)  {
+        this.socket = socket;
+        this.callback = callback;
+        this.buffer = new XByteBuffer();
+    }
+
+    
+    /**
+     * Append new bytes to buffer. 
+     * Is message complete receiver send message to callback
+     * @see org.apache.catalina.cluster.tcp.ClusterReceiverBase#messageDataReceived(ClusterData)
+     * @see XByteBuffer#doesPackageExist()
+     * @see XByteBuffer#extractPackage(boolean)
+     * @param data new transfer buffer
+     * @param off offset
+     * @param len length in buffer
+     * @return number of messages that sended to callback
+     * @throws java.io.IOException
+     */
+    public int append(byte[] data,int off,int len) throws java.io.IOException {
+        if(len > 0)
+            buffer.append(data,off,len);
+        boolean pkgExists = buffer.doesPackageExist();
+        int pkgCnt = 0;
+        while ( pkgExists ) {
+            ClusterData cdata = buffer.extractPackage(true);
+            if(callback.isSendAck())
+                callback.sendAck() ;
+            callback.messageDataReceived(cdata);
+            pkgCnt++;
+            pkgExists = buffer.doesPackageExist();
+        }
+        return pkgCnt;
+    }
+
+    
+    /**
+     * send message to callback
+     * @see SocketObjectReader#append(byte[], int, int)
+     * @return Count of packages written
+     * @throws java.io.IOException
+     */
+    public int execute() throws java.io.IOException {
+        return append(null,0,0);
+    }
+
+    /**
+     * write data to socket (ack)
+     * @see org.apache.catalina.cluster.tcp.SocketReplicationListener#sendAck
+     * @param data
+     * @return Always zero
+     * @throws java.io.IOException
+     */
+    public int write(byte[] data)
+       throws java.io.IOException {
+       socket.getOutputStream().write(data);
+       return 0;
+
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/io/XByteBuffer.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/io/XByteBuffer.java
new file mode 100644
index 0000000..5a19ce3
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/io/XByteBuffer.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.io;
+
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.tcp.ClusterData;
+
+/**
+ * The XByteBuffer provides a dual functionality.
+ * One, it stores message bytes and automatically extends the byte buffer if needed.<BR>
+ * Two, it can encode and decode packages so that they can be defined and identified
+ * as they come in on a socket.
+ * <br/>
+ * Transfer package:
+ * <ul>
+ * <li><b>START_DATA/b> - 7 bytes - <i>FLT2002</i></li>
+ * <li><b>SIZE</b>      - 4 bytes - size of the data package</li>
+ * <li><b>DATA</b>      - should be as many bytes as the prev SIZE</li>
+ * <li><b>END_DATA</b>  - 7 bytes - <i>TLF2003</i></lI>
+ * </ul>
+ * FIXME: Why we not use a list of byte buffers?
+ * FIXME: Used a pool of buffers instead, every time new generation
+ * FIXME: Compress mode send real data length at the first bytes?
+ * FIXME: s to-do.txt for new format proposal
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public class XByteBuffer
+{
+    
+    public static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( XByteBuffer.class );
+    
+    /**
+     * This is a package header, 7 bytes (FLT2002)
+     */
+    public static final byte[] START_DATA = {70,76,84,50,48,48,50};
+    
+    /**
+     * This is the package footer, 7 bytes (TLF2003)
+     */
+    public static final byte[] END_DATA = {84,76,70,50,48,48,51};
+ 
+    /**
+     * Default size on the initial byte buffer
+     */
+    static final int DEF_SIZE = 1024;
+ 
+    /**
+     * Default size to extend the buffer with
+     */
+    static final int DEF_EXT  = 1024;
+    
+    /**
+     * Variable to hold the data
+     */
+    protected byte[] buf = null;
+    
+    /**
+     * Current length of data in the buffer
+     */
+    protected int bufSize = 0;
+    
+    /**
+     * Constructs a new XByteBuffer
+     * @param size - the initial size of the byte buffer
+     */
+    public XByteBuffer(int size) {
+        buf = new byte[size];
+    }
+
+    /**
+     * Constructs a new XByteBuffer with an initial size of 1024 bytes
+     */
+    public XByteBuffer()  {
+        this(DEF_SIZE);
+    }
+
+    /**
+     * Returns the bytes in the buffer, in its exact length
+     */
+    public byte[] getBytes() {
+        byte[] b = new byte[bufSize];
+        System.arraycopy(buf,0,b,0,bufSize);
+        return b;
+    }
+
+    /**
+     * Resets the buffer
+     */
+    public void clear() {
+        bufSize = 0;
+    }
+
+    /**
+     * Appends the data to the buffer. If the data is incorrectly formatted, ie, the data should always start with the
+     * header, false will be returned and the data will be discarded.
+     * @param b - bytes to be appended
+     * @param off - the offset to extract data from
+     * @param len - the number of bytes to append.
+     * @return true if the data was appended correctly. Returns false if the package is incorrect, ie missing header or something, or the length of data is 0
+     */
+    public boolean append(byte[] b, int off, int len) {
+        if ((off < 0) || (off > b.length) || (len < 0) ||
+            ((off + len) > b.length) || ((off + len) < 0))  {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return false;
+        }
+
+        int newcount = bufSize + len;
+        if (newcount > buf.length) {
+            byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
+            System.arraycopy(buf, 0, newbuf, 0, bufSize);
+            buf = newbuf;
+        }
+        System.arraycopy(b, off, buf, bufSize, len);
+        bufSize = newcount;
+
+        if (bufSize > START_DATA.length && (firstIndexOf(buf,0,START_DATA)==-1)){
+            bufSize = 0;
+            log.error("Discarded the package, invalid header");
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * Internal mechanism to make a check if a complete package exists
+     * within the buffer
+     * @return - true if a complete package (header,size,data,footer) exists within the buffer
+     */
+    public int countPackages()
+    {
+        int cnt = 0;
+        int pos = START_DATA.length;
+        int start = 0;
+
+        while ( start < bufSize ) {
+            //first check start header
+            int index = XByteBuffer.firstIndexOf(buf,start,START_DATA);
+            //if the header (START_DATA) isn't the first thing or
+            //the buffer isn't even 10 bytes
+            if ( index != start || ((bufSize-start)<10) ) break;
+            //then get the size 4 bytes
+            int compress = toInt(buf, pos);
+            int size = toInt(buf, pos+4);
+            //now the total buffer has to be long enough to hold
+            //START_DATA.length+8+size+END_DATA.length
+            pos = start + START_DATA.length + 8 + size;
+            if ( (pos + END_DATA.length) > bufSize) break;
+            //and finally check the footer of the package END_DATA
+            int newpos = firstIndexOf(buf, pos, END_DATA);
+            //mismatch, there is no package
+            if (newpos != pos) break;
+            //increase the packet count
+            cnt++;
+            //reset the values
+            start = pos + END_DATA.length;
+            pos = start + START_DATA.length;
+        }
+        return cnt;
+    }
+
+    /**
+     * Method to check if a package exists in this byte buffer.
+     * @return - true if a complete package (header,size,data,footer) exists within the buffer
+     */
+    public boolean doesPackageExist()  {
+        return (countPackages()>0);
+    }
+
+    /**
+     * Extracts the message bytes from a package.
+     * If no package exists, a IllegalStateException will be thrown.
+     * @param clearFromBuffer - if true, the package will be removed from the byte buffer
+     * @return - returns the actual message bytes (header, size and footer not included).
+     */
+    public ClusterData extractPackage(boolean clearFromBuffer)
+            throws java.io.IOException {
+        int psize = countPackages();
+        if (psize == 0)
+            throw new java.lang.IllegalStateException(
+                    "No package exists in XByteBuffer");
+        int compress = toInt(buf, START_DATA.length);
+        int size = toInt(buf, START_DATA.length +4);
+        byte[] data = new byte[size];
+        System.arraycopy(buf, START_DATA.length + 8, data, 0, size);
+        ClusterData cdata = new ClusterData() ;
+        cdata.setMessage(data);
+        cdata.setCompress(compress);
+        if (clearFromBuffer) {
+            int totalsize = START_DATA.length + 8 + size + END_DATA.length;
+            bufSize = bufSize - totalsize;
+            System.arraycopy(buf, totalsize, buf, 0, bufSize);
+        }
+        //int size = toInt(buf, START_DATA.length);
+        //byte[] data = new byte[size];
+        //System.arraycopy(buf, START_DATA.length + 4, data, 0, size);
+        //if (clearFromBuffer) {
+        //    int totalsize = START_DATA.length + 4 + size + END_DATA.length;
+
+        return cdata;
+    }
+
+    /**
+     * Convert four bytes to an int
+     * @param b - the byte array containing the four bytes
+     * @param off - the offset
+     * @return the integer value constructed from the four bytes
+     * @exception java.lang.ArrayIndexOutOfBoundsException
+     */
+    public static int toInt(byte[] b,int off){
+        return ( ( (int) b[off+3]) & 0xFF) +
+            ( ( ( (int) b[off+2]) & 0xFF) << 8) +
+            ( ( ( (int) b[off+1]) & 0xFF) << 16) +
+            ( ( ( (int) b[off+0]) & 0xFF) << 24);
+    }
+
+    /**
+     * Convert eight bytes to a long
+     * @param b - the byte array containing the four bytes
+     * @param off - the offset
+     * @return the long value constructed from the eight bytes
+     * @exception java.lang.ArrayIndexOutOfBoundsException
+     */
+    public static long toLong(byte[] b,int off){
+        return ( ( (long) b[off+7]) & 0xFF) +
+            ( ( ( (long) b[off+6]) & 0xFF) << 8) +
+            ( ( ( (long) b[off+5]) & 0xFF) << 16) +
+            ( ( ( (long) b[off+4]) & 0xFF) << 24) +
+            ( ( ( (long) b[off+3]) & 0xFF) << 32) +
+            ( ( ( (long) b[off+2]) & 0xFF) << 40) +
+            ( ( ( (long) b[off+1]) & 0xFF) << 48) +
+            ( ( ( (long) b[off+0]) & 0xFF) << 56);
+    }
+
+    /**
+     * Converts an integer to four bytes
+     * @param n - the integer
+     * @return - four bytes in an array
+     */
+    public static byte[] toBytes(int n) {
+        byte[] b = new byte[4];
+        b[3] = (byte) (n);
+        n >>>= 8;
+        b[2] = (byte) (n);
+        n >>>= 8;
+        b[1] = (byte) (n);
+        n >>>= 8;
+        b[0] = (byte) (n);
+        return b;
+    }
+
+    /**
+     * Converts an long to eight bytes
+     * @param n - the long
+     * @return - eight bytes in an array
+     */
+    public static byte[] toBytes(long n) {
+        byte[] b = new byte[8];
+        b[7] = (byte) (n);
+        n >>>= 8;
+        b[6] = (byte) (n);
+        n >>>= 8;
+        b[5] = (byte) (n);
+        n >>>= 8;
+        b[4] = (byte) (n);
+        n >>>= 8;
+        b[3] = (byte) (n);
+        n >>>= 8;
+        b[2] = (byte) (n);
+        n >>>= 8;
+        b[1] = (byte) (n);
+        n >>>= 8;
+        b[0] = (byte) (n);
+        return b;
+    }
+
+    /**
+     * Similar to a String.IndexOf, but uses pure bytes
+     * @param src - the source bytes to be searched
+     * @param srcOff - offset on the source buffer
+     * @param find - the string to be found within src
+     * @return - the index of the first matching byte. -1 if the find array is not found
+     */
+    public static int firstIndexOf(byte[] src, int srcOff, byte[] find){
+        int result = -1;
+        if (find.length > src.length) return result;
+        if (find.length == 0 || src.length == 0) return result;
+        if (srcOff >= src.length ) throw new java.lang.ArrayIndexOutOfBoundsException();
+        boolean found = false;
+        int srclen = src.length;
+        int findlen = find.length;
+        byte first = find[0];
+        int pos = srcOff;
+        while (!found) {
+            //find the first byte
+            while (pos < srclen){
+                if (first == src[pos])
+                    break;
+                pos++;
+            }
+            if (pos >= srclen)
+                return -1;
+
+            //we found the first character
+            //match the rest of the bytes - they have to match
+            if ( (srclen - pos) < findlen)
+                return -1;
+            //assume it does exist
+            found = true;
+            for (int i = 1; ( (i < findlen) && found); i++)
+                found = found && (find[i] == src[pos + i]);
+            if (found)
+                result = pos;
+            else if ( (srclen - pos) < findlen)
+                return -1; //no more matches possible
+            else
+                pos++;
+        }
+        return result;
+    }
+
+    /**
+     * Creates a complete data package
+     * @param indata - the message data to be contained within the package
+     * @return - a full package (header,compress,size,data,footer)
+     * @deprecated since 5.5.10
+     */
+    public static byte[] createDataPackage(byte[] indata)
+            throws java.io.IOException {
+        byte[] data;
+        data = indata;
+        byte[] result = new byte[START_DATA.length + 8 + data.length
+                + END_DATA.length];
+        System.arraycopy(START_DATA, 0, result, 0, START_DATA.length);
+        System.arraycopy(toBytes(ClusterMessage.FLAG_FORBIDDEN), 0, result, START_DATA.length, 4);
+        System.arraycopy(toBytes(data.length), 0, result, START_DATA.length, 8);
+        System.arraycopy(data, 0, result, START_DATA.length + 8, data.length);
+        System.arraycopy(END_DATA, 0, result, START_DATA.length + 8
+                + data.length, END_DATA.length);
+
+        return result;
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/mbeans-descriptors.xml b/container/modules/cluster/src/share/org/apache/catalina/cluster/mbeans-descriptors.xml
new file mode 100644
index 0000000..3f5bf56
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/mbeans-descriptors.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean         name="SimpleTcpCluster"
+            className="org.apache.catalina.mbeans.ClassNameMBean"
+          description="Tcp Cluster implementation"
+               domain="Catalina"
+                group="Cluster"
+                 type="org.apache.catalina.cluster.tcp.SimpleTcpCluster">
+
+    <attribute   name="protocolStack"
+          description="JavaGroups protocol stack selection"
+                 type="java.lang.String"/>
+
+  </mbean>
+
+
+  <mbean         name="SimpleTcpReplicationManager"
+            className="org.apache.catalina.mbeans.ClassNameMBean"
+          description="Clustered implementation of the Manager interface"
+               domain="Catalina"
+                group="Manager"
+                 type="org.apache.catalina.cluster.tcp.SimpleTcpReplicationManager">
+
+    <attribute   name="algorithm"
+          description="The message digest algorithm to be used when generating
+                       session identifiers"
+                 type="java.lang.String"/>
+
+    <attribute   name="checkInterval"
+          description="The interval (in seconds) between checks for expired
+                       sessions"
+                 type="int"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="distributable"
+          description="The distributable flag for Sessions created by this
+                       Manager"
+                 type="boolean"/>
+
+    <attribute   name="entropy"
+          description="A String initialization parameter used to increase the
+                       entropy of the initialization of our random number
+                       generator"
+                 type="java.lang.String"/>
+
+    <attribute   name="managedResource"
+          description="The managed resource this MBean is associated with"
+                 type="java.lang.Object"/>
+
+    <attribute   name="maxActiveSessions"
+          description="The maximum number of active Sessions allowed, or -1
+                       for no limit"
+                 type="int"/>
+
+    <attribute   name="maxInactiveInterval"
+          description="The default maximum inactive interval for Sessions
+                       created by this Manager"
+                 type="int"/>
+
+    <attribute   name="name"
+          description="The descriptive name of this Manager implementation
+                       (for logging)"
+                 type="java.lang.String"
+            writeable="false"/>
+
+  </mbean>
+
+
+
+<mbean         name="ReplicationValve"
+            className="org.apache.catalina.mbeans.ClassNameMBean"
+          description="Valve for simple tcp replication"
+               domain="Catalina"
+                group="Valve"
+                 type="org.apache.catalina.cluster.tcp.ReplicationValve">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+  </mbean>
+
+
+</mbeans-descriptors>
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/Constants.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/Constants.java
new file mode 100644
index 0000000..4fa7e32
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/Constants.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.cluster.mcast;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.cluster.mcast</code>
+ * package.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.cluster.mcast";
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/LocalStrings.properties b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/LocalStrings.properties
new file mode 100644
index 0000000..b179189
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/LocalStrings.properties
@@ -0,0 +1 @@
+cluster.mbean.register.allready=MBean {0} allready registered!
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastMember.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastMember.java
new file mode 100644
index 0000000..f03f82a
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastMember.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.mcast;
+
+import org.apache.catalina.cluster.Member;
+import org.apache.catalina.cluster.io.XByteBuffer;
+
+/**
+ * A <b>membership</b> implementation using simple multicast.
+ * This is the representation of a multicast member.
+ * Carries the host, and port of the this or other cluster nodes.
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public class McastMember implements Member, java.io.Serializable {
+
+    /**
+     * Digits, used for "superfast" de-serialization of an
+     * IP address
+     */
+    final transient static char[] digits = {
+        '0', '1', '2', '3', '4', '5',
+        '6', '7', '8', '9'};
+
+    /**
+     * Public properties specific to this implementation
+     */
+    public static final transient String TCP_LISTEN_PORT = "tcpListenPort";
+    public static final transient String TCP_LISTEN_HOST = "tcpListenHost";
+    public static final transient String MEMBER_NAME = "memberName";
+    public static final transient String MEMBER_DOMAIN = "memberDomain";
+    
+    /**
+     * The listen host for this member
+     */
+    protected String host;
+    /**
+     * The tcp listen port for this member
+     */
+    protected int port;
+    /**
+     * The name for this member, has be be unique within the cluster.
+     */
+    private String name;
+
+    /**
+     * The name of the cluster domain from this node
+     */
+    private String domain;
+    
+    /**
+     * Counter for how many messages have been sent from this member
+     */
+    protected int msgCount = 0;
+    /**
+     * The number of milliseconds since this members was
+     * created, is kept track of using the start time
+     */
+    protected long memberAliveTime = 0;
+
+
+    /**
+     * Construct a new member object
+     * @param name - the name of this member, cluster unique
+     * @param domain - the cluster domain name of this member
+     * @param host - the tcp listen host
+     * @param port - the tcp listen port
+     */
+    public McastMember(String name,
+                       String domain,
+                       String host,
+                       int port,
+                       long aliveTime) {
+        this.host = host;
+        this.port = port;
+        this.name = name;
+        this.domain = domain;
+        this.memberAliveTime=aliveTime;
+    }
+
+    /**
+     *
+     * @return a Hashmap containing the following properties:<BR>
+     * 1. tcpListenPort - the port this member listens to for messages - string<BR>
+     * 2. tcpListenHost - the host address of this member - string<BR>
+     * 3. memberName    - the name of this member - string<BR>
+     */
+    public java.util.HashMap getMemberProperties() {
+        java.util.HashMap map = new java.util.HashMap(2);
+        map.put(McastMember.TCP_LISTEN_HOST,this.host);
+        map.put(McastMember.TCP_LISTEN_PORT,String.valueOf(this.port));
+        map.put(McastMember.MEMBER_NAME,name);
+        map.put(McastMember.MEMBER_DOMAIN,domain);
+        return map;
+    }
+
+    /**
+     * Increment the message count.
+     */
+    protected void inc() {
+        msgCount++;
+    }
+
+    /**
+     * Create a data package to send over the wire representing this member.
+     * This is faster than serialization.
+     * @return - the bytes for this member deserialized
+     * @throws Exception
+     */
+    protected byte[] getData(long startTime) throws Exception {
+        //package looks like
+        //alive - 8 bytes
+        //port - 4 bytes
+        //host - 4 bytes
+        //nlen - 4 bytes
+        //name - nlen bytes
+        //dlen - 4 bytes
+        //domain - dlen bytes
+        byte[] named = getName().getBytes();
+        byte[] domaind = getDomain().getBytes();
+        byte[] addr = java.net.InetAddress.getByName(host).getAddress();
+        byte[] data = new byte[8+4+addr.length+4+named.length+4+domaind.length];
+        long alive=System.currentTimeMillis()-startTime;
+        System.arraycopy(XByteBuffer.toBytes((long)alive),0,data,0,8);
+        System.arraycopy(XByteBuffer.toBytes(port),0,data,8,4);
+        System.arraycopy(addr,0,data,12,addr.length);
+        System.arraycopy(XByteBuffer.toBytes(named.length),0,data,16,4);
+        System.arraycopy(named,0,data,20,named.length);
+        System.arraycopy(XByteBuffer.toBytes(domaind.length),0,data,named.length+20,4);
+        System.arraycopy(domaind,0,data,named.length+24,domaind.length);
+        return data;
+    }
+    /**
+     * Deserializes a member from data sent over the wire
+     * @param data - the bytes received
+     * @return a member object.
+     */
+    protected static McastMember getMember(byte[] data) {
+       //package looks like
+       //alive - 8 bytes
+       //port - 4 bytes
+       //host - 4 bytes
+       //nlen - 4 bytes
+       //name - nlen bytes
+       //dlen - 4 bytes
+       //domain - dlen bytes
+       byte[] alived = new byte[8];
+       System.arraycopy(data, 0, alived, 0, 8);
+       byte[] portd = new byte[4];
+       System.arraycopy(data, 8, portd, 0, 4);
+       byte[] addr = new byte[4];
+       System.arraycopy(data, 12, addr, 0, 4);
+       //FIXME control the nlen
+       byte[] nlend = new byte[4];
+       System.arraycopy(data, 16, nlend, 0, 4);
+       int nlen = XByteBuffer.toInt(nlend, 0);
+       byte[] named = new byte[nlen];
+       System.arraycopy(data, 20, named, 0, named.length);
+       //FIXME control the dlen
+       byte[] dlend = new byte[4];
+       System.arraycopy(data, nlen + 20, dlend, 0, 4);
+       int dlen = XByteBuffer.toInt(dlend, 0);
+       byte[] domaind = new byte[dlen];
+       System.arraycopy(data, nlen + 24, domaind, 0, domaind.length);
+       return new McastMember(new String(named),
+                              new String(domaind),
+                              addressToString(addr),
+                              XByteBuffer.toInt(portd, 0),
+                              XByteBuffer.toLong(alived, 0));
+    }
+
+    /**
+     * Return the name of this object
+     * @return a unique name to the cluster
+     */
+    public String getName() {
+        return name;
+    }
+    
+    /**
+     * Return the domain of this object
+     * @return a cluster domain to the cluster
+     */
+    public String getDomain() {
+        return domain;
+    }
+    
+    /**
+     * Return the listen port of this member
+     * @return - tcp listen port
+     */
+    public int getPort()  {
+        return this.port;
+    }
+
+    /**
+     * Return the TCP listen host for this member
+     * @return IP address or host name
+     */
+    public String getHost()  {
+        return this.host;
+    }
+
+    /**
+     * Contains information on how long this member has been online.
+     * The result is the number of milli seconds this member has been
+     * broadcasting its membership to the cluster.
+     * @return nr of milliseconds since this member started.
+     */
+    public long getMemberAliveTime() {
+       return memberAliveTime;
+    }
+
+    public void setMemberAliveTime(long time) {
+       memberAliveTime=time;
+    }
+
+
+
+    /**
+     * String representation of this object
+     */
+    public String toString()  {
+        return "org.apache.catalina.cluster.mcast.McastMember["+name+","+domain+","+host+","+port+", alive="+memberAliveTime+"]";
+    }
+
+    /**
+     * @see java.lang.Object#hashCode()
+     * @return The hash code
+     */
+    public int hashCode() {
+        return this.name.hashCode();
+    }
+
+    /**
+     * Returns true if the param o is a McastMember with the same name
+     * @param o
+     */
+    public boolean equals(Object o) {
+        if ( o instanceof McastMember )    {
+            return this.name.equals(((McastMember)o).getName());
+        }
+        else
+            return false;
+    }
+
+    /**
+     * Converts for bytes (ip address) to a string representation of it<BR>
+     * Highly optimized method.
+     * @param address (4 bytes ip address)
+     * @return string representation of that ip address
+     */
+    private static final String addressToString(byte[] address) {
+        int q, r = 0;
+        int charPos = 15;
+        char[] buf = new char[15];
+        char dot = '.';
+
+        int i = address[3] & 0xFF;
+        for (; ; )
+        {
+            q = (i * 52429) >>> (19);
+            r = i - ( (q << 3) + (q << 1));
+            buf[--charPos] = digits[r];
+            i = q;
+            if (i == 0)
+                break;
+        }
+        buf[--charPos] = dot;
+        i = address[2] & 0xFF;
+        for (; ; )
+        {
+            q = (i * 52429) >>> (19);
+            r = i - ( (q << 3) + (q << 1));
+            buf[--charPos] = digits[r];
+            i = q;
+            if (i == 0)
+                break;
+        }
+        buf[--charPos] = dot;
+
+        i = address[1] & 0xFF;
+        for (; ; )
+        {
+            q = (i * 52429) >>> (19);
+            r = i - ( (q << 3) + (q << 1));
+            buf[--charPos] = digits[r];
+            i = q;
+            if (i == 0)
+                break;
+        }
+
+        buf[--charPos] = dot;
+        i = address[0] & 0xFF;
+
+        for (; ; )
+        {
+            q = (i * 52429) >>> (19);
+            r = i - ( (q << 3) + (q << 1));
+            buf[--charPos] = digits[r];
+            i = q;
+            if (i == 0)
+                break;
+        }
+        return new String(buf, charPos, 15 - charPos);
+    }
+    public void setHost(String host) {
+        this.host = host;
+    }
+    public void setMsgCount(int msgCount) {
+        this.msgCount = msgCount;
+    }
+    public void setName(String name) {
+        this.name = name;
+    }
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    public void setPort(int port) {
+        this.port = port;
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastMembership.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastMembership.java
new file mode 100644
index 0000000..2c1eb51
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastMembership.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.mcast;
+
+
+import java.util.HashMap;
+/**
+ * A <b>membership</b> implementation using simple multicast.
+ * This is the representation of a multicast membership.
+ * This class is responsible for maintaining a list of active cluster nodes in the cluster.
+ * If a node fails to send out a heartbeat, the node will be dismissed.
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+
+public class McastMembership
+{
+    /**
+     * The name of this membership, has to be the same as the name for the local
+     * member
+     */
+    protected String name;
+    /**
+     * A map of all the members in the cluster.
+     */
+    protected HashMap map = new java.util.HashMap();
+
+    /**
+     * Constructs a new membership
+     * @param myName - has to be the name of the local member. Used to filter the local member from the cluster membership
+     */
+    public McastMembership(String myName) {
+        name = myName;
+    }
+
+    /**
+     * Reset the membership and start over fresh.
+     * Ie, delete all the members and wait for them to ping again and join this membership
+     */
+    public synchronized void reset() {
+        map.clear();
+    }
+
+    /**
+     * Notify the membership that this member has announced itself.
+     *
+     * @param m - the member that just pinged us
+     * @return - true if this member is new to the cluster, false otherwise.
+     * @return - false if this member is the local member.
+     */
+    public synchronized boolean memberAlive(McastMember m) {
+        boolean result = false;
+        //ignore ourselves
+        if ( m.getName().equals(name) ) return result;
+
+        //return true if the membership has changed
+        MbrEntry entry = (MbrEntry)map.get(m.getName());
+        if ( entry == null ) {
+            entry = new MbrEntry(m);
+            map.put(m.getName(),entry);
+            result = true;
+        } else {
+            //update the member alive time
+            entry.getMember().setMemberAliveTime(m.getMemberAliveTime());
+        }//end if
+        entry.accessed();
+        return result;
+    }
+
+    /**
+     * Runs a refresh cycle and returns a list of members that has expired.
+     * This also removes the members from the membership, in such a way that
+     * getMembers() = getMembers() - expire()
+     * @param maxtime - the max time a member can remain unannounced before it is considered dead.
+     * @return the list of expired members
+     */
+    public synchronized McastMember[] expire(long maxtime) {
+        MbrEntry[] members = getMemberEntries();
+        java.util.ArrayList list = new java.util.ArrayList();
+        for (int i=0; i<members.length; i++) {
+            MbrEntry entry = members[i];
+            if ( entry.hasExpired(maxtime) ) {
+                list.add(entry.getMember());
+            }//end if
+        }//while
+        McastMember[] result = new McastMember[list.size()];
+        list.toArray(result);
+        for ( int j=0; j<result.length; j++) map.remove(result[j].getName());
+        return result;
+
+    }//expire
+
+    /**
+     * Returning a list of all the members in the membership
+     */
+    public synchronized McastMember[] getMembers() {
+        McastMember[] result = new McastMember[map.size()];
+        java.util.Iterator i = map.entrySet().iterator();
+        int pos = 0;
+        while ( i.hasNext() )
+            result[pos++] = ((MbrEntry)((java.util.Map.Entry)i.next()).getValue()).getMember();
+        return result;
+    }
+
+    protected synchronized MbrEntry[] getMemberEntries()
+    {
+        MbrEntry[] result = new MbrEntry[map.size()];
+        java.util.Iterator i = map.entrySet().iterator();
+        int pos = 0;
+        while ( i.hasNext() )
+            result[pos++] = ((MbrEntry)((java.util.Map.Entry)i.next()).getValue());
+        return result;
+    }
+
+
+    /**
+     * Inner class that represents a member entry
+     */
+    protected static class MbrEntry
+    {
+
+        protected McastMember mbr;
+        protected long lastHeardFrom;
+        public MbrEntry(McastMember mbr) {
+            this.mbr = mbr;
+        }
+        /**
+         * Indicate that this member has been accessed.
+         */
+        public void accessed(){
+            lastHeardFrom = System.currentTimeMillis();
+        }
+        /**
+         * Return the actual McastMember object
+         */
+        public McastMember getMember() {
+            return mbr;
+        }
+
+        /**
+         * Check if this dude has expired
+         * @param maxtime The time threshold
+         */
+        public boolean hasExpired(long maxtime) {
+            long delta = System.currentTimeMillis() - lastHeardFrom;
+            return delta > maxtime;
+        }
+    }//MbrEntry
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastService.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastService.java
new file mode 100644
index 0000000..518bba8
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastService.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.mcast;
+
+import java.util.Properties;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.Container;
+import org.apache.catalina.cluster.Member;
+import org.apache.catalina.cluster.MembershipListener;
+import org.apache.catalina.cluster.MembershipService;
+import org.apache.catalina.cluster.tcp.SimpleTcpCluster;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.modeler.Registry;
+
+/**
+ * A <b>membership</b> implementation using simple multicast.
+ * This is the representation of a multicast membership service.
+ * This class is responsible for maintaining a list of active cluster nodes in the cluster.
+ * If a node fails to send out a heartbeat, the node will be dismissed.
+ *
+ * FIXME i18n messages
+ * 
+ * @author Peter Rossbach
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+
+public class McastService implements MembershipService,MembershipListener {
+
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( McastService.class );
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "McastService/2.0";
+
+    /**
+     * The implementation specific properties
+     */
+    protected Properties properties = new Properties();
+    /**
+     * A handle to the actual low level implementation
+     */
+    protected McastServiceImpl impl;
+    /**
+     * A membership listener delegate (should be the cluster :)
+     */
+    protected MembershipListener listener;
+    /**
+     * The local member
+     */
+    protected McastMember localMember ;
+    private int mcastSoTimeout;
+    private int mcastTTL;
+
+    /**
+     * my cluster
+     */
+    private SimpleTcpCluster cluster;
+
+    /**
+     * Transmitter Mbean name
+     */
+    private ObjectName objectName;
+
+    private Registry registry;
+
+    /**
+     * Create a membership service.
+     */
+    public McastService() {
+        properties.setProperty("mcastClusterDomain", "catalina");
+    }
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+    
+    /**
+     * Transmitter ObjectName
+     * 
+     * @param name
+     */
+    public void setObjectName(ObjectName name) {
+        objectName = name;
+    }
+
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+
+    /**
+     *
+     * @param properties
+     * <BR/>All are required<BR />
+     * 1. mcastPort - the port to listen to<BR>
+     * 2. mcastAddress - the mcast group address<BR>
+     * 3. mcastClusterDomain - the mcast cluster domain<BR>
+     * 4. bindAddress - the bind address if any - only one that can be null<BR>
+     * 5. memberDropTime - the time a member is gone before it is considered gone.<BR>
+     * 6. msgFrequency - the frequency of sending messages<BR>
+     * 7. tcpListenPort - the port this member listens to<BR>
+     * 8. tcpListenHost - the bind address of this member<BR>
+     * @exception java.lang.IllegalArgumentException if a property is missing.
+     */
+    public void setProperties(Properties properties) {
+        hasProperty(properties,"mcastPort");
+        hasProperty(properties,"mcastAddress");
+        hasProperty(properties,"mcastClusterDomain");
+        hasProperty(properties,"memberDropTime");
+        hasProperty(properties,"msgFrequency");
+        hasProperty(properties,"tcpListenPort");
+        hasProperty(properties,"tcpListenHost");
+        this.properties = properties;
+    }
+
+    /**
+     * Return the properties, see setProperties
+     */
+    public Properties getProperties() {
+        return properties;
+    }
+
+    /*
+     * configured in cluster
+     * 
+     * @see org.apache.catalina.cluster.ClusterSender#setCatalinaCluster(org.apache.catalina.cluster.tcp.SimpleTcpCluster)
+     */
+    public void setCatalinaCluster(SimpleTcpCluster cluster) {
+        this.cluster = cluster;
+
+    }
+
+    /**
+     * Return the cluster for this membership service
+     */
+    public Cluster getCatalinaCluster() {
+        return cluster ;
+    }
+
+    /**
+     * Return the local member name
+     */
+    public String getLocalMemberName() {
+        return localMember.toString() ;
+    }
+ 
+    /**
+     * Return the local member
+     */
+    public Member getLocalMember() {
+        localMember.setMemberAliveTime(System.currentTimeMillis()-impl.getServiceStartTime());
+        return localMember;
+    }
+    
+    /**
+     * Sets the local member properties for broadcasting
+     */
+    public void setLocalMemberProperties(String listenHost, int listenPort) {
+        properties.setProperty("tcpListenHost",listenHost);
+        properties.setProperty("tcpListenPort",String.valueOf(listenPort));
+    }
+    
+    public void setMcastAddr(String addr) {
+        properties.setProperty("mcastAddress", addr);
+    }
+
+    public String getMcastAddr() {
+        return properties.getProperty("mcastAddress");
+    }
+
+    public void setMcastBindAddress(String bindaddr) {
+        properties.setProperty("mcastBindAddress", bindaddr);
+    }
+
+    public String getMcastBindAddress() {
+        return properties.getProperty("mcastBindAddress");
+    }
+
+    public void setMcastClusterDomain(String clusterDomain) {
+        properties.setProperty("mcastClusterDomain", clusterDomain);
+    }
+
+    public String getMcastClusterDomain() {
+        return properties.getProperty("mcastClusterDomain");
+    }
+
+    public void setMcastPort(int port) {
+        properties.setProperty("mcastPort", String.valueOf(port));
+    }
+
+    public int getMcastPort() {
+        String p = properties.getProperty("mcastPort");
+        return new Integer(p).intValue();
+    }
+    
+    public void setMcastFrequency(long time) {
+        properties.setProperty("msgFrequency", String.valueOf(time));
+    }
+
+    public long getMcastFrequency() {
+        String p = properties.getProperty("msgFrequency");
+        return new Long(p).longValue();
+    }
+
+    public void setMcastDropTime(long time) {
+        properties.setProperty("memberDropTime", String.valueOf(time));
+    }
+
+    public long getMcastDropTime() {
+        String p = properties.getProperty("memberDropTime");
+        return new Long(p).longValue();
+    }
+
+    /**
+     * Check if a required property is available.
+     * @param properties The set of properties
+     * @param name The property to check for
+     */
+    protected void hasProperty(Properties properties, String name){
+        if ( properties.getProperty(name)==null) throw new IllegalArgumentException("Required property \""+name+"\" is missing.");
+    }
+
+    /**
+     * Start broadcasting and listening to membership pings
+     * @throws java.lang.Exception if a IO error occurs
+     */
+    public void start() throws java.lang.Exception {
+        start(1);
+        start(2);
+        registerMBean();
+    }
+    
+    public void start(int level) throws java.lang.Exception {
+        if ( impl != null ) {
+            impl.start(level);
+            return;
+        }
+        String host = getProperties().getProperty("tcpListenHost");
+        String domain = getProperties().getProperty("mcastClusterDomain");
+        int port = Integer.parseInt(getProperties().getProperty("tcpListenPort"));
+        String name = "tcp://"+host+":"+port;
+        if ( localMember == null ) {
+            localMember = new McastMember(name, domain, host, port, 100);
+        } else {
+            localMember.setName(name);
+            localMember.setDomain(domain);
+            localMember.setHost(host);
+            localMember.setPort(port);
+            localMember.setMemberAliveTime(100);
+        }
+        java.net.InetAddress bind = null;
+        if ( properties.getProperty("mcastBindAddress")!= null ) {
+            bind = java.net.InetAddress.getByName(properties.getProperty("mcastBindAddress"));
+        }
+        int ttl = -1;
+        int soTimeout = -1;
+        if ( properties.getProperty("mcastTTL") != null ) {
+            try {
+                ttl = Integer.parseInt(properties.getProperty("mcastTTL"));
+            } catch ( Exception x ) {
+                log.error("Unable to parse mcastTTL="+properties.getProperty("mcastTTL"),x);
+            }
+        }
+        if ( properties.getProperty("mcastSoTimeout") != null ) {
+            try {
+                soTimeout = Integer.parseInt(properties.getProperty("mcastSoTimeout"));
+            } catch ( Exception x ) {
+                log.error("Unable to parse mcastSoTimeout="+properties.getProperty("mcastSoTimeout"),x);
+            }
+        }
+
+        impl = new McastServiceImpl((McastMember)localMember,Long.parseLong(properties.getProperty("msgFrequency")),
+                                    Long.parseLong(properties.getProperty("memberDropTime")),
+                                    Integer.parseInt(properties.getProperty("mcastPort")),
+                                    bind,
+                                    java.net.InetAddress.getByName(properties.getProperty("mcastAddress")),
+                                    ttl,
+                                    soTimeout,
+                                    this);
+
+        impl.start(level);
+		long memberwait = (Long.parseLong(properties.getProperty("msgFrequency"))*4);
+        if(log.isInfoEnabled())
+            log.info("Sleeping for "+memberwait+" secs to establish cluster membership");
+        Thread.sleep(memberwait);
+
+    }
+
+ 
+    /**
+     * Stop broadcasting and listening to membership pings
+     */
+    public void stop() {
+        try  {
+            if ( impl != null) impl.stop();
+        } catch ( Exception x)  {
+            log.error("Unable to stop the mcast service.",x);
+        }
+        impl = null;
+        unregisterMBean();
+    }
+
+    /**
+     * register mbean descriptor for package mcast 
+     * @throws Exception
+     */
+    protected void initMBeans() throws Exception {
+      if(registry == null) {
+        registry = Registry.getRegistry(null, null);
+        registry.loadMetadata(this.getClass().getResourceAsStream(
+            "mbeans-descriptors.xml"));
+      }
+    }
+    
+    /**
+     * register MBeans for Membership
+     *
+     */
+    protected void registerMBean() {
+        if (cluster != null) {
+            ObjectName clusterName = cluster.getObjectName();
+            try {
+                MBeanServer mserver = cluster.getMBeanServer();
+                initMBeans();
+                Container container = cluster.getContainer();
+                String name = clusterName.getDomain() + ":type=ClusterMembership";
+                if (container instanceof StandardHost) {
+                    name += ",host=" + clusterName.getKeyProperty("host");
+                }
+                ObjectName mcastName = new ObjectName(name);
+                if (mserver.isRegistered(mcastName)) {
+                    if (log.isWarnEnabled())
+                        log.warn(sm.getString(
+                                "cluster.mbean.register.allready", mcastName));
+                    return;
+                }
+                setObjectName(mcastName);
+                mserver.registerMBean(cluster.getManagedBean(this),
+                        getObjectName());
+                if (log.isInfoEnabled())
+                    log.info("membership mbean registered (" + mcastName + ")");
+            } catch (Exception e) {
+                log.warn("membership mbean creation failed (" + clusterName + ")" ,e);
+            }
+        }
+    }
+
+    /**
+     * unregister MBeans for Membership
+     *
+     */
+    protected void unregisterMBean() {
+        if (cluster != null && getObjectName() != null) {
+            try {
+                MBeanServer mserver = cluster.getMBeanServer();
+                mserver.unregisterMBean(getObjectName());
+            } catch (Exception e) {
+                log.error(e);
+            }
+        }
+    }
+
+    /**
+     * Return all the members by name
+     */
+    public String[] getMembersByName() {
+        Member[] currentMembers = getMembers();
+        String [] membernames ;
+        if(currentMembers != null) {
+            membernames = new String[currentMembers.length];
+            for (int i = 0; i < currentMembers.length; i++) {
+                membernames[i] = currentMembers[i].toString() ;
+            }
+        } else
+            membernames = new String[0] ;
+        return membernames ;
+    }
+ 
+    /**
+     * Return the member by name
+     */
+    public Member findMemberByName(String name) {
+        Member[] currentMembers = getMembers();
+        for (int i = 0; i < currentMembers.length; i++) {
+            if (name.equals(currentMembers[i].toString()))
+                return currentMembers[i];
+        }
+        return null;
+    }
+ 
+    /**
+     * Return all the members
+     */
+    public Member[] getMembers() {
+        if ( impl == null || impl.membership == null ) return null;
+        return impl.membership.getMembers();
+    }
+    /**
+     * Add a membership listener, this version only supports one listener per service,
+     * so calling this method twice will result in only the second listener being active.
+     * @param listener The listener
+     */
+    public void addMembershipListener(MembershipListener listener) {
+        this.listener = listener;
+    }
+    /**
+     * Remove the membership listener
+     */
+    public void removeMembershipListener(){
+        listener = null;
+    }
+
+    public void memberAdded(Member member) {
+        if ( listener!=null ) listener.memberAdded(member);
+    }
+
+    /**
+     * Callback from the impl when a new member has been received
+     * @param member The member
+     */
+    public void memberDisappeared(Member member)
+    {
+        if ( listener!=null ) listener.memberDisappeared(member);
+    }
+
+    public int getMcastSoTimeout() {
+        return mcastSoTimeout;
+    }
+    public void setMcastSoTimeout(int mcastSoTimeout) {
+        this.mcastSoTimeout = mcastSoTimeout;
+        properties.setProperty("mcastSoTimeout", String.valueOf(mcastSoTimeout));
+    }
+    public int getMcastTTL() {
+        return mcastTTL;
+    }
+    public void setMcastTTL(int mcastTTL) {
+        this.mcastTTL = mcastTTL;
+        properties.setProperty("mcastTTL", String.valueOf(mcastTTL));
+    }
+
+    /**
+     * Simple test program
+     * @param args Command-line arguments
+     * @throws Exception If an error occurs
+     */
+    public static void main(String args[]) throws Exception {
+		if(log.isInfoEnabled())
+            log.info("Usage McastService hostname tcpport");
+        McastService service = new McastService();
+        java.util.Properties p = new java.util.Properties();
+        p.setProperty("mcastPort","5555");
+        p.setProperty("mcastAddress","224.10.10.10");
+        p.setProperty("mcastClusterDomain","catalina");
+        p.setProperty("bindAddress","localhost");
+        p.setProperty("memberDropTime","3000");
+        p.setProperty("msgFrequency","500");
+        p.setProperty("tcpListenPort",args[1]);
+        p.setProperty("tcpListenHost",args[0]);
+        service.setProperties(p);
+        service.start();
+        Thread.sleep(60*1000*60);
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastServiceImpl.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastServiceImpl.java
new file mode 100644
index 0000000..93e64a3
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/McastServiceImpl.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.mcast;
+
+/**
+ * A <b>membership</b> implementation using simple multicast.
+ * This is the representation of a multicast membership service.
+ * This class is responsible for maintaining a list of active cluster nodes in the cluster.
+ * If a node fails to send out a heartbeat, the node will be dismissed.
+ * This is the low level implementation that handles the multicasting sockets.
+ * Need to fix this, could use java.nio and only need one thread to send and receive, or
+ * just use a timeout on the receive
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+import java.net.MulticastSocket;
+import java.io.IOException;
+import java.net.InetAddress ;
+import java.net.DatagramPacket;
+import org.apache.catalina.cluster.MembershipListener;
+public class McastServiceImpl
+{
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( McastService.class );
+    /**
+     * Internal flag used for the listen thread that listens to the multicasting socket.
+     */
+    protected boolean doRun = false;
+    /**
+     * Socket that we intend to listen to
+     */
+    protected MulticastSocket socket;
+    /**
+     * The local member that we intend to broad cast over and over again
+     */
+    protected McastMember member;
+    /**
+     * The multicast address
+     */
+    protected InetAddress address;
+    /**
+     * The multicast port
+     */
+    protected int port;
+    /**
+     * The time it takes for a member to expire.
+     */
+    protected long timeToExpiration;
+    /**
+     * How often to we send out a broadcast saying we are alive, must be smaller than timeToExpiration
+     */
+    protected long sendFrequency;
+    /**
+     * Reuse the sendPacket, no need to create a new one everytime
+     */
+    protected DatagramPacket sendPacket;
+    /**
+     * Reuse the receivePacket, no need to create a new one everytime
+     */
+    protected DatagramPacket receivePacket;
+    /**
+     * The membership, used so that we calculate memberships when they arrive or don't arrive
+     */
+    protected McastMembership membership;
+    /**
+     * The actual listener, for callback when shits goes down
+     */
+    protected MembershipListener service;
+    /**
+     * Thread to listen for pings
+     */
+    protected ReceiverThread receiver;
+    /**
+     * Thread to send pings
+     */
+    protected SenderThread sender;
+
+    /**
+     * When was the service started
+     */
+    protected long serviceStartTime = System.currentTimeMillis();
+    
+    protected int mcastTTL = -1;
+    protected int mcastSoTimeout = -1;
+    protected InetAddress mcastBindAddress = null;
+
+    /**
+     * Create a new mcast service impl
+     * @param member - the local member
+     * @param sendFrequency - the time (ms) in between pings sent out
+     * @param expireTime - the time (ms) for a member to expire
+     * @param port - the mcast port
+     * @param bind - the bind address (not sure this is used yet)
+     * @param mcastAddress - the mcast address
+     * @param service - the callback service
+     * @throws IOException
+     */
+    public McastServiceImpl(
+        McastMember member,
+        long sendFrequency,
+        long expireTime,
+        int port,
+        InetAddress bind,
+        InetAddress mcastAddress,
+        int ttl,
+        int soTimeout,
+        MembershipListener service)
+    throws IOException {
+        this.member = member;
+        address = mcastAddress;
+        this.port = port;
+        this.mcastSoTimeout = soTimeout;
+        this.mcastTTL = ttl;
+        this.mcastBindAddress = bind;
+        setupSocket();
+        sendPacket = new DatagramPacket(new byte[1000],1000);
+        sendPacket.setAddress(address);
+        sendPacket.setPort(port);
+        receivePacket = new DatagramPacket(new byte[1000],1000);
+        receivePacket.setAddress(address);
+        receivePacket.setPort(port);
+        membership = new McastMembership(member.getName());
+        timeToExpiration = expireTime;
+        this.service = service;
+        this.sendFrequency = sendFrequency;
+    }
+    
+    protected void setupSocket() throws IOException {
+        if (mcastBindAddress != null) socket = new MulticastSocket(new java.net.
+            InetSocketAddress(mcastBindAddress, port));
+        else socket = new MulticastSocket(port);
+        if (mcastBindAddress != null) {
+			if(log.isInfoEnabled())
+                log.info("Setting multihome multicast interface to:" +
+                         mcastBindAddress);
+            socket.setInterface(mcastBindAddress);
+        } //end if
+        if ( mcastSoTimeout >= 0 ) {
+ 			if(log.isInfoEnabled())
+                log.info("Setting cluster mcast soTimeout to "+mcastSoTimeout);
+            socket.setSoTimeout(mcastSoTimeout);
+        }
+        if ( mcastTTL >= 0 ) {
+			if(log.isInfoEnabled())
+                log.info("Setting cluster mcast TTL to " + mcastTTL);
+            socket.setTimeToLive(mcastTTL);
+        }
+    }
+
+    /**
+     * Start the service
+     * @param level 1 starts the receiver, level 2 starts the sender
+     * @throws IOException if the service fails to start
+     * @throws IllegalStateException if the service is already started
+     */
+    public synchronized void start(int level) throws IOException {
+        if ( sender != null && receiver != null ) throw new IllegalStateException("Service already running.");
+        if ( level == 1 ) {
+            socket.joinGroup(address);
+            doRun = true;
+            receiver = new ReceiverThread();
+            receiver.setDaemon(true);
+            receiver.start();
+        }
+        if ( level==2 ) {
+            serviceStartTime = System.currentTimeMillis();
+            sender = new SenderThread(sendFrequency);
+            sender.setDaemon(true);
+            sender.start();
+            
+        }
+    }
+
+    /**
+     * Stops the service
+     * @throws IOException if the service fails to disconnect from the sockets
+     */
+    public synchronized void stop() throws IOException {
+        socket.leaveGroup(address);
+        doRun = false;
+        sender = null;
+        receiver = null;
+        serviceStartTime = Long.MAX_VALUE;
+    }
+
+    /**
+     * Receive a datagram packet, locking wait
+     * @throws IOException
+     */
+    public void receive() throws IOException {
+        socket.receive(receivePacket);
+        byte[] data = new byte[receivePacket.getLength()];
+        System.arraycopy(receivePacket.getData(),receivePacket.getOffset(),data,0,data.length);
+        McastMember m = McastMember.getMember(data);
+        if(log.isDebugEnabled())
+            log.debug("Mcast receive ping from member " + m);
+        if ( membership.memberAlive(m) ) {
+            if(log.isDebugEnabled())
+                log.debug("Mcast add member " + m);
+            service.memberAdded(m);
+        }
+        McastMember[] expired = membership.expire(timeToExpiration);
+        for ( int i=0; i<expired.length; i++) {
+            if(log.isDebugEnabled())
+                log.debug("Mcast exipre  member " + m);
+            service.memberDisappeared(expired[i]);
+        }
+    }
+
+    /**
+     * Send a ping
+     * @throws Exception
+     */
+    public void send() throws Exception{
+        member.inc();
+        if(log.isDebugEnabled())
+            log.debug("Mcast send ping from member " + member);
+        byte[] data = member.getData(this.serviceStartTime);
+        DatagramPacket p = new DatagramPacket(data,data.length);
+        p.setAddress(address);
+        p.setPort(port);
+        socket.send(p);
+    }
+
+    public long getServiceStartTime() {
+       return this.serviceStartTime;
+    }
+
+
+    public class ReceiverThread extends Thread {
+        public ReceiverThread() {
+            super();
+            setName("Cluster-MembershipReceiver");
+        }
+        public void run() {
+            while ( doRun ) {
+                try {
+                    receive();
+                } catch ( Exception x ) {
+                    log.warn("Error receiving mcast package. Sleeping 500ms",x);
+                    try { Thread.sleep(500); } catch ( Exception ignore ){}
+                    
+                }
+            }
+        }
+    }//class ReceiverThread
+
+    public class SenderThread extends Thread {
+        long time;
+        public SenderThread(long time) {
+            this.time = time;
+            setName("Cluster-MembershipSender");
+
+        }
+        public void run() {
+            while ( doRun ) {
+                try {
+                    send();
+                } catch ( Exception x ) {
+                    log.warn("Unable to send mcast message.",x);
+                }
+                try { Thread.sleep(time); } catch ( Exception ignore ) {}
+            }
+        }
+    }//class SenderThread
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/mbeans-descriptors.xml b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/mbeans-descriptors.xml
new file mode 100644
index 0000000..d6e3fc4
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/mcast/mbeans-descriptors.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mbeans-descriptors PUBLIC
+   "-//Apache Software Foundation//DTD Model MBeans Configuration File"
+   "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
+<mbeans-descriptors>
+
+  <mbean         name="McastService"
+           description="Cluster Membership service implementation"
+               domain="Catalina"
+                group="Cluster"
+                 type="org.apache.catalina.cluster.mcast.McastService">
+    <attribute   name="info"
+          description="Class version info"
+                 type="java.lang.String"
+                 writeable="false"/>
+    <attribute   name="mcastAddr"
+          description="Multicast IP Address"
+                 type="java.lang.String"/>
+    <attribute   name="mcastBindAddress"
+          description="Multicast IP Interface address (default auto)"
+                 type="java.lang.String"/>
+    <attribute   name="mcastPort"
+          description="Multicast UDP Port"
+                 type="int"/>
+    <attribute   name="mcastFrequency"
+          description="Ping Frequency at msec"
+                 type="long"/>
+    <attribute   name="mcastClusterDomain"
+          description="Cluster Domain of this member"
+                 type="java.lang.String"/>
+    <attribute   name="mcastDropTime"
+          description="Timeout from frequency ping after member disapper notify"
+                 type="long"/>
+    <attribute   name="mcastSoTimeout"
+          description="Multicast Socket Timeout"
+                 type="int"/>
+    <attribute   name="mcastTTL"
+          description=""
+                 type="int"/>
+    <attribute   name="localMemberName"
+          description="Complete local receiver information"
+                 type="java.lang.String"
+                 writeable="false"/>
+    <attribute   name="membersByName"
+          description="Complete remote sender information"
+                 type="[Ljava.lang.String;"
+                 writeable="false"/>
+
+    <operation   name="start"
+               description="Start the cluster membership"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="stop"
+               description="Stop the cluster membership"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+                 
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/package.html b/container/modules/cluster/src/share/org/apache/catalina/cluster/package.html
new file mode 100644
index 0000000..ce83a0d
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/package.html
@@ -0,0 +1,11 @@
+<body>
+
+<p>This package contains code for Clustering, the base class
+of a Cluster is <code>org.apache.catalina.Cluster</code> implementations
+of this class is done when implementing a new Cluster protocol</p>
+
+<p>The only Cluster protocol currently implemented is a JavaGroups based<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<b>JGCluster.java</b>
+</p>
+
+</body>
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ClusterListener.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ClusterListener.java
new file mode 100644
index 0000000..85c62ee
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ClusterListener.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.session;
+
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.MessageListener;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * Receive SessionID cluster change from other backup node after primary session
+ * node is failed.
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public abstract class ClusterListener implements MessageListener {
+
+    public static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(ClusterListener.class);
+
+
+    //--Instance Variables--------------------------------------
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+    protected CatalinaCluster cluster = null;
+
+    //--Constructor---------------------------------------------
+
+    public ClusterListener() {
+    }
+    
+    //--Instance Getters/Setters--------------------------------
+    
+    public CatalinaCluster getCluster() {
+        return cluster;
+    }
+
+    public void setCluster(CatalinaCluster cluster) {
+        if (log.isDebugEnabled()) {
+            if (cluster != null)
+                log.debug("add ClusterListener " + this.toString()
+                        + " to cluster" + cluster);
+            else
+                log.debug("remove ClusterListener " + this.toString()
+                        + " from cluster");
+        }
+        this.cluster = cluster;
+    }
+
+    public boolean equals(Object listener) {
+        return super.equals(listener);
+    }
+
+    public int hashCode() {
+        return super.hashCode();
+    }
+
+    //--Logic---------------------------------------------------
+
+
+    /**
+     * Callback from the cluster, when a message is received, The cluster will
+     * broadcast it invoking the messageReceived on the receiver.
+     * 
+     * @param msg
+     *            ClusterMessage - the message received from the cluster
+     */
+    public abstract void messageReceived(ClusterMessage msg) ;
+    
+
+    /**
+     * Accept only SessionIDMessages
+     * 
+     * @param msg
+     *            ClusterMessage
+     * @return boolean - returns true to indicate that messageReceived should be
+     *         invoked. If false is returned, the messageReceived method will
+     *         not be invoked.
+     */
+    public abstract boolean accept(ClusterMessage msg) ;
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ClusterSessionListener.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ClusterSessionListener.java
new file mode 100644
index 0000000..87bed26
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ClusterSessionListener.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.session;
+
+import java.util.Map;
+
+import org.apache.catalina.cluster.ClusterManager;
+import org.apache.catalina.cluster.ClusterMessage;
+
+/**
+ * Receive replicated SessionMessage form other cluster node.
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class ClusterSessionListener extends ClusterListener {
+ 
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info = "org.apache.catalina.session.ClusterSessionListener/1.1";
+
+    //--Constructor---------------------------------------------
+
+    public ClusterSessionListener() {
+    }
+
+    //--Logic---------------------------------------------------
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * Callback from the cluster, when a message is received, The cluster will
+     * broadcast it invoking the messageReceived on the receiver.
+     * 
+     * @param myobj
+     *            ClusterMessage - the message received from the cluster
+     */
+    public void messageReceived(ClusterMessage myobj) {
+        if (myobj != null && myobj instanceof SessionMessage) {
+            SessionMessage msg = (SessionMessage) myobj;
+            String ctxname = msg.getContextName();
+            //check if the message is a EVT_GET_ALL_SESSIONS,
+            //if so, wait until we are fully started up
+            Map managers = cluster.getManagers() ;
+            if (ctxname == null) {
+                java.util.Iterator i = managers.keySet().iterator();
+                while (i.hasNext()) {
+                    String key = (String) i.next();
+                    ClusterManager mgr = (ClusterManager) managers.get(key);
+                    if (mgr != null)
+                        mgr.messageDataReceived(msg);
+                    else {
+                        //this happens a lot before the system has started
+                        // up
+                        if (log.isDebugEnabled())
+                            log.debug("Context manager doesn't exist:"
+                                    + key);
+                    }
+                }
+            } else {
+                ClusterManager mgr = (ClusterManager) managers.get(ctxname);
+                if (mgr != null)
+                    mgr.messageDataReceived(msg);
+                else if (log.isWarnEnabled())
+                    log.warn("Context manager doesn't exist:" + ctxname);
+            }
+        }
+    }
+
+    /**
+     * Accept only SessionMessage
+     * 
+     * @param msg
+     *            ClusterMessage
+     * @return boolean - returns true to indicate that messageReceived should be
+     *         invoked. If false is returned, the messageReceived method will
+     *         not be invoked.
+     */
+    public boolean accept(ClusterMessage msg) {
+        return (msg instanceof SessionMessage);
+    }
+}
+
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/Constants.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/Constants.java
new file mode 100644
index 0000000..944b3dd
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/Constants.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.cluster.session;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.cluster.session</code>
+ * package.
+ *
+ * @author Peter Rossbach Pero
+ */
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.cluster.session";
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaManager.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaManager.java
new file mode 100644
index 0000000..d27806b
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaManager.java
@@ -0,0 +1,1689 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.session;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Session;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterManager;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.Member;
+import org.apache.catalina.session.ManagerBase;
+import org.apache.catalina.util.CustomObjectInputStream;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * The DeltaManager manages replicated sessions by only replicating the deltas
+ * in data. For applications written to handle this, the DeltaManager is the
+ * optimal way of replicating data.
+ * 
+ * This code is almost identical to StandardManager with a difference in how it
+ * persists sessions and some modifications to it.
+ * 
+ * <b>IMPLEMENTATION NOTE </b>: Correct behavior of session storing and
+ * reloading depends upon external calls to the <code>start()</code> and
+ * <code>stop()</code> methods of this class at the correct times.
+ * 
+ * @author Filip Hanik
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class DeltaManager extends ManagerBase implements Lifecycle,
+        PropertyChangeListener, ClusterManager {
+
+    // ---------------------------------------------------- Security Classes
+
+    public static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(DeltaManager.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm = StringManager
+            .getManager(Constants.Package);
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "DeltaManager/2.0";
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+    /**
+     * The descriptive name of this Manager implementation (for logging).
+     */
+    protected static String managerName = "DeltaManager";
+
+    protected String name = null;
+    
+    protected boolean defaultMode = false;
+
+    private CatalinaCluster cluster = null;
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+    /**
+     * The maximum number of active Sessions allowed, or -1 for no limit.
+     */
+    private int maxActiveSessions = -1;
+    
+    private boolean expireSessionsOnShutdown = false;
+
+    private boolean notifyListenersOnReplication = true;
+
+    private boolean notifySessionListenersOnReplication = true;
+
+    private boolean stateTransferred = false ;
+
+    private int stateTransferTimeout = 60;
+
+    private boolean sendAllSessions = true;
+
+    private boolean sendClusterDomainOnly = true ;
+    
+    private int sendAllSessionsSize = 1000 ;
+    
+    /**
+     * wait time between send session block (default 2 sec) 
+     */
+    private int sendAllSessionsWaitTime = 2 * 1000 ; 
+
+    private ArrayList receivedMessageQueue = new ArrayList() ;
+    
+    private boolean receiverQueue = false ;
+
+    private boolean stateTimestampDrop = true ;
+
+    private long stateTransferCreateSendTime; 
+    
+    // ------------------------------------------------------------------ stats attributes
+    
+    int rejectedSessions = 0;
+
+    private long sessionReplaceCounter = 0 ;
+
+    long processingTime = 0;
+
+    private long counterReceive_EVT_GET_ALL_SESSIONS = 0 ;
+
+    private long counterSend_EVT_ALL_SESSION_DATA = 0 ;
+
+    private long counterReceive_EVT_ALL_SESSION_DATA = 0 ;
+
+    private long counterReceive_EVT_SESSION_CREATED = 0 ;
+
+    private long counterReceive_EVT_SESSION_EXPIRED = 0;
+
+    private long counterReceive_EVT_SESSION_ACCESSED = 0 ;
+
+    private long counterReceive_EVT_SESSION_DELTA = 0;
+
+    private long counterSend_EVT_GET_ALL_SESSIONS = 0 ;
+
+    private long counterSend_EVT_SESSION_CREATED = 0;
+
+    private long counterSend_EVT_SESSION_DELTA = 0 ;
+
+    private long counterSend_EVT_SESSION_ACCESSED = 0;
+
+    private long counterSend_EVT_SESSION_EXPIRED = 0;
+
+    private int counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0 ;
+
+    private int counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0 ;
+
+    private int counterNoStateTransfered = 0 ;
+
+
+    // ------------------------------------------------------------- Constructor
+  
+    public DeltaManager() {
+        super();
+    }
+
+    // ------------------------------------------------------------- Properties
+    
+    /**
+     * Return descriptive information about this Manager implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Return the descriptive short name of this Manager implementation.
+     */
+    public String getName() {
+
+        return (name);
+
+    }
+
+    /**
+     * @return Returns the counterSend_EVT_GET_ALL_SESSIONS.
+     */
+    public long getCounterSend_EVT_GET_ALL_SESSIONS() {
+        return counterSend_EVT_GET_ALL_SESSIONS;
+    }
+    
+    /**
+     * @return Returns the counterSend_EVT_SESSION_ACCESSED.
+     */
+    public long getCounterSend_EVT_SESSION_ACCESSED() {
+        return counterSend_EVT_SESSION_ACCESSED;
+    }
+    
+    /**
+     * @return Returns the counterSend_EVT_SESSION_CREATED.
+     */
+    public long getCounterSend_EVT_SESSION_CREATED() {
+        return counterSend_EVT_SESSION_CREATED;
+    }
+
+    /**
+     * @return Returns the counterSend_EVT_SESSION_DELTA.
+     */
+    public long getCounterSend_EVT_SESSION_DELTA() {
+        return counterSend_EVT_SESSION_DELTA;
+    }
+
+    /**
+     * @return Returns the counterSend_EVT_SESSION_EXPIRED.
+     */
+    public long getCounterSend_EVT_SESSION_EXPIRED() {
+        return counterSend_EVT_SESSION_EXPIRED;
+    }
+ 
+    /**
+     * @return Returns the counterSend_EVT_ALL_SESSION_DATA.
+     */
+    public long getCounterSend_EVT_ALL_SESSION_DATA() {
+        return counterSend_EVT_ALL_SESSION_DATA;
+    }
+
+    /**
+     * @return Returns the counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE.
+     */
+    public int getCounterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE() {
+        return counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE;
+    }
+ 
+    /**
+     * @return Returns the counterReceive_EVT_ALL_SESSION_DATA.
+     */
+    public long getCounterReceive_EVT_ALL_SESSION_DATA() {
+        return counterReceive_EVT_ALL_SESSION_DATA;
+    }
+    
+    /**
+     * @return Returns the counterReceive_EVT_GET_ALL_SESSIONS.
+     */
+    public long getCounterReceive_EVT_GET_ALL_SESSIONS() {
+        return counterReceive_EVT_GET_ALL_SESSIONS;
+    }
+    
+    /**
+     * @return Returns the counterReceive_EVT_SESSION_ACCESSED.
+     */
+    public long getCounterReceive_EVT_SESSION_ACCESSED() {
+        return counterReceive_EVT_SESSION_ACCESSED;
+    }
+    
+    /**
+     * @return Returns the counterReceive_EVT_SESSION_CREATED.
+     */
+    public long getCounterReceive_EVT_SESSION_CREATED() {
+        return counterReceive_EVT_SESSION_CREATED;
+    }
+    
+    /**
+     * @return Returns the counterReceive_EVT_SESSION_DELTA.
+     */
+    public long getCounterReceive_EVT_SESSION_DELTA() {
+        return counterReceive_EVT_SESSION_DELTA;
+    }
+    
+    /**
+     * @return Returns the counterReceive_EVT_SESSION_EXPIRED.
+     */
+    public long getCounterReceive_EVT_SESSION_EXPIRED() {
+        return counterReceive_EVT_SESSION_EXPIRED;
+    }
+    
+    
+    /**
+     * @return Returns the counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE.
+     */
+    public int getCounterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE() {
+        return counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE;
+    }
+    
+    /**
+     * @return Returns the processingTime.
+     */
+    public long getProcessingTime() {
+        return processingTime;
+    }
+ 
+    /**
+     * @return Returns the sessionReplaceCounter.
+     */
+    public long getSessionReplaceCounter() {
+        return sessionReplaceCounter;
+    }
+    
+    /**
+     * Number of session creations that failed due to maxActiveSessions
+     * 
+     * @return The count
+     */
+    public int getRejectedSessions() {
+        return rejectedSessions;
+    }
+
+    public void setRejectedSessions(int rejectedSessions) {
+        this.rejectedSessions = rejectedSessions;
+    }
+
+    /**
+     * @return Returns the counterNoStateTransfered.
+     */
+    public int getCounterNoStateTransfered() {
+        return counterNoStateTransfered;
+    }
+    
+    public int getReceivedQueueSize() {
+        return receivedMessageQueue.size() ;
+    }
+    
+    /**
+     * @return Returns the stateTransferTimeout.
+     */
+    public int getStateTransferTimeout() {
+        return stateTransferTimeout;
+    }
+    /**
+     * @param timeoutAllSession The timeout
+     */
+    public void setStateTransferTimeout(int timeoutAllSession) {
+        this.stateTransferTimeout = timeoutAllSession;
+    }
+
+    public boolean getStateTransferred() {
+        return stateTransferred;
+    }
+
+    public void setStateTransferred(boolean stateTransferred) {
+        this.stateTransferred = stateTransferred;
+    }
+    
+    /**
+     * @return Returns the sendAllSessionsWaitTime in msec
+     */
+    public int getSendAllSessionsWaitTime() {
+        return sendAllSessionsWaitTime;
+    }
+    
+    /**
+     * @param sendAllSessionsWaitTime The sendAllSessionsWaitTime to set at msec.
+     */
+    public void setSendAllSessionsWaitTime(int sendAllSessionsWaitTime) {
+        this.sendAllSessionsWaitTime = sendAllSessionsWaitTime;
+    }
+    
+    /**
+     * @return Returns the sendClusterDomainOnly.
+     */
+    public boolean isSendClusterDomainOnly() {
+        return sendClusterDomainOnly;
+    }
+    
+    /**
+     * @param sendClusterDomainOnly The sendClusterDomainOnly to set.
+     */
+    public void setSendClusterDomainOnly(boolean sendClusterDomainOnly) {
+        this.sendClusterDomainOnly = sendClusterDomainOnly;
+    }
+
+    /**
+     * @return Returns the stateTimestampDrop.
+     */
+    public boolean isStateTimestampDrop() {
+        return stateTimestampDrop;
+    }
+    
+    /**
+     * @param isTimestampDrop The new flag value
+     */
+    public void setStateTimestampDrop(boolean isTimestampDrop) {
+        this.stateTimestampDrop = isTimestampDrop;
+    }
+    
+    /**
+     * Return the maximum number of active Sessions allowed, or -1 for no limit.
+     */
+    public int getMaxActiveSessions() {
+
+        return (this.maxActiveSessions);
+
+    }
+
+    /**
+     * Set the maximum number of actives Sessions allowed, or -1 for no limit.
+     * 
+     * @param max
+     *            The new maximum number of sessions
+     */
+    public void setMaxActiveSessions(int max) {
+
+        int oldMaxActiveSessions = this.maxActiveSessions;
+        this.maxActiveSessions = max;
+        support.firePropertyChange("maxActiveSessions", new Integer(
+                oldMaxActiveSessions), new Integer(this.maxActiveSessions));
+
+    }
+    
+    /**
+     * @return Returns the sendAllSessions.
+     */
+    public boolean isSendAllSessions() {
+        return sendAllSessions;
+    }
+    
+    /**
+     * @param sendAllSessions The sendAllSessions to set.
+     */
+    public void setSendAllSessions(boolean sendAllSessions) {
+        this.sendAllSessions = sendAllSessions;
+    }
+    
+    /**
+     * @return Returns the sendAllSessionsSize.
+     */
+    public int getSendAllSessionsSize() {
+        return sendAllSessionsSize;
+    }
+    
+    /**
+     * @param sendAllSessionsSize The sendAllSessionsSize to set.
+     */
+    public void setSendAllSessionsSize(int sendAllSessionsSize) {
+        this.sendAllSessionsSize = sendAllSessionsSize;
+    }
+    
+    /**
+     * @return Returns the notifySessionListenersOnReplication.
+     */
+    public boolean isNotifySessionListenersOnReplication() {
+        return notifySessionListenersOnReplication;
+    }
+    
+    /**
+     * @param notifyListenersCreateSessionOnReplication The notifySessionListenersOnReplication to set.
+     */
+    public void setNotifySessionListenersOnReplication(
+            boolean notifyListenersCreateSessionOnReplication) {
+        this.notifySessionListenersOnReplication = notifyListenersCreateSessionOnReplication;
+    }
+    
+    
+    public boolean isExpireSessionsOnShutdown() {
+        return expireSessionsOnShutdown;
+    }
+
+    public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown) {
+        this.expireSessionsOnShutdown = expireSessionsOnShutdown;
+    }
+    
+    public boolean isNotifyListenersOnReplication() {
+        return notifyListenersOnReplication;
+    }
+
+    public void setNotifyListenersOnReplication(
+            boolean notifyListenersOnReplication) {
+        this.notifyListenersOnReplication = notifyListenersOnReplication;
+    }
+
+    
+    /**
+     * @return Returns the defaultMode.
+     */
+    public boolean isDefaultMode() {
+        return defaultMode;
+    }
+    /**
+     * @param defaultMode The defaultMode to set.
+     */
+    public void setDefaultMode(boolean defaultMode) {
+        this.defaultMode = defaultMode;
+    }
+    
+    public CatalinaCluster getCluster() {
+        return cluster;
+    }
+
+    public void setCluster(CatalinaCluster cluster) {
+        this.cluster = cluster;
+    }
+
+    /**
+     * Set the Container with which this Manager has been associated. If it is a
+     * Context (the usual case), listen for changes to the session timeout
+     * property.
+     * 
+     * @param container
+     *            The associated Container
+     */
+    public void setContainer(Container container) {
+
+        // De-register from the old Container (if any)
+        if ((this.container != null) && (this.container instanceof Context))
+            ((Context) this.container).removePropertyChangeListener(this);
+
+        // Default processing provided by our superclass
+        super.setContainer(container);
+
+        // Register with the new Container (if any)
+        if ((this.container != null) && (this.container instanceof Context)) {
+            setMaxInactiveInterval(((Context) this.container)
+                    .getSessionTimeout() * 60);
+            ((Context) this.container).addPropertyChangeListener(this);
+        }
+
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Construct and return a new session object, based on the default settings
+     * specified by this Manager's properties. The session id will be assigned
+     * by this method, and available via the getId() method of the returned
+     * session. If a new session cannot be created for any reason, return
+     * <code>null</code>.
+     * 
+     * @exception IllegalStateException
+     *                if a new session cannot be instantiated for any reason
+     * 
+     * Construct and return a new session object, based on the default settings
+     * specified by this Manager's properties. The session id will be assigned
+     * by this method, and available via the getId() method of the returned
+     * session. If a new session cannot be created for any reason, return
+     * <code>null</code>.
+     * 
+     * @exception IllegalStateException
+     *                if a new session cannot be instantiated for any reason
+     */
+    public Session createSession(String sessionId) {
+        return createSession(sessionId, true);
+    }
+
+    /**
+     * create new session with check maxActiveSessions and send session creation
+     * to other cluster nodes.
+     * 
+     * @param distribute
+     * @return The session
+     */
+    public Session createSession(String sessionId, boolean distribute) {
+
+        if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) {
+            rejectedSessions++;
+            throw new IllegalStateException(sm
+                    .getString("deltaManager.createSession.ise"));
+        }
+
+        DeltaSession session = (DeltaSession) super.createSession(sessionId) ;
+        session.resetDeltaRequest();
+        if (distribute) {
+            sendCreateSession(session.getId(), session);
+        }
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("deltaManager.createSession.newSession",
+                    session.getId(), new Integer(sessions.size())));
+
+        return (session);
+
+    }
+
+    /**
+     * Send create session evt to all backup node
+     * @param sessionId
+     * @param session
+     */
+    protected void sendCreateSession(String sessionId, DeltaSession session) {
+        if(cluster.getMembers().length > 0 ) {
+            SessionMessage msg = new SessionMessageImpl(getName(),
+                    SessionMessage.EVT_SESSION_CREATED, null, sessionId,
+                    sessionId + System.currentTimeMillis());
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("deltaManager.sendMessage.newSession",
+                        name, sessionId));
+            counterSend_EVT_SESSION_CREATED++;
+            send(msg);
+        }
+        session.resetDeltaRequest();
+    }
+    
+    /**
+     * Send messages to other backup member (domain or all)
+     * @param msg Session message
+     */
+    protected void send(SessionMessage msg) {
+        if(cluster != null) {
+            if(isSendClusterDomainOnly())
+                cluster.sendClusterDomain(msg);
+            else
+                cluster.send(msg);
+        }
+    }
+
+    /**
+     * Create DeltaSession
+     * @see org.apache.catalina.Manager#createEmptySession()
+     */
+    public Session createEmptySession() {
+        return getNewDeltaSession() ;
+    }
+    
+    /**
+     * Get new session class to be used in the doLoad() method.
+     */
+    protected DeltaSession getNewDeltaSession() {
+        return new DeltaSession(this);
+    }
+
+    /**
+     * Load Deltarequest from external node
+     * Load the Class at container classloader
+     * @see DeltaRequest#readExternal(java.io.ObjectInput)
+     * @param session
+     * @param data message data
+     * @return The request
+     * @throws ClassNotFoundException
+     * @throws IOException
+     */
+    protected DeltaRequest loadDeltaRequest(DeltaSession session, byte[] data)
+            throws ClassNotFoundException, IOException {
+        ByteArrayInputStream fis = null;
+        ReplicationStream ois = null;
+        Loader loader = null;
+        ClassLoader classLoader = null;
+        //fix to be able to run the DeltaManager
+        //stand alone without a container.
+        //use the Threads context class loader
+        if (container != null)
+            loader = container.getLoader();
+        if (loader != null)
+            classLoader = loader.getClassLoader();
+        else
+            classLoader = Thread.currentThread().getContextClassLoader();
+        //end fix
+        fis = new ByteArrayInputStream(data);
+        ois = new ReplicationStream(fis, classLoader);
+        session.getDeltaRequest().readExternal(ois);
+        ois.close();
+        return session.getDeltaRequest();
+    }
+
+    /**
+     * serialize DeltaRequest
+     * @see DeltaRequest#writeExternal(java.io.ObjectOutput)
+     * 
+     * @param deltaRequest
+     * @return serialized delta request
+     * @throws IOException
+     */
+    protected byte[] unloadDeltaRequest(DeltaRequest deltaRequest)
+            throws IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(bos);
+        deltaRequest.writeExternal(oos);
+        oos.flush();
+        oos.close();
+        return bos.toByteArray();
+    }
+
+    /**
+     * Load sessions from other cluster node.
+     * FIXME replace currently sessions with same id without notifcation.
+     * FIXME SSO handling is not really correct with the session replacement!
+     * @exception ClassNotFoundException
+     *                if a serialized class cannot be found during the reload
+     * @exception IOException
+     *                if an input/output error occurs
+     */
+    protected void deserializeSessions(byte[] data) throws ClassNotFoundException,
+            IOException {
+
+        // Initialize our internal data structures
+        //sessions.clear(); //should not do this
+        // Open an input stream to the specified pathname, if any
+        ClassLoader originalLoader = Thread.currentThread()
+                .getContextClassLoader();
+        ObjectInputStream ois = null;
+        // Load the previously unloaded active sessions
+        try {
+            ois = openDeserializeObjectStream(data);
+            Integer count = (Integer) ois.readObject();
+            int n = count.intValue();
+            for (int i = 0; i < n; i++) {
+                DeltaSession session = (DeltaSession) createEmptySession();
+                session.readObjectData(ois);
+                session.setManager(this);
+                session.setValid(true);
+                session.setPrimarySession(false);
+                //in case the nodes in the cluster are out of
+                //time synch, this will make sure that we have the
+                //correct timestamp, isValid returns true, cause
+                // accessCount=1
+                session.access();
+                //make sure that the session gets ready to expire if
+                // needed
+                session.setAccessCount(0);
+                session.resetDeltaRequest();
+                // FIXME How inform other session id cache like SingleSignOn
+                // increment sessionCounter to correct stats report
+                if (findSession(session.getIdInternal()) != null ) {
+                    sessionCounter++;
+                } else {
+                    sessionReplaceCounter++;
+                    // FIXME better is to grap this sessions again !
+                    if (log.isWarnEnabled())
+                        log.warn(sm.getString(
+                                "deltaManager.loading.existing.session",
+                                session.getIdInternal()));
+                }
+                add(session);
+            }
+        } catch (ClassNotFoundException e) {
+            log.error(sm.getString("deltaManager.loading.cnfe", e), e);
+            throw e;
+        } catch (IOException e) {
+            log.error(sm.getString("deltaManager.loading.ioe", e), e);
+            throw e;
+        } finally {
+            // Close the input stream
+            try {
+                if (ois != null)
+                    ois.close();
+            } catch (IOException f) {
+                // ignored
+            }
+            ois = null;
+            if (originalLoader != null)
+                Thread.currentThread().setContextClassLoader(originalLoader);
+        }
+
+    }
+
+    /**
+     * Open Stream and use correct ClassLoader (Container) Switch
+     * ThreadClassLoader
+     * 
+     * @param data
+     * @return The object input stream
+     * @throws IOException
+     */
+    protected ObjectInputStream openDeserializeObjectStream(byte[] data) throws IOException {
+        ObjectInputStream ois = null;
+        ByteArrayInputStream fis = null;
+        try {
+            Loader loader = null;
+            ClassLoader classLoader = null;
+            fis = new ByteArrayInputStream(data);
+            BufferedInputStream bis = new BufferedInputStream(fis);
+            if (container != null)
+                loader = container.getLoader();
+            if (loader != null)
+                classLoader = loader.getClassLoader();
+            if (classLoader != null) {
+                if (log.isTraceEnabled())
+                    log.trace(sm.getString(
+                            "deltaManager.loading.withContextClassLoader",
+                            getName()));
+                ois = new CustomObjectInputStream(bis, classLoader);
+                Thread.currentThread().setContextClassLoader(classLoader);
+            } else {
+                if (log.isTraceEnabled())
+                    log.trace(sm.getString(
+                            "deltaManager.loading.withoutClassLoader",
+                            getName()));
+                ois = new ObjectInputStream(bis);
+            }
+        } catch (IOException e) {
+            log.error(sm.getString("deltaManager.loading.ioe", e), e);
+            if (ois != null) {
+                try {
+                    ois.close();
+                } catch (IOException f) {
+                    ;
+                }
+                ois = null;
+            }
+            throw e;
+        }
+        return ois;
+    }
+
+    /**
+     * Save any currently active sessions in the appropriate persistence
+     * mechanism, if any. If persistence is not supported, this method returns
+     * without doing anything.
+     * 
+     * @exception IOException
+     *                if an input/output error occurs
+     */
+    protected byte[] serializeSessions(Session[] currentSessions) throws IOException {
+
+        // Open an output stream to the specified pathname, if any
+        ByteArrayOutputStream fos = null;
+        ObjectOutputStream oos = null;
+
+        try {
+            fos = new ByteArrayOutputStream();
+            oos = new ObjectOutputStream(new BufferedOutputStream(fos));
+            oos.writeObject(new Integer(currentSessions.length));
+            for(int i=0 ; i < currentSessions.length;i++) {
+                ((DeltaSession)currentSessions[i]).writeObjectData(oos);                
+            }
+            // Flush and close the output stream
+            oos.flush();
+        } catch (IOException e) {
+            log.error(sm.getString("deltaManager.unloading.ioe", e), e);
+            throw e;
+        } finally {
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (IOException f) {
+                    ;
+                }
+                oos = null;
+            }
+        }
+        // send object data as byte[]
+        return fos.toByteArray();
+    }
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+    /**
+     * Add a lifecycle event listener to this component.
+     * 
+     * @param listener
+     *            The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     * 
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component. This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     * 
+     * @exception LifecycleException
+     *                if this component detects a fatal error that prevents this
+     *                component from being used
+     */
+    public void start() throws LifecycleException {
+        if (!initialized)
+            init();
+
+        // Validate and update our current component state
+        if (started) {
+            return;
+        }
+        started = true;
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+
+        // Force initialization of the random number generator
+        String dummy = generateSessionId();
+
+        // Load unloaded sessions, if any
+        try {
+            //the channel is already running
+            Cluster cluster = getCluster() ;
+            // stop remove cluster binding
+            if(cluster == null) {
+                Container context = getContainer() ;
+                if(context != null && context instanceof Context) {
+                     Container host = context.getParent() ;
+                     if(host != null && host instanceof Host) {
+                         cluster = host.getCluster();
+                         if(cluster != null && cluster instanceof CatalinaCluster) {
+                             setCluster((CatalinaCluster) cluster) ;
+                         } else {
+                             Container engine = host.getParent() ;
+                             if(engine != null && engine instanceof Engine) {
+                                 cluster = engine.getCluster();
+                                 if(cluster != null && cluster instanceof CatalinaCluster) {
+                                         setCluster((CatalinaCluster) cluster) ;
+                                 }
+                             } else {
+                                     cluster = null ;
+                             }
+                         }
+                     }
+                }
+            }
+            if (cluster == null) {
+                log.error(sm.getString("deltaManager.noCluster", getName()));
+                return;
+            } else {
+                if (log.isInfoEnabled()) {
+                    String type = "unknown" ;
+                    if( cluster.getContainer() instanceof Host){
+                        type = "Host" ;
+                    } else if( cluster.getContainer() instanceof Engine){
+                        type = "Engine" ;
+                    }
+                    log.info(sm
+                            .getString("deltaManager.registerCluster", getName(), type, cluster.getClusterName()));
+                }
+            }
+            if (log.isInfoEnabled())
+                log.info(sm
+                        .getString("deltaManager.startClustering", getName()));
+            //to survice context reloads, as only a stop/start is called, not
+            // createManager
+            ((CatalinaCluster)cluster).addManager(getName(), this);
+
+            getAllClusterSessions();
+
+        } catch (Throwable t) {
+            log.error(sm.getString("deltaManager.managerLoad"), t);
+        }
+    }
+
+    /**
+     * get from first session master the backup from all clustered sessions
+     * @see #findSessionMasterMember()
+     */
+    public synchronized void getAllClusterSessions() {
+        if (cluster != null && cluster.getMembers().length > 0) {
+            long beforeSendTime = System.currentTimeMillis();
+            Member mbr = findSessionMasterMember();
+            if(mbr == null) { // No domain member found
+                 return;
+            }
+            SessionMessage msg = new SessionMessageImpl(this.getName(),
+                    SessionMessage.EVT_GET_ALL_SESSIONS, null, "GET-ALL",
+                    "GET-ALL-" + getName());
+            msg.setResend(ClusterMessage.FLAG_FORBIDDEN);
+            // set reference time
+            msg.setTimestamp(beforeSendTime);
+            stateTransferCreateSendTime = beforeSendTime ;
+            // request session state
+            counterSend_EVT_GET_ALL_SESSIONS++;
+            stateTransferred = false ;
+            // FIXME This send call block the deploy thread, when sender waitForAck is enabled
+            try {
+                synchronized(receivedMessageQueue) {
+                     receiverQueue = true ;
+                }
+                cluster.send(msg, mbr);
+                if (log.isWarnEnabled())
+                    log.warn(sm.getString("deltaManager.waitForSessionState",
+                            getName(), mbr));
+                // FIXME At sender ack mode this method check only the state transfer and resend is a problem!
+                waitForSendAllSessions(beforeSendTime);
+            } finally {
+                synchronized(receivedMessageQueue) {
+                    for (Iterator iter = receivedMessageQueue.iterator(); iter
+                            .hasNext();) {
+                        SessionMessage smsg = (SessionMessage) iter.next();
+                        if (!stateTimestampDrop) {
+                            messageReceived(smsg,
+                                    smsg.getAddress() != null ? (Member) smsg
+                                            .getAddress() : null);
+                        } else {
+                            if (smsg.getEventType() != SessionMessage.EVT_GET_ALL_SESSIONS
+                                    && smsg.getTimestamp() >= stateTransferCreateSendTime) {
+                                // FIXME handle EVT_GET_ALL_SESSIONS later
+                                messageReceived(
+                                        smsg,
+                                        smsg.getAddress() != null ? (Member) smsg
+                                                .getAddress()
+                                                : null);
+                            } else {
+                                if (log.isWarnEnabled()) {
+                                    log.warn(sm.getString(
+                                            "deltaManager.dropMessage",
+                                            getName(), smsg
+                                                    .getEventTypeString(),
+                                            new Date(stateTransferCreateSendTime), new Date(
+                                                    smsg.getTimestamp())));
+                                }
+                            }
+                        }
+                    }        
+                    receivedMessageQueue.clear();
+                    receiverQueue = false ;
+                }
+           }
+        } else {
+            if (log.isInfoEnabled())
+                log.info(sm.getString("deltaManager.noMembers", getName()));
+        }
+    }
+
+    /**
+     * Find the master óf the session state
+     * @return master member of sessions 
+     */
+    protected Member findSessionMasterMember() {
+        Member mbr = null;
+        Member mbrs[] = cluster.getMembers();
+        String localMemberDomain = cluster.getMembershipService().getLocalMember().getDomain();
+        if(isSendClusterDomainOnly()) {
+            for (int i = 0; mbr == null && i < mbrs.length; i++) {
+                Member member = mbrs[i];
+                if(localMemberDomain.equals(member.getDomain()))
+                    mbr = member ;
+            }
+        } else {
+            // FIXME Why only the first Member?
+            if(mbrs.length != 0 )
+                mbr = mbrs[0];
+        }
+        if(mbr == null && log.isWarnEnabled())
+           log.warn(sm.getString("deltaManager.noMasterMember",
+                    getName(), localMemberDomain));
+        if(mbr != null && log.isDebugEnabled())
+            log.warn(sm.getString("deltaManager.foundMasterMember",
+                     getName(), mbr));
+        return mbr;
+    }
+
+    /**
+     * Wait that cluster session state is transfer or timeout after 60 Sec
+     * With stateTransferTimeout == -1 wait that backup is transfered (forever mode)
+     */
+    protected void waitForSendAllSessions(long beforeSendTime) {
+        long reqStart = System.currentTimeMillis();
+        long reqNow = reqStart ;
+        boolean isTimeout = false;
+        if(getStateTransferTimeout() > 0) {
+            // wait that state is transfered with timeout check
+            do {
+                try {
+                    Thread.sleep(100);
+                } catch (Exception sleep) {
+                }
+                reqNow = System.currentTimeMillis();
+                isTimeout = ((reqNow - reqStart) > (1000 * getStateTransferTimeout()));
+            } while ((!getStateTransferred()) && (!isTimeout));
+        } else {
+            if(getStateTransferTimeout() == -1) {
+                // wait that state is transfered
+                do {
+                    try {
+                        Thread.sleep(100);
+                    } catch (Exception sleep) {
+                    }
+                } while ((!getStateTransferred()));
+                reqNow = System.currentTimeMillis();
+            }
+        }
+        if (isTimeout || (!getStateTransferred())) {
+            counterNoStateTransfered++ ;
+            log.error(sm.getString("deltaManager.noSessionState",
+                    getName(),new Date(beforeSendTime),new Long(reqNow - beforeSendTime)));
+        } else {
+            if (log.isInfoEnabled())
+                log.info(sm.getString("deltaManager.sessionReceived",
+                        getName(), new Date(beforeSendTime), new Long(reqNow - beforeSendTime)));
+        }
+    }
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component. This method should be the last one called on a given instance
+     * of this component.
+     * 
+     * @exception LifecycleException
+     *                if this component detects a fatal error that needs to be
+     *                reported
+     */
+    public void stop() throws LifecycleException {
+
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("deltaManager.stopped", getName()));
+
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException(sm
+                    .getString("deltaManager.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Expire all active sessions
+        if (log.isInfoEnabled())
+            log.info(sm.getString("deltaManager.expireSessions", getName()));
+        Session sessions[] = findSessions();
+        for (int i = 0; i < sessions.length; i++) {
+            DeltaSession session = (DeltaSession) sessions[i];
+            if (!session.isValid())
+                continue;
+            try {
+                session.expire(true, isExpireSessionsOnShutdown());
+            } catch (Throwable ignore) {
+                ;
+            } 
+        }
+
+        // Require a new random number generator if we are restarted
+        this.random = null;
+        getCluster().removeManager(getName(),this);
+        if (initialized) {
+            destroy();
+        }
+    }
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+    /**
+     * Process property change events from our associated Context.
+     * 
+     * @param event
+     *            The property change event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        // Validate the source of this event
+        if (!(event.getSource() instanceof Context))
+            return;
+        Context context = (Context) event.getSource();
+
+        // Process a relevant property change
+        if (event.getPropertyName().equals("sessionTimeout")) {
+            try {
+                setMaxInactiveInterval(((Integer) event.getNewValue())
+                        .intValue() * 60);
+            } catch (NumberFormatException e) {
+                log.error(sm.getString("deltaManager.sessionTimeout", event
+                        .getNewValue()));
+            }
+        }
+
+    }
+
+    // -------------------------------------------------------- Replication
+    // Methods
+
+    /**
+     * A message was received from another node, this is the callback method to
+     * implement if you are interested in receiving replication messages.
+     * 
+     * @param cmsg -
+     *            the message received.
+     */
+    public void messageDataReceived(ClusterMessage cmsg) {
+        if (cmsg != null && cmsg instanceof SessionMessage) {
+            SessionMessage msg = (SessionMessage) cmsg;
+            switch (msg.getEventType()) {
+            case SessionMessage.EVT_GET_ALL_SESSIONS:
+            case SessionMessage.EVT_SESSION_CREATED: 
+            case SessionMessage.EVT_SESSION_EXPIRED: 
+            case SessionMessage.EVT_SESSION_ACCESSED:
+            case SessionMessage.EVT_SESSION_DELTA: {
+                synchronized(receivedMessageQueue) {
+                    if(receiverQueue) {
+                        receivedMessageQueue.add(msg);
+                        return ;
+                    }
+                }
+               break;
+            }
+            default: {
+                //we didn't queue, do nothing
+                break;
+            }
+            } //switch
+            
+            messageReceived(msg, msg.getAddress() != null ? (Member) msg
+                    .getAddress() : null);
+        }
+    }
+
+    /**
+     * When the request has been completed, the replication valve will notify
+     * the manager, and the manager will decide whether any replication is
+     * needed or not. If there is a need for replication, the manager will
+     * create a session message and that will be replicated. The cluster
+     * determines where it gets sent.
+     * 
+     * @param sessionId -
+     *            the sessionId that just completed.
+     * @return a SessionMessage to be sent,
+     */
+    public ClusterMessage requestCompleted(String sessionId) {
+        try {
+            DeltaSession session = (DeltaSession) findSession(sessionId);
+            DeltaRequest deltaRequest = session.getDeltaRequest();
+            SessionMessage msg = null;
+            if (deltaRequest.getSize() > 0) {
+
+                counterSend_EVT_SESSION_DELTA++;
+                byte[] data = unloadDeltaRequest(deltaRequest);
+                msg = new SessionMessageImpl(name,
+                        SessionMessage.EVT_SESSION_DELTA, data, sessionId,
+                        sessionId + System.currentTimeMillis());
+                session.resetDeltaRequest();
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString(
+                            "deltaManager.createMessage.delta",
+                            getName(), sessionId));
+                }
+                
+            } else if (!session.isPrimarySession()) {
+                counterSend_EVT_SESSION_ACCESSED++;
+                msg = new SessionMessageImpl(getName(),
+                        SessionMessage.EVT_SESSION_ACCESSED, null, sessionId,
+                        sessionId + System.currentTimeMillis());
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString(
+                            "deltaManager.createMessage.accessChangePrimary",
+                            getName(), sessionId));
+                }
+            }
+            session.setPrimarySession(true);
+            //check to see if we need to send out an access message
+            if ((msg == null)) {
+                long replDelta = System.currentTimeMillis()
+                        - session.getLastTimeReplicated();
+                if (replDelta > (getMaxInactiveInterval() * 1000)) {
+                    counterSend_EVT_SESSION_ACCESSED++;
+                    msg = new SessionMessageImpl(getName(),
+                            SessionMessage.EVT_SESSION_ACCESSED, null,
+                            sessionId, sessionId + System.currentTimeMillis());
+                    if (log.isDebugEnabled()) {
+                        log.debug(sm.getString(
+                                "deltaManager.createMessage.access", getName(),
+                                sessionId));
+                    }
+                }
+
+            }
+
+            //update last replicated time
+            if (msg != null)
+                session.setLastTimeReplicated(System.currentTimeMillis());
+            return msg;
+        } catch (IOException x) {
+            log.error(sm.getString(
+                    "deltaManager.createMessage.unableCreateDeltaRequest",
+                    sessionId), x);
+            return null;
+        }
+
+    }
+    /**
+     * Reset manager statistics
+     */
+    public synchronized void resetStatistics() {
+        processingTime = 0 ;
+        expiredSessions = 0 ;
+        rejectedSessions = 0 ;
+        sessionReplaceCounter = 0 ;
+        counterNoStateTransfered = 0 ;
+        maxActive = getActiveSessions() ;
+        sessionCounter = getActiveSessions() ;
+        counterReceive_EVT_ALL_SESSION_DATA = 0;
+        counterReceive_EVT_GET_ALL_SESSIONS = 0;
+        counterReceive_EVT_SESSION_ACCESSED = 0 ;
+        counterReceive_EVT_SESSION_CREATED = 0 ;
+        counterReceive_EVT_SESSION_DELTA = 0 ;
+        counterReceive_EVT_SESSION_EXPIRED = 0 ;
+        counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0;
+        counterSend_EVT_ALL_SESSION_DATA = 0;
+        counterSend_EVT_GET_ALL_SESSIONS = 0;
+        counterSend_EVT_SESSION_ACCESSED = 0 ;
+        counterSend_EVT_SESSION_CREATED = 0 ;
+        counterSend_EVT_SESSION_DELTA = 0 ;
+        counterSend_EVT_SESSION_EXPIRED = 0 ;
+        counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0;
+        
+    }
+   
+    //  -------------------------------------------------------- persistence handler
+
+    public void load() {
+
+    }
+
+    public void unload() {
+
+    }
+
+    //  -------------------------------------------------------- expire
+
+    /**
+     * send session expired to other cluster nodes
+     * 
+     * @param id
+     *            session id
+     */
+    protected void sessionExpired(String id) {
+        counterSend_EVT_SESSION_EXPIRED++ ;
+        SessionMessage msg = new SessionMessageImpl(getName(),
+                SessionMessage.EVT_SESSION_EXPIRED, null, id, id
+                        + "-EXPIRED-MSG");
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("deltaManager.createMessage.expire",
+                    getName(), id));
+        send(msg);
+    }
+
+    /**
+     * Exipre all find sessions.
+     */
+    public void expireAllLocalSessions()
+    {
+        long timeNow = System.currentTimeMillis();
+        Session sessions[] = findSessions();
+        int expireDirect  = 0 ;
+        int expireIndirect = 0 ;
+        
+        if(log.isDebugEnabled())
+            log.debug("Start expire all sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
+        for (int i = 0; i < sessions.length; i++) {
+            if (sessions[i] instanceof DeltaSession) {
+                DeltaSession session = (DeltaSession) sessions[i];
+                if (session.isPrimarySession()) {
+                    if (session.isValid()) {
+                        session.expire();
+                        expireDirect++;
+                    } else {
+                        expireIndirect++;
+                    }
+                }
+            }
+        }
+        long timeEnd = System.currentTimeMillis();
+        if(log.isDebugEnabled())
+             log.debug("End expire sessions " + getName() + " exipre processingTime " + (timeEnd - timeNow) + " expired direct sessions: " + expireDirect + " expired direct sessions: " + expireIndirect);
+      
+    }
+    
+    /**
+     * When the manager expires session not tied to a request. The cluster will
+     * periodically ask for a list of sessions that should expire and that
+     * should be sent across the wire.
+     * 
+     * @return The invalidated sessions array
+     */
+    public String[] getInvalidatedSessions() {
+        return new String[0];
+    }
+
+    //  -------------------------------------------------------- message receive
+
+    /**
+     * Test that sender and local domain is the same
+     */
+    protected boolean checkSenderDomain(SessionMessage msg,Member sender) {
+        String localMemberDomain = cluster.getMembershipService().getLocalMember().getDomain();
+        boolean sameDomain= localMemberDomain.equals(sender.getDomain());
+        if (!sameDomain && log.isWarnEnabled()) {
+                log.warn(sm.getString("deltaManager.receiveMessage.fromWrongDomain",
+                        new Object[] {getName(), 
+                        msg.getEventTypeString(), 
+                        sender,
+                        sender.getDomain(),
+                        localMemberDomain }
+                ));
+        }
+        return sameDomain ;
+    }
+
+    /**
+     * This method is called by the received thread when a SessionMessage has
+     * been received from one of the other nodes in the cluster.
+     * 
+     * @param msg -
+     *            the message received
+     * @param sender -
+     *            the sender of the message, this is used if we receive a
+     *            EVT_GET_ALL_SESSION message, so that we only reply to the
+     *            requesting node
+     */
+    protected void messageReceived(SessionMessage msg, Member sender) {
+        if(isSendClusterDomainOnly() && !checkSenderDomain(msg,sender)) {
+            return;
+        }
+        try {
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("deltaManager.receiveMessage.eventType",
+                        getName(), msg.getEventTypeString(), sender));
+ 
+            switch (msg.getEventType()) {
+            case SessionMessage.EVT_GET_ALL_SESSIONS: {
+                handleGET_ALL_SESSIONS(msg,sender);
+                break;
+            }
+            case SessionMessage.EVT_ALL_SESSION_DATA: {
+                handleALL_SESSION_DATA(msg,sender);
+                break;
+            }
+            case SessionMessage.EVT_ALL_SESSION_TRANSFERCOMPLETE: {
+                handleALL_SESSION_TRANSFERCOMPLETE(msg,sender);
+                break;
+            }
+            case SessionMessage.EVT_SESSION_CREATED: {
+                handleSESSION_CREATED(msg,sender);
+                break;
+            }
+            case SessionMessage.EVT_SESSION_EXPIRED: {
+                handleSESSION_EXPIRRED(msg,sender);
+                break;
+            }
+            case SessionMessage.EVT_SESSION_ACCESSED: {
+                handleSESSION_ACCESSED(msg,sender);
+                break;
+            }
+            case SessionMessage.EVT_SESSION_DELTA: {
+               handleSESSION_DELTA(msg,sender);
+               break;
+            }
+            default: {
+                //we didn't recognize the message type, do nothing
+                break;
+            }
+            } //switch
+        } catch (Exception x) {
+            log.error(sm.getString("deltaManager.receiveMessage.error",
+                    getName()), x);
+        }
+    }
+
+    // -------------------------------------------------------- message receiver handler
+
+
+    /**
+     * handle receive session state is complete transfered
+     * @param msg
+     * @param sender
+     */
+    protected void handleALL_SESSION_TRANSFERCOMPLETE(SessionMessage msg, Member sender) {
+        counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE++ ;
+        if (log.isDebugEnabled())
+            log.debug(sm.getString(
+                    "deltaManager.receiveMessage.transfercomplete",
+                    getName(), sender.getHost(), new Integer(sender.getPort())));
+        stateTransferCreateSendTime = msg.getTimestamp() ;
+        stateTransferred = true ;
+    }
+
+    /**
+     * handle receive session delta
+     * @param msg
+     * @param sender
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    protected void handleSESSION_DELTA(SessionMessage msg, Member sender)
+            throws IOException, ClassNotFoundException {
+        counterReceive_EVT_SESSION_DELTA++;
+        byte[] delta = msg.getSession();
+        DeltaSession session = (DeltaSession) findSession(msg.getSessionID());
+        if (session != null) {
+            log.debug(sm.getString("deltaManager.receiveMessage.delta",
+                    getName(), msg.getSessionID()));
+            DeltaRequest dreq = loadDeltaRequest(session, delta);
+            dreq.execute(session, notifyListenersOnReplication);
+            session.setPrimarySession(false);
+        }
+    }
+
+    /**
+     * handle receive session is access at other node ( primary session is now false)
+     * @param msg
+     * @param sender
+     * @throws IOException
+     */
+    protected void handleSESSION_ACCESSED(SessionMessage msg,Member sender) throws IOException {
+        counterReceive_EVT_SESSION_ACCESSED++;
+        DeltaSession session = (DeltaSession) findSession(msg
+                .getSessionID());
+        if (session != null) {
+            if (log.isDebugEnabled())
+                log.debug(sm.getString(
+                        "deltaManager.receiveMessage.accessed",
+                        getName(), msg.getSessionID()));
+            session.access();
+            session.setPrimarySession(false);
+            session.endAccess();
+        }
+    }
+
+    /**
+     * handle receive session is expire at other node ( expire session also here)
+     * @param msg
+     * @param sender
+     * @throws IOException
+     */
+    protected void handleSESSION_EXPIRRED(SessionMessage msg,Member sender) throws IOException {
+        counterReceive_EVT_SESSION_EXPIRED++;
+        DeltaSession session = (DeltaSession) findSession(msg
+                .getSessionID());
+        if (session != null) {
+            if (log.isDebugEnabled())
+                log.debug(sm.getString(
+                        "deltaManager.receiveMessage.expired",
+                        getName(), msg.getSessionID()));
+            session.expire(notifySessionListenersOnReplication, false);
+        }
+    }
+
+    /**
+     * handle receive new session is created at other node (create backup - primary false)
+     * @param msg
+     * @param sender
+     */
+    protected void handleSESSION_CREATED(SessionMessage msg,Member sender) {
+        counterReceive_EVT_SESSION_CREATED++;
+        if (log.isDebugEnabled())
+            log.debug(sm.getString(
+                    "deltaManager.receiveMessage.createNewSession",
+                    getName(), msg.getSessionID()));
+        DeltaSession session = (DeltaSession) createSession(msg
+                .getSessionID(), false);
+        if(notifySessionListenersOnReplication)
+            session.setId(msg.getSessionID());
+        else
+            session.getDeltaRequest().setSessionId(msg.getSessionID());
+        session.setNew(false);
+        session.setPrimarySession(false);
+        session.resetDeltaRequest();
+    }
+
+    /**
+     * handle receive sessions from other not ( restart )
+     * @param msg
+     * @param sender
+     * @throws ClassNotFoundException
+     * @throws IOException
+     */
+    protected void handleALL_SESSION_DATA(SessionMessage msg,Member sender) throws ClassNotFoundException, IOException {
+        counterReceive_EVT_ALL_SESSION_DATA++;
+        if (log.isDebugEnabled())
+            log.debug(sm.getString(
+                    "deltaManager.receiveMessage.allSessionDataBegin",
+                    getName()));
+        byte[] data = msg.getSession();
+        deserializeSessions(data);
+        if (log.isDebugEnabled())
+            log.debug(sm.getString(
+                    "deltaManager.receiveMessage.allSessionDataAfter",
+                    getName()));
+        //stateTransferred = true;
+    }
+
+    /**
+     * handle receive that other node want all sessions ( restart )
+     * a) send all sessions with one message
+     * b) send session at blocks
+     * After sending send state is complete transfered
+     * @param msg
+     * @param sender
+     * @throws IOException
+     */
+    protected void handleGET_ALL_SESSIONS(SessionMessage msg, Member sender)
+            throws IOException {
+        counterReceive_EVT_GET_ALL_SESSIONS++;
+        //get a list of all the session from this manager
+        if (log.isDebugEnabled())
+            log.debug(sm.getString(
+                    "deltaManager.receiveMessage.unloadingBegin", getName()));
+        // Write the number of active sessions, followed by the details
+        // get all sessions and serialize without sync
+        Session[] currentSessions = findSessions();
+        long findSessionTimestamp = System.currentTimeMillis() ;
+        if (isSendAllSessions()) {
+            sendSessions(sender, currentSessions, findSessionTimestamp);
+        } else {
+            // send session at blocks
+            int len = currentSessions.length < getSendAllSessionsSize() ? currentSessions.length
+                    : getSendAllSessionsSize();
+            Session[] sendSessions = new Session[len];
+            for (int i = 0; i < currentSessions.length; i += getSendAllSessionsSize()) {
+                len = i + getSendAllSessionsSize() > currentSessions.length ? currentSessions.length
+                        - i 
+                        : getSendAllSessionsSize();
+                System.arraycopy(currentSessions, i, sendSessions, 0, len);
+                sendSessions(sender, sendSessions,findSessionTimestamp);
+                if (getSendAllSessionsWaitTime() > 0) {
+                    try {
+                        Thread.sleep(getSendAllSessionsWaitTime());
+                    } catch (Exception sleep) {
+                    }
+                }
+            }
+        }
+        
+        SessionMessage newmsg = new SessionMessageImpl(name,
+                SessionMessage.EVT_ALL_SESSION_TRANSFERCOMPLETE, null,
+                "SESSION-STATE-TRANSFERED", "SESSION-STATE-TRANSFERED"
+                        + getName());
+        newmsg.setTimestamp(findSessionTimestamp);
+        if (log.isDebugEnabled())
+            log.debug(sm.getString(
+                    "deltaManager.createMessage.allSessionTransfered",
+                    getName()));
+        counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE++;
+        cluster.send(newmsg, sender);
+    }
+
+
+    /**
+     * send a block of session to sender
+     * @param sender
+     * @param currentSessions
+     * @param sendTimestamp
+     * @throws IOException
+     */
+    protected void sendSessions(Member sender, Session[] currentSessions,
+            long sendTimestamp) throws IOException {
+        byte[] data = serializeSessions(currentSessions);
+        if (log.isDebugEnabled())
+            log.debug(sm.getString(
+                    "deltaManager.receiveMessage.unloadingAfter",
+                    getName()));
+        SessionMessage newmsg = new SessionMessageImpl(name,
+                SessionMessage.EVT_ALL_SESSION_DATA, data,
+                "SESSION-STATE", "SESSION-STATE-" + getName());
+        newmsg.setTimestamp(sendTimestamp);
+        //if(isSendSESSIONSTATEcompressed()) {
+        //    newmsg.setCompress(ClusterMessage.FLAG_ALLOWED);
+        //}
+        if (log.isDebugEnabled())
+            log.debug(sm.getString(
+                    "deltaManager.createMessage.allSessionData",
+                    getName()));
+        counterSend_EVT_ALL_SESSION_DATA++;
+        cluster.send(newmsg, sender);
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaRequest.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaRequest.java
new file mode 100644
index 0000000..1dd1c8e
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaRequest.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.cluster.session;
+
+/**
+ * This class is used to track the series of actions that happens when
+ * a request is executed. These actions will then translate into invokations of methods 
+ * on the actual session.
+ * This class is NOT thread safe. One DeltaRequest per session
+ * @author <a href="mailto:fhanik@apache.org">Filip Hanik</a>
+ * @version 1.0
+ */
+
+import java.io.Externalizable;
+import java.security.Principal;
+import java.util.LinkedList;
+
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.util.StringManager;
+
+
+public class DeltaRequest implements Externalizable {
+
+    public static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( DeltaRequest.class );
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm = StringManager
+            .getManager(Constants.Package);
+
+    public static final int TYPE_ATTRIBUTE = 0;
+    public static final int TYPE_PRINCIPAL = 1;
+    public static final int TYPE_ISNEW = 2;
+    public static final int TYPE_MAXINTERVAL = 3;
+
+    public static final int ACTION_SET = 0;
+    public static final int ACTION_REMOVE = 1;
+
+    public static final String NAME_PRINCIPAL = "__SET__PRINCIPAL__";
+    public static final String NAME_MAXINTERVAL = "__SET__MAXINTERVAL__";
+    public static final String NAME_ISNEW = "__SET__ISNEW__";
+
+    private String sessionId;
+    private LinkedList actions = new LinkedList();
+    private LinkedList actionPool = new LinkedList();
+    
+    private boolean recordAllActions = false;
+
+    public DeltaRequest() {
+        
+    }
+    
+    public DeltaRequest(String sessionId, boolean recordAllActions) {
+        this.recordAllActions=recordAllActions;
+        if(sessionId != null)
+            setSessionId(sessionId);
+    }
+
+
+    public void setAttribute(String name, Object value) {
+        int action = (value==null)?ACTION_REMOVE:ACTION_SET;
+        addAction(TYPE_ATTRIBUTE,action,name,value);
+    }
+
+    public void removeAttribute(String name) {
+        int action = ACTION_REMOVE;
+        addAction(TYPE_ATTRIBUTE,action,name,null);
+    }
+
+    public void setMaxInactiveInterval(int interval) {
+        int action = ACTION_SET;
+        addAction(TYPE_MAXINTERVAL,action,NAME_MAXINTERVAL,new Integer(interval));
+    }
+    
+    /**
+     * convert principal at SerializablePrincipal for backup nodes.
+     * Only support principals from type {@link GenericPrincipal GenericPrincipal}
+     * @param p Session principal
+     * @see GenericPrincipal
+     */
+    public void setPrincipal(Principal p) {
+        int action = (p==null)?ACTION_REMOVE:ACTION_SET;
+        SerializablePrincipal sp = null;
+        if ( p != null ) {
+            if(p instanceof GenericPrincipal) {
+                sp = SerializablePrincipal.createPrincipal((GenericPrincipal)p);
+                if(log.isDebugEnabled())
+                    log.debug(sm.getString("deltaRequest.showPrincipal", p.getName() , getSessionId()));
+            } else
+                log.error(sm.getString("deltaRequest.wrongPrincipalClass",p.getClass().getName()));
+        }
+        addAction(TYPE_PRINCIPAL,action,NAME_PRINCIPAL,sp);
+    }
+
+    public void setNew(boolean n) {
+        int action = ACTION_SET;
+        addAction(TYPE_ISNEW,action,NAME_ISNEW,new Boolean(n));
+    }
+
+    protected synchronized void addAction(int type,
+                             int action,
+                             String name,
+                             Object value) {
+        AttributeInfo info = null;
+        if ( this.actionPool.size() > 0 ) {
+            try {
+                info = (AttributeInfo) actionPool.removeFirst();
+            }catch ( Exception x ) {
+                log.error("Unable to remove element:",x);
+                info = new AttributeInfo(type, action, name, value);
+            }
+            info.init(type,action,name,value);
+        } else {
+            info = new AttributeInfo(type, action, name, value);
+        }
+        //if we have already done something to this attribute, make sure
+        //we don't send multiple actions across the wire
+        if ( !recordAllActions) {
+            try {
+                actions.remove(info);
+            } catch (java.util.NoSuchElementException x) {
+                //do nothing, we wanted to remove it anyway
+            }
+        }
+        //add the action
+        actions.addLast(info);
+    }
+    
+    public void execute(DeltaSession session) {
+        execute(session,true);
+    }
+
+    public synchronized void execute(DeltaSession session, boolean notifyListeners) {
+        if ( !this.sessionId.equals( session.getId() ) )
+            throw new java.lang.IllegalArgumentException("Session id mismatch, not executing the delta request");
+        session.access();
+        for ( int i=0; i<actions.size(); i++ ) {
+            AttributeInfo info = (AttributeInfo)actions.get(i);
+            switch ( info.getType() ) {
+                case TYPE_ATTRIBUTE: {
+                    if ( info.getAction() == ACTION_SET ) {
+                        session.setAttribute(info.getName(), info.getValue(),notifyListeners,false);
+                    }  else
+                        session.removeAttribute(info.getName(),notifyListeners,false);
+                    break;
+                }//case
+                case TYPE_ISNEW: {
+                    session.setNew(((Boolean)info.getValue()).booleanValue(),false);
+                    break;
+                }//case
+                case TYPE_MAXINTERVAL: {
+                    session.setMaxInactiveInterval(((Integer)info.getValue()).intValue(),false);
+                    break;
+                }//case
+                case TYPE_PRINCIPAL: {
+                    Principal p = null;
+                    if ( info.getAction() == ACTION_SET ) {
+                        SerializablePrincipal sp = (SerializablePrincipal)info.getValue();
+                        p = (Principal)sp.getPrincipal(session.getManager().getContainer().getRealm());
+                    }
+                    session.setPrincipal(p,false);
+                    break;
+                }//case
+                default : throw new java.lang.IllegalArgumentException("Invalid attribute info type="+info);
+            }//switch
+        }//for
+        session.endAccess();
+        reset();
+    }
+
+    public synchronized void reset() {
+        while ( actions.size() > 0 ) {
+            try {
+                AttributeInfo info = (AttributeInfo) actions.removeFirst();
+                info.recycle();
+                actionPool.addLast(info);
+            }catch  ( Exception x ) {
+                log.error("Unable to remove element",x);
+            }
+        }
+        actions.clear();
+    }
+    
+    public String getSessionId() {
+        return sessionId;
+    }
+    public void setSessionId(String sessionId) {
+        this.sessionId = sessionId;
+        if ( sessionId == null ) {
+            new Exception("Session Id is null for setSessionId").fillInStackTrace().printStackTrace();
+        }
+    }
+    public int getSize() {
+        return actions.size();
+    }
+    
+    public synchronized void clear() {
+        actions.clear();
+        actionPool.clear();
+    }
+    
+    public synchronized void readExternal(java.io.ObjectInput in) throws java.io.IOException,
+        java.lang.ClassNotFoundException {
+        //sessionId - String
+        //recordAll - boolean
+        //size - int
+        //AttributeInfo - in an array
+        reset();
+        sessionId = in.readUTF();
+        recordAllActions = in.readBoolean();
+        int cnt = in.readInt();
+        if (actions == null)
+            actions = new LinkedList();
+        else
+            actions.clear();
+        for (int i = 0; i < cnt; i++) {
+            AttributeInfo info = null;
+            if (this.actionPool.size() > 0) {
+                try {
+                    info = (AttributeInfo) actionPool.removeFirst();
+                } catch ( Exception x )  {
+                    log.error("Unable to remove element",x);
+                    info = new AttributeInfo(-1,-1,null,null);
+                }
+            }
+            else {
+                info = new AttributeInfo(-1,-1,null,null);
+            }
+            info.readExternal(in);
+            actions.addLast(info);
+        }//for
+    }
+        
+
+
+    public synchronized void writeExternal(java.io.ObjectOutput out ) throws java.io.IOException {
+        //sessionId - String
+        //recordAll - boolean
+        //size - int
+        //AttributeInfo - in an array
+        out.writeUTF(getSessionId());
+        out.writeBoolean(recordAllActions);
+        out.writeInt(getSize());
+        for ( int i=0; i<getSize(); i++ ) {
+            AttributeInfo info = (AttributeInfo)actions.get(i);
+            info.writeExternal(out);
+        }
+    }
+    
+    private static class AttributeInfo implements java.io.Externalizable {
+        private String name = null;
+        private Object value = null;
+        private int action;
+        private int type;
+
+        public AttributeInfo() {}
+
+        public AttributeInfo(int type,
+                             int action,
+                             String name,
+                             Object value) {
+            super();
+            init(type,action,name,value);
+        }
+
+        public void init(int type,
+                         int action,
+                         String name,
+                         Object value) {
+            this.name = name;
+            this.value = value;
+            this.action = action;
+            this.type = type;
+        }
+
+        public int getType() {
+            return type;
+        }
+
+        public int getAction() {
+            return action;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+        public int hashCode() {
+            return name.hashCode();
+        }
+
+        public String getName() {
+            return name;
+        }
+        
+        public void recycle() {
+            name = null;
+            value = null;
+            type=-1;
+            action=-1;
+        }
+
+        public boolean equals(Object o) {
+            if ( ! (o instanceof AttributeInfo ) ) return false;
+            AttributeInfo other =  (AttributeInfo)o;
+            return other.getName().equals(this.getName());
+        }
+        
+        public synchronized void readExternal(java.io.ObjectInput in ) throws java.io.IOException,
+            java.lang.ClassNotFoundException {
+            //type - int
+            //action - int
+            //name - String
+            //value - object
+            type = in.readInt();
+            action = in.readInt();
+            name = in.readUTF();
+            value = in.readObject();
+        }
+
+        public synchronized void writeExternal(java.io.ObjectOutput out) throws java.io.
+            IOException {
+            //type - int
+            //action - int
+            //name - String
+            //value - object
+            out.writeInt(getType());
+            out.writeInt(getAction());
+            out.writeUTF(getName());
+            out.writeObject(getValue());
+        }
+        
+        public String toString() {
+            StringBuffer buf = new StringBuffer("AttributeInfo[type=");
+            buf.append(getType()).append(", action=").append(getAction());
+            buf.append(", name=").append(getName()).append(", value=").append(getValue());
+            buf.append(", addr=").append(super.toString()).append("]");
+            return buf.toString();
+        }
+
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSession.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSession.java
new file mode 100644
index 0000000..e439a1d
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSession.java
@@ -0,0 +1,1705 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.session;
+
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.SessionListener;
+import org.apache.catalina.cluster.ClusterSession;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * 
+ * Similar to the StandardSession, this code is identical, but for update and
+ * some small issues, simply copied in the first release. This session will keep
+ * track of deltas during a request.
+ * <p>
+ * <b>IMPLEMENTATION NOTE </b>: An instance of this class represents both the
+ * internal (Session) and application level (HttpSession) view of the session.
+ * However, because the class itself is not declared public, Java logic outside
+ * of the <code>org.apache.catalina.session</code> package cannot cast an
+ * HttpSession view of this instance back to a Session view.
+ * <p>
+ * <b>IMPLEMENTATION NOTE </b>: If you add fields to this class, you must make
+ * sure that you carry them over in the read/writeObject methods so that this
+ * class is properly serialized.
+ * 
+ * @author Filip Hanik
+ * @author Craig R. McClanahan
+ * @author Sean Legassick
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens </a>
+ * @version $Revision$ $Date$
+ */
+
+public class DeltaSession implements HttpSession, Session, Serializable,
+        ClusterSession {
+
+    public static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(DeltaManager.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager smp = StringManager
+            .getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The dummy attribute value serialized when a NotSerializableException is
+     * encountered in <code>writeObject()</code>.
+     */
+    private static final String NOT_SERIALIZED = "___NOT_SERIALIZABLE_EXCEPTION___";
+
+    /**
+     * The collection of user data attributes associated with this Session.
+     */
+    private HashMap attributes = new HashMap();
+
+    /**
+     * The authentication type used to authenticate our cached Principal, if
+     * any. NOTE: This value is not included in the serialized version of this
+     * object.
+     */
+    private transient String authType = null;
+
+    /**
+     * The <code>java.lang.Method</code> for the
+     * <code>fireContainerEvent()</code> method of the
+     * <code>org.apache.catalina.core.StandardContext</code> method, if our
+     * Context implementation is of this class. This value is computed
+     * dynamically the first time it is needed, or after a session reload (since
+     * it is declared transient).
+     */
+    private transient Method containerEventMethod = null;
+
+    /**
+     * The method signature for the <code>fireContainerEvent</code> method.
+     */
+    private static final Class containerEventTypes[] = { String.class,
+            Object.class };
+
+    /**
+     * The time this session was created, in milliseconds since midnight,
+     * January 1, 1970 GMT.
+     */
+    private long creationTime = 0L;
+
+    /**
+     * The debugging detail level for this component. NOTE: This value is not
+     * included in the serialized version of this object.
+     */
+    private transient int debug = 0;
+
+    /**
+     * We are currently processing a session expiration, so bypass certain
+     * IllegalStateException tests. NOTE: This value is not included in the
+     * serialized version of this object.
+     */
+    private transient boolean expiring = false;
+
+    /**
+     * The facade associated with this session. NOTE: This value is not included
+     * in the serialized version of this object.
+     */
+    private transient DeltaSessionFacade facade = null;
+
+    /**
+     * The session identifier of this Session.
+     */
+    private String id = null;
+
+    /**
+     * Descriptive information describing this Session implementation.
+     */
+    private static final String info = "DeltaSession/1.0";
+
+    /**
+     * The last accessed time for this Session.
+     */
+    private long lastAccessedTime = creationTime;
+
+    /**
+     * The session event listeners for this Session.
+     */
+    private transient ArrayList listeners = new ArrayList();
+
+    /**
+     * The Manager with which this Session is associated.
+     */
+    private transient Manager manager = null;
+
+    /**
+     * The maximum time interval, in seconds, between client requests before the
+     * servlet container may invalidate this session. A negative time indicates
+     * that the session should never time out.
+     */
+    private int maxInactiveInterval = -1;
+
+    /**
+     * Flag indicating whether this session is new or not.
+     */
+    private boolean isNew = false;
+
+    /**
+     * Flag indicating whether this session is valid or not.
+     */
+    protected boolean isValid = false;
+
+    /**
+     * Internal notes associated with this session by Catalina components and
+     * event listeners. <b>IMPLEMENTATION NOTE: </b> This object is <em>not</em>
+     * saved and restored across session serializations!
+     */
+    private transient HashMap notes = new HashMap();
+
+    /**
+     * The authenticated Principal associated with this session, if any.
+     * <b>IMPLEMENTATION NOTE: </b> This object is <i>not </i> saved and
+     * restored across session serializations!
+     */
+    private transient Principal principal = null;
+
+    /**
+     * The string manager for this package.
+     */
+    private static StringManager sm = StringManager
+            .getManager(Constants.Package);
+
+    /**
+     * The HTTP session context associated with this session.
+     */
+    private static HttpSessionContext sessionContext = null;
+
+    /**
+     * The property change support for this component. NOTE: This value is not
+     * included in the serialized version of this object.
+     */
+    private transient PropertyChangeSupport support = new PropertyChangeSupport(
+            this);
+
+    /**
+     * The current accessed time for this session.
+     */
+    private long thisAccessedTime = creationTime;
+
+    /**
+     * only the primary session will expire, or be able to expire due to
+     * inactivity. This is set to false as soon as I receive this session over
+     * the wire in a session message. That means that someone else has made a
+     * request on another server.
+     */
+    private transient boolean isPrimarySession = true;
+
+    /**
+     * The delta request contains all the action info
+     *  
+     */
+    private transient DeltaRequest deltaRequest = null;
+
+    /**
+     * Last time the session was replicatd, used for distributed expiring of
+     * session
+     */
+    private transient long lastTimeReplicated = System.currentTimeMillis();
+
+    /**
+     * The access count for this session
+     */
+    protected transient int accessCount = 0;
+
+    // ----------------------------------------------------------- Constructors
+    
+    /**
+     * Construct a new Session associated with the specified Manager.
+     * 
+     * @param manager
+     *            The manager with which this Session is associated
+     */
+    public DeltaSession(Manager manager) {
+
+        super();
+        this.manager = manager;
+        this.resetDeltaRequest();
+    }
+
+    // ----------------------------------------------------- Session Properties
+
+    /**
+     * returns true if this session is the primary session, if that is the case,
+     * the manager can expire it upon timeout.
+     */
+    public boolean isPrimarySession() {
+        return isPrimarySession;
+    }
+
+    /**
+     * Sets whether this is the primary session or not.
+     * 
+     * @param primarySession
+     *            Flag value
+     */
+    public void setPrimarySession(boolean primarySession) {
+        this.isPrimarySession = primarySession;
+    }
+
+    /**
+     * Return the authentication type used to authenticate our cached Principal,
+     * if any.
+     */
+    public String getAuthType() {
+
+        return (this.authType);
+
+    }
+
+    /**
+     * Set the authentication type used to authenticate our cached Principal, if
+     * any.
+     * 
+     * @param authType
+     *            The new cached authentication type
+     */
+    public void setAuthType(String authType) {
+
+        String oldAuthType = this.authType;
+        this.authType = authType;
+        support.firePropertyChange("authType", oldAuthType, this.authType);
+
+    }
+
+    /**
+     * Set the creation time for this session. This method is called by the
+     * Manager when an existing Session instance is reused.
+     * 
+     * @param time
+     *            The new creation time
+     */
+    public void setCreationTime(long time) {
+
+        this.creationTime = time;
+        this.lastAccessedTime = time;
+        this.thisAccessedTime = time;
+
+    }
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getId() {
+
+        if ( !isValid() ) {
+            throw new IllegalStateException
+            (sm.getString("standardSession.getId.ise"));
+        }
+
+        return (this.id);
+
+    }
+
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getIdInternal() {
+
+        return (this.id);
+
+    }
+
+
+    /**
+     * Set the session identifier for this session.
+     * 
+     * @param id
+     *            The new session identifier
+     */
+    public void setId(String id) {
+
+        if ((this.id != null) && (manager != null))
+            manager.remove(this);
+
+        this.id = id;
+
+        if (manager != null)
+            manager.add(this);
+        tellNew();
+        if ( deltaRequest == null ) resetDeltaRequest();
+        else deltaRequest.setSessionId(id);
+    }
+
+    /**
+     * Inform the listeners about the new session.
+     *  
+     */
+    public void tellNew() {
+
+        // Notify interested session event listeners
+        fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
+
+        // Notify interested application event listeners
+        Context context = (Context) manager.getContainer();
+        //fix for standalone manager without container
+        if (context != null) {
+            Object listeners[] = context.getApplicationLifecycleListeners();
+            if (listeners != null) {
+                HttpSessionEvent event = new HttpSessionEvent(getSession());
+                for (int i = 0; i < listeners.length; i++) {
+                    if (!(listeners[i] instanceof HttpSessionListener))
+                        continue;
+                    HttpSessionListener listener = (HttpSessionListener) listeners[i];
+                    try {
+                        fireContainerEvent(context, "beforeSessionCreated",
+                                listener);
+                        listener.sessionCreated(event);
+                        fireContainerEvent(context, "afterSessionCreated",
+                                listener);
+                    } catch (Throwable t) {
+                        try {
+                            fireContainerEvent(context, "afterSessionCreated",
+                                    listener);
+                        } catch (Exception e) {
+                            ;
+                        }
+                        // FIXME - should we do anything besides log these?
+                        log.error(sm.getString("standardSession.sessionEvent"),
+                                t);
+                    }
+                }
+            }
+        }//end if
+        //end fix
+
+    }
+
+    /**
+     * Return descriptive information about this Session implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * Return the last time the client sent a request associated with this
+     * session, as the number of milliseconds since midnight, January 1, 1970
+     * GMT. Actions that your application takes, such as getting or setting a
+     * value associated with the session, do not affect the access time.
+     */
+    public long getLastAccessedTime() {
+        if (!isValid) {
+            throw new IllegalStateException(sm
+                    .getString("standardSession.getLastAccessedTime"));
+
+        }
+        return (this.lastAccessedTime);
+
+    }
+
+    /**
+     * Return the Manager within which this Session is valid.
+     */
+    public Manager getManager() {
+
+        return (this.manager);
+
+    }
+
+    /**
+     * Set the Manager within which this Session is valid.
+     * 
+     * @param manager
+     *            The new Manager
+     */
+    public void setManager(Manager manager) {
+
+        this.manager = manager;
+
+    }
+
+    /**
+     * Return the maximum time interval, in seconds, between client requests
+     * before the servlet container will invalidate the session. A negative time
+     * indicates that the session should never time out.
+     */
+    public int getMaxInactiveInterval() {
+
+        return (this.maxInactiveInterval);
+
+    }
+
+    /**
+     * Set the maximum time interval, in seconds, between client requests before
+     * the servlet container will invalidate the session. A negative time
+     * indicates that the session should never time out.
+     * 
+     * @param interval
+     *            The new maximum interval
+     */
+    public void setMaxInactiveInterval(int interval) {
+        setMaxInactiveInterval(interval, true);
+    }
+
+    public void setMaxInactiveInterval(int interval, boolean addDeltaRequest) {
+
+        this.maxInactiveInterval = interval;
+        if (isValid && interval == 0) {
+            expire();
+        } else {
+            if (addDeltaRequest && (deltaRequest != null))
+                deltaRequest.setMaxInactiveInterval(interval);
+        }
+
+    }
+
+    /**
+     * Set the <code>isNew</code> flag for this session.
+     * 
+     * @param isNew
+     *            The new value for the <code>isNew</code> flag
+     */
+    public void setNew(boolean isNew) {
+        setNew(isNew, true);
+    }
+
+    public void setNew(boolean isNew, boolean addDeltaRequest) {
+        this.isNew = isNew;
+        if (addDeltaRequest && (deltaRequest != null))
+            deltaRequest.setNew(isNew);
+    }
+
+    /**
+     * Return the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request. If there is
+     * no current associated Principal, return <code>null</code>.
+     */
+    public Principal getPrincipal() {
+
+        return (this.principal);
+
+    }
+
+    /**
+     * Set the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.
+     * 
+     * @param principal
+     *            The new Principal, or <code>null</code> if none
+     */
+    public void setPrincipal(Principal principal) {
+        setPrincipal(principal, true);
+    }
+
+    public void setPrincipal(Principal principal, boolean addDeltaRequest) {
+        Principal oldPrincipal = this.principal;
+        this.principal = principal;
+        support.firePropertyChange("principal", oldPrincipal, this.principal);
+        if (addDeltaRequest && (deltaRequest != null))
+            deltaRequest.setPrincipal(principal);
+    }
+
+    /**
+     * Return the <code>HttpSession</code> for which this object is the
+     * facade.
+     */
+    public HttpSession getSession() {
+
+        if (facade == null) {
+            if (System.getSecurityManager() != null) {
+                final DeltaSession fsession = this;
+                facade = (DeltaSessionFacade) AccessController
+                        .doPrivileged(new PrivilegedAction() {
+                            public Object run() {
+                                return new DeltaSessionFacade(fsession);
+                            }
+                        });
+            } else {
+                facade = new DeltaSessionFacade(this);
+            }
+        }
+        return (facade);
+
+    }
+
+    /**
+     * Return the <code>isValid</code> flag for this session.
+     */
+    public boolean isValid() {
+
+        if (this.expiring) {
+            return true;
+        }
+
+        if (!this.isValid) {
+            return false;
+        }
+
+        if (accessCount > 0) {
+            return true;
+        }
+
+        if (maxInactiveInterval >= 0) {
+            long timeNow = System.currentTimeMillis();
+            int timeIdle = (int) ((timeNow - lastAccessedTime) / 1000L);
+            if (isPrimarySession()) {
+                if(timeIdle >= maxInactiveInterval) {
+                    expire(true);
+                }
+            } else {
+                if (timeIdle >= (2 * maxInactiveInterval)) {
+                //if the session has been idle twice as long as allowed,
+                //the primary session has probably crashed, and no other
+                //requests are coming in. that is why we do this. otherwise
+                //we would have a memory leak
+                    expire(true, false);
+                }
+            }
+        }
+
+        return (this.isValid);
+    }
+
+    /**
+     * Set the <code>isValid</code> flag for this session.
+     * 
+     * @param isValid
+     *            The new value for the <code>isValid</code> flag
+     */
+    public void setValid(boolean isValid) {
+
+        this.isValid = isValid;
+    }
+
+    // ------------------------------------------------- Session Public Methods
+
+    /**
+     * Update the accessed time information for this session. This method should
+     * be called by the context when a request comes in for a particular
+     * session, even if the application does not reference it.
+     */
+    public void access() {
+
+        this.lastAccessedTime = this.thisAccessedTime;
+        this.thisAccessedTime = System.currentTimeMillis();
+
+        evaluateIfValid();
+
+        accessCount++;
+    }
+
+    public void endAccess() {
+        isNew = false;
+        accessCount--;
+    }
+
+    /**
+     * Add a session event listener to this component.
+     */
+    public void addSessionListener(SessionListener listener) {
+
+        synchronized (listeners) {
+            listeners.add(listener);
+        }
+
+    }
+
+    /**
+     * Perform the internal processing required to invalidate this session,
+     * without triggering an exception if the session has already expired.
+     */
+    public void expire() {
+
+        expire(true);
+
+    }
+
+    /**
+     * Perform the internal processing required to invalidate this session,
+     * without triggering an exception if the session has already expired.
+     * 
+     * @param notify
+     *            Should we notify listeners about the demise of this session?
+     */
+    public void expire(boolean notify) {
+        expire(notify, true);
+    }
+
+    public void expire(boolean notify, boolean notifyCluster) {
+
+        // Mark this session as "being expired" if needed
+        if (expiring)
+            return;
+        String expiredId = getIdInternal();
+
+        synchronized (this) {
+
+            if (manager == null)
+                return;
+
+            expiring = true;
+
+            // Notify interested application event listeners
+            // FIXME - Assumes we call listeners in reverse order
+            Context context = (Context) manager.getContainer();
+            //fix for standalone manager without container
+            if (context != null) {
+                Object listeners[] = context.getApplicationLifecycleListeners();
+                if (notify && (listeners != null)) {
+                    HttpSessionEvent event = new HttpSessionEvent(getSession());
+                    for (int i = 0; i < listeners.length; i++) {
+                        int j = (listeners.length - 1) - i;
+                        if (!(listeners[j] instanceof HttpSessionListener))
+                            continue;
+                        HttpSessionListener listener = (HttpSessionListener) listeners[j];
+                        try {
+                            fireContainerEvent(context,
+                                    "beforeSessionDestroyed", listener);
+                            listener.sessionDestroyed(event);
+                            fireContainerEvent(context,
+                                    "afterSessionDestroyed", listener);
+                        } catch (Throwable t) {
+                            try {
+                                fireContainerEvent(context,
+                                        "afterSessionDestroyed", listener);
+                            } catch (Exception e) {
+                                ;
+                            }
+                            // FIXME - should we do anything besides log these?
+                            log.error(sm
+                                    .getString("standardSession.sessionEvent"),
+                                    t);
+                        }
+                    }
+                }
+            }//end if
+            //end fix
+            accessCount = 0;
+            setValid(false);
+
+            // Remove this session from our manager's active sessions
+            if (manager != null)
+                manager.remove(this);
+
+            // Notify interested session event listeners
+            if (notify) {
+                fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
+            }
+
+            // We have completed expire of this session
+            expiring = false;
+
+            // Unbind any objects associated with this session
+            String keys[] = keys();
+            for (int i = 0; i < keys.length; i++)
+                removeAttributeInternal(keys[i], notify, false);
+
+            if (notifyCluster) {
+                if (log.isDebugEnabled())
+                    log.debug(smp.getString("deltaSession.notifying",
+                            ((DeltaManager) manager).getName(), new Boolean(
+                                    isPrimarySession()), expiredId));
+                ((DeltaManager) manager).sessionExpired(expiredId);
+            }
+
+        }
+
+    }
+
+    /**
+     * Return the object bound with the specified name to the internal notes for
+     * this session, or <code>null</code> if no such binding exists.
+     * 
+     * @param name
+     *            Name of the note to be returned
+     */
+    public Object getNote(String name) {
+
+        synchronized (notes) {
+            return (notes.get(name));
+        }
+
+    }
+
+    /**
+     * Return an Iterator containing the String names of all notes bindings that
+     * exist for this session.
+     */
+    public Iterator getNoteNames() {
+
+        synchronized (notes) {
+            return (notes.keySet().iterator());
+        }
+
+    }
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle() {
+
+        // Reset the instance variables associated with this Session
+        attributes.clear();
+        setAuthType(null);
+        creationTime = 0L;
+        expiring = false;
+        id = null;
+        lastAccessedTime = 0L;
+        maxInactiveInterval = -1;
+        accessCount = 0;
+        notes.clear();
+        setPrincipal(null);
+        isNew = false;
+        isValid = false;
+        manager = null;
+        deltaRequest.clear();
+
+    }
+
+    /**
+     * Remove any object bound to the specified name in the internal notes for
+     * this session.
+     * 
+     * @param name
+     *            Name of the note to be removed
+     */
+    public void removeNote(String name) {
+
+        synchronized (notes) {
+            notes.remove(name);
+        }
+
+    }
+
+    /**
+     * Remove a session event listener from this component.
+     */
+    public void removeSessionListener(SessionListener listener) {
+
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
+
+    }
+
+    /**
+     * Bind an object to a specified name in the internal notes associated with
+     * this session, replacing any existing binding for this name.
+     * 
+     * @param name
+     *            Name to which the object should be bound
+     * @param value
+     *            Object to be bound to the specified name
+     */
+    public void setNote(String name, Object value) {
+
+        synchronized (notes) {
+            notes.put(name, value);
+        }
+
+    }
+
+    /**
+     * Return a string representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer();
+        sb.append("StandardSession[");
+        sb.append(id);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    // ------------------------------------------------ Session Package Methods
+
+    /**
+     * Read a serialized version of the contents of this session object from the
+     * specified object input stream, without requiring that the StandardSession
+     * itself have been serialized.
+     * 
+     * @param stream
+     *            The object input stream to read from
+     * 
+     * @exception ClassNotFoundException
+     *                if an unknown class is specified
+     * @exception IOException
+     *                if an input/output error occurs
+     */
+    public void readObjectData(ObjectInputStream stream)
+            throws ClassNotFoundException, IOException {
+
+        readObject(stream);
+
+    }
+
+    /**
+     * Write a serialized version of the contents of this session object to the
+     * specified object output stream, without requiring that the
+     * StandardSession itself have been serialized.
+     * 
+     * @param stream
+     *            The object output stream to write to
+     * 
+     * @exception IOException
+     *                if an input/output error occurs
+     */
+    public void writeObjectData(ObjectOutputStream stream) throws IOException {
+
+        writeObject(stream);
+
+    }
+
+    public void resetDeltaRequest() {
+        if (deltaRequest == null) {
+            deltaRequest = new DeltaRequest(getIdInternal(), false);
+        } else {
+            deltaRequest.reset();
+            deltaRequest.setSessionId(getIdInternal());
+        }
+    }
+
+    public DeltaRequest getDeltaRequest() {
+        if (deltaRequest == null)
+            resetDeltaRequest();
+        return deltaRequest;
+    }
+
+    // ------------------------------------------------- HttpSession Properties
+
+    /**
+     * Return the time when this session was created, in milliseconds since
+     * midnight, January 1, 1970 GMT.
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     */
+    public long getCreationTime() {
+
+        if (!expiring && !isValid)
+            throw new IllegalStateException(sm
+                    .getString("standardSession.getCreationTime.ise"));
+
+        return (this.creationTime);
+
+    }
+
+    /**
+     * Return the ServletContext to which this session belongs.
+     */
+    public ServletContext getServletContext() {
+
+        if (manager == null)
+            return (null);
+        Context context = (Context) manager.getContainer();
+        if (context == null)
+            return (null);
+        else
+            return (context.getServletContext());
+
+    }
+
+    /**
+     * Return the session context with which this session is associated.
+     * 
+     * @deprecated As of Version 2.1, this method is deprecated and has no
+     *             replacement. It will be removed in a future version of the
+     *             Java Servlet API.
+     */
+    public HttpSessionContext getSessionContext() {
+
+        if (sessionContext == null)
+            sessionContext = new StandardSessionContext();
+        return (sessionContext);
+
+    }
+
+    // ----------------------------------------------HttpSession Public Methods
+
+    /**
+     * Return the object bound with the specified name in this session, or
+     * <code>null</code> if no object is bound with that name.
+     * 
+     * @param name
+     *            Name of the attribute to be returned
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     */
+    public Object getAttribute(String name) {
+
+        if (!isValid())
+            throw new IllegalStateException(sm
+                    .getString("standardSession.getAttribute.ise"));
+
+        synchronized (attributes) {
+            return (attributes.get(name));
+        }
+
+    }
+
+    /**
+     * Return an <code>Enumeration</code> of <code>String</code> objects
+     * containing the names of the objects bound to this session.
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     */
+    public Enumeration getAttributeNames() {
+
+        if (!isValid())
+            throw new IllegalStateException(sm
+                    .getString("standardSession.getAttributeNames.ise"));
+
+        synchronized (attributes) {
+            return (new Enumerator(attributes.keySet(), true));
+        }
+
+    }
+
+    /**
+     * Return the object bound with the specified name in this session, or
+     * <code>null</code> if no object is bound with that name.
+     * 
+     * @param name
+     *            Name of the value to be returned
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     * 
+     * @deprecated As of Version 2.2, this method is replaced by
+     *             <code>getAttribute()</code>
+     */
+    public Object getValue(String name) {
+
+        return (getAttribute(name));
+
+    }
+
+    /**
+     * Return the set of names of objects bound to this session. If there are no
+     * such objects, a zero-length array is returned.
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     * 
+     * @deprecated As of Version 2.2, this method is replaced by
+     *             <code>getAttributeNames()</code>
+     */
+    public String[] getValueNames() {
+
+        if (!isValid())
+            throw new IllegalStateException(sm
+                    .getString("standardSession.getValueNames.ise"));
+
+        return (keys());
+
+    }
+
+    /**
+     * Invalidates this session and unbinds any objects bound to it.
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     */
+    public void invalidate() {
+
+        if (!isValid())
+            throw new IllegalStateException(sm
+                    .getString("standardSession.invalidate.ise"));
+
+        // Cause this session to expire
+        expire();
+
+    }
+
+    /**
+     * Return <code>true</code> if the client does not yet know about the
+     * session, or if the client chooses not to join the session. For example,
+     * if the server used only cookie-based sessions, and the client has
+     * disabled the use of cookies, then a session would be new on each request.
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     */
+    public boolean isNew() {
+
+        if (!isValid())
+            throw new IllegalStateException(sm
+                    .getString("standardSession.isNew.ise"));
+
+        return (this.isNew);
+
+    }
+
+    /**
+     * Bind an object to this session, using the specified name. If an object of
+     * the same name is already bound to this session, the object is replaced.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueBound()</code> on the object.
+     * 
+     * @param name
+     *            Name to which the object is bound, cannot be null
+     * @param value
+     *            Object to be bound, cannot be null
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     * 
+     * @deprecated As of Version 2.2, this method is replaced by
+     *             <code>setAttribute()</code>
+     */
+    public void putValue(String name, Object value) {
+
+        setAttribute(name, value);
+
+    }
+
+    /**
+     * Remove the object bound with the specified name from this session. If the
+     * session does not have an object bound with this name, this method does
+     * nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     * 
+     * @param name
+     *            Name of the object to remove from this session.
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     */
+    public void removeAttribute(String name) {
+
+        removeAttribute(name, true);
+
+    }
+
+    /**
+     * Remove the object bound with the specified name from this session. If the
+     * session does not have an object bound with this name, this method does
+     * nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     * 
+     * @param name
+     *            Name of the object to remove from this session.
+     * @param notify
+     *            Should we notify interested listeners that this attribute is
+     *            being removed?
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     */
+    public void removeAttribute(String name, boolean notify) {
+        removeAttribute(name, notify, true);
+    }
+
+    public void removeAttribute(String name, boolean notify,
+            boolean addDeltaRequest) {
+
+        // Validate our current state
+        if (!isValid())
+            throw new IllegalStateException(sm
+                    .getString("standardSession.removeAttribute.ise"));
+        removeAttributeInternal(name, notify, addDeltaRequest);
+    }
+
+    /**
+     * Remove the object bound with the specified name from this session. If the
+     * session does not have an object bound with this name, this method does
+     * nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     * 
+     * @param name
+     *            Name of the object to remove from this session.
+     * 
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     * 
+     * @deprecated As of Version 2.2, this method is replaced by
+     *             <code>removeAttribute()</code>
+     */
+    public void removeValue(String name) {
+
+        removeAttribute(name);
+
+    }
+
+    /**
+     * Bind an object to this session, using the specified name. If an object of
+     * the same name is already bound to this session, the object is replaced.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueBound()</code> on the object.
+     * 
+     * @param name
+     *            Name to which the object is bound, cannot be null
+     * @param value
+     *            Object to be bound, cannot be null
+     * 
+     * @exception IllegalArgumentException
+     *                if an attempt is made to add a non-serializable object in
+     *                an environment marked distributable.
+     * @exception IllegalStateException
+     *                if this method is called on an invalidated session
+     */
+    public void setAttribute(String name, Object value) {
+        setAttribute(name, value, true, true);
+    }
+
+    public void setAttribute(String name, Object value, boolean notify,
+            boolean addDeltaRequest) {
+
+        // Name cannot be null
+        if (name == null)
+            throw new IllegalArgumentException(sm
+                    .getString("standardSession.setAttribute.namenull"));
+
+        // Null value is the same as removeAttribute()
+        if (value == null) {
+            removeAttribute(name);
+            return;
+        }
+
+        if (!(value instanceof java.io.Serializable)) {
+            throw new IllegalArgumentException("Attribute [" + name
+                    + "] is not serializable");
+        }
+
+        if (addDeltaRequest && (deltaRequest != null))
+            deltaRequest.setAttribute(name, value);
+
+        // Validate our current state
+        if (!isValid())
+            throw new IllegalStateException(sm
+                    .getString("standardSession.setAttribute.ise"));
+        if ((manager != null) && manager.getDistributable()
+                && !(value instanceof Serializable))
+            throw new IllegalArgumentException(sm
+                    .getString("standardSession.setAttribute.iae"));
+
+        // Construct an event with the new value
+        HttpSessionBindingEvent event = null;
+
+        // Call the valueBound() method if necessary
+        if (value instanceof HttpSessionBindingListener && notify) {
+            event = new HttpSessionBindingEvent(getSession(), name, value);
+            try {
+                ((HttpSessionBindingListener) value).valueBound(event);
+            } catch (Exception x) {
+                log.error(smp.getString("deltaSession.valueBound.ex"), x);
+            }
+        }
+
+        // Replace or add this attribute
+        Object unbound = attributes.put(name, value);
+
+        // Call the valueUnbound() method if necessary
+        if ((unbound != null) && notify
+                && (unbound instanceof HttpSessionBindingListener)) {
+            try {
+                ((HttpSessionBindingListener) unbound)
+                        .valueUnbound(new HttpSessionBindingEvent(
+                                (HttpSession) getSession(), name));
+            } catch (Exception x) {
+                log.error(smp.getString("deltaSession.valueBinding.ex"), x);
+            }
+
+        }
+
+        //dont notify any listeners
+        if (!notify)
+            return;
+
+        // Notify interested application event listeners
+        Context context = (Context) manager.getContainer();
+        //fix for standalone manager without container
+        if (context != null) {
+            Object listeners[] = context.getApplicationEventListeners();
+            if (listeners == null)
+                return;
+            for (int i = 0; i < listeners.length; i++) {
+                if (!(listeners[i] instanceof HttpSessionAttributeListener))
+                    continue;
+                HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners[i];
+                try {
+                    if (unbound != null) {
+                        fireContainerEvent(context,
+                                "beforeSessionAttributeReplaced", listener);
+                        if (event == null) {
+                            event = new HttpSessionBindingEvent(getSession(),
+                                    name, unbound);
+                        }
+                        listener.attributeReplaced(event);
+                        fireContainerEvent(context,
+                                "afterSessionAttributeReplaced", listener);
+                    } else {
+                        fireContainerEvent(context,
+                                "beforeSessionAttributeAdded", listener);
+                        if (event == null) {
+                            event = new HttpSessionBindingEvent(getSession(),
+                                    name, unbound);
+                        }
+                        listener.attributeAdded(event);
+                        fireContainerEvent(context,
+                                "afterSessionAttributeAdded", listener);
+                    }
+                } catch (Throwable t) {
+                    try {
+                        if (unbound != null) {
+                            fireContainerEvent(context,
+                                    "afterSessionAttributeReplaced", listener);
+                        } else {
+                            fireContainerEvent(context,
+                                    "afterSessionAttributeAdded", listener);
+                        }
+                    } catch (Exception e) {
+                        ;
+                    }
+                    // FIXME - should we do anything besides log these?
+                    log
+                            .error(
+                                    sm
+                                            .getString("standardSession.attributeEvent"),
+                                    t);
+                }
+            } //for
+        }//end if
+        //end fix
+
+    }
+
+    // -------------------------------------------- HttpSession Private Methods
+
+    /**
+     * Read a serialized version of this session object from the specified
+     * object input stream.
+     * <p>
+     * <b>IMPLEMENTATION NOTE </b>: The reference to the owning Manager is not
+     * restored by this method, and must be set explicitly.
+     * 
+     * @param stream
+     *            The input stream to read from
+     * 
+     * @exception ClassNotFoundException
+     *                if an unknown class is specified
+     * @exception IOException
+     *                if an input/output error occurs
+     */
+    private void readObject(ObjectInputStream stream)
+            throws ClassNotFoundException, IOException {
+
+        // Deserialize the scalar instance variables (except Manager)
+        authType = null; // Transient only
+        creationTime = ((Long) stream.readObject()).longValue();
+        lastAccessedTime = ((Long) stream.readObject()).longValue();
+        maxInactiveInterval = ((Integer) stream.readObject()).intValue();
+        isNew = ((Boolean) stream.readObject()).booleanValue();
+        isValid = ((Boolean) stream.readObject()).booleanValue();
+        thisAccessedTime = ((Long) stream.readObject()).longValue();
+        boolean hasPrincipal = stream.readBoolean();
+        principal = null;
+        if (hasPrincipal) {
+            principal = SerializablePrincipal.readPrincipal(stream,
+                    getManager().getContainer().getRealm());
+        }
+
+        //        setId((String) stream.readObject());
+        id = (String) stream.readObject();
+        if (log.isDebugEnabled())
+            log.debug(smp.getString("deltaSession.readSession",  id));
+
+        // Deserialize the attribute count and attribute values
+        if (attributes == null)
+            attributes = new HashMap();
+        int n = ((Integer) stream.readObject()).intValue();
+        boolean isValidSave = isValid;
+        isValid = true;
+        for (int i = 0; i < n; i++) {
+            String name = (String) stream.readObject();
+            Object value = (Object) stream.readObject();
+            if ((value instanceof String) && (value.equals(NOT_SERIALIZED)))
+                continue;
+            //if (log.isTraceEnabled())
+            //    log.trace(smp.getString("deltaSession.readAttribute", id,name,value));
+            synchronized (attributes) {
+                attributes.put(name, value);
+            }
+        }
+        isValid = isValidSave;
+        
+        if (listeners == null) {
+            listeners = new ArrayList();
+        }
+        
+        if (notes == null) {
+            notes = new HashMap();
+        }
+    }
+
+    /**
+     * Write a serialized version of this session object to the specified object
+     * output stream.
+     * <p>
+     * <b>IMPLEMENTATION NOTE </b>: The owning Manager will not be stored in the
+     * serialized representation of this Session. After calling
+     * <code>readObject()</code>, you must set the associated Manager
+     * explicitly.
+     * <p>
+     * <b>IMPLEMENTATION NOTE </b>: Any attribute that is not Serializable will
+     * be unbound from the session, with appropriate actions if it implements
+     * HttpSessionBindingListener. If you do not want any such attributes, be
+     * sure the <code>distributable</code> property of the associated Manager
+     * is set to <code>true</code>.
+     * 
+     * @param stream
+     *            The output stream to write to
+     * 
+     * @exception IOException
+     *                if an input/output error occurs
+     */
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+
+        // Write the scalar instance variables (except Manager)
+        stream.writeObject(new Long(creationTime));
+        stream.writeObject(new Long(lastAccessedTime));
+        stream.writeObject(new Integer(maxInactiveInterval));
+        stream.writeObject(new Boolean(isNew));
+        stream.writeObject(new Boolean(isValid));
+        stream.writeObject(new Long(thisAccessedTime));
+        stream.writeBoolean(getPrincipal() != null);
+        if (getPrincipal() != null) {
+            SerializablePrincipal.writePrincipal((GenericPrincipal) principal,
+                    stream);
+        }
+
+        stream.writeObject(id);
+        if (log.isDebugEnabled())
+            log.debug(smp.getString("deltaSession.writeSession",id));
+
+        // Accumulate the names of serializable and non-serializable attributes
+        String keys[] = keys();
+        ArrayList saveNames = new ArrayList();
+        ArrayList saveValues = new ArrayList();
+        for (int i = 0; i < keys.length; i++) {
+            Object value = null;
+            synchronized (attributes) {
+                value = attributes.get(keys[i]);
+            }
+            if (value == null)
+                continue;
+            else if (value instanceof Serializable) {
+                saveNames.add(keys[i]);
+                saveValues.add(value);
+            }
+        }
+
+        // Serialize the attribute count and the Serializable attributes
+        int n = saveNames.size();
+        stream.writeObject(new Integer(n));
+        for (int i = 0; i < n; i++) {
+            stream.writeObject((String) saveNames.get(i));
+            try {
+                stream.writeObject(saveValues.get(i));
+                //                if (log.isDebugEnabled())
+                //                    log.debug(" storing attribute '" + saveNames.get(i) +
+                //                        "' with value '" + saveValues.get(i) + "'");
+            } catch (NotSerializableException e) {
+                log.error(sm.getString("standardSession.notSerializable",
+                        saveNames.get(i), id), e);
+                stream.writeObject(NOT_SERIALIZED);
+                log.error("  storing attribute '" + saveNames.get(i)
+                        + "' with value NOT_SERIALIZED");
+            }
+        }
+
+    }
+
+    private void evaluateIfValid() {
+        /*
+         * If this session has expired or is in the process of expiring or will
+         * never expire, return
+         */
+        if (!this.isValid || expiring || maxInactiveInterval < 0)
+            return;
+
+        isValid();
+
+    }
+
+    // -------------------------------------------------------- Private Methods
+
+    /**
+     * Fire container events if the Context implementation is the
+     * <code>org.apache.catalina.core.StandardContext</code>.
+     * 
+     * @param context
+     *            Context for which to fire events
+     * @param type
+     *            Event type
+     * @param data
+     *            Event data
+     * 
+     * @exception Exception
+     *                occurred during event firing
+     */
+    private void fireContainerEvent(Context context, String type, Object data)
+            throws Exception {
+
+        if (!"org.apache.catalina.core.StandardContext".equals(context
+                .getClass().getName())) {
+            return; // Container events are not supported
+        }
+        // NOTE: Race condition is harmless, so do not synchronize
+        if (containerEventMethod == null) {
+            containerEventMethod = context.getClass().getMethod(
+                    "fireContainerEvent", containerEventTypes);
+        }
+        Object containerEventParams[] = new Object[2];
+        containerEventParams[0] = type;
+        containerEventParams[1] = data;
+        containerEventMethod.invoke(context, containerEventParams);
+
+    }
+
+    /**
+     * Notify all session event listeners that a particular event has occurred
+     * for this Session. The default implementation performs this notification
+     * synchronously using the calling thread.
+     * 
+     * @param type
+     *            Event type
+     * @param data
+     *            Event data
+     */
+    public void fireSessionEvent(String type, Object data) {
+        if (listeners.size() < 1)
+            return;
+        SessionEvent event = new SessionEvent(this, type, data);
+        SessionListener list[] = new SessionListener[0];
+        synchronized (listeners) {
+            list = (SessionListener[]) listeners.toArray(list);
+        }
+
+        for (int i = 0; i < list.length; i++) {
+            ((SessionListener) list[i]).sessionEvent(event);
+        }
+
+    }
+
+    /**
+     * Return the names of all currently defined session attributes as an array
+     * of Strings. If there are no defined attributes, a zero-length array is
+     * returned.
+     */
+    protected String[] keys() {
+
+        String results[] = new String[0];
+        synchronized (attributes) {
+            return ((String[]) attributes.keySet().toArray(results));
+        }
+
+    }
+
+    /**
+     * Return the value of an attribute without a check for validity.
+     */
+    protected Object getAttributeInternal(String name) {
+
+        synchronized (attributes) {
+            return (attributes.get(name));
+        }
+
+    }
+
+    protected void removeAttributeInternal(String name, boolean notify,
+            boolean addDeltaRequest) {
+
+        // Remove this attribute from our collection
+        Object value = attributes.remove(name);
+        if (value == null)
+            return;
+
+        if (addDeltaRequest && (deltaRequest != null))
+            deltaRequest.removeAttribute(name);
+
+        // Do we need to do valueUnbound() and attributeRemoved() notification?
+        if (!notify) {
+            return;
+        }
+
+        // Call the valueUnbound() method if necessary
+        HttpSessionBindingEvent event = new HttpSessionBindingEvent(
+                (HttpSession) getSession(), name, value);
+        if ((value != null) && (value instanceof HttpSessionBindingListener))
+            try {
+                ((HttpSessionBindingListener) value).valueUnbound(event);
+            } catch (Exception x) {
+                log.error(smp.getString("deltaSession.valueUnbound.ex"), x);
+            }
+
+        // Notify interested application event listeners
+        Context context = (Context) manager.getContainer();
+        //fix for standalone manager without container
+        if (context != null) {
+            Object listeners[] = context.getApplicationEventListeners();
+            if (listeners == null)
+                return;
+            for (int i = 0; i < listeners.length; i++) {
+                if (!(listeners[i] instanceof HttpSessionAttributeListener))
+                    continue;
+                HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners[i];
+                try {
+                    fireContainerEvent(context,
+                            "beforeSessionAttributeRemoved", listener);
+                    listener.attributeRemoved(event);
+                    fireContainerEvent(context, "afterSessionAttributeRemoved",
+                            listener);
+                } catch (Throwable t) {
+                    try {
+                        fireContainerEvent(context,
+                                "afterSessionAttributeRemoved", listener);
+                    } catch (Exception e) {
+                        ;
+                    }
+                    // FIXME - should we do anything besides log these?
+                    log
+                            .error(
+                                    sm
+                                            .getString("standardSession.attributeEvent"),
+                                    t);
+                }
+            } //for
+        }//end if
+        //end fix
+
+    }
+
+    protected long getLastTimeReplicated() {
+        return lastTimeReplicated;
+    }
+
+    protected void setLastTimeReplicated(long lastTimeReplicated) {
+        this.lastTimeReplicated = lastTimeReplicated;
+    }
+
+    protected void setAccessCount(int accessCount) {
+        this.accessCount = accessCount;
+    }
+
+    protected int getAccessCount() {
+        return accessCount;
+    }
+
+}
+
+// -------------------------------------------------------------- Private Class
+
+/**
+ * This class is a dummy implementation of the <code>HttpSessionContext</code>
+ * interface, to conform to the requirement that such an object be returned when
+ * <code>HttpSession.getSessionContext()</code> is called.
+ * 
+ * @author Craig R. McClanahan
+ * 
+ * @deprecated As of Java Servlet API 2.1 with no replacement. The interface
+ *             will be removed in a future version of this API.
+ */
+
+final class StandardSessionContext implements HttpSessionContext {
+
+    private HashMap dummy = new HashMap();
+
+    /**
+     * Return the session identifiers of all sessions defined within this
+     * context.
+     * 
+     * @deprecated As of Java Servlet API 2.1 with no replacement. This method
+     *             must return an empty <code>Enumeration</code> and will be
+     *             removed in a future version of the API.
+     */
+    public Enumeration getIds() {
+
+        return (new Enumerator(dummy));
+
+    }
+
+    /**
+     * Return the <code>HttpSession</code> associated with the specified
+     * session identifier.
+     * 
+     * @param id
+     *            Session identifier for which to look up a session
+     * 
+     * @deprecated As of Java Servlet API 2.1 with no replacement. This method
+     *             must return null and will be removed in a future version of
+     *             the API.
+     */
+    public HttpSession getSession(String id) {
+
+        return (null);
+
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSessionFacade.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSessionFacade.java
new file mode 100644
index 0000000..1d6ab24
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSessionFacade.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.cluster.session;
+
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+
+
+/**
+ * Facade for the DeltaSession object.
+ *
+ * @author Remy Maucherat
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+
+public class DeltaSessionFacade
+    implements HttpSession {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new session facade.
+     */
+    public DeltaSessionFacade(DeltaSession session) {
+        super();
+        this.session = (HttpSession) session;
+    }
+
+
+    /**
+     * Construct a new session facade.
+     */
+    public DeltaSessionFacade(HttpSession session) {
+        super();
+        this.session = session;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Wrapped session object.
+     */
+    private HttpSession session = null;
+
+
+    // ---------------------------------------------------- HttpSession Methods
+
+
+    public long getCreationTime() {
+        return session.getCreationTime();
+    }
+
+
+    public String getId() {
+        return session.getId();
+    }
+
+
+    public long getLastAccessedTime() {
+        return session.getLastAccessedTime();
+    }
+
+
+    public ServletContext getServletContext() {
+        // FIXME : Facade this object ?
+        return session.getServletContext();
+    }
+
+
+    public void setMaxInactiveInterval(int interval) {
+        session.setMaxInactiveInterval(interval);
+    }
+
+
+    public int getMaxInactiveInterval() {
+        return session.getMaxInactiveInterval();
+    }
+
+
+    public HttpSessionContext getSessionContext() {
+        return session.getSessionContext();
+    }
+
+
+    public Object getAttribute(String name) {
+        return session.getAttribute(name);
+    }
+
+
+    public Object getValue(String name) {
+        return session.getAttribute(name);
+    }
+
+
+    public Enumeration getAttributeNames() {
+        return session.getAttributeNames();
+    }
+
+
+    public String[] getValueNames() {
+        return session.getValueNames();
+    }
+
+
+    public void setAttribute(String name, Object value) {
+        session.setAttribute(name, value);
+    }
+
+
+    public void putValue(String name, Object value) {
+        session.setAttribute(name, value);
+    }
+
+
+    public void removeAttribute(String name) {
+        session.removeAttribute(name);
+    }
+
+
+    public void removeValue(String name) {
+        session.removeAttribute(name);
+    }
+
+
+    public void invalidate() {
+        session.invalidate();
+    }
+
+
+    public boolean isNew() {
+        return session.isNew();
+    }
+
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/JvmRouteBinderValve.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/JvmRouteBinderValve.java
new file mode 100644
index 0000000..19b915c
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/JvmRouteBinderValve.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster.session;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterManager;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.ClusterValve;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.session.ManagerBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+
+/**
+ * Valve to handle Tomcat jvmRoute takeover using mod_jk module after node
+ * failure. After a node crashed the next request going to other cluster node.
+ * Now the answering from apache is slower ( make some error handshaking. Very
+ * bad with apache at my windows.). We rewrite now the cookie jsessionid
+ * information to the backup cluster node. After the next response all client
+ * request goes direct to the backup node. The change sessionid send also to all
+ * other cluster nodes. Well, now the session stickyness work directly to the
+ * backup node and traffic don't go back too restarted cluster nodes!
+ * 
+ * At all cluster node you must configure the as ClusterListener since 5.5.10
+ * {@link org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListener JvmRouteSessionIDBinderListener}
+ * or before with
+ * org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListenerLifecycle.
+ * 
+ * Add this Valve to your host definition at conf/server.xml .
+ * 
+ * Since 5.5.10 as direct cluster valve:<br/>
+ * <pre>
+ *  &lt;Cluster&gt;
+ *  &lt;Valve className=&quot;org.apache.catalina.cluster.session.JvmRouteBinderValve&quot; /&gt;  
+ *  &lt;/Cluster&gt;
+ * </pre>
+ * <br />
+ * Before 5.5.10 as Host element:<br/>
+ * <pre>
+ *  &lt;Hostr&gt;
+ *  &lt;Valve className=&quot;org.apache.catalina.cluster.session.JvmRouteBinderValve&quot; /&gt;  
+ *  &lt;/Hostr&gt;
+ * </pre>
+ * 
+ * Trick:<br/>
+ * You can enable this mod_jk turnover mode via JMX before you drop a node to all backup nodes!
+ * Set enable true on all JvmRouteBinderValve backups, disable worker at mod_jk 
+ * and then drop node and restart it! Then enable mod_jk Worker and disable JvmRouteBinderValves again. 
+ * This use case means that only requested session are migrated.
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class JvmRouteBinderValve extends ValveBase implements ClusterValve, Lifecycle {
+
+    /*--Static Variables----------------------------------------*/
+    public static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(JvmRouteBinderValve.class);
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info = "org.apache.catalina.cluster.session.JvmRouteBinderValve/1.2";
+
+    /*--Instance Variables--------------------------------------*/
+
+    /**
+     * the cluster
+     */
+    protected CatalinaCluster cluster;
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+    /**
+     * enabled this component
+     */
+    protected boolean enabled = true;
+
+    /**
+     * number of session that no at this tomcat instanz hosted
+     */
+    protected long numberOfSessions = 0;
+
+    protected String sessionIdAttribute = "org.apache.catalina.cluster.session.JvmRouteOrignalSessionID";
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+    /*--Logic---------------------------------------------------*/
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * set session id attribute to failed node for request.
+     * 
+     * @return Returns the sessionIdAttribute.
+     */
+    public String getSessionIdAttribute() {
+        return sessionIdAttribute;
+    }
+
+    /**
+     * get name of failed reqeust session attribute
+     * 
+     * @param sessionIdAttribute
+     *            The sessionIdAttribute to set.
+     */
+    public void setSessionIdAttribute(String sessionIdAttribute) {
+        this.sessionIdAttribute = sessionIdAttribute;
+    }
+
+    /**
+     * @return Returns the number of migrated sessions.
+     */
+    public long getNumberOfSessions() {
+        return numberOfSessions;
+    }
+
+    /**
+     * @return Returns the enabled.
+     */
+    public boolean getEnabled() {
+        return enabled;
+    }
+
+    /**
+     * @param enabled
+     *            The enabled to set.
+     */
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    /**
+     * Detect possible the JVMRoute change at cluster backup node..
+     * 
+     * @param request
+     *            tomcat request being processed
+     * @param response
+     *            tomcat response being processed
+     * @exception IOException
+     *                if an input/output error has occurred
+     * @exception ServletException
+     *                if a servlet error has occurred
+     */
+    public void invoke(Request request, Response response) throws IOException,
+            ServletException {
+
+         if (getEnabled() 
+             && getCluster() != null
+             && request.getContext() != null
+             && request.getContext().getDistributable() ) {
+             // valve cluster can access manager - other cluster handle turnover 
+             // at host level - hopefully!
+             Manager manager = request.getContext().getManager();
+             if (manager != null && manager instanceof ClusterManager
+                     && getCluster().getManager(((ClusterManager)manager).getName()) != null)
+                 handlePossibleTurnover(request, response);
+        }
+        // Pass this request on to the next valve in our pipeline
+        getNext().invoke(request, response);
+    }
+
+    /**
+     * handle possible session turn over.
+     * 
+     * @see JvmRouteBinderValve#handleJvmRoute(Request, Response, String, String)
+     * @param request current request
+     * @param response current response
+     */
+    protected void handlePossibleTurnover(Request request, Response response) {
+        Session session = request.getSessionInternal(false);
+        if (session != null) {
+            long t1 = System.currentTimeMillis();
+            String jvmRoute = getLocalJvmRoute(request);
+            if (jvmRoute == null) {
+                if (log.isWarnEnabled())
+                    log.warn(sm.getString("jvmRoute.missingJvmRouteAttribute"));
+                return;
+            }
+            if (request.isRequestedSessionIdFromURL()) {
+                if (log.isDebugEnabled())
+                    log.debug(sm.getString("jvmRoute.skipURLSessionIDs"));
+            } else {
+                handleJvmRoute( request, response,session.getIdInternal(), jvmRoute);
+            }
+            if (log.isDebugEnabled()) {
+                long t2 = System.currentTimeMillis();
+                long time = t2 - t1;
+                log.debug(sm.getString("jvmRoute.turnoverInfo", new Long(time)));
+            }
+        }
+    }
+
+    /**
+     * get jvmroute from engine
+     * 
+     * @param request current request
+     * @return return jvmRoute from ManagerBase or null
+     */
+    protected String getLocalJvmRoute(Request request) {
+        Manager manager = getManager(request);
+        if(manager instanceof ManagerBase)
+            return ((ManagerBase) manager).getJvmRoute();
+        return null ;
+    }
+
+    /**
+     * get Cluster DeltaManager
+     * 
+     * @param request current request
+     * @return manager or null
+     */
+    protected Manager getManager(Request request) {
+        Manager manager = request.getContext().getManager();
+        if (log.isDebugEnabled()) {
+            if(manager != null)
+                log.debug(sm.getString("jvmRoute.foundManager", manager,  request.getContext().getName()));
+            else 
+                log.debug(sm.getString("jvmRoute.notFoundManager", manager,  request.getContext().getName()));
+        }
+        return manager;
+    }
+
+    /**
+     * @return Returns the cluster.
+     */
+    public CatalinaCluster getCluster() {
+        return cluster;
+    }
+    
+    /**
+     * @param cluster The cluster to set.
+     */
+    public void setCluster(CatalinaCluster cluster) {
+        this.cluster = cluster;
+    }
+    
+    /**
+     * Handle jvmRoute stickyness after tomcat instance failed. After this
+     * correction a new Cookie send to client with new jvmRoute and the
+     * SessionID change propage to the other cluster nodes.
+     * 
+     * @param request current request
+     * @param response
+     *            Tomcat Response
+     * @param sessionId
+     *            request SessionID from Cookie
+     * @param localJvmRoute
+     *            local jvmRoute
+     */
+    protected void handleJvmRoute(
+            Request request, Response response,String sessionId, String localJvmRoute) {
+        // get requested jvmRoute.
+        String requestJvmRoute = null;
+        int index = sessionId.indexOf(".");
+        if (index > 0) {
+            requestJvmRoute = sessionId
+                    .substring(index + 1, sessionId.length());
+        }
+        if (requestJvmRoute != null && !requestJvmRoute.equals(localJvmRoute)) {
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("jvmRoute.failover", requestJvmRoute,
+                        localJvmRoute, sessionId));
+            }
+            // OK - turnover the session ?
+            String newSessionID = sessionId.substring(0, index) + "."
+                    + localJvmRoute;
+            Session catalinaSession = null;
+            try {
+                catalinaSession = getManager(request).findSession(sessionId);
+            } catch (IOException e) {
+                // Hups!
+            }
+            if (catalinaSession != null) {
+                changeSessionID(request, response, sessionId, newSessionID,
+                        catalinaSession);
+                numberOfSessions++;
+            } else {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("jvmRoute.cannotFindSession",
+                            sessionId));
+                }
+            }
+        }
+    }
+
+    /**
+     * change session id and send to all cluster nodes
+     * 
+     * @param request current request
+     * @param response current response
+     * @param sessionId
+     *            original session id
+     * @param newSessionID
+     *            new session id for node migration
+     * @param catalinaSession
+     *            current session with original session id
+     */
+    protected void changeSessionID(Request request,
+            Response response, String sessionId, String newSessionID, Session catalinaSession) {
+        lifecycle.fireLifecycleEvent("Before session migration",
+                catalinaSession);
+        request.setRequestedSessionId(newSessionID);
+        catalinaSession.setId(newSessionID);
+        if (catalinaSession instanceof DeltaSession)
+            ((DeltaSession) catalinaSession).resetDeltaRequest();
+        setNewSessionCookie(request, response,newSessionID);
+        // set orginal sessionid at request, to allow application detect the
+        // change
+        if (sessionIdAttribute != null && !"".equals(sessionIdAttribute)) {
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("jvmRoute.set.orignalsessionid",sessionIdAttribute,sessionId));
+            }
+            request.setAttribute(sessionIdAttribute, sessionId);
+        }
+        // now sending the change to all other clusternode!
+        ClusterManager manager = (ClusterManager)catalinaSession.getManager();
+        sendSessionIDClusterBackup(manager,request,sessionId, newSessionID);
+        lifecycle
+                .fireLifecycleEvent("After session migration", catalinaSession);
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("jvmRoute.changeSession", sessionId,
+                    newSessionID));
+        }
+    }
+
+    /**
+     * Send the changed Sessionid to all clusternodes.
+     * 
+     * @see JvmRouteSessionIDBinderListener#messageReceived(ClusterMessage)
+     * @param manager
+     *            ClusterManager
+     * @param sessionId
+     *            current failed sessionid
+     * @param newSessionID
+     *            new session id, bind to the new cluster node
+     */
+    protected void sendSessionIDClusterBackup(ClusterManager manager,Request request,String sessionId,
+            String newSessionID) {
+        SessionIDMessage msg = new SessionIDMessage();
+        msg.setOrignalSessionID(sessionId);
+        msg.setBackupSessionID(newSessionID);
+        Context context = request.getContext();
+        msg.setContextPath(context.getPath());
+        if(manager.isSendClusterDomainOnly())
+            cluster.sendClusterDomain(msg);
+        else
+            cluster.send(msg);
+    }
+
+    /**
+     * Sets a new cookie for the given session id and response and see
+     * {@link org.apache.catalina.connector.Request#configureSessionCookie(javax.servlet.http.Cookie)}
+     * 
+     * @param request current request
+     * @param response Tomcat Response
+     * @param sessionId The session id
+     */
+    protected void setNewSessionCookie(Request request,
+                                       Response response, String sessionId) {
+        if (response != null) {
+            Context context = request.getContext();
+            if (context.getCookies()) {
+                // set a new session cookie
+                Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME,
+                        sessionId);
+                newCookie.setMaxAge(-1);
+                String contextPath = null;
+                if (!response.getConnector().getEmptySessionPath()
+                        && (context != null)) {
+                    contextPath = context.getEncodedPath();
+                }
+                if ((contextPath != null) && (contextPath.length() > 0)) {
+                    newCookie.setPath(contextPath);
+                } else {
+                    newCookie.setPath("/");
+                }
+                if (request.isSecure()) {
+                    newCookie.setSecure(true);
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("jvmRoute.newSessionCookie",
+                            sessionId, Globals.SESSION_COOKIE_NAME, newCookie
+                                    .getPath(), new Boolean(newCookie
+                                    .getSecure())));
+                }
+                response.addCookie(newCookie);
+            }
+        }
+    }
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+    /**
+     * Add a lifecycle event listener to this component.
+     * 
+     * @param listener
+     *            The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     * 
+     * @param listener
+     *            The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component. This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     * 
+     * @exception LifecycleException
+     *                if this component detects a fatal error that prevents this
+     *                component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException(sm
+                    .getString("jvmRoute.valve.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+        if (cluster == null) {
+            Container hostContainer = getContainer();
+            // compatibility with JvmRouteBinderValve version 1.1
+            // ( setup at context.xml or context.xml.default )
+            if (!(hostContainer instanceof Host)) {
+                if (log.isWarnEnabled())
+                    log.warn(sm.getString("jvmRoute.configure.warn"));
+                hostContainer = hostContainer.getParent();
+            }
+            if (hostContainer instanceof Host
+                    && ((Host) hostContainer).getCluster() != null) {
+                cluster = (CatalinaCluster) ((Host) hostContainer).getCluster();
+            } else {
+                Container engine = hostContainer.getParent() ;
+                if (engine instanceof Engine
+                        && ((Engine) engine).getCluster() != null) {
+                    cluster = (CatalinaCluster) ((Engine) engine).getCluster();
+                }
+            }
+        }
+        if (cluster == null) {
+            throw new RuntimeException("No clustering support at container "
+                    + container.getName());
+        }
+        
+        if (log.isInfoEnabled())
+            log.info(sm.getString("jvmRoute.valve.started"));
+
+    }
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component. This method should be the last one called on a given instance
+     * of this component.
+     * 
+     * @exception LifecycleException
+     *                if this component detects a fatal error that needs to be
+     *                reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException(sm
+                    .getString("jvmRoute.valve.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+        cluster = null;
+        numberOfSessions = 0;
+        if (log.isInfoEnabled())
+            log.info(sm.getString("jvmRoute.valve.stopped"));
+
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/JvmRouteSessionIDBinderLifecycleListener.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/JvmRouteSessionIDBinderLifecycleListener.java
new file mode 100644
index 0000000..2c839da
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/JvmRouteSessionIDBinderLifecycleListener.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster.session;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.modelmbean.ModelMBean;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.MessageListener;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+
+/**
+ * Register new JvmRouteSessionIDBinderListener to receive Session ID changes.
+ * 
+ * add following at your server.xml Host section
+ * 
+ * <pre>
+ *        &lt;Host &gt;... 
+ *          &lt;Listener className=&quot;org.apache.catalina.cluster.session.JvmRouteSessionIDBinderLifecycleListener&quot; /&gt;
+ *          &lt;Cluster ...&gt;
+ *        &lt;/Host&gt;
+ * </pre>
+ * FIXME add Engine support
+ * @deprecated
+ * @author Peter Rossbach
+ */
+public class JvmRouteSessionIDBinderLifecycleListener implements
+        LifecycleListener {
+    private static Log log = LogFactory
+            .getLog(JvmRouteSessionIDBinderLifecycleListener.class);
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info = "org.apache.catalina.cluster.session.JvmRouteSessionIDBinderLifecycleListener/1.0";
+
+    /**
+     * The string resources for this package.
+     */
+    protected static final StringManager sm = StringManager
+            .getManager(Constants.Package);
+
+    private boolean enabled = true;
+
+    private MBeanServer mserver = null;
+
+    private Registry registry = null;
+
+    private MessageListener sessionMoverListener;
+
+    /*
+     * start and stop cluster
+     * 
+     * @see org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent)
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        if (enabled && event.getSource() instanceof StandardHost) {
+
+            if (Lifecycle.AFTER_START_EVENT.equals(event.getType())) {
+                if (log.isDebugEnabled())
+                    log.debug(sm.getString("jvmRoute.listener.started"));
+                startSessionIDListener((StandardHost) event.getSource());
+            } else if (Lifecycle.BEFORE_STOP_EVENT.equals(event.getType())) {
+                if (log.isDebugEnabled())
+                    log.debug(sm.getString("jvmRoute.listener.stopped"));
+                stopSessionIDListener((StandardHost) event.getSource());
+            }
+        }
+    }
+
+    /**
+     * stop sessionID binder at cluster
+     * 
+     * @param host
+     *            clustered host
+     */
+    protected void stopSessionIDListener(StandardHost host) {
+        if (sessionMoverListener != null) {
+            CatalinaCluster cluster = (CatalinaCluster) host.getCluster();
+            cluster.removeClusterListener(sessionMoverListener);
+            if (mserver != null) {
+                try {
+                    ObjectName objectName = getObjectName(host);
+                    mserver.unregisterMBean(objectName);
+                } catch (Exception e) {
+                    log.error(e);
+                }
+            }
+        }
+    }
+
+    /**
+     * @param host
+     * @return The object name
+     * @throws MalformedObjectNameException
+     */
+    protected ObjectName getObjectName(StandardHost host) throws MalformedObjectNameException {
+        ObjectName objectName = new ObjectName(
+                host.getDomain()
+                        + ":type=Listener,name=JvmRouteSessionIDBinderListener,host=" + host.getName());
+        return objectName;
+    }
+
+    /**
+     * start sessionID mover at cluster
+     * 
+     * @param host
+     *            clustered host
+     */
+    protected void startSessionIDListener(StandardHost host) {
+        try {
+            ObjectName objectName = null;
+            getMBeanServer();
+            objectName = getObjectName(host);
+            if (mserver.isRegistered(objectName)) {
+                if (log.isInfoEnabled())
+                    log.info(sm.getString("jvmRoute.run.already"));
+                return;
+            }
+            sessionMoverListener = new JvmRouteSessionIDBinderListener();
+            mserver.registerMBean(getManagedBean(sessionMoverListener),
+                    objectName);
+            CatalinaCluster cluster = (CatalinaCluster) host.getCluster();
+            sessionMoverListener.setCluster(cluster);
+            ((JvmRouteSessionIDBinderListener) sessionMoverListener).start();
+        } catch (Exception ex) {
+            log.error(ex.getMessage(), ex);
+        }
+    }
+
+    protected MBeanServer getMBeanServer() throws Exception {
+        if (mserver == null) {
+            if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
+                mserver = (MBeanServer) MBeanServerFactory
+                        .findMBeanServer(null).get(0);
+            } else {
+                mserver = MBeanServerFactory.createMBeanServer();
+            }
+            registry = Registry.getRegistry(null, null);
+            registry.loadMetadata(this.getClass().getResourceAsStream(
+                    "mbeans-descriptors.xml"));
+        }
+        return (mserver);
+    }
+
+    /**
+     * Returns the ModelMBean
+     * 
+     * @param object
+     *            The Object to get the ModelMBean for
+     * @return The ModelMBean
+     * @throws Exception
+     *             If an error occurs this constructors throws this exception
+     */
+    protected ModelMBean getManagedBean(Object object) throws Exception {
+        ModelMBean mbean = null;
+        if (registry != null) {
+            ManagedBean managedBean = registry.findManagedBean(object
+                    .getClass().getName());
+            mbean = managedBean.createMBean(object);
+        }
+        return mbean;
+    }
+
+    /**
+     * @return Returns the enabled.
+     */
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    /**
+     * @param enabled
+     *            The enabled to set.
+     */
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    /**
+     * Return descriptive information about this Listener implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/JvmRouteSessionIDBinderListener.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/JvmRouteSessionIDBinderListener.java
new file mode 100644
index 0000000..90e8908
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/JvmRouteSessionIDBinderListener.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.session;
+
+import java.io.IOException;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.core.StandardEngine;
+
+/**
+ * Receive SessionID cluster change from other backup node after primary session
+ * node is failed.
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class JvmRouteSessionIDBinderListener extends ClusterListener {
+ 
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info = "org.apache.catalina.session.JvmRouteSessionIDBinderListener/1.1";
+
+    //--Instance Variables--------------------------------------
+
+
+    protected boolean started = false;
+
+    /**
+     * number of session that goes to this cluster node
+     */
+    private long numberOfSessions = 0;
+
+    //--Constructor---------------------------------------------
+
+    public JvmRouteSessionIDBinderListener() {
+    }
+
+    //--Logic---------------------------------------------------
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * @return Returns the numberOfSessions.
+     */
+    public long getNumberOfSessions() {
+        return numberOfSessions;
+    }
+
+    /**
+     * Add this Mover as Cluster Listener ( receiver)
+     * 
+     * @throws LifecycleException
+     */
+    public void start() throws LifecycleException {
+        if (started)
+            return;
+        getCluster().addClusterListener(this);
+        started = true;
+        if (log.isInfoEnabled())
+            log.info(sm.getString("jvmRoute.clusterListener.started"));
+    }
+
+    /**
+     * Remove this from Cluster Listener
+     * 
+     * @throws LifecycleException
+     */
+    public void stop() throws LifecycleException {
+        started = false;
+        getCluster().removeClusterListener(this);
+        if (log.isInfoEnabled())
+            log.info(sm.getString("jvmRoute.clusterListener.stopped"));
+    }
+
+    /**
+     * Callback from the cluster, when a message is received, The cluster will
+     * broadcast it invoking the messageReceived on the receiver.
+     * 
+     * @param msg
+     *            ClusterMessage - the message received from the cluster
+     */
+    public void messageReceived(ClusterMessage msg) {
+        if (msg instanceof SessionIDMessage && msg != null) {
+            SessionIDMessage sessionmsg = (SessionIDMessage) msg;
+            if (log.isDebugEnabled())
+                log.debug(sm.getString(
+                        "jvmRoute.receiveMessage.sessionIDChanged", sessionmsg
+                                .getOrignalSessionID(), sessionmsg
+                                .getBackupSessionID(), sessionmsg
+                                .getContextPath()));
+            Container host = getCluster().getContainer();
+            if (host != null) {
+                Context context = (Context) host.findChild(sessionmsg
+                        .getContextPath());
+                if (context != null) {
+                    try {
+                        Session session = context.getManager().findSession(
+                                sessionmsg.getOrignalSessionID());
+                        if (session != null) {
+                            session.setId(sessionmsg.getBackupSessionID());
+                        } else if (log.isInfoEnabled())
+                            log.info(sm.getString("jvmRoute.lostSession",
+                                    sessionmsg.getOrignalSessionID(),
+                                    sessionmsg.getContextPath()));
+                    } catch (IOException e) {
+                        log.error(e);
+                    }
+
+                } else if (log.isErrorEnabled())
+                    log.error(sm.getString("jvmRoute.contextNotFound",
+                            sessionmsg.getContextPath(), ((StandardEngine) host
+                                    .getParent()).getJvmRoute()));
+            } else if (log.isErrorEnabled())
+                log.error(sm.getString("jvmRoute.hostNotFound", sessionmsg.getContextPath()));
+        }
+    }
+
+    /**
+     * Accept only SessionIDMessages
+     * 
+     * @param msg
+     *            ClusterMessage
+     * @return boolean - returns true to indicate that messageReceived should be
+     *         invoked. If false is returned, the messageReceived method will
+     *         not be invoked.
+     */
+    public boolean accept(ClusterMessage msg) {
+        return (msg instanceof SessionIDMessage);
+    }
+}
+
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/LocalStrings.properties b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/LocalStrings.properties
new file mode 100644
index 0000000..4a9301c
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/LocalStrings.properties
@@ -0,0 +1,78 @@
+deltaManager.createSession.ise=createSession: Too many active sessions
+deltaManager.createSession.newSession=Created a DeltaSession with Id [{0}] Total count={1}
+deltaManager.createMessage.access=Manager [{0}]: create session message [{1}] access.
+deltaManager.createMessage.accessChangePrimary=Manager [{0}]: create session message [{1}] access to change primary.
+deltaManager.createMessage.allSessionData=Manager [{0}] send all session data.
+deltaManager.createMessage.allSessionTransfered=Manager [{0}] send all session data transfered
+deltaManager.createMessage.delta=Manager [{0}]: create session message [{1}] delta request.
+deltaManager.createMessage.expire=Manager [{0}]: create session message [{1}] expire.
+deltaManager.createMessage.unableCreateDeltaRequest=Unable to serialize delta request for sessionid [{0}]
+deltaManager.dropMessage=Manager [{0}]: Drop message {1} inside GET_ALL_SESSIONS sync phase start date {2} message date {3}
+deltaManager.foundMasterMember=Found for context [{0}] the replication master member [{1}]
+deltaManager.loading.cnfe=ClassNotFoundException while loading persisted sessions: {0}
+deltaManager.loading.existing.session=overload existing session {0} 
+deltaManager.loading.ioe=IOException while loading persisted sessions: {0}
+deltaManager.loading.withContextClassLoader=Manager [{0}]: Loading the object data with a context class loader.
+deltaManager.loading.withoutClassLoader=Manager [{0}]: Loading the object data without a context class loader.
+deltaManager.managerLoad=Exception loading sessions from persistent storage
+deltaManager.noCluster=Starting... no cluster associated with this context: [{0}]
+deltaManager.noMasterMember=Starting... with no other member for context [{0}] at domain [{1}]
+deltaManager.noMembers=Manager [{0}]: skipping state transfer. No members active in cluster group.
+deltaManager.noSessionState=Manager [{0}]: No session state send at {1} received, timing out after {2} ms.
+deltaManager.notStarted=Manager has not yet been started
+deltaManager.sendMessage.newSession=Manager [{0}] send new session ({1})
+deltaManager.expireSessions=Manager [{0}] expiring sessions upon shutdown
+deltaManager.receiveMessage.accessed=Manager [{0}]: received session [{1}] accessed.
+deltaManager.receiveMessage.createNewSession=Manager [{0}]: received session [{1}] created.
+deltaManager.receiveMessage.delta=Manager [{0}]: received session [{1}] delta.
+deltaManager.receiveMessage.error=Manager [{0}]: Unable to receive message through TCP channel
+deltaManager.receiveMessage.eventType=Manager [{0}]: Received SessionMessage of type=({1}) from [{2}]
+deltaManager.receiveMessage.expired=Manager [{0}]: received session [{1}] expired.
+deltaManager.receiveMessage.transfercomplete=Manager [{0}] received from node [{1}:{2}] session state transfered.
+deltaManager.receiveMessage.unloadingAfter=Manager [{0}]: unloading sessions complete
+deltaManager.receiveMessage.unloadingBegin=Manager [{0}]: start unloading sessions
+deltaManager.receiveMessage.allSessionDataAfter=Manager [{0}]: session state deserialized
+deltaManager.receiveMessage.allSessionDataBegin=Manager [{0}]: received session state data
+deltaManager.receiveMessage.fromWrongDomain=Manager [{0}]: Received wrong SessionMessage of type=({1}) from [{2}] with domain [{3}] (localdomain [{4}] 
+deltaManager.registerCluster=Register manager {0} to cluster element {1} with name {2}
+deltaManager.sessionReceived=Manager [{0}]; session state send at {1} received in {2} ms.
+deltaManager.sessionTimeout=Invalid session timeout setting {0}
+deltaManager.startClustering=Starting clustering manager at {0}
+deltaManager.stopped=Manager [{0}] is stopping
+deltaManager.unloading.ioe=IOException while saving persisted sessions: {0}
+deltaManager.waitForSessionState=Manager [{0}], requesting session state from {1}. This operation will timeout if no session state has been received within 60 seconds.
+deltaRequest.showPrincipal=Principal [{0}] is set to session {1}
+deltaRequest.wrongPrincipalClass=DeltaManager only support GenericPrincipal. Your realm used principal class {0}.
+deltaSession.notifying=Notifying cluster of expiration primary={0} sessionId [{1}]
+deltaSession.valueBound.ex=Session bound listener throw an exception
+deltaSession.valueBinding.ex=Session binding listener throw an exception
+deltaSession.valueUnbound.ex=Session unbound listener throw an exception
+deltaSession.readSession=readObject() loading session [{0}]
+deltaSession.readAttribute=session [{0}] loading attribute '{1}' with value '{2}'
+deltaSession.writeSession=writeObject() storing session [{0}]
+jvmRoute.cannotFindSession=Can't find session [{0}]
+jvmRoute.changeSession=Changed session from [{0}] to [{1}]
+jvmRoute.clusterListener.started=Cluster JvmRouteSessionIDBinderListener started
+jvmRoute.clusterListener.stopped=Cluster JvmRouteSessionIDBinderListener stoppedjvmRoute.listener.started=SessionID Binder Listener started
+jvmRoute.configure.warn=Please, setup your JvmRouteBinderValve at host valve, not at context valve!
+jvmRoute.contextNotFound=Context [{0}] not found at note [{1}]!
+jvmRoute.failover=Detected a failover with different jvmRoute - orginal route: [{0}] new one: [{1}] at session id [{2}]
+jvmRoute.foundManager=Found Cluster DeltaManager {0} at {1}
+jvmRoute.hostNotFound=No host found [{0}]
+jvmRoute.listener.started=SessionID Binder Listener started
+jvmRoute.listener.stopped=SessionID Binder Listener stopped
+jvmRoute.lostSession=Lost Session [{0}] at path [{1}]
+jvmRoute.missingJvmRouteAttribute=No engine jvmRoute attribute configured!
+jvmRoute.newSessionCookie=Setting cookie with session id [{0}] name: [{1}] path: [{2}] secure: [{3}]
+jvmRoute.notFoundManager=Not found Cluster DeltaManager {0} at {1}
+jvmRoute.receiveMessage.sessionIDChanged=Cluster JvmRouteSessionIDBinderListener received orginal session ID [{0}] set to new id [{1}] for context path [{2}]
+jvmRoute.run.already=jvmRoute SessionID receiver run already
+jvmRoute.skipURLSessionIDs=Skip reassign jvm route check, sessionid comes from URL!
+jvmRoute.turnoverInfo=Turnover Check time {0} msec
+jvmRoute.valve.alreadyStarted=jvmRoute backup sessionID correction is started
+jvmRoute.valve.notStarted=jvmRoute backup sessionID correction run already
+jvmRoute.valve.started=JvmRouteBinderValve started
+jvmRoute.valve.stopped=JvmRouteBinderValve stopped
+jvmRoute.set.orignalsessionid=Set Orginal Session id at request attriute {0} value: {1}
+standardSession.getId.ise=getId: Session already invalidated
+standardSession.attributeEvent=Session attribute event listener threw exception
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicatedSession.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicatedSession.java
new file mode 100644
index 0000000..2932ae0
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicatedSession.java
@@ -0,0 +1,285 @@
+
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.session;
+
+/**
+ * Title:        Tomcat Session Replication for Tomcat 4.0 <BR>
+ * Description:  A very simple straight forward implementation of
+ *               session replication of servers in a cluster.<BR>
+ *               This session replication is implemented "live". By live
+ *               I mean, when a session attribute is added into a session on Node A
+ *               a message is broadcasted to other messages and setAttribute is called on the replicated
+ *               sessions.<BR>
+ *               A full description of this implementation can be found under
+ *               <href="http://www.filip.net/tomcat/">Filip's Tomcat Page</a><BR>
+ *
+ * Copyright:    See apache license
+ * @author  Filip Hanik
+ * @version $Revision$ $Date$
+ * Description:<BR>
+ * The ReplicatedSession class is a simple extension of the StandardSession class
+ * It overrides a few methods (setAttribute, removeAttribute, expire, access) and has
+ * hooks into the InMemoryReplicationManager to broadcast and receive events from the cluster.<BR>
+ * This class inherits the readObjectData and writeObject data methods from the StandardSession
+ * and does not contain any serializable elements in addition to the inherited ones from the StandardSession
+ *
+ */
+import org.apache.catalina.Manager;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.Principal;
+
+public class ReplicatedSession extends org.apache.catalina.session.StandardSession
+implements org.apache.catalina.cluster.ClusterSession{
+
+    private transient Manager mManager = null;
+    protected boolean isDirty = false;
+    private transient long lastAccessWasDistributed = System.currentTimeMillis();
+    private boolean isPrimarySession=true;
+    
+
+    public ReplicatedSession(Manager manager) {
+        super(manager);
+        mManager = manager;
+    }
+
+
+    public boolean isDirty()
+    {
+        return isDirty;
+    }
+
+    public void setIsDirty(boolean dirty)
+    {
+        isDirty = dirty;
+    }
+
+
+    public void setLastAccessWasDistributed(long time) {
+        lastAccessWasDistributed = time;
+    }
+
+    public long getLastAccessWasDistributed() {
+        return lastAccessWasDistributed;
+    }
+
+
+    public void removeAttribute(String name) {
+        setIsDirty(true);
+        super.removeAttribute(name);
+    }
+
+    /**
+     * see parent description,
+     * plus we also notify other nodes in the cluster
+     */
+    public void removeAttribute(String name, boolean notify) {
+        setIsDirty(true);
+        super.removeAttribute(name,notify);
+    }
+
+
+    /**
+     * Sets an attribute and notifies the other nodes in the cluster
+     */
+    public void setAttribute(String name, Object value)
+    {
+        if ( value == null ) {
+          removeAttribute(name);
+          return;
+        }
+        if (!(value instanceof java.io.Serializable))
+            throw new java.lang.IllegalArgumentException("Value for attribute "+name+" is not serializable.");
+        setIsDirty(true);
+        super.setAttribute(name,value);
+    }
+
+    public void setMaxInactiveInterval(int interval) {
+        setIsDirty(true);
+        super.setMaxInactiveInterval(interval);
+    }
+
+
+    /**
+     * Sets the manager for this session
+     * @param mgr - the servers InMemoryReplicationManager
+     */
+    public void setManager(SimpleTcpReplicationManager mgr)
+    {
+        mManager = mgr;
+        super.setManager(mgr);
+    }
+
+
+    /**
+     * Set the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.
+     *
+     * @param principal The new Principal, or <code>null</code> if none
+     */
+    public void setPrincipal(Principal principal) {
+        super.setPrincipal(principal);
+        setIsDirty(true);
+    }
+
+    public void expire() {
+        SimpleTcpReplicationManager mgr =(SimpleTcpReplicationManager)getManager();
+        mgr.sessionInvalidated(getIdInternal());
+        setIsDirty(true);
+        super.expire();
+    }
+
+    public void invalidate() {
+        SimpleTcpReplicationManager mgr =(SimpleTcpReplicationManager)getManager();
+        mgr.sessionInvalidated(getIdInternal());
+        setIsDirty(true);
+        super.invalidate();
+    }
+
+
+    /**
+     * Read a serialized version of the contents of this session object from
+     * the specified object input stream, without requiring that the
+     * StandardSession itself have been serialized.
+     *
+     * @param stream The object input stream to read from
+     *
+     * @exception ClassNotFoundException if an unknown class is specified
+     * @exception IOException if an input/output error occurs
+     */
+    public void readObjectData(ObjectInputStream stream)
+        throws ClassNotFoundException, IOException {
+
+        super.readObjectData(stream);
+
+    }
+
+
+    /**
+     * Write a serialized version of the contents of this session object to
+     * the specified object output stream, without requiring that the
+     * StandardSession itself have been serialized.
+     *
+     * @param stream The object output stream to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void writeObjectData(ObjectOutputStream stream)
+        throws IOException {
+
+        super.writeObjectData(stream);
+
+    }
+    
+    public void setId(String id, boolean tellNew) {
+
+        if ((this.id != null) && (manager != null))
+            manager.remove(this);
+
+        this.id = id;
+
+        if (manager != null)
+            manager.add(this);
+        if (tellNew) tellNew();
+    }
+    
+    
+
+
+
+
+
+
+    /**
+     * returns true if this session is the primary session, if that is the
+     * case, the manager can expire it upon timeout.
+     */
+    public boolean isPrimarySession() {
+        return isPrimarySession;
+    }
+
+    /**
+     * Sets whether this is the primary session or not.
+     * @param primarySession Flag value
+     */
+    public void setPrimarySession(boolean primarySession) {
+        this.isPrimarySession=primarySession;
+    }
+
+
+
+
+    /**
+     * Implements a log method to log through the manager
+     */
+    protected void log(String message) {
+
+        if ((mManager != null) && (mManager instanceof SimpleTcpReplicationManager)) {
+            ((SimpleTcpReplicationManager) mManager).log.debug("ReplicatedSession: " + message);
+        } else {
+            System.out.println("ReplicatedSession: " + message);
+        }
+
+    }
+
+    protected void log(String message, Throwable x) {
+
+        if ((mManager != null) && (mManager instanceof SimpleTcpReplicationManager)) {
+            ((SimpleTcpReplicationManager) mManager).log.error("ReplicatedSession: " + message,x);
+        } else {
+            System.out.println("ReplicatedSession: " + message);
+            x.printStackTrace();
+        }
+
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer("ReplicatedSession id=");
+        buf.append(getIdInternal()).append(" ref=").append(super.toString()).append("\n");
+        java.util.Enumeration e = getAttributeNames();
+        while ( e.hasMoreElements() ) {
+            String name = (String)e.nextElement();
+            Object value = getAttribute(name);
+            buf.append("\tname=").append(name).append("; value=").append(value).append("\n");
+        }
+        buf.append("\tLastAccess=").append(getLastAccessedTime()).append("\n");
+        return buf.toString();
+    }
+    public int getAccessCount() {
+        return accessCount;
+    }
+    public void setAccessCount(int accessCount) {
+        this.accessCount = accessCount;
+    }
+    public long getLastAccessedTime() {
+        return lastAccessedTime;
+    }
+    public void setLastAccessedTime(long lastAccessedTime) {
+        this.lastAccessedTime = lastAccessedTime;
+    }
+    public long getThisAccessedTime() {
+        return thisAccessedTime;
+    }
+    public void setThisAccessedTime(long thisAccessedTime) {
+        this.thisAccessedTime = thisAccessedTime;
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicationStream.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicationStream.java
new file mode 100644
index 0000000..5dc6a43
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicationStream.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.cluster.session;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+
+/**
+ * Custom subclass of <code>ObjectInputStream</code> that loads from the
+ * class loader for this web application.  This allows classes defined only
+ * with the web application to be found correctly.
+ *
+ * @author Craig R. McClanahan
+ * @author Bip Thelin
+ * @version $Revision$, $Date$
+ */
+
+public final class ReplicationStream extends ObjectInputStream {
+
+
+    /**
+     * The class loader we will use to resolve classes.
+     */
+    private ClassLoader classLoader = null;
+
+    /**
+     * Construct a new instance of CustomObjectInputStream
+     *
+     * @param stream The input stream we will read from
+     * @param classLoader The class loader used to instantiate objects
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public ReplicationStream(InputStream stream,
+                             ClassLoader classLoader)
+        throws IOException {
+
+        super(stream);
+        this.classLoader = classLoader;
+    }
+
+    /**
+     * Load the local class equivalent of the specified stream class
+     * description, by using the class loader assigned to this Context.
+     *
+     * @param classDesc Class description from the input stream
+     *
+     * @exception ClassNotFoundException if this class cannot be found
+     * @exception IOException if an input/output error occurs
+     */
+    public Class resolveClass(ObjectStreamClass classDesc)
+        throws ClassNotFoundException, IOException {
+        String name = classDesc.getName();
+        boolean tryRepFirst = name.startsWith("org.apache.catalina.cluster");
+        try
+        {
+            if ( tryRepFirst ) return findReplicationClass(name);
+            else return findWebappClass(name);
+        }
+        catch ( Exception x )
+        {
+            if ( tryRepFirst ) return findWebappClass(name);
+            else return findReplicationClass(name);
+        }
+    }
+    
+    public Class findReplicationClass(String name)
+        throws ClassNotFoundException, IOException {
+        return Class.forName(name, false, getClass().getClassLoader());
+    }
+
+    public Class findWebappClass(String name)
+        throws ClassNotFoundException, IOException {
+        return Class.forName(name, false, classLoader);
+    }
+
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SerializablePrincipal.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SerializablePrincipal.java
new file mode 100644
index 0000000..ec5a47e
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SerializablePrincipal.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.cluster.session;
+
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.catalina.Realm;
+
+
+/**
+ * Generic implementation of <strong>java.security.Principal</strong> that
+ * is available for use by <code>Realm</code> implementations.
+ * The GenericPrincipal does NOT implement serializable and I didn't want to change that implementation
+ * hence I implemented this one instead.
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+import org.apache.catalina.realm.GenericPrincipal;
+public class SerializablePrincipal  implements java.io.Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+    public SerializablePrincipal()
+    {
+        super();
+    }
+    /**
+     * Construct a new Principal, associated with the specified Realm, for the
+     * specified username and password.
+     *
+     * @param realm The Realm that owns this Principal
+     * @param name The username of the user represented by this Principal
+     * @param password Credentials used to authenticate this user
+     */
+    public SerializablePrincipal(Realm realm, String name, String password) {
+
+        this(realm, name, password, null);
+
+    }
+
+
+    /**
+     * Construct a new Principal, associated with the specified Realm, for the
+     * specified username and password, with the specified role names
+     * (as Strings).
+     *
+     * @param realm The Realm that owns this principal
+     * @param name The username of the user represented by this Principal
+     * @param password Credentials used to authenticate this user
+     * @param roles List of roles (must be Strings) possessed by this user
+     */
+    public SerializablePrincipal(Realm realm, String name, String password,
+                            List roles) {
+
+        super();
+        this.realm = realm;
+        this.name = name;
+        this.password = password;
+        if (roles != null) {
+            this.roles = new String[roles.size()];
+            this.roles = (String[]) roles.toArray(this.roles);
+            if (this.roles.length > 0)
+                Arrays.sort(this.roles);
+        }
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The username of the user represented by this Principal.
+     */
+    protected String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+
+    /**
+     * The authentication credentials for the user represented by
+     * this Principal.
+     */
+    protected String password = null;
+
+    public String getPassword() {
+        return (this.password);
+    }
+
+
+    /**
+     * The Realm with which this Principal is associated.
+     */
+    protected transient Realm realm = null;
+
+    public Realm getRealm() {
+        return (this.realm);
+    }
+
+    public void setRealm(Realm realm) {
+        this.realm = realm;
+    }
+
+
+
+
+    /**
+     * The set of roles associated with this user.
+     */
+    protected String roles[] = new String[0];
+
+    public String[] getRoles() {
+        return (this.roles);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+
+
+    /**
+     * Return a String representation of this object, which exposes only
+     * information that should be public.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SerializablePrincipal[");
+        sb.append(this.name);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    public static SerializablePrincipal createPrincipal(GenericPrincipal principal)
+    {
+        if ( principal==null) return null;
+        return new SerializablePrincipal(principal.getRealm(),
+                                         principal.getName(),
+                                         principal.getPassword(),
+                                         principal.getRoles()!=null?Arrays.asList(principal.getRoles()):null);
+    }
+
+    public GenericPrincipal getPrincipal( Realm realm )
+    {
+        return new GenericPrincipal(realm,name,password,getRoles()!=null?Arrays.asList(getRoles()):null);
+    }
+    
+    public static GenericPrincipal readPrincipal(java.io.ObjectInputStream in, Realm realm) throws java.io.IOException{
+        String name = in.readUTF();
+        boolean hasPwd = in.readBoolean();
+        String pwd = null;
+        if ( hasPwd ) pwd = in.readUTF();
+        int size = in.readInt();
+        String[] roles = new String[size];
+        for ( int i=0; i<size; i++ ) roles[i] = in.readUTF();
+        return new GenericPrincipal(realm,name,pwd,Arrays.asList(roles));
+    }
+    
+    public static void writePrincipal(GenericPrincipal p, java.io.ObjectOutputStream out) throws java.io.IOException {
+        out.writeUTF(p.getName());
+        out.writeBoolean(p.getPassword()!=null);
+        if ( p.getPassword()!= null ) out.writeUTF(p.getPassword());
+        String[] roles = p.getRoles();
+        if ( roles == null ) roles = new String[0];
+        out.writeInt(roles.length);
+        for ( int i=0; i<roles.length; i++ ) out.writeUTF(roles[i]);
+    }
+
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SessionIDMessage.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SessionIDMessage.java
new file mode 100644
index 0000000..c70a377
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SessionIDMessage.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster.session;
+
+import org.apache.catalina.cluster.ClusterMessage;
+
+/**
+ * Session id change cluster message
+ * 
+ * @author Peter Rossbach
+ * 
+ * @version $Revision$ $Date$
+ */
+public class SessionIDMessage implements ClusterMessage {
+
+	private org.apache.catalina.cluster.Member address;
+
+	private int messageNumber;
+
+	private long timestamp;
+
+	private String orignalSessionID;
+
+	private String backupSessionID;
+
+	private String contextPath;
+    private int resend = ClusterMessage.FLAG_DEFAULT ;
+    private int compress = ClusterMessage.FLAG_DEFAULT ;
+
+	public org.apache.catalina.cluster.Member getAddress() {
+		return address;
+	}
+
+	public void setAddress(org.apache.catalina.cluster.Member address) {
+		this.address = address;
+	}
+
+	public String getUniqueId() {
+		StringBuffer result = new StringBuffer(getOrignalSessionID());
+		result.append("#-#");
+		result.append(getBackupSessionID());
+		result.append("#-#");
+		result.append(getMessageNumber());
+		result.append("#-#");
+		result.append(System.currentTimeMillis());
+		return result.toString();
+	}
+
+	/**
+	 * @return Returns the contextPath.
+	 */
+	public String getContextPath() {
+		return contextPath;
+	}
+	/**
+	 * @param contextPath The contextPath to set.
+	 */
+	public void setContextPath(String contextPath) {
+		this.contextPath = contextPath;
+	}
+	/**
+	 * @return Returns the messageNumber.
+	 */
+	public int getMessageNumber() {
+		return messageNumber;
+	}
+
+	/**
+	 * @param messageNumber
+	 *            The messageNumber to set.
+	 */
+	public void setMessageNumber(int messageNumber) {
+		this.messageNumber = messageNumber;
+	}
+
+	/**
+	 * @return Returns the timestamp.
+	 */
+	public long getTimestamp() {
+		return timestamp;
+	}
+
+	/**
+	 * @param timestamp
+	 *            The timestamp to set.
+	 */
+	public void setTimestamp(long timestamp) {
+		this.timestamp = timestamp;
+	}
+
+	/**
+	 * @return Returns the backupSessionID.
+	 */
+	public String getBackupSessionID() {
+		return backupSessionID;
+	}
+
+	/**
+	 * @param backupSessionID
+	 *            The backupSessionID to set.
+	 */
+	public void setBackupSessionID(String backupSessionID) {
+		this.backupSessionID = backupSessionID;
+	}
+
+	/**
+	 * @return Returns the orignalSessionID.
+	 */
+	public String getOrignalSessionID() {
+		return orignalSessionID;
+	}
+
+	/**
+	 * @param orignalSessionID
+	 *            The orignalSessionID to set.
+	 */
+	public void setOrignalSessionID(String orignalSessionID) {
+		this.orignalSessionID = orignalSessionID;
+	}
+
+    /**
+     * @return Returns the compress.
+     * @since 5.5.10 
+     */
+    public int getCompress() {
+        return compress;
+    }
+    /**
+     * @param compress The compress to set.
+     * @since 5.5.10
+     */
+    public void setCompress(int compress) {
+        this.compress = compress;
+    }
+    /**
+     * @return Returns the resend.
+     * @since 5.5.10
+     */
+    public int getResend() {
+        return resend;
+    }
+    /**
+     * @param resend The resend to set.
+     * @since 5.5.10
+     */
+    public void setResend(int resend) {
+        this.resend = resend;
+    }
+
+
+}
+
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SessionMessage.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SessionMessage.java
new file mode 100644
index 0000000..6ea1fd7
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SessionMessage.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster.session;
+import org.apache.catalina.cluster.ClusterMessage;
+
+/**
+ *
+ * <B>Class Description:</B><BR>
+ * The SessionMessage class is a class that is used when a session has been
+ * created, modified, expired in a Tomcat cluster node.<BR>
+ *
+ * The following events are currently available:
+ * <ul>
+ *   <li><pre>public static final int EVT_SESSION_CREATED</pre><li>
+ *   <li><pre>public static final int EVT_SESSION_ACCESSED</pre><li>
+ *   <li><pre>public static final int EVT_ATTRIBUTE_ADDED</pre><li>
+ *   <li><pre>public static final int EVT_ATTRIBUTE_REMOVED</pre><li>
+ *   <li><pre>public static final int EVT_SESSION_EXPIRED_WONOTIFY</pre><li>
+ *   <li><pre>public static final int EVT_SESSION_EXPIRED_WNOTIFY</pre><li>
+ *   <li><pre>public static final int EVT_GET_ALL_SESSIONS</pre><li>
+ *   <li><pre>public static final int EVT_SET_USER_PRINCIPAL</pre><li>
+ *   <li><pre>public static final int EVT_SET_SESSION_NOTE</pre><li>
+ *   <li><pre>public static final int EVT_REMOVE_SESSION_NOTE</pre><li>
+ * </ul>
+ *
+ */
+
+public interface SessionMessage extends ClusterMessage, java.io.Serializable
+{
+
+    /**
+     * Event type used when a session has been created on a node
+     */
+    public static final int EVT_SESSION_CREATED = 1;
+    /**
+     * Event type used when a session has expired
+     */
+    public static final int EVT_SESSION_EXPIRED = 2;
+
+    /**
+     * Event type used when a session has been accessed (ie, last access time
+     * has been updated. This is used so that the replicated sessions will not expire
+     * on the network
+     */
+    public static final int EVT_SESSION_ACCESSED = 3;
+    /**
+     * Event type used when a server comes online for the first time.
+     * The first thing the newly started server wants to do is to grab the
+     * all the sessions from one of the nodes and keep the same state in there
+     */
+    public static final int EVT_GET_ALL_SESSIONS = 4;
+    /**
+     * Event type used when an attribute has been added to a session,
+     * the attribute will be sent to all the other nodes in the cluster
+     */
+    public static final int EVT_SESSION_DELTA  = 13;
+
+    /**
+     * When a session state is transferred, this is the event.
+     */
+    public static final int EVT_ALL_SESSION_DATA = 12;
+    
+    /**
+     * When a session state is complete transferred, this is the event.
+     */
+    public static final int EVT_ALL_SESSION_TRANSFERCOMPLETE = 14;
+    
+
+    
+    public String getContextName();
+    
+    public String getEventTypeString();
+    
+    /**
+     * returns the event type
+     * @return one of the event types EVT_XXXX
+     */
+    public int getEventType(); 
+    /**
+     * @return the serialized data for the session
+     */
+    public byte[] getSession();
+    /**
+     * @return the session ID for the session
+     */
+    public String getSessionID();
+    
+
+
+}//SessionMessage
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SessionMessageImpl.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SessionMessageImpl.java
new file mode 100644
index 0000000..06dbf01
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SessionMessageImpl.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster.session;
+
+
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.Member;
+
+/**
+ * Session cluster message
+ * 
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * 
+ * @version $Revision$ $Date$
+ */
+public class SessionMessageImpl implements SessionMessage, java.io.Serializable {
+    
+    public SessionMessageImpl() {
+    }
+    
+    
+    /*
+
+     * Private serializable variables to keep the messages state
+     */
+    private int mEvtType = -1;
+    private byte[] mSession;
+    private String mSessionID;
+    private Member mSrc;
+    private String mContextName;
+    private long serializationTimestamp;
+    private boolean timestampSet = false ;
+    private String uniqueId;
+    private int resend = ClusterMessage.FLAG_DEFAULT ;
+    private int compress = ClusterMessage.FLAG_DEFAULT ;
+
+
+    private SessionMessageImpl( String contextName,
+                           int eventtype,
+                           byte[] session,
+                           String sessionID)
+    {
+        mEvtType = eventtype;
+        mSession = session;
+        mSessionID = sessionID;
+        mContextName = contextName;
+        uniqueId = sessionID;
+    }
+
+    /**
+     * Creates a session message. Depending on what event type you want this
+     * message to represent, you populate the different parameters in the constructor<BR>
+      * The following rules apply dependent on what event type argument you use:<BR>
+     * <B>EVT_SESSION_CREATED</B><BR>
+     *    The parameters: session, sessionID must be set.<BR>
+     * <B>EVT_SESSION_EXPIRED</B><BR>
+     *    The parameters: sessionID must be set.<BR>
+     * <B>EVT_SESSION_ACCESSED</B><BR>
+     *    The parameters: sessionID must be set.<BR>
+     * <B>EVT_SESSION_EXPIRED_XXXX</B><BR>
+     *    The parameters: sessionID must be set.<BR>
+     * <B>EVT_SESSION_DELTA</B><BR>
+     *    Send attribute delta (add,update,remove attribute or principal, ...).<BR>
+     * <B>EVT_ALL_SESSION_DATA</B><BR>
+     *    Send complete serializes session list<BR>
+     * <B>EVT_ALL_SESSION_TRANSFERCOMPLETE</B><BR>
+     *    send that all session state information are transfered
+     *    after GET_ALL_SESSION received from this sender.<BR>
+     * @param contextName - the name of the context (application
+     * @param eventtype - one of the 8 event type defined in this class
+     * @param session - the serialized byte array of the session itself
+     * @param sessionID - the id that identifies this session
+     * @param uniqueID - the id that identifies this message
+
+     */
+    public SessionMessageImpl( String contextName,
+                           int eventtype,
+                           byte[] session,
+                           String sessionID,
+                           String uniqueID)
+    {
+        this(contextName,eventtype,session,sessionID);
+        uniqueId = uniqueID;
+    }
+
+    /**
+     * returns the event type
+     * @return one of the event types EVT_XXXX
+     */
+    public int getEventType() { return mEvtType; }
+    /**
+     * @return the serialized data for the session
+     */
+    public byte[] getSession() { return mSession;}
+    /**
+     * @return the session ID for the session
+     */
+    public String getSessionID(){ return mSessionID; }
+    
+    /**
+     * set message send time but only the first setting works (one shot)
+     */
+    public void setTimestamp(long time) {
+        synchronized(this) {
+            if(!timestampSet) {
+                serializationTimestamp=time;
+                timestampSet = true ;
+            }
+        }
+    }
+    
+    public long getTimestamp() { return serializationTimestamp;}
+    
+    /**
+     * clear text event type name (for logging purpose only) 
+     * @return the event type in a string representating, useful for debugging
+     */
+    public String getEventTypeString()
+    {
+        switch (mEvtType)
+        {
+            case EVT_SESSION_CREATED : return "SESSION-MODIFIED";
+            case EVT_SESSION_EXPIRED : return "SESSION-EXPIRED";
+            case EVT_SESSION_ACCESSED : return "SESSION-ACCESSED";
+            case EVT_GET_ALL_SESSIONS : return "SESSION-GET-ALL";
+            case EVT_SESSION_DELTA : return "SESSION-DELTA";
+            case EVT_ALL_SESSION_DATA : return "ALL-SESSION-DATA";
+            case EVT_ALL_SESSION_TRANSFERCOMPLETE : return "SESSION-STATE-TRANSFERED";
+            default : return "UNKNOWN-EVENT-TYPE";
+        }
+    }
+
+    /**
+     * Get the address that this message originated from.  This would be set
+     * if the message was being relayed from a host other than the one
+     * that originally sent it.
+     */
+    public Member getAddress()
+    {
+        return this.mSrc;
+    }
+
+    /**
+     * Use this method to set the address that this message originated from.
+     * This can be used when re-sending the EVT_GET_ALL_SESSIONS message to
+     * another machine in the group.
+     */
+    public void setAddress(Member src)
+    {
+        this.mSrc = src;
+    }
+
+    public String getContextName() {
+       return mContextName;
+    }
+    public String getUniqueId() {
+        return uniqueId;
+    }
+    public void setUniqueId(String uniqueId) {
+        this.uniqueId = uniqueId;
+    }
+
+    /**
+     * @return Returns the compress.
+     * @since 5.5.10 
+     */
+    public int getCompress() {
+        return compress;
+    }
+    /**
+     * @param compress The compress to set.
+     * @since 5.5.10
+     */
+    public void setCompress(int compress) {
+        this.compress = compress;
+    }
+    /**
+     * @return Returns the resend.
+     * @since 5.5.10
+     */
+    public int getResend() {
+        return resend;
+    }
+    /**
+     * @param resend The resend to set.
+     * @since 5.5.10
+     */
+    public void setResend(int resend) {
+        this.resend = resend;
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SimpleTcpReplicationManager.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SimpleTcpReplicationManager.java
new file mode 100644
index 0000000..125641b
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/SimpleTcpReplicationManager.java
@@ -0,0 +1,651 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster.session;
+
+import java.io.IOException;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterManager;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.Member;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.session.StandardManager;
+
+/**
+ * Title:        Tomcat Session Replication for Tomcat 4.0 <BR>
+ * Description:  A very simple straight forward implementation of
+ *               session replication of servers in a cluster.<BR>
+ *               This session replication is implemented "live". By live
+ *               I mean, when a session attribute is added into a session on Node A
+ *               a message is broadcasted to other messages and setAttribute is called on the
+ *               replicated sessions.<BR>
+ *               A full description of this implementation can be found under
+ *               <href="http://www.filip.net/tomcat/">Filip's Tomcat Page</a><BR>
+ *
+ * Copyright:    See apache license
+ * Company:      www.filip.net
+ * @author  <a href="mailto:mail@filip.net">Filip Hanik</a>
+ * @author Bela Ban (modifications for synchronous replication)
+ * @version 1.0 for TC 4.0
+ * Description: The InMemoryReplicationManager is a session manager that replicated
+ * session information in memory. It uses <a href="www.javagroups.com">JavaGroups</a> as
+ * a communication protocol to ensure guaranteed and ordered message delivery.
+ * JavaGroups also provides a very flexible protocol stack to ensure that the replication
+ * can be used in any environment.
+ * <BR><BR>
+ * The InMemoryReplicationManager extends the StandardManager hence it allows for us
+ * to inherit all the basic session management features like expiration, session listeners etc
+ * <BR><BR>
+ * To communicate with other nodes in the cluster, the InMemoryReplicationManager sends out 7 different type of multicast messages
+ * all defined in the SessionMessage class.<BR>
+ * When a session is replicated (not an attribute added/removed) the session is serialized into
+ * a byte array using the StandardSession.readObjectData, StandardSession.writeObjectData methods.
+ */
+public class SimpleTcpReplicationManager extends StandardManager
+implements ClusterManager
+{
+    public static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( SimpleTcpReplicationManager.class );
+
+    //the channel configuration
+    protected String mChannelConfig = null;
+
+    //the group name
+    protected String mGroupName = "TomcatReplication";
+
+    //somehow start() gets called more than once
+    protected boolean mChannelStarted = false;
+
+    //log to screen
+    protected boolean mPrintToScreen = true;
+
+    protected boolean defaultMode = false;
+
+    protected boolean mManagerRunning = false;
+
+    /** Use synchronous rather than asynchronous replication. Every session modification (creation, change, removal etc)
+     * will be sent to all members. The call will then wait for max milliseconds, or forever (if timeout is 0) for
+     * all responses.
+     */
+    protected boolean synchronousReplication=true;
+
+    /** Set to true if we don't want the sessions to expire on shutdown */
+    protected boolean mExpireSessionsOnShutdown = true;
+
+    protected boolean useDirtyFlag = false;
+
+    protected String name;
+
+    protected boolean distributable = true;
+
+    protected CatalinaCluster cluster;
+
+    protected java.util.HashMap invalidatedSessions = new java.util.HashMap();
+
+    /**
+     * Flag to keep track if the state has been transferred or not
+     * Assumes false.
+     */
+    protected boolean stateTransferred = false;
+    private boolean notifyListenersOnReplication;
+    private boolean sendClusterDomainOnly = true ;
+
+    /**
+     * Constructor, just calls super()
+     *
+     */
+    public SimpleTcpReplicationManager()
+    {
+        super();
+    }
+
+    public boolean isSendClusterDomainOnly() {
+        return sendClusterDomainOnly;
+    }
+    
+    /**
+     * @param sendClusterDomainOnly The sendClusterDomainOnly to set.
+     */
+    public void setSendClusterDomainOnly(boolean sendClusterDomainOnly) {
+        this.sendClusterDomainOnly = sendClusterDomainOnly;
+    }
+  
+    /**
+     * @return Returns the defaultMode.
+     */
+    public boolean isDefaultMode() {
+        return defaultMode;
+    }
+    /**
+     * @param defaultMode The defaultMode to set.
+     */
+    public void setDefaultMode(boolean defaultMode) {
+        this.defaultMode = defaultMode;
+    }
+    
+    public boolean isManagerRunning()
+    {
+        return mManagerRunning;
+    }
+
+    public void setUseDirtyFlag(boolean usedirtyflag)
+    {
+        this.useDirtyFlag = usedirtyflag;
+    }
+
+    public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown)
+    {
+        mExpireSessionsOnShutdown = expireSessionsOnShutdown;
+    }
+
+    public void setCluster(CatalinaCluster cluster) {
+        if(log.isDebugEnabled())
+            log.debug("Cluster associated with SimpleTcpReplicationManager");
+        this.cluster = cluster;
+    }
+
+    public boolean getExpireSessionsOnShutdown()
+    {
+        return mExpireSessionsOnShutdown;
+    }
+
+    public void setPrintToScreen(boolean printtoscreen)
+    {
+        if(log.isDebugEnabled())
+            log.debug("Setting screen debug to:"+printtoscreen);
+        mPrintToScreen = printtoscreen;
+    }
+
+    public void setSynchronousReplication(boolean flag)
+    {
+        synchronousReplication=flag;
+    }
+
+    /**
+     * Override persistence since they don't go hand in hand with replication for now.
+     */
+    public void unload() throws IOException {
+        if ( !getDistributable() ) {
+            super.unload();
+        }
+    }
+
+    /**
+     * Creates a HTTP session.
+     * Most of the code in here is copied from the StandardManager.
+     * This is not pretty, yeah I know, but it was necessary since the
+     * StandardManager had hard coded the session instantiation to the a
+     * StandardSession, when we actually want to instantiate a ReplicatedSession<BR>
+     * If the call comes from the Tomcat servlet engine, a SessionMessage goes out to the other
+     * nodes in the cluster that this session has been created.
+     * @param notify - if set to true the other nodes in the cluster will be notified.
+     *                 This flag is needed so that we can create a session before we deserialize
+     *                 a replicated one
+     *
+     * @see ReplicatedSession
+     */
+    protected Session createSession(String sessionId, boolean notify, boolean setId)
+    {
+
+        //inherited from the basic manager
+        if ((getMaxActiveSessions() >= 0) &&
+           (sessions.size() >= getMaxActiveSessions()))
+            throw new IllegalStateException(sm.getString("standardManager.createSession.ise"));
+
+
+        Session session = new ReplicatedSession(this);
+
+        // Initialize the properties of the new session and return it
+        session.setNew(true);
+        session.setValid(true);
+        session.setCreationTime(System.currentTimeMillis());
+        session.setMaxInactiveInterval(this.maxInactiveInterval);
+        if(sessionId == null)
+            sessionId = generateSessionId();
+        if ( setId ) session.setId(sessionId);
+        if ( notify && (cluster!=null) ) {
+            ((ReplicatedSession)session).setIsDirty(true);
+        }
+        return (session);
+    }//createSession
+
+    //=========================================================================
+    // OVERRIDE THESE METHODS TO IMPLEMENT THE REPLICATION
+    //=========================================================================
+
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id will be assigned by this method, and available via the getId()
+     * method of the returned session.  If a new session cannot be created
+     * for any reason, return <code>null</code>.
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     */
+    public Session createSession(String sessionId)
+    {
+        //create a session and notify the other nodes in the cluster
+        Session session =  createSession(sessionId,getDistributable(),true);
+        add(session);
+        return session;
+    }
+
+    public void sessionInvalidated(String sessionId) {
+        synchronized ( invalidatedSessions ) {
+            invalidatedSessions.put(sessionId, sessionId);
+        }
+    }
+
+    public String[] getInvalidatedSessions() {
+        synchronized ( invalidatedSessions ) {
+            String[] result = new String[invalidatedSessions.size()];
+            invalidatedSessions.values().toArray(result);
+            return result;
+        }
+
+    }
+
+    public ClusterMessage requestCompleted(String sessionId)
+    {
+        if (  !getDistributable() ) {
+            log.warn("Received requestCompleted message, although this context["+
+                     getName()+"] is not distributable. Ignoring message");
+            return null;
+        }
+        //notify javagroups
+        try
+        {
+            if ( invalidatedSessions.get(sessionId) != null ) {
+                synchronized ( invalidatedSessions ) {
+                    invalidatedSessions.remove(sessionId);
+                    SessionMessage msg = new SessionMessageImpl(name,
+                    SessionMessage.EVT_SESSION_EXPIRED,
+                    null,
+                    sessionId,
+                    sessionId);
+                return msg;
+                }
+            } else {
+                ReplicatedSession session = (ReplicatedSession) findSession(
+                    sessionId);
+                if (session != null) {
+                    //return immediately if the session is not dirty
+                    if (useDirtyFlag && (!session.isDirty())) {
+                        //but before we return doing nothing,
+                        //see if we should send
+                        //an updated last access message so that
+                        //sessions across cluster dont expire
+                        long interval = session.getMaxInactiveInterval();
+                        long lastaccdist = System.currentTimeMillis() -
+                            session.getLastAccessWasDistributed();
+                        if ( ((interval*1000) / lastaccdist)< 3 ) {
+                            SessionMessage accmsg = new SessionMessageImpl(name,
+                                SessionMessage.EVT_SESSION_ACCESSED,
+                                null,
+                                sessionId,
+                                sessionId);
+                            session.setLastAccessWasDistributed(System.currentTimeMillis());
+                            return accmsg;
+                        }
+                        return null;
+                    }
+
+                    session.setIsDirty(false);
+                    if (log.isDebugEnabled()) {
+                        try {
+                            log.debug("Sending session to cluster=" + session);
+                        }
+                        catch (Exception ignore) {}
+                    }
+                    SessionMessage msg = new SessionMessageImpl(name,
+                        SessionMessage.EVT_SESSION_CREATED,
+                        writeSession(session),
+                        session.getIdInternal(),
+                        session.getIdInternal());
+                    return msg;
+                } //end if
+            }//end if
+        }
+        catch (Exception x )
+        {
+            log.error("Unable to replicate session",x);
+        }
+        return null;
+    }
+
+    /**
+     * Serialize a session into a byte array<BR>
+     * This method simple calls the writeObjectData method on the session
+     * and returns the byte data from that call
+     * @param session - the session to be serialized
+     * @return a byte array containing the session data, null if the serialization failed
+     */
+    protected byte[] writeSession( Session session )
+    {
+        try
+        {
+            java.io.ByteArrayOutputStream session_data = new java.io.ByteArrayOutputStream();
+            java.io.ObjectOutputStream session_out = new java.io.ObjectOutputStream(session_data);
+            session_out.flush();
+            boolean hasPrincipal = session.getPrincipal() != null;
+            session_out.writeBoolean(hasPrincipal);
+            if ( hasPrincipal )
+            {
+                session_out.writeObject(SerializablePrincipal.createPrincipal((GenericPrincipal)session.getPrincipal()));
+            }//end if
+            ((ReplicatedSession)session).writeObjectData(session_out);
+            return session_data.toByteArray();
+
+        }
+        catch ( Exception x )
+        {
+            log.error("Failed to serialize the session!",x);
+        }
+        return null;
+    }
+
+    /**
+     * Reinstantiates a serialized session from the data passed in.
+     * This will first call createSession() so that we get a fresh instance with all
+     * the managers set and all the transient fields validated.
+     * Then it calls Session.readObjectData(byte[]) to deserialize the object
+     * @param data - a byte array containing session data
+     * @return a valid Session object, null if an error occurs
+     *
+     */
+    protected Session readSession( byte[] data, String sessionId )
+    {
+        try
+        {
+            java.io.ByteArrayInputStream session_data = new java.io.ByteArrayInputStream(data);
+            ReplicationStream session_in = new ReplicationStream(session_data,container.getLoader().getClassLoader());
+
+            Session session = sessionId!=null?this.findSession(sessionId):null;
+            boolean isNew = (session==null);
+            //clear the old values from the existing session
+            if ( session!=null ) {
+                ReplicatedSession rs = (ReplicatedSession)session;
+                rs.expire(false);  //cleans up the previous values, since we are not doing removes
+                session = null;
+            }//end if
+
+            if (session==null) {
+                session = createSession(null,false, false);
+                sessions.remove(session.getIdInternal());
+            }
+            
+            
+            boolean hasPrincipal = session_in.readBoolean();
+            SerializablePrincipal p = null;
+            if ( hasPrincipal )
+                p = (SerializablePrincipal)session_in.readObject();
+            ((ReplicatedSession)session).readObjectData(session_in);
+            if ( hasPrincipal )
+                session.setPrincipal(p.getPrincipal(getContainer().getRealm()));
+            ((ReplicatedSession)session).setId(sessionId,isNew);
+            ReplicatedSession rsession = (ReplicatedSession)session; 
+            rsession.setAccessCount(1);
+            session.setManager(this);
+            session.setValid(true);
+            rsession.setLastAccessedTime(System.currentTimeMillis());
+            rsession.setThisAccessedTime(System.currentTimeMillis());
+            ((ReplicatedSession)session).setAccessCount(0);
+            session.setNew(false);
+            if(log.isTraceEnabled())
+                 log.trace("Session loaded id="+sessionId +
+                               " actualId="+session.getId()+ 
+                               " exists="+this.sessions.containsKey(sessionId)+
+                               " valid="+rsession.isValid());
+            return session;
+
+        }
+        catch ( Exception x )
+        {
+            log.error("Failed to deserialize the session!",x);
+        }
+        return null;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.<BR>
+     * Starts the cluster communication channel, this will connect with the other nodes
+     * in the cluster, and request the current session state to be transferred to this node.
+     * @exception IllegalStateException if this component has already been
+     *  started
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+        mManagerRunning = true;
+        super.start();
+        //start the javagroups channel
+        try {
+            //the channel is already running
+            if ( mChannelStarted ) return;
+            if(log.isInfoEnabled())
+                log.info("Starting clustering manager...:"+getName());
+            if ( cluster == null ) {
+                log.error("Starting... no cluster associated with this context:"+getName());
+                return;
+            }
+            cluster.addManager(getName(),this);
+
+            if (cluster.getMembers().length > 0) {
+                Member mbr = cluster.getMembers()[0];
+                SessionMessage msg =
+                    new SessionMessageImpl(this.getName(),
+                                       SessionMessage.EVT_GET_ALL_SESSIONS,
+                                       null,
+                                       "GET-ALL",
+                                       "GET-ALL-"+this.getName());
+                cluster.send(msg, mbr);
+                if(log.isWarnEnabled())
+                     log.warn("Manager["+getName()+"], requesting session state from "+mbr+
+                         ". This operation will timeout if no session state has been received within "+
+                         "60 seconds");
+                long reqStart = System.currentTimeMillis();
+                long reqNow = 0;
+                boolean isTimeout=false;
+                do {
+                    try {
+                        Thread.sleep(100);
+                    }catch ( Exception sleep) {}
+                    reqNow = System.currentTimeMillis();
+                    isTimeout=((reqNow-reqStart)>(1000*60));
+                } while ( (!isStateTransferred()) && (!isTimeout));
+                if ( isTimeout || (!isStateTransferred()) ) {
+                    log.error("Manager["+getName()+"], No session state received, timing out.");
+                }else {
+                    if(log.isInfoEnabled())
+                        log.info("Manager["+getName()+"], session state received in "+(reqNow-reqStart)+" ms.");
+                }
+            } else {
+                if(log.isInfoEnabled())
+                    log.info("Manager["+getName()+"], skipping state transfer. No members active in cluster group.");
+            }//end if
+            mChannelStarted = true;
+        }  catch ( Exception x ) {
+            log.error("Unable to start SimpleTcpReplicationManager",x);
+        }
+    }
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.<BR>
+     * This will disconnect the cluster communication channel and stop the listener thread.
+     * @exception IllegalStateException if this component has not been started
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException
+    {
+        mManagerRunning = false;
+        mChannelStarted = false;
+        super.stop();
+        //stop the javagroup channel
+        try
+        {
+            this.sessions.clear();
+            cluster.removeManager(getName(),this);
+//            mReplicationListener.stopListening();
+//            mReplicationTransmitter.stop();
+//            service.stop();
+//            service = null;
+        }
+        catch ( Exception x )
+        {
+            log.error("Unable to stop SimpleTcpReplicationManager",x);
+        }
+    }
+
+    public void setDistributable(boolean dist) {
+        this.distributable = dist;
+    }
+
+    public boolean getDistributable() {
+        return distributable;
+    }
+
+    /**
+     * This method is called by the received thread when a SessionMessage has
+     * been received from one of the other nodes in the cluster.
+     * @param msg - the message received
+     * @param sender - the sender of the message, this is used if we receive a
+     *                 EVT_GET_ALL_SESSION message, so that we only reply to
+     *                 the requesting node
+     */
+    protected void messageReceived( SessionMessage msg, Member sender ) {
+        try  {
+            if(log.isInfoEnabled()) {
+                log.debug("Received SessionMessage of type="+msg.getEventTypeString());
+                log.debug("Received SessionMessage sender="+sender);
+            }
+            switch ( msg.getEventType() ) {
+                case SessionMessage.EVT_GET_ALL_SESSIONS: {
+                    //get a list of all the session from this manager
+                    Object[] sessions = findSessions();
+                    java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
+                    java.io.ObjectOutputStream oout = new java.io.ObjectOutputStream(bout);
+                    oout.writeInt(sessions.length);
+                    for (int i=0; i<sessions.length; i++){
+                        ReplicatedSession ses = (ReplicatedSession)sessions[i];
+                        oout.writeUTF(ses.getIdInternal());
+                        byte[] data = writeSession(ses);
+                        oout.writeObject(data);
+                    }//for
+                    //don't send a message if we don't have to
+                    oout.flush();
+                    oout.close();
+                    byte[] data = bout.toByteArray();
+                    SessionMessage newmsg = new SessionMessageImpl(name,
+                        SessionMessage.EVT_ALL_SESSION_DATA,
+                        data, "SESSION-STATE","SESSION-STATE-"+getName());
+                    cluster.send(newmsg, sender);
+                    break;
+                }
+                case SessionMessage.EVT_ALL_SESSION_DATA: {
+                    java.io.ByteArrayInputStream bin =
+                        new java.io.ByteArrayInputStream(msg.getSession());
+                    java.io.ObjectInputStream oin = new java.io.ObjectInputStream(bin);
+                    int size = oin.readInt();
+                    for ( int i=0; i<size; i++) {
+                        String id = oin.readUTF();
+                        byte[] data = (byte[])oin.readObject();
+                        Session session = readSession(data,id);
+                    }//for
+                    stateTransferred=true;
+                    break;
+                }
+                case SessionMessage.EVT_SESSION_CREATED: {
+                    Session session = this.readSession(msg.getSession(),msg.getSessionID());
+                    if ( log.isDebugEnabled() ) {
+                        log.debug("Received replicated session=" + session +
+                            " isValid=" + session.isValid());
+                    }
+                    break;
+                }
+                case SessionMessage.EVT_SESSION_EXPIRED: {
+                    Session session = findSession(msg.getSessionID());
+                    if ( session != null ) {
+                        session.expire();
+                        this.remove(session);
+                    }//end if
+                    break;
+                }
+                case SessionMessage.EVT_SESSION_ACCESSED :{
+                    Session session = findSession(msg.getSessionID());
+                    if ( session != null ) {
+                        session.access();
+                        session.endAccess();
+                    }
+                    break;
+                }
+                default:  {
+                    //we didn't recognize the message type, do nothing
+                    break;
+                }
+            }//switch
+        }
+        catch ( Exception x )
+        {
+            log.error("Unable to receive message through TCP channel",x);
+        }
+    }
+
+    public void messageDataReceived(ClusterMessage cmsg) {
+        try {
+            if ( cmsg instanceof SessionMessage ) {
+                SessionMessage msg = (SessionMessage)cmsg;
+                messageReceived(msg,
+                                msg.getAddress() != null ? (Member) msg.getAddress() : null);
+            }
+        } catch(Throwable ex){
+            log.error("InMemoryReplicationManager.messageDataReceived()", ex);
+        }//catch
+    }
+
+    public boolean isStateTransferred() {
+        return stateTransferred;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+    public boolean isNotifyListenersOnReplication() {
+        return notifyListenersOnReplication;
+    }
+    public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
+        this.notifyListenersOnReplication = notifyListenersOnReplication;
+    }
+
+
+    /* 
+     * @see org.apache.catalina.cluster.ClusterManager#getCluster()
+     */
+    public CatalinaCluster getCluster() {
+        return cluster;
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/session/mbeans-descriptors.xml b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/mbeans-descriptors.xml
new file mode 100644
index 0000000..9f214c0
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/session/mbeans-descriptors.xml
@@ -0,0 +1,316 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mbeans-descriptors PUBLIC
+   "-//Apache Software Foundation//DTD Model MBeans Configuration File"
+   "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
+<mbeans-descriptors>
+    <mbean name="JvmRouteBinderValve" description="mod_jk jvmRoute jsessionid cookie backup correction" domain="Catalina"
+        group="Valve" type="org.apache.catalina.cluster.session.JvmRouteBinderValve">
+        <attribute name="info" 
+		           description="describe version" type="java.lang.String" writeable="false"/>
+        <attribute name="enabled" 
+		           description="enable a jvm Route check" type="boolean"/>
+        <attribute name="numberOfSessions"
+		           description="number of jvmRoute session corrections" type="long" writeable="false"/>
+        <attribute name="sessionIdAttribute" 
+		    description="Name of attribute with sessionid value before turnover a session" 
+		    type="java.lang.String" 
+		    />
+        <operation name="start" description="Stops the Cluster JvmRouteBinderValve" 
+		           impact="ACTION" returnType="void"/>
+        <operation name="stop" description="Stops the Cluster JvmRouteBinderValve" 
+		           impact="ACTION" returnType="void"/>
+    </mbean>
+	<mbean name="JvmRouteSessionIDBinderListener"
+		description="Monitors the jvmRoute activity"
+		domain="Catalina"
+        group="Listener"
+		type="org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListener">
+        <attribute name="info" 
+		           description="describe version" type="java.lang.String" writeable="false"/>
+        <attribute name="numberOfSessions" 
+		    description="number of jvmRoute session corrections" 
+		    type="long" 
+		    writeable="false"/>
+    </mbean>
+    
+   <mbean        name="DeltaManager"
+          description="Cluster Manager implementation of the Manager interface"
+               domain="Catalina"
+                group="Manager"
+                 type="org.apache.catalina.cluster.session.DeltaManager">
+
+    <attribute   name="info" 
+		  description="describe version"
+		         type="java.lang.String"
+		    writeable="false"/>
+		    
+    <attribute   name="algorithm"
+          description="The message digest algorithm to be used when generating
+                       session identifiers"
+                 type="java.lang.String"/>
+                 
+    <attribute   name="randomFile"
+          description="File source of random - /dev/urandom or a pipe"
+                 type="java.lang.String"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="distributable"
+          description="The distributable flag for Sessions created by this
+                       Manager"
+                 type="boolean"/>
+
+    <attribute   name="entropy"
+          description="A String initialization parameter used to increase the
+                       entropy of the initialization of our random number
+                       generator"
+                 type="java.lang.String"/>
+
+    <attribute   name="maxActiveSessions"
+          description="The maximum number of active Sessions allowed, or -1
+                       for no limit"
+                 type="int"/>
+
+    <attribute   name="maxInactiveInterval"
+          description="The default maximum inactive interval for Sessions
+                       created by this Manager"
+                 type="int"/>
+
+    <attribute name="processExpiresFrequency"
+               description="The frequency of the manager checks (expiration and passivation)"
+               type="int"/>
+               
+    <attribute   name="sessionIdLength"
+          description="The session id length (in bytes) of Sessions
+                       created by this Manager"
+                 type="int"/>
+
+    <attribute   name="name"
+          description="The descriptive name of this Manager implementation
+                       (for logging)"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="activeSessions"
+          description="Number of active sessions at this moment"
+                 type="int" 
+            writeable="false"/>
+
+    <attribute   name="sessionCounter"
+          description="Total number of sessions created by this manager"
+                 type="int" />
+
+    <attribute   name="sessionReplaceCounter"
+          description="Total number of replaced sessions that load from external nodes"
+                 type="long" />
+
+    <attribute   name="maxActive"
+          description="Maximum number of active sessions so far"
+                 type="int" />
+
+    <attribute   name="sessionMaxAliveTime"
+          description="Longest time an expired session had been alive"
+                 type="int" />
+
+    <attribute   name="sessionAverageAliveTime"
+          description="Average time an expired session had been alive"
+                 type="int" />
+
+    <attribute   name="sendClusterDomainOnly"
+                   is="true"
+          description="The sendClusterDomainOnly flag send sessions only to members as same cluster domain"
+                 type="boolean"/>
+
+    <attribute   name="rejectedSessions"
+          description="Number of sessions we rejected due to maxActive beeing reached"
+                 type="int" />
+
+    <attribute   name="expiredSessions"
+          description="Number of sessions that expired ( doesn't include explicit invalidations )"
+                 type="int" />
+
+    <attribute   name="stateTransferTimeout"
+          description="state transfer timeout in sec"
+                 type="int"/>
+
+    <attribute   name="processingTime"
+          description="Time spent doing housekeeping and expiration"
+                 type="long" />
+
+    <attribute   name="duplicates"
+          description="Number of duplicated session ids generated"
+                 type="int" />
+
+    <attribute   name="counterReceive_EVT_GET_ALL_SESSIONS"
+          description="Count receive EVT_GET_ALL_SESSIONS messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterReceive_EVT_ALL_SESSION_DATA"
+          description="Count receive EVT_ALL_SESSION_DATA messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterReceive_EVT_SESSION_CREATED"
+          description="Count receive EVT_SESSION_CREATED messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterReceive_EVT_SESSION_DELTA"
+          description="Count receive EVT_SESSION_DELTA messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterReceive_EVT_SESSION_ACCESSED"
+          description="Count receive EVT_SESSION_ACCESSED messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterReceive_EVT_SESSION_EXPIRED"
+          description="Count receive EVT_SESSION_EXPIRED messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE"
+          description="Count receive EVT_ALL_SESSION_TRANSFERCOMPLETE messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterSend_EVT_GET_ALL_SESSIONS"
+          description="Count send EVT_GET_ALL_SESSIONS messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterSend_EVT_ALL_SESSION_DATA"
+          description="Count send EVT_ALL_SESSION_DATA messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterSend_EVT_SESSION_CREATED"
+          description="Count send EVT_SESSION_CREATED messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterSend_EVT_SESSION_DELTA"
+          description="Count send EVT_SESSION_DELTA messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterSend_EVT_SESSION_ACCESSED"
+          description="Count send EVT_SESSION_ACCESSED messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterSend_EVT_SESSION_EXPIRED"
+          description="Count send EVT_SESSION_EXPIRED messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE"
+          description="Count send EVT_ALL_SESSION_TRANSFERCOMPLETE messages"
+                 type="long"
+            writeable="false" />
+
+    <attribute   name="counterNoStateTransfered"
+          description="Count the failed session transfers noStateTransfered"
+                 type="int"
+            writeable="false" />
+            
+    <attribute   name="receivedQueueSize"
+          description="length of receive queue size when session received from other node"
+                 type="int"
+            writeable="false" />            
+
+    <attribute   name="expireSessionsOnShutdown"
+                   is="true"
+          description="exipre all sessions cluster wide as one node goes down"
+                 type="boolean" />
+
+    <attribute   name="notifyListenersOnReplication"
+                   is="true"
+          description="Send session attribute change events on backup nodes"
+                 type="boolean" />
+
+    <attribute   name="notifySessionListenersOnReplication"
+                   is="true"
+          description="Send session start/stop events on backup nodes"
+                 type="boolean" />
+
+    <attribute   name="sendAllSessions"
+                   is="true"
+          description="Send all sessions at one big block"
+                 type="boolean" />
+
+    <attribute   name="sendAllSessionsSize"
+          description="session block size when sendAllSessions=false (default=1000)"
+                 type="int" />
+
+    <attribute   name="sendAllSessionsWaitTime"
+          description="wait time between send session block (default 2 sec)"
+                 type="int" />
+
+    <operation   name="listSessionIds"
+          description="Return the list of active session ids"
+               impact="ACTION"
+           returnType="java.lang.String">
+    </operation>
+
+    <operation   name="getSessionAttribute"
+          description="Return a session attribute"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+      <parameter name="key"
+          description="key of the attribute"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="expireSession"
+          description="Expire a session"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="getLastAccessedTime"
+          description="Get the last access time"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+    
+	<operation name="expireAllLocalSessions"
+               description="Exipre all active local sessions and replicate the invalid sessions"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+
+	<operation name="processExpires"
+               description="force process to expire sessions"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+	<operation name="resetStatistics"
+               description="Reset all statistics"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="getAllClusterSessions"
+               description="send to oldest cluster member that this node need all cluster sessions (resync member)"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/AsyncSocketSender.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/AsyncSocketSender.java
new file mode 100644
index 0000000..7b25494
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/AsyncSocketSender.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.net.InetAddress;
+
+import org.apache.catalina.cluster.util.SmartQueue;
+
+/**
+ * Send cluster messages from a Message queue with only one socket. Ack and keep
+ * Alive Handling is supported.
+ * <ul>
+ * <li>With autoConnect=false at ReplicationTransmitter, you can disconnect the
+ * sender and all messages are queued. Only use this for small maintaince
+ * isuses!</li>
+ * <li>waitForAck=true, means that receiver ack the transfer</li>
+ * <li>after one minute idle time, or number of request (100) the connection is
+ * reconnected with next request. Change this for production use!</li>
+ * <li>default ackTimeout is 15 sec: this is very low for big all session replication messages after restart a node</li>
+ * <li>disable keepAlive: keepAliveTimeout="-1" and keepAliveMaxRequestCount="-1"</li>
+ * </ul>
+ * 
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class AsyncSocketSender extends DataSender {
+    
+    private static int threadCounter = 1;
+
+    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(AsyncSocketSender.class);
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "AsyncSocketSender/2.0";
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Message Queue
+     */
+    private SmartQueue queue = new SmartQueue();
+
+    /**
+     * Active thread to push messages asynchronous to the other replication node
+     */
+    private QueueThread queueThread = null;
+
+    /**
+     * Count number of queue message
+     */
+    private long inQueueCounter = 0;
+
+    /**
+     * Count all successfull push messages from queue
+     */
+    private long outQueueCounter = 0;
+
+    // ------------------------------------------------------------- Constructor
+
+    /**
+     * start background thread to push incomming cluster messages to replication
+     * node
+     * 
+     * @param domain replication cluster domain (session domain)
+     * @param host replication node tcp address
+     * @param port replication node tcp port
+     */
+    public AsyncSocketSender(String domain,InetAddress host, int port) {
+        super(domain,host, port);
+        checkThread();
+    }
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * @return Returns the inQueueCounter.
+     */
+    public long getInQueueCounter() {
+        return inQueueCounter;
+    }
+
+    /**
+     * @return Returns the outQueueCounter.
+     */
+    public long getOutQueueCounter() {
+        return outQueueCounter;
+    }
+
+    /**
+     * @return Returns the queueSize.
+     */
+    public int getQueueSize() {
+        return queue.size();
+    }
+
+    /**
+     * @return Returns the queuedNrOfBytes.
+     */
+    public long getQueuedNrOfBytes() {
+        if(queueThread != null)
+            return queueThread.getQueuedNrOfBytes();
+        return 0l ;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /*
+     * Connect to socket and start background thread to ppush queued messages
+     * 
+     * @see org.apache.catalina.cluster.tcp.IDataSender#connect()
+     */
+    public void connect() throws java.io.IOException {
+        super.connect();
+        checkThread();
+    }
+
+    /**
+     * Disconnect socket ad stop queue thread
+     * 
+     * @see org.apache.catalina.cluster.tcp.IDataSender#disconnect()
+     */
+    public void disconnect() {
+        stopThread();
+        super.disconnect();
+    }
+
+    /*
+     * Send message to queue for later sending
+     * 
+     * @see org.apache.catalina.cluster.tcp.IDataSender#sendMessage(java.lang.String,
+     *      byte[])
+     */
+    public void sendMessage(String messageid, ClusterData data)
+            throws java.io.IOException {
+        SmartQueue.SmartEntry entry = new SmartQueue.SmartEntry(messageid, data);
+        queue.add(entry);
+        synchronized (this) {
+            inQueueCounter++;
+            queueThread.incQueuedNrOfBytes(data.getMessage().length);
+       }
+        if (log.isTraceEnabled())
+            log.trace(sm.getString("AsyncSocketSender.queue.message",
+                    getAddress().getHostAddress(), new Integer(getPort()), messageid, new Long(
+                            data.getMessage().length)));
+    }
+
+    /*
+     * Reset sender statistics
+     */
+    public synchronized void resetStatistics() {
+        super.resetStatistics();
+        inQueueCounter = queue.size();
+        outQueueCounter = 0;
+
+    }
+
+    /**
+     * Name of this SockerSender
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("AsyncSocketSender[");
+        buf.append(getAddress().getHostAddress()).append(":").append(getPort()).append("]");
+        return buf.toString();
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Start Queue thread as daemon
+     */
+    protected void checkThread() {
+        if (queueThread == null) {
+            if (log.isInfoEnabled())
+                log.info(sm.getString("AsyncSocketSender.create.thread",
+                        getAddress(), new Integer(getPort())));
+            queueThread = new QueueThread(this);
+            queueThread.setDaemon(true);
+            queueThread.start();
+        }
+    }
+
+    /**
+     * stop queue worker thread
+     */
+    protected void stopThread() {
+        if (queueThread != null) {
+            queueThread.stopRunning();
+            queueThread = null;
+        }
+    }
+
+    // -------------------------------------------------------- Inner Class
+
+    private class QueueThread extends Thread {
+        AsyncSocketSender sender;
+
+        private boolean keepRunning = true;
+
+        /**
+         * Current number of bytes from all queued messages
+         */
+        private long queuedNrOfBytes = 0;
+
+        public QueueThread(AsyncSocketSender sender) {
+            this.sender = sender;
+            setName("Cluster-AsyncSocketSender-" + (threadCounter++));
+        }
+
+        protected long getQueuedNrOfBytes() {
+            return queuedNrOfBytes ;
+        }
+        
+        protected synchronized void setQueuedNrOfBytes(long queuedNrOfBytes) {
+            this.queuedNrOfBytes = queuedNrOfBytes;
+        }
+
+        protected synchronized void incQueuedNrOfBytes(long size) {
+            queuedNrOfBytes += size;
+        }
+        
+        protected synchronized void decQueuedNrOfBytes(long size) {
+            queuedNrOfBytes -= size;
+        }
+
+        public void stopRunning() {
+            keepRunning = false;
+        }
+
+        /**
+         * Get one queued message and push it to the replication node
+         * 
+         * @see DataSender#pushMessage(String, byte[])
+         */
+        public void run() {
+            while (keepRunning) {
+                SmartQueue.SmartEntry entry = sender.queue.remove(5000);
+                if (entry != null) {
+                    int messagesize = 0;
+                    try {
+                        ClusterData data = (ClusterData) entry.getValue();
+                        messagesize = data.getMessage().length;
+                        sender.pushMessage(data);
+                        outQueueCounter++;
+                    } catch (Exception x) {
+                        log.warn(sm.getString("AsyncSocketSender.send.error",
+                                entry.getKey()));
+                    } finally {
+                        decQueuedNrOfBytes(messagesize);
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ClusterData.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ClusterData.java
new file mode 100644
index 0000000..350e192
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ClusterData.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster.tcp;
+
+import org.apache.catalina.cluster.ClusterMessage;
+
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ */
+public class ClusterData {
+
+    private int resend = ClusterMessage.FLAG_DEFAULT ;
+    private int compress = ClusterMessage.FLAG_DEFAULT ;
+    private byte[] message ;
+    private long timestamp ;
+    private String uniqueId ;
+    private String type ;
+    
+    public ClusterData() {}
+    
+    /**
+     * @param type message type (class)
+     * @param uniqueId unique message id
+     * @param message message data
+     * @param timestamp message creation date
+     */
+    public ClusterData(String type, String uniqueId, byte[] message, long timestamp
+            ) {
+        this.uniqueId = uniqueId;
+        this.message = message;
+        this.timestamp = timestamp;
+    }
+    
+    
+    /**
+     * @return Returns the type.
+     */
+    public String getType() {
+        return type;
+    }
+    /**
+     * @param type The type to set.
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+    /**
+     * @return Returns the message.
+     */
+    public byte[] getMessage() {
+        return message;
+    }
+    /**
+     * @param message The message to set.
+     */
+    public void setMessage(byte[] message) {
+        this.message = message;
+    }
+    /**
+     * @return Returns the timestamp.
+     */
+    public long getTimestamp() {
+        return timestamp;
+    }
+    /**
+     * @param timestamp The timestamp to set.
+     */
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+    /**
+     * @return Returns the uniqueId.
+     */
+    public String getUniqueId() {
+        return uniqueId;
+    }
+    /**
+     * @param uniqueId The uniqueId to set.
+     */
+    public void setUniqueId(String uniqueId) {
+        this.uniqueId = uniqueId;
+    }
+    /**
+     * @return Returns the compress.
+     */
+    public int getCompress() {
+        return compress;
+    }
+    /**
+     * @param compress The compress to set.
+     */
+    public void setCompress(int compress) {
+        this.compress = compress;
+    }
+    /**
+     * @return Returns the resend.
+     */
+    public int getResend() {
+        return resend;
+    }
+    /**
+     * @param resend The resend to set.
+     */
+    public void setResend(int resend) {
+        this.resend = resend;
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ClusterReceiverBase.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ClusterReceiverBase.java
new file mode 100644
index 0000000..a2259d3
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ClusterReceiverBase.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.ClusterReceiver;
+import org.apache.catalina.cluster.io.ListenCallback;
+import org.apache.catalina.cluster.session.ClusterSessionListener;
+import org.apache.catalina.cluster.session.ReplicationStream;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.StringManager;
+
+/**
+* FIXME i18n log messages
+* @author Peter Rossbach
+* @version $Revision$ $Date$
+*/
+
+public abstract class ClusterReceiverBase implements Runnable, ClusterReceiver,ListenCallback {
+    
+    protected static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( ClusterReceiverBase.class );
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+    private CatalinaCluster cluster;
+    private java.net.InetAddress bind;
+    private String tcpListenAddress;
+    private int tcpListenPort;
+    private boolean sendAck;
+    protected boolean doListen = false;
+
+    /**
+     * total bytes to recevied
+     */
+    protected long totalReceivedBytes = 0;
+    
+    /**
+     * doProcessingStats
+     */
+    protected boolean doReceivedProcessingStats = false;
+
+    /**
+     * proessingTime
+     */
+    protected long receivedProcessingTime = 0;
+    
+    /**
+     * min proessingTime
+     */
+    protected long minReceivedProcessingTime = Long.MAX_VALUE ;
+
+    /**
+     * max proessingTime
+     */
+    protected long maxReceivedProcessingTime = 0;
+    
+    /**
+     * Sending Stats
+     */
+    private long nrOfMsgsReceived = 0;
+
+    private long receivedTime = 0;
+
+    private long lastChecked = System.currentTimeMillis();
+
+
+    /**
+     * Compress message data bytes
+     */
+    private boolean compress = true ;
+
+    /**
+     * Transmitter Mbean name
+     */
+    private ObjectName objectName;
+
+    /**
+     * @return Returns the doListen.
+     */
+    public boolean isDoListen() {
+        return doListen;
+    }
+
+    /**
+     * @return Returns the bind.
+     */
+    public java.net.InetAddress getBind() {
+        if (bind == null) {
+            try {
+                if ("auto".equals(tcpListenAddress)) {
+                    tcpListenAddress = java.net.InetAddress.getLocalHost()
+                            .getHostAddress();
+                }
+                if (log.isDebugEnabled())
+                    log.debug("Starting replication listener on address:"
+                            + tcpListenAddress);
+                bind = java.net.InetAddress.getByName(tcpListenAddress);
+            } catch (IOException ioe) {
+                log.error("Failed bind replication listener on address:"
+                        + tcpListenAddress, ioe);
+            }
+        }
+      return bind;
+    }
+    
+    /**
+     * @param bind The bind to set.
+     */
+    public void setBind(java.net.InetAddress bind) {
+        this.bind = bind;
+    }
+    public void setCatalinaCluster(CatalinaCluster cluster) {
+        this.cluster = cluster;
+    }
+
+    public CatalinaCluster getCatalinaCluster() {
+        return (CatalinaCluster) cluster;
+    }
+    
+    /**
+     *  set Receiver ObjectName
+     * 
+     * @param name
+     */
+    public void setObjectName(ObjectName name) {
+        objectName = name;
+    }
+
+    /**
+     * Receiver ObjectName
+     * 
+     */
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+    
+    /**
+     * @return Returns the compress.
+     */
+    public boolean isCompress() {
+        return compress;
+    }
+    
+    /**
+     * @param compressMessageData The compress to set.
+     */
+    public void setCompress(boolean compressMessageData) {
+        this.compress = compressMessageData;
+    }
+    
+    /**
+     * Send ACK to sender
+     * 
+     * @return True if sending ACK
+     */
+    public boolean isSendAck() {
+        return sendAck;
+    }
+
+    /**
+     * set ack mode or not!
+     * 
+     * @param sendAck
+     */
+    public void setSendAck(boolean sendAck) {
+        this.sendAck = sendAck;
+    }
+ 
+    public String getTcpListenAddress() {
+        return tcpListenAddress;
+    }
+    
+    public void setTcpListenAddress(String tcpListenAddress) {
+        this.tcpListenAddress = tcpListenAddress;
+    }
+    
+    public int getTcpListenPort() {
+        return tcpListenPort;
+    }
+    
+    public void setTcpListenPort(int tcpListenPort) {
+        this.tcpListenPort = tcpListenPort;
+    }
+  
+    public String getHost() {
+        return getTcpListenAddress();
+    }
+
+    public int getPort() {
+        return getTcpListenPort();
+    }
+    // ------------------------------------------------------------- stats
+
+    /**
+     * @return Returns the doReceivedProcessingStats.
+     */
+    public boolean isDoReceivedProcessingStats() {
+        return doReceivedProcessingStats;
+    }
+    /**
+     * @param doReceiverProcessingStats The doReceivedProcessingStats to set.
+     */
+    public void setDoReceivedProcessingStats(boolean doReceiverProcessingStats) {
+        this.doReceivedProcessingStats = doReceiverProcessingStats;
+    }
+    /**
+     * @return Returns the maxReceivedProcessingTime.
+     */
+    public long getMaxReceivedProcessingTime() {
+        return maxReceivedProcessingTime;
+    }
+    /**
+     * @return Returns the minReceivedProcessingTime.
+     */
+    public long getMinReceivedProcessingTime() {
+        return minReceivedProcessingTime;
+    }
+    /**
+     * @return Returns the receivedProcessingTime.
+     */
+    public long getReceivedProcessingTime() {
+        return receivedProcessingTime;
+    }
+    /**
+     * @return Returns the totalReceivedBytes.
+     */
+    public long getTotalReceivedBytes() {
+        return totalReceivedBytes;
+    }
+    
+    /**
+     * @return Returns the avg receivedProcessingTime/nrOfMsgsReceived.
+     */
+    public double getAvgReceivedProcessingTime() {
+        return ((double)receivedProcessingTime) / nrOfMsgsReceived;
+    }
+
+    /**
+     * @return Returns the avg totalReceivedBytes/nrOfMsgsReceived.
+     */
+    public long getAvgTotalReceivedBytes() {
+        return ((long)totalReceivedBytes) / nrOfMsgsReceived;
+    }
+
+    /**
+     * @return Returns the receivedTime.
+     */
+    public long getReceivedTime() {
+        return receivedTime;
+    }
+
+    /**
+     * @return Returns the lastChecked.
+     */
+    public long getLastChecked() {
+        return lastChecked;
+    }
+
+    /**
+     * @return Returns the nrOfMsgsReceived.
+     */
+    public long getNrOfMsgsReceived() {
+        return nrOfMsgsReceived;
+    }
+
+    /**
+     * start cluster receiver
+     * 
+     * @see org.apache.catalina.cluster.ClusterReceiver#start()
+     */
+    public void start() {
+        try {
+            getBind();
+            Thread t = new Thread(this, "ClusterReceiver");
+            t.setDaemon(true);
+            t.start();
+        } catch (Exception x) {
+            log.fatal("Unable to start cluster receiver", x);
+        }
+        registerReceiverMBean();
+    }
+
+ 
+    /**
+     * Stop accept
+     * 
+     * @see org.apache.catalina.cluster.ClusterReceiver#stop()
+     * @see #stopListening()
+     */
+    public void stop() {
+        stopListening();
+        unregisterRecevierMBean();
+     
+    }
+    
+    /**
+     * Register Recevier MBean
+     * <domain>:type=ClusterReceiver,host=<host>
+     */
+    protected void registerReceiverMBean() {
+        if (cluster != null && cluster instanceof SimpleTcpCluster) {
+            SimpleTcpCluster scluster = (SimpleTcpCluster) cluster;
+            ObjectName clusterName = scluster.getObjectName();
+            try {
+                MBeanServer mserver = scluster.getMBeanServer();
+                Container container = cluster.getContainer();
+                String name = clusterName.getDomain() + ":type=ClusterReceiver";
+                if (container instanceof StandardHost) {
+                    name += ",host=" + clusterName.getKeyProperty("host");
+                }
+                ObjectName receiverName = new ObjectName(name);
+                if (mserver.isRegistered(receiverName)) {
+                    if (log.isWarnEnabled())
+                        log.warn(sm.getString(
+                                "cluster.mbean.register.allready",
+                                receiverName));
+                    return;
+                }
+                setObjectName(receiverName);
+                mserver.registerMBean(scluster.getManagedBean(this),
+                        getObjectName());
+            } catch (Exception e) {
+                log.warn(e);
+            }
+        }
+    }
+   
+    /**
+     * UnRegister Recevier MBean
+     * <domain>:type=ClusterReceiver,host=<host>
+     */
+    protected void unregisterRecevierMBean() {
+        if (cluster != null && getObjectName() != null
+                && cluster instanceof SimpleTcpCluster) {
+            SimpleTcpCluster scluster = (SimpleTcpCluster) cluster;
+            try {
+                MBeanServer mserver = scluster.getMBeanServer();
+                mserver.unregisterMBean(getObjectName());
+            } catch (Exception e) {
+                log.error(e);
+            }
+        }
+    }
+
+    /**
+     * stop Listener sockets
+     */
+    protected abstract void stopListening() ;
+
+    /**
+     * Start Listener
+     * @throws Exception
+     */
+    protected abstract void listen ()
+       throws Exception ;
+
+    
+    /**
+     * Start thread and listen
+     */
+    public void run()
+    {
+        try
+        {
+            listen();
+        }
+        catch ( Exception x )
+        {
+            log.error("Unable to start cluster listener.",x);
+        }
+    }
+
+    // --------------------------------------------------------- receiver messages
+
+    /**
+     * receiver Message from other node.
+     * All SessionMessage forward to ClusterManager and other message dispatch to all accept MessageListener.
+     *
+     * @see ClusterSessionListener#messageReceived(ClusterMessage)
+     */
+    public void messageDataReceived(ClusterData data) {
+    //public void messageDataReceived(byte[] data) {
+        long timeSent = 0 ;
+        if (doReceivedProcessingStats) {
+            timeSent = System.currentTimeMillis();
+        }
+        try {
+            ClusterMessage message = deserialize(data);
+            cluster.receive(message);
+        } catch (Exception x) {
+            log
+                    .error(
+                            "Unable to deserialize session message or unexpected exception from message listener.",
+                            x);
+        } finally {
+            if (doReceivedProcessingStats) {
+                addReceivedProcessingStats(timeSent);
+            }
+        }
+    }
+
+    /**
+     * deserialize the receieve cluster message
+     * @param data uncompress data
+     * @return The message
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    //protected ClusterMessage deserialize(byte[] data)
+    protected ClusterMessage deserialize(ClusterData data)
+            throws IOException, ClassNotFoundException {
+        Object message = null;
+        if (data != null) {
+            InputStream instream;
+            if (isCompress() || data.getCompress() == ClusterMessage.FLAG_ALLOWED ) {
+                instream = new GZIPInputStream(new ByteArrayInputStream(data.getMessage()));
+            } else {
+                instream = new ByteArrayInputStream(data.getMessage());
+            }
+            ReplicationStream stream = new ReplicationStream(instream,
+                    getClass().getClassLoader());
+            message = stream.readObject();
+            // calc stats really received bytes
+            totalReceivedBytes += data.getMessage().length;
+            //totalReceivedBytes += data.length;
+            nrOfMsgsReceived++;
+            instream.close();
+        }
+        if (message instanceof ClusterMessage)
+            return (ClusterMessage) message;
+        else {
+            if (log.isDebugEnabled())
+                log.debug("Message " + message.toString() + " from type "
+                        + message.getClass().getName()
+                        + " transfered but is not a cluster message");
+            return null;
+        }
+    }
+    
+    // --------------------------------------------- Performance Stats
+
+    /**
+     * Reset sender statistics
+     */
+    public synchronized void resetStatistics() {
+        nrOfMsgsReceived = 0;
+        totalReceivedBytes = 0;
+        minReceivedProcessingTime = Long.MAX_VALUE ;
+        maxReceivedProcessingTime = 0 ;
+        receivedProcessingTime = 0 ;
+        receivedTime = 0 ;
+    }
+
+    /**
+     * Add receiver processing stats times
+     * @param startTime
+     */
+    protected void addReceivedProcessingStats(long startTime) {
+        long current = System.currentTimeMillis() ;
+        long time = current - startTime ;
+        synchronized(this) {
+            if(time < minReceivedProcessingTime)
+                minReceivedProcessingTime = time ;
+            if( time > maxReceivedProcessingTime)
+                maxReceivedProcessingTime = time ;
+            receivedProcessingTime += time ;
+        }
+        if (log.isDebugEnabled()) {
+            if ((current - lastChecked) > 5000) {
+                log.debug("Calc msg send time total=" + receivedTime
+                        + "ms num request=" + nrOfMsgsReceived
+                        + " average per msg="
+                        + (receivedTime / nrOfMsgsReceived) + "ms.");
+                lastChecked=current ;
+            }
+        }
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.catalina.cluster.io.ListenCallback#sendAck()
+     */
+    public void sendAck() throws IOException {
+        // do nothing
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/Constants.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/Constants.java
new file mode 100644
index 0000000..a1acc4b
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/Constants.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.cluster.tcp;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.cluster.tcp</code>
+ * package.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.cluster.tcp";
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/DataSender.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/DataSender.java
new file mode 100644
index 0000000..14fc09c
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/DataSender.java
@@ -0,0 +1,884 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.io.XByteBuffer;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * Send cluster messages with only one socket. Ack and keep Alive Handling is
+ * supported
+ * 
+ * @author Peter Rossbach
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ * @since 5.5.7
+ */
+public class DataSender implements IDataSender {
+
+    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(DataSender.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm = StringManager
+            .getManager(Constants.Package);
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "DataSender/2.1";
+
+    /**
+     * receiver address
+     */
+    private InetAddress address;
+
+    /**
+     * receiver port
+     */
+    private int port;
+
+    
+    /**
+     * cluster domain
+     */
+    private String domain;
+
+    /**
+     * current sender socket
+     */
+    private Socket socket = null;
+
+    /**
+     * is Socket really connected
+     */
+    private boolean isSocketConnected = false;
+
+    /**
+     * sender is in suspect state (last transfer failed)
+     */
+    private boolean suspect;
+
+    /**
+     * wait time for ack
+     */
+    private long ackTimeout;
+
+    /**
+     * number of requests
+     */
+    protected long nrOfRequests = 0;
+
+    /**
+     * total bytes to transfer
+     */
+    protected long totalBytes = 0;
+
+    /**
+     * number of connects
+     */
+    protected long connectCounter = 0;
+
+    /**
+     * number of explizit disconnects
+     */
+    protected long disconnectCounter = 0;
+
+    /**
+     * number of failing acks
+     */
+    protected long missingAckCounter = 0;
+
+    /**
+     * number of data resends (second trys after socket failure)
+     */
+    protected long dataResendCounter = 0;
+
+    /**
+     * number of data failure sends 
+     */
+    protected long dataFailureCounter = 0;
+    
+    /**
+     * doProcessingStats
+     */
+    protected boolean doProcessingStats = false;
+
+    /**
+     * proessingTime
+     */
+    protected long processingTime = 0;
+    
+    /**
+     * min proessingTime
+     */
+    protected long minProcessingTime = Long.MAX_VALUE ;
+
+    /**
+     * max proessingTime
+     */
+    protected long maxProcessingTime = 0;
+   
+    /**
+     * doWaitAckStats
+     */
+    protected boolean doWaitAckStats = false;
+
+    /**
+     * waitAckTime
+     */
+    protected long waitAckTime = 0;
+    
+    /**
+     * min waitAckTime
+     */
+    protected long minWaitAckTime = Long.MAX_VALUE ;
+
+    /**
+     * max waitAckTime
+     */
+    protected long maxWaitAckTime = 0;
+
+    /**
+     * keep socket open for no more than one min
+     */
+    private long keepAliveTimeout = 60 * 1000;
+
+    /**
+     * max requests before reconnecting (default -1 unlimited)
+     */
+    private int keepAliveMaxRequestCount = -1;
+
+    /**
+     * Last connect timestamp
+     */
+    protected long keepAliveConnectTime = 0;
+
+    /**
+     * keepalive counter
+     */
+    protected int keepAliveCount = 0;
+
+    /**
+     * wait for receiver Ack
+     */
+    private boolean waitForAck = false;
+
+    /**
+     * number of socket close
+     */
+    private int socketCloseCounter = 0 ;
+
+    /**
+     * number of socket open
+     */
+    private int socketOpenCounter = 0 ;
+
+    /**
+     * number of socket open failures
+     */
+    private int socketOpenFailureCounter = 0 ;
+
+    /**
+     * After failure make a resend
+     */
+    private boolean resend = false ;
+
+    
+    // ------------------------------------------------------------- Constructor
+    
+    public DataSender(String domain,InetAddress host, int port) {
+        this.address = host;
+        this.port = port;
+        this.domain = domain;
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("IDataSender.create",address, new Integer(
+                    port)));
+    }
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * @return Returns the nrOfRequests.
+     */
+    public long getNrOfRequests() {
+        return nrOfRequests;
+    }
+
+    /**
+     * @return Returns the totalBytes.
+     */
+    public long getTotalBytes() {
+        return totalBytes;
+    }
+
+    /**
+     * @return Returns the avg totalBytes/nrOfRequests.
+     */
+    public long getAvgMessageSize() {
+        return totalBytes / nrOfRequests;
+    }
+
+    /**
+     * @return Returns the avg processingTime/nrOfRequests.
+     */
+    public double getAvgProcessingTime() {
+        return ((double)processingTime) / nrOfRequests;
+    }
+ 
+    /**
+     * @return Returns the maxProcessingTime.
+     */
+    public long getMaxProcessingTime() {
+        return maxProcessingTime;
+    }
+    
+    /**
+     * @return Returns the minProcessingTime.
+     */
+    public long getMinProcessingTime() {
+        return minProcessingTime;
+    }
+    
+    /**
+     * @return Returns the processingTime.
+     */
+    public long getProcessingTime() {
+        return processingTime;
+    }
+    
+    /**
+     * @return Returns the doProcessingStats.
+     */
+    public boolean isDoProcessingStats() {
+        return doProcessingStats;
+    }
+    
+    /**
+     * @param doProcessingStats The doProcessingStats to set.
+     */
+    public void setDoProcessingStats(boolean doProcessingStats) {
+        this.doProcessingStats = doProcessingStats;
+    }
+ 
+ 
+    /**
+     * @return Returns the doWaitAckStats.
+     */
+    public boolean isDoWaitAckStats() {
+        return doWaitAckStats;
+    }
+    
+    /**
+     * @param doWaitAckStats The doWaitAckStats to set.
+     */
+    public void setDoWaitAckStats(boolean doWaitAckStats) {
+        this.doWaitAckStats = doWaitAckStats;
+    }
+    
+    /**
+     * @return Returns the avg waitAckTime/nrOfRequests.
+     */
+    public double getAvgWaitAckTime() {
+        return ((double)waitAckTime) / nrOfRequests;
+    }
+ 
+    /**
+     * @return Returns the maxWaitAckTime.
+     */
+    public long getMaxWaitAckTime() {
+        return maxWaitAckTime;
+    }
+    
+    /**
+     * @return Returns the minWaitAckTime.
+     */
+    public long getMinWaitAckTime() {
+        return minWaitAckTime;
+    }
+    
+    /**
+     * @return Returns the waitAckTime.
+     */
+    public long getWaitAckTime() {
+        return waitAckTime;
+    }
+    
+    /**
+     * @return Returns the connectCounter.
+     */
+    public long getConnectCounter() {
+        return connectCounter;
+    }
+
+    /**
+     * @return Returns the disconnectCounter.
+     */
+    public long getDisconnectCounter() {
+        return disconnectCounter;
+    }
+
+    /**
+     * @return Returns the missingAckCounter.
+     */
+    public long getMissingAckCounter() {
+        return missingAckCounter;
+    }
+
+    /**
+     * @return Returns the socketOpenCounter.
+     */
+    public int getSocketOpenCounter() {
+        return socketOpenCounter;
+    }
+    
+    /**
+     * @return Returns the socketOpenFailureCounter.
+     */
+    public int getSocketOpenFailureCounter() {
+        return socketOpenFailureCounter;
+    }
+
+    /**
+     * @return Returns the socketCloseCounter.
+     */
+    public int getSocketCloseCounter() {
+        return socketCloseCounter;
+    }
+
+    /**
+     * @return Returns the dataResendCounter.
+     */
+    public long getDataResendCounter() {
+        return dataResendCounter;
+    }
+
+    /**
+     * @return Returns the dataFailureCounter.
+     */
+    public long getDataFailureCounter() {
+        return dataFailureCounter;
+    }
+    
+    /**
+     * @param address The address to set.
+     */
+    public void setAddress(InetAddress address) {
+        this.address = address;
+    }
+
+    public InetAddress getAddress() {
+        return address;
+    }
+
+    
+    /**
+     * @param port The port to set.
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+    
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * @return Returns the domain.
+     */
+    public String getDomain() {
+        return domain;
+    }
+    
+    /**
+     * @param domain The domain to set.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    
+    public boolean isConnected() {
+        return isSocketConnected;
+    }
+
+    /**
+     * @param isSocketConnected
+     *            The isSocketConnected to set.
+     */
+    protected void setSocketConnected(boolean isSocketConnected) {
+        this.isSocketConnected = isSocketConnected;
+    }
+
+    public boolean isSuspect() {
+        return suspect;
+    }
+
+    public boolean getSuspect() {
+        return suspect;
+    }
+
+    public void setSuspect(boolean suspect) {
+        this.suspect = suspect;
+    }
+
+    public long getAckTimeout() {
+        return ackTimeout;
+    }
+
+    public void setAckTimeout(long ackTimeout) {
+        this.ackTimeout = ackTimeout;
+    }
+
+    public long getKeepAliveTimeout() {
+        return keepAliveTimeout;
+    }
+
+    public void setKeepAliveTimeout(long keepAliveTimeout) {
+        this.keepAliveTimeout = keepAliveTimeout;
+    }
+
+    public int getKeepAliveMaxRequestCount() {
+        return keepAliveMaxRequestCount;
+    }
+
+    public void setKeepAliveMaxRequestCount(int keepAliveMaxRequestCount) {
+        this.keepAliveMaxRequestCount = keepAliveMaxRequestCount;
+    }
+
+    /**
+     * @return Returns the keepAliveConnectTime.
+     */
+    public long getKeepAliveConnectTime() {
+        return keepAliveConnectTime;
+    }
+
+    /**
+     * @return Returns the keepAliveCount.
+     */
+    public int getKeepAliveCount() {
+        return keepAliveCount;
+    }
+
+    /**
+     * @return Returns the waitForAck.
+     */
+    public boolean isWaitForAck() {
+        return waitForAck;
+    }
+
+    /**
+     * @param waitForAck
+     *            The waitForAck to set.
+     */
+    public void setWaitForAck(boolean waitForAck) {
+        this.waitForAck = waitForAck;
+    }
+
+    /**
+     * @return Returns the resend.
+     */
+    public boolean isResend() {
+        return resend;
+    }
+    /**
+     * @param resend The resend to set.
+     */
+    public void setResend(boolean resend) {
+        this.resend = resend;
+    }
+    /**
+     * @return Returns the socket.
+     */
+    public Socket getSocket() {
+        return socket;
+    }
+    /**
+     * @param socket The socket to set.
+     */
+    public void setSocket(Socket socket) {
+        this.socket = socket;
+    }
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Connect other cluster member receiver 
+     * @see org.apache.catalina.cluster.tcp.IDataSender#connect()
+     */
+    public synchronized void connect() throws java.io.IOException {
+        openSocket();
+        if(isConnected()) {
+            connectCounter++;
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("IDataSender.connect", address.getHostAddress(),
+                        new Integer(port),new Long(connectCounter)));
+        }
+   }
+
+ 
+    /**
+     * disconnect and close socket
+     * 
+     * @see IDataSender#disconnect()
+     */
+    public synchronized void disconnect() {
+        boolean connect = isConnected() ;
+        closeSocket();
+        if(connect) {
+            disconnectCounter++;
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("IDataSender.disconnect", address.getHostAddress(),
+                    new Integer(port),new Long(disconnectCounter)));
+        }
+    }
+
+    /**
+     * Check, if time to close socket! Important for AsyncSocketSender that
+     * replication thread is not fork again! <b>Only work when keepAliveTimeout
+     * or keepAliveMaxRequestCount greater -1 </b>
+     * FIXME Can we close a socket when a message wait for ack?
+     * @return true, is socket close
+     * @see DataSender#closeSocket()
+     */
+    public synchronized boolean checkKeepAlive() {
+        boolean isCloseSocket = true ;
+        if(isConnected()) {
+            if ((keepAliveTimeout > -1 && (System.currentTimeMillis() - keepAliveConnectTime) > keepAliveTimeout)
+                || (keepAliveMaxRequestCount > -1 && keepAliveCount >= keepAliveMaxRequestCount)) {
+                closeSocket();
+            } else
+                isCloseSocket = false ;
+        }
+        return isCloseSocket;
+    }
+
+    /**
+     * Send message
+     * 
+     * @see org.apache.catalina.cluster.tcp.IDataSender#sendMessage(,
+     *      ClusterData)
+     */
+    public synchronized void sendMessage(ClusterData data)
+            throws java.io.IOException {
+        pushMessage(data);
+    }
+
+    /**
+     * Reset sender statistics
+     */
+    public synchronized void resetStatistics() {
+        nrOfRequests = 0;
+        totalBytes = 0;
+        disconnectCounter = 0;
+        connectCounter = isConnected() ? 1 : 0;
+        missingAckCounter = 0;
+        dataResendCounter = 0;
+        dataFailureCounter = 0 ;
+        socketOpenCounter =isConnected() ? 1 : 0;
+        socketOpenFailureCounter = 0 ;
+        socketCloseCounter = 0;
+        processingTime = 0 ;
+        minProcessingTime = Long.MAX_VALUE ;
+        maxProcessingTime = 0 ;
+        waitAckTime = 0 ;
+        minWaitAckTime = Long.MAX_VALUE ;
+        maxWaitAckTime = 0 ;
+    }
+
+    /**
+     * Name of this SockerSender
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("DataSender[");
+        buf.append(getAddress()).append(":").append(getPort()).append("]");
+        return buf.toString();
+    }
+
+    // --------------------------------------------------------- Protected Methods
+ 
+    /**
+     * open real socket and set time out when waitForAck is enabled
+     * is socket open return directly
+     * @throws IOException
+     * @throws SocketException
+     */
+    protected void openSocket() throws IOException, SocketException {
+       if(isConnected())
+           return ;
+       try {
+            createSocket();
+            if (isWaitForAck())
+                socket.setSoTimeout((int) ackTimeout);
+            isSocketConnected = true;
+            socketOpenCounter++;
+            this.keepAliveCount = 0;
+            this.keepAliveConnectTime = System.currentTimeMillis();
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("IDataSender.openSocket", address
+                        .getHostAddress(), new Integer(port),new Long(socketOpenCounter)));
+      } catch (IOException ex1) {
+            socketOpenFailureCounter++ ;
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("IDataSender.openSocket.failure",
+                        address.getHostAddress(), new Integer(port),new Long(socketOpenFailureCounter)), ex1);
+            throw ex1;
+        }
+        
+     }
+
+    /**
+     * @throws IOException
+     * @throws SocketException
+     */
+    protected void createSocket() throws IOException, SocketException {
+        socket = new Socket(getAddress(), getPort());
+    }
+
+    /**
+     * close socket
+     * 
+     * @see DataSender#disconnect()
+     * @see DataSender#closeSocket()
+     */
+    protected void closeSocket() {
+        if(isConnected()) {
+             if (socket != null) {
+                try {
+                    socket.close();
+                } catch (IOException x) {
+                } finally {
+                    socket = null;
+                }
+            }
+            this.keepAliveCount = 0;
+            isSocketConnected = false;
+            socketCloseCounter++;
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("IDataSender.closeSocket",
+                        address.getHostAddress(), new Integer(port),new Long(socketCloseCounter)));
+       }
+    }
+
+    /**
+     * Add statistic for this socket instance
+     * 
+     * @param length
+     */
+    protected void addStats(int length) {
+        nrOfRequests++;
+        totalBytes += length;
+        if (log.isInfoEnabled() && (nrOfRequests % 100) == 0) {
+            log.info(sm.getString("IDataSender.stats", new Object[] {
+                    getAddress().getHostAddress(), new Integer(getPort()),
+                    new Long(totalBytes), new Long(nrOfRequests),
+                    new Long(totalBytes / nrOfRequests),
+                    new Long(getProcessingTime()),
+                    new Double(getAvgProcessingTime())}));
+        }
+    }
+
+    /**
+     * Add processing stats times
+     * @param startTime
+     */
+    protected void addProcessingStats(long startTime) {
+        long time = System.currentTimeMillis() - startTime ;
+        if(time < minProcessingTime)
+            minProcessingTime = time ;
+        if( time > maxProcessingTime)
+            maxProcessingTime = time ;
+        processingTime += time ;
+    }
+    
+    /**
+     * Add waitAck stats times
+     * @param startTime
+     */
+    protected void addWaitAckStats(long startTime) {
+        long time = System.currentTimeMillis() - startTime ;
+        if(time < minWaitAckTime)
+            minWaitAckTime = time ;
+        if( time > maxWaitAckTime)
+            maxWaitAckTime = time ;
+        waitAckTime += time ;
+    }
+    /**
+     * Push messages with only one socket at a time
+     * Wait for ack is needed and make auto retry when write message is failed.
+     * After sending error close and reopen socket again.
+     * 
+     * After successfull sending update stats
+     * 
+     * WARNING: Subclasses must be very carefull that only one thread call this pushMessage at once!!!
+     * 
+     * @see #closeSocket()
+     * @see #openSocket()
+     * @see #writeData(ClusterData)
+     * 
+     * @param data
+     *            data to send
+     * @throws java.io.IOException
+     * @since 5.5.10
+     */
+    protected void pushMessage( ClusterData data)
+            throws java.io.IOException {
+        long time = 0 ;
+        if(doProcessingStats) {
+            time = System.currentTimeMillis();
+        }
+        boolean messageTransfered = false ;
+        synchronized(this) {
+            checkKeepAlive();
+            if (!isConnected())
+                openSocket();
+        }
+        Exception exception = null;
+        try {
+             writeData(data);
+             messageTransfered = true ;
+        } catch (java.io.IOException x) {
+            if(data.getResend() == ClusterMessage.FLAG_ALLOWED || 
+                    (data.getResend() == ClusterMessage.FLAG_DEFAULT && isResend() )) {
+                // second try with fresh connection
+                dataResendCounter++;
+                if (log.isTraceEnabled())
+                    log.trace(sm.getString("IDataSender.send.again", address.getHostAddress(),
+                            new Integer(port)),x);
+                synchronized(this) {
+                    closeSocket();
+                    openSocket();
+                }
+                try {
+                    writeData(data);
+                    messageTransfered = true;
+                } catch (IOException xx) {
+                    exception = xx;
+                    throw xx ;
+                }
+            } else {
+                exception = x;
+                // FIXME Hmm, throw the exception or not?
+            }
+        } finally {
+            this.keepAliveCount++;
+            checkKeepAlive();
+            if(doProcessingStats) {
+                addProcessingStats(time);
+            }
+            if(messageTransfered) {
+                addStats(data.getMessage().length);
+                if (log.isTraceEnabled()) {
+                    log.trace(sm.getString("IDataSender.send.message", address.getHostAddress(),
+                        new Integer(port), data.getUniqueId(), new Long(data.getMessage().length)));
+                }
+            } else {
+                dataFailureCounter++;
+                if (log.isWarnEnabled())
+                    log.warn(sm.getString("IDataSender.send.lost",  address.getHostAddress(),
+                            new Integer(port), data.getType(), data.getUniqueId()),exception);
+            }
+        }
+    }
+
+    /**
+     * Sent real cluster Message to socket stream
+     * FIXME send compress
+     * @param data
+     * @throws IOException
+     * @since 5.5.10
+     */
+    protected void writeData(ClusterData data) throws IOException {
+        OutputStream out = socket.getOutputStream();
+        out.write(XByteBuffer.START_DATA);
+        out.write(XByteBuffer.toBytes(data.getCompress()));
+        out.write(XByteBuffer.toBytes(data.getMessage().length));
+        out.write(data.getMessage());
+        out.write(XByteBuffer.END_DATA);
+        out.flush();
+        if (isWaitForAck())
+            waitForAck(ackTimeout);
+        
+    }
+
+    /**
+     * Wait for Acknowledgement from other server
+     * FIXME Please, not wait only for three charcters, better control that the wait ack message is correct.
+     * @param timeout
+     * @throws java.io.IOException
+     * @throws java.net.SocketTimeoutException
+     */
+    protected void waitForAck(long timeout) throws java.io.IOException {
+        long time = 0 ;
+        if(doWaitAckStats) {
+            time = System.currentTimeMillis();
+        }
+        try {
+            int bytesRead = 0;
+            if ( log.isTraceEnabled() ) 
+                log.trace(sm.getString("IDataSender.ack.start",getAddress(), new Integer(socket.getLocalPort())));
+            int i = socket.getInputStream().read();
+            while ((i != -1) && (i != 3) && bytesRead < 10) {
+                if ( log.isTraceEnabled() ) 
+                    log.trace(sm.getString("IDataSender.ack.read",getAddress(), new Integer(socket.getLocalPort()),new Character((char) i)));
+                bytesRead++;
+                i = socket.getInputStream().read();
+            }
+            if (i != 3) {
+                if (i == -1) {
+                    throw new IOException(sm.getString("IDataSender.ack.eof",getAddress(), new Integer(socket.getLocalPort())));
+                } else {
+                    throw new IOException(sm.getString("IDataSender.ack.wrong",getAddress(), new Integer(socket.getLocalPort())));
+                }
+            } else {
+                if (log.isTraceEnabled()) {
+                    log.trace(sm.getString("IDataSender.ack.receive", getAddress(),new Integer(socket.getLocalPort())));
+                }
+            }
+        } catch (IOException x) {
+            missingAckCounter++;
+            log.warn(sm.getString("IDataSender.ack.missing", getAddress(),
+                    new Integer(socket.getLocalPort()), new Long(
+                            this.ackTimeout)),x);
+            throw x;
+        } finally {
+            if(doWaitAckStats) {
+                addWaitAckStats(time);
+            }
+        }
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/DataSenders.properties b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/DataSenders.properties
new file mode 100644
index 0000000..b56265c
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/DataSenders.properties
@@ -0,0 +1,4 @@
+fastasyncqueue=org.apache.catalina.cluster.tcp.FastAsyncSocketSender
+asynchronous=org.apache.catalina..cluster.tcp.AsyncSocketSender
+synchronous=org.apache.catalina..cluster.tcp.SocketSender
+pooled=org.apache.catalina.cluster.tcp.PooledSocketSender
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/FastAsyncSocketSender.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/FastAsyncSocketSender.java
new file mode 100644
index 0000000..60447f5
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/FastAsyncSocketSender.java
@@ -0,0 +1,481 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.net.InetAddress;
+
+import org.apache.catalina.cluster.util.FastQueue;
+import org.apache.catalina.cluster.util.LinkObject;
+import org.apache.catalina.cluster.util.IQueue;
+
+/**
+ * Send cluster messages from a Message queue with only one socket. Ack and keep
+ * Alive Handling is supported. Fast Queue can limit queue size and consume all messages at queue at one block.<br/>
+ * Limit the queue lock contention under high load!<br/>
+ * <ul>
+ * <li>With autoConnect=false at ReplicationTransmitter, you can disconnect the
+ * sender and all messages are queued. Only use this for small maintaince
+ * isuses!</li>
+ * <li>waitForAck=true, means that receiver ack the transfer</li>
+ * <li>after one minute idle time, or number of request (100) the connection is
+ * reconnected with next request. Change this for production use!</li>
+ * <li>default ackTimeout is 15 sec: this is very low for big all session
+ * replication messages after restart a node</li>
+ * <li>disable keepAlive: keepAliveTimeout="-1" and
+ * keepAliveMaxRequestCount="-1"</li>
+ * <li>maxQueueLength: Limit the sender queue length (membership goes well, but transfer is failure!!)</li>
+ * </ul>
+ * FIXME: refactor code duplications with AsyncSocketSender => configurable or extract super class 
+ * @author Peter Rossbach ( idea comes form Rainer Jung)
+ * @version $Revision$ $Date$
+ * @since 5.5.9
+ */
+public class FastAsyncSocketSender extends DataSender {
+
+    private static int threadCounter = 1;
+
+    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(FastAsyncSocketSender.class);
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "FastAsyncSocketSender/3.0";
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Message Queue
+     */
+    private FastQueue queue = new FastQueue();
+
+    /**
+     * Active thread to push messages asynchronous to the other replication node
+     */
+    private FastQueueThread queueThread = null;
+
+    /**
+     * Count number of queue message
+     */
+    private long inQueueCounter = 0;
+
+    /**
+     * Count all successfull push messages from queue
+     */
+    private long outQueueCounter = 0;
+
+    private int threadPriority = Thread.NORM_PRIORITY;;
+
+    // ------------------------------------------------------------- Constructor
+
+    /**
+     * start background thread to push incomming cluster messages to replication
+     * node
+     * 
+     * @param domain replication cluster domain (session domain)
+     * @param host replication node tcp address
+     * @param port replication node tcp port
+     */
+    public FastAsyncSocketSender(String domain,InetAddress host, int port) {
+        super(domain,host, port);
+        checkThread();
+    }
+  
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+ 
+
+    /**
+     * get current add wait timeout 
+     * @return current wait timeout
+     */
+    public long getQueueAddWaitTimeout() {
+        
+        return queue.getAddWaitTimeout();
+    }
+
+    /**
+     * Set add wait timeout (default 10000 msec)
+     * @param timeout
+     */
+    public void setQueueAddWaitTimeout(long timeout) {
+        queue.setAddWaitTimeout(timeout);
+    }
+
+    /**
+     * get current remove wait timeout
+     * @return The timeout
+     */
+    public long getQueueRemoveWaitTimeout() {
+        return queue.getRemoveWaitTimeout();
+    }
+
+    /**
+     * set remove wait timeout ( default 30000 msec)
+     * @param timeout
+     */
+    public void setRemoveWaitTimeout(long timeout) {
+        queue.setRemoveWaitTimeout(timeout);
+    }
+
+    /**
+     * @return Returns the checkLock.
+     */
+    public boolean isQueueCheckLock() {
+        return queue.isCheckLock();
+    }
+    /**
+     * @param checkLock The checkLock to set.
+     */
+    public void setQueueCheckLock(boolean checkLock) {
+        queue.setCheckLock(checkLock);
+    }
+    /**
+     * @return Returns the doStats.
+     */
+    public boolean isQueueDoStats() {
+        return queue.isDoStats();
+    }
+    /**
+     * @param doStats The doStats to set.
+     */
+    public void setQueueDoStats(boolean doStats) {
+        queue.setDoStats(doStats);
+    }
+    /**
+     * @return Returns the timeWait.
+     */
+    public boolean isQueueTimeWait() {
+        return queue.isTimeWait();
+    }
+    /**
+     * @param timeWait The timeWait to set.
+     */
+    public void setQueueTimeWait(boolean timeWait) {
+        queue.setTimeWait(timeWait);
+    }
+        
+    /**
+     * @return Returns the inQueueCounter.
+     */
+    public int getMaxQueueLength() {
+        return queue.getMaxQueueLength();
+    }
+
+    /**
+     * @param length max queue length
+     */
+    public void setMaxQueueLength(int length) {
+        queue.setMaxQueueLength(length);
+    }
+
+    /**
+     * @return Returns the add wait times.
+     */
+    public long getQueueAddWaitTime() {
+        return queue.getAddWait();
+    }
+
+    /**
+     * @return Returns the add wait times.
+     */
+    public long getQueueRemoveWaitTime() {
+        return queue.getRemoveWait();
+    }
+
+    /**
+     * @return Returns the inQueueCounter.
+     */
+    public long getInQueueCounter() {
+        return inQueueCounter;
+    }
+
+    /**
+     * @return Returns the outQueueCounter.
+     */
+    public long getOutQueueCounter() {
+        return outQueueCounter;
+    }
+
+    /**
+     * @return Returns the queueSize.
+     */
+    public int getQueueSize() {
+        return queue.getSize();
+    }
+
+    /**
+     * change active the queue Thread priority 
+     * @param threadPriority value must be between MIN and MAX Thread Priority
+     * @exception IllegalArgumentException
+     */
+    public void setThreadPriority(int threadPriority) {
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("FastAsyncSocketSender.setThreadPriority",
+                    getAddress().getHostAddress(), new Integer(getPort()),
+                    new Integer(threadPriority)));
+        if (threadPriority < Thread.MIN_PRIORITY) {
+            throw new IllegalArgumentException(sm.getString(
+                    "FastAsyncSocketSender.min.exception", getAddress()
+                            .getHostAddress(), new Integer(getPort()),
+                    new Integer(threadPriority)));
+        } else if (threadPriority > Thread.MAX_PRIORITY) {
+            throw new IllegalArgumentException(sm.getString(
+                    "FastAsyncSocketSender.max.exception", getAddress()
+                            .getHostAddress(), new Integer(getPort()),
+                    new Integer(threadPriority)));
+        }
+        this.threadPriority = threadPriority;
+        if (queueThread != null)
+            queueThread.setPriority(threadPriority);
+    }
+
+    /**
+     * Get the current threadPriority
+     * @return The thread priority
+     */
+    public int getThreadPriority() {
+        return threadPriority;
+    }
+
+    /**
+     * @return Returns the queuedNrOfBytes.
+     */
+    public long getQueuedNrOfBytes() {
+        if(queueThread != null)
+            return queueThread.getQueuedNrOfBytes();
+        return 0l ;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /*
+     * Connect to socket and start background thread to ppush queued messages
+     * 
+     * @see org.apache.catalina.cluster.tcp.IDataSender#connect()
+     */
+    public void connect() throws java.io.IOException {
+        super.connect();
+        checkThread();
+        queue.start() ;
+    }
+
+    /**
+     * Disconnect socket ad stop queue thread
+     * 
+     * @see org.apache.catalina.cluster.tcp.IDataSender#disconnect()
+     */
+    public void disconnect() {
+        stopThread();
+        queue.stop() ;
+        super.disconnect();
+    }
+
+    /**
+     * Send message to queue for later sending.
+     * 
+     * @see org.apache.catalina.cluster.tcp.IDataSender#sendMessage(ClusterData)
+     */
+    public void sendMessage(String messageid, ClusterData data)
+            throws java.io.IOException {
+        queue.add(messageid, data);
+        synchronized (this) {
+            inQueueCounter++;
+            queueThread.incQueuedNrOfBytes(data.getMessage().length);
+       }
+       if (log.isTraceEnabled())
+            log.trace(sm.getString("AsyncSocketSender.queue.message",
+                    getAddress().getHostAddress(), new Integer(getPort()), messageid, new Long(
+                            data.getMessage().length)));
+    }
+
+    /**
+     * Reset sender statistics
+     */
+    public synchronized void resetStatistics() {
+        super.resetStatistics();
+        inQueueCounter = queue.getSize();
+        outQueueCounter = 0;
+        queue.resetStatistics();
+    }
+
+    /**
+     * Name of this SockerSender
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("FastAsyncSocketSender[");
+        buf.append(getAddress().getHostAddress()).append(":").append(getPort()).append("]");
+        return buf.toString();
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Start Queue thread as daemon
+     */
+    protected void checkThread() {
+        if (queueThread == null) {
+            if (log.isInfoEnabled())
+                log.info(sm.getString("AsyncSocketSender.create.thread",
+                        getAddress(), new Integer(getPort())));
+            queueThread = new FastQueueThread(this, queue);
+            queueThread.setDaemon(true);
+            queueThread.setPriority(getThreadPriority());
+            queueThread.start();
+        }
+    }
+
+    /**
+     * stop queue worker thread
+     */
+    protected void stopThread() {
+        if (queueThread != null) {
+            queueThread.stopRunning();
+            queueThread = null;
+        }
+    }
+
+    // -------------------------------------------------------- Inner Class
+
+    private class FastQueueThread extends Thread {
+
+        
+        /**
+         * Sender queue
+         */
+        private IQueue queue = null;
+
+        /**
+         * Active sender
+         */
+        private FastAsyncSocketSender sender = null;
+
+        /**
+         * Thread is active
+         */
+        private boolean keepRunning = true;
+
+        /**
+         * Current number of bytes from all queued messages
+         */
+        private long queuedNrOfBytes = 0;
+
+       
+
+        /**
+         * Only use inside FastAsyncSocketSender
+         * @param sender
+         * @param queue
+         */
+        private FastQueueThread(FastAsyncSocketSender sender, IQueue queue) {
+            setName("Cluster-FastAsyncSocketSender-" + (threadCounter++));
+            this.queue = queue;
+            this.sender = sender;
+        }
+        
+        /**
+         * @return Returns the queuedNrOfBytes.
+         */
+        public long getQueuedNrOfBytes() {
+            return queuedNrOfBytes;
+        }
+        
+        protected synchronized void setQueuedNrOfBytes(long queuedNrOfBytes) {
+            this.queuedNrOfBytes = queuedNrOfBytes;
+        }
+
+        protected synchronized void incQueuedNrOfBytes(long size) {
+            queuedNrOfBytes += size;
+        }
+        
+        protected synchronized void decQueuedNrOfBytes(long size) {
+            queuedNrOfBytes -= size;
+        }
+
+
+        public void stopRunning() {
+            keepRunning = false;
+        }
+        
+        
+        /* Get the objects from queue and send all mesages to the sender.
+         * @see java.lang.Runnable#run()
+         */
+        public void run() {
+            while (keepRunning) {
+                long queueSize;
+                LinkObject entry = getQueuedMessage();
+                if (entry != null) {
+                    pushQueuedMessages(entry);
+                } else {
+                    if (keepRunning) {
+                        log.warn(sm.getString("AsyncSocketSender.queue.empty",
+                                sender.getAddress(), new Integer(sender
+                                        .getPort())));
+                    }
+                }
+            }
+        }
+
+        /**
+         * @return
+         */
+        protected LinkObject getQueuedMessage() {
+            // get a link list of all queued objects
+            if (log.isTraceEnabled())
+                log.trace("Queuesize before=" + ((FastQueue) queue).getSize());
+            LinkObject entry = queue.remove();
+            if (log.isTraceEnabled())
+                log.trace("Queuesize after=" + ((FastQueue) queue).getSize());
+            return entry;
+        }
+
+        /**
+         * @param entry
+         */
+        protected void pushQueuedMessages(LinkObject entry) {
+            do {
+                int messagesize = 0;
+                try {
+                    ClusterData data = (ClusterData) entry.data();
+                    messagesize = data.getMessage().length;
+                    sender.pushMessage(data);
+                    outQueueCounter++;
+                } catch (Exception x) {
+                    log.warn(sm.getString(
+                            "AsyncSocketSender.send.error", entry
+                                    .getKey()), x);
+                } finally {
+                    decQueuedNrOfBytes(messagesize);
+                }
+                entry = entry.next();
+            } while (entry != null);
+        }
+
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/IDataSender.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/IDataSender.java
new file mode 100644
index 0000000..7fde93a
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/IDataSender.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.7
+ */
+
+public interface IDataSender
+{
+    public void setAddress(java.net.InetAddress address);
+    public java.net.InetAddress getAddress();
+    public void setPort(int port);
+    public int getPort();
+    public void connect() throws java.io.IOException;
+    public void disconnect();
+    public void sendMessage(ClusterData data) throws java.io.IOException;
+    public boolean isConnected();
+    public void setSuspect(boolean suspect);
+    public boolean getSuspect();
+    public void setAckTimeout(long timeout);
+    public long getAckTimeout();
+    public boolean isWaitForAck();
+    public void setWaitForAck(boolean isWaitForAck);
+    public boolean checkKeepAlive();
+    public String getDomain() ;
+    public void setDomain(String domain) ;
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/IDataSenderFactory.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/IDataSenderFactory.java
new file mode 100644
index 0000000..7d505d6
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/IDataSenderFactory.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.net.InetAddress;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.catalina.cluster.Member;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * Create DataSender for different modes. DataSender factory load mode list from 
+ * <code>org/apache/catalina/cluster/tcp/DataSenders.properties</code> resource.
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.7
+ */
+public class IDataSenderFactory {
+
+    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(IDataSenderFactory.class);
+    
+    private static final String DATASENDERS_PROPERTIES = "org/apache/catalina/cluster/tcp/DataSenders.properties";
+    public static final String SYNC_MODE = "synchronous";
+    public static final String ASYNC_MODE = "asynchronous";
+    public static final String POOLED_SYNC_MODE = "pooled";
+    public static final String FAST_ASYNC_QUEUE_MODE = "fastasyncqueue";
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm = StringManager
+            .getManager(Constants.Package);
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "IDataSenderFactory/2.0";
+
+    private IDataSenderFactory() {
+    }
+
+    private Properties senderModes;
+
+    private static IDataSenderFactory factory ;
+
+    static {
+        factory = new IDataSenderFactory();
+        factory.loadSenderModes();
+    }
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public static String getInfo() {
+        return (info);
+    }
+    
+    // ------------------------------------------------------------- static
+
+    /**
+     * Create a new DataSender
+     * @param mode replicaton mode 
+     * @param mbr sender target
+     * @return new sender object
+     * @throws java.io.IOException
+     */
+    public synchronized static IDataSender getIDataSender(String mode,
+            Member mbr) throws java.io.IOException {
+       // Identify the class name of the DataSender we should configure
+       IDataSender sender = factory.getSender(mode,mbr);
+       if(sender == null)
+           throw new java.io.IOException("Invalid replication mode=" + mode);          
+       return sender ;    
+    }
+
+    /**
+     * Check that mode is valid
+     * @param mode
+     * @return The replication mode (may be null if sender mode)
+     */
+    public static String validateMode(String mode) {
+        if(factory.isSenderMode(mode))
+            return null ;
+        else {
+            StringBuffer buffer = new StringBuffer("Replication mode has to be '");
+            for (Iterator iter = factory.senderModes.keySet().iterator(); iter.hasNext();) {
+                String key = (String) iter.next();  
+                buffer.append(key);
+                if(iter.hasNext())
+                    buffer.append("', '");
+            }
+            return buffer.toString();        
+        }
+    }
+    
+    // ------------------------------------------------------------- private
+
+    private boolean isSenderMode(String mode){
+        return senderModes != null && senderModes.containsKey(mode) ;           
+    }
+
+    private IDataSender getSender(String mode,Member mbr) {
+        IDataSender sender = null;
+        String senderName = null;
+        senderName = senderModes.getProperty(mode);
+        if (senderName != null) {
+
+            // Instantiate and install a data replication sender of the requested class
+            try {
+                Class senderClass = Class.forName(senderName);
+                Class paramTypes[] = new Class[3];
+                paramTypes[0] = Class.forName("java.lang.String");
+                paramTypes[1] = Class.forName("java.net.InetAddress");
+                paramTypes[2] = Integer.TYPE ;
+                Constructor constructor = senderClass.getConstructor(paramTypes);
+                if (constructor != null) {
+                    Object paramValues[] = new Object[3];
+                    paramValues[0] = mbr.getDomain();
+                    paramValues[1] = InetAddress.getByName(mbr.getHost());
+                    paramValues[2] = new Integer(mbr.getPort());
+                    sender = (IDataSender) constructor.newInstance(paramValues);
+                } else {
+                    log.error(sm.getString("IDataSender.senderModes.Instantiate",
+                            senderName));
+                }
+            } catch (Throwable t) {
+                log.error(sm.getString("IDataSender.senderModes.Instantiate",
+                        senderName), t);
+            }
+        } else {
+            log.error(sm.getString("IDataSender.senderModes.Missing", mode));
+        }
+        return sender;
+    }
+
+    private synchronized void loadSenderModes() {
+        // Load our mapping properties if necessary
+        if (senderModes == null) {
+            try {
+                InputStream is = IDataSender.class
+                        .getClassLoader()
+                        .getResourceAsStream(
+                                DATASENDERS_PROPERTIES);
+                if (is != null) {
+                    senderModes = new Properties();
+                    senderModes.load(is);
+                } else {
+                    log.error(sm.getString("IDataSender.senderModes.Resources"));
+                    return;
+                }
+            } catch (IOException e) {
+                log.error(sm.getString("IDataSender.senderModes.Resources"), e);
+                return;
+            }
+        }
+
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/LocalStrings.properties b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/LocalStrings.properties
new file mode 100644
index 0000000..6d3b5cd
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/LocalStrings.properties
@@ -0,0 +1,65 @@
+AsyncSocketSender.create.thread=Create sender [{0}:{1,number,integer}] queue thread to tcp background replication
+AsyncSocketSender.queue.message=Queue message to [{0}:{1,number,integer}] id=[{2}] size={3}
+AsyncSocketSender.send.error=Unable to asynchronously send session w/ id=[{0}] message will be ignored.
+AsyncSocketSender.queue.empty=Queue in sender [{0}:{1,number,integer}] returned null element!
+cluster.mbean.register.allready=MBean {0} allready registered!
+FastAsyncSocketSender.setThreadPriority=[{0}:{1,number,integer}] setPriority({2}) here
+FastAsyncSocketSender.min.exception=[{0}:{1,number,integer}] new priority {2} < MIN_PRIORITY
+FastAsyncSocketSender.max.exception=[{0}:{1,number,integer}] new priority {2} > MAX_PRIORITY
+IDataSender.ack.eof=EOF reached at local port [{0}:{1,number,integer}]
+IDataSender.ack.receive=Got ACK at local port [{0}:{1,number,integer}]
+IDataSender.ack.missing=Wasn't able to read acknowledgement from [{0}:{1,number,integer}] in {2,number,integer} ms. Disconnecting socket, and trying again.
+IDataSender.ack.read=Read wait ack char '{2}' [{0}:{1,number,integer}]
+IDataSender.ack.start=Waiting for ACK message [{0}:{1,number,integer}]
+IDataSender.ack.wrong=Missing correct ACK after 10 bytes read at local port [{0}:{1,number,integer}]
+IDataSender.connect=Sender connect to [{0}:{1,number,integer}] (connect count {2,number,integer})
+IDataSender.create=Create sender [{0}:{1,number,integer}]
+IDataSender.disconnect=Sender disconnect from [{0}:{1,number,integer}] (disconnect count {2,number,integer})
+IDataSender.closeSocket=Sender close socket to [{0}:{1,number,integer}] (close count {2,number,integer})
+IDataSender.openSocket=Sender open socket to [{0}:{1,number,integer}] (open count {2,number,integer})
+IDataSender.openSocket.failure=Open sender socket [{0}:{1,number,integer}] failure! (open failure count {2,number,integer})
+IDataSender.send.again=Send data again to [{0}:{1,number,integer}]
+IDataSender.send.crash=Send message crashed [{0}:{1,number,integer}] type=[{2}], id=[{3}]
+IDataSender.send.message=Send message to [{0}:{1,number,integer}] id=[{2}] size={3,number,integer}
+IDataSender.send.lost=Send message don't send [{0}:{1,number,integer}] type=[{2}], id=[{3}]
+IDataSender.senderModes.Configured=Configured a data replication sender for mode {0}
+IDataSender.senderModes.Instantiate=Cannot instantiate a data replication sender of class {0}
+IDataSender.senderModes.Missing=Cannot configure a data replication sender for mode {0}
+IDataSender.senderModes.Resources=Cannot load data replication sender mapping list
+IDataSender.stats=Send stats from [{0}:{1,number,integer}] Nr of bytes sent={2,number,integer} over {3} = {4,number,integer} bytes/request, processing time {5,number,integer} msec, avg processing time {6,number,integer} msec
+PoolSocketSender.senderQueue.sender.failed=PoolSocketSender create new sender to [{0}:{1,number,integer}] failed
+PoolSocketSender.noMoreSender=No socket sender available for client [{0}:{1,number,integer}] did it disappear?
+ReplicationTransmitter.getProperty=get property {0}
+ReplicationTransmitter.setProperty=set property {0}: {1} old value {2}
+ReplicationTransmitter.started=Start ClusterSender at cluster {0} with name {1}
+ReplicationTransmitter.stopped=Stopped ClusterSender at cluster {0} with name {1}
+ReplicationValve.filter.loading=Loading request filters={0}
+ReplicationValve.filter.token=Request filter={0}
+ReplicationValve.filter.token.failure=Unable to compile filter={0}
+ReplicationValve.invoke.uri=Invoking replication request on {0}
+ReplicationValve.nocluster=No cluster configured for this request.
+ReplicationValve.send.failure=Unable to perform replication request.
+ReplicationValve.send.invalid.failure=Unable to send session [id={0}] invalid message over cluster.
+ReplicationValve.session.found=Context {0}: Found session {1} but it isn't a ClusterSession.
+ReplicationValve.session.indicator=Context {0}: Primarity of session {0} in request attribute {1} is {2}.
+ReplicationValve.session.invalid=Context {0}: Requested session {1} is invalid, removed or not replicated at this node.
+ReplicationValve.stats=Average request time= {0} ms for Cluster overhead time={1} ms for {2} requests {3} filter requests (Request={4} ms Cluster={5} ms).
+SimpleTcpCluster.event.log=Cluster receive listener event {0} with data {1}
+SimpleTcpCluster.getProperty=get property {0}
+SimpleTcpCluster.setProperty=set property {0}: {1} old value {2}
+SimpleTcpCluster.default.addClusterListener=Add Default ClusterListener at cluster {0}
+SimpleTcpCluster.default.addClusterValves=Add Default ClusterValves at cluster {0}
+SimpleTcpCluster.default.addClusterReceiver=Add Default ClusterReceiver at cluster {0}
+SimpleTcpCluster.default.addClusterSender=Add Default ClusterSender at cluster {0}
+SimpleTcpCluster.default.addMembershipService=Add Default Membership Service at cluster {0}
+SimpleTcpCluster.log.receive=RECEIVE {0,date}:{0,time} {1,number} {2}:{3,number,integer} {4} {5}
+SimpleTcpCluster.log.send=SEND {0,date}:{0,time} {1,number} {2}:{3,number,integer} {4} 
+SimpleTcpCluster.log.send.all=SEND {0,date}:{0,time} {1,number} - {2}
+SocketReplictionListener.allreadyExists=ServerSocket [{0}:{1}] allready started!
+SocketReplictionListener.accept.failure=ServerSocket [{0}:{1}] - Exception to start thread or accept server socket
+SocketReplictionListener.open=Open Socket at [{0}:{1}]
+SocketReplictionListener.openclose.failure=ServerSocket [{0}:{1}] - Exception to open or close server socket
+SocketReplictionListener.portbusy=Port busy at [{0}:{i}] - reason [{2}]
+SocketReplictionListener.serverSocket.notExists=Fatal error: Receiver socket not bound address={0} port={1} maxport={2}
+SocketReplictionListener.timeout=Receiver ServerSocket no started [{0}:{1}] - reason: timeout={2} or listen={3}
+SocketReplictionListener.unlockSocket.failure=UnLocksocket failure at ServerSocket [{0:{1}]
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/PooledSocketSender.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/PooledSocketSender.java
new file mode 100644
index 0000000..1c9bdb1
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/PooledSocketSender.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.LinkedList;
+
+/**
+ * Send cluster messages with a pool of sockets (25).
+ * 
+ * FIXME support processing stats
+ * 
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version 1.2
+ */
+
+public class PooledSocketSender extends DataSender {
+
+    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(org.apache.catalina.cluster.tcp.PooledSocketSender.class);
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "PooledSocketSender/2.0";
+
+    // ----------------------------------------------------- Instance Variables
+
+    private int maxPoolSocketLimit = 25;
+
+    private SenderQueue senderQueue = null;
+
+    //  ----------------------------------------------------- Constructor
+
+   /**
+    * @param domain replication cluster domain (session domain)
+    * @param host replication node tcp address
+    * @param port replication node tcp port
+    */
+    public PooledSocketSender(String domain,InetAddress host, int port) {
+        super(domain,host, port);
+        senderQueue = new SenderQueue(this, maxPoolSocketLimit);
+    }
+   
+    //  ----------------------------------------------------- Public Properties
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    public void setMaxPoolSocketLimit(int limit) {
+        maxPoolSocketLimit = limit;
+        senderQueue.setLimit(limit);
+    }
+
+    public int getMaxPoolSocketLimit() {
+        return maxPoolSocketLimit;
+    }
+
+    public int getInPoolSize() {
+        return senderQueue.getInPoolSize();
+    }
+
+    public int getInUsePoolSize() {
+        return senderQueue.getInUsePoolSize();
+    }
+
+    //  ----------------------------------------------------- Public Methode
+
+    public void connect() throws java.io.IOException {
+        //do nothing, happens in the socket sender itself
+        senderQueue.open();
+        setSocketConnected(true);
+        connectCounter++;
+    }
+
+    public void disconnect() {
+        senderQueue.close();
+        setSocketConnected(false);
+        disconnectCounter++;
+    }
+
+    /**
+     * send Message and use a pool of SocketSenders
+     * 
+     * @param messageId Message unique identifier
+     * @param data Message data
+     * @throws java.io.IOException
+     */
+    public void sendMessage(String messageId, ClusterData data) throws IOException {
+        //get a socket sender from the pool
+        if(!isConnected()) {
+            synchronized(this) {
+            if(!isConnected())
+                connect();
+            }
+        }
+        SocketSender sender = senderQueue.getSender(0);
+        if (sender == null) {
+            log.warn(sm.getString("PoolSocketSender.noMoreSender", this.getAddress(), new Integer(this.getPort())));
+            return;
+        }
+        //send the message
+        try {
+            sender.sendMessage(data);
+        } finally {
+            //return the connection to the pool
+            senderQueue.returnSender(sender);
+        }
+        addStats(data.getMessage().length);
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer("PooledSocketSender[");
+        buf.append(getAddress()).append(":").append(getPort()).append("]");
+        return buf.toString();
+    }
+
+    //  ----------------------------------------------------- Inner Class
+
+    private class SenderQueue {
+        private int limit = 25;
+
+        PooledSocketSender parent = null;
+
+        private LinkedList queue = new LinkedList();
+
+        private LinkedList inuse = new LinkedList();
+
+        private Object mutex = new Object();
+
+        private boolean isOpen = true;
+
+        public SenderQueue(PooledSocketSender parent, int limit) {
+            this.limit = limit;
+            this.parent = parent;
+        }
+
+        /**
+         * @return Returns the limit.
+         */
+        public int getLimit() {
+            return limit;
+        }
+        /**
+         * @param limit The limit to set.
+         */
+        public void setLimit(int limit) {
+            this.limit = limit;
+        }
+        /**
+         * @return
+         */
+        public int getInUsePoolSize() {
+            return inuse.size();
+        }
+
+        /**
+         * @return
+         */
+        public int getInPoolSize() {
+            return queue.size();
+        }
+
+        public SocketSender getSender(long timeout) {
+            SocketSender sender = null;
+            long start = System.currentTimeMillis();
+            long delta = 0;
+            do {
+                synchronized (mutex) {
+                    if (!isOpen)
+                        throw new IllegalStateException(
+                                "Socket pool is closed.");
+                    if (queue.size() > 0) {
+                        sender = (SocketSender) queue.removeFirst();
+                    } else if (inuse.size() < limit) {
+                        sender = getNewSocketSender();
+                    } else {
+                        try {
+                            mutex.wait(timeout);
+                        } catch (Exception x) {
+                            PooledSocketSender.log.warn(sm.getString("PoolSocketSender.senderQueue.sender.failed",parent.getAddress(),new Integer(parent.getPort())),x);
+                        }//catch
+                    }//end if
+                    if (sender != null) {
+                        inuse.add(sender);
+                    }
+                }//synchronized
+                delta = System.currentTimeMillis() - start;
+            } while ((isOpen) && (sender == null)
+                    && (timeout == 0 ? true : (delta < timeout)));
+            //to do
+            return sender;
+        }
+
+        public void returnSender(SocketSender sender) {
+            //to do
+            synchronized (mutex) {
+                queue.add(sender);
+                inuse.remove(sender);
+                mutex.notify();
+            }
+        }
+
+        private SocketSender getNewSocketSender() {
+            //new SocketSender(
+            SocketSender sender = new SocketSender(getDomain(),parent.getAddress(), parent
+                    .getPort());
+            sender.setKeepAliveMaxRequestCount(parent
+                    .getKeepAliveMaxRequestCount());
+            sender.setKeepAliveTimeout(parent.getKeepAliveTimeout());
+            sender.setAckTimeout(parent.getAckTimeout());
+            sender.setWaitForAck(parent.isWaitForAck());
+            sender.setResend(parent.isResend());
+            return sender;
+
+        }
+
+        public void close() {
+            synchronized (mutex) {
+                for (int i = 0; i < queue.size(); i++) {
+                    SocketSender sender = (SocketSender) queue.get(i);
+                    sender.disconnect();
+                }//for
+                for (int i = 0; i < inuse.size(); i++) {
+                    SocketSender sender = (SocketSender) inuse.get(i);
+                    sender.disconnect();
+                }//for
+                queue.clear();
+                inuse.clear();
+                isOpen = false;
+                mutex.notifyAll();
+            }
+        }
+
+        public void open() {
+            synchronized (mutex) {
+                isOpen = true;
+                mutex.notifyAll();
+            }
+        }
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ReplicationListener.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ReplicationListener.java
new file mode 100644
index 0000000..46881d6
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ReplicationListener.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+
+import org.apache.catalina.cluster.io.ObjectReader;
+
+/**
+* FIXME i18n log messages
+* FIXME jmx support
+* @author Peter Rossbach
+* @author Filip Hanik
+* @version $Revision$ $Date$
+*/
+public class ReplicationListener extends ClusterReceiverBase
+{
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "ReplicationListener/1.2";
+    
+    private ThreadPool pool = null;
+    private int tcpThreadCount;
+    private long tcpSelectorTimeout;    
+    private Selector selector = null;
+    
+    private Object interestOpsMutex = new Object();
+    
+    public ReplicationListener() {
+    }
+    
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+ 
+    public long getTcpSelectorTimeout() {
+        return tcpSelectorTimeout;
+    }
+    public void setTcpSelectorTimeout(long tcpSelectorTimeout) {
+        this.tcpSelectorTimeout = tcpSelectorTimeout;
+    }
+    public int getTcpThreadCount() {
+        return tcpThreadCount;
+    }
+    public void setTcpThreadCount(int tcpThreadCount) {
+        this.tcpThreadCount = tcpThreadCount;
+    }
+    public Object getInterestOpsMutex() {
+        return interestOpsMutex;
+    }
+
+    /**
+     * start cluster receiver
+     * @throws Exception
+     * @see org.apache.catalina.cluster.ClusterReceiver#start()
+     */
+    public void start() {
+            try {
+                pool = new ThreadPool(tcpThreadCount, TcpReplicationThread.class, interestOpsMutex);
+            } catch (Exception e) {
+                log.error("ThreadPool can initilzed. Listener not started",e);
+                return ;
+            }
+            super.start() ;
+     }
+    
+
+    /**
+     * get data from channel and store in byte array
+     * send it to cluster
+     * @throws IOException
+     * @throws java.nio.channels.ClosedChannelException
+     */
+    protected void listen ()
+        throws Exception
+    {
+        if (doListen) {
+            log.warn("ServerSocketChannel allready started");
+            return;
+        }
+        doListen=true;
+        // allocate an unbound server socket channel
+        ServerSocketChannel serverChannel = ServerSocketChannel.open();
+        // Get the associated ServerSocket to bind it with
+        ServerSocket serverSocket = serverChannel.socket();
+        // create a new Selector for use below
+        selector = Selector.open();
+        // set the port the server channel will listen to
+        serverSocket.bind (new InetSocketAddress (getBind(),getTcpListenPort()));
+        // set non-blocking mode for the listening socket
+        serverChannel.configureBlocking (false);
+        // register the ServerSocketChannel with the Selector
+        serverChannel.register (selector, SelectionKey.OP_ACCEPT);
+        while (doListen) {
+            // this may block for a long time, upon return the
+            // selected set contains keys of the ready channels
+            try {
+
+                int n = selector.select(tcpSelectorTimeout);
+                if (n == 0) {
+                    //there is a good chance that we got here 
+                    //because the TcpReplicationThread called
+                    //selector wakeup().
+                    //if that happens, we must ensure that that
+                    //thread has enough time to call interestOps
+                    synchronized (interestOpsMutex) {
+                        //if we got the lock, means there are no
+                        //keys trying to register for the 
+                        //interestOps method
+                    }
+                    continue; // nothing to do
+                }
+                // get an iterator over the set of selected keys
+                Iterator it = selector.selectedKeys().iterator();
+                // look at each key in the selected set
+                while (it.hasNext()) {
+                    SelectionKey key = (SelectionKey) it.next();
+                    // Is a new connection coming in?
+                    if (key.isAcceptable()) {
+                        ServerSocketChannel server =
+                            (ServerSocketChannel) key.channel();
+                        SocketChannel channel = server.accept();
+                        Object attach = new ObjectReader(channel, selector,
+                                    this) ;
+                        registerChannel(selector,
+                                        channel,
+                                        SelectionKey.OP_READ,
+                                        attach);
+                    }
+                    // is there data to read on this channel?
+                    if (key.isReadable()) {
+                        readDataFromSocket(key);
+                    } else {
+                        key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));
+                    }
+
+                    // remove key from selected set, it's been handled
+                    it.remove();
+                }
+            }
+            catch (java.nio.channels.CancelledKeyException nx) {
+                log.warn(
+                    "Replication client disconnected, error when polling key. Ignoring client.");
+            }
+            catch (Exception x) {
+                log.error("Unable to process request in ReplicationListener", x);
+            }
+
+        }
+        serverChannel.close();
+        selector.close();
+    }
+
+    /**
+     * Close Selector
+     * @see org.apache.catalina.cluster.tcp.ClusterReceiverBase#stopListening()
+     */
+    protected void stopListening(){
+        if ( selector != null ) {
+            try {
+                selector.close();
+            } catch ( Exception x ) {
+                log.error("Unable to close cluster receiver selector.",x);
+            } finally {
+                selector = null;                
+            }
+        }
+        doListen = false;
+   }
+        
+    // ----------------------------------------------------------
+
+    /**
+     * Register the given channel with the given selector for
+     * the given operations of interest
+     */
+    protected void registerChannel (Selector selector,
+                                    SelectableChannel channel,
+                                    int ops,
+                                    Object attach)
+    throws Exception {
+        if (channel == null) return; // could happen
+        // set the new channel non-blocking
+        channel.configureBlocking (false);
+        // register it with the selector
+        channel.register (selector, ops, attach);
+    }
+
+    // ----------------------------------------------------------
+
+    /**
+     * Sample data handler method for a channel with data ready to read.
+     * @param key A SelectionKey object associated with a channel
+     *  determined by the selector to be ready for reading.  If the
+     *  channel returns an EOF condition, it is closed here, which
+     *  automatically invalidates the associated key.  The selector
+     *  will then de-register the channel on the next select call.
+     */
+    protected void readDataFromSocket (SelectionKey key)
+        throws Exception
+    {
+        TcpReplicationThread worker = (TcpReplicationThread)pool.getWorker();
+        if (worker == null) {
+            // No threads available, do nothing, the selection
+            // loop will keep calling this method until a
+            // thread becomes available.
+            // FIXME: This design could be improved.
+            if(log.isDebugEnabled())
+                log.debug("No TcpReplicationThread available");
+        } else {
+            // invoking this wakes up the worker thread then returns
+            worker.serviceChannel(key, isSendAck());
+        }
+    }
+
+    
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ReplicationTransmitter.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ReplicationTransmitter.java
new file mode 100644
index 0000000..5fb521a
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ReplicationTransmitter.java
@@ -0,0 +1,896 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.zip.GZIPOutputStream;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.ClusterSender;
+import org.apache.catalina.cluster.Member;
+import org.apache.catalina.cluster.util.IDynamicProperty;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+/**
+ * Transmit message to ohter cluster members create sender from replicationMode
+ * type 
+ * FIXME i18n log messages
+ * FIXME compress data depends on message type and size 
+ * FIXME send very big messages at some block see FarmWarDeployer!
+ * TODO pause and resume senders
+ * 
+ * @author Peter Rossbach
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+public class ReplicationTransmitter implements ClusterSender,IDynamicProperty {
+    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(ReplicationTransmitter.class);
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "ReplicationTransmitter/3.0";
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+    private Map map = new HashMap();
+
+    public ReplicationTransmitter() {
+    }
+
+    /**
+     * number of transmitted messages>
+     */
+    private long nrOfRequests = 0;
+
+    /**
+     * number of transmitted bytes
+     */
+    private long totalBytes = 0;
+
+    /**
+     * number of failure
+     */
+    private long failureCounter = 0;
+
+    /**
+     * Iteration count for background processing.
+     */
+    private int count = 0;
+
+    /**
+     * Frequency of the check sender keepAlive Socket Status.
+     */
+    protected int processSenderFrequency = 2;
+
+    /**
+     * current sender replication mode
+     */
+    private String replicationMode;
+
+    /**
+     * sender default ackTimeout
+     */
+    private long ackTimeout = 15000; //15 seconds by default
+
+    /**
+     * enabled wait for ack
+     */
+    private boolean waitForAck = true;
+
+    /**
+     * autoConnect sender when next message send
+     */
+    private boolean autoConnect = false;
+
+    /**
+     * Compress message data bytes
+     */
+    private boolean compress = false;
+
+    /**
+     * doTransmitterProcessingStats
+     */
+    protected boolean doTransmitterProcessingStats = false;
+
+    /**
+     * proessingTime
+     */
+    protected long processingTime = 0;
+    
+    /**
+     * min proessingTime
+     */
+    protected long minProcessingTime = Long.MAX_VALUE ;
+
+    /**
+     * max proessingTime
+     */
+    protected long maxProcessingTime = 0;
+   
+    /**
+     * dynamic sender <code>properties</code>
+     */
+    private Map properties = new HashMap();
+
+    /**
+     * my cluster
+     */
+    private SimpleTcpCluster cluster;
+
+    /**
+     * Transmitter Mbean name
+     */
+    private ObjectName objectName;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+    /**
+     * @return Returns the nrOfRequests.
+     */
+    public long getNrOfRequests() {
+        return nrOfRequests;
+    }
+
+    /**
+     * @return Returns the totalBytes.
+     */
+    public long getTotalBytes() {
+        return totalBytes;
+    }
+
+    /**
+     * @return Returns the failureCounter.
+     */
+    public long getFailureCounter() {
+        return failureCounter;
+    }
+
+    /**
+     * current replication mode
+     * 
+     * @return The mode
+     */
+    public String getReplicationMode() {
+        return replicationMode;
+    }
+
+    /**
+     * set replication Mode (pooled, synchonous, asynchonous, fastasyncqueue)
+     * 
+     * @see IDataSenderFactory#validateMode(String)
+     * @param mode
+     */
+    public void setReplicationMode(String mode) {
+        String msg = IDataSenderFactory.validateMode(mode);
+        if (msg == null) {
+            if (log.isDebugEnabled())
+                log.debug("Setting replcation mode to " + mode);
+            this.replicationMode = mode;
+        } else
+            throw new IllegalArgumentException(msg);
+
+    }
+
+    /**
+     * @return Returns the avg processingTime/nrOfRequests.
+     */
+    public double getAvgProcessingTime() {
+        return ((double)processingTime) / nrOfRequests;
+    }
+ 
+    /**
+     * @return Returns the maxProcessingTime.
+     */
+    public long getMaxProcessingTime() {
+        return maxProcessingTime;
+    }
+    
+    /**
+     * @return Returns the minProcessingTime.
+     */
+    public long getMinProcessingTime() {
+        return minProcessingTime;
+    }
+    
+    /**
+     * @return Returns the processingTime.
+     */
+    public long getProcessingTime() {
+        return processingTime;
+    }
+    
+    /**
+     * @return Returns the doTransmitterProcessingStats.
+     */
+    public boolean isDoTransmitterProcessingStats() {
+        return doTransmitterProcessingStats;
+    }
+    
+    /**
+     * @param doProcessingStats The doTransmitterProcessingStats to set.
+     */
+    public void setDoTransmitterProcessingStats(boolean doProcessingStats) {
+        this.doTransmitterProcessingStats = doProcessingStats;
+    }
+ 
+
+    /**
+     * Transmitter ObjectName
+     * 
+     * @param name
+     */
+    public void setObjectName(ObjectName name) {
+        objectName = name;
+    }
+
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+
+    /**
+     * @return Returns the compress.
+     */
+    public boolean isCompress() {
+        return compress;
+    }
+
+    /**
+     * @param compressMessageData
+     *            The compress to set.
+     */
+    public void setCompress(boolean compressMessageData) {
+        this.compress = compressMessageData;
+    }
+
+    /**
+     * @return Returns the autoConnect.
+     */
+    public boolean isAutoConnect() {
+        return autoConnect;
+    }
+
+    /**
+     * @param autoConnect
+     *            The autoConnect to set.
+     */
+    public void setAutoConnect(boolean autoConnect) {
+        this.autoConnect = autoConnect;
+        setProperty("autoConnect", String.valueOf(autoConnect));
+
+    }
+
+    /**
+     * @return The ack timeout
+     */
+    public long getAckTimeout() {
+        return ackTimeout;
+    }
+
+    /**
+     * @param ackTimeout
+     */
+    public void setAckTimeout(long ackTimeout) {
+        this.ackTimeout = ackTimeout;
+        setProperty("ackTimeout", String.valueOf(ackTimeout));
+    }
+
+    /**
+     * @return Returns the waitForAck.
+     */
+    public boolean isWaitForAck() {
+        return waitForAck;
+    }
+
+    /**
+     * @param waitForAck
+     *            The waitForAck to set.
+     */
+    public void setWaitForAck(boolean waitForAck) {
+        this.waitForAck = waitForAck;
+        setProperty("waitForAck", String.valueOf(waitForAck));
+    }
+
+    
+    /**
+     * @return Returns the processSenderFrequency.
+     */
+    public int getProcessSenderFrequency() {
+        return processSenderFrequency;
+    }
+    
+    /**
+     * @param processSenderFrequency The processSenderFrequency to set.
+     */
+    public void setProcessSenderFrequency(int processSenderFrequency) {
+        this.processSenderFrequency = processSenderFrequency;
+    }
+    
+    /*
+     * configured in cluster
+     * 
+     * @see org.apache.catalina.cluster.ClusterSender#setCatalinaCluster(org.apache.catalina.cluster.tcp.SimpleTcpCluster)
+     */
+    public void setCatalinaCluster(SimpleTcpCluster cluster) {
+        this.cluster = cluster;
+
+    }
+
+    /**
+     * @return True if synchronized sender
+     * @deprecated since version 5.5.7
+     */
+    public boolean getIsSenderSynchronized() {
+        return IDataSenderFactory.SYNC_MODE.equals(replicationMode)
+                || IDataSenderFactory.POOLED_SYNC_MODE.equals(replicationMode);
+    }
+
+    // ------------------------------------------------------------- dynamic
+    // sender property handling
+
+    /**
+     * set config attributes with reflect
+     * 
+     * @param name
+     * @param value
+     */
+    public void setProperty(String name, Object value) {
+        if (log.isTraceEnabled())
+            log.trace(sm.getString("ReplicationTransmitter.setProperty", name,
+                    value, properties.get(name)));
+
+        properties.put(name, value);
+    }
+
+    /**
+     * get current config
+     * 
+     * @param key
+     * @return The property
+     */
+    public Object getProperty(String key) {
+        if (log.isTraceEnabled())
+            log.trace(sm.getString("ReplicationTransmitter.getProperty", key));
+        return properties.get(key);
+    }
+
+    /**
+     * Get all properties keys
+     * 
+     * @return An iterator over the propery name set
+     */
+    public Iterator getPropertyNames() {
+        return properties.keySet().iterator();
+    }
+
+    /**
+     * remove a configured property.
+     * 
+     * @param key
+     */
+    public void removeProperty(String key) {
+        properties.remove(key);
+    }
+
+    // ------------------------------------------------------------- public
+    
+    /**
+     * Send data to one member
+     * FIXME set filtering messages
+     * @see org.apache.catalina.cluster.ClusterSender#sendMessage(org.apache.catalina.cluster.ClusterMessage, org.apache.catalina.cluster.Member)
+     */
+    public void sendMessage(ClusterMessage message, Member member)
+            throws java.io.IOException {       
+        long time = 0 ;
+        if(doTransmitterProcessingStats) {
+            time = System.currentTimeMillis();
+        }
+        try {
+            ClusterData data = serialize(message);
+            String key = getKey(member);
+            IDataSender sender = (IDataSender) map.get(key);
+            sendMessageData(data, sender);
+        } finally {
+            if (doTransmitterProcessingStats) {
+                addProcessingStats(time);
+            }
+        }
+    }
+    
+    /**
+     * Send to all senders at same cluster domain as message from address
+     * @param message Cluster message to send
+     * @since 5.5.10
+     */
+    public void sendMessageClusterDomain(ClusterMessage message) 
+         throws java.io.IOException {
+        long time = 0;
+        if (doTransmitterProcessingStats) {
+            time = System.currentTimeMillis();
+        }
+        try {
+            String domain = message.getAddress().getDomain();
+            if(domain == null)
+                throw new RuntimeException("Domain at member not set");
+            ClusterData data = serialize(message);
+            IDataSender[] senders = getSenders();
+            for (int i = 0; i < senders.length; i++) {
+
+                IDataSender sender = senders[i];
+                if(domain.equals(sender.getDomain())) {
+                    try {
+                        sendMessageData(data, sender);
+                    } catch (Exception x) {
+                        if (!sender.getSuspect()) {
+                            log.warn("Unable to send replicated message to "
+                                    + sender + ", is server down?", x);
+                            sender.setSuspect(true);
+                        }
+                    }
+                }
+            }
+        } finally {
+            if (doTransmitterProcessingStats) {
+                addProcessingStats(time);
+            }
+        }
+    
+    }
+
+    /**
+     * send message to all senders (broadcast)
+     * @see org.apache.catalina.cluster.ClusterSender#sendMessage(org.apache.catalina.cluster.ClusterMessage)
+     */
+    public void sendMessage(ClusterMessage message)
+            throws java.io.IOException {
+        long time = 0;
+        if (doTransmitterProcessingStats) {
+            time = System.currentTimeMillis();
+        }
+        try {
+            ClusterData data = serialize(message);
+            IDataSender[] senders = getSenders();
+            for (int i = 0; i < senders.length; i++) {
+
+                IDataSender sender = senders[i];
+                try {
+                    sendMessageData(data, sender);
+                } catch (Exception x) {
+                    if (!sender.getSuspect()) {
+                        log.warn("Unable to send replicated message to "
+                                + sender + ", is server down?", x);
+                        sender.setSuspect(true);
+                    }
+                }
+            }
+        } finally {
+            if (doTransmitterProcessingStats) {
+                addProcessingStats(time);
+            }
+        }
+    }
+
+    /**
+     * start the sender and register transmitter mbean
+     * 
+     * @see org.apache.catalina.cluster.ClusterSender#start()
+     */
+    public void start() throws java.io.IOException {
+        if (cluster != null) {
+            ObjectName clusterName = cluster.getObjectName();
+            ObjectName transmitterName = null ;
+            try {
+                MBeanServer mserver = cluster.getMBeanServer();
+                Container container = cluster.getContainer();
+                String name = clusterName.getDomain() + ":type=ClusterSender";
+                if (container instanceof StandardHost) {
+                    name += ",host=" + clusterName.getKeyProperty("host");
+                }
+                transmitterName = new ObjectName(name);
+                if (mserver.isRegistered(transmitterName)) {
+                    if (log.isWarnEnabled())
+                        log.warn(sm.getString(
+                                "cluster.mbean.register.allready",
+                                transmitterName));
+                    return;
+                }
+                setObjectName(transmitterName);
+                mserver.registerMBean(cluster.getManagedBean(this),
+                        getObjectName());
+                if(log.isInfoEnabled())
+                    log.info(sm.getString("ReplicationTransmitter.started",
+                            clusterName, transmitterName));
+
+            } catch (Exception e) {
+                log.warn(e);
+            }
+        }
+    }
+
+    /*
+     * stop the sender and deregister mbeans (transmitter, senders)
+     * 
+     * @see org.apache.catalina.cluster.ClusterSender#stop()
+     */
+    public synchronized void stop() {
+        Iterator i = map.entrySet().iterator();
+        while (i.hasNext()) {
+            IDataSender sender = (IDataSender) ((java.util.Map.Entry) i.next())
+                    .getValue();
+            try {
+                unregisterSenderMBean(sender);
+                sender.disconnect();
+            } catch (Exception x) {
+            }
+            i.remove();
+        }
+        if (cluster != null && getObjectName() != null) {
+            try {
+                MBeanServer mserver = cluster.getMBeanServer();
+                mserver.unregisterMBean(getObjectName());
+            } catch (Exception e) {
+                log.error(e);
+            }
+            if(log.isInfoEnabled())
+                log.info(sm.getString("ReplicationTransmitter.stopped",
+                        cluster.getObjectName(), getObjectName()));
+        }
+
+    }
+
+    /**
+     * Call transmitter to check for sender socket status
+     * 
+     * @see SimpleTcpCluster#backgroundProcess()
+     */
+    public void backgroundProcess() {
+        count = (count + 1) % processSenderFrequency;
+        if (count == 0) {
+            checkKeepAlive();
+        }
+    }
+
+    /**
+     * Check all DataSender Socket to close socket at keepAlive mode
+     * @see DataSender#checkKeepAlive()
+     */
+    public void checkKeepAlive() {
+        if (map.size() > 0) {
+            java.util.Iterator iter = map.entrySet().iterator();
+            while (iter.hasNext()) {
+                IDataSender sender = (IDataSender) ((java.util.Map.Entry) iter
+                        .next()).getValue();
+                if (sender != null)
+                    sender.checkKeepAlive();
+            }
+        }
+    }
+
+    /**
+     * get all current senders
+     * 
+     * @return The senders
+     */
+    public IDataSender[] getSenders() {
+        java.util.Iterator iter = map.entrySet().iterator();
+        IDataSender[] array = new IDataSender[map.size()];
+        int i = 0;
+        while (iter.hasNext()) {
+            IDataSender sender = (IDataSender) ((java.util.Map.Entry) iter
+                    .next()).getValue();
+            if (sender != null)
+                array[i] = sender;
+            i++;
+        }
+        return array;
+    }
+
+    /**
+     * get all current senders
+     * 
+     * @return The sender object names
+     */
+    public ObjectName[] getSenderObjectNames() {
+        java.util.Iterator iter = map.entrySet().iterator();
+        ObjectName array[] = new ObjectName[map.size()];
+        int i = 0;
+        while (iter.hasNext()) {
+            IDataSender sender = (IDataSender) ((java.util.Map.Entry) iter
+                    .next()).getValue();
+            if (sender != null)
+                array[i] = getSenderObjectName(sender);
+            i++;
+        }
+        return array;
+    }
+
+    /**
+     * Reset sender statistics
+     */
+    public synchronized void resetStatistics() {
+        nrOfRequests = 0;
+        totalBytes = 0;
+        failureCounter = 0;
+        processingTime = 0;
+        minProcessingTime = Long.MAX_VALUE;
+        maxProcessingTime = 0;
+    }
+
+    /**
+     * add new cluster member and create sender ( s. replicationMode) transfer
+     * current properties to sender
+     * 
+     * @see org.apache.catalina.cluster.ClusterSender#add(org.apache.catalina.cluster.Member)
+     */
+    public synchronized void add(Member member) {
+        try {
+            String key = getKey(member);
+            if (!map.containsKey(key)) {
+                IDataSender sender = IDataSenderFactory.getIDataSender(
+                        replicationMode, member);
+                transferSenderProperty(sender);
+                map.put(key, sender);
+                registerSenderMBean(member, sender);
+            }
+        } catch (java.io.IOException x) {
+            log.error("Unable to create and add a IDataSender object.", x);
+        }
+    }
+
+    /**
+     * remove sender from transmitter. ( deregister mbean and disconnect sender )
+     * 
+     * @see org.apache.catalina.cluster.ClusterSender#remove(org.apache.catalina.cluster.Member)
+     */
+    public synchronized void remove(Member member) {
+        String key = getKey(member);
+        IDataSender toberemoved = (IDataSender) map.get(key);
+        if (toberemoved == null)
+            return;
+        unregisterSenderMBean(toberemoved);
+        toberemoved.disconnect();
+        map.remove(key);
+
+    }
+
+    // ------------------------------------------------------------- protected
+
+    /**
+     * calc number of requests and transfered bytes. Log stats all 100 requets
+     * 
+     * @param length
+     */
+    protected synchronized void addStats(int length) {
+        nrOfRequests++;
+        totalBytes += length;
+        if (log.isDebugEnabled() && (nrOfRequests % 100) == 0) {
+            log.debug("Nr of bytes sent=" + totalBytes + " over "
+                    + nrOfRequests + "; avg=" + (totalBytes / nrOfRequests)
+                    + " bytes/request; failures=" + failureCounter);
+        }
+
+    }
+
+    /**
+     * Transfer all properties from transmitter to concrete sender
+     * 
+     * @param sender
+     */
+    protected void transferSenderProperty(IDataSender sender) {
+        for (Iterator iter = getPropertyNames(); iter.hasNext();) {
+            String pkey = (String) iter.next();
+            Object value = getProperty(pkey);
+            IntrospectionUtils.setProperty(sender, pkey, value.toString());
+        }
+    }
+
+    /**
+     * set unique key to find sender
+     * 
+     * @param member
+     * @return concat member.host:member.port
+     */
+    protected String getKey(Member member) {
+        return member.getHost() + ":" + member.getPort();
+    }
+
+    /**
+     * unregsister sendern Mbean
+     * 
+     * @see #getSenderObjectName(IDataSender)
+     * @param sender
+     */
+    protected void unregisterSenderMBean(IDataSender sender) {
+        try {
+            MBeanServer mserver = cluster.getMBeanServer();
+            if (mserver != null) {
+                mserver.unregisterMBean(getSenderObjectName(sender));
+            }
+        } catch (Exception e) {
+            log.warn(e);
+        }
+    }
+
+    /**
+     * register MBean and check it exist (big problem!)
+     * 
+     * @param member
+     * @param sender
+     */
+    protected void registerSenderMBean(Member member, IDataSender sender) {
+        if (member != null && cluster != null) {
+            try {
+                MBeanServer mserver = cluster.getMBeanServer();
+                ObjectName senderName = getSenderObjectName(sender);
+                if (mserver.isRegistered(senderName)) {
+                    if (log.isWarnEnabled())
+                        log.warn(sm.getString(
+                                "cluster.mbean.register.allready", senderName));
+                    return;
+                }
+                mserver.registerMBean(cluster.getManagedBean(sender),
+                        senderName);
+            } catch (Exception e) {
+                log.warn(e);
+            }
+        }
+    }
+
+    
+    /**
+     * build sender ObjectName (
+     * engine.domain:type=IDataSender,host="host",senderAddress="receiver.address",senderPort="port" )
+     * 
+     * @param sender
+     * @return The sender object name
+     */
+    protected ObjectName getSenderObjectName(IDataSender sender) {
+        ObjectName senderName = null;
+        try {
+            ObjectName clusterName = cluster.getObjectName();
+            Container container = cluster.getContainer();
+            String name = clusterName.getDomain() + ":type=IDataSender";
+            if (container instanceof StandardHost) {
+                name += ",host=" + clusterName.getKeyProperty("host");
+            }
+            senderName = new ObjectName(name + ",senderAddress="
+                    + sender.getAddress().getHostAddress() + ",senderPort="
+                    + sender.getPort());
+        } catch (Exception e) {
+            log.warn(e);
+        }
+        return senderName;
+    }
+
+    /**
+     * serialize message and add timestamp
+     * @see GZIPOutputStream
+     * @param msg cluster message
+     * @return cluster message as byte array
+     * @throws IOException
+     * @since 5.5.10
+     */
+    protected ClusterData serialize(ClusterMessage msg) throws IOException {
+        msg.setTimestamp(System.currentTimeMillis());
+        ByteArrayOutputStream outs = new ByteArrayOutputStream();
+        ObjectOutputStream out;
+        GZIPOutputStream gout = null;
+        ClusterData data = new ClusterData();
+        data.setType(msg.getClass().getName());
+        data.setUniqueId(msg.getUniqueId());
+        data.setTimestamp(msg.getTimestamp());
+        data.setCompress(msg.getCompress());
+        data.setResend(msg.getResend());
+        // FIXME add Stats how much comress and uncompress messages and bytes are transfered
+        if ((isCompress() && msg.getCompress() != ClusterMessage.FLAG_FORBIDDEN)
+                || msg.getCompress() == ClusterMessage.FLAG_ALLOWED) {
+            System.out.println("msg compress: " + msg.getUniqueId() );
+            gout = new GZIPOutputStream(outs);
+            out = new ObjectOutputStream(gout);
+        } else {
+            out = new ObjectOutputStream(outs);
+        }
+        out.writeObject(msg);
+        // flush out the gzip stream to byte buffer
+        if(gout != null) {
+            gout.flush();
+            gout.close();
+        }
+        data.setMessage(outs.toByteArray());
+        return data;
+    }
+ 
+
+    /**
+     * Send message to concrete sender. If autoConnect is true, check is
+     * connection broken and the reconnect the complete sender.
+     * <ul>
+     * <li>failure the suspect flag is set true. After successfully sending the
+     * suspect flag is set to false.</li>
+     * <li>Stats is only update after sussesfull sending</li>
+     * </ul>
+     * 
+     * @param data message Data
+     * @param sender concrete message sender
+     * @throws java.io.IOException If an error occurs
+     */
+    protected void sendMessageData(ClusterData data,
+            IDataSender sender) throws java.io.IOException {
+        if (sender == null)
+            throw new java.io.IOException(
+                    "Sender not available. Make sure sender information is available to the ReplicationTransmitter.");
+        try {
+            // deprecated not needed DataSender#pushMessage can handle connection
+            if (autoConnect) {
+                synchronized(sender) {
+                    if(!sender.isConnected())
+                        sender.connect();
+                }
+            }
+            sender.sendMessage(data);
+            sender.setSuspect(false);
+            addStats(data.getMessage().length);
+        } catch (Exception x) {
+            if (log.isWarnEnabled()) {
+                if (!sender.getSuspect()) {
+                    log.warn("Unable to send replicated message, is server down?",x);
+                }
+            }
+            sender.setSuspect(true);
+            failureCounter++;
+        }
+
+    }
+    /**
+     * Add processing stats times
+     * @param startTime
+     */
+    protected void addProcessingStats(long startTime) {
+        long time = System.currentTimeMillis() - startTime ;
+        if(time < minProcessingTime)
+            minProcessingTime = time ;
+        if( time > maxProcessingTime)
+            maxProcessingTime = time ;
+        processingTime += time ;
+    }
+ 
+ 
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ReplicationValve.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ReplicationValve.java
new file mode 100644
index 0000000..922255d
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ReplicationValve.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterManager;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.ClusterSession;
+import org.apache.catalina.cluster.ClusterValve;
+import org.apache.catalina.cluster.session.DeltaManager;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+
+/**
+ * <p>Implementation of a Valve that logs interesting contents from the
+ * specified Request (before processing) and the corresponding Response
+ * (after processing).  It is especially useful in debugging problems
+ * related to headers and cookies.</p>
+ *
+ * <p>This Valve may be attached to any Container, depending on the granularity
+ * of the logging you wish to perform.</p>
+ *
+ * <p>primaryIndicator=true, then the request attribute <i>org.apache.catalina.cluster.tcp.isPrimarySession.</i>
+ * is set true, when request processing is at sessions primary node.
+ * </p>
+ *
+ * @author Craig R. McClanahan
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class ReplicationValve
+    extends ValveBase implements ClusterValve {
+    
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( ReplicationValve.class );
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.cluster.tcp.ReplicationValve/1.2";
+
+
+    /**
+     * The StringManager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    private CatalinaCluster cluster = null ;
+
+    /**
+     * holds file endings to not call for like images and others
+     */
+    protected java.util.regex.Pattern[] reqFilters = new java.util.regex.Pattern[0];
+    protected String filter ;
+
+    protected long totalRequestTime=0;
+    protected long totalSendTime=0;
+    protected long nrOfRequests =0;
+    protected long lastSendTime =0;
+    protected long nrOfFilterRequests=0;
+    protected boolean primaryIndicator = false ;
+    protected String primaryIndicatorName = "org.apache.catalina.cluster.tcp.isPrimarySession";
+   
+    // ------------------------------------------------------------- Properties
+
+    public ReplicationValve() {
+    }
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+    
+    /**
+     * @return Returns the cluster.
+     */
+    public CatalinaCluster getCluster() {
+        return cluster;
+    }
+    
+    /**
+     * @param cluster The cluster to set.
+     */
+    public void setCluster(CatalinaCluster cluster) {
+        this.cluster = cluster;
+    }
+ 
+    /**
+     * @return Returns the filter
+     */
+    public String getFilter() {
+       return filter ;
+    }
+
+    /**
+     * compile filter string to regular expressions
+     * @see Pattern#compile(java.lang.String)
+     * @param filter
+     *            The filter to set.
+     */
+    public void setFilter(String filter) {
+        if (log.isDebugEnabled())
+            log.debug(sm.getString("ReplicationValve.filter.loading", filter));
+        this.filter = filter;
+        StringTokenizer t = new StringTokenizer(filter, ";");
+        this.reqFilters = new Pattern[t.countTokens()];
+        int i = 0;
+        while (t.hasMoreTokens()) {
+            String s = t.nextToken();
+            if (log.isTraceEnabled())
+                log.trace(sm.getString("ReplicationValve.filter.token", s));
+            try {
+                reqFilters[i++] = Pattern.compile(s);
+            } catch (Exception x) {
+                log.error(sm.getString("ReplicationValve.filter.token.failure",
+                        s), x);
+            }
+        }
+    }
+
+    /**
+     * @return Returns the primaryIndicator.
+     */
+    public boolean isPrimaryIndicator() {
+        return primaryIndicator;
+    }
+    /**
+     * @param primaryIndicator The primaryIndicator to set.
+     */
+    public void setPrimaryIndicator(boolean primaryIndicator) {
+        this.primaryIndicator = primaryIndicator;
+    }
+    /**
+     * @return Returns the primaryIndicatorName.
+     */
+    public String getPrimaryIndicatorName() {
+        return primaryIndicatorName;
+    }
+    /**
+     * @param primaryIndicatorName The primaryIndicatorName to set.
+     */
+    public void setPrimaryIndicatorName(String primaryIndicatorName) {
+        this.primaryIndicatorName = primaryIndicatorName;
+    }
+      
+    /**
+     * @return Returns the lastSendTime.
+     */
+    public long getLastSendTime() {
+        return lastSendTime;
+    }
+    
+    /**
+     * @return Returns the nrOfRequests.
+     */
+    public long getNrOfRequests() {
+        return nrOfRequests;
+    }
+    
+    /**
+     * @return Returns the nrOfFilterRequests.
+     */
+    public long getNrOfFilterRequests() {
+        return nrOfFilterRequests;
+    }
+
+    /**
+     * @return Returns the totalRequestTime.
+     */
+    public long getTotalRequestTime() {
+        return totalRequestTime;
+    }
+    
+    /**
+     * @return Returns the totalSendTime.
+     */
+    public long getTotalSendTime() {
+        return totalSendTime;
+    }
+
+    /**
+     * @return Returns the reqFilters.
+     */
+    protected java.util.regex.Pattern[] getReqFilters() {
+        return reqFilters;
+    }
+    /**
+     * @param reqFilters The reqFilters to set.
+     */
+    protected void setReqFilters(java.util.regex.Pattern[] reqFilters) {
+        this.reqFilters = reqFilters;
+    }
+    
+    // --------------------------------------------------------- Public Methods
+    
+
+    /**
+     * Log the interesting request parameters, invoke the next Valve in the
+     * sequence, and log the interesting response parameters.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException
+    {
+        long totalstart = System.currentTimeMillis();
+        //this happens before the request
+        if (primaryIndicator)
+            createPrimaryIndicator( request) ;
+        getNext().invoke(request, response);
+        //this happens after the request
+        long start = System.currentTimeMillis();
+        Manager manager = request.getContext().getManager();
+        if (manager != null && manager instanceof ClusterManager) {
+            ClusterManager clusterManager = (ClusterManager) manager;
+            CatalinaCluster cluster = (CatalinaCluster) getContainer()
+                    .getCluster();
+            if (cluster == null) {
+                if (log.isWarnEnabled())
+                    log.warn(sm.getString("ReplicationValve.nocluster"));
+                return;
+            }
+            // valve cluster can access manager - other clusterhandle replication 
+            // at host level - hopefully!
+            if(cluster.getManager(clusterManager.getName()) == null)
+                return ;
+            if(cluster.getMembers().length > 0  ) {
+                try {
+                    // send invalid sessions
+                    // DeltaManager returns String[0]
+                    if (!(clusterManager instanceof DeltaManager))
+                        sendInvalidSessions(clusterManager, cluster);
+                    // send replication
+                    sendSessionReplicationMessage(request, clusterManager, cluster);
+                } catch (Exception x) {
+                    log.error(sm.getString("ReplicationValve.send.failure"), x);
+                } finally {
+                    long stop = System.currentTimeMillis();
+                    updateStats(stop - totalstart, stop - start);
+                }
+            }
+        }
+    }
+  
+    /**
+     * reset the active statitics 
+     */
+    public void resetStatistics() {
+        totalRequestTime = 0 ;
+        totalSendTime = 0 ;
+        lastSendTime = 0 ;
+        nrOfFilterRequests = 0 ;
+        nrOfRequests = 0 ;
+    }
+    
+    /**
+     * Return a String rendering of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ReplicationValve[");
+        if (container != null)
+            sb.append(container.getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    // --------------------------------------------------------- Protected Methods
+
+    /**
+     * Send Cluster Replication Request
+     * @see DeltaManager#requestCompleted(String)
+     * @see SimpleTcpCluster#send(ClusterMessage)
+     * @param request
+     * @param manager
+     * @param cluster
+     */
+    protected void sendSessionReplicationMessage(Request request,
+            ClusterManager manager, CatalinaCluster cluster) {
+        Session session = request.getSessionInternal(false);
+        if (session != null) {
+            String uri = request.getDecodedRequestURI();
+            // request without session change
+            if (!isRequestWithoutSessionChange(uri)) {
+
+                if (log.isDebugEnabled())
+                    log.debug(sm.getString("ReplicationValve.invoke.uri", uri));
+                String id = session.getIdInternal();
+                if (id != null) {
+                    ClusterMessage msg = manager.requestCompleted(id);
+                    // really send replication send request
+                    // FIXME send directly via ClusterManager.send
+                    if (msg != null) {
+                        if(manager.isSendClusterDomainOnly())
+                            cluster.sendClusterDomain(msg);
+                        else
+                            cluster.send(msg);
+                    }
+                }
+            } else
+                nrOfFilterRequests++;
+        }
+
+    }
+    
+    /**
+     * check for session invalidations
+     * @param manager
+     * @param cluster
+     */
+    protected void sendInvalidSessions(ClusterManager manager, CatalinaCluster cluster) {
+        String[] invalidIds=manager.getInvalidatedSessions();
+        if ( invalidIds.length > 0 ) {
+            for ( int i=0;i<invalidIds.length; i++ ) {
+                try {
+                    ClusterMessage imsg = manager.requestCompleted(invalidIds[i]);
+                    // FIXME send directly via ClusterManager.send
+                    if (imsg != null) {
+                        if(manager.isSendClusterDomainOnly())
+                            cluster.sendClusterDomain(imsg);
+                        else
+                            cluster.send(imsg);
+                    }
+                } catch ( Exception x ) {
+                    log.error(sm.getString("ReplicationValve.send.invalid.failure",invalidIds[i]),x);
+                }
+            }
+        }
+    }
+    
+    /**
+     * is request without possible session change
+     * @param uri The request uri
+     * @return True if no session change
+     */
+    protected boolean isRequestWithoutSessionChange(String uri) {
+
+        boolean filterfound = false;
+
+        for (int i = 0; (i < reqFilters.length) && (!filterfound); i++) {
+            java.util.regex.Matcher matcher = reqFilters[i].matcher(uri);
+            filterfound = matcher.matches();
+        }
+        return filterfound;
+    }
+
+    /**
+     * protocol cluster replications stats
+     * @param requestTime
+     * @param clusterTime
+     */
+    protected synchronized void updateStats(long requestTime, long clusterTime) {
+        totalSendTime+=clusterTime;
+        totalRequestTime+=requestTime;
+        nrOfRequests++;
+        if ( (nrOfRequests % 100) == 0 ) {
+            if(log.isInfoEnabled()) {
+                 log.info(sm.getString("ReplicationValve.stats",
+                     new Object[]{
+                         new Long(totalRequestTime/nrOfRequests),
+                         new Long(totalSendTime/nrOfRequests),
+                         new Long(nrOfRequests),
+                         new Long(nrOfFilterRequests),
+                         new Long(totalRequestTime),
+                         new Long(totalSendTime)}));
+             }
+        }
+        lastSendTime=System.currentTimeMillis();
+    }
+
+
+    /**
+     * Mark Request that processed at primary node with attribute
+     * primaryIndicatorName
+     * 
+     * @param request
+     * @throws IOException
+     */
+    protected void createPrimaryIndicator(Request request) throws IOException {
+        String id = request.getRequestedSessionId();
+        if ((id != null) && (id.length() > 0)) {
+            Manager manager = request.getContext().getManager();
+            Session session = manager.findSession(id);
+            if (session instanceof ClusterSession) {
+                ClusterSession cses = (ClusterSession) session;
+                if (cses != null) {
+                    Boolean isPrimary = new Boolean(cses.isPrimarySession());
+                    if (log.isDebugEnabled())
+                        log.debug(sm.getString(
+                                "ReplicationValve.session.indicator", request.getContext().getName(),id,
+                                primaryIndicatorName, isPrimary));
+                    request.setAttribute(primaryIndicatorName, isPrimary);
+                }
+            } else {
+                if (log.isDebugEnabled()) {
+                    if (session != null) {
+                        log.debug(sm.getString(
+                                "ReplicationValve.session.found", request.getContext().getName(),id));
+                    } else {
+                        log.debug(sm.getString(
+                                "ReplicationValve.session.invalid", request.getContext().getName(),id));
+                    }
+                }
+            }
+        }
+    }
+
+
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SendMessageData.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SendMessageData.java
new file mode 100644
index 0000000..702feb4
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SendMessageData.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import org.apache.catalina.cluster.Member;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class SendMessageData {
+
+    private Object message ;
+    private Member destination ;
+    private Exception exception ;
+    
+    
+    /**
+     * @param message
+     * @param destination
+     * @param exception
+     */
+    public SendMessageData(Object message, Member destination,
+            Exception exception) {
+        super();
+        this.message = message;
+        this.destination = destination;
+        this.exception = exception;
+    }
+    
+    /**
+     * @return Returns the destination.
+     */
+    public Member getDestination() {
+        return destination;
+    }
+    /**
+     * @param destination The destination to set.
+     */
+    public void setDestination(Member destination) {
+        this.destination = destination;
+    }
+    /**
+     * @return Returns the exception.
+     */
+    public Exception getException() {
+        return exception;
+    }
+    /**
+     * @param exception The exception to set.
+     */
+    public void setException(Exception exception) {
+        this.exception = exception;
+    }
+    /**
+     * @return Returns the message.
+     */
+    public Object getMessage() {
+        return message;
+    }
+    /**
+     * @param message The message to set.
+     */
+    public void setMessage(Object message) {
+        this.message = message;
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SimpleTcpCluster.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SimpleTcpCluster.java
new file mode 100644
index 0000000..a7b385c
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SimpleTcpCluster.java
@@ -0,0 +1,1415 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+import javax.management.modelmbean.ModelMBean;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Valve;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterManager;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.ClusterReceiver;
+import org.apache.catalina.cluster.ClusterSender;
+import org.apache.catalina.cluster.ClusterValve;
+import org.apache.catalina.cluster.Member;
+import org.apache.catalina.cluster.MembershipListener;
+import org.apache.catalina.cluster.MembershipService;
+import org.apache.catalina.cluster.MessageListener;
+import org.apache.catalina.cluster.mcast.McastService;
+import org.apache.catalina.cluster.session.ClusterSessionListener;
+import org.apache.catalina.cluster.session.DeltaManager;
+import org.apache.catalina.cluster.util.IDynamicProperty;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+/**
+ * A <b>Cluster </b> implementation using simple multicast. Responsible for
+ * setting up a cluster and provides callers with a valid multicast
+ * receiver/sender.
+ * 
+ * FIXME remove install/remove/start/stop context dummys
+ * FIXME wrote testcases 
+ * 
+ * @author Filip Hanik
+ * @author Remy Maucherat
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public class SimpleTcpCluster implements CatalinaCluster, Lifecycle,
+        MembershipListener, LifecycleListener, IDynamicProperty {
+
+    public static Log log = LogFactory.getLog(SimpleTcpCluster.class);
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Descriptive information about this component implementation.
+     */
+    protected static final String info = "SimpleTcpCluster/2.2";
+
+    public static final String BEFORE_MEMBERREGISTER_EVENT = "before_member_register";
+
+    public static final String AFTER_MEMBERREGISTER_EVENT = "after_member_register";
+
+    public static final String BEFORE_MANAGERREGISTER_EVENT = "before_manager_register";
+
+    public static final String AFTER_MANAGERREGISTER_EVENT = "after_manager_register";
+
+    public static final String BEFORE_MANAGERUNREGISTER_EVENT = "before_manager_unregister";
+
+    public static final String AFTER_MANAGERUNREGISTER_EVENT = "after_manager_unregister";
+
+    public static final String BEFORE_MEMBERUNREGISTER_EVENT = "before_member_unregister";
+
+    public static final String AFTER_MEMBERUNREGISTER_EVENT = "after_member_unregister";
+
+    public static final String SEND_MESSAGE_FAILURE_EVENT = "send_message_failure";
+
+    public static final String RECEIVE_MESSAGE_FAILURE_EVENT = "receive_message_failure";
+
+    /**
+     * the service that provides the membership
+     */
+    protected MembershipService membershipService = null;
+
+    /**
+     * Name for logging purpose
+     */
+    protected String clusterImpName = "SimpleTcpCluster";
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager(Constants.Package);
+
+    /**
+     * The cluster name to join
+     */
+    protected String clusterName ;
+
+    /**
+     * The Container associated with this Cluster.
+     */
+    protected Container container = null;
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+    /**
+     * Globale MBean Server
+     */
+    private MBeanServer mserver = null;
+
+    /**
+     * Current Catalina Registry
+     */
+    private Registry registry = null;
+
+    /**
+     * Has this component been started?
+     */
+    protected boolean started = false;
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+    /**
+     * The context name <->manager association for distributed contexts.
+     */
+    protected Map managers = new HashMap();
+
+    //sort members by alive time
+    protected MemberComparator memberComparator = new MemberComparator();
+
+    private String managerClassName = "org.apache.catalina.cluster.session.DeltaManager";
+
+    /**
+     * Sender to send data with
+     */
+    private org.apache.catalina.cluster.ClusterSender clusterSender;
+
+    /**
+     * Receiver to register call back with
+     */
+    private org.apache.catalina.cluster.ClusterReceiver clusterReceiver;
+
+    private List valves = new ArrayList();
+
+    private org.apache.catalina.cluster.ClusterDeployer clusterDeployer;
+
+    private boolean defaultMode = true ;
+    
+    /**
+     * Listeners of messages
+     */
+    protected List clusterListeners = new ArrayList();
+
+    /**
+     * Comment for <code>notifyLifecycleListenerOnFailure</code>
+     */
+    private boolean notifyLifecycleListenerOnFailure = false;
+
+    private ObjectName objectName = null;
+
+    /**
+     * dynamic sender <code>properties</code>
+     */
+    private Map properties = new HashMap();
+
+    /**
+     * The cluster log device name to log at level info
+     */
+    private String clusterLogName = "org.apache.catalina.cluster.tcp.SimpleTcpCluster";
+
+    private boolean doClusterLog = false;
+
+    private Log clusterLog = null;
+
+    // ------------------------------------------------------------- Properties
+
+    public SimpleTcpCluster() {
+    }
+
+    /**
+     * Return descriptive information about this Cluster implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+    /**
+     * Set the name of the cluster to join, if no cluster with this name is
+     * present create one.
+     * 
+     * @param clusterName
+     *            The clustername to join
+     */
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    /**
+     * Return the name of the cluster that this Server is currently configured
+     * to operate within.
+     * 
+     * @return The name of the cluster associated with this server
+     */
+    public String getClusterName() {
+        if(clusterName == null && container != null)
+            return container.getName() ;
+        return clusterName;
+    }
+
+    /**
+     * Set the Container associated with our Cluster
+     * 
+     * @param container
+     *            The Container to use
+     */
+    public void setContainer(Container container) {
+        Container oldContainer = this.container;
+        this.container = container;
+        support.firePropertyChange("container", oldContainer, this.container);
+    }
+
+    /**
+     * Get the Container associated with our Cluster
+     * 
+     * @return The Container associated with our Cluster
+     */
+    public Container getContainer() {
+        return (this.container);
+    }
+
+    /**
+     * @return Returns the notifyLifecycleListenerOnFailure.
+     */
+    public boolean isNotifyLifecycleListenerOnFailure() {
+        return notifyLifecycleListenerOnFailure;
+    }
+
+    /**
+     * @param notifyListenerOnFailure
+     *            The notifyLifecycleListenerOnFailure to set.
+     */
+    public void setNotifyLifecycleListenerOnFailure(
+            boolean notifyListenerOnFailure) {
+        boolean oldNotifyListenerOnFailure = this.notifyLifecycleListenerOnFailure;
+        this.notifyLifecycleListenerOnFailure = notifyListenerOnFailure;
+        support.firePropertyChange("notifyLifecycleListenerOnFailure",
+                oldNotifyListenerOnFailure,
+                this.notifyLifecycleListenerOnFailure);
+    }
+
+    /**
+     * @return Returns the defaultMode.
+     */
+    public boolean isDefaultMode() {
+        return defaultMode;
+    }
+    
+    /**
+     * @param defaultMode The defaultMode to set.
+     */
+    public void setDefaultMode(boolean defaultMode) {
+        this.defaultMode = defaultMode;
+    }
+    
+    public String getManagerClassName() {
+        if(managerClassName != null)
+            return managerClassName;
+        return (String)getProperty("manager.className");
+    }
+
+    public void setManagerClassName(String managerClassName) {
+        this.managerClassName = managerClassName;
+    }
+
+    public ClusterSender getClusterSender() {
+        return clusterSender;
+    }
+
+    public void setClusterSender(ClusterSender clusterSender) {
+        this.clusterSender = clusterSender;
+    }
+
+    public ClusterReceiver getClusterReceiver() {
+        return clusterReceiver;
+    }
+
+    public void setClusterReceiver(ClusterReceiver clusterReceiver) {
+        this.clusterReceiver = clusterReceiver;
+    }
+
+    public MembershipService getMembershipService() {
+        return membershipService;
+    }
+
+    public void setMembershipService(MembershipService membershipService) {
+        this.membershipService = membershipService;
+    }
+
+    /**
+     * Add cluster valve 
+     * Cluster Valves are only add to container when cluster is started!
+     * @param valve The new cluster Valve.
+     */
+    public void addValve(Valve valve) {
+        if (valve instanceof ClusterValve)
+            valves.add(valve);
+    }
+
+    /**
+     * get all cluster valves
+     * @return current cluster valves
+     */
+    public Valve[] getValves() {
+        return (Valve[]) valves.toArray(new Valve[valves.size()]);
+    }
+
+    /**
+     * Get the cluster listeners associated with this cluster. If this Array has
+     * no listeners registered, a zero-length array is returned.
+     */
+    public MessageListener[] findClusterListeners() {
+        if (clusterListeners.size() > 0) {
+            MessageListener[] listener = new MessageListener[clusterListeners
+                    .size()];
+            clusterListeners.toArray(listener);
+            return listener;
+        } else
+            return new MessageListener[0];
+
+    }
+
+    /**
+     * add cluster message listener and register cluster to this listener
+     * 
+     * @see org.apache.catalina.cluster.CatalinaCluster#addClusterListener(org.apache.catalina.cluster.MessageListener)
+     */
+    public void addClusterListener(MessageListener listener) {
+        if (listener != null && !clusterListeners.contains(listener)) {
+            clusterListeners.add(listener);
+            listener.setCluster(this);
+        }
+    }
+
+    /**
+     * remove message listener and deregister Cluster from listener
+     * 
+     * @see org.apache.catalina.cluster.CatalinaCluster#removeClusterListener(org.apache.catalina.cluster.MessageListener)
+     */
+    public void removeClusterListener(MessageListener listener) {
+        if (listener != null) {
+            clusterListeners.remove(listener);
+            listener.setCluster(null);
+        }
+    }
+
+    public org.apache.catalina.cluster.ClusterDeployer getClusterDeployer() {
+        return clusterDeployer;
+    }
+
+    public void setClusterDeployer(
+            org.apache.catalina.cluster.ClusterDeployer clusterDeployer) {
+        this.clusterDeployer = clusterDeployer;
+    }
+
+    /**
+     * Get all current cluster members
+     * @return all members or empty array 
+     */
+    public Member[] getMembers() {
+        Member[] members = membershipService.getMembers();
+        if(members != null) {
+            //sort by alive time
+            java.util.Arrays.sort(members, memberComparator);
+        } else 
+            members = new Member[0];
+        return members;
+    }
+
+    /**
+     * Return the member that represents this node.
+     * 
+     * @return Member
+     */
+    public Member getLocalMember() {
+        return membershipService.getLocalMember();
+    }
+
+    // ------------------------------------------------------------- dynamic
+    // manager property handling
+
+    /**
+     * JMX hack to direct use at jconsole
+     * 
+     * @param name
+     * @param value
+     */
+    public void setProperty(String name, String value) {
+        setProperty(name, (Object) value);
+    }
+
+    /**
+     * set config attributes with reflect and propagate to all managers
+     * 
+     * @param name
+     * @param value
+     */
+    public void setProperty(String name, Object value) {
+        if (log.isTraceEnabled())
+            log.trace(sm.getString("SimpleTcpCluster.setProperty", name, value,
+                    properties.get(name)));
+
+        properties.put(name, value);
+        if(started) {
+            // FIXME Hmm, is that correct when some DeltaManagers are direct configured inside Context?
+            // Why we not support it for other elements, like sender, receiver or membership?
+            // Must we restart element after change?
+            if (name.startsWith("manager")) {
+                String key = name.substring("manager".length() + 1);
+                String pvalue = value.toString();
+                for (Iterator iter = managers.values().iterator(); iter.hasNext();) {
+                    Manager manager = (Manager) iter.next();
+                    if(manager instanceof DeltaManager && ((ClusterManager) manager).isDefaultMode()) {
+                        IntrospectionUtils.setProperty(manager, key, pvalue );
+                    }
+                }
+            } 
+        }
+    }
+
+    /**
+     * get current config
+     * 
+     * @param key
+     * @return The property
+     */
+    public Object getProperty(String key) {
+        if (log.isTraceEnabled())
+            log.trace(sm.getString("SimpleTcpCluster.getProperty", key));
+        return properties.get(key);
+    }
+
+    /**
+     * Get all properties keys
+     * 
+     * @return An iterator over the property names.
+     */
+    public Iterator getPropertyNames() {
+        return properties.keySet().iterator();
+    }
+
+    /**
+     * remove a configured property.
+     * 
+     * @param key
+     */
+    public void removeProperty(String key) {
+        properties.remove(key);
+    }
+
+    /**
+     * transfer properties from cluster configuration to subelement bean.
+     * @param prefix
+     * @param bean
+     */
+    protected void transferProperty(String prefix, Object bean) {
+        if (prefix != null) {
+            for (Iterator iter = getPropertyNames(); iter.hasNext();) {
+                String pkey = (String) iter.next();
+                if (pkey.startsWith(prefix)) {
+                    String key = pkey.substring(prefix.length() + 1);
+                    Object value = getProperty(pkey);
+                    IntrospectionUtils.setProperty(bean, key, value.toString());
+                }
+            }
+        }
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * @return Returns the managers.
+     */
+    public Map getManagers() {
+        return managers;
+    }
+
+    /**
+     * Create new Manager without add to cluster (comes with start the manager)
+     * 
+     * @param name
+     *            Context Name of this manager
+     * @see org.apache.catalina.Cluster#createManager(java.lang.String)
+     * @see #addManager(String, Manager)
+     * @see DeltaManager#start()
+     */
+    public synchronized Manager createManager(String name) {
+        if (log.isDebugEnabled())
+            log.debug("Creating ClusterManager for context " + name
+                    + " using class " + getManagerClassName());
+        Manager manager = null;
+        try {
+            manager = (Manager) getClass().getClassLoader().loadClass(
+                    getManagerClassName()).newInstance();
+        } catch (Exception x) {
+            log.error("Unable to load class for replication manager", x);
+            manager = new org.apache.catalina.cluster.session.DeltaManager();
+        } finally {
+            if(manager != null) {
+                manager.setDistributable(true);
+                if (manager instanceof ClusterManager) {
+                    ClusterManager cmanager = (ClusterManager) manager ;
+                    cmanager.setDefaultMode(true);
+                    cmanager.setName(getManagerName(name,manager));
+                    cmanager.setCluster(this);
+                }
+            }
+        }
+        return manager;
+    }
+
+    /**
+     * remove an application form cluster replication bus
+     * 
+     * @see org.apache.catalina.cluster.CatalinaCluster#removeManager(java.lang.String,Manager)
+     */
+    public void removeManager(String name,Manager manager) {
+        if (manager != null) {
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(BEFORE_MANAGERUNREGISTER_EVENT,
+                    manager);
+            managers.remove(getManagerName(name,manager));
+            if (manager instanceof ClusterManager)
+                ((ClusterManager) manager).setCluster(null);
+            // Notify our interested LifecycleListeners
+            lifecycle
+                    .fireLifecycleEvent(AFTER_MANAGERUNREGISTER_EVENT, manager);
+        }
+    }
+
+    /**
+     * add an application to cluster replication bus
+     * 
+     * @param name
+     *            of the context
+     * @param manager
+     *            manager to register
+     * @see org.apache.catalina.cluster.CatalinaCluster#addManager(java.lang.String,
+     *      org.apache.catalina.Manager)
+     */
+    public void addManager(String name, Manager manager) {
+        if (!manager.getDistributable()) {
+            log.warn("Manager with name " + name
+                    + " is not distributable, can't add as cluster manager");
+            return;
+        }
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_MANAGERREGISTER_EVENT, manager);
+        String clusterName = getManagerName(name, manager);
+        if (manager instanceof ClusterManager) {
+            ClusterManager cmanager = (ClusterManager) manager ;
+            cmanager.setName(clusterName);
+            cmanager.setCluster(this);
+            if(cmanager.isDefaultMode())
+                transferProperty("manager",cmanager);
+        }
+        managers.put(clusterName, manager);
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_MANAGERREGISTER_EVENT, manager);
+    }
+
+    /**
+     * @param name
+     * @param manager
+     * @return
+     */
+    private String getManagerName(String name, Manager manager) {
+        String clusterName = name ;
+        if(getContainer() instanceof Engine) {
+            Container context = manager.getContainer() ;
+            if(context != null && context instanceof Context) {
+                Container host = ((Context)context).getParent();
+                if(host != null && host instanceof Host)
+                    clusterName = host.getName()  + name ;
+            }
+        }
+        return clusterName;
+    }
+
+    /*
+     * Get Manager
+     * 
+     * @see org.apache.catalina.cluster.CatalinaCluster#getManager(java.lang.String)
+     */
+    public Manager getManager(String name) {
+        return (Manager) managers.get(name);
+    }
+
+ 
+    // ------------------------------------------------------ Lifecycle Methods
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     * @see org.apache.catalina.cluster.deploy.FarmWarDeployer#backgroundProcess()
+     * @see ReplicationTransmitter#backgroundProcess()
+     */
+    public void backgroundProcess() {
+        if (clusterDeployer != null)
+            clusterDeployer.backgroundProcess();
+        if (clusterSender != null)
+            clusterSender.backgroundProcess();
+    }
+
+    /**
+     * Add a lifecycle event listener to this component.
+     * 
+     * @param listener
+     *            The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     * 
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+    /**
+     * Use as base to handle start/stop/periodic Events from host. Currently
+     * only log the messages as trace level.
+     * 
+     * @see org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent)
+     */
+    public void lifecycleEvent(LifecycleEvent lifecycleEvent) {
+        if (log.isTraceEnabled())
+            log.trace(sm.getString("SimpleTcpCluster.event.log", lifecycleEvent
+                    .getType(), lifecycleEvent.getData()));
+    }
+
+    // ------------------------------------------------------ public
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component. This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized. <BR>
+     * Starts the cluster communication channel, this will connect with the
+     * other nodes in the cluster, and request the current session state to be
+     * transferred to this node.
+     * 
+     * @exception IllegalStateException
+     *                if this component has already been started
+     * @exception LifecycleException
+     *                if this component detects a fatal error that prevents this
+     *                component from being used
+     */
+    public void start() throws LifecycleException {
+        if (started)
+            throw new LifecycleException(sm.getString("cluster.alreadyStarted"));
+        if (log.isInfoEnabled())
+            log.info("Cluster is about to start");
+        getClusterLog();
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, this);
+        try {
+            if(isDefaultMode() && valves.size() == 0) {
+                createDefaultClusterValves() ;
+            }
+            registerClusterValve();
+            registerMBeans();
+            // setup the default cluster session listener (DeltaManager support)
+            if(isDefaultMode() && clusterListeners.size() == 0) {
+                createDefaultClusterListener();
+            }
+            // setup the default cluster Receiver
+            if(isDefaultMode() && clusterReceiver == null) {
+                createDefaultClusterReceiver();
+            }
+            // setup the default cluster sender
+            if(isDefaultMode() && clusterSender == null) {
+                createDefaultClusterSender();
+            }
+            // start the receiver.
+            if(clusterReceiver != null) {
+                clusterReceiver.setSendAck(clusterSender.isWaitForAck());
+                clusterReceiver.setCompress(clusterSender.isCompress());
+                clusterReceiver.setCatalinaCluster(this);
+                clusterReceiver.start();
+            }
+     
+            // start the sender.
+            if(clusterSender != null && clusterReceiver != null) {
+                clusterSender.setCatalinaCluster(this);
+                clusterSender.start();
+            }
+            
+            // start the membership service.
+            if(isDefaultMode() && membershipService == null) {
+                createDefaultMembershipService();
+            }
+            
+            if(membershipService != null && clusterReceiver != null) {
+                membershipService.setLocalMemberProperties(clusterReceiver
+                    .getHost(), clusterReceiver.getPort());
+                membershipService.addMembershipListener(this);
+                membershipService.setCatalinaCluster(this);
+                membershipService.start();
+                // start the deployer.
+                try {
+                    if (clusterDeployer != null) {
+                        clusterDeployer.setCluster(this);
+                        clusterDeployer.start();
+                    }
+                } catch (Throwable x) {
+                    log.fatal("Unable to retrieve the container deployer. Cluster deployment disabled.",x);
+                }
+            }
+            this.started = true;
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(AFTER_START_EVENT, this);
+        } catch (Exception x) {
+            log.error("Unable to start cluster.", x);
+            throw new LifecycleException(x);
+        }
+    }
+
+    /**
+     * Create default membership service:
+     * <pre>
+     * &lt;Membership 
+     *             className="org.apache.catalina.cluster.mcast.McastService"
+     *             mcastAddr="228.0.0.4"
+     *             mcastPort="8012"
+     *             mcastFrequency="1000"
+     *             mcastDropTime="30000"/&gt;
+     * </pre>
+     */
+    protected void createDefaultMembershipService() {
+        if (log.isInfoEnabled()) {
+            log.info(sm.getString(
+                    "SimpleTcpCluster.default.addMembershipService",
+                    getClusterName()));
+        }
+        
+        McastService mService= new McastService();
+        mService.setMcastAddr("228.0.0.4");
+        mService.setMcastPort(8012);
+        mService.setMcastFrequency(1000);
+        mService.setMcastDropTime(30000);
+        transferProperty("service",mService);        
+        setMembershipService(mService);          
+    }
+
+    
+    /**
+     * Create default cluster sender
+     * <pre>
+     *  &lt;Sender
+     *     className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+     *     replicationMode="fastasyncqueue"
+     *     doTransmitterProcessingStats="true"
+     *     doProcessingStats="true"/&gt;
+     *  </pre>
+     */
+    protected void createDefaultClusterSender() {
+        if (log.isInfoEnabled()) {
+            log.info(sm.getString(
+                    "SimpleTcpCluster.default.addClusterSender",
+                    getClusterName()));
+        }        
+        ReplicationTransmitter sender= new ReplicationTransmitter();
+        sender.setReplicationMode("fastasyncqueue");
+        sender.setDoTransmitterProcessingStats(true);
+        sender.setProperty("doProcessingStats", "true");
+        transferProperty("sender",sender);
+        setClusterSender(sender);          
+    }
+
+    /**
+     * Create default receiver:
+     * <pre>
+     *   &lt;Receiver 
+     *     className="org.apache.catalina.cluster.tcp.SocketReplicationListener"
+     *     tcpListenAddress="auto"
+     *     tcpListenPort="8015"
+     *     tcpListenMaxPort="8019"
+     *     doReceivedProcessingStats="true"
+     *   /&gt;
+     * </pre>
+     */
+    protected void createDefaultClusterReceiver() {
+        if (log.isInfoEnabled()) {
+            log.info(sm.getString(
+                    "SimpleTcpCluster.default.addClusterReceiver",
+                    getClusterName()));
+        }
+        SocketReplicationListener receiver= new SocketReplicationListener();
+        receiver.setTcpListenAddress("auto");
+        receiver.setDoReceivedProcessingStats(true);
+        receiver.setTcpListenPort(8015);
+        receiver.setTcpListenMaxPort(8019);
+        transferProperty("receiver",receiver);
+        setClusterReceiver(receiver);          
+        
+    }
+
+    /**
+     * Create default session cluster listener:
+     *  <pre>
+     * &lt;ClusterListener 
+     *   className="org.apache.catalina.cluster.session.ClusterSessionListener" /&gt;
+     * </pre>
+     */
+    protected void createDefaultClusterListener() {
+        if (log.isInfoEnabled()) {
+            log.info(sm.getString(
+                    "SimpleTcpCluster.default.addClusterListener",
+                    getClusterName()));
+        }
+        ClusterSessionListener listener = new ClusterSessionListener();
+        transferProperty("listener",listener);
+        addClusterListener(listener);
+        
+    }
+
+    /**
+     * Create default ReplicationValve
+     * <pre>
+     * &lt;Valve 
+     *    className="org.apache.catalina.cluster.tcp.ReplicationValve"
+     *    filter=".*\.gif;.*\.js;.*\.css;.*\.png;.*\.jpeg;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"
+     *    primaryIndicator="true" /&gt;
+     * </pre>
+     */
+    protected void createDefaultClusterValves() {
+        if (log.isInfoEnabled()) {
+            log.info(sm.getString(
+                    "SimpleTcpCluster.default.addClusterValves",
+                    getClusterName()));
+        }
+        ReplicationValve valve= new ReplicationValve() ;
+        valve.setFilter(".*\\.gif;.*\\.js;.*\\.css;.*\\.png;.*\\.jpeg;.*\\.jpg;.*\\.htm;.*\\.html;.*\\.txt;");
+        valve.setPrimaryIndicator(true);
+        transferProperty("valve",valve);
+        addValve(valve);
+        
+    }
+
+    /**
+     * register all cluster valve to host or engine
+     * @throws Exception
+     * @throws ClassNotFoundException
+     */
+    protected void registerClusterValve() throws Exception {
+        for (Iterator iter = valves.iterator(); iter.hasNext();) {
+            ClusterValve valve = (ClusterValve) iter.next();
+            if (log.isDebugEnabled())
+                log.debug("Invoking addValve on " + getContainer()
+                        + " with class=" + valve.getClass().getName());
+            if (valve != null) {
+                IntrospectionUtils.callMethodN(getContainer(), "addValve",
+                        new Object[] { valve }, new Class[] { Thread
+                                .currentThread().getContextClassLoader()
+                                .loadClass("org.apache.catalina.Valve") });
+
+            }
+            valve.setCluster(this);
+        }
+    }
+
+    /**
+     * unregister all cluster valve to host or engine
+     * @throws Exception
+     * @throws ClassNotFoundException
+     */
+    protected void unregisterClusterValve() throws Exception {
+        for (Iterator iter = valves.iterator(); iter.hasNext();) {
+            ClusterValve valve = (ClusterValve) iter.next();
+            if (log.isDebugEnabled())
+                log.debug("Invoking removeValve on " + getContainer()
+                        + " with class=" + valve.getClass().getName());
+            if (valve != null) {
+                IntrospectionUtils.callMethodN(getContainer(), "removeValve",
+                        new Object[] { valve }, new Class[] { Thread
+                                .currentThread().getContextClassLoader()
+                                .loadClass("org.apache.catalina.Valve") });
+
+            }
+            valve.setCluster(this);
+        }
+    }
+
+    /**
+     * Gracefully terminate the active cluster component.<br/>
+     * This will disconnect the cluster communication channel, stop the
+     * listener and deregister the valves from host or engine.<br/><br/>
+     * <b>Note:</b><br/>The sub elements receiver, sender, membership,
+     * listener or valves are not removed. You can easily start the cluster again.
+     * 
+     * @exception IllegalStateException
+     *                if this component has not been started
+     * @exception LifecycleException
+     *                if this component detects a fatal error that needs to be
+     *                reported
+     */
+    public void stop() throws LifecycleException {
+
+        if (!started)
+            throw new IllegalStateException(sm.getString("cluster.notStarted"));
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+
+        if (clusterDeployer != null) {
+            clusterDeployer.stop();
+        }
+        // FIXME remove registered managers!!
+        if(membershipService != null) {
+            membershipService.stop();
+            membershipService.removeMembershipListener();
+        }
+        if(clusterSender != null) {
+            try {
+                clusterSender.stop();
+            } catch (Exception x) {
+                log.error("Unable to stop cluster sender.", x);
+            }
+        }
+        if(clusterReceiver != null ){
+            try {
+                clusterReceiver.stop();
+                clusterReceiver.setCatalinaCluster(null);
+            } catch (Exception x) {
+                log.error("Unable to stop cluster receiver.", x);
+            }
+        }
+        unregisterMBeans();
+        try {
+            unregisterClusterValve();
+        } catch (Exception x) {
+            log.error("Unable to stop cluster valve.", x);
+        }
+        started = false;
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+        clusterLog = null ;
+   }
+
+    /**
+     * send message to all cluster members same cluster domain
+     * 
+     * @see org.apache.catalina.cluster.CatalinaCluster#send(org.apache.catalina.cluster.ClusterMessage)
+     */
+    public void sendClusterDomain(ClusterMessage msg) {
+        long start = 0;
+        if (doClusterLog)
+            start = System.currentTimeMillis();
+        try {
+            msg.setAddress(membershipService.getLocalMember());
+            clusterSender.sendMessageClusterDomain(msg);
+        } catch (Exception x) {
+            if (notifyLifecycleListenerOnFailure) {
+                // Notify our interested LifecycleListeners
+                lifecycle.fireLifecycleEvent(SEND_MESSAGE_FAILURE_EVENT,
+                        new SendMessageData(msg, null, x));
+            }
+            log.error("Unable to send message through cluster sender.", x);
+        }
+        if (doClusterLog)
+            logSendMessage(msg, start, null);
+    } 
+
+
+    /**
+     * send message to all cluster members
+     * @param msg message to transfer
+     * 
+     * @see org.apache.catalina.cluster.CatalinaCluster#send(org.apache.catalina.cluster.ClusterMessage)
+     */
+    public void send(ClusterMessage msg) {
+        send(msg, null);
+    }
+
+    /**
+     * send a cluster message to one member (very usefull JMX method for remote scripting)
+     * 
+     * @param msg message to transfer
+     * @param dest Receiver member with name
+     * @see org.apache.catalina.cluster.CatalinaCluster#send(org.apache.catalina.cluster.ClusterMessage,
+     *      org.apache.catalina.cluster.Member)
+     * @see McastService#findMemberByName(String)
+     */
+    public void sendToMember(ClusterMessage msg, String dest) {
+        Member member = getMembershipService().findMemberByName(dest);
+        if (member != null) {
+            send(msg, member);
+        } else {
+            log.error("sendToMember: member " + dest + " not found!");
+        }        
+    }
+    
+    /**
+     * send a cluster message to one member
+     * 
+     * @param msg message to transfer
+     * @param dest Receiver member
+     * @see org.apache.catalina.cluster.CatalinaCluster#send(org.apache.catalina.cluster.ClusterMessage,
+     *      org.apache.catalina.cluster.Member)
+     */
+    public void send(ClusterMessage msg, Member dest) {
+        long start = 0;
+        if (doClusterLog)
+            start = System.currentTimeMillis();
+        try {
+            msg.setAddress(membershipService.getLocalMember());
+            if (dest != null) {
+                if (!membershipService.getLocalMember().equals(dest)) {
+                    clusterSender.sendMessage(msg, dest);
+                } else
+                    log.error("Unable to send message to local member " + msg);
+            } else {
+                clusterSender.sendMessage(msg);
+            }
+        } catch (Exception x) {
+            if (notifyLifecycleListenerOnFailure) {
+                // Notify our interested LifecycleListeners
+                lifecycle.fireLifecycleEvent(SEND_MESSAGE_FAILURE_EVENT,
+                        new SendMessageData(msg, dest, x));
+            }
+            log.error("Unable to send message through cluster sender.", x);
+        }
+        if (doClusterLog)
+            logSendMessage(msg, start, dest);
+    }
+
+    /**
+     * New cluster member is registered
+     * 
+     * @see org.apache.catalina.cluster.MembershipListener#memberAdded(org.apache.catalina.cluster.Member)
+     */
+    public void memberAdded(Member member) {
+        try {
+            if (log.isInfoEnabled())
+                log.info("Replication member added:" + member);
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(BEFORE_MEMBERREGISTER_EVENT, member);
+            clusterSender.add(member);
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(AFTER_MEMBERREGISTER_EVENT, member);
+        } catch (Exception x) {
+            log.error("Unable to connect to replication system.", x);
+        }
+
+    }
+
+    /**
+     * Cluster member is gone
+     * 
+     * @see org.apache.catalina.cluster.MembershipListener#memberDisappeared(org.apache.catalina.cluster.Member)
+     */
+    public void memberDisappeared(Member member) {
+        if (log.isInfoEnabled())
+            log.info("Received member disappeared:" + member);
+        try {
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(BEFORE_MEMBERUNREGISTER_EVENT, member);
+            clusterSender.remove(member);
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(AFTER_MEMBERUNREGISTER_EVENT, member);
+        } catch (Exception x) {
+            log.error("Unable remove cluster node from replication system.", x);
+        }
+
+    }
+
+    // --------------------------------------------------------- receiver
+    // messages
+
+    /**
+     * notify all listeners from receiving a new message is not ClusterMessage
+     * emitt Failure Event to LifecylceListener
+     * 
+     * @param message
+     *            receveived Message
+     */
+    public void receive(ClusterMessage message) {
+
+        long start = 0;
+        if (doClusterLog)
+            start = System.currentTimeMillis();
+        if (log.isDebugEnabled() && message != null)
+            log.debug("Assuming clocks are synched: Replication for "
+                    + message.getUniqueId() + " took="
+                    + (System.currentTimeMillis() - (message).getTimestamp())
+                    + " ms.");
+
+        //invoke all the listeners
+        boolean accepted = false;
+        if (message != null) {
+            for (Iterator iter = clusterListeners.iterator(); iter.hasNext();) {
+                MessageListener listener = (MessageListener) iter.next();
+                if (listener.accept(message)) {
+                    accepted = true;
+                    listener.messageReceived(message);
+                }
+            }
+        }
+        if (!accepted && log.isDebugEnabled()) {
+            if (notifyLifecycleListenerOnFailure) {
+                Member dest = message.getAddress();
+                // Notify our interested LifecycleListeners
+                lifecycle.fireLifecycleEvent(RECEIVE_MESSAGE_FAILURE_EVENT,
+                        new SendMessageData(message, dest, null));
+            }
+            log.debug("Message " + message.toString() + " from type "
+                    + message.getClass().getName()
+                    + " transfered but no listener registered");
+        }
+        if (doClusterLog)
+            logReceiveMessage(message, start, accepted);
+    }
+
+    // --------------------------------------------------------- Logger
+
+    /**
+     * @return Returns the clusterLogName.
+     */
+    public String getClusterLogName() {
+        return clusterLogName;
+    }
+    
+    /**
+     * @param clusterLogName The clusterLogName to set.
+     */
+    public void setClusterLogName(String clusterLogName) {
+        this.clusterLogName = clusterLogName;
+    }
+    
+    /**
+     * @return Returns the doClusterLog.
+     */
+    public boolean isDoClusterLog() {
+        return doClusterLog;
+    }
+    
+    /**
+     * @param doClusterLog The doClusterLog to set.
+     */
+    public void setDoClusterLog(boolean doClusterLog) {
+        this.doClusterLog = doClusterLog;
+    }    
+    public Log getLogger() {
+        return log;
+    }
+
+    public Log getClusterLog() {
+        if (clusterLog == null && clusterLogName != null
+                && !"".equals(clusterLogName))
+            clusterLog = LogFactory.getLog(clusterLogName);
+
+        return clusterLog;
+    }
+
+    /**
+     * log received message to cluster transfer log
+     * @param message
+     * @param start
+     * @param accepted
+     */
+    protected void logReceiveMessage(ClusterMessage message, long start,
+            boolean accepted) {
+        if (clusterLog != null && clusterLog.isInfoEnabled()) {
+            clusterLog.info(sm.getString("SimpleTcpCluster.log.receive", new Object[] {
+                    new Date(start),
+                    new Long(System.currentTimeMillis() - start),
+                    message.getAddress().getHost(),
+                    new Integer(message.getAddress().getPort()),
+                    message.getUniqueId(), new Boolean(accepted) }));
+        }
+    }
+
+    /**
+     * log sended message to cluster transfer log
+     * @param message
+     * @param start
+     * @param dest
+     */
+    protected void logSendMessage(ClusterMessage message, long start,
+            Member dest) {
+        if (clusterLog != null && clusterLog.isInfoEnabled()) {
+            if (dest != null) {
+                clusterLog.info(sm.getString("SimpleTcpCluster.log.send",
+                        new Object[] { new Date(start),
+                                new Long(System.currentTimeMillis() - start),
+                                dest.getHost(), new Integer(dest.getPort()),
+                                message.getUniqueId() }));
+            } else {
+                clusterLog.info(sm.getString("SimpleTcpCluster.log.send.all",
+                        new Object[] { new Date(start),
+                                new Long(System.currentTimeMillis() - start),
+                                message.getUniqueId() }));
+            }
+        }
+    }
+
+    // --------------------------------------------- JMX MBeans
+
+    /**
+     * register Means at cluster.
+     */
+    protected void registerMBeans() {
+        try {
+            getMBeanServer();
+            String domain = mserver.getDefaultDomain();
+            String name = ":type=Cluster";
+            if (container instanceof StandardHost) {
+                domain = ((StandardHost) container).getDomain();
+                name += ",host=" + container.getName();
+            } else {
+                if (container instanceof StandardEngine) {
+                    domain = ((StandardEngine) container).getDomain();
+                }
+            }
+            ObjectName clusterName = new ObjectName(domain + name);
+
+            if (mserver.isRegistered(clusterName)) {
+                if (log.isWarnEnabled())
+                    log.warn(sm.getString("cluster.mbean.register.allready",
+                            clusterName));
+                return;
+            }
+            setObjectName(clusterName);
+            mserver.registerMBean(getManagedBean(this), getObjectName());
+        } catch (Exception ex) {
+            log.error(ex.getMessage(), ex);
+        }
+    }
+
+    protected void unregisterMBeans() {
+        if (mserver != null) {
+            try {
+                mserver.unregisterMBean(getObjectName());
+            } catch (Exception e) {
+                log.error(e);
+            }
+        }
+    }
+
+    /**
+     * Get current Catalina MBean Server and load mbean registry
+     * 
+     * @return The server
+     * @throws Exception
+     */
+    public MBeanServer getMBeanServer() throws Exception {
+        if (mserver == null) {
+            if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
+                mserver = (MBeanServer) MBeanServerFactory
+                        .findMBeanServer(null).get(0);
+            } else {
+                mserver = MBeanServerFactory.createMBeanServer();
+            }
+            registry = Registry.getRegistry(null, null);
+            registry.loadMetadata(this.getClass().getResourceAsStream(
+                    "mbeans-descriptors.xml"));
+        }
+        return (mserver);
+    }
+
+    /**
+     * Returns the ModelMBean
+     * 
+     * @param object
+     *            The Object to get the ModelMBean for
+     * @return The ModelMBean
+     * @throws Exception
+     *             If an error occurs this constructors throws this exception
+     */
+    public ModelMBean getManagedBean(Object object) throws Exception {
+        ModelMBean mbean = null;
+        if (registry != null) {
+            ManagedBean managedBean = registry.findManagedBean(object
+                    .getClass().getName());
+            mbean = managedBean.createMBean(object);
+        }
+        return mbean;
+    }
+
+    public void setObjectName(ObjectName name) {
+        objectName = name;
+    }
+
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+
+    // --------------------------------------------- Inner Class
+
+    private class MemberComparator implements java.util.Comparator {
+
+        public int compare(Object o1, Object o2) {
+            try {
+                return compare((Member) o1, (Member) o2);
+            } catch (ClassCastException x) {
+                return 0;
+            }
+        }
+
+        public int compare(Member m1, Member m2) {
+            //longer alive time, means sort first
+            long result = m2.getMemberAliveTime() - m1.getMemberAliveTime();
+            if (result < 0)
+                return -1;
+            else if (result == 0)
+                return 0;
+            else
+                return 1;
+        }
+    }
+ 
+
+    // ------------------------------------------------------------- deprecated
+
+    /**
+     * 
+     * @see org.apache.catalina.Cluster#setProtocol(java.lang.String)
+     */
+    public void setProtocol(String protocol) {
+    }
+
+    /**
+     * @see org.apache.catalina.Cluster#getProtocol()
+     */
+    public String getProtocol() {
+        return null;
+    }
+
+    /**
+     * @see org.apache.catalina.Cluster#startContext(java.lang.String)
+     */
+    public void startContext(String contextPath) throws IOException {
+        
+    }
+
+    /**
+     * @see org.apache.catalina.Cluster#installContext(java.lang.String, java.net.URL)
+     */
+    public void installContext(String contextPath, URL war) {
+        
+    }
+
+    /**
+     * @see org.apache.catalina.Cluster#stop(java.lang.String)
+     */
+    public void stop(String contextPath) throws IOException {
+        
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SocketReplicationListener.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SocketReplicationListener.java
new file mode 100644
index 0000000..d17dd47
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SocketReplicationListener.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.apache.catalina.util.StringManager;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public class SocketReplicationListener extends ClusterReceiverBase {
+
+    // ---------------------------------------------------- Statics
+
+    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(SocketReplicationListener.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm = StringManager
+            .getManager(Constants.Package);
+   
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "SocketReplicationListener/1.2";
+
+    //  ---------------------------------------------------- Properties
+    private ServerSocket serverSocket = null;
+
+    private int tcpListenMaxPort ;
+    
+    /**
+     * 
+     * One second timeout to wait that socket started
+     */
+    private int tcpListenTimeout = 1 ;
+    
+    //  ---------------------------------------------------- Constructor
+
+    public SocketReplicationListener() {
+    }
+
+    //  ---------------------------------------------------- Properties
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+    
+    /**
+     * @return Returns the tcpListenMaxPort.
+     */
+    public int getTcpListenMaxPort() {
+        return tcpListenMaxPort;
+    }
+    
+    /**
+     * @param maxListenPort The tcpListenMaxPort to set.
+     */
+    public void setTcpListenMaxPort(int maxListenPort) {
+        this.tcpListenMaxPort = maxListenPort;
+    }
+     
+    /**
+     * @return Returns the tcpListenTimeout.
+     */
+    public int getTcpListenTimeout() {
+        return tcpListenTimeout;
+    }
+    /**
+     * @param tcpListenTimeout The tcpListenTimeout to set.
+     */
+    public void setTcpListenTimeout(int tcpListenTimeout) {
+        this.tcpListenTimeout = tcpListenTimeout;
+    }
+
+    //  ---------------------------------------------------- public methods
+
+    /**
+     * Wait the createServerSocket find the correct socket port when default config is used.
+     * @see org.apache.catalina.cluster.ClusterReceiver#start()
+     * @see #createServerSocket()
+     */
+    public void start() {
+        super.start();
+        long reqStart = System.currentTimeMillis();
+        long reqNow = 0 ;
+        boolean isTimeout = false ;
+        do {
+            try {
+                Thread.sleep(50);
+            } catch (Exception sleep) {
+            }
+            reqNow = System.currentTimeMillis();
+            isTimeout = ((reqNow - reqStart) > (1000 * getTcpListenTimeout()));
+        } while (!doListen && (!isTimeout));
+        if (isTimeout || (!doListen)) {
+            log.error(sm.getString("SocketReplictionListener.timeout",
+                    getTcpListenAddress(),Integer.toString(getTcpListenPort()),
+                    Long.toString(reqNow - reqStart), Boolean.toString(doListen)));
+        }
+    }
+    
+    //  ---------------------------------------------------- protected methods
+
+    /**
+     * Master/Slave Sender handling / bind Server Socket at addres and port
+     * 
+     * @throws Exception
+     */
+    protected void listen() {
+        if (doListen) {
+            log.warn(sm.getString("SocketReplictionListener.allreadyExists",
+                    getTcpListenAddress(),Integer.toString(getTcpListenPort())));
+            return;
+        }
+
+        // Get the associated ServerSocket to bind it with
+        try {
+            serverSocket = createServerSocket();
+            if(serverSocket != null) {
+                doListen = true;
+                while (doListen) {
+                    try {
+                        Socket socket = serverSocket.accept();
+                        if (doListen) {
+                            SocketReplicationThread t = new SocketReplicationThread(
+                                    this, socket);
+                            t.setDaemon(true);
+                            t.start();
+                        }
+                    } catch (IOException iex) {
+                        log.warn(sm.getString("SocketReplictionListener.accept.failure",
+                                getTcpListenAddress(),
+                                Integer.toString(getTcpListenPort())), iex);
+                    }
+                }
+                serverSocket.close();
+            } else {
+                log.fatal(sm.getString("SocketReplictionListener.serverSocket.notExists",
+                        getTcpListenAddress(),
+                        Integer.toString(getTcpListenPort()),
+                        Integer.toString(getTcpListenMaxPort())));
+            }                
+        } catch (IOException iex) {
+            log.warn(sm.getString("SocketReplictionListener.openclose.failure",
+                    getTcpListenAddress(),
+                    Integer.toString(getTcpListenPort())), iex);
+        } finally {
+            doListen = false;
+            serverSocket = null;
+        }
+    }
+
+    /**
+     * create a Server Socket between tcpListenerPort and tcpListenMaxPort
+     */
+    protected ServerSocket createServerSocket() {
+        int startPort = getTcpListenPort() ;
+        int maxPort = getTcpListenMaxPort() ;
+        InetAddress inet = getBind() ;
+        ServerSocket sSocket = null ;
+        if (maxPort < startPort)
+            maxPort = startPort;
+        for( int i=startPort; i<=maxPort; i++ ) {
+            try {
+                if( inet == null ) {
+                    sSocket = new ServerSocket( i, 0 );
+                } else {
+                    sSocket=new ServerSocket( i, 0, inet );
+                }
+                setTcpListenPort(i);
+                break;
+            } catch( IOException ex ) {
+                if(log.isDebugEnabled())
+                    log.debug(sm.getString("SocketReplictionListener.portbusy",
+                            inet.getHostAddress(),
+                            Integer.toString(i), 
+                            ex.toString()));
+                continue;
+            }
+        }
+        if(sSocket != null && log.isInfoEnabled())
+            log.info(sm.getString("SocketReplictionListener.open",
+                    inet.getHostAddress(),
+                    Integer.toString(getTcpListenPort())));
+        return sSocket ;
+   }
+
+    /**
+     * Need to create a connection to unlock the ServerSocker#accept(). Very
+     * fine trick from channelSocket :-)  See
+     * org.apache.jk.common.ChannelSocket's unLockSocket method (it's private,
+     * so you may need to inspect the source).
+     */
+    protected void unLockSocket() {
+        Socket s = null;
+        InetAddress ladr = getBind();
+
+        try {
+            if (ladr == null || "0.0.0.0".equals(ladr.getHostAddress())) {
+                ladr = InetAddress.getLocalHost();
+            }
+            s = new Socket(ladr, getTcpListenPort());
+            // setting soLinger to a small value will help shutdown the
+            // connection quicker
+            s.setSoLinger(true, 0);
+
+        } catch (IOException iex) {
+            log.warn(sm.getString("SocketReplictionListener.unlockSocket.failure",
+                    getTcpListenAddress(),
+                    Integer.toString(getTcpListenPort())), iex);
+        } finally {
+            try {
+                if (s != null)
+                    s.close();
+            } catch (IOException ignore) {
+            }
+        }
+    }
+
+    /**
+     * Close serverSockets
+     * FIXME the channelSocket to connect own socket to terminate accpet loop!
+     */
+    protected void stopListening() {
+        unLockSocket();
+        doListen = false;
+    }
+    
+ }
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SocketReplicationThread.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SocketReplicationThread.java
new file mode 100644
index 0000000..aa9ae2c
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SocketReplicationThread.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.cluster.tcp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+
+import org.apache.catalina.cluster.io.ListenCallback;
+import org.apache.catalina.cluster.io.SocketObjectReader;
+
+/**
+ * @author Peter Rossbach
+ * FIXME ThreadPooling
+ * FIXME Socket timeout
+ * @version $Revision$, $Date$
+ */
+public class SocketReplicationThread extends Thread implements ListenCallback {
+    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(SocketReplicationThread.class);
+
+    private static byte[] ACK_COMMAND = new byte[] { 6, 2, 3 };
+
+    private static int count = 0;
+
+    private SocketReplicationListener master;
+
+    private Socket socket;
+
+    private SocketObjectReader reader;
+
+    private boolean keepRunning = true;
+
+    /**
+     * Fork Listen Worker Thread!
+     * 
+     * @param socket
+     * @param reader
+     * @param sendAck
+     */
+    SocketReplicationThread(SocketReplicationListener master, Socket socket
+           ) {
+        super("ClusterListenThread-" + count++);
+        this.master = master;
+        this.socket = socket;
+        this.reader =  new SocketObjectReader(socket,this);
+    }
+
+    /**
+     * read sender messages / is message complete send ack and wait for next
+     * message!
+     * 
+     * @see SocketObjectReader#append(byte[],int,int)
+     * @see java.lang.Runnable#run()
+     */
+    public void run() {
+        try {
+            byte[] buffer = new byte[1024];
+            InputStream in = socket.getInputStream();
+            while (keepRunning) {
+                int cnt = in.read(buffer);
+                if (log.isTraceEnabled()) {
+                    log.trace("read " + cnt + " bytes from " + socket.getPort());
+                }
+                int ack = 0;
+                if (cnt > 0) {
+                    ack = reader.append(buffer, 0, cnt);
+                    if (log.isTraceEnabled()) {
+                        log.trace("sending " + ack + " ack packages to " + socket.getLocalPort() );
+                    }
+                    /**
+                    if (sendAck) {
+                        // ack only when message is complete receive
+                        while (ack > 0) {
+                            sendAck();
+                            ack--;
+                        }
+                    }
+                    **/
+                    keepRunning = master.isDoListen();
+                } else
+                    // EOF
+                    keepRunning = false;
+            }
+        } catch (IOException x) {
+            log.error("Unable to read data from client, disconnecting.", x);
+        } finally {
+            // finish socket
+            if (socket != null) {
+                try {
+
+                    socket.close();
+                } catch (Exception ignore) {
+                }
+            }
+            keepRunning = false;
+            socket = null;
+        }
+    }
+    
+    public void messageDataReceived(ClusterData data) {
+        master.messageDataReceived(data);
+    }   
+    
+    public boolean isSendAck() {
+        return master.isSendAck();
+    }
+    
+    /**
+     * send a reply-acknowledgement
+     * 
+     * @throws java.io.IOException
+     */
+    public void sendAck() throws java.io.IOException {
+        socket.getOutputStream().write(ACK_COMMAND);
+        if (log.isTraceEnabled()) {
+            log.trace("ACK sent to " + socket.getPort());
+        }
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SocketSender.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SocketSender.java
new file mode 100644
index 0000000..ab8856d
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SocketSender.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.net.InetAddress;
+
+/**
+ * Send cluster messages sync to request with only one socket.
+ * 
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version 1.2
+ */
+
+public class SocketSender extends DataSender {
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "SocketSender/1.2";
+
+    // ------------------------------------------------------------- Constructor
+   
+   /**
+    * @param domain replication cluster domain (session domain)
+    * @param host replication node tcp address
+    * @param port replication node tcp port
+    */
+    public SocketSender(String domain,InetAddress host, int port) {
+        super(domain,host, port);
+    }
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer("SocketSender[");
+        buf.append(getAddress()).append(":").append(getPort()).append("]");
+        return buf.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/TcpReplicationThread.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/TcpReplicationThread.java
new file mode 100644
index 0000000..a0f6e4f
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/TcpReplicationThread.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+
+import org.apache.catalina.cluster.io.ObjectReader;
+
+/**
+ * A worker thread class which can drain channels and echo-back the input. Each
+ * instance is constructed with a reference to the owning thread pool object.
+ * When started, the thread loops forever waiting to be awakened to service the
+ * channel associated with a SelectionKey object. The worker is tasked by
+ * calling its serviceChannel() method with a SelectionKey object. The
+ * serviceChannel() method stores the key reference in the thread object then
+ * calls notify() to wake it up. When the channel has been drained, the worker
+ * thread returns itself to its parent pool.
+ * 
+ * @author Filip Hanik
+ * 
+ * @version $Revision$, $Date$
+ */
+public class TcpReplicationThread extends WorkerThread {
+    public static final byte[] ACK_COMMAND = new byte[] {6, 2, 3};
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( TcpReplicationThread.class );
+    private ByteBuffer buffer = ByteBuffer.allocate (1024);
+    private SelectionKey key;
+    private boolean sendAck=true;
+
+    
+    TcpReplicationThread ()
+    {
+    }
+
+    // loop forever waiting for work to do
+    public synchronized void run()
+    {
+        while (doRun) {
+            try {
+                // sleep and release object lock
+                this.wait();
+            } catch (InterruptedException e) {
+                if(log.isInfoEnabled())
+                    log.info("TCP worker thread interrupted in cluster",e);
+                // clear interrupt status
+                Thread.interrupted();
+            }
+            if (key == null) {
+                continue;	// just in case
+            }
+            try {
+                drainChannel (key);
+            } catch (Exception e) {
+                log.error ("TCP Worker thread in cluster caught '"
+                    + e + "' closing channel", e);
+
+                // close channel and nudge selector
+                try {
+                    key.channel().close();
+                } catch (IOException ex) {
+                    log.error("Unable to close channel.",ex);
+                }
+                key.selector().wakeup();
+            }
+            key = null;
+            // done, ready for more, return to pool
+            this.pool.returnWorker (this);
+        }
+    }
+
+    /**
+     * Called to initiate a unit of work by this worker thread
+     * on the provided SelectionKey object.  This method is
+     * synchronized, as is the run() method, so only one key
+     * can be serviced at a given time.
+     * Before waking the worker thread, and before returning
+     * to the main selection loop, this key's interest set is
+     * updated to remove OP_READ.  This will cause the selector
+     * to ignore read-readiness for this channel while the
+     * worker thread is servicing it.
+     */
+    synchronized void serviceChannel (SelectionKey key, boolean sendAck)
+    {
+        this.key = key;
+        this.sendAck=sendAck;
+        key.interestOps (key.interestOps() & (~SelectionKey.OP_READ));
+        key.interestOps (key.interestOps() & (~SelectionKey.OP_WRITE));
+        this.notify();		// awaken the thread
+    }
+
+    /**
+     * The actual code which drains the channel associated with
+     * the given key.  This method assumes the key has been
+     * modified prior to invocation to turn off selection
+     * interest in OP_READ.  When this method completes it
+     * re-enables OP_READ and calls wakeup() on the selector
+     * so the selector will resume watching this channel.
+     */
+    protected void drainChannel (SelectionKey key)
+        throws Exception
+    {
+        boolean packetReceived=false;
+        SocketChannel channel = (SocketChannel) key.channel();
+        int count;
+        buffer.clear();			// make buffer empty
+        ObjectReader reader = (ObjectReader)key.attachment();
+        // loop while data available, channel is non-blocking
+        while ((count = channel.read (buffer)) > 0) {
+            buffer.flip();		// make buffer readable
+            reader.append(buffer.array(),0,count);
+            buffer.clear();		// make buffer empty
+        }
+        //check to see if any data is available
+        int pkgcnt = reader.execute();
+        if (log.isTraceEnabled()) {
+            log.trace("sending " + pkgcnt + " ack packages to " + channel.socket().getLocalPort() );
+        }
+        
+        if (sendAck) {
+            while ( pkgcnt > 0 ) {
+                sendAck(key,channel);
+                pkgcnt--;
+            }
+        }
+        
+        if (count < 0) {
+            // close channel on EOF, invalidates the key
+            channel.close();
+            return;
+        }
+        
+        //acquire the interestOps mutex
+        Object mutex = this.getPool().getInterestOpsMutex();
+        synchronized (mutex) {
+            // cycle the selector so this key is active again
+            key.selector().wakeup();
+            // resume interest in OP_READ, OP_WRITE
+            int resumeOps = key.interestOps() | SelectionKey.OP_READ;
+            key.interestOps(resumeOps);
+        }
+        
+    }
+
+    /**
+     * send a reply-acknowledgement (6,2,3)
+     * @param key
+     * @param channel
+     */
+    protected void sendAck(SelectionKey key, SocketChannel channel) {
+        
+        try {
+            channel.write(ByteBuffer.wrap(ACK_COMMAND));
+            if (log.isTraceEnabled()) {
+                log.trace("ACK sent to " + channel.socket().getPort());
+            }
+        } catch ( java.io.IOException x ) {
+            log.warn("Unable to send ACK back through channel, channel disconnected?: "+x.getMessage());
+        }
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ThreadPool.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ThreadPool.java
new file mode 100644
index 0000000..c3a91be
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/ThreadPool.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+import java.util.List;
+import java.util.LinkedList;
+
+/**
+ * @author not attributable
+ * @version 1.0
+ */
+
+public class ThreadPool
+{
+    /**
+     * A very simple thread pool class.  The pool size is set at
+     * construction time and remains fixed.  Threads are cycled
+     * through a FIFO idle queue.
+     */
+
+    List idle = new LinkedList();
+    Object mutex = new Object();
+    Object interestOpsMutex = null;
+
+    ThreadPool (int poolSize, Class threadClass, Object interestOpsMutex) throws Exception {
+        // fill up the pool with worker threads
+        this.interestOpsMutex = interestOpsMutex;
+        for (int i = 0; i < poolSize; i++) {
+            WorkerThread thread = (WorkerThread)threadClass.newInstance();
+            thread.setPool(this);
+
+            // set thread name for debugging, start it
+            thread.setName (threadClass.getName()+"[" + (i + 1)+"]");
+            thread.setDaemon(true);
+            thread.setPriority(Thread.MAX_PRIORITY);
+            thread.start();
+
+            idle.add (thread);
+        }
+    }
+
+    /**
+     * Find an idle worker thread, if any.  Could return null.
+     */
+    WorkerThread getWorker()
+    {
+        WorkerThread worker = null;
+
+        
+        synchronized (mutex) {
+            while ( worker == null ) {
+                if (idle.size() > 0) {
+                    try {
+                        worker = (WorkerThread) idle.remove(0);
+                    } catch (java.util.NoSuchElementException x) {
+                        //this means that there are no available workers
+                        worker = null;
+                    }
+                } else {
+                    try { mutex.wait(); } catch ( java.lang.InterruptedException x ) {}
+                }
+            }
+        }
+
+        return (worker);
+    }
+
+    /**
+     * Called by the worker thread to return itself to the
+     * idle pool.
+     */
+    void returnWorker (WorkerThread worker)
+    {
+        synchronized (mutex) {
+            idle.add (worker);
+            mutex.notify();
+        }
+    }
+    public Object getInterestOpsMutex() {
+        return interestOpsMutex;
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/WorkerThread.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/WorkerThread.java
new file mode 100644
index 0000000..3b632e8
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/WorkerThread.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+
+/**
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+public class WorkerThread extends Thread
+{
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( WorkerThread.class );
+    protected ThreadPool pool;
+    protected boolean doRun = true;
+
+
+    public void setPool(ThreadPool pool) {
+        this.pool = pool;
+    }
+    
+    public ThreadPool getPool() {
+        return pool;
+    }
+
+    public void close()
+    {
+        doRun = false;
+        notify();
+
+    }
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/mbeans-descriptors.xml b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/mbeans-descriptors.xml
new file mode 100644
index 0000000..0b1b684
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/tcp/mbeans-descriptors.xml
@@ -0,0 +1,1015 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mbeans-descriptors PUBLIC
+   "-//Apache Software Foundation//DTD Model MBeans Configuration File"
+   "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
+<mbeans-descriptors>
+
+  <mbean         name="SimpleTcpCluster"
+           description="Tcp Cluster implementation"
+               domain="Catalina"
+                group="Cluster"
+                 type="org.apache.catalina.cluster.tcp.SimpleTcpCluster">
+    <attribute   name="info"
+          description="Class version info"
+                 type="java.lang.String"
+                 writeable="false"/>
+    <attribute   name="notifyLifecycleListenerOnFailure"
+          description="notify lifecycleListener from message transfer failure"
+			     is="true"
+                 type="boolean"/>                 
+    <attribute   name="clusterName"
+          description="name of cluster"
+                 type="java.lang.String"/>
+    <attribute   name="managerClassName"
+          description="session mananager classname"
+                 type="java.lang.String"/>
+    <attribute   name="clusterLogName"
+          description="Name of cluster transfer log device"
+                 type="java.lang.String"/>
+    <attribute   name="doClusterLog"
+			     is="true"
+          description="enable cluster log transfer logging"
+                 type="boolean"/>
+    <operation   name="setProperty"
+               description="set a property to all cluster managers (with prefix 'manager.')"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="key"
+                 description="Property name"
+                 type="java.lang.String"/>
+      <parameter name="value"
+                 description="Property value"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="send"
+               description="send message to all cluster members"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="message"
+                 description="replication message"
+                 type="org.apache.catalina.cluster.ClusterMessage"/>
+    </operation>
+    
+    <operation   name="sendClusterDomain"
+               description="send message to all cluster members with same domain"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="message"
+                 description="replication message"
+                 type="org.apache.catalina.cluster.ClusterMessage"/>
+    </operation>
+        
+    <operation   name="sendToMember"
+               description="send message to one cluster member"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="message"
+                 description="replication message"
+                 type="org.apache.catalina..cluster.ClusterMessage"/>
+      <parameter name="member"
+                 description="cluster member"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="start"
+               description="Start the cluster"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="stop"
+               description="Stop the cluster"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+                 
+  </mbean>
+
+  <mbean         name="ReplicationListener"
+           description="Tcp Cluster ReplicationListener implementation"
+               domain="Catalina"
+                group="Cluster"
+                 type="org.apache.catalina.cluster.tcp.ReplicationListener">
+    <attribute   name="info"
+          description="Class version info"
+                 type="java.lang.String"
+                 writeable="false"/>
+    <attribute   name="tcpListenAddress"
+          description="tcp listener address"
+                 type="java.lang.String"/>
+    <attribute   name="tcpListenPort"
+          description="tcp listener port"
+                 type="int"/>
+    <attribute   name="tcpThreadCount"
+          description="number of tcp listener worker threads"
+                 type="int"/>
+    <attribute   name="tcpSelectorTimeout"
+          description="tcp listener Selector timeout"
+                 type="long"/>
+    <attribute   name="nrOfMsgsReceived"
+          description="number of messages received from other nodes"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="receivedTime"
+          description="total time message send time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="receivedProcessingTime"
+          description="received processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="minReceivedProcessingTime"
+          description="minimal received processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="avgReceivedProcessingTime"
+          description="received processing time / nrOfRequests"
+                 type="double"
+                 writeable="false"/>
+    <attribute   name="maxReceivedProcessingTime"
+          description="maximal received processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="doReceivedProcessingStats"
+          description="create received processing time stats"
+			     is="true"
+                 type="boolean" />                
+    <attribute   name="avgTotalReceivedBytes"
+          description="received totalReceivedBytes / nrOfMsgsReceived"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="totalReceivedBytes"
+          description="number of bytes received"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="sendAck"
+          description="send ack after data received"
+			     is="true"
+                 type="boolean"
+                 writeable="false" />
+    <attribute   name="compress"
+          description="data received compressed"
+			     is="true"
+                 type="boolean"
+                 writeable="false" />
+    <attribute   name="doListen"
+          description="is port really started"
+			     is="true"
+                 type="boolean"
+                 writeable="false" />
+                 
+	<operation name="resetStatistics"
+               description="Reset all statistics"
+               impact="ACTION"
+               returnType="void">
+    </operation>	
+
+    <operation   name="start"
+               description="Start the cluster"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="stop"
+               description="Stop the cluster"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+                 
+  </mbean>
+
+  <mbean         name="SocketReplicationListener"
+           description="Tcp Cluster SocketReplicationListener implementation"
+               domain="Catalina"
+                group="Cluster"
+                 type="org.apache.catalina.cluster.tcp.SocketReplicationListener">
+    <attribute   name="info"
+          description="Class version info"
+                 type="java.lang.String"
+                 writeable="false"/>
+    <attribute   name="tcpListenAddress"
+          description="tcp listener address"
+                 type="java.lang.String"/>
+    <attribute   name="tcpListenPort"
+          description="tcp listener port"
+                 type="int"/>
+    <attribute   name="tcpListenMaxPort"
+          description="max tcp listen used port"
+                 type="int"/>
+    <attribute   name="tcpListenTimeout"
+          description="max tcp listen timeout (sec) wait for ServerSocket start"
+                 type="int"/>                
+    <attribute   name="nrOfMsgsReceived"
+          description="number of messages received from other nodes"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="receivedTime"
+          description="total time message send time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="receivedProcessingTime"
+          description="received processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="minReceivedProcessingTime"
+          description="minimal received processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="avgReceivedProcessingTime"
+          description="received processing time / nrOfRequests"
+                 type="double"
+                 writeable="false"/>
+    <attribute   name="maxReceivedProcessingTime"
+          description="maximal received processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="doReceivedProcessingStats"
+          description="create received processing time stats"
+			     is="true"
+                 type="boolean" />                
+    <attribute   name="avgTotalReceivedBytes"
+          description="received totalReceivedBytes / nrOfMsgsReceived"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="totalReceivedBytes"
+          description="number of bytes received"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="sendAck"
+          description="send ack after data received"
+			     is="true"
+                 type="boolean"
+                 writeable="false" />
+   <attribute   name="compress"
+          description="data received compressed"
+			     is="true"
+                 type="boolean"
+                 writeable="false" />
+   <attribute   name="doListen"
+          description="is port really started"
+			     is="true"
+                 type="boolean"
+                 writeable="false" />
+                 
+	<operation name="resetStatistics"
+               description="Reset all statistics"
+               impact="ACTION"
+               returnType="void">
+    </operation>	
+
+    <operation   name="start"
+               description="Start the cluster"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="stop"
+               description="Stop the cluster"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+                 
+  </mbean>
+  
+  <mbean         name="ReplicationTransmitter"
+          description="Tcp replicatio transmitter"
+               domain="Catalina"
+                group="ClusterSender"
+                 type="org.apache.catalina.cluster.tcp.ReplicationTransmitter">
+    <attribute   name="info"
+          description="Class version info"
+                 type="java.lang.String"
+                 writeable="false"/>
+    <attribute   name="replicationMode"
+          description="replication mode (synchnous,pooled.asynchnous,fastasyncqueue)"
+                 type="java.lang.String"/>
+    <attribute   name="ackTimeout"
+          description="acknowledge timeout"
+                 type="long"/>
+    <attribute   name="autoConnect"
+          description="is sender disabled, fork a new socket"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="waitForAck"
+          description="Wait for ack after data send"
+			     is="true"
+                 type="boolean"
+                 writeable="false" />
+    <attribute   name="processingTime"
+          description="sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="minProcessingTime"
+          description="minimal sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="avgProcessingTime"
+          description="processing time / nrOfRequests"
+                 type="double"
+                 writeable="false"/>
+    <attribute   name="maxProcessingTime"
+          description="maximal sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="doTransmitterProcessingStats"
+          description="create processing time stats"
+			     is="true"
+                 type="boolean" />                
+    <attribute   name="nrOfRequests"
+          description="number of send messages to other members"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="totalBytes"
+          description="number of bytes transfered"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="failureCounter"
+          description="number of wrong transfers"
+                 type="long"
+                 writeable="false"/>
+	<attribute name="senderObjectNames"
+               description="get all sender object names"
+               type="[Ljavax.management.ObjectName;"
+               writeable="false"/>
+    <operation   name="start"
+               description="Start the cluster"
+               impact="ACTION"
+               returnType="void">
+    </operation>    
+    <operation name="stop"
+               description="Stop the cluster"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="resetStatistics"
+               description="Reset all statistics"
+               impact="ACTION"
+               returnType="void">
+    </operation>	
+	<operation name="checkKeepAlive"
+               description="Check all sender connection for close socket (keepalive)"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+  </mbean>
+
+  <mbean         name="AsyncSocketSender"
+          description="Async Cluster Sender"
+               domain="Catalina"
+                group="IDataSender"
+                 type="org.apache.catalina.cluster.tcp.AsyncSocketSender">
+    <attribute   name="info"
+          description="Class version info"
+                 type="java.lang.String"
+                 writeable="false"/>
+    <attribute   name="address"
+          description="sender ip address"
+                 type="java.net.InetAddress"
+                 writeable="false"/>
+    <attribute   name="port"
+          description="sender port"
+                 type="int"
+                 writeable="false" />
+    <attribute   name="suspect"
+          description="Socket is gone"
+                 type="boolean"/>
+    <attribute   name="waitForAck"
+          description="Wait for ack after data send"
+			     is="true"
+                 type="boolean"
+                 writeable="false"/>
+    <attribute   name="ackTimeout"
+          description="acknowledge timeout"
+                 type="long"/>                 
+    <attribute   name="avgMessageSize"
+                 writeable="false"
+          description="avg message size (totalbytes/nrOfRequests"
+                 type="long"/>
+    <attribute   name="queueSize"
+                 writeable="false"
+          description="queue size"
+                 type="int"/>
+    <attribute   name="queuedNrOfBytes"
+                 writeable="false"
+          description="number of bytes over all queued messages"
+                 type="long"/>
+    <attribute   name="keepAliveTimeout"
+          description="active socket keep alive timeout"
+                 type="long"/>
+    <attribute   name="keepAliveMaxRequestCount"
+          description="max request over this socket"
+                 type="int"/>
+    <attribute   name="keepAliveCount"
+          description="keep Alive request count"
+                 type="int"
+                 writeable="false"/>
+    <attribute   name="keepAliveConnectTime"
+          description="Connect time for keep alive"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="resend"
+          description="after send failure make a resend"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="connected"
+                 is="true"
+          description="socket connected"
+                 type="boolean"
+                 writeable="false"/>
+    <attribute   name="nrOfRequests"
+          description="number of send messages to other members"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="totalBytes"
+          description="number of bytes transfered"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="processingTime"
+          description="sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="minProcessingTime"
+          description="minimal sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="avgProcessingTime"
+          description="processing time / nrOfRequests"
+                 type="double"
+                 writeable="false"/>
+    <attribute   name="maxProcessingTime"
+          description="maximal sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="doProcessingStats"
+          description="create processing time stats"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="waitAckTime"
+          description="sending waitAck time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="minWaitAckTime"
+          description="minimal sending waitAck time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="avgWaitAckTime"
+          description="waitAck time / nrOfRequests"
+                 type="double"
+                 writeable="false"/>
+    <attribute   name="maxWaitAckTime"
+          description="maximal sending waitAck time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="doWaitAckStats"
+          description="create waitAck time stats"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="connectCounter"
+          description="counts connects"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="disconnectCounter"
+          description="counts disconnects"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="socketOpenCounter"
+          description="counts open socket (KeepAlive and connects)"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="socketOpenFailureCounter"
+          description="counts open socket failures"
+                 type="long"
+                 writeable="false"/>                 				 
+    <attribute   name="socketCloseCounter"
+          description="counts closed socket (KeepAlive and disconnects)"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="missingAckCounter"
+          description="counts missing ack"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="dataResendCounter"
+          description="counts data resends"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="dataFailureCounter"
+          description="counts data send failures"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="inQueueCounter"
+          description="counts all queued messages"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="outQueueCounter"
+          description="counts all successfully sended messages"
+                 type="long"
+                 writeable="false"/>
+	<operation name="connect"
+               description="connect to other replication node"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="disconnect"
+               description="disconnect to other replication node"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="checkKeepAlive"
+               description="Check connection for close socket"
+               impact="ACTION"
+               returnType="boolean">
+    </operation>
+	<operation name="resetStatistics"
+               description="Reset all statistics"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+				 
+  </mbean>
+
+ <mbean         name="FastAsyncSocketSender"
+          description="Fast Async Cluster Sender"
+               domain="Catalina"
+                group="IDataSender"
+                 type="org.apache.catalina.cluster.tcp.FastAsyncSocketSender">
+    <attribute   name="info"
+          description="Class version info"
+                 type="java.lang.String"
+                 writeable="false"/>
+    <attribute   name="threadPriority"
+          description="change queue thread priority"
+                 type="int"/>                 
+    <attribute   name="address"
+          description="sender ip address"
+                 type="java.net.InetAddress"
+                 writeable="false"/>
+    <attribute   name="port"
+          description="sender port"
+                 type="int"
+                 writeable="false" />
+    <attribute   name="suspect"
+          description="Socket is gone"
+                 type="boolean"/>
+    <attribute   name="waitForAck"
+          description="Wait for ack after data send"
+			     is="true"
+                 type="boolean"
+                 writeable="false"/>
+    <attribute   name="ackTimeout"
+          description="acknowledge timeout"
+                 type="long"/>
+    <attribute   name="avgMessageSize"
+                 writeable="false"
+          description="avg message size (totalbytes/nrOfRequests"
+                 type="long" />
+    <attribute   name="queueSize"
+                 writeable="false"
+          description="queue size"
+                 type="int"/>
+    <attribute   name="queuedNrOfBytes"
+                 writeable="false"
+          description="number of bytes over all queued messages"
+                 type="long"/>
+    <attribute   name="keepAliveTimeout"
+          description="active socket keep alive timeout"
+                 type="long"/>
+    <attribute   name="keepAliveMaxRequestCount"
+          description="max request over this socket"
+                 type="int"/>
+    <attribute   name="queueAddWaitTimeout"
+          description="add wait timeout (default 10000 msec)"
+                 type="long"/>
+    <attribute   name="queueRemoveWaitTimeout"
+          description="remove wait timeout (default 30000 msec)"
+                 type="long"/>
+    <attribute   name="maxQueueLength"
+          description="max queue length"
+                 type="int"/>
+    <attribute   name="queueTimeWait"
+          description="remember queue wait times"
+                 is="true"
+                 type="boolean"/>
+    <attribute   name="queueCheckLock"
+          description="check to lost locks"
+                 is="true"
+                 type="boolean"/>
+    <attribute   name="queueDoStats"
+          description="activated queue stats"
+                 is="true"
+                 type="boolean"/>
+    <attribute   name="keepAliveCount"
+          description="keep Alive request count"
+                 type="int"
+                 writeable="false"/>
+    <attribute   name="keepAliveConnectTime"
+          description="Connect time for keep alive"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="resend"
+          description="after send failure make a resend"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="connected"
+                 is="true"
+          description="socket connected"
+                 type="boolean"
+                 writeable="false"/>
+    <attribute   name="nrOfRequests"
+          description="number of send messages to other members"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="totalBytes"
+          description="number of bytes transfered"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="processingTime"
+          description="sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="minProcessingTime"
+          description="minimal sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="avgProcessingTime"
+          description="processing time / nrOfRequests"
+                 type="double"
+                 writeable="false"/>
+    <attribute   name="maxProcessingTime"
+          description="maximal sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="doProcessingStats"
+          description="create Processing time stats"
+			     is="true"
+                 type="boolean" />                 
+    <attribute   name="waitAckTime"
+          description="sending waitAck time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="minWaitAckTime"
+          description="minimal sending waitAck time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="avgWaitAckTime"
+          description="waitAck time / nrOfRequests"
+                 type="double"
+                 writeable="false"/>
+    <attribute   name="maxWaitAckTime"
+          description="maximal sending waitAck time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="doWaitAckStats"
+          description="create waitAck time stats"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="connectCounter"
+          description="counts connects"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="disconnectCounter"
+          description="counts disconnects"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="socketOpenCounter"
+          description="counts open socket (KeepAlive and connects)"
+                 type="long"
+                 writeable="false"/>				 
+    <attribute   name="socketOpenFailureCounter"
+          description="counts open socket failures"
+                 type="long"
+                 writeable="false"/>                 				 
+    <attribute   name="socketCloseCounter"
+          description="counts closed socket (KeepAlive and disconnects)"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="missingAckCounter"
+          description="counts missing ack"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="dataResendCounter"
+          description="counts data resends"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="dataFailureCounter"
+          description="counts data send failures"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="inQueueCounter"
+          description="counts all queued messages"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="outQueueCounter"
+          description="counts all successfully sended messages"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="queueAddWaitTime"
+          description="queue add wait time (tomcat thread waits)"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="queueRemoveWaitTime"
+          description="queue remove wait time (queue thread waits)"
+                 type="long"
+                 writeable="false"/>
+ 	<operation name="connect"
+               description="connect to other replication node"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="disconnect"
+               description="disconnect to other replication node"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="checkKeepAlive"
+               description="Check connection for close socket"
+               impact="ACTION"
+               returnType="boolean">
+    </operation>
+	<operation name="resetStatistics"
+               description="Reset all statistics"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+				 
+  </mbean>
+
+  <mbean         name="PooledSocketSender"
+          description="Pooled Cluster Sender"
+               domain="Catalina"
+                group="IDataSender"
+                 type="org.apache.catalina.cluster.tcp.PooledSocketSender">
+    <attribute   name="address"
+          description="sender ip address"
+                 type="java.net.InetAddress"
+                 writeable="false"/>
+    <attribute   name="port"
+          description="sender port"
+                 type="int"
+                 writeable="false" />
+    <attribute   name="suspect"
+          description="Socket is gone"
+                 type="boolean"/>
+    <attribute   name="ackTimeout"
+          description="acknowledge timeout"
+                 type="long"/>
+    <attribute   name="waitForAck"
+          description="Wait for ack after data send"
+			     is="true"
+                 type="boolean"
+                 writeable="false" />
+    <attribute   name="maxPoolSocketLimit"
+          description="Max parallel sockets"
+                 type="int"/>
+    <attribute   name="keepAliveTimeout"
+          description="active socket keep alive timeout"
+                 type="long"/>
+    <attribute   name="keepAliveMaxRequestCount"
+          description="max request over this socket"
+                 type="int"/>
+    <attribute   name="resend"
+          description="after send failure make a resend"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="connected"
+                 is="true"
+          description="socket connected"
+                 type="boolean"
+                 writeable="false"/>
+    <attribute   name="avgMessageSize"
+                 writeable="false"
+          description="avg message size (totalbytes/nrOfRequests"
+                 type="long"/>
+    <attribute   name="nrOfRequests"
+          description="number of send messages to other members"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="totalBytes"
+          description="number of bytes transfered"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="connectCounter"
+          description="counts connects"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="disconnectCounter"
+          description="counts disconnects"
+                 type="long"
+                 writeable="false"/>
+	<operation name="connect"
+               description="start Queue to connect to ohter replication node"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="disconnect"
+               description="stop Queue to other replication node"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="resetStatistics"
+               description="Reset all statistics"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+				 
+  </mbean>
+
+  <mbean         name="SocketSender"
+          description="Sync Cluster Sender"
+               domain="Catalina"
+                group="IDataSender"
+                 type="org.apache.catalina.cluster.tcp.SocketSender">
+    <attribute   name="address"
+          description="sender ip address"
+                 type="java.net.InetAddress"
+                 writeable="false"/>
+    <attribute   name="port"
+          description="sender port"
+                 type="int"
+                 writeable="false" />
+    <attribute   name="suspect"
+          description="Socket is gone"
+                 type="boolean"/>
+    <attribute   name="ackTimeout"
+          description="acknowledge timeout"
+                 type="long"/>
+    <attribute   name="waitForAck"
+          description="Wait for ack after data send"
+			     is="true"
+                 type="boolean"
+                 writeable="false" />
+    <attribute   name="keepAliveTimeout"
+          description="active socket keep alive timeout"
+                 type="long"/>
+    <attribute   name="keepAliveMaxRequestCount"
+          description="max request over this socket"
+                 type="int"/>
+    <attribute   name="keepAliveCount"
+          description="keep Alive request count"
+                 type="int"
+                 writeable="false"/>
+    <attribute   name="keepAliveConnectTime"
+          description="Connect time for keep alive"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="resend"
+          description="after send failure make a resend"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="connected"
+                 is="true"
+          description="socket connected"
+                 type="boolean"
+                 writeable="false"/>
+    <attribute   name="avgMessageSize"
+                 writeable="false"
+          description="avg message size (totalbytes/nrOfRequests"
+                 type="long"/>
+    <attribute   name="nrOfRequests"
+          description="number of send messages to other members"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="totalBytes"
+          description="number of bytes transfered"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="processingTime"
+          description="sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="minProcessingTime"
+          description="minimal sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="avgProcessingTime"
+          description="processing time / nrOfRequests"
+                 type="double"
+                 writeable="false"/>
+    <attribute   name="maxProcessingTime"
+          description="maximal sending processing time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="doProcessingStats"
+          description="create Processing time stats"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="waitAckTime"
+          description="sending waitAck time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="minWaitAckTime"
+          description="minimal sending waitAck time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="avgWaitAckTime"
+          description="waitAck time / nrOfRequests"
+                 type="double"
+                 writeable="false"/>
+    <attribute   name="maxWaitAckTime"
+          description="maximal sending waitAck time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="doWaitAckStats"
+          description="create waitAck time stats"
+			     is="true"
+                 type="boolean" />
+    <attribute   name="connectCounter"
+          description="counts connects"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="disconnectCounter"
+          description="counts disconnects"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="socketCloseCounter"
+          description="counts closed socket (KeepAlive and disconnects)"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="socketOpenFailureCounter"
+          description="counts open socket failures"
+                 type="long"
+                 writeable="false"/>                 				 
+    <attribute   name="socketOpenCounter"
+          description="counts open socket (KeepAlive and connects)"
+                 type="long"
+                 writeable="false"/>				 
+    <attribute   name="missingAckCounter"
+          description="counts missing ack"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="dataResendCounter"
+          description="counts data resends"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="dataFailureCounter"
+          description="counts data send failures"
+                 type="long"
+                 writeable="false"/>
+	<operation name="connect"
+               description="connect to other replication node"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="disconnect"
+               description="disconnect to other replication node"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+	<operation name="checkKeepAlive"
+               description="Check connection for close socket"
+               impact="ACTION"
+               returnType="boolean">
+    </operation>
+	<operation name="resetStatistics"
+               description="Reset all statistics"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+				 
+  </mbean>
+    
+  <mbean         name="ReplicationValve"
+          description="Valve for simple tcp replication"
+               domain="Catalina"
+                group="Valve"
+                 type="org.apache.catalina.cluster.tcp.ReplicationValve">
+    <attribute   name="filter"
+          description="resource filter to disable session replication check"
+                 type="java.lang.String"/>
+    <attribute   name="primaryIndicator"
+ 			     is="true"
+          description="set indicator that request processing is at primary session node"
+                 type="boolean"/>
+    <attribute   name="primaryIndicatorName"
+          description="Request attribute name to indicate that request processing is at primary session node"
+                 type="java.lang.String"/>
+	<attribute   name="nrOfRequests"
+          description="number of replicated requests"
+                 type="long"
+                 writeable="false"/>
+	<attribute   name="nrOfFilterRequests"
+          description="number of filtered requests"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="totalRequestTime"
+          description="total replicated request time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="totalSendTime"
+          description="total replicated send time"
+                 type="long"
+                 writeable="false"/>
+    <attribute   name="lastSendTime"
+          description="last replicated request time"
+                 type="long"
+                 writeable="false"/>
+    <operation name="resetStatistics"
+               description="Reset all statistics"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+                 
+  </mbean>
+
+
+</mbeans-descriptors>
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/util/FastQueue.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/FastQueue.java
new file mode 100644
index 0000000..7e7841b
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/FastQueue.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.util;
+
+/**
+ * A fast queue that remover thread lock the adder thread. <br/>Limit the queue
+ * length when you have strange producer thread problemes.
+ * 
+ * FIXME add i18n support to log messages
+ * @author Rainer Jung
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class FastQueue implements IQueue {
+
+    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(FastQueue.class);
+
+    /**
+     * This is the actual queue
+     */
+    private SingleRemoveSynchronizedAddLock lock = null;
+
+    /**
+     * First Object at queue (consumer message)
+     */
+    private LinkObject first = null;
+
+    /**
+     * Last object in queue (producer Object)
+     */
+    private LinkObject last = null;
+
+    /**
+     * Current Queue elements size
+     */
+    private int size = 0;
+
+    /**
+     * check lock to detect strange threadings things
+     */
+    private boolean checkLock = false;
+
+    /**
+     * protocol the thread wait times
+     */
+    private boolean timeWait = false;
+
+    /**
+     * calc stats data
+     */
+    private boolean doStats = false;
+
+    /**
+     *  
+     */
+    private boolean inAdd = false;
+
+    /**
+     *  
+     */
+    private boolean inRemove = false;
+
+    /**
+     *  
+     */
+    private boolean inMutex = false;
+
+    /**
+     * limit the queue legnth ( default is unlimited)
+     */
+    private int maxQueueLength = 0;
+
+    /**
+     * addWaitTimeout for producer
+     */
+    private long addWaitTimeout = 10000L;
+
+    
+    /**
+     * removeWaitTimeout for consumer
+     */
+    private long removeWaitTimeout = 30000L;
+
+    /**
+     * enabled the queue
+     */
+    private boolean enabled = true;
+
+    /**
+     * calc all add objects
+     */
+    private long addCounter = 0;
+
+    /**
+     * calc all add objetcs in error state ( see limit queue length)
+     */
+    private long addErrorCounter = 0;
+
+    /**
+     * calc all remove objects
+     */
+    private long removeCounter = 0;
+
+    /**
+     * calc all remove objects failures (hupps probleme detection)
+     */
+    private long removeErrorCounter = 0;
+
+    /**
+     * Calc wait time thread
+     */
+    private long addWait = 0;
+
+    /**
+     * Calc remove time threads
+     */
+    private long removeWait = 0;
+
+    /**
+     *  max queue size
+     */
+    private int maxSize = 0;
+
+    /**
+     * avg queue size
+     */
+    private long avgSize = 0;
+
+    /*
+     *  
+     */
+    private int maxSizeSample = 0;
+
+    /*
+     *  
+     */
+    private long avgSizeSample = 0;
+
+    /**
+     *  avg size sample interval
+     */
+    private int sampleInterval = 100;
+
+    /**
+     * Generate Queue SingleRemoveSynchronizedAddLock and set add and wait
+     * Timeouts
+     */
+    public FastQueue() {
+        lock = new SingleRemoveSynchronizedAddLock();
+        lock.setAddWaitTimeout(addWaitTimeout);
+        lock.setRemoveWaitTimeout(removeWaitTimeout);
+    }
+
+    /**
+     * get current add wait timeout
+     * 
+     * @return current wait timeout
+     */
+    public long getAddWaitTimeout() {
+        addWaitTimeout = lock.getAddWaitTimeout();
+        return addWaitTimeout;
+    }
+
+    /**
+     * Set add wait timeout (default 10000 msec)
+     * 
+     * @param timeout
+     */
+    public void setAddWaitTimeout(long timeout) {
+        addWaitTimeout = timeout;
+        lock.setAddWaitTimeout(addWaitTimeout);
+    }
+
+    /**
+     * get current remove wait timeout
+     * 
+     * @return The timeout
+     */
+    public long getRemoveWaitTimeout() {
+        removeWaitTimeout = lock.getRemoveWaitTimeout();
+        return removeWaitTimeout;
+    }
+
+    /**
+     * set remove wait timeout ( default 30000 msec)
+     * 
+     * @param timeout
+     */
+    public void setRemoveWaitTimeout(long timeout) {
+        removeWaitTimeout = timeout;
+        lock.setRemoveWaitTimeout(removeWaitTimeout);
+    }
+
+    /*
+     * get Max Queue length
+     * 
+     * @see org.apache.catalina.cluster.util.IQueue#getMaxQueueLength()
+     */
+    public int getMaxQueueLength() {
+        return maxQueueLength;
+    }
+
+    public void setMaxQueueLength(int length) {
+        maxQueueLength = length;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enable) {
+        enabled = enable;
+        if (!enabled) {
+            lock.abortRemove();
+        }
+    }
+
+    /*
+     * @return Returns the checkLock.
+     */
+    public boolean isCheckLock() {
+        return checkLock;
+    }
+
+    /*
+     * @param checkLock The checkLock to set.
+     */
+    public void setCheckLock(boolean checkLock) {
+        this.checkLock = checkLock;
+    }
+
+    /*
+     * @return Returns the doStats.
+     */
+    public boolean isDoStats() {
+        return doStats;
+    }
+
+    /*
+     * @param doStats The doStats to set.
+     */
+    public void setDoStats(boolean doStats) {
+        this.doStats = doStats;
+    }
+
+    /*
+     * @return Returns the timeWait.
+     */
+    public boolean isTimeWait() {
+        return timeWait;
+    }
+
+    /*
+     * @param timeWait The timeWait to set.
+     */
+    public void setTimeWait(boolean timeWait) {
+        this.timeWait = timeWait;
+    }
+
+    public int getSampleInterval() {
+        return sampleInterval;
+    }
+
+    public void setSampleInterval(int interval) {
+        sampleInterval = interval;
+    }
+
+    public long getAddCounter() {
+        return addCounter;
+    }
+
+    public void setAddCounter(long counter) {
+        addCounter = counter;
+    }
+
+    public long getAddErrorCounter() {
+        return addErrorCounter;
+    }
+
+    public void setAddErrorCounter(long counter) {
+        addErrorCounter = counter;
+    }
+
+    public long getRemoveCounter() {
+        return removeCounter;
+    }
+
+    public void setRemoveCounter(long counter) {
+        removeCounter = counter;
+    }
+
+    public long getRemoveErrorCounter() {
+        return removeErrorCounter;
+    }
+
+    public void setRemoveErrorCounter(long counter) {
+        removeErrorCounter = counter;
+    }
+
+    public long getAddWait() {
+        return addWait;
+    }
+
+    public void setAddWait(long wait) {
+        addWait = wait;
+    }
+
+    public long getRemoveWait() {
+        return removeWait;
+    }
+
+    public void setRemoveWait(long wait) {
+        removeWait = wait;
+    }
+
+    /**
+     * @return The max size
+     */
+    public int getMaxSize() {
+        return maxSize;
+    }
+
+    /**
+     * @param size
+     */
+    public void setMaxSize(int size) {
+        maxSize = size;
+    }
+
+    
+    /**
+     * Avg queue size
+     * @return The average queue size
+     */
+    public long getAvgSize() {
+        if (addCounter > 0) {
+            return avgSize / addCounter;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * reset all stats data 
+     */
+    public void resetStatistics() {
+        addCounter = 0;
+        addErrorCounter = 0;
+        removeCounter = 0;
+        removeErrorCounter = 0;
+        avgSize = 0;
+        maxSize = 0;
+        addWait = 0;
+        removeWait = 0;
+    }
+
+    /**
+     * unlock queue for next add 
+     */
+    public void unlockAdd() {
+        lock.unlockAdd(size > 0 ? true : false);
+    }
+
+    /**
+     * unlock queue for next remove 
+     */
+    public void unlockRemove() {
+        lock.unlockRemove();
+    }
+
+    /**
+     * start queuing
+     */
+    public void start() {
+        setEnabled(true);
+    }
+
+    /**
+     * start queuing
+     */
+    public void stop() {
+        setEnabled(false);
+    }
+
+    public long getSample() {
+        return addCounter % sampleInterval;
+    }
+
+    public int getMaxSizeSample() {
+        return maxSizeSample;
+    }
+
+    public void setMaxSizeSample(int size) {
+        maxSizeSample = size;
+    }
+
+    public long getAvgSizeSample() {
+        long sample = addCounter % sampleInterval;
+        if (sample > 0) {
+            return avgSizeSample / sample;
+        } else if (addCounter > 0) {
+            return avgSizeSample / sampleInterval;
+        } else {
+            return 0;
+        }
+    }
+
+    public int getSize() {
+        int sz;
+        sz = size;
+        return sz;
+    }
+
+    /* Add new data to the queue
+     * @see org.apache.catalina.cluster.util.IQueue#add(java.lang.String, java.lang.Object)
+     * FIXME extract some method
+     */
+    public boolean add(String key, Object data) {
+        boolean ok = true;
+        long time = 0;
+
+        if (!enabled) {
+            if (log.isInfoEnabled())
+                log.info("FastQueue: queue disabled, add aborted");
+            return false;
+        }
+
+        if (timeWait) {
+            time = System.currentTimeMillis();
+        }
+        lock.lockAdd();
+        try {
+            if (timeWait) {
+                addWait += (System.currentTimeMillis() - time);
+            }
+
+            if (log.isTraceEnabled()) {
+                log.trace("FastQueue: add starting with size " + size);
+            }
+            if (checkLock) {
+                if (inAdd)
+                    log.warn("FastQueue.add: Detected other add");
+                inAdd = true;
+                if (inMutex)
+                    log.warn("FastQueue.add: Detected other mutex in add");
+                inMutex = true;
+            }
+
+            if ((maxQueueLength > 0) && (size >= maxQueueLength)) {
+                ok = false;
+                if (log.isTraceEnabled()) {
+                    log.trace("FastQueue: Could not add, since queue is full ("
+                            + size + ">=" + maxQueueLength + ")");
+                }
+
+            } else {
+                LinkObject element = new LinkObject(key, data);
+                if (size == 0) {
+                    first = last = element;
+                    size = 1;
+                } else {
+                    if (last == null) {
+                        ok = false;
+                        log
+                                .error("FastQueue: Could not add, since last is null although size is "
+                                        + size + " (>0)");
+                    } else {
+                        last.append(element);
+                        last = element;
+                        size++;
+                    }
+                }
+
+            }
+
+            if (doStats) {
+                if (ok) {
+                    if (addCounter % sampleInterval == 0) {
+                        maxSizeSample = 0;
+                        avgSizeSample = 0;
+                    }
+                    addCounter++;
+                    if (size > maxSize) {
+                        maxSize = size;
+                    }
+                    if (size > maxSizeSample) {
+                        maxSizeSample = size;
+                    }
+                    avgSize += size;
+                    avgSizeSample += size;
+                } else {
+                    addErrorCounter++;
+                }
+            }
+
+            if (first == null) {
+                log.error("FastQueue: first is null, size is " + size
+                        + " at end of add");
+            }
+            if (last == null) {
+                log.error("FastQueue: last is null, size is " + size
+                        + " at end of add");
+            }
+
+            if (checkLock) {
+                if (!inMutex)
+                    log.warn("FastQueue: Cancelled by other mutex in add");
+                inMutex = false;
+                if (!inAdd)
+                    log.warn("FastQueue: Cancelled by other add");
+                inAdd = false;
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("FastQueue: add ending with size " + size);
+            }
+
+            if (timeWait) {
+                time = System.currentTimeMillis();
+            }
+        } finally {
+            lock.unlockAdd(true);
+        }
+        if (timeWait) {
+            addWait += (System.currentTimeMillis() - time);
+        }
+        return ok;
+    }
+
+    /* remove the complete queued object list
+     * @see org.apache.catalina.cluster.util.IQueue#remove()
+     * FIXME extract some method
+     */
+    public LinkObject remove() {
+        LinkObject element;
+        boolean gotLock;
+        long time = 0;
+
+        if (!enabled) {
+            if (log.isInfoEnabled())
+                log.info("FastQueue: queue disabled, remove aborted");
+            return null;
+        }
+
+        if (timeWait) {
+            time = System.currentTimeMillis();
+        }
+        gotLock = lock.lockRemove();
+        try {
+
+            if (!gotLock) {
+                if (enabled) {
+                    if (timeWait) {
+                        removeWait += (System.currentTimeMillis() - time);
+                    }
+                    if (doStats) {
+                        removeErrorCounter++;
+                    }
+                    if (log.isInfoEnabled())
+                        log
+                                .info("FastQueue: Remove aborted although queue enabled");
+                } else {
+                    if (log.isInfoEnabled())
+                        log.info("FastQueue: queue disabled, remove aborted");
+                }
+                return null;
+            }
+
+            if (timeWait) {
+                removeWait += (System.currentTimeMillis() - time);
+            }
+
+            if (log.isTraceEnabled()) {
+                log.trace("FastQueue: remove starting with size " + size);
+            }
+            if (checkLock) {
+                if (inRemove)
+                    log.warn("FastQueue: Detected other remove");
+                inRemove = true;
+                if (inMutex)
+                    log.warn("FastQueue: Detected other mutex in remove");
+                inMutex = true;
+            }
+
+            element = first;
+
+            if (doStats) {
+                if (element != null) {
+                    removeCounter++;
+                } else {
+                    removeErrorCounter++;
+                    log
+                            .error("FastQueue: Could not remove, since first is null although size is "
+                                    + size + " (>0)");
+                }
+            }
+
+            first = last = null;
+            size = 0;
+
+            if (checkLock) {
+                if (!inMutex)
+                    log.warn("FastQueue: Cancelled by other mutex in remove");
+                inMutex = false;
+                if (!inRemove)
+                    log.warn("FastQueue: Cancelled by other remove");
+                inRemove = false;
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("FastQueue: remove ending with size " + size);
+            }
+
+            if (timeWait) {
+                time = System.currentTimeMillis();
+            }
+        } finally {
+            lock.unlockRemove();
+        }
+        if (timeWait) {
+            removeWait += (System.currentTimeMillis() - time);
+        }
+        return element;
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/util/IDynamicProperty.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/IDynamicProperty.java
new file mode 100644
index 0000000..199a4b0
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/IDynamicProperty.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.util;
+
+import java.util.Iterator;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+
+public interface IDynamicProperty {
+
+    /**
+     * set config attributes with reflect
+     * 
+     * @param name
+     * @param value
+     */
+    public void setProperty(String name, Object value) ;
+
+    /**
+     * get current config
+     * 
+     * @param key
+     * @return The property
+     */
+    public Object getProperty(String key) ;
+    /**
+     * Get all properties keys
+     * 
+     * @return An iterator over the property names
+     */
+    public Iterator getPropertyNames() ;
+
+    /**
+     * remove a configured property.
+     * 
+     * @param key
+     */
+    public void removeProperty(String key) ;
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/util/IQueue.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/IQueue.java
new file mode 100644
index 0000000..e331d92
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/IQueue.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.util;
+
+/**
+ * A queue interface<BR>
+ *
+ * @author Rainer Jung
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public interface IQueue {
+
+    public LinkObject remove();
+    public boolean add(String key,Object data);
+    public int getMaxQueueLength();
+    public void setMaxQueueLength(int length);
+    public void start();
+    public void stop();
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/util/LinkObject.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/LinkObject.java
new file mode 100644
index 0000000..885e07b
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/LinkObject.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.util;
+
+/**
+ * The class <b>LinkObject</b> implements an element
+ * for a linked list, consisting of a general
+ * data object and a pointer to the next element.
+ *
+ * @author Rainer Jung
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+
+ */
+
+public class LinkObject {
+
+    private Object payload;
+    private LinkObject next;
+    private String key ;
+    
+    /**
+     * Construct a new element from the data object.
+     * Sets the pointer to null.
+     *
+     * @param key The key
+     * @param payload The data object.
+     */
+    public LinkObject(String key,Object payload) {
+        this.payload = payload;
+        this.next = null;
+        this.key = key ;
+    }
+
+    /**
+     * Set the next element.
+     * @param next The next element.
+     */
+    public void append(LinkObject next) {
+        this.next = next;
+    }
+
+    /**
+     * Get the next element.
+     * @return The next element.
+     */
+    public LinkObject next() {
+        return next;
+    }
+
+    /**
+     * Get the data object from the element.
+     * @return The data object from the element.
+     */
+    public Object data() {
+        return payload;
+    }
+
+    /**
+     * Get the unique message id
+     * @return the unique message id
+     */
+    public Object getKey() {
+        return key;
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/util/SingleRemoveSynchronizedAddLock.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/SingleRemoveSynchronizedAddLock.java
new file mode 100644
index 0000000..fc11378
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/SingleRemoveSynchronizedAddLock.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.util;
+
+/**
+ * The class <b>SingleRemoveSynchronizedAddLock</b> implement locking for accessing the queue
+ * by a single remove thread and multiple add threads.
+ *
+ * A thread is only allowed to be either the remove or
+ * an add thread.
+ *
+ * The lock can either be owned by the remove thread
+ * or by a single add thread.
+ *
+ * If the remove thread tries to get the lock,
+ * but the queue is empty, it will block (poll)
+ * until an add threads adds an entry to the queue and
+ * releases the lock.
+ * 
+ * If the remove thread and add threads compete for
+ * the lock and an add thread releases the lock, then
+ * the remove thread will get the lock first.
+ *
+ * The remove thread removes all entries in the queue
+ * at once and proceeses them without further
+ * polling the queue.
+ *
+ * The lock is not reentrant, in the sense, that all
+ * threads must release an owned lock before competing
+ * for the lock again!
+ *
+ * @author Rainer Jung
+ * @author Peter Rossbach
+ * @version 1.1
+ */
+ 
+public class SingleRemoveSynchronizedAddLock {
+    
+    public SingleRemoveSynchronizedAddLock() {
+    }
+    
+    public SingleRemoveSynchronizedAddLock(boolean dataAvailable) {
+        this.dataAvailable=dataAvailable;
+    }
+    
+    /**
+     * Time in milliseconds after which threads
+     * waiting for an add lock are woken up.
+     * This is used as a safety measure in case
+     * thread notification via the unlock methods
+     * has a bug.
+     */
+    private long addWaitTimeout = 10000L;
+
+    /**
+     * Time in milliseconds after which threads
+     * waiting for a remove lock are woken up.
+     * This is used as a safety measure in case
+     * thread notification via the unlock methods
+     * has a bug.
+     */
+    private long removeWaitTimeout = 30000L;
+
+    /**
+     * The current remove thread.
+     * It is set to the remove thread polling for entries.
+     * It is reset to null when the remove thread
+     * releases the lock and proceeds processing
+     * the removed entries.
+     */
+    private Thread remover = null;
+
+    /**
+     * A flag indicating, if an add thread owns the lock.
+     */
+    private boolean addLocked = false;
+
+    /**
+     * A flag indicating, if the remove thread owns the lock.
+     */
+    private boolean removeLocked = false;
+
+    /**
+     * A flag indicating, if the remove thread is allowed
+     * to wait for the lock. The flag is set to false, when aborting.
+     */
+    private boolean removeEnabled = true;
+
+    /**
+     * A flag indicating, if the remover needs polling.
+     * It indicates, if the locked object has data available
+     * to be removed.
+     */
+    private boolean dataAvailable = false;
+
+    /**
+     * @return Value of addWaitTimeout
+     */
+    public synchronized long getAddWaitTimeout() {
+        return addWaitTimeout;
+    }
+
+    /**
+     * Set value of addWaitTimeout
+     */
+    public synchronized void setAddWaitTimeout(long timeout) {
+        addWaitTimeout = timeout;
+    }
+
+    /**
+     * @return Value of removeWaitTimeout
+     */
+    public synchronized long getRemoveWaitTimeout() {
+        return removeWaitTimeout;
+    }
+
+    /**
+     * Set value of removeWaitTimeout
+     */
+    public synchronized void setRemoveWaitTimeout(long timeout) {
+        removeWaitTimeout = timeout;
+    }
+
+    /**
+     * Check if the locked object has data available
+     * i.e. the remover can stop poling and get the lock.
+     * @return True iff the lock Object has data available.
+     */
+    public synchronized boolean isDataAvailable() {
+        return dataAvailable;
+    }
+
+    /**
+     * Check if an add thread owns the lock.
+     * @return True iff an add thread owns the lock.
+     */
+    public synchronized boolean isAddLocked() {
+        return addLocked;
+    }
+
+    /**
+     * Check if the remove thread owns the lock.
+     * @return True iff the remove thread owns the lock.
+     */
+    public synchronized boolean isRemoveLocked() {
+        return removeLocked;
+    }
+
+    /**
+     * Check if the remove thread is polling.
+     * @return True iff the remove thread is polling.
+     */
+    public synchronized boolean isRemovePolling() {
+        if ( remover != null ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Acquires the lock by an add thread and sets the add flag.
+     * If any add thread or the remove thread already acquired the lock
+     * this add thread will block until the lock is released.
+     */
+    public synchronized void lockAdd() {
+        if ( addLocked || removeLocked ) {
+            do {
+                try {
+                    wait(addWaitTimeout);
+                } catch ( InterruptedException e ) {
+                }
+            } while ( addLocked || removeLocked );
+        }
+        addLocked=true;
+    }
+
+    /**
+     * Acquires the lock by the remove thread and sets the remove flag.
+     * If any add thread already acquired the lock or the queue is
+     * empty, the remove thread will block until the lock is released
+     * and the queue is not empty.
+     */
+    public synchronized boolean lockRemove() {
+        removeLocked=false;
+        if ( ( addLocked || ! dataAvailable ) && removeEnabled ) {
+            remover=Thread.currentThread();
+            do {
+                try {
+                    wait(removeWaitTimeout);
+                } catch ( InterruptedException e ) {
+                }
+            } while ( ( addLocked || ! dataAvailable ) && removeEnabled );
+            remover=null;
+        }
+        if ( removeEnabled ) {
+            removeLocked=true;
+        } 
+        return removeLocked;
+    }
+
+    /**
+     * Releases the lock by an add thread and reset the remove flag.
+     * If the reader thread is polling, notify it.
+     */
+    public synchronized void unlockAdd(boolean dataAvailable) {
+        addLocked=false;
+        this.dataAvailable=dataAvailable;
+        if ( ( remover != null ) && ( dataAvailable || ! removeEnabled ) ) {
+            remover.interrupt();
+        } else {
+            notifyAll();
+        }
+    }
+
+    /**
+     * Releases the lock by the remove thread and reset the add flag.
+     * Notify all waiting add threads,
+     * that the lock has been released by the remove thread.
+     */
+    public synchronized void unlockRemove() {
+        removeLocked=false;
+        dataAvailable=false;
+        notifyAll();
+    }
+
+    /**
+     * Abort any polling remover thread
+     */
+    public synchronized void abortRemove() {
+        removeEnabled=false;
+        if ( remover != null ) {
+            remover.interrupt();
+        }
+    }
+
+}
diff --git a/container/modules/cluster/src/share/org/apache/catalina/cluster/util/SmartQueue.java b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/SmartQueue.java
new file mode 100644
index 0000000..330b040
--- /dev/null
+++ b/container/modules/cluster/src/share/org/apache/catalina/cluster/util/SmartQueue.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.cluster.util;
+
+/**
+ * A smart queue, used for async replication <BR>
+ * the "smart" part of this queue is that if the session is already queued for
+ * replication, and it is updated again, the session will simply be replaced,
+ * hence we don't replicate stuff that is obsolete. Put this into util, since it
+ * is quite generic.
+ * 
+ * @author Filip Hanik
+ * @version 1.0
+ */
+
+import java.util.LinkedList;
+import java.util.HashMap;
+
+public class SmartQueue {
+
+    public static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
+            .getLog(SmartQueue.class);
+
+    /**
+     * This is the actual queue
+     */
+    private LinkedList queue = new LinkedList();
+
+    /**
+     * And this is only for performance, fast lookups
+     */
+    private HashMap queueMap = new HashMap();
+
+    private Object mutex = new Object();
+
+    public SmartQueue() {
+    }
+
+    /**
+     * Add an object to the queue
+     * 
+     * @param entry -
+     *            the smart entry
+     */
+    public void add(SmartEntry entry) {
+        /*
+         * make sure we are within a synchronized block since we are dealing
+         * with two unsync collections
+         */
+        synchronized (mutex) {
+            /* check to see if this object has already been queued */
+            SmartEntry current = (SmartEntry) queueMap.get(entry.getKey());
+            if (current == null) {
+                /* the object has not been queued, at it to the end of the queue */
+                if (log.isDebugEnabled())
+                    log.debug("[" + Thread.currentThread().getName()
+                            + "][SmartQueue] Adding new object=" + entry);
+                queue.addLast(entry);
+                queueMap.put(entry.getKey(), entry);
+            } else {
+                /* the object has been queued, replace the value */
+                if (log.isDebugEnabled())
+                    log.debug("[" + Thread.currentThread().getName()
+                            + "][SmartQueue] Replacing old object=" + current);
+                current.setValue(entry.getValue());
+                if (log.isDebugEnabled())
+                    log.debug("with new object=" + current);
+            }
+            /*
+             * wake up all the threads that are waiting for the lock to be
+             * released
+             */
+            mutex.notifyAll();
+        }
+    }
+
+    public int size() {
+        synchronized (mutex) {
+            return queue.size();
+        }
+    }
+
+    /**
+     * Blocks forever until an element has been added to the queue
+     */
+    public SmartEntry remove() {
+        return remove(0);
+    }
+
+    public SmartEntry remove(long timeout) {
+        SmartEntry result = null;
+        long startEntry = System.currentTimeMillis();
+        synchronized (mutex) {
+            while (size() == 0) {
+                try {
+                    if (log.isDebugEnabled())
+                        log
+                                .debug("["
+                                        + Thread.currentThread().getName()
+                                        + "][SmartQueue] Queue sleeping until object added size="
+                                        + size() + ".");
+                    if ((timeout != 0)
+                            && ((System.currentTimeMillis() - startEntry) > timeout)) {
+                        return null;
+                    }
+                    mutex.wait(timeout);
+                    if (log.isDebugEnabled())
+                        log
+                                .debug("["
+                                        + Thread.currentThread().getName()
+                                        + "][SmartQueue] Queue woke up or interrupted size="
+                                        + size() + ".");
+                } catch (IllegalMonitorStateException ex) {
+                    throw ex;
+                } catch (InterruptedException ex) {
+                }//catch
+            }//while
+            /* guaranteed that we are not empty by now */
+            result = (SmartEntry) queue.removeFirst();
+            queueMap.remove(result.getKey());
+            if (log.isDebugEnabled())
+                log.debug("[" + Thread.currentThread().getName()
+                        + "][SmartQueue] Returning=" + result);
+        }
+        return result;
+    }
+
+    public static class SmartEntry {
+        protected Object key;
+
+        protected Object value;
+
+        public SmartEntry(Object key, Object value) {
+            if (key == null)
+                throw new IllegalArgumentException(
+                        "SmartEntry key can not be null.");
+            if (value == null)
+                throw new IllegalArgumentException(
+                        "SmartEntry value can not be null.");
+            this.key = key;
+            this.value = value;
+        }
+
+        public Object getKey() {
+            return key;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        public void setValue(Object value) {
+            if (value == null)
+                throw new IllegalArgumentException(
+                        "SmartEntry value can not be null.");
+            this.value = value;
+        }
+
+        public int hashCode() {
+            return key.hashCode();
+        }
+
+        public boolean equals(Object o) {
+            if (!(o instanceof SmartEntry))
+                return false;
+            SmartEntry other = (SmartEntry) o;
+            return other.getKey().equals(getKey());
+        }
+
+        public String toString() {
+            return "[SmartyEntry key=" + key + " value=" + value + "]";
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/cluster/test/build.xml b/container/modules/cluster/test/build.xml
new file mode 100644
index 0000000..41c62e1
--- /dev/null
+++ b/container/modules/cluster/test/build.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- @author Peter Rossbach -->
+<project name="Tomcat: Cluster Testcases" basedir="." default="test">
+	<property file="../../../../build.properties" />
+	<property file="../../../../jakarta-tomcat-5/build.properties.default" />
+	<property name="test.report.logs" value="logs/reports" />
+	<property name="test.results" value="logs/test-results" />
+
+	<property name="compile.optimize" value="true" />
+	<property name="compile.debug" value="true" />
+	<property name="compile.source" value="1.4" />
+	<property name="compile.deprecation" value="true" />
+	<property name="compile.nowarn" value="off" />
+	<property name="compile.encoding" value="ISO-8859-1" />
+	<property name="build.dir" value="build/test" />
+	<property name="src.dir" value="src/share" />
+	<property name="catalina.home" value="../../../../jakarta-tomcat-5/build" />
+
+	<!-- Build the classpath -->
+	<path id="project.classpath">
+		<pathelement location="${jmx.jar}" />
+		<pathelement location="${commons-logging.jar}" />
+		<pathelement location="${log4j.jar}" />
+		<fileset dir="${catalina.home}/common/endorsed">
+			<include name="*.jar" />
+		</fileset>
+		<fileset dir="${catalina.home}/common/lib">
+			<include name="*.jar" />
+		</fileset>
+		<fileset dir="${catalina.home}/server/lib">
+			<include name="*.jar" />
+		</fileset>
+	</path>
+
+	<target name="build-prepare">
+		<mkdir dir="${build.dir}" />
+	</target>
+
+	<target name="info" description="Shows a information about this ant script">
+		<echo>
+			This ant script implements some testcases to verify the key functions of tomcat clustering module.
+			You find this script at: ${ant.file}
+		</echo>
+	</target>
+
+	<!-- This target compiles all sources out of the 
+			projects source tree -->
+	<target name="compile" depends="build-prepare" description="This target compiles all sources out of the projects source tree">
+
+		<!-- Copies the static resources out of the src tree
+				to the build/classes dir -->
+		<copy todir="${build.dir}/classes">
+			<fileset dir="${src.dir}">
+				<include name="**" />
+				<exclude name="**/*.java" />
+			</fileset>
+		</copy>
+
+		<!-- Compiles all sources -->
+		<javac destdir="${build.dir}/classes" srcdir="${src.dir}" includes="**/*.java" excludes="**/CVS/**" deprecation="${compile.deprecation}" debug="${compile.debug}" source="${compile.source}" optimize="${compile.optimize}" nowarn="${compile.nowarn}" encoding="${compile.encoding}">
+			<classpath>
+				<path refid="project.classpath" />
+			</classpath>
+		</javac>
+	</target>
+
+	<target name="test" depends="compile" description="Run unit tests">
+		<delete dir="${test.results}" />
+		<mkdir dir="${test.results}" />
+		<junit fork="yes" failureProperty="test.failure">
+			<jvmarg value="-Dcatalina.base=${basedir}" />
+			<jvmarg value="-Dcatalina.home=${catalina.home}" />
+			<jvmarg value="-Dlog4j.configuration=file:conf/log4j.xml" />
+			<classpath>
+				<pathelement location="${build.dir}/classes" />
+				<path refid="project.classpath" />
+			</classpath>
+			<formatter type="plain" usefile="false" />
+			<formatter type="xml" />
+			<batchtest todir="${test.results}">
+				<fileset dir="${build.dir}/classes" includes="**/*Test.class" />
+			</batchtest>
+		</junit>
+		<mkdir dir="${test.report.logs}" />
+		<junitreport todir="${test.report.logs}">
+			<fileset dir="${test.results}" />
+			<report format="frames" todir="${test.report.logs}" />
+		</junitreport>
+		<antcall target="checktest" />
+	</target>
+
+	<target name="checktest" if="test.failure">
+		<fail message="some test failed" />
+	</target>
+
+	<target name="clean">
+		<delete dir="${build}/dir" />
+		<delete dir="build" />
+		<delete dir="${test.report.logs}" />
+		<delete dir="${test.results}" />
+		<delete dir="logs" />
+	</target>
+</project>
diff --git a/container/modules/cluster/test/conf/log4j.xml b/container/modules/cluster/test/conf/log4j.xml
new file mode 100644
index 0000000..9dd3ea3
--- /dev/null
+++ b/container/modules/cluster/test/conf/log4j.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!-- ===================================================================== -->
+<!-- -->
+<!-- Log4j Configuration -->
+<!-- -->
+<!-- ===================================================================== -->
+<!-- $Id$ -->
+<!--
+| For more configuration infromation and examples see the Jakarta Log4j
+| owebsite: http://jakarta.apache.org/log4j
+-->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+<!-- ============================== -->
+<!-- Append messages to the console -->
+<!-- ==============================-->
+
+<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+   <param name="Target" value="System.out"/>
+   <param name="Threshold" value="debug"/>
+   <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
+   </layout>
+</appender>
+
+
+<category name="org.apache.catalina.cluster" 
+           additivity="false"> 
+   <priority value="info" />
+   <appender-ref ref="CONSOLE" />
+</category>
+
+<category name="org.apache.catalina" 
+           additivity="false"> 
+   <priority value="info" />
+   <appender-ref ref="CONSOLE" />
+</category>
+
+<!-- Setup the Root c  -->
+<root>
+   <priority value="info" />
+   <appender-ref ref="CONSOLE"/>
+</root>
+</log4j:configuration> 
+
diff --git a/container/modules/cluster/test/src/share/org/apache/catalina/cluster/io/XByteBufferTest.java b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/io/XByteBufferTest.java
new file mode 100644
index 0000000..14cf263
--- /dev/null
+++ b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/io/XByteBufferTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.catalina.cluster.io;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.cluster.tcp.ClusterData;
+
+/**
+ * @author Peter Rossbach
+ * 
+ * @version $Revision$ $Date$
+ */
+public class XByteBufferTest extends TestCase {
+
+    public void testBytes() {
+        XByteBuffer xbuf = new XByteBuffer() ;
+        assertEquals(0,xbuf.bufSize);
+        assertEquals(1024,xbuf.buf.length);
+        assertEquals(0,xbuf.getBytes().length);
+        assertNotSame(xbuf.buf,xbuf.getBytes());
+    }
+    
+    
+    /**
+     * Test append to message buffer
+     * Test that only correct message header are appended 
+     */
+    public void testAppend() {
+        XByteBuffer xbuf = new XByteBuffer();
+        byte[] bs = new byte[] { 1, 2, 3 };
+        boolean status = xbuf.append(bs, 0, 3);
+        assertTrue(status);
+        assertEquals(3, xbuf.bufSize);
+        byte buf[] = xbuf.getBytes();
+        assertEquals(3, buf.length);
+        status = xbuf.append(bs, 0, 3);
+        assertTrue(status);
+        status = xbuf.append(bs, 0, 3);
+        // not a correct message !!
+        assertFalse(status);
+        // buffer is cleared
+        assertEquals(0,xbuf.bufSize);
+        status = xbuf.append(XByteBuffer.START_DATA,0, XByteBuffer.START_DATA.length);
+        assertTrue(status);      
+    }
+    
+    /**
+     * Test create a uncompressed multi message data package and extract it
+     * @throws IOException
+     */
+    public void testSendUncompressedMessage() throws IOException {
+        assertSendMessage();
+    }
+
+    /**
+     * @throws IOException
+     */
+    private void assertSendMessage() throws IOException {
+        byte[] test = createMessage();
+        XByteBuffer b = new XByteBuffer();
+        b.append(test, 0, test.length);
+        int s = b.countPackages();
+        byte[] d ;
+        ClusterData data ;
+        assertEquals(3, s);
+        for (byte i = 1; i < 4; i++) {
+            data = b.extractPackage(true);
+            d = data.getMessage();
+            assertEquals(i, d[0]);
+        }
+    }
+
+    /**
+     * @return
+     * @throws IOException
+     */
+    private byte[] createMessage() throws IOException {
+        byte[] d1 = XByteBuffer.createDataPackage(new byte[] { 1 });
+        byte[] d2 = XByteBuffer.createDataPackage(new byte[] { 2 });
+        byte[] d3 = XByteBuffer.createDataPackage(new byte[] { 3 });
+        byte[] test = new byte[d1.length + d2.length + d3.length + 5];
+        System.arraycopy(d1, 0, test, 0, d1.length);
+        System.arraycopy(d2, 0, test, d1.length, d2.length);
+        System.arraycopy(d3, 0, test, d2.length + d1.length, d3.length);
+        return test;
+    }
+
+
+    /**
+     * Test the type convertes to and from byte array
+     */
+    public void testTypeconverter() {
+        byte[] d = XByteBuffer.toBytes(Integer.MAX_VALUE);
+        assertEquals(4, d.length);
+        assertEquals(Integer.MAX_VALUE, XByteBuffer.toInt(d, 0));
+
+        d = XByteBuffer.toBytes(Long.MAX_VALUE);
+        assertEquals(8, d.length);
+        assertEquals(Long.MAX_VALUE, XByteBuffer.toLong(d, 0));
+        d = XByteBuffer.toBytes((long) 4564564);
+        assertEquals(4564564, XByteBuffer.toLong(d, 0));
+    }
+
+}
diff --git a/container/modules/cluster/test/src/share/org/apache/catalina/cluster/mcast/McastMemberTest.java b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/mcast/McastMemberTest.java
new file mode 100644
index 0000000..2be3b6e
--- /dev/null
+++ b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/mcast/McastMemberTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.catalina.cluster.mcast;
+
+import org.apache.catalina.cluster.io.XByteBuffer;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Peter Rossbach
+ * 
+ * @version $Revision$
+ */
+public class McastMemberTest extends TestCase{
+
+    private static final String TEST = "0123456789";
+    public void testGetMember() throws Exception {
+        byte[] data = new byte[44];
+        long alive=System.currentTimeMillis()-20;
+        System.arraycopy(XByteBuffer.toBytes((long)alive),0,data,0,8);
+        System.arraycopy(XByteBuffer.toBytes(20000),0,data,8,4);
+        System.arraycopy(XByteBuffer.toBytes(120000000),0,data,12,4);
+        System.arraycopy(XByteBuffer.toBytes(10),0,data,16,4);
+        System.arraycopy(TEST.getBytes(),0,data,20,TEST.length());
+        System.arraycopy(XByteBuffer.toBytes(10),0,data,30,4);
+        System.arraycopy(TEST.getBytes(),0,data,34,TEST.length());
+        McastMember member = McastMember.getMember(data);
+        assertEquals(member.getName(),TEST);
+        assertEquals(member.getDomain(),TEST);
+        assertEquals(member.getPort(),20000);
+        assertEquals(member.getHost(),"7.39.14.0");
+        byte[] data1 = member.getData(20);
+        assertEquals(data.length,data1.length);
+        McastMember member1 = McastMember.getMember(data1);
+        assertEquals(member1.getName(),TEST);
+        assertEquals(member1.getDomain(),TEST);
+        assertEquals(member1.getPort(),20000);
+        assertEquals(member1.getHost(),"7.39.14.0");
+        
+    }
+}
diff --git a/container/modules/cluster/test/src/share/org/apache/catalina/cluster/session/DeltaManagerTest.java b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/session/DeltaManagerTest.java
new file mode 100644
index 0000000..3a39587
--- /dev/null
+++ b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/session/DeltaManagerTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.catalina.cluster.session;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.cluster.ClusterMessage;
+import org.apache.catalina.cluster.Member;
+import org.apache.catalina.cluster.mcast.McastMember;
+import org.apache.catalina.cluster.mcast.McastService;
+import org.apache.catalina.cluster.tcp.SimpleTcpCluster;
+
+/**
+ * @author Peter Rossbach
+ * 
+ * @version $Revision$ $Date$
+ */
+public class DeltaManagerTest extends TestCase {
+
+    public void testCreateSession() {
+        MockDeltaManager manager = new MockDeltaManager() ;
+        Session session = manager.createSession(null,false);
+        assertNotNull(session.getId());
+        assertEquals(32, session.getId().length());
+        session = manager.createSession(null,true);
+        assertEquals(session,manager.session);
+        assertEquals(session.getId(),manager.sessionID);
+    }
+    
+    public void testhandleGET_ALL_SESSIONS() throws Exception {
+        MockDeltaManager manager = new MockDeltaManager() ;
+        Session session = manager.createSession(null,false);
+        assertEquals(session, manager.findSession(session.getId()));
+        for (int i = 0; i < 10; i++) {
+            manager.createSession(null,false);
+        }
+        assertEquals(11,manager.getSessionCounter());
+        Member sender = new McastMember("test","d10","localhost",8080,3000);
+        MockCluster cluster = new MockCluster ();
+        manager.setCluster(cluster);
+        manager.setSendAllSessionsSize(2);
+        manager.handleGET_ALL_SESSIONS(null,sender);
+        // send all session activ - 6 sessions message and one transfer complete
+        assertEquals(2,cluster.sendcounter);
+        
+        // send session blockwise
+        cluster.sendcounter=0;
+        manager.setSendAllSessions(false);
+        manager.handleGET_ALL_SESSIONS(null,sender);
+        // 11 session activ - 6 sessions message and one transfer complete
+        assertEquals(7,cluster.sendcounter);
+    }
+    
+    public void testFirstMemberhandleGET_ALL_SESSIONS() throws Exception {
+        MockDeltaManager manager = new MockDeltaManager() ;
+        MockCluster cluster = new MockCluster ();
+        McastService service = new MockMcastService() ; 
+        cluster.setMembershipService(service);
+        manager.setCluster(cluster);
+        manager.getAllClusterSessions();
+        assertEquals(0,cluster.sendcounter);
+        manager.setSendClusterDomainOnly(false);
+        manager.getAllClusterSessions();
+        assertEquals(0,cluster.sendcounter);
+        manager.setSendClusterDomainOnly(true);
+        service.memberAdded(new McastMember("franz2", "d11", "127.0.0.1", 7002, 100));
+        manager.getAllClusterSessions();
+        assertEquals(0,cluster.sendcounter);
+        service.memberAdded(new McastMember("franz3", "d10", "127.0.0.1", 7003, 100));
+        assertNotNull(manager.findSessionMasterMember());
+    }
+    
+    class MockMcastService extends McastService {
+        List members = new ArrayList();
+        
+        public MockMcastService() {
+            localMember =new McastMember("franz", "d10", "127.0.0.1", 7001, 100) ; 
+       }
+        public void memberAdded(Member member) {
+            members.add(member);
+        }
+        public Member getLocalMember() {
+            return localMember;
+        }
+
+        public Member[] getMembers() {
+            return (Member[]) members.toArray(new Member[members.size()]);
+        }
+    }
+    
+    class MockDeltaManager extends DeltaManager {
+        
+        private String sessionID;
+        private DeltaSession session;
+
+
+        /**
+         * 
+         */
+        public MockDeltaManager() {
+            super();
+        }
+        
+        
+        /* (non-Javadoc)
+         * @see org.apache.catalina.cluster.session.DeltaManager#sendCreateSession(java.lang.String, org.apache.catalina.cluster.session.DeltaSession)
+         */
+        protected void sendCreateSession(String sessionId, DeltaSession session) {
+           this.sessionID = sessionId ;
+           this.session = session ;
+        }       
+        
+    }
+    
+    class MockCluster extends SimpleTcpCluster {
+    
+        private int sendcounter;
+
+        /** don't send only count sends
+         * @see org.apache.catalina.cluster.CatalinaCluster#send(org.apache.catalina.cluster.ClusterMessage, org.apache.catalina.cluster.Member)
+         */
+        public void send(ClusterMessage msg, Member dest) {
+            sendcounter++ ;
+        }
+}
+}
diff --git a/container/modules/cluster/test/src/share/org/apache/catalina/cluster/session/DeltaSessionTest.java b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/session/DeltaSessionTest.java
new file mode 100644
index 0000000..3250d6d
--- /dev/null
+++ b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/session/DeltaSessionTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.catalina.cluster.session;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.Manager;
+
+/**
+ * @author Peter Rossbach
+ * 
+ * @version $Revision$ $Date$
+ */
+public class DeltaSessionTest extends TestCase {
+
+    public void testPrimarySessionisValid() {
+        DeltaManager manager = new DeltaManager() { public DeltaSession getNewDeltaSession() { return new MockSession(this) ; } } ;
+        manager.setMaxInactiveInterval(1);
+        MockSession session = (MockSession)manager.createSession("hello",false);
+        assertTrue(session.isPrimarySession());
+        assertTrue(session.isValid()) ;
+        assertEquals(1,session.getMaxInactiveInterval());
+        try {
+            Thread.sleep(2000);
+        } catch (Exception sleep) {
+        }
+        long timeNow = System.currentTimeMillis();
+        int timeIdle = (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+        assertEquals(2,timeIdle);
+        assertTrue(timeIdle > session.getMaxInactiveInterval());
+        assertFalse(session.isValid()) ;
+        assertEquals(1,session.expireNotifyCalled);
+        assertEquals(1,session.expireNotifyClusterCalled);
+    }
+ 
+    public void testBackupSessionisValid() {
+        DeltaManager manager = new DeltaManager() { public DeltaSession getNewDeltaSession() { return new MockSession(this) ; } } ;
+        manager.setMaxInactiveInterval(1);
+        MockSession session = (MockSession)manager.createSession("hello",false);
+        session.setPrimarySession(false);
+        try {
+            Thread.sleep(1000);
+        } catch (Exception sleep) {
+        }
+        assertTrue(session.isValid()) ;
+        assertEquals(0,session.expireNotifyCalled);
+        assertEquals(0,session.expireNotifyClusterCalled);
+        try {
+            Thread.sleep(2000);
+        } catch (Exception sleep) {
+        }
+        assertFalse(session.isValid()) ;
+        assertEquals(1,session.expireNotifyCalled);
+        // no cluster notification
+        assertEquals(0,session.expireNotifyClusterCalled);
+    }
+    
+    class MockSession extends DeltaSession {
+
+        long expireCalled = 0 ;
+        long expireNotifyCalled = 0 ;
+        long expireNotifyClusterCalled = 0 ;
+        
+        
+        /**
+         * @param manager
+         */
+        public MockSession(Manager manager) {
+            super(manager);
+        }
+      
+        /* (non-Javadoc)
+         * @see org.apache.catalina.cluster.session.DeltaSession#expire(boolean, boolean)
+         */
+        public void expire(boolean notify, boolean notifyCluster) {
+            expireCalled++ ;
+            if(notify)
+                expireNotifyCalled++ ;
+            if(notifyCluster)
+                expireNotifyClusterCalled++ ;
+            isValid = false ;
+        }
+    }
+}
diff --git a/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/DataSenderTest.java b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/DataSenderTest.java
new file mode 100644
index 0000000..2f04814
--- /dev/null
+++ b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/DataSenderTest.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Peter Rossbach
+ * 
+ * @version $Revision$ $Date$
+ */
+public class DataSenderTest extends TestCase {
+    
+    /**
+     * Test that close Socket before socket open again!
+     * @throws Exception
+     */
+    public void testOpenAgain() throws Exception {
+        DataSender sender = createMockDataSender(); 
+        assertEquals(1,sender.getSocketOpenCounter());
+        assertEquals(0,sender.getSocketCloseCounter());
+        sender.openSocket() ;
+        assertEquals(1,sender.getSocketOpenCounter());
+        assertEquals(0,sender.getSocketCloseCounter());
+        sender.closeSocket() ;
+        sender.openSocket() ;
+        assertEquals(1,sender.getSocketCloseCounter());
+        assertEquals(2,sender.getSocketOpenCounter());
+    }
+    
+    /**
+     * Test Connect/disconnet open and close underlying sockets
+     * @throws Exception
+     */
+    public void testConnectDisconnect() throws Exception {
+        InetAddress host = InetAddress.getByName("127.0.0.1");
+        DataSender sender = new MockDataSender("catalina",host, 3434);
+        sender.connect() ;
+        assertTrue(sender.isConnected());
+        assertEquals(1,sender.getSocketOpenCounter());
+        assertEquals(0,sender.getSocketCloseCounter());
+        assertEquals(1,sender.getConnectCounter());
+        sender.disconnect();
+        assertFalse(sender.isConnected());
+        assertEquals(1,sender.getSocketCloseCounter());
+        assertEquals(1,sender.getDisconnectCounter());
+   }
+    
+    /**
+     * Test Socket setup and OpenClose Counter
+     * @throws Exception
+     */
+    public void testOpenCloseSocketCounter() throws Exception {
+        DataSender sender = createMockDataSender();
+        assertEquals(0, sender.getSocket().getSoTimeout());
+        sender.closeSocket();
+        assertEquals(1, sender.getSocketOpenCounter());
+        assertEquals(1, sender.getSocketCloseCounter());
+    }
+
+    /**
+     * Test Socket IOException with SocketCounter increment
+     * @throws Exception
+     */
+    public void testFailedOpenSocketCounter() throws Exception {
+        InetAddress host = InetAddress.getByName("127.0.0.1");
+        DataSender sender = new MockFailedDataSender("catalina",host, 3434);
+        try {
+            sender.openSocket();
+            fail("Sender not send expected IOException");
+        } catch (IOException ioe) {
+            assertEquals(0, sender.getSocketOpenCounter());
+            assertEquals(1, sender.getSocketOpenFailureCounter());
+        }
+    }
+
+    /**
+     * read ack from receiver
+     * @throws Exception
+     */
+    public void testWaitForAck() throws Exception {
+        DataSender sender = createMockDataSender();
+        assertNotNull(sender.getSocket());
+        sender.waitForAck(15000);  
+        ByteArrayInputStream stream = (ByteArrayInputStream)sender.getSocket().getInputStream();
+        assertEquals(-1,stream.read());
+    }
+    
+    /**
+     * @return
+     * @throws UnknownHostException
+     * @throws IOException
+     * @throws SocketException
+     */
+    private DataSender createMockDataSender() throws UnknownHostException, IOException, SocketException {
+        InetAddress host = InetAddress.getByName("127.0.0.1");
+        DataSender sender = new MockDataSender("catalina",host, 3434);
+        sender.openSocket();
+        return sender;
+    }
+
+    /**
+     * Send message with  wait ack and simulate ack Exceptions
+     * @throws Exception
+     */
+    public void testWriteData()throws Exception {
+        DataSender sender = createMockDataSender();
+        ClusterData data = new ClusterData("test", "123",new byte[]{ 1,2,3 }, System.currentTimeMillis() );
+        sender.writeData(data) ;
+        ByteArrayOutputStream stream = (ByteArrayOutputStream)sender.getSocket().getOutputStream();
+        assertEquals(21,stream.size());
+        ByteArrayInputStream istream = (ByteArrayInputStream)sender.getSocket().getInputStream();
+        assertEquals(-1,istream.read());    
+        MockSocket socket =((MockSocket)sender.getSocket());
+        socket.reset();
+        socket.setReadIOException(true);
+        try {
+            sender.writeData(data);
+            fail("Missing Ack IOExcpetion") ;
+        } catch (IOException ioe) {} ;
+        socket.reset();
+        socket.setReadIOException(false);
+        socket.setReadSocketTimeoutException(true);
+        try {
+            sender.writeData(data);
+            fail("Missing Ack SocketTimeoutException") ;
+        } catch (SocketTimeoutException soe) {} ;               
+
+    }
+    
+    /**
+     * Send message without wait ack
+     * @throws Exception
+     */
+    public void testWriteDataWithOutAck()throws Exception {
+        InetAddress host = InetAddress.getByName("127.0.0.1");
+        DataSender sender = new MockDataSender("catalina",host, 3434);
+        sender.setWaitForAck(false);
+        sender.openSocket();
+        ClusterData data = new ClusterData("test", "123",new byte[]{ 1,2,3 }, System.currentTimeMillis() );
+        sender.writeData(data) ;
+        ByteArrayOutputStream stream = (ByteArrayOutputStream)sender.getSocket().getOutputStream();
+        assertEquals(21,stream.size());
+        ByteArrayInputStream istream = (ByteArrayInputStream)sender.getSocket().getInputStream();
+        assertEquals(3,TcpReplicationThread.ACK_COMMAND.length);
+        assertEquals(TcpReplicationThread.ACK_COMMAND[0],istream.read());        
+        assertEquals(TcpReplicationThread.ACK_COMMAND[1],istream.read());        
+        assertEquals(TcpReplicationThread.ACK_COMMAND[2],istream.read());   
+     }
+    
+    /**
+     * Check close socket fro keep alive handling is correct (number of request and timeout
+     * @throws Exception
+     */
+    public void testcheckKeepAlive() throws Exception {
+        DataSender sender = createMockDataSender() ;
+        assertFalse(sender.checkKeepAlive()) ;
+        sender.setKeepAliveMaxRequestCount(1);
+        sender.keepAliveCount = 1;
+        assertTrue(sender.checkKeepAlive());
+        assertEquals(1,sender.getSocketCloseCounter());
+        assertEquals(0,sender.getKeepAliveCount());
+        sender.openSocket();
+        assertEquals(0,sender.getKeepAliveCount());
+        sender.setKeepAliveMaxRequestCount(100);
+        sender.keepAliveConnectTime = System.currentTimeMillis() - sender.getKeepAliveTimeout() ;
+        assertFalse(sender.checkKeepAlive());
+        assertTrue(sender.isConnected());
+        assertEquals(1,sender.getSocketCloseCounter());
+        sender.keepAliveConnectTime-- ;
+        assertTrue(sender.checkKeepAlive());
+        assertEquals(2,sender.getSocketCloseCounter());
+    }
+    
+    
+    /**
+     * Push a mesage over moch socket to receiver
+     * @throws Exception
+     */
+    public void testPushMessage() throws Exception {
+        InetAddress host = InetAddress.getByName("127.0.0.1");
+        DataSender sender = new MockDataSender("catalina",host, 3434);
+        assertFalse(sender.isConnected());
+        assertPushMessage(sender);
+        ((MockSocket)sender.getSocket()).reset();
+        // let see the processingtime 
+        sender.setDoProcessingStats(true);
+        pushMessage(sender);
+        assertEquals(sender.getProcessingTime(),sender.getMinProcessingTime());
+        assertEquals(sender.getProcessingTime(),sender.getMaxProcessingTime());
+    }
+   
+    /**
+     * Test retry after socket write failure
+     * @throws Exception
+     */
+    public void testPushMessageRetryFailure() throws Exception {
+        InetAddress host = InetAddress.getByName("127.0.0.1");
+        DataSender sender = new MockDataSender("catalina",host, 3434);
+        sender.openSocket() ;
+        ((MockSocket)sender.getSocket()).setWriteIOException(true);
+        assertPushMessage(sender);
+        assertEquals(2,sender.getSocketOpenCounter());
+        assertEquals(1,sender.getSocketCloseCounter());
+    }
+    
+    /**
+     * @param sender
+     * @throws IOException
+     */
+    private void assertPushMessage(DataSender sender) throws IOException {
+        ByteArrayOutputStream stream = pushMessage(sender);
+        assertEquals(21,stream.size());
+        assertEquals(1,sender.getKeepAliveCount());
+        assertEquals(1,sender.getNrOfRequests());
+        assertEquals(0,sender.getProcessingTime());
+        assertEquals(Long.MAX_VALUE,sender.getMinProcessingTime());
+    }
+
+    /**
+     * @param sender
+     * @return
+     * @throws IOException
+     */
+    private ByteArrayOutputStream pushMessage(DataSender sender) throws IOException {
+        ClusterData data = new ClusterData("unique-id", "123",new byte[]{ 1,2,3 }, System.currentTimeMillis() );
+        sender.pushMessage(data );
+        assertTrue(sender.isConnected());
+        ByteArrayOutputStream stream = (ByteArrayOutputStream)sender.getSocket().getOutputStream();
+        return stream;
+    }
+
+    /**
+     * Simulate Create socket failure 
+     */
+    class MockFailedDataSender extends DataSender {
+
+        /**
+         * @param host
+         * @param port
+         */
+        public MockFailedDataSender(String domain,InetAddress host, int port) {
+            super(domain,host, port);
+        }
+
+        /*
+         * throw IOException
+         * 
+         * @see org.apache.catalina.cluster.tcp.DataSender#createSocket()
+         */
+        protected void createSocket() throws IOException, SocketException {
+            throw new IOException();
+        }
+    }
+
+    /**
+     * Simulate open real socket to a server!!
+     */
+    class MockDataSender extends DataSender {
+
+        
+        /**
+         * @param host
+         * @param port
+         */
+        public MockDataSender(String domain,InetAddress host, int port) {
+            super(domain,host, port);
+            
+        }
+
+        protected void createSocket() throws IOException, SocketException {
+            setSocket(new MockSocket(getAddress(), getPort()));
+        }
+        
+    }
+    
+    /**
+     * Don't open Socket really
+     */
+    class MockSocket extends Socket {
+
+        private InputStream ackInputStream ;
+        private OutputStream messageStream ;
+        private boolean writeIOException = false ;
+        private boolean readIOException = false ;
+        private boolean readSocketTimeoutException = false ;
+               
+        /**
+         * @param address
+         * @param port
+         * @throws java.io.IOException
+         */
+        public MockSocket(InetAddress address, int port) throws IOException {
+            ackInputStream = new ByteArrayInputStream(TcpReplicationThread.ACK_COMMAND);
+            messageStream = new ByteArrayOutputStream() ;
+        }
+        
+        public void reset() throws IOException {
+           ackInputStream.reset() ;
+        }
+        
+        
+        /**
+         * @return Returns the readIOException.
+         */
+        public boolean isReadIOException() {
+            return readIOException;
+        }
+        
+        /**
+         * @param readIOException The readIOException to set.
+         */
+        public void setReadIOException(boolean readIOException) {
+            this.readIOException = readIOException;
+        }
+        
+        /**
+         * @return Returns the readSocketTimeoutException.
+         */
+        public boolean isReadSocketTimeoutException() {
+            return readSocketTimeoutException;
+        }
+        
+        /**
+         * @param readSocketTimeoutException The readSocketTimeoutException to set.
+         */
+        public void setReadSocketTimeoutException(
+                boolean readSocketTimeoutException) {
+            this.readSocketTimeoutException = readSocketTimeoutException;
+        }
+        /**
+         * @return Returns the writeIOException.
+         */
+        public boolean isWriteIOException() {
+            return writeIOException;
+        }
+        
+        /**
+         * @param writeIOException The writeIOException to set.
+         */
+        public void setWriteIOException(boolean writeIOException) {
+            this.writeIOException = writeIOException;
+        }
+        
+        /**
+         *  get ack Stream ( 3 bytes)
+         * @see TcpReplicationThread#ACK_COMMAND
+         * @see java.net.Socket#getInputStream()
+         */
+        public InputStream getInputStream() throws IOException {
+            if(isReadIOException()) {
+                throw new IOException("MockSocket");
+            }
+            if(isReadSocketTimeoutException()) {
+                throw new SocketTimeoutException("MockSocket");
+            }
+            return ackInputStream;
+        }
+        
+        
+        /**
+         * Buffer Output in simple byte array stream
+         * @see java.net.Socket#getOutputStream()
+         */
+        public OutputStream getOutputStream() throws IOException {
+            if(isWriteIOException()) {
+                throw new IOException("MockSocket");
+            }
+            return messageStream;
+        }
+    }
+}
diff --git a/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/ReplicationTransmitterTest.java b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/ReplicationTransmitterTest.java
new file mode 100644
index 0000000..33612ac
--- /dev/null
+++ b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/ReplicationTransmitterTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.catalina.cluster.tcp;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.zip.GZIPInputStream;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.cluster.session.ReplicationStream;
+import org.apache.catalina.cluster.session.SessionMessageImpl;
+
+/**
+ * @author Peter Rossbach
+ * 
+ * @version $Revision$ $Date$
+ */
+public class ReplicationTransmitterTest extends TestCase {
+
+    public void testCreateMessageData() throws Exception {
+        ReplicationTransmitter transmitter = new ReplicationTransmitter();
+        transmitter.setCompress(true);
+        SessionMessageImpl message= new SessionMessageImpl();
+        message.setUniqueId("test");
+        ClusterData data = transmitter.serialize(message);
+        assertTrue(200 < data.getMessage().length);
+        Object myobj = getGZPObject(data.getMessage());
+        assertTrue(myobj instanceof SessionMessageImpl);
+        assertEquals("test", ((SessionMessageImpl)myobj).getUniqueId());
+        
+    }
+
+    /**
+     * @param data
+     * @return
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    private Object getGZPObject(byte[] data) throws IOException, ClassNotFoundException {
+        ByteArrayInputStream bin = 
+            new ByteArrayInputStream(data);
+        GZIPInputStream gin = 
+            new GZIPInputStream(bin);
+        byte[] tmp = new byte[1024];
+        int length = gin.read(tmp);
+        byte[] result = new byte[0];
+        while (length > 0) {
+            byte[] tmpdata = result;
+            result = new byte[result.length + length];
+            System.arraycopy(tmpdata, 0, result, 0, tmpdata.length);
+            System.arraycopy(tmp, 0, result, tmpdata.length, length);
+            length = gin.read(tmp);
+        }
+        gin.close();
+        ReplicationStream stream = new ReplicationStream(
+                new java.io.ByteArrayInputStream(result), getClass()
+                        .getClassLoader());
+        Object myobj = stream.readObject();
+        return myobj;
+    }
+}
diff --git a/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/ReplicationValveTest.java b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/ReplicationValveTest.java
new file mode 100644
index 0000000..16074a3
--- /dev/null
+++ b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/ReplicationValveTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.catalina.cluster.tcp;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Peter Rossbach
+ * 
+ * @version $Revision$ $Date$
+ */
+public class ReplicationValveTest extends TestCase {
+
+    
+    public void testIsRequestWithoutSessionChange() {
+        ReplicationValve valve = new ReplicationValve() ;
+        valve.setFilter(".*\\.html");
+        assertEquals(1,valve.getReqFilters().length) ;
+        assertTrue(valve.isRequestWithoutSessionChange("/ClusterTest/index.html")) ;        
+        assertFalse(valve.isRequestWithoutSessionChange("/ClusterTest/index.jsp")) ;        
+        valve.setFilter(".*\\.html;.*\\.css");
+        assertEquals(2,valve.getReqFilters().length) ;
+        assertTrue(valve.isRequestWithoutSessionChange("/ClusterTest/index.html")) ;        
+        assertTrue(valve.isRequestWithoutSessionChange("/ClusterTest/layout.css")) ;        
+    }
+    
+}
diff --git a/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/SimpleTcpClusterTest.java b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/SimpleTcpClusterTest.java
new file mode 100644
index 0000000..b15f638
--- /dev/null
+++ b/container/modules/cluster/test/src/share/org/apache/catalina/cluster/tcp/SimpleTcpClusterTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.catalina.cluster.tcp;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.cluster.deploy.FarmWarDeployer;
+import org.apache.catalina.cluster.mcast.McastService;
+import org.apache.catalina.cluster.session.ClusterSessionListener;
+
+/*
+* @author Peter Rossbach
+* @version $Revision$ $Date$
+*/
+public class SimpleTcpClusterTest extends TestCase {
+
+    
+    public void testCreateClusterSessionListenerAtStart() throws LifecycleException
+    {
+        SimpleTcpCluster cluster = new SimpleTcpCluster() ;
+        cluster.setMembershipService( new McastService() { public void start() {} });
+        cluster.setClusterDeployer(new FarmWarDeployer() { public void start() {}});
+        SocketReplicationListener receiver = new SocketReplicationListener(){ public void start() {}};
+        receiver.setTcpListenAddress("localhost");
+        receiver.setTcpListenPort(45660);
+        cluster.setClusterReceiver(receiver);
+        cluster.setClusterSender(new ReplicationTransmitter(){ public void start() {}});
+        cluster.start();
+        assertEquals(1,cluster.clusterListeners.size());
+        assertTrue( cluster.clusterListeners.get(0) instanceof ClusterSessionListener);
+    }
+}
diff --git a/container/modules/cluster/to-do.txt b/container/modules/cluster/to-do.txt
new file mode 100644
index 0000000..2051cec
--- /dev/null
+++ b/container/modules/cluster/to-do.txt
@@ -0,0 +1,314 @@
+==============================
+Next actions
+==============================
+
+- Add MbeanFactory to generate dynamic cluster at runtime.
+    Problem: How we can start those central services?
+    - StandardEngine support load Mbean from external file.     
+- when a lot of messages expire it comes to burst of messages 
+     - all 60 Sec when ManagerBase#processExpires is called a lot of messages are send!
+     - Better is to transfer a spezial epxire message with an array of expired session messages.
+     - This reduce message transfer and reduce waits for acks.
+     - complicated implementation thing , sessions expires when call isValid :-(     
+- build a tool that receive the stage and present it as simple web app.
+    	detect some problems
+    	different active sessions
+    	long queues
+    	detect long wait acks
+        Idea: Wrote a ant script with the new jmx tasks!
+        Optimzied information access:
+        	Create a MBean.attribute that store the complete cluster state 
+        	information inside one attribute from type TabularData and CompositeData!
+        	Implement as SimpleTcpCluster operation
+        	Cluster <CompositeData>
+        	  -- Attributes <SimpleType>
+        	  -- ClusterMembership
+        	      -- Attributes <SimpleType>
+        	      -- Members <CompositeType>
+        	         -- Attributes <SimpeType>
+        	  -- ClusterReceiver
+        	      -- Attributes <SimpleType>
+        	  -- ClusterSender
+        	      -- Attributes <SimpleType>
+        	      -- DataSenders <TabularData>
+        	         -- Sender <CompositeType>
+        	         	-- Attributes <SimpeType>
+        	         	-- (optional)Queue Stats <CompositeData>
+ 	       	         		-- Attributes <SimpeType>      	    
+- add cluster setup template (src)	
+- documentation
+	wrote a complete new how-to
+	add example configurations
+	add complete attribute descriptions
+	add JMX information
+	add deployment help
+	Need help (pero)
+- implement fragmentation of large replication objects
+	Compress at message level 
+	Splitting Messages ala FarmDeployer war handling
+- add a message type to the message header.
+    - filtering at receiver that drop message before build Object
+    - define short type definition.
+       - Every Session message with differenz type
+       - type with String
+- add test cluster project
+	functional testing a lot of szenarios
+		differen replication mode
+		restart after failure
+		crash under load
+		compress and uncompressed
+    Junit test ( started)
+    automated regression testing with some standard configs
+    wrote a test client that can get a JMX State to verify the different cluster testszenarios
+	 - direct JSR 160 client - preferred option
+	 - via mx4j http adaptor ( xml protocol)
+	 - setup different szenarios
+	use new remote jmx ant tasks to grab information from mbeans 
+- filter all cluster messages 
+     - Filtering that no all message send to all member.
+     - Now with domain mode registeration session message only send to
+       members from same domain.
+- setup a Cluster LifecycleListener to send the cluster status! (monitoring)
+     - Make the compact state via JMX API avialable first.
+     - setup cluster Listener that send own state to spezial member (sender from a message like GET_ALL_SESSIONS)
+     - create a cluster status app (html/xml)
+         - attributes
+            - send message
+            - current members
+            - receive message
+            - active session at differen clustered manager
+            - which app are clusters (registered managers)
+            - avg processing times
+         
+         - operations
+            - resetStats
+            - send a message (String)
+            - getDisplay state from other members??
+            - stop queue to send
+            - queue receiver message from some members ( later send after redeploying)
+            - configure some send parameters
+                 - keepalive
+                 - compress
+                 - wait for ack
+         - other things
+            - based on Cluster JMX API                     
+            - watch some values from complete cluster and display some graphs
+            - display the informations from all nodes
+            - display informations from other cluster domains
+            	via XML documents and http
+            - display stats as xml
+            - operation via JMX (MX4J adaptor)	    
+
+================
+problems
+================
+- MemoryUser principal from UserDatabaseRealm not handled to replicated 
+	- look inside DeltaRequest.setPrincipal(Principal,boolean)
+		 detected by Dirk de Kok (tomdev 16.8.2005)
+	- only GenericPrincipal from all other realms are handled well.
+- We not set SimpleTcpCluster Properties when element exists inside config.
+	Element must have all properties!! - Note inside docs!!  
+- How we can stop the request traffic when restart an application?
+    currently the jk 1.2.10 can only disable the complete loadbalancer,
+    but this detect only the new session request desicion.
+    Request with sessions marks send to tomcat. 
+    Fix: jk > 1.2.11 has a stopped flag, but then all application stop traffic
+    and session transfer from other nodes not stopped!!
+- Can't stop message replication for a spezial member and application
+   - this need a spezial cluster message and send filter at SimpleTcpCluster 
+- Don't generate cluster message when no member is at cluster!
+   - Register DeltaManager as Cluster LifecycleListener and stop cresting and sending
+   - Reduce memory consume when only local node is active
+   - Important feature when nodes crashed, and only one server exists under load...
+   
+==============================
+Nice to have:
+==============================
+- Replication ContextAttributes
+- Cluster config at engine level (user request 06/05)
+	 Register a cluster infrastructur for many vhosts
+	 configure backup systems!
+	 Add Cluster Element to digister		
+- Configure the McastService no accept every member.
+	- receive a secret key
+	- have a allow or deny list like RequestFilterValve
+	- Also receiver don't accept request from not allowed members
+- ReplicationListener and SocketReplicationListener only accept data from cluster member (low level ip restriction)
+- PooledSocketSender
+	Add more stats
+	check all Pooled sender checkKeepAlive
+- Implement a NonSerializable interface for session attributes that do not
+wish to be replicated
+        Then we must have ClusterNonSerializable at common classloader
+- Extend StandardSession if possible
+- Implement primary/secondary replication logic
+	Now we have a domain sending mode, but we can send a broadcast when 
+	local node have no backup.
+		Wait a time periode, then find a backup
+- Implement context attribute replication (?)
+	pero:
+		Also send Start/Stop messages from Context to complete cluster!
+        With 5.5.10 you can wrote a Cluster Lifecycle Manager that do this.
+            Register for AFTER_MANAGERREGISTER_EVENT and AFTER_MANAGERUNREGISTER_EVENT
+            and also to the context ServletContextAttributeEvent Listener
+            Access the Context
+            	((Manager)event.getData()).getContainer()                     	
+- Fix farm deployment for 5.5
+   pero:
+     Every start all application are deployed only to all running cluster nodes
+     	New registered nodes don't get the applications!
+     	Deploy must send a GET_ALL_APP to all other Deployers.
+        FH: Correction, you should not send GET_ALL_APP to all deployers, only
+to the main one. Which could be another property of the Member object, it
+would not make sense to transfer the same webapp over and over again.
+     		Only watchEnabled Deployer send this member all deployed application.
+		pero:
+		   Yes very true, but currently we distribute also all wars from watch node at begining.
+		   Waiting to start other nodes is only change to not got these war's!
+		   I have made some experiments to register war deployment at new memberAdded to cluster.	   
+     Add JMX Support
+     	Resend Deployed Applications to all or one cluster node.
+     	Show all watch Resource
+     	Processing Time
+     	Change fileMessage Buffersize.
+     	Start/Stop Cluster wide application
+     Deployer and Watcher sync with engine background thread! 
+     	Fixed!		
+     Last FileMessage fragment need longe ackTimeout	
+    	<Cluster ..> <Sender ... ackTimeout="60000"/> </Cluster> 	
+- Change the cluster protocol that developer can add there own data serialzable/deserialzable format (high risk)
+  
+   Currently 
+   	header 		    6 bytes (FLT2002)
+    compressflag    4 byte
+   	data.length     4 bytes
+   	data, 
+   	end header      6 bytes (TLF2003)
+   	
+   Optimized to 
+      header 		2 bytes (TC)
+      type   		1 byte
+      compressflag  1 byte
+      data.length 	4 bytes, 
+      data | <real uncompressed data.length (4 bytes)> data
+      
+      "type" means user defined type and receiver extract bytes and type and sende it to callback
+      	s. ObjectReader or SocketObjectReader
+                       
+       compress     1
+       first data 4 data bytes are the real uncompressed data length. ( Is for better memory management atr recevier side, S. XByteBuffer)
+       
+   change at DataSender.writeData and XByteBuffer and add flexible handling to ClusterSender and ClusterReceiver
+   
+- Add single sign on support
+
+==============================   
+COMPLETED
+==============================
+5.5.10 (pero)
+- add mapping sender mapping properties file (IDataSenderFactory)
+	- let advanced people eaiser implemented there own sender mode
+- We register different application with same name from different host?
+	SimpleTcpManager register manager with app name + hostname when Cluster is configured as Engine element.
+- Configured DeltaManager inside context
+	- SimpleTcpCluster setProperty and transferproperty reflect changes only to defaultMode managers
+    - Look inside SimpleTcpCluster.addManager and DeltaManager.start?
+- Session serialization eat memory but now we can send session messages with blocks...
+	When all sessions serialze after GET_ALL_SESSION is received following works
+	- find all sessions
+	- serialize a block or all sessions as byte array
+	- serialize the complete SessionMessageImpl to transfer message
+- WaitForAck mode and resend probleme
+   - Now message creator can configure resend and compress mode!
+- Add a default simple cluster config with good defaults and only
+  one cluster element inside server.xml. Setup with fastasyncmode.
+  Service Elements
+  	ReplicationTransmitter,
+  	SocketReplicationListener,
+  	McastService,
+  	ClusterSessionListener
+  	ReplicationValve
+  You can change property setting with SimpleTcpCluster prefix "sender.XXX, receiver.XXX, valve.XXX, listener.XXX, service.XXX"	
+- Fix resend GET_ALL_SESSIONS when wait ACK failed at receiver side
+- Fix that ClusterValve not remove when cluster stops 
+- Set timestamp only at first time inside SessionMessageImpl
+- Set timestamp from findsessions when handling GET_ALL_SESSION
+    - Set this timestamp to all SEND_SESSION_DATA and TRANSFER Complete messages
+    - Drop all received message inside GET_ALL_SESSION message queue (DeltaManager)    
+
+- Mcast Service as JMX MBean (change cluster domain at runtime)    
+- send cluster domain with mcast ping
+    - With sendClusterDomainOnly=true only session message from same domain are received
+    - Session only replicated to members from same domain, with sendClusterDomainOnly=false
+      at Sender (ReplicationTransmitter) session messages send to all members.
+    - GET ALL Session send to first member inside same cluster domain
+- better restart szenario at DeltaManager after failure restart (java service wrapper).
+   queue all other session events
+   as STATE Transfer Complete is received, dequeue all received sessions messages.   
+- restructure methods at DeltaManager
+- extract handleXXS methods for better DeltaManager subclassing.
+- split big get all sessions from one server into blocks of sessions and separate STATE Transfer message!
+- no complete sync sessions when GET ALL Sessions event is received.
+- add JMX API for ClusteRreceiver
+- ClusterReceiver is now Callback when message is received
+- SimpleTcpCluster only receive ClusterMessage (API change)
+       Redesign SimpleTcpCluster message receiving to ClusterReceiverBase:
+          - optimized data uncompressed
+          - better extendablity
+          - XByteBuffer only buffer bytes and don't uncompress.
+          - Add receiver JMX stats with new attribute 'Receiver.doReceivedProcessingStats'   
+          - optimized createManager and addManager that also can configured normal StandardManager 
+            to use cluster message transfer without replication.
+- add support to dynamic property transfer from SimpleTcpCluster to the Manager
+    like ReplicationTransmitter
+        All manager attributes can be configured:
+          - expireSessionsOnShutdown (false)
+          - notifySessionListenersOnReplication (true)
+          - notifyListenersOnReplication (true)
+          - maxActiveSessions (-1)
+          - timeoutAllSession (60 sec)
+          - sessionIdLength (16)
+          - processExpiresFrequency (6 - exipre all six engine background periodic event (60 sec))
+          - algorithm (MD5)
+          - entropy
+          - randomFile
+          - randomClass
+- Setup the cluster without SessionReplication Manager
+         Only a message bus.
+         Configure the bus with you ClusterListener and valves and a StandardManager         
+- reduce cpu and memory consume (Receiver)
+    - set new compress sender flag at default=false ( < CPU usage)
+    - Make compact algo 
+        currently message receive data is split at XbyteBuffer#extractPackage and
+        SimpleTcpCluster#messageDataReceived     
+- reduce memory and cpu consume (send message)
+    - set new compress sender flag at default=false ( < CPU usage)
+    - don't copy the buffer to add message header
+        transfer this from SimpleTcpCluster to DataSender pushMessage
+        successfull refactored
+    - make it possible that a subclass crypt the transfered messages
+       sub class ReplicationTransmitter and override createMessageData
+    - don't copy START and END Header for every message, instead send dirctly and DataSender.writeData.   
+- Add a flag for replicated attribute events, to enable or disable them 
+     Now can configued with notifyListenersOnReplication=false at SimpleTCPCluster
+     Also can drop HttpSessionLsitener events
+         can configued with notifySessionListenersOnReplication=false at SimpleTCPCluster
+- Refactoring DeltaManager
+- Transfer attributes from Cluster config to DeltaManager
+- Fix at 5.5.9 cluster hang bug! 
+- Add more Valve to direct cluster config
+- Add Lifecycle Listener support to direct cluster config
+- Add ClusterListener support to direct cluster config
+- Add new SocketReplicationListener
+- Add Stats to DeltaManager
+
+5.5.9  (pero)
+- JMX friendly
+    pero:  Add some MBeanSupport to SimpleTCPCluster, ReplicationTransmitter and Senders
+- Add Keep Alive and WaitForAck at async mode implementation.
+       Make this feature configurable to Sender element at server.xml
+       Is include with 5.5.8
+- Add support to new Async Mode from Rainer Jung
+       Integrated with 5.5.9
+       fastasyncqueue mode
+ 
\ No newline at end of file
diff --git a/container/modules/storeconfig/build.xml b/container/modules/storeconfig/build.xml
new file mode 100644
index 0000000..6c9d35a
--- /dev/null
+++ b/container/modules/storeconfig/build.xml
@@ -0,0 +1,132 @@
+<project name="StoreConfig" default="dist" basedir=".">
+
+
+	<!-- ===================== Initialize Property Values =================== -->
+
+	<!-- See "build.properties.sample" in the top level directory for all     -->
+	<!-- property values you must customize for successful building!!!        -->
+    <property file="../../../build.properties" />
+    <property file="../../../jakarta-tomcat-5/build.properties.default" />
+
+    <!-- Build Defaults -->
+    <property name="catalina.home"  location="../.."/>
+    <property name="catalina.build" location="../../../jakarta-tomcat-5/build"/>
+    <property name="config.build" value="${catalina.home}/modules/storeconfig/build" />
+	<property name="config.dist" value="${catalina.home}/modules/storeconfig/dist" />
+
+	<!-- Construct Catalina classpath -->
+	<path id="config.classpath">
+		<pathelement location="${catalina.build}/server/lib/catalina.jar" />
+		<pathelement location="${catalina.build}/server/lib/tomcat-util.jar" />
+		<pathelement location="${commons-logging.jar}" />
+		<pathelement location="${commons-modeler.jar}" />
+		<pathelement location="${jmx.jar}" />
+		<pathelement location="${catalina.build}/common/lib/servlet-api.jar" />
+	</path>
+
+	<!-- Source path -->
+	<path id="javadoc.sourcepath">
+		<pathelement location="src/share" />
+	</path>
+
+
+	<!-- =================== BUILD: Set compile flags ======================= -->
+	<target name="flags">
+		<!-- JDK flags -->
+		<available property="jdk.1.2.present" classname="java.util.HashMap" />
+		<available property="jdk.1.3.present" classname="java.lang.reflect.Proxy" />
+		<available property="jdk.1.4.present" classname="java.nio.Buffer" />
+	</target>
+
+
+	<!-- =================== BUILD: Set compile flags ======================= -->
+	<target name="flags.display" depends="flags" unless="flags.hide">
+
+		<echo message="--- Build environment for Catalina ---" />
+
+		<echo message="If ${property_name} is displayed, then the property is not set)" />
+
+		<echo message="--- Build options ---" />
+		<echo message="full.dist=${full.dist}" />
+		<echo message="build.sysclasspath=${build.sysclasspath}" />
+		<echo message="compile.debug=${compile.debug}" />
+		<echo message="compile.deprecation=${compile.deprecation}" />
+		<echo message="compile.optimize=${compile.optimize}" />
+		<echo message="compile.source=${compile.source}" />
+
+		<echo message="--- Ant Flags ---" />
+		<echo message="&lt;style&gt; task available (required)=${style.available}" />
+
+		<echo message="--- JDK ---" />
+		<echo message="jdk.1.2.present=${jdk.1.2.present}" />
+		<echo message="jdk.1.3.present=${jdk.1.3.present}" />
+		<echo message="jdk.1.4.present=${jdk.1.4.present}" />
+
+	</target>
+
+	<!-- =================== BUILD: Create Directories ====================== -->
+	<target name="build-prepare">
+		<mkdir dir="${catalina.build}" />
+		<mkdir dir="${catalina.build}/classes" />
+		<mkdir dir="${config.dist}" />
+	</target>
+
+
+
+
+	<!-- ================ BUILD: Compile Catalina Components ================ -->
+
+	<target name="build-catalina-config" depends="build-prepare">
+		<!-- Compile internal server components -->
+		<javac srcdir="${basedir}/src/share" destdir="${catalina.build}/classes" deprecation="${compile.deprecation}" debug="${compile.debug}" source="${compile.source}" optimize="${compile.optimize}" excludes="**/CVS/**">
+			<classpath refid="config.classpath" />
+		</javac>
+		<copy todir="${catalina.build}/classes/org/apache/catalina/storeconfig">
+			<fileset dir="${basedir}/src/share/org/apache/catalina/storeconfig">
+				<include name="*.properties" />
+				<include name="*.xml" />
+			</fileset>
+		</copy>
+	</target>
+
+
+	<!-- ================ BUILD: Create Catalina Javadocs =================== -->
+	<target name="javadoc">
+		<delete dir="${config.build}/javadoc" />
+		<mkdir dir="${config.build}/javadoc" />
+		<javadoc packagenames="org.apache.catalina.storeconfig.*" classpathref="config.classpath" sourcepathref="javadoc.sourcepath" destdir="${config.build}/javadoc" author="true" version="true" windowtitle="Catalina Internal StoreConfig API Documentation" doctitle="Catalina StoreConfig API" bottom="Copyright &#169; 2000-2004 Apache Software Foundation.  All Rights Reserved." />
+	</target>
+
+
+	<!-- ======================= BUILD: Clean Directory ===================== -->
+	<target name="build-clean">
+		<delete dir="${catalina.build}" />
+	</target>
+
+
+	<!-- ==================== BUILD: Rebuild Everything ===================== -->
+
+
+
+
+	<!-- ================ DIST: Create Distribution ========================= -->
+	<target name="dist" depends="build-catalina-config">
+
+		<jar destfile="${config.dist}/catalina-storeconfig.jar" basedir="${catalina.build}/classes">
+			<include name="org/apache/catalina/storeconfig/**" />
+			<exclude name="**/package.html" />
+            <exclude name="**/LocalStrings_*" />
+		</jar>
+	</target>
+
+    <target name="copy" depends="dist" >
+       <copy file="${config.dist}/catalina-storeconfig.jar" todir="${catalina.build}/server/lib" />
+   </target>
+
+	<!-- ======================== DIST: Clean Directory ===================== -->
+
+
+	<!-- ====================== Convenient Synonyms ========================= -->
+
+
+</project>
diff --git a/container/modules/storeconfig/docs/Readme.txt b/container/modules/storeconfig/docs/Readme.txt
new file mode 100644
index 0000000..e19c485
--- /dev/null
+++ b/container/modules/storeconfig/docs/Readme.txt
@@ -0,0 +1,63 @@
+Status:
+
+Peter Roßbach
+29.11.2004
+	
+Feature
+=========================
+	Backup context.xml
+	Backup für server.xml
+	Cluster Handling
+	Store all 5.5.5 elements
+	Context workDir default Handling
+	WatchedResource Default Handling
+	Write your own Registry
+		System Property -Dcatalina.storeconfig=file:../xxx.xml
+		${catalina.base}/conf bzw ${catalina.home}/conf
+		Classpath org.apache.catalina.storeconfig
+	JMX StoreConfig MBean
+		Store context.xml and server.xmls
+	Store Format is changeable with StoreAppenders
+	Easy extendable 
+	Connector support
+		AJP
+		HTTP
+		HTTPS
+	Context.xml and server.xml with backup				
+		 
+Example
+=========================
+	
+	<Server ...>
+	      <Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>
+          <Service ...
+		  </Service>
+    </Server>
+	
+	Usages with JMX Adaptor 
+		ObjectName : Catalina:type=StoreConfig
+		
+		important operations:
+			storeConfig			Store complete server
+			storeServer			Store Server with ObjectName"               
+			   parameter name="objectname"
+                 description="Objectname from Server"
+                 default="Catalina:type=Server"
+			   parameter name="backup"
+                 description="store Context with backup"
+               parameter name="externalAllowed"
+                 description="store all Context external that have a configFile"
+            storeContext 		Store Context from ObjectName 
+               parameter name="objectname"
+                 description="ObjectName from Context"
+                 example="Catalina:j2eeType=WebModule,name=//localhost/manager,J2EEApplication=none,J2EEServer=none
+			   parameter name="backup"
+                 description="store with Backup"
+               parameter name="externalAllowed"
+                 description="store all or store only internal server.xml context (configFile == null)"
+			
+Have fun
+Peter Rossbach
+
+
+	
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/CatalinaClusterSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/CatalinaClusterSF.java
new file mode 100644
index 0000000..9cbf0d7
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/CatalinaClusterSF.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Valve;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterDeployer;
+import org.apache.catalina.cluster.ClusterReceiver;
+import org.apache.catalina.cluster.ClusterSender;
+import org.apache.catalina.cluster.MembershipService;
+import org.apache.catalina.cluster.MessageListener;
+import org.apache.catalina.cluster.tcp.SimpleTcpCluster;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Generate Cluster Element with Membership,Sender,Receiver,Deployer and
+ * ReplicationValve
+ * 
+ * @author Peter Rossbach
+ */
+public class CatalinaClusterSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(CatalinaClusterSF.class);
+
+    /**
+     * Store the specified Cluster childs.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aCluster
+     *            Cluster whose properties are being stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aCluster,
+            StoreDescription parentDesc) throws Exception {
+        if (aCluster instanceof CatalinaCluster) {
+            CatalinaCluster cluster = (CatalinaCluster) aCluster;
+
+            // Store nested <Membership> element
+            MembershipService service = cluster.getMembershipService();
+            if (service != null) {
+                storeElement(aWriter, indent, service);
+            }
+            // Store nested <Sender> element
+            ClusterSender sender = cluster.getClusterSender();
+            if (sender != null) {
+                storeElement(aWriter, indent, sender);
+            }
+            // Store nested <Receiver> element
+            ClusterReceiver receiver = cluster.getClusterReceiver();
+            if (receiver != null) {
+                storeElement(aWriter, indent, receiver);
+            }
+            // Store nested <Deployer> element
+            ClusterDeployer deployer = cluster.getClusterDeployer();
+            if (deployer != null) {
+                storeElement(aWriter, indent, deployer);
+            }
+            // Store nested <Valve> element
+            // ClusterValve are not store at Hosts element, see
+            Valve valves[] = cluster.getValves();
+            storeElementArray(aWriter, indent, valves);
+ 
+            if (aCluster instanceof SimpleTcpCluster) {
+                // Store nested <Listener> elements
+                LifecycleListener listeners[] = ((SimpleTcpCluster)cluster).findLifecycleListeners();
+                storeElementArray(aWriter, indent, listeners);
+                // Store nested <ClusterListener> elements
+                MessageListener mlisteners[] = ((SimpleTcpCluster)cluster).findClusterListeners();
+                storeElementArray(aWriter, indent, mlisteners);
+            }            
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/ConnectorSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/ConnectorSF.java
new file mode 100644
index 0000000..fb2d03b
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/ConnectorSF.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Connector;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store Connector and Listeners
+ * 
+ * @author Peter Rossbach
+ */
+public class ConnectorSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(ConnectorSF.class);
+
+    /**
+     * Store Connector description
+     * 
+     * @param aWriter
+     * @param indent
+     * @param aConnector
+     * @throws Exception
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aConnector,
+            StoreDescription parentDesc) throws Exception {
+
+        if (aConnector instanceof Connector) {
+            Connector connector = (Connector) aConnector;
+            // Store nested <Listener> elements
+            if (connector instanceof Lifecycle) {
+                LifecycleListener listeners[] = ((Lifecycle) connector)
+                        .findLifecycleListeners();
+                storeElementArray(aWriter, indent, listeners);
+            }
+        }
+    }
+
+    protected void printOpenTag(PrintWriter aWriter, int indent, Object bean,
+            StoreDescription aDesc) throws Exception {
+        aWriter.print("<");
+        aWriter.print(aDesc.getTag());
+        storeConnectorAttribtues(aWriter, indent, bean, aDesc);
+        aWriter.println(">");
+    }
+
+    protected void storeConnectorAttribtues(PrintWriter aWriter, int indent,
+            Object bean, StoreDescription aDesc) throws Exception {
+        if (aDesc.isAttributes()) {
+            getStoreAppender().printAttributes(aWriter, indent, false, bean,
+                    aDesc);
+            /*
+             * if (bean instanceof Connector) { StoreDescription elementDesc =
+             * getRegistry().findDescription( bean.getClass().getName() +
+             * ".[ProtocolHandler]"); if (elementDesc != null) { ProtocolHandler
+             * protocolHandler = ((Connector) bean) .getProtocolHandler(); if
+             * (protocolHandler != null)
+             * getStoreAppender().printAttributes(aWriter, indent, false,
+             * protocolHandler, elementDesc); } }
+             */
+        }
+    }
+
+    protected void printTag(PrintWriter aWriter, int indent, Object bean,
+            StoreDescription aDesc) throws Exception {
+        aWriter.print("<");
+        aWriter.print(aDesc.getTag());
+        storeConnectorAttribtues(aWriter, indent, bean, aDesc);
+        aWriter.println("/>");
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/ConnectorStoreAppender.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/ConnectorStoreAppender.java
new file mode 100644
index 0000000..72cdf0a
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/ConnectorStoreAppender.java
@@ -0,0 +1,281 @@
+/**
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.catalina.connector.Connector;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+/**
+ * Store the Connector attributes. Connector has really special design. A
+ * Connector is only a startup Wrapper for a ProtocolHandler. This meant that
+ * ProtocolHandler get all there attribtues from the Connector attribtue map.
+ * Strange is that some attributes change there name and the attribute
+ * sslProtocol need a sepzial handling
+ * 
+ * @author Peter Rossbach
+ *  
+ */
+public class ConnectorStoreAppender extends StoreAppender {
+
+    protected static HashMap replacements = new HashMap();
+    static {
+        replacements.put("backlog", "acceptCount");
+        replacements.put("soLinger", "connectionLinger");
+        replacements.put("soTimeout", "connectionTimeout");
+        replacements.put("timeout", "connectionUploadTimeout");
+        replacements.put("clientauth", "clientAuth");
+        replacements.put("keystore", "keystoreFile");
+        replacements.put("randomfile", "randomFile");
+        replacements.put("rootfile", "rootFile");
+        replacements.put("keypass", "keystorePass");
+        replacements.put("keytype", "keystoreType");
+        replacements.put("protocol", "sslProtocol");
+        replacements.put("protocols", "sslProtocols");
+    }
+
+    /**
+     * Store the relevant attributes of the specified JavaBean.
+     * 
+     * @param writer
+     *            PrintWriter to which we are storing
+     * @param include
+     *            Should we include a <code>className</code> attribute?
+     * @param bean
+     *            Bean whose properties are to be rendered as attributes,
+     * @param desc
+     *            RegistryDescrpitor from this bean
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    public void printAttributes(PrintWriter writer, int indent,
+            boolean include, Object bean, StoreDescription desc)
+            throws Exception {
+
+        // Render the relevant properties of this bean
+        String className = bean.getClass().getName();
+
+        // Render a className attribute if requested
+        if (include && desc != null && !desc.isStandard()) {
+            writer.print(" className=\"");
+            writer.print(bean.getClass().getName());
+            writer.print("\"");
+        }
+
+        List propertyKeys = getPropertyKeys((Connector) bean);
+        // Create blank instance
+        Object bean2 = defaultInstance(bean);
+        for (Iterator propertyIterator = propertyKeys.iterator(); propertyIterator
+                .hasNext();) {
+            String key = (String) propertyIterator.next();
+            if (replacements.get(key) != null) {
+                key = (String) replacements.get(key);
+            }
+            Object value = (Object) IntrospectionUtils.getProperty(bean, key);
+
+            if (desc.isTransientAttribute(key)) {
+                continue; // Skip the specified exceptions
+            }
+            if (value == null) {
+                continue; // Null values are not persisted
+            }
+            if (!isPersistable(value.getClass())) {
+                continue;
+            }
+            Object value2 = IntrospectionUtils.getProperty(bean2, key);
+            if (value.equals(value2)) {
+                // The property has its default value
+                continue;
+            }
+            if (isPrintValue(bean, bean2, key, desc))
+                printValue(writer, indent, key, value);
+        }
+        String protocol = ((Connector) bean).getProtocol();
+        if (protocol != null && !"HTTP/1.1".equals(protocol))
+            super.printValue(writer, indent, "protocol", protocol);
+
+    }
+
+    /**
+     * Get all properties from Connector and current ProtocolHandler
+     * 
+     * @param bean
+     * @return List of Connector Properties
+     * @throws IntrospectionException
+     */
+    protected List getPropertyKeys(Connector bean)
+            throws IntrospectionException {
+        ArrayList propertyKeys = new ArrayList();
+        // Acquire the list of properties for this bean
+        ProtocolHandler protocolHandler = bean.getProtocolHandler();
+        // Acquire the list of properties for this bean
+        PropertyDescriptor descriptors[] = Introspector.getBeanInfo(
+                bean.getClass()).getPropertyDescriptors();
+        if (descriptors == null) {
+            descriptors = new PropertyDescriptor[0];
+        }
+        for (int i = 0; i < descriptors.length; i++) {
+            if (descriptors[i] instanceof IndexedPropertyDescriptor) {
+                continue; // Indexed properties are not persisted
+            }
+            if (!isPersistable(descriptors[i].getPropertyType())
+                    || (descriptors[i].getReadMethod() == null)
+                    || (descriptors[i].getWriteMethod() == null)) {
+                continue; // Must be a read-write primitive or String
+            }
+            if ("protocol".equals(descriptors[i].getName())
+                    || "protocolHandlerClassName".equals(descriptors[i]
+                            .getName()))
+                continue;
+            propertyKeys.add(descriptors[i].getName());
+        }
+        for (Iterator propertyIterator = protocolHandler.getAttributeNames(); propertyIterator
+                .hasNext();) {
+            Object key = propertyIterator.next();
+            if (propertyKeys.contains(key))
+                continue;
+            propertyKeys.add(key);
+        }
+        return propertyKeys;
+    }
+
+    /**
+     * print Attributes
+     * 
+     * @param aWriter
+     * @param indent
+     * @param bean
+     * @param aDesc
+     * @throws Exception
+     */
+    protected void storeConnectorAttribtues(PrintWriter aWriter, int indent,
+            Object bean, StoreDescription aDesc) throws Exception {
+        if (aDesc.isAttributes()) {
+            printAttributes(aWriter, indent, false, bean, aDesc);
+        }
+    }
+
+    /*
+     * Print the open tag for connector attributes (override)
+     * 
+     * @see org.apache.catalina.storeconfig.StoreAppender#printOpenTag(java.io.PrintWriter,
+     *      int, java.lang.Object,
+     *      org.apache.catalina.storeconfig.StoreDescription)
+     */
+    public void printOpenTag(PrintWriter aWriter, int indent, Object bean,
+            StoreDescription aDesc) throws Exception {
+        aWriter.print("<");
+        aWriter.print(aDesc.getTag());
+        storeConnectorAttribtues(aWriter, indent, bean, aDesc);
+        aWriter.println(">");
+    }
+
+    /**
+     * print a tag for connector attributes (override)
+     * 
+     * @see org.apache.catalina.storeconfig.StoreAppender#printTag(java.io.PrintWriter,
+     *      int, java.lang.Object,
+     *      org.apache.catalina.storeconfig.StoreDescription)
+     */
+    public void printTag(PrintWriter aWriter, int indent, Object bean,
+            StoreDescription aDesc) throws Exception {
+        aWriter.print("<");
+        aWriter.print(aDesc.getTag());
+        storeConnectorAttribtues(aWriter, indent, bean, aDesc);
+        aWriter.println("/>");
+    }
+
+    /**
+     * print a value but replace attribute name
+     * 
+     * @param writer
+     * @param name
+     * @param value
+     * @see org.apache.catalina.storeconfig.StoreAppender#printValue(java.io.PrintWriter,
+     *      int, java.lang.String, java.lang.Object)
+     */
+    public void printValue(PrintWriter writer, int indent, String name,
+            Object value) {
+        String repl = name;
+        if (replacements.get(name) != null) {
+            repl = (String) replacements.get(name);
+        }
+        super.printValue(writer, indent, repl, value);
+    }
+    
+    /*
+     * Print Connector Values. <ul><li> Spezial handling to default jkHome.
+     * </li><li> Don't save catalina.base path at server.xml</li><li></ul>
+     * 
+     * @see org.apache.catalina.config.StoreAppender#isPrintValue(java.lang.Object,
+     *      java.lang.Object, java.lang.String,
+     *      org.apache.catalina.config.StoreDescription)
+     */
+    public boolean isPrintValue(Object bean, Object bean2, String attrName,
+            StoreDescription desc) {
+        boolean isPrint = super.isPrintValue(bean, bean2, attrName, desc);
+        if (isPrint) {
+            if ("jkHome".equals(attrName)) {
+                Connector connector = ((Connector) bean);
+                File catalinaBase = getCatalinaBase();
+                File jkHomeBase = getJkHomeBase((String) connector
+                        .getProperty("jkHome"), catalinaBase);
+                isPrint = !catalinaBase.equals(jkHomeBase);
+
+            }
+        }
+        return isPrint;
+    }
+
+    protected File getCatalinaBase() {
+
+        File appBase;
+        File file = new File(System.getProperty("catalina.base"));
+        try {
+            file = file.getCanonicalFile();
+        } catch (IOException e) {
+        }
+        return (file);
+    }
+
+    protected File getJkHomeBase(String jkHome, File appBase) {
+
+        File jkHomeBase;
+        File file = new File(jkHome);
+        if (!file.isAbsolute())
+            file = new File(appBase, jkHome);
+        try {
+            jkHomeBase = file.getCanonicalFile();
+        } catch (IOException e) {
+            jkHomeBase = file;
+        }
+        return (jkHomeBase);
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/Constants.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/Constants.java
new file mode 100644
index 0000000..298b018
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/Constants.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.storeconfig";
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/GlobalNamingResourcesSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/GlobalNamingResourcesSF.java
new file mode 100644
index 0000000..0df9e3d
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/GlobalNamingResourcesSF.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * store server.xml GlobalNamingResource.
+ * 
+ * @author Peter Rossbach 
+ *  
+ */
+public class GlobalNamingResourcesSF extends StoreFactoryBase {
+    private static Log log = LogFactory.getLog(GlobalNamingResourcesSF.class);
+
+    /*
+     * Store with NamingResource Factory
+     * 
+     * @see org.apache.catalina.storeconfig.IStoreFactory#store(java.io.PrintWriter,
+     *      int, java.lang.Object)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception {
+
+        if (aElement instanceof NamingResources) {
+
+            StoreDescription elementDesc = getRegistry().findDescription(
+                    NamingResources.class.getName()
+                            + ".[GlobalNamingResources]");
+
+            if (elementDesc != null) {
+                getStoreAppender().printIndent(aWriter, indent + 2);
+                getStoreAppender().printOpenTag(aWriter, indent + 2, aElement,
+                        elementDesc);
+                NamingResources resources = (NamingResources) aElement;
+                StoreDescription resourcesdesc = getRegistry().findDescription(
+                        NamingResources.class.getName());
+                if (resourcesdesc != null) {
+                    resourcesdesc.getStoreFactory().store(aWriter, indent + 2,
+                            resources);
+                } else {
+                    if(log.isWarnEnabled())
+                        log.warn("Can't find NamingRsources Store Factory!");
+                }
+                    
+                getStoreAppender().printIndent(aWriter, indent + 2);
+                getStoreAppender().printCloseTag(aWriter, elementDesc);
+            } else {
+                if (log.isWarnEnabled())
+                    log.warn("Descriptor for element" + aElement.getClass()
+                            + " not configured!");
+            }
+        } else {
+            if (log.isWarnEnabled())
+                log.warn("wrong element " + aElement.getClass());
+
+        }
+    }
+}
+
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/IDynamicPropertyStoreAppender.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/IDynamicPropertyStoreAppender.java
new file mode 100644
index 0000000..1bfddc8
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/IDynamicPropertyStoreAppender.java
@@ -0,0 +1,136 @@
+/**
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.catalina.cluster.util.IDynamicProperty;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+/**
+ * Store the IDynamicProperty attributes. 
+ * 
+ * @author Peter Rossbach
+ *  
+ */
+public class IDynamicPropertyStoreAppender extends StoreAppender {
+
+    /**
+     * Store the relevant attributes of the specified JavaBean.
+     * 
+     * @param writer
+     *            PrintWriter to which we are storing
+     * @param include
+     *            Should we include a <code>className</code> attribute?
+     * @param bean
+     *            Bean whose properties are to be rendered as attributes,
+     * @param desc
+     *            RegistryDescrpitor from this bean
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    public void printAttributes(PrintWriter writer, int indent,
+            boolean include, Object bean, StoreDescription desc)
+            throws Exception {
+
+        // Render the relevant properties of this bean
+        String className = bean.getClass().getName();
+
+        // Render a className attribute if requested
+        if (include && desc != null && !desc.isStandard()) {
+            writer.print(" className=\"");
+            writer.print(bean.getClass().getName());
+            writer.print("\"");
+        }
+
+        if (bean instanceof IDynamicProperty) {
+            List propertyKeys = getPropertyKeys((IDynamicProperty) bean);
+            // Create blank instance
+            Object bean2 = defaultInstance(bean);
+            for (Iterator propertyIterator = propertyKeys.iterator(); propertyIterator
+                    .hasNext();) {
+                String key = (String) propertyIterator.next();
+                Object value = (Object) IntrospectionUtils.getProperty(bean,
+                        key);
+
+                if (desc.isTransientAttribute(key)) {
+                    continue; // Skip the specified exceptions
+                }
+                if (value == null) {
+                    continue; // Null values are not persisted
+                }
+                if (!isPersistable(value.getClass())) {
+                    continue;
+                }
+                Object value2 = IntrospectionUtils.getProperty(bean2, key);
+                if (value.equals(value2)) {
+                    // The property has its default value
+                    continue;
+                }
+                if (isPrintValue(bean, bean2, key, desc))
+                    printValue(writer, indent, key, value);
+            }
+        }
+    }
+
+    /**
+     * Get all properties from ReplicationTransmitter (also dynamic properties)
+     * 
+     * @param bean
+     * @return List of Connector Properties
+     * @throws IntrospectionException
+     */
+    protected List getPropertyKeys(IDynamicProperty bean)
+            throws IntrospectionException {
+        ArrayList propertyKeys = new ArrayList();
+        // Acquire the list of properties for this bean
+        PropertyDescriptor descriptors[] = Introspector.getBeanInfo(
+                bean.getClass()).getPropertyDescriptors();
+        if (descriptors == null) {
+            descriptors = new PropertyDescriptor[0];
+        }
+        for (int i = 0; i < descriptors.length; i++) {
+            if (descriptors[i] instanceof IndexedPropertyDescriptor) {
+                continue; // Indexed properties are not persisted
+            }
+            if (!isPersistable(descriptors[i].getPropertyType())
+                    || (descriptors[i].getReadMethod() == null)
+                    || (descriptors[i].getWriteMethod() == null)) {
+                continue; // Must be a read-write primitive or String
+            }
+            propertyKeys.add(descriptors[i].getName());
+        }
+        for (Iterator propertyIterator = bean.getPropertyNames(); propertyIterator
+                .hasNext();) {
+            Object key = propertyIterator.next();
+            if (propertyKeys.contains(key))
+                continue;
+            if ("className".equals(key))
+                continue;
+            propertyKeys.add(key);
+        }
+        return propertyKeys;
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/IStoreConfig.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/IStoreConfig.java
new file mode 100644
index 0000000..f020dca
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/IStoreConfig.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+
+/**
+ * @author Peter Rossbach 
+ *  
+ */
+public interface IStoreConfig {
+
+    /**
+     * Get Configuration Registry
+     * 
+     * @return aRegistry that handle the store operations
+     */
+    StoreRegistry getRegistry();
+
+    /**
+     * Set Configuration Registry
+     * 
+     * @param aRegistry
+     *            aregistry that handle the store operations
+     */
+    void setRegistry(StoreRegistry aRegistry);
+
+    /**
+     * Store the current StoreFactory Server.
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    void storeConfig() throws Exception;
+
+    /**
+     * Store the specified Server properties.
+     * 
+     * @param aServer
+     *            Object to be stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    void store(Server aServer) throws Exception;
+
+    /**
+     * Store the specified Server properties.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aServer
+     *            Object to be stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    void store(PrintWriter aWriter, int indent, Server aServer);
+
+    /**
+     * Store the specified Service properties.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aService
+     *            Object to be stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    void store(PrintWriter aWriter, int indent, Service aService);
+
+    /**
+     * Store the specified Host properties.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aHost
+     *            Object to be stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    void store(PrintWriter aWriter, int indent, Host aHost);
+
+    /**
+     * Store the specified Context properties.
+     * 
+     * @param aContext
+     *            Object to be stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    void store(Context aContext);
+
+    /**
+     * Store the specified Context properties.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aContext
+     *            Object to be stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    void store(PrintWriter aWriter, int indent, Context aContext);
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/IStoreFactory.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/IStoreFactory.java
new file mode 100644
index 0000000..1e147f2
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/IStoreFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+/**
+ * @author Peter Rossbach 
+ *  
+ */
+public interface IStoreFactory {
+    StoreAppender getStoreAppender();
+
+    void setStoreAppender(StoreAppender storeWriter);
+
+    void setRegistry(StoreRegistry aRegistry);
+
+    StoreRegistry getRegistry();
+
+    void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception;
+
+    void storeXMLHead(PrintWriter aWriter);
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/InstanceListenerSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/InstanceListenerSF.java
new file mode 100644
index 0000000..b351219
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/InstanceListenerSF.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store Context InstanceListener
+ * 
+ * @author Peter Rossbach 
+ *  
+ */
+public class InstanceListenerSF extends StoreFactoryBase {
+    private static Log log = LogFactory.getLog(InstanceListenerSF.class);
+
+    /*
+     * Store nested Element Value Arrays
+     * 
+     * @see org.apache.catalina.config.IStoreFactory#store(java.io.PrintWriter,
+     *      int, java.lang.Object)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception {
+        if (aElement instanceof StandardContext) {
+            StoreDescription elementDesc = getRegistry().findDescription(
+                    aElement.getClass().getName() + ".[InstanceListener]");
+            String[] listeners = ((StandardContext) aElement)
+                    .findInstanceListeners();
+            if (elementDesc != null) {
+                if (log.isDebugEnabled())
+                    log.debug("store " + elementDesc.getTag() + "( " + aElement
+                            + " )");
+                getStoreAppender().printTagArray(aWriter, "InstanceListener",
+                        indent, listeners);
+            }
+        } else {
+            if (log.isWarnEnabled())
+                log.warn("Descriptor for element" + aElement.getClass()
+                        + ".[InstanceListener] not configured!");
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/LoaderSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/LoaderSF.java
new file mode 100644
index 0000000..0dc3a94
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/LoaderSF.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.Loader;
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store Loader Element.
+ * 
+ * @author Peter Rossbach
+ */
+public class LoaderSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(LoaderSF.class);
+
+    /**
+     * Store the only the Loader elements, when not default
+     * 
+     * @see NamingResourcesSF#storeChilds(PrintWriter, int, Object, StoreDescription)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception {
+        StoreDescription elementDesc = getRegistry().findDescription(
+                aElement.getClass());
+        if (elementDesc != null) {
+            Loader loader = (Loader) aElement;
+            if (!isDefaultLoader(loader)) {
+                if (log.isDebugEnabled())
+                    log.debug("store " + elementDesc.getTag() + "( " + aElement
+                            + " )");
+                getStoreAppender().printIndent(aWriter, indent + 2);
+                getStoreAppender().printTag(aWriter, indent + 2, loader,
+                        elementDesc);
+            }
+        } else {
+            if (log.isWarnEnabled()) {
+                log
+                        .warn("Descriptor for element"
+                                + aElement.getClass()
+                                + " not configured or element class not StandardManager!");
+            }
+        }
+    }
+
+    /**
+     * Is this an instance of the default <code>Loader</code> configuration,
+     * with all-default properties?
+     * 
+     * @param loader
+     *            Loader to be tested
+     */
+    protected boolean isDefaultLoader(Loader loader) {
+
+        if (!(loader instanceof WebappLoader)) {
+            return (false);
+        }
+        WebappLoader wloader = (WebappLoader) loader;
+        if ((wloader.getDelegate() != false)
+                || !wloader.getLoaderClass().equals(
+                        "org.apache.catalina.loader.WebappClassLoader")) {
+            return (false);
+        }
+        return (true);
+    }
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/LocalStrings.properties b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/LocalStrings.properties
new file mode 100644
index 0000000..253d4fb
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/LocalStrings.properties
@@ -0,0 +1,2 @@
+factory.storeTag=store tag {0} ( Object: {1} )
+factory.storeNoDescriptor=Descriptor for element class {0} not configured!
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/ManagerSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/ManagerSF.java
new file mode 100644
index 0000000..d496c44
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/ManagerSF.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.session.StandardManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store server.xml Manager element
+ * 
+ * @author Peter Rossbach
+ */
+public class ManagerSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(ManagerSF.class);
+
+    /**
+     * Store the only the Manager elements
+     * 
+     * @see NamingResourcesSF#storeChilds(PrintWriter, int, Object, StoreDescription)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception {
+        StoreDescription elementDesc = getRegistry().findDescription(
+                aElement.getClass());
+        if (elementDesc != null && aElement instanceof StandardManager) {
+            StandardManager manager = (StandardManager) aElement;
+            if (!isDefaultManager(manager)) {
+                if (log.isDebugEnabled())
+                    log.debug(sm.getString("factory.storeTag", elementDesc
+                            .getTag(), aElement));
+                getStoreAppender().printIndent(aWriter, indent + 2);
+                getStoreAppender().printTag(aWriter, indent + 2, manager,
+                        elementDesc);
+            }
+        } else {
+            if (log.isWarnEnabled())
+                log.warn(sm.getString("factory.storeNoDescriptor", aElement
+                        .getClass()));
+        }
+    }
+
+    /**
+     * Is this an instance of the default <code>Manager</code> configuration,
+     * with all-default properties?
+     * 
+     * @param smanager
+     *            Manager to be tested
+     */
+    protected boolean isDefaultManager(StandardManager smanager) {
+
+        if (!"SESSIONS.ser".equals(smanager.getPathname())
+                || !"java.security.SecureRandom".equals(smanager
+                        .getRandomClass())
+                || (smanager.getMaxActiveSessions() != -1)
+                || !"MD5".equals(smanager.getAlgorithm())) {
+            return (false);
+        }
+        return (true);
+
+    }
+
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/NamingResourcesSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/NamingResourcesSF.java
new file mode 100644
index 0000000..04ebb34
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/NamingResourcesSF.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.deploy.ContextEjb;
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.ContextLocalEjb;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.ContextResourceEnvRef;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store server.xml elements Resources at context and GlobalNamingResources
+ * 
+ * @author Peter Rossbach
+ *  
+ */
+public class NamingResourcesSF extends StoreFactoryBase {
+    private static Log log = LogFactory.getLog(NamingResourcesSF.class);
+
+    /**
+     * Store the only the NamingResources elements
+     * 
+     * @see NamingResourcesSF#storeChilds(PrintWriter, int, Object, StoreDescription)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception {
+        StoreDescription elementDesc = getRegistry().findDescription(
+                aElement.getClass());
+        if (elementDesc != null) {
+            if (log.isDebugEnabled())
+                log.debug("store " + elementDesc.getTag() + "( " + aElement
+                        + " )");
+            storeChilds(aWriter, indent, aElement, elementDesc);
+        } else {
+            if (log.isWarnEnabled())
+                log.warn("Descriptor for element" + aElement.getClass()
+                        + " not configured!");
+        }
+    }
+
+    /**
+     * Store the specified NamingResources properties.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aElement
+     *            Object whose properties are being stored
+     * @param elementDesc
+     *            element descriptor
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     * 
+     * @see org.apache.catalina.storeconfig.StoreFactoryBase#storeChilds(java.io.PrintWriter,
+     *      int, java.lang.Object, StoreDescription)
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aElement,
+            StoreDescription elementDesc) throws Exception {
+
+        if (aElement instanceof NamingResources) {
+            NamingResources resources = (NamingResources) aElement;
+            // Store nested <Ejb> elements
+            ContextEjb[] ejbs = resources.findEjbs();
+            storeElementArray(aWriter, indent, ejbs);
+            // Store nested <Environment> elements
+            ContextEnvironment[] envs = resources.findEnvironments();
+            storeElementArray(aWriter, indent, envs);
+            // Store nested <LocalEjb> elements
+            ContextLocalEjb[] lejbs = resources.findLocalEjbs();
+            storeElementArray(aWriter, indent, lejbs);
+
+            // Store nested <Resource> elements
+            ContextResource[] dresources = resources.findResources();
+            storeElementArray(aWriter, indent, dresources);
+
+            // Store nested <ResourceEnvRef> elements
+            ContextResourceEnvRef[] resEnv = resources.findResourceEnvRefs();
+            storeElementArray(aWriter, indent, resEnv);
+
+            // Store nested <ResourceLink> elements
+            ContextResourceLink[] resourceLinks = resources.findResourceLinks();
+            storeElementArray(aWriter, indent, resourceLinks);
+        }
+    }
+}
+
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/PersistentManagerSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/PersistentManagerSF.java
new file mode 100644
index 0000000..3aba8e4
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/PersistentManagerSF.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.Store;
+import org.apache.catalina.session.PersistentManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * store server.xml PersistentManager element with nested "Store"
+ * 
+ * @author Peter Rossbach
+ */
+public class PersistentManagerSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(PersistentManagerSF.class);
+
+    /**
+     * Store the specified PersistentManager properties.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aManager
+     *            PersistentManager whose properties are being stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aManager,
+            StoreDescription parentDesc) throws Exception {
+        if (aManager instanceof PersistentManager) {
+            PersistentManager manager = (PersistentManager) aManager;
+
+            // Store nested <Manager> elements
+            Store store = manager.getStore();
+            storeElement(aWriter, indent, store);
+
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardContextSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardContextSF.java
new file mode 100644
index 0000000..0d598af
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardContextSF.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.directory.DirContext;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.deploy.ApplicationParameter;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.naming.resources.ProxyDirContext;
+
+/**
+ * Store server.xml Context element with all childs
+ * <ul>
+ * <li>Store all context at server.xml</li>
+ * <li>Store existing app.xml context a conf/enginename/hostname/app.xml</li>
+ * <li>Store with backup</li>
+ * </ul>
+ * 
+ * @author Peter Rossbach
+ */
+public class StandardContextSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(StandardContextSF.class);
+
+    /*
+     * Store a Context as Separate file as configFile value from context exists.
+     * filename can be relative to catalina.base.
+     * 
+     * @see org.apache.catalina.config.IStoreFactory#store(java.io.PrintWriter,
+     *      int, java.lang.Object)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aContext)
+            throws Exception {
+
+        if (aContext instanceof StandardContext) {
+            StoreDescription desc = getRegistry().findDescription(
+                    aContext.getClass());
+            if (desc.isStoreSeparate()) {
+                String configFile = ((StandardContext) aContext)
+                        .getConfigFile();
+                if (configFile != null) {
+                    if (desc.isExternalAllowed()) {
+                        if (desc.isBackup())
+                            storeWithBackup((StandardContext) aContext);
+                        else
+                            storeContextSeparate(aWriter, indent,
+                                    (StandardContext) aContext);
+                        return;
+                    }
+                }
+            }
+        }
+        super.store(aWriter, indent, aContext);
+
+    }
+
+    /**
+     * Store a Context without backup add separate file or when configFile =
+     * null a aWriter.
+     * 
+     * @param aWriter
+     * @param indent
+     * @param aContext
+     * @throws Exception
+     */
+    protected void storeContextSeparate(PrintWriter aWriter, int indent,
+            StandardContext aContext) throws Exception {
+        String configFile = aContext.getConfigFile();
+        PrintWriter writer = null;
+        if (configFile != null) {
+            File config = new File(configFile);
+            if (!config.isAbsolute()) {
+                config = new File(System.getProperty("catalina.base"),
+                        configFile);
+            }
+            if (log.isInfoEnabled())
+                log.info("Store Context " + aContext.getPath()
+                        + " separate at file " + config);
+            try {
+                writer = new PrintWriter(new OutputStreamWriter(
+                        new FileOutputStream(config), getRegistry()
+                                .getEncoding()));
+                storeXMLHead(writer);
+                super.store(writer, -2, aContext);
+            } finally {
+                if (writer != null) {
+                    try {
+                        writer.flush();
+                    } catch (Exception e) {
+                        ;
+                    }
+                    try {
+                        writer.close();
+                    } catch (Throwable t) {
+                        ;
+                    }
+                }
+            }
+        } else {
+            super.store(aWriter, indent, aContext);
+        }
+    }
+
+    /**
+     * Store the Context with a Backup
+     * 
+     * @param aContext
+     * @throws Exception
+     */
+    protected void storeWithBackup(StandardContext aContext) throws Exception {
+        StoreFileMover mover = getConfigFileWriter((Context) aContext);
+        if (mover != null) {
+            if (log.isInfoEnabled())
+                log.info("Store Context " + aContext.getPath()
+                        + " separate with backup (at file "
+                        + mover.getConfigSave() + " )");
+            PrintWriter writer = mover.getWriter();
+            try {
+                storeXMLHead(writer);
+                super.store(writer, -2, aContext);
+            } finally {
+                // Flush and close the output file
+                try {
+                    writer.flush();
+                } catch (Exception e) {
+                    log.error(e);
+                }
+                try {
+                    writer.close();
+                } catch (Exception e) {
+                    throw (e);
+                }
+            }
+            mover.move();
+        }
+    }
+
+    /**
+     * Get explicit writer for context (context.getConfigFile()).
+     * 
+     * @param context
+     * @return The file mover
+     * @throws IOException
+     */
+    protected StoreFileMover getConfigFileWriter(Context context)
+            throws IOException {
+        String configFile = context.getConfigFile();
+        PrintWriter writer = null;
+        StoreFileMover mover = null;
+        if (configFile != null) {
+            File config = new File(configFile);
+            if (!config.isAbsolute()) {
+                config = new File(System.getProperty("catalina.base"),
+                        configFile);
+            }
+            // Open an output writer for the new configuration file
+            mover = new StoreFileMover("", config.getCanonicalPath(),
+                    getRegistry().getEncoding());
+        }
+        return mover;
+    }
+
+    /**
+     * Store the specified Host properties.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aContext
+     *            Context whose properties are being stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aContext,
+            StoreDescription parentDesc) throws Exception {
+        if (aContext instanceof StandardContext) {
+            StandardContext context = (StandardContext) aContext;
+            // Store nested <Listener> elements
+            if (context instanceof Lifecycle) {
+                LifecycleListener listeners[] = context
+                        .findLifecycleListeners();
+                storeElementArray(aWriter, indent, listeners);
+            }
+            // Store nested <Valve> elements
+            if (context instanceof Pipeline) {
+                Valve valves[] = ((Pipeline) context).getValves();
+                storeElementArray(aWriter, indent, valves);
+            }
+
+            // Store nested <Loader> elements
+            Loader loader = context.getLoader();
+            storeElement(aWriter, indent, loader);
+
+            // Store nested <Manager> elements
+            Manager manager = context.getManager();
+            storeElement(aWriter, indent, manager);
+
+            // Store nested <Realm> element
+            Realm realm = context.getRealm();
+            if (realm != null) {
+                Realm parentRealm = null;
+                // @TODO is this case possible?
+                if (context.getParent() != null) {
+                    parentRealm = context.getParent().getRealm();
+                }
+                if (realm != parentRealm) {
+                    storeElement(aWriter, indent, realm);
+
+                }
+            }
+            // Store nested resources
+            DirContext resources = context.getResources();
+            if (resources instanceof ProxyDirContext)
+                resources = ((ProxyDirContext) resources).getDirContext();
+            storeElement(aWriter, indent, resources);
+
+            // Store nested <InstanceListener> elements
+            String iListeners[] = context.findInstanceListeners();
+            getStoreAppender().printTagArray(aWriter, "InstanceListener",
+                    indent + 2, iListeners);
+
+            // Store nested <WrapperListener> elements
+            String wLifecycles[] = context.findWrapperLifecycles();
+            getStoreAppender().printTagArray(aWriter, "WrapperListener",
+                    indent + 2, wLifecycles);
+            // Store nested <WrapperLifecycle> elements
+            String wListeners[] = context.findWrapperListeners();
+            getStoreAppender().printTagArray(aWriter, "WrapperLifecycle",
+                    indent + 2, wListeners);
+
+            // Store nested <Parameter> elements
+            ApplicationParameter[] appParams = context
+                    .findApplicationParameters();
+            storeElementArray(aWriter, indent, appParams);
+
+            // Store nested naming resources elements (EJB,Resource,...)
+            NamingResources nresources = context.getNamingResources();
+            storeElement(aWriter, indent, nresources);
+
+            // Store nested watched resources <WatchedResource>
+            String[] wresources = context.findWatchedResources();
+            wresources = filterWatchedResources(context, wresources);
+            getStoreAppender().printTagArray(aWriter, "WatchedResource",
+                    indent + 2, wresources);
+        }
+    }
+
+    /**
+     * Return a File object representing the "configuration root" directory for
+     * our associated Host.
+     */
+    protected File configBase(Context context) {
+
+        File file = new File(System.getProperty("catalina.base"), "conf");
+        Container host = (Host) context.getParent();
+
+        if ((host != null) && (host instanceof Host)) {
+            Container engine = host.getParent();
+            if ((engine != null) && (engine instanceof Engine)) {
+                file = new File(file, engine.getName());
+            }
+            file = new File(file, host.getName());
+            try {
+                file = file.getCanonicalFile();
+            } catch (IOException e) {
+                log.error(e);
+            }
+        }
+        return (file);
+
+    }
+
+    /**
+     * filter out the default watched resources
+     * 
+     * @param context
+     * @param wresources
+     * @return The watched resources
+     * @throws IOException
+     * TODO relative watchedresource
+     * TODO absolute handling configFile
+     * TODO Filename case handling for Windows?
+     * TODO digester variable subsitution $catalina.base, $catalina.home
+     */
+    protected String[] filterWatchedResources(StandardContext context,
+            String[] wresources) throws IOException {
+        File configBase = configBase(context);
+        String confContext = new File(System.getProperty("catalina.base"),
+                "conf/context.xml").getCanonicalPath();
+        String confHostDefault = new File(configBase, "context.xml.default")
+                .getCanonicalPath();
+        String configFile = context.getConfigFile();
+        String webxml = "WEB-INF/web.xml" ;
+        
+        List resource = new ArrayList();
+        for (int i = 0; i < wresources.length; i++) {
+
+            if (wresources[i].equals(confContext))
+                continue;
+            if (wresources[i].equals(confHostDefault))
+                continue;
+            if (wresources[i].equals(configFile))
+                continue;
+            if (wresources[i].equals(webxml))
+                continue;
+            resource.add(wresources[i]);
+        }
+        return (String[]) resource.toArray(new String[resource.size()]);
+    }
+
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardEngineSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardEngineSF.java
new file mode 100644
index 0000000..78cd0ef
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardEngineSF.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store server.xml Element Engine
+ * 
+ * @author Peter Rossbach
+ */
+public class StandardEngineSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(StandardEngineSF.class);
+
+    /**
+     * Store the specified Engine properties.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aEngine
+     *            Object whose properties are being stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aEngine,
+            StoreDescription parentDesc) throws Exception {
+        if (aEngine instanceof StandardEngine) {
+            StandardEngine engine = (StandardEngine) aEngine;
+            // Store nested <Listener> elements
+            if (engine instanceof Lifecycle) {
+                LifecycleListener listeners[] = ((Lifecycle) engine)
+                        .findLifecycleListeners();
+                storeElementArray(aWriter, indent, listeners);
+            }
+
+            // Store nested <Realm> element
+            Realm realm = engine.getRealm();
+            if (realm != null) {
+                Realm parentRealm = null;
+                // TODO is this case possible? (see it a old Server 5.0 impl)
+                if (engine.getParent() != null) {
+                    parentRealm = engine.getParent().getRealm();
+                }
+                if (realm != parentRealm) {
+                    storeElement(aWriter, indent, realm);
+
+                }
+            }
+
+            // Store nested <Valve> elements
+            if (engine instanceof Pipeline) {
+                Valve valves[] = ((Pipeline) engine).getValves();
+                storeElementArray(aWriter, indent, valves);
+
+            }
+            // store all <Host> elements
+            Container children[] = engine.findChildren();
+            storeElementArray(aWriter, indent, children);
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardHostSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardHostSF.java
new file mode 100644
index 0000000..9edade2
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardHostSF.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Valve;
+import org.apache.catalina.cluster.ClusterValve;
+import org.apache.catalina.core.StandardHost;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store server.xml Element Host
+ * 
+ * @author Peter Rossbach
+ */
+public class StandardHostSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(StandardHostSF.class);
+
+    /**
+     * Store the specified Host properties and childs
+     * (Listener,Alias,Realm,Valve,Cluster, Context)
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aHost
+     *            Host whose properties are being stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aHost,
+            StoreDescription parentDesc) throws Exception {
+        if (aHost instanceof StandardHost) {
+            StandardHost host = (StandardHost) aHost;
+            // Store nested <Listener> elements
+            if (host instanceof Lifecycle) {
+                LifecycleListener listeners[] = ((Lifecycle) host)
+                        .findLifecycleListeners();
+                storeElementArray(aWriter, indent, listeners);
+            }
+
+            // Store nested <Alias> elements
+            String aliases[] = host.findAliases();
+            getStoreAppender().printTagArray(aWriter, "Alias", indent + 2,
+                    aliases);
+
+            // Store nested <Realm> element
+            Realm realm = host.getRealm();
+            if (realm != null) {
+                Realm parentRealm = null;
+                if (host.getParent() != null) {
+                    parentRealm = host.getParent().getRealm();
+                }
+                if (realm != parentRealm) {
+                    storeElement(aWriter, indent, realm);
+                }
+            }
+
+            // Store nested <Valve> elements
+            if (host instanceof Pipeline) {
+                Valve valves[] = ((Pipeline) host).getValves();
+                if(valves != null && valves.length > 0 ) {
+                    List hostValves = new ArrayList() ;
+                    for(int i = 0 ; i < valves.length ; i++ ) {
+                        if(!( valves[i] instanceof ClusterValve))
+                            hostValves.add(valves[i]);
+                    }                
+                    storeElementArray(aWriter, indent, hostValves.toArray());
+                }
+            }
+
+            // store all <Cluster> elements
+            Cluster cluster = host.getCluster();
+            if (cluster != null) {
+                storeElement(aWriter, indent, cluster);
+            }
+
+            // store all <Context> elements
+            Container children[] = host.findChildren();
+            storeElementArray(aWriter, indent, children);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardServerSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardServerSF.java
new file mode 100644
index 0000000..0786ee8
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardServerSF.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Service;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.mbeans.ServerLifecycleListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store server.xml Server element and childs (
+ * Listener,GlobalNamingResource,Service)
+ * 
+ * @author Peter Rossbach
+ */
+public class StandardServerSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(StandardServerSF.class);
+
+    /**
+     * Store the specified Server properties.
+     * 
+     * @param aWriter
+     *            PrintWriter to which we are storing
+     * @param indent
+     *            Number of spaces to indent this element
+     * @param aServer
+     *            Object to be stored
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     * @see org.apache.catalina.storeconfig.IStoreFactory#store(java.io.PrintWriter,
+     *      int, java.lang.Object)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aServer)
+            throws Exception {
+        storeXMLHead(aWriter);
+        super.store(aWriter, indent, aServer);
+    }
+
+    /**
+     * Store Childs from this StandardServer descrition
+     * 
+     * @param aWriter
+     * @param indent
+     * @param aObject
+     * @param parentDesc
+     * @throws Exception
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aObject,
+            StoreDescription parentDesc) throws Exception {
+        if (aObject instanceof StandardServer) {
+            StandardServer server = (StandardServer) aObject;
+            // Store nested <Listener> elements
+            if (server instanceof Lifecycle) {
+                LifecycleListener listeners[] = ((Lifecycle) server)
+                        .findLifecycleListeners();
+                storeElementArray(aWriter, indent, listeners);
+                LifecycleListener listener = null;
+                for (int i = 0; listener == null && i < listeners.length; i++)
+                    if (listeners[i] instanceof ServerLifecycleListener)
+                        listener = listeners[i];
+                if (listener != null) {
+                    StoreDescription elementDesc = getRegistry()
+                            .findDescription(
+                                    StandardServer.class.getName()
+                                            + ".[ServerLifecycleListener]");
+                    if (elementDesc != null) {
+                        elementDesc.getStoreFactory().store(aWriter, indent,
+                                listener);
+                    }
+                }
+            }
+            // Store nested <GlobalNamingResources> element
+            NamingResources globalNamingResources = server
+                    .getGlobalNamingResources();
+            StoreDescription elementDesc = getRegistry().findDescription(
+                    NamingResources.class.getName()
+                            + ".[GlobalNamingResources]");
+            if (elementDesc != null) {
+                elementDesc.getStoreFactory().store(aWriter, indent,
+                        globalNamingResources);
+            }
+            // Store nested <Service> elements
+            Service services[] = server.findServices();
+            storeElementArray(aWriter, indent, services);
+        }
+    }
+
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardServiceSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardServiceSF.java
new file mode 100644
index 0000000..f44a651
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StandardServiceSF.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store server.xml Element Service and all childs 
+ * @author Peter Rossbach (pero@apache.org)
+ */
+public class StandardServiceSF extends StoreFactoryBase {
+
+    private static Log log = LogFactory.getLog(StandardServiceSF.class);
+
+    /**
+     * Store Childs from this StandardService description
+     * 
+     * @param aWriter
+     * @param indent
+     * @param aService
+     * @throws Exception
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aService,
+            StoreDescription parentDesc) throws Exception {
+        if (aService instanceof StandardService) {
+            StandardService service = (StandardService) aService;
+            // Store nested <Listener> elements
+            if (service instanceof Lifecycle) {
+                LifecycleListener listeners[] = ((Lifecycle) service)
+                        .findLifecycleListeners();
+                storeElementArray(aWriter, indent, listeners);
+            }
+
+            Connector connectors[] = service.findConnectors();
+            storeElementArray(aWriter, indent, connectors);
+
+            // Store nested <Engine> element (or other appropriate container)
+            Container container = service.getContainer();
+            if (container != null) {
+                StoreDescription elementDesc = getRegistry().findDescription(
+                        container.getClass());
+                if (elementDesc != null) {
+                    IStoreFactory factory = elementDesc.getStoreFactory();
+                    factory.store(aWriter, indent, container);
+                }
+            }
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreAppender.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreAppender.java
new file mode 100644
index 0000000..3671476
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreAppender.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.PrintWriter;
+import java.util.Iterator;
+
+import org.apache.catalina.deploy.ResourceBase;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+/**
+ * StoreAppends generate really the xml tag elements
+ * 
+ * @author Peter Rossbach
+ *  
+ */
+public class StoreAppender {
+    private static Log log = LogFactory.getLog(StoreAppender.class);
+
+    /**
+     * The set of classes that represent persistable properties.
+     */
+    private static Class persistables[] = { String.class, Integer.class,
+            Integer.TYPE, Boolean.class, Boolean.TYPE, Byte.class, Byte.TYPE,
+            Character.class, Character.TYPE, Double.class, Double.TYPE,
+            Float.class, Float.TYPE, Long.class, Long.TYPE, Short.class,
+            Short.TYPE, };
+
+    /**
+     * print the closing tag
+     * 
+     * @param aWriter
+     * @param aDesc
+     * @throws Exception
+     */
+    public void printCloseTag(PrintWriter aWriter, StoreDescription aDesc)
+            throws Exception {
+        aWriter.print("</");
+        aWriter.print(aDesc.getTag());
+        aWriter.println(">");
+    }
+
+    /**
+     * print only the open tag with all attributes
+     * 
+     * @param aWriter
+     * @param indent
+     * @param bean
+     * @param aDesc
+     * @throws Exception
+     */
+    public void printOpenTag(PrintWriter aWriter, int indent, Object bean,
+            StoreDescription aDesc) throws Exception {
+        aWriter.print("<");
+        aWriter.print(aDesc.getTag());
+        if (aDesc.isAttributes() && bean != null)
+            printAttributes(aWriter, indent, bean, aDesc);
+        aWriter.println(">");
+    }
+
+    /**
+     * Print tag with all attributes
+     * 
+     * @param aWriter
+     * @param indent
+     * @param bean
+     * @param aDesc
+     * @throws Exception
+     */
+    public void printTag(PrintWriter aWriter, int indent, Object bean,
+            StoreDescription aDesc) throws Exception {
+        aWriter.print("<");
+        aWriter.print(aDesc.getTag());
+        if (aDesc.isAttributes() && bean != null)
+            printAttributes(aWriter, indent, bean, aDesc);
+        aWriter.println("/>");
+    }
+
+    /**
+     * print the value from tag as content
+     * 
+     * @param aWriter
+     * @param tag
+     * @param content
+     * @throws Exception
+     */
+    public void printTagContent(PrintWriter aWriter, String tag, String content)
+            throws Exception {
+        aWriter.print("<");
+        aWriter.print(tag);
+        aWriter.print(">");
+        aWriter.print(convertStr(content));
+        aWriter.print("</");
+        aWriter.print(tag);
+        aWriter.println(">");
+    }
+
+    /**
+     * print an array of values
+     * 
+     * @param aWriter
+     * @param tag
+     * @param indent
+     * @param elements
+     */
+    public void printTagValueArray(PrintWriter aWriter, String tag, int indent,
+            String[] elements) {
+        if (elements != null && elements.length > 0) {
+            printIndent(aWriter, indent + 2);
+            aWriter.print("<");
+            aWriter.print(tag);
+            aWriter.print(">");
+            for (int i = 0; i < elements.length; i++) {
+                printIndent(aWriter, indent + 4);
+                aWriter.print(elements[i]);
+                if (i + 1 < elements.length)
+                    aWriter.println(",");
+            }
+            printIndent(aWriter, indent + 2);
+            aWriter.print("</");
+            aWriter.print(tag);
+            aWriter.println(">");
+        }
+    }
+
+    /**
+     * print a array of elements
+     * 
+     * @param aWriter
+     * @param tag
+     * @param indent
+     * @param elements
+     */
+    public void printTagArray(PrintWriter aWriter, String tag, int indent,
+            String[] elements) throws Exception {
+        if (elements != null) {
+            for (int i = 0; i < elements.length; i++) {
+                printIndent(aWriter, indent);
+                printTagContent(aWriter, tag, elements[i]);
+            }
+        }
+    }
+
+    /**
+     * Print some spaces
+     * 
+     * @param aWriter
+     * @param indent
+     *            number of spaces
+     */
+    public void printIndent(PrintWriter aWriter, int indent) {
+        for (int i = 0; i < indent; i++) {
+            aWriter.print(' ');
+        }
+    }
+
+    /**
+     * Store the relevant attributes of the specified JavaBean, plus a
+     * <code>className</code> attribute defining the fully qualified Java
+     * class name of the bean.
+     * 
+     * @param writer
+     *            PrintWriter to which we are storing
+     * @param bean
+     *            Bean whose properties are to be rendered as attributes,
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    public void printAttributes(PrintWriter writer, int indent, Object bean,
+            StoreDescription desc) throws Exception {
+
+        printAttributes(writer, indent, true, bean, desc);
+
+    }
+
+    /**
+     * Store the relevant attributes of the specified JavaBean.
+     * 
+     * @param writer
+     *            PrintWriter to which we are storing
+     * @param include
+     *            Should we include a <code>className</code> attribute?
+     * @param bean
+     *            Bean whose properties are to be rendered as attributes,
+     * @param desc
+     *            RegistryDescrpitor from this bean
+     * 
+     * @exception Exception
+     *                if an exception occurs while storing
+     */
+    public void printAttributes(PrintWriter writer, int indent,
+            boolean include, Object bean, StoreDescription desc)
+            throws Exception {
+
+        // Render the relevant properties of this bean
+        String className = bean.getClass().getName();
+
+        // Render a className attribute if requested
+        if (include && desc != null && !desc.isStandard()) {
+            writer.print(" className=\"");
+            writer.print(bean.getClass().getName());
+            writer.print("\"");
+        }
+
+        // Acquire the list of properties for this bean
+        PropertyDescriptor descriptors[] = Introspector.getBeanInfo(
+                bean.getClass()).getPropertyDescriptors();
+        if (descriptors == null) {
+            descriptors = new PropertyDescriptor[0];
+        }
+
+        // Create blank instance
+        Object bean2 = defaultInstance(bean);
+        for (int i = 0; i < descriptors.length; i++) {
+            if (descriptors[i] instanceof IndexedPropertyDescriptor) {
+                continue; // Indexed properties are not persisted
+            }
+            if (!isPersistable(descriptors[i].getPropertyType())
+                    || (descriptors[i].getReadMethod() == null)
+                    || (descriptors[i].getWriteMethod() == null)) {
+                continue; // Must be a read-write primitive or String
+            }
+            if (desc.isTransientAttribute(descriptors[i].getName())) {
+                continue; // Skip the specified exceptions
+            }
+            Object value = IntrospectionUtils.getProperty(bean, descriptors[i]
+                    .getName());
+            if (value == null) {
+                continue; // Null values are not persisted
+            }
+            Object value2 = IntrospectionUtils.getProperty(bean2,
+                    descriptors[i].getName());
+            if (value.equals(value2)) {
+                // The property has its default value
+                continue;
+            }
+            if (isPrintValue(bean, bean2, descriptors[i].getName(), desc))
+                printValue(writer, indent, descriptors[i].getName(), value);
+        }
+
+        if (bean instanceof ResourceBase) {
+            ResourceBase resource = (ResourceBase) bean;
+            for (Iterator iter = resource.listProperties(); iter.hasNext();) {
+                String name = (String) iter.next();
+                Object value = resource.getProperty(name);
+                if (!isPersistable(value.getClass())) {
+                    continue;
+                }
+                if (desc.isTransientAttribute(name)) {
+                    continue; // Skip the specified exceptions
+                }
+                printValue(writer, indent, name, value);
+
+            }
+        }
+    }
+
+    /**
+     * print this Attribute value or not
+     * 
+     * @param bean
+     *            orginal bean
+     * @param bean2
+     *            default bean
+     * @param attrName
+     *            attribute name
+     * @param desc
+     *            StoreDescription from bean
+     * @return True if it's a printing value
+     */
+    public boolean isPrintValue(Object bean, Object bean2, String attrName,
+            StoreDescription desc) {
+        boolean printValue = false;
+
+        Object value = IntrospectionUtils.getProperty(bean, attrName);
+        if (value != null) {
+            Object value2 = IntrospectionUtils.getProperty(bean2, attrName);
+            printValue = !value.equals(value2);
+
+        }
+        return printValue;
+    }
+
+    /**
+     * generate default Instance
+     * 
+     * @param bean
+     * @return an object from same class as bean parameter
+     * @throws InstantiationException
+     * @throws IllegalAccessException
+     */
+    public Object defaultInstance(Object bean) throws InstantiationException,
+            IllegalAccessException {
+        return bean.getClass().newInstance();
+    }
+
+    /**
+     * print an attribute value
+     * 
+     * @param writer
+     * @param name
+     * @param value
+     */
+    public void printValue(PrintWriter writer, int indent, String name,
+            Object value) {
+        if (!(value instanceof String)) {
+            value = value.toString();
+        }
+        writer.println();
+        printIndent(writer, indent + 4);
+        writer.print(name);
+        writer.print("=\"");
+        String strValue = convertStr((String) value);
+        writer.print(strValue);
+        writer.print("\"");
+    }
+
+    /**
+     * Given a string, this method replaces all occurrences of ' <', '>', '&',
+     * and '"'.
+     */
+    public String convertStr(String input) {
+
+        StringBuffer filtered = new StringBuffer(input.length());
+        char c;
+        for (int i = 0; i < input.length(); i++) {
+            c = input.charAt(i);
+            if (c == '<') {
+                filtered.append("&lt;");
+            } else if (c == '>') {
+                filtered.append("&gt;");
+            } else if (c == '\'') {
+                filtered.append("&apos;");
+            } else if (c == '"') {
+                filtered.append("&quot;");
+            } else if (c == '&') {
+                filtered.append("&amp;");
+            } else {
+                filtered.append(c);
+            }
+        }
+        return (filtered.toString());
+    }
+
+    /**
+     * Is the specified property type one for which we should generate a
+     * persistence attribute?
+     * 
+     * @param clazz
+     *            Java class to be tested
+     */
+    protected boolean isPersistable(Class clazz) {
+
+        for (int i = 0; i < persistables.length; i++) {
+            if (persistables[i] == clazz) {
+                return (true);
+            }
+        }
+        return (false);
+
+    }
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreConfig.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreConfig.java
new file mode 100644
index 0000000..b41a5fa
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreConfig.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.Server;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.Service;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.mbeans.MBeanUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Store Server/Service/Host/Context at file or PrintWriter. Default server.xml
+ * is at $catalina.base/conf/server.xml
+ * 
+ * @author Peter Rossbach
+ *  
+ */
+public class StoreConfig implements IStoreConfig {
+    private static Log log = LogFactory.getLog(StoreConfig.class);
+
+    private String serverFilename = "conf/server.xml";
+
+    private StoreRegistry registry;
+
+    /**
+     * get server.xml location
+     * 
+     * @return The server file name
+     */
+    public String getServerFilename() {
+        return serverFilename;
+    }
+
+    /**
+     * set new server.xml location
+     * 
+     * @param string
+     */
+    public void setServerFilename(String string) {
+        serverFilename = string;
+    }
+
+    /*
+     * Get the StoreRegistry with all factory to generate the
+     * server.xml/context.xml files
+     * 
+     * @see org.apache.catalina.config.IStoreConfig#getRegistry()
+     */
+    public StoreRegistry getRegistry() {
+        return registry;
+    }
+
+    /*
+     * set StoreRegistry
+     * 
+     * @see org.apache.catalina.config.IStoreConfig#setRegistry(org.apache.catalina.config.ConfigurationRegistry)
+     */
+    public void setRegistry(StoreRegistry aRegistry) {
+        registry = aRegistry;
+    }
+
+    /**
+     * Store current Server
+     * 
+     * @see org.apache.catalina.ServerFactory#getServer()
+     */
+    public synchronized void storeConfig() {
+        store(ServerFactory.getServer());
+    }
+
+    /**
+     * Store Server from Object Name (Catalina:type=Server)
+     * 
+     * @param aServerName
+     *            Server ObjectName
+     * @param backup
+     * @param externalAllowed
+     *            s *
+     * @throws MalformedObjectNameException
+     */
+    public synchronized void storeServer(String aServerName, boolean backup,
+            boolean externalAllowed) throws MalformedObjectNameException {
+        if (aServerName == null || aServerName.length() == 0) {
+            if (log.isErrorEnabled())
+                log.error("Please, call with a correct server ObjectName!");
+            return;
+        }
+        MBeanServer mserver = MBeanUtils.createServer();
+        ObjectName objectName = new ObjectName(aServerName);
+        if (mserver.isRegistered(objectName)) {
+            try {
+                Server aServer = (Server) mserver.getAttribute(objectName,
+                        "managedResource");
+                StoreDescription desc = null;
+                desc = getRegistry().findDescription(StandardContext.class);
+                if (desc != null) {
+                    boolean oldSeparate = desc.isStoreSeparate();
+                    boolean oldBackup = desc.isBackup();
+                    boolean oldExternalAllowed = desc.isExternalAllowed();
+                    try {
+                        desc.setStoreSeparate(true);
+                        desc.setBackup(backup);
+                        desc.setExternalAllowed(externalAllowed);
+                        store((Server) aServer);
+                    } finally {
+                        desc.setStoreSeparate(oldSeparate);
+                        desc.setBackup(oldBackup);
+                        desc.setExternalAllowed(oldExternalAllowed);
+                    }
+                } else
+                    store((Server) aServer);
+            } catch (Exception e) {
+                if (log.isInfoEnabled())
+                    log.info("Object " + aServerName
+                            + " is no a Server instance or store exception", e);
+            }
+        } else if (log.isInfoEnabled())
+            log.info("Server " + aServerName + " not found!");
+    }
+
+    /**
+     * Store a Context from ObjectName
+     * 
+     * @param aContextName
+     *            MBean ObjectName
+     * @param backup
+     * @param externalAllowed
+     * @throws MalformedObjectNameException
+     */
+    public synchronized void storeContext(String aContextName, boolean backup,
+            boolean externalAllowed) throws MalformedObjectNameException {
+        if (aContextName == null || aContextName.length() == 0) {
+            if (log.isErrorEnabled())
+                log.error("Please, call with a correct context ObjectName!");
+            return;
+        }
+        MBeanServer mserver = MBeanUtils.createServer();
+        ObjectName objectName = new ObjectName(aContextName);
+        if (mserver.isRegistered(objectName)) {
+            try {
+                Context aContext = (Context) mserver.getAttribute(objectName,
+                        "managedResource");
+                String configFile = aContext.getConfigFile();
+                if (configFile != null) {
+                    try {
+                        StoreDescription desc = null;
+                        desc = getRegistry().findDescription(
+                                aContext.getClass());
+                        if (desc != null) {
+                            boolean oldSeparate = desc.isStoreSeparate();
+                            boolean oldBackup = desc.isBackup();
+                            boolean oldExternalAllowed = desc
+                                    .isExternalAllowed();
+                            try {
+                                desc.setStoreSeparate(true);
+                                desc.setBackup(backup);
+                                desc.setExternalAllowed(externalAllowed);
+                                desc.getStoreFactory()
+                                        .store(null, -2, aContext);
+                            } finally {
+                                desc.setStoreSeparate(oldSeparate);
+                                desc.setBackup(oldBackup);
+                                desc.setBackup(oldExternalAllowed);
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.error(e);
+                    }
+                } else
+                    log.error("Missing configFile at Context "
+                            + aContext.getPath() + " to store!");
+            } catch (Exception e) {
+                if (log.isInfoEnabled())
+                    log
+                            .info(
+                                    "Object "
+                                            + aContextName
+                                            + " is no a context instance or store exception",
+                                    e);
+            }
+        } else if (log.isInfoEnabled())
+            log.info("Context " + aContextName + " not found!");
+    }
+
+    /**
+     * Write the configuration information for this entire <code>Server</code>
+     * out to the server.xml configuration file.
+     *  
+     */
+    public synchronized void store(Server aServer) {
+
+        StoreFileMover mover = new StoreFileMover(System
+                .getProperty("catalina.base"), getServerFilename(),
+                getRegistry().getEncoding());
+        // Open an output writer for the new configuration file
+        try {
+            PrintWriter writer = mover.getWriter();
+
+            try {
+                store(writer, -2, aServer);
+            } finally {
+                // Flush and close the output file
+                try {
+                    writer.flush();
+                } catch (Exception e) {
+                    log.error(e);
+                }
+                try {
+                    writer.close();
+                } catch (Exception e) {
+                    throw (e);
+                }
+            }
+            mover.move();
+        } catch (Exception e) {
+            log.error(e);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.catalina.config.IStoreConfig#store(org.apache.catalina.Context)
+     */
+    public synchronized void store(Context aContext) {
+        String configFile = aContext.getConfigFile();
+        if (configFile != null) {
+            try {
+                StoreDescription desc = null;
+                desc = getRegistry().findDescription(aContext.getClass());
+                if (desc != null) {
+                    boolean old = desc.isStoreSeparate();
+                    try {
+                        desc.setStoreSeparate(true);
+                        desc.getStoreFactory().store(null, -2, aContext);
+                    } finally {
+                        desc.setStoreSeparate(old);
+                    }
+                }
+            } catch (Exception e) {
+                log.error(e);
+            }
+        } else
+            log.error("Missing configFile at Context " + aContext.getPath());
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.catalina.config.IStoreConfig#store(java.io.PrintWriter,
+     *      int, org.apache.catalina.Context)
+     */
+    public synchronized void store(PrintWriter aWriter, int indent,
+            Context aContext) {
+        boolean oldSeparate = true;
+        StoreDescription desc = null;
+        try {
+            desc = getRegistry().findDescription(aContext.getClass());
+            oldSeparate = desc.isStoreSeparate();
+            desc.setStoreSeparate(false);
+            desc.getStoreFactory().store(aWriter, indent, aContext);
+        } catch (Exception e) {
+            log.error(e);
+        } finally {
+            if (desc != null)
+                desc.setStoreSeparate(oldSeparate);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.catalina.config.IStoreConfig#store(java.io.PrintWriter,
+     *      int, org.apache.catalina.Host)
+     */
+    public synchronized void store(PrintWriter aWriter, int indent, Host aHost) {
+        try {
+            StoreDescription desc = getRegistry().findDescription(
+                    aHost.getClass());
+            desc.getStoreFactory().store(aWriter, indent, aHost);
+        } catch (Exception e) {
+            log.error(e);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.catalina.config.IStoreConfig#store(java.io.PrintWriter,
+     *      int, org.apache.catalina.Service)
+     */
+    public synchronized void store(PrintWriter aWriter, int indent,
+            Service aService) {
+        try {
+            StoreDescription desc = getRegistry().findDescription(
+                    aService.getClass());
+            desc.getStoreFactory().store(aWriter, indent, aService);
+        } catch (Exception e) {
+            log.error(e);
+        }
+    }
+
+    /**
+     * Store the state of this Server MBean (which will recursively store
+     * everything)
+     * 
+     * @param writer
+     * @param indent
+     * @param aServer
+     */
+    public synchronized void store(PrintWriter writer, int indent,
+            Server aServer) {
+        try {
+            StoreDescription desc = getRegistry().findDescription(
+                    aServer.getClass());
+            desc.getStoreFactory().store(writer, indent, aServer);
+        } catch (Exception e) {
+            log.error(e);
+        }
+    }
+
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java
new file mode 100644
index 0000000..fb47963
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.InputStream;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.modelmbean.ModelMBean;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.mbeans.MBeanUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+
+/**
+ * Load and Register StoreConfig MBean <i>Catalina:type=StoreConfig,resource="url"</i>
+ * 
+ * @author Peter Rossbach
+ */
+public class StoreConfigLifecycleListener implements LifecycleListener {
+    private static Log log = LogFactory
+            .getLog(StoreConfigLifecycleListener.class);
+
+    IStoreConfig storeConfig;
+
+    private String storeConfigClass = "org.apache.catalina.storeconfig.StoreConfig";
+
+    private String storeRegistry = null;
+
+    /*
+     * register StoreRegistry after Start the complete Server
+     * 
+     * @see org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent)
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+        if (Lifecycle.AFTER_START_EVENT.equals(event.getType())) {
+            if (event.getSource() instanceof StandardServer) {
+                createMBean();
+            }
+        }
+    }
+
+    /**
+     * create StoreConfig MBean and load StoreRgistry MBeans name is
+     * <i>Catalina:type=StoreConfig </i>
+     */
+    protected void createMBean() {
+        StoreLoader loader = new StoreLoader();
+        try {
+            Class clazz = Class.forName(getStoreConfigClass(), true, this
+                    .getClass().getClassLoader());
+            storeConfig = (IStoreConfig) clazz.newInstance();
+            if (null == getStoreRegistry())
+                // default Loading
+                loader.load();
+            else
+                // load a spezial file registry (url)
+                loader.load(getStoreRegistry());
+            // use the loader Registry
+            storeConfig.setRegistry(loader.getRegistry());
+        } catch (Exception e) {
+            log.error("createMBean load", e);
+            return;
+        }
+        MBeanServer mserver = MBeanUtils.createServer();
+        InputStream descriptor = null;
+        try {
+            ObjectName objectName = new ObjectName("Catalina:type=StoreConfig" );
+            if (!mserver.isRegistered(objectName)) {
+                descriptor = this.getClass().getResourceAsStream(
+                        "mbeans-descriptors.xml");
+                Registry registry = MBeanUtils.createRegistry();
+                registry.loadMetadata(descriptor);
+                mserver.registerMBean(getManagedBean(storeConfig), objectName);
+            }
+        } catch (Exception ex) {
+            log.error("createMBean register MBean", ex);
+
+        } finally {
+            if (descriptor != null) {
+                try {
+                    descriptor.close();
+                    descriptor = null;
+                } catch (Exception ex) {
+                    log.error("createMBean register MBean", ex);
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a ManagedBean (StoreConfig)
+     * 
+     * @param object
+     * @return The bean
+     * @throws Exception
+     */
+    protected ModelMBean getManagedBean(Object object) throws Exception {
+        ManagedBean managedBean = Registry.getRegistry(null, null)
+                .findManagedBean(object.getClass().getName());
+        return managedBean.createMBean(object);
+    }
+
+    /**
+     * @return Returns the storeConfig.
+     */
+    public IStoreConfig getStoreConfig() {
+        return storeConfig;
+    }
+
+    /**
+     * @param storeConfig
+     *            The storeConfig to set.
+     */
+    public void setStoreConfig(IStoreConfig storeConfig) {
+        this.storeConfig = storeConfig;
+    }
+
+    /**
+     * @return Returns the storeConfigClass.
+     */
+    public String getStoreConfigClass() {
+        return storeConfigClass;
+    }
+
+    /**
+     * @param storeConfigClass
+     *            The storeConfigClass to set.
+     */
+    public void setStoreConfigClass(String storeConfigClass) {
+        this.storeConfigClass = storeConfigClass;
+    }
+
+    /**
+     * @return Returns the storeRegistry.
+     */
+    public String getStoreRegistry() {
+        return storeRegistry;
+    }
+
+    /**
+     * @param storeRegistry
+     *            The storeRegistry to set.
+     */
+    public void setStoreRegistry(String storeRegistry) {
+        this.storeRegistry = storeRegistry;
+    }
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreContextAppender.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreContextAppender.java
new file mode 100644
index 0000000..cfffa96
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreContextAppender.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+
+/**
+ * store StandardContext Attributes ... TODO DefaultContext Handling
+ * 
+ * @author Peter Rossbach
+ *  
+ */
+public class StoreContextAppender extends StoreAppender {
+
+    /*
+     * Print Context Values. <ul><li> Spezial handling to default workDir.
+     * </li><li> Don't save path at external context.xml </li><li> Don't
+     * generate docBase for host.appBase webapps <LI></ul>
+     * 
+     * @see org.apache.catalina.config.StoreAppender#isPrintValue(java.lang.Object,
+     *      java.lang.Object, java.lang.String,
+     *      org.apache.catalina.config.StoreDescription)
+     */
+    public boolean isPrintValue(Object bean, Object bean2, String attrName,
+            StoreDescription desc) {
+        boolean isPrint = super.isPrintValue(bean, bean2, attrName, desc);
+        if (isPrint) {
+            StandardContext context = ((StandardContext) bean);
+            if ("workDir".equals(attrName)) {
+                String defaultWorkDir = getDefaultWorkDir(context);
+                isPrint = !defaultWorkDir.equals(context.getWorkDir());
+            } else if ("path".equals(attrName)) {
+                isPrint = desc.isStoreSeparate() 
+                            && desc.isExternalAllowed()
+                            && context.getConfigFile() == null;
+            } else if ("docBase".equals(attrName)) {
+                Container host = context.getParent();
+                if (host instanceof StandardHost) {
+                    File appBase = getAppBase(((StandardHost) host));
+                    File docBase = getDocBase(context,appBase);
+                    isPrint = !appBase.equals(docBase.getParentFile());
+                }
+            }
+        }
+        return isPrint;
+    }
+
+    protected File getAppBase(StandardHost host) {
+
+        File appBase;
+        File file = new File(host.getAppBase());
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"), host
+                    .getAppBase());
+        try {
+            appBase = file.getCanonicalFile();
+        } catch (IOException e) {
+            appBase = file;
+        }
+        return (appBase);
+
+    }
+
+    protected File getDocBase(StandardContext context, File appBase) {
+
+        File docBase;
+        File file = new File(context.getDocBase());
+        if (!file.isAbsolute())
+            file = new File(appBase, context.getDocBase());
+        try {
+            docBase = file.getCanonicalFile();
+        } catch (IOException e) {
+            docBase = file;
+        }
+        return (docBase);
+
+    }
+
+    /**
+     * Make default Work Dir.
+     * 
+     * @param context
+     * @return The default working directory for the context.
+     */
+    protected String getDefaultWorkDir(StandardContext context) {
+        String defaultWorkDir = null;
+        String contextPath = context.getPath().length() == 0 ? "_" : context
+                .getPath().substring(1);
+        Container host = context.getParent();
+        if (host instanceof StandardHost) {
+            String hostWorkDir = ((StandardHost) host).getWorkDir();
+            if (hostWorkDir != null) {
+                defaultWorkDir = hostWorkDir + File.separator + contextPath;
+            } else {
+                String engineName = context.getParent().getParent().getName();
+                String hostName = context.getParent().getName();
+                defaultWorkDir = "work" + File.separator + engineName
+                        + File.separator + hostName + File.separator
+                        + contextPath;
+            }
+        }
+        return defaultWorkDir;
+    }
+
+    /*
+     * Generate a real default StandardContext TODO read and interpret the
+     * default context.xml and context.xml.default TODO Cache a Default
+     * StandardContext ( with reloading strategy) TODO remove really all
+     * elements, but detection is hard... To Listener or Valve from same class?>
+     * 
+     * @see org.apache.catalina.storeconfig.StoreAppender#defaultInstance(java.lang.Object)
+     */
+    public Object defaultInstance(Object bean) throws InstantiationException,
+            IllegalAccessException {
+        if (bean instanceof StandardContext) {
+            StandardContext defaultContext = new StandardContext();
+            /*
+             * if (!((StandardContext) bean).getOverride()) {
+             * defaultContext.setParent(((StandardContext)bean).getParent());
+             * ContextConfig contextConfig = new ContextConfig();
+             * defaultContext.addLifecycleListener(contextConfig);
+             * contextConfig.setContext(defaultContext);
+             * contextConfig.processContextConfig(new File(contextConfig
+             * .getBaseDir(), "conf/context.xml"));
+             * contextConfig.processContextConfig(new File(contextConfig
+             * .getConfigBase(), "context.xml.default")); }
+             */
+            return defaultContext;
+        } else
+            return super.defaultInstance(bean);
+    }
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreDescription.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreDescription.java
new file mode 100644
index 0000000..4762a90
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreDescription.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Bean of a StoreDescription 
+ * @author Peter Rossbach 
+ * 
+ * <pre>
+ * 
+ *  &lt;Description
+ *  tag=&quot;Context&quot;
+ *  standard=&quot;true&quot;
+ *  default=&quot;true&quot; 
+ *  externalAllowed=&quot;true&quot; 
+ *  storeSeparate=&quot;true&quot;
+ *  backup=&quot;true&quot; 
+ *  childs=&quot;true&quot;
+ *  tagClass=&quot;org.apache.catalina.core.StandardContext&quot;
+ *  storeFactoryClass=&quot;org.apache.catalina.storeconfig.StandardContextSF&quot;
+ *  storeAppenderClass=&quot;org.apache.catalina.storeconfig.StoreContextAppender&quot;&gt;
+ *     &lt;TransientAttribute&gt;available&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;configFile&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;configured&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;displayName&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;distributable&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;domain&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;engineName&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;name&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;publicId&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;replaceWelcomeFiles&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;saveConfig&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;sessionTimeout&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;startupTime&lt;/TransientAttribute&gt;
+ *     &lt;TransientAttribute&gt;tldScanTime&lt;/TransientAttribute&gt;
+ *  &lt;/Description&gt;
+ * 
+ *  
+ * </pre>
+ */
+public class StoreDescription {
+
+    private String id;
+
+    private String tag;
+
+    private String tagClass;
+
+    private boolean standard = false;
+
+    private boolean backup = false;
+
+    private boolean externalAllowed = false;
+
+    private boolean myDefault = false;
+
+    private boolean attributes = true;
+
+    private String storeFactoryClass;
+
+    private IStoreFactory storeFactory;
+
+    private String storeWriterClass;
+
+    private boolean childs = false;
+
+    private List transientAttributes;
+
+    private List transientChilds;
+
+    private boolean storeSeparate = false;
+
+    /**
+     * @return Returns the external.
+     */
+    public boolean isExternalAllowed() {
+        return externalAllowed;
+    }
+
+    /**
+     * @param external
+     *            The external to set.
+     */
+    public void setExternalAllowed(boolean external) {
+        this.externalAllowed = external;
+    }
+
+    /**
+     * @return Returns the standard.
+     */
+    public boolean isStandard() {
+        return standard;
+    }
+
+    /**
+     * @param standard
+     *            The standard to set.
+     */
+    public void setStandard(boolean standard) {
+        this.standard = standard;
+    }
+
+    /**
+     * @return Returns the backup.
+     */
+    public boolean isBackup() {
+        return backup;
+    }
+
+    /**
+     * @param backup
+     *            The backup to set.
+     */
+    public void setBackup(boolean backup) {
+        this.backup = backup;
+    }
+
+    /**
+     * @return Returns the myDefault.
+     */
+    public boolean isDefault() {
+        return myDefault;
+    }
+
+    /**
+     * @param aDefault
+     *            The myDefault to set.
+     */
+    public void setDefault(boolean aDefault) {
+        this.myDefault = aDefault;
+    }
+
+    /**
+     * @return Returns the storeFactory.
+     */
+    public String getStoreFactoryClass() {
+        return storeFactoryClass;
+    }
+
+    /**
+     * @param storeFactoryClass
+     *            The storeFactory to set.
+     */
+    public void setStoreFactoryClass(String storeFactoryClass) {
+        this.storeFactoryClass = storeFactoryClass;
+    }
+
+    /**
+     * @return Returns the storeFactory.
+     */
+    public IStoreFactory getStoreFactory() {
+        return storeFactory;
+    }
+
+    /**
+     * @param storeFactory
+     *            The storeFactory to set.
+     */
+    public void setStoreFactory(IStoreFactory storeFactory) {
+        this.storeFactory = storeFactory;
+    }
+
+    /**
+     * @return Returns the storeWriterClass.
+     */
+    public String getStoreWriterClass() {
+        return storeWriterClass;
+    }
+
+    /**
+     * @param storeWriterClass
+     *            The storeWriterClass to set.
+     */
+    public void setStoreWriterClass(String storeWriterClass) {
+        this.storeWriterClass = storeWriterClass;
+    }
+
+    /**
+     * @return Returns the tagClass.
+     */
+    public String getTag() {
+        return tag;
+    }
+
+    /**
+     * @param tag
+     *            The tag to set.
+     */
+    public void setTag(String tag) {
+        this.tag = tag;
+    }
+
+    /**
+     * @return Returns the tagClass.
+     */
+    public String getTagClass() {
+        return tagClass;
+    }
+
+    /**
+     * @param tagClass
+     *            The tagClass to set.
+     */
+    public void setTagClass(String tagClass) {
+        this.tagClass = tagClass;
+    }
+
+    /**
+     * @return Returns the transientAttributes.
+     */
+    public List getTransientAttributes() {
+        return transientAttributes;
+    }
+
+    /**
+     * @param transientAttributes
+     *            The transientAttributes to set.
+     */
+    public void setTransientAttributes(List transientAttributes) {
+        this.transientAttributes = transientAttributes;
+    }
+
+    public void addTransientAttribute(String attribute) {
+        if (transientAttributes == null)
+            transientAttributes = new ArrayList();
+        transientAttributes.add(attribute);
+    }
+
+    public void removeTransientAttribute(String attribute) {
+        if (transientAttributes != null)
+            transientAttributes.remove(attribute);
+    }
+
+    /**
+     * @return Returns the transientChilds.
+     */
+    public List getTransientChilds() {
+        return transientChilds;
+    }
+
+    /**
+     * @param transientChilds
+     *            The transientChilds to set.
+     */
+    public void setTransientChilds(List transientChilds) {
+        this.transientChilds = transientChilds;
+    }
+
+    public void addTransientChild(String classname) {
+        if (transientChilds == null)
+            transientChilds = new ArrayList();
+        transientChilds.add(classname);
+    }
+
+    public void removeTransientChild(String classname) {
+        if (transientChilds != null)
+            transientChilds.remove(classname);
+    }
+
+    /**
+     * is child transient, please don't save this.
+     * 
+     * @param classname
+     * @return is classname attribute?
+     */
+    public boolean isTransientChild(String classname) {
+        if (transientChilds != null)
+            return transientChilds.contains(classname);
+        return false;
+    }
+
+    /**
+     * is attribute transient, please don't save this.
+     * 
+     * @param attribute
+     * @return is transient attribute?
+     */
+    public boolean isTransientAttribute(String attribute) {
+        if (transientAttributes != null)
+            return transientAttributes.contains(attribute);
+        return false;
+    }
+
+    /**
+     * Return the real id or TagClass
+     * 
+     * @return Returns the id.
+     */
+    public String getId() {
+        if (id != null)
+            return id;
+        else
+            return getTagClass();
+    }
+
+    /**
+     * @param id
+     *            The id to set.
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * @return Returns the attributes.
+     */
+    public boolean isAttributes() {
+        return attributes;
+    }
+
+    /**
+     * @param attributes
+     *            The attributes to set.
+     */
+    public void setAttributes(boolean attributes) {
+        this.attributes = attributes;
+    }
+
+    /**
+     * @return True if it's a separate store
+     */
+    public boolean isStoreSeparate() {
+        return storeSeparate;
+    }
+
+    public void setStoreSeparate(boolean storeSeparate) {
+        this.storeSeparate = storeSeparate;
+    }
+
+    /**
+     * @return Returns the childs.
+     */
+    public boolean isChilds() {
+        return childs;
+    }
+
+    /**
+     * @param childs
+     *            The childs to set.
+     */
+    public void setChilds(boolean childs) {
+        this.childs = childs;
+    }
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreFactoryBase.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreFactoryBase.java
new file mode 100644
index 0000000..eebb466
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreFactoryBase.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * StoreFactory saves spezial elements.
+ * Output was generate with StoreAppenders.
+ * 
+ * @author Peter Rossbach
+ * @version 1.0
+ *  
+ */
+public class StoreFactoryBase implements IStoreFactory {
+    private static Log log = LogFactory.getLog(StoreFactoryBase.class);
+
+    private StoreRegistry registry;
+
+    private StoreAppender storeAppender = new StoreAppender();
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm = StringManager
+            .getManager(Constants.Package);
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info = "org.apache.catalina.config.StoreFactoryBase/1.0";
+
+    /**
+     * Return descriptive information about this Facotry implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * @return Returns the storeAppender.
+     */
+    public StoreAppender getStoreAppender() {
+        return storeAppender;
+    }
+
+    /**
+     * @param storeAppender
+     *            The storeAppender to set.
+     */
+    public void setStoreAppender(StoreAppender storeAppender) {
+        this.storeAppender = storeAppender;
+    }
+
+    /*
+     * set Registry
+     * 
+     * @see org.apache.catalina.config.IStoreFactory#setRegistry(org.apache.catalina.config.ConfigurationRegistry)
+     */
+    public void setRegistry(StoreRegistry aRegistry) {
+        registry = aRegistry;
+
+    }
+
+    /*
+     * get Registry
+     * 
+     * @see org.apache.catalina.config.IStoreFactory#getRegistry()
+     */
+    public StoreRegistry getRegistry() {
+
+        return registry;
+    }
+
+    public void storeXMLHead(PrintWriter aWriter) {
+        // Store the beginning of this element
+        aWriter.print("<?xml version=\"1.0\" encoding=\"");
+        aWriter.print(getRegistry().getEncoding());
+        aWriter.println("\"?>");
+    }
+
+    /*
+     * Store a server.xml element with attributes and childs
+     * 
+     * @see org.apache.catalina.storeconfig.IStoreFactory#store(java.io.PrintWriter,
+     *      int, java.lang.Object)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception {
+
+        StoreDescription elementDesc = getRegistry().findDescription(
+                aElement.getClass());
+
+        if (elementDesc != null) {
+            if (log.isDebugEnabled())
+                log.debug(sm.getString("factory.storeTag",
+                        elementDesc.getTag(), aElement));
+            getStoreAppender().printIndent(aWriter, indent + 2);
+            if (!elementDesc.isChilds()) {
+                getStoreAppender().printTag(aWriter, indent, aElement,
+                        elementDesc);
+            } else {
+                getStoreAppender().printOpenTag(aWriter, indent + 2, aElement,
+                        elementDesc);
+                storeChilds(aWriter, indent + 2, aElement, elementDesc);
+                getStoreAppender().printIndent(aWriter, indent + 2);
+                getStoreAppender().printCloseTag(aWriter, elementDesc);
+            }
+        } else
+            log.warn(sm.getString("factory.storeNoDescriptor", aElement
+                    .getClass()));
+    }
+
+    /**
+     * Must Implement at subclass for sepzial store childs handling
+     * 
+     * @param aWriter
+     * @param indent
+     * @param aElement
+     * @param elementDesc
+     */
+    public void storeChilds(PrintWriter aWriter, int indent, Object aElement,
+            StoreDescription elementDesc) throws Exception {
+    }
+
+    /**
+     * Store only elements from storeChilds methods that are not a transient
+     * child.
+     * 
+     * @param aWriter
+     * @param indent
+     * @param aTagElement
+     * @throws Exception
+     */
+    protected void storeElement(PrintWriter aWriter, int indent,
+            Object aTagElement) throws Exception {
+        if (aTagElement != null) {
+            IStoreFactory elementFactory = getRegistry().findStoreFactory(
+                    aTagElement.getClass());
+
+            if (elementFactory != null) {
+                StoreDescription desc = getRegistry().findDescription(
+                        aTagElement.getClass());
+                if (!desc.isTransientChild(aTagElement.getClass().getName()))
+                    elementFactory.store(aWriter, indent, aTagElement);
+            } else {
+                log.warn(sm.getString("factory.storeNoDescriptor", aTagElement
+                        .getClass()));
+            }
+        }
+    }
+
+    /*
+     * Save a array of elements @param aWriter @param indent @param elements
+     */
+    protected void storeElementArray(PrintWriter aWriter, int indent,
+            Object[] elements) throws Exception {
+        if (elements != null) {
+            for (int i = 0; i < elements.length; i++) {
+                storeElement(aWriter, indent, elements[i]);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreFactoryRule.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreFactoryRule.java
new file mode 100644
index 0000000..9d40a55
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreFactoryRule.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+
+/**
+ * <p>
+ * Rule that creates a new <code>IStoreFactory</code> instance, and associates
+ * it with the top object on the stack (which must implement
+ * <code>IStoreFactory</code>).
+ * </p>
+ * 
+ * @author Peter Rossbach
+ */
+
+public class StoreFactoryRule extends Rule {
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Construct a new instance of this Rule.
+     * 
+     * @param storeFactoryClass
+     *            Default name of the StoreFactory implementation class to be
+     *            created
+     * @param attributeName
+     *            Name of the attribute that optionally includes an override
+     *            name of the IStoreFactory class
+     */
+    public StoreFactoryRule(String storeFactoryClass, String attributeName,
+            String storeAppenderClass, String appenderAttributeName) {
+
+        this.storeFactoryClass = storeFactoryClass;
+        this.attributeName = attributeName;
+        this.appenderAttributeName = appenderAttributeName;
+        this.storeAppenderClass = storeAppenderClass;
+
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The attribute name of an attribute that can override the implementation
+     * class name.
+     */
+    private String attributeName;
+
+    private String appenderAttributeName;
+
+    /**
+     * The name of the <code>IStoreFactory</code> implementation class.
+     */
+    private String storeFactoryClass;
+
+    private String storeAppenderClass;
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Handle the beginning of an XML element.
+     * 
+     * @param attributes
+     *            The attributes of this element
+     * 
+     * @exception Exception
+     *                if a processing error occurs
+     */
+    public void begin(String namespace, String name, Attributes attributes)
+            throws Exception {
+
+        IStoreFactory factory = (IStoreFactory) newInstance(attributeName,
+                storeFactoryClass, attributes);
+        StoreAppender storeAppender = (StoreAppender) newInstance(
+                appenderAttributeName, storeAppenderClass, attributes);
+        factory.setStoreAppender(storeAppender);
+
+        // Add this StoreFactory to our associated component
+        StoreDescription desc = (StoreDescription) digester.peek(0);
+        StoreRegistry registry = (StoreRegistry) digester.peek(1);
+        factory.setRegistry(registry);
+        desc.setStoreFactory(factory);
+
+    }
+
+    /**
+     * create new instance from attribte className!
+     * 
+     * @param attr class Name attribute
+     * @param defaultName Default Class
+     * @param attributes current digester attribute elements
+     * @return new config object instance
+     * @throws ClassNotFoundException
+     * @throws InstantiationException
+     * @throws IllegalAccessException
+     */
+    protected Object newInstance(String attr, String defaultName,
+            Attributes attributes) throws ClassNotFoundException,
+            InstantiationException, IllegalAccessException {
+        String className = defaultName;
+        if (attr != null) {
+            String value = attributes.getValue(attr);
+            if (value != null)
+                className = value;
+        }
+        Class clazz = Class.forName(className);
+        return clazz.newInstance();
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreFileMover.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreFileMover.java
new file mode 100644
index 0000000..bf697ce
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreFileMover.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.sql.Timestamp;
+
+/**
+ * Move server.xml or context.xml as backup
+ * 
+ * @author Peter Rossbach
+ * 
+ * TODO Get Encoding from Registry
+ */
+public class StoreFileMover {
+
+    private String filename = "conf/server.xml";
+
+    private String encoding = "UTF-8";
+
+    private String basename = System.getProperty("catalina.base");
+
+    private File configOld;
+
+    private File configNew;
+
+    private File configSave;
+
+    /**
+     * @return Returns the configNew.
+     */
+    public File getConfigNew() {
+        return configNew;
+    }
+
+    /**
+     * @return Returns the configOld.
+     */
+    public File getConfigOld() {
+        return configOld;
+    }
+
+    /**
+     * @return Returns the configSave.
+     */
+    public File getConfigSave() {
+        return configSave;
+    }
+
+    /**
+     * @return Returns the basename.
+     */
+    public String getBasename() {
+        return basename;
+    }
+
+    /**
+     * @param basename
+     *            The basename to set.
+     */
+    public void setBasename(String basename) {
+        this.basename = basename;
+    }
+
+    /**
+     * @return The file name
+     */
+    public String getFilename() {
+        return filename;
+    }
+
+    /**
+     * @param string
+     */
+    public void setFilename(String string) {
+        filename = string;
+    }
+
+    /**
+     * @return The encoding
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * @param string
+     */
+    public void setEncoding(String string) {
+        encoding = string;
+    }
+
+    /**
+     * Calculate file objects for the old and new configuration files.
+     */
+    public StoreFileMover(String basename, String filename, String encoding) {
+        setBasename(basename);
+        setEncoding(encoding);
+        setFilename(filename);
+        init();
+    }
+
+    /**
+     * Calculate file objects for the old and new configuration files.
+     */
+    public StoreFileMover() {
+        init();
+    }
+
+    /**
+     * generate the Filename to new with TimeStamp
+     */
+    public void init() {
+        String configFile = getFilename();
+        configOld = new File(configFile);
+        if (!configOld.isAbsolute()) {
+            configOld = new File(getBasename(), configFile);
+        }
+        configNew = new File(configFile + ".new");
+        if (!configNew.isAbsolute()) {
+            configNew = new File(getBasename(), configFile + ".new");
+        }
+        if (!configNew.getParentFile().exists()) {
+            configNew.getParentFile().mkdirs();
+        }
+        String sb = getTimeTag();
+        configSave = new File(configFile + sb);
+        if (!configSave.isAbsolute()) {
+            configSave = new File(getBasename(), configFile + sb);
+        }
+    }
+
+    /**
+     * Shuffle old->save and new->old
+     * 
+     * @throws IOException
+     */
+    public void move() throws IOException {
+        if (configOld.renameTo(configSave)) {
+            if (!configNew.renameTo(configOld)) {
+                configSave.renameTo(configOld);
+                throw new IOException("Cannot rename "
+                        + configNew.getAbsolutePath() + " to "
+                        + configOld.getAbsolutePath());
+            }
+        } else {
+            if (!configOld.exists()) {
+                if (!configNew.renameTo(configOld)) {
+                    throw new IOException("Cannot move "
+                            + configNew.getAbsolutePath() + " to "
+                            + configOld.getAbsolutePath());
+                }
+            } else {
+                throw new IOException("Cannot rename "
+                    + configOld.getAbsolutePath() + " to "
+                    + configSave.getAbsolutePath());
+            }
+        }
+    }
+
+    /**
+     * Open an output writer for the new configuration file
+     * 
+     * @return The writer
+     * @throws IOException
+     */
+    public PrintWriter getWriter() throws IOException {
+        PrintWriter writer = null;
+        try {
+            writer = new PrintWriter(new OutputStreamWriter(
+                    new FileOutputStream(configNew), getEncoding()));
+        } catch (IOException e) {
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (Throwable t) {
+                    ;
+                }
+            }
+            throw (e);
+        }
+        return writer;
+    }
+
+    /**
+     * Time value for backup yyyy-mm-dd.hh-mm-ss
+     * 
+     * @return The time
+     */
+    protected String getTimeTag() {
+        String ts = (new Timestamp(System.currentTimeMillis())).toString();
+        //        yyyy-mm-dd hh:mm:ss
+        //        0123456789012345678
+        StringBuffer sb = new StringBuffer(".");
+        sb.append(ts.substring(0, 10));
+        sb.append('.');
+        sb.append(ts.substring(11, 13));
+        sb.append('-');
+        sb.append(ts.substring(14, 16));
+        sb.append('-');
+        sb.append(ts.substring(17, 19));
+        return sb.toString();
+    }
+
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreLoader.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreLoader.java
new file mode 100644
index 0000000..4a0b547
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreLoader.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.digester.Digester;
+import org.xml.sax.SAXException;
+
+/**
+ * 
+ * @author Peter Rossbach
+ * 
+ * <b>XML Format </b>
+ * 
+ * <pre>
+ *    
+ *       &lt;Registry name=&quot;&quot; encoding=&quot;UTF8&quot; &gt;
+ *       &lt;Description tag=&quot;Server&quot; standard=&quot;true&quot; default=&quot;true&quot;/&gt; 
+ *          tagClass=&quot;org.apache.catalina.core.StandardServer&quot;
+ *          storeFactory=&quot;org.apache.catalina.storeconfig.StandardServerSF&quot;&gt;
+ *        &lt;TransientAttributes&gt;
+ *          &lt;Attribute&gt;&lt;/Attribute&gt;
+ *        &lt;/TransientAttributes&gt;
+ *        &lt;TransientChilds&gt;
+ *          &lt;Child&gt;&lt;/Child&gt;
+ *        &lt;/TransientChilds&gt;
+ *       &lt;/Description&gt;
+ *   ...
+ *       &lt;/Tegistry&gt;
+ *     
+ * </pre>
+ * 
+ * 
+ * Convention:
+ * <ul>
+ * <li>Factories at subpackage <i>org.apache.catalina.core.storeconfig.xxxSF
+ * </i>.</li>
+ * <li>Element name are the unique Class name</li>
+ * <li>SF for StoreFactory</li>
+ * <li>standard implementation is false</li>
+ * </ul>
+ * other things:
+ * <ul>
+ * <li>Registry XML format is a very good option</li>
+ * <li>Store format is not fix</li>
+ * <li>We hope with the parent declaration we can build recursive child store
+ * operation //dream</li>
+ * <li>Problem is to access child data from array,collections or normal detail
+ * object</li>
+ * <li>Default definitions for Listener, Valve Resource? - Based on interface
+ * type!</li>
+ * </ul>
+ */
+public class StoreLoader {
+    private static Log log = LogFactory.getLog(StoreLoader.class);
+
+    /**
+     * The <code>Digester</code> instance used to parse registry descriptors.
+     */
+    protected static Digester digester = createDigester();
+
+    private StoreRegistry registry;
+    
+    private URL registryResource ;
+
+    /**
+     * @return Returns the registry.
+     */
+    public StoreRegistry getRegistry() {
+        return registry;
+    }
+
+    /**
+     * @param registry
+     *            The registry to set.
+     */
+    public void setRegistry(StoreRegistry registry) {
+        this.registry = registry;
+    }
+
+    /**
+     * Create and configure the Digester we will be using for setup store
+     * registry.
+     */
+    protected static Digester createDigester() {
+        long t1 = System.currentTimeMillis();
+        // Initialize the digester
+        Digester digester = new Digester();
+        digester.setValidating(false);
+        digester.setClassLoader(StoreRegistry.class.getClassLoader());
+
+        // Configure the actions we will be using
+        digester.addObjectCreate("Registry",
+                "org.apache.catalina.storeconfig.StoreRegistry", "className");
+        digester.addSetProperties("Registry");
+        digester
+                .addObjectCreate("Registry/Description",
+                        "org.apache.catalina.storeconfig.StoreDescription",
+                        "className");
+        digester.addSetProperties("Registry/Description");
+        digester.addRule("Registry/Description", new StoreFactoryRule(
+                "org.apache.catalina.storeconfig.StoreFactoryBase",
+                "storeFactoryClass",
+                "org.apache.catalina.storeconfig.StoreAppender",
+                "storeAppenderClass"));
+        digester.addSetNext("Registry/Description", "registerDescription",
+                "org.apache.catalina.storeconfig.StoreDescription");
+        digester.addCallMethod("Registry/Description/TransientAttribute",
+                "addTransientAttribute", 0);
+        digester.addCallMethod("Registry/Description/TransientChild",
+                "addTransientChild", 0);
+
+        long t2 = System.currentTimeMillis();
+        if (log.isDebugEnabled())
+            log.debug("Digester for server-registry.xml created " + (t2 - t1));
+        return (digester);
+
+    }
+
+    /**
+     * 
+     * @param aFile
+     * @return The server file
+     */
+    protected File serverFile(String aFile) {
+
+        if (aFile == null || (aFile != null && aFile.length() < 1))
+            aFile = "server-registry.xml";
+        File file = new File(aFile);
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base") + "/conf",
+                    aFile);
+        try {
+            file = file.getCanonicalFile();
+        } catch (IOException e) {
+            log.error(e);
+        }
+        return (file);
+    }
+
+    /**
+     * Load Description from external source
+     * 
+     * @param aURL
+     */
+    public void load(String aURL) {
+        synchronized (digester) {
+            File aRegistryFile = serverFile(aURL);
+            try {
+                registry = (StoreRegistry) digester.parse(aRegistryFile);
+                registryResource = aRegistryFile.toURL();
+            } catch (IOException e) {
+                log.error(e);
+            } catch (SAXException e) {
+                log.error(e);
+            }
+        }
+
+    }
+
+    /**
+     * Load from defaults
+     * <ul>
+     * <li>System Property URL catalina.storeregistry</li>
+     * <li>File $catalina.base/conf/server-registry.xml</li>
+     * <li>class resource org/apache/catalina/storeconfig/server-registry.xml
+     * </li>
+     * </ul>
+     */
+    public void load() {
+
+        InputStream is = null;
+        Throwable error = null;
+        registryResource = null ;
+        try {
+            String configUrl = getConfigUrl();
+            if (configUrl != null) {
+                is = (new URL(configUrl)).openStream();
+                if (log.isInfoEnabled())
+                    log
+                            .info("Find registry server-registry.xml from system property at url "
+                                    + configUrl);
+                ;
+                registryResource = new URL(configUrl);
+            }
+        } catch (Throwable t) {
+            // Ignore
+        }
+        if (is == null) {
+            try {
+                File home = new File(getCatalinaBase());
+                File conf = new File(home, "conf");
+                File reg = new File(conf, "server-registry.xml");
+                is = new FileInputStream(reg);
+                if (log.isInfoEnabled())
+                    log.info("Find registry server-registry.xml at file "
+                            + reg.getCanonicalPath());
+                ;
+                registryResource = reg.toURL() ;
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+        if (is == null) {
+            try {
+                is = StoreLoader.class
+                        .getResourceAsStream("/org/apache/catalina/storeconfig/server-registry.xml");
+                if (log.isInfoEnabled())
+                    log
+                            .info("Find registry server-registry.xml at classpath resource");
+                registryResource = StoreLoader.class
+                    .getResource("/org/apache/catalina/storeconfig/server-registry.xml");
+                
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+        if (is != null) {
+            try {
+                synchronized (digester) {
+                    registry = (StoreRegistry) digester.parse(is);
+                }
+            } catch (Throwable t) {
+                error = t;
+            } finally {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        if ((is == null) || (error != null)) {
+            log.error(error);
+        }
+    }
+
+    /**
+     * Get the value of the catalina.home environment variable.
+     */
+    private static String getCatalinaHome() {
+        return System.getProperty("catalina.home", System
+                .getProperty("user.dir"));
+    }
+
+    /**
+     * Get the value of the catalina.base environment variable.
+     */
+    private static String getCatalinaBase() {
+        return System.getProperty("catalina.base", getCatalinaHome());
+    }
+
+    /**
+     * Get the value of the configuration URL.
+     */
+    private static String getConfigUrl() {
+        return System.getProperty("catalina.storeconfig");
+    }
+    
+    
+
+    /**
+     * @return Returns the registryResource.
+     */
+    public URL getRegistryResource() {
+        return registryResource;
+    }
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreRegistry.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreRegistry.java
new file mode 100644
index 0000000..10323ed
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/StoreRegistry.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.directory.DirContext;
+
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Valve;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterDeployer;
+import org.apache.catalina.cluster.ClusterReceiver;
+import org.apache.catalina.cluster.ClusterSender;
+import org.apache.catalina.cluster.MembershipService;
+import org.apache.catalina.cluster.MessageListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Central StoreRegistry for all server.xml elements
+ * 
+ * @author Peter Rossbach
+ *  
+ */
+public class StoreRegistry {
+    private static Log log = LogFactory.getLog(StoreRegistry.class);
+
+    private Map descriptors = new HashMap();
+
+    private String encoding = "UTF-8";
+
+    private String name;
+
+    private String version;
+
+    // Access Information
+    private static Class interfaces[] = { CatalinaCluster.class,
+            ClusterSender.class, ClusterReceiver.class,
+            MembershipService.class, ClusterDeployer.class, Realm.class,
+            Manager.class, DirContext.class, LifecycleListener.class,
+            Valve.class, MessageListener.class };
+
+    /**
+     * @return Returns the name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @param name
+     *            The name to set.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * @return Returns the version.
+     */
+    public String getVersion() {
+        return version;
+    }
+
+    /**
+     * @param version
+     *            The version to set.
+     */
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    /**
+     * Find a description for id. Handle interface search when no direct match
+     * found.
+     * 
+     * @param id
+     * @return The description
+     */
+    public StoreDescription findDescription(String id) {
+        if (log.isDebugEnabled())
+            log.debug("search descriptor " + id);
+        StoreDescription desc = (StoreDescription) descriptors.get(id);
+        if (desc == null) {
+            Class aClass = null;
+            try {
+                aClass = Class.forName(id, true, this.getClass()
+                        .getClassLoader());
+            } catch (ClassNotFoundException e) {
+                log.error("ClassName:" + id, e);
+            }
+            if (aClass != null) {
+                desc = (StoreDescription) descriptors.get(aClass.getName());
+                for (int i = 0; desc == null && i < interfaces.length; i++) {
+                    if (interfaces[i].isAssignableFrom(aClass)) {
+                        desc = (StoreDescription) descriptors.get(interfaces[i]
+                                .getName());
+                    }
+                }
+            }
+        }
+        if (log.isDebugEnabled())
+            if (desc != null)
+                log.debug("find descriptor " + id + "#" + desc.getTag() + "#"
+                        + desc.getStoreFactoryClass());
+            else
+                log.debug(("Can't find descriptor for key " + id));
+        return desc;
+    }
+
+    /**
+     * Find Description by class
+     * 
+     * @param aClass
+     * @return The description
+     */
+    public StoreDescription findDescription(Class aClass) {
+        return findDescription(aClass.getName());
+    }
+
+    /**
+     * Find factory from classname
+     * 
+     * @param aClassName
+     * @return The factory
+     */
+    public IStoreFactory findStoreFactory(String aClassName) {
+        StoreDescription desc = findDescription(aClassName);
+        if (desc != null)
+            return desc.getStoreFactory();
+        else
+            return null;
+
+    }
+
+    /**
+     * find factory from class
+     * 
+     * @param aClass
+     * @return The factory
+     */
+    public IStoreFactory findStoreFactory(Class aClass) {
+        return findStoreFactory(aClass.getName());
+    }
+
+    /**
+     * Register a new description
+     * 
+     * @param desc
+     */
+    public void registerDescription(StoreDescription desc) {
+        String key = desc.getId();
+        if (key == null || "".equals(key))
+            key = desc.getTagClass();
+        descriptors.put(key, desc);
+        if (log.isDebugEnabled())
+            log.debug("register store descriptor " + key + "#" + desc.getTag()
+                    + "#" + desc.getTagClass());
+    }
+
+    public StoreDescription unregisterDescription(StoreDescription desc) {
+        String key = desc.getId();
+        if (key == null || "".equals(key))
+            key = desc.getTagClass();
+        return (StoreDescription) descriptors.remove(key);
+    }
+
+    // Attributes
+
+    /**
+     * @return The encoding
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * @param string
+     */
+    public void setEncoding(String string) {
+        encoding = string;
+    }
+
+}
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/WatchedResourceSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/WatchedResourceSF.java
new file mode 100644
index 0000000..d661413
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/WatchedResourceSF.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class WatchedResourceSF extends StoreFactoryBase {
+    private static Log log = LogFactory.getLog(WatchedResourceSF.class);
+
+    /*
+     * Store nested Element Value Arrays WatchedResource
+     * 
+     * @see org.apache.catalina.config.IStoreFactory#store(java.io.PrintWriter,
+     *      int, java.lang.Object)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception {
+        if (aElement instanceof StandardContext) {
+            StoreDescription elementDesc = getRegistry().findDescription(
+                    aElement.getClass().getName() + ".[WatchedResource]");
+            String[] resources = ((StandardContext) aElement)
+                    .findWatchedResources();
+            if (elementDesc != null) {
+                if (log.isDebugEnabled())
+                    log.debug("store " + elementDesc.getTag() + "( " + aElement
+                            + " )");
+                getStoreAppender().printTagArray(aWriter, "WatchedResource",
+                        indent, resources);
+            }
+        } else
+            log.warn("Descriptor for element" + aElement.getClass()
+                    + ".[WatchedResource] not configured!");
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/WrapperLifecycleSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/WrapperLifecycleSF.java
new file mode 100644
index 0000000..1ad9f23
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/WrapperLifecycleSF.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class WrapperLifecycleSF extends StoreFactoryBase {
+    private static Log log = LogFactory.getLog(WrapperLifecycleSF.class);
+
+    /*
+     * Store nested Element Value Arrays
+     * 
+     * @see org.apache.catalina.config.IStoreFactory#store(java.io.PrintWriter,
+     *      int, java.lang.Object)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception {
+        if (aElement instanceof StandardContext) {
+            StoreDescription elementDesc = getRegistry().findDescription(
+                    aElement.getClass().getName() + ".[WrapperLifecycle]");
+            String[] listeners = ((StandardContext) aElement)
+                    .findWrapperLifecycles();
+            if (elementDesc != null) {
+                if (log.isDebugEnabled())
+                    log.debug("store " + elementDesc.getTag() + "( " + aElement
+                            + " )");
+                getStoreAppender().printTagArray(aWriter, "WrapperLifecycle",
+                        indent, listeners);
+            }
+        } else
+            log.warn("Descriptor for element" + aElement.getClass()
+                    + ".[WrapperLifecycle] not configured!");
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/WrapperListenerSF.java b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/WrapperListenerSF.java
new file mode 100644
index 0000000..f52d14b
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/WrapperListenerSF.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Peter Rossbach (pero@apache.org)
+ *  
+ */
+public class WrapperListenerSF extends StoreFactoryBase {
+    private static Log log = LogFactory.getLog(WrapperListenerSF.class);
+
+    /*
+     * Store nested Element Value Arrays
+     * 
+     * @see org.apache.catalina.config.IStoreFactory#store(java.io.PrintWriter,
+     *      int, java.lang.Object)
+     */
+    public void store(PrintWriter aWriter, int indent, Object aElement)
+            throws Exception {
+        if (aElement instanceof StandardContext) {
+            StoreDescription elementDesc = getRegistry().findDescription(
+                    aElement.getClass().getName() + ".[WrapperListener]");
+            String[] listeners = ((StandardContext) aElement)
+                    .findWrapperListeners();
+            if (elementDesc != null) {
+                if (log.isDebugEnabled())
+                    log.debug("store " + elementDesc.getTag() + "( " + aElement
+                            + " )");
+                getStoreAppender().printTagArray(aWriter, "WrapperListener",
+                        indent, listeners);
+            }
+        } else
+            log.warn("Descriptor for element" + aElement.getClass()
+                    + ".[WrapperListener] not configured!");
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/mbeans-descriptors.xml b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/mbeans-descriptors.xml
new file mode 100644
index 0000000..ba85405
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/mbeans-descriptors.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean         name="StoreConfig"
+          description="Implementation of a store server.xml config"
+               domain="Catalina"
+                group="StoreConfig"
+                 type="org.apache.catalina.storeconfig.StoreConfig">
+    <operation name="storeConfig" 
+               description="Store Server" 
+               impact="ACTION" returnType="void" />
+    <operation name="storeServer" 
+               description="Store Server from ObjectName" 
+               impact="ACTION" returnType="void" >
+         <parameter name="objectname"
+                 description="Objectname from Server"
+                 type="java.lang.String"
+                 default="Catalina:type=Server"/>
+         <parameter name="backup"
+                 description="store Context with backup"
+                 type="boolean"/>
+         <parameter name="externalAllowed"
+                 description="store all Context external that have a configFile"
+                 type="boolean"/>
+    </operation>
+
+<!--
+   Catalina:j2eeType=WebModule,name=//localhost/manager,J2EEApplication=none,J2EEServer=none
+-->
+   <operation name="storeContext" 
+               description="Store Context from ObjectName" 
+               impact="ACTION" returnType="void" >
+         <parameter name="objectname"
+                 description="ObjectName from Context"
+                 type="java.lang.String"/>
+         <parameter name="backup"
+                 description="store with Backup"
+                 type="boolean"/>
+         <parameter name="externalAllowed"
+                 description="store all or store only internal server.xml context (configFile == null)"
+                 type="boolean"/>
+    </operation>           
+    <operation name="store" 
+               description="Store Server" 
+               impact="ACTION" returnType="void" >
+          <parameter name="server"
+                 description="Server"
+                 type="org.apache.catalina.Server"
+                 />
+    </operation>           
+    <operation name="store" 
+               description="Store Context" 
+               impact="ACTION" returnType="void" >
+          <parameter name="context"
+                 description="Context"
+                 type="org.apache.catalina.context"/>
+    </operation>           
+    <operation name="store" 
+               description="Store Host" 
+               impact="ACTION" returnType="void" >
+          <parameter name="host"
+                 description="Host"
+                 type="org.apache.catalina.Host"/>
+    </operation>           
+    <operation name="store" 
+               description="Store Service" 
+               impact="ACTION" returnType="void" >
+          <parameter name="service"
+                 description="service"
+                 type="org.apache.catalina.Service"/>
+    </operation>           
+ 
+  </mbean>
+
+</mbeans-descriptors>
+
+
diff --git a/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/server-registry.xml b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/server-registry.xml
new file mode 100644
index 0000000..9c65f65
--- /dev/null
+++ b/container/modules/storeconfig/src/share/org/apache/catalina/storeconfig/server-registry.xml
@@ -0,0 +1,338 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Registry name="Tomcat" version="5.5.5" encoding="UTF-8" >
+     <Description
+	    tag="Server"
+		standard="true"
+		default="true" 
+		externalAllowed="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardServer"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardServerSF">
+     </Description>
+     <Description
+	    tag="Service"
+		standard="true"
+		default="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardService"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardServiceSF">
+     </Description>
+     <Description
+	    tag="Engine"
+		standard="true"
+		default="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardEngine"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardEngineSF">
+	    <TransientAttribute>domain</TransientAttribute>
+     </Description>
+     <Description
+	    tag="Host"
+		standard="true"
+		default="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardHost"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardHostSF">
+	    <TransientAttribute>domain</TransientAttribute>
+     </Description>
+     <Description
+	    tag="Context"
+		standard="true"
+		default="true" 
+		externalAllowed="true" 
+ 		storeSeparate="true"
+		backup="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardContext"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardContextSF"
+        storeAppenderClass="org.apache.catalina.storeconfig.StoreContextAppender">
+	    <TransientAttribute>available</TransientAttribute>
+	    <TransientAttribute>configFile</TransientAttribute>
+	    <TransientAttribute>configured</TransientAttribute>
+	    <TransientAttribute>displayName</TransientAttribute>
+	    <TransientAttribute>distributable</TransientAttribute>
+	    <TransientAttribute>domain</TransientAttribute>
+	    <TransientAttribute>engineName</TransientAttribute>
+	    <TransientAttribute>name</TransientAttribute>
+	    <TransientAttribute>publicId</TransientAttribute>
+	    <TransientAttribute>replaceWelcomeFiles</TransientAttribute>
+	    <TransientAttribute>saveConfig</TransientAttribute>
+	    <TransientAttribute>sessionTimeout</TransientAttribute>
+	    <TransientAttribute>startupTime</TransientAttribute>
+	    <TransientAttribute>tldScanTime</TransientAttribute>
+     </Description>
+     <Description
+        id="org.apache.catalina.deploy.NamingResources.[GlobalNamingResources]"
+	    tag="GlobalNamingResources"
+		standard="true"
+		default="false"
+		attributes="false"
+        childs="true"
+        tagClass="org.apache.catalina.deploy.NamingResources"
+        storeFactoryClass="org.apache.catalina.storeconfig.GlobalNamingResourcesSF">
+     </Description>
+      <Description
+	    tag="Connector"
+		standard="true"
+		default="true" 
+        tagClass="org.apache.catalina.connector.Connector"
+        childs="true"
+        storeFactoryClass="org.apache.catalina.storeconfig.ConnectorSF"
+        storeAppenderClass="org.apache.catalina.storeconfig.ConnectorStoreAppender">
+	    <TransientAttribute>maxProcessor</TransientAttribute>
+ 	    <TransientAttribute>minProcessor</TransientAttribute>
+     </Description>
+     <Description
+	    tag="NamingResources"
+		standard="true"
+		default="false"
+		attributes="false"
+        childs="true"
+        tagClass="org.apache.catalina.deploy.NamingResources"
+        storeFactoryClass="org.apache.catalina.storeconfig.NamingResourcesSF">
+     </Description>
+	 <Description
+	    tag="Manager"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.Manager"
+        storeFactoryClass="org.apache.catalina.storeconfig.ManagerSF">
+	    <TransientAttribute>entropy</TransientAttribute>
+	    <TransientAttribute>distributable</TransientAttribute>
+        <TransientChild>org.apache.catalina.cluster.session.DeltaManager</TransientChild>
+        <TransientChild>org.apache.catalina.cluster.session.SimpleTcpReplicationManager</TransientChild>
+    </Description>
+    <Description
+	    tag="Manager"
+		standard="false"
+		default="false" 
+        childs="true"
+		tagClass="org.apache.catalina.session.PersistentManager"
+        storeFactoryClass="org.apache.catalina.storeconfig.PersistentManagerSF">
+        <TransientAttribute>entropy</TransientAttribute>
+	    <TransientAttribute>distributable</TransientAttribute>
+     </Description>
+     <Description
+	    tag="Store"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.session.FileStore"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Store"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.session.JDBCStore"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Cluster"
+		standard="false"
+		default="false" 
+        childs="true"
+		tagClass="org.apache.catalina.cluster.CatalinaCluster"
+        storeFactoryClass="org.apache.catalina.storeconfig.CatalinaClusterSF"
+        storeAppenderClass="org.apache.catalina.storeconfig.IDynamicPropertyStoreAppender">
+     </Description>
+     <Description
+	    tag="Realm"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.Realm"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Parameter"
+		standard="true"
+		default="false"
+		tagClass="org.apache.catalina.deploy.ApplicationParameter"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Loader"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.loader.WebappLoader"
+        storeFactoryClass="org.apache.catalina.storeconfig.LoaderSF">
+     </Description>
+      <Description
+	    tag="Listener"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.LifecycleListener"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+       <TransientChild>org.apache.catalina.mbeans.ServerLifecycleListener</TransientChild>
+       <TransientChild>org.apache.catalina.core.NamingContextListener</TransientChild>
+       <TransientChild>org.apache.catalina.startup.ContextConfig</TransientChild>
+       <TransientChild>org.apache.catalina.startup.EngineConfig</TransientChild>
+       <TransientChild>org.apache.catalina.startup.HostConfig</TransientChild>
+     </Description>
+     <Description
+        id="org.apache.catalina.core.StandardServer.[ServerLifecycleListener]"
+	    tag="Listener"
+		standard="false"
+		default="false" 
+        tagClass="org.apache.catalina.mbeans.ServerLifecycleListener"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Valve"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.Valve"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+	     <TransientChild>org.apache.catalina.authenticator.BasicAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.authenticator.DigestAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.authenticator.FormAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.authenticator.NonLoginAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.authenticator.SSLAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.core.StandardContextValve</TransientChild>
+	     <TransientChild>org.apache.catalina.core.StandardEngineValve</TransientChild>
+	     <TransientChild>org.apache.catalina.core.StandardHostValve</TransientChild>
+	     <TransientChild>org.apache.catalina.valves.CertificatesValve</TransientChild>
+	     <TransientChild>org.apache.catalina.valves.ErrorReportValve</TransientChild>
+	     <TransientChild>org.apache.catalina.valves.RequestListenerValve</TransientChild>
+	 </Description>
+     <Description
+	    tag="Environment"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextEnvironment"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="EJB"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextEjb"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="LocalEjb"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextLocalEjb"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Resource"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextResource"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+    <Description
+	    tag="Resources"
+		standard="false"
+		default="false" 
+		tagClass="javax.naming.directory.DirContext"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+        <TransientAttribute>docBase</TransientAttribute>
+        <TransientAttribute>allowLinking</TransientAttribute>
+        <TransientAttribute>cacheMaxSize</TransientAttribute>
+        <TransientAttribute>cacheTTL</TransientAttribute>
+        <TransientAttribute>cached</TransientAttribute>
+        <TransientAttribute>caseSensitive</TransientAttribute>
+        <TransientChild>org.apache.naming.resources.WARDirContext</TransientChild>
+        <TransientChild>org.apache.naming.resources.FileDirContext</TransientChild>
+        <TransientChild>org.apache.naming.resources.ProxyDirContext</TransientChild>
+     </Description>
+     <Description
+	    tag="ResourceEnvRef"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextResourceEnvRef"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="ResourceLink"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextResourceLink"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Transaction"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextTransaction"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    id="org.apache.catalina.core.StandardContext.[InstanceListener]"
+	    tag="InstanceListener"
+		standard="true"
+		default="false" 
+		attributes="false"
+        storeFactoryClass="org.apache.catalina.storeconfig.InstanceListenerSF">
+     </Description>
+     <Description
+	    id="org.apache.catalina.core.StandardContext.[WrapperLifecycle]"
+	    tag="WrapperLifecycle"
+		standard="true"
+		default="false" 
+		attributes="false"
+        storeFactoryClass="org.apache.catalina.storeconfig.WrapperLifecycleSF">
+     </Description>
+     <Description
+	    id="org.apache.catalina.core.StandardContext.[WrapperListener]"
+	    tag="WrapperListener"
+		standard="true"
+		default="false" 
+		attributes="false"
+        storeFactoryClass="org.apache.catalina.storeconfig.WrapperListenerSF">
+     </Description>
+     <Description
+	    id="org.apache.catalina.core.StandardContext.[WatchedResource]"
+	    tag="WatchedResource"
+		standard="true"
+		default="false" 
+		attributes="false"
+        storeFactoryClass="org.apache.catalina.storeconfig.WatchedResourceSF">
+     </Description>
+     <Description
+	    tag="Sender"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase"
+        storeAppenderClass="org.apache.catalina.storeconfig.IDynamicPropertyStoreAppender">
+     </Description>
+     <Description
+	    tag="Sender"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.cluster.ClusterSender"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Receiver"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.cluster.ClusterReceiver"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Membership"
+		standard="false"
+		default="false"
+		tagClass="org.apache.catalina.cluster.MembershipService"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Deployer"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.cluster.ClusterDeployer"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description> 
+     <Description
+	    tag="ClusterListener"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.cluster.MessageListener"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+</Registry>
+
diff --git a/container/modules/storeconfig/test/build.xml b/container/modules/storeconfig/test/build.xml
new file mode 100644
index 0000000..3ed7da6
--- /dev/null
+++ b/container/modules/storeconfig/test/build.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- @author Peter Rossbach -->
+<project name="Tomcat: StoreConfig Testcases" basedir="." default="test">
+	<property file="../../../../build.properties" />
+	<property file="../../../../jakarta-tomcat-5/build.properties.default" />
+	<property name="test.report.logs" value="logs/reports" />
+	<property name="test.results" value="logs/test-results" />
+
+	<property name="compile.optimize" value="true" />
+	<property name="compile.debug" value="true" />
+	<property name="compile.source" value="1.4" />
+	<property name="compile.deprecation" value="true" />
+	<property name="compile.nowarn" value="off" />
+	<property name="compile.encoding" value="ISO-8859-1" />
+	<property name="build.dir" value="build/test" />
+	<property name="src.dir" value="src/share" />
+	<property name="catalina.home" value="../../../../jakarta-tomcat-5/build" />
+
+	<!-- Build the classpath -->
+	<path id="project.classpath">
+		<pathelement location="${jmx.jar}" />
+		<pathelement location="${commons-logging.jar}" />
+		<pathelement location="${log4j.jar}" />
+		<fileset dir="${catalina.home}/common/endorsed">
+			<include name="*.jar" />
+		</fileset>
+		<fileset dir="${catalina.home}/common/lib">
+			<include name="*.jar" />
+		</fileset>
+		<fileset dir="${catalina.home}/server/lib">
+			<include name="*.jar" />
+		</fileset>
+	</path>
+
+	<target name="build-prepare">
+		<available property="certstore.present" file="conf/catalina.keystore" />
+		<mkdir dir="${build.dir}" />
+		<mkdir dir="webapps/myapps" />
+		<mkdir dir="conf/Catalina/localhost" />
+	</target>
+
+	<target name="info" description="Shows a information about this ant script">
+		<echo>
+			This ant script implements some testcases to verify the key functions of store tomcat server.xml .
+			You find this script at: ${ant.file}
+		</echo>
+	</target>
+
+	<!-- This target compiles all sources out of the 
+			projects source tree -->
+	<target name="compile" depends="build-prepare" description="This target compiles all sources out of the projects source tree">
+
+		<!-- Copies the static resources out of the src tree
+				to the build/classes dir -->
+		<copy todir="${build.dir}/classes">
+			<fileset dir="${src.dir}">
+				<include name="**" />
+				<exclude name="**/*.java" />
+			</fileset>
+		</copy>
+
+		<!-- Compiles all sources -->
+		<javac destdir="${build.dir}/classes" srcdir="${src.dir}" includes="**/*.java" excludes="**/CVS/**" deprecation="${compile.deprecation}" debug="${compile.debug}" source="${compile.source}" optimize="${compile.optimize}" nowarn="${compile.nowarn}" encoding="${compile.encoding}">
+			<classpath>
+				<path refid="project.classpath" />
+			</classpath>
+		</javac>
+	</target>
+
+	<target name="test" depends="compile,genstore" description="Run unit tests">
+		<delete dir="${test.results}" />
+		<mkdir dir="${test.results}" />
+		<junit fork="yes" failureProperty="test.failure" filtertrace="false" >
+			<jvmarg value="-Dcatalina.base=${basedir}" />
+			<jvmarg value="-Dcatalina.home=${catalina.home}" />
+			<jvmarg value="-Dlog4j.configuration=file:conf/log4j.xml" />
+			<classpath>
+				<pathelement location="${build.dir}/classes" />
+				<path refid="project.classpath" />
+			</classpath>
+			<formatter type="plain" usefile="false" />
+			<formatter type="xml" />
+			<batchtest todir="${test.results}">
+				<fileset dir="${build.dir}/classes" includes="**/*Test.class" />
+			</batchtest>
+		</junit>
+		<mkdir dir="${test.report.logs}" />
+		<junitreport todir="${test.report.logs}">
+			<fileset dir="${test.results}" />
+			<report format="frames" todir="${test.report.logs}" />
+		</junitreport>
+		<antcall target="checktest" />
+	</target>
+
+	<target name="genstore" unless="certstore.present">
+		<ant antfile="genstore.xml" target="store" />
+	</target>
+
+	<target name="checktest" if="test.failure">
+		<fail message="some test failed" />
+	</target>
+
+	<target name="clean">
+		<delete dir="${build}/dir" />
+		<delete dir="webapps" />
+		<delete dir="build" />
+		<delete dir="${test.report.logs}" />
+		<delete dir="${test.results}" />
+		<delete dir="logs" />
+	</target>
+</project>
diff --git a/container/modules/storeconfig/test/conf/catalina.keystore b/container/modules/storeconfig/test/conf/catalina.keystore
new file mode 100644
index 0000000..e6a124e
--- /dev/null
+++ b/container/modules/storeconfig/test/conf/catalina.keystore
Binary files differ
diff --git a/container/modules/storeconfig/test/conf/context.xml b/container/modules/storeconfig/test/conf/context.xml
new file mode 100644
index 0000000..9ee88ff
--- /dev/null
+++ b/container/modules/storeconfig/test/conf/context.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Context
+    cookies="false"
+    reloadable="true">
+	<Listener className="org.apache.catalina.storeconfig.InfoLifecycleListener" />
+</Context>
diff --git a/container/modules/storeconfig/test/conf/log4j.xml b/container/modules/storeconfig/test/conf/log4j.xml
new file mode 100644
index 0000000..ddfad4b
--- /dev/null
+++ b/container/modules/storeconfig/test/conf/log4j.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!-- ===================================================================== -->
+<!-- -->
+<!-- Log4j Configuration -->
+<!-- -->
+<!-- ===================================================================== -->
+<!-- $Id$ -->
+<!--
+| For more configuration infromation and examples see the Jakarta Log4j
+| owebsite: http://jakarta.apache.org/log4j
+-->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+<!-- ============================== -->
+<!-- Append messages to the console -->
+<!-- ==============================-->
+
+<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+<param name="Target" value="System.out"/>
+<param name="Threshold" value="debug"/>
+<layout class="org.apache.log4j.PatternLayout">
+<!--The default pattern: Date Priority [Category] Message\n-->
+<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
+</layout>
+</appender>
+
+
+<category name="org.apache.catalina.storeconfig" 
+           additivity="false"> 
+   <priority value="info" />
+   <appender-ref ref="CONSOLE" />
+</category>
+
+<category name="org.apache.catalina" 
+           additivity="false"> 
+   <priority value="info" />
+   <appender-ref ref="CONSOLE" />
+</category>
+<category name="org.apache.tomcat" 
+           additivity="false"> 
+   <priority value="error" />
+   <appender-ref ref="CONSOLE" />
+</category>
+<category name="org.apache.naming" 
+           additivity="false"> 
+   <priority value="info" />
+   <appender-ref ref="CONSOLE" />
+</category>
+
+<!-- Setup the Root c  -->
+<root>
+   <priority value="info" />
+   <appender-ref ref="CONSOLE"/>
+</root>
+</log4j:configuration> 
+
diff --git a/container/modules/storeconfig/test/conf/server-registry-test.xml b/container/modules/storeconfig/test/conf/server-registry-test.xml
new file mode 100644
index 0000000..e028e98
--- /dev/null
+++ b/container/modules/storeconfig/test/conf/server-registry-test.xml
@@ -0,0 +1,314 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Registry name="Tomcat" version="5.1.0" encoding="UTF-8" >
+     <Description
+	    tag="Server"
+		standard="true"
+		default="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardServer"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardServerSF">
+     </Description>
+     <Description
+	    tag="Service"
+		standard="true"
+		default="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardService"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardServiceSF">
+     </Description>
+     <Description
+	    tag="Engine"
+		standard="true"
+		default="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardEngine"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardEngineSF">
+	    <TransientAttribute>domain</TransientAttribute>
+     </Description>
+     <Description
+	    tag="Host"
+		standard="true"
+		default="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardHost"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardHostSF">
+	    <TransientAttribute>domain</TransientAttribute>
+     </Description>
+     <Description
+	    tag="Context"
+		standard="true"
+		default="true" 
+		storeSeparate="true"
+		backup="true" 
+        childs="true"
+        tagClass="org.apache.catalina.core.StandardContext"
+        storeFactoryClass="org.apache.catalina.storeconfig.StandardContextSF"
+        storeAppenderClass="org.apache.catalina.storeconfig.StoreContextAppender">
+	    <TransientAttribute>available</TransientAttribute>
+	    <TransientAttribute>configFile</TransientAttribute>
+	    <TransientAttribute>configured</TransientAttribute>
+	    <TransientAttribute>displayName</TransientAttribute>
+	    <TransientAttribute>distributable</TransientAttribute>
+	    <TransientAttribute>domain</TransientAttribute>
+	    <TransientAttribute>engineName</TransientAttribute>
+	    <TransientAttribute>name</TransientAttribute>
+	    <TransientAttribute>publicId</TransientAttribute>
+	    <TransientAttribute>replaceWelcomeFiles</TransientAttribute>
+	    <TransientAttribute>saveConfig</TransientAttribute>
+	    <TransientAttribute>sessionTimeout</TransientAttribute>
+	    <TransientAttribute>startupTime</TransientAttribute>
+	    <TransientAttribute>tldScanTime</TransientAttribute>
+     </Description>
+     <Description
+        id="org.apache.catalina.deploy.NamingResources.[GlobalNamingResources]"
+	    tag="GlobalNamingResources"
+		standard="true"
+		default="false"
+		attributes="false"
+        childs="true"
+        tagClass="org.apache.catalina.deploy.NamingResources"
+        storeFactoryClass="org.apache.catalina.storeconfig.GlobalNamingResourcesSF">
+     </Description>
+      <Description
+	    tag="Connector"
+		standard="true"
+		default="true" 
+        tagClass="org.apache.catalina.connector.Connector"
+        childs="true"
+        storeFactoryClass="org.apache.catalina.storeconfig.ConnectorSF"
+        storeWriterClass="org.apache.catalina.storeconfig.ConnectorStoreAppender">
+	    <TransientAttribute>maxProcessor</TransientAttribute>
+ 	    <TransientAttribute>minProcessor</TransientAttribute>
+     </Description>
+      <Description
+        id="org.apache.catalina.connector.Connector.[ProtocolHandler]"
+	    tag="ProtocolHandler"
+		standard="true"
+		default="false" 
+        tagClass="org.apache.coyote.ProtocolHandler"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+	    <TransientAttribute>clientauth</TransientAttribute>
+ 	    <TransientAttribute>keypass</TransientAttribute>
+	    <TransientAttribute>keystore</TransientAttribute>
+	    <TransientAttribute>keytype</TransientAttribute>
+	    <TransientAttribute>name</TransientAttribute>
+	    <TransientAttribute>port</TransientAttribute>
+	    <TransientAttribute>protocol</TransientAttribute>
+	    <TransientAttribute>protocols</TransientAttribute>
+	    <TransientAttribute>randomfile</TransientAttribute>
+	    <TransientAttribute>secure</TransientAttribute>
+	 </Description>
+     <Description
+	    tag="NamingResources"
+		standard="true"
+		default="false"
+		attributes="false"
+        childs="true"
+        tagClass="org.apache.catalina.deploy.NamingResources"
+        storeFactoryClass="org.apache.catalina.storeconfig.NamingResourcesSF">
+     </Description>
+	 <Description
+	    tag="Manager"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.session.StandardManager"
+        storeFactoryClass="org.apache.catalina.storeconfig.ManagerSF">
+	    <TransientAttribute>entropy</TransientAttribute>
+	    <TransientAttribute>distributable</TransientAttribute>
+    </Description>
+    <Description
+	    tag="Manager"
+		standard="false"
+		default="false" 
+        childs="true"
+		tagClass="org.apache.catalina.session.PersistentManager"
+        storeFactoryClass="org.apache.catalina.storeconfig.PersistentManagerSF">
+        <TransientAttribute>entropy</TransientAttribute>
+	    <TransientAttribute>distributable</TransientAttribute>
+     </Description>
+     <Description
+	    tag="Store"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.session.FileStore"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Store"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.session.JDBCStore"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Cluster"
+		standard="false"
+		default="false" 
+        childs="true"
+		tagClass="org.apache.catalina.cluster.CatalinaCluster"
+        storeFactoryClass="org.apache.catalina.storeconfig.CatalinaClusterSF">
+     </Description>
+     <Description
+	    tag="Realm"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.Realm"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Parameter"
+		standard="true"
+		default="false"
+		tagClass="org.apache.catalina.deploy.ApplicationParameter"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Loader"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.loader.WebappLoader"
+        storeFactoryClass="org.apache.catalina.storeconfig.LoaderSF">
+     </Description>
+      <Description
+	    tag="Listener"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.LifecycleListener"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+       <TransientChild>org.apache.catalina.mbeans.ServerLifecycleListener</TransientChild>
+       <TransientChild>org.apache.catalina.core.NamingContextListener</TransientChild>
+       <TransientChild>org.apache.catalina.startup.ContextConfig</TransientChild>
+       <TransientChild>org.apache.catalina.startup.EngineConfig</TransientChild>
+       <TransientChild>org.apache.catalina.startup.HostConfig</TransientChild>
+     </Description>
+     <Description
+        id="org.apache.catalina.core.StandardServer.[ServerLifecycleListener]"
+	    tag="Listener"
+		standard="false"
+		default="false" 
+        tagClass="org.apache.catalina.mbeans.ServerLifecycleListener"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Valve"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.Valve"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+	     <TransientChild>org.apache.catalina.authenticator.BasicAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.authenticator.DigestAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.authenticator.FormAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.authenticator.NonLoginAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.authenticator.SSLAuthenticator</TransientChild>
+	     <TransientChild>org.apache.catalina.core.StandardContextValve</TransientChild>
+	     <TransientChild>org.apache.catalina.core.StandardEngineValve</TransientChild>
+	     <TransientChild>org.apache.catalina.core.StandardHostValve</TransientChild>
+	     <TransientChild>org.apache.catalina.valves.CertificatesValve</TransientChild>
+	     <TransientChild>org.apache.catalina.valves.ErrorReportValve</TransientChild>
+	     <TransientChild>org.apache.catalina.valves.RequestListenerValve</TransientChild>
+	     <TransientChild>org.apache.catalina.cluster.tcp.ReplicationValve</TransientChild>
+	 </Description>
+     <Description
+	    tag="Environment"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextEnvironment"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="EJB"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextEjb"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="LocalEjb"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextLocalEjb"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Resource"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextResource"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="ResourceEnvRef"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextResourceEnvRef"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="ResourceLink"
+		standard="true"
+		default="false" 
+		tagClass="org.apache.catalina.deploy.ContextResourceLink"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+		id="org.apache.catalina.core.StandardContext.[InstanceListener]"
+	    tag="InstanceListener"
+		standard="true"
+		default="false" 
+		attributes="false"
+        storeFactoryClass="org.apache.catalina.storeconfig.InstanceListenerSF">
+     </Description>
+     <Description
+		id="org.apache.catalina.core.StandardContext.[WrapperLifecycle]"
+	    tag="WrapperLifecycle"
+		standard="true"
+		default="false" 
+		attributes="false"
+        storeFactoryClass="org.apache.catalina.storeconfig.WrapperLifecycleSF">
+     </Description>
+     <Description
+		id="org.apache.catalina.core.StandardContext.[WrapperListener]"
+	    tag="WrapperListener"
+		standard="true"
+		default="false" 
+		attributes="false"
+        storeFactoryClass="org.apache.catalina.storeconfig.WrapperListenerSF">
+     </Description>
+     <Description
+		id="org.apache.catalina.core.StandardContext.[WatchedResource]"
+	    tag="WatchedResource"
+		standard="true"
+		default="false" 
+		attributes="false"
+        storeFactoryClass="org.apache.catalina.storeconfig.WatchedResourceSF">
+     </Description>
+     <Description
+	    tag="Sender"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.cluster.ClusterSender"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Receiver"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.cluster.ClusterReceiver"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Membership"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.cluster.ClusterMembership"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description>
+     <Description
+	    tag="Deployer"
+		standard="false"
+		default="false" 
+		tagClass="org.apache.catalina.cluster.ClusterDeployer"
+        storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+     </Description> 
+</Registry>
+
diff --git a/container/modules/storeconfig/test/conf/server.xml b/container/modules/storeconfig/test/conf/server.xml
new file mode 100644
index 0000000..4fedef5
--- /dev/null
+++ b/container/modules/storeconfig/test/conf/server.xml
@@ -0,0 +1,359 @@
+<!-- Example Server Configuration File -->
+<!-- Note that component elements are nested corresponding to their
+     parent-child relationships with each other -->
+
+<!-- A "Server" is a singleton element that represents the entire JVM,
+     which may contain one or more "Service" instances.  The Server
+     listens for a shutdown command on the indicated port.
+
+     Note:  A "Server" is not itself a "Container", so you may not
+     define subcomponents such as "Valves" or "Loggers" at this level.
+ -->
+
+<Server port="8005" shutdown="SHUTDOWN" debug="0">
+
+
+  <!-- Comment these entries out to disable JMX MBeans support -->
+  <!-- You may also configure custom components (e.g. Valves/Realms) by 
+       including your own mbean-descriptor file(s), and setting the 
+       "descriptors" attribute to point to a ';' seperated list of paths
+       (in the ClassLoader sense) of files to add to the default list.
+       e.g. descriptors="/com/myfirm/mypackage/mbean-descriptor.xml"
+  -->
+  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener"
+            debug="0"/>
+  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"
+            debug="0"/>
+
+  <!-- Global JNDI resources -->
+  <GlobalNamingResources>
+
+    <!-- Test entry for demonstration purposes -->
+    <Environment name="simpleValue" type="java.lang.Integer" value="30"/>
+
+    <!-- Editable user database that can also be used by
+         UserDatabaseRealm to authenticate users -->
+    <Resource name="UserDatabase" auth="Container"
+              type="org.apache.catalina.UserDatabase"
+       description="User database that can be updated and saved"
+           factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
+          pathname="conf/tomcat-users.xml" />
+
+  </GlobalNamingResources>
+
+  <!-- A "Service" is a collection of one or more "Connectors" that share
+       a single "Container" (and therefore the web applications visible
+       within that Container).  Normally, that Container is an "Engine",
+       but this is not required.
+
+       Note:  A "Service" is not itself a "Container", so you may not
+       define subcomponents such as "Valves" or "Loggers" at this level.
+   -->
+
+  <!-- Define the Tomcat Stand-Alone Service -->
+  <Service name="Catalina">
+
+    <!-- A "Connector" represents an endpoint by which requests are received
+         and responses are returned.  Each Connector passes requests on to the
+         associated "Container" (normally an Engine) for processing.
+
+         By default, a non-SSL HTTP/1.1 Connector is established on port 8080.
+         You can also enable an SSL HTTP/1.1 Connector on port 8443 by
+         following the instructions below and uncommenting the second Connector
+         entry.  SSL support requires the following steps (see the SSL Config
+         HOWTO in the Tomcat 5 documentation bundle for more detailed
+         instructions):
+         * If your JDK version 1.3 or prior, download and install JSSE 1.0.2 or
+           later, and put the JAR files into "$JAVA_HOME/jre/lib/ext".
+         * Execute:
+             %JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA (Windows)
+             $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA  (Unix)
+           with a password value of "changeit" for both the certificate and
+           the keystore itself.
+
+         By default, DNS lookups are enabled when a web application calls
+         request.getRemoteHost().  This can have an adverse impact on
+         performance, so you can disable it by setting the
+         "enableLookups" attribute to "false".  When DNS lookups are disabled,
+         request.getRemoteHost() will return the String version of the
+         IP address of the remote client.
+    -->
+
+    <!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
+    <Connector port="8080"
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false" redirectPort="8443" acceptCount="100"
+               debug="0" connectionTimeout="20000" 
+               disableUploadTimeout="true" />
+    <!-- Note : To disable connection timeouts, set connectionTimeout value
+     to 0 -->
+	
+	<!-- Note : To use gzip compression you could set the following properties :
+	
+			   compression="on" 
+			   compressionMinSize="2048" 
+			   noCompressionUserAgents="gozilla, traviata" 
+			   compressableMimeType="text/html,text/xml"
+	-->
+
+    <!-- Define a SSL HTTP/1.1 Connector on port 8443 -->
+    <!--
+    <Connector port="8443" 
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false" disableUploadTimeout="true"
+               acceptCount="100" debug="0" scheme="https" secure="true"
+               clientAuth="false" sslProtocol="TLS" />
+    -->
+
+    <!-- Define an AJP 1.3 Connector on port 8009 -->
+    <Connector port="8009" 
+               enableLookups="false" redirectPort="8443" debug="0"
+               protocol="AJP/1.3" />
+
+    <!-- Define a Proxied HTTP/1.1 Connector on port 8082 -->
+    <!-- See proxy documentation for more information about using this. -->
+    <!--
+    <Connector port="8082" 
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false"
+               acceptCount="100" debug="0" connectionTimeout="20000"
+               proxyPort="80" disableUploadTimeout="true" />
+    -->
+
+    <!-- An Engine represents the entry point (within Catalina) that processes
+         every request.  The Engine implementation for Tomcat stand alone
+         analyzes the HTTP headers included with the request, and passes them
+         on to the appropriate Host (virtual host). -->
+
+    <!-- You should set jvmRoute to support load-balancing via AJP ie :
+    <Engine name="Standalone" defaultHost="localhost" debug="0" jvmRoute="jvm1">         
+    --> 
+         
+    <!-- Define the top level container in our container hierarchy -->
+    <Engine name="Catalina" defaultHost="localhost" debug="0">
+
+      <!-- The request dumper valve dumps useful debugging information about
+           the request headers and cookies that were received, and the response
+           headers and cookies that were sent, for all requests received by
+           this instance of Tomcat.  If you care only about requests to a
+           particular virtual host, or a particular application, nest this
+           element inside the corresponding <Host> or <Context> entry instead.
+
+           For a similar mechanism that is portable to all Servlet 2.4
+           containers, check out the "RequestDumperFilter" Filter in the
+           example application (the source for this filter may be found in
+           "$CATALINA_HOME/webapps/examples/WEB-INF/classes/filters").
+
+           Request dumping is disabled by default.  Uncomment the following
+           element to enable it. -->
+      <!--
+      <Valve className="org.apache.catalina.valves.RequestDumperValve"/>
+      -->
+
+      <!-- Because this Realm is here, an instance will be shared globally -->
+
+      <!-- This Realm uses the UserDatabase configured in the global JNDI
+           resources under the key "UserDatabase".  Any edits
+           that are performed against this UserDatabase are immediately
+           available for use by the Realm.  -->
+      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
+                 debug="0" resourceName="UserDatabase"/>
+
+      <!-- Comment out the old realm but leave here for now in case we
+           need to go back quickly -->
+      <!--
+      <Realm className="org.apache.catalina.realm.MemoryRealm" />
+      -->
+
+      <!-- Replace the above Realm with one of the following to get a Realm
+           stored in a database and accessed via JDBC -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
+             driverName="org.gjt.mm.mysql.Driver"
+          connectionURL="jdbc:mysql://localhost/authority"
+         connectionName="test" connectionPassword="test"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
+             driverName="oracle.jdbc.driver.OracleDriver"
+          connectionURL="jdbc:oracle:thin:@ntserver:1521:ORCL"
+         connectionName="scott" connectionPassword="tiger"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
+             driverName="sun.jdbc.odbc.JdbcOdbcDriver"
+          connectionURL="jdbc:odbc:CATALINA"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!-- Define the default virtual host
+           Note: XML Schema validation will not work with Xerces 2.2.
+       -->
+      <Host name="localhost" debug="0" appBase="webapps"
+       unpackWARs="true" autoDeploy="true"
+       xmlValidation="false" xmlNamespaceAware="false">
+
+        <!-- Defines a cluster for this node,
+             By defining this element, means that every manager will be changed.
+             So when running a cluster, only make sure that you have webapps in there
+             that need to be clustered and remove the other ones.
+             A cluster has the following parameters:
+
+             className = the fully qualified name of the cluster class
+
+             name = a descriptive name for your cluster, can be anything
+
+             debug = the debug level, higher means more output
+
+             mcastAddr = the multicast address, has to be the same for all the nodes
+
+             mcastPort = the multicast port, has to be the same for all the nodes
+             
+             mcastBindAddr = bind the multicast socket to a specific address
+             
+             mcastTTL = the multicast TTL if you want to limit your broadcast
+             
+             mcastSoTimeout = the multicast readtimeout 
+
+             mcastFrequency = the number of milliseconds in between sending a "I'm alive" heartbeat
+
+             mcastDropTime = the number a milliseconds before a node is considered "dead" if no heartbeat is received
+
+             tcpThreadCount = the number of threads to handle incoming replication requests, optimal would be the same amount of threads as nodes 
+
+             tcpListenAddress = the listen address (bind address) for TCP cluster request on this host, 
+                                in case of multiple ethernet cards.
+                                auto means that address becomes
+                                InetAddress.getLocalHost().getHostAddress()
+
+             tcpListenPort = the tcp listen port
+
+             tcpSelectorTimeout = the timeout (ms) for the Selector.select() method in case the OS
+                                  has a wakup bug in java.nio. Set to 0 for no timeout
+
+             printToScreen = true means that managers will also print to std.out
+
+             expireSessionsOnShutdown = true means that 
+
+             useDirtyFlag = true means that we only replicate a session after setAttribute,removeAttribute has been called.
+                            false means to replicate the session after each request.
+                            false means that replication would work for the following piece of code:
+                            <%
+                            HashMap map = (HashMap)session.getAttribute("map");
+                            map.put("key","value");
+                            %>
+             replicationMode = can be either 'pooled', 'synchronous' or 'asynchronous'.
+                               * Pooled means that the replication happens using several sockets in a synchronous way. Ie, the data gets replicated, then the request return. This is the same as the 'synchronous' setting except it uses a pool of sockets, hence it is multithreaded. This is the fastest and safest configuration. To use this, also increase the nr of tcp threads that you have dealing with replication.
+                               * Synchronous means that the thread that executes the request, is also the
+                               thread the replicates the data to the other nodes, and will not return until all
+                               nodes have received the information.
+                               * Asynchronous means that there is a specific 'sender' thread for each cluster node,
+                               so the request thread will queue the replication request into a "smart" queue,
+                               and then return to the client.
+                               The "smart" queue is a queue where when a session is added to the queue, and the same session
+                               already exists in the queue from a previous request, that session will be replaced
+                               in the queue instead of replicating two requests. This almost never happens, unless there is a 
+                               large network delay.
+        -->             
+        <!--
+            When configuring for clustering, you also add in a valve to catch all the requests
+            coming in, at the end of the request, the session may or may not be replicated.
+            A session is replicated if and only if all the conditions are met:
+            1. useDirtyFlag is true or setAttribute or removeAttribute has been called AND
+            2. a session exists (has been created)
+            3. the request is not trapped by the "filter" attribute
+
+            The filter attribute is to filter out requests that could not modify the session,
+            hence we don't replicate the session after the end of this request.
+            The filter is negative, ie, anything you put in the filter, you mean to filter out,
+            ie, no replication will be done on requests that match one of the filters.
+            The filter attribute is delimited by ;, so you can't escape out ; even if you wanted to.
+
+            filter=".*\.gif;.*\.js;" means that we will not replicate the session after requests with the URI
+            ending with .gif and .js are intercepted.
+            
+            The deployer element can be used to deploy apps cluster wide.
+            Currently the deployment only deploys/undeploys to working members in the cluster
+            so no WARs are copied upons startup of a broken node.
+            The deployer watches a directory (watchDir) for WAR files when watchEnabled="true"
+            When a new war file is added the war gets deployed to the local instance,
+            and then deployed to the other instances in the cluster.
+            When a war file is deleted from the watchDir the war is undeployed locally 
+            and cluster wide
+        -->
+        
+        <!--
+        <Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
+                 managerClassName="org.apache.catalina.cluster.session.DeltaManager"
+                 expireSessionsOnShutdown="false"
+                 useDirtyFlag="true">
+
+            <Membership 
+                className="org.apache.catalina.cluster.mcast.McastService"
+                mcastAddr="228.0.0.4"
+                mcastPort="45564"
+                mcastFrequency="500"
+                mcastDropTime="3000"/>
+
+            <Receiver 
+                className="org.apache.catalina.cluster.tcp.ReplicationListener"
+                tcpListenAddress="auto"
+                tcpListenPort="4001"
+                tcpSelectorTimeout="100"
+                tcpThreadCount="6"/>
+
+            <Sender
+                className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+                replicationMode="pooled"/>
+
+            <Valve className="org.apache.catalina.cluster.tcp.ReplicationValve"
+                   filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"/>
+                   
+            <Deployer className="org.apache.catalina.cluster.deploy.FarmWarDeployer"
+                      tempDir="/tmp/war-temp/"
+                      deployDir="/tmp/war-deploy/"
+                      watchDir="/tmp/war-listen/"
+                      watchEnabled="false"/>
+        </Cluster>
+        -->        
+
+
+
+        <!-- Normally, users must authenticate themselves to each web app
+             individually.  Uncomment the following entry if you would like
+             a user to be authenticated the first time they encounter a
+             resource protected by a security constraint, and then have that
+             user identity maintained across *all* web applications contained
+             in this virtual host. -->
+        <!--
+        <Valve className="org.apache.catalina.authenticator.SingleSignOn"
+                   debug="0"/>
+        -->
+
+        <!-- Access log processes all requests for this virtual host.  By
+             default, log files are created in the "logs" directory relative to
+             $CATALINA_HOME.  If you wish, you can specify a different
+             directory with the "directory" attribute.  Specify either a relative
+             (to $CATALINA_HOME) or absolute path to the desired directory.
+        -->
+        <!--
+        <Valve className="org.apache.catalina.valves.AccessLogValve"
+                 directory="logs"  prefix="localhost_access_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/>
+        -->
+
+      </Host>
+
+    </Engine>
+
+  </Service>
+
+</Server>
diff --git a/container/modules/storeconfig/test/genstore.xml b/container/modules/storeconfig/test/genstore.xml
new file mode 100644
index 0000000..6d0bf57
--- /dev/null
+++ b/container/modules/storeconfig/test/genstore.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="store" default="store" basedir=".">
+
+     <target name="store">
+        <delete file="conf/catalina.keystore"/>
+		<genkey alias="tomcat" storepass="changeit" keypass="changeit" 
+			keystore="conf/catalina.keystore" keyalg="rsa">
+			<dname>
+				<param name="CN" value="localhost"/>
+				<param name="OU" value="Software Deveploment Tomcat"/>
+				<param name="O" value="Apache Foundation"/>
+				<param name="L" value="Bochum"/>
+				<param name="C" value="DE"/>
+			</dname>
+		</genkey>
+    </target>
+
+</project>
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ConnectorSFTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ConnectorSFTest.java
new file mode 100644
index 0000000..e4f2f98
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ConnectorSFTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.beans.IntrospectionException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Connector;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class ConnectorSFTest extends TestCase {
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    Connector connector;
+
+    ConnectorSF factory;
+
+    StoreDescription desc;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = new StoreDescription();
+        desc.setTag("Connector");
+        desc.setTagClass("org.apache.catalina.connector.Connector");
+        desc.setStandard(false);
+        desc
+                .setStoreFactoryClass("org.apache.catalina.storeconfig.ConnectorSF");
+        registry.registerDescription(desc);
+        factory = new ConnectorSF();
+        desc.setStoreFactory(factory);
+        desc.getStoreFactory().setStoreAppender(new ConnectorStoreAppender());
+        factory.setRegistry(registry);
+        desc.addTransientAttribute("minProcessor");
+        desc.addTransientAttribute("maxProcessor");
+        registerDescriptor("Listener", LifecycleListener.class);
+        connector = new Connector("HTTP/1.1");
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass) {
+        return registerDescriptor(tag, aClass,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass,
+            String factoryClass, boolean fstandard, boolean fdefault) {
+        return DescriptorHelper.registerDescriptor(desc, registry, aClass
+                .getName(), tag, aClass.getName(), factoryClass, fstandard,
+                fdefault);
+    }
+
+    public void testListener() throws Exception {
+        connector
+                .addLifecycleListener(new org.apache.catalina.mbeans.ServerLifecycleListener());
+        String aspectedResult = "<Connector>"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.mbeans.ServerLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR + "</Connector>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testWithProtocolHandler() throws Exception {
+        connector.setProperty("acceptCount", "10");
+        connector.setProperty("maxSpareThreads", "74");
+        String aspectedResult = "<Connector" + LF.LINE_SEPARATOR
+                + "    maxSpareThreads=\"74\"" + LF.LINE_SEPARATOR
+                + "    acceptCount=\"10\">" + LF.LINE_SEPARATOR
+                + "</Connector>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testStoreAJP() throws Exception {
+        connector.setProtocol("AJP/1.3");
+        String aspectedResult = "<Connector" + LF.LINE_SEPARATOR
+                + "    protocol=\"AJP/1.3\">" + LF.LINE_SEPARATOR
+                + "</Connector>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testSSL() throws Exception {
+        setupSecureConnector();
+        String aspectedResult = "<Connector" + LF.LINE_SEPARATOR
+                + "    port=\"8443\"" + LF.LINE_SEPARATOR
+                + "    scheme=\"https\"" + LF.LINE_SEPARATOR
+                + "    secure=\"true\"" + LF.LINE_SEPARATOR
+                + "    minSpareThreads=\"30\"" + LF.LINE_SEPARATOR
+                + "    clientAuth=\"false\"" + LF.LINE_SEPARATOR
+                + "    keystorePass=\"changeit\"" + LF.LINE_SEPARATOR
+                + "    keystoreFile=\"conf/catalina.keystore\""
+                + LF.LINE_SEPARATOR + "    maxSpareThreads=\"175\""
+                + LF.LINE_SEPARATOR + "    sslProtocol=\"TLS\">"
+                + LF.LINE_SEPARATOR + "</Connector>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    protected void setupSecureConnector() {
+        connector.setPort(8443);
+        connector.setProperty("minSpareThreads", "30");
+        connector.setProperty("maxSpareThreads", "175");
+        connector.setEnableLookups(false);
+        connector.setProperty("disableUploadTimeout", "true");
+        connector.setSecure(true);
+        connector.setProperty("backlog", "100");
+        connector.setScheme("https");
+        connector.setProperty("clientAuth", "false");
+        connector.setProperty("sslProtocol", "TLS");
+        connector.setProperty("keystoreFile", "conf/catalina.keystore");
+        connector.setProperty("keystorePass", "changeit");
+    }
+
+    public void testConnectorAppender() throws IntrospectionException {
+        setupSecureConnector();
+        ConnectorStoreAppender appender = (ConnectorStoreAppender)desc.getStoreFactory().getStoreAppender();
+        List propertyList = appender.getPropertyKeys(connector);
+        assertTrue(propertyList.contains("protocol"));   
+    }
+    
+    public void testStoreEmpty() throws Exception {
+        String aspectedResult = "<Connector>" + LF.LINE_SEPARATOR
+                + "</Connector>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    protected void check(String aspectedResult) throws Exception {
+        factory.store(pWriter, -2, connector);
+        assertEquals(aspectedResult, writer.toString());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ConnectorStoreAppenderTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ConnectorStoreAppenderTest.java
new file mode 100644
index 0000000..f240911
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ConnectorStoreAppenderTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Connector;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class ConnectorStoreAppenderTest extends TestCase {
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    Connector connector;
+
+    ConnectorSF factory;
+
+    StoreDescription desc;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = new StoreDescription();
+        desc.setTag("Connector");
+        desc.setTagClass("org.apache.catalina.connector.Connector");
+        desc.setStandard(false);
+        desc
+                .setStoreFactoryClass("org.apache.catalina.storeconfig.ConnectorSF");
+        registry.registerDescription(desc);
+        factory = new ConnectorSF();
+        desc.setStoreFactory(factory);
+        desc.getStoreFactory().setStoreAppender(new ConnectorStoreAppender());
+        factory.setRegistry(registry);
+        registerDescriptor("Listener", LifecycleListener.class);
+        connector = new Connector("HTTP/1.1");
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass) {
+        return registerDescriptor(tag, aClass,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass,
+            String factoryClass, boolean fstandard, boolean fdefault) {
+        return DescriptorHelper.registerDescriptor(desc, registry, aClass
+                .getName(), tag, aClass.getName(), factoryClass, fstandard,
+                fdefault);
+    }
+
+    public void testGetProperties() throws Exception {
+        ConnectorStoreAppender appender = new ConnectorStoreAppender();
+        List properties = appender.getPropertyKeys(connector);
+        assertTrue(properties.contains("emptySessionPath"));
+        assertFalse(properties.contains("protocol"));
+        assertFalse(properties.contains("protocolHandlerClassName"));
+        // HTTP/1.1 SSL Protocol Test
+        connector.setProperty("sslProtocol", "TLS");
+        properties = appender.getPropertyKeys(connector);
+        assertTrue(properties.contains("protocol"));
+    }
+
+    public void testPrintAttributes() throws Exception {
+        ConnectorStoreAppender appender = new ConnectorStoreAppender();
+        connector.setProxyPort(80);
+        connector.setProperty("acceptCount", "110");
+        appender.printAttributes(pWriter, -2, false, connector, desc);
+
+        String aspectedResult = LF.LINE_SEPARATOR + "  proxyPort=\"80\""
+                + LF.LINE_SEPARATOR + "  acceptCount=\"110\"";
+        assertEquals(aspectedResult, writer.toString());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/DescriptorHelper.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/DescriptorHelper.java
new file mode 100644
index 0000000..e6ba6cd
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/DescriptorHelper.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.ClusterDeployer;
+import org.apache.catalina.cluster.ClusterReceiver;
+import org.apache.catalina.cluster.ClusterSender;
+import org.apache.catalina.cluster.MembershipService;
+import org.apache.catalina.cluster.MessageListener;
+import org.apache.catalina.deploy.ContextEjb;
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.ContextLocalEjb;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.ContextResourceEnvRef;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class DescriptorHelper {
+    private static Log log = LogFactory.getLog(DescriptorHelper.class);
+
+    public static StoreDescription registerDescriptor(
+            StoreDescription parentdesc, StoreRegistry registry, String tag,
+            Class aClass) {
+        return registerDescriptor(parentdesc, registry, aClass.getName(), tag,
+                aClass.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+    }
+
+    public static StoreDescription registerDescriptor(
+            StoreDescription parentdesc, StoreRegistry registry, String tag,
+            String aClassToken, String factoryClass, boolean fstandard,
+            boolean fdefault) {
+        return registerDescriptor(parentdesc, registry, aClassToken, tag,
+                aClassToken,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+    }
+
+    /**
+     * Generate a descriptor and register this to another parent descriptor.
+     * 
+     * @param parentdesc
+     * @param registry
+     * @param id
+     * @param tag
+     * @param aClassToken
+     * @param factoryClass
+     * @param fstandard
+     * @param fdefault
+     * @return
+     */
+    public static StoreDescription registerDescriptor(
+            StoreDescription parentdesc, StoreRegistry registry, String id,
+            String tag, String aClassToken, String factoryClass,
+            boolean fstandard, boolean fdefault) {
+        // add Listener
+        StoreDescription descChild = new StoreDescription();
+        descChild.setId(id);
+        descChild.setTag(tag);
+        descChild.setTagClass(aClassToken);
+        descChild.setStandard(fstandard);
+        descChild.setDefault(fdefault);
+        descChild.setStoreFactoryClass(factoryClass);
+        Object factory = null;
+        try {
+            Class aFactoryClass = Class.forName(factoryClass);
+            factory = aFactoryClass.newInstance();
+        } catch (Exception e) {
+            log.error(e);
+        }
+        if (factory != null) {
+            ((IStoreFactory) factory).setRegistry(registry);
+            descChild.setStoreFactory((IStoreFactory) factory);
+        }
+        if (parentdesc != null)
+            parentdesc.setChilds(true);
+        registry.registerDescription(descChild);
+        return descChild;
+    }
+
+    /**
+     * register all Registery descriptors on naming support to context!
+     * 
+     * @param parent
+     * @param registry
+     * @return
+     * @throws Exception
+     */
+    public static StoreDescription registerNamingDescriptor(
+            StoreDescription parent, StoreRegistry registry) throws Exception {
+
+        StoreDescription nameingDesc = DescriptorHelper.registerDescriptor(
+                parent, registry, NamingResources.class.getName(),
+                "NamingResources", NamingResources.class.getName(),
+                "org.apache.catalina.storeconfig.NamingResourcesSF", true,
+                false);
+        registerDescriptor(nameingDesc, registry, ContextEjb.class.getName(),
+                "EJB", ContextEjb.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", true, false);
+        registerDescriptor(nameingDesc, registry, ContextEnvironment.class
+                .getName(), "Environment", ContextEnvironment.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", true, false);
+        registerDescriptor(nameingDesc, registry, ContextLocalEjb.class
+                .getName(), "LocalEjb", ContextLocalEjb.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", true, false);
+        registerDescriptor(nameingDesc, registry, ContextResource.class
+                .getName(), "Resource", ContextResource.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", true, false);
+        registerDescriptor(nameingDesc, registry, ContextResourceLink.class
+                .getName(), "ResourceLink",
+                ContextResourceLink.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", true, false);
+        registerDescriptor(nameingDesc, registry, ContextResourceEnvRef.class
+                .getName(), "ResourceEnvRef", ContextResourceEnvRef.class
+                .getName(), "org.apache.catalina.storeconfig.StoreFactoryBase",
+                true, false);
+        return nameingDesc;
+    }
+
+    /**
+     * register all cluster and subelement descriptors to registery
+     * 
+     * @param parent
+     * @param registry
+     * @return
+     * @throws Exception
+     */
+    public static StoreDescription registerClusterDescriptor(
+            StoreDescription parent, StoreRegistry registry) throws Exception {
+
+        StoreDescription clusterDesc = DescriptorHelper.registerDescriptor(
+                parent, registry, CatalinaCluster.class.getName(), "Cluster",
+                CatalinaCluster.class.getName(),
+                "org.apache.catalina.storeconfig.CatalinaClusterSF", false,
+                false);
+        registerDescriptor(clusterDesc, registry,
+                ClusterSender.class.getName(), "Sender", ClusterSender.class
+                        .getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        registerDescriptor(clusterDesc, registry, ClusterReceiver.class
+                .getName(), "Receiver", ClusterReceiver.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        registerDescriptor(clusterDesc, registry, MembershipService.class
+                .getName(), "Membership", MembershipService.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        registerDescriptor(clusterDesc, registry, ClusterDeployer.class
+                .getName(), "Deployer", ClusterDeployer.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        registerDescriptor(clusterDesc, registry, MessageListener.class
+                .getName(), "ClusterListener", MessageListener.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        return clusterDesc;
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/GlobalNamingResourcesSFTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/GlobalNamingResourcesSFTest.java
new file mode 100644
index 0000000..a071252
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/GlobalNamingResourcesSFTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.deploy.ContextEjb;
+import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.deploy.ContextLocalEjb;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.deploy.ContextResourceEnvRef;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.storeconfig.GlobalNamingResourcesSF;
+import org.apache.catalina.storeconfig.StoreDescription;
+import org.apache.catalina.storeconfig.StoreRegistry;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class GlobalNamingResourcesSFTest extends TestCase {
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    NamingResources reource = new NamingResources();
+
+    GlobalNamingResourcesSF factory;
+
+    StoreDescription desc;
+
+    StoreDescription nameingDesc;
+
+    /*
+     * create registery and configure naming decriptors
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+        registry = new StoreRegistry();
+        desc = DescriptorHelper.registerDescriptor(null, registry,
+                NamingResources.class.getName() + ".[GlobalNamingResources]",
+                "GlobalNamingResources", NamingResources.class.getName(),
+                "org.apache.catalina.storeconfig.GlobalNamingResourcesSF",
+                true, false);
+        factory = (GlobalNamingResourcesSF) desc.getStoreFactory();
+        nameingDesc = DescriptorHelper.registerNamingDescriptor(desc, registry);
+        super.setUp();
+    }
+
+    protected void registerDescriptor(String tag, Class aClass,
+            String factoryClass, boolean fstandard, boolean fdefault) {
+        DescriptorHelper.registerDescriptor(nameingDesc, registry, aClass
+                .getName(), tag, aClass.getName(), factoryClass, fstandard,
+                fdefault);
+    }
+
+    public void testStore() throws Exception {
+        assertNotNull(registry.findDescription(NamingResources.class));
+        assertNotNull(registry.findDescription(ContextResourceEnvRef.class
+                .getName()));
+        assertEquals("ResourceEnvRef", registry.findDescription(
+                ContextResourceEnvRef.class.getName()).getTag());
+
+        NamingResources resources = new NamingResources();
+        ContextResourceEnvRef ref = new ContextResourceEnvRef();
+        ref.setName("peter");
+        ref.setType("type");
+        resources.addResourceEnvRef(ref);
+        String aspectedResult = "<GlobalNamingResources>" + LF.LINE_SEPARATOR
+                + "  <ResourceEnvRef" + LF.LINE_SEPARATOR
+                + "    name=\"peter\"" + LF.LINE_SEPARATOR
+                + "    type=\"type\"/>" + LF.LINE_SEPARATOR
+                + "</GlobalNamingResources>" + LF.LINE_SEPARATOR;
+        check(resources, aspectedResult);
+
+    }
+
+    public void testEJBStore() throws Exception {
+
+        NamingResources resources = new NamingResources();
+        ContextEjb ejb = new ContextEjb();
+        ejb.setName("ejb/Service");
+        ejb.setType("org.super.Bean");
+        ejb.setHome("org.super.BeanHome");
+        resources.addEjb(ejb);
+        String aspectedResult = "<GlobalNamingResources>" + LF.LINE_SEPARATOR
+                + "  <EJB" + LF.LINE_SEPARATOR
+                + "    home=\"org.super.BeanHome\"" + LF.LINE_SEPARATOR
+                + "    name=\"ejb/Service\"" + LF.LINE_SEPARATOR
+                + "    type=\"org.super.Bean\"/>" + LF.LINE_SEPARATOR
+                + "</GlobalNamingResources>" + LF.LINE_SEPARATOR;
+        check(resources, aspectedResult);
+    }
+
+    public void testLocalEjbStore() throws Exception {
+
+        NamingResources resources = new NamingResources();
+        ContextLocalEjb ejb = new ContextLocalEjb();
+        ejb.setName("ejb/Service");
+        ejb.setType("org.super.Bean");
+        ejb.setHome("org.super.BeanHome");
+        resources.addLocalEjb(ejb);
+        String aspectedResult = "<GlobalNamingResources>" + LF.LINE_SEPARATOR
+                + "  <LocalEjb" + LF.LINE_SEPARATOR
+                + "    home=\"org.super.BeanHome\"" + LF.LINE_SEPARATOR
+                + "    name=\"ejb/Service\"" + LF.LINE_SEPARATOR
+                + "    type=\"org.super.Bean\"/>" + LF.LINE_SEPARATOR
+                + "</GlobalNamingResources>" + LF.LINE_SEPARATOR;
+        check(resources, aspectedResult);
+    }
+
+    public void testEnvironmentStore() throws Exception {
+
+        NamingResources resources = new NamingResources();
+        ContextEnvironment env = new ContextEnvironment();
+        env.setName("env/SelectEmp");
+        env.setType("java.lang.String");
+        env.setValue("select * from emp");
+        resources.addEnvironment(env);
+        String aspectedResult = "<GlobalNamingResources>" + LF.LINE_SEPARATOR
+                + "  <Environment" + LF.LINE_SEPARATOR
+                + "    name=\"env/SelectEmp\"" + LF.LINE_SEPARATOR
+                + "    type=\"java.lang.String\"" + LF.LINE_SEPARATOR
+                + "    value=\"select * from emp\"/>" + LF.LINE_SEPARATOR
+                + "</GlobalNamingResources>" + LF.LINE_SEPARATOR;
+        check(resources, aspectedResult);
+    }
+
+    public void testResourceStore() throws Exception {
+
+        NamingResources resources = new NamingResources();
+        ContextResource res = new ContextResource();
+        res.setName("jdbc/Emp");
+        res.setType("javax.sql.DataSource");
+        res.setAuth("Container");
+        resources.addResource(res);
+        String aspectedResult = "<GlobalNamingResources>" + LF.LINE_SEPARATOR
+                + "  <Resource" + LF.LINE_SEPARATOR + "    auth=\"Container\""
+                + LF.LINE_SEPARATOR + "    name=\"jdbc/Emp\""
+                + LF.LINE_SEPARATOR + "    type=\"javax.sql.DataSource\"/>"
+                + LF.LINE_SEPARATOR + "</GlobalNamingResources>"
+                + LF.LINE_SEPARATOR;
+        check(resources, aspectedResult);
+    }
+
+    public void testResourceStoreProperty() throws Exception {
+
+        NamingResources resources = new NamingResources();
+        ContextResource res = new ContextResource();
+        res.setName("mail/MailSession");
+        res.setType("javax.mail.Session");
+        res.setAuth("Container");
+        res.setProperty("mail.host", "localhost");
+        resources.addResource(res);
+        String aspectedResult = "<GlobalNamingResources>" + LF.LINE_SEPARATOR
+                + "  <Resource" + LF.LINE_SEPARATOR + "    auth=\"Container\""
+                + LF.LINE_SEPARATOR + "    name=\"mail/MailSession\""
+                + LF.LINE_SEPARATOR + "    type=\"javax.mail.Session\""
+                + LF.LINE_SEPARATOR + "    mail.host=\"localhost\"/>"
+                + LF.LINE_SEPARATOR + "</GlobalNamingResources>"
+                + LF.LINE_SEPARATOR;
+        check(resources, aspectedResult);
+    }
+
+    // @TODO ResourceLink can only be exists at Context Tag
+    public void testResourceLinkStore() throws Exception {
+
+        NamingResources resources = new NamingResources();
+        ContextResourceLink res = new ContextResourceLink();
+        res.setName("jdbc/Emp1");
+        res.setType("javax.sql.DataSource");
+        res.setGlobal("jdbc/Emp");
+        resources.addResourceLink(res);
+        String aspectedResult = "<GlobalNamingResources>" + LF.LINE_SEPARATOR
+                + "  <ResourceLink" + LF.LINE_SEPARATOR
+                + "    global=\"jdbc/Emp\"" + LF.LINE_SEPARATOR
+                + "    name=\"jdbc/Emp1\"" + LF.LINE_SEPARATOR
+                + "    type=\"javax.sql.DataSource\"/>" + LF.LINE_SEPARATOR
+                + "</GlobalNamingResources>" + LF.LINE_SEPARATOR;
+        check(resources, aspectedResult);
+    }
+
+    public void testStoreEmpty() throws Exception {
+        NamingResources resources = new NamingResources();
+        String aspectedResult = "<GlobalNamingResources>" + LF.LINE_SEPARATOR
+                + "</GlobalNamingResources>" + LF.LINE_SEPARATOR;
+        check(resources, aspectedResult);
+    }
+
+    protected void check(NamingResources resources, String aspectedResult)
+            throws Exception {
+        factory.store(pWriter, -2, resources);
+        assertEquals(aspectedResult, writer.toString());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/InfoLifecycleListener.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/InfoLifecycleListener.java
new file mode 100644
index 0000000..dffcc90
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/InfoLifecycleListener.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class InfoLifecycleListener implements LifecycleListener {
+
+    private static Log log = LogFactory.getLog(InfoLifecycleListener.class);
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info = "org.apache.catalina.listener.InfoLifecycleListener/1.0";
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent)
+     */
+    public void lifecycleEvent(LifecycleEvent arg0) {
+        if (log.isInfoEnabled())
+            log.info(arg0.getSource().toString() + ": " + arg0.getType());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/LF.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/LF.java
new file mode 100644
index 0000000..6228711
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/LF.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+
+public class LF {
+
+    // -------------------------------------------------------------- Constants
+
+    public static String LINE_SEPARATOR = System.getProperty("line.separator");
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/LoaderSFTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/LoaderSFTest.java
new file mode 100644
index 0000000..dbea3e2
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/LoaderSFTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.Loader;
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.storeconfig.LoaderSF;
+import org.apache.catalina.storeconfig.StoreDescription;
+import org.apache.catalina.storeconfig.StoreRegistry;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class LoaderSFTest extends TestCase {
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    Loader loader;
+
+    LoaderSF factory;
+
+    StoreDescription desc;
+
+    /*
+     * create Registry and register Loader
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = DescriptorHelper.registerDescriptor(null, registry,
+                WebappLoader.class.getName(), "Loader", WebappLoader.class
+                        .getName(), "org.apache.catalina.storeconfig.LoaderSF",
+                false, false);
+        factory = (LoaderSF) desc.getStoreFactory();
+        loader = new WebappLoader();
+
+    }
+
+    public void testManagerNonStandardStore() throws Exception {
+        assertTrue(factory.isDefaultLoader(loader));
+        loader.setDelegate(true);
+        assertFalse(factory.isDefaultLoader(loader));
+        String aspectedResult = "<Loader className=\"org.apache.catalina.loader.WebappLoader\""
+                + LF.LINE_SEPARATOR
+                + "    delegate=\"true\"/>"
+                + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testStoreEmpty() throws Exception {
+        assertTrue(factory.isDefaultLoader(loader));
+        String aspectedResult = "";
+        check(aspectedResult);
+    }
+
+    protected void check(String aspectedResult) throws Exception {
+        factory.store(pWriter, -2, loader);
+        assertEquals(aspectedResult, writer.toString());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ManagerSFTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ManagerSFTest.java
new file mode 100644
index 0000000..8fbb394
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ManagerSFTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.Manager;
+import org.apache.catalina.session.StandardManager;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class ManagerSFTest extends TestCase {
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    StandardManager manager;
+
+    ManagerSF factory;
+
+    StoreDescription desc;
+
+    /*
+     * create registery and register Manager descriptor
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = DescriptorHelper.registerDescriptor(null, registry,
+                Manager.class.getName(), "Manager",
+                Manager.class.getName(),
+                "org.apache.catalina.storeconfig.ManagerSF", false, false);
+        desc.addTransientAttribute("entropy");
+        desc.addTransientAttribute("distributable");
+        factory = (ManagerSF) desc.getStoreFactory();
+        manager = new StandardManager();
+
+    }
+
+    public void testFindStandardManager() {
+        StoreDescription managerdesc = registry.findDescription(manager.getClass());
+        assertEquals(desc,managerdesc);
+    }
+    
+    public void testManagerNonStandardStore() throws Exception {
+        assertTrue(factory.isDefaultManager(manager));
+        manager.setMaxActiveSessions(100);
+        assertFalse(factory.isDefaultManager(manager));
+        String aspectedResult = "<Manager className=\"org.apache.catalina.session.StandardManager\""
+                + LF.LINE_SEPARATOR
+                + "    maxActiveSessions=\"100\"/>"
+                + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testStoreEmpty() throws Exception {
+        assertTrue(factory.isDefaultManager(manager));
+        String aspectedResult = "";
+        check(aspectedResult);
+    }
+
+    protected void check(String aspectedResult) throws Exception {
+        factory.store(pWriter, -2, manager);
+        assertEquals(aspectedResult, writer.toString());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ServerChildsTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ServerChildsTest.java
new file mode 100644
index 0000000..8e97fa8
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/ServerChildsTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.core.StandardService;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.storeconfig.StandardServerSF;
+import org.apache.catalina.storeconfig.StoreDescription;
+import org.apache.catalina.storeconfig.StoreRegistry;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class ServerChildsTest extends TestCase {
+
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    StandardServer standardServer = new StandardServer();
+
+    StandardServerSF factory;
+
+    StoreDescription desc;
+
+    /*
+     * create registery and register Server and direct subelement descriptors
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = new StoreDescription();
+        desc.setTag("Server");
+        desc.setTagClass("org.apache.catalina.core.StandardServer");
+        desc.setStandard(true);
+        desc
+                .setStoreFactoryClass("org.apache.catalina.storeconfig.StandardServerSF");
+        registry.registerDescription(desc);
+        factory = new StandardServerSF();
+        desc.setStoreFactory(factory);
+        factory.setRegistry(registry);
+        StoreDescription listdesc = registerDescriptor("Listener",
+                LifecycleListener.class);
+        listdesc
+                .addTransientChild("org.apache.catalina.core.NamingContextListener");
+        listdesc
+                .addTransientChild("org.apache.catalina.mbeans.ServerLifecycleListener");
+        standardServer
+                .addLifecycleListener(new org.apache.catalina.mbeans.ServerLifecycleListener());
+        // add GlobalNamingResource
+        DescriptorHelper.registerDescriptor(desc, registry,
+                NamingResources.class.getName() + ".[GlobalNamingResources]",
+                "GlobalNamingResources", NamingResources.class.getName(),
+                "org.apache.catalina.storeconfig.GlobalNamingResourcesSF",
+                true, false);
+        DescriptorHelper.registerNamingDescriptor(desc, registry);
+        registerDescriptor("Service", StandardService.class,
+                "org.apache.catalina.storeconfig.StandardServiceSF", true,
+                false);
+        DescriptorHelper.registerDescriptor(desc, registry,
+                StandardServer.class.getName() + ".[ServerLifecycleListener]",
+                "ServerLifecycleListener", StandardServer.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        standardServer.addService(new StandardService());
+
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass) {
+        return registerDescriptor(tag, aClass,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass,
+            String factoryClass, boolean fstandard, boolean fdefault) {
+        return DescriptorHelper.registerDescriptor(desc, registry, aClass
+                .getName(), tag, aClass.getName(), factoryClass, fstandard,
+                fdefault);
+    }
+
+    public void testSaveListenerAddServer() throws Exception {
+        assertTrue(standardServer instanceof Lifecycle);
+        assertNotNull(
+                "No Listener Descriptor",
+                registry
+                        .findDescription("org.apache.catalina.mbeans.ServerLifecycleListener"));
+        assertNotNull(
+                "No Listener StoreFactory",
+                registry
+                        .findStoreFactory("org.apache.catalina.mbeans.ServerLifecycleListener"));
+        factory.store(pWriter, -2, standardServer);
+ 
+        String aspectedResult = "<?xml version=\"1.0\" encoding=\""
+                + registry.getEncoding()
+                + "\"?>"
+                + LF.LINE_SEPARATOR
+                + "<Server>"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.mbeans.ServerLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR + "  <GlobalNamingResources>"
+                + LF.LINE_SEPARATOR + "  </GlobalNamingResources>"
+                + LF.LINE_SEPARATOR + "  <Service/>" + LF.LINE_SEPARATOR
+                + "</Server>" + LF.LINE_SEPARATOR;
+        assertEquals(aspectedResult, writer.toString());
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardContextSFTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardContextSFTest.java
new file mode 100644
index 0000000..36cb287
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardContextSFTest.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.naming.directory.DirContext;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.deploy.ContextResourceEnvRef;
+import org.apache.catalina.deploy.ContextResourceLink;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.realm.JAASRealm;
+import org.apache.catalina.session.FileStore;
+import org.apache.catalina.session.JDBCStore;
+import org.apache.catalina.session.PersistentManager;
+import org.apache.catalina.session.StandardManager;
+import org.apache.catalina.storeconfig.StandardContextSF;
+import org.apache.catalina.storeconfig.StoreDescription;
+import org.apache.catalina.storeconfig.StoreRegistry;
+import org.apache.naming.resources.FileDirContext;
+import org.apache.naming.resources.ProxyDirContext;
+import org.apache.naming.resources.WARDirContext;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class StandardContextSFTest extends TestCase {
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    StandardContext standardContext;
+
+    StandardContextSF factory;
+
+    StoreDescription desc;
+
+    /*
+     * create registery and register Context and all subelements
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = new StoreDescription();
+        desc.setTag("Context");
+        desc.setTagClass("org.apache.catalina.core.StandardContext");
+        desc.setStandard(true);
+        desc
+                .setStoreFactoryClass("org.apache.catalina.storeconfig.StandardContextSF");
+        String exceptions[] = { "available", "configFile", "configured",
+                "distributable", "domain", "engineName", "name", "override",
+                "publicId", "replaceWelcomeFiles", "sessionTimeout",
+                "startupTime", "tldScanTime" };
+        for (int i = 0; i < exceptions.length; i++)
+            desc.addTransientAttribute(exceptions[i]);
+
+        registry.registerDescription(desc);
+        factory = new StandardContextSF();
+        desc.setStoreFactory(factory);
+        factory.setRegistry(registry);
+
+        StoreDescription listenerdesc = registerDescriptor("Listener",
+                LifecycleListener.class);
+
+        String listenerskippables[] = {
+                "org.apache.catalina.core.NamingContextListener",
+                "org.apache.catalina.startup.ContextConfig", };
+        for (int i = 0; i < listenerskippables.length; i++)
+            listenerdesc.addTransientChild(listenerskippables[i]);
+
+        StoreDescription realmdesc = registerDescriptor("Realm",
+                JAASRealm.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        StoreDescription managerdesc = registerDescriptor("Manager",
+                StandardManager.class,
+                "org.apache.catalina.storeconfig.ManagerSF", false, false);
+        managerdesc.addTransientAttribute("entropy");
+        managerdesc.addTransientAttribute("distributable");
+        StoreDescription pmanagerdesc = registerDescriptor("Manager",
+                PersistentManager.class,
+                "org.apache.catalina.storeconfig.PersistentManagerSF", false,
+                false);
+        pmanagerdesc.addTransientAttribute("entropy");
+        pmanagerdesc.addTransientAttribute("distributable");
+        DescriptorHelper.registerDescriptor(pmanagerdesc, registry,
+                FileStore.class.getName(), "Store", FileStore.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        DescriptorHelper.registerDescriptor(pmanagerdesc, registry,
+                JDBCStore.class.getName(), "Store", JDBCStore.class.getName(),
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        DescriptorHelper.registerNamingDescriptor(desc, registry);
+        StoreDescription valvedesc = registerDescriptor("Valve", Valve.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        String skippables[] = {
+                "org.apache.catalina.authenticator.BasicAuthenticator",
+                "org.apache.catalina.authenticator.DigestAuthenticator",
+                "org.apache.catalina.authenticator.FormAuthenticator",
+                "org.apache.catalina.authenticator.NonLoginAuthenticator",
+                "org.apache.catalina.authenticator.SSLAuthenticator",
+                "org.apache.catalina.core.StandardContextValve",
+                "org.apache.catalina.valves.CertificatesValve" };
+        for (int i = 0; i < skippables.length; i++)
+            valvedesc.addTransientChild(skippables[i]);
+
+        StoreDescription resdesc = registerDescriptor("Resources",
+                DirContext.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        resdesc.addTransientAttribute("docBase");
+        resdesc.addTransientAttribute("allowLinking");
+        resdesc.addTransientAttribute("cacheMaxSize");
+        resdesc.addTransientAttribute("cacheTTL");
+        resdesc.addTransientAttribute("caseSensitive");
+        resdesc.addTransientChild(FileDirContext.class.getName());
+        resdesc.addTransientChild(ProxyDirContext.class.getName());
+        resdesc.addTransientChild(WARDirContext.class.getName());
+        standardContext = new StandardContext();
+        standardContext.setPath("/myapps");
+        standardContext.setDocBase("myapps");
+
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass) {
+        return registerDescriptor(tag, aClass,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass,
+            String factoryClass, boolean fstandard, boolean fdefault) {
+        return DescriptorHelper.registerDescriptor(desc, registry, aClass
+                .getName(), tag, aClass.getName(), factoryClass, fstandard,
+                fdefault);
+    }
+
+    /**
+     * @TODO Listener only saved at real context. Wrong when next Context
+     *       deployed! see Changes.txt
+     * @throws Exception
+     */
+    public void testListenerStore() throws Exception {
+        standardContext
+                .addLifecycleListener(new org.apache.catalina.storeconfig.InfoLifecycleListener());
+        standardContext
+                .addInstanceListener("org.apache.catalina.ContainerListener");
+        standardContext
+                .addWrapperListener("org.apache.catalina.ContainerListener");
+        standardContext
+                .addWrapperLifecycle("org.apache.catalina.ContainerListener");
+        standardContext.addWatchedResource("/tmp/reloaded");
+        String aspectedResult = "<Context"
+                + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\""
+                + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\">"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.storeconfig.InfoLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR
+                + "  <InstanceListener>org.apache.catalina.ContainerListener</InstanceListener>"
+                + LF.LINE_SEPARATOR
+                + "  <WrapperListener>org.apache.catalina.ContainerListener</WrapperListener>"
+                + LF.LINE_SEPARATOR
+                + "  <WrapperLifecycle>org.apache.catalina.ContainerListener</WrapperLifecycle>"
+                + LF.LINE_SEPARATOR
+                + "  <WatchedResource>/tmp/reloaded</WatchedResource>"
+                + LF.LINE_SEPARATOR + "</Context>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testNamingStore() throws Exception {
+        standardContext
+                .addLifecycleListener(new org.apache.catalina.storeconfig.InfoLifecycleListener());
+        NamingResources resources = standardContext.getNamingResources();
+        ContextResourceEnvRef ref = new ContextResourceEnvRef();
+        ref.setName("foo");
+        ref.setType("type");
+        resources.addResourceEnvRef(ref);
+        ContextResourceLink res = new ContextResourceLink();
+        res.setName("jdbc/Barlocal");
+        res.setType("javax.sql.DataSource");
+        res.setGlobal("jdbc/Bar");
+        resources.addResourceLink(res);
+        String aspectedResult = "<Context"
+                + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\""
+                + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\">"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.storeconfig.InfoLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR + "  <ResourceEnvRef" + LF.LINE_SEPARATOR
+                + "    name=\"foo\"" + LF.LINE_SEPARATOR
+                + "    type=\"type\"/>" + LF.LINE_SEPARATOR + "  <ResourceLink"
+                + LF.LINE_SEPARATOR + "    global=\"jdbc/Bar\""
+                + LF.LINE_SEPARATOR + "    name=\"jdbc/Barlocal\""
+                + LF.LINE_SEPARATOR + "    type=\"javax.sql.DataSource\"/>"
+                + LF.LINE_SEPARATOR + "</Context>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testManagerStore() throws Exception {
+        standardContext.setManager(new StandardManager());
+        String aspectedResult = "<Context" + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\"" + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\">" + LF.LINE_SEPARATOR
+                + "</Context>\r\n";
+        check(aspectedResult);
+    }
+
+    public void testRealmStore() throws Exception {
+        standardContext.setManager(new StandardManager());
+        JAASRealm realm = new JAASRealm();
+        standardContext.setRealm(realm);
+        String aspectedResult = "<Context" + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\"" + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\">" + LF.LINE_SEPARATOR
+                + "  <Realm className=\"org.apache.catalina.realm.JAASRealm\""
+                + LF.LINE_SEPARATOR + "    appName=\"myapps\"/>"
+                + LF.LINE_SEPARATOR + "</Context>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    // @TODO Why the MaxInactiveInterval is after setManager set to 1800 sec?
+    public void testManagerNonStandardStore() throws Exception {
+        StandardManager manager = new StandardManager();
+        manager.setMaxActiveSessions(100);
+        assertEquals(60, manager.getMaxInactiveInterval());
+        standardContext.setManager(manager);
+        assertEquals(1800, manager.getMaxInactiveInterval());
+        String aspectedResult = "<Context"
+                + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\""
+                + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\">"
+                + LF.LINE_SEPARATOR
+                + "  <Manager className=\"org.apache.catalina.session.StandardManager\""
+                + LF.LINE_SEPARATOR + "      maxActiveSessions=\"100\""
+                + LF.LINE_SEPARATOR + "      maxInactiveInterval=\"1800\"/>"
+                + LF.LINE_SEPARATOR + "</Context>\r\n";
+        check(aspectedResult);
+    }
+
+    public void testPersistentManagerStore() throws Exception {
+        PersistentManager manager = new PersistentManager();
+        manager.setSaveOnRestart(false);
+        standardContext.setManager(manager);
+        String aspectedResult = "<Context"
+                + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\""
+                + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\">"
+                + LF.LINE_SEPARATOR
+                + "  <Manager className=\"org.apache.catalina.session.PersistentManager\""
+                + LF.LINE_SEPARATOR + "      maxInactiveInterval=\"1800\""
+                + LF.LINE_SEPARATOR + "      saveOnRestart=\"false\">"
+                + LF.LINE_SEPARATOR + "  </Manager>" + LF.LINE_SEPARATOR
+                + "</Context>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testPersistentManagerFileStore() throws Exception {
+        PersistentManager manager = new PersistentManager();
+        manager.setSaveOnRestart(false);
+        FileStore store = new FileStore();
+        manager.setStore(store);
+        standardContext.setManager(manager);
+        String aspectedResult = "<Context"
+                + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\""
+                + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\">"
+                + LF.LINE_SEPARATOR
+                + "  <Manager className=\"org.apache.catalina.session.PersistentManager\""
+                + LF.LINE_SEPARATOR
+                + "      maxInactiveInterval=\"1800\""
+                + LF.LINE_SEPARATOR
+                + "      saveOnRestart=\"false\">"
+                + LF.LINE_SEPARATOR
+                + "    <Store className=\"org.apache.catalina.session.FileStore\"/>"
+                + LF.LINE_SEPARATOR + "  </Manager>" + LF.LINE_SEPARATOR
+                + "</Context>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+
+    }
+
+    public void testDefaultResources() throws Exception {
+        FileDirContext dirC = new FileDirContext();
+        standardContext.setAllowLinking(true);
+        standardContext.setResources(dirC);
+        StandardHost host = new StandardHost();
+        host.addChild(standardContext);
+        host.setName("localhost");
+        host.setAppBase("webapps");
+        standardContext.resourcesStart();
+        assertNotNull(standardContext.getResources());
+        String aspectedResult = "<Context" + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\"" + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\">" + LF.LINE_SEPARATOR + "</Context>"
+                + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    /*
+     * + " <Resources className=\"org.apache.naming.resources.FileDirContext\"" +
+     * Constants.LINE_SEPARATOR + " allowLinking=\"true\"/>" +
+     * Constants.LINE_SEPARATOR
+     */
+    public void testStoreEmpty() throws Exception {
+        String aspectedResult = "<Context" + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\"" + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\">" + LF.LINE_SEPARATOR + "</Context>"
+                + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    protected void check(String aspectedResult) throws Exception {
+        factory.store(pWriter, -2, standardContext);
+        assertEquals(aspectedResult, writer.toString());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardEngineSFTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardEngineSFTest.java
new file mode 100644
index 0000000..fe0d933
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardEngineSFTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.realm.JAASRealm;
+import org.apache.catalina.storeconfig.StandardEngineSF;
+import org.apache.catalina.storeconfig.StoreDescription;
+import org.apache.catalina.storeconfig.StoreRegistry;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class StandardEngineSFTest extends TestCase {
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    StandardEngine standardEngine;
+
+    StandardEngineSF factory;
+
+    StoreDescription desc;
+
+    /*
+     * create registery and register Engine and direct subelement descriptors
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = new StoreDescription();
+        desc.setTag("Engine");
+        desc.setTagClass("org.apache.catalina.core.StandardEngine");
+        desc.setStandard(true);
+        desc
+                .setStoreFactoryClass("org.apache.catalina.storeconfig.StandardEngineSF");
+        desc.addTransientAttribute("domain");
+        registry.registerDescription(desc);
+        factory = new StandardEngineSF();
+        desc.setStoreFactory(factory);
+        factory.setRegistry(registry);
+        StoreDescription listenerdesc = registerDescriptor("Listener",
+                LifecycleListener.class);
+
+        String listenerskippables[] = {
+                "org.apache.catalina.core.NamingContextListener",
+                "org.apache.catalina.startup.ContextConfig",
+                "org.apache.catalina.startup.EngineConfig",
+                "org.apache.catalina.startup.HostConfig", };
+        for (int i = 0; i < listenerskippables.length; i++)
+            listenerdesc.addTransientChild(listenerskippables[i]);
+
+        StoreDescription realmdesc = registerDescriptor("Realm",
+                JAASRealm.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", true, false);
+        StoreDescription hostdesc = registerDescriptor("Host",
+                StandardHost.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", true, false);
+        hostdesc.addTransientAttribute("domain");
+        StoreDescription valvedesc = registerDescriptor("Valve", Valve.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+
+        String skippables[] = {
+                "org.apache.catalina.authenticator.BasicAuthenticator",
+                "org.apache.catalina.authenticator.DigestAuthenticator",
+                "org.apache.catalina.authenticator.FormAuthenticator",
+                "org.apache.catalina.authenticator.NonLoginAuthenticator",
+                "org.apache.catalina.authenticator.SSLAuthenticator",
+                "org.apache.catalina.core.StandardContextValve",
+                "org.apache.catalina.core.StandardEngineValve",
+                "org.apache.catalina.core.StandardHostValve",
+                "org.apache.catalina.valves.CertificatesValve",
+                "org.apache.catalina.valves.ErrorReportValve",
+                "org.apache.catalina.valves.RequestListenerValve", };
+        for (int i = 0; i < skippables.length; i++)
+            valvedesc.addTransientChild(skippables[i]);
+
+        standardEngine = new StandardEngine();
+        standardEngine.setName("Catalina");
+
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass) {
+        return registerDescriptor(tag, aClass,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+    }
+
+    /**
+     *  
+     */
+    private StoreDescription registerDescriptor(String tag, Class aClass,
+            String factoryClass, boolean fstandard, boolean fdefault) {
+        return DescriptorHelper.registerDescriptor(desc, registry, aClass
+                .getName(), tag, aClass.getName(), factoryClass, fstandard,
+                fdefault);
+    }
+
+    public void testStore() throws Exception {
+        standardEngine
+                .addLifecycleListener(new org.apache.catalina.storeconfig.InfoLifecycleListener());
+        StandardHost host = new StandardHost();
+        host.setName("localhost");
+        standardEngine.addChild(host);
+        String aspectedResult = "<Engine"
+                + LF.LINE_SEPARATOR
+                + "    name=\"Catalina\">"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.storeconfig.InfoLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR + "  <Realm" + LF.LINE_SEPARATOR
+                + "    appName=\"Catalina\"/>" + LF.LINE_SEPARATOR + "  <Host"
+                + LF.LINE_SEPARATOR + "    name=\"localhost\"/>"
+                + LF.LINE_SEPARATOR + "</Engine>" + LF.LINE_SEPARATOR;
+
+        check(aspectedResult);
+    }
+
+    public void testElements() throws Exception {
+        standardEngine.setName("Catalina");
+        standardEngine
+                .addLifecycleListener(new org.apache.catalina.storeconfig.InfoLifecycleListener());
+        standardEngine
+                .addLifecycleListener(new org.apache.catalina.startup.EngineConfig());
+        StandardHost host = new StandardHost();
+        host.setName("localhost");
+        standardEngine.addChild(host);
+        String aspectedResult = "<Engine"
+                + LF.LINE_SEPARATOR
+                + "    name=\"Catalina\">"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.storeconfig.InfoLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR + "  <Realm" + LF.LINE_SEPARATOR
+                + "    appName=\"Catalina\"/>" + LF.LINE_SEPARATOR + "  <Host"
+                + LF.LINE_SEPARATOR + "    name=\"localhost\"/>"
+                + LF.LINE_SEPARATOR + "</Engine>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testValve() throws Exception {
+        standardEngine.setName("Catalina");
+        standardEngine
+                .addLifecycleListener(new org.apache.catalina.storeconfig.InfoLifecycleListener());
+        standardEngine
+                .addLifecycleListener(new org.apache.catalina.startup.EngineConfig());
+        StandardHost host = new StandardHost();
+        host.setName("localhost");
+        standardEngine.addChild(host);
+        standardEngine
+                .addValve(new org.apache.catalina.valves.ErrorReportValve());
+        standardEngine
+                .addValve(new org.apache.catalina.valves.RequestDumperValve());
+        String aspectedResult = "<Engine"
+                + LF.LINE_SEPARATOR
+                + "    name=\"Catalina\">"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.storeconfig.InfoLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR
+                + "  <Realm"
+                + LF.LINE_SEPARATOR
+                + "    appName=\"Catalina\"/>"
+                + LF.LINE_SEPARATOR
+                + "  <Valve className=\"org.apache.catalina.valves.RequestDumperValve\"/>"
+                + LF.LINE_SEPARATOR + "  <Host" + LF.LINE_SEPARATOR
+                + "    name=\"localhost\"/>" + LF.LINE_SEPARATOR + "</Engine>"
+                + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testStoreEmpty() throws Exception {
+        String aspectedResult = "<Engine" + LF.LINE_SEPARATOR
+                + "    name=\"Catalina\">" + LF.LINE_SEPARATOR + "  <Realm"
+                + LF.LINE_SEPARATOR + "    appName=\"Catalina\"/>"
+                + LF.LINE_SEPARATOR + "</Engine>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    protected void check(String aspectedResult) throws Exception {
+        factory.store(pWriter, -2, standardEngine);
+        assertEquals(aspectedResult, writer.toString());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardHostSFTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardHostSFTest.java
new file mode 100644
index 0000000..d7f498b
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardHostSFTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Valve;
+import org.apache.catalina.cluster.CatalinaCluster;
+import org.apache.catalina.cluster.deploy.FarmWarDeployer;
+import org.apache.catalina.cluster.mcast.McastService;
+import org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListener;
+import org.apache.catalina.cluster.tcp.ReplicationListener;
+import org.apache.catalina.cluster.tcp.ReplicationTransmitter;
+import org.apache.catalina.cluster.tcp.ReplicationValve;
+import org.apache.catalina.cluster.tcp.SimpleTcpCluster;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.realm.JAASRealm;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class StandardHostSFTest extends TestCase {
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    StandardHost standardHost;
+
+    StandardHostSF factory;
+
+    StoreDescription desc;
+
+    /*
+     * create registry and register Host and all subelement descriptors
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = new StoreDescription();
+        desc.setTag("Host");
+        desc.setTagClass("org.apache.catalina.core.StandardHost");
+        desc.setStandard(true);
+        desc.setStoreFactoryClass("org.apache.catalina.core.StandardHostSF");
+        desc.addTransientAttribute("domain");
+        registry.registerDescription(desc);
+        factory = new StandardHostSF();
+        desc.setStoreFactory(factory);
+        factory.setRegistry(registry);
+        StoreDescription listenerdesc = registerDescriptor("Listener",
+                LifecycleListener.class);
+
+        String listenerskippables[] = {
+                "org.apache.catalina.core.NamingContextListener",
+                "org.apache.catalina.startup.HostConfig", };
+        for (int i = 0; i < listenerskippables.length; i++)
+            listenerdesc.addTransientChild(listenerskippables[i]);
+
+        StoreDescription realmdesc = registerDescriptor("Realm",
+                JAASRealm.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+        StoreDescription contextdesc = registerDescriptor("Context",
+                StandardContext.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", true, false);
+        String exceptions[] = { "available", "configFile", "configured",
+                "distributable", "domain", "engineName", "name", "publicId",
+                "sessionTimeout", "startupTime", "tldScanTime" };
+        for (int i = 0; i < exceptions.length; i++)
+            contextdesc.addTransientAttribute(exceptions[i]);
+
+        StoreDescription valvedesc = registerDescriptor("Valve", Valve.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+
+        String skippables[] = { "org.apache.catalina.core.StandardHostValve",
+                "org.apache.catalina.valves.CertificatesValve",
+                "org.apache.catalina.valves.ErrorReportValve",
+                "org.apache.catalina.valves.RequestListenerValve", };
+        for (int i = 0; i < skippables.length; i++)
+            valvedesc.addTransientChild(skippables[i]);
+
+        DescriptorHelper.registerClusterDescriptor(desc, registry);
+        standardHost = new StandardHost();
+        standardHost.setName("localhost");
+
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass) {
+        return registerDescriptor(tag, aClass,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass,
+            String factoryClass, boolean fstandard, boolean fdefault) {
+        return DescriptorHelper.registerDescriptor(desc, registry, aClass
+                .getName(), tag, aClass.getName(), factoryClass, fstandard,
+                fdefault);
+    }
+
+    public void testStore() throws Exception {
+        standardHost
+                .addLifecycleListener(new org.apache.catalina.storeconfig.InfoLifecycleListener());
+        String aspectedResult = "<Host"
+                + LF.LINE_SEPARATOR
+                + "    name=\"localhost\">"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.storeconfig.InfoLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR + "</Host>" + LF.LINE_SEPARATOR;
+
+        check(aspectedResult);
+    }
+
+    public void testElements() throws Exception {
+        standardHost
+                .addLifecycleListener(new org.apache.catalina.storeconfig.InfoLifecycleListener());
+        standardHost
+                .addLifecycleListener(new org.apache.catalina.startup.HostConfig());
+        standardHost.setRealm(new JAASRealm());
+        StandardContext context = new StandardContext();
+        context.setDocBase("myapps");
+        context.setPath("/myapps");
+        standardHost.addChild(context);
+        standardHost.addAlias("jovi");
+        String aspectedResult = "<Host"
+                + LF.LINE_SEPARATOR
+                + "    name=\"localhost\">"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.storeconfig.InfoLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR + "  <Alias>jovi</Alias>"
+                + LF.LINE_SEPARATOR
+                + "  <Realm className=\"org.apache.catalina.realm.JAASRealm\""
+                + LF.LINE_SEPARATOR + "    appName=\"localhost\"/>"
+                + LF.LINE_SEPARATOR + "  <Context" + LF.LINE_SEPARATOR
+                + "    docBase=\"myapps\"" + LF.LINE_SEPARATOR
+                + "    path=\"/myapps\"/>" + LF.LINE_SEPARATOR + "</Host>"
+                + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testValve() throws Exception {
+        standardHost
+                .addLifecycleListener(new org.apache.catalina.storeconfig.InfoLifecycleListener());
+        standardHost
+                .addValve(new org.apache.catalina.valves.ErrorReportValve());
+        standardHost
+                .addValve(new org.apache.catalina.valves.RequestDumperValve());
+        standardHost.addValve(new ReplicationValve());
+        String aspectedResult = "<Host"
+                + LF.LINE_SEPARATOR
+                + "    name=\"localhost\">"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.storeconfig.InfoLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR
+                + "  <Valve className=\"org.apache.catalina.valves.RequestDumperValve\"/>"
+                + LF.LINE_SEPARATOR + "</Host>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testClusterEmpty() throws Exception {
+        CatalinaCluster cluster = new SimpleTcpCluster();
+        standardHost.setCluster(cluster);
+        String aspectedResult = "<Host"
+                + LF.LINE_SEPARATOR
+                + "    name=\"localhost\">"
+                + LF.LINE_SEPARATOR
+                + "  <Cluster className=\"org.apache.catalina.cluster.tcp.SimpleTcpCluster\">"
+                + LF.LINE_SEPARATOR + "  </Cluster>" + LF.LINE_SEPARATOR
+                + "</Host>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testCluster() throws Exception {
+        SimpleTcpCluster cluster = new SimpleTcpCluster();
+        cluster.setClusterName("cluster");
+        cluster.setProperty("expireSessionsOnShutdown","false");
+        cluster
+                .setManagerClassName("org.apache.catalina.cluster.session.DeltaManager");
+        McastService service = new McastService();
+        service.setMcastAddr("228.0.0.4");
+        service.setMcastPort(45564);
+        service.setMcastFrequency(500l);
+        service.setMcastDropTime(3000l);
+        cluster.setMembershipService(service);
+        ReplicationListener receiver = new ReplicationListener();
+        receiver.setTcpListenAddress("auto");
+        receiver.setTcpListenPort(4001);
+        receiver.setTcpSelectorTimeout(100l);
+        receiver.setTcpThreadCount(6);
+        cluster.setClusterReceiver(receiver);
+        ReplicationTransmitter sender = new ReplicationTransmitter();
+        sender.setReplicationMode("pooled");
+        cluster.setClusterSender(sender);
+        ReplicationValve valve = new ReplicationValve();
+        valve
+                .setFilter(".*\\.gif;.*\\.js;.*\\.jpg;.*\\.jpeg;.*\\.htm;.*\\.html;.*\\.txt;");
+        cluster.addValve(valve);
+        FarmWarDeployer deployer = new FarmWarDeployer();
+        deployer.setTempDir("/tmp/war-temp/");
+        deployer.setDeployDir("/tmp/war-deploy/");
+        deployer.setWatchDir("/tmp/war-listen/");
+        deployer.setWatchEnabled(false);
+        cluster.setClusterDeployer(deployer);
+        standardHost.setCluster(cluster);
+        cluster.addLifecycleListener(new InfoLifecycleListener());
+        cluster.addClusterListener(new JvmRouteSessionIDBinderListener());
+        // DeltaManager is default!
+        String aspectedResult = "<Host"
+                + LF.LINE_SEPARATOR
+                + "    name=\"localhost\">"
+                + LF.LINE_SEPARATOR
+                + "  <Cluster className=\"org.apache.catalina.cluster.tcp.SimpleTcpCluster\""
+                + LF.LINE_SEPARATOR
+                + "      clusterName=\"cluster\">"
+                + LF.LINE_SEPARATOR
+                + "    <Membership className=\"org.apache.catalina.cluster.mcast.McastService\""
+                + LF.LINE_SEPARATOR
+                + "      mcastAddr=\"228.0.0.4\""
+                + LF.LINE_SEPARATOR
+                + "      mcastDropTime=\"3000\""
+                + LF.LINE_SEPARATOR
+                + "      mcastFrequency=\"500\""
+                + LF.LINE_SEPARATOR
+                + "      mcastPort=\"45564\"/>"
+                + LF.LINE_SEPARATOR
+                + "    <Sender className=\"org.apache.catalina.cluster.tcp.ReplicationTransmitter\""
+                + LF.LINE_SEPARATOR
+                + "      replicationMode=\"pooled\"/>"
+                + LF.LINE_SEPARATOR
+                + "    <Receiver className=\"org.apache.catalina.cluster.tcp.ReplicationListener\""
+                + LF.LINE_SEPARATOR
+                + "      tcpListenAddress=\"auto\""
+                + LF.LINE_SEPARATOR
+                + "      tcpListenPort=\"4001\""
+                + LF.LINE_SEPARATOR
+                + "      tcpSelectorTimeout=\"100\""
+                + LF.LINE_SEPARATOR
+                + "      tcpThreadCount=\"6\"/>"
+                + LF.LINE_SEPARATOR
+                + "    <Deployer className=\"org.apache.catalina.cluster.deploy.FarmWarDeployer\""
+                + LF.LINE_SEPARATOR
+                + "      deployDir=\"/tmp/war-deploy/\""
+                + LF.LINE_SEPARATOR
+                + "      tempDir=\"/tmp/war-temp/\""
+                + LF.LINE_SEPARATOR
+                + "      watchDir=\"/tmp/war-listen/\"/>"
+                + LF.LINE_SEPARATOR
+                + "    <Valve className=\"org.apache.catalina.cluster.tcp.ReplicationValve\""
+                + LF.LINE_SEPARATOR
+                + "      filter=\".*\\.gif;.*\\.js;.*\\.jpg;.*\\.jpeg;.*\\.htm;.*\\.html;.*\\.txt;\"/>"
+                + LF.LINE_SEPARATOR
+                + "    <Listener className=\"org.apache.catalina.storeconfig.InfoLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR
+                + "    <ClusterListener className=\"org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListener\"/>"
+                + LF.LINE_SEPARATOR + "  </Cluster>" + LF.LINE_SEPARATOR
+                + "</Host>" + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testStoreEmpty() throws Exception {
+        String aspectedResult = "<Host" + LF.LINE_SEPARATOR
+                + "    name=\"localhost\">" + LF.LINE_SEPARATOR + "</Host>"
+                + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    protected void check(String aspectedResult) throws Exception {
+        factory.store(pWriter, -2, standardHost);
+        assertEquals(aspectedResult, writer.toString());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardServiceSFTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardServiceSFTest.java
new file mode 100644
index 0000000..4997c45
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StandardServiceSFTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardService;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class StandardServiceSFTest extends TestCase {
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    StandardService standardService = new StandardService();
+
+    StandardServiceSF factory;
+
+    StoreDescription desc;
+
+    /*
+     * create registry and register Service and all direct subelement
+     * descriptors
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = new StoreDescription();
+        desc.setTag("Service");
+        desc.setTagClass("org.apache.catalina.core.StandardService");
+        desc.setStandard(true);
+        desc.setStoreFactoryClass("org.apache.catalina.core.StandardServiceSF");
+        registry.registerDescription(desc);
+        factory = new StandardServiceSF();
+        desc.setStoreFactory(factory);
+        factory.setRegistry(registry);
+        registerDescriptor("Listener", LifecycleListener.class);
+        registerDescriptor("Engine", StandardEngine.class,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", true, false);
+        StoreDescription cdesc = registerDescriptor("Connector",
+                Connector.class, "org.apache.catalina.storeconfig.ConnectorSF",
+                true, true);
+        cdesc.getStoreFactory().setStoreAppender(new ConnectorStoreAppender());
+        StoreDescription pdesc = DescriptorHelper
+                .registerDescriptor(null, registry, Connector.class.getName()
+                        + ".[ProtocolHandler]", "ProtocolHandler",
+                        Connector.class.getName(),
+                        "org.apache.catalina.storeconfig.StoreFactoryBase",
+                        true, false);
+        pdesc.addTransientAttribute("keystore");
+        pdesc.addTransientAttribute("keypass");
+        pdesc.addTransientAttribute("keytype");
+        pdesc.addTransientAttribute("randomfile");
+        pdesc.addTransientAttribute("protocols");
+        pdesc.addTransientAttribute("clientauth");
+        pdesc.addTransientAttribute("protocol");
+        pdesc.addTransientAttribute("port");
+        pdesc.addTransientAttribute("secure");
+
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass) {
+        return registerDescriptor(tag, aClass,
+                "org.apache.catalina.storeconfig.StoreFactoryBase", false,
+                false);
+    }
+
+    private StoreDescription registerDescriptor(String tag, Class aClass,
+            String factoryClass, boolean fstandard, boolean fdefault) {
+        return DescriptorHelper.registerDescriptor(desc, registry, aClass
+                .getName(), tag, aClass.getName(), factoryClass, fstandard,
+                fdefault);
+    }
+
+    public void testStoreAJP() throws Exception {
+        standardService
+                .addLifecycleListener(new org.apache.catalina.mbeans.ServerLifecycleListener());
+        Connector connector = new Connector();
+        standardService.addConnector(connector);
+        standardService.setContainer(new StandardEngine());
+        String aspectedResult = "<Service>"
+                + LF.LINE_SEPARATOR
+                + "  <Listener className=\"org.apache.catalina.mbeans.ServerLifecycleListener\"/>"
+                + LF.LINE_SEPARATOR + "  <Connector/>" + LF.LINE_SEPARATOR
+                + "  <Engine/>" + LF.LINE_SEPARATOR + "</Service>"
+                + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    public void testStoreEmpty() throws Exception {
+        String aspectedResult = "<Service>" + LF.LINE_SEPARATOR + "</Service>"
+                + LF.LINE_SEPARATOR;
+        check(aspectedResult);
+    }
+
+    protected void check(String aspectedResult) throws Exception {
+        factory.store(pWriter, -2, standardService);
+        assertEquals(aspectedResult, writer.toString());
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreAppenderTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreAppenderTest.java
new file mode 100644
index 0000000..6431614
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreAppenderTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.cluster.tcp.ReplicationTransmitter;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.deploy.ContextResource;
+import org.apache.catalina.startup.SetAllPropertiesRule;
+import org.apache.tomcat.util.digester.Digester;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Peter Rossbach
+ */
+public class StoreAppenderTest extends TestCase {
+
+    /**
+     * Create the digester which will be used to parse context config files.
+     */
+    protected Digester createDigester() {
+        Digester digester = new Digester();
+        digester.setValidating(false);
+        digester.addObjectCreate("Resource",
+                "org.apache.catalina.deploy.ContextResource");
+        digester.addRule("Resource", new SetAllPropertiesRule());
+        return digester;
+    }
+
+    public void testNormalResource() throws IOException, SAXException {
+        Digester digester = createDigester();
+        String example = "<Resource auth=\"Container\" name=\"jdbc/Emp\" type=\"javax.sql.DataSource\"/>";
+        StringReader reader = new StringReader(example);
+        ContextResource resource = (ContextResource) digester.parse(reader);
+        assertNotNull(resource);
+        assertEquals("javax.sql.DataSource", resource.getType());
+    }
+
+    public void testPropertyResouce() throws IOException, SAXException {
+        Digester digester = createDigester();
+        String example = "<Resource auth=\"Container\" name=\"mail/MailSession\" type=\"javax.mail.session\" mail.host=\"localhost\"/>";
+        StringReader reader = new StringReader(example);
+        ContextResource resource = (ContextResource) digester.parse(reader);
+        assertNotNull(resource);
+        assertEquals("localhost", resource.getProperty("mail.host"));
+    }
+
+    public void testStoreStandard() throws Exception {
+        StoreDescription desc = new StoreDescription();
+        desc.setStandard(true);
+        PrintWriter writer = new PrintWriter(new StringWriter());
+        StandardServer bean = new StandardServer();
+        new StoreAppender().printAttributes(writer, 0, true, bean, desc);
+    }
+
+    public void testStoreReplicationTransmitter() throws Exception {
+        StoreDescription desc = new StoreDescription();
+        desc.setStandard(true);
+        StringWriter swriter = new StringWriter();
+        PrintWriter writer = new PrintWriter(swriter);
+        ReplicationTransmitter bean = new ReplicationTransmitter();
+        bean.setReplicationMode("asynchronous");
+        bean.setProperty("keepAliveTimeout","80000");
+        new IDynamicPropertyStoreAppender().printAttributes(writer, 0, true, bean, desc);
+        String aspectedResult =LF.LINE_SEPARATOR           
+           + "    replicationMode=\"asynchronous\"" + LF.LINE_SEPARATOR 
+           + "    keepAliveTimeout=\"80000\"" ;
+        assertEquals(aspectedResult, swriter.getBuffer().toString());
+
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreContextAppenderTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreContextAppenderTest.java
new file mode 100644
index 0000000..4a55d12
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreContextAppenderTest.java
@@ -0,0 +1,134 @@
+/*
+ * Created on 24.08.2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.apache.catalina.storeconfig;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.startup.ContextConfig;
+import java.io.File ;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class StoreContextAppenderTest extends TestCase {
+
+    StoreContextAppender appender = new StoreContextAppender();
+
+    StandardContext context = new StandardContext();
+
+    StandardHost host = new StandardHost();
+
+    /*
+     * setup default Engine, Host and Context
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+        host.setName("localhost");
+        context.setParent(host);
+        StandardEngine engine = new StandardEngine();
+        engine.setName("Catalina");
+        host.setParent(engine);
+        super.setUp();
+    }
+
+    public void testWorkDirManager() {
+        context.setPath("/manager");
+        String defaultDir = appender.getDefaultWorkDir(context);
+        assertEquals("work\\Catalina\\localhost\\manager", defaultDir);
+
+    }
+
+    public void testWorkDirRoot() {
+        context.setPath("");
+        String defaultDir = appender.getDefaultWorkDir(context);
+        assertEquals("work\\Catalina\\localhost\\_", defaultDir);
+    }
+
+    public void testHostWorkDirRoot() {
+        context.setPath("");
+        host.setWorkDir("hostwork");
+        String defaultDir = appender.getDefaultWorkDir(context);
+        assertEquals("hostwork\\_", defaultDir);
+    }
+
+    public void testIsPrintValueDefault() {
+        StandardContext context2 = new StandardContext();
+        context.setPath("");
+        context.setWorkDir("work\\Catalina\\localhost\\_");
+        assertFalse(appender.isPrintValue(context, context2, "workDir", null));
+    }
+
+    public void testIsPrintValue() {
+        StandardContext context2 = new StandardContext();
+        context.setPath("");
+        context.setWorkDir("C:\\work\\Catalina\\localhost\\_");
+        assertTrue(appender.isPrintValue(context, context2, "workDir", null));
+    }
+
+    public void testHostIsPrintValuedefault() {
+        StandardContext context2 = new StandardContext();
+        context.setPath("");
+        host.setWorkDir("hostwork");
+        context.setWorkDir("hostwork\\_");
+        assertFalse(appender.isPrintValue(context, context2, "workDir", null));
+    }
+
+    public void _testDefaultInstance() throws Exception {
+        assertTrue(context.getCookies());
+        assertFalse(context.getReloadable());
+        StandardContext defaultContext = (StandardContext) appender
+                .defaultInstance(context);
+        assertFalse(defaultContext.getCookies());
+        assertTrue(defaultContext.getReloadable());
+        assertEquals(2, defaultContext.findLifecycleListeners().length);
+        assertTrue(defaultContext.findLifecycleListeners()[0] instanceof ContextConfig);
+        assertTrue(defaultContext.findLifecycleListeners()[1] instanceof InfoLifecycleListener);
+    }
+
+    public void _testDefaultInstanceWithoutOverride() throws Exception {
+        context.setOverride(true);
+        StandardContext defaultContext = (StandardContext) appender
+                .defaultInstance(context);
+        assertEquals(0, defaultContext.findLifecycleListeners().length);
+
+    }
+    
+    public void testPath() throws Exception {
+        StandardContext defaultContext = (StandardContext) appender
+        .defaultInstance(context);
+        context.setPath("/myapps");
+        assertNull(context.getConfigFile());
+        StoreDescription desc = new StoreDescription();
+        desc.setExternalAllowed(true);
+        desc.setStoreSeparate(true);
+        assertTrue(appender.isPrintValue(context, defaultContext, "path", desc));
+        context.setConfigFile("conf/Catalina/locahost/myapps.xml");
+        assertFalse(appender.isPrintValue(context, defaultContext, "path", desc));
+        desc.setExternalAllowed(false);
+        assertFalse(appender.isPrintValue(context, defaultContext, "path", desc));
+        desc.setExternalAllowed(true);
+        desc.setStoreSeparate(false);
+        assertFalse(appender.isPrintValue(context, defaultContext, "path", desc));
+    }
+    
+    public void testDocBase() throws Exception {
+        StandardContext defaultContext = (StandardContext) appender
+        .defaultInstance(context);
+        context.setPath("/myapps");
+        context.setDocBase("myapps");
+        host.setAppBase("webapps");
+        assertFalse(appender.isPrintValue(context, defaultContext, "docBase", null));
+        context.setDocBase(System.getProperty("java.io.tmpdir") + "/myapps");
+        assertTrue(appender.isPrintValue(context, defaultContext, "docBase", null));
+        
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreLoaderTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreLoaderTest.java
new file mode 100644
index 0000000..dcd1979
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreLoaderTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.cluster.tcp.ReplicationTransmitter;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.mbeans.ServerLifecycleListener;
+import org.apache.tomcat.util.digester.Digester;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class StoreLoaderTest extends TestCase {
+
+    public void testDigester() throws IOException, SAXException {
+        Digester digester = StoreLoader.createDigester();
+        String example = "<Registry name=\"Tomcat\" version=\"5.5.0\" encoding=\"UTF-8\" >"
+                + " <Description "
+                + "  tag=\"Server\""
+                + "	standard=\"true\""
+                + "	default=\"true\""
+                + "  tagClass=\"org.apache.catalina.core.StandardServer\""
+                + "  storeFactoryClass=\"org.apache.catalina.storeconfig.StandardServerSF\">"
+                + " </Description>" + "</Registry>";
+        StringReader reader = new StringReader(example);
+        StoreRegistry registry = (StoreRegistry) digester.parse(reader);
+        assertNotNull(registry);
+        assertEquals("Tomcat", registry.getName());
+        assertEquals("5.5.0", registry.getVersion());
+        StoreDescription desc = registry.findDescription(StandardServer.class);
+        assertNotNull(desc);
+        assertEquals("org.apache.catalina.core.StandardServer", desc
+                .getTagClass());
+        assertEquals("Server", desc.getTag());
+    }
+
+    public void testLoadRegistry() {
+        StoreLoader loader = new StoreLoader();
+        loader.load();
+        StoreRegistry registry = loader.getRegistry();
+        assertNotNull(registry);
+        assertEquals("UTF-8", registry.getEncoding());
+        StoreDescription desc = registry.findDescription(StandardServer.class);
+        assertNotNull(desc);
+        assertEquals("org.apache.catalina.core.StandardServer", desc
+                .getTagClass());
+        desc = registry.findDescription(StandardContext.class);
+        assertNotNull(desc);
+        assertEquals(StandardContext.class.getName(), desc.getTagClass());
+        assertTrue(desc.isStoreSeparate());
+        assertNotNull(desc.getStoreFactory());
+        assertEquals(registry, desc.getStoreFactory().getRegistry());
+        assertEquals(StandardContextSF.class, desc.getStoreFactory().getClass());
+        desc = registry
+                .findDescription("org.apache.catalina.core.StandardServer.[ServerLifecycleListener]");
+        assertEquals(ServerLifecycleListener.class.getName(), desc
+                .getTagClass());
+        desc = registry.findDescription(ReplicationTransmitter.class);
+        assertNotNull(desc);
+        assertEquals(ReplicationTransmitter.class.getName(), desc
+                .getTagClass());
+        assertNotNull(desc.getStoreFactory());
+        assertEquals(IDynamicPropertyStoreAppender.class, desc.getStoreFactory().getStoreAppender().getClass()
+                );
+        
+    }
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreRegistryTest.java b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreRegistryTest.java
new file mode 100644
index 0000000..5d8c462
--- /dev/null
+++ b/container/modules/storeconfig/test/src/share/org/apache/catalina/storeconfig/StoreRegistryTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.core.StandardServer;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class StoreRegistryTest extends TestCase {
+
+    StoreRegistry registry;
+
+    StringWriter writer = new StringWriter();
+
+    PrintWriter pWriter = new PrintWriter(writer);
+
+    StandardServer standardServer = new StandardServer();
+
+    IStoreFactory factory;
+
+    StoreDescription desc;
+
+    /*
+     * create registry and register Server 
+     * 
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception {
+
+        super.setUp();
+        registry = new StoreRegistry();
+        desc = new StoreDescription();
+        desc.setTag("Server");
+        desc.setTagClass("org.apache.catalina.core.StandardServer");
+        desc.setStandard(true);
+        desc
+                .setStoreFactoryClass("org.apache.catalina.storeconfig.StoreFactoryBase");
+        registry.registerDescription(desc);
+        factory = new StoreFactoryBase();
+        factory.setRegistry(registry);
+    }
+
+    public void testSaveServer() throws Exception {
+        assertNotNull(registry.findDescription(StandardServer.class));
+        factory.store(pWriter, -2, standardServer);
+        assertEquals("XML Diff", "<Server/>" + LF.LINE_SEPARATOR, writer
+                .toString());
+    }
+
+    public void testAttributes() throws Exception {
+        standardServer.setPort(7305);
+        factory.store(pWriter, -2, standardServer);
+        assertEquals("XML Diff", "<Server" + LF.LINE_SEPARATOR
+                + "  port=\"7305\"/>" + LF.LINE_SEPARATOR, writer.toString());
+
+    }
+
+}
\ No newline at end of file
diff --git a/container/modules/storeconfig/to-do.txt b/container/modules/storeconfig/to-do.txt
new file mode 100644
index 0000000..23297c4
--- /dev/null
+++ b/container/modules/storeconfig/to-do.txt
@@ -0,0 +1,9 @@
+1. Fix new connector properties and protocolhandler for 5.5 (testcases)
+	 Work!
+2. Fix context default handling (hard stuff)
+3. Check cluster transient attributes
+4. add documentation
+5. replace old server implementation instead current listener implementation
+6. Context path attribute handling ( delete at external context.xml and used inside server.xml!)
+7. Test with Admin App
+
diff --git a/container/tester/.cvsignore b/container/tester/.cvsignore
new file mode 100644
index 0000000..9d0b71a
--- /dev/null
+++ b/container/tester/.cvsignore
@@ -0,0 +1,2 @@
+build
+dist
diff --git a/container/tester/build.xml b/container/tester/build.xml
new file mode 100644
index 0000000..17d0021
--- /dev/null
+++ b/container/tester/build.xml
@@ -0,0 +1,226 @@
+<project name="Tester" default="build-main" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="api.home" value="../../jakarta-servletapi-5/dist"/>
+  <property name="tester.build"    value="${basedir}/build"/>
+  <property name="tester.deploy"   value="${basedir}/../build"/>
+  <property name="tester.dist"     value="${basedir}/dist"/>
+
+  <!-- ================== Derived Property Values ========================= -->
+  <property name="ant.jar"         value="${ant.home}/lib/ant.jar"/>
+  <property name="servlet-api.jar" value="${api.home}/jsr154/dist/lib/servlet-api.jar"/>
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${tester.build}"/>
+    <mkdir dir="${tester.build}/bin"/>
+    <mkdir dir="${tester.build}/classes"/>
+    <mkdir dir="${tester.build}/conf"/>
+    <mkdir dir="${tester.build}/lib"/>
+  </target>
+
+
+  <!-- =================== BUILD: Copy Static Files ======================= -->
+  <target name="build-static" depends="build-prepare">
+
+    <!-- Executable Commands -->
+    <mkdir  dir="${tester.build}/bin"/>
+    <copy todir="${tester.build}/bin">
+      <fileset dir="src/bin" />
+    </copy>
+    <fixcrlf srcdir="${tester.build}/bin" includes="*.sh"  eol="lf"/>
+    <fixcrlf srcdir="${tester.build}/bin" includes="*.bat" eol="crlf"/>
+    <chmod perm="+x" file="${tester.build}/bin/tester.sh"/>
+    <copy todir="${tester.build}/conf">
+      <fileset dir="src/conf" />
+    </copy>
+
+    <!-- Compiled Classes -->
+    <mkdir  dir="${tester.build}/classes"/>
+
+    <!-- Web Application -->
+    <mkdir  dir="${tester.build}/web"/>
+    <copy todir="${tester.build}/web">
+      <fileset dir="web"/>
+    </copy>
+    <mkdir  dir="${tester.build}/web/WEB-INF/classes"/>
+
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <target name="build-main" depends="build-static">
+
+    <!-- Compile tester components and tools -->
+    <javac srcdir="src/tester" destdir="${tester.build}/classes"
+     classpath="${ant.jar}:${servlet-api.jar}:${xercesImpl.jar}"
+     deprecation="${compile.deprecation}"
+     debug="${compile.debug}" optimize="off"
+     excludes="**/CVS/**"/>
+
+    <!-- Copy static resource files -->
+    <copy todir="${tester.build}/classes">
+      <fileset dir="src/tester">
+        <include name="**/*.properties"/>
+      </fileset>
+      <fileset dir="src/tester">
+        <include name="**/*.txt"/>
+      </fileset>
+    </copy>
+    <copy file="${tester.build}/classes/org/apache/tester/unshared/UnsharedSessionBean.class"
+        tofile="${tester.build}/web/WEB-INF/classes/org/apache/tester/unshared/UnsharedSessionBean.class"/>
+    <copy file="src/tester/org/apache/tester/Resources01.txt"
+        tofile="${tester.build}/web/WEB-INF/classes/org/apache/tester/Unpacked01.txt"/>
+    <copy file="src/tester/org/apache/tester/Resources01.txt"
+        tofile="${tester.build}/classes/org/apache/tester/shared/Shared01.txt"/>
+    <copy file="src/tester/org/apache/tester/Resources01.txt"
+        tofile="${tester.build}/classes/org/apache/tester/unpshared/UnpShared01.txt"/>
+    <copy file="src/tester/org/apache/tester/Resources03.txt"
+        tofile="${tester.build}/web/WEB-INF/classes/org/apache/tester/Unpacked03.txt"/>
+    <copy file="src/tester/org/apache/tester/Resources03.txt"
+        tofile="${tester.build}/classes/org/apache/tester/shared/Shared03.txt"/>
+    <copy file="src/tester/org/apache/tester/Resources03.txt"
+        tofile="${tester.build}/classes/org/apache/tester/unpshared/UnpShared03.txt"/>
+    <copy file="src/tester/org/apache/tester/Resources05.txt"
+        tofile="${tester.build}/web/WEB-INF/classes/org/apache/tester/Unpacked05.txt"/>
+
+    <!-- Install Xerces -->
+    <copy  todir="${tester.build}/web/WEB-INF/lib" file="${xercesImpl.jar}"/>
+    <copy  todir="${tester.build}/web/WEB-INF/lib" file="${xml-apis.jar}"/>
+
+    <!-- Create and install tester library -->
+    <mkdir   dir="${tester.build}/web/WEB-INF/lib"/>
+    <jar jarfile="${tester.build}/web/WEB-INF/lib/tester.jar">
+      <fileset dir="${tester.build}/classes">
+        <exclude name="**/shared/*"/>
+        <exclude name="**/unshared/*"/>
+      </fileset>
+    </jar>
+
+  </target>
+
+
+  <!-- ================ BUILD: Create Tester Javadocs ===================== -->
+  <target name="javadoc" depends="build-main">
+    <delete dir="${tester.build}/javadoc"/>
+    <mkdir dir="${tester.build}/javadoc"/>
+    <javadoc packagenames="org.apache.tester.*"
+     classpath="${ant.jar}:${tester.build}/classes"
+     sourcepath="src/tester"
+     destdir="${tester.build}/javadoc"
+     author="true"
+     version="true"
+     windowtitle="Tester Internal API Documentation"
+     doctitle="Tester Tools and Tests API"
+     bottom="Copyright &#169; 2000-2004 Apache Software Foundation.  All Rights Reserved."
+    />
+  </target>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${tester.build}"/>
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"/>
+
+
+  <!-- ====================== DEPLOY: Create Directories ================== -->
+  <target name="deploy-prepare">
+    <mkdir dir="${tester.deploy}"/>
+    <mkdir dir="${tester.deploy}/bin"/>
+    <mkdir dir="${tester.deploy}/conf"/>
+  </target>
+
+
+  <!-- ====================== DEPLOY: Copy Static Files =================== -->
+  <target name="deploy-static" depends="build-main,deploy-prepare">
+
+    <!-- Executable Commands -->
+    <copy todir="${tester.deploy}/bin">
+      <fileset dir="${tester.build}/bin" />
+    </copy>
+    <fixcrlf srcdir="${tester.deploy}/bin" includes="*.sh"  eol="lf"/>
+    <fixcrlf srcdir="${tester.deploy}/bin" includes="*.bat" eol="crlf"/>
+    <chmod perm="+x" file="${tester.deploy}/bin/tester.sh"/>
+    <copy todir="${tester.deploy}/conf" overwrite="true">
+      <fileset dir="${tester.build}/conf" />
+    </copy>
+
+    <!-- Unpacked Shared Classes -->
+    <mkdir   dir="${tester.deploy}/shared/classes"/>
+    <copy  todir="${tester.deploy}/shared/classes">
+      <fileset dir="${tester.build}/classes">
+        <include name="**/unpshared/*"/>
+      </fileset>
+    </copy>
+
+    <!-- Shared Library -->
+    <mkdir   dir="${tester.deploy}/shared/lib"/>
+    <jar jarfile="${tester.deploy}/shared/lib/tester-shared.jar">
+      <fileset dir="${tester.build}/classes">
+        <include name="**/shared/*"/>
+      </fileset>
+    </jar>
+
+    <!-- Web Application -->
+    <mkdir  dir="${tester.deploy}/webapps/tester"/>
+    <copy todir="${tester.deploy}/webapps/tester">
+      <fileset dir="${tester.build}/web"/>
+    </copy>
+
+  </target>
+
+
+  <!-- ====================== DEPLOY: Create Tester JAR =================== -->
+  <target name="deploy-main" depends="deploy-static"/>
+
+
+  <!-- ====================== DEPLOY: Deploy Tester Build ================= -->
+  <target name="deploy" depends="deploy-main"/>
+
+
+  <!-- ====================== DEPLOY: Clean Directories =================== -->
+  <target name="deploy-clean">
+    <delete dir="${tester.deploy}/webapps/tester"/>
+  </target>
+
+
+  <!-- ================ DIST: Create Distribution ========================= -->
+  <target name="dist" depends="build-main">
+
+    <mkdir dir="${tester.dist}/bin"/>
+    <copy todir="${tester.dist}/bin">
+      <fileset dir="${tester.build}/bin" />
+    </copy>
+    <fixcrlf srcdir="${tester.dist}/bin" includes="*.sh"  eol="lf"/>
+    <fixcrlf srcdir="${tester.dist}/bin" includes="*.bat" eol="crlf"/>
+    <chmod perm="+x" file="${tester.dist}/bin/tester.sh"/>
+
+    <mkdir   dir="${tester.dist}/webapps"/>
+    <jar jarfile="${tester.dist}/webapps/tester.war"
+         basedir="${tester.build}/web"/>
+
+  </target>
+
+
+  <!-- ======================== DIST: Clean Directory ===================== -->
+  <target name="dist-clean">
+    <delete dir="${tester.dist}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean, dist-clean"/>
+
+
+</project>
diff --git a/container/tester/src/bin/tester.xml b/container/tester/src/bin/tester.xml
new file mode 100644
index 0000000..04c7e9e
--- /dev/null
+++ b/container/tester/src/bin/tester.xml
@@ -0,0 +1,1923 @@
+<project name="Tester" default="all">
+
+  <!-- ========== Global Properties ======================================= -->
+  <property name="catalina.home"  value="../../build/tomcat-4.0"/>
+  <property name="debug"          value="0"/>
+  <property name="host"           value="localhost"/>
+  <property name="port"           value="8080"/>
+<!--  <property name="protocol"       value="HTTP/1.0"/> -->
+  <property name="protocol"       value="HTTP/1.0"/> <!-- Use HttpURLConnection -->
+  <property name="context.path"   value="/tester"/>
+  <property name="jsp-examples.path"  value="/jsp-examples"/>
+  <property name="servlets-examples.path"  value="/servlets-examples"/>
+  <property name="golden.path"    value="${context.path}/golden"/>
+  <property name="manager.path"   value="/manager"/>
+  <property name="reload.path"    value="/tester"/>
+  <taskdef  name="tester"     classname="org.apache.tester.TestClient">
+    <classpath>
+      <pathelement location="${catalina.home}/webapps/tester/WEB-INF/lib/tester.jar"/>
+    </classpath>
+  </taskdef>
+
+
+  <target name="all" depends="ROOT,Authentication,CaseSensitive,Decoding,ErrorPage,FilterRequest,FilterResponse,Jndi,Jsp,Lifecycle,RequestDispatcher,Resources,Security,ServletContext,ServletRequest,ServletResponse,HttpSession,XercesTest"/>
+
+  <target name="ROOT">
+
+    <!-- ========== Basic Run State ======================================= -->
+
+    <!-- Should be able to see the home page -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="/index.jsp" debug="${debug}"
+          status="200"/>
+
+    <!-- Should be able to use relative path to document root -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${jsp-examples.path}/.." debug="${debug}"
+          status="200"/>
+
+    <!-- Should be able to successfully retrieve a golden file -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Golden01"
+          golden="${golden.path}/Golden01.txt"/>
+
+    <!-- Should be able to successfully retrieve a golden file -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedGolden01"
+          golden="${golden.path}/Golden01.txt"/>
+
+  </target>
+
+
+  <target name="Authentication">
+
+    <!-- ========== Authentication ======================================== -->
+
+    <!-- Once a user has been authenticated, the corresponding user identity
+         should be visible to all other requests in this web application, even
+         for URIs that are not protected by security constraints.  This is
+         tested by invoking a protected URI followed by a non-protected URI
+    -->
+
+    <!-- ========== Basic Access to Authenticated Resources =============== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+          debug="${debug}"
+         request="${context.path}/protected/Authentication01"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+      outContent="Authentication01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+          debug="${debug}"
+         request="${context.path}/protected/Authentication02"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+      outContent="Authentication02 PASSED"/>
+
+    <!-- Test isUserInRole() on actual role and on an alias (servlet) -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+          debug="${debug}"
+         request="${context.path}/protected/Authentication03"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+      outContent="Authentication03 PASSED"/>
+
+    <!-- Test isUserInRole() on actual role and on an alias (JSP page) -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+          debug="${debug}"
+         request="${context.path}/protected/Authentication04"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+      outContent="Authentication04 PASSED"/>
+
+    <!-- ========== "All Allowed" and "All Disallowed" Access ============= -->
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+          debug="${debug}"
+         request="${context.path}/allowed/Authentication05"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+      outContent="Authentication05 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+          debug="${debug}"
+         request="${context.path}/disallowed/Authentication05"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+          status="403"/>
+
+  </target>
+
+
+  <target name="CaseSensitive">
+
+    <!-- ========== Case Sensitive Request URI Matching =================== -->
+
+    <!-- Make sure that static resources are matched case sensitively -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="/index.HTML" debug="${debug}"
+          status="404"/>
+
+    <!-- Should be able to execute the Date example -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${jsp-examples.path}/dates/date.jsp" debug="${debug}"
+          status="200"/>
+
+    <!-- Should not be able to view the source of the Date example -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${jsp-examples.path}/dates/date.Jsp" debug="${debug}"
+          status="404"/>
+
+    <!-- Should not be able to view the source of the Date example -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${jsp-examples.path}/dates/Date.jsp" debug="${debug}"
+          status="404"/>
+
+    <!-- Should not be able to view the source of the Date example -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${jsp-examples.path}/Dates/date.jsp" debug="${debug}"
+          status="404"/>
+
+    <!-- Should be able to execute the HelloWorld servlet example -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${servlets-examples.path}/servlet/HelloWorldExample" debug="${debug}"
+          status="200"/>
+
+    <!-- Should not be able to execute HelloWorld with different cases -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${servlets-examples.path}/servlet/helloWorldExample" debug="${debug}"
+          status="404"/>
+
+    <!-- Should not be able to execute HelloWorld with different cases -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${servlets-examples.path}/Servlet/HelloWorldExample" debug="${debug}"
+          status="404"/>
+
+  </target>
+
+
+  <target name="Decoding">
+
+    <!-- ========== URL Decoding Tests ==================================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Decoding01?servlet=/Decoding01"
+      outContent="Decoding01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Decoding01/extra?servlet=/Decoding01&amp;path=/extra"
+      outContent="Decoding01 PASSED"/>
+ 
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Decoding0%31?servlet=/Decoding01"
+      outContent="Decoding01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Decoding01/extr%61?servlet=/Decoding01&amp;path=/extra"
+      outContent="Decoding01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Decoding0%31/extr%61?servlet=/Decoding01&amp;path=/extra"
+      outContent="Decoding01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedDecoding01?servlet=/WrappedDecoding01"
+      outContent="Decoding01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedDecoding01/extra?servlet=/WrappedDecoding01&amp;path=/extra"
+      outContent="Decoding01 PASSED"/>
+ 
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedDecoding0%31?servlet=/WrappedDecoding01"
+      outContent="Decoding01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedDecoding01/extr%61?servlet=/WrappedDecoding01&amp;path=/extra"
+      outContent="Decoding01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedDecoding0%31/extr%61?servlet=/WrappedDecoding01&amp;path=/extra"
+      outContent="Decoding01 PASSED"/>
+
+    <!-- Verify we can access the JSP page normally -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${jsp-examples.path}/snp/snoop.jsp"
+          status="200"/>
+
+  </target>
+
+
+  <target name="ErrorPage">
+
+    <!-- ========== Error Code Mapping ==================================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ErrorPage01" debug="${debug}"
+      outContent="ErrorPage02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedErrorPage01" debug="${debug}"
+      outContent="ErrorPage02 PASSED"/>
+
+
+    <!-- ========== Exception Mapping (Servlet Source) ==================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ErrorPage03" debug="${debug}"
+          status="200"
+      outContent="ErrorPage04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedErrorPage03" debug="${debug}"
+          status="200"
+      outContent="ErrorPage04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ErrorPage05?type=Arithmetic"
+           debug="${debug}"
+          status="200"
+      outContent="ErrorPage06 PASSED - SERVLET"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedErrorPage05?type=Arithmetic"
+           debug="${debug}"
+          status="200"
+      outContent="ErrorPage06 PASSED - SERVLET"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ErrorPage05?type=Array"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage06 PASSED - JSP"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedErrorPage05?type=Array"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage06 PASSED - JSP"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ErrorPage05?type=Number"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage06 PASSED - HTML"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedErrorPage05?type=Number"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage06 PASSED - HTML"/>
+
+    <!-- ========== Load On Startup Exception Handling ==================== -->
+
+    <!-- NOTE: HttpURLConnection throws FileNotFoundException on 503s -->
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/ErrorPage07"
+           debug="${debug}"
+          status="404"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/WrappedErrorPage07"
+           debug="${debug}"
+          status="404"/>
+
+    <!-- ========== Exception Mapping (JSP Source) ======================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ErrorPage08?type=Arithmetic"
+           debug="${debug}"
+          status="200"
+      outContent="ErrorPage06 PASSED - SERVLET"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedErrorPage08?type=Arithmetic"
+           debug="${debug}"
+          status="200"
+      outContent="ErrorPage06 PASSED - SERVLET"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ErrorPage08?type=Array"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage06 PASSED - JSP"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedErrorPage08?type=Array"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage06 PASSED - JSP"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ErrorPage08?type=Number"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage06 PASSED - HTML"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedErrorPage08?type=Number"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage06 PASSED - HTML"/>
+
+    <!-- ========== Exception Mapping (JSP Error Page) ==================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ErrorPage09"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage10 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedErrorPage09"
+           debug="${debug}"
+          status="500"
+      outContent="ErrorPage10 PASSED"/>
+
+  </target>
+
+
+  <target name="FilterRequest">
+
+    <!-- ========== Apply Upper Case Filter =============================== -->
+
+    <!-- Input via buffered reader -->
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/FilterRequest01?type=reader"
+           debug="${debug}"
+          status="200"
+       inContent="FilterRequest01 Unwrapped Reader PASSED"
+      outContent="FILTERREQUEST01 UNWRAPPED READER PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/WrappedFilterRequest01?type=reader"
+           debug="${debug}"
+          status="200"
+       inContent="FilterRequest01 Wrapped Reader PASSED"
+      outContent="FILTERREQUEST01 WRAPPED READER PASSED"/>
+
+    <!-- Input via servlet input stream -->
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/FilterRequest01?type=reader"
+           debug="${debug}"
+          status="200"
+       inContent="FilterRequest01 Unwrapped Stream PASSED"
+      outContent="FILTERREQUEST01 UNWRAPPED STREAM PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/WrappedFilterRequest01?type=reader"
+           debug="${debug}"
+          status="200"
+       inContent="FilterRequest01 Wrapped Stream PASSED"
+      outContent="FILTERREQUEST01 WRAPPED STREAM PASSED"/>
+
+    <!-- ========== Servlet Sees Application Wrapper ===================== -->
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/FilterRequest02?wrap=false"
+      outContent="FilterRequest02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/WrappedFilterRequest02?wrap=true"
+      outContent="FilterRequest02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/FilterRequest02?wrap=false&amp;dispatch=F"
+      outContent="FilterRequest02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/WrappedFilterRequest02?wrap=true&amp;dispatch=F"
+      outContent="FilterRequest02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/FilterRequest02?wrap=false&amp;dispatch=I"
+      outContent="FilterRequest02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/WrappedFilterRequest02?wrap=true&amp;dispatch=I"
+      outContent="FilterRequest02 PASSED"/>
+
+  </target>
+
+
+  <target name="FilterResponse">
+
+    <!-- ========== Apply Upper Case Filter =============================== -->
+
+    <!-- Output from a servlet -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/FilterResponse01"
+           debug="${debug}"
+          status="200"
+      outContent="FILTERRESPONSE01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedFilterResponse01"
+           debug="${debug}"
+          status="200"
+      outContent="FILTERRESPONSE01 PASSED"/>
+
+    <!-- Output from a JSP Page -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/FilterResponse02.jsp"
+           debug="${debug}"
+          status="200"
+      outContent="FILTERRESPONSE02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedFilterResponse02.jsp"
+           debug="${debug}"
+          status="200"
+      outContent="FILTERRESPONSE02 PASSED"/>
+
+    <!-- Output from a static page -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/FilterResponse03.txt"
+           debug="${debug}"
+          status="200"
+      outContent="FILTERRESPONSE03 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedFilterResponse03.txt"
+           debug="${debug}"
+          status="200"
+      outContent="FILTERRESPONSE03 PASSED"/>
+
+    <!-- ========== Servlet Sees Application Wrapper ===================== -->
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/FilterResponse04?wrap=false" debug="${debug}"
+      outContent="FilterResponse04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/WrappedFilterResponse04?wrap=true"
+      outContent="FilterResponse04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/FilterResponse04?wrap=false&amp;dispatch=F"
+      outContent="FilterResponse04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/WrappedFilterResponse04?wrap=true&amp;dispatch=F"
+      outContent="FilterResponse04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/FilterResponse04?wrap=false&amp;dispatch=I"
+      outContent="FilterResponse04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0" debug="${debug}"
+         request="${context.path}/WrappedFilterResponse04?wrap=true&amp;dispatch=I"
+      outContent="FilterResponse04 PASSED"/>
+
+  </target>
+
+
+  <target name="Internals">
+
+
+    <!-- ========== Access Internals Via Reflection ======================= -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Reflection01"
+           debug="${debug}"
+      outContent="Reflection01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedReflection01"
+           debug="${debug}"
+      outContent="Reflection01 PASSED"/>
+
+  </target>
+
+
+  <target name="Jndi">
+
+    <!-- ========== JNDI Naming Context =================================== -->
+
+    <!-- Perform the tests before restarting the application -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Jndi01" debug="${debug}"
+      outContent="Jndi01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedJndi01" debug="${debug}"
+      outContent="Jndi01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Jndi02" debug="${debug}"
+      outContent="Jndi02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedJndi02" debug="${debug}"
+      outContent="Jndi02 PASSED"/>
+
+    <!-- Restart this web application -->
+    <!-- NOTE: Assign role "manager" to user "tomcat" for this to work -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+         request="${manager.path}/reload?path=${reload.path}"
+      outContent="OK - "/>
+
+    <!-- Repeat the tests to ensure the naming context is reinitialized -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Jndi01" debug="${debug}"
+      outContent="Jndi01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedJndi01" debug="${debug}"
+      outContent="Jndi01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Jndi02" debug="${debug}"
+      outContent="Jndi02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedJndi02" debug="${debug}"
+      outContent="Jndi02 PASSED"/>
+
+  </target>
+
+
+  <target name="Jsp">
+
+    <echo message="----- JSP Access To Bean Classes -----"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/JspBeans01.jsp" debug="${debug}"
+      outContent="JspBeans01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/JspBeans02.jsp" debug="${debug}"
+      outContent="JspBeans02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/JspBeans03.jsp" debug="${debug}"
+      outContent="JspBeans03 PASSED"/>
+
+    <echo message="----- jsp:params not legal in jsp:include/forward -----"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/JspParams01.jsp" debug="${debug}"
+          status="500"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/JspParams02.jsp" debug="${debug}"
+          status="500"/>
+
+    <echo message="----- Character Encoding and Escaping Tests -----"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/Encoding01.jsp" debug="${debug}"
+          golden="${golden.path}/Encoding01.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/Encoding02.jsp" debug="${debug}"
+          golden="${golden.path}/Encoding02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/Encoding03.jsp" debug="${debug}"
+          golden="${golden.path}/Encoding03.txt"/>
+
+    <echo message="----- PropertyEditor Support -----"/>
+
+<!-- Cannot test PropertyEditor support under security manager
+     unless catalina.policy grants read/write access to
+     system properties -->
+<!--
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/Property01.jsp" debug="${debug}"
+          golden="${golden.path}/Property01.txt"/>
+-->
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/Property02.jsp" debug="${debug}"
+          status="500"/>
+
+    <echo message="----- JSP Document Parsing -----"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/JspDoc01.jsp" debug="${debug}"
+          golden="${golden.path}/JspDoc01.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/JspDoc02.jsp" debug="${debug}"
+          golden="${golden.path}/JspDoc02.txt"/>
+
+    <echo message="----- jsp:forward -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/JspForward01.jsp?path=/JspForward01a.jsp" debug="${debug}"
+      outContent="JspForward01a PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/JspForward01.jsp?path=/Forward00a" debug="${debug}"
+      outContent="Forward00a PASSED"/>
+
+    <echo message="----- jsp:include flush=true -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/JspInclude01.jsp?path=/JspInclude01a.jsp" debug="${debug}"
+          golden="${golden.path}/JspInclude01.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/JspInclude01.jsp?path=/Include00a"
+           debug="${debug}"
+          golden="${golden.path}/JspInclude01a.txt"/>
+
+    <echo message="----- jsp:include flush=false -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/JspInclude02.jsp?path=/JspInclude02a.jsp" debug="${debug}"
+          golden="${golden.path}/JspInclude02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/JspInclude02.jsp?path=/Include00a"
+           debug="${debug}"
+          golden="${golden.path}/JspInclude02a.txt"/>
+
+  </target>
+
+
+  <target name="Lifecycle">
+
+    <!-- ========== Lifecycle Management ================================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Lifecycle01"
+      outContent="Lifecycle01 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedLifecycle01"
+      outContent="Lifecycle01 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Lifecycle02" method="POST"
+      outContent="Lifecycle02 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedLifecycle02" method="POST"
+      outContent="Lifecycle02 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/Lifecycle03?step=1" debug="${debug}"
+          status="404"/>
+
+<!-- NOTE - cannot do this as originally intended, because the
+     servlet was marked as permanently unavailable, so just
+     check for another 503 instead!
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/Lifecycle03?step=2"
+      outContent="Lifecycle03 PASSED" debug="${debug}"/>
+-->
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/Lifecycle03?step=2" debug="${debug}"
+          status="404"/>
+
+  </target>
+
+
+  <target name="RequestDispatcher">
+
+    <echo message="----- Basic Forward Functionality -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward00?path=/Forward00a"
+      outContent="Forward00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward00?path=/Forward00a"
+      outContent="Forward00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward00?path=/Forward00b"
+      outContent="Forward00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward00?path=/Forward00b"
+      outContent="Forward00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward00?path=/Forward00c.jsp"
+      outContent="Forward00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward00?path=/Forward00c.jsp"
+      outContent="Forward00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward00?path=!Forward00d"
+      outContent="Forward00d PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward00?path=!Forward00d"
+      outContent="Forward00d PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward00?path=!Forward00e"
+      outContent="Forward00e PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward00?path=!Forward00e"
+      outContent="Forward00e PASSED" debug="${debug}"/>
+
+    <echo message="----- Basic Include Functionality (flush=false) -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=/Include00a&amp;flush=false"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=/Include00a&amp;flush=false"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=/Include00b&amp;flush=false"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=/Include00b&amp;flush=false"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=/Include00c.jsp&amp;flush=false"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=/Include00c.jsp&amp;flush=false"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=!Include00d&amp;flush=false"
+      outContent="Include00d PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=!Include00d&amp;flush=false"
+      outContent="Include00d PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=!Include00e&amp;flush=false"
+      outContent="Include00e PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=!Include00e&amp;flush=false"
+      outContent="Include00e PASSED" debug="${debug}"/>
+
+    <echo message="----- Basic Include (flush=true, create=true) -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=/Include00a&amp;flush=true&amp;create=true"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=/Include00a&amp;flush=true&amp;create=true"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=/Include00b&amp;flush=true&amp;create=true"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=/Include00b&amp;flush=true&amp;create=true"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=/Include00c.jsp&amp;flush=true&amp;create=true"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=/Include00c.jsp&amp;flush=true&amp;create=true"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=!Include00d&amp;flush=true&amp;create=true"
+      outContent="Include00d PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=!Include00d&amp;flush=true&amp;create=true"
+      outContent="Include00d PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=!Include00e&amp;flush=true&amp;create=true"
+      outContent="Include00e PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=!Include00e&amp;flush=true&amp;create=true"
+      outContent="Include00e PASSED" debug="${debug}"/>
+
+    <echo message="----- Basic Include (flush=true, create=false) -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=/Include00a&amp;flush=true&amp;create=false"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=/Include00a&amp;flush=true&amp;create=false"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=/Include00b&amp;flush=true&amp;create=false"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=/Include00b&amp;flush=true&amp;create=false"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=/Include00c.jsp&amp;flush=true&amp;create=false"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=/Include00c.jsp&amp;flush=true&amp;create=false"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=!Include00d&amp;flush=true&amp;create=false"
+      outContent="Include00d PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=!Include00d&amp;flush=true&amp;create=false"
+      outContent="Include00d PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include00?path=!Include00e&amp;flush=true&amp;create=false"
+      outContent="Include00e PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude00?path=!Include00e&amp;flush=true&amp;create=false"
+      outContent="Include00e PASSED" debug="${debug}"/>
+
+    <echo message="----- Forward and Include to Static Resource -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward01" debug="${debug}"
+      outContent="Forward01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward01" debug="${debug}"
+      outContent="Forward01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include01" debug="${debug}"
+      outContent="Include01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include01" debug="${debug}"
+       inHeaders="If-modified-since: Mon, 20 Dec 2010 12:12:54 GMT"
+      outContent="Include01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude01" debug="${debug}"
+      outContent="Include01 PASSED"/>
+
+    <echo message="----- Included Servlet Throwing Exceptions -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include02?exception=IOException"
+      outContent="Include02 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include02?exception=ServletException"
+      outContent="Include02 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include02?exception=NullPointerException"
+      outContent="Include02 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude02?exception=IOException"
+      outContent="Include02 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude02?exception=ServletException"
+      outContent="Include02 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude02?exception=NullPointerException"
+      outContent="Include02 PASSED" debug="${debug}"/>
+
+    <echo message="----- Forwarded Servlet/Page Sets Attribute -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward03?path=/Forward03a"
+      outContent="Forward03 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward03?path=/Forward03a"
+      outContent="Forward03 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward03?path=/Forward03b.jsp"
+      outContent="Forward03 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward03?path=/Forward03b.jsp"
+      outContent="Forward03 PASSED" debug="${debug}"/>
+
+    <echo message="----- Included Servlet/Page Sets Attribute -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include03?path=/Include03a"
+      outContent="Include03 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude03?path=/Include03a"
+      outContent="Include03 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include03?path=/Include03b.jsp"
+      outContent="Include03 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude03?path=/Include03b.jsp"
+      outContent="Include03 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include03c.jsp?path=/Include03a"
+      outContent="Include03c.jsp PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include03c.jsp?path=/Include03b.jsp"
+      outContent="Include03c.jsp PASSED" debug="${debug}"/>
+
+    <echo message="----- Include Then Forward -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include04"
+      outContent="Include04b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude04"
+      outContent="Include04b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include05.jsp"
+      outContent="Include05b PASSED" debug="${debug}"/>
+
+    <echo message="----- Include Then Include -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include06.jsp" debug="${debug}"
+          golden="${golden.path}/Include06.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include07" debug="${debug}"
+          golden="${golden.path}/Include07.txt"/>
+
+    <echo message="----- Forward Then Forward -----"/>
+
+    <!-- Servlet to Servlet to Servlet -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward04"
+      outContent="Forward04b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward04"
+      outContent="Forward04b PASSED" debug="${debug}"/>
+
+    <!-- JSP to JSP to JSP -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward04.jsp"
+      outContent="Forward04b.jsp PASSED" debug="${debug}"/>
+
+    <!-- Servlet to JSP to Servlet -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward05"
+      outContent="Forward05b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward05"
+      outContent="Forward05b PASSED" debug="${debug}"/>
+
+    <!-- JSP to Servlet to JSP -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward05.jsp"
+      outContent="Forward05b.jsp PASSED" debug="${debug}"/>
+
+    <!-- Invoker to JSP to Invoker -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/servlet/Forward06"
+      outContent="Forward06b PASSED" debug="${debug}"/>
+
+    <!-- JSP to Invoker to JSP -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward06.jsp"
+      outContent="Forward06b.jsp PASSED" debug="${debug}"/>
+
+    <!-- Servlet to Invoker to Servlet -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward07"
+      outContent="Forward07b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward07"
+      outContent="Forward07b PASSED" debug="${debug}"/>
+
+    <!-- Invoker to Servlet to Invoker -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/servlet/Forward08"
+      outContent="Forward08b PASSED" debug="${debug}"/>
+
+    <echo message="----- ServletRequest.getRequestDispatcher() -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward09?path=/Forward00a"
+      outContent="Forward00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward09?path=/Forward00a"
+      outContent="Forward00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward09?path=/Forward00b"
+      outContent="Forward00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward09?path=/Forward00b"
+      outContent="Forward00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward09?path=/Forward00c.jsp"
+      outContent="Forward00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward09?path=/Forward00c.jsp"
+      outContent="Forward00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward09?path=Forward00a"
+      outContent="Forward00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward09?path=Forward00a"
+      outContent="Forward00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward09?path=Forward00b"
+      outContent="Forward00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward09?path=Forward00b"
+      outContent="Forward00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Forward09?path=Forward00c.jsp"
+      outContent="Forward00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedForward09?path=Forward00c.jsp"
+      outContent="Forward00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include09?path=/Include00a"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude09?path=/Include00a"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include09?path=/Include00b"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude09?path=/Include00b"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include09?path=/Include00c.jsp"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude09?path=/Include00c.jsp"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include09?path=Include00a"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude09?path=Include00a"
+      outContent="Include00a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include09?path=Include00b"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude09?path=Include00b"
+      outContent="Include00b PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include09?path=Include00c.jsp"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude09?path=Include00c.jsp"
+      outContent="Include00c PASSED" debug="${debug}"/>
+
+    <echo message="----- Container-Created Include Attributes -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Include10/extra/path?name1=value1"
+      outContent="Include10a PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedInclude10/extra/path?name1=value1"
+      outContent="Include10a PASSED" debug="${debug}"/>
+
+    <echo message="----- Response Wrapping -----"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ResponseWrap01?type=F&amp;page=/ResponseWrap01a"
+      outContent="RESPONSEWRAP01A PASSED" debug="${debug}" />
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ResponseWrap01?type=F&amp;page=/ResponseWrap01b.jsp"
+      outContent="RESPONSEWRAP01B PASSED" debug="${debug}" />
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ResponseWrap01?type=I&amp;page=/ResponseWrap01c"
+      outContent="RESPONSEWRAP01C PASSED" debug="${debug}" />
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/ResponseWrap01?type=I&amp;page=/ResponseWrap01d.jsp"
+      outContent="RESPONSEWRAP01D PASSED" debug="${debug}" />
+
+  </target>
+
+
+  <target name="Resources">
+
+    <!-- ========== Positive ServletContext.getResource() Tests =========== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources01?mode=context&amp;path=/WEB-INF/web.xml"
+      outContent="Resources01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources01?mode=context&amp;path=/Forward01.txt"
+      outContent="Resources01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources01?mode=context&amp;path=/Include01.txt"
+      outContent="Resources01 PASSED"/>
+
+    <!-- ========== Positive Class.getResource() Tests ==================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources01?mode=class&amp;path=/org/apache/tester/Resources01.txt"
+      outContent="Resources01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources01?mode=class&amp;path=/org/apache/tester/Unpacked01.txt"
+      outContent="Resources01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources01?mode=class&amp;path=/org/apache/tester/shared/Shared01.txt"
+      outContent="Resources01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources01?mode=class&amp;path=/org/apache/tester/unpshared/UnpShared01.txt"
+      outContent="Resources01 PASSED"/>
+
+    <!-- ========== Negative ServletContext.getResource() Tests =========== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources02?mode=context&amp;path=/WEB-INF/web.xml.bad"
+      outContent="Resources02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources02?mode=context&amp;path=/Forward02.txt.bad"
+      outContent="Resources02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources02?mode=context&amp;path=/Include02.txt.bad"
+      outContent="Resources02 PASSED"/>
+
+    <!-- ========== Negative Class.getResource() Tests ==================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources02?mode=class&amp;path=/org/apache/tester/Resources02.txt.bad"
+      outContent="Resources02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources02?mode=class&amp;path=/org/apache/tester/Unpacked02.txt.bad"
+      outContent="Resources02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources02?mode=class&amp;path=/org/apache/tester/shared/Shared01.txt.bad"
+      outContent="Resources02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources02?mode=class&amp;path=/org/apache/tester/unpshared/UnpShared01.txt.bad"
+      outContent="Resources02 PASSED"/>
+
+    <!-- ========== Positive ServletContext.getResourceAsStream() Tests === -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources03?mode=context&amp;path=/WEB-INF/web.xml"
+      outContent="&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources03?mode=context&amp;path=/Forward01.txt"
+      outContent="Forward01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources03?mode=context&amp;path=/Include01.txt"
+      outContent="Include01 PASSED"/>
+
+    <!-- ========== Positive Class.getResourceAsStream() Tests ============ -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources03?mode=class&amp;path=/org/apache/tester/Resources03.txt"
+      outContent="Resources03 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources03?mode=class&amp;path=/org/apache/tester/Unpacked03.txt"
+      outContent="Resources03 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources03?mode=class&amp;path=/org/apache/tester/shared/Shared03.txt"
+      outContent="Resources03 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources03?mode=class&amp;path=/org/apache/tester/unpshared/UnpShared03.txt"
+      outContent="Resources03 PASSED"/>
+
+    <!-- ========== Negative ServletContext.getResourceAsStream() Tests === -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources04?mode=context&amp;path=/WEB-INF/web.xml.bad"
+      outContent="Resources04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources04?mode=context&amp;path=/Forward04.txt.bad"
+      outContent="Resources04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources04?mode=context&amp;path=/Include04.txt.bad"
+      outContent="Resources04 PASSED"/>
+
+    <!-- ========== Negative Class.getResourceAsStream() Tests ============ -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources04?mode=class&amp;path=/org/apache/tester/Resources04.txt.bad"
+      outContent="Resources04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources04?mode=class&amp;path=/org/apache/tester/Unpacked04.txt.bad"
+      outContent="Resources04 PASSED"/>
+
+    <!-- ========== Positive Combined getResource/Open Tests ============== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=context&amp;path=/WEB-INF/web.xml"
+      outContent="&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=context&amp;path=/Forward01.txt"
+      outContent="Forward01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=context&amp;path=/Include01.txt"
+      outContent="Include01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=class&amp;path=/org/apache/tester/Resources05.txt"
+      outContent="Resources05 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=class&amp;path=/org/apache/tester/Unpacked05.txt"
+      outContent="Resources05 PASSED"/>
+
+    <!-- ========== Positive Combined getResource/Open/Stringify Tests ==== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=context&amp;path=/WEB-INF/web.xml&amp;stringify=true"
+      outContent="&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=context&amp;path=/Forward01.txt&amp;stringify=true"
+      outContent="Forward01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=context&amp;path=/Include01.txt&amp;stringify=true"
+      outContent="Include01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=class&amp;path=/org/apache/tester/Resources05.txt&amp;stringify=true"
+      outContent="Resources05 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources05?mode=class&amp;path=/org/apache/tester/Unpacked05.txt&amp;stringify=true"
+      outContent="Resources05 PASSED"/>
+
+    <!-- ========== getResourcePaths() ==================================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources06?path=/"
+      outContent="Resources06 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources06?path=/golden"
+      outContent="Resources06 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Resources06?path=/WEB-INF"
+      outContent="Resources06 PASSED"/>
+
+  </target>
+
+
+  <target name="Security">
+
+    <!-- ========== Security Tests ======================================== -->
+
+    <!-- Should not be able to use relative path above document root -->
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${jsp-examples.path}/../.." debug="${debug}"
+          status="400"/>
+
+    <!-- Should not be able to use specially crafted URLs to get around 
+         security constraints -->
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="/tester//./protected//Authentication01" 
+         debug="${debug}" status="401"/>
+
+    <!-- DefaultServlet should not decode the path again -->
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${jsp-examples.path}/snp/snoop%252ejsp"
+          status="404"/>
+
+  </target>
+
+
+  <target name="ServletContext">
+
+    <!-- ========== Servlet Context Attributes ============================ -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Context00"
+      outContent="Context00 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Context01"
+      outContent="Context01 PASSED"/>
+
+    <!-- NOTE: Assign role "manager" to user "tomcat" for this to work -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+         request="${manager.path}/reload?path=${reload.path}"
+      outContent="OK - "/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Context02"
+      outContent="Context02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/WrappedContext00"
+      outContent="Context00 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/WrappedContext01"
+      outContent="Context01 PASSED"/>
+
+    <!-- NOTE: Assign role "manager" to user "tomcat" for this to work -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+         request="${manager.path}/reload?path=${reload.path}"
+      outContent="OK - "/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/WrappedContext02"
+      outContent="Context02 PASSED"/>
+
+  </target>
+
+
+  <target name="ServletRequest">
+
+    <!-- ========== Parameters and Query Strings ========================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/GetParameter01?foo=1" debug="${debug}"
+      outContent="GetParameter01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedGetParameter01?foo=1" debug="${debug}"
+      outContent="GetParameter01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/GetParameterMap00?BestLanguage=Java&amp;BestJSP=Java2"
+      outContent="GetParameterMap00 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedGetParameterMap00?BestLanguage=Java&amp;BestJSP=Java2"
+      outContent="GetParameterMap00 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/GetQueryString01?foo=1"
+      outContent="GetQueryString01 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedGetQueryString01?foo=1"
+      outContent="GetQueryString01 PASSED" debug="${debug}"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}" method="POST"
+       inHeaders="Content-Type:application/x-www-form-urlencoded"
+       inContent="b=3&amp;a=2"
+         request="${context.path}/Aggregate01?a=1"
+      outContent="Aggregate01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}" method="POST"
+       inHeaders="Content-Type:application/x-www-form-urlencoded"
+       inContent="b=3&amp;a=2"
+         request="${context.path}/WrappedAggregate01?a=1"
+      outContent="Aggregate01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}" method="POST"
+       inHeaders="Content-Type:application/x-www-form-urlencoded"
+       inContent="b=3&amp;a=2"
+         request="${context.path}/Aggregate02?a=1"
+      outContent="Aggregate02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}" method="POST"
+       inHeaders="Content-Type:application/x-www-form-urlencoded"
+       inContent="b=3&amp;a=2"
+         request="${context.path}/WrappedAggregate02?a=1"
+      outContent="Aggregate02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/Request01"
+      outContent="Request01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+         request="${context.path}/WrappedRequest01"
+      outContent="Request01 PASSED"/>
+
+    <!-- ========== Other ServletRequest Tests ============================ -->
+
+    <!-- HttpURLConnection does not handle multiple headers for the same
+         name correctly, so use native socket connections, selected by
+         setting the protocol to "HTTP/1.0" -->
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/GetHeaders01" debug="${debug}"
+       inHeaders="Accept-Language:en-us##Accept-Language:en-gb"
+      outContent="GetHeaders01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/WrappedGetHeaders01" debug="${debug}"
+       inHeaders="Accept-Language:en-us##Accept-Language:en-gb"
+      outContent="GetHeaders01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/GetInputStream01" debug="${debug}"
+      outContent="GetInputStream01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedGetInputStream01" debug="${debug}"
+      outContent="GetInputStream01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/GetLocales01" debug="${debug}"
+       inHeaders="Accept-Language:en-ca##Accept-Language:en-gb"
+      outContent="GetLocales01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/WrappedGetLocales01" debug="${debug}"
+       inHeaders="Accept-Language:en-ca##Accept-Language:en-gb"
+      outContent="GetLocales01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/GetLocales01" debug="${debug}"
+       inHeaders="Accept-Language:en-ca,en-gb"
+      outContent="GetLocales01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/WrappedGetLocales01" debug="${debug}"
+       inHeaders="Accept-Language:en-ca,en-gb"
+      outContent="GetLocales01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/GetLocales02" debug="${debug}"
+       inHeaders="Accept-Language:en-ca##Accept-Language:en-gb"
+      outContent="GetLocales02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/WrappedGetLocales02" debug="${debug}"
+       inHeaders="Accept-Language:en-ca##Accept-Language:en-gb"
+      outContent="GetLocales02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/GetLocales02" debug="${debug}"
+       inHeaders="Accept-Language:en-ca,en-gb"
+      outContent="GetLocales02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="HTTP/1.0"
+         request="${context.path}/WrappedGetLocales02" debug="${debug}"
+       inHeaders="Accept-Language:en-ca,en-gb"
+      outContent="GetLocales02 PASSED"/>
+
+  </target>
+
+
+  <target name="ServletResponse">
+
+
+    <!-- ========== Other ServletResponse Tests =========================== -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Reset01" debug="${debug}"
+      outContent="Reset01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedReset01" debug="${debug}"
+      outContent="Reset01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SetBufferSize01" debug="${debug}"
+      outContent="SetBufferSize01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedSetBufferSize01" debug="${debug}"
+      outContent="SetBufferSize01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SetLocale01" debug="${debug}"
+      outContent="SetLocale01 PASSED"
+      outHeaders="Content-Language:en-US"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedSetLocale01" debug="${debug}"
+      outContent="SetLocale01 PASSED"
+      outHeaders="Content-Language:en-US"/>
+
+
+    <!-- ========== SendRedirect Handling ================================= -->
+
+    <!-- Also check debug output to make sure no extra content was included -->
+    <tester host="${host}" port="${port}" protocol=""
+         request="${context.path}/Redirect01" debug="${debug}"
+      outContent=""
+          status="302" redirect="false"/>
+
+    <tester host="${host}" port="${port}" protocol=""
+         request="${context.path}/Redirect01" debug="${debug}"
+      outContent="Redirect01a PASSED" redirect="true"/>
+
+    <tester host="${host}" port="${port}" protocol=""
+         request="${context.path}/WrappedRedirect01" debug="${debug}"
+      outContent="Redirect01a PASSED" redirect="true"/>
+
+    <!-- JSP page includes "return" after redirect -->
+    <tester host="${host}" port="${port}" protocol=""
+         request="${context.path}/Redirect02.jsp" debug="${debug}"
+      outContent="Redirect02a.jsp PASSED" redirect="true"/>
+
+    <!-- Same as "Redirect02.jsp" except without the "return" -->
+    <tester host="${host}" port="${port}" protocol=""
+         request="${context.path}/Redirect03.jsp" debug="${debug}"
+      outContent="Redirect03a.jsp PASSED" redirect="true"/>
+
+  </target>
+
+
+  <target name="HttpSession">
+
+    <!-- ========== Session Attribute Persistence ========================= -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Session01" debug="${debug}"
+      outContent="Session01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Session02" debug="${debug}"
+     joinSession="true"
+      outContent="Session02 PASSED"/>
+
+    <!-- NOTE: Assign role "manager" to user "tomcat" for this to work -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+         request="${manager.path}/reload?path=${reload.path}"
+      outContent="OK - "/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Session03" debug="${debug}"
+     joinSession="true"
+      outContent="Session03 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Session04" debug="${debug}"
+     joinSession="true"
+      outContent="Session04 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedSession01" debug="${debug}"
+      outContent="Session01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedSession02" debug="${debug}"
+     joinSession="true"
+      outContent="Session02 PASSED"/>
+
+    <!-- NOTE: Assign role "manager" to user "tomcat" for this to work -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+           debug="${debug}"
+       inHeaders="Authorization:Basic dG9tY2F0OnRvbWNhdA=="
+         request="${manager.path}/reload?path=${reload.path}"
+      outContent="OK - "/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedSession03" debug="${debug}"
+     joinSession="true"
+      outContent="Session03 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedSession04" debug="${debug}"
+     joinSession="true"
+      outContent="Session04 PASSED"/>
+
+    <!-- Exercise session event listeners -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Session05" debug="${debug}"
+          golden="${golden.path}/Session05.txt"/>
+
+    <!-- Exercise session event listeners -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedSession05" debug="${debug}"
+          golden="${golden.path}/WrappedSession05.txt"/>
+
+    <!-- Session creation after response has been committed -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Session06" debug="${debug}"
+      outContent="Session06 PASSED"/>
+
+    <!-- Session creation after response has been committed -->
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedSession06" debug="${debug}"
+      outContent="Session06 PASSED"/>
+
+    <!-- ========== Pass Attributes Across Redirect ======================= -->
+
+    <!-- Session maintained across redirect -->
+    <!-- NOTE:  The following two-step pattern is required because
+         HttpURLConnection does not forward the session cookie it
+         receives on to the redirected location (the way that a
+         browser will do so) -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Session07a.jsp" debug="${debug}"
+          status="302"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Session07b.jsp" debug="${debug}"
+     joinSession="true"
+      outContent="Session07 PASSED"/>
+
+  </target>
+
+
+  <target name="XercesTest">
+
+    <!-- ========== Xerces Sealing Violation Test ========================= -->
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Xerces00.jsp" debug="${debug}"
+      outContent="Xerces00 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Xerces00" debug="${debug}"
+      outContent="Xerces00 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedXerces00" debug="${debug}"
+      outContent="Xerces00 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Xerces01" debug="${debug}"
+      outContent="Xerces01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedXerces01" debug="${debug}"
+      outContent="Xerces01 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Xerces02.jsp" debug="${debug}"
+      outContent="Xerces02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/Xerces02" debug="${debug}"
+      outContent="Xerces02 PASSED"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/WrappedXerces02" debug="${debug}"
+      outContent="Xerces02 PASSED"/>
+
+  </target>
+
+<!--
+<target name="SSITest">
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIInclude01.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude01.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIInclude02.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIInclude03.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIInclude04.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIInclude05.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIInclude06.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIInclude07.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIInclude08.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIInclude09.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude03.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIConfig01.shtml" debug="${debug}"
+          golden="${golden.path}/SSIConfig01.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIConfig03.shtml" debug="${debug}"
+          golden="${golden.path}/SSIConfig03.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIFsize01.shtml" debug="${debug}"
+          golden="${golden.path}/SSIInclude01.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIFsize02.shtml" debug="${debug}"
+          golden="${golden.path}/SSIFsize02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIFsize03.shtml" debug="${debug}"
+          golden="${golden.path}/SSIFsize02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIFsize04.shtml" debug="${debug}"
+          golden="${golden.path}/SSIFsize02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIFsize05.shtml" debug="${debug}"
+          golden="${golden.path}/SSIFsize02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIFsize06.shtml" debug="${debug}"
+          golden="${golden.path}/SSIFsize02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIFsize07.shtml" debug="${debug}"
+          golden="${golden.path}/SSIFsize02.txt"/>
+
+    <tester host="${host}" port="${port}" protocol="${protocol}"
+         request="${context.path}/SSIFsize08.shtml" debug="${debug}"
+          golden="${golden.path}/SSIFsize02.txt"/>
+
+  </target>
+-->
+
+<!--
+  <target name="CGITest">
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/array.pl" debug="${debug}"
+        golden="${golden.path}/array.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/binary.pl?counter=102" debug="${debug}"
+        golden="${golden.path}/binary.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/concat.pl?first_name=jane&amp;last_name=johnson&amp;fiance_first=john&amp;fiance_last=smith" debug="${debug}"
+        golden="${golden.path}/concat.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/days.pl" debug="${debug}"
+        golden="${golden.path}/days.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/dowhile.pl?start=10" debug="${debug}"
+        golden="${golden.path}/dowhile.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/else.pl?food=spinach" debug="${debug}"
+        golden="${golden.path}/else.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/elsif.pl?food=chocolate" debug="${debug}"
+        golden="${golden.path}/elsif.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/exponents.pl?number=2&amp;power=4" debug="${debug}"
+        golden="${golden.path}/exponents.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/for.pl?start=10" debug="${debug}"
+        golden="${golden.path}/dowhile.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/getday.pl" debug="${debug}"
+        golden="${golden.path}/getday.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/helloperl.pl" debug="${debug}"
+        golden="${golden.path}/helloperl.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/if.pl?food=spinach" debug="${debug}"
+        golden="${golden.path}/else.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/increment.pl?counter=7" debug="${debug}"
+        golden="${golden.path}/increment.txt"/>
+
+  <tester host="${host}" port="${port}" protocol="${protocol}"
+        request="${context.path}/cgi-bin/modifyall.pl?number=289" debug="${debug}"
+        golden="${golden.path}/modifyall.txt"/>
+
+  </target>
+-->
+
+</project>
diff --git a/container/tester/src/conf/tomcat-users.xml b/container/tester/src/conf/tomcat-users.xml
new file mode 100644
index 0000000..42f577a
--- /dev/null
+++ b/container/tester/src/conf/tomcat-users.xml
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='utf-8'?>
+<tomcat-users>
+  <role rolename="tomcat"/>
+  <role rolename="role1"/>
+  <user username="tomcat" password="tomcat" roles="tomcat,admin,manager"/>
+  <user username="role1" password="tomcat" roles="role1"/>
+  <user username="both" password="tomcat" roles="tomcat,role1"/>
+</tomcat-users>
diff --git a/container/tester/src/tester/org/apache/tester/Aggregate01.java b/container/tester/src/tester/org/apache/tester/Aggregate01.java
new file mode 100644
index 0000000..68cc52a
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Aggregate01.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.Enumeration;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test aggregation of query string and POST parameters.  According to
+ * Servlet 2.4 PFD, Section 4.1, all such parameters should be aggregated,
+ * and if there are duplicate parameter names from both sources, the
+ * parameter value(s) from the query string should appear first in the
+ * values returned by request.getParameterValues().
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Aggregate01 extends HttpServlet {
+
+    public void doPost(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Accumulate any errors that are noticed
+        StringBuffer errors = new StringBuffer();
+        String values[] = request.getParameterValues("a");
+        if (values == null)
+            errors.append("  Received no parameter values for 'a'.");
+        else if (values.length != 2)
+            errors.append("  Received " + values.length +
+                          " parameter values for 'a' instead of 2.");
+        else {
+            if (!"1".equals(values[0]))
+                errors.append("  First value for 'a' was '" + values[0] +
+                              "' instead of '1'.");
+            if (!"2".equals(values[1]))
+                errors.append("  Second value for 'a' was '" + values[1] +
+                              "' instead of '2'.");
+        }
+        values = request.getParameterValues("b");
+        if (values == null)
+            errors.append("  Received no parameter values for 'b'.");
+        else if (values.length != 1)
+            errors.append("  Received " + values.length +
+                          " parameter values for 'b' instead of 1.");
+        else {
+            if (!"3".equals(values[0]))
+                errors.append("  Value for 'b' was '" + values[0] +
+                              "' instead of '3'.");
+        }
+        Enumeration names = request.getParameterNames();
+        while (names.hasMoreElements()) {
+            String name = (String) names.nextElement();
+            if ("a".equals(name))
+                continue;
+            if ("b".equals(name))
+                continue;
+            errors.append("  Received parameter '" + name + "'.");
+        }
+
+        // Report the results
+        if (errors.length() < 1)
+            writer.println("Aggregate01 PASSED");
+        else
+            writer.println("Aggregate01 FAILED -" + errors.toString());
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Aggregate02.java b/container/tester/src/tester/org/apache/tester/Aggregate02.java
new file mode 100644
index 0000000..88f90ee
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Aggregate02.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test aggregation of query string and POST parameters.  According to
+ * Servlet 2.4 PFD, Section 4.1, all such parameters should be aggregated,
+ * and if there are duplicate parameter names from both sources, the
+ * parameter value(s) from the query string should appear first in the
+ * values returned by request.getParameterValues().
+ * <p>
+ * This test is the same as Aggregate01, except that it uses the new
+ * <code>getParameterMap()</code> method to retrieve parameter values.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Aggregate02 extends HttpServlet {
+
+    public void doPost(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Accumulate any errors that are noticed
+        StringBuffer errors = new StringBuffer();
+        Map map = request.getParameterMap();
+        if (map == null) {
+            errors.append("  No parameter map returned.");
+            map = new HashMap();
+        }
+        String values[] = (String[]) map.get("a");
+        if (values == null)
+            errors.append("  Received no parameter values for 'a'.");
+        else if (values.length != 2)
+            errors.append("  Received " + values.length +
+                          " parameter values for 'a' instead of 2.");
+        else {
+            if (!"1".equals(values[0]))
+                errors.append("  First value for 'a' was '" + values[0] +
+                              "' instead of '1'.");
+            if (!"2".equals(values[1]))
+                errors.append("  Second value for 'a' was '" + values[1] +
+                              "' instead of '2'.");
+        }
+        values = (String[]) map.get("b");
+        if (values == null)
+            errors.append("  Received no parameter values for 'b'.");
+        else if (values.length != 1)
+            errors.append("  Received " + values.length +
+                          " parameter values for 'b' instead of 1.");
+        else {
+            if (!"3".equals(values[0]))
+                errors.append("  Value for 'b' was '" + values[0] +
+                              "' instead of '3'.");
+        }
+        Iterator names = map.keySet().iterator();
+        while (names.hasNext()) {
+            String name = (String) names.next();
+            if ("a".equals(name))
+                continue;
+            if ("b".equals(name))
+                continue;
+            errors.append("  Received parameter '" + name + "'.");
+        }
+
+        // Report the results
+        if (errors.length() < 1)
+            writer.println("Aggregate02 PASSED");
+        else
+            writer.println("Aggregate02 FAILED -" + errors.toString());
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Authentication01.java b/container/tester/src/tester/org/apache/tester/Authentication01.java
new file mode 100644
index 0000000..5af9554
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Authentication01.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.security.Principal;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Ensure that the "tomcat" user has been successfully authenticated.  This
+ * should be guaranteed by the fact that this URI is protected by a security
+ * constraint in the deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Authentication01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        String remoteUser = request.getRemoteUser();
+        Principal userPrincipal = request.getUserPrincipal();
+        String errors = "";
+        if (remoteUser == null)
+            errors += "  getRemoteUser() returned NULL.";
+        else if (!remoteUser.equals("tomcat"))
+            errors += "  remoteUser=" + remoteUser + ".";
+        if (userPrincipal == null)
+            errors += "  getUserPrincpal() returned NULL.";
+        else if (!userPrincipal.getName().equals("tomcat"))
+            errors += "  userPrincipal=" + userPrincipal.getName() + ".";
+        if ((remoteUser != null) &&
+            (userPrincipal != null) &&
+            !remoteUser.equals(userPrincipal.getName()))
+            errors += "  remoteUser=" + remoteUser + " userPrincipal=" +
+                userPrincipal.getName();
+        if (errors.length() > 0)
+            writer.println("Authentication01 FAILED:" + errors);
+        else
+            writer.println("Authentication01 PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Authentication02.java b/container/tester/src/tester/org/apache/tester/Authentication02.java
new file mode 100644
index 0000000..156954c
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Authentication02.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.security.Principal;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Ensure that the "tomcat" user has been successfully authenticated.  Although
+ * this URI is not protected by a security constraint, the test client will
+ * have authenticated a user previously by calling "/Authentication01".
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Authentication02 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        String remoteUser = request.getRemoteUser();
+        Principal userPrincipal = request.getUserPrincipal();
+        String errors = "";
+        if (remoteUser == null)
+            errors += "  getRemoteUser() returned NULL.";
+        else if (!remoteUser.equals("tomcat"))
+            errors += "  remoteUser=" + remoteUser + ".";
+        if (userPrincipal == null)
+            errors += "  getUserPrincpal() returned NULL.";
+        else if (!userPrincipal.getName().equals("tomcat"))
+            errors += "  userPrincipal=" + userPrincipal.getName() + ".";
+        if ((remoteUser != null) &&
+            (userPrincipal != null) &&
+            !remoteUser.equals(userPrincipal.getName()))
+            errors += "  remoteUser=" + remoteUser + " userPrincipal=" +
+                userPrincipal.getName();
+        if (errors.length() > 0)
+            writer.println("Authentication02 FAILED:" + errors);
+        else
+            writer.println("Authentication02 PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Authentication03.java b/container/tester/src/tester/org/apache/tester/Authentication03.java
new file mode 100644
index 0000000..dd34d3b
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Authentication03.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.security.Principal;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Ensure that we get the correct results from <code>isUserInRole()</code>
+ * for an actual role, a role aliased with a
+ * <code>&lt;security-role-ref&gt;</code> element, and for a role that is
+ * not assigned to the specified user.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Authentication03 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare to create this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        StringBuffer results = new StringBuffer();
+
+        // Validate that we have been authenticated correctly
+        String remoteUser = request.getRemoteUser();
+        if (remoteUser == null) {
+            results.append("  Not Authenticated/");
+        } else if (!"tomcat".equals(remoteUser)) {
+            results.append("  Authenticated as '");
+            results.append(remoteUser);
+            results.append("'/");
+        }
+
+        // Validate that this user is part of the "tomcat" role
+        if (!request.isUserInRole("tomcat")) {
+            results.append("  Not in role 'tomcat'/");
+        }
+
+        // Validate that this user is part of the "alias" role
+        // (mapped to "tomcat" in a <security-role-ref> element
+        if (!request.isUserInRole("alias")) {
+            results.append("  Not in role 'alias'/");
+        }
+
+        // Validate that this user is NOT part of the "unknown" role
+        if (request.isUserInRole("unknown")) {
+            results.append("  In role 'unknown'/");
+        }
+
+        // Generate our response
+        if (results.length() < 1) {
+            writer.println("Authentication03 PASSED");
+        } else {
+            writer.print("Authentication03 FAILED -");
+            writer.println(results.toString());
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Authentication05.java b/container/tester/src/tester/org/apache/tester/Authentication05.java
new file mode 100644
index 0000000..e7a311e
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Authentication05.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.security.Principal;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Ensure that a resource protected a a security constratint that allows all
+ * roles will permit access to an authenticated user.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Authentication05 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        StringBuffer sb = new StringBuffer();
+
+        String remoteUser = request.getRemoteUser();
+        if (remoteUser == null)
+            sb.append(" No remote user returned/");
+        else if (!"tomcat".equals(remoteUser)) {
+            sb.append(" Remote user is '");
+            sb.append(remoteUser);
+            sb.append("'/");
+        }
+
+        Principal userPrincipal = request.getUserPrincipal();
+        if (userPrincipal == null)
+            sb.append(" No user principal returned/");
+        else if (!"tomcat".equals(userPrincipal.getName())) {
+            sb.append(" User principal is '");
+            sb.append(userPrincipal);
+            sb.append("'/");
+        }
+
+        if (!request.isUserInRole("tomcat"))
+            sb.append(" Not in role 'tomcat'/");
+
+        if (sb.length() < 1)
+            writer.println("Authentication05 PASSED");
+        else {
+            writer.print("Authentication05 FAILED -");
+            writer.println(sb.toString());
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/CharArrayResponse.java b/container/tester/src/tester/org/apache/tester/CharArrayResponse.java
new file mode 100644
index 0000000..033af00
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/CharArrayResponse.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * HttpServletResponse wrapper that converts all output characters to
+ * upper case via an intermediate buffer.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class CharArrayResponse extends HttpServletResponseWrapper {
+
+
+    CharArrayWriterUpperCase writer = null;
+
+    public CharArrayResponse(HttpServletResponse response) {
+        super(response);
+        writer = new CharArrayWriterUpperCase();
+    }
+
+    public void flushBuffer() throws IOException {
+        int n = 0;
+        Reader reader = getReader();
+        PrintWriter writer = getResponse().getWriter();
+        while (true) {
+            int ch = reader.read();
+            if (ch < 0)
+                break;
+            n++;
+            writer.print((char) ch);
+        }
+        writer.println("[" + n + "]");
+        this.writer.reset();
+    }
+
+    public Reader getReader() {
+        return (new CharArrayReader(writer.toCharArray()));
+    }
+
+    public PrintWriter getWriter() throws IOException {
+        return (new PrintWriter(writer, true));
+    }
+
+
+}
+
+
+
diff --git a/container/tester/src/tester/org/apache/tester/CharArrayWriterUpperCase.java b/container/tester/src/tester/org/apache/tester/CharArrayWriterUpperCase.java
new file mode 100644
index 0000000..391755a
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/CharArrayWriterUpperCase.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Implementation of CharArrayWriter that upper cases its output.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class CharArrayWriterUpperCase extends CharArrayWriter {
+
+
+    CharArrayWriter writer = new CharArrayWriter();
+
+    public void close() {
+        writer.close();
+    }
+
+    public void flush() {
+        writer.flush();
+    }
+
+    public void reset() {
+        writer.reset();
+    }
+
+    public int size() {
+        return (writer.size());
+    }
+
+    public char[] toCharArray() {
+        return (writer.toCharArray());
+    }
+
+    public String toString() {
+        return (writer.toString());
+    }
+
+    public void write(int c) {
+        char ch = (char) c;
+        if (Character.isLowerCase(ch))
+            ch = Character.toUpperCase(ch);
+        writer.write((int) ch);
+    }
+
+    public void write(char c[]) throws IOException {
+        write(c, 0, c.length);
+    }
+
+    public void write(char c[], int off, int len) {
+        for (int i = off; i < (off + len); i++)
+            write(c[i]);
+    }
+
+    public void write(String str) throws IOException {
+        write(str, 0, str.length());
+    }
+
+    public void write(String str, int off, int len) {
+        for (int i = off; i < (off + len); i++)
+            write(str.charAt(i));
+    }
+
+    public void writeTo(Writer out) throws IOException {
+        writer.writeTo(out);
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Context00.java b/container/tester/src/tester/org/apache/tester/Context00.java
new file mode 100644
index 0000000..6a4d090
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Context00.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import org.apache.tester.shared.SharedSessionBean;
+import org.apache.tester.unshared.UnsharedSessionBean;
+
+
+/**
+ * Part 0 of Context Tests.  This servlet is never executed directly.  Its
+ * purpose is to create a servlet context attribute at <code>init()</code>
+ * time, and remove it at <code>destroy()</code> time.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Context00 extends HttpServlet {
+
+
+    public void destroy() {
+        getServletContext().log("Context00: Removing attribute 'context00'");
+        getServletContext().removeAttribute("context00");
+    }
+
+
+    public void init() throws ServletException {
+        getServletContext().log("Context00: Setting attribute 'context00'");
+        ContextBean cb = new ContextBean();
+        cb.setStringProperty("Context00");
+        getServletContext().setAttribute("context00", cb);
+    }
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("Context00 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Context01.java b/container/tester/src/tester/org/apache/tester/Context01.java
new file mode 100644
index 0000000..2e60ccd
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Context01.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import org.apache.tester.shared.SharedSessionBean;
+import org.apache.tester.unshared.UnsharedSessionBean;
+
+
+/**
+ * Part 1 of Context Tests.  Exercise various methods for dealing with
+ * servlet context attributes.  Leave an attribute named "context01"
+ * present, which should be erased after a web application restart.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Context01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        boolean ok = true;
+        PrintWriter writer = response.getWriter();
+        ServletContext context = getServletContext();
+
+        // Ensure that there is no existing attribute
+        if (ok) {
+            if (context.getAttribute("context01") != null) {
+                writer.println("Context01 FAILED - Attribute already exists");
+                ok = false;
+            }
+        }
+
+        // Create and stash a context attribute
+        if (ok) {
+            ContextBean bean = new ContextBean();
+            bean.setStringProperty("Context01");
+            context.setAttribute("context01", bean);
+        }
+
+        // Ensure that we can retrieve the attribute successfully
+        if (ok) {
+            Object bean = context.getAttribute("context01");
+            if (bean == null) {
+                writer.println("Context01 FAILED - Cannot retrieve attribute");
+                ok = false;
+            }
+            if (ok) {
+                if (!(bean instanceof ContextBean)) {
+                    writer.println("Context01 FAILED - Bean instance of " +
+                                   bean.getClass().getName());
+                    ok = false;
+                }
+            }
+            if (ok) {
+                String value = ((ContextBean) bean).getStringProperty();
+                if (!"Context01".equals(value)) {
+                    writer.println("Context01 FAILED - Value = " + value);
+                    ok = false;
+                }
+            }
+            if (ok) {
+                String lifecycle = ((ContextBean) bean).getLifecycle();
+                if (!"/add".equals(lifecycle)) {
+                    writer.println("Context01 FAILED - Bean lifecycle is " +
+                                   lifecycle);
+                    ok = false;
+                }
+            }
+        }
+
+        // Ensure that we can update this attribute and check its lifecycle
+        if (ok) {
+            ContextBean bean = (ContextBean) context.getAttribute("context01");
+            context.setAttribute("context01", bean);
+            String lifecycle = bean.getLifecycle();
+            if (!"/add/rep".equals(lifecycle)) {
+                writer.println("Context01 FAILED - Bean lifecycle is " +
+                               lifecycle);
+                ok = false;
+            }
+        }
+
+        // Ensure that we can remove this attribute and check its lifecycle
+        if (ok) {
+            ContextBean bean = (ContextBean) context.getAttribute("context01");
+            context.removeAttribute("context01");
+            String lifecycle = bean.getLifecycle();
+            if (!"/add/rep/rem".equals(lifecycle)) {
+                writer.println("Context01 FAILED - Bean lifecycle is " +
+                               lifecycle);
+                ok = false;
+            }
+        }
+
+        // Add a bean back for the restart application test
+        context.setAttribute("context01", new ContextBean());
+
+        // Ensure that setAttribute("name", null) works correctly
+        if (ok) {
+            context.setAttribute("FOO", "BAR");
+            context.setAttribute("FOO", null);
+            if (context.getAttribute("FOO") != null) {
+                writer.println("Context01 FAILED - setAttribute(name,null)");
+                ok = false;
+            }
+        }
+
+        // Report success if everything is still ok
+        if (ok)
+            writer.println("Context01 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Context02.java b/container/tester/src/tester/org/apache/tester/Context02.java
new file mode 100644
index 0000000..f0a6c97
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Context02.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import org.apache.tester.shared.SharedSessionBean;
+import org.apache.tester.unshared.UnsharedSessionBean;
+
+
+/**
+ * Part 2 of Context Tests.  The context attribute from Context00 should
+ * still be here after a restart (because Context00 is a load-on-startup
+ * servlet, so the <code>init()</code> method should have been triggered
+ * during the restart).  However, the context attribute from Context01
+ * should <strong>not</strong> be here, because context attributes should
+ * be cleaned up during a restart.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Context02 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        boolean ok = true;
+        PrintWriter writer = response.getWriter();
+        ServletContext context = getServletContext();
+
+        // Check for the attribute from Context01
+        if (ok) {
+            Object bean = context.getAttribute("context01");
+            if (bean != null) {
+                writer.println("Context02 FAILED - context01 value " +
+                               bean);
+                ok = false;
+                context.removeAttribute("context01");
+            }
+        }
+
+        // Check for the attribute from Context00
+        if (ok) {
+            Object bean = context.getAttribute("context00");
+            if (bean == null) {
+                writer.println("Context02 FAILED - context00 missing");
+                ok = false;
+            } else if (!(bean instanceof ContextBean)) {
+                writer.println("Context02 FAILED - context00 class " +
+                               bean.getClass().getName());
+                ok = false;
+            } else {
+                String value = ((ContextBean) bean).getStringProperty();
+                if (!"Context00".equals(value)) {
+                    writer.println("Context02 FAILED - context00 value " +
+                                   value);
+                    ok = false;
+                } else {
+                    String lifecycle = ((ContextBean) bean).getLifecycle();
+                    if (!"/add".equals(lifecycle)) {
+                        writer.println("Context02 FAILED -" +
+                                       " context00 lifecycle " +
+                                       lifecycle);
+                        ok = false;
+                    }
+                }
+            }
+        }
+
+        // Check for the attribute from ContextListener01
+        if (ok) {
+            Object bean = context.getAttribute("contextListener01");
+            if (bean == null) {
+                writer.println("Context02 FAILED - contextListener01 missing");
+                ok = false;
+            } else if (!(bean instanceof ContextBean)) {
+                writer.println("Context02 FAILED - contextListener01 class " +
+                               bean.getClass().getName());
+                ok = false;
+            } else {
+                String value = ((ContextBean) bean).getStringProperty();
+                if (!"ContextListener01".equals(value)) {
+                    writer.println("Context02 FAILED - contextListener01 " +
+                                   "value " + value);
+                    ok = false;
+                } else {
+                    String lifecycle = ((ContextBean) bean).getLifecycle();
+                    if (!"/add".equals(lifecycle)) {
+                        writer.println("Context02 FAILED -" +
+                                       " contextListener01 lifecycle " +
+                                       lifecycle);
+                        ok = false;
+                    }
+                }
+            }
+        }
+
+        // Report success if everything is still ok
+        if (ok)
+            writer.println("Context02 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ContextBean.java b/container/tester/src/tester/org/apache/tester/ContextBean.java
new file mode 100644
index 0000000..02322c0
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ContextBean.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.Serializable;
+
+
+/**
+ * Simple JavaBean to use for context attribute tests.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ContextBean implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The lifecycle events that have happened on this bean instance.
+     */
+    protected String lifecycle = "";
+
+    public String getLifecycle() {
+        return (this.lifecycle);
+    }
+
+    public void setLifecycle(String lifecycle) {
+        this.lifecycle = lifecycle;
+    }
+
+
+    /**
+     * A string property.
+     */
+    protected String stringProperty = "Default String Property Value";
+
+    public String getStringProperty() {
+        return (this.stringProperty);
+    }
+
+    public void setStringProperty(String stringProperty) {
+        this.stringProperty = stringProperty;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a string representation of this bean.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ContextBean[lifecycle=");
+        sb.append(lifecycle);
+        sb.append(", stringProperty=");
+        sb.append(this.stringProperty);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
+
diff --git a/container/tester/src/tester/org/apache/tester/ContextListener01.java b/container/tester/src/tester/org/apache/tester/ContextListener01.java
new file mode 100644
index 0000000..5d3e26b
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ContextListener01.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Application event listener for context events.  All events that occur
+ * are logged appropriately to the static logger..
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ContextListener01
+    implements ServletContextAttributeListener, ServletContextListener {
+
+
+    public void attributeAdded(ServletContextAttributeEvent event) {
+        StaticLogger.write("ContextListener01: attributeAdded(" +
+                           event.getName() + "," + event.getValue() + ")");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("ContextListener01: attributeAdded(" +
+                    event.getName() + "," + event.getValue() + ")");
+        if (event.getValue() instanceof ContextBean) {
+            ContextBean bean = (ContextBean) event.getValue();
+            bean.setLifecycle(bean.getLifecycle() + "/add");
+        }
+    }
+
+    public void attributeRemoved(ServletContextAttributeEvent event) {
+        StaticLogger.write("ContextListener01: attributeRemoved(" +
+                           event.getName() + "," + event.getValue() + ")");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("ContextListener01: attributeRemoved(" +
+                    event.getName() + "," + event.getValue() + ")");
+        if (event.getValue() instanceof ContextBean) {
+            ContextBean bean = (ContextBean) event.getValue();
+            bean.setLifecycle(bean.getLifecycle() + "/rem");
+        }
+    }
+
+    public void attributeReplaced(ServletContextAttributeEvent event) {
+        StaticLogger.write("ContextListener01: attributeReplaced(" +
+                           event.getName() + "," + event.getValue() + ")");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("ContextListener01: attributeReplaced(" +
+                    event.getName() + "," + event.getValue() + ")");
+        if (event.getValue() instanceof ContextBean) {
+            ContextBean bean = (ContextBean) event.getValue();
+            bean.setLifecycle(bean.getLifecycle() + "/rep");
+        }
+    }
+
+    public void contextDestroyed(ServletContextEvent event) {
+        StaticLogger.write("ContextListener01: contextDestroyed()");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("ContextListener01: contextDestroyed()");
+        context.removeAttribute("contextListener01");
+    }
+
+    public void contextInitialized(ServletContextEvent event) {
+        StaticLogger.write("ContextListener01: contextInitialized()");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("ContextListener01: contextInitialized()");
+        ContextBean bean = new ContextBean();
+        bean.setStringProperty("ContextListener01");
+        context.setAttribute("contextListener01", bean);
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ContextListener02.java b/container/tester/src/tester/org/apache/tester/ContextListener02.java
new file mode 100644
index 0000000..1506935
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ContextListener02.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.beans.PropertyEditorManager;
+import java.sql.Date;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+
+/**
+ * Application event listener for context events.  Ensures that the property
+ * editor classes for this web application are appropriately registered.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ContextListener02
+    implements ServletContextListener {
+
+
+    private ServletContext context = null;
+
+
+    public void contextDestroyed(ServletContextEvent event) {
+        context.log("ContextListener02: contextDestroyed()");
+        context = null;
+    }
+
+    public void contextInitialized(ServletContextEvent event) {
+        context = (ServletContext) event.getSource();
+        context.log("ContextListener02: contextInitialized()");
+        PropertyEditorManager.registerEditor(Date.class,
+                                             DatePropertyEditor.class);
+        context.log("ContextListener02: getEditorSearchPath() -->");
+        String search[] = PropertyEditorManager.getEditorSearchPath();
+        if (search == null)
+            search = new String[0];
+        for (int i = 0; i < search.length; i++)
+            context.log("ContextListener02:   " + search[i]);
+        context.log("ContextListener02: findEditor() --> " +
+                    PropertyEditorManager.findEditor(Date.class));
+               
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/DatePropertyEditor.java b/container/tester/src/tester/org/apache/tester/DatePropertyEditor.java
new file mode 100644
index 0000000..d956ec5
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/DatePropertyEditor.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+import java.beans.PropertyEditorSupport;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.sql.Date;
+
+/**
+ * PropertyEditor implementation for a java.sql.Date property.
+ *
+ * @author Craig R. McClanahan
+ * @revision $Date$ $Revision$
+ */
+public class DatePropertyEditor extends PropertyEditorSupport {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The date format to which dates converted by this property editor
+     * must conform.
+     */
+    private SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Convert our Date object into a String that conforms to our
+     * specified <code>format</code>, and return it.  If this is not
+     * possible, return <code>null</code>.
+     */
+    public String getAsText() {
+
+        try {
+            Date date = (Date) getValue();
+            return (format.format(date));
+        } catch (ClassCastException e) {
+            return (null);
+        } catch (IllegalArgumentException e) {
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Convert the specified String value into a Date, if it conforms to
+     * our specified <code>format</code> , else throw IllegalArgumentException.
+     *
+     * @param value String value to be converted
+     *
+     * @exception IllegalArgumentException if a conversion error occurs
+     */
+    public void setAsText(String value) throws IllegalArgumentException {
+
+        // Validate the format of the input string
+        if (value == null)
+            throw new IllegalArgumentException
+                ("Cannot convert null String to a Date");
+        if (value.length() != 10)
+            throw new IllegalArgumentException
+                ("String '" + value + "' has invalid length " +
+                 value.length());
+        for (int i = 0; i < 10; i++) {
+            char ch = value.charAt(i);
+            if ((i == 2) || (i == 5)) {
+                if (ch != '/')
+                    throw new IllegalArgumentException
+                        ("String '" + value + "' missing slash at index " +
+                         i);
+            } else {
+                if (!Character.isDigit(ch))
+                    throw new IllegalArgumentException
+                        ("String '" + value + "' missing digit at index " +
+                         i);
+            }
+        }
+
+        // Convert the incoming value to a java.sql.Date
+        java.util.Date temp = format.parse(value, new ParsePosition(0)); 
+        java.sql.Date date = new java.sql.Date(temp.getTime());
+        setValue(date);
+    }
+
+        
+}
diff --git a/container/tester/src/tester/org/apache/tester/Decoding01.java b/container/tester/src/tester/org/apache/tester/Decoding01.java
new file mode 100644
index 0000000..7f478fa
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Decoding01.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Test for proper URL decoding of the getServletPath() and getPathInfo()
+ * methods of HttpServletRequest.  The desired values are specified by the
+ * <strong>servlet</strong> and <strong>path</strong> request parameters,
+ * respectively.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Decoding01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Identify our configuration parameters
+        String desiredServlet = request.getParameter("servlet");
+        String desiredPath = request.getParameter("path");
+
+        // Prepare for the desired test
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        StringBuffer results = new StringBuffer();
+
+        // Check the value returned by getServletPath()
+        String servletPath = request.getServletPath();
+        if (desiredServlet == null) {
+            if (servletPath != null)
+                results.append(" servletPath is '" + servletPath +
+                               "' instead of NULL/");
+        } else {
+            if (servletPath == null)
+                results.append(" servletPath is NULL instead of '" +
+                               desiredPath + "'/");
+            else if (!servletPath.equals(desiredServlet))
+                results.append(" servletPath is '" + servletPath +
+                               "' instead of '" + desiredServlet + "'/");
+        }
+
+        // Check the value returned by getPathInfo()
+        String pathInfo = request.getPathInfo();
+        if (desiredPath == null) {
+            if (pathInfo != null)
+                results.append(" pathInfo is '" + pathInfo +
+                               "' instead of NULL/");
+        } else {
+            if (pathInfo == null)
+                results.append(" pathInfo is NULL instead of '" +
+                               desiredPath + "'/");
+            else if (!pathInfo.equals(desiredPath))
+                results.append(" pathInfo is '" + pathInfo +
+                               "' instead of '" + desiredPath + "'/");
+        }
+
+        // Report success or failure
+        if (results.length() < 1)
+            writer.println("Decoding01 PASSED");
+        else {
+            writer.print("Decoding01 FAILED -");
+            writer.println(results.toString());
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ErrorPage01.java b/container/tester/src/tester/org/apache/tester/ErrorPage01.java
new file mode 100644
index 0000000..a6996ee
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ErrorPage01.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 1 of the ErrorPage Tests.  Returns a response status code of 412
+ * (HttpServletResponse.SC_PRECONDITION_FAILED) which is mapped to the
+ * ErrorPage02 servlet in the deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ErrorPage01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Write a FAILED message that should get replaced by the error text
+        writer.println("ErrorPage01 FAILED - Original response returned");
+
+        // Return the appropriate response code
+        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED,
+                           "ErrorPage01 Returned Status Code 412");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ErrorPage02.java b/container/tester/src/tester/org/apache/tester/ErrorPage02.java
new file mode 100644
index 0000000..0f24f66
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ErrorPage02.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 2 of the ErrorPage Tests.  Should be mapped by the container when
+ * the ErrorPage01 servlet returns the appropriate status code.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ErrorPage02 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.reset();
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Accumulate all the reasons this request might fail
+        StringBuffer sb = new StringBuffer();
+        Object value = null;
+
+        value = request.getAttribute("javax.servlet.error.status_code");
+        if (value == null)
+            sb.append(" status_code is missing/");
+        else if (!(value instanceof Integer)) {
+            sb.append(" status_code class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            int intValue = ((Integer) value).intValue();
+            if (intValue != HttpServletResponse.SC_PRECONDITION_FAILED) {
+                sb.append(" status_code is ");
+                sb.append(value);
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.message");
+        if (value == null)
+            sb.append(" message is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" message class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String message = (String) value;
+            if (!"ErrorPage01 Returned Status Code 412".equals(message)) {
+                sb.append(" message is ");
+                sb.append(message);
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.request_uri");
+        if (value == null)
+            sb.append(" request_uri is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" request_uri class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String request_uri = (String) value;
+            String test1 = request.getContextPath() + "/ErrorPage01";
+            String test2 = request.getContextPath() + "/WrappedErrorPage01";
+            if (!request_uri.equals(test1) && !request_uri.equals(test2)) {
+                sb.append(" request_uri is ");
+                sb.append(request_uri);
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.servlet_name");
+        if (value == null)
+            sb.append(" servlet_name is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" servlet_name class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String servlet_name = (String) value;
+            if (!"ErrorPage01".equals(servlet_name)) {
+                sb.append(" servlet_name is ");
+                sb.append(servlet_name);
+                sb.append("/");
+            }
+        }
+
+        // Report ultimate success or failure
+        if (sb.length() < 1)
+            writer.println("ErrorPage02 PASSED");
+        else
+            writer.println("ErrorPage02 FAILED -" + sb.toString());
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ErrorPage03.java b/container/tester/src/tester/org/apache/tester/ErrorPage03.java
new file mode 100644
index 0000000..9cf1f59
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ErrorPage03.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 3 of the ErrorPage Tests.  Throws an exception of a specified type
+ * (wrapped in a servlet exception as required by the throws clause), which
+ * is mapped to the ErrorPage04 servlet in the deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ErrorPage03 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Write a FAILED message that should get replaced by the error text
+        writer.println("ErrorPage03 FAILED - Original response returned");
+
+        // Throw the specified exception
+        throw new ServletException
+            (new TesterException("ErrorPage03 Threw Exception"));
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ErrorPage04.java b/container/tester/src/tester/org/apache/tester/ErrorPage04.java
new file mode 100644
index 0000000..6ff5125
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ErrorPage04.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 4 of the ErrorPage Tests.  Should be mapped by the container when
+ * the ErrorPage01 servlet returns the appropriate exception.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ErrorPage04 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.reset();
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Accumulate all the reasons this request might fail
+        ServletException exception = null;
+        StringBuffer sb = new StringBuffer();
+        Object value = null;
+
+        value = request.getAttribute("javax.servlet.error.exception");
+        if (value == null)
+            sb.append(" exception is missing/");
+        else if (!(value instanceof org.apache.tester.TesterException)) {
+            sb.append(" exception class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            TesterException te = (TesterException) value;
+            if (!"ErrorPage03 Threw Exception".equals(te.getMessage())) {
+                sb.append(" exception message is ");
+                sb.append(te.getMessage());
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.exception_type");
+        if (value == null)
+            sb.append(" exception_type is missing/");
+        else if (!(value instanceof Class)) {
+            sb.append(" exception_type class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            Class clazz = (Class) value;
+            if (!"org.apache.tester.TesterException".equals(clazz.getName())) {
+                sb.append(" exception_type class is ");
+                sb.append(clazz.getName());
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.message");
+        if (value == null)
+            sb.append(" message is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" message class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String message = (String) value;
+            if (!"ErrorPage03 Threw Exception".equals(message)) {
+                sb.append(" message is ");
+                sb.append(message);
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.request_uri");
+        if (value == null)
+            sb.append(" request_uri is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" request_uri class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String request_uri = (String) value;
+            String test1 = request.getContextPath() + "/ErrorPage03";
+            String test2 = request.getContextPath() + "/WrappedErrorPage03";
+            if (!request_uri.equals(test1) && !request_uri.equals(test2)) {
+                sb.append(" request_uri is ");
+                sb.append(request_uri);
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.servlet_name");
+        if (value == null)
+            sb.append(" servlet_name is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" servlet_name class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String servlet_name = (String) value;
+            if (!"ErrorPage03".equals(servlet_name)) {
+                sb.append(" servlet_name is ");
+                sb.append(servlet_name);
+                sb.append("/");
+            }
+        }
+
+        // Report ultimate success or failure
+        if (sb.length() < 1)
+            writer.println("ErrorPage04 PASSED");
+        else
+            writer.println("ErrorPage04 FAILED -" + sb.toString());
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ErrorPage05.java b/container/tester/src/tester/org/apache/tester/ErrorPage05.java
new file mode 100644
index 0000000..378c471
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ErrorPage05.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 5 of the ErrorPage Tests.  Throws a RuntimeException of a type
+ * specified by the <code>type</code> query parameter, which must be one
+ * of the following:
+ * <ul>
+ * <li><strong>ArithmeticException</strong> - Forwarded to "/ErrorPage06".</li>
+ * <li><strong>ArrayIndexOutOfBoundsException</strong> -
+ *     Forwarded to "/ErrorPage06.jsp".</li>
+ * <li><strong>NumberFormatException</strong> -
+ *     Forwarded to "/ErrorPage06.html".</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ErrorPage05 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Write a FAILED message that should get replaced by the error text
+        writer.println("ErrorPage05 FAILED - Original response returned");
+
+        // Throw the specified exception
+        String type = request.getParameter("type");
+        if ("Arithmetic".equals(type)) {
+            throw new ArithmeticException
+                ("ErrorPage05 Threw ArithmeticException");
+        } else if ("Array".equals(type)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("ErrorPage05 Threw ArrayIndexOutOfBoundsException");
+        } else if ("Number".equals(type)) {
+            throw new NumberFormatException
+                ("ErrorPage05 Threw NumberFormatException");
+        }
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ErrorPage06.java b/container/tester/src/tester/org/apache/tester/ErrorPage06.java
new file mode 100644
index 0000000..85cf9f0
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ErrorPage06.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 6 of the ErrorPage Tests.  Should be mapped by the container when
+ * the ErrorPage05 servlet returns the appropriate exception.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ErrorPage06 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.reset();
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Accumulate all the reasons this request might fail
+        StringBuffer sb = new StringBuffer();
+        Object value = null;
+
+        value = request.getAttribute("javax.servlet.error.exception");
+        StaticLogger.write("exception is '" + value + "'");
+        if (value == null) {
+            sb.append(" exception is missing/");
+        } else if (!(value instanceof java.lang.ArithmeticException)) {
+            sb.append(" exception class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        }
+
+        value = request.getAttribute("javax.servlet.error.exception_type");
+        StaticLogger.write("exception_type is '" + value + "'");
+        if (value != null)
+            StaticLogger.write("exception_type class is '" +
+                               value.getClass().getName() + "'");
+        if (value == null)
+            sb.append(" exception_type is missing/");
+        else if (!(value instanceof Class)) {
+            sb.append(" exception_type class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            Class clazz = (Class) value;
+            String name = clazz.getName();
+            if (!"java.lang.ArithmeticException".equals(name)) {
+                sb.append(" exception_type is ");
+                sb.append(name);
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.message");
+        StaticLogger.write("message is '" + value + "'");
+        if (value == null)
+            sb.append(" message is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" message class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else if (!"ErrorPage05 Threw ArithmeticException".equals(value) &&
+                   !"ErrorPage08 Threw ArithmeticException".equals(value)) {
+            sb.append(" message is not correct");
+        }
+
+        value = request.getAttribute("javax.servlet.error.request_uri");
+        StaticLogger.write("request_uri is '" + value + "'");
+        if (value == null)
+            sb.append(" request_uri is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" request_uri class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String request_uri = (String) value;
+            String test1 = request.getContextPath() + "/ErrorPage05";
+            String test2 = request.getContextPath() + "/WrappedErrorPage05";
+            String test3 = request.getContextPath() + "/ErrorPage08";
+            String test4 = request.getContextPath() + "/WrappedErrorPage08";
+            if (!request_uri.equals(test1) && !request_uri.equals(test2) &&
+                !request_uri.equals(test3) && !request_uri.equals(test4)) {
+                sb.append(" request_uri is ");
+                sb.append(request_uri);
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.servlet_name");
+        StaticLogger.write("servlet_name is '" + value + "'");
+        if (value == null)
+            sb.append(" servlet_name is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" servlet_name class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String servlet_name = (String) value;
+            if (!"ErrorPage05".equals(servlet_name) &&
+                !"ErrorPage08".equals(servlet_name)) {
+                sb.append(" servlet_name is ");
+                sb.append(servlet_name);
+                sb.append("/");
+            }
+        }
+
+        // Report ultimate success or failure
+        if (sb.length() < 1)
+            writer.println("ErrorPage06 PASSED - SERVLET");
+        else
+            writer.println("ErrorPage06 FAILED -" + sb.toString());
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ErrorPage07.java b/container/tester/src/tester/org/apache/tester/ErrorPage07.java
new file mode 100644
index 0000000..159062b
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ErrorPage07.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 7 of the error page tests.  This servlet is configured as
+ * <code>load-on-startup</code> in the web.xml file, and should return
+ * status 503 (service unavailable) when it is called later.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ErrorPage07 extends HttpServlet {
+
+    public void init() throws ServletException {
+        throw new UnavailableException
+            ("ErrorPage07 Threw UnavailableException in init()");
+    }
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("ErrorPage07 FAILED - Called unavailable servlet");
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/FilterRequest01.java b/container/tester/src/tester/org/apache/tester/FilterRequest01.java
new file mode 100644
index 0000000..bce278f
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/FilterRequest01.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for being able to filter input.  The input will be echoed
+ * back in the response, after having been converted to upper case by the
+ * associated filter.  Use request parameter <code>type</code> to determine
+ * whether to call getReader() ("reader") or getInputStream() ("stream").
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class FilterRequest01 extends HttpServlet {
+
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        StringBuffer sb = new StringBuffer();
+
+        String type = request.getParameter("type");
+        if (type == null)
+            type = "reader";
+        if (type.equalsIgnoreCase("reader")) {
+            BufferedReader br = request.getReader();
+            while (true) {
+                int c = br.read();
+                if (c < 0)
+                    break;
+                sb.append((char) c);
+            }
+            br.close();
+        } else {
+            ServletInputStream sis = request.getInputStream();
+            while (true) {
+                int c = sis.read();
+                if (c < 0)
+                    break;
+                sb.append((char) c);
+            }
+            sis.close();
+        }
+
+        writer.println(sb.toString());
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/FilterRequest02.java b/container/tester/src/tester/org/apache/tester/FilterRequest02.java
new file mode 100644
index 0000000..e80750b
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/FilterRequest02.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Verify that request and response wrappers added by a Filter are in fact
+ * visible to the called servlet.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class FilterRequest02 extends HttpServlet {
+
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        String dispatch = request.getParameter("dispatch");
+        if ("F".equals(dispatch)) {
+            RequestDispatcher rd =
+                getServletContext().getRequestDispatcher("/FilterRequest02a");
+            if (rd == null)
+                writer.println("FilterRequest02 FAILED - No forward request dispatcher");
+            else
+                rd.forward(request, response);
+        } else if ("I".equals(dispatch)) {
+            RequestDispatcher rd =
+                getServletContext().getRequestDispatcher("/FilterRequest02a");
+            if (rd == null)
+                writer.println("FilterRequest02 FAILED - No include request dispatcher");
+            else
+                rd.include(request, response);
+        } else {
+            String wrap = request.getParameter("wrap");
+            if ("false".equals(wrap)) {
+                if (request instanceof TesterHttpServletRequestWrapper)
+                    writer.println("FilterRequest02 FAILED - Request was wrapped");
+                else
+                    writer.println("FilterRequest02 PASSED");
+            } else if ("/WrappedFilterRequest02".equals(request.getServletPath())) {
+                if (request instanceof TesterHttpServletRequestWrapper)
+                    writer.println("FilterRequest02 PASSED");
+                else
+                    writer.println("FilterRequest02 FAILED - Wrapper class is "
+                                   + request.getClass().getName());
+            }
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/FilterRequest02a.java b/container/tester/src/tester/org/apache/tester/FilterRequest02a.java
new file mode 100644
index 0000000..00936d5
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/FilterRequest02a.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Verify that request and response wrappers added by a Filter are in fact
+ * visible to the called servlet.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class FilterRequest02a extends HttpServlet {
+
+
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        String wrap = request.getParameter("wrap");
+
+        if ("false".equals(wrap)) {
+            if (request instanceof TesterHttpServletRequestWrapper)
+                writer.println("FilterRequest02 FAILED - Request was wrapped");
+            else
+                writer.println("FilterRequest02 PASSED");
+        } else {
+            if (request instanceof TesterHttpServletRequestWrapper)
+                writer.println("FilterRequest02 PASSED");
+            else
+                writer.println("FilterRequest02 FAILED - Wrapper class is "
+                               + request.getClass().getName());
+        }
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/FilterResponse01.java b/container/tester/src/tester/org/apache/tester/FilterResponse01.java
new file mode 100644
index 0000000..9cdc2d8
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/FilterResponse01.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for being able to filter output.  The output should be
+ * filtered and converted to upper case before return to the client.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class FilterResponse01 extends HttpServlet {
+
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        writer.println("FilterResponse01 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/FilterResponse04.java b/container/tester/src/tester/org/apache/tester/FilterResponse04.java
new file mode 100644
index 0000000..8cb47a8
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/FilterResponse04.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Verify that request and response wrappers added by a Filter are in fact
+ * visible to the called servlet.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class FilterResponse04 extends HttpServlet {
+
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        String dispatch = request.getParameter("dispatch");
+        if ("F".equals(dispatch)) {
+            RequestDispatcher rd =
+                getServletContext().getRequestDispatcher("/FilterResponse04a");
+            if (rd == null)
+                writer.println("FilterResponse04 FAILED - No forward request dispatcher");
+            else
+                rd.forward(request, response);
+        } else if ("I".equals(dispatch)) {
+            RequestDispatcher rd =
+                getServletContext().getRequestDispatcher("/FilterResponse04a");
+            if (rd == null)
+                writer.println("FilterResponse04 FAILED - No include request dispatcher");
+            else
+                rd.include(request, response);
+        } else {
+            String wrap = request.getParameter("wrap");
+            if ("false".equals(wrap)) {
+                if (response instanceof TesterHttpServletResponseWrapper)
+                    writer.println("FilterResponse04 FAILED - Response was wrapped");
+                else
+                    writer.println("FilterResponse04 PASSED");
+            } else if ("/WrappedFilterResponse04".equals(request.getServletPath())) {
+                if (response instanceof TesterHttpServletResponseWrapper)
+                    writer.println("FilterResponse04 PASSED");
+                else
+                    writer.println("FilterResponse04 FAILED - Wrapper class is "
+                                   + response.getClass().getName());
+            }
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/FilterResponse04a.java b/container/tester/src/tester/org/apache/tester/FilterResponse04a.java
new file mode 100644
index 0000000..9817b88
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/FilterResponse04a.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Verify that request and response wrappers added by a Filter are in fact
+ * visible to the called servlet.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class FilterResponse04a extends HttpServlet {
+
+
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        String wrap = request.getParameter("wrap");
+
+        if ("false".equals(wrap)) {
+            if (response instanceof TesterHttpServletResponseWrapper)
+                writer.println("FilterResponse04 FAILED - Response was wrapped");
+            else
+                writer.println("FilterResponse04 PASSED");
+        } else {
+            if (response instanceof TesterHttpServletResponseWrapper)
+                writer.println("FilterResponse04 PASSED");
+            else
+                writer.println("FilterResponse04 FAILED - Wrapper class is "
+                               + response.getClass().getName());
+        }
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward00.java b/container/tester/src/tester/org/apache/tester/Forward00.java
new file mode 100644
index 0000000..e4eca38
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward00.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Exercise basic forwarding functionality.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward00 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Acquire the path to which we will issue a forward
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/Forward00a";
+
+        // Create a request dispatcher and call forward() on it
+        RequestDispatcher rd = null;
+        if (path.startsWith("!"))
+            rd = getServletContext().getNamedDispatcher(path.substring(1));
+        else
+            rd = getServletContext().getRequestDispatcher(path);
+        if (rd == null) {
+            sb.append(" No RequestDispatcher returned/");
+        } else {
+            if (sb.length() < 1)
+                rd.forward(request, response);
+        }
+
+        // Write our response if an error occurred
+        if (sb.length() >= 1) {
+            writer.print("Forward00 FAILED -");
+            writer.println(sb.toString());
+            while (true) {
+                String message = StaticLogger.read();
+                if (message == null)
+                    break;
+                writer.println(message);
+            }
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward00a.java b/container/tester/src/tester/org/apache/tester/Forward00a.java
new file mode 100644
index 0000000..8029dce
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward00a.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Exercise basic forwarding functionality.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward00a extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Write our response if an error occurred
+        writer.print("Forward00a PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward00d.java b/container/tester/src/tester/org/apache/tester/Forward00d.java
new file mode 100644
index 0000000..4b4295f
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward00d.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Exercise basic forwarding functionality.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward00d extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Write our response if an error occurred
+        writer.print("Forward00d PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward01.java b/container/tester/src/tester/org/apache/tester/Forward01.java
new file mode 100644
index 0000000..20850c8
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward01.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.security.Principal;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Positive test for forwarding to a static resource.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Forward01.txt");
+        if (rd == null) {
+            PrintWriter writer = response.getWriter();
+            writer.println("Forward01 FAILED - No RequestDispatcher returned");
+            return;
+        }
+        rd.forward(request, response);
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward03.java b/container/tester/src/tester/org/apache/tester/Forward03.java
new file mode 100644
index 0000000..8fdca0f
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward03.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test to ensure that a forwarded-to servlet can receive request attributes
+ * set by the calling servlet, as well as set their own request attributes.
+ *
+ * The test forwards to either a servlet ("/Forward03a") or a JSP page
+ * ("/Forward03b.jsp") depending on the value specified for the "path"
+ * parameter.  The default is the servlet.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward03 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Acquire the path to which we will issue a forward
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/Forward03a";
+
+        // Write the request attribute we will be forwarding
+        request.setAttribute("Forward03", "This is the forwarded attribute");
+        if (request.getAttribute("Forward03") == null)
+            sb.append(" Cannot retrieve attribute to forward/");
+
+        // Create a request dispatcher and call forward() on it
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher(path);
+        if (rd == null) {
+            sb.append(" No RequestDispatcher returned/");
+        } else {
+            if (sb.length() < 1)
+                rd.forward(request, response);
+        }
+
+        // Write our response if an error occurred
+        if (sb.length() >= 1) {
+            writer.print("Include03 FAILED -");
+            writer.println(sb.toString());
+            while (true) {
+                String message = StaticLogger.read();
+                if (message == null)
+                    break;
+                writer.println(message);
+            }
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward03a.java b/container/tester/src/tester/org/apache/tester/Forward03a.java
new file mode 100644
index 0000000..b6dd031
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward03a.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test to ensure that a forwarded-to servlet can receive request attributes
+ * set by the calling servlet, as well as set their own request attributes.
+ *
+ * The test forwards to either a servlet ("/Forward03a") or a JSP page
+ * ("/Forward03b.jsp") depending on the value specified for the "path"
+ * parameter.  The default is the servlet.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward03a extends HttpServlet {
+
+    private static final String specials[] =
+    { "javax.servlet.include.request_uri",
+      "javax.servlet.include.context_path",
+      "javax.servlet.include.servlet_path",
+      "javax.servlet.include.path_info",
+      "javax.servlet.include.query_string" };
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Verify that we can retrieve the forwarded attribute
+        if (request.getAttribute("Forward03") == null)
+            sb.append(" Cannot retrieve forwarded attribute/");
+
+        // Verify that we can set and retrieve our own attribute
+        request.setAttribute("Forward03a", "This is our own attribute");
+        if (request.getAttribute("Forward03a") == null)
+            sb.append(" Cannot retrieve our own attribute");
+
+        // Verify that no special attributes are present
+        for (int i = 0; i < specials.length; i++) {
+            if (request.getAttribute(specials[i]) != null) {
+                sb.append(" Returned attribute ");
+                sb.append(specials[i]);
+                sb.append("/");
+            }
+        }
+
+        // Write our response
+        if (sb.length() < 1)
+            writer.println("Forward03 PASSED");
+        else {
+            writer.print("Forward03 FAILED -");
+            writer.println(sb.toString());
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward04.java b/container/tester/src/tester/org/apache/tester/Forward04.java
new file mode 100644
index 0000000..493bd39
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward04.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward04 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the first servlet
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Forward04a");
+        if (rd == null) {
+            writer.println("Forward04 FAILED - No request dispatcher" +
+                           " for /Forward04a");
+        } else {
+            rd.forward(request, response);
+            writer.println("Forward04 text should NOT be present");
+        }
+
+        // Static logger output (should not actually be rendered)
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward04a.java b/container/tester/src/tester/org/apache/tester/Forward04a.java
new file mode 100644
index 0000000..e9ab3f3
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward04a.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward04a extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the second servlet
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Forward04b");
+        if (rd == null) {
+            writer.println("Forward04a FAILED - No request dispatcher" +
+                           " for /Forward04b");
+        } else {
+            rd.forward(request, response);
+            writer.println("Forward04a text should NOT be present");
+        }
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward04b.java b/container/tester/src/tester/org/apache/tester/Forward04b.java
new file mode 100644
index 0000000..8e61f08
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward04b.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward04b extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("Forward04b PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward05.java b/container/tester/src/tester/org/apache/tester/Forward05.java
new file mode 100644
index 0000000..efef17d
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward05.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward05 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the first servlet
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Forward05a.jsp");
+        if (rd == null) {
+            writer.println("Forward05 FAILED - No request dispatcher" +
+                           " for /Forward05a.jsp");
+        } else {
+            rd.forward(request, response);
+            writer.println("Forward05 text should NOT be present");
+        }
+
+        // Static logger output (should not actually be rendered)
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward05a.java b/container/tester/src/tester/org/apache/tester/Forward05a.java
new file mode 100644
index 0000000..091116e
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward05a.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward05a extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the second servlet
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Forward05b.jsp");
+        if (rd == null) {
+            writer.println("Forward05a FAILED - No request dispatcher" +
+                           " for /Forward05b.jsp");
+        } else {
+            rd.forward(request, response);
+            writer.println("Forward05a text should NOT be present");
+        }
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward05b.java b/container/tester/src/tester/org/apache/tester/Forward05b.java
new file mode 100644
index 0000000..3b2d63d
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward05b.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward05b extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("Forward05b PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward06.java b/container/tester/src/tester/org/apache/tester/Forward06.java
new file mode 100644
index 0000000..55e4407
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward06.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward06 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the first servlet
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Forward06a.jsp");
+        if (rd == null) {
+            writer.println("Forward05 FAILED - No request dispatcher" +
+                           " for /Forward06a.jsp");
+        } else {
+            rd.forward(request, response);
+            writer.println("Forward06 text should NOT be present");
+        }
+
+        // Static logger output (should not actually be rendered)
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward06a.java b/container/tester/src/tester/org/apache/tester/Forward06a.java
new file mode 100644
index 0000000..d1e4bcf
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward06a.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward06a extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the second servlet
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Forward06b.jsp");
+        if (rd == null) {
+            writer.println("Forward06a FAILED - No request dispatcher" +
+                           " for /Forward06b.jsp");
+        } else {
+            rd.forward(request, response);
+            writer.println("Forward06a text should NOT be present");
+        }
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward06b.java b/container/tester/src/tester/org/apache/tester/Forward06b.java
new file mode 100644
index 0000000..6434bcf
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward06b.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward06b extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("Forward06b PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward07.java b/container/tester/src/tester/org/apache/tester/Forward07.java
new file mode 100644
index 0000000..4908312
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward07.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward07 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the first servlet
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/servlet/Forward07a");
+        if (rd == null) {
+            writer.println("Forward07 FAILED - No request dispatcher" +
+                           " for /servlet/Forward07a");
+        } else {
+            rd.forward(request, response);
+            writer.println("Forward07 text should NOT be present");
+        }
+
+        // Static logger output (should not actually be rendered)
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward07a.java b/container/tester/src/tester/org/apache/tester/Forward07a.java
new file mode 100644
index 0000000..a206980
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward07a.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward07a extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the second servlet
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Forward07b");
+        if (rd == null) {
+            writer.println("Forward07a FAILED - No request dispatcher" +
+                           " for /Forward07b");
+        } else {
+            rd.forward(request, response);
+            writer.println("Forward07a text should NOT be present");
+        }
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward07b.java b/container/tester/src/tester/org/apache/tester/Forward07b.java
new file mode 100644
index 0000000..34afa1a
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward07b.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward07b extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("Forward07b PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward08.java b/container/tester/src/tester/org/apache/tester/Forward08.java
new file mode 100644
index 0000000..3ffd945
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward08.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward08 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the first servlet
+        log("Getting RD for /Forward08a");
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Forward08a");
+        if (rd == null) {
+            log("Missing RD for /Forward08a");
+            writer.println("Forward08 FAILED - No request dispatcher" +
+                           " for /Forward08a");
+        } else {
+            log("Forwarding to /Forward08a");
+            rd.forward(request, response);
+            log("Returned from /Forward08a");
+            writer.println("Forward08 text should NOT be present");
+        }
+
+        // Static logger output (should not actually be rendered)
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward08a.java b/container/tester/src/tester/org/apache/tester/Forward08a.java
new file mode 100644
index 0000000..ee67751
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward08a.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward08a extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Forward to the second servlet
+        log("Getting RD for /servlet/Forward08b");
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/servlet/Forward08b");
+        if (rd == null) {
+            log("Missing RD for /servlet/Forward08b");
+            writer.println("Forward08a FAILED - No request dispatcher" +
+                           " for /servlet/Forward08b");
+        } else {
+            log("Forwarding to /servlet/Forward08b");
+            rd.forward(request, response);
+            log("Returned from /servlet/Forward08b");
+            writer.println("Forward08a text should NOT be present");
+        }
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward08b.java b/container/tester/src/tester/org/apache/tester/Forward08b.java
new file mode 100644
index 0000000..7ec364c
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward08b.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Testing for double forwarding.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward08b extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("Forward08b PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Forward09.java b/container/tester/src/tester/org/apache/tester/Forward09.java
new file mode 100644
index 0000000..3c7ec25
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Forward09.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Exercise basic forwarding functionality using
+ * <code>request.getRequestDispatcher()</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Forward09 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Acquire the path to which we will issue a forward
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/Forward00a";
+
+        // Create a request dispatcher and call forward() on it
+        RequestDispatcher rd = request.getRequestDispatcher(path);
+        if (rd == null) {
+            sb.append(" No RequestDispatcher returned/");
+        } else {
+            if (sb.length() < 1)
+                rd.forward(request, response);
+        }
+
+        // Write our response if an error occurred
+        if (sb.length() >= 1) {
+            writer.print("Forward09 FAILED -");
+            writer.println(sb.toString());
+            while (true) {
+                String message = StaticLogger.read();
+                if (message == null)
+                    break;
+                writer.println(message);
+            }
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/GetHeaders01.java b/container/tester/src/tester/org/apache/tester/GetHeaders01.java
new file mode 100644
index 0000000..ade0f59
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/GetHeaders01.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test retrieval of headers.  The client is expected to send two
+ * "Accept-Language" headers.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class GetHeaders01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        ArrayList values = new ArrayList();
+        Enumeration headers = request.getHeaders("Accept-Language");
+        while (headers.hasMoreElements()) {
+            String header = (String) headers.nextElement() + ",";
+            while (true) {
+                int comma = header.indexOf(",");
+                if (comma < 0)
+                    break;
+                String value = header.substring(0, comma).trim();
+                values.add(value);
+                header = header.substring(comma + 1).trim();
+            }
+        }
+        if (values.size() != 2)
+            writer.println("GetHeaders01 FAILED - Returned " + values.size()
+                           + " headers instead of 2");
+        else if (values.get(0) == values.get(1))
+            writer.println("GetHeaders01 FAILED - Returned identical values "
+                           + values.get(0));
+        else {
+            int n = 0;
+            for (int i = 0; i < values.size(); i++) {
+                if ("en-us".equals((String) values.get(i)))
+                    n++;
+                else if ("en-gb".equals((String) values.get(i)))
+                    n++;
+            }
+            if (n != 2)
+                writer.println("GetHeaders01 FAILED - Returned unknown values "
+                               + values.get(0) + " and " + values.get(1));
+            else
+                writer.println("GetHeaders01 PASSED");
+        }
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/GetInputStream01.java b/container/tester/src/tester/org/apache/tester/GetInputStream01.java
new file mode 100644
index 0000000..b4ec204
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/GetInputStream01.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test getting the servlet input stream after we have retrieved the reader.
+ * This should throw an IllegalStateException.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class GetInputStream01 extends GenericServlet {
+
+    public void service(ServletRequest request, ServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        BufferedReader reader = request.getReader();
+        try {
+            ServletInputStream sis = request.getInputStream();
+            writer.println("GetInputStream01 FAILED - Did not throw " +
+                           "IllegalStateException");
+        } catch (IllegalStateException e) {
+            writer.println("GetInputStream01 PASSED");
+        }
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/GetLocales01.java b/container/tester/src/tester/org/apache/tester/GetLocales01.java
new file mode 100644
index 0000000..03464bd
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/GetLocales01.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 1 of the request locale tests.  Should receive a Locale that
+ * corresponds to "en_CA" Accept-Language header that is sent first.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class GetLocales01 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.reset();
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        Locale expected = new Locale("en", "CA");
+        Locale received = request.getLocale();
+        if (received == null)
+            writer.println("GetLocales01 FAILED - No locale was received");
+        else if (!expected.equals(received))
+            writer.println("GetLocales01 FAILED - Expected='" +
+                           expected.toString() + "' Received='" +
+                           received.toString() + "'");
+        else
+            writer.println("GetLocales01 PASSED");
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/GetLocales02.java b/container/tester/src/tester/org/apache/tester/GetLocales02.java
new file mode 100644
index 0000000..859b818
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/GetLocales02.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 2 of the request locale tests.  Should receive a Locale that
+ * corresponds to "en_CA" and then "en_GB" as sent by the client in
+ * "Accept-Language" headers.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class GetLocales02 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.reset();
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        StringBuffer sb = new StringBuffer();
+        boolean ok = true;
+
+        Enumeration locales = request.getLocales();
+        if (locales == null) {
+            sb.append(" No locales returned/");
+            ok = false;
+        }
+
+        if (ok) {
+            if (locales.hasMoreElements()) {
+                Locale expected = new Locale("en", "CA");
+                Locale received = (Locale) locales.nextElement();
+                if (!expected.equals(received)) {
+                    sb.append(" Expected1='" +
+                           expected.toString() + "' Received1='" +
+                           received.toString() + "'");
+                }
+            } else {
+                sb.append(" Zero locales returned/");
+                ok = false;
+            }
+        }
+
+        if (ok) {
+            if (locales.hasMoreElements()) {
+                Locale expected = new Locale("en", "GB");
+                Locale received = (Locale) locales.nextElement();
+                if (!expected.equals(received)) {
+                    sb.append(" Expected2='" +
+                           expected.toString() + "' Received2='" +
+                           received.toString() + "'");
+                }
+            } else {
+                sb.append(" One locale returned/");
+                ok = false;
+            }
+        }
+
+        if (ok) {
+            if (locales.hasMoreElements()) {
+                sb.append(" More than two locales returned/");
+                ok = false;
+            }
+        }
+
+        if (ok && (sb.length() < 1)) {
+            writer.println("GetLocales02 PASSED");
+        } else {
+            writer.print("GetLocales02 FAILED -");
+            writer.println(sb.toString());
+        }
+
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/GetParameter01.java b/container/tester/src/tester/org/apache/tester/GetParameter01.java
new file mode 100644
index 0000000..23cab49
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/GetParameter01.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test retrieval of parameters.  The client is expected to send a request
+ * URI like "/tester/GetParameter01?foo=1".
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class GetParameter01 extends GenericServlet {
+
+    public void service(ServletRequest request, ServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        String expected = "1";
+        String result = request.getParameter("foo");
+        if (expected.equals(result)) {
+            writer.println("GetParameter01 PASSED");
+        } else {
+            writer.println("GetParameter01 FAILED - Received '" + result +
+                           "' instead of '" + expected + "'");
+        }
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/GetParameterMap00.java b/container/tester/src/tester/org/apache/tester/GetParameterMap00.java
new file mode 100644
index 0000000..629ee41
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/GetParameterMap00.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.Map;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test getting the servlet input stream after we have retrieved the reader.
+ * This should throw an IllegalStateException.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class GetParameterMap00 extends GenericServlet {
+
+    public void service(ServletRequest request, ServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        String errors = "";
+        Map map = request.getParameterMap();
+        if (map == null)
+            errors += "  getParameterMap() returned null.";
+        else {
+            if (map.size() != 2)
+                errors += "  map size is " + map.size() + ".";
+            String value1[] = (String[]) map.get("BestLanguage");
+            if (value1 == null)
+                errors += "  BestLanguage is NULL.";
+            else if (value1.length != 1)
+                errors += "  BestLanguage has " + value1.length + " values.";
+            else if (!value1[0].equals("Java"))
+                errors += "  BestLanguage is " + value1 + ".";
+            String value2[] = (String[]) map.get("BestJSP");
+            if (value2 == null)
+                errors += "  BestJSP is NULL.";
+            else if (value2.length != 1)
+                errors += "  BestJSP has " + value2.length + " values.";
+            else if (!value2[0].equals("Java2"))
+                errors += "  BestJSP is " + value2 + ".";
+            try {
+                map.put("ABC", "XYZ");
+                errors += "   map.put() was allowed.";
+                if (map.get("ABC") != null)
+                    errors += "  map is not immutable.";
+            } catch (Throwable t) {
+                ;
+            }
+        }
+
+        if (errors.equals(""))
+            writer.println("GetParameterMap00 PASSED");
+        else {
+            writer.println("GetParameterMap00 FAILED:" + errors);
+        }
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/GetQueryString01.java b/container/tester/src/tester/org/apache/tester/GetQueryString01.java
new file mode 100644
index 0000000..9362099
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/GetQueryString01.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test retrieval of query string.  The client is expected to send a request
+ * URI like "/tester/GetQueryString01?foo=1".
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class GetQueryString01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        String expected = "foo=1";
+        String result = request.getQueryString();
+        if (expected.equals(result)) {
+            writer.println("GetQueryString01 PASSED");
+        } else {
+            writer.println("GetQueryString01 FAILED - Received '" + result +
+                           "' instead of '" + expected + "'");
+        }
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Golden01.java b/container/tester/src/tester/org/apache/tester/Golden01.java
new file mode 100644
index 0000000..836e986
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Golden01.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Simple test for the golden file mechanism.  This test generates a response
+ * with known contents for comparison to the golden file.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Golden01 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        writer.println("This is the");
+        writer.println("first golden file");
+        writer.println("to be tested.");
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include00.java b/container/tester/src/tester/org/apache/tester/Include00.java
new file mode 100644
index 0000000..acabc20
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include00.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Exercise basic including functionality.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include00 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Acquire the path to which we will issue a include
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/Include00a";
+
+        // Acquire the flush flag
+        boolean flush = "true".equals(request.getParameter("flush"));
+
+        // Acquire the "create a session" flag
+        boolean create = "true".equals(request.getParameter("create"));
+
+        // Create a request dispatcher and call include() on it
+        RequestDispatcher rd = null;
+        if (path.startsWith("!"))
+            rd = getServletContext().getNamedDispatcher(path.substring(1));
+        else
+            rd = getServletContext().getRequestDispatcher(path);
+        if (rd == null) {
+            sb.append(" No RequestDispatcher returned/");
+        } else {
+            HttpSession session;
+            if (create) {
+                session = request.getSession();
+            }
+            if (flush) {
+                try {
+                    response.flushBuffer();
+                } catch (IOException e) {
+                    sb.append(" Flush threw IOException/");
+                }
+            }
+            if (sb.length() < 1)
+                rd.include(request, response);
+        }
+
+        // Write our response if an error occurred
+        if (sb.length() >= 1) {
+            writer.print("Include00 FAILED -");
+            writer.println(sb.toString());
+            while (true) {
+                String message = StaticLogger.read();
+                if (message == null)
+                    break;
+                writer.println(message);
+            }
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include00a.java b/container/tester/src/tester/org/apache/tester/Include00a.java
new file mode 100644
index 0000000..b5db721
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include00a.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Exercise basic including functionality.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include00a extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Write our response if an error occurred
+        writer.print("Include00a PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include00d.java b/container/tester/src/tester/org/apache/tester/Include00d.java
new file mode 100644
index 0000000..7c50f47
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include00d.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Exercise basic including functionality.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include00d extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Write our response if an error occurred
+        writer.print("Include00d PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include01.java b/container/tester/src/tester/org/apache/tester/Include01.java
new file mode 100644
index 0000000..efb1de3
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include01.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.security.Principal;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Positive test for including a static resource.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Include01.txt");
+        if (rd == null) {
+            PrintWriter writer = response.getWriter();
+            writer.println("Include01 FAILED - No RequestDispatcher returned");
+            return;
+        }
+        rd.include(request, response);
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include02.java b/container/tester/src/tester/org/apache/tester/Include02.java
new file mode 100644
index 0000000..e5d4f73
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include02.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Positive test for handling exceptions thrown by an included servlet.
+ * Request parameter <strong>exception</strong> is used to indicate the type
+ * of exception that should be thrown, which must be one of
+ * <code>IOException</code>, <code>ServletException</code>, or
+ * <code>ServletException</code>.  According to the spec, any exceptions of
+ * these types should be propogated back to the caller unchanged.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include02 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        boolean ok = true;
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher("/Include02a");
+        if (rd == null) {
+            writer.println("Include02 FAILED - No RequestDispatcher returned");
+	    ok = false;
+        }
+	String type = request.getParameter("exception");
+	if (ok) {
+	    if (type == null) {
+	        writer.println("Include02 FAILED - No exception type specified");
+		ok = false;
+	    } else if (!type.equals("IOException") &&
+		       !type.equals("ServletException") &&
+		       !type.equals("NullPointerException")) {
+	        writer.println("Include02 FAILED - Invalid exception type " +
+			       type + " requested");
+		ok = false;
+	    }
+	}
+
+	IOException ioException = null;
+	ServletException servletException = null;
+	Throwable throwable = null;
+	try {
+            if (ok)
+                rd.include(request, response);
+	} catch (IOException e) {
+	    ioException = e;
+	} catch (ServletException e) {
+	    servletException = e;
+	} catch (Throwable e) {
+	    throwable = e;
+	}
+
+	if (ok) {
+            if (type.equals("IOException")) {
+                if (ioException == null) {
+		    writer.println("Include02 FAILED - No IOException thrown");
+		    ok = false;
+		} else {
+		    String message = ioException.getMessage();
+		    if (!"Include02 IOException".equals(message)) {
+		        writer.println("Include02 FAILED - IOException was " +
+				       message);
+			ok = false;
+		    }
+		}
+	    } else if (type.equals("ServletException")) {
+                if (servletException == null) {
+		    writer.println("Include02 FAILED - No ServletException thrown");
+		    ok = false;
+		} else {
+		    String message = servletException.getMessage();
+		    if (!"Include02 ServletException".equals(message)) {
+		        writer.println("Include02 FAILED - ServletException was " +
+				       message);
+			ok = false;
+		    }
+		}
+	    } else if (type.equals("NullPointerException")) {
+                if (throwable == null) {
+		    writer.println("Include02 FAILED - No NullPointerException thrown");
+		    ok = false;
+		} else if (!(throwable instanceof NullPointerException)) {
+		    writer.println("Include02 FAILED - Thrown Exception was " +
+				   throwable.getClass().getName());
+		    ok = false;
+		} else {
+		    String message = throwable.getMessage();
+		    if (!"Include02 NullPointerException".equals(message)) {
+		        writer.println("Include02 FAILED - NullPointerException was " +
+				       message);
+			ok = false;
+		    }
+		}
+	    }
+	}
+
+	if (ok)
+	    writer.println("Include02 PASSED");
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include02a.java b/container/tester/src/tester/org/apache/tester/Include02a.java
new file mode 100644
index 0000000..4436264
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include02a.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Included servlet for the test performed by Include02.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include02a extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        String type = request.getParameter("exception");
+	if (type == null)
+	    return;
+	else if (type.equals("IOException"))
+	    throw new IOException("Include02 IOException");
+	else if (type.equals("ServletException"))
+	    throw new ServletException("Include02 ServletException");
+	else if (type.equals("NullPointerException"))
+	    throw new NullPointerException("Include02 NullPointerException");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include03.java b/container/tester/src/tester/org/apache/tester/Include03.java
new file mode 100644
index 0000000..12f8a90
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include03.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test to insure that an included servlet can set request attributes that are
+ * visible to the calling servlet after the <code>include()</code> returns.
+ * The spec is silent on this topic, but it seems consistent with the overall
+ * intent to behave in this manner.
+ *
+ * The test includes either a servlet ("/Include03a") or a JSP page
+ * ("/Include03b.jsp") depending on the value specified for the "path"
+ * parameter.  The default is the servlet.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include03 extends HttpServlet {
+
+    private static final String specials[] =
+    { "javax.servlet.include.request_uri",
+      "javax.servlet.include.context_path",
+      "javax.servlet.include.servlet_path",
+      "javax.servlet.include.path_info",
+      "javax.servlet.include.query_string" };
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Acquire the path to which we will issue an include
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/Include03a";
+
+        // Create a request dispatcher and call include() on it
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher(path);
+        if (rd == null) {
+            sb.append(" No RequestDispatcher returned/");
+        } else {
+            rd.include(request, response);
+        }
+        response.resetBuffer();
+
+        // We MUST be able to see the attribute created by the includee
+        String value = null;
+        try {
+            value = (String)
+                request.getAttribute(path.substring(1));
+        } catch (ClassCastException e) {
+            sb.append(" Returned attribute not of type String/");
+        }
+        if ((sb.length() < 1) && (value == null)) {
+            sb.append(" No includee-created attribute was returned/");
+        }
+
+        // We MUST NOT see the special attributes created by the container
+        for (int i = 0; i < specials.length; i++) {
+            if (request.getAttribute(specials[i]) != null) {
+                sb.append(" Returned attribute ");
+                sb.append(specials[i]);
+                sb.append("/");
+            }
+        }
+
+        // Write our response
+        if (sb.length() < 1)
+            writer.println("Include03 PASSED");
+        else {
+            writer.print("Include03 FAILED -");
+            writer.println(sb.toString());
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include03a.java b/container/tester/src/tester/org/apache/tester/Include03a.java
new file mode 100644
index 0000000..69f5a5c
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include03a.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test to insure that an included servlet can set request attributes that are
+ * visible to the calling servlet after the <code>include()</code> returns.
+ * The spec is silent on this topic, but it seems consistent with the overall
+ * intent to behave in this manner.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include03a extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        request.setAttribute("Include03a", "This is a new attribute");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include04.java b/container/tester/src/tester/org/apache/tester/Include04.java
new file mode 100644
index 0000000..935ba33
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include04.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test to ensure that we can include a servlet (without flushing the buffer),
+ * then forward to another servlet that replaces the original contents.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include04 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setBufferSize(8192);
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Include our first subservlet
+        RequestDispatcher rd1 =
+          getServletContext().getRequestDispatcher("/Include04a");
+        if (rd1 == null) {
+            sb.append(" No RD for '/Include04a'/");
+        } else {
+            rd1.include(request, response);
+        }
+
+        // Forward to our second subservlet
+        RequestDispatcher rd2 =
+          getServletContext().getRequestDispatcher("/Include04b");
+        if (rd2 == null) {
+            sb.append("No RD for '/Include04b'/");
+        } else {
+          rd2.forward(request, response);
+        }
+
+        // Append error messages if necessary
+        if (sb.length() > 0) {
+            writer.print("Include04 FAILED -");
+            writer.println(sb.toString());
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include04a.java b/container/tester/src/tester/org/apache/tester/Include04a.java
new file mode 100644
index 0000000..73aa744
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include04a.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * First subservlet for "include then forward" test.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include04a extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+	PrintWriter writer = response.getWriter();
+        writer.println("Include04a PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include04b.java b/container/tester/src/tester/org/apache/tester/Include04b.java
new file mode 100644
index 0000000..c99a3ba
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include04b.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Second subservlet for "include then forward" test.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include04b extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+	PrintWriter writer = response.getWriter();
+        writer.println("Include04b PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include06a.java b/container/tester/src/tester/org/apache/tester/Include06a.java
new file mode 100644
index 0000000..4a11faf
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include06a.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * First subservlet for "include then include" test.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include06a extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+	PrintWriter writer = response.getWriter();
+        writer.println("Include06a output");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include07.java b/container/tester/src/tester/org/apache/tester/Include07.java
new file mode 100644
index 0000000..c4f9a59
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include07.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test that insures we can include a servlet that does a forward, then
+ * includes another servlet, and we see all of the output in the right order.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include07 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setBufferSize(8192);
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+        writer.println("Output from Include07");
+
+        // Include our first subservlet
+        RequestDispatcher rd1 =
+          getServletContext().getRequestDispatcher("/Include07a");
+        if (rd1 == null) {
+            writer.println("No RD for '/Include07a'");
+        } else {
+            rd1.include(request, response);
+        }
+
+        // Include our second subservlet
+        RequestDispatcher rd2 =
+          getServletContext().getRequestDispatcher("/Include07c");
+        if (rd2 == null) {
+            writer.println("No RD for '/Include07c'");
+        } else {
+            rd2.include(request, response);
+        }
+
+        // Finish this response
+        writer.println("Output from Include07");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include07a.java b/container/tester/src/tester/org/apache/tester/Include07a.java
new file mode 100644
index 0000000..d68b558
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include07a.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test that insures we can include a servlet that does a forward, then
+ * includes another servlet, and we see all of the output in the right order.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include07a extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+	PrintWriter writer = response.getWriter();
+
+        // Forward to our subsubservlet
+        RequestDispatcher rd =
+          getServletContext().getRequestDispatcher("/Include07b");
+        if (rd == null) {
+            writer.println("No RD for '/Include07b'");
+        } else {
+            rd.include(request, response);
+        }
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include07b.java b/container/tester/src/tester/org/apache/tester/Include07b.java
new file mode 100644
index 0000000..ffe3aaa
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include07b.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test that insures we can include a servlet that does a forward, then
+ * includes another servlet, and we see all of the output in the right order.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include07b extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+	PrintWriter writer = response.getWriter();
+        writer.println("Output from Include07b");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include07c.java b/container/tester/src/tester/org/apache/tester/Include07c.java
new file mode 100644
index 0000000..f09f445
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include07c.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Test that insures we can include a servlet that does a forward, then
+ * includes another servlet, and we see all of the output in the right order.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include07c extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+	PrintWriter writer = response.getWriter();
+        writer.println("Output from Include07c");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include09.java b/container/tester/src/tester/org/apache/tester/Include09.java
new file mode 100644
index 0000000..ead1b3d
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include09.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Exercise basic including functionality using
+ * <code>request.getRequestDispatcher()</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include09 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Acquire the path to which we will issue a include
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/Include00a";
+
+        // Create a request dispatcher and call include() on it
+        RequestDispatcher rd = request.getRequestDispatcher(path);
+        if (rd == null) {
+            sb.append(" No RequestDispatcher returned/");
+        } else {
+            if (sb.length() < 1)
+                rd.include(request, response);
+        }
+
+        // Write our response if an error occurred
+        if (sb.length() >= 1) {
+            writer.print("Include00 FAILED -");
+            writer.println(sb.toString());
+            while (true) {
+                String message = StaticLogger.read();
+                if (message == null)
+                    break;
+                writer.println(message);
+            }
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include10.java b/container/tester/src/tester/org/apache/tester/Include10.java
new file mode 100644
index 0000000..b66b86c
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include10.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Make sure container sets up include reques attributes.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include10 extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+        response.setContentType("text/plain");
+	PrintWriter writer = response.getWriter();
+
+        // Pass copies of the original request properties
+        request.setAttribute("original.request_uri",
+                             request.getRequestURI());
+        request.setAttribute("original.context_path",
+                             request.getContextPath());
+        request.setAttribute("original.servlet_path",
+                             request.getServletPath());
+        request.setAttribute("original.path_info",
+                             request.getPathInfo());
+        request.setAttribute("original.query_string",
+                             request.getQueryString());
+
+        // Create a request dispatcher and call include() on it
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher
+            ("/Include10a/include/path?name2=value2");
+        if (rd == null) {
+            sb.append(" No RequestDispatcher returned/");
+        } else {
+            if (sb.length() < 1)
+                rd.include(request, response);
+        }
+
+        // Write our response if an error occurred
+        if (sb.length() >= 1) {
+            writer.print("Include00 FAILED -");
+            writer.println(sb.toString());
+            while (true) {
+                String message = StaticLogger.read();
+                if (message == null)
+                    break;
+                writer.println(message);
+            }
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Include10a.java b/container/tester/src/tester/org/apache/tester/Include10a.java
new file mode 100644
index 0000000..157315a
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Include10a.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Ensure the correct container managed request attributes are set.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Include10a extends HttpServlet {
+
+    private static final String specials[] =
+    { "javax.servlet.include.request_uri",
+      "javax.servlet.include.context_path",
+      "javax.servlet.include.servlet_path",
+      "javax.servlet.include.path_info",
+      "javax.servlet.include.query_string" };
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        StringBuffer sb = new StringBuffer();
+	PrintWriter writer = response.getWriter();
+
+        // Validate the original request properties
+        String value = null;
+        value = (String) request.getAttribute("original.request_uri");
+        if (!value.equals(request.getRequestURI()))
+            sb.append(" getRequestURI() is " + request.getRequestURI() +
+                      " but should be " + value + "|");
+        value = (String) request.getAttribute("original.context_path");
+        if (!value.equals(request.getContextPath()))
+            sb.append(" getContextPath() is " + request.getContextPath() +
+                      " but should be " + value + "|");
+        value = (String) request.getAttribute("original.servlet_path");
+        if (!value.equals(request.getServletPath()))
+            sb.append(" getServletPath() is " + request.getServletPath() +
+                      " but should be " + value + "|");
+        value = (String) request.getAttribute("original.path_info");
+        if (!value.equals(request.getPathInfo()))
+            sb.append(" getPathInfo() is " + request.getPathInfo() +
+                      " but should be " + value + "|");
+        value = (String) request.getAttribute("original.query_string");
+        if (!value.equals(request.getQueryString()))
+            sb.append(" getQueryString() is " + request.getQueryString() +
+                      " but should be " + value + "|");
+
+        // Validate the container provided request attributes
+        value = (String)
+            request.getAttribute("javax.servlet.include.request_uri");
+        if (!(request.getContextPath() + "/Include10a/include/path").equals(value))
+            sb.append(" request_uri is " + value +
+                      " but should be " + request.getContextPath() +
+                      "/Include10a/include/path|");
+        value = (String)
+            request.getAttribute("javax.servlet.include.context_path");
+        if (!request.getContextPath().equals(value))
+            sb.append(" context_path is " + value +
+                      " but should be " + request.getContextPath() + "|");
+        value = (String)
+            request.getAttribute("javax.servlet.include.servlet_path");
+        if (!"/Include10a".equals(value))
+            sb.append(" servlet_path is " + value +
+                      " but should be /Include10a|");
+        value = (String)
+            request.getAttribute("javax.servlet.include.path_info");
+        if (!"/include/path".equals(value))
+            sb.append(" path_info is " + value +
+                      " but should be /include/path|");
+        value = (String)
+            request.getAttribute("javax.servlet.include.query_string");
+        if (!"name2=value2".equals(value))
+            sb.append(" query_string is " + value +
+                      " but should be name2=value2|");
+
+        // Generate our success or failure report
+        if (sb.length() < 1)
+            writer.println("Include10a PASSED");
+        else
+            writer.println("Include10a FAILED -" + sb.toString());
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Jndi01.java b/container/tester/src/tester/org/apache/tester/Jndi01.java
new file mode 100644
index 0000000..3577b24
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Jndi01.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.naming.Binding;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import org.apache.tester.SessionBean;
+import org.apache.tester.shared.SharedSessionBean;
+import org.apache.tester.unpshared.UnpSharedSessionBean;
+import org.apache.tester.unshared.UnsharedSessionBean;
+
+
+/**
+ * Negative test for ensuring that the naming context provided by the servlet
+ * container is immutable.  No attempt to add, modify, or delete any binding
+ * should succeed.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Jndi01 extends HttpServlet {
+
+    public void init() throws ServletException {
+
+        // Access the naming context from init()
+        Context ctx = null;
+        try {
+            ctx = new InitialContext();
+            ctx.lookup("java:/comp");
+            log("initialized successfully in init()");
+        } catch (NamingException e) {
+            e.printStackTrace();
+            log("Cannot create context in init()", e);
+            throw new ServletException(e);
+        }
+
+        // Access some application beans from init()
+
+        try {
+            SessionBean sb = new SessionBean();
+            log("OK Accessing SessionBean");
+        } catch (Throwable t) {
+            log("FAIL Accessing SessionBean", t);
+        }
+
+        try {
+            SharedSessionBean sb = new SharedSessionBean();
+            log("OK Accessing SharedSessionBean");
+        } catch (Throwable t) {
+            log("FAIL Accessing SharedSessionBean", t);
+        }
+
+        try {
+            UnpSharedSessionBean sb = new UnpSharedSessionBean();
+            log("OK Accessing UnpSharedSessionBean");
+        } catch (Throwable t) {
+            log("FAIL Accessing UnpSharedSessionBean", t);
+        }
+
+        try {
+            UnsharedSessionBean sb = new UnsharedSessionBean();
+            log("OK Accessing UnsharedSessionBean");
+        } catch (Throwable t) {
+            log("FAIL Accessing UnsharedSessionBean", t);
+        }
+
+    }
+
+    public void destroy() {
+        Context ctx = null;
+        try {
+            ctx = new InitialContext();
+            ctx.lookup("java:/comp");
+            log("initialized successfully in destroy()");
+        } catch (NamingException e) {
+            e.printStackTrace();
+            log("Cannot create context in destroy()", e);
+        }
+    }
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare to render our output
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        StringBuffer sb = new StringBuffer();
+        boolean ok = true;
+        Object value = null;
+
+        // Look up the initial context provided by our servlet container
+        Context initContext = null;
+        try {
+            initContext = new InitialContext();
+        } catch (NamingException e) {
+            log("Create initContext", e);
+            sb.append("  Cannot create initContext.");
+            ok = false;
+        }
+
+        // Look up the environment context provided to our web application
+        Context envContext = null;
+        try {
+            if (ok) {
+                value = initContext.lookup("java:comp/env");
+                envContext = (Context) value;
+                if (envContext == null) {
+                    sb.append("  Missing envContext.");
+                    ok = false;
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  envContext class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");
+            ok = false;
+        } catch (NamingException e) {
+            log("Create envContext", e);
+            sb.append("  Cannot create envContext.");
+            ok = false;
+        }
+
+        // Attempt to add a new binding to our environment context
+        try {
+            if (ok) {
+                envContext.bind("newEntry", "New Value");
+                sb.append("  Allowed bind().");
+                value = envContext.lookup("newEntry");
+                if (value != null)
+                    sb.append("  Allowed lookup() of added entry.");
+            }
+        } catch (Throwable e) {
+            log("Add binding", e);
+        }
+
+        // Attempt to change the value of an existing binding
+        try {
+            if (ok) {
+                envContext.rebind("stringEntry", "Changed Value");
+                sb.append("  Allowed rebind().");
+                value = envContext.lookup("stringEntry");
+                if ((value != null) &&
+                    (value instanceof String) &&
+                    "Changed Value".equals((String) value))
+                    sb.append("  Allowed lookup() of changed entry.");
+            }
+        } catch (Throwable e) {
+            log("Change binding", e);
+        }
+
+        // Attempt to delete an existing binding
+        try {
+            if (ok) {
+                envContext.unbind("byteEntry");
+                sb.append("  Allowed unbind().");
+                value = envContext.lookup("byteEntry");
+                if (value == null)
+                    sb.append("  Allowed unbind of deleted entry.");
+            }
+        } catch (Throwable e) {
+            log("Delete binding", e);
+        }
+
+        // Report our ultimate success or failure
+        if (sb.length() < 1)
+            writer.println("Jndi01 PASSED");
+        else {
+            writer.print("Jndi01 FAILED -");
+            writer.println(sb);
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Jndi02.java b/container/tester/src/tester/org/apache/tester/Jndi02.java
new file mode 100644
index 0000000..5d06658
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Jndi02.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.naming.Binding;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import org.apache.tester.SessionBean;
+import org.apache.tester.shared.SharedSessionBean;
+import org.apache.tester.unpshared.UnpSharedSessionBean;
+import org.apache.tester.unshared.UnsharedSessionBean;
+
+
+/**
+ * Positive test for looking up environment entries from the naming context
+ * provided by the servlet container.  The looked-up values are initialized
+ * via <code>&lt;env-entry&gt;</code> elements in the web application
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Jndi02 extends HttpServlet {
+
+    // Names of the known <env-entry> elements
+    String names[] =
+    { "booleanEntry", "byteEntry", "doubleEntry", "floatEntry",
+      "integerEntry", "longEntry", "stringEntry", "nested" };
+
+
+    // Reference some application classes for the first time in destroy()
+    // and log the results
+    public void destroy() {
+
+        try {
+            SessionBean sb = new SessionBean();
+            log("OK Accessing SessionBean");
+        } catch (Throwable t) {
+            log("FAIL Accessing SessionBean", t);
+        }
+
+        try {
+            SharedSessionBean sb = new SharedSessionBean();
+            log("OK Accessing SharedSessionBean");
+        } catch (Throwable t) {
+            log("FAIL Accessing SharedSessionBean", t);
+        }
+
+        try {
+            UnpSharedSessionBean sb = new UnpSharedSessionBean();
+            log("OK Accessing UnpSharedSessionBean");
+        } catch (Throwable t) {
+            log("FAIL Accessing UnpSharedSessionBean", t);
+        }
+
+        try {
+            UnsharedSessionBean sb = new UnsharedSessionBean();
+            log("OK Accessing UnsharedSessionBean");
+        } catch (Throwable t) {
+            log("FAIL Accessing UnsharedSessionBean", t);
+        }
+
+    }
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare to render our output
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        StringBuffer sb = new StringBuffer();
+        boolean ok = true;
+        Object value = null;
+
+        // Look up the initial context provided by our servlet container
+        Context initContext = null;
+        try {
+            initContext = new InitialContext();
+        } catch (NamingException e) {
+            log("Create initContext", e);
+            sb.append("  Cannot create initContext.");
+            ok = false;
+        }
+
+        // Look up the environment context provided to our web application
+        Context envContext = null;
+        try {
+            if (ok) {
+                value = initContext.lookup("java:comp/env");
+                envContext = (Context) value;
+                if (envContext == null) {
+                    sb.append("  Missing envContext.");
+                    ok = false;
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  envContext class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");
+            ok = false;
+        } catch (NamingException e) {
+            log("Create envContext", e);
+            sb.append("  Cannot create envContext.");
+            ok = false;
+        }
+
+        // Validate the booleanEntry environment entry
+        try {
+            if (ok) {
+                value = envContext.lookup("booleanEntry");
+                Boolean booleanValue = (Boolean) value;
+                if (!(booleanValue.booleanValue() == true)) {
+                    sb.append("  booleanValue is ");
+                    sb.append(booleanValue);
+                    sb.append(".");
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  booleanValue class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");                      
+        } catch (NullPointerException e) {
+            sb.append("  booleanValue is missing.");
+        } catch (NamingException e) {
+            log("Get booleanValue", e);
+            sb.append("  Cannot get booleanValue.");
+        }
+
+        // Validate the byteEntry environment entry
+        try {
+            if (ok) {
+                value = envContext.lookup("byteEntry");
+                Byte byteValue = (Byte) value;
+                if (!(byteValue.byteValue() == 123)) {
+                    sb.append("  byteValue is ");
+                    sb.append(byteValue);
+                    sb.append(".");
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  byteValue class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");                      
+        } catch (NullPointerException e) {
+            sb.append("  byteValue is missing.");
+        } catch (NamingException e) {
+            log("Get byteValue", e);
+            sb.append("  Cannot get byteValue.");
+        }
+
+        // Validate the doubleEntry environment entry
+        try {
+            if (ok) {
+                value = envContext.lookup("doubleEntry");
+                Double doubleValue = (Double) value;
+                if (!(doubleValue.doubleValue() == 123.45)) {
+                    sb.append("  doubleValue is ");
+                    sb.append(doubleValue);
+                    sb.append(".");
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  doubleValue class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");                      
+        } catch (NullPointerException e) {
+            sb.append("  doubleValue is missing.");
+        } catch (NamingException e) {
+            log("Get doubleValue", e);
+            sb.append("  Cannot get doubleValue.");
+        }
+
+        // Validate the floatEntry environment entry
+        try {
+            if (ok) {
+                value = envContext.lookup("floatEntry");
+                Float floatValue = (Float) value;
+                float difference = floatValue.floatValue() - ((float) 54.32);
+                if ((difference < ((float) -0.01)) ||
+                    (difference > ((float)  0.01))) {
+                    sb.append("  floatValue is ");
+                    sb.append(floatValue);
+                    sb.append(".");
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  floatValue class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");                      
+        } catch (NullPointerException e) {
+            sb.append("  floatValue is missing.");
+        } catch (NamingException e) {
+            log("Get floatValue", e);
+            sb.append("  Cannot get floatValue.");
+        }
+
+        // Validate the integerEntry environment entry
+        try {
+            if (ok) {
+                value = envContext.lookup("integerEntry");
+                Integer integerValue = (Integer) value;
+                if (!(integerValue.intValue() == 12345)) {
+                    sb.append("  integerValue is ");
+                    sb.append(integerValue);
+                    sb.append(".");
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  integerValue class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");                      
+        } catch (NullPointerException e) {
+            sb.append("  integerValue is missing.");
+        } catch (NamingException e) {
+            log("Get integerValue", e);
+            sb.append("  Cannot get integerValue.");
+        }
+
+        // Validate the longEntry environment entry
+        try {
+            if (ok) {
+                value = envContext.lookup("longEntry");
+                Long longValue = (Long) value;
+                if (!(longValue.longValue() == 54321)) {
+                    sb.append("  longValue is ");
+                    sb.append(longValue);
+                    sb.append(".");
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  longValue class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");                      
+        } catch (NullPointerException e) {
+            sb.append("  longValue is missing.");
+        } catch (NamingException e) {
+            log("Get longValue", e);
+            sb.append("  Cannot get longValue.");
+        }
+
+        // Validate the stringEntry environment entry
+        try {
+            if (ok) {
+                value = envContext.lookup("stringEntry");
+                String stringValue = (String) value;
+                if (!"String Value".equals(stringValue)) {
+                    sb.append("  stringValue is ");
+                    sb.append(stringValue);
+                    sb.append(".");
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  stringValue class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");                      
+        } catch (NullPointerException e) {
+            sb.append("  stringValue is missing.");
+        } catch (NamingException e) {
+            log("Get stringValue", e);
+            sb.append("  Cannot get stringValue.");
+        }
+
+        // Validate the nestedEntry environment entry
+        try {
+            if (ok) {
+                value = envContext.lookup("nested/nestedEntry");
+                String stringValue = (String) value;
+                if (!"Nested Value".equals(stringValue)) {
+                    sb.append("  stringValue is ");
+                    sb.append(stringValue);
+                    sb.append(".");
+                }
+            }
+        } catch (ClassCastException e) {
+            sb.append("  stringValue class is ");
+            sb.append(value.getClass().getName());
+            sb.append(".");                      
+        } catch (NullPointerException e) {
+            sb.append("  stringValue is missing.");
+        } catch (NamingException e) {
+            log("Get stringValue", e);
+            sb.append("  Cannot get stringValue.");
+        }
+
+        // Validate that we can enumerate the contents of our environment
+        try {
+            if (ok) {
+                int counts[] = new int[names.length];
+                for (int i = 0; i < names.length; i++)
+                    counts[i] = 0;
+                NamingEnumeration namingEnum =
+                    initContext.listBindings("java:comp/env");
+                while (namingEnum.hasMore()) {
+                    Binding binding = (Binding) namingEnum.next();
+                    String name = binding.getName();
+                    boolean found = false;
+                    for (int i = 0; i < names.length; i++) {
+                        if (name.equals(names[i])) {
+                            counts[i]++;
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found)
+                        StaticLogger.write("Found binding for '" + name + "'");
+                }
+                for (int i = 0; i < names.length; i++) {
+                    if (counts[i] < 1) {
+                        sb.append("  Missing binding for ");
+                        sb.append(names[i]);
+                        sb.append(".");
+                    } else if (counts[i] > 1) {
+                        sb.append("  Found ");
+                        sb.append(counts[i]);
+                        sb.append(" bindings for ");
+                        sb.append(names[i]);
+                        sb.append(".");
+                    }
+                }
+            }
+        } catch (NamingException e) {
+            log("Enumerate envContext", e);
+            sb.append("  Cannot enumerate envContext");
+        }
+
+        // Report our ultimate success or failure
+        if (sb.length() < 1)
+            writer.println("Jndi02 PASSED");
+        else {
+            writer.print("Jndi02 FAILED -");
+            writer.println(sb);
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Lifecycle01.java b/container/tester/src/tester/org/apache/tester/Lifecycle01.java
new file mode 100644
index 0000000..bb940c1
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Lifecycle01.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for servlet lifecycle management.  This servlet is
+ * <strong>not</strong> declared to be load-on-startup, and the first request
+ * made to it should be a "GET".
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Lifecycle01 extends HttpServlet {
+
+    private boolean doubled = false;
+
+    private boolean initialized = false;
+
+    public void init() throws ServletException {
+        if (initialized)
+            doubled = true;
+        else
+            initialized = true;
+    }
+
+    public void destroy() {
+        initialized = false;
+    }
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        if (doubled) {
+            writer.println("Lifecycle01 FAILED - Double initialization");
+        } else if (initialized) {
+            writer.println("Lifecycle01 PASSED");
+        } else {
+            writer.println("Lifecycle01 FAILED - GET but not initialized");
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+    public void doPost(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        if (doubled) {
+            writer.println("Lifecycle01 FAILED - POST and double initialization");
+        } else if (initialized) {
+            writer.println("Lifecycle01 FAILED - POST called");
+        } else {
+            writer.println("Lifecycle01 FAILED - POST and not initialized");
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Lifecycle02.java b/container/tester/src/tester/org/apache/tester/Lifecycle02.java
new file mode 100644
index 0000000..3ec608f
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Lifecycle02.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for servlet lifecycle management.  This servlet is
+ * <strong>not</strong> declared to be load-on-startup, and the first request
+ * made to it should be a "POST".
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Lifecycle02 extends HttpServlet {
+
+    private boolean doubled = false;
+
+    private boolean initialized = false;
+
+    public void init() throws ServletException {
+        if (initialized)
+            doubled = true;
+        else
+            initialized = true;
+    }
+
+    public void destroy() {
+        initialized = false;
+    }
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        if (doubled) {
+            writer.println("Lifecycle02 FAILED - GET and double initialization");
+        } else if (initialized) {
+            writer.println("Lifecycle02 FAILED - GET called");
+        } else {
+            writer.println("Lifecycle02 FAILED - GET and not initialized");
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+    public void doPost(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        if (doubled) {
+            writer.println("Lifecycle02 FAILED - Double initialization");
+        } else if (initialized) {
+            writer.println("Lifecycle02 PASSED");
+        } else {
+            writer.println("Lifecycle02 FAILED - POST but not initialized");
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Lifecycle03.java b/container/tester/src/tester/org/apache/tester/Lifecycle03.java
new file mode 100644
index 0000000..be6bcb6
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Lifecycle03.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Test for servlet lifecycle management.  It's behavior is controlled by
+ * a request parameter <strong>step</strong>, which must be set to one of
+ * the following values:
+ * <ul>
+ * <li><em>1</em> - Throw an <code>UnavailableException</code> that indicates
+ *     permanent unavailablility, which should cause this servlet instance
+ *     to be destroyed and thrown away.</li>
+ * <li><em>2</em> - Check the lifecycle variables to ensure that the old
+ *     instance was not reused.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Lifecycle03 extends HttpServlet {
+
+    private static String staticTrail = "";
+
+    private String instanceTrail = "";
+
+    public void init() throws ServletException {
+        staticTrail += "I";
+        instanceTrail += "I";
+    }
+
+    public void destroy() {
+        staticTrail += "D";
+        instanceTrail += "D";
+    }
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        staticTrail += "S";
+        instanceTrail += "S";
+
+        // For step=1, throw an exception
+        if ("1".equals(request.getParameter("step"))) {
+            staticTrail = "IS";
+            throw new UnavailableException("Lifecycle03 is permanently unavailable");
+        }
+
+        // For step=2, evaluate the results.
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        if (staticTrail.equals("ISDIS") && instanceTrail.equals("IS"))
+            writer.println("Lifecycle03 PASSED");
+        else
+            writer.println("Lifecycle03 FAILED - staticTrail=" + staticTrail +
+                           ", instanceTrail=" + instanceTrail);
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Redirect01.java b/container/tester/src/tester/org/apache/tester/Redirect01.java
new file mode 100644
index 0000000..02d72c6
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Redirect01.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for HttpServletResponse.sendRedirect().
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Redirect01 extends HttpServlet {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process a servlet request and create the corresponding response.
+     *
+     * @param request The request we are processing
+     * @param response The response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        try {
+            response.sendRedirect(request.getContextPath() + "/Redirect01a");
+            return;
+        } catch (IllegalStateException e) {
+            writer.println("Redirect01 FAILED - Threw IllegaStateException");
+            e.printStackTrace(writer);
+        }
+        try {
+            writer.println("Redirect01 FAILED - Output text after redirect");
+        } catch (Throwable t) {
+            throw new ServletException("Redirect01 Post-Redirect Output Error",
+                                       t);
+        }
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Redirect01a.java b/container/tester/src/tester/org/apache/tester/Redirect01a.java
new file mode 100644
index 0000000..2ef0488
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Redirect01a.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for HttpServletResponse.sendRedirect().
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Redirect01a extends HttpServlet {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process a servlet request and create the corresponding response.
+     *
+     * @param request The request we are processing
+     * @param response The response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("Redirect01a PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Reflection01.java b/container/tester/src/tester/org/apache/tester/Reflection01.java
new file mode 100644
index 0000000..a450762
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Reflection01.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Negative test for access to Catalina internals through the objects that
+ * are exposed to this servlet by the container.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Reflection01 extends HttpServlet {
+
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        StringBuffer results = new StringBuffer();
+
+        // Check the ServletConfig object
+        try {
+            ServletConfig servletConfig = getServletConfig();
+            Method method = servletConfig.getClass().getMethod
+                ("getParent", new Class[] {});
+            Object parent = method.invoke(servletConfig,
+                                          new Object[] {});
+            results.append(" Can reflect on ServletConfig/");
+        } catch (Throwable t) {
+            StaticLogger.write("ServletConfig: " + t);
+        }
+
+        // Check the ServletContext object
+        try {
+            ServletContext servletContext = getServletContext();
+            Method method = servletContext.getClass().getMethod
+                ("getResources", new Class[] {});
+            Object resources = method.invoke(servletContext,
+                                             new Object[] {});
+            results.append(" Can reflect on ServletContext/");
+        } catch (Throwable t) {
+            StaticLogger.write("ServletContext: " + t);
+        }
+
+        // Check the HttpServletRequest object
+        try {
+            Method method = request.getClass().getMethod
+                ("getInfo", new Class[] {});
+            Object info = method.invoke(request,
+                                        new Object[] {});
+            results.append(" Can reflect on HttpServletRequest/");
+        } catch (Throwable t) {
+            StaticLogger.write("HttpServletRequest: " + t);
+        }
+
+        // Check the HttpServletResponse object
+        try {
+            Method method = request.getClass().getMethod
+                ("getInfo", new Class[] {});
+            Object info = method.invoke(request,
+                                        new Object[] {});
+            results.append(" Can reflect on HttpServletResponse/");
+        } catch (Throwable t) {
+            StaticLogger.write("HttpServletResponse: " + t);
+        }
+
+        // Check the HttpSession object
+        try {
+            HttpSession session = request.getSession(true);
+            Method method = session.getClass().getMethod
+                ("getInfo", new Class[] {});
+            results.append(" Can reflect on HttpSession/");
+        } catch (Throwable t) {
+            StaticLogger.write("HttpSession: " + t);
+        }
+
+        // Check the RequestDispatcher object
+        try {
+            RequestDispatcher rd =
+                getServletContext().getRequestDispatcher("/index.shtml");
+            Method method = rd.getClass().getMethod
+                ("getInfo", new Class[] {});
+            results.append(" Can reflect on RequestDispatcher/");
+        } catch (Throwable t) {
+            StaticLogger.write("RequestDispatcher: " + t);
+        }
+
+        // Report final results
+        if (results.length() < 1)
+            writer.println("Reflection01 PASSED");
+        else {
+            writer.print("Reflection01 FAILED -");
+            writer.println(results.toString());
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Request01.java b/container/tester/src/tester/org/apache/tester/Request01.java
new file mode 100644
index 0000000..0a7017e
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Request01.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Request listener test.  Exercise various methods for dealing with
+ * servlet request attributes.  Leave an attribute named "request01"
+ * present, which should be erased after a web application restart.
+ *
+ * @author Justyna Horwat
+ * @version $Revision$ $Date$
+ */
+
+public class Request01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        boolean ok = true;
+        PrintWriter writer = response.getWriter();
+        ServletContext context = getServletContext();
+
+        // Ensure that there is no existing attribute
+        if (ok) {
+            if (request.getAttribute("request01") != null) {
+                writer.println("Request01 FAILED - Attribute already exists");
+                ok = false;
+            }
+        }
+
+        // Create and stash a request attribute
+        if (ok) {
+            ContextBean bean = new ContextBean();
+            bean.setStringProperty("Request01");
+            request.setAttribute("request01", bean);
+        }
+
+        // Ensure that we can retrieve the attribute successfully
+        if (ok) {
+            Object bean = request.getAttribute("request01");
+            if (bean == null) {
+                writer.println("Request01 FAILED - Cannot retrieve attribute");
+                ok = false;
+            }
+            if (ok) {
+                if (!(bean instanceof ContextBean)) {
+                    writer.println("Request01 FAILED - Bean instance of " +
+                                   bean.getClass().getName());
+                    ok = false;
+                }
+            }
+            if (ok) {
+                String value = ((ContextBean) bean).getStringProperty();
+                if (!"Request01".equals(value)) {
+                    writer.println("Request01 FAILED - Value = " + value);
+                    ok = false;
+                }
+            }
+            if (ok) {
+                String lifecycle = ((ContextBean) bean).getLifecycle();
+                if (!"/add".equals(lifecycle)) {
+                    writer.println("Request01 FAILED - Bean lifecycle is " +
+                                   lifecycle);
+                    ok = false;
+                }
+            }
+        }
+
+        // Ensure that we can update this attribute and check its lifecycle
+        if (ok) {
+            ContextBean bean = (ContextBean) request.getAttribute("request01");
+            request.setAttribute("request01", bean);
+            String lifecycle = bean.getLifecycle();
+            if (!"/add/rep".equals(lifecycle)) {
+                writer.println("Request01 FAILED - Bean lifecycle is " +
+                               lifecycle);
+                ok = false;
+            }
+        }
+
+        // Ensure that we can remove this attribute and check its lifecycle
+        if (ok) {
+            ContextBean bean = (ContextBean) request.getAttribute("request01");
+            request.removeAttribute("request01");
+            String lifecycle = bean.getLifecycle();
+            if (!"/add/rep/rem".equals(lifecycle)) {
+                writer.println("Request01 FAILED - Bean lifecycle is " +
+                               lifecycle);
+                ok = false;
+            }
+        }
+
+        // Add a bean back for the restart application test
+        request.setAttribute("request01", new ContextBean());
+
+        // Ensure that setAttribute("name", null) works correctly
+        if (ok) {
+            request.setAttribute("FOO", "BAR");
+            request.setAttribute("FOO", null);
+            if (request.getAttribute("FOO") != null) {
+                writer.println("Request01 FAILED - setAttribute(name,null)");
+                ok = false;
+            }
+        }
+
+        // Report success if everything is still ok
+        if (ok)
+            writer.println("Request01 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/RequestListener01.java b/container/tester/src/tester/org/apache/tester/RequestListener01.java
new file mode 100644
index 0000000..c75693a
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/RequestListener01.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Application event listener for request events.  All events that occur
+ * are logged appropriately to the static logger..
+ *
+ * @author Justyna Horwat
+ * @version $Revision$ $Date$
+ */
+
+public class RequestListener01
+    implements ServletRequestAttributeListener, ServletRequestListener {
+
+
+    public void attributeAdded(ServletRequestAttributeEvent event) {
+        StaticLogger.write("RequestListener01: attributeAdded(" +
+                           event.getName() + "," + event.getValue() + ")");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("RequestListener01: attributeAdded(" +
+                    event.getName() + "," + event.getValue() + ")");
+        if (event.getValue() instanceof ContextBean) {
+            ContextBean bean = (ContextBean) event.getValue();
+            bean.setLifecycle(bean.getLifecycle() + "/add");
+        }
+    }
+
+    public void attributeRemoved(ServletRequestAttributeEvent event) {
+        StaticLogger.write("RequestListener01: attributeRemoved(" +
+                           event.getName() + "," + event.getValue() + ")");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("RequestListener01: attributeRemoved(" +
+                    event.getName() + "," + event.getValue() + ")");
+        if (event.getValue() instanceof ContextBean) {
+            ContextBean bean = (ContextBean) event.getValue();
+            bean.setLifecycle(bean.getLifecycle() + "/rem");
+        }
+    }
+
+    public void attributeReplaced(ServletRequestAttributeEvent event) {
+        StaticLogger.write("RequestListener01: attributeReplaced(" +
+                           event.getName() + "," + event.getValue() + ")");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("RequestListener01: attributeReplaced(" +
+                    event.getName() + "," + event.getValue() + ")");
+        if (event.getValue() instanceof ContextBean) {
+            ContextBean bean = (ContextBean) event.getValue();
+            bean.setLifecycle(bean.getLifecycle() + "/rep");
+        }
+    }
+
+    public void requestDestroyed(ServletRequestEvent event) {
+        StaticLogger.write("RequestListener01: requestDestroyed() -- probably cached from previous request");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("RequestListener01: requestDestroyed()");
+    }
+
+    public void requestInitialized(ServletRequestEvent event) {
+        StaticLogger.write("RequestListener01: requestInitialized()");
+        ServletContext context = (ServletContext) event.getSource();
+        context.log("RequestListener01: requestInitialized()");
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Reset01.java b/container/tester/src/tester/org/apache/tester/Reset01.java
new file mode 100644
index 0000000..b12aaa9
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Reset01.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for ServletResponse.reset().
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Reset01 extends GenericServlet {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process a servlet request and create the corresponding response.
+     *
+     * @param request The request we are processing
+     * @param response The response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void service(ServletRequest request, ServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        try {
+            writer.println("Reset01 FAILED - Did not reset buffer");
+            response.reset();
+            response.setContentType("text/plain");
+            writer.println("Reset01 PASSED");
+        } catch (IllegalStateException e) {
+            writer.println("Reset01 FAILED - Threw IllegaStateException");
+            e.printStackTrace(writer);
+        }
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Resources01.java b/container/tester/src/tester/org/apache/tester/Resources01.java
new file mode 100644
index 0000000..dddb7bc
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Resources01.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for <code>ServletContext.getResource()</code> as well as
+ * <code>ClassLoader.getResource()</code>.  Operation is controlled by query
+ * parameters:
+ * <ul>
+ * <li><strong>mode</strong> - Use <code>context</code> for servlet context
+ *     test, or <code>class</code> for class loader test.  [context]</li>
+ * <li><strong>path</strong> - Resource path to the requested resource,
+ *     starting with a slash.  [/WEB-INF/web.xml]</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Resources01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Identify our configuration parameters
+        String mode = request.getParameter("mode");
+        if (mode == null)
+            mode = "context";
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/WEB-INF/web.xml";
+
+        // Execute the desired test
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        URL url = null;
+        try {
+            if ("context".equals(mode))
+                url = getServletContext().getResource(path);
+            else
+                url = this.getClass().getResource(path);
+            if (url == null)
+                writer.println("Resources01 FAILED - No URL was returned");
+            else {
+                writer.println("Resources01 PASSED");
+                writer.println("url = " + url.toString());
+            }
+        } catch (MalformedURLException e) {
+            writer.println("Resources01 FAILED - MalformedURLException: "
+                           + e);
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Resources01.txt b/container/tester/src/tester/org/apache/tester/Resources01.txt
new file mode 100644
index 0000000..675de58
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Resources01.txt
@@ -0,0 +1 @@
+Resources01 PASSED
diff --git a/container/tester/src/tester/org/apache/tester/Resources02.java b/container/tester/src/tester/org/apache/tester/Resources02.java
new file mode 100644
index 0000000..0e00c16
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Resources02.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Negative test for <code>ServletContext.getResource()</code> as well as
+ * <code>ClassLoader.getResource()</code>.  Operation is controlled by query
+ * parameters:
+ * <ul>
+ * <li><strong>mode</strong> - Use <code>context</code> for servlet context
+ *     test, or <code>class</code> for class loader test.  [context]</li>
+ * <li><strong>path</strong> - Resource path to the requested resource,
+ *     starting with a slash.  [/WEB-INF/web.xml]</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Resources02 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Identify our configuration parameters
+        String mode = request.getParameter("mode");
+        if (mode == null)
+            mode = "context";
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/WEB-INF/web.xml";
+
+        // Execute the desired test
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        URL url = null;
+        try {
+            if ("context".equals(mode))
+                url = getServletContext().getResource(path);
+            else
+                url = this.getClass().getResource(path);
+            if (url == null)
+                writer.println("Resources02 PASSED");
+            else
+                writer.println("Resources02 FAILED - Returned URL " +
+                               url.toString());
+        } catch (MalformedURLException e) {
+            writer.println("Resources02 FAILED - MalformedURLException: "
+                           + e);
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Resources03.java b/container/tester/src/tester/org/apache/tester/Resources03.java
new file mode 100644
index 0000000..03505fb
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Resources03.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for <code>ServletContext.getResourceAsStream()</code> as well
+ * as <code>ClassLoader.getResourceAsStream()</code>.  Operation is controlled
+ * by query parameters:
+ * <ul>
+ * <li><strong>mode</strong> - Use <code>context</code> for servlet context
+ *     test, or <code>class</code> for class loader test.  [context]</li>
+ * <li><strong>path</strong> - Resource path to the requested resource,
+ *     starting with a slash.  [/WEB-INF/web.xml]</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Resources03 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Identify our configuration parameters
+        String mode = request.getParameter("mode");
+        if (mode == null)
+            mode = "context";
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/WEB-INF/web.xml";
+
+        // Execute the desired test
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        InputStream is = null;
+        URL url = null;
+        try {
+            if ("context".equals(mode)) {
+                is = getServletContext().getResourceAsStream(path);
+                url = getServletContext().getResource(path);
+            } else {
+                is = this.getClass().getResourceAsStream(path);
+                url = this.getClass().getResource(path);
+            }
+            if (url == null) {
+                if (is == null)
+                    writer.println("Resources03 FAILED - No IS or URL was returned");
+                else
+                    writer.println("Resources03 FAILED - Returned IS but no URL");
+            } else {
+                if (is == null)
+                    writer.println("Resources03 FAILED - Returned URL but no IS");
+                else {
+                    InputStreamReader isr = new InputStreamReader(is);
+                    while (true) {
+                        int c = isr.read();
+                        if (c < 0)
+                            break;
+                        char ch = (char) c;
+                        if (ch < ' ')
+                            break;
+                        writer.print(ch);
+                    }
+                    isr.close();
+                }
+                writer.println();
+                writer.println("url = " + url.toString());
+            }
+        } catch (MalformedURLException e) {
+            writer.println("Resources03 FAILED - MalformedURLException: "
+                           + e);
+        } catch (IOException e) {
+            writer.println("Resources03 FAILED - IOException: " + e);
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Resources03.txt b/container/tester/src/tester/org/apache/tester/Resources03.txt
new file mode 100644
index 0000000..770b484
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Resources03.txt
@@ -0,0 +1 @@
+Resources03 PASSED
diff --git a/container/tester/src/tester/org/apache/tester/Resources04.java b/container/tester/src/tester/org/apache/tester/Resources04.java
new file mode 100644
index 0000000..6f4f19a
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Resources04.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Negative test for <code>ServletContext.getResourceAsStream()</code> as well
+ * as <code>ClassLoader.getResourceAsStream()</code>.  Operation is controlled
+ * by query parameters:
+ * <ul>
+ * <li><strong>mode</strong> - Use <code>context</code> for servlet context
+ *     test, or <code>class</code> for class loader test.  [context]</li>
+ * <li><strong>path</strong> - Resource path to the requested resource,
+ *     starting with a slash.  [/WEB-INF/web.xml]</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Resources04 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Identify our configuration parameters
+        String mode = request.getParameter("mode");
+        if (mode == null)
+            mode = "context";
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/WEB-INF/web.xml";
+
+        // Execute the desired test
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        InputStream is = null;
+        URL url = null;
+        try {
+            if ("context".equals(mode)) {
+                is = getServletContext().getResourceAsStream(path);
+                url = getServletContext().getResource(path);
+            } else {
+                is = this.getClass().getResourceAsStream(path);
+                url = this.getClass().getResource(path);
+            }
+            if (is == null) {
+                if (url == null)
+                    writer.println("Resources04 PASSED");
+                else
+                    writer.println("Resources04 FAILED - Stream is null but URL is " + url);
+            } else {
+                if (url != null)
+                    writer.println("Resources04 FAILED - Stream is not null and  URL is " + url);
+                else
+                    writer.println("Resources04 FAILED - Stream is not null and URL is null");
+            }
+        } catch (MalformedURLException e) {
+            writer.println("Resources04 FAILED - MalformedURLException: "
+                           + e);
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Resources05.java b/container/tester/src/tester/org/apache/tester/Resources05.java
new file mode 100644
index 0000000..9f4cb0b
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Resources05.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for <code>ServletContext.getResource()</code> as well as
+ * <code>ClassLoader.getResource()</code>.  The URL returned by
+ * these calls is then opened with <code>url.openStream()</code>
+ * in order to ensure that the correct data is actually read.
+ * Operation is controlled by query parameters:
+ * <ul>
+ * <li><strong>mode</strong> - Use <code>context</code> for servlet context
+ *     test, or <code>class</code> for class loader test.  [context]</li>
+ * <li><strong>path</strong> - Resource path to the requested resource,
+ *     starting with a slash.  [/WEB-INF/web.xml]</li>
+ * <li><strong>stringify</strong> - If set to any arbitrary value, the URL
+ *     returned by getResource() will be converted to a String and then back
+ *     to a URL before being opened.  [not set]</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Resources05 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Identify our configuration parameters
+        String mode = request.getParameter("mode");
+        if (mode == null)
+            mode = "context";
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/WEB-INF/web.xml";
+        boolean stringify = (request.getParameter("stringify") != null);
+
+        // Prepare for the desired test
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        InputStream is = null;
+        InputStreamReader isr = null;
+        URL url = null;
+        StringBuffer results = new StringBuffer();
+        boolean ok = true;
+
+        // Acquire the appropriate URL
+        try {
+            if ("context".equals(mode))
+                url = getServletContext().getResource(path);
+            else
+                url = this.getClass().getResource(path);
+            if (url == null) {
+                results.append(" No URL returned");
+                ok = false;
+            }
+        } catch (MalformedURLException e) {
+            results.append(" getResource MalformedURLException");
+            ok = false;
+        }
+
+        // Stringify the URL if requested
+        try {
+            if (ok) {
+                StaticLogger.write("Stringifying the URL");
+                String urlString = url.toString();
+                url = new URL(urlString);
+            }
+        } catch (MalformedURLException e) {
+            results.append(" stringify MalformedURLException");
+        }
+
+        // Open an input stream and input stream reader on this URL
+        try {
+            if (ok) {
+                is = url.openStream();
+                isr = new InputStreamReader(is);
+            }
+        } catch (IOException e) {
+            results.append(" Open IOException: " + e);
+            ok = false;
+        }
+
+        // Copy the contents of this stream to our output
+        try {
+            if (ok) {
+                while (true) {
+                    int ch = isr.read();
+                    if (ch < 0)
+                        break;
+                    writer.print((char) ch);
+                }
+            }
+        } catch (IOException e) {
+            results.append(" Copy IOException: " + e);
+            ok = false;
+        }
+
+        // Close the input stream
+        try {
+            if (ok) {
+                isr.close();
+            }
+        } catch (IOException e) {
+            results.append(" Close IOException: " + e);
+        }
+
+        // Report any failures we have encountered
+        if (!ok) {
+            writer.print("Resources05 FAILED -");
+            writer.println(results.toString());
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Resources05.txt b/container/tester/src/tester/org/apache/tester/Resources05.txt
new file mode 100644
index 0000000..80b04f1
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Resources05.txt
@@ -0,0 +1 @@
+Resources05 PASSED
diff --git a/container/tester/src/tester/org/apache/tester/Resources06.java b/container/tester/src/tester/org/apache/tester/Resources06.java
new file mode 100644
index 0000000..5e18df2
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Resources06.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for <code>getResourcePaths()</code>, which will specify the
+ * directory path indicated by the <strong>path</strong> request parameter.
+ * For known paths, at least the known set of included resources must be
+ * found in order to pass.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Resources06 extends HttpServlet {
+
+    // Resource Lists (incomplete) for known directory paths
+    String rootPaths[] =
+    { "/ErrorPage06.html", "/ErrorPage06.jsp", "/Forward01.txt",
+      "/Include01.txt", "/WEB-INF/", "/Xerces00.jsp", "/Xerces01.xml",
+      "/Xerces02.jsp", "/golden/", "/includeme.txt", "/index.shtml",
+      "/ssidir/" };
+
+    String goldenPaths[] =
+    { "/golden/Golden01.txt", "/golden/SSIConfig01.txt",
+      "/golden/SSIConfig03.txt", "/golden/SSIFsize02.txt",
+      "/golden/SSIInclude01.txt", "/golden/SSIInclude02.txt",
+      "/golden/Session05.txt" };
+
+    String webinfPaths[] =
+    { "/WEB-INF/classes/", "/WEB-INF/lib/", "/WEB-INF/web.xml" };
+
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare our output writer
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Identify our configuration parameters
+        StringBuffer sb = new StringBuffer();
+        String path = request.getParameter("path");
+        if (path == null)
+            path = "/";
+        String paths[] = null;
+        if ("/".equals(path))
+            paths = rootPaths;
+        else if ("/golden".equals(path))
+            paths = goldenPaths;
+        else if ("/WEB-INF".equals(path))
+            paths = webinfPaths;
+        else {
+            sb.append(" Unknown path '");
+            sb.append(path);
+            sb.append("'/");
+        }
+        int counts[] = null;
+        if (paths != null)
+            counts = new int[paths.length];
+
+        // Request the set of resources in the specified path
+        StaticLogger.write("Processing path '" + path + "'");
+        String first = null;
+        Set set = getServletContext().getResourcePaths(path);
+        if (set == null) {
+            sb.append(" No resources returned/");
+            set = new HashSet();
+        }
+
+        // Count the occurrences of the resources we know about
+        Iterator resources = set.iterator();
+        while (resources.hasNext()) {
+            String resource = (String) resources.next();
+            if (first == null)
+                first = resource;
+            StaticLogger.write("Found resource '" + resource + "'");
+            for (int i = 0; i < paths.length; i++) {
+                if (paths[i].equals(resource)) {
+                    counts[i]++;
+                    break;
+                }
+            }
+        }
+
+        // Report on any missing or duplicated resources
+        for (int i = 0; i < paths.length; i++) {
+            if (counts[i] < 1) {
+                sb.append(" Missing resource '");
+                sb.append(paths[i]);
+                sb.append("'/");
+            } else if (counts[i] > 2) {
+                sb.append(" Resource '");
+                sb.append(paths[i]);
+                sb.append("' occurred ");
+                sb.append(counts[i]);
+                sb.append(" times/");
+            }
+        }
+
+        // Verify that the returned set is immutable
+        try {
+            String newElement = "NEW FOO";
+            set.add(newElement);
+            if (set.contains(newElement))
+              sb.append(" Set allowed add()/");
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            if (first != null) {
+                set.remove(first);
+                if (!set.contains(first))
+                  sb.append(" Set allowed remove()/");
+            }
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            set.clear();
+            if (set.size() == 0)
+                sb.append(" Set allowed clear()/");
+        } catch (Throwable t) {
+            ;
+        }
+
+
+        // Report any failures we have encountered
+        if (sb.length() > 0) {
+            writer.print("Resources06 FAILED -");
+            writer.println(sb.toString());
+        } else {
+            writer.println("Resources06 PASSED");
+        }
+
+        // Add wrapper messages as required
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ResponseWrap01.java b/container/tester/src/tester/org/apache/tester/ResponseWrap01.java
new file mode 100644
index 0000000..0e22d81
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ResponseWrap01.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Basis for testing wrapped responses with combinations of forwarding to
+ * or including both servlets and JSP pages.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ResponseWrap01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Acquire request parameters
+        String type = request.getParameter("type");
+        String page = request.getParameter("page");
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        //	PrintWriter writer = response.getWriter();
+
+        // Forward or include as requested
+        RequestDispatcher rd =
+            getServletContext().getRequestDispatcher(page);
+        if (rd == null) {
+            PrintWriter writer = response.getWriter();
+            writer.println("ResponseWrap01 FAILED - No request dispatcher" +
+                           " for " + page);
+        } else if ("F".equals(type)) {
+            HttpServletResponseWrapper wrapper =
+                new CharArrayResponse(response);
+            rd.forward(request, wrapper);
+            wrapper.flushBuffer();
+        } else {
+            HttpServletResponseWrapper wrapper =
+                new CharArrayResponse(response);
+            rd.include(request, wrapper);
+            wrapper.flushBuffer();
+        }
+
+        // No filter wrapping for this test series
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ResponseWrap01a.java b/container/tester/src/tester/org/apache/tester/ResponseWrap01a.java
new file mode 100644
index 0000000..e83122b
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ResponseWrap01a.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Basis for testing wrapped responses with combinations of forwarding to
+ * or including both servlets and JSP pages.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ResponseWrap01a extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("ResponseWrap01a PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/ResponseWrap01c.java b/container/tester/src/tester/org/apache/tester/ResponseWrap01c.java
new file mode 100644
index 0000000..cce00fc
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/ResponseWrap01c.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Basis for testing wrapped responses with combinations of forwarding to
+ * or including both servlets and JSP pages.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ResponseWrap01c extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare this response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.println("ResponseWrap01c PASSED");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Session01.java b/container/tester/src/tester/org/apache/tester/Session01.java
new file mode 100644
index 0000000..8b388ee
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Session01.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import org.apache.tester.shared.SharedSessionBean;
+import org.apache.tester.unpshared.UnpSharedSessionBean;
+import org.apache.tester.unshared.UnsharedSessionBean;
+
+
+/**
+ * Part 1 of Session Tests.  Ensures that there is no current session, then
+ * creates a new session and sets a session attribute.  Also, ensure that
+ * calling setAttribute("name", null) acts like removeAttribute().
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Session01 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Ensure that there is no current session
+        boolean ok = true;
+        HttpSession session = request.getSession(false);
+        if (session != null) {
+            writer.println("Session01 FAILED - Requested existing session " +
+                           session.getId());
+            ok = false;
+        }
+
+        // Create a new session
+        if (ok) {
+            session = request.getSession(true);
+            if (session == null) {
+                writer.println("Session01 FAILED - No session created");
+                ok = false;
+            }
+        }
+
+        // Store an activation event listener in the session
+        if (ok) {
+            session.setAttribute("activationListener",
+                                 new SessionListener03());
+        }
+
+        // Ensure that there is no existing attribute
+        if (ok) {
+            if (session.getAttribute("sessionBean") != null) {
+                writer.println("Session01 FAILED - Attribute already exists");
+                ok = false;
+            }
+        }
+
+        // Create and stash a session attribute
+        if (ok) {
+            SessionBean bean = new SessionBean();
+            bean.setStringProperty("Session01");
+            session.setAttribute("sessionBean", bean);
+        }
+
+        // Ensure that we can retrieve the attribute successfully
+        if (ok) {
+            Object bean = session.getAttribute("sessionBean");
+            if (bean == null) {
+                writer.println("Session01 FAILED - Cannot retrieve attribute");
+                ok = false;
+            } else if (!(bean instanceof SessionBean)) {
+                writer.println("Session01 FAILED - Attribute instance of " +
+                               bean.getClass().getName());
+                ok = false;
+            } else {
+                String value = ((SessionBean) bean).getStringProperty();
+                if (!"Session01".equals(value)) {
+                    writer.println("Session01 FAILED - Property = " + value);
+                    ok = false;
+                }
+            }
+        }
+
+        // Ensure that setAttribute("name", null) works correctly
+        if (ok) {
+            session.setAttribute("FOO", "BAR");
+            session.setAttribute("FOO", null);
+            if (session.getAttribute("FOO") != null) {
+                writer.println("Session01 FAILED - setAttribute(name,null)");
+                ok = false;
+            }
+        }
+
+        // Create more beans that will be used to test application restart
+        if (ok) {
+            SharedSessionBean ssb = new SharedSessionBean();
+            ssb.setStringProperty("Session01");
+            session.setAttribute("sharedSessionBean", ssb);
+            UnpSharedSessionBean ussb = new UnpSharedSessionBean();
+            ussb.setStringProperty("Session01");
+            session.setAttribute("unpSharedSessionBean", ussb);
+            UnsharedSessionBean usb = new UnsharedSessionBean();
+            usb.setStringProperty("Session01");
+            session.setAttribute("unsharedSessionBean", usb);
+        }
+
+        // Report success if everything is still ok
+        if (ok)
+            writer.println("Session01 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Session02.java b/container/tester/src/tester/org/apache/tester/Session02.java
new file mode 100644
index 0000000..e5e92e7
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Session02.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 2 of Session Tests.  Ensures that there is an existing session, and
+ * that the session bean stashed in Part 1 can be retrieved successfully.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Session02 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Ensure that there is a current session
+        boolean ok = true;
+        HttpSession session = request.getSession(false);
+        if (session == null) {
+            writer.println("Session02 FAILED - No existing session " +
+                           request.getRequestedSessionId());
+            ok = false;
+        }
+
+        // Ensure that we can retrieve the attribute successfully
+        if (ok) {
+            Object bean = session.getAttribute("sessionBean");
+            if (bean == null) {
+                writer.println("Session02 FAILED - Cannot retrieve attribute");
+                ok = false;
+            } else if (!(bean instanceof SessionBean)) {
+                writer.println("Session02 FAILED - Attribute instance of " +
+                               bean.getClass().getName());
+                ok = false;
+            } else {
+                String value = ((SessionBean) bean).getStringProperty();
+                if (!"Session01".equals(value)) {
+                    writer.println("Session02 FAILED - Property = " + value);
+                    ok = false;
+                }
+            }
+        }
+
+        // Report success if everything is still ok
+        if (ok)
+            writer.println("Session02 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Session03.java b/container/tester/src/tester/org/apache/tester/Session03.java
new file mode 100644
index 0000000..f2bd60b
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Session03.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import org.apache.tester.shared.SharedSessionBean;
+import org.apache.tester.unpshared.UnpSharedSessionBean;
+import org.apache.tester.unshared.UnsharedSessionBean;
+
+
+/**
+ * Part 3 of Session Tests.  Ensures that there is an existing session, and
+ * that the session bean stashed in Part 1 can be retrieved successfully.
+ * Then, it removes that attribute and verifies successful removal.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Session03 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Ensure that there is a current session
+        boolean ok = true;
+        HttpSession session = request.getSession(false);
+        if (session == null) {
+            writer.println("Session03 FAILED - No existing session " +
+                           request.getRequestedSessionId());
+            ok = false;
+        }
+
+        // Ensure that we can retrieve the attribute successfully
+	SessionBean bean = null;
+        if (ok) {
+            Object object = session.getAttribute("sessionBean");
+            if (object == null) {
+                writer.println("Session03 FAILED - Cannot retrieve attribute");
+                ok = false;
+            } else if (!(object instanceof SessionBean)) {
+                writer.println("Session03 FAILED - Attribute instance of " +
+                               object.getClass().getName());
+                ok = false;
+            } else {
+                bean = (SessionBean) object;
+                String value = bean.getStringProperty();
+                if (!"Session01".equals(value)) {
+                    writer.println("Session03 FAILED - Property = " + value);
+                    ok = false;
+                }
+            }
+        }
+
+        // Remove the attribute and guarantee that this was successful
+        if (ok) {
+            session.removeAttribute("sessionBean");
+            if (session.getAttribute("sessionBean") != null) {
+                writer.println("Session03 FAILED - Removal failed");
+                ok = false;
+            }
+        }
+
+	// Validate the bean lifecycle of this bean
+	if (ok) {
+	    String lifecycle = bean.getLifecycle();
+	    if (!"/vb/swp/sda/vu".equals(lifecycle)) {
+	        writer.println("Session03 FAILED - Invalid bean lifecycle '" +
+			       lifecycle + "'");
+		ok = false;
+	    }
+	}
+
+        // Retrieve and validate the shared session bean
+        SharedSessionBean ssb = null;
+        if (ok) {
+            Object object = session.getAttribute("sharedSessionBean");
+            if (object == null) {
+                writer.println("Session03 FAILED - Cannot retrieve ssb");
+                ok = false;
+            } else if (!(object instanceof SharedSessionBean)) {
+                writer.println("Session03 FAILED - Shared attribute class "
+                               + object.getClass().getName());
+                ok = false;
+            } else {
+                ssb = (SharedSessionBean) object;
+                String value = ssb.getStringProperty();
+                if (!"Session01".equals(value)) {
+                    writer.println("Session03 FAILED - Shared property ="
+                                   + value);
+                    ok = false;
+                } else {
+                    session.removeAttribute("sharedSessionBean");
+                    String lifecycle = ssb.getLifecycle();
+                    if (!"/vb/swp/sda/vu".equals(lifecycle)) {
+                        writer.println("Session03 FAILED - Shared lifecycle ="
+                                       + lifecycle);
+                        ok = false;
+                    }
+                }
+            }
+        }
+
+        // Retrieve and validate the unpacked shared session bean
+        UnpSharedSessionBean ussb = null;
+        if (ok) {
+            Object object = session.getAttribute("unpSharedSessionBean");
+            if (object == null) {
+                writer.println("Session03 FAILED - Cannot retrieve ussb");
+                ok = false;
+            } else if (!(object instanceof UnpSharedSessionBean)) {
+                writer.println("Session03 FAILED - unpShared attribute class "
+                               + object.getClass().getName());
+                ok = false;
+            } else {
+                ussb = (UnpSharedSessionBean) object;
+                String value = ussb.getStringProperty();
+                if (!"Session01".equals(value)) {
+                    writer.println("Session03 FAILED - unpShared property ="
+                                   + value);
+                    ok = false;
+                } else {
+                    session.removeAttribute("unpSharedSessionBean");
+                    String lifecycle = ssb.getLifecycle();
+                    if (!"/vb/swp/sda/vu".equals(lifecycle)) {
+                        writer.println("Session03 FAILED - unpShared lifecycle ="
+                                       + lifecycle);
+                        ok = false;
+                    }
+                }
+            }
+        }
+
+        // Retrieve and validate the unshared session bean
+        UnsharedSessionBean usb = null;
+        if (ok) {
+            Object object = session.getAttribute("unsharedSessionBean");
+            if (object == null) {
+                writer.println("Session03 FAILED - Cannot retrieve usb");
+                ok = false;
+            } else if (!(object instanceof UnsharedSessionBean)) {
+                writer.println("Session03 FAILED - Unshared attribute class "
+                               + object.getClass().getName());
+                ok = false;
+            } else {
+                usb = (UnsharedSessionBean) object;
+                String value = usb.getStringProperty();
+                if (!"Session01".equals(value)) {
+                    writer.println("Session03 FAILED - Unshared property = "
+                                   + value);
+                    ok = false;
+                } else {
+                    session.removeAttribute("unsharedSessionBean");
+                    String lifecycle = usb.getLifecycle();
+                    if (!"/vb/swp/sda/vu".equals(lifecycle)) {
+                        writer.println("Session03 FAILED - Unshared lifecycle"
+                                       + " = " + lifecycle);
+                        ok = false;
+                    }
+                }
+            }
+        }
+
+
+        // Report success if everything is still ok
+        if (ok)
+            writer.println("Session03 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Session04.java b/container/tester/src/tester/org/apache/tester/Session04.java
new file mode 100644
index 0000000..25b9869
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Session04.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 4 of Session Tests.  Ensures that there is an existing session, and
+ * that the requested session information matches it.  Also, ensure that we
+ * can invalidate this session and create a new one (with a different session
+ * identifier) while processing this request.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Session04 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        log("Session04 - Starting, requestedSessionId = " +
+            request.getRequestedSessionId());
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+
+        // Ensure that there is a current session
+        StringBuffer results = new StringBuffer();
+        HttpSession oldSession = request.getSession(false);
+        if (oldSession == null)
+            results.append(" No existing session/");
+
+        // Acquire the session identifier of the old session
+        String oldSessionId = null;
+        if (oldSession != null) {
+            try {
+                oldSessionId = oldSession.getId();
+            } catch (IllegalStateException e) {
+                results.append(" Old session is expired/");
+            }
+        }
+
+        // Match against the requested session identifier
+        String requestedSessionId = null;
+        if (oldSessionId != null) {
+            requestedSessionId = request.getRequestedSessionId();
+            if (requestedSessionId == null) {
+                results.append(" No requested session id/");
+            } else {
+                if (!request.isRequestedSessionIdValid())
+                    results.append(" Requested session id is not valid/");
+                if (!oldSessionId.equals(requestedSessionId)) {
+                    results.append(" Requested session=");
+                    results.append(requestedSessionId);
+                    results.append(" Old session=");
+                    results.append(oldSessionId);
+                    results.append("/");
+                }
+            }
+        }
+
+        // Verify that we received the requested session identifier correctly
+        if (requestedSessionId != null) {
+            if (!request.isRequestedSessionIdFromCookie())
+                results.append(" Requested session not from cookie/");
+            if (request.isRequestedSessionIdFromURL())
+                results.append(" Requested session from URL/");
+        }
+
+        // Verify that we can create an attribute in the old session
+        if (oldSession != null) {
+            SessionBean bean = new SessionBean();
+            bean.setStringProperty("Session04");
+            oldSession.setAttribute("sessionBean", bean);
+        }
+
+        // Verify that we can invalidate the old session
+        if (oldSession != null) {
+            try {
+                oldSession.invalidate();
+            } catch (IllegalStateException e) {
+                results.append(" Old session is already invalidated/");
+            }
+        }
+
+        // Verify that we can create a new session
+        HttpSession newSession = request.getSession(true);
+        if (newSession == null) {
+            results.append(" Cannot create new session/");
+        } else {
+            String newSessionId = null;
+            try {
+                newSessionId = newSession.getId();
+            } catch (IllegalStateException e) {
+                results.append(" New session is already invalidated/");
+            }
+            if ((oldSession != null) && (newSession != null)) {
+                if (oldSession == newSession)
+                    results.append(" oldSession == newSession/");
+                if (oldSession.equals(newSession))
+                    results.append(" oldSession equals newSession/");
+            }
+            if ((oldSessionId != null) && (newSessionId != null) &&
+                oldSessionId.equals(newSessionId)) {
+                results.append(" New session id = old session id/");
+            }
+        }
+
+        // Verify that the old session's attribute did not carry forward
+        if (newSession != null) {
+            SessionBean bean =
+                (SessionBean) newSession.getAttribute("sessionBean");
+            if (bean != null)
+                results.append(" New session has attribute already/");
+        }
+
+        // Store an activation event listener in the session
+        newSession.setAttribute("activationListener",
+                                    new SessionListener03());
+
+        // Report success if everything is still ok
+        if (results.length() == 0)
+            writer.println("Session04 PASSED");
+        else {
+            writer.print("Session04 FAILED -");
+            writer.println(results.toString());
+        }
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+        log("Session04 - Stopping");
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Session05.java b/container/tester/src/tester/org/apache/tester/Session05.java
new file mode 100644
index 0000000..bad75c6
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Session05.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 5 of Session Tests.  Ensures that the appropriate session listener
+ * events get called in the appropriate order.  Relies on proper configuration
+ * of SessionListener01 and SessionListener02 in the web.xml file
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Session05 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Reset the static logger to ensure no leftovers exist
+        StaticLogger.reset();
+
+        // Perform session management activities to trigger listener events
+        HttpSession session = request.getSession(true);
+        session.setAttribute("attribute1", "value1");
+        session.setAttribute("attribute1", "value2");
+        session.removeAttribute("attribute1");
+        session.removeAttribute("attribute2"); // Not present, so no logging
+        session.invalidate();
+
+        // Render the response (to be compared as a golden file)
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Session06.java b/container/tester/src/tester/org/apache/tester/Session06.java
new file mode 100644
index 0000000..790388f
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Session06.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Part 6 of Session Tests.  Ensures that an attempt to create a new session
+ * after the response has been committed throws IllegalStateException.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Session06 extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Prepare and commit our response
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        writer.print("Session06 ");
+        response.flushBuffer();
+
+        // Attempt to create a new session
+        try {
+            HttpSession session = request.getSession(true);
+            if (session == null)
+                writer.println("FAILED - Did not throw IllegalStateException");
+            else
+                writer.println("FAILED - Returned new session");
+        } catch (IllegalStateException e) {
+            writer.println("PASSED");
+        } catch (Throwable t) {
+            writer.println("FAILED - Threw " + t);
+            t.printStackTrace(writer);
+        }
+
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+        response.flushBuffer();
+
+    }
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/SessionBean.java b/container/tester/src/tester/org/apache/tester/SessionBean.java
new file mode 100644
index 0000000..7d0bc8a
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/SessionBean.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.Serializable;
+import java.sql.Date;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionEvent;
+
+
+/**
+ * Simple JavaBean to use for session attribute tests.  It is Serializable
+ * so that instances can be saved and restored across server restarts.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SessionBean implements
+    HttpSessionActivationListener, HttpSessionBindingListener, Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * A date property for use with property editor tests.
+     */
+    protected Date dateProperty =
+        new Date(System.currentTimeMillis());
+
+    public Date getDateProperty() {
+        return (this.dateProperty);
+    }
+
+    public void setDateProperty(Date dateProperty) {
+        this.dateProperty = dateProperty;
+    }
+
+
+    /**
+     * The lifecycle events that have happened on this bean instance.
+     */
+    protected String lifecycle = "";
+
+    public String getLifecycle() {
+        return (this.lifecycle);
+    }
+
+    public void setLifecycle(String lifecycle) {
+        this.lifecycle = lifecycle;
+    }
+
+
+    /**
+     * A string property.
+     */
+    protected String stringProperty = "Default String Property Value";
+
+    public String getStringProperty() {
+        return (this.stringProperty);
+    }
+
+    public void setStringProperty(String stringProperty) {
+        this.stringProperty = stringProperty;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a string representation of this bean.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SessionBean[lifecycle=");
+        sb.append(this.lifecycle);
+        sb.append(",dateProperty=");
+        sb.append(dateProperty);
+        sb.append(",stringProperty=");
+        sb.append(this.stringProperty);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ---------------------------------- HttpSessionActivationListener Methods
+
+
+    /**
+     * Receive notification that this session was activated.
+     *
+     * @param event The session event that has occurred
+     */
+    public void sessionDidActivate(HttpSessionEvent event) {
+
+        lifecycle += "/sda";
+
+    }
+
+
+    /**
+     * Receive notification that this session will be passivated.
+     *
+     * @param event The session event that has occurred
+     */
+    public void sessionWillPassivate(HttpSessionEvent event) {
+
+        lifecycle += "/swp";
+
+    }
+
+
+    // ------------------------------------- HttpSessionBindingListener Methods
+
+
+    /**
+     * Receive notification that this attribute has been bound.
+     *
+     * @param event The session event that has occurred
+     */
+    public void valueBound(HttpSessionBindingEvent event) {
+
+        lifecycle += "/vb";
+
+    }
+
+
+    /**
+     * Receive notification that this attribute has been unbound.
+     *
+     * @param event The session event that has occurred
+     */
+    public void valueUnbound(HttpSessionBindingEvent event) {
+
+        lifecycle += "/vu";
+
+    }
+
+
+}
+
diff --git a/container/tester/src/tester/org/apache/tester/SessionListener01.java b/container/tester/src/tester/org/apache/tester/SessionListener01.java
new file mode 100644
index 0000000..d183cbd
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/SessionListener01.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Application event listener for session events.  All events that occur
+ * are logged appropriately to the static logger.  In addition, session
+ * creation and destruction events are logged to the servlet context log.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SessionListener01
+    implements HttpSessionListener, HttpSessionAttributeListener {
+
+
+    public void attributeAdded(HttpSessionBindingEvent event) {
+        StaticLogger.write("SessionListener01: attributeAdded(" +
+                           event.getName() + "," + event.getValue() + ")");
+        event.getSession().getServletContext().log
+            ("SessionListener01: attributeAdded(" + event.getSession().getId()
+             + "," + event.getName() + ")");
+    }
+
+    public void attributeRemoved(HttpSessionBindingEvent event) {
+        StaticLogger.write("SessionListener01: attributeRemoved(" +
+                           event.getName() + "," + event.getValue() + ")");
+        event.getSession().getServletContext().log
+            ("SessionListener01: attributeRemoved(" +
+             event.getSession().getId() + "," + event.getName() + ")");
+    }
+
+    public void attributeReplaced(HttpSessionBindingEvent event) {
+        StaticLogger.write("SessionListener01: attributeReplaced(" +
+                           event.getName() + "," + event.getValue() + ")");
+        event.getSession().getServletContext().log
+            ("SessionListener01: attributeReplaced(" +
+             event.getSession().getId() + "," + event.getName() + ")");
+    }
+
+    public void sessionCreated(HttpSessionEvent event) {
+        StaticLogger.write("SessionListener01: sessionCreated()");
+        HttpSession session = event.getSession();
+        session.getServletContext().log("SessionListener01: sessionCreated(" +
+                                        session.getId() + ")");
+    }
+
+    public void sessionDestroyed(HttpSessionEvent event) {
+        StaticLogger.write("SessionListener01: sessionDestroyed()");
+        HttpSession session = event.getSession();
+        session.getServletContext().log("SessionListener01: sessionDestroyed("
+                                        + session.getId() + ")");
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/SessionListener02.java b/container/tester/src/tester/org/apache/tester/SessionListener02.java
new file mode 100644
index 0000000..5c35a2c
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/SessionListener02.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Application event listener for session events.  All events that occur
+ * are logged appropriately to the static logger.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SessionListener02
+    implements HttpSessionListener, HttpSessionAttributeListener {
+
+
+    public void attributeAdded(HttpSessionBindingEvent event) {
+        StaticLogger.write("SessionListener02: attributeAdded(" +
+                           event.getName() + "," + event.getValue() + ")");
+    }
+
+    public void attributeRemoved(HttpSessionBindingEvent event) {
+        StaticLogger.write("SessionListener02: attributeRemoved(" +
+                           event.getName() + "," + event.getValue() + ")");
+    }
+
+    public void attributeReplaced(HttpSessionBindingEvent event) {
+        StaticLogger.write("SessionListener02: attributeReplaced(" +
+                           event.getName() + "," + event.getValue() + ")");
+    }
+
+    public void sessionCreated(HttpSessionEvent event) {
+        StaticLogger.write("SessionListener02: sessionCreated()");
+    }
+
+    public void sessionDestroyed(HttpSessionEvent event) {
+        StaticLogger.write("SessionListener02: sessionDestroyed()");
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/SessionListener03.java b/container/tester/src/tester/org/apache/tester/SessionListener03.java
new file mode 100644
index 0000000..d93eb38
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/SessionListener03.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1999, 2000, 2001, 2002 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Session attribute that listens to passivation and activation events.
+ * All events that occur are logged to the servlet context log.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SessionListener03
+    implements HttpSessionActivationListener, HttpSessionBindingListener,
+               Serializable {
+
+    public void sessionDidActivate(HttpSessionEvent event) {
+        event.getSession().getServletContext().log
+            ("SessionListener03: sessionDidActivate(" +
+             event.getSession().getId() + ")");
+    }
+
+    public void sessionWillPassivate(HttpSessionEvent event) {
+        event.getSession().getServletContext().log
+            ("SessionListener03: sessionWillPassivate(" +
+             event.getSession().getId() + ")");
+    }
+
+    public void valueBound(HttpSessionBindingEvent event) {
+        event.getSession().getServletContext().log
+            ("SessionListener03: valueBound(" +
+             event.getSession().getId() + "," +
+             event.getName() + ")");
+    }
+
+    public void valueUnbound(HttpSessionBindingEvent event) {
+        event.getSession().getServletContext().log
+            ("SessionListener03: valueUnbound(" +
+             event.getName() + ")");
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/SetBufferSize01.java b/container/tester/src/tester/org/apache/tester/SetBufferSize01.java
new file mode 100644
index 0000000..0a067db
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/SetBufferSize01.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Negative test for ServletResponse.setBufferSize().
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SetBufferSize01 extends GenericServlet {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process a servlet request and create the corresponding response.
+     *
+     * @param request The request we are processing
+     * @param response The response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void service(ServletRequest request, ServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        try {
+            writer.print("SetBufferSize01 ");
+            response.flushBuffer();
+            response.setBufferSize(100);
+            writer.println("FAILED - Did not throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            writer.println("PASSED");
+        } catch (IOException e) {
+            writer.println("FAILED - flushBuffer() threw IOException");
+            throw e;
+        }
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/SetLocale01.java b/container/tester/src/tester/org/apache/tester/SetLocale01.java
new file mode 100644
index 0000000..ba87a99
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/SetLocale01.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Positive test for ServletResponse.setLocale().
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SetLocale01 extends GenericServlet {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process a servlet request and create the corresponding response.
+     *
+     * @param request The request we are processing
+     * @param response The response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void service(ServletRequest request, ServletResponse response)
+        throws IOException, ServletException {
+
+        response.setContentType("text/plain");
+        response.setLocale(new Locale("en", "US"));
+        PrintWriter writer = response.getWriter();
+        writer.println("SetLocale01 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/StaticFilter.java b/container/tester/src/tester/org/apache/tester/StaticFilter.java
new file mode 100644
index 0000000..da78780
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/StaticFilter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Simple filter to reset the static log at the beginning of each request,
+ * so that no leftovers from the previous request are inadvertently included.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class StaticFilter implements Filter {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The filter configuration object for this filter.
+     */
+    protected FilterConfig config = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Release this Filter instance from service.
+     */
+    public void destroy() {
+
+        config = null;
+
+    }
+
+
+    /**
+     * Wrap this request and/or response as configured and pass it on.
+     */
+    public void doFilter(ServletRequest inRequest, ServletResponse inResponse,
+                         FilterChain chain)
+        throws IOException, ServletException {
+
+        // Reset our logger and perform this request
+        StaticLogger.reset();
+        chain.doFilter(inRequest, inResponse);
+
+    }
+
+
+    /**
+     * Place this Filter instance into service.
+     *
+     * @param config The filter configuration object
+     */
+    public void init(FilterConfig config) throws ServletException {
+
+        this.config = config;
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/StaticLogger.java b/container/tester/src/tester/org/apache/tester/StaticLogger.java
new file mode 100644
index 0000000..32e917c
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/StaticLogger.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Logger that uses a static message buffer to facilitate intra-web-app
+ * recording and retrieval of log messages.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class StaticLogger {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The set of messages that have been logged.
+     */
+    protected static ArrayList messages = new ArrayList();
+
+
+    /**
+     * The index of the next message that will be retrieved by a read() call.
+     */
+    protected static int position = 0;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the next message that has been logged, or <code>null</code>
+     * if there are no more messages.
+     */
+    public static String read() {
+
+        synchronized (messages) {
+            if (position < messages.size())
+                return ((String) messages.get(position++));
+            else
+                return (null);
+        }
+
+    }
+
+
+    /**
+     * Reset the messages buffer and position.
+     */
+    public static void reset() {
+
+        synchronized (messages) {
+            messages.clear();
+            position = 0;
+        }
+
+    }
+
+
+    /**
+     * Write a new message to the end of the messages buffer.
+     *
+     * @param message The message to be added
+     */
+    public static void write(String message) {
+
+        synchronized (messages) {
+            messages.add(message);
+        }
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/TestClient.java b/container/tester/src/tester/org/apache/tester/TestClient.java
new file mode 100644
index 0000000..9ce90c4
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/TestClient.java
@@ -0,0 +1,1046 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.ConnectException;
+import java.net.HttpURLConnection;
+import java.net.Socket;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+
+/**
+ * <p>This class contains a <strong>Task</strong> for Ant that is used to
+ * send HTTP requests to a servlet container, and examine the responses.
+ * It is similar in purpose to the <code>GTest</code> task in Watchdog,
+ * but uses the JDK's HttpURLConnection for underlying connectivity.</p>
+ *
+ * <p>The task is registered with Ant using a <code>taskdef</code> directive:
+ * <pre>
+ *   &lt;taskdef name="tester" classname="org.apache.tester.TestClient"&gt;
+ * </pre>
+ * and accepts the following configuration properties:</p>
+ * <ul>
+ * <li><strong>golden</strong> - The server-relative path of the static
+ *     resource containing the golden file for this request.</li>
+ * <li><strong>host</strong> - The server name to which this request will be
+ *     sent.  Defaults to <code>localhost</code> if not specified.</li>
+ * <li><strong>inContent</strong> - The data content that will be submitted
+ *     with this request.  The test client will transparently add a carriage
+ *     return and line feed, and set the content length header, if this is
+ *     specified.  Otherwise, no content will be included in the request.</li>
+ * <li><strong>inHeaders</strong> - The set of one or more HTTP headers that
+ *     will be included on the request.</li>
+ * <li><strong>message</strong> - The HTTP response message that is expected
+ *     in the response from the server.  No check is made if no message
+ *     is specified.</li>
+ * <li><strong>method</strong> - The HTTP request method to be used on this
+ *     request.  Defaults to <ocde>GET</code> if not specified.</li>
+ * <li><strong>outContent</strong> - The first line of the response data
+ *     content that we expect to receive.  No check is made if no content is
+ *     specified.</li>
+ * <li><strong>outHeaders</strong> - The set of one or more HTTP headers that
+ *     are expected in the response (order independent).</li>
+ * <li><strong>port</strong> - The port number to which this request will be
+ *     sent.  Defaults to <code>8080</code> if not specified.</li>
+ * <li><strong>redirect</strong> - If set to true, follow any redirect that
+ *     is returned by the server.  (Only works when using HttpURLConnection).
+ *     </li>
+ * <li><strong>request</strong> - The request URI to be transmitted for this
+ *     request.  This value should start with a slash character ("/"), and
+ *     be the server-relative URI of the requested resource.</li>
+ * <li><strong>status</strong> - The HTTP status code that is expected in the
+ *     response from the server.  Defaults to <code>200</code> if not
+ *     specified.  Set to zero to disable checking the return value.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TestClient extends Task {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The saved golden file we will compare to the response.  Each element
+     * contains a line of text without any line delimiters.
+     */
+    protected ArrayList saveGolden = new ArrayList();
+
+
+    /**
+     * The saved headers we received in our response.  The key is the header
+     * name (converted to lower case), and the value is an ArrayList of the
+     * string value(s) received for that header.
+     */
+    protected HashMap saveHeaders = new HashMap();
+
+
+    /**
+     * The response file to be compared to the golden file.  Each element
+     * contains a line of text without any line delimiters.
+     */
+    protected ArrayList saveResponse = new ArrayList();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The debugging detail level for this execution.
+     */
+    protected int debug = 0;
+
+    public int getDebug() {
+        return (this.debug);
+    }
+
+    public void setDebug(int debug) {
+        this.debug = debug;
+    }
+
+
+    /**
+     * The server-relative request URI of the golden file for this request.
+     */
+    protected String golden = null;
+
+    public String getGolden() {
+        return (this.golden);
+    }
+
+    public void setGolden(String golden) {
+        this.golden = golden;
+    }
+
+
+    /**
+     * The host name to which we will connect.
+     */
+    protected String host = "localhost";
+
+    public String getHost() {
+        return (this.host);
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+
+    /**
+     * The first line of the request data that will be included on this
+     * request.
+     */
+    protected String inContent = null;
+
+    public String getInContent() {
+        return (this.inContent);
+    }
+
+    public void setInContent(String inContent) {
+        this.inContent = inContent;
+    }
+
+
+    /**
+     * The HTTP headers to be included on the request.  Syntax is
+     * <code>{name}:{value}[##{name}:{value}] ...</code>.
+     */
+    protected String inHeaders = null;
+
+    public String getInHeaders() {
+        return (this.inHeaders);
+    }
+
+    public void setInHeaders(String inHeaders) {
+        this.inHeaders = inHeaders;
+    }
+
+
+    /**
+     * Should we join the session whose session identifier was returned
+     * on the previous request.
+     */
+    protected boolean joinSession = false;
+
+    public boolean getJoinSession() {
+        return (this.joinSession);
+    }
+
+    public void setJoinSession(boolean joinSession) {
+        this.joinSession = true;
+    }
+
+
+    /**
+     * The HTTP response message to be expected in the response.
+     */
+    protected String message = null;
+
+    public String getMessage() {
+        return (this.message);
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+
+    /**
+     * The HTTP request method that will be used.
+     */
+    protected String method = "GET";
+
+    public String getMethod() {
+        return (this.method);
+    }
+
+    public void setMethod(String method) {
+        this.method = method;
+    }
+
+
+    /**
+     * The first line of the response data content that we expect to receive.
+     */
+    protected String outContent = null;
+
+    public String getOutContent() {
+        return (this.outContent);
+    }
+
+    public void setOutContent(String outContent) {
+        this.outContent = outContent;
+    }
+
+
+    /**
+     * The HTTP headers to be checked on the response.  Syntax is
+     * <code>{name}:{value}[##{name}:{value}] ...</code>.
+     */
+    protected String outHeaders = null;
+
+    public String getOutHeaders() {
+        return (this.outHeaders);
+    }
+
+    public void setOutHeaders(String outHeaders) {
+        this.outHeaders = outHeaders;
+    }
+
+
+    /**
+     * The port number to which we will connect.
+     */
+    protected int port = 8080;
+
+    public int getPort() {
+        return (this.port);
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+
+    /**
+     * The protocol and version to include in the request, if executed as
+     * a direct socket connection.  Lack of a value here indicates that an
+     * HttpURLConnection should be used instead.
+     */
+    protected String protocol = null;
+
+    public String getProtocol() {
+        return (this.protocol);
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+
+    /**
+     * Should we follow redirects returned by the server?
+     */
+    protected boolean redirect = false;
+
+    public boolean getRedirect() {
+        return (this.redirect);
+    }
+
+    public void setRedirect(boolean redirect) {
+        this.redirect = redirect;
+    }
+
+
+    /**
+     * The request URI to be sent to the server.  This value is required.
+     */
+    protected String request = null;
+
+    public String getRequest() {
+        return (this.request);
+    }
+
+    public void setRequest(String request) {
+        this.request = request;
+    }
+
+
+    /**
+     * The HTTP status code expected on the response.
+     */
+    protected int status = 200;
+
+    public int getStatus() {
+        return (this.status);
+    }
+
+    public void setStatus(int status) {
+        this.status = status;
+    }
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The session identifier returned by the most recent request, or
+     * <code>null</code> if the previous request did not specify a session
+     * identifier.
+     */
+    protected static String sessionId = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the test that has been configured by our property settings.
+     *
+     * @exception BuildException if an exception occurs
+     */
+    public void execute() throws BuildException {
+
+        saveHeaders.clear();
+        try {
+            readGolden();
+        } catch (IOException e) {
+            log("FAIL:  readGolden(" + golden + ")");
+            e.printStackTrace(System.out);
+        }
+        if ((protocol == null) || (protocol.length() == 0))
+            executeHttp();
+        else
+            executeSocket();
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Execute the test via use of an HttpURLConnection.
+     *
+     * @exception BuildException if an exception occurs
+     */
+    protected void executeHttp() throws BuildException {
+
+        // Construct a summary of the request we will be sending
+        String summary = "[" + method + " " + request + "]";
+        if (debug >= 1)
+            log("RQST: " + summary);
+        boolean success = true;
+        String result = null;
+        Throwable throwable = null;
+        HttpURLConnection conn = null;
+
+        try {
+
+            // Configure an HttpURLConnection for this request
+            URL url = new URL("http", host, port, request);
+            conn = (HttpURLConnection) url.openConnection();
+            conn.setAllowUserInteraction(false);
+            conn.setDoInput(true);
+            if (inContent != null) {
+                conn.setDoOutput(true);
+                conn.setRequestProperty("Content-Length",
+                                        "" + inContent.length());
+                if (debug >= 1)
+                    log("INPH: Content-Length: " +
+                                       inContent.length());
+            } else {
+                conn.setDoOutput(false);
+            }
+
+            // Send the session id cookie (if any)
+            if (joinSession && (sessionId != null)) {
+                conn.setRequestProperty("Cookie",
+                                        "JSESSIONID=" + sessionId);
+                if (debug >= 1)
+                    log("INPH: Cookie: JSESSIONID=" +
+                                       sessionId);
+            }
+
+            if (this.redirect && (debug >= 1))
+                log("FLAG: setInstanceFollowRedirects(" +
+                                   this.redirect + ")");
+            conn.setInstanceFollowRedirects(this.redirect);
+            conn.setRequestMethod(method);
+            if (inHeaders != null) {
+                String headers = inHeaders;
+                while (headers.length() > 0) {
+                    int delimiter = headers.indexOf("##");
+                    String header = null;
+                    if (delimiter < 0) {
+                        header = headers;
+                        headers = "";
+                    } else {
+                        header = headers.substring(0, delimiter);
+                        headers = headers.substring(delimiter + 2);
+                    }
+                    int colon = header.indexOf(":");
+                    if (colon < 0)
+                        break;
+                    String name = header.substring(0, colon).trim();
+                    String value = header.substring(colon + 1).trim();
+                    conn.setRequestProperty(name, value);
+                    if (debug >= 1)
+                        log("INPH: " + name + ": " + value);
+                }
+            }
+
+            // Connect to the server and send our output if necessary
+            conn.connect();
+            if (inContent != null) {
+                if (debug >= 1)
+                    log("INPD: " + inContent);
+                OutputStream os = conn.getOutputStream();
+                for (int i = 0; i < inContent.length(); i++)
+                    os.write(inContent.charAt(i));
+                os.close();
+            }
+
+            // Acquire the response data, if there is any
+            String outData = "";
+            String outText = "";
+            boolean eol = false;
+            InputStream is = conn.getInputStream();
+            int lines = 0;
+            while (true) {
+                String line = read(is);
+                if (line == null)
+                    break;                
+                if (lines == 0)
+                    outData = line;
+                else
+                    outText += line + "\r\n";
+                saveResponse.add(line);
+                lines++;
+            }
+            is.close();
+
+            // Dump out the response stuff
+            if (debug >= 1)
+                log("RESP: " + conn.getResponseCode() + " " +
+                                   conn.getResponseMessage());
+            for (int i = 1; i < 1000; i++) {
+                String name = conn.getHeaderFieldKey(i);
+                String value = conn.getHeaderField(i);
+                if ((name == null) || (value == null))
+                    break;
+                if (debug >= 1)
+                    log("HEAD: " + name + ": " + value);
+                save(name, value);
+                if ("Set-Cookie".equals(name))
+                    parseSession(value);
+            }
+            if (debug >= 1) {
+                log("DATA: " + outData);
+                if (outText.length() > 2)
+                    log("TEXT: " + outText);
+            }
+
+            // Validate the response against our criteria
+            if (success) {
+                result = validateStatus(conn.getResponseCode());
+                if (result != null)
+                    success = false;
+            }
+            if (success) {
+                result = validateMessage(conn.getResponseMessage());
+                if (result != null)
+                    success = false;
+            }
+            if (success) {
+                result = validateHeaders();
+                if (result != null)
+                    success = false;
+            }
+            if (success) {
+                result = validateData(outData);
+                if (result != null)
+                    success = false;
+            }
+            if (success) {
+                result = validateGolden();
+                if (result != null)
+                    success = false;
+            }
+
+        } catch (Throwable t) {
+            if (t instanceof FileNotFoundException) {
+                if (status == 404) {
+                    success = true;
+                    result = "Not Found";
+                    throwable = null;
+                } else {
+                    success = false;
+                    try {
+                        result = "Status=" + conn.getResponseCode() +
+                            ", Message=" + conn.getResponseMessage();
+                    } catch (IOException e) {
+                        result = e.toString();
+                    }
+                    throwable = null;
+                }
+            } else if (t instanceof ConnectException) {
+                success = false;
+                result = t.getMessage();
+                throwable = null;
+            } else {
+                success = false;
+                result = t.getMessage();
+                throwable = t;
+            }
+        }
+
+        // Log the results of executing this request
+        if (success)
+            log("OK " + summary);
+        else {
+            log("FAIL " + summary + " " + result);
+            if (throwable != null)
+                throwable.printStackTrace(System.out);
+        }
+
+    }
+
+
+    /**
+     * Execute the test via use of a socket with direct input/output.
+     *
+     * @exception BuildException if an exception occurs
+     */
+    protected void executeSocket() throws BuildException {
+
+        // Construct a summary of the request we will be sending
+        String command = method + " " + request + " " + protocol;
+        String summary = "[" + command + "]";
+        if (debug >= 1)
+            log("RQST: " + summary);
+        boolean success = true;
+        String result = null;
+        Socket socket = null;
+        OutputStream os = null;
+        PrintWriter pw = null;
+        InputStream is = null;
+        Throwable throwable = null;
+        int outStatus = 0;
+        String outMessage = null;
+
+        try {
+
+            // Open a client socket for this request
+            socket = new Socket(host, port);
+            os = socket.getOutputStream();
+            pw = new PrintWriter(os);
+            is = socket.getInputStream();
+
+            // Send the command and content length header (if any)
+            pw.print(command + "\r\n");
+            if (inContent != null) {
+                if (debug >= 1)
+                    log("INPH: " + "Content-Length: " +
+                                       inContent.length());
+                pw.print("Content-Length: " + inContent.length() + "\r\n");
+            }
+
+            // Send the session id cookie (if any)
+            if (joinSession && (sessionId != null)) {
+                pw.println("Cookie: JSESSIONID=" + sessionId);
+                if (debug >= 1)
+                    log("INPH: Cookie: JSESSIONID=" +
+                                       sessionId);
+            }
+
+            // Send the specified headers (if any)
+            if (inHeaders != null) {
+                String headers = inHeaders;
+                while (headers.length() > 0) {
+                    int delimiter = headers.indexOf("##");
+                    String header = null;
+                    if (delimiter < 0) {
+                        header = headers;
+                        headers = "";
+                    } else {
+                        header = headers.substring(0, delimiter);
+                        headers = headers.substring(delimiter + 2);
+                    }
+                    int colon = header.indexOf(":");
+                    if (colon < 0)
+                        break;
+                    String name = header.substring(0, colon).trim();
+                    String value = header.substring(colon + 1).trim();
+                    if (debug >= 1)
+                        log("INPH: " + name + ": " + value);
+                    pw.print(name + ": " + value + "\r\n");
+                }
+            }
+            pw.print("\r\n");
+
+            // Send our content (if any)
+            if (inContent != null) {
+                if (debug >= 1)
+                    log("INPD: " + inContent);
+                for (int i = 0; i < inContent.length(); i++)
+                    pw.print(inContent.charAt(i));
+            }
+            pw.flush();
+
+            // Read the response status and associated message
+            String line = read(is);
+            if (line == null) {
+                outStatus = -1;
+                outMessage = "NO RESPONSE";
+            } else {
+                line = line.trim();
+                if (debug >= 1)
+                    System.out.println("RESP: " + line);
+                int space = line.indexOf(" ");
+                if (space >= 0) {
+                    line = line.substring(space + 1).trim();
+                    space = line.indexOf(" ");
+                }
+                try {
+                    if (space < 0) {
+                        outStatus = Integer.parseInt(line);
+                        outMessage = "";
+                    } else {
+                        outStatus = Integer.parseInt(line.substring(0, space));
+                        outMessage = line.substring(space + 1).trim();
+                    }
+                } catch (NumberFormatException e) {
+                    outStatus = -1;
+                    outMessage = "NUMBER FORMAT EXCEPTION";
+                }
+            }
+            if (debug >= 1)
+                System.out.println("STAT: " + outStatus + " MESG: " +
+                                   outMessage);
+
+            // Read the response headers (if any)
+            String headerName = null;
+            String headerValue = null;
+            while (true) {
+                line = read(is);
+                if ((line == null) || (line.length() == 0))
+                    break;
+                int colon = line.indexOf(":");
+                if (colon < 0) {
+                    if (debug >= 1)
+                        System.out.println("????: " + line);
+                } else {
+                    headerName = line.substring(0, colon).trim();
+                    headerValue = line.substring(colon + 1).trim();
+                    if (debug >= 1)
+                        System.out.println("HEAD: " + headerName + ": " +
+                                           headerValue);
+                    save(headerName, headerValue);
+                    if ("Set-Cookie".equals(headerName))
+                        parseSession(headerValue);
+                }
+            }
+
+            // Acquire the response data (if any)
+            String outData = "";
+            String outText = "";
+            int lines = 0;
+            while (true) {
+                line = read(is);
+                if (line == null)
+                    break;                
+                if (lines == 0)
+                    outData = line;
+                else
+                    outText += line + "\r\n";
+                saveResponse.add(line);
+                lines++;
+            }
+            is.close();
+            if (debug >= 1) {
+                System.out.println("DATA: " + outData);
+                if (outText.length() > 2)
+                    System.out.println("TEXT: " + outText);
+            }
+
+            // Validate the response against our criteria
+            if (success) {
+                result = validateStatus(outStatus);
+                if (result != null)
+                    success = false;
+            }
+            if (success) {
+                result = validateMessage(message);
+                if (result != null)
+                    success = false;
+            }
+            if (success) {
+                result = validateHeaders();
+                if (result != null)
+                    success = false;
+            }
+            if (success) {
+                result = validateData(outData);
+                if (result != null)
+                    success = false;
+            }
+            if (success) {
+                result = validateGolden();
+                if (result != null)
+                    success = false;
+            }
+
+        } catch (Throwable t) {
+            success = false;
+            result = "Status=" + outStatus +
+                ", Message=" + outMessage;
+            throwable = null;
+        } finally {
+            if (pw != null) {
+                try {
+                    pw.close();
+                } catch (Throwable w) {
+                    ;
+                }
+            }
+            if (os != null) {
+                try {
+                    os.close();
+                } catch (Throwable w) {
+                    ;
+                }
+            }
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (Throwable w) {
+                    ;
+                }
+            }
+            if (socket != null) {
+                try {
+                    socket.close();
+                } catch (Throwable w) {
+                    ;
+                }
+            }
+        }
+
+        if (success)
+            System.out.println("OK " + summary);
+        else {
+            System.out.println("FAIL " + summary + " " + result);
+            if (throwable != null)
+                throwable.printStackTrace(System.out);
+        }
+
+    }
+
+
+    /**
+     * Parse the session identifier from the specified Set-Cookie value.
+     *
+     * @param value The Set-Cookie value to parse
+     */
+    protected void parseSession(String value) {
+
+        if (value == null)
+            return;
+        int equals = value.indexOf("JSESSIONID=");
+        if (equals < 0)
+            return;
+        value = value.substring(equals + "JSESSIONID=".length());
+        int semi = value.indexOf(";");
+        if (semi >= 0)
+            value = value.substring(0, semi);
+
+        if (debug >= 1)
+            System.out.println("SESSION ID: " + value);
+        sessionId = value;
+
+    }
+
+
+    /**
+     * Read and return the next line from the specified input stream, with
+     * no carriage return or line feed delimiters.  If
+     * end of file is reached, return <code>null</code> instead.
+     *
+     * @param stream The input stream to read from
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected String read(InputStream stream) throws IOException {
+
+        StringBuffer result = new StringBuffer();
+        while (true) {
+            int b = stream.read();
+            if (b < 0) {
+                if (result.length() == 0)
+                    return (null);
+                else
+                    break;
+            }
+            char c = (char) b;
+            if (c == '\r')
+                continue;
+            else if (c == '\n')
+                break;
+            else
+                result.append(c);
+        }
+        return (result.toString());
+
+    }
+
+
+    /**
+     * Read and save the contents of the golden file for this test, if any.
+     * Otherwise, the <code>saveGolden</code> list will be empty.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected void readGolden() throws IOException {
+
+        // Was a golden file specified?
+        saveGolden.clear();
+        if (golden == null)
+            return;
+
+        // Create a connection to receive the golden file contents
+        URL url = new URL("http", host, port, golden);
+        HttpURLConnection conn =
+            (HttpURLConnection) url.openConnection();
+        conn.setAllowUserInteraction(false);
+        conn.setDoInput(true);
+        conn.setDoOutput(false);
+        conn.setFollowRedirects(true);
+        conn.setRequestMethod("GET");
+
+        // Connect to the server and retrieve the golden file
+        conn.connect();
+        InputStream is = conn.getInputStream();
+        while (true) {
+            String line = read(is);
+            if (line == null)
+                break;
+            saveGolden.add(line);
+        }
+        is.close();
+        conn.disconnect();
+
+    }
+
+
+    /**
+     * Save the specified header name and value in our collection.
+     *
+     * @param name Header name to save
+     * @param value Header value to save
+     */
+    protected void save(String name, String value) {
+
+        String key = name.toLowerCase();
+        ArrayList list = (ArrayList) saveHeaders.get(key);
+        if (list == null) {
+            list = new ArrayList();
+            saveHeaders.put(key, list);
+        }
+        list.add(value);
+
+    }
+
+
+    /**
+     * Validate the output data against what we expected.  Return
+     * <code>null</code> for no problems, or an error message.
+     *
+     * @param data The output data to be tested
+     */
+    protected String validateData(String data) {
+
+        if (outContent == null)
+            return (null);
+        else if (data.startsWith(outContent))
+            return (null);
+        else
+            return ("Expected data '" + outContent + "', got data '" +
+                    data + "'");
+
+    }
+
+
+    /**
+     * Validate the response against the golden file (if any).  Return
+     * <code>null</code> for no problems, or an error message.
+     */
+    protected String validateGolden() {
+
+        if (golden == null)
+            return (null);
+        boolean ok = true;
+        if (saveGolden.size() != saveResponse.size())
+            ok = false;
+        if (ok) {
+            for (int i = 0; i < saveGolden.size(); i++) {
+                String golden = (String) saveGolden.get(i);
+                String response = (String) saveResponse.get(i);
+                if (!golden.equals(response)) {
+                    ok = false;
+                    break;
+                }
+            }
+        }
+        if (ok)
+            return (null);
+        System.out.println("EXPECTED: ======================================");
+        for (int i = 0; i < saveGolden.size(); i++)
+            System.out.println((String) saveGolden.get(i));
+        System.out.println("================================================");
+        System.out.println("RECEIVED: ======================================");
+        for (int i = 0; i < saveResponse.size(); i++)
+            System.out.println((String) saveResponse.get(i));
+        System.out.println("================================================");
+        return ("Failed Golden File Comparison");
+
+    }
+
+
+    /**
+     * Validate the saved headers against the <code>outHeaders</code>
+     * property, and return an error message if there is anything missing.
+     * If all of the expected headers are present, return <code>null</code>.
+     */
+    protected String validateHeaders() {
+
+        // Do we have any headers to check for?
+        if (outHeaders == null)
+            return (null);
+
+        // Check each specified name:value combination
+        String headers = outHeaders;
+        while (headers.length() > 0) {
+            // Parse the next name:value combination
+            int delimiter = headers.indexOf("##");
+            String header = null;
+            if (delimiter < 0) {
+                header = headers;
+                headers = "";
+            } else {
+                header = headers.substring(0, delimiter);
+                headers = headers.substring(delimiter + 2);
+            }
+            int colon = header.indexOf(":");
+            String name = header.substring(0, colon).trim();
+            String value = header.substring(colon + 1).trim();
+            // Check for the occurrence of this header
+            ArrayList list = (ArrayList) saveHeaders.get(name.toLowerCase());
+            if (list == null)
+                return ("Missing header name '" + name + "'");
+            boolean found = false;
+            for (int i = 0; i < list.size(); i++) {
+                if (value.equals((String) list.get(i))) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found)
+                return ("Missing header name '" + name + "' with value '" +
+                        value + "'");
+        }
+
+        // Everything was found successfully
+        return (null);
+
+    }
+
+
+    /**
+     * Validate the returned response message against what we expected.
+     * Return <code>null</code> for no problems, or an error message.
+     *
+     * @param message The returned response message
+     */
+    protected String validateMessage(String message) {
+
+        if (this.message == null)
+            return (null);
+        else if (this.message.equals(message))
+            return (null);
+        else
+            return ("Expected message='" + this.message + "', got message='" +
+                    message + "'");
+
+    }
+
+
+    /**
+     * Validate the returned status code against what we expected.  Return
+     * <code>null</code> for no problems, or an error message.
+     *
+     * @param status The returned status code
+     */
+    protected String validateStatus(int status) {
+
+        if (this.status == 0)
+            return (null);
+        if (this.status == status)
+            return (null);
+        else
+            return ("Expected status=" + this.status + ", got status=" +
+                    status);
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/TesterException.java b/container/tester/src/tester/org/apache/tester/TesterException.java
new file mode 100644
index 0000000..d4e8d4e
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/TesterException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Generic exception class to use for testing error page assertions.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TesterException extends Exception {
+
+
+    /**
+     * Construct an exception with no associated message.
+     */
+    public TesterException() {
+
+        super();
+
+    }
+
+
+    /**
+     * Construct an exception with the associated message.
+     *
+     * @param message The associated message
+     */
+    public TesterException(String message) {
+
+        super(message);
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/TesterHttpServletRequestWrapper.java b/container/tester/src/tester/org/apache/tester/TesterHttpServletRequestWrapper.java
new file mode 100644
index 0000000..2d43e45
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/TesterHttpServletRequestWrapper.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.security.Principal;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Tester request wrapper that logs all calls to the configured logger,
+ * before passing them on to the underlying request.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TesterHttpServletRequestWrapper
+    extends HttpServletRequestWrapper {
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Configure a new request wrapper.
+     *
+     * @param request The request we are wrapping
+     */
+    public TesterHttpServletRequestWrapper(HttpServletRequest request) {
+
+        super(request);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // For each public method, log the call and pass it to the wrapped response
+
+
+    public Object getAttribute(String name) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getAttribute()");
+        return (getRequest().getAttribute(name));
+    }
+
+
+    public Enumeration getAttributeNames() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getAttributeNames()");
+        return (getRequest().getAttributeNames());
+    }
+
+
+    public String getAuthType() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getAuthType()");
+        return (((HttpServletRequest) getRequest()).getAuthType());
+    }
+
+
+    public String getCharacterEncoding() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getCharacterEncoding()");
+        return (getRequest().getCharacterEncoding());
+    }
+
+
+    public int getContentLength() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getContentLength()");
+        return (getRequest().getContentLength());
+    }
+
+
+    public String getContentType() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getContentType()");
+        return (getRequest().getContentType());
+    }
+
+
+    public String getContextPath() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getContextPath()");
+        return (((HttpServletRequest) getRequest()).getContextPath());
+    }
+
+
+    public Cookie[] getCookies() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getCookies()");
+        return (((HttpServletRequest) getRequest()).getCookies());
+    }
+
+
+    public long getDateHeader(String name) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getDateHeader()");
+        return (((HttpServletRequest) getRequest()).getDateHeader(name));
+    }
+
+
+    public String getHeader(String name) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getHeader()");
+        return (((HttpServletRequest) getRequest()).getHeader(name));
+    }
+
+
+    public Enumeration getHeaders(String name) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getHeaders()");
+        return (((HttpServletRequest) getRequest()).getHeaders(name));
+    }
+
+
+    public Enumeration getHeaderNames() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getHeaderNames()");
+        return (((HttpServletRequest) getRequest()).getHeaderNames());
+    }
+
+
+    public ServletInputStream getInputStream() throws IOException {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getInputStream()");
+        return (getRequest().getInputStream());
+    }
+
+
+    public int getIntHeader(String name) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getIntHeader()");
+        return (((HttpServletRequest) getRequest()).getIntHeader(name));
+    }
+
+
+    public Locale getLocale() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getLocale()");
+        return (getRequest().getLocale());
+    }
+
+
+    public Enumeration getLocales() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getLocales()");
+        return (getRequest().getLocales());
+    }
+
+
+    public String getMethod() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getMethod()");
+        return (((HttpServletRequest) getRequest()).getMethod());
+    }
+
+
+    public String getParameter(String name) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getParameter()");
+        return (getRequest().getParameter(name));
+    }
+
+
+    public Map getParameterMap() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getParameterMap()");
+        return (getRequest().getParameterMap());
+    }
+
+
+    public Enumeration getParameterNames() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getParameterNames()");
+        return (getRequest().getParameterNames());
+    }
+
+
+    public String[] getParameterValues(String name) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getParameterValues()");
+        return (getRequest().getParameterValues(name));
+    }
+
+
+    public String getPathInfo() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getPathInfo()");
+        return (((HttpServletRequest) getRequest()).getPathInfo());
+    }
+
+
+    public String getPathTranslated() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getPathTranslated()");
+        return (((HttpServletRequest) getRequest()).getPathTranslated());
+    }
+
+
+    public String getProtocol() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getProtocol()");
+        return (getRequest().getProtocol());
+    }
+
+
+    public String getQueryString() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getQueryString()");
+        return (((HttpServletRequest) getRequest()).getQueryString());
+    }
+
+
+    public BufferedReader getReader() throws IOException {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getReader()");
+        return (getRequest().getReader());
+    }
+
+
+    public String getRealPath(String path) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getRealPath()");
+        return (getRequest().getRealPath(path));
+    }
+
+
+    public String getRemoteAddr() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getRemoteAddr()");
+        return (getRequest().getRemoteAddr());
+    }
+
+
+    public String getRemoteHost() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getRemoteHost()");
+        return (getRequest().getRemoteHost());
+    }
+
+
+    public String getRemoteUser() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getRemoteUser()");
+        return (((HttpServletRequest) getRequest()).getRemoteUser());
+    }
+
+
+    public RequestDispatcher getRequestDispatcher(String path) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getRequestDispatcher()");
+        return (getRequest().getRequestDispatcher(path));
+    }
+
+
+    public String getRequestURI() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getRequestURI()");
+        return (((HttpServletRequest) getRequest()).getRequestURI());
+    }
+
+
+    public StringBuffer getRequestURL() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getRequestURL()");
+        return (((HttpServletRequest) getRequest()).getRequestURL());
+    }
+
+
+    public String getRequestedSessionId() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getRequestedSessionId()");
+        return (((HttpServletRequest) getRequest()).getRequestedSessionId());
+    }
+
+
+    public String getScheme() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getScheme()");
+        return (getRequest().getScheme());
+    }
+
+
+    public String getServerName() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getServerName()");
+        return (getRequest().getServerName());
+    }
+
+
+    public int getServerPort() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getServerPort()");
+        return (getRequest().getServerPort());
+    }
+
+
+    public String getServletPath() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getServletPath()");
+        return (((HttpServletRequest) getRequest()).getServletPath());
+    }
+
+
+    public HttpSession getSession() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getSession()");
+        return (((HttpServletRequest) getRequest()).getSession());
+    }
+
+
+    public HttpSession getSession(boolean create) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getSession(b)");
+        return (((HttpServletRequest) getRequest()).getSession(create));
+    }
+
+
+    public Principal getUserPrincipal() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.getUserPrincipal()");
+        return (((HttpServletRequest) getRequest()).getUserPrincipal());
+    }
+
+
+    public boolean isRequestedSessionIdFromCookie() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.isRequestedSessionIdFromCookie()");
+        return (((HttpServletRequest) getRequest()).isRequestedSessionIdFromCookie());
+    }
+
+
+    public boolean isRequestedSessionIdFromUrl() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.isRequestedSessionIdFromUrl()");
+        return (((HttpServletRequest) getRequest()).isRequestedSessionIdFromUrl());
+    }
+
+
+    public boolean isRequestedSessionIdFromURL() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.isRequestedSessionIdFromURL()");
+        return (((HttpServletRequest) getRequest()).isRequestedSessionIdFromURL());
+    }
+
+
+    public boolean isRequestedSessionIdValid() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.isRequestedSessionIdValid()");
+        return (((HttpServletRequest) getRequest()).isRequestedSessionIdValid());
+    }
+
+
+    public boolean isSecure() {
+        StaticLogger.write("TesterHttpServletRequestWrapper.isSecure()");
+        return (getRequest().isSecure());
+    }
+
+
+    public boolean isUserInRole(String role) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.isUserInRole()");
+        return (((HttpServletRequest) getRequest()).isUserInRole(role));
+    }
+
+
+    public void removeAttribute(String name) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.removeAttribute()");
+        getRequest().removeAttribute(name);
+    }
+
+
+    public void setAttribute(String name, Object value) {
+        StaticLogger.write("TesterHttpServletRequestWrapper.setAttribute()");
+        getRequest().setAttribute(name, value);
+    }
+
+
+    public void setCharacterEncoding(String enc)
+        throws UnsupportedEncodingException {
+        StaticLogger.write("TesterHttpServletRequestWrapper.setCharacterEncoding()");
+        getRequest().setCharacterEncoding(enc);
+    }
+
+
+
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/TesterHttpServletResponseWrapper.java b/container/tester/src/tester/org/apache/tester/TesterHttpServletResponseWrapper.java
new file mode 100644
index 0000000..507b1cb
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/TesterHttpServletResponseWrapper.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Tester response wrapper that logs all calls to the configured logger,
+ * before passing them on to the underlying response.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TesterHttpServletResponseWrapper
+    extends HttpServletResponseWrapper {
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Configure a new response wrapper.
+     *
+     * @param response The response we are wrapping
+     */
+    public TesterHttpServletResponseWrapper(HttpServletResponse response) {
+
+        super(response);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // For each public method, log the call and pass it to the wrapped response
+
+
+    public void addCookie(Cookie cookie) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.addCookie()");
+        ((HttpServletResponse) getResponse()).addCookie(cookie);
+    }
+
+
+    public void addDateHeader(String name, long value) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.addDateHeader()");
+        ((HttpServletResponse) getResponse()).addDateHeader(name, value);
+    }
+
+
+    public void addHeader(String name, String value) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.addHeader()");
+        ((HttpServletResponse) getResponse()).addHeader(name, value);
+    }
+
+
+    public void addIntHeader(String name, int value) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.addIntHeader()");
+        ((HttpServletResponse) getResponse()).addIntHeader(name, value);
+    }
+
+
+    public boolean containsHeader(String name) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.containsHeader()");
+        return (((HttpServletResponse) getResponse()).containsHeader(name));
+    }
+
+
+    public String encodeURL(String url) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.encodeURL()");
+        return (((HttpServletResponse) getResponse()).encodeURL(url));
+    }
+
+
+    public String encodeUrl(String url) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.encodeUrl()");
+        return (((HttpServletResponse) getResponse()).encodeUrl(url));
+    }
+
+
+    public String encodeRedirectURL(String url) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.encodeRedirectURL()");
+        return (((HttpServletResponse) getResponse()).encodeRedirectURL(url));
+    }
+
+
+    public String encodeRedirectUrl(String url) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.encodeRedirectUrl()");
+        return (((HttpServletResponse) getResponse()).encodeRedirectUrl(url));
+    }
+
+
+    public void flushBuffer() throws IOException {
+        StaticLogger.write("TesterHttpServletResponseWrapper.flushBuffer()");
+        getResponse().flushBuffer();
+    }
+
+
+    public int getBufferSize() {
+        StaticLogger.write("TesterHttpServletResponseWrapper.getBufferSize()");
+        return (getResponse().getBufferSize());
+    }
+
+
+    public String getCharacterEncoding() {
+        StaticLogger.write("TesterHttpServletResponseWrapper.getCharacterEncoding()");
+        return (getResponse().getCharacterEncoding());
+    }
+
+
+    public Locale getLocale() {
+        StaticLogger.write("TesterHttpServletResponseWrapper.getLocale()");
+        return (getResponse().getLocale());
+    }
+
+
+    public ServletOutputStream getOutputStream() throws IOException {
+        StaticLogger.write("TesterHttpServletResponseWrapper.getOutputStream()");
+        return (getResponse().getOutputStream());
+    }
+
+
+    public PrintWriter getWriter() throws IOException {
+        StaticLogger.write("TesterHttpServletResponseWrapper.getWriter()");
+        return (getResponse().getWriter());
+    }
+
+
+    public boolean isCommitted() {
+        StaticLogger.write("TesterHttpServletResponseWrapper.isCommitted()");
+        return (getResponse().isCommitted());
+    }
+
+
+    public void reset() {
+        StaticLogger.write("TesterHttpServletResponseWrapper.reset()");
+        getResponse().reset();
+    }
+
+
+    public void resetBuffer() {
+        StaticLogger.write("TesterHttpServletResponseWrapper.resetBuffer()");
+        getResponse().resetBuffer();
+    }
+
+
+    public void sendError(int sc) throws IOException {
+        StaticLogger.write("TesterHttpServletResponseWrapper.sendError(i)");
+        ((HttpServletResponse) getResponse()).sendError(sc);
+    }
+
+
+    public void sendError(int sc, String msg) throws IOException {
+        StaticLogger.write("TesterHttpServletResponseWrapper.sendError(i,s)");
+        ((HttpServletResponse) getResponse()).sendError(sc, msg);
+    }
+
+
+    public void sendRedirect(String location) throws IOException {
+        StaticLogger.write("TesterHttpServletResponseWrapper.sendRedirect()");
+        ((HttpServletResponse) getResponse()).sendRedirect(location);
+    }
+
+
+    public void setBufferSize(int size) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.setBufferSize()");
+        getResponse().setBufferSize(size);
+    }
+
+
+    public void setContentLength(int len) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.setContentLength()");
+        getResponse().setContentLength(len);
+    }
+
+
+    public void setContentType(String type) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.setContentType()");
+        getResponse().setContentType(type);
+    }
+
+
+    public void setDateHeader(String name, long value) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.setDateHeader()");
+        ((HttpServletResponse) getResponse()).setDateHeader(name, value);
+    }
+
+
+    public void setHeader(String name, String value) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.setHeader()");
+        ((HttpServletResponse) getResponse()).setHeader(name, value);
+    }
+
+
+    public void setIntHeader(String name, int value) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.setIntHeader()");
+        ((HttpServletResponse) getResponse()).setIntHeader(name, value);
+    }
+
+
+    public void setLocale(Locale locale) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.setLocale()");
+        getResponse().setLocale(locale);
+    }
+
+
+    public void setStatus(int sc) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.setStatus(i)");
+        ((HttpServletResponse) getResponse()).setStatus(sc);
+    }
+
+
+    public void setStatus(int sc, String msg) {
+        StaticLogger.write("TesterHttpServletResponseWrapper.setStatus(i,s)");
+        ((HttpServletResponse) getResponse()).setStatus(sc, msg);
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/TesterServletRequestWrapper.java b/container/tester/src/tester/org/apache/tester/TesterServletRequestWrapper.java
new file mode 100644
index 0000000..5fd11ad
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/TesterServletRequestWrapper.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Tester request wrapper that logs all calls to the configured logger,
+ * before passing them on to the underlying request.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TesterServletRequestWrapper extends ServletRequestWrapper {
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Configure a new request wrapper.
+     *
+     * @param request The request we are wrapping
+     */
+    public TesterServletRequestWrapper(ServletRequest request) {
+
+        super(request);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // For each public method, log the call and pass it to the wrapped response
+
+
+    public Object getAttribute(String name) {
+        StaticLogger.write("TesterServletRequestWrapper.getAttribute()");
+        return (getRequest().getAttribute(name));
+    }
+
+
+    public Enumeration getAttributeNames() {
+        StaticLogger.write("TesterServletRequestWrapper.getAttributeNames()");
+        return (getRequest().getAttributeNames());
+    }
+
+
+    public String getCharacterEncoding() {
+        StaticLogger.write("TesterServletRequestWrapper.getCharacterEncoding()");
+        return (getRequest().getCharacterEncoding());
+    }
+
+
+    public int getContentLength() {
+        StaticLogger.write("TesterServletRequestWrapper.getContentLength()");
+        return (getRequest().getContentLength());
+    }
+
+
+    public String getContentType() {
+        StaticLogger.write("TesterServletRequestWrapper.getContentType()");
+        return (getRequest().getContentType());
+    }
+
+
+    public ServletInputStream getInputStream() throws IOException {
+        StaticLogger.write("TesterServletRequestWrapper.getInputStream()");
+        return (getRequest().getInputStream());
+    }
+
+
+    public Locale getLocale() {
+        StaticLogger.write("TesterServletRequestWrapper.getLocale()");
+        return (getRequest().getLocale());
+    }
+
+
+    public Enumeration getLocales() {
+        StaticLogger.write("TesterServletRequestWrapper.getLocales()");
+        return (getRequest().getLocales());
+    }
+
+
+    public String getParameter(String name) {
+        StaticLogger.write("TesterServletRequestWrapper.getParameter()");
+        return (getRequest().getParameter(name));
+    }
+
+
+    public Map getParameterMap() {
+        StaticLogger.write("TesterServletRequestWrapper.getParameterMap()");
+        return (getRequest().getParameterMap());
+    }
+
+
+    public Enumeration getParameterNames() {
+        StaticLogger.write("TesterServletRequestWrapper.getParameterNames()");
+        return (getRequest().getParameterNames());
+    }
+
+
+    public String[] getParameterValues(String name) {
+        StaticLogger.write("TesterServletRequestWrapper.getParameterValues()");
+        return (getRequest().getParameterValues(name));
+    }
+
+
+    public String getProtocol() {
+        StaticLogger.write("TesterServletRequestWrapper.getProtocol()");
+        return (getRequest().getProtocol());
+    }
+
+
+    public BufferedReader getReader() throws IOException {
+        StaticLogger.write("TesterServletRequestWrapper.getReader()");
+        return (getRequest().getReader());
+    }
+
+
+    public String getRealPath(String path) {
+        StaticLogger.write("TesterServletRequestWrapper.getRealPath()");
+        return (getRequest().getRealPath(path));
+    }
+
+
+    public String getRemoteAddr() {
+        StaticLogger.write("TesterServletRequestWrapper.getRemoteAddr()");
+        return (getRequest().getRemoteAddr());
+    }
+
+
+    public String getRemoteHost() {
+        StaticLogger.write("TesterServletRequestWrapper.getRemoteHost()");
+        return (getRequest().getRemoteHost());
+    }
+
+
+    public RequestDispatcher getRequestDispatcher(String path) {
+        StaticLogger.write("TesterServletRequestWrapper.getRequestDispatcher()");
+        return (getRequest().getRequestDispatcher(path));
+    }
+
+
+    public String getScheme() {
+        StaticLogger.write("TesterServletRequestWrapper.getScheme()");
+        return (getRequest().getScheme());
+    }
+
+
+    public String getServerName() {
+        StaticLogger.write("TesterServletRequestWrapper.getServerName()");
+        return (getRequest().getServerName());
+    }
+
+
+    public int getServerPort() {
+        StaticLogger.write("TesterServletRequestWrapper.getServerPort()");
+        return (getRequest().getServerPort());
+    }
+
+
+    public boolean isSecure() {
+        StaticLogger.write("TesterServletRequestWrapper.isSecure()");
+        return (getRequest().isSecure());
+    }
+
+
+    public void removeAttribute(String name) {
+        StaticLogger.write("TesterServletRequestWrapper.removeAttribute()");
+        getRequest().removeAttribute(name);
+    }
+
+
+    public void setAttribute(String name, Object value) {
+        StaticLogger.write("TesterServletRequestWrapper.setAttribute()");
+        getRequest().setAttribute(name, value);
+    }
+
+
+    public void setCharacterEncoding(String enc)
+        throws UnsupportedEncodingException {
+        StaticLogger.write("TesterServletRequestWrapper.setCharacterEncoding()");
+        getRequest().setCharacterEncoding(enc);
+    }
+
+
+
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/TesterServletResponseWrapper.java b/container/tester/src/tester/org/apache/tester/TesterServletResponseWrapper.java
new file mode 100644
index 0000000..891884c
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/TesterServletResponseWrapper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Tester response wrapper that logs all calls to the configured logger,
+ * before passing them on to the underlying response.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TesterServletResponseWrapper extends ServletResponseWrapper {
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Configure a new response wrapper.
+     *
+     * @param response The response we are wrapping
+     */
+    public TesterServletResponseWrapper(ServletResponse response) {
+
+        super(response);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // For each public method, log the call and pass it to the wrapped response
+
+
+    public void flushBuffer() throws IOException {
+        StaticLogger.write("TesterServletResponseWrapper.flushBuffer()");
+        getResponse().flushBuffer();
+    }
+
+
+    public int getBufferSize() {
+        StaticLogger.write("TesterServletResponseWrapper.getBufferSize()");
+        return (getResponse().getBufferSize());
+    }
+
+
+    public String getCharacterEncoding() {
+        StaticLogger.write("TesterServletResponseWrapper.getCharacterEncoding()");
+        return (getResponse().getCharacterEncoding());
+    }
+
+
+    public Locale getLocale() {
+        StaticLogger.write("TesterServletResponseWrapper.getLocale()");
+        return (getResponse().getLocale());
+    }
+
+
+    public ServletOutputStream getOutputStream() throws IOException {
+        StaticLogger.write("TesterServletResponseWrapper.getOutputStream()");
+        return (getResponse().getOutputStream());
+    }
+
+
+    public PrintWriter getWriter() throws IOException {
+        StaticLogger.write("TesterServletResponseWrapper.getWriter()");
+        return (getResponse().getWriter());
+    }
+
+
+    public boolean isCommitted() {
+        StaticLogger.write("TesterServletResponseWrapper.isCommitted()");
+        return (getResponse().isCommitted());
+    }
+
+
+    public void reset() {
+        StaticLogger.write("TesterServletResponseWrapper.reset()");
+        getResponse().reset();
+    }
+
+
+    public void resetBuffer() {
+        StaticLogger.write("TesterServletResponseWrapper.resetBuffer()");
+        getResponse().resetBuffer();
+    }
+
+
+    public void setBufferSize(int size) {
+        StaticLogger.write("TesterServletResponseWrapper.setBufferSize()");
+        getResponse().setBufferSize(size);
+    }
+
+
+    public void setContentLength(int len) {
+        StaticLogger.write("TesterServletResponseWrapper.setContentLength()");
+        getResponse().setContentLength(len);
+    }
+
+
+    public void setContentType(String type) {
+        StaticLogger.write("TesterServletResponseWrapper.setContentType()");
+        getResponse().setContentType(type);
+    }
+
+
+    public void setLocale(Locale locale) {
+        StaticLogger.write("TesterServletResponseWrapper.setLocale()");
+        getResponse().setLocale(locale);
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/UpperCaseFilter.java b/container/tester/src/tester/org/apache/tester/UpperCaseFilter.java
new file mode 100644
index 0000000..217e15f
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/UpperCaseFilter.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Filter that simply transforms its output to upper case.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UpperCaseFilter implements Filter {
+
+
+    private FilterConfig config = null;
+
+    public void destroy() {
+        ; // No action required
+    }
+
+    public void init(FilterConfig config) throws ServletException {
+        this.config = config;
+    }
+
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain)
+        throws IOException, ServletException {
+
+        HttpServletRequest wrequest =
+            new UpperCaseRequest((HttpServletRequest) request);
+        HttpServletResponse wresponse =
+            new UpperCaseResponse((HttpServletResponse) response);
+        StaticLogger.write("UpperCaseFilter.doFilter() begin");
+        chain.doFilter(wrequest, wresponse);
+        StaticLogger.write("UpperCaseFilter.doFilter() end");
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/UpperCaseInputStream.java b/container/tester/src/tester/org/apache/tester/UpperCaseInputStream.java
new file mode 100644
index 0000000..3814f63
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/UpperCaseInputStream.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * ServletInputStream that converts all characters to upper case.
+ * WARNING:  This will only work on 8-bit character sets!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UpperCaseInputStream extends ServletInputStream {
+
+    ServletInputStream stream = null;
+
+    public UpperCaseInputStream(ServletInputStream stream)
+      throws IOException {
+        super();
+        this.stream = stream;
+    }
+
+
+    public int read() throws IOException {
+        int c = stream.read();
+        if (c < 0)
+            return (c);
+        char ch = (char) c;
+        if (Character.isLowerCase(ch))
+            ch = Character.toUpperCase(ch);
+        return ((int) ch);
+    }
+
+    public int read(byte buf[], int off, int len) throws IOException {
+        int n = 0;
+        for (int i = off; i < (off + len); i++) {
+            int c = stream.read();
+            if (c < 0) {
+                if (n == 0)
+                    return (-1);
+                break;
+            }
+            char ch = (char) c;
+            if (Character.isLowerCase(ch))
+                ch = Character.toUpperCase(ch);
+            buf[i] = (byte) ch;
+            n++;
+        }
+        return (n);
+    }
+
+    public int read(byte buf[]) throws IOException {
+        return (read(buf, 0, buf.length));
+    }
+
+    public int readLine(byte buf[], int off, int len) throws IOException {
+        int n = 0;
+        for (int i = off; i < (off + len); i++) {
+            int c = stream.read();
+            if (c < 0) {
+                if (n == 0)
+                    return (-1);
+                break;
+            }
+            char ch = (char) c;
+            if (Character.isLowerCase(ch))
+                ch = Character.toUpperCase(ch);
+            buf[i] = (byte) ch;
+            n++;
+            if (ch == '\n')
+                break;
+        }
+        return (n);
+    }
+
+}
+
diff --git a/container/tester/src/tester/org/apache/tester/UpperCaseOutputStream.java b/container/tester/src/tester/org/apache/tester/UpperCaseOutputStream.java
new file mode 100644
index 0000000..ab4daa2
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/UpperCaseOutputStream.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * ServletOutputStream that converts all characters to upper case.
+ * WARNING:  This will only work on 8-bit character sets!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UpperCaseOutputStream extends ServletOutputStream {
+
+    ServletOutputStream stream = null;
+
+    public UpperCaseOutputStream(ServletOutputStream stream)
+      throws IOException {
+        super();
+        this.stream = stream;
+    }
+
+    public void write(int c) throws IOException {
+        char ch = (char) c;
+        if (Character.isLowerCase(ch))
+            ch = Character.toUpperCase(ch);
+        stream.write((int) ch);
+    }
+
+    public void write(byte buf[], int off, int len) throws IOException {
+        for (int i = off; i < (off + len); i++) {
+            char ch = (char) buf[i];
+            if (Character.isLowerCase(ch))
+                ch = Character.toUpperCase(ch);
+            stream.write((int) ch);
+        }
+    }
+
+    public void write(byte buf[]) throws IOException {
+        write(buf, 0, buf.length);
+    }
+
+}
+
diff --git a/container/tester/src/tester/org/apache/tester/UpperCaseReader.java b/container/tester/src/tester/org/apache/tester/UpperCaseReader.java
new file mode 100644
index 0000000..20352fb
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/UpperCaseReader.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * BufferedReader that converts all characters to upper case.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UpperCaseReader extends BufferedReader {
+
+    public UpperCaseReader(BufferedReader reader) throws IOException {
+        super(reader);
+    }
+
+    public int read() throws IOException {
+        int c = super.read();
+        if (c < 0)
+            return (c);
+        char ch = (char) c;
+        if (Character.isLowerCase(ch))
+            ch = Character.toUpperCase(ch);
+        return ((int) ch);
+    }
+
+    public int read(char buf[], int off, int len) throws IOException {
+        int n = 0;
+        for (int i = off; i < (off + len); i++) {
+            int c = super.read();
+            if (c < 0) {
+                if (n == 0)
+                    return (-1);
+                break;
+            }
+            char ch = (char) c;
+            if (Character.isLowerCase(ch))
+                ch = Character.toUpperCase(ch);
+            buf[i] = ch;
+            n++;
+        }
+        return (n);
+    }
+
+    public int read(char buf[]) throws IOException {
+        return (read(buf, 0, buf.length));
+    }
+
+}
+
diff --git a/container/tester/src/tester/org/apache/tester/UpperCaseRequest.java b/container/tester/src/tester/org/apache/tester/UpperCaseRequest.java
new file mode 100644
index 0000000..5c01be4
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/UpperCaseRequest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * HttpServletRequest wrapper that converts all input characters to
+ * upper case.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UpperCaseRequest extends HttpServletRequestWrapper {
+
+
+    HttpServletRequest request = null;
+
+    public UpperCaseRequest(HttpServletRequest request) {
+        super(request);
+        this.request = request;
+    }
+
+    public ServletInputStream getInputStream() throws IOException {
+        return (new UpperCaseInputStream(request.getInputStream()));
+    }
+
+    public BufferedReader getReader() throws IOException {
+        return (new UpperCaseReader(request.getReader()));
+    }
+
+
+}
+
+
+
diff --git a/container/tester/src/tester/org/apache/tester/UpperCaseResponse.java b/container/tester/src/tester/org/apache/tester/UpperCaseResponse.java
new file mode 100644
index 0000000..f391c1e
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/UpperCaseResponse.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * HttpServletResponse wrapper that converts all output characters to
+ * upper case.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UpperCaseResponse extends HttpServletResponseWrapper {
+
+
+    HttpServletResponse response = null;
+
+    boolean stream = false; // Wrap our own output stream
+
+    public UpperCaseResponse(HttpServletResponse response) {
+        this(response, false);
+    }
+
+    public UpperCaseResponse(HttpServletResponse response, boolean stream) {
+        super(response);
+        this.response = response;
+        this.stream = stream;
+    }
+
+    public ServletOutputStream getOutputStream() throws IOException {
+        return (new UpperCaseOutputStream(response.getOutputStream()));
+    }
+
+    public PrintWriter getWriter() throws IOException {
+        if (stream)
+            return (new PrintWriter(getOutputStream(), true));
+        else
+            return (new UpperCaseWriter(response.getWriter()));
+    }
+
+
+}
+
+
+
diff --git a/container/tester/src/tester/org/apache/tester/UpperCaseWriter.java b/container/tester/src/tester/org/apache/tester/UpperCaseWriter.java
new file mode 100644
index 0000000..211ea4f
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/UpperCaseWriter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * PrintWriter that converts all characters to upper case.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UpperCaseWriter extends PrintWriter {
+
+
+    public UpperCaseWriter(PrintWriter writer) throws IOException {
+        super(writer);
+    }
+
+    public void write(int c) {
+        char ch = (char) c;
+        if (Character.isLowerCase(ch))
+            ch = Character.toUpperCase(ch);
+        super.write((int) ch);
+    }
+
+    public void write(char buf[], int off, int len) {
+        for (int i = off; i < (off + len); i++) {
+            char ch = buf[i];
+            if (Character.isLowerCase(ch))
+                ch = Character.toUpperCase(ch);
+            super.write((int) ch);
+        }
+    }
+
+    public void write(char buf[]) {
+        write(buf, 0, buf.length);
+    }
+
+    public void write(String s, int off, int len) {
+        for (int i = off; i < (off + len); i++) {
+            char ch = s.charAt(i);
+            if (Character.isLowerCase(ch))
+                ch = Character.toUpperCase(ch);
+            super.write((int) ch);
+        }
+    }
+
+    public void write(String s) {
+        write(s, 0, s.length());
+    }
+
+
+}
+
diff --git a/container/tester/src/tester/org/apache/tester/WrapperFilter.java b/container/tester/src/tester/org/apache/tester/WrapperFilter.java
new file mode 100644
index 0000000..2235ab4
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/WrapperFilter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester;
+
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Configurable filter that will wrap the request and/or response objects
+ * it passes on with either generic or HTTP-specific wrappers.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class WrapperFilter implements Filter {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The filter configuration object for this filter.
+     */
+    protected FilterConfig config = null;
+
+
+    /**
+     * The type of wrapper for each request ("none", "generic", "http").
+     */
+    protected String requestWrapper = "none";
+
+
+    /**
+     * The type of wrapper for each response ("none", "generic", "http").
+     */
+    protected String responseWrapper = "none";
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Release this Filter instance from service.
+     */
+    public void destroy() {
+
+        config = null;
+        requestWrapper = "none";
+        responseWrapper = "none";
+
+    }
+
+
+    /**
+     * Wrap this request and/or response as configured and pass it on.
+     */
+    public void doFilter(ServletRequest inRequest, ServletResponse inResponse,
+                         FilterChain chain)
+        throws IOException, ServletException {
+
+        // Create the appropriate wrappers
+        ServletRequest outRequest = inRequest;
+        ServletResponse outResponse = inResponse;
+        if (requestWrapper.equals("generic")) {
+            outRequest = new TesterServletRequestWrapper(inRequest);
+        } else if (requestWrapper.equals("http")) {
+            outRequest = new TesterHttpServletRequestWrapper
+                ((HttpServletRequest) inRequest);
+        }
+        if (responseWrapper.equals("generic")) {
+            outResponse = new TesterServletResponseWrapper(inResponse);
+        } else if (responseWrapper.equals("http")) {
+            outResponse = new TesterHttpServletResponseWrapper
+                ((HttpServletResponse) inResponse);
+        }
+
+        // Perform this request
+        chain.doFilter(outRequest, outResponse);
+
+    }
+
+
+    /**
+     * Place this Filter instance into service.
+     *
+     * @param config The filter configuration object
+     */
+    public void init(FilterConfig config) throws ServletException {
+
+        this.config = config;
+        String value = null;
+        value = config.getInitParameter("request");
+        if (value != null)
+            requestWrapper = value;
+        value = config.getInitParameter("response");
+        if (value != null)
+            responseWrapper = value;
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Xerces01.java b/container/tester/src/tester/org/apache/tester/Xerces01.java
new file mode 100644
index 0000000..916feed
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Xerces01.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.tester;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+
+/**
+ * Ensure that we can use the Xerces parser while included in a web application
+ * even though the servlet container might utilize its own parser for internal
+ * use.
+ *
+ * @author Amy Roh
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Xerces01 extends HttpServlet {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Perform a simple SAX parse using Xerces (based on the SAXCount
+     * example application).
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+        throws ServletException, IOException
+    {
+
+        // Prepare our output stream
+        response.setContentType("text/plain");
+        PrintWriter writer = response.getWriter();
+        boolean ok = true;
+
+        // Construct a new instance of our parser driver
+        URL url = null;
+        try {
+            url = getServletContext().getResource("/Xerces01.xml");
+        } catch (MalformedURLException e) {
+            writer.println("Xerces01 FAILED - " + e);
+            e.printStackTrace(writer);
+            ok = false;
+        }
+        Xerces01Parser parser = new Xerces01Parser();
+        try {
+            if (ok)
+                parser.parse(url);
+        } catch (Exception e) {
+            writer.println("Xerces01 FAILED - " + e);
+            e.printStackTrace(writer);
+            ok = false;
+        }
+
+        // Report successful completion if OK
+        if (ok)
+            writer.println("Xerces01 PASSED");
+        while (true) {
+            String message = StaticLogger.read();
+            if (message == null)
+                break;
+            writer.println(message);
+        }
+        StaticLogger.reset();
+
+    }
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/Xerces01Parser.java b/container/tester/src/tester/org/apache/tester/Xerces01Parser.java
new file mode 100644
index 0000000..670aa8b
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/Xerces01Parser.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 1999, 2000, 2001 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.tester;
+
+
+import java.net.URL;
+import org.xml.sax.AttributeList;
+import org.xml.sax.HandlerBase;
+import org.xml.sax.Parser;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.ParserFactory;
+
+
+/**
+ * SAX parser (based on SAXCount) that exercises the Xerces parser within the
+ * environment of a web application.
+ *
+ * @author Amy Roh
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class Xerces01Parser extends HandlerBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The number of XML attributes we have encountered.
+     */
+    protected int attributes = 0;
+
+
+    /**
+     * The number of characters we have encountered.
+     */
+    protected int characters = 0;
+
+
+    /**
+     * The number of XML elements we have encountered.
+     */
+    protected int elements = 0;
+
+
+    /**
+     * The amount of ignorable whitespace we have encountered.
+     */
+    protected int whitespace = 0;
+
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute the requested parse.
+     *
+     * @param url The URL of the XML resource to be parsed
+     *
+     * @exception Exception if any processing exception occurs
+     */
+    public void parse(URL url) throws Exception {
+
+        // Construct a parser for our use
+        Parser parser =
+            ParserFactory.makeParser("org.apache.xerces.parsers.SAXParser");
+        parser.setDocumentHandler(this);
+        parser.setErrorHandler(this);
+
+        // Perform the requested parse
+        long before = System.currentTimeMillis();
+        parser.parse(url.toString());
+        long after = System.currentTimeMillis();
+
+        // Log the results
+        StaticLogger.write("Parsing time = " + (after - before) +
+                           " milliseconds");
+        StaticLogger.write("Processed " + elements + " elements");
+        StaticLogger.write("Processed " + attributes + " attributes");
+        StaticLogger.write("Processed " + characters + " characters");
+        StaticLogger.write("Processed " + whitespace + " whitespaces");
+
+    }
+
+
+    // ------------------------------------------------------ SAX Error Methods
+
+
+    /**
+     * Receive notification of a parser error.
+     *
+     * @param e The parser exception being reported
+     *
+     * @exception SAXException if a parsing error occurs
+     */
+    public void error(SAXParseException e) throws SAXException {
+
+        StaticLogger.write("[Error] " +
+                           getLocationString(e) + ": " +
+                           e.getMessage());
+
+    }
+
+
+    /**
+     * Receive notification of a fatal error.
+     *
+     * @param e The parser exception being reported
+     *
+     * @exception SAXException if a parsing error occurs
+     */
+    public void fatalError(SAXParseException e) throws SAXException {
+
+        StaticLogger.write("[Fatal] " +
+                           getLocationString(e) + ": " +
+                           e.getMessage());
+
+    }
+
+
+    /**
+     * Receive notification of a parser warning.
+     *
+     * @param e The parser exception being reported
+     *
+     * @exception SAXException if a parsing error occurs
+     */
+    public void warning(SAXParseException e) throws SAXException {
+
+        StaticLogger.write("[Warning] " +
+                           getLocationString(e) + ": " +
+                           e.getMessage());
+
+    }
+
+
+    /**
+     * Return the location at which this exception occurred.
+     *
+     * @param e The SAXParseException we are reporting on
+     */
+    private String getLocationString(SAXParseException e) {
+
+        StringBuffer sb = new StringBuffer();
+        String systemId = e.getSystemId();
+        if (systemId != null) {
+            int index = systemId.lastIndexOf('/');
+            if (index != -1)
+                systemId = systemId.substring(index + 1);
+            sb.append(systemId);
+        }
+        sb.append(':');
+        sb.append(e.getLineNumber());
+        sb.append(':');
+        sb.append(e.getColumnNumber());
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ SAX Event Methods
+
+
+    /**
+     * Character data event handler.
+     *
+     * @param ch Character array containing the characters
+     * @param start Starting position in the array
+     * @param length Number of characters to process
+     *
+     * @exception SAXException if a parsing error occurs
+     */
+    public void characters(char ch[], int start, int length)
+        throws SAXException {
+
+        characters += length;
+
+    }
+
+
+    /**
+     * Ignorable whitespace event handler.
+     *
+     * @param ch Character array containing the characters
+     * @param start Starting position in the array
+     * @param length Number of characters to process
+     *
+     * @exception SAXException if a parsing error occurs
+     */
+    public void ignorableWhitespace(char ch[], int start, int length)
+        throws SAXException {
+
+        whitespace += length;
+
+    }
+
+
+    /**
+     * Start of element event handler.
+     *
+     * @param name The element type name
+     * @param attrs The specified or defaulted attributes
+     *
+     * @exception SAXException if a parsing error occurs
+     */
+    public void startElement(String name, AttributeList attrs) {
+
+        elements++;
+        if (attrs != null)
+            attributes += attrs.getLength();
+
+    }
+
+
+
+}
diff --git a/container/tester/src/tester/org/apache/tester/shared/SharedSessionBean.java b/container/tester/src/tester/org/apache/tester/shared/SharedSessionBean.java
new file mode 100644
index 0000000..0621879
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/shared/SharedSessionBean.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester.shared;
+
+
+import java.io.Serializable;
+import java.sql.Date;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionEvent;
+
+
+/**
+ * Simple JavaBean to use for session attribute tests.  It is Serializable
+ * so that instances can be saved and restored across server restarts.
+ * <p>
+ * This is functionally equivalent to <code>SessionBean</code>, but stored
+ * in a different package so that it gets deployed into a JAR file under
+ * <code>$CATALINA_HOME/lib</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SharedSessionBean implements
+    HttpSessionActivationListener, HttpSessionBindingListener, Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * A date property for use with property editor tests.
+     */
+    protected Date dateProperty =
+        new Date(System.currentTimeMillis());
+
+    public Date getDateProperty() {
+        return (this.dateProperty);
+    }
+
+    public void setDateProperty(Date dateProperty) {
+        this.dateProperty = dateProperty;
+    }
+
+
+    /**
+     * The lifecycle events that have happened on this bean instance.
+     */
+    protected String lifecycle = "";
+
+    public String getLifecycle() {
+        return (this.lifecycle);
+    }
+
+    public void setLifecycle(String lifecycle) {
+        this.lifecycle = lifecycle;
+    }
+
+
+    /**
+     * A string property.
+     */
+    protected String stringProperty = "Default String Property Value";
+
+    public String getStringProperty() {
+        return (this.stringProperty);
+    }
+
+    public void setStringProperty(String stringProperty) {
+        this.stringProperty = stringProperty;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a string representation of this bean.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SharedSessionBean[lifecycle=");
+        sb.append(this.lifecycle);
+        sb.append(",dateProperty=");
+        sb.append(dateProperty);
+        sb.append(",stringProperty=");
+        sb.append(this.stringProperty);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ---------------------------------- HttpSessionActivationListener Methods
+
+
+    /**
+     * Receive notification that this session was activated.
+     *
+     * @param event The session event that has occurred
+     */
+    public void sessionDidActivate(HttpSessionEvent event) {
+
+        lifecycle += "/sda";
+
+    }
+
+
+    /**
+     * Receive notification that this session will be passivated.
+     *
+     * @param event The session event that has occurred
+     */
+    public void sessionWillPassivate(HttpSessionEvent event) {
+
+        lifecycle += "/swp";
+
+    }
+
+
+    // ------------------------------------- HttpSessionBindingListener Methods
+
+
+    /**
+     * Receive notification that this attribute has been bound.
+     *
+     * @param event The session event that has occurred
+     */
+    public void valueBound(HttpSessionBindingEvent event) {
+
+        lifecycle += "/vb";
+
+    }
+
+
+    /**
+     * Receive notification that this attribute has been unbound.
+     *
+     * @param event The session event that has occurred
+     */
+    public void valueUnbound(HttpSessionBindingEvent event) {
+
+        lifecycle += "/vu";
+
+    }
+
+
+}
+
diff --git a/container/tester/src/tester/org/apache/tester/unpshared/UnpSharedSessionBean.java b/container/tester/src/tester/org/apache/tester/unpshared/UnpSharedSessionBean.java
new file mode 100644
index 0000000..7d03dc7
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/unpshared/UnpSharedSessionBean.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester.unpshared;
+
+
+import java.io.Serializable;
+import java.sql.Date;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionEvent;
+
+
+/**
+ * Simple JavaBean to use for session attribute tests.  It is Serializable
+ * so that instances can be saved and restored across server restarts.
+ * <p>
+ * This is functionally equivalent to <code>SessionBean</code>, but stored
+ * in a different package so that it gets deployed unpacked under
+ * <code>$CATALINA_HOME/classes</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UnpSharedSessionBean implements
+    HttpSessionActivationListener, HttpSessionBindingListener, Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * A date property for use with property editor tests.
+     */
+    protected Date dateProperty =
+        new Date(System.currentTimeMillis());
+
+    public Date getDateProperty() {
+        return (this.dateProperty);
+    }
+
+    public void setDateProperty(Date dateProperty) {
+        this.dateProperty = dateProperty;
+    }
+
+
+    /**
+     * The lifecycle events that have happened on this bean instance.
+     */
+    protected String lifecycle = "";
+
+    public String getLifecycle() {
+        return (this.lifecycle);
+    }
+
+    public void setLifecycle(String lifecycle) {
+        this.lifecycle = lifecycle;
+    }
+
+
+    /**
+     * A string property.
+     */
+    protected String stringProperty = "Default String Property Value";
+
+    public String getStringProperty() {
+        return (this.stringProperty);
+    }
+
+    public void setStringProperty(String stringProperty) {
+        this.stringProperty = stringProperty;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a string representation of this bean.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SharedSessionBean[lifecycle=");
+        sb.append(this.lifecycle);
+        sb.append(",dateProperty=");
+        sb.append(dateProperty);
+        sb.append(",stringProperty=");
+        sb.append(this.stringProperty);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ---------------------------------- HttpSessionActivationListener Methods
+
+
+    /**
+     * Receive notification that this session was activated.
+     *
+     * @param event The session event that has occurred
+     */
+    public void sessionDidActivate(HttpSessionEvent event) {
+
+        lifecycle += "/sda";
+
+    }
+
+
+    /**
+     * Receive notification that this session will be passivated.
+     *
+     * @param event The session event that has occurred
+     */
+    public void sessionWillPassivate(HttpSessionEvent event) {
+
+        lifecycle += "/swp";
+
+    }
+
+
+    // ------------------------------------- HttpSessionBindingListener Methods
+
+
+    /**
+     * Receive notification that this attribute has been bound.
+     *
+     * @param event The session event that has occurred
+     */
+    public void valueBound(HttpSessionBindingEvent event) {
+
+        lifecycle += "/vb";
+
+    }
+
+
+    /**
+     * Receive notification that this attribute has been unbound.
+     *
+     * @param event The session event that has occurred
+     */
+    public void valueUnbound(HttpSessionBindingEvent event) {
+
+        lifecycle += "/vu";
+
+    }
+
+
+}
+
diff --git a/container/tester/src/tester/org/apache/tester/unshared/UnsharedSessionBean.java b/container/tester/src/tester/org/apache/tester/unshared/UnsharedSessionBean.java
new file mode 100644
index 0000000..9338546
--- /dev/null
+++ b/container/tester/src/tester/org/apache/tester/unshared/UnsharedSessionBean.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999, 2000 ,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tester.unshared;
+
+
+import java.io.Serializable;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionEvent;
+import org.apache.tester.SessionBean;
+
+
+/**
+ * Simple JavaBean to use for session attribute tests.  It is Serializable
+ * so that instances can be saved and restored across server restarts.
+ * <p>
+ * This bean is functionally equivalent to
+ * <code>org.apache.tester.SessionBean</code>, but will be deployed under
+ * <code>/WEB-INF/classes</code> instead of inside
+ * <code>/WEB-INF/lib/tester.jar</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class UnsharedSessionBean extends SessionBean implements
+    HttpSessionActivationListener, HttpSessionBindingListener, Serializable {
+
+
+    /**
+     * Return a string representation of this bean.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("UnsharedSessionBean[lifecycle=");
+        sb.append(this.lifecycle);
+        sb.append(",stringProperty=");
+        sb.append(this.stringProperty);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
+
diff --git a/container/tester/web/Authentication04.jsp b/container/tester/web/Authentication04.jsp
new file mode 100644
index 0000000..6e1dd39
--- /dev/null
+++ b/container/tester/web/Authentication04.jsp
@@ -0,0 +1,26 @@
+<%@ page contentType="text/plain" %><%
+  StringBuffer results = new StringBuffer();
+  String remoteUser = request.getRemoteUser();
+  if (remoteUser == null) {
+    results.append(" Not Authenticated/");
+  } else if (!"tomcat".equals(remoteUser)) {
+    results.append(" Authenticated as '");
+    results.append(remoteUser);
+    results.append("'/");
+  }
+  if (!request.isUserInRole("tomcat")) {
+    results.append(" Not in role 'tomcat'/");
+  }
+  if (!request.isUserInRole("alias")) {
+    results.append(" Not in role 'alias'/");
+  }
+  if (request.isUserInRole("unknown")) {
+    results.append(" In role 'unknown'/");
+  }
+  if (results.length() < 1) {
+    out.println("Authentication04 PASSED");
+  } else {
+    out.print("Authentication04 FAILED -");
+    out.println(results.toString());
+  }
+%>
diff --git a/container/tester/web/Encoding01.jsp b/container/tester/web/Encoding01.jsp
new file mode 100644
index 0000000..c66916a
--- /dev/null
+++ b/container/tester/web/Encoding01.jsp
@@ -0,0 +1,5 @@
+
+<%@ page contentType="text/html;charset=SJIS" %>
+
+<h2>Test Character</h2>
+<h2><%="\u9b5a"%> Unicode = 9b5a</hr>
diff --git a/container/tester/web/Encoding02.jsp b/container/tester/web/Encoding02.jsp
new file mode 100644
index 0000000..cf2c33d
--- /dev/null
+++ b/container/tester/web/Encoding02.jsp
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>Encoding02.jsp</title>
+</head>
+<body bgcolor="white">
+This is legal in the spec:<br>
+<%= "Joe said %\> foo" %>.
+</body>
+</html>
diff --git a/container/tester/web/Encoding03.jsp b/container/tester/web/Encoding03.jsp
new file mode 100644
index 0000000..e64ac19
--- /dev/null
+++ b/container/tester/web/Encoding03.jsp
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>Encoding03.jsp</title>
+</head>
+<body bgcolor="white">
+This is not recognized as a delimiter either:<br>
+<%= "Joe said %\\> bar" %>.
+</body>
+</html>
diff --git a/container/tester/web/ErrorPage06.html b/container/tester/web/ErrorPage06.html
new file mode 100644
index 0000000..01d6232
--- /dev/null
+++ b/container/tester/web/ErrorPage06.html
@@ -0,0 +1 @@
+ErrorPage06 PASSED - HTML
diff --git a/container/tester/web/ErrorPage06.jsp b/container/tester/web/ErrorPage06.jsp
new file mode 100644
index 0000000..e871a87
--- /dev/null
+++ b/container/tester/web/ErrorPage06.jsp
@@ -0,0 +1,106 @@
+<%@ page contentType="text/plain" %><%
+
+        // Accumulate all the reasons this request might fail
+        StringBuffer sb = new StringBuffer();
+        Object value = null;
+
+        value = request.getAttribute("javax.servlet.error.exception");
+        if (value == null) {
+            sb.append(" exception is missing/");
+        } else if (!(value instanceof java.lang.ArrayIndexOutOfBoundsException)) {
+            sb.append(" exception class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        }
+
+        value = request.getAttribute("javax.servlet.error.exception_type");
+        if (value == null)
+            sb.append(" exception_type is missing/");
+        else if (!(value instanceof Class)) {
+            sb.append(" exception_type class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            Class clazz = (Class) value;
+            String name = clazz.getName();
+            if (!"java.lang.ArrayIndexOutOfBoundsException".equals(name)) {
+                sb.append(" exception_type is ");
+                sb.append(name);
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.message");
+        if (value == null)
+            sb.append(" message is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" message class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else if (!"ErrorPage05 Threw ArrayIndexOutOfBoundsException".equals(value) &&
+                   !"ErrorPage08 Threw ArrayIndexOutOfBoundsException".equals(value)) {
+            sb.append(" message is not correct");
+        }
+
+        value = request.getAttribute("javax.servlet.error.request_uri");
+        if (value == null)
+            sb.append(" request_uri is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" request_uri class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String request_uri = (String) value;
+            String test1 = request.getContextPath() + "/ErrorPage05";
+            String test2 = request.getContextPath() + "/WrappedErrorPage05";
+            String test3 = request.getContextPath() + "/ErrorPage08";
+            String test4 = request.getContextPath() + "/WrappedErrorPage08";
+            if (!request_uri.equals(test1) && !request_uri.equals(test2) &&
+                !request_uri.equals(test3) && !request_uri.equals(test4)) {
+                sb.append(" request_uri is ");
+                sb.append(request_uri);
+                sb.append("/");
+            }
+        }
+
+        value = request.getAttribute("javax.servlet.error.servlet_name");
+        if (value == null)
+            sb.append(" servlet_name is missing/");
+        else if (!(value instanceof String)) {
+            sb.append(" servlet_name class is ");
+            sb.append(value.getClass().getName());
+            sb.append("/");
+        } else {
+            String servlet_name = (String) value;
+            if (!"ErrorPage05".equals(servlet_name) &&
+                !"ErrorPage08".equals(servlet_name)) {
+                sb.append(" servlet_name is ");
+                sb.append(servlet_name);
+                sb.append("/");
+            }
+        }
+
+        // Report ultimate success or failure
+        if (sb.length() < 1)
+            out.println("ErrorPage06 PASSED - JSP");
+        else
+            out.println("ErrorPage06 FAILED -" + sb.toString());
+
+%>
+<%
+  Exception e = (Exception)
+   request.getAttribute("javax.servlet.error.exception");
+  out.println("EXCEPTION:  " + e);
+  Class et = (Class)
+   request.getAttribute("javax.servlet.error.exception_type");
+  out.println("EXCEPTION_TYPE:  " + et.getName());
+  String m = (String)
+   request.getAttribute("javax.servlet.error.message");
+  out.println("MESSAGE:  " + m);
+  String ru = (String)
+   request.getAttribute("javax.servlet.error.request_uri");
+  out.println("REQUEST_URI:  " + ru);
+  String sn = (String)
+   request.getAttribute("javax.servlet.error.servlet_name");
+  out.println("SERVLET_NAME:  " + sn);
+%>
diff --git a/container/tester/web/ErrorPage08.jsp b/container/tester/web/ErrorPage08.jsp
new file mode 100644
index 0000000..7ed42b9
--- /dev/null
+++ b/container/tester/web/ErrorPage08.jsp
@@ -0,0 +1,19 @@
+<%@ page contentType="text/plain" %><%
+
+        // Write a FAILED message that should get replaced by the error text
+        out.println("ErrorPage08 FAILED - Original response returned");
+
+        // Throw the specified exception
+        String type = request.getParameter("type");
+        if ("Arithmetic".equals(type)) {
+            throw new ArithmeticException
+                ("ErrorPage08 Threw ArithmeticException");
+        } else if ("Array".equals(type)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("ErrorPage08 Threw ArrayIndexOutOfBoundsException");
+        } else if ("Number".equals(type)) {
+            throw new NumberFormatException
+                ("ErrorPage08 Threw NumberFormatException");
+        }
+
+%>
diff --git a/container/tester/web/ErrorPage09.jsp b/container/tester/web/ErrorPage09.jsp
new file mode 100644
index 0000000..15c8e59
--- /dev/null
+++ b/container/tester/web/ErrorPage09.jsp
@@ -0,0 +1,13 @@
+<%@ page contentType="text/plain" errorPage="/ErrorPage10.jsp" %><%
+
+        // Write a FAILED message that should get replaced by the error text
+        out.println("ErrorPage09 FAILED - Original response returned");
+
+        // Throw the specified exception
+        int i = 1;
+        if (i > 0) {
+            throw new ArrayIndexOutOfBoundsException
+                ("ErrorPage09 Threw ArrayIndexOutOfBoundsException");
+        }
+
+%>
diff --git a/container/tester/web/ErrorPage10.jsp b/container/tester/web/ErrorPage10.jsp
new file mode 100644
index 0000000..1a62be3
--- /dev/null
+++ b/container/tester/web/ErrorPage10.jsp
@@ -0,0 +1,31 @@
+<%@ page contentType="text/plain" isErrorPage="true" %><%
+
+        // Accumulate all the reasons this request might fail
+        StringBuffer sb = new StringBuffer();
+        Object value = null;
+
+        if (exception == null) {
+            sb.append(" exception is missing/");
+        } else {
+            if (!(exception instanceof java.lang.ArrayIndexOutOfBoundsException)) {
+                sb.append(" exception class is ");
+                sb.append(exception.getClass().getName());
+                sb.append("/");
+            }
+            if (!"ErrorPage09 Threw ArrayIndexOutOfBoundsException".equals(exception.getMessage())) {
+                sb.append(" exception message is ");
+                sb.append(exception.getMessage());
+                sb.append("/");
+            }
+        }
+
+        // Report ultimate success or failure
+        if (sb.length() < 1)
+            out.println("ErrorPage10 PASSED");
+        else
+            out.println("ErrorPage10 FAILED -" + sb.toString());
+
+%>
+<%
+  out.println("EXCEPTION:  " + exception);
+%>
diff --git a/container/tester/web/FilterResponse02.jsp b/container/tester/web/FilterResponse02.jsp
new file mode 100644
index 0000000..8467dbc
--- /dev/null
+++ b/container/tester/web/FilterResponse02.jsp
@@ -0,0 +1,10 @@
+<%@ page contentType="text/plain" %>FilterResponse02 PASSED
+<%
+        while (true) {
+            String message = org.apache.tester.StaticLogger.read();
+            if (message == null)
+                break;
+            out.println(message);
+        }
+        org.apache.tester.StaticLogger.reset();
+%>
diff --git a/container/tester/web/FilterResponse03.txt b/container/tester/web/FilterResponse03.txt
new file mode 100644
index 0000000..035d220
--- /dev/null
+++ b/container/tester/web/FilterResponse03.txt
@@ -0,0 +1 @@
+FilterResponse03 PASSED
diff --git a/container/tester/web/Forward00b.jsp b/container/tester/web/Forward00b.jsp
new file mode 100644
index 0000000..42d2053
--- /dev/null
+++ b/container/tester/web/Forward00b.jsp
@@ -0,0 +1,6 @@
+<%@ page contentType="text/plain" %>Forward00b PASSED
+requestURI=<%= request.getRequestURI() %>
+contextPath=<%= request.getContextPath() %>
+servletPath=<%= request.getServletPath() %>
+pathInfo=<%= request.getPathInfo() %>
+queryString=<%= request.getQueryString() %>
diff --git a/container/tester/web/Forward00c.jsp b/container/tester/web/Forward00c.jsp
new file mode 100644
index 0000000..553509a
--- /dev/null
+++ b/container/tester/web/Forward00c.jsp
@@ -0,0 +1,6 @@
+<%@ page contentType="text/plain" %>Forward00c PASSED
+requestURI=<%= request.getRequestURI() %>
+contextPath=<%= request.getContextPath() %>
+servletPath=<%= request.getServletPath() %>
+pathInfo=<%= request.getPathInfo() %>
+queryString=<%= request.getQueryString() %>
diff --git a/container/tester/web/Forward00e.jsp b/container/tester/web/Forward00e.jsp
new file mode 100644
index 0000000..2fba8c8
--- /dev/null
+++ b/container/tester/web/Forward00e.jsp
@@ -0,0 +1,6 @@
+<%@ page contentType="text/plain" %>Forward00e PASSED
+requestURI=<%= request.getRequestURI() %>
+contextPath=<%= request.getContextPath() %>
+servletPath=<%= request.getServletPath() %>
+pathInfo=<%= request.getPathInfo() %>
+queryString=<%= request.getQueryString() %>
diff --git a/container/tester/web/Forward01.txt b/container/tester/web/Forward01.txt
new file mode 100644
index 0000000..67d32cc
--- /dev/null
+++ b/container/tester/web/Forward01.txt
@@ -0,0 +1 @@
+Forward01 PASSED
diff --git a/container/tester/web/Forward03b.jsp b/container/tester/web/Forward03b.jsp
new file mode 100644
index 0000000..212c905
--- /dev/null
+++ b/container/tester/web/Forward03b.jsp
@@ -0,0 +1,30 @@
+<%
+  String specials[] =
+    { "javax.servlet.include.request_uri",
+      "javax.servlet.include.context_path",
+      "javax.servlet.include.servlet_path",
+      "javax.servlet.include.path_info",
+      "javax.servlet.include.query_string" };
+
+  StringBuffer sb = new StringBuffer();
+  if (request.getAttribute("Forward03") == null)
+    sb.append(" Cannot retrieve forwarded attribute/");
+  request.setAttribute("Forward03b", "This is our very own attribute");
+  if (request.getAttribute("Forward03b") == null)
+    sb.append(" Cannot retrieve our own attribute/");
+
+  for (int i = 0; i < specials.length; i++) {
+    if (request.getAttribute(specials[i]) != null) {
+      sb.append(" Exposed attribute ");
+      sb.append(specials[i]);
+      sb.append("/");
+    }
+  }
+
+  if (sb.length() < 1) {
+    out.println("Forward03 PASSED");
+  } else {
+    out.print("Forward03 FAILED - ");
+    out.println(sb.toString());
+  }
+%>
diff --git a/container/tester/web/Forward04.jsp b/container/tester/web/Forward04.jsp
new file mode 100644
index 0000000..3daaf56
--- /dev/null
+++ b/container/tester/web/Forward04.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %><jsp:forward page="/Forward04a.jsp"/>Forward04.jsp FAILED - Content should not be visible
diff --git a/container/tester/web/Forward04a.jsp b/container/tester/web/Forward04a.jsp
new file mode 100644
index 0000000..e366a05
--- /dev/null
+++ b/container/tester/web/Forward04a.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %><jsp:forward page="/Forward04b.jsp"/>Forward04a.jsp FAILED - Content should not be visible
diff --git a/container/tester/web/Forward04b.jsp b/container/tester/web/Forward04b.jsp
new file mode 100644
index 0000000..dbea1bf
--- /dev/null
+++ b/container/tester/web/Forward04b.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %>Forward04b.jsp PASSED
diff --git a/container/tester/web/Forward05.jsp b/container/tester/web/Forward05.jsp
new file mode 100644
index 0000000..8f3e145
--- /dev/null
+++ b/container/tester/web/Forward05.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %><jsp:forward page="/Forward05a"/>Forward05.jsp FAILED - Content should not be visible
diff --git a/container/tester/web/Forward05a.jsp b/container/tester/web/Forward05a.jsp
new file mode 100644
index 0000000..62fcc66
--- /dev/null
+++ b/container/tester/web/Forward05a.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %><jsp:forward page="/Forward05b"/>Forward05a.jsp FAILED - Content should not be visible
diff --git a/container/tester/web/Forward05b.jsp b/container/tester/web/Forward05b.jsp
new file mode 100644
index 0000000..0f99c23
--- /dev/null
+++ b/container/tester/web/Forward05b.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %>Forward05b.jsp PASSED
diff --git a/container/tester/web/Forward06.jsp b/container/tester/web/Forward06.jsp
new file mode 100644
index 0000000..47afcc9
--- /dev/null
+++ b/container/tester/web/Forward06.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %><jsp:forward page="/servlet/Forward06a"/>Forward06.jsp FAILED - Content should not be visible
diff --git a/container/tester/web/Forward06a.jsp b/container/tester/web/Forward06a.jsp
new file mode 100644
index 0000000..b700885
--- /dev/null
+++ b/container/tester/web/Forward06a.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %><jsp:forward page="/servlet/Forward06b"/>Forward06a.jsp FAILED - Content should not be visible
diff --git a/container/tester/web/Forward06b.jsp b/container/tester/web/Forward06b.jsp
new file mode 100644
index 0000000..c41d7b1
--- /dev/null
+++ b/container/tester/web/Forward06b.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %>Forward06b.jsp PASSED
diff --git a/container/tester/web/Include00b.jsp b/container/tester/web/Include00b.jsp
new file mode 100644
index 0000000..145e654
--- /dev/null
+++ b/container/tester/web/Include00b.jsp
@@ -0,0 +1,6 @@
+<%@ page contentType="text/plain" session="false" %>Include00b PASSED
+requestURI=<%= request.getRequestURI() %>
+contextPath=<%= request.getContextPath() %>
+servletPath=<%= request.getServletPath() %>
+pathInfo=<%= request.getPathInfo() %>
+queryString=<%= request.getQueryString() %>
diff --git a/container/tester/web/Include00c.jsp b/container/tester/web/Include00c.jsp
new file mode 100644
index 0000000..df76ac4
--- /dev/null
+++ b/container/tester/web/Include00c.jsp
@@ -0,0 +1,6 @@
+<%@ page contentType="text/plain" session="false"%>Include00c PASSED
+requestURI=<%= request.getRequestURI() %>
+contextPath=<%= request.getContextPath() %>
+servletPath=<%= request.getServletPath() %>
+pathInfo=<%= request.getPathInfo() %>
+queryString=<%= request.getQueryString() %>
diff --git a/container/tester/web/Include00e.jsp b/container/tester/web/Include00e.jsp
new file mode 100644
index 0000000..36e40c1
--- /dev/null
+++ b/container/tester/web/Include00e.jsp
@@ -0,0 +1,6 @@
+<%@ page contentType="text/plain" session="false" %>Include00e PASSED
+requestURI=<%= request.getRequestURI() %>
+contextPath=<%= request.getContextPath() %>
+servletPath=<%= request.getServletPath() %>
+pathInfo=<%= request.getPathInfo() %>
+queryString=<%= request.getQueryString() %>
diff --git a/container/tester/web/Include01.txt b/container/tester/web/Include01.txt
new file mode 100644
index 0000000..2c327bb
--- /dev/null
+++ b/container/tester/web/Include01.txt
@@ -0,0 +1 @@
+Include01 PASSED
diff --git a/container/tester/web/Include03b.jsp b/container/tester/web/Include03b.jsp
new file mode 100644
index 0000000..feadcb0
--- /dev/null
+++ b/container/tester/web/Include03b.jsp
@@ -0,0 +1 @@
+<% request.setAttribute("Include03b.jsp", "This is a new attribute"); %>
diff --git a/container/tester/web/Include03c.jsp b/container/tester/web/Include03c.jsp
new file mode 100644
index 0000000..6ed33d0
--- /dev/null
+++ b/container/tester/web/Include03c.jsp
@@ -0,0 +1,30 @@
+<%@ page contentType="text/plain" %><%
+  // Duplicate logic from "Include03.java"
+  StringBuffer sb = new StringBuffer();
+  String path = request.getParameter("path");
+  if (path == null)
+    path = "/Include03a";
+  RequestDispatcher rd =
+    getServletContext().getRequestDispatcher(path);
+  if (rd == null) {
+    sb.append(" No RequestDispatcher returned/");
+  } else {
+    rd.include(request, response);
+  }
+  response.resetBuffer();
+  String value = null;
+  try {
+    value = (String) request.getAttribute(path.substring(1));
+  } catch (ClassCastException e) {
+      sb.append(" Returned attribute not of type String/");
+  }
+  if ((sb.length() < 1) && (value == null)) {
+      sb.append(" No includee-created attribute was returned/");
+  }
+  if (sb.length() < 1)
+    out.println("Include03c.jsp PASSED");
+  else {
+    out.print("Include03c.jsp FAILED -");
+    out.println(sb.toString());
+  }
+%>
diff --git a/container/tester/web/Include05.jsp b/container/tester/web/Include05.jsp
new file mode 100644
index 0000000..bc93070
--- /dev/null
+++ b/container/tester/web/Include05.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %><jsp:include page="/Include05a.jsp" flush="false"/><jsp:forward page="/Include05b.jsp"/>
diff --git a/container/tester/web/Include05a.jsp b/container/tester/web/Include05a.jsp
new file mode 100644
index 0000000..f283caf
--- /dev/null
+++ b/container/tester/web/Include05a.jsp
@@ -0,0 +1 @@
+Include05a PASSED
diff --git a/container/tester/web/Include05b.jsp b/container/tester/web/Include05b.jsp
new file mode 100644
index 0000000..87df1ec
--- /dev/null
+++ b/container/tester/web/Include05b.jsp
@@ -0,0 +1 @@
+Include05b PASSED
diff --git a/container/tester/web/Include06.jsp b/container/tester/web/Include06.jsp
new file mode 100644
index 0000000..20f6f8c
--- /dev/null
+++ b/container/tester/web/Include06.jsp
@@ -0,0 +1,3 @@
+<%@ page contentType="text/plain" %>==========
+<jsp:include page="servlet/org.apache.tester.Include06a" flush="false"/>==========
+<jsp:include page="/Include06b.jsp"/>==========
diff --git a/container/tester/web/Include06b.jsp b/container/tester/web/Include06b.jsp
new file mode 100644
index 0000000..9d7dbf9
--- /dev/null
+++ b/container/tester/web/Include06b.jsp
@@ -0,0 +1 @@
+Include06b.jsp output
diff --git a/container/tester/web/JspBeans01.jsp b/container/tester/web/JspBeans01.jsp
new file mode 100644
index 0000000..9e3de03
--- /dev/null
+++ b/container/tester/web/JspBeans01.jsp
@@ -0,0 +1,4 @@
+<%@ page contentType="text/plain" %><jsp:useBean id="bean" class="org.apache.tester.SessionBean"/>JspBeans01 PASSED
+lifecycle = <%= bean.getLifecycle() %>
+stringProperty= <%= bean.getStringProperty() %>
+toString = <%= bean.toString() %>
diff --git a/container/tester/web/JspBeans02.jsp b/container/tester/web/JspBeans02.jsp
new file mode 100644
index 0000000..0e2c380
--- /dev/null
+++ b/container/tester/web/JspBeans02.jsp
@@ -0,0 +1,4 @@
+<%@ page contentType="text/plain" %><jsp:useBean id="bean" class="org.apache.tester.shared.SharedSessionBean"/>JspBeans02 PASSED
+lifecycle = <%= bean.getLifecycle() %>
+stringProperty= <%= bean.getStringProperty() %>
+toString = <%= bean.toString() %>
diff --git a/container/tester/web/JspBeans03.jsp b/container/tester/web/JspBeans03.jsp
new file mode 100644
index 0000000..c437d6d
--- /dev/null
+++ b/container/tester/web/JspBeans03.jsp
@@ -0,0 +1,4 @@
+<%@ page contentType="text/plain" %><jsp:useBean id="bean" class="org.apache.tester.unshared.UnsharedSessionBean"/>JspBeans03 PASSED
+lifecycle = <%= bean.getLifecycle() %>
+stringProperty= <%= bean.getStringProperty() %>
+toString = <%= bean.toString() %>
diff --git a/container/tester/web/JspDoc01.jsp b/container/tester/web/JspDoc01.jsp
new file mode 100644
index 0000000..5405e9d
--- /dev/null
+++ b/container/tester/web/JspDoc01.jsp
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2">
+  <a>text<b></b></a>
+  <jsp:text>&lt;a&gt;text&lt;b&gt;&lt;/b&gt;&lt;/a&gt;</jsp:text>
+  <c><d>text</d></c>
+  <jsp:text>&lt;c&gt;&lt;d&gt;text&lt;/d&gt;&lt;/c&gt;</jsp:text>
+  <e><f></f>text<f></f></e>
+  <jsp:text>&lt;e&gt;&lt;f&gt;&lt;/f&gt;text&lt;f&gt;&lt;/f&gt;&lt;/e&gt;</jsp:text>
+</jsp:root>
diff --git a/container/tester/web/JspDoc02.jsp b/container/tester/web/JspDoc02.jsp
new file mode 100644
index 0000000..9233c25
--- /dev/null
+++ b/container/tester/web/JspDoc02.jsp
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
+  version="1.2">
+<jsp:directive.page contentType="text/html"/>
+<jsp:directive.page import="java.util.Date, java.util.Locale"/>
+<jsp:directive.page import="java.text.*"/>
+
+<jsp:declaration>
+  String getData() {
+    return "foo";
+  }
+</jsp:declaration>
+
+<html>
+<head>
+  <title>Example JSP in XML format</title>
+</head>
+
+<body>
+This is the output of a simple JSP using XML format. 
+<br />
+
+<div>Use a jsp:scriptlet to loop from 1 to 10: </div>
+<jsp:scriptlet>
+// Note we need to declare CDATA because we don't escape the less than symbol
+<![CDATA[
+  for (int i = 1; i<=10; i++) {
+    out.print(i);
+    if (i < 10) {
+      out.print(", ");
+    }
+  }
+]]>
+</jsp:scriptlet>
+
+<!-- Because I omit br's end tag, declare it as CDATA -->
+<![CDATA[
+  <br><br>
+]]>
+
+<div align="left">
+  Use a jsp:expression to write something: 
+  <jsp:expression>getData()</jsp:expression>
+</div>
+
+
+<jsp:text>
+  &lt;p&gt;This sentence is enclosed in a jsp:text element.&lt;/p&gt;
+</jsp:text>
+
+</body>
+</html>
+</jsp:root>
diff --git a/container/tester/web/JspForward01.jsp b/container/tester/web/JspForward01.jsp
new file mode 100644
index 0000000..47e2615
--- /dev/null
+++ b/container/tester/web/JspForward01.jsp
@@ -0,0 +1,2 @@
+<%@ page contentType="text/plain" %><jsp:forward page="<%= request.getParameter(\"path\") %>"/>
+JspForward01 FAILED - Content from forwarded-to page not included
diff --git a/container/tester/web/JspForward01a.jsp b/container/tester/web/JspForward01a.jsp
new file mode 100644
index 0000000..a42c132
--- /dev/null
+++ b/container/tester/web/JspForward01a.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %>JspForward01a PASSED
diff --git a/container/tester/web/JspInclude01.jsp b/container/tester/web/JspInclude01.jsp
new file mode 100644
index 0000000..6fd7b3b
--- /dev/null
+++ b/container/tester/web/JspInclude01.jsp
@@ -0,0 +1,4 @@
+<%@ page contentType="text/plain" %>This is before the include
+<jsp:include page="<%= request.getParameter(\"path\") %>" flush="true"/>
+This is after the include
+
diff --git a/container/tester/web/JspInclude01a.jsp b/container/tester/web/JspInclude01a.jsp
new file mode 100644
index 0000000..6f4ddcc
--- /dev/null
+++ b/container/tester/web/JspInclude01a.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %>This is the include
diff --git a/container/tester/web/JspInclude02.jsp b/container/tester/web/JspInclude02.jsp
new file mode 100644
index 0000000..37e3790
--- /dev/null
+++ b/container/tester/web/JspInclude02.jsp
@@ -0,0 +1,4 @@
+<%@ page contentType="text/plain" %>This is before the include
+<jsp:include page="<%= request.getParameter(\"path\") %>" flush="false"/>
+This is after the include
+
diff --git a/container/tester/web/JspInclude02a.jsp b/container/tester/web/JspInclude02a.jsp
new file mode 100644
index 0000000..6f4ddcc
--- /dev/null
+++ b/container/tester/web/JspInclude02a.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %>This is the include
diff --git a/container/tester/web/JspParams01.jsp b/container/tester/web/JspParams01.jsp
new file mode 100644
index 0000000..c33775c
--- /dev/null
+++ b/container/tester/web/JspParams01.jsp
@@ -0,0 +1,5 @@
+<jsp:include page="JspParams01a.jsp" flush="true">
+  <jsp:params>
+    <jsp:param name="foo" value="bar"/>
+  </jsp:params>
+</jsp:include>
diff --git a/container/tester/web/JspParams02.jsp b/container/tester/web/JspParams02.jsp
new file mode 100644
index 0000000..e2a86cc
--- /dev/null
+++ b/container/tester/web/JspParams02.jsp
@@ -0,0 +1,5 @@
+<jsp:forward page="JspParams01a.jsp">
+  <jsp:params>
+    <jsp:param name="foo" value="bar"/>
+  </jsp:params>
+</jsp:forward>
diff --git a/container/tester/web/Property01.jsp b/container/tester/web/Property01.jsp
new file mode 100644
index 0000000..121c832
--- /dev/null
+++ b/container/tester/web/Property01.jsp
@@ -0,0 +1,12 @@
+<html>
+<head>
+<title>Property01.jsp - Positive PropertyEditor Test</title>
+</head>
+<body bgcolor="white">
+<jsp:useBean id="bean" scope="request"
+          class="org.apache.tester.SessionBean"/>
+<jsp:setProperty name="bean" property="dateProperty"
+                value="07/25/2001"/>
+Date property is '<jsp:getProperty name="bean" property="dateProperty"/>'.
+</body>
+</html>
diff --git a/container/tester/web/Property02.jsp b/container/tester/web/Property02.jsp
new file mode 100644
index 0000000..036c9e7
--- /dev/null
+++ b/container/tester/web/Property02.jsp
@@ -0,0 +1,12 @@
+<html>
+<head>
+<title>Property02.jsp - Negative PropertyEditor Test</title>
+</head>
+<body bgcolor="white">
+<jsp:useBean id="bean" scope="request"
+          class="org.apache.tester.SessionBean"/>
+<jsp:setProperty name="bean" property="dateProperty"
+                value="07/25/200A"/>
+Date property is '<jsp:getProperty name="bean" property="dateProperty"/>'.
+</body>
+</html>
diff --git a/container/tester/web/Redirect02.jsp b/container/tester/web/Redirect02.jsp
new file mode 100644
index 0000000..0347713
--- /dev/null
+++ b/container/tester/web/Redirect02.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %><% String dummy = null; if (dummy == null) { response.sendRedirect(request.getContextPath() + "/Redirect02a.jsp"); return; } %>Redirect02.jsp FAILED - Content should not be visible
diff --git a/container/tester/web/Redirect02a.jsp b/container/tester/web/Redirect02a.jsp
new file mode 100644
index 0000000..18a8ac3
--- /dev/null
+++ b/container/tester/web/Redirect02a.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %>Redirect02a.jsp PASSED
diff --git a/container/tester/web/Redirect03.jsp b/container/tester/web/Redirect03.jsp
new file mode 100644
index 0000000..1bef14b
--- /dev/null
+++ b/container/tester/web/Redirect03.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %><% String dummy = null; if (dummy == null) { response.sendRedirect(request.getContextPath() + "/Redirect03a.jsp");  } %>Redirect03.jsp FAILED - Content should not be visible
diff --git a/container/tester/web/Redirect03a.jsp b/container/tester/web/Redirect03a.jsp
new file mode 100644
index 0000000..d1e030b
--- /dev/null
+++ b/container/tester/web/Redirect03a.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %>Redirect03a.jsp PASSED
diff --git a/container/tester/web/ResponseWrap01b.jsp b/container/tester/web/ResponseWrap01b.jsp
new file mode 100644
index 0000000..ca54c9b
--- /dev/null
+++ b/container/tester/web/ResponseWrap01b.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain"%>ResponseWrap01b PASSED
diff --git a/container/tester/web/ResponseWrap01d.jsp b/container/tester/web/ResponseWrap01d.jsp
new file mode 100644
index 0000000..c95e7be
--- /dev/null
+++ b/container/tester/web/ResponseWrap01d.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain"%>ResponseWrap01d PASSED
diff --git a/container/tester/web/SSIConditional01.shtml b/container/tester/web/SSIConditional01.shtml
new file mode 100644
index 0000000..fb55c4a
--- /dev/null
+++ b/container/tester/web/SSIConditional01.shtml
@@ -0,0 +1,10 @@
+Before if block.
+<!--#set var="test" value="value1" -->
+<!--#if expr="\"$test\" = \"value1\"" -->
+test = value1
+<!--#elif expr="\"$test\" = \"value2\"" -->
+test = value2
+<!--#else -->
+test = neither value1 or value2
+<!--#endif -->
+After if block.
diff --git a/container/tester/web/SSIConditional02.shtml b/container/tester/web/SSIConditional02.shtml
new file mode 100644
index 0000000..4dc7f24
--- /dev/null
+++ b/container/tester/web/SSIConditional02.shtml
@@ -0,0 +1,10 @@
+Before if block.
+<!--#set var="test" value="value2" -->
+<!--#if expr="\"$test\" = \"value1\"" -->
+test = value1
+<!--#elif expr="\"$test\" = \"value2\"" -->
+test = value2
+<!--#else -->
+test = neither value1 or value2
+<!--#endif -->
+After if block.
diff --git a/container/tester/web/SSIConditional03.shtml b/container/tester/web/SSIConditional03.shtml
new file mode 100644
index 0000000..588d7b2
--- /dev/null
+++ b/container/tester/web/SSIConditional03.shtml
@@ -0,0 +1,8 @@
+Before if block.
+<!--#set var="test" value="unquoted multi-word value" -->
+<!--#if expr="\"$test\" = unquoted multi-word value" -->
+test = "unquoted multi-word value"
+<!--#else -->
+test = not "unquoted multi-word value"
+<!--#endif -->
+After if block.
diff --git a/container/tester/web/SSIConditional04.shtml b/container/tester/web/SSIConditional04.shtml
new file mode 100644
index 0000000..55d50f7
--- /dev/null
+++ b/container/tester/web/SSIConditional04.shtml
@@ -0,0 +1,8 @@
+<!--#set var="test1" value="not one" -->
+<!--#set var="test2" value="two" -->
+<!--#set var="test3" value="three" -->
+<!--#if expr="\"$test1\" = \"one\" && \"$test2\" = \"two\" || \"$test3\" = \"three\"" -->
+true
+<!--#else -->
+false
+<!--#endif -->
diff --git a/container/tester/web/SSIConditional05.shtml b/container/tester/web/SSIConditional05.shtml
new file mode 100644
index 0000000..31e10e9
--- /dev/null
+++ b/container/tester/web/SSIConditional05.shtml
@@ -0,0 +1,8 @@
+<!--#set var="test1" value="not one" -->
+<!--#set var="test2" value="two" -->
+<!--#set var="test3" value="three" -->
+<!--#if expr="\"$test1\" = \"one\" && (\"$test2\" = \"two\" || \"$test3\" = \"three\")" -->
+true
+<!--#else -->
+false
+<!--#endif -->
diff --git a/container/tester/web/SSIConditional06.shtml b/container/tester/web/SSIConditional06.shtml
new file mode 100644
index 0000000..14cf1d4
--- /dev/null
+++ b/container/tester/web/SSIConditional06.shtml
@@ -0,0 +1,16 @@
+<!--#set var="test1" value="not one" -->
+<!--#set var="test2" value="two" -->
+<!--#set var="test3" value="three" -->
+<!--#if expr="\"$test1\" = \"one\" || \"$test2\" = \"two\"" -->
+<!--#if expr="\"$test1\" = \"one\"" -->
+one
+<!--#elif expr="\"$test1\" = \"not one\"" -->
+not one
+<!--#endif -->
+<!--#else -->
+<!--#if expr="\"$test2\" = \"two\"" -->
+two
+<!--#elif expr="\"$test2\" = \"not two\"" -->
+not two
+<!--#endif -->
+<!--#endif -->
diff --git a/container/tester/web/SSIConditional07.shtml b/container/tester/web/SSIConditional07.shtml
new file mode 100644
index 0000000..ec450a2
--- /dev/null
+++ b/container/tester/web/SSIConditional07.shtml
@@ -0,0 +1,16 @@
+<!--#set var="test1" value="not one" -->
+<!--#set var="test2" value="not two" -->
+<!--#set var="test3" value="three" -->
+<!--#if expr="\"$test1\" = \"one\" || \"$test2\" = \"two\"" -->
+<!--#if expr="\"$test1\" = \"one\"" -->
+one
+<!--#elif expr="\"$test1\" = \"not one\"" -->
+not one
+<!--#endif -->
+<!--#else -->
+<!--#if expr="\"$test2\" = \"two\"" -->
+two
+<!--#elif expr="\"$test2\" = \"not two\"" -->
+not two
+<!--#endif -->
+<!--#endif -->
diff --git a/container/tester/web/SSIConditional08.shtml b/container/tester/web/SSIConditional08.shtml
new file mode 100644
index 0000000..7de645f
--- /dev/null
+++ b/container/tester/web/SSIConditional08.shtml
@@ -0,0 +1,7 @@
+<!--#set var="test" value="one" -->
+<!--#if expr="\"$test\" = \"one\"" -->
+<!--#set var="test" value="two" -->
+<!--#else -->
+<!--#set var="test" value="three" -->
+<!--#endif -->
+<!--#echo var="test" -->
diff --git a/container/tester/web/SSIConfig01.shtml b/container/tester/web/SSIConfig01.shtml
new file mode 100644
index 0000000..1f6d9bf
--- /dev/null
+++ b/container/tester/web/SSIConfig01.shtml
@@ -0,0 +1,2 @@
+<!--#config errmsg="errmsg:Sorry this command won't work." -->
+<!--#fsize file="foo.shtml" -->
diff --git a/container/tester/web/SSIConfig03.shtml b/container/tester/web/SSIConfig03.shtml
new file mode 100644
index 0000000..b425cd9
--- /dev/null
+++ b/container/tester/web/SSIConfig03.shtml
@@ -0,0 +1,4 @@
+<!--#config sizefmt="bytes" -->
+<!--#fsize file="includeme.txt" -->
+<!--#config sizefmt="abbrev" -->
+<!--#fsize file="includeme.txt" -->
diff --git a/container/tester/web/SSIExecCGI.shtml b/container/tester/web/SSIExecCGI.shtml
new file mode 100644
index 0000000..60604f9
--- /dev/null
+++ b/container/tester/web/SSIExecCGI.shtml
@@ -0,0 +1,38 @@
+<html> 
+  <head> 
+ <title>exec command  test examples </title> 
+ </head> 
+ <body>
+
+ <!--#exec cgi="/cgi-bin/array.pl" --><br>
+
+ <!--#exec cgi="/cgi-bin/binary.pl?counter=102" --><br>
+
+ <!--#exec cgi="/cgi-bin/chores.pl" --><br>
+
+ <!--#exec cgi="/cgi-bin/concat.pl?first_name=jane&last_name=johnson&fiance_first=john&fiance_last=smith" --><br>
+
+ <!--#exec cgi="/cgi-bin/days.pl" --><br>
+
+ <!--#exec cgi="/cgi-bin/dowhile.pl?start=10" --><br>
+
+ <!--#exec cgi="/cgi-bin/else.pl?food=spinach" --><br>
+
+ <!--#exec cgi="/cgi-bin/elsif.pl?food=chocolate" --><br>
+
+ <!--#exec cgi="/cgi-bin/exponents.pl?number=2&power=4" --><br>
+
+ <!--#exec cgi="/cgi-bin/for.pl?start=10" --><br>
+
+ <!--#exec cgi="/cgi-bin/getday.pl" --><br>
+
+ <!--#exec cgi="/cgi-bin/helloperl.cgi" --><br>
+
+ <!--#exec cgi="/cgi-bin/if.pl?food=spinach" --><br>
+
+ <!--#exec cgi="/cgi-bin/increment.pl?counter=7" --><br>
+
+ <!--#exec cgi="/cgi-bin/modifyall.pl?number=289" --><br>
+
+ </body> 
+ </html>
diff --git a/container/tester/web/SSIExecCmd.shtml b/container/tester/web/SSIExecCmd.shtml
new file mode 100644
index 0000000..a23e074
--- /dev/null
+++ b/container/tester/web/SSIExecCmd.shtml
@@ -0,0 +1,23 @@
+<html>
+  <head>
+ <title>exec command  test examples </title>
+ </head>
+ <body>
+ 
+ <h3>hello.exe
+      <!--#exec cmd="hello.exe" --> </h3>
+       using &lt;!--#exec cmd="hello.exe" --&gt; <br>
+       
+ <h3> The UNIX date of the server ::
+      <!--#exec cmd="date" --> </h3>
+      using &lt;!--#exec cmd="date" --&gt; <br>
+
+ <hr>
+
+ <h3>The current working directory ::
+      <!--#exec cmd="pwd" --> </h3>
+       using &lt;!--#exec cmd="pwd" --&gt; <br>
+
+
+ </body>
+ </html>
diff --git a/container/tester/web/SSIFsize01.shtml b/container/tester/web/SSIFsize01.shtml
new file mode 100644
index 0000000..af97597
--- /dev/null
+++ b/container/tester/web/SSIFsize01.shtml
@@ -0,0 +1 @@
+<!--#fsize -->
diff --git a/container/tester/web/SSIFsize02.shtml b/container/tester/web/SSIFsize02.shtml
new file mode 100644
index 0000000..62cde8c
--- /dev/null
+++ b/container/tester/web/SSIFsize02.shtml
@@ -0,0 +1 @@
+<!--#fsize file="includeme.txt" -->
diff --git a/container/tester/web/SSIFsize03.shtml b/container/tester/web/SSIFsize03.shtml
new file mode 100644
index 0000000..af373be
--- /dev/null
+++ b/container/tester/web/SSIFsize03.shtml
@@ -0,0 +1 @@
+<!--#fsize file="./includeme.txt" -->
diff --git a/container/tester/web/SSIFsize04.shtml b/container/tester/web/SSIFsize04.shtml
new file mode 100644
index 0000000..f6791ec
--- /dev/null
+++ b/container/tester/web/SSIFsize04.shtml
@@ -0,0 +1 @@
+<!--#fsize virtual="includeme.txt" -->
diff --git a/container/tester/web/SSIFsize05.shtml b/container/tester/web/SSIFsize05.shtml
new file mode 100644
index 0000000..f09fcfa
--- /dev/null
+++ b/container/tester/web/SSIFsize05.shtml
@@ -0,0 +1 @@
+<!--#fsize virtual="/tester/includeme.txt" -->
diff --git a/container/tester/web/SSIFsize06.shtml b/container/tester/web/SSIFsize06.shtml
new file mode 100644
index 0000000..b3eb34e
--- /dev/null
+++ b/container/tester/web/SSIFsize06.shtml
@@ -0,0 +1 @@
+<!--#fsize virtual="ssidir/includeme.txt" -->
diff --git a/container/tester/web/SSIFsize07.shtml b/container/tester/web/SSIFsize07.shtml
new file mode 100644
index 0000000..afc8767
--- /dev/null
+++ b/container/tester/web/SSIFsize07.shtml
@@ -0,0 +1 @@
+<!--#fsize virtual="./ssidir/includeme.txt" -->
diff --git a/container/tester/web/SSIFsize08.shtml b/container/tester/web/SSIFsize08.shtml
new file mode 100644
index 0000000..f91fac2
--- /dev/null
+++ b/container/tester/web/SSIFsize08.shtml
@@ -0,0 +1,2 @@
+<!--#fsize file="includeme.txt"
+  -->
diff --git a/container/tester/web/SSIInclude01.shtml b/container/tester/web/SSIInclude01.shtml
new file mode 100644
index 0000000..7c8b416
--- /dev/null
+++ b/container/tester/web/SSIInclude01.shtml
@@ -0,0 +1 @@
+<!--#include -->
diff --git a/container/tester/web/SSIInclude02.shtml b/container/tester/web/SSIInclude02.shtml
new file mode 100644
index 0000000..46a3270
--- /dev/null
+++ b/container/tester/web/SSIInclude02.shtml
@@ -0,0 +1 @@
+<!--#include file="includeme.txt" -->
diff --git a/container/tester/web/SSIInclude03.shtml b/container/tester/web/SSIInclude03.shtml
new file mode 100644
index 0000000..68d4e94
--- /dev/null
+++ b/container/tester/web/SSIInclude03.shtml
@@ -0,0 +1 @@
+<!--#include file="./includeme.txt" -->
diff --git a/container/tester/web/SSIInclude04.shtml b/container/tester/web/SSIInclude04.shtml
new file mode 100644
index 0000000..fc710a0
--- /dev/null
+++ b/container/tester/web/SSIInclude04.shtml
@@ -0,0 +1 @@
+<!--#include virtual="includeme.txt" -->
diff --git a/container/tester/web/SSIInclude05.shtml b/container/tester/web/SSIInclude05.shtml
new file mode 100644
index 0000000..2bb198a
--- /dev/null
+++ b/container/tester/web/SSIInclude05.shtml
@@ -0,0 +1 @@
+<!--#include virtual="/tester/includeme.txt" -->
diff --git a/container/tester/web/SSIInclude06.shtml b/container/tester/web/SSIInclude06.shtml
new file mode 100644
index 0000000..bb0b0d0
--- /dev/null
+++ b/container/tester/web/SSIInclude06.shtml
@@ -0,0 +1 @@
+<!--#include virtual="ssidir/includeme.txt" -->
diff --git a/container/tester/web/SSIInclude07.shtml b/container/tester/web/SSIInclude07.shtml
new file mode 100644
index 0000000..be98a0f
--- /dev/null
+++ b/container/tester/web/SSIInclude07.shtml
@@ -0,0 +1 @@
+<!--#include virtual="./ssidir/includeme.txt" -->
diff --git a/container/tester/web/SSIInclude08.shtml b/container/tester/web/SSIInclude08.shtml
new file mode 100644
index 0000000..6023c6f
--- /dev/null
+++ b/container/tester/web/SSIInclude08.shtml
@@ -0,0 +1,2 @@
+<!--#include file="includeme.txt"
+  -->
diff --git a/container/tester/web/SSIInclude09.shtml b/container/tester/web/SSIInclude09.shtml
new file mode 100644
index 0000000..3ea51bf
--- /dev/null
+++ b/container/tester/web/SSIInclude09.shtml
@@ -0,0 +1,6 @@
+<!--#include file="includeme.txt"
+  -->
+<!--#include file="SSIInclude08.shtml"
+  -->
+<!--#include file="includeme.txt"
+  -->
diff --git a/container/tester/web/SSIVarSub01.shtml b/container/tester/web/SSIVarSub01.shtml
new file mode 100644
index 0000000..de82e6b
--- /dev/null
+++ b/container/tester/web/SSIVarSub01.shtml
@@ -0,0 +1,3 @@
+<!--#set var="test" value="$DOCUMENT_URI" -->
+<!--#echo var="test" -->
+
diff --git a/container/tester/web/SSIVarSub02.shtml b/container/tester/web/SSIVarSub02.shtml
new file mode 100644
index 0000000..ebf81a4
--- /dev/null
+++ b/container/tester/web/SSIVarSub02.shtml
@@ -0,0 +1,4 @@
+<!--#set var="test" value="value of test" -->
+<!--#set var="test2" value="${test}" -->
+<!--#echo var="test2" -->
+
diff --git a/container/tester/web/SSIVarSub03.shtml b/container/tester/web/SSIVarSub03.shtml
new file mode 100644
index 0000000..076a772
--- /dev/null
+++ b/container/tester/web/SSIVarSub03.shtml
@@ -0,0 +1,5 @@
+<!--#set var="test" value="value of test" -->
+<!--#set var="test2" value="value of test2" -->
+<!--#set var="test3" value="$test $test2" -->
+<!--#echo var="test3" -->
+
diff --git a/container/tester/web/SSIVarSub04.shtml b/container/tester/web/SSIVarSub04.shtml
new file mode 100644
index 0000000..aa6d991
--- /dev/null
+++ b/container/tester/web/SSIVarSub04.shtml
@@ -0,0 +1,5 @@
+<!--#set var="test" value="value of test" -->
+<!--#set var="test2" value="value of test2" -->
+<!--#set var="test3" value="${test} ${test2}" -->
+<!--#echo var="test3" -->
+
diff --git a/container/tester/web/SSIVarSub05.shtml b/container/tester/web/SSIVarSub05.shtml
new file mode 100644
index 0000000..5016381
--- /dev/null
+++ b/container/tester/web/SSIVarSub05.shtml
@@ -0,0 +1,5 @@
+<!--#set var="test" value="value of test" -->
+<!--#set var="test2" value="value of test2" -->
+<!--#set var="test3" value="${test}|${test2}" -->
+<!--#echo var="test3" -->
+
diff --git a/container/tester/web/SSIVarSub06.shtml b/container/tester/web/SSIVarSub06.shtml
new file mode 100644
index 0000000..a308bc7
--- /dev/null
+++ b/container/tester/web/SSIVarSub06.shtml
@@ -0,0 +1,3 @@
+<!--#set var="test" value="value of test" -->
+<!--#set var="test3" value="${test}\${test2}" -->
+<!--#echo var="test3" -->
diff --git a/container/tester/web/Session07a.jsp b/container/tester/web/Session07a.jsp
new file mode 100644
index 0000000..1d14165
--- /dev/null
+++ b/container/tester/web/Session07a.jsp
@@ -0,0 +1,5 @@
+<%@ page contentType="text/plain" %><jsp:useBean id="simpleBean"
+    scope="session" class="org.apache.tester.SessionBean"/><%
+  simpleBean.setStringProperty("From Session07a");
+  response.sendRedirect("Session07b.jsp");
+%>
diff --git a/container/tester/web/Session07b.jsp b/container/tester/web/Session07b.jsp
new file mode 100644
index 0000000..8a3daa9
--- /dev/null
+++ b/container/tester/web/Session07b.jsp
@@ -0,0 +1,9 @@
+<%@ page contentType="text/plain" %><jsp:useBean id="simpleBean"
+    scope="session" class="org.apache.tester.SessionBean"/><%
+  if ("From Session07a".equals(simpleBean.getStringProperty())) {
+    out.println("Session07 PASSED");
+  } else {
+    out.println("Session07 FAILED - Property = '" +
+                simpleBean.getStringProperty() + "'");
+  }
+%>
diff --git a/container/tester/web/WEB-INF/cgi/array.pl b/container/tester/web/WEB-INF/cgi/array.pl
new file mode 100644
index 0000000..b61435d
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/array.pl
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+
+print "Content-type: text/html\n\n";
+
+@days = ("Monday", 
+         "Tuesday", 
+         "Wednesday", 
+         "Thursday", 
+         "Friday", 
+         "Saturday", 
+         "Sunday");
+
+print "These are the days of the week:";
+
+print "<p>";
+print "@days";
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/binary.pl b/container/tester/web/WEB-INF/cgi/binary.pl
new file mode 100644
index 0000000..def0d74
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/binary.pl
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+
+$counter = $formdata{'counter'};
+
+$counter +=1;
+
+print "Content-type: text/html\n\n";
+print "<FONT SIZE=+2>If you add one to your number, the result is <B>$counter</B>";
+
+$counter +=5;
+print "<P>If you add 5 to that, the result is <B>$counter</B></FONT>";
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/concat.pl b/container/tester/web/WEB-INF/cgi/concat.pl
new file mode 100644
index 0000000..aebaa10
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/concat.pl
@@ -0,0 +1,12 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+
+$first = $formdata{'first_name'};
+$married = $formdata{'fiance_last'};
+
+$fullname = $first . " " . $married;
+
+print "Content-type: text/html\n\n";
+print "Congratulations! Your married name would be $fullname.";
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/days.pl b/container/tester/web/WEB-INF/cgi/days.pl
new file mode 100644
index 0000000..41b4341
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/days.pl
@@ -0,0 +1,8 @@
+#!/usr/bin/perl
+
+@days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday");
+
+print "Content-type: text/html\n\n";
+print "These are the days of the week:";
+print "<P>";
+print "@days";
diff --git a/container/tester/web/WEB-INF/cgi/dowhile.pl b/container/tester/web/WEB-INF/cgi/dowhile.pl
new file mode 100644
index 0000000..cded9da
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/dowhile.pl
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+
+$start = $formdata{'start'};
+
+print "Content-type: text/html\n\n";
+print "<P>Starting countdown...";
+
+do {
+	print "$start... ";
+	--$start;
+} while ($start > 0);
+
+print "KABOOM!";
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/else.pl b/container/tester/web/WEB-INF/cgi/else.pl
new file mode 100644
index 0000000..f299760
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/else.pl
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+
+$food = $formdata{'food'};
+
+print "Content-type: text/html\n\n";
+
+if ($food eq "spinach") {
+	
+	print "You ate spinach, so you get dessert!";
+} else {
+	print "No spinach, no dessert!";
+}
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/elsif.pl b/container/tester/web/WEB-INF/cgi/elsif.pl
new file mode 100644
index 0000000..97a28e6
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/elsif.pl
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+
+$food = $formdata{'food'};
+
+print "Content-type: text/html\n\n";
+
+if ($food eq "spinach") {
+	
+	print "You ate spinach, so you get dessert!";
+} elsif ($food eq "broccoli") {
+	print "Broccoli's OK. Maybe you'll get dessert.";
+} else {
+	print "No spinach, no dessert!";
+}
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/exponents.pl b/container/tester/web/WEB-INF/cgi/exponents.pl
new file mode 100644
index 0000000..94db11c
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/exponents.pl
@@ -0,0 +1,13 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+
+$number = $formdata{'number'};
+$power = $formdata{'power'};
+
+$result = $number ** $power;
+
+print "Content-type: text/html\n\n";
+print "<P>You entered $number with an exponent of $power";
+print "<P>$number raised to the $power power is $result.";
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/for.pl b/container/tester/web/WEB-INF/cgi/for.pl
new file mode 100644
index 0000000..9bfc918
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/for.pl
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+
+$start = $formdata{'start'};
+
+print "Content-type: text/html\n\n";
+print "<P>Starting countdown...";
+
+for ($i = $start; $i > 0; --$i) {
+	print "$i... ";
+} 
+
+print "KABOOM!";
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/getday.pl b/container/tester/web/WEB-INF/cgi/getday.pl
new file mode 100644
index 0000000..e3979e7
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/getday.pl
@@ -0,0 +1,8 @@
+#!/usr/bin/perl
+
+@days = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday);
+
+print "Content-type: text/html\n\n";
+print "The first day of the week is $days[0]";
+
+print "<P>The third day of the week is $days[2]";
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/helloperl.pl b/container/tester/web/WEB-INF/cgi/helloperl.pl
new file mode 100644
index 0000000..f9caa7b
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/helloperl.pl
@@ -0,0 +1,9 @@
+#!/usr/bin/perl
+
+	$t	= "Hello World!";
+	print	<<EOT;
+Content-type: text/html
+
+	<Title> $t </Title>
+	<H1>	$t </H1>
+EOT
diff --git a/container/tester/web/WEB-INF/cgi/if.pl b/container/tester/web/WEB-INF/cgi/if.pl
new file mode 100644
index 0000000..3fd2320
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/if.pl
@@ -0,0 +1,13 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+
+$food = $formdata{'food'};
+
+
+
+if ($food eq "spinach") {
+	print "Content-type: text/html\n\n";
+	print "You ate spinach, so you get dessert!\n";
+}
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/increment.pl b/container/tester/web/WEB-INF/cgi/increment.pl
new file mode 100644
index 0000000..121169d
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/increment.pl
@@ -0,0 +1,19 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+print "Content-type: text/html\n\n";
+
+$counter = $formdata{'counter'};
+
+++$counter;
+print "Your number incremented by 1 is $counter";
+$watch = ++$counter;
+print "<BR>Your number, incremented again by 1, is now $counter. If we store that operation, its value is also $watch.";
+
+$counter = $formdata{'counter'};
+print "<HR>Let's start over, with your original number $counter";
+$counter++;
+print "<BR>Again, your number incremented by 1 is $counter";
+$watch = $counter++;
+print "<BR>Now we store the value of your number in a second variable, which is now equal to $watch, and then we increment your number again. It's now $counter.";
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/modifyall.pl b/container/tester/web/WEB-INF/cgi/modifyall.pl
new file mode 100644
index 0000000..4c08969
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/modifyall.pl
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+require "subparseform.lib";
+&Parse_Form;
+
+@numbers = split(/,/, $formdata{'number'});
+print "Content-type: text/html\n\n";
+
+print "The numbers you entered were:";
+foreach $number (@numbers) {
+	print "<LI>$number";
+	}
+	
+foreach $number(@numbers) {
+	$number = sqrt($number);
+	}
+
+print "<P>The square roots of those numbers are: ";
+
+foreach $number(@numbers) {
+	print "<LI>$number";
+	}
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/preference.pl b/container/tester/web/WEB-INF/cgi/preference.pl
new file mode 100644
index 0000000..51b75ec
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/preference.pl
@@ -0,0 +1,9 @@
+#!/usr/bin/perl
+
+$div = 40 / 5 + 3;
+$parens = 40 / (5 + 3);
+$subadd = 9 - 3 + 5;
+$parens_subadd = 9 - (3 + 5);
+
+print "Content-type: text/html\n\n";
+print "div is $div, parens is $parens, subadd is $subadd and parens_subadd is $parens_subadd";
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/subparseform.lib b/container/tester/web/WEB-INF/cgi/subparseform.lib
new file mode 100644
index 0000000..c936be7
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/subparseform.lib
@@ -0,0 +1,43 @@
+sub Parse_Form 
+{
+if ($ENV{'REQUEST_METHOD'} eq 'GET') 
+{
+        @pairs = split(/&/, $ENV{'QUERY_STRING'});
+} 
+elsif ($ENV{'REQUEST_METHOD'} eq 'POST') 
+{
+    read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
+    @pairs = split(/&/, $buffer);
+		
+    if ($ENV{'QUERY_STRING'}) 
+    {
+        @getpairs =split(/&/, $ENV{'QUERY_STRING'});
+        push(@pairs,@getpairs);
+    }
+} 
+else 
+{
+    print "Content-type: text/html\n\n";
+    print "<P>Use Post or Get";
+}
+
+foreach $pair (@pairs) 
+{
+    ($key, $value) = split (/=/, $pair);
+    $key =~ tr/+/ /;
+    $key =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+    $value =~ tr/+/ /;
+    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+    $value =~s/<!--(.|\n)*-->//g;
+	
+    if ($formdata{$key}) 
+    {
+        $formdata{$key} .= ", $value";
+    } 
+    else 
+    {
+        $formdata{$key} = $value;
+    }
+}
+}	
+1;
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/subroutines.lib b/container/tester/web/WEB-INF/cgi/subroutines.lib
new file mode 100644
index 0000000..566a8aa
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/subroutines.lib
@@ -0,0 +1,34 @@
+#!/usr/local/bin/perl;
+
+sub cap {
+	$captext = $_[0];
+	$captext =~ tr/a-z/A-Z/;
+	return $captext;
+}
+
+sub which {
+	$browser = $ENV{'HTTP_USER_AGENT'};
+	if ($browser =~ /MSIE/) {
+		$browser = "Explorer";
+	} elsif ($browser =~/Mozilla/) {
+		$browser = "Netscape";
+	} else {
+		$browser = "something besides Netscape and Explorer";
+	}
+}
+
+sub header {
+	print "<HTML><HEAD><TITLE>";
+	print "$_[0]";
+	print "<TITLE><HEAD><BODY>"
+}
+
+sub footer {
+	print "<BODY><HTML>";
+}
+
+sub mime {
+	print "Content-type: text/html\n\n";
+}
+
+1;
\ No newline at end of file
diff --git a/container/tester/web/WEB-INF/cgi/test-cgi.pl b/container/tester/web/WEB-INF/cgi/test-cgi.pl
new file mode 100644
index 0000000..06da9bd
--- /dev/null
+++ b/container/tester/web/WEB-INF/cgi/test-cgi.pl
@@ -0,0 +1,93 @@
+#!/usr/local/bin/perl
+#
+# ========================================================================= *
+#                                                                           *
+#                 The Apache Software License,  Version 1.1                 *
+#                                                                           *
+#     Copyright (c) 1999, 2000, 2001  The Apache Software Foundation.       *
+#                           All rights reserved.                            *
+#                                                                           *
+# ========================================================================= *
+#                                                                           *
+# Redistribution and use in source and binary forms,  with or without modi- *
+# fication, are permitted provided that the following conditions are met:   *
+#                                                                           *
+# 1. Redistributions of source code  must retain the above copyright notice *
+#    notice, this list of conditions and the following disclaimer.          *
+#                                                                           *
+# 2. Redistributions  in binary  form  must  reproduce the  above copyright *
+#    notice,  this list of conditions  and the following  disclaimer in the *
+#    documentation and/or other materials provided with the distribution.   *
+#                                                                           *
+# 3. The end-user documentation  included with the redistribution,  if any, *
+#    must include the following acknowlegement:                             *
+#                                                                           *
+#       "This product includes  software developed  by the Apache  Software *
+#        Foundation <http://www.apache.org/>."                              *
+#                                                                           *
+#    Alternately, this acknowlegement may appear in the software itself, if *
+#    and wherever such third-party acknowlegements normally appear.         *
+#                                                                           *
+# 4. The names  "The  Jakarta  Project",  "Tomcat",  and  "Apache  Software *
+#    Foundation"  must not be used  to endorse or promote  products derived *
+#    from this  software without  prior  written  permission.  For  written *
+#    permission, please contact <apache@apache.org>.                        *
+#                                                                           *
+# 5. Products derived from this software may not be called "Apache" nor may *
+#    "Apache" appear in their names without prior written permission of the *
+#    Apache Software Foundation.                                            *
+#                                                                           *
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES *
+# INCLUDING, BUT NOT LIMITED TO,  THE IMPLIED WARRANTIES OF MERCHANTABILITY *
+# AND FITNESS FOR  A PARTICULAR PURPOSE  ARE DISCLAIMED.  IN NO EVENT SHALL *
+# THE APACHE  SOFTWARE  FOUNDATION OR  ITS CONTRIBUTORS  BE LIABLE  FOR ANY *
+# DIRECT,  INDIRECT,   INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR  CONSEQUENTIAL *
+# DAMAGES (INCLUDING,  BUT NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE GOODS *
+# OR SERVICES;  LOSS OF USE,  DATA,  OR PROFITS;  OR BUSINESS INTERRUPTION) *
+# HOWEVER CAUSED AND  ON ANY  THEORY  OF  LIABILITY,  WHETHER IN  CONTRACT, *
+# STRICT LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
+# ANY  WAY  OUT OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF  ADVISED  OF THE *
+# POSSIBILITY OF SUCH DAMAGE.                                               *
+#                                                                           *
+# ========================================================================= *
+#                                                                           *
+# This software  consists of voluntary  contributions made  by many indivi- *
+# duals on behalf of the  Apache Software Foundation.  For more information *
+# on the Apache Software Foundation, please see <http://www.apache.org/>.   *
+#                                                                           *
+# ========================================================================= */
+#
+
+print <<EOM;
+Content-type: text/html
+
+<html>
+<head><title>CGI Test</title></head>
+<body>
+<h1>CGI Test</h1>
+<pre>
+EOM
+
+print "argc is " . ($#ARGV + 1) . "\n\n";
+
+print "argv is\n";
+
+for($i = 0; $i<($#ARGV+1); $i++) {
+	print $ARGV[$i] . "\n";
+}
+
+print "\n";
+	
+foreach $key (sort keys %ENV) {
+
+	print("$key: $ENV{$key}\n");
+
+}
+
+print <<EOM;
+</pre>
+</body>
+</html>
+EOM
+
+close OUT;
diff --git a/container/tester/web/WEB-INF/web.xml b/container/tester/web/WEB-INF/web.xml
new file mode 100644
index 0000000..1aaed42
--- /dev/null
+++ b/container/tester/web/WEB-INF/web.xml
@@ -0,0 +1,2056 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+    <!-- ========== Filter Definitions ==================================== -->
+
+    <filter>
+        <filter-name>GenericFilter</filter-name>
+        <filter-class>org.apache.tester.WrapperFilter</filter-class>
+        <init-param>
+            <param-name>request</param-name>
+            <param-value>generic</param-value>
+        </init-param>
+        <init-param>
+            <param-name>response</param-name>
+            <param-value>generic</param-value>
+        </init-param>
+    </filter>
+
+
+    <filter>
+        <filter-name>HttpFilter</filter-name>
+        <filter-class>org.apache.tester.WrapperFilter</filter-class>
+        <init-param>
+            <param-name>request</param-name>
+            <param-value>http</param-value>
+        </init-param>
+        <init-param>
+            <param-name>response</param-name>
+            <param-value>http</param-value>
+        </init-param>
+    </filter>
+
+
+    <filter>
+        <filter-name>StaticFilter</filter-name>
+        <filter-class>org.apache.tester.StaticFilter</filter-class>
+    </filter>
+
+
+    <filter>
+        <filter-name>UpperCaseFilter</filter-name>
+        <filter-class>org.apache.tester.UpperCaseFilter</filter-class>
+    </filter>
+
+
+    <!-- ========== Filter Mappings ======================================= -->
+
+    <!-- Use StaticFilter on *all* requests to clear static log -->
+    <filter-mapping>
+        <filter-name>StaticFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedAggregate01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedAggregate02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedContext00</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedContext01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedContext02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedDecoding01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedErrorPage01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedErrorPage02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedErrorPage03</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedErrorPage04</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedErrorPage05</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedErrorPage06</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedErrorPage07</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedErrorPage08</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedErrorPage09</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>UpperCaseFilter</filter-name>
+        <url-pattern>/FilterRequest01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedFilterRequest01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>UpperCaseFilter</filter-name>
+        <url-pattern>/WrappedFilterRequest01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedFilterRequest02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>UpperCaseFilter</filter-name>
+        <url-pattern>/FilterResponse01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedFilterResponse01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>UpperCaseFilter</filter-name>
+        <url-pattern>/WrappedFilterResponse01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>UpperCaseFilter</filter-name>
+        <url-pattern>/FilterResponse02.jsp</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedFilterResponse02.jsp</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>UpperCaseFilter</filter-name>
+        <url-pattern>/WrappedFilterResponse02.jsp</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>UpperCaseFilter</filter-name>
+        <url-pattern>/FilterResponse03.txt</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedFilterResponse03.txt</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>UpperCaseFilter</filter-name>
+        <url-pattern>/WrappedFilterResponse03.txt</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedFilterResponse04</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedForward00</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedForward01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedForward03</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedForward04</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedForward05</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedForward09</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedGetHeaders01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>GenericFilter</filter-name>
+        <url-pattern>/WrappedGetInputStream01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedGetLocales01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedGetLocales02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>GenericFilter</filter-name>
+        <url-pattern>/WrappedGetParameter01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>GenericFilter</filter-name>
+        <url-pattern>/WrappedGetParameterMap00</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedGetQueryString01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedGolden01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedInclude00</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedInclude01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedInclude02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedInclude03</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedInclude04</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedInclude09</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedInclude10</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedJndi01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedJndi02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedLifecycle01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedLifecycle02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedLifecycle03</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedRedirect01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedReflection01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedRequest01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>GenericFilter</filter-name>
+        <url-pattern>/WrappedReset01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedResources01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedResources02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedResources03</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedResources04</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedResources05</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedResources06</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedSession01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedSession02</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedSession03</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedSession04</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedSession05</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedSession06</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>GenericFilter</filter-name>
+        <url-pattern>/WrappedSetBufferSize01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>GenericFilter</filter-name>
+        <url-pattern>/WrappedSetLocale01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedXerces00</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedXerces01</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>HttpFilter</filter-name>
+        <url-pattern>/WrappedXerces02</url-pattern>
+    </filter-mapping>
+
+
+    <!-- ========== Listener Definitions ================================== -->
+
+    <listener>
+        <listener-class>org.apache.tester.ContextListener01</listener-class>
+    </listener>
+
+<!-- Can not run PropertyEditor tests under security manager unless
+     catalina.policy allows read/write access to system properties -->
+<!--
+    <listener>
+        <listener-class>org.apache.tester.ContextListener02</listener-class>
+    </listener>
+-->
+
+    <listener>
+        <listener-class>org.apache.tester.RequestListener01</listener-class>
+    </listener>
+
+    <listener>
+        <listener-class>org.apache.tester.SessionListener01</listener-class>
+    </listener>
+
+    <listener>
+        <listener-class>org.apache.tester.SessionListener02</listener-class>
+    </listener>
+
+
+    <!-- ========== Servlet Definitions =================================== -->
+
+    <servlet>
+        <servlet-name>Aggregate01</servlet-name>
+        <servlet-class>org.apache.tester.Aggregate01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Aggregate02</servlet-name>
+        <servlet-class>org.apache.tester.Aggregate02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Authentication01</servlet-name>
+        <servlet-class>org.apache.tester.Authentication01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Authentication02</servlet-name>
+        <servlet-class>org.apache.tester.Authentication02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Authentication03</servlet-name>
+        <servlet-class>org.apache.tester.Authentication03</servlet-class>
+        <security-role-ref>
+            <role-name>alias</role-name>
+            <role-link>tomcat</role-link>
+        </security-role-ref>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Authentication04</servlet-name>
+        <jsp-file>/Authentication04.jsp</jsp-file>
+        <security-role-ref>
+            <role-name>alias</role-name>
+            <role-link>tomcat</role-link>
+        </security-role-ref>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Authentication05</servlet-name>
+        <servlet-class>org.apache.tester.Authentication05</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Context00</servlet-name>
+        <servlet-class>org.apache.tester.Context00</servlet-class>
+        <load-on-startup>99</load-on-startup>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Context01</servlet-name>
+        <servlet-class>org.apache.tester.Context01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Context02</servlet-name>
+        <servlet-class>org.apache.tester.Context02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Decoding01</servlet-name>
+        <servlet-class>org.apache.tester.Decoding01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ErrorPage01</servlet-name>
+        <servlet-class>org.apache.tester.ErrorPage01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ErrorPage02</servlet-name>
+        <servlet-class>org.apache.tester.ErrorPage02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ErrorPage03</servlet-name>
+        <servlet-class>org.apache.tester.ErrorPage03</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ErrorPage04</servlet-name>
+        <servlet-class>org.apache.tester.ErrorPage04</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ErrorPage05</servlet-name>
+        <servlet-class>org.apache.tester.ErrorPage05</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ErrorPage06</servlet-name>
+        <servlet-class>org.apache.tester.ErrorPage06</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ErrorPage07</servlet-name>
+        <servlet-class>org.apache.tester.ErrorPage07</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ErrorPage08</servlet-name>
+        <jsp-file>/ErrorPage08.jsp</jsp-file>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ErrorPage09</servlet-name>
+        <jsp-file>/ErrorPage09.jsp</jsp-file>
+    </servlet>
+
+    <servlet>
+        <servlet-name>FilterRequest01</servlet-name>
+        <servlet-class>org.apache.tester.FilterRequest01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>FilterRequest02</servlet-name>
+        <servlet-class>org.apache.tester.FilterRequest02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>FilterRequest02a</servlet-name>
+        <servlet-class>org.apache.tester.FilterRequest02a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>FilterResponse01</servlet-name>
+        <servlet-class>org.apache.tester.FilterResponse01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>FilterResponse04</servlet-name>
+        <servlet-class>org.apache.tester.FilterResponse04</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>FilterResponse04a</servlet-name>
+        <servlet-class>org.apache.tester.FilterResponse04a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward00</servlet-name>
+        <servlet-class>org.apache.tester.Forward00</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward00a</servlet-name>
+        <servlet-class>org.apache.tester.Forward00a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward00b</servlet-name>
+        <jsp-file>/Forward00b.jsp</jsp-file>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward00d</servlet-name>
+        <servlet-class>org.apache.tester.Forward00d</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward00e</servlet-name>
+        <jsp-file>/Forward00e.jsp</jsp-file>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward01</servlet-name>
+        <servlet-class>org.apache.tester.Forward01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward03</servlet-name>
+        <servlet-class>org.apache.tester.Forward03</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward03a</servlet-name>
+        <servlet-class>org.apache.tester.Forward03a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward04</servlet-name>
+        <servlet-class>org.apache.tester.Forward04</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward04a</servlet-name>
+        <servlet-class>org.apache.tester.Forward04a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward04b</servlet-name>
+        <servlet-class>org.apache.tester.Forward04b</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward05</servlet-name>
+        <servlet-class>org.apache.tester.Forward05</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward05a</servlet-name>
+        <servlet-class>org.apache.tester.Forward05a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward05b</servlet-name>
+        <servlet-class>org.apache.tester.Forward05b</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward06</servlet-name>
+        <servlet-class>org.apache.tester.Forward06</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward06a</servlet-name>
+        <servlet-class>org.apache.tester.Forward06a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward06b</servlet-name>
+        <servlet-class>org.apache.tester.Forward06b</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward07</servlet-name>
+        <servlet-class>org.apache.tester.Forward07</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward07a</servlet-name>
+        <servlet-class>org.apache.tester.Forward07a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward07b</servlet-name>
+        <servlet-class>org.apache.tester.Forward07b</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward08</servlet-name>
+        <servlet-class>org.apache.tester.Forward08</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward08a</servlet-name>
+        <servlet-class>org.apache.tester.Forward08a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward08b</servlet-name>
+        <servlet-class>org.apache.tester.Forward08b</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Forward09</servlet-name>
+        <servlet-class>org.apache.tester.Forward09</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>GetHeaders01</servlet-name>
+        <servlet-class>org.apache.tester.GetHeaders01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>GetInputStream01</servlet-name>
+        <servlet-class>org.apache.tester.GetInputStream01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>GetLocales01</servlet-name>
+        <servlet-class>org.apache.tester.GetLocales01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>GetLocales02</servlet-name>
+        <servlet-class>org.apache.tester.GetLocales02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>GetParameter01</servlet-name>
+        <servlet-class>org.apache.tester.GetParameter01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>GetParameterMap00</servlet-name>
+        <servlet-class>org.apache.tester.GetParameterMap00</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>GetQueryString01</servlet-name>
+        <servlet-class>org.apache.tester.GetQueryString01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Golden01</servlet-name>
+        <servlet-class>org.apache.tester.Golden01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include00</servlet-name>
+        <servlet-class>org.apache.tester.Include00</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include00a</servlet-name>
+        <servlet-class>org.apache.tester.Include00a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include00b</servlet-name>
+        <jsp-file>/Include00b.jsp</jsp-file>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include00d</servlet-name>
+        <servlet-class>org.apache.tester.Include00d</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include00e</servlet-name>
+        <jsp-file>/Include00e.jsp</jsp-file>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include01</servlet-name>
+        <servlet-class>org.apache.tester.Include01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include02</servlet-name>
+        <servlet-class>org.apache.tester.Include02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include02a</servlet-name>
+        <servlet-class>org.apache.tester.Include02a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include03</servlet-name>
+        <servlet-class>org.apache.tester.Include03</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include03a</servlet-name>
+        <servlet-class>org.apache.tester.Include03a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include04</servlet-name>
+        <servlet-class>org.apache.tester.Include04</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include04a</servlet-name>
+        <servlet-class>org.apache.tester.Include04a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include04b</servlet-name>
+        <servlet-class>org.apache.tester.Include04b</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include06a</servlet-name>
+        <servlet-class>org.apache.tester.Include06a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include07</servlet-name>
+        <servlet-class>org.apache.tester.Include07</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include07a</servlet-name>
+        <servlet-class>org.apache.tester.Include07a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include07b</servlet-name>
+        <servlet-class>org.apache.tester.Include07b</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include07c</servlet-name>
+        <servlet-class>org.apache.tester.Include07c</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include09</servlet-name>
+        <servlet-class>org.apache.tester.Include09</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include10</servlet-name>
+        <servlet-class>org.apache.tester.Include10</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Include10a</servlet-name>
+        <servlet-class>org.apache.tester.Include10a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Jndi01</servlet-name>
+        <servlet-class>org.apache.tester.Jndi01</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Jndi02</servlet-name>
+        <servlet-class>org.apache.tester.Jndi02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Lifecycle01</servlet-name>
+        <servlet-class>org.apache.tester.Lifecycle01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Lifecycle02</servlet-name>
+        <servlet-class>org.apache.tester.Lifecycle02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Lifecycle03</servlet-name>
+        <servlet-class>org.apache.tester.Lifecycle03</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Redirect01</servlet-name>
+        <servlet-class>org.apache.tester.Redirect01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Redirect01a</servlet-name>
+        <servlet-class>org.apache.tester.Redirect01a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Reflection01</servlet-name>
+        <servlet-class>org.apache.tester.Reflection01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Request01</servlet-name>
+        <servlet-class>org.apache.tester.Request01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Reset01</servlet-name>
+        <servlet-class>org.apache.tester.Reset01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Resources01</servlet-name>
+        <servlet-class>org.apache.tester.Resources01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Resources02</servlet-name>
+        <servlet-class>org.apache.tester.Resources02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Resources03</servlet-name>
+        <servlet-class>org.apache.tester.Resources03</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Resources04</servlet-name>
+        <servlet-class>org.apache.tester.Resources04</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Resources05</servlet-name>
+        <servlet-class>org.apache.tester.Resources05</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Resources06</servlet-name>
+        <servlet-class>org.apache.tester.Resources06</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ResponseWrap01</servlet-name>
+        <servlet-class>org.apache.tester.ResponseWrap01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ResponseWrap01a</servlet-name>
+        <servlet-class>org.apache.tester.ResponseWrap01a</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ResponseWrap01c</servlet-name>
+        <servlet-class>org.apache.tester.ResponseWrap01c</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Session01</servlet-name>
+        <servlet-class>org.apache.tester.Session01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Session02</servlet-name>
+        <servlet-class>org.apache.tester.Session02</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Session03</servlet-name>
+        <servlet-class>org.apache.tester.Session03</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Session04</servlet-name>
+        <servlet-class>org.apache.tester.Session04</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Session05</servlet-name>
+        <servlet-class>org.apache.tester.Session05</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Session06</servlet-name>
+        <servlet-class>org.apache.tester.Session06</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>SetBufferSize01</servlet-name>
+        <servlet-class>org.apache.tester.SetBufferSize01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>SetLocale01</servlet-name>
+        <servlet-class>org.apache.tester.SetLocale01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Xerces00</servlet-name>
+        <jsp-file>/Xerces00.jsp</jsp-file>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Xerces01</servlet-name>
+        <servlet-class>org.apache.tester.Xerces01</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Xerces02</servlet-name>
+        <jsp-file>/Xerces02.jsp</jsp-file>
+    </servlet>
+
+
+    <servlet>
+        <servlet-name>invoker</servlet-name>
+        <servlet-class>
+          org.apache.catalina.servlets.InvokerServlet
+        </servlet-class>
+        <init-param>
+            <param-name>debug</param-name>
+            <param-value>0</param-value>
+        </init-param>
+        <load-on-startup>2</load-on-startup>
+    </servlet>
+
+
+    <!-- ========== Servlet Mappings ====================================== -->
+
+
+    <servlet-mapping>
+        <servlet-name>invoker</servlet-name>
+        <url-pattern>/servlet/*</url-pattern>
+    </servlet-mapping>
+
+    <!-- Map CGI Gateway Service for this Web App -->
+<!--
+    <servlet-mapping>
+        <servlet-name>cgi</servlet-name>
+        <url-pattern>/cgi-bin/*</url-pattern>
+    </servlet-mapping>
+-->
+
+    <!-- Map SSI Service for this Web App -->
+<!--
+    <servlet-mapping>
+        <servlet-name>ssi</servlet-name>
+        <url-pattern>*.shtml</url-pattern>
+    </servlet-mapping>
+-->
+
+    <servlet-mapping>
+        <servlet-name>Aggregate01</servlet-name>
+        <url-pattern>/Aggregate01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Aggregate01</servlet-name>
+        <url-pattern>/WrappedAggregate01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Aggregate02</servlet-name>
+        <url-pattern>/Aggregate02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Aggregate02</servlet-name>
+        <url-pattern>/WrappedAggregate02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Authentication01</servlet-name>
+        <url-pattern>/protected/Authentication01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Authentication02</servlet-name>
+        <url-pattern>/protected/Authentication02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Authentication03</servlet-name>
+        <url-pattern>/protected/Authentication03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Authentication04</servlet-name>
+        <url-pattern>/protected/Authentication04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Authentication05</servlet-name>
+        <url-pattern>/allowed/Authentication05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Authentication05</servlet-name>
+        <url-pattern>/disallowed/Authentication05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Context00</servlet-name>
+        <url-pattern>/Context00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Context00</servlet-name>
+        <url-pattern>/WrappedContext00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Context01</servlet-name>
+        <url-pattern>/Context01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Context01</servlet-name>
+        <url-pattern>/WrappedContext01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Context02</servlet-name>
+        <url-pattern>/Context02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Context02</servlet-name>
+        <url-pattern>/WrappedContext02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Decoding01</servlet-name>
+        <url-pattern>/Decoding01/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Decoding01</servlet-name>
+        <url-pattern>/WrappedDecoding01/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage01</servlet-name>
+        <url-pattern>/ErrorPage01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage01</servlet-name>
+        <url-pattern>/WrappedErrorPage01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage02</servlet-name>
+        <url-pattern>/ErrorPage02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage02</servlet-name>
+        <url-pattern>/WrappedErrorPage02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage03</servlet-name>
+        <url-pattern>/ErrorPage03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage03</servlet-name>
+        <url-pattern>/WrappedErrorPage03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage04</servlet-name>
+        <url-pattern>/ErrorPage04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage04</servlet-name>
+        <url-pattern>/WrappedErrorPage04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage05</servlet-name>
+        <url-pattern>/ErrorPage05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage05</servlet-name>
+        <url-pattern>/WrappedErrorPage05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage06</servlet-name>
+        <url-pattern>/ErrorPage06</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage06</servlet-name>
+        <url-pattern>/WrappedErrorPage06</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage07</servlet-name>
+        <url-pattern>/ErrorPage07</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage07</servlet-name>
+        <url-pattern>/WrappedErrorPage07</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage08</servlet-name>
+        <url-pattern>/ErrorPage08</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage08</servlet-name>
+        <url-pattern>/WrappedErrorPage08</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage09</servlet-name>
+        <url-pattern>/ErrorPage09</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ErrorPage09</servlet-name>
+        <url-pattern>/WrappedErrorPage09</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterRequest01</servlet-name>
+        <url-pattern>/FilterRequest01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterRequest01</servlet-name>
+        <url-pattern>/WrappedFilterRequest01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterRequest02</servlet-name>
+        <url-pattern>/FilterRequest02/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterRequest02</servlet-name>
+        <url-pattern>/WrappedFilterRequest02/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterRequest02a</servlet-name>
+        <url-pattern>/FilterRequest02a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterResponse01</servlet-name>
+        <url-pattern>/FilterResponse01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterResponse01</servlet-name>
+        <url-pattern>/WrappedFilterResponse01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterResponse04</servlet-name>
+        <url-pattern>/FilterResponse04/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterResponse04</servlet-name>
+        <url-pattern>/WrappedFilterResponse04/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>FilterResponse04a</servlet-name>
+        <url-pattern>/FilterResponse04a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward00</servlet-name>
+        <url-pattern>/Forward00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward00</servlet-name>
+        <url-pattern>/WrappedForward00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward00a</servlet-name>
+        <url-pattern>/Forward00a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward00b</servlet-name>
+        <url-pattern>/Forward00b</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward01</servlet-name>
+        <url-pattern>/Forward01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward01</servlet-name>
+        <url-pattern>/WrappedForward01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward03</servlet-name>
+        <url-pattern>/Forward03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward03</servlet-name>
+        <url-pattern>/WrappedForward03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward03a</servlet-name>
+        <url-pattern>/Forward03a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward04</servlet-name>
+        <url-pattern>/Forward04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward04</servlet-name>
+        <url-pattern>/WrappedForward04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward04a</servlet-name>
+        <url-pattern>/Forward04a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward04b</servlet-name>
+        <url-pattern>/Forward04b</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward05</servlet-name>
+        <url-pattern>/Forward05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward05</servlet-name>
+        <url-pattern>/WrappedForward05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward05a</servlet-name>
+        <url-pattern>/Forward05a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward05b</servlet-name>
+        <url-pattern>/Forward05b</url-pattern>
+    </servlet-mapping>
+
+    <!-- No mappings for Forward06 series, using invoker to access them -->
+
+    <servlet-mapping>
+        <servlet-name>Forward07</servlet-name>
+        <url-pattern>/Forward07</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward07</servlet-name>
+        <url-pattern>/WrappedForward07</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward07a</servlet-name>
+        <url-pattern>/Forward07a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward07b</servlet-name>
+        <url-pattern>/Forward07b</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward08</servlet-name>
+        <url-pattern>/WrappedForward08</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward08a</servlet-name>
+        <url-pattern>/Forward08a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward08b</servlet-name>
+        <url-pattern>/Forward08b</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward09</servlet-name>
+        <url-pattern>/Forward09</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Forward09</servlet-name>
+        <url-pattern>/WrappedForward09</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetHeaders01</servlet-name>
+        <url-pattern>/GetHeaders01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetHeaders01</servlet-name>
+        <url-pattern>/WrappedGetHeaders01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetInputStream01</servlet-name>
+        <url-pattern>/GetInputStream01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetInputStream01</servlet-name>
+        <url-pattern>/WrappedGetInputStream01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetLocales01</servlet-name>
+        <url-pattern>/GetLocales01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetLocales01</servlet-name>
+        <url-pattern>/WrappedGetLocales01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetLocales02</servlet-name>
+        <url-pattern>/GetLocales02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetLocales02</servlet-name>
+        <url-pattern>/WrappedGetLocales02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetParameter01</servlet-name>
+        <url-pattern>/WrappedGetParameter01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetParameter01</servlet-name>
+        <url-pattern>/GetParameter01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetParameter01</servlet-name>
+        <url-pattern>/WrappedGetParameter01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetParameterMap00</servlet-name>
+        <url-pattern>/GetParameterMap00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetParameterMap00</servlet-name>
+        <url-pattern>/WrappedGetParameterMap00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetQueryString01</servlet-name>
+        <url-pattern>/GetQueryString01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>GetQueryString01</servlet-name>
+        <url-pattern>/WrappedGetQueryString01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Golden01</servlet-name>
+        <url-pattern>/Golden01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Golden01</servlet-name>
+        <url-pattern>/WrappedGolden01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include00</servlet-name>
+        <url-pattern>/Include00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include00</servlet-name>
+        <url-pattern>/WrappedInclude00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include00a</servlet-name>
+        <url-pattern>/Include00a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include00b</servlet-name>
+        <url-pattern>/Include00b</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include01</servlet-name>
+        <url-pattern>/Include01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include01</servlet-name>
+        <url-pattern>/WrappedInclude01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include02</servlet-name>
+        <url-pattern>/Include02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include02</servlet-name>
+        <url-pattern>/WrappedInclude02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include02a</servlet-name>
+        <url-pattern>/Include02a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include02a</servlet-name>
+        <url-pattern>/WrappedInclude02a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include03</servlet-name>
+        <url-pattern>/Include03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include03</servlet-name>
+        <url-pattern>/WrappedInclude03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include03a</servlet-name>
+        <url-pattern>/Include03a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include04</servlet-name>
+        <url-pattern>/Include04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include04</servlet-name>
+        <url-pattern>/WrappedInclude04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include04a</servlet-name>
+        <url-pattern>/Include04a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include04b</servlet-name>
+        <url-pattern>/Include04b</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include06a</servlet-name>
+        <url-pattern>/Include06a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include07</servlet-name>
+        <url-pattern>/Include07</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include07a</servlet-name>
+        <url-pattern>/Include07a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include07b</servlet-name>
+        <url-pattern>/Include07b</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include07c</servlet-name>
+        <url-pattern>/Include07c</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include09</servlet-name>
+        <url-pattern>/Include09</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include09</servlet-name>
+        <url-pattern>/WrappedInclude09</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include10</servlet-name>
+        <url-pattern>/Include10/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include10</servlet-name>
+        <url-pattern>/WrappedInclude10/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Include10a</servlet-name>
+        <url-pattern>/Include10a/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Jndi01</servlet-name>
+        <url-pattern>/Jndi01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Jndi01</servlet-name>
+        <url-pattern>/WrappedJndi01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Jndi02</servlet-name>
+        <url-pattern>/Jndi02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Jndi02</servlet-name>
+        <url-pattern>/WrappedJndi02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Lifecycle01</servlet-name>
+        <url-pattern>/Lifecycle01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Lifecycle01</servlet-name>
+        <url-pattern>/WrappedLifecycle01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Lifecycle02</servlet-name>
+        <url-pattern>/Lifecycle02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Lifecycle02</servlet-name>
+        <url-pattern>/WrappedLifecycle02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Lifecycle03</servlet-name>
+        <url-pattern>/Lifecycle03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Lifecycle03</servlet-name>
+        <url-pattern>/WrappedLifecycle03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Redirect01</servlet-name>
+        <url-pattern>/Redirect01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Redirect01</servlet-name>
+        <url-pattern>/WrappedRedirect01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Redirect01a</servlet-name>
+        <url-pattern>/Redirect01a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Redirect01a</servlet-name>
+        <url-pattern>/WrappedRedirect01a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Reflection01</servlet-name>
+        <url-pattern>/Reflection01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Reflection01</servlet-name>
+        <url-pattern>/WrappedReflection01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Request01</servlet-name>
+        <url-pattern>/Request01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Request01</servlet-name>
+        <url-pattern>/WrappedRequest01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Reset01</servlet-name>
+        <url-pattern>/Reset01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Reset01</servlet-name>
+        <url-pattern>/WrappedReset01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources01</servlet-name>
+        <url-pattern>/Resources01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources01</servlet-name>
+        <url-pattern>/WrappedResources01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources02</servlet-name>
+        <url-pattern>/Resources02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources02</servlet-name>
+        <url-pattern>/WrappedResources02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources03</servlet-name>
+        <url-pattern>/Resources03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources03</servlet-name>
+        <url-pattern>/WrappedResources03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources04</servlet-name>
+        <url-pattern>/Resources04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources04</servlet-name>
+        <url-pattern>/WrappedResources04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources05</servlet-name>
+        <url-pattern>/Resources05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources05</servlet-name>
+        <url-pattern>/WrappedResources05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources06</servlet-name>
+        <url-pattern>/Resources06</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resources06</servlet-name>
+        <url-pattern>/WrappedResources06</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ResponseWrap01</servlet-name>
+        <url-pattern>/ResponseWrap01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ResponseWrap01a</servlet-name>
+        <url-pattern>/ResponseWrap01a</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ResponseWrap01c</servlet-name>
+        <url-pattern>/ResponseWrap01c</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session01</servlet-name>
+        <url-pattern>/Session01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session01</servlet-name>
+        <url-pattern>/WrappedSession01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session02</servlet-name>
+        <url-pattern>/Session02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session02</servlet-name>
+        <url-pattern>/WrappedSession02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session03</servlet-name>
+        <url-pattern>/Session03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session03</servlet-name>
+        <url-pattern>/WrappedSession03</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session04</servlet-name>
+        <url-pattern>/Session04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session04</servlet-name>
+        <url-pattern>/WrappedSession04</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session05</servlet-name>
+        <url-pattern>/Session05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session05</servlet-name>
+        <url-pattern>/WrappedSession05</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session06</servlet-name>
+        <url-pattern>/Session06</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Session06</servlet-name>
+        <url-pattern>/WrappedSession06</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>SetBufferSize01</servlet-name>
+        <url-pattern>/SetBufferSize01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>SetBufferSize01</servlet-name>
+        <url-pattern>/WrappedSetBufferSize01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>SetLocale01</servlet-name>
+        <url-pattern>/SetLocale01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>SetLocale01</servlet-name>
+        <url-pattern>/WrappedSetLocale01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Xerces00</servlet-name>
+        <url-pattern>/Xerces00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Xerces00</servlet-name>
+        <url-pattern>/WrappedXerces00</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Xerces01</servlet-name>
+        <url-pattern>/Xerces01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Xerces01</servlet-name>
+        <url-pattern>/WrappedXerces01</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Xerces02</servlet-name>
+        <url-pattern>/Xerces02</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Xerces02</servlet-name>
+        <url-pattern>/WrappedXerces02</url-pattern>
+    </servlet-mapping>
+
+
+    <!-- ========== Session Configuration ================================= -->
+
+    <session-config>
+        <session-timeout>5</session-timeout>
+    </session-config>
+
+
+    <!-- ========== Error Page Mappings =================================== -->
+
+    <error-page>
+        <error-code>412</error-code>  <!-- SC_PRECONDITION_FAILED -->
+        <location>/ErrorPage02</location>
+    </error-page>
+
+    <error-page>
+        <exception-type>org.apache.tester.TesterException</exception-type>
+        <location>/ErrorPage04</location>
+    </error-page>
+
+    <error-page>
+        <exception-type>java.lang.ArithmeticException</exception-type>
+        <location>/ErrorPage06</location>
+    </error-page>
+
+    <error-page>
+        <exception-type>java.lang.ArrayIndexOutOfBoundsException</exception-type>
+        <location>/ErrorPage06.jsp</location>
+    </error-page>
+
+    <error-page>
+        <exception-type>java.lang.NumberFormatException</exception-type>
+        <location>/ErrorPage06.html</location>
+    </error-page>
+
+
+    <!-- ========== Security Constraints ================================== -->
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>General Protected Area</web-resource-name>
+            <url-pattern>/protected/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>tomcat</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>All Users Allowed Area</web-resource-name>
+            <url-pattern>/allowed/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>*</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>No Users Allowed Area</web-resource-name>
+            <url-pattern>/disallowed/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>Authentication Servlet</realm-name>
+    </login-config>
+
+    <security-role>
+        <description>Security role we are testing for</description>
+        <role-name>tomcat</role-name>
+    </security-role>
+
+
+    <!-- ========== Environment Entries =================================== -->
+
+    <env-entry>
+        <env-entry-name>booleanEntry</env-entry-name>
+        <env-entry-value>true</env-entry-value>
+        <env-entry-type>java.lang.Boolean</env-entry-type>
+    </env-entry>
+
+    <env-entry>
+        <env-entry-name>byteEntry</env-entry-name>
+        <env-entry-value>123</env-entry-value>
+        <env-entry-type>java.lang.Byte</env-entry-type>
+    </env-entry>
+
+    <env-entry>
+        <env-entry-name>doubleEntry</env-entry-name>
+        <env-entry-value>123.45</env-entry-value>
+        <env-entry-type>java.lang.Double</env-entry-type>
+    </env-entry>
+
+    <env-entry>
+        <env-entry-name>floatEntry</env-entry-name>
+        <env-entry-value>54.32</env-entry-value>
+        <env-entry-type>java.lang.Float</env-entry-type>
+    </env-entry>
+
+    <env-entry>
+        <env-entry-name>integerEntry</env-entry-name>
+        <env-entry-value>12345</env-entry-value>
+        <env-entry-type>java.lang.Integer</env-entry-type>
+    </env-entry>
+
+    <env-entry>
+        <env-entry-name>longEntry</env-entry-name>
+        <env-entry-value>54321</env-entry-value>
+        <env-entry-type>java.lang.Long</env-entry-type>
+    </env-entry>
+
+    <env-entry>
+        <env-entry-name>stringEntry</env-entry-name>
+        <env-entry-value>String Value</env-entry-value>
+        <env-entry-type>java.lang.String</env-entry-type>
+    </env-entry>
+
+    <env-entry>
+        <env-entry-name>nested/nestedEntry</env-entry-name>
+        <env-entry-value>Nested Value</env-entry-value>
+        <env-entry-type>java.lang.String</env-entry-type>
+    </env-entry>
+
+</web-app>
diff --git a/container/tester/web/WrappedFilterResponse02.jsp b/container/tester/web/WrappedFilterResponse02.jsp
new file mode 100644
index 0000000..15b3634
--- /dev/null
+++ b/container/tester/web/WrappedFilterResponse02.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain" %>FilterResponse02 PASSED
diff --git a/container/tester/web/WrappedFilterResponse03.txt b/container/tester/web/WrappedFilterResponse03.txt
new file mode 100644
index 0000000..035d220
--- /dev/null
+++ b/container/tester/web/WrappedFilterResponse03.txt
@@ -0,0 +1 @@
+FilterResponse03 PASSED
diff --git a/container/tester/web/Xerces00.jsp b/container/tester/web/Xerces00.jsp
new file mode 100644
index 0000000..5494103
--- /dev/null
+++ b/container/tester/web/Xerces00.jsp
@@ -0,0 +1,8 @@
+<!-- This File is generated automatically by jsp2XML converter tool --> 
+<!-- Written By Ramesh Mandava/Santosh Singh -->
+<jsp:root
+xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"
+><jsp:directive.page contentType="text/plain"
+/><jsp:text><![CDATA[Xerces00 PASSED]]></jsp:text>
+</jsp:root>
+
diff --git a/container/tester/web/Xerces01.xml b/container/tester/web/Xerces01.xml
new file mode 100644
index 0000000..05bbee1
--- /dev/null
+++ b/container/tester/web/Xerces01.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<personnel>
+
+  <person id="Big.Boss">
+    <name><family>Boss</family> <given>Big</given></name>
+    <email>chief@foo.com</email>
+    <link subordinates="one.worker two.worker three.worker four.worker five.worker"/>
+  </person>
+
+  <person id="one.worker">
+    <name><family>Worker</family> <given>One</given></name>
+    <email>one@foo.com</email>
+    <link manager="Big.Boss"/>
+  </person>
+
+  <person id="two.worker">
+    <name><family>Worker</family> <given>Two</given></name>
+    <email>two@foo.com</email>
+    <link manager="Big.Boss"/>
+  </person>
+
+  <person id="three.worker">
+    <name><family>Worker</family> <given>Three</given></name>
+    <email>three@foo.com</email>
+    <link manager="Big.Boss"/>
+  </person>
+
+  <person id="four.worker">
+    <name><family>Worker</family> <given>Four</given></name>
+    <email>four@foo.com</email>
+    <link manager="Big.Boss"/>
+  </person>
+
+  <person id="five.worker">
+    <name><family>Worker</family> <given>Five</given></name>
+    <email>five@foo.com</email>
+    <link manager="Big.Boss"/>
+  </person>
+
+</personnel>
diff --git a/container/tester/web/Xerces02.jsp b/container/tester/web/Xerces02.jsp
new file mode 100644
index 0000000..da18988
--- /dev/null
+++ b/container/tester/web/Xerces02.jsp
@@ -0,0 +1 @@
+<%@ page contentType="text/plain"%>Xerces02 PASSED
diff --git a/container/tester/web/golden/Encoding01.txt b/container/tester/web/golden/Encoding01.txt
new file mode 100644
index 0000000..a137d8d
--- /dev/null
+++ b/container/tester/web/golden/Encoding01.txt
@@ -0,0 +1,5 @@
+
+
+
+<h2>Test Character</h2>
+<h2>‹› Unicode = 9b5a</hr>
diff --git a/container/tester/web/golden/Encoding02.txt b/container/tester/web/golden/Encoding02.txt
new file mode 100644
index 0000000..5dc7076
--- /dev/null
+++ b/container/tester/web/golden/Encoding02.txt
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>Encoding02.jsp</title>
+</head>
+<body bgcolor="white">
+This is legal in the spec:<br>
+Joe said %> foo.
+</body>
+</html>
diff --git a/container/tester/web/golden/Encoding03.txt b/container/tester/web/golden/Encoding03.txt
new file mode 100644
index 0000000..5c47628
--- /dev/null
+++ b/container/tester/web/golden/Encoding03.txt
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>Encoding03.jsp</title>
+</head>
+<body bgcolor="white">
+This is not recognized as a delimiter either:<br>
+Joe said %\> bar.
+</body>
+</html>
diff --git a/container/tester/web/golden/Golden01.txt b/container/tester/web/golden/Golden01.txt
new file mode 100644
index 0000000..7607340
--- /dev/null
+++ b/container/tester/web/golden/Golden01.txt
@@ -0,0 +1,3 @@
+This is the
+first golden file
+to be tested.
diff --git a/container/tester/web/golden/Include06.txt b/container/tester/web/golden/Include06.txt
new file mode 100644
index 0000000..8acef79
--- /dev/null
+++ b/container/tester/web/golden/Include06.txt
@@ -0,0 +1,5 @@
+==========
+Include06a output
+==========
+Include06b.jsp output
+==========
diff --git a/container/tester/web/golden/Include07.txt b/container/tester/web/golden/Include07.txt
new file mode 100644
index 0000000..546d24a
--- /dev/null
+++ b/container/tester/web/golden/Include07.txt
@@ -0,0 +1,4 @@
+Output from Include07
+Output from Include07b
+Output from Include07c
+Output from Include07
diff --git a/container/tester/web/golden/JspDoc01.txt b/container/tester/web/golden/JspDoc01.txt
new file mode 100644
index 0000000..2a7bb62
--- /dev/null
+++ b/container/tester/web/golden/JspDoc01.txt
@@ -0,0 +1 @@
+<a>text<b/></a><a>text<b></b></a><c><d>text</d></c><c><d>text</d></c><e><f/>text<f/></e><e><f></f>text<f></f></e>
diff --git a/container/tester/web/golden/JspDoc02.txt b/container/tester/web/golden/JspDoc02.txt
new file mode 100644
index 0000000..b1bc3bd
--- /dev/null
+++ b/container/tester/web/golden/JspDoc02.txt
@@ -0,0 +1,9 @@
+<html><head><title>Example JSP in XML format</title></head><body>
+This is the output of a simple JSP using XML format. 
+<br/><div>Use a jsp:scriptlet to loop from 1 to 10: </div>1, 2, 3, 4, 5, 6, 7, 8, 9, 10
+  <br><br>
+<div align="left">
+  Use a jsp:expression to write something: 
+  foo</div>
+  <p>This sentence is enclosed in a jsp:text element.</p>
+</body></html>
\ No newline at end of file
diff --git a/container/tester/web/golden/JspInclude01.txt b/container/tester/web/golden/JspInclude01.txt
new file mode 100644
index 0000000..0ce7eb2
--- /dev/null
+++ b/container/tester/web/golden/JspInclude01.txt
@@ -0,0 +1,5 @@
+This is before the include
+This is the include
+
+This is after the include
+
diff --git a/container/tester/web/golden/JspInclude01a.txt b/container/tester/web/golden/JspInclude01a.txt
new file mode 100644
index 0000000..f8bb282
--- /dev/null
+++ b/container/tester/web/golden/JspInclude01a.txt
@@ -0,0 +1,6 @@
+This is before the include
+Include00a PASSEDSessionListener01: sessionCreated()
+SessionListener02: sessionCreated()
+
+This is after the include
+
diff --git a/container/tester/web/golden/JspInclude02.txt b/container/tester/web/golden/JspInclude02.txt
new file mode 100644
index 0000000..0ce7eb2
--- /dev/null
+++ b/container/tester/web/golden/JspInclude02.txt
@@ -0,0 +1,5 @@
+This is before the include
+This is the include
+
+This is after the include
+
diff --git a/container/tester/web/golden/JspInclude02a.txt b/container/tester/web/golden/JspInclude02a.txt
new file mode 100644
index 0000000..f8bb282
--- /dev/null
+++ b/container/tester/web/golden/JspInclude02a.txt
@@ -0,0 +1,6 @@
+This is before the include
+Include00a PASSEDSessionListener01: sessionCreated()
+SessionListener02: sessionCreated()
+
+This is after the include
+
diff --git a/container/tester/web/golden/Property01.txt b/container/tester/web/golden/Property01.txt
new file mode 100644
index 0000000..1e440d8
--- /dev/null
+++ b/container/tester/web/golden/Property01.txt
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>Property01.jsp - Positive PropertyEditor Test</title>
+</head>
+<body bgcolor="white">
+
+
+Date property is '2001-07-25'.
+</body>
+</html>
diff --git a/container/tester/web/golden/SSIConditional01.txt b/container/tester/web/golden/SSIConditional01.txt
new file mode 100644
index 0000000..780cbab
--- /dev/null
+++ b/container/tester/web/golden/SSIConditional01.txt
@@ -0,0 +1,6 @@
+Before if block.
+
+
+test = value1
+
+After if block.
diff --git a/container/tester/web/golden/SSIConditional02.txt b/container/tester/web/golden/SSIConditional02.txt
new file mode 100644
index 0000000..a9701bb
--- /dev/null
+++ b/container/tester/web/golden/SSIConditional02.txt
@@ -0,0 +1,6 @@
+Before if block.
+
+
+test = value2
+
+After if block.
diff --git a/container/tester/web/golden/SSIConditional03.txt b/container/tester/web/golden/SSIConditional03.txt
new file mode 100644
index 0000000..45142c3
--- /dev/null
+++ b/container/tester/web/golden/SSIConditional03.txt
@@ -0,0 +1,6 @@
+Before if block.
+
+
+test = "unquoted multi-word value"
+
+After if block.
diff --git a/container/tester/web/golden/SSIConditional04.txt b/container/tester/web/golden/SSIConditional04.txt
new file mode 100644
index 0000000..8637c6f
--- /dev/null
+++ b/container/tester/web/golden/SSIConditional04.txt
@@ -0,0 +1,6 @@
+
+
+
+
+true
+
diff --git a/container/tester/web/golden/SSIConditional05.txt b/container/tester/web/golden/SSIConditional05.txt
new file mode 100644
index 0000000..ed356ea
--- /dev/null
+++ b/container/tester/web/golden/SSIConditional05.txt
@@ -0,0 +1,6 @@
+
+
+
+
+false
+
diff --git a/container/tester/web/golden/SSIConditional06.txt b/container/tester/web/golden/SSIConditional06.txt
new file mode 100644
index 0000000..7f9b93f
--- /dev/null
+++ b/container/tester/web/golden/SSIConditional06.txt
@@ -0,0 +1,8 @@
+
+
+
+
+
+not one
+
+
diff --git a/container/tester/web/golden/SSIConditional07.txt b/container/tester/web/golden/SSIConditional07.txt
new file mode 100644
index 0000000..6733287
--- /dev/null
+++ b/container/tester/web/golden/SSIConditional07.txt
@@ -0,0 +1,8 @@
+
+
+
+
+
+not two
+
+
diff --git a/container/tester/web/golden/SSIConditional08.txt b/container/tester/web/golden/SSIConditional08.txt
new file mode 100644
index 0000000..a1bed86
--- /dev/null
+++ b/container/tester/web/golden/SSIConditional08.txt
@@ -0,0 +1,5 @@
+
+
+
+
+two
diff --git a/container/tester/web/golden/SSIConfig01.txt b/container/tester/web/golden/SSIConfig01.txt
new file mode 100644
index 0000000..0bf3c60
--- /dev/null
+++ b/container/tester/web/golden/SSIConfig01.txt
@@ -0,0 +1,2 @@
+
+errmsg:Sorry this command won't work.
diff --git a/container/tester/web/golden/SSIConfig03.txt b/container/tester/web/golden/SSIConfig03.txt
new file mode 100644
index 0000000..b1b92ba
--- /dev/null
+++ b/container/tester/web/golden/SSIConfig03.txt
@@ -0,0 +1,4 @@
+
+34
+
+   1k
diff --git a/container/tester/web/golden/SSIExecCGI.txt b/container/tester/web/golden/SSIExecCGI.txt
new file mode 100644
index 0000000..20b5a04
--- /dev/null
+++ b/container/tester/web/golden/SSIExecCGI.txt
@@ -0,0 +1,41 @@
+<html> 
+  <head> 
+ <title>exec command  test examples </title> 
+ </head> 
+ <body>
+
+ These are the days of the week:<p>Monday Tuesday Wednesday Thursday Friday Saturday Sunday<br>
+
+ <FONT SIZE=+2>If you add one to your number, the result is <B>103</B><P>If you add 5 to that, the result is <B>108</B></FONT><br>
+
+ <P>On Wednesday, we have to mop<P>On Friday, we have to wash windows<P>On Monday, we have to vacuum<br>
+
+ Congratulations! Your married name would be jane smith.<br>
+
+ These are the days of the week:<P>Monday Tuesday Wednesday Thursday Friday Saturday Sunday<br>
+
+ <P>Starting countdown...10... 9... 8... 7... 6... 5... 4... 3... 2... 1... KABOOM!<br>
+
+ You ate spinach, so you get dessert!<br>
+
+ No spinach, no dessert!<br>
+
+ <P>You entered 2 with an exponent of 4<P>2 raised to the 4 power is 16.<br>
+
+ <P>Starting countdown...10... 9... 8... 7... 6... 5... 4... 3... 2... 1... KABOOM!<br>
+
+ The first day of the week is Sunday<P>The third day of the week is Tuesday<br>
+
+ 	<Title> Hello World! </Title>
+	<H1>	Hello World! </H1>
+<br>
+
+ You ate spinach, so you get dessert!
+<br>
+
+ Your number incremented by 1 is 8<BR>Your number, incremented again by 1, is now 9. If we store that operation, its value is also 9.<HR>Let's start over, with your original number 7<BR>Again, your number incremented by 1 is 8<BR>Now we store the value of your number in a second variable, which is now equal to 8, and then we increment your number again. It's now 9.<br>
+
+ The numbers you entered were:<LI>289<P>The square roots of those numbers are: <LI>17<br>
+
+ </body> 
+ </html>
diff --git a/container/tester/web/golden/SSIFsize02.txt b/container/tester/web/golden/SSIFsize02.txt
new file mode 100644
index 0000000..ee64a25
--- /dev/null
+++ b/container/tester/web/golden/SSIFsize02.txt
@@ -0,0 +1 @@
+   1k
diff --git a/container/tester/web/golden/SSIInclude01.txt b/container/tester/web/golden/SSIInclude01.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/container/tester/web/golden/SSIInclude01.txt
@@ -0,0 +1 @@
+
diff --git a/container/tester/web/golden/SSIInclude02.txt b/container/tester/web/golden/SSIInclude02.txt
new file mode 100644
index 0000000..5bc9d8a
--- /dev/null
+++ b/container/tester/web/golden/SSIInclude02.txt
@@ -0,0 +1 @@
+This is Content of "includeme.txt"
diff --git a/container/tester/web/golden/SSIInclude03.txt b/container/tester/web/golden/SSIInclude03.txt
new file mode 100644
index 0000000..8b05420
--- /dev/null
+++ b/container/tester/web/golden/SSIInclude03.txt
@@ -0,0 +1,4 @@
+This is Content of "includeme.txt"
+This is Content of "includeme.txt"
+
+This is Content of "includeme.txt"
diff --git a/container/tester/web/golden/SSIVarSub01.txt b/container/tester/web/golden/SSIVarSub01.txt
new file mode 100644
index 0000000..3ddec69
--- /dev/null
+++ b/container/tester/web/golden/SSIVarSub01.txt
@@ -0,0 +1,3 @@
+
+/SSIVarSub01.shtml
+
diff --git a/container/tester/web/golden/SSIVarSub02.txt b/container/tester/web/golden/SSIVarSub02.txt
new file mode 100644
index 0000000..2317f18
--- /dev/null
+++ b/container/tester/web/golden/SSIVarSub02.txt
@@ -0,0 +1,4 @@
+
+
+value of test
+
diff --git a/container/tester/web/golden/SSIVarSub03.txt b/container/tester/web/golden/SSIVarSub03.txt
new file mode 100644
index 0000000..57505e4
--- /dev/null
+++ b/container/tester/web/golden/SSIVarSub03.txt
@@ -0,0 +1,5 @@
+
+
+
+value of test value of test2
+
diff --git a/container/tester/web/golden/SSIVarSub04.txt b/container/tester/web/golden/SSIVarSub04.txt
new file mode 100644
index 0000000..9cd7538
--- /dev/null
+++ b/container/tester/web/golden/SSIVarSub04.txt
@@ -0,0 +1,5 @@
+
+
+
+value of test|value of test2
+
diff --git a/container/tester/web/golden/SSIVarSub05.txt b/container/tester/web/golden/SSIVarSub05.txt
new file mode 100644
index 0000000..64fb3a1
--- /dev/null
+++ b/container/tester/web/golden/SSIVarSub05.txt
@@ -0,0 +1,3 @@
+
+
+value of test\${test2}
diff --git a/container/tester/web/golden/Session05.txt b/container/tester/web/golden/Session05.txt
new file mode 100644
index 0000000..a828f7f
--- /dev/null
+++ b/container/tester/web/golden/Session05.txt
@@ -0,0 +1,10 @@
+SessionListener01: sessionCreated()
+SessionListener02: sessionCreated()
+SessionListener01: attributeAdded(attribute1,value1)
+SessionListener02: attributeAdded(attribute1,value1)
+SessionListener01: attributeReplaced(attribute1,value1)
+SessionListener02: attributeReplaced(attribute1,value1)
+SessionListener01: attributeRemoved(attribute1,value2)
+SessionListener02: attributeRemoved(attribute1,value2)
+SessionListener02: sessionDestroyed()
+SessionListener01: sessionDestroyed()
diff --git a/container/tester/web/golden/WrappedSession05.txt b/container/tester/web/golden/WrappedSession05.txt
new file mode 100644
index 0000000..4de9d9d
--- /dev/null
+++ b/container/tester/web/golden/WrappedSession05.txt
@@ -0,0 +1,13 @@
+TesterHttpServletRequestWrapper.getSession(b)
+SessionListener01: sessionCreated()
+SessionListener02: sessionCreated()
+SessionListener01: attributeAdded(attribute1,value1)
+SessionListener02: attributeAdded(attribute1,value1)
+SessionListener01: attributeReplaced(attribute1,value1)
+SessionListener02: attributeReplaced(attribute1,value1)
+SessionListener01: attributeRemoved(attribute1,value2)
+SessionListener02: attributeRemoved(attribute1,value2)
+SessionListener02: sessionDestroyed()
+SessionListener01: sessionDestroyed()
+TesterHttpServletResponseWrapper.setContentType()
+TesterHttpServletResponseWrapper.getWriter()
diff --git a/container/tester/web/golden/array.txt b/container/tester/web/golden/array.txt
new file mode 100644
index 0000000..be8b78b
--- /dev/null
+++ b/container/tester/web/golden/array.txt
@@ -0,0 +1 @@
+These are the days of the week:<p>Monday Tuesday Wednesday Thursday Friday Saturday Sunday
\ No newline at end of file
diff --git a/container/tester/web/golden/binary.txt b/container/tester/web/golden/binary.txt
new file mode 100644
index 0000000..cbf2711
--- /dev/null
+++ b/container/tester/web/golden/binary.txt
@@ -0,0 +1 @@
+<FONT SIZE=+2>If you add one to your number, the result is <B>103</B><P>If you add 5 to that, the result is <B>108</B></FONT>
\ No newline at end of file
diff --git a/container/tester/web/golden/concat.txt b/container/tester/web/golden/concat.txt
new file mode 100644
index 0000000..311e7fc
--- /dev/null
+++ b/container/tester/web/golden/concat.txt
@@ -0,0 +1 @@
+Congratulations! Your married name would be jane smith.
diff --git a/container/tester/web/golden/days.txt b/container/tester/web/golden/days.txt
new file mode 100644
index 0000000..5ce087c
--- /dev/null
+++ b/container/tester/web/golden/days.txt
@@ -0,0 +1 @@
+These are the days of the week:<P>Monday Tuesday Wednesday Thursday Friday Saturday Sunday
\ No newline at end of file
diff --git a/container/tester/web/golden/dowhile.txt b/container/tester/web/golden/dowhile.txt
new file mode 100644
index 0000000..3075d95
--- /dev/null
+++ b/container/tester/web/golden/dowhile.txt
@@ -0,0 +1 @@
+<P>Starting countdown...10... 9... 8... 7... 6... 5... 4... 3... 2... 1... KABOOM!
\ No newline at end of file
diff --git a/container/tester/web/golden/else.txt b/container/tester/web/golden/else.txt
new file mode 100644
index 0000000..d019a02
--- /dev/null
+++ b/container/tester/web/golden/else.txt
@@ -0,0 +1 @@
+You ate spinach, so you get dessert!
diff --git a/container/tester/web/golden/elsif.txt b/container/tester/web/golden/elsif.txt
new file mode 100644
index 0000000..0dcb127
--- /dev/null
+++ b/container/tester/web/golden/elsif.txt
@@ -0,0 +1 @@
+No spinach, no dessert!
diff --git a/container/tester/web/golden/exponents.txt b/container/tester/web/golden/exponents.txt
new file mode 100644
index 0000000..b6f47e6
--- /dev/null
+++ b/container/tester/web/golden/exponents.txt
@@ -0,0 +1 @@
+<P>You entered 2 with an exponent of 4<P>2 raised to the 4 power is 16.
\ No newline at end of file
diff --git a/container/tester/web/golden/getday.txt b/container/tester/web/golden/getday.txt
new file mode 100644
index 0000000..20ab9ab
--- /dev/null
+++ b/container/tester/web/golden/getday.txt
@@ -0,0 +1 @@
+The first day of the week is Sunday<P>The third day of the week is Tuesday
\ No newline at end of file
diff --git a/container/tester/web/golden/helloperl.txt b/container/tester/web/golden/helloperl.txt
new file mode 100644
index 0000000..454bfb1
--- /dev/null
+++ b/container/tester/web/golden/helloperl.txt
@@ -0,0 +1,2 @@
+	<Title> Hello World! </Title>
+	<H1>	Hello World! </H1>
diff --git a/container/tester/web/golden/increment.txt b/container/tester/web/golden/increment.txt
new file mode 100644
index 0000000..3ece628
--- /dev/null
+++ b/container/tester/web/golden/increment.txt
@@ -0,0 +1 @@
+Your number incremented by 1 is 8<BR>Your number, incremented again by 1, is now 9. If we store that operation, its value is also 9.<HR>Let's start over, with your original number 7<BR>Again, your number incremented by 1 is 8<BR>Now we store the value of your number in a second variable, which is now equal to 8, and then we increment your number again. It's now 9.
\ No newline at end of file
diff --git a/container/tester/web/golden/modifyall.txt b/container/tester/web/golden/modifyall.txt
new file mode 100644
index 0000000..2c57557
--- /dev/null
+++ b/container/tester/web/golden/modifyall.txt
@@ -0,0 +1 @@
+The numbers you entered were:<LI>289<P>The square roots of those numbers are: <LI>17
\ No newline at end of file
diff --git a/container/tester/web/golden/preference.txt b/container/tester/web/golden/preference.txt
new file mode 100644
index 0000000..10cdc16
--- /dev/null
+++ b/container/tester/web/golden/preference.txt
@@ -0,0 +1 @@
+div is 11, parens is 5, subadd is 11 and parens_subadd is 1
\ No newline at end of file
diff --git a/container/tester/web/includeme.shtml b/container/tester/web/includeme.shtml
new file mode 100644
index 0000000..e32cd70
--- /dev/null
+++ b/container/tester/web/includeme.shtml
@@ -0,0 +1 @@
+This is Content of "includeme.shtml"
diff --git a/container/tester/web/includeme.txt b/container/tester/web/includeme.txt
new file mode 100644
index 0000000..7971296
--- /dev/null
+++ b/container/tester/web/includeme.txt
@@ -0,0 +1 @@
+This is Content of "includeme.txt"
\ No newline at end of file
diff --git a/container/tester/web/index.shtml b/container/tester/web/index.shtml
new file mode 100644
index 0000000..e72cb16
--- /dev/null
+++ b/container/tester/web/index.shtml
@@ -0,0 +1,139 @@
+<html> 
+ <head> 
+ <title>Config command examples </title>
+ </head> 
+ <body bgcolor=white> 
+
+
+Setting errormsg using: &lt;!--#config errmsg="..." --&gt; <br>
+<!--#config errmsg="errormsg: Sorry this command won't work." --> 
+
+Trying to access non-existent "foo.shtml" using  
+&lt;!--#fsize file="foo.shtml" --&gt;
+<br>
+
+<!--#fsize file="foo.shtml" -->
+<br>
+<br>
+
+Using &lt;!--#config sizefmt="bytes" --&gt;
+<br>
+              This file ( index.shtml::size-> 2665) is 
+ <!--#config sizefmt="bytes" --> 
+ <!--#fsize file="index.shtml" -->
+              bytes in size. 
+
+<br>
+<br>
+  Setting sizefmt to abbrev using : &lt;!--#config sizefmt="abbrev" --&gt;
+<br>
+
+
+<!--#config sizefmt="abbrev" -->
+
+Size of file "index.shtml ( size->2665)" using sizefmt=abbrev ->
+<!--#fsize   file="index.shtml" -->
+
+<br> <br>
+Size of file "saveindex.shtml(size->464)" using  : &lt;!--#fsize file="saveindex.shtml" --&gt;  ->
+<!--#fsize   file="saveindex.shtml" -->
+<br><br>
+
+Size of file "check1.shtml(size->1000)" using  : &lt;!--#fsize file="check1.shtml" --&gt;  ->
+<!--#fsize   file="check1.shtml" -->
+<br><br>
+
+Size of file "check2.html(size->1001)" using  : &lt;!--#fsize file="check2.html" --&gt;  ->
+<!--#fsize   file="check2.html" -->
+<br><br>
+
+Size of file "check3.shtml(size->1499)" using  : &lt;!--#fsize file="check3.shtml" --&gt;  ->
+<!--#fsize   file="check3.shtml" -->
+<br><br>
+
+Size of file "check4.shtml(size->1500)" using  : &lt;!--#fsize file="check4.shtml" --&gt;  ->
+<!--#fsize   file="check4.shtml" -->
+<br><br>
+
+Size of file "check5.shtml(size->1501)" using  : &lt;!--#fsize file="check5.shtml" --&gt;  ->
+<!--#fsize   file="check5.shtml" -->
+<br><br>
+
+Size of file "checkme ( size->1535)" using : &lt;!--#fsize file"checkme" --&gt; -> 
+<!--#fsize   file="checkme" -->
+<br><br>
+
+Size of file "check5.html(size->1536)" using  : &lt;!--#fsize file="check5.html" --&gt;  ->
+<!--#fsize   file="check5.html" -->
+<br><br>
+
+Size of file "check55.html(size->1537)" using  : &lt;!--#fsize file="check55.html" --&gt;  ->
+<!--#fsize   file="check55.html" -->
+<br><br>
+
+Size of file "check555.html(size->1538)" using  : &lt;!--#fsize file="check555.html" --&gt;  ->
+<!--#fsize   file="check555.html" -->
+<br><br>
+
+Size of file "check5555.html(size->1539)" using  : &lt;!--#fsize file="check5555.html" --&gt;  ->
+<!--#fsize   file="check5555.html" -->
+<br><br>
+
+Size of file "check6.shtml(size->1988)" using  : &lt;!--#fsize file="check6.shtml" --&gt;  ->
+<!--#fsize   file="check6.shtml" -->
+<br><br>
+
+Size of file "check7.shtml(size->1999)" using  : &lt;!--#fsize file="check7.shtml" --&gt;  ->
+<!--#fsize   file="check7.shtml" -->
+<br><br>
+ <p> 
+
+
+
+
+ <FONT SIZE=+2 Color=blue> timefmt Tests : </FONT>
+
+<br> <br>
+  Using &lt;!--#config timefmt="%A" --&gt; 
+          &lt;!--#echo var="DATE_LOCAL" --&gt;
+<br>
+  Day ->  <!--#config timefmt="%A" -->
+              <!--#echo var="DATE_LOCAL" -->, 
+<br> <br>
+  Using &lt;!--#config timefmt="%d" --&gt;<br>
+          &lt;!--#echo var="DATE_LOCAL" --&gt; 
+  <br>
+  Day of Month -> <!--#config timefmt="%d" -->
+              <!--#echo var="DATE_LOCAL" --> 
+
+ <br> <br>
+  Using &lt;!--#config timefmt="%B" --&gt;<br>
+          &lt;!--#echo var="DATE_LOCAL" --&gt; 
+  <br>
+  Month(full) -> <!--#config timefmt="%B" --> 
+		<!--#echo var="DATE_LOCAL" --> 
+
+  <br> <br>
+  Using &lt;!--#config timefmt="%b" --&gt;<br>
+          &lt;!--#echo var="DATE_LOCAL" --&gt; 
+  <br>
+  Month(abbreviated) -> <!--#config timefmt="%b" --> 
+			<!--#echo var="DATE_LOCAL" --> 
+
+  <br> <br>
+  Using &lt;!--#config timefmt="%Y" --&gt;<br>
+          &lt;!--#echo var="DATE_LOCAL" --&gt; 
+  <br>
+   Year ->  <!--#config timefmt="%Y"
+              --> <!--#echo var="DATE_LOCAL" -->. 
+
+  <br> <br>
+  Using &lt;!--#config timefmt="%W" --&gt;<br>
+          &lt;!--#echo var="DATE_LOCAL" --&gt; 
+
+  <br>
+  Week number of year as a decimal  number: <!--#config timefmt="%W" --> 
+					<!--#echo var="DATE_LOCAL" -->. 
+
+</body> 
+</html> 
diff --git a/container/tester/web/ssidir/includeme.txt b/container/tester/web/ssidir/includeme.txt
new file mode 100644
index 0000000..7971296
--- /dev/null
+++ b/container/tester/web/ssidir/includeme.txt
@@ -0,0 +1 @@
+This is Content of "includeme.txt"
\ No newline at end of file
diff --git a/container/webapps/.cvsignore b/container/webapps/.cvsignore
new file mode 100644
index 0000000..9d0b71a
--- /dev/null
+++ b/container/webapps/.cvsignore
@@ -0,0 +1,2 @@
+build
+dist
diff --git a/container/webapps/ROOT/WEB-INF/web.xml b/container/webapps/ROOT/WEB-INF/web.xml
new file mode 100644
index 0000000..879ec87
--- /dev/null
+++ b/container/webapps/ROOT/WEB-INF/web.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+  <display-name>Welcome to Tomcat</display-name>
+  <description>
+     Welcome to Tomcat
+  </description>
+
+</web-app>
diff --git a/container/webapps/ROOT/admin/index.html b/container/webapps/ROOT/admin/index.html
new file mode 100644
index 0000000..a094b61
--- /dev/null
+++ b/container/webapps/ROOT/admin/index.html
@@ -0,0 +1,14 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html>
+    <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <title>Administration</title>
+</head>
+
+<body>
+
+Tomcat's administration web application is no longer installed by default. Download and install 
+the "admin" package to use it.
+
+</body>
+</html>
diff --git a/container/webapps/ROOT/build.xml b/container/webapps/ROOT/build.xml
new file mode 100644
index 0000000..cb24380
--- /dev/null
+++ b/container/webapps/ROOT/build.xml
@@ -0,0 +1,76 @@
+<project name="ROOT" default="build-main" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="../../build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="webapps.build"   value="../build"/>
+  <property name="webapps.dist"    value="../dist"/>
+  <property name="webapp.name"     value="ROOT"/>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${webapps.build}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ================ BUILD: Copy Static Files ========================== -->
+  <target name="build-static" depends="build-prepare">
+    <copy todir="${webapps.build}/${webapp.name}">
+      <fileset dir=".">
+        <exclude name="build.*"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <target name="build-main" depends="build-static"/>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean and build ROOT webapp"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${webapps.build}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Directories =================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${webapps.dist}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Distribution Files ============ -->
+  <target name="dist" depends="build-main,dist-prepare"
+   description="Create ROOT webapp binary distribution">
+      <jar   jarfile="${webapps.dist}/${webapp.name}.war"
+             basedir="${webapps.build}/${webapp.name}" includes="**"/>
+  </target>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <delete dir="${webapps.dist}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
diff --git a/container/webapps/ROOT/favicon.ico b/container/webapps/ROOT/favicon.ico
new file mode 100644
index 0000000..6c5bd2c
--- /dev/null
+++ b/container/webapps/ROOT/favicon.ico
Binary files differ
diff --git a/container/webapps/ROOT/index.jsp b/container/webapps/ROOT/index.jsp
new file mode 100644
index 0000000..5c5af15
--- /dev/null
+++ b/container/webapps/ROOT/index.jsp
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<%@ page session="false" %>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+    <head>
+    <title><%= application.getServerInfo() %></title>
+    <style type="text/css">
+    /*<![CDATA[*/
+      body {
+          color: #000000;
+          background-color: #FFFFFF;
+	  font-family: Arial, "Times New Roman", Times, serif;
+          margin: 10px 0px;
+      }
+
+    img {
+       border: none;
+    }
+    
+    a:link, a:visited {
+        color: blue
+    }
+
+    th {
+        font-family: Verdana, "Times New Roman", Times, serif;
+        font-size: 110%;
+        font-weight: normal;
+        font-style: italic;
+        background: #D2A41C;
+        text-align: left;
+    }
+
+    td {
+        color: #000000;
+	font-family: Arial, Helvetica, sans-serif;
+    }
+    
+    td.menu {
+        background: #FFDC75;
+    }
+
+    .center {
+        text-align: center;
+    }
+
+    .code {
+        color: #000000;
+        font-family: "Courier New", Courier, monospace;
+        font-size: 110%;
+        margin-left: 2.5em;
+    }
+    
+     #banner {
+        margin-bottom: 12px;
+     }
+
+     p#congrats {
+         margin-top: 0;
+         font-weight: bold;
+         text-align: center;
+     }
+
+     p#footer {
+         text-align: right;
+         font-size: 80%;
+     }
+     /*]]>*/
+   </style>
+</head>
+
+<body>
+
+<!-- Header -->
+<table id="banner" width="100%">
+    <tr>
+      <td align="left" style="width:130px">
+        <a href="http://jakarta.apache.org/tomcat/index.html">
+	  <img src="tomcat.gif" height="92" width="130" alt="The Mighty Tomcat - MEOW!"/>
+	</a>
+      </td>
+      <td align="left" valign="top"><b><%= application.getServerInfo() %></b></td>
+      <td align="right">
+        <a href="http://jakarta.apache.org/">
+	  <img src="jakarta-banner.gif" height="48" width="505" alt="The Jakarta Project"/>
+	</a>
+       </td>
+     </tr>
+</table>
+
+<table>
+    <tr>
+
+        <!-- Table of Contents -->
+        <td valign="top">
+            <table width="100%" border="1" cellspacing="0" cellpadding="3">
+                <tr>
+		  <th>Administration</th>
+                </tr>
+                <tr>
+		  <td class="menu">
+		    <a href="manager/status">Status</a><br/>
+                    <a href="admin">Tomcat&nbsp;Administration</a><br/>
+                    <a href="manager/html">Tomcat&nbsp;Manager</a><br/>
+                    &nbsp;
+                  </td>
+                </tr>
+            </table>
+
+	    <br />
+            <table width="100%" border="1" cellspacing="0" cellpadding="3">
+                <tr>
+		  <th>Documentation</th>
+                </tr>
+                <tr>
+                  <td class="menu">
+                    <a href="RELEASE-NOTES.txt">Release&nbsp;Notes</a><br/>
+                    <a href="tomcat-docs/changelog.html">Change&nbsp;Log</a><br/>
+                    <a href="tomcat-docs">Tomcat&nbsp;Documentation</a><br/>                        &nbsp;
+                    &nbsp;
+		    </td>
+                </tr>
+            </table>
+	    
+            <br/>
+            <table width="100%" border="1" cellspacing="0" cellpadding="3">
+                <tr>
+                  <th>Tomcat Online</th>
+                </tr>
+                <tr>
+                  <td class="menu">
+                    <a href="http://jakarta.apache.org/tomcat/">Home&nbsp;Page</a><br/>
+		    <a href="http://jakarta.apache.org/tomcat/faq">FAQ</a><br/>
+                    <a href="http://jakarta.apache.org/tomcat/bugreport.html">Bug&nbsp;Database</a><br/>
+                    <a href="http://issues.apache.org/bugzilla/buglist.cgi?bug_status=UNCONFIRMED&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;bug_status=RESOLVED&amp;resolution=LATER&amp;resolution=REMIND&amp;resolution=---&amp;bugidtype=include&amp;product=Tomcat+5&amp;cmdtype=doit&amp;order=Importance">Open Bugs</a><br/>
+                    <a href="http://nagoya.apache.org/eyebrowse/SummarizeList?listId=88">Users&nbsp;Mailing&nbsp;List</a><br/>
+                    <a href="http://nagoya.apache.org/eyebrowse/SummarizeList?listId=46">Developers&nbsp;Mailing&nbsp;List</a><br/>
+                    <a href="irc://irc.freenode.net/#tomcat">IRC</a><br/>
+		    &nbsp;
+                  </td>
+                </tr>
+            </table>
+	    
+            <br/>
+            <table width="100%" border="1" cellspacing="0" cellpadding="3">
+                <tr>
+                  <th>Examples</th>
+                </tr>
+                <tr>
+                  <td class="menu">
+                    <a href="jsp-examples/">JSP&nbsp;Examples</a><br/>
+                    <a href="servlets-examples/">Servlet&nbsp;Examples</a><br/>
+                    <a href="webdav/">WebDAV&nbsp;capabilities</a><br/>
+     		    &nbsp;
+                  </td>
+                </tr>
+            </table>
+	    
+            <br/>
+            <table width="100%" border="1" cellspacing="0" cellpadding="3">
+                <tr>
+		  <th>Miscellaneous</th>
+                </tr>
+                <tr>
+                  <td class="menu">
+                    <a href="http://java.sun.com/products/jsp">Sun's&nbsp;Java&nbsp;Server&nbsp;Pages&nbsp;Site</a><br/>
+                    <a href="http://java.sun.com/products/servlet">Sun's&nbsp;Servlet&nbsp;Site</a><br/>
+    		    &nbsp;
+                  </td>
+                </tr>
+            </table>
+        </td>
+
+        <td style="width:20px">&nbsp;</td>
+	
+        <!-- Body -->
+        <td align="left" valign="top">
+          <p id="congrats">If you're seeing this page via a web browser, it means you've setup Tomcat successfully. Congratulations!</p>
+ 
+          <p>As you may have guessed by now, this is the default Tomcat home page. It can be found on the local filesystem at:</p>
+          <p class="code">$CATALINA_HOME/webapps/ROOT/index.jsp</p>
+	  
+          <p>where "$CATALINA_HOME" is the root of the Tomcat installation directory. If you're seeing this page, and you don't think you should be, then either you're either a user who has arrived at new installation of Tomcat, or you're an administrator who hasn't got his/her setup quite right. Providing the latter is the case, please refer to the <a href="tomcat-docs">Tomcat Documentation</a> for more detailed setup and administration information than is found in the INSTALL file.</p>
+
+            <p><b>NOTE:</b> This page is precompiled. If you change it, this page will not change since
+                  it was compiled into a servlet at build time.
+                  (See <tt>$CATALINA_HOME/webapps/ROOT/WEB-INF/web.xml</tt> as to how it was mapped.)
+            </p>
+
+            <p><b>NOTE: For security reasons, using the administration webapp
+            is restricted to users with role "admin". The manager webapp
+            is restricted to users with role "manager".</b>
+            Users are defined in <code>$CATALINA_HOME/conf/tomcat-users.xml</code>.</p>
+
+            <p>Included with this release are a host of sample Servlets and JSPs (with associated source code), extensive documentation (including the Servlet 2.4 and JSP 2.0 API JavaDoc), and an introductory guide to developing web applications.</p>
+
+            <p>Tomcat mailing lists are available at the Jakarta project web site:</p>
+
+           <ul>
+               <li><b><a href="mailto:tomcat-user-subscribe@jakarta.apache.org">tomcat-user@jakarta.apache.org</a></b> for general questions related to configuring and using Tomcat</li>
+               <li><b><a href="mailto:tomcat-dev-subscribe@jakarta.apache.org">tomcat-dev@jakarta.apache.org</a></b> for developers working on Tomcat</li>
+           </ul>
+
+            <p>Thanks for using Tomcat!</p>
+
+            <p id="footer"><img src="tomcat-power.gif" width="77" height="80" alt="Powered by Tomcat"/><br/>
+	    &nbsp;
+
+	    Copyright &copy; 1999-2005 Apache Software Foundation<br/>
+            All Rights Reserved
+            </p>
+        </td>
+
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/container/webapps/ROOT/jakarta-banner.gif b/container/webapps/ROOT/jakarta-banner.gif
new file mode 100644
index 0000000..049cf82
--- /dev/null
+++ b/container/webapps/ROOT/jakarta-banner.gif
Binary files differ
diff --git a/container/webapps/ROOT/tomcat-power.gif b/container/webapps/ROOT/tomcat-power.gif
new file mode 100644
index 0000000..c1a7404
--- /dev/null
+++ b/container/webapps/ROOT/tomcat-power.gif
Binary files differ
diff --git a/container/webapps/ROOT/tomcat.gif b/container/webapps/ROOT/tomcat.gif
new file mode 100644
index 0000000..6175673
--- /dev/null
+++ b/container/webapps/ROOT/tomcat.gif
Binary files differ
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ActionTag.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ActionTag.java
new file mode 100644
index 0000000..7fee33c
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ActionTag.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+import javax.servlet.jsp.tagext.Tag;
+
+
+/**
+ * <p>Nested tag that represents an individual "instant action".  This tag
+ * is valid <strong>only</strong> when nested within an ActoinsTag tag.
+ * This tag has the following user-settable attributes:</p>
+ * <ul>
+ * <li><strong>selected</strong> - Set to <code>true</code> if this action
+ *     should be selected when the control is initially displayed.</li>
+ * <li><strong>url</strong> - URL to which control should be transferred
+ *     (in the current frame or window) if this action is selected.</li>
+ * </ul>
+ *
+ * <p>In addition, the body content of this tag is used as the user-visible
+ * label for the action, so that it may be conveniently localized.</p>
+ *
+ * <strong>FIXME</strong> - Internationalize the exception messages!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ActionTag extends BodyTagSupport {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The label that will be rendered for this action.
+     */
+    protected String label = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Should this action be selected when the control is initially displayed?
+     */
+    protected boolean selected = false;
+
+    public boolean getSelected() {
+        return (this.selected);
+    }
+
+    public void setSelected(boolean selected) {
+        this.selected = selected;
+    }
+
+    /**
+     * Should this action selection be disabled? 
+     * e.g. Action separators should be disabled.
+     */
+    protected boolean disabled = false;
+
+    public boolean getDisabled() {
+        return (this.disabled);
+    }
+
+    public void setDisabled(boolean disabled) {
+        this.disabled = disabled;
+    }
+
+    /**
+     * The URL to which control is transferred if this action is selected.
+     */
+    protected String url = null;
+
+    public String getUrl() {
+        return (this.url);
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the start of this tag.
+     *
+     * @exception JspException if a JSP exception has occurred
+     */
+    public int doStartTag() throws JspException {
+
+        // Initialize the holder for our label text
+        this.label = null;
+
+        // Do no further processing for now
+        return (EVAL_BODY_TAG);
+
+    }
+
+
+    /**
+     * Process the body text of this tag (if any).
+     *
+     * @exception JspException if a JSP exception has occurred
+     */
+    public int doAfterBody() throws JspException {
+
+        String label = bodyContent.getString();
+        if (label != null) {
+            label = label.trim();
+            if (label.length() > 0)
+                this.label = label;
+        }
+        return (SKIP_BODY);
+
+    }
+
+
+    /**
+     * Record this action with our surrounding ActionsTag instance.
+     *
+     * @exception JspException if a processing error occurs
+     */
+    public int doEndTag() throws JspException {
+
+        // Find our parent ActionsTag instance
+        Tag parent = getParent();
+        while ((parent != null) && !(parent instanceof ActionsTag)) {
+            parent = parent.getParent();
+        }
+        if ((parent == null) || !(parent instanceof ActionsTag))
+            throw new JspException("Must be nested in an ActionsTag isntance");
+        ActionsTag actions = (ActionsTag) parent;
+
+        // Register the information for the action represented by
+        // this action
+        HttpServletRequest request =
+            (HttpServletRequest) pageContext.getRequest();
+        HttpServletResponse response =
+            (HttpServletResponse) pageContext.getResponse();
+        String path = null;
+        if ((url != null) && (url.startsWith("/"))) {
+            path = request.getContextPath() + url;
+        } else {
+            path = url;
+        }
+        actions.addAction(label, selected, disabled,
+                          response.encodeURL(path));
+
+        return (EVAL_PAGE);
+
+    }
+
+
+    /**
+     * Release all state information set by this tag.
+     */
+    public void release() {
+
+        this.label = null;
+        this.selected = false;
+        this.disabled = false;
+        this.url = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ActionsTag.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ActionsTag.java
new file mode 100644
index 0000000..fcfdb32
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ActionsTag.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+
+/**
+ * <p>JSP custom tag that renders an "instant actions" control.  To the user,
+ * it appears as an HTML &lt;select&gt; element (i.e. a combo box), with
+ * the behavior of selecting a new page for the current frame or window when
+ * a different option is selected, without requiring a submit action.
+ * This tag has the following user-settable attributes:</p>
+ * <ul>
+ * <li><strong>size</strong> - (Integer) number of rows that will be visible
+ *     to the user.  If not specified, one row will be visible.</li>
+ * <li><strong>style</strong> - The CSS style class to be applied to the
+ *     entire rendered output of the instant actions control, if any.</li>
+ * </ul>
+ *
+ * <strong>FIXME</strong> - Internationalize the exception messages!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ActionsTag extends BodyTagSupport {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * Attribute name used to indicate that we have generated the JavaScript
+     * function already on the current page.  The value stored for this
+     * attribute is arbitrary - only its existence is relevant.
+     */
+    protected static final String FUNCTION_TAG =
+        "org.apache.webapp.admin.ActionsTag.FUNCTION_TAG";
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of labels for the Actions displayed by this control.
+     */
+    protected ArrayList labels = new ArrayList();
+
+
+    /**
+     * The set of "selected" flags for Actions displayed by this control.
+     */
+    protected ArrayList selecteds = new ArrayList();
+
+    /**
+     * The set of "disabled" flags for Actions displayed by this control.
+     */
+    protected ArrayList disableds = new ArrayList();
+
+    /**
+     * The set of URLs for the Actions displayed by this control.
+     */
+    protected ArrayList urls = new ArrayList();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The number of elements that will be displayed to the user.
+     */
+    protected int size = 1;
+
+    public int getSize() {
+        return (this.size);
+    }
+
+    public void setSize(int size) {
+        this.size = size;
+    }
+
+
+    /**
+     * The CSS style class to be applied to the entire rendered output
+     * of this "instant actions" object.
+     */
+    protected String style = null;
+
+    public String getStyle() {
+        return (this.style);
+    }
+
+    public void setStyle(String style) {
+        this.style = style;
+    }
+
+
+    /**
+     *  HTML Label tag text.
+     */
+    protected String label = null;
+
+    public String getLabel() {
+        return (this.label);
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    public int doStartTag() throws JspException {
+
+        this.labels.clear();
+        this.selecteds.clear();
+        this.urls.clear();
+
+        return (EVAL_BODY_TAG);
+       
+    }
+    
+    
+    /**
+     * Render this instant actions control.
+     *
+     * @exception JspException if a processing error occurs
+     */
+    public int doEndTag() throws JspException {
+
+        JspWriter out = pageContext.getOut();
+
+        try {
+
+            // Render (once only) the JavaScript function we need
+            if (pageContext.getAttribute(FUNCTION_TAG) == null) {
+                out.println();
+                out.println("<script language=\"JavaScript\">");
+                out.println("<!--");
+                out.println("function IA_jumpMenu(targ,selObj) {");
+                out.println("  dest = selObj.options[selObj.selectedIndex].value;");
+                out.println("  if (dest.length > 0) {");
+                out.println("    eval(targ+\".location='\"+dest+\"'\");");
+                out.println("  }");
+                out.println("}");
+                out.println("//-->");
+                out.println("</script>");
+                out.println();
+                pageContext.setAttribute(FUNCTION_TAG, Boolean.TRUE);
+            }
+
+            // Render LABEL element for section 508 accessibility
+            
+            if (label != null) {
+                out.print("<label for=\"labelId\">");
+                out.print(label);
+                out.println("</label>");
+            }
+            // Render the beginning of this element
+            out.println();
+            out.print("<select");
+            if (size > 1) {
+                out.print(" size=\"");
+                out.print(size);
+                out.print("\"");
+            }
+            if (style != null) {
+                out.print(" class=\"");
+                out.print(style);
+                out.print("\"");
+            }
+            if (label != null) {
+                out.print(" id=\"");
+                out.print("labelId");
+                out.print("\"");
+            }
+            
+            out.print(" onchange=\"IA_jumpMenu('self',this)\"");
+            out.println(">");
+
+            // Render each defined action
+            int n = labels.size();
+            for (int i = 0; i < n; i++) {
+                String label = (String) labels.get(i);
+                boolean selected = ((Boolean) selecteds.get(i)).booleanValue();
+                boolean disabled = ((Boolean) disableds.get(i)).booleanValue();             
+                String url = (String) urls.get(i);
+                out.print("<option");
+                if (selected)
+                    out.print(" selected=\"selected\"");
+                if (disabled)
+                    out.print(" disabled=\"true\"");                
+                out.print(" value=\"");
+                if (url != null)
+                    out.print(url);
+                out.print("\"");
+                out.print(">");
+                if (label != null)
+                    out.print(label);
+                out.println("</option>");
+            }
+
+            // Render the end of this element
+            out.println("</select>");
+            out.println();
+
+        } catch (IOException e) {
+            throw new JspException(e);
+        }
+
+        return (EVAL_PAGE);
+
+    }
+
+
+    /**
+     * Release all state information set by this tag.
+     */
+    public void release() {
+
+        this.labels.clear();
+        this.selecteds.clear();
+        this.urls.clear();
+
+        this.size = 1;
+        this.style = null;
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Add a new Action to the set that will be rendered by this control.
+     *
+     * @param label Localized label visible to the user
+     * @param selected Initial selected state of this option
+     * @param disabled Ability to be selected state of this option
+     * @param url URL to which control should be transferred if selected
+     */
+    void addAction(String label, boolean selected, boolean disabled, String url) {
+
+        labels.add(label);
+        selecteds.add(new Boolean(selected));
+        disableds.add(new Boolean(disabled));
+        urls.add(url);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationLocales.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationLocales.java
new file mode 100644
index 0000000..8e70662
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationLocales.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import org.apache.struts.Globals;
+import org.apache.struts.action.ActionServlet;
+import org.apache.struts.util.MessageResources;
+
+
+/**
+ * Class to hold the Locales supported by this package.
+ *
+ * @author Patrick Luby
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class ApplicationLocales {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Initialize the set of Locales supported by this application.
+     *
+     * @param servlet ActionServlet we are associated with
+     */
+    public ApplicationLocales(ActionServlet servlet) {
+
+        super();
+        Locale list[] = Locale.getAvailableLocales();
+        MessageResources resources = (MessageResources)
+                servlet.getServletContext().getAttribute(Globals.MESSAGES_KEY);
+        if (resources == null)
+            return;
+        String config = resources.getConfig();
+        if (config == null)
+            return;
+
+        for (int i = 0; i < list.length; i++) {
+            try {
+                ResourceBundle bundle =
+                    ResourceBundle.getBundle(config, list[i]);
+                if (bundle == null)
+                    continue;
+                if (list[i].equals(bundle.getLocale())) {
+                    localeLabels.add(list[i].getDisplayName());
+                    localeValues.add(list[i].toString());
+                    supportedLocales.add(list[i]);
+                }
+            } catch( Exception ex ) {
+                servlet.log("Missing locale " + list[i] );
+                continue;
+            }
+        }
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of Locale labels supported by this application.
+     */
+    protected ArrayList localeLabels = new ArrayList();
+
+
+    /**
+     * The set of Locale values supported by this application.
+     */
+    protected ArrayList localeValues = new ArrayList();
+
+
+    /**
+     * The set of supported Locales for this application.
+     */
+    protected ArrayList supportedLocales = new ArrayList();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the set of Locale labels supported by this application.
+     */
+    public List getLocaleLabels() {
+
+        return (localeLabels);
+
+    }
+
+
+    /**
+     * Return the set of Locale values supported by this application.
+     */
+    public List getLocaleValues() {
+
+        return (localeValues);
+
+    }
+
+
+    /**
+     * Return the set of Locales supported by this application.
+     */
+    public List getSupportedLocales() {
+
+        return (supportedLocales);
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationResources.properties b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationResources.properties
new file mode 100644
index 0000000..1f3875f
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationResources.properties
@@ -0,0 +1,449 @@
+application.title=Tomcat Server Administration
+errors.header=<h2><font color="red">Validation Errors</font></h2>You must correct the following errors before proceeding:<ul>
+errors.footer=</ul><hr>
+error.login=<li>Invalid username or password</li>
+prompt.username=User Name
+prompt.password=Password
+button.login=Login
+button.reset=Reset
+button.save=Save
+button.change=Change
+button.cancel=Reset
+button.commit=Commit Changes
+button.logout=Log Out
+login.enter=Enter a username and password to start a new session
+login.changeLanguage=Change the language
+error.login=Invalid username or password
+error.tryagain=To try again, click
+error.here=here
+prompt.someText=Some Text
+prompt.moreText=More Text
+sample.someText.required=<li>"Some Text" cannot be empty</li>
+sample.moreText.required=<li>"More Text" cannot be empty</li>
+save.success=Save sucessful!
+save.fail=Save failed!
+server.portnumber=Port Number
+server.debuglevel=Debug Level
+server.shutdown=Shutdown
+server.properties=Server Properties
+warning.header=<center><h4><font color="red">Warning!</font></h4></center> <ul>
+server.port.warning=<li>Port number less than 1024 requires special software capabilities.
+error.portNumber.required=<li>PortNumber cannot be empty</li>
+error.portNumber.format=<li>PortNumber not a valid integer!</li>
+error.portNumber.range=<li>PortNumber seems out of range. Valid range is 1-65535. </li>
+error.shutdownText.length=<li>Shutdown Text must be atleast 6 characters</li>
+error.engineName.required=<li>Engine name is required</li>
+error.acceptCountText.required=<li>Accept count required</li>
+error.acceptCountText.format=<li>Accept count not a valid integer!</li>
+error.acceptCountText.range=<li>Accept count seems out of range. Valid range is 0-128. </li>
+error.connTimeOutText.required=<li>Connection time out required</li>
+error.connTimeOutText.format=<li>Connection time out not a valid integer!</li>
+error.connTimeOutText.range=<li>Connection time out seems out of range. Valid range is 0-60000. </li>
+error.bufferSizeText.required=<li>Buffer Size required</li>
+error.bufferSizeText.format=<li>Buffer Size not a valid integer!</li>
+error.bufferSizeText.range=<li>Buffer Size seems out of range. Valid range is 1-8192. </li>
+error.address.invalid=<li>IP Address invalid</li>
+error.redirectPortText.required=<li>Redirect Port Number cannot be empty</li>
+error.redirectPortText.format=<li>Redirect Port Number not a valid integer!</li>
+error.redirectPortText.range=<li>Redirect Port Number seems out of range. Valid range is 1-65535. </li>
+error.minProcessorsText.required=<li>Minimum Processors cannot be empty</li>
+error.minProcessorsText.format=<li>Minimum Processors not a valid integer!</li>
+error.minProcessorsText.range=<li>Minimum Processors seems out of range. Valid range is 1-512. </li>
+error.maxProcessorsText.required=<li>Maximum Processors cannot be empty</li>
+error.maxProcessorsText.format=<li>Maximum Processors not a valid integer!</li>
+error.maxProcessorsText.range=<li>Maximum Processors seems out of range. Valid range is 1-512. Also, maximum >= minimum. </li>
+error.proxyName.invalid=<li>Proxy name is invalid</li>
+error.proxyPortText.required=<li>Proxy Port Number cannot be empty</li>
+error.proxyPortText.format=<li>Proxy Port Number not a valid integer!</li>
+error.proxyPortText.range=<li>Proxy Port Number seems out of range. Valid range is 1-65535. </li>
+error.hostName.bad=Invalid host name {0}
+error.hostName.required=<li>Hostname is required</li>
+error.hostName.exists=<li>Hostname already exists</li>
+error.appBase.required=<li>Application Base is required</li>
+service.name=Name
+service.engine.props=Engine Properties
+service.defaulthostname=Default Hostname
+service.properties=Service Properties
+service.property=Property
+service.value=Value
+service.create.new=Create New Service
+actions.available.actions=Available Actions
+actions.services.create=Create New Service
+actions.services.delete=Delete This Service
+actions.services.deletes=Delete Existing Services
+actions.services.edit=Edit Existing Service
+actions.accesslogger.create=Create New Access Logger
+actions.accesslogger.delete=Delete Access Logger
+actions.connectors.create=Create New Connector
+actions.connectors.delete=Delete This Connector
+actions.connectors.deletes=Delete Existing Connectors
+actions.connectors.edit=Edit Existing Connector
+actions.contexts.create=Create New Context
+actions.contexts.delete=Delete This Context
+actions.contexts.deletes=Delete Existing Contexts
+actions.contexts.edit=Edit Existing Context
+actions.defaultcontexts.create=Create New DefaultContext
+actions.defaultcontexts.delete=Delete This DefaultContext
+actions.defaultcontexts.deletes=Delete Existing DefaultContext
+actions.defaultcontexts.edit=Edit Existing DefaultContext
+actions.group.create=Create New Group
+actions.group.delete=Delete Existing Group
+actions.hosts.create=Create New Host
+actions.hosts.delete=Delete This Host
+actions.hosts.deletes=Delete Existing Hosts
+actions.hosts.edit=Edit Existing Host
+actions.loggers.create=Create New Logger
+actions.loggers.delete=Delete This Logger
+actions.loggers.deletes=Delete Existing Loggers
+actions.loggers.edit=Edit Existing Logger
+actions.realms.create=Create New User Realm
+actions.realms.delete=Delete This User Realm
+actions.realms.deletes=Delete User Realms
+actions.realms.edit=Edit Existing Realm
+actions.requestfilter.create=Create New Request Filter
+actions.requestfilter.delete=Delete Request Filters
+actions.user.create=Create New User
+actions.user.delete=Delete Existing User
+actions.valves.create=Create New Valve
+actions.valves.edit=Edit Valve
+actions.valves.delete=Delete This Valve
+actions.valves.deletes=Delete Existing Valves
+actions.alias.create=Create New Aliases
+actions.alias.delete=Delete Aliases
+new.alias=New Alias
+connector.type=Type
+connector.scheme=Scheme
+connector.accept.count=Accept Count
+connector.compression=Compression
+connector.connection.linger=Connection Linger
+connector.connection.timeout=Connection Timeout
+connector.connection.uploadTimeout=Connection Upload Timeout
+connector.default.buffer=Default Buffer Size
+connector.connection.disableUploadTimeout=Disable Upload Timeout
+connector.enable.dns=Enable DNS Lookups
+connector.uriencoding=URI Encoding
+connector.useBodyEncodingForURI=Use Body Encoding For URI Query Parameters
+connector.allowTrace=Allow TRACE Method
+connector.address.ip=IP Address
+connector.redirect.portnumber=Redirect Port Number
+connector.min=Minimum
+connector.milliseconds=milliseconds
+connector.max=Maximum
+connector.maxkeepalive=Max KeepAlive Requests
+connector.maxspare=Max Spare Threads
+connector.maxthreads=Max Threads
+connector.minspare=Min Spare Threads
+connector.threadpriority=Processor Thread Priority
+connector.proxy.name=Proxy Name
+connector.proxy.portnumber=Proxy Port Number
+connector.algorithm=Algorithm
+connector.ciphers=Ciphers
+connector.client.auth=Client Authentication
+connector.keystore.filename=Keystore Filename
+connector.keystore.password=Keystore Password
+connector.keystore.type=Keystore Type
+connector.sslProtocol=SSL Protocol
+connector.keyPass.warning=<li>Please use keytool to generate certificate.</li>
+connector.secure=Secure
+connector.tcpNoDelay=TCP No Delay 
+connector.xpoweredby=X Powered By 
+host.properties=Host Properties
+host.name=Name
+host.base=Application Base
+host.autoDeploy=Auto Deploy
+host.deployXML=Deploy XML
+host.deployOnStartup=Deploy On Startup
+host.wars=Unpack WARs
+host.aliases=Aliases
+host.alias.name=Alias Name
+host.xmlNamespaceAware=XML Namespace Aware
+host.xmlValidation=XML Validation
+error.aliasName.exists=<li>Alias already exists</li>
+error.aliasName.required=<li>Alias name is required</li>
+context.properties=Context Properties
+context.cookies=Cookies
+context.cross.context=Cross Context
+context.docBase=Document Base
+context.override=Override
+context.privileged=Privileged
+context.path=Path
+context.reloadable=Reloadable
+context.swallowOutput=Swallow Output
+context.usenaming=Use Naming
+context.workdir=Working Directory
+context.loader.properties=Loader Properties
+context.sessionmgr.properties=Session Manager Properties
+context.checkInterval=Check interval
+context.sessionId=Session ID Initializer
+context.max.sessions=Maximum Active Sessions
+context.antiResourceLocking=Prevent Locking Resources
+context.antiJarLocking=Prevent Jar Locking
+defaultcontext.properties=DefaultContext Properties
+error.context.directory=Document base does not exist or is not a readable directory
+error.docBase.required=<li>Document base cannot be null</li>
+error.path.required=<li>Path cannot be null</li>
+error.workDir.required=<li>Working directory cannot be null</li>
+error.ldrCheckInterval.required=<li>Loader check interval cannot be empty</li>
+error.ldrCheckInterval.format=<li>Loader check interval not a valid integer!</li>
+error.ldrCheckInterval.range=<li>Loader check interval seems out of range. Valid range is 1-1000. </li>
+error.mgrCheckInterval.required=<li>Manager check interval cannot be empty</li>
+error.mgrCheckInterval.format=<li>Manager check interval not a valid integer!</li>
+error.mgrCheckInterval.range=<li>Manager check interval seems out of range. Valid range is 1-1000. </li>
+error.mgrSessionIDInit.required=<li>Session Manager Initialization ID cannot be empty</li>
+error.mgrMaxSessions.required=<li>Maximum sessions cannot be empty</li>
+error.mgrMaxSessions.format=<li>Maximum sessions not a valid integer!</li>
+error.mgrMaxSessions.range=<li>Maximum sessions seems out of range. Valid range is -1 to 100. </li>
+list.none=(None)
+logger.directory=Directory
+logger.prefix=Prefix
+logger.suffix=Suffix
+logger.timestamp=Timestamp
+logger.filelogger.properties=Filelogger specific Properties
+logger.verbositylevel=Verbosity Level
+error.loggerName.bad=Invalid logger name {0}
+error.loggerName.exists=<li>A Logger already exists.</li>
+error.directory.required=<li>Directory cannot be empty.</li>
+error.prefix.required=<li>Prefix cannot be empty.</li>
+error.suffix.required=<li>Suffix cannot be empty.</li>
+error.valveName.bad=Invalid valve name {0}
+error.vavlveName.exists=<li>Valve already exists</li>
+error.singleSignOn.exists=<li>SingleSignOn Valve already exists</li>
+user.fullName=Full Name
+user.groups=Member in Groups
+user.newUser=Create New User Properties
+user.oldUser=Edit Existing User Properties
+user.password=Password
+user.properties=User Properties
+user.roles=Security Roles
+user.username=Username
+error.password.required=<li>Password is required</li>
+error.username.required=<li>Username is required</li>
+error.get.attributes=Error retrieving information properties.
+error.set.attributes=Error setting information properties.
+actions.delete=Delete
+error.defaultHost.required=<li>Default Hostname required</li>
+error.engineName.bad=Invalid engine name {0}
+error.engineName.exists=<li>Engine Name already exists</li>
+error.serviceName.bad=Invalid service name {0}
+error.serviceName.required=<li>Service Name required</li>
+error.serviceName.exists=<li>Service Name already exists</li>
+error.jdbcrealm=Error occured during setting JDBCRealm.
+error.jndirealm=Error occured during setting JNDIRealm.
+error.userdbrealm=Error occured during setting UserdatabaseRealm.
+error.datasourcerealm=Error occured during setting DataSourceRealm.
+error.realmName.bad=Invalid realm name {0}
+error.realmName.required=<li>Realm Name required.</li>
+error.realmName.exists=<li>A realm already exists.</li>
+error.contextName.bad=Invalid context name {0}
+error.contextName.exists=<li>Context already exists.</li>
+error.defaultcontextName.exists=<li>DefaultContext already exists.</li>
+error.path.prefix=<li>Path must begin with a '/'.</li>
+error.loaderName.bad=Invalid loader name {0}
+error.managerName.bad=Invalid manager name {0}
+error.connectorName.bad=Invalid connector name {0}
+error.connectorName.exists=<li>Connector already exists</li>
+error.pattern.required=<li>Pattern is required</li>
+error.valveName.bad=Invalid valve name {0}
+error.valveName.exists=<li>Valve already exists</li>
+realm.driver=Database Driver
+realm.passwd=Database Password
+realm.url=Database URL
+realm.userName=Database User Name
+realm.digest=Digest Algorithm
+realm.passwordCol=Password Column
+realm.roleNameCol=Role Name Column
+realm.userNameCol=User Name Column
+realm.userRoleTable=User Role Table
+realm.userTable=User Table
+realm.resource=Resource Name
+realm.pathName=Path Name
+realm.connName=Connection Name
+realm.connPassword=Connection Password
+realm.connURL=Connection URL
+realm.connFactory=Context Factory
+realm.roleBase=Role Base Element
+realm.roleName=Role Name
+realm.user.roleName=User Role Name
+realm.pattern=Role Search Pattern
+realm.role.subtree=Search Role Subtree
+realm.userBase=User Base Element
+realm.user.subtree=Search User Subtree
+realm.userPassword=User Password
+realm.userPattern=User Pattern
+realm.userSearch=User Search
+realm.dataSourceName=DataSource Name
+realm.localDataSource=Local DataSource
+realm.userCredCol=User Credential Column 
+valve.access.properties=Access Logger Properties
+valve.request.properties=Request Filter Properties
+valve.single.properties=Single SignOn Valve Properties
+valve.remotehost.properties=Remote Host Valve Properties
+valve.remoteaddress.properties=Remote Address Valve Properties
+valve.resolveHosts=Resolve Hosts
+valve.rotatable=Rotatable
+valve.pattern=Pattern
+valve.allowHosts=Allow these Hosts
+valve.denyHosts=Deny these Hosts
+valve.allowIPs=Allow IP addresses
+valve.denyIPs=Deny IP addresses
+error.allowHost=<li>Allow is invalid.  Need to include the admin's Hostname.</li>
+error.denyHost=<li>Deny is invalid.  Need to exclude the admin's Hostname.</li>
+error.allowIP=<li>Allow is invalid.  Need to include the admin's IP address.</li>
+error.denyIP=<li>Deny is invalid.  Need to exclude the admin's IP address.</li>
+error.allow.deny.required=<li>Allow or deny is required.</li>
+error.syntax=<li>Syntax error in request filter pattern.</li>
+error.resource.required=<li>Resource Name is required.</li>
+error.resource.javaprefix=<li>Resource must have "java:" prefix.</li>
+error.pathName.required=<li>Path Name is required.</li>
+error.driver.required=<li>Database driver is required.</li>
+error.roleNameCol.required=<li>Role name column is required.</li>
+error.userNameCol.required=<li>User name column is required.</li>
+error.passwordCol.required=<li>Password column is required.</li>
+error.userTable.required=<li>User table is required.</li>
+error.roleTable.required=<li>User role table is required.</li>
+error.connectionPassword.required=<li>Database password is required.</li>
+error.connectionURL.required=<li>Database URL is required.</li>
+error.connectionName.required=<li>Database username is required.</li>
+error.roleName.required=<li>Role name is required.</li>
+error.userRoleName.required=<li>User Role name is required.</li>
+error.digest.required=<li>Digest algorithm is required.</li>
+error.roleBase.required=<li>Role base element is required.</li>
+error.rolePattern.required=<li>Role search pattern is required.</li>
+error.userBase.required=<li>User base element is required.</li>
+error.userPassword.required=<li>User Password is required.</li>
+error.userPattern.required=<li>User pattern is required.</li>
+error.userSearch.required=<li>User search is required.</li>
+error.userPattern.userSearch.defined=<li>Either userPattern or userSearch must be specified not both.</li>
+error.contextFactory.required=<li>Context Factory is required.</li>
+error.connPassword.required=<li>Connection password is required.</li>
+error.connURL.required=<li>Connection URL is required.</li>
+error.connName.required=<li>Connection name is required.</li>
+error.dataSourceName.required=<li>DataSource name is required.</li>
+error.userCredCol.required=<li>User credential is required.</li>
+error.userRoleTable.required=<li>User role table is required.</li>
+
+# ---------- Server Module ----------
+server.service.treeBuilder.subtreeNode=Service
+server.service.treeBuilder.connector=Connector
+server.service.treeBuilder.host=Host
+server.service.treeBuilder.context=Context 
+server.service.treeBuilder.loggerFor=Logger for {0}
+server.service.treeBuilder.realmFor=Realm for {0} 
+server.service.treeBuilder.logger=Logger
+server.service.treeBuilder.realm=Realm
+
+# ---------- Resources Module ----------
+resources.treeBuilder.subtreeNode=Resources
+resources.treeBuilder.datasources=Data Sources
+resources.treeBuilder.mailsessions=Mail Sessions
+resources.treeBuilder.resourcelinks=Resource Links
+resources.env.entries=Environment Entries
+resources.env.entry=Entry Name
+resources.env.props=Environment Entry Properties
+resources.env.override=Override Application Level Entries
+resources.actions.env.create=Create New Env Entry
+resources.actions.env.edit=Edit Environment Entry
+resources.actions.env.delete=Delete Environment Entries
+resources.actions.env.list=List Existing Entries
+resources.datasrc.jdbc=JDBC Driver
+resources.actions.datasrc=Data Sources
+resources.actions.datasrc.create=Create New Data Source
+resources.actions.datasrc.delete=Delete Data Sources
+resources.actions.datasrc.edit=Edit Data Source
+resources.datasrc.url=Data Source URL
+resources.datasrc.jdbcclass=JDBC Driver Class
+resources.datasrc.connections=Connections
+resources.datasrc.active=Max. Active Connections
+resources.datasrc.idle=Max. Idle Connections
+resources.datasrc.wait=Max. Wait for Connection
+resources.datasrc.validation=Validation Query
+resources.datasrc.jndi=JNDI Name
+resources.actions.mailsession=Mail Sessions
+resources.actions.mailsession.create=Create New Mail Session
+resources.actions.mailsession.delete=Delete Mail Session
+resources.actions.mailsession.edit=Edit Mail Session
+resources.mailsession.name=Name
+resources.mailsession.mailhost=mail.smtp.host
+resources.actions.resourcelk=Resource Links
+resources.actions.resourcelk.create=Create New Resource Link
+resources.actions.resourcelk.delete=Delete Resource Link
+resources.actions.resourcelk.edit=Edit Resource Link
+resources.resourcelk.name=Name
+resources.resourcelk.global=Global
+resources.resourcelk.type=Type
+resources.error.name.required=<li>Name is required.</li>
+resources.error.global.required=<li>Global is required.</li>
+resources.error.type.required=<li>Type is required.</li>
+resources.error.value.required=<li>Value is required.</li>
+resources.error.value.mismatch=<li>Type and value do not seem to match.</li>
+resources.error.entryType.invalid=<li>Entry Type not recognized.</li>
+resources.error.entryType.notimpl=<li>Validation for this type not implemented yet.</li>
+resources.error.url.required=<li>Data Source URL is required.</li>
+resources.error.driverClass.required=<li>JDBC Driver Class is required.</li>
+resources.error.active.required=<li>Max Active Connections is required.</li>
+resources.error.idle.required=<li>Max Idle Connections is required.</li>
+resources.error.wait.required=<li>Max Wait for a Connection is required.</li>
+resources.error.mailhost.required=<li>mail.smtp.host is required.</li>
+resources.integer.error=<li>Invalid integer error.</li>
+resources.actions.userdb.create=Create New User Database
+resources.actions.userdb.edit=Edit User Database
+resources.actions.userdb.delete=Delete User Databases
+resources.userdb.location=Location
+resources.userdb.factory=Factory
+resources.treeBuilder.databases=User Databases
+resources.error.path.required=<li>Path is required</li>
+resources.error.jndiName.required=<li>JNDI Name is required</li>
+resources.invalid.name=<li>Invalid resource name - Name already exists.</li>
+resources.invalid.env=<li>Invalid environment name - Name already exists.</li>
+
+# ---------- User Database Module ----------
+users.actions.group.create=Create New Group
+users.actions.group.delete=Delete Existing Groups
+users.actions.group.list=List Existing Groups
+users.actions.role.create=Create New Role
+users.actions.role.delete=Delete Existing Roles
+users.actions.role.list=List Existing Roles
+users.actions.user.create=Create New User
+users.actions.user.delete=Delete Existing Users
+users.actions.user.list=List Existing Users
+users.deleteGroups.title=Delete Existing Groups
+users.deleteRoles.title=Delete Existing Roles
+users.deleteUsers.title=Delete Existing Users
+users.error.attribute.get=Error retrieving attribute {0}
+users.error.attribute.set=Error modifying attribute {0}
+users.error.invoke=Error invoking operation {0}
+users.error.groupname.required=Group Name is required
+users.error.password.required=Password is required
+users.error.quotes=Double quote characters are not allowed in field values
+users.error.rolename.required=Role Name is required
+users.error.select=Error selecting managed objects
+users.error.token=Transaction submitted out of order
+users.error.username.required=<li>User Name is required</li>
+users.group.newGroup=Create New Group Properties
+users.group.oldGroup=Edit Existing Group Properties
+users.group.properties=Group Properties
+users.list.description=Description
+users.list.fullName=Full Name
+users.list.groupname=Group Name
+users.list.rolename=Role Name
+users.list.username=User Name
+users.listGroups.title=Groups List
+users.listRoles.title=Roles List
+users.listUsers.title=Users List
+users.prompt.description=Description:
+users.prompt.fullName=Full Name:
+users.prompt.groupname=Group Name:
+users.prompt.password=Password:
+users.prompt.rolename=Role Name:
+users.prompt.username=User Name:
+users.role.newRole=Create New Role Properties
+users.role.oldRole=Edit Existing Role Properties
+users.role.properties=Role Properties
+users.treeBuilder.groupsNode=Groups
+users.treeBuilder.rolesNode=Roles
+users.treeBuilder.subtreeNode=User Definition
+users.treeBuilder.usersNode=Users
+users.user.newUser=Create New User Properties
+users.user.oldUser=Edit Existing User Properties
+users.user.properties=User Properties
+# ---------- -------------------- ----------
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationResources_es.properties b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationResources_es.properties
new file mode 100644
index 0000000..cf55796
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationResources_es.properties
@@ -0,0 +1,439 @@
+application.title=Administración del Servidor Tomcat
+errors.header=<h3><font color="red">Error(es) de Validación</font></h3>Debe corregir el/los siguiente(s) error(es) antes de continuar:<ul>
+errors.footer=</ul><hr>
+error.login=<li>Nombre de usuario o contraseña inválidos</li>
+prompt.username=Nombre de Usuario
+prompt.password=Contraseña
+button.login=Login
+button.reset=Limpiar
+button.save=Guardar
+button.change=Cambiar
+button.cancel=Cancelar
+button.commit=Acometer Cambios
+button.logout=Salir
+login.enter=Introduzca un nombre de usuario y una contraseña para iniciar una nueva sesión
+login.changeLanguage=Cambio de idioma
+error.login=Nombre de usuario o contraseña inválidos
+error.tryagain=Para intentar de nuevo, haga click
+error.here=aquí
+prompt.someText=Algún Texto
+prompt.moreText=Más Texto
+sample.someText.required=<li>"Algún Texto" no puede estar vacío</li>
+sample.moreText.required=<li>"Más Texto" no puede estar vacío</li>
+save.success=¡Guardardo con éxito!
+save.fail=¡Fallo al guardar!
+server.portnumber=Número de Puerto
+server.debuglevel=Nivel de Depuración
+server.shutdown=Apagar
+server.properties=Propiedades del Servidor
+warning.header=<center><h4><font color="red">¡Aviso!</font></h4></center> <ul>
+server.port.warning=<li>El número de puerto inferior a 1024 requiere capacidades especiales del software.</li>
+error.portNumber.required=<li>El número del puerto no puede estar vacío.</li>
+error.portNumber.format=<li>¡El número del puerto no es un entero válido! </li>
+error.portNumber.range=<li>El número del puerto esta fuera del rango. El rango válido es 1-65535.</li>
+error.shutdownText.length=<li>¡La longitud del Texto para Apagar debe ser de al menos 6 caracteres!</li>
+error.engineName.required=<li>Se requiere el nombre del Motor</li>
+error.acceptCountText.required=<li>Se requiere contador de Aceptación</li>
+error.acceptCountText.format=<li>¡El contador de Aceptación no es un entero váido!</li>
+error.acceptCountText.range=<li>El contador de aceptación parece estar fuera de rango. El rango válido va de 0-128. </li>
+error.connTimeOutText.required=<li>El tiempo de espera de la conexión no puede estar vacío.</li>
+error.connTimeOutText.format=<li>¡El tiempo de espera de la conexión no es un entero válido! </li>
+error.connTimeOutText.range=<li>El tiempo de espera de la conexión parece estar fuera de rango. El rango válido va de 0-60000. </li>
+error.bufferSizeText.required=<li>Tamaño del Búfer requerido</li>
+error.bufferSizeText.format=<li>¡El Tamaño del Búfer no es un entero! </li>
+error.bufferSizeText.range=<li>Tamaño del Búfer parece estar fuera del rango. El rango válido va de 1-8192. </li>
+error.address.invalid=<li>Dirección IP inválida</li>
+error.redirectPortText.required=<li>Número del puerto de redireccionamiento no puede estar vacío </li>
+error.redirectPortText.format=<li>¡El Número del puerto de redireccionamiento no es un entero válido! </li>
+error.redirectPortText.range=<li>Número del puerto de redireccionamiento parece estar fuera de rango. El rango válido va de 1-65535.</li>
+error.minProcessorsText.required=<li>El mínimo de procesadores no puede estar vacío</li>
+error.minProcessorsText.format=<li>¡El Mínimo de procesadores no es un entero válido!</li>
+error.minProcessorsText.range=<li>El mínimo de procesadores parece estar fuera de rango. El rango válido va de 1-512.</li>
+error.maxProcessorsText.required=<li>El máximo de procesadores no puede estar vacído</li>
+error.maxProcessorsText.format=<li>¡El Máximo de procesadores no es un entero válido!</li>
+error.maxProcessorsText.range=<li>El máximo de procesadores parece estar fuera de rango. El rango válido va de 1-512. También, máximo > = mínimo.</li>
+error.proxyName.invalid=<li>El nombre del apoderado (proxy) es inválido</li>
+error.proxyPortText.required=<li>El número del puerto del apoderado (proxy) no puede estar vacío</li>
+error.proxyPortText.format=<li>¡El Número del puerto del apoderado (proxy) no es un entero válido!</li>
+error.proxyPortText.range=<li>El número del puerto del apoderado (proxy) parece estar fuera de rango. El rango válido va de 1-65535.</li>
+error.hostName.bad=nombre de máquina inválida {0}
+error.hostName.required=<li>Se requiere el nombre de máquina</li>
+error.hostName.exists=<li>Ya existe el Nombre de Máquina</li>
+error.appBase.required=<li>Se requiere la Base de la Aplicación</li>
+service.name=Nombre
+service.engine.props=Propiedades del Motor
+service.defaulthostname=Nombre de Máquina por defecto
+service.properties=Propiedades del Servicio
+service.property=Propiedad
+service.value=Valor
+service.create.new=Crear Nuevo Servicio
+actions.available.actions=Acciones Disponibles
+actions.services.create=Crear Nuevo Servicio
+actions.services.delete=Eliminar Este Servicio
+actions.services.deletes=Eliminar Servicios Existentes
+actions.services.edit=Editar Servicio Existente
+actions.accesslogger.create=Crear Nuevo (Access Logger) Registrador de Acceso
+actions.accesslogger.delete=Eliminar (Access Logger) Registrador de Acceso
+actions.connectors.create=Crear Nuevo Conector
+actions.connectors.delete=Eliminar este Conector
+actions.connectors.deletes=Eliminar Conectores Existentes
+actions.connectors.edit=Editar Conector Existente
+actions.contexts.create=Crear Nuevo Contexto
+actions.contexts.delete=Borrar este Contexto
+actions.contexts.deletes=Borrar Contextos Existentes
+actions.contexts.edit=Editar Contexto Existente
+actions.defaultcontexts.create=Crear DefaultContext Nuevo
+actions.defaultcontexts.delete=Borrar Este DefaultContext
+actions.defaultcontexts.deletes=Borrar DefaultContext Existente
+actions.defaultcontexts.edit=Editar DefaultContext Existente 
+actions.group.create=Crear Nuevo Grupo
+actions.group.delete=Borrar Grupo Existente
+actions.hosts.create=Crear Nueva Máquina
+actions.hosts.delete=Borrar Esta Máquina
+actions.hosts.deletes=Borrar Máquinas Existentes
+actions.hosts.edit=Editar Máquina Existente
+actions.loggers.create=Crear Nuevo (Logger) Registrador
+actions.loggers.delete=Borrar Este (Logger) Registrador
+actions.loggers.deletes=Borrar (Loggers) Registradores Existentes
+actions.loggers.edit=Editar (Logger) Registrador Existente
+actions.realms.create=Crear Nuevo Reino (Realm) de Usuario
+actions.realms.delete=Borrar Este Reino (Realm) de Usuario
+actions.realms.deletes=Borrar Reinos (Realms) de Usuario
+actions.realms.edit=Editar Reino (Realm) Existente
+actions.requestfilter.create=Crear Nuevo Filtro de Petición
+actions.requestfilter.delete=Borrar Filtros de Petición
+actions.user.create=Crear Nuevo Usuario
+actions.user.delete=Eliminar Usuario Existente
+actions.valves.create=Crear Nueva Válvula
+actions.valves.edit=Editar Válvula
+actions.valves.delete=Borrar Esta Válvula
+actions.valves.deletes=Borrar Vávulas Existentes
+actions.alias.create=Crear Nuevo Alias
+actions.alias.delete=Borrar Alias
+new.alias=Nuevo Alias
+connector.type=Tipo
+connector.scheme=Esquema
+connector.accept.count=Contador de Aceptación
+connector.compression=Compresión
+connector.connection.linger=Demora de la Conexión
+connector.connection.timeout=Tiempo de Espera de la Conexión
+connector.connection.uploadTimeout=Tiempo de Espera de Conexión en la Carga
+connector.default.buffer=Tamaño por defecto del Búfer
+connector.connection.disableUploadTimeout=Desactivar Tiempo de Espera de la Carga
+connector.enable.dns=Permitir Búsquedas en el DNS
+connector.uriencoding=Codificación de URI
+connector.useBodyEncodingForURI=Usar Codificación de Cuerpo (Body) para Parámetros de Consulta de URI
+connector.allowTrace=Permitir Método de TRAZA
+connector.address.ip=Dirección IP
+connector.redirect.portnumber=Número del puerto de redireccionamiento
+connector.min=Mínimo
+connector.milliseconds=milisegundos
+connector.max=Máximo
+connector.maxkeepalive=Peticiones Máximas de Maneter Viva (KeepAlive)
+connector.maxspare=Máx. Hilos de Repuesto
+connector.maxthreads=Máx. Hilos
+connector.minspare=Mín. Hilos de Repuesto
+connector.threadpriority=Processor Thread Priority
+connector.proxy.name=Nombre del Apoderado (Proxy)
+connector.proxy.portnumber=Número de Puerto del Apoderado (Proxy)
+connector.algorithm=Algoritmo
+connector.ciphers=Cifrados
+connector.client.auth=Autenticación de Cliente
+connector.keystore.filename=Nombre de Archivo de Almacén de Claves
+connector.keystore.password=Contraseña de Almacén de Claves
+connector.keystore.type=Tipo de Almacén de Claves
+connector.sslProtocol=Protocolo SSL
+connector.keyPass.warning=<li>Utilice por favor keytool para generar el certificado.</li>
+connector.secure=Seguro
+connector.tcpNoDelay=TCP Sin Retardo
+connector.xpoweredby=X Potenciado Mediante
+host.properties=Propiedades de la Máquina
+host.name=Nombre
+host.base=Base De la Aplicación
+host.autoDeploy=Desplegar Automáticamente
+host.deployXML=Despliegue XML 
+host.deployOnStartup=Despliegue En Arranque 
+host.wars=Desempaquetar WARs
+host.aliases=Aliases
+host.alias.name=Nombre de Alias
+host.xmlNamespaceAware=Conocedor de Espacio de Nombres XML
+host.xmlValidation=Validación XML
+error.aliasName.exists=<li>El Alias ya existe</li>
+error.aliasName.required=<li>Se requiere el nombre de Alias</li>
+context.properties=Propiedades del contexto
+context.cookies=Cookies
+context.cross.context=Contexto Cruzado
+context.docBase=Base Del Documento
+context.override=Pasar por Alto
+context.path=Trayectoria
+context.reloadable=Recargable
+context.swallowOutput=Salida del Trago (Swallow)
+context.usenaming=Utilizar el Nombramiento (Naming)
+context.workdir=Directorio de Trabajo
+context.loader.properties=Propiedades del cargador
+context.sessionmgr.properties=Propiedades del gestor de Sesión
+context.checkInterval=Intervalo de Chequeo
+context.sessionId=Inicializador de la Identificación De la Sesión
+context.max.sessions=Máximas Sesiones Activas
+defaultcontext.properties=Propiedades de DefaultContext 
+error.context.directory=La base del documento ni existe ni es un directorio legible 
+error.docBase.required=<li>La base del documento no puede ser nula</li>
+error.path.required=<li>La trayectoria no puede ser nula</li>
+error.workDir.required=<li>El directorio de trabajo no puede ser nulo</li>
+error.ldrCheckInterval.required=<li>El intervalo de chequeo del cargador no puede estar vacío</li>
+error.ldrCheckInterval.format=<li>¡El Intervalo de chequeo del cargador no un número entero válido!</li>
+error.ldrCheckInterval.range=<li>El intervalo de chequeo del cargador parece fuera de rango. El rango válido va de 1-1000.</li>
+error.mgrCheckInterval.required=<li>El intervalo de chequeo del gestor no puede estar vacío</li>
+error.mgrCheckInterval.format=<li>¡El Intervalo del chequeo del gestor no es un número entero válido!</li>
+error.mgrCheckInterval.range=<li>El intervalo de chequeo del gestor parece fuera de rango. El rango valido va de 1-1000.</li>
+error.mgrSessionIDInit.required=<li>La identificación de la inicialización del gestor de la sesión no puede estar vacía</li>
+error.mgrMaxSessions.required=<li>Las sesiones máximas no pueden estar vacías</li>
+error.mgrMaxSessions.format=<li>¡Las Sesiones máximas no son un entero válido!</li>
+error.mgrMaxSessions.range=<li>Las sesiones máximas parecen estar fuera de rango. El rango válido va de -1 a 100.</li>
+list.none=(Nada)
+logger.directory=Directorio
+logger.prefix=Prefijo
+logger.suffix=Sufijo
+logger.timestamp=Sello temporal (timestamp)
+logger.filelogger.properties=Propiedades específicas del Registrador de Archivo (FileLogger)
+logger.verbositylevel=Nivel de Detalle
+error.loggerName.bad=Nombre de registrador inválido {0}
+error.loggerName.exists=<li>El registrador ya existe.</li>
+error.directory.required=<li>El Directorio no puede estar vacío.</li>
+error.prefix.required=<li>El prefijo no puede estar vacío.</li>
+error.suffix.required=<li>El sufijo no puede estar vacío.</li>
+error.valveName.bad=Nombre de válvula inválido {0}
+error.valveName.exists=<li>Ya existe la válvula</li>
+error.singleSignOn.exists=<li>Ya existe la válvula de Login Único (SingleSignOn)</li>
+user.fullName=Nombre Completo
+user.groups=Miembro en los Grupos
+user.newUser=Crear Nuevas Propiedades de Usuario
+user.oldUser=Editar Propiedades ya existentes de Usuario
+user.password=Contraseña
+user.properties=Propiedades de Usuario
+user.roles=Papeles Desempeñados de Seguridad
+user.username=Nombre de Usuario
+error.password.required=<li>Se requiere la contraseña</li>
+error.username.required=<li>Se requiere el nombre de usuario</li>
+error.get.attributes=Error extrayendo propiedades de información.
+error.set.attributes=Error poniendo propiedades de información.
+actions.delete=Borrar
+error.defaultHost.required=<li>Es necesaro el Nombre de Máquina por defecto</li>
+error.engineName.bad=Nombre de motor inválido{0}
+error.engineName.exists=<li>Ya existe el Nombre de Motor</li>
+error.serviceName.bad=Nombre de servicio inválido {0}
+error.serviceName.required=<li>Es necesario el Nombre del Servicio</li>
+error.serviceName.exists=<li>Ya existe el Nombre del Servicio</li>
+error.jdbcrealm=Ha tenido lugar un error al poner valor de JDBCRealm.
+error.jndirealm=Ha tenido lugar un error al poner valor de JNDIRealm.
+error.userdbrealm=Ha tenido lugar un error al poner valor de UserdatabaseRealm.
+error.realmName.bad=Nombre de reino inválido {0}
+error.realmName.required=<li>Es necesario el nombre de Reino.</li>
+error.realmName.exists=<li>Ya existe un Reino.</li>
+error.contextName.bad=Nombre de contexto inválido {0}
+error.contextName.exists=<li>Ya existe el Contexto.</li>
+error.defaultcontextName.exists=<li>Ya existe Contexto por Defecto (DefaultContext).</li>
+error.path.prefix=<li>La trayectoria debe de comenzar con '/'.</li>
+error.loaderName.bad=Nombre de cargador inválido {0}
+error.managerName.bad=Nombre de gestor inválido {0}
+error.connectorName.bad=Nombre de conector inválido {0}
+error.connectorName.exists=<li>Ya existe el Conector</li>
+error.pattern.required=<li>Es necesario el Patrón (Pattern)</li>
+error.valveName.bad=Nombre de válvula inválido {0}
+error.valveName.exists=<li>Ya existe la Válvula</li>
+realm.driver=Manejador (Driver) de Base de Datos
+realm.passwd=Contraseña de la Base de Datos
+realm.url=URL de la Base de Datos
+realm.userName=Nombre de Usuario de la Base de Datos
+realm.digest=Algoritmo Resumen (Digest)
+realm.passwordCol=Columna de Contraseña
+realm.roleNameCol=Columna del Nombre del Papel Desempeñado (Role)
+realm.userNameCol=Columna del Nombre del Usuario
+realm.userRoleTable=Tabla del Papel desempeñado por el Usuario
+realm.userTable=Tabla de Usuario
+realm.resource=Nombre del Recurso
+realm.pathName=Nombre de la Trayectoria
+realm.connName=Nombre de la Conexión
+realm.connPassword=Contraseña de la Conexión
+realm.connURL=URL de la Conexión
+realm.connFactory=Fábrica del Contexto
+realm.roleBase=Elemento Base del Papel Desempeñado
+realm.roleName=Nombre del Papel Desempeñado
+realm.user.roleName=Nombre del Papel Desempeñado
+realm.pattern=Patrón de la Búsqueda del Papel Desempeñado
+realm.role.subtree=Buscar Subárbol del Papel Desempeñado
+realm.userBase=Elemento Base de Usuario
+realm.user.subtree=Buscar Subárbol de Usuario
+realm.userPassword=Contraseña del Usuario
+realm.userPattern=Patrón de Usuario
+realm.userSearch=Búsqueda de Usuario
+valve.access.properties=Propiedades del Registrador de Acceso
+valve.request.properties=Propiedades del Filtro de Requerimiento
+valve.single.properties=Propiedades de la válvula de SignOn Único
+valve.remotehost.properties=Propiedades de la Válvula de Máquina Remota
+valve.remoteaddress.properties=Propiedades de la Válvula de Dirección Remota
+valve.resolveHosts=Resolver Máquinas
+valve.rotatable=Rotativo
+valve.pattern=Patrón
+valve.allowHosts=Permitir a estas Máquinas
+valve.denyHosts=Denegar a estas Máquinas
+valve.allowIPs=Permitir a direcciones IP
+valve.denyIPs=Denegar a direcciones IP
+error.allowHost=<li>Permitir es inválido. Es necesario incluir el nombre de máquina del administrador.</li>
+error.denyHost=<li>Denegar es inválido. Es necesario excluir el nombre de máquina del administrador.</li>
+error.allowIP=<li>Permitir es inválido. Es necesario incluir la dirección IP del administrador.</li>
+error.denyIP=<li>Denegar es inválido. Es necesario excluir la dirección IP del administrador.</li>
+error.allow.deny.required=<li>Es necesario poner Permitir o Denegar.</li>
+error.syntax=<li>Error de sintáxis en patrón del filtro de la petición.</li>
+error.resource.required=<li>Se requiere el Nombre del Recurso.</li>
+error.resource.javaprefix=<li>El recurso debe de tener el prefijo "java:"</li>
+error.pathName.required=<li>Es necesario poner el nombre de la trayectoria.</li>
+error.driver.required=<li>Es necesario poner el manejador (driver) de la base de datos.</li>
+error.roleNameCol.required=<li>Es necesario poner la columna del nombre de Papel Desempeñado.</li>
+error.userNameCol.required=<li>Es necesario poner la columna del nombre de Usuario.</li>
+error.passwordCol.required=<li>Es necesario poner la columna de la Contraseña.</li>
+error.userTable.required=<li>Es necesario poner la Tabla de Usuario.</li>
+error.roleTable.required=<li>Es necesario poner la Tabla del Papel Desempeñado por el Usuario.</li>
+error.connectionPassword.required=<li>Es necesario poner la contraseña de la base de datos.</li>
+error.connectionURL.required=<li>Es necesario poner la URL de la Base de Datos.</li>
+error.connectionName.required=<li>Es necesario poner el nombre de usuario de la Base de Datos.</li>
+error.roleName.required=<li>Es necesario poner el nombre del Papel Desempeñadoo.</li>
+error.userRoleName.required=<li>Es necesario poner el nombre del Papel Desempeñado por el Usuario.</li>
+error.digest.required=<li>Es necesario poner el algoritmo de resumen (digest).</li>
+error.roleBase.required=<li>Es necesario poner el elemento base del Papel Desempeñado.</li>
+error.rolePattern.required=<li>Es necesario poner el patrón de búsqueda del Papel Desempeñado.</li>
+error.userBase.required=<li>Es necesario poner el elemento base del usuario.</li>
+error.userPassword.required=<li>Es necesario poner la Contraseña del Usuario.</li>
+error.userPattern.required=<li>Es necesario poner el patrón del Usuario.</li>
+error.userSearch.required=<li>Es necesario poner la búsqueda del usuario.</li>
+error.userPattern.userSearch.defined=<li>Se debe de especificar UserPattern o userSearch, pero no ambos.</li>
+error.contextFactory.required=<li>Es necesario poner la Fábrica del Contexto.</li>
+error.connPassword.required=<li>Es necesario poner la Contraseña de la Conexión.</li>
+error.connURL.required=<li>Es necesario poner la URL de la Conexión.</li>
+error.connName.required=<li>Es necesario poner el nombre de Conexión.</li>
+
+# ---------- Server Module ----------
+server.service.treeBuilder.subtreeNode=Servicio
+server.service.treeBuilder.connector=Conectador
+server.service.treeBuilder.host=Host
+server.service.treeBuilder.context=Context 
+server.service.treeBuilder.loggerFor=Logger for {0}
+server.service.treeBuilder.realmFor=Realm for {0} 
+server.service.treeBuilder.logger=Logger
+server.service.treeBuilder.realm=Realm
+
+# ---------- Resources Module ----------
+resources.treeBuilder.subtreeNode=Recursos
+resources.treeBuilder.datasources=Fuentes de Datos
+resources.treeBuilder.mailsessions=Sesiones de Correo
+resources.treeBuilder.resourcelinks=Enlaces de Recurso
+resources.env.entries=Entradas de Entorno
+resources.env.entry=Nombre de Entrada
+resources.env.props=Propiedades de la Entrada de Entorno
+resources.env.override=Pasar por alto Entradas de Nivel de Aplicación
+resources.actions.env.create=Crear Nueva Entrada de Entorno
+resources.actions.env.edit=Editar Entrada de Entorno
+resources.actions.env.delete=Borrar Entradas de Entorno
+resources.actions.env.list=Listar Entradas Existentes
+resources.datasrc.jdbc=Manejador JDBC
+resources.actions.datasrc=Fuentes de Datos
+resources.actions.datasrc.create=Crear Nueva Fuente de Datos
+resources.actions.datasrc.delete=Borrar Funtes de Datos
+resources.actions.datasrc.edit=Editar Fuente de Datos
+resources.datasrc.url=URL de Fuente de Datos
+resources.datasrc.jdbcclass=Clase de Manejador JDBC
+resources.datasrc.connections=Conexiones
+resources.datasrc.active=Máx. Conexiones Activas
+resources.datasrc.idle=Máx. Conexiones Ociosas
+resources.datasrc.wait=Máx. Espera por conexión
+resources.datasrc.validation=Consulta de Validación
+resources.datasrc.jndi=Nombre JNDI
+resources.actions.mailsession=Sesiones De Correo
+resources.actions.mailsession.create=Crear Nueva Sesión de Correo
+resources.actions.mailsession.delete=Borrar Sesión de Correo
+resources.actions.mailsession.edit=Editar Sesión de Correo
+resources.mailsession.name=Nombre
+resources.mailsession.mailhost=mail.smtp.host
+resources.actions.resourcelk=Enlaces de Recurso
+resources.actions.resourcelk.create=Crear Nuevo Enlace de Recurso
+resources.actions.resourcelk.delete=Borrar Enlace de Recurso
+resources.actions.resourcelk.edit=Edtiar Enlace de Recurso
+resources.resourcelk.name=Nombre
+resources.resourcelk.global=Global
+resources.resourcelk.type=Tipo
+resources.error.name.required=<li>Nombre requerido.</li>
+resources.error.global.required=<li>Global requerido.</li>
+resources.error.type.required=<li>Tipo requerido.</li>
+resources.error.value.required=<li>Valor requerido.</li>
+resources.error.value.mismatch=<li>El tipo y el valor no parecen coincidir.</li>
+resources.error.entryType.invalid=<li>El tipo de Entrada no se reconoce.</li>
+resources.error.entryType.notimpl=<li>La Validación para este tipo no ha sido implementada aún.</li>
+resources.error.url.required=<li>Se requiere la URL de Fuente de Datos.</li>
+resources.error.driverClass.required=<li>Se requiere la Clase del Conductor (driver) de JDBC.</li>
+resources.error.active.required=<li>Se requieren las conexiones activas máximas.</li>
+resources.error.idle.required=<li>Se requieren las conexiones ociosas máximas.</li>
+resources.error.wait.required=<li>La espera máxima por una conexión se requiere.</li>
+resources.error.mailhost.required=<li>mail.smtp.host se requiere.</li>
+resources.integer.error=<li>Error de número entero inválido.</li>
+resources.actions.userdb.create=Crear Nueva Base de Datos de Usuario
+resources.actions.userdb.edit=Editar Base de Datos de Usuario
+resources.actions.userdb.delete=Borrar Bases de Datos de Usuario
+resources.userdb.location=Localización
+resources.userdb.factory=Fábrica
+resources.treeBuilder.userdbs=Bases de Datos de Usuario
+resources.error.path.required=<li>Se requiere la Trayectoria</li>
+resources.error.jndiName.required=<li>Se requiere el Nombre JNDI</li>
+resources.invalid.name=<li>Nombre inválido de recurso - el nombre ya existe.</li>
+resources.invalid.env=<li>Nombre inválido de entorno - el nombre ya existe.</li>
+
+# ---------- User Database Module ----------
+users.actions.group.create=Crear Nuevo Grupo
+users.actions.group.delete=Borrar Grupos Existentes
+users.actions.group.list=Listar Grupos Existentes
+users.actions.role.create=Crear Nuevo Papel a Desempeñar
+users.actions.role.delete=Borrar Papeles a Desempeñar ya Existentes
+users.actions.role.list=Listar Papeles a Desempeñar ya Existentes
+users.actions.user.create=Crear Nuevo Usuario
+users.actions.user.delete=Borrar Usuarios ya Existentes
+users.actions.user.list=Listar Usuarios ya Existentes
+users.deleteGroups.title=Borrar Grupos ya Existentes
+users.deleteRoles.title=Borrar Papeles a Desempeñar ya Existentes
+users.deleteUsers.title=Borrar Usuarios ya Existentes
+users.error.attribute.get=Error recuperando atributo {0}
+users.error.attribute.set=Error modificando atributo {0}
+users.error.invoke=Error invocando operación {0}
+users.error.groupname.required=Se requiere el Nombre de Grupo
+users.error.password.required=Contraseña Requerida
+users.error.quotes=La doble comilla no esta permitida en los valores de campo
+users.error.rolename.required=Se requiere el Nombre del Papel a Desempeñar
+users.error.select=Error seleccionando objetos gestionados
+users.error.token=Transacción enviada fuera de servicio
+users.error.username.required=<li>Se requiere el nombre del usuario</li>
+users.group.newGroup=Crear Propiedades de Nuevo Grupo
+users.group.oldGroup=Editar Propiedades de Grupo ya Existente
+users.group.properties=Propiedades de Grupo
+users.list.description=Descripción
+users.list.fullName=Nombre Completo
+users.list.groupname=Nombre de Grupo
+users.list.rolename=Nombre de Papel a Desempeñar
+users.list.username=Nombre de Usuario
+users.listGroups.title=Lista de Grupos
+users.listRoles.title=Lista de Papeles a Desempeñar
+users.listUsers.title=Lista de Usuarios
+users.prompt.description=Descripción:
+users.prompt.fullName=Nombre Completo:
+users.prompt.groupname=Nombre de Grupo:
+users.prompt.password=Contraseña:
+users.prompt.rolename=Nombre de Papel a Desempeñar:
+users.prompt.username=Nombre de Usuario:
+users.role.newRole=Crear Nuevas Propiedades de Papel a Desempeñar
+users.role.oldRole=Editar Propiedades de Papel a Desempeñar Existente
+users.role.properties=Propiedades del Papel a Desempeñar
+users.treeBuilder.groupsNode=Grupos
+users.treeBuilder.rolesNode=Papeles a Desempeñar
+users.treeBuilder.subtreeNode=Definición de Usuario
+users.treeBuilder.usersNode=Usuarios
+users.user.newUser=Crear Nuevas Propiedades de Usuario
+users.user.oldUser=Editar Propiedades de Usuario Existente
+users.user.properties=Propiedades de Usuario
+# ---------- -------------------- ----------
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationResources_ja.properties b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationResources_ja.properties
new file mode 100644
index 0000000..44289c9
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationResources_ja.properties
@@ -0,0 +1,444 @@
+application.title=Tomcat\u30b5\u30fc\u30d0\u30fc\u7ba1\u7406\u30b3\u30f3\u30bd\u30fc\u30eb
+errors.header=<h2><fontcolor="red">\u4e0d\u6b63\u306a\u30a8\u30e9\u30fc</font></h2>\u5b9f\u884c\u3059\u308b\u524d\u306b\u3001\u4e0b\u8a18\u306e\u30a8\u30e9\u30fc\u3092\u4fee\u6b63\u3057\u3066\u4e0b\u3055\u3044:<ul>
+errors.footer=</ul><hr>
+error.login=<li>\u30e6\u30fc\u30b6\u540d\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u9055\u3044\u307e\u3059</li>
+prompt.username=\u30e6\u30fc\u30b6\u540d
+prompt.password=\u30d1\u30b9\u30ef\u30fc\u30c9
+button.login=\u30ed\u30b0\u30a4\u30f3
+button.reset=\u30ea\u30bb\u30c3\u30c8
+button.save=\u4fdd\u5b58
+button.change=\u5909\u66f4
+button.cancel=\u30ea\u30bb\u30c3\u30c8
+button.commit=\u5909\u66f4\u3092\u53cd\u6620
+button.logout=\u30ed\u30b0\u30a2\u30a6\u30c8
+login.enter=\u65b0\u3057\u3044\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u958b\u59cb\u3059\u308b\u305f\u3081\u306b\u30e6\u30fc\u30b6\u540d\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044
+login.changeLanguage=\u8a00\u8a9e\u306e\u5909\u66f4
+error.login=\u30e6\u30fc\u30b6\u540d\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u9055\u3044\u307e\u3059
+error.tryagain=\u3082\u3046\u4e00\u5ea6\u5b9f\u884c\u3059\u308b\u305f\u3081\u306b\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044
+error.here=\u3053\u3053
+prompt.someText=Some Text
+prompt.moreText=More Text
+sample.someText.required=<li>"Some Text"\u306f\u7a7a\u306b\u3067\u304d\u307e\u305b\u3093</li>
+sample.moreText.required=<li>"More Text"\u306f\u7a7a\u306b\u3067\u304d\u307e\u305b\u3093</li>
+save.success=\u4fdd\u5b58\u6210\u529f!
+save.fail=\u4fdd\u5b58\u5931\u6557!
+server.portnumber=\u30dd\u30fc\u30c8\u756a\u53f7
+server.debuglevel=\u30c7\u30d0\u30c3\u30b0\u30ec\u30d9\u30eb
+server.shutdown=\u30b7\u30e3\u30c3\u30c8\u30c0\u30a6\u30f3
+server.properties=\u30b5\u30fc\u30d0\u30fc\u30d7\u30ed\u30d1\u30c6\u30a3
+warning.header=<center><h4><fontcolor="red">\u8b66\u544a!</font></h4></center><ul>
+server.port.warning=<li>1024\u756a\u4ee5\u4e0b\u306e\u30dd\u30fc\u30c8\u756a\u53f7\u306e\u5229\u7528\u306b\u306f\u3001\u7279\u5225\u306a\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u6a29\u9650\u304c\u5fc5\u8981\u3067\u3059\u3002
+error.portNumber.required=<li>\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093</li>
+error.portNumber.format=<li>\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.portNumber.range=<li>\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f1-65535\u3067\u3059\u3002</li>
+error.shutdownText.length=<li>\u30b7\u30e3\u30c3\u30c8\u30c0\u30a6\u30f3\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u306f6\u5b57\u4ee5\u4e0a\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093</li>
+error.engineName.required=<li>\u30a8\u30f3\u30b8\u30f3\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+error.acceptCountText.required=<li>\u540c\u6642\u63a5\u7d9a\u6570\u304c\u5fc5\u8981\u3067\u3059</li>
+error.acceptCountText.format=<li>\u540c\u6642\u63a5\u7d9a\u6570\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.acceptCountText.range=<li>\u540c\u6642\u63a5\u7d9a\u6570\u304c\u7bc4\u56f2\u5916\u3067\u3059. \u7bc4\u56f2\u306f0-128\u3067\u3059\u3002</li>
+error.connTimeOutText.required=<li>\u63a5\u7d9a\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u304c\u5fc5\u8981\u3067\u3059</li>
+error.connTimeOutText.format=<li>\u63a5\u7d9a\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.connTimeOutText.range=<li>\u63a5\u7d9a\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f0-60000\u3067\u3059\u3002</li>
+error.bufferSizeText.required=<li>\u30d0\u30c3\u30d5\u30a1\u30b5\u30a4\u30ba\u304c\u5fc5\u8981\u3067\u3059</li>
+error.bufferSizeText.format=<li>\u30d0\u30c3\u30d5\u30a1\u30b5\u30a4\u30ba\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.bufferSizeText.range=<li>\u30d0\u30c3\u30d5\u30a1\u30b5\u30a4\u30ba\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f1-8192\u3067\u3059\u3002</li>
+error.address.invalid=<li>IP\u30a2\u30c9\u30ec\u30b9\u304c\u4e0d\u6b63\u3067\u3059</li>
+error.redirectPortText.required=<li>\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093</li>
+error.redirectPortText.format=<li>\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.redirectPortText.range=<li>\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f1-65535\u3067\u3059\u3002</li>
+error.minProcessorsText.required=<li>\u6700\u5c0f\u30d7\u30ed\u30bb\u30b9\u6570\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093</li>
+error.minProcessorsText.format=<li>\u6700\u5c0f\u30d7\u30ed\u30bb\u30b9\u6570\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.minProcessorsText.range=<li>\u6700\u5c0f\u30d7\u30ed\u30bb\u30b9\u6570\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f1-512\u3067\u3059\u3002</li>
+error.maxProcessorsText.required=<li>\u6700\u5927\u30d7\u30ed\u30bb\u30b9\u6570\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093</li>
+error.maxProcessorsText.format=<li>\u6700\u5927\u30d7\u30ed\u30bb\u30b9\u6570\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.maxProcessorsText.range=<li>\u6700\u5927\u30d7\u30ed\u30bb\u30b9\u6570\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f1-512\u3067\u3059. \u307e\u305f\u3001\u6700\u5927\u30d7\u30ed\u30bb\u30b9\u6570 >= \u6700\u5c0f\u30d7\u30ed\u30bb\u30b9\u6570\u3001\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002</li>
+error.proxyName.invalid=<li>\u30d7\u30ed\u30ad\u30b7\u540d\u304c\u4e0d\u6b63\u3067\u3059</li>
+error.proxyPortText.required=<li>\u30d7\u30ed\u30ad\u30b7\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093</li>
+error.proxyPortText.format=<li>\u30d7\u30ed\u30ad\u30b7\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.proxyPortText.range=<li>\u30d7\u30ed\u30ad\u30b7\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f1-65535\u3067\u3059\u3002</li>
+error.hostName.bad=\u30db\u30b9\u30c8\u540d {0} \u304c\u4e0d\u6b63\u3067\u3059
+error.hostName.required=<li>\u30db\u30b9\u30c8\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+error.hostName.exists=<li>\u540c\u540d\u306e\u30db\u30b9\u30c8\u540d\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059</li>
+error.appBase.required=<li>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d9\u30fc\u30b9\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+service.name=\u540d\u524d
+service.engine.props=\u30a8\u30f3\u30b8\u30f3\u30d7\u30ed\u30d1\u30c6\u30a3
+service.defaulthostname=\u30c7\u30d5\u30a9\u30eb\u30c8\u30db\u30b9\u30c8\u540d
+service.properties=\u30b5\u30fc\u30d3\u30b9\u30d7\u30ed\u30d1\u30c6\u30a3
+service.property=\u30d7\u30ed\u30d1\u30c6\u30a3
+service.value=\u5024
+service.create.new=\u65b0\u3057\u3044\u30b5\u30fc\u30d3\u30b9\u306e\u4f5c\u6210
+actions.available.actions=\u5229\u7528\u53ef\u80fd\u306a\u30a2\u30af\u30b7\u30e7\u30f3
+actions.services.create=\u65b0\u3057\u3044\u30b5\u30fc\u30d3\u30b9\u306e\u4f5c\u6210
+actions.services.delete=\u73fe\u5728\u306e\u30b5\u30fc\u30d3\u30b9\u3092\u524a\u9664
+actions.services.deletes=\u65e2\u5b58\u306e\u30b5\u30fc\u30d3\u30b9\u306e\u524a\u9664
+actions.services.edit=\u65e2\u5b58\u306e\u30b5\u30fc\u30d3\u30b9\u306e\u7de8\u96c6
+actions.accesslogger.create=\u65b0\u3057\u3044\u30a2\u30af\u30bb\u30b9Logger\u306e\u4f5c\u6210
+actions.accesslogger.delete=\u30a2\u30af\u30bb\u30b9Logger\u306e\u524a\u9664
+actions.connectors.create=\u65b0\u3057\u3044\u30b3\u30cd\u30af\u30bf\u306e\u4f5c\u6210
+actions.connectors.delete=\u73fe\u5728\u306e\u30b3\u30cd\u30af\u30bf\u3092\u524a\u9664
+actions.connectors.deletes=\u65e2\u5b58\u306e\u30b3\u30cd\u30af\u30bf\u306e\u524a\u9664
+actions.connectors.edit=\u65e2\u5b58\u306e\u30b3\u30cd\u30af\u30bf\u306e\u7de8\u96c6
+actions.contexts.create=\u65b0\u3057\u3044\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u4f5c\u6210
+actions.contexts.delete=\u73fe\u5728\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u524a\u9664
+actions.contexts.deletes=\u65e2\u5b58\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u524a\u9664
+actions.contexts.edit=\u65e2\u5b58\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u7de8\u96c6
+actions.defaultcontexts.create=\u65b0\u3057\u3044\u30c7\u30d5\u30a9\u30eb\u30c8\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u4f5c\u6210
+actions.defaultcontexts.delete=\u73fe\u5728\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u524a\u9664
+actions.defaultcontexts.deletes=\u65e2\u5b58\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u524a\u9664
+actions.defaultcontexts.edit=\u65e2\u5b58\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u7de8\u96c6
+actions.group.create=\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7\u306e\u4f5c\u6210
+actions.group.delete=\u65e2\u5b58\u306e\u30b0\u30eb\u30fc\u30d7\u306e\u524a\u9664
+actions.hosts.create=\u65b0\u3057\u3044\u30db\u30b9\u30c8\u306e\u4f5c\u6210
+actions.hosts.delete=\u73fe\u5728\u306e\u30db\u30b9\u30c8\u3092\u524a\u9664
+actions.hosts.deletes=\u65e2\u5b58\u306e\u30db\u30b9\u30c8\u306e\u524a\u9664
+actions.hosts.edit=\u65e2\u5b58\u306e\u30db\u30b9\u30c8\u306e\u7de8\u96c6
+actions.loggers.create=\u65b0\u3057\u3044Logger\u306e\u4f5c\u6210
+actions.loggers.delete=\u73fe\u5728\u306eLogger\u3092\u524a\u9664
+actions.loggers.deletes=\u65e2\u5b58\u306eLoggers\u306e\u524a\u9664
+actions.loggers.edit=\u65e2\u5b58\u306eLogger\u306e\u7de8\u96c6
+actions.realms.create=\u65b0\u3057\u3044\u30e6\u30fc\u30b6\u30ec\u30eb\u30e0\u306e\u4f5c\u6210
+actions.realms.delete=\u73fe\u5728\u306e\u30e6\u30fc\u30b6\u30ec\u30eb\u30e0\u306e\u524a\u9664
+actions.realms.deletes=\u30e6\u30fc\u30b6\u30ec\u30eb\u30e0\u306e\u524a\u9664
+actions.realms.edit=\u65e2\u5b58\u306e\u30ec\u30eb\u30e0\u306e\u7de8\u96c6
+actions.requestfilter.create=\u65b0\u3057\u3044\u30ea\u30af\u30a8\u30b9\u30c8\u30d5\u30a3\u30eb\u30bf\u306e\u4f5c\u6210
+actions.requestfilter.delete=\u30ea\u30af\u30a8\u30b9\u30c8\u30d5\u30a3\u30eb\u30bf\u306e\u524a\u9664
+actions.user.create=\u65b0\u3057\u3044\u30e6\u30fc\u30b6\u306e\u4f5c\u6210
+actions.user.delete=\u65e2\u5b58\u306e\u30e6\u30fc\u30b6\u306e\u524a\u9664
+actions.valves.create=\u65b0\u3057\u3044\u30d0\u30eb\u30d6\u306e\u4f5c\u6210
+actions.valves.edit=\u30d0\u30eb\u30d6\u306e\u7de8\u96c6
+actions.valves.delete=\u73fe\u5728\u306e\u30d0\u30eb\u30d6\u3092\u524a\u9664
+actions.valves.deletes=\u65e2\u5b58\u306e\u30d0\u30eb\u30d6\u306e\u524a\u9664
+actions.alias.create=\u65b0\u3057\u3044\u30a8\u30a4\u30ea\u30a2\u30b9\u306e\u4f5c\u6210
+actions.alias.delete=\u30a8\u30a4\u30ea\u30a2\u30b9\u306e\u524a\u9664
+new.alias=\u65b0\u3057\u3044\u30a8\u30a4\u30ea\u30a2\u30b9
+connector.type=\u7a2e\u985e
+connector.scheme=\u30b9\u30ad\u30fc\u30e0
+connector.accept.count=\u540c\u6642\u63a5\u7d9a\u6570
+connector.compression=\u5727\u7e2e
+connector.connection.linger=\u63a5\u7d9a\u306e\u6b8b\u5b58[linger]
+connector.connection.timeout=\u63a5\u7d9a\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8
+connector.connection.uploadTimeout=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8
+connector.default.buffer=\u30c7\u30d5\u30a9\u30eb\u30c8\u30d0\u30c3\u30d5\u30a1\u30b5\u30a4\u30ba
+connector.connection.disableUploadTimeout=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u306e\u7121\u52b9\u5316
+connector.enable.dns=DNS\u306e\u30eb\u30c3\u30af\u30a2\u30c3\u30d7
+connector.uriencoding=URI \u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0
+connector.useBodyEncodingForURI=URI\u30af\u30a8\u30ea\u30d1\u30e9\u30e1\u30fc\u30bf\u306b\u30dc\u30c7\u30a3\u306e\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u3092\u4f7f\u7528
+connector.allowTrace=TRACE\u30e1\u30bd\u30c3\u30c9\u3092\u8a31\u53ef
+connector.address.ip=IP\u30a2\u30c9\u30ec\u30b9
+connector.redirect.portnumber=\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u30dd\u30fc\u30c8\u756a\u53f7
+connector.min=\u6700\u5c0f
+connector.milliseconds=\u30df\u30ea\u79d2
+connector.max=\u6700\u5927
+connector.maxkeepalive=\u30ad\u30fc\u30d7\u30a2\u30e9\u30a4\u30d6\u30ea\u30af\u30a8\u30b9\u30c8\u6700\u5927\u6570
+connector.maxspare=\u4e88\u5099\u30b9\u30ec\u30c3\u30c9\u6700\u5927\u6570
+connector.maxthreads=\u30b9\u30ec\u30c3\u30c9\u6700\u5927\u6570
+connector.minspare=\u4e88\u5099\u30b9\u30ec\u30c3\u30c9\u6700\u5c0f\u6570
+connector.threadpriority=Processor Thread Priority
+connector.proxy.name=\u30d7\u30ed\u30ad\u30b7\u540d
+connector.proxy.portnumber=\u30d7\u30ed\u30ad\u30b7\u30dd\u30fc\u30c8\u756a\u53f7
+connector.algorithm=\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0
+connector.ciphers=\u6697\u53f7\u5316\u65b9\u5f0f
+connector.client.auth=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u8a8d\u8a3c
+connector.keystore.filename=\u30ad\u30fc\u30b9\u30c8\u30a2\u30d5\u30a1\u30a4\u30eb\u540d
+connector.keystore.password=\u30ad\u30fc\u30b9\u30c8\u30a2\u30d1\u30b9\u30ef\u30fc\u30c9
+connector.keystore.type=\u30ad\u30fc\u30b9\u30c8\u30a2\u306e\u7a2e\u985e
+connector.sslProtocol=SSL\u30d7\u30ed\u30c8\u30b3\u30eb
+connector.keyPass.warning=<li>keytool\u3092\u4f7f\u3063\u3066\u8a3c\u660e\u66f8\u3092\u4f5c\u6210\u3057\u3066\u4e0b\u3055\u3044\u3002</li>
+connector.secure=SSL\u901a\u4fe1
+connector.tcpNoDelay=TCP\u9045\u5ef6\u7121\u3057
+connector.xpoweredby=X Powered By \u30d8\u30c3\u30c0
+host.properties=\u30db\u30b9\u30c8\u30d7\u30ed\u30d1\u30c6\u30a3
+host.name=\u540d\u524d
+host.base=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d9\u30fc\u30b9
+host.autoDeploy=\u81ea\u52d5\u914d\u5099
+host.deployXML=\u914d\u5099XML
+host.deployOnStartup=\u8d77\u52d5\u6642\u306e\u914d\u5099
+host.wars=WAR\u30d5\u30a1\u30a4\u30eb\u3092\u5c55\u958b\u3059\u308b
+host.aliases=\u30a8\u30a4\u30ea\u30a2\u30b9
+host.alias.name=\u30a8\u30a4\u30ea\u30a2\u30b9\u540d
+host.xmlNamespaceAware=XML\u540d\u524d\u7a7a\u9593\u5bfe\u5fdc
+host.xmlValidation=XML\u691c\u8a3c
+error.aliasName.exists=<li>\u540c\u540d\u306e\u30a8\u30a4\u30ea\u30a2\u30b9\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059</li>
+error.aliasName.required=<li>\u30a8\u30a4\u30ea\u30a2\u30b9\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+context.properties=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d7\u30ed\u30d1\u30c6\u30a3
+context.cookies=\u30af\u30c3\u30ad\u30fc
+context.cross.context=\u30af\u30ed\u30b9\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8
+context.docBase=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9
+context.override=\u4e0a\u66f8\u304d
+context.path=\u30d1\u30b9
+context.reloadable=\u518d\u30ed\u30fc\u30c9\u53ef\u80fd
+context.swallowOutput=\u6a19\u6e96\u51fa\u529b\u30fb\u6a19\u6e96\u30a8\u30e9\u30fc\u51fa\u529b\u3092\u30ed\u30b0\u30d5\u30a1\u30a4\u30eb\u306b\u51fa\u529b
+context.usenaming=\u30cd\u30fc\u30e0\u30b5\u30fc\u30d3\u30b9\u306e\u5229\u7528
+context.workdir=\u30ef\u30fc\u30ad\u30f3\u30b0\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
+context.loader.properties=\u30ed\u30fc\u30c0\u30d7\u30ed\u30d1\u30c6\u30a3
+context.sessionmgr.properties=\u30bb\u30c3\u30b7\u30e7\u30f3\u30de\u30cd\u30fc\u30b8\u30e3\u30d7\u30ed\u30d1\u30c6\u30a3
+context.checkInterval=\u30c1\u30a7\u30c3\u30af\u9593\u9694
+context.sessionId=\u30bb\u30c3\u30b7\u30e7\u30f3ID\u30a4\u30cb\u30b7\u30e3\u30e9\u30a4\u30b6
+context.max.sessions=\u6700\u5927\u6709\u52b9\u30bb\u30c3\u30b7\u30e7\u30f3\u6570
+defaultcontext.properties=\u30c7\u30d5\u30a9\u30eb\u30c8\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d7\u30ed\u30d1\u30c6\u30a3
+error.context.directory=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u304c\u5b58\u5728\u3057\u306a\u3044\uff0c\u53c8\u306f\u8aad\u3081\u306a\u3044\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3067\u3059
+error.docBase.required=<li>\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u306fnull\u306b\u3067\u304d\u307e\u305b\u3093</li>
+error.path.required=<li>\u30d1\u30b9\u306fnull\u306b\u3067\u304d\u307e\u305b\u3093</li>
+error.workDir.required=<li>\u30ef\u30fc\u30ad\u30f3\u30b0\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306fnull\u306b\u3067\u304d\u307e\u305b\u3093</li>
+error.ldrCheckInterval.required=<li>\u30ed\u30fc\u30c0\u30c1\u30a7\u30c3\u30af\u9593\u9694\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093</li>
+error.ldrCheckInterval.format=<li>\u30ed\u30fc\u30c0\u30c1\u30a7\u30c3\u30af\u9593\u9694\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.ldrCheckInterval.range=<li>\u30ed\u30fc\u30c0\u30c1\u30a7\u30c3\u30af\u9593\u9694\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f1-1000\u3067\u3059\u3002</li>
+error.mgrCheckInterval.required=<li>\u30de\u30cd\u30fc\u30b8\u30e3\u30c1\u30a7\u30c3\u30af\u9593\u9694\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093</li>
+error.mgrCheckInterval.format=<li>\u30de\u30cd\u30fc\u30b8\u30e3\u30c1\u30a7\u30c3\u30af\u9593\u9694\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.mgrCheckInterval.range=<li>\u30de\u30cd\u30fc\u30b8\u30e3\u30c1\u30a7\u30c3\u30af\u9593\u9694\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f1-1000\u3067\u3059\u3002</li>
+error.mgrSessionIDInit.required=<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u30de\u30cd\u30fc\u30b8\u30e3\u521d\u671f\u5316ID\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093</li>
+error.mgrMaxSessions.required=<li>\u6700\u5927\u30bb\u30c3\u30b7\u30e7\u30f3\u6570\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093</li>
+error.mgrMaxSessions.format=<li>\u6700\u5927\u30bb\u30c3\u30b7\u30e7\u30f3\u6570\u304c\u59a5\u5f53\u306a\u6574\u6570\u3067\u306f\u3042\u308a\u307e\u305b\u3093</li>
+error.mgrMaxSessions.range=<li>\u6700\u5927\u30bb\u30c3\u30b7\u30e7\u30f3\u6570\u304c\u7bc4\u56f2\u5916\u3067\u3059\u3002\u7bc4\u56f2\u306f-1\u304b\u3089100\u3067\u3059\u3002</li>
+list.none=(None)
+logger.directory=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
+logger.prefix=\u30d7\u30ea\u30d5\u30a3\u30c3\u30af\u30b9
+logger.suffix=\u30b5\u30d5\u30a3\u30c3\u30af\u30b9
+logger.timestamp=\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7
+logger.filelogger.properties=FileLogger\u56fa\u6709\u306e\u30d7\u30ed\u30d1\u30c6\u30a3
+logger.verbositylevel=\u5197\u9577\u30ec\u30d9\u30eb
+error.loggerName.bad=\u4e0d\u6b63\u306aLogger\u540d {0}
+error.loggerName.exists=<li>\u540c\u540d\u306eLogger\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002</li>
+error.directory.required=<li>\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002</li>
+error.prefix.required=<li>\u30d7\u30ea\u30d5\u30a3\u30c3\u30af\u30b9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002</li>
+error.suffix.required=<li>\u30b5\u30d5\u30a3\u30c3\u30af\u30b9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002</li>
+error.valveName.bad=\u4e0d\u6b63\u306a\u30d0\u30eb\u30d6\u540d {0}
+error.vavlveName.exists=<li>\u540c\u540d\u306e\u30d0\u30eb\u30d6\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059</li>
+error.singleSignOn.exists=<li>SingleSignOn\u30d0\u30eb\u30d6\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059</li>
+user.fullName=\u30d5\u30eb\u30cd\u30fc\u30e0
+user.groups=\u30b0\u30eb\u30fc\u30d7\u4e2d\u306e\u30e1\u30f3\u30d0
+user.newUser=\u65b0\u3057\u3044\u30e6\u30fc\u30b6\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u4f5c\u6210
+user.oldUser=\u65e2\u5b58\u306e\u30e6\u30fc\u30b6\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u7de8\u96c6
+user.password=\u30d1\u30b9\u30ef\u30fc\u30c9
+user.properties=\u30e6\u30fc\u30b6\u30d7\u30ed\u30d1\u30c6\u30a3
+user.roles=\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ed\u30fc\u30eb
+user.username=\u30e6\u30fc\u30b6\u540d
+error.password.required=<li>\u30d1\u30b9\u30ef\u30fc\u30c9\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+error.username.required=<li>\u30e6\u30fc\u30b6\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+error.get.attributes=\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u56de\u5fa9\u6642\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
+error.set.attributes=\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u8a2d\u5b9a\u6642\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
+actions.delete=\u524a\u9664
+error.defaultHost.required=<li>\u30c7\u30d5\u30a9\u30eb\u30c8\u30db\u30b9\u30c8\u540d\u304c\u5fc5\u8981\u3067\u3059</li>
+error.engineName.bad=\u30a8\u30f3\u30b8\u30f3\u540d {0} \u304c\u4e0d\u6b63\u3067\u3059
+error.engineName.exists=<li>\u30a8\u30f3\u30b8\u30f3\u540d\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059</li>
+error.serviceName.bad=\u30b5\u30fc\u30d3\u30b9\u540d {0} \u304c\u4e0d\u6b63\u3067\u3059
+error.serviceName.required=<li>\u30b5\u30fc\u30d3\u30b9\u540d\u304c\u5fc5\u8981\u3067\u3059</li>
+error.serviceName.exists=<li>\u540c\u540d\u306e\u30b5\u30fc\u30d3\u30b9\u540d\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059</li>
+error.jdbcrealm=JDBCRealm\u306e\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
+error.jndirealm=JNDIRealm\u306e\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
+error.userdbrealm=UserdatabaseRealm\u306e\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
+error.realmName.bad=\u30ec\u30eb\u30e0\u540d {0} \u304c\u4e0d\u6b63\u3067\u3059
+error.realmName.required=<li>\u30ec\u30eb\u30e0\u540d\u304c\u5fc5\u8981\u3067\u3059\u3002</li>
+error.realmName.exists=<li>\u540c\u540d\u306e\u30ec\u30eb\u30e0\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002</li>
+error.contextName.bad=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u540d {0} \u304c\u4e0d\u6b63\u3067\u3059
+error.contextName.exists=<li>\u540c\u540d\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002</li>
+error.defaultcontextName.exists=<li>\u540c\u540d\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002</li>
+error.path.prefix=<li>\u30d1\u30b9\u306f'/'\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002</li>
+error.loaderName.bad=\u30ed\u30fc\u30c0\u540d {0} \u304c\u4e0d\u6b63\u3067\u3059
+error.managerName.bad=\u30de\u30cd\u30fc\u30b8\u30e3\u540d {0} \u304c\u4e0d\u6b63\u3067\u3059
+error.connectorName.bad=\u30b3\u30cd\u30af\u30bf\u540d {0} \u304c\u4e0d\u6b63\u3067\u3059
+error.connectorName.exists=<li>\u540c\u540d\u306e\u30b3\u30cd\u30af\u30bf\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059</li>
+error.pattern.required=<li>\u30d1\u30bf\u30fc\u30f3\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+error.valveName.bad=\u4e0d\u6b63\u306a\u30d0\u30eb\u30d6\u540d {0}
+error.valveName.exists=<li>\u540c\u540d\u306e\u30d0\u30eb\u30d6\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059</li>
+realm.driver=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30c9\u30e9\u30a4\u30d0
+realm.passwd=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\u30ef\u30fc\u30c9
+realm.url=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9URL
+realm.userName=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30e6\u30fc\u30b6\u540d
+realm.digest=\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0
+realm.passwordCol=\u30d1\u30b9\u30ef\u30fc\u30c9\u30ab\u30e9\u30e0
+realm.roleNameCol=\u30ed\u30fc\u30eb\u540d\u30ab\u30e9\u30e0
+realm.userNameCol=\u30e6\u30fc\u30b6\u540d\u30ab\u30e9\u30e0
+realm.userRoleTable=\u30e6\u30fc\u30b6\u30ed\u30fc\u30eb\u30c6\u30fc\u30d6\u30eb
+realm.userTable=\u30e6\u30fc\u30b6\u30c6\u30fc\u30d6\u30eb
+realm.resource=\u30ea\u30bd\u30fc\u30b9\u540d
+realm.pathName=\u30d1\u30b9\u540d
+realm.connName=\u63a5\u7d9a\u540d
+realm.connPassword=\u63a5\u7d9a\u30d1\u30b9\u30ef\u30fc\u30c9
+realm.connURL=\u63a5\u7d9aURL
+realm.connFactory=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d5\u30a1\u30af\u30c8\u30ea
+realm.roleBase=\u30ed\u30fc\u30eb\u57fa\u672c\u8981\u7d20
+realm.roleName=\u30ed\u30fc\u30eb\u540d
+realm.user.roleName=\u30e6\u30fc\u30b6\u30ed\u30fc\u30eb\u540d
+realm.pattern=\u30ed\u30fc\u30eb\u691c\u7d22\u30d1\u30bf\u30fc\u30f3
+realm.role.subtree=\u30ed\u30fc\u30eb\u306e\u30b5\u30d6\u30c4\u30ea\u30fc\u306e\u691c\u7d22
+realm.userBase=\u30e6\u30fc\u30b6\u57fa\u672c\u8981\u7d20
+realm.user.subtree=\u30e6\u30fc\u30b6\u306e\u30b5\u30d6\u30c4\u30ea\u30fc\u306e\u691c\u7d22
+realm.userPassword=\u30e6\u30fc\u30b6\u30d1\u30b9\u30ef\u30fc\u30c9
+realm.userPattern=\u30e6\u30fc\u30b6\u30d1\u30bf\u30fc\u30f3
+realm.userSearch=\u30e6\u30fc\u30b6\u691c\u7d22
+realm.dataSourceName=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d
+realm.localDataSource=\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
+realm.userCredCol==\u30e6\u30fc\u30b6\u30af\u30ec\u30c7\u30f3\u30b7\u30e3\u30eb\u30b3\u30e9\u30e0
+valve.access.properties=\u30a2\u30af\u30bb\u30b9Logger\u30d7\u30ed\u30d1\u30c6\u30a3
+valve.request.properties=\u30ea\u30af\u30a8\u30b9\u30c8\u30d5\u30a3\u30eb\u30bf\u30d7\u30ed\u30d1\u30c6\u30a3
+valve.single.properties=\u30b7\u30f3\u30b0\u30eb\u30b5\u30a4\u30f3\u30aa\u30f3\u30d0\u30eb\u30d6\u30d7\u30ed\u30d1\u30c6\u30a3
+valve.remotehost.properties=\u30ea\u30e2\u30fc\u30c8\u30db\u30b9\u30c8\u30d0\u30eb\u30d6\u30d7\u30ed\u30d1\u30c6\u30a3
+valve.remoteaddress.properties=\u30ea\u30e2\u30fc\u30c8\u30a2\u30c9\u30ec\u30b9\u30d0\u30eb\u30d6\u30d7\u30ed\u30d1\u30c6\u30a3
+valve.resolveHosts=\u30db\u30b9\u30c8\u540d\u306e\u89e3\u6c7a
+valve.rotatable=\u30ed\u30fc\u30c6\u30fc\u30c8\u53ef\u80fd
+valve.pattern=\u30d1\u30bf\u30fc\u30f3
+valve.allowHosts=\u8a31\u53ef\u3059\u308b\u30db\u30b9\u30c8
+valve.denyHosts=\u62d2\u5426\u3059\u308b\u30db\u30b9\u30c8
+valve.allowIPs=\u8a31\u53ef\u3059\u308bIP\u30a2\u30c9\u30ec\u30b9
+valve.denyIPs=\u62d2\u5426\u3059\u308bIP\u30a2\u30c9\u30ec\u30b9
+error.allowHost=<li>allow\u304c\u7121\u52b9\u3067\u3059\u3002\u7ba1\u7406\u8005\u306e\u30db\u30b9\u30c8\u540d\u3092\u542b\u3081\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002</li>+error.denyHost=<li>deny\u304c\u7121\u52b9\u3067\u3059\u3002\u7ba1\u7406\u8005\u306e\u30db\u30b9\u30c8\u540d\u3092\u9664\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002</li>
+error.allowIP=<li>allow\u304c\u7121\u52b9\u3067\u3059\u3002\u7ba1\u7406\u8005\u306eIP\u30a2\u30c9\u30ec\u30b9\u3092\u542b\u3081\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002</li>
+error.denyIP=<li>deny\u304c\u7121\u52b9\u3067\u3059\u3002\u7ba1\u7406\u8005\u306eIP\u30a2\u30c9\u30ec\u30b9\u3092\u9664\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002</li>
+error.allow.deny.required=<li>allow\u53c8\u306fdeny\u304c\u5fc5\u8981\u3067\u3059\u3002</li>
+error.syntax=<li>\u30ea\u30af\u30a8\u30b9\u30c8\u30d5\u30a3\u30eb\u30bf\u30d1\u30bf\u30fc\u30f3\u306e\u4e2d\u3067\u69cb\u6587\u30a8\u30e9\u30fc\u3067\u3059\u3002</li>
+error.resource.required=<li>\u30ea\u30bd\u30fc\u30b9\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.resource.javaprefix=<li>\u30ea\u30bd\u30fc\u30b9\u306b\u306f"java:"\u306e\u30d7\u30ea\u30d5\u30a3\u30af\u30b9\u304c\u5fc5\u8981\u3067\u3059\u3002</li>
+error.pathName.required=<li>\u30d1\u30b9\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.driver.required=<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30c9\u30e9\u30a4\u30d0\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.roleNameCol.required=<li>\u30ed\u30fc\u30eb\u540d\u30ab\u30e9\u30e0\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.userNameCol.required=<li>\u30e6\u30fc\u30b6\u540d\u30ab\u30e9\u30e0\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.passwordCol.required=<li>\u30d1\u30b9\u30ef\u30fc\u30c9\u30ab\u30e9\u30e0\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.userTable.required=<li>\u30e6\u30fc\u30b6\u30c6\u30fc\u30d6\u30eb\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.roleTable.required=<li>\u30e6\u30fc\u30b6\u30ed\u30fc\u30eb\u30c6\u30fc\u30d6\u30eb\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.connectionPassword.required=<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\u30ef\u30fc\u30c9\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.connectionURL.required=<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9URL\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.connectionName.required=<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30e6\u30fc\u30b6\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.roleName.required=<li>\u30ed\u30fc\u30eb\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.userRoleName.required=<li>\u30e6\u30fc\u30b6\u30ed\u30fc\u30eb\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.digest.required=<li>\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.roleBase.required=<li>\u30ed\u30fc\u30eb\u57fa\u672c\u8981\u7d20\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.rolePattern.required=<li>\u30ed\u30fc\u30eb\u691c\u7d22\u30d1\u30bf\u30fc\u30f3\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.userBase.required=<li>\u30e6\u30fc\u30b6\u57fa\u672c\u8981\u7d20\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.userPassword.required=<li>\u30e6\u30fc\u30b6\u30d1\u30b9\u30ef\u30fc\u30c9\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.userPattern.required=<li>\u30e6\u30fc\u30b6\u30d1\u30bf\u30fc\u30f3\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.userSearch.required=<li>\u30e6\u30fc\u30b6\u691c\u7d22\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.userPattern.userSearch.defined=<li>\u30e6\u30fc\u30b6\u30d1\u30bf\u30fc\u30f3\u307e\u305f\u306f\u30e6\u30fc\u30b6\u691c\u7d22\u306e\u3044\u305a\u308c\u304b\u4e00\u65b9\u306e\u307f\u3092\u6307\u5b9a\u3057\u307e\u3059. \u4e21\u65b9\u6307\u5b9a\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.contextFactory.required=<li>\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d5\u30a1\u30af\u30c8\u30ea\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.connPassword.required=<li>\u63a5\u7d9a\u30d1\u30b9\u30ef\u30fc\u30c9\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.connURL.required=<li>\u63a5\u7d9aURL\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.connName.required=<li>\u63a5\u7d9a\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+error.dataSourceName.required=<li>\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d\u304c\u5fc5\u8981\u3067\u3059\u3002</li>
+error.userCredCol.required=<li>\u30e6\u30fc\u30b6\u30af\u30ec\u30c7\u30f3\u30b7\u30e3\u30eb\u304c\u5fc5\u8981\u3067\u3059\u3002</li>
+error.userRoleTable.required=<li>\u30e6\u30fc\u30b6\u306e\u30ed\u30fc\u30eb\u30c6\u30fc\u30d6\u30eb\u304c\u5fc5\u8981\u3067\u3059\u3002</li>
+
+# ---------- Server Module ----------
+server.service.treeBuilder.subtreeNode=\u30b5\u30fc\u30d3\u30b9
+server.service.treeBuilder.connector=\u30b3\u30cd\u30af\u30bf
+server.service.treeBuilder.host=\u30db\u30b9\u30c8
+server.service.treeBuilder.context=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8
+server.service.treeBuilder.loggerFor={0}\u306e\u30ed\u30ac\u30fc 
+server.service.treeBuilder.realmFor={0}\u306e\u30ec\u30eb\u30e0
+server.service.treeBuilder.logger=\u30ed\u30ac\u30fc 
+server.service.treeBuilder.realm=\u30ec\u30eb\u30e0
+
+# ---------- Resources Module ----------
+resources.treeBuilder.subtreeNode=\u30ea\u30bd\u30fc\u30b9
+resources.treeBuilder.datasources=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
+resources.treeBuilder.mailsessions=\u30e1\u30fc\u30eb\u30bb\u30c3\u30b7\u30e7\u30f3
+resources.treeBuilder.resourcelinks=\u30ea\u30bd\u30fc\u30b9\u30ea\u30f3\u30af
+resources.env.entries=\u74b0\u5883\u30a8\u30f3\u30c8\u30ea
+resources.env.entry=\u30a8\u30f3\u30c8\u30ea\u540d
+resources.env.props=\u74b0\u5883\u30a8\u30f3\u30c8\u30ea\u30d7\u30ed\u30d1\u30c6\u30a3
+resources.env.override=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30ec\u30d9\u30eb\u30a8\u30f3\u30c8\u30ea\u3092\u4e0a\u66f8\u304d
+resources.actions.env.create=\u65b0\u3057\u3044\u74b0\u5883\u30a8\u30f3\u30c8\u30ea\u306e\u4f5c\u6210
+resources.actions.env.edit=\u74b0\u5883\u30a8\u30f3\u30c8\u30ea\u306e\u7de8\u96c6
+resources.actions.env.delete=\u74b0\u5883\u30a8\u30f3\u30c8\u30ea\u306e\u524a\u9664
+resources.actions.env.list=\u65e2\u5b58\u306e\u30a8\u30f3\u30c8\u30ea\u306e\u4e00\u89a7
+resources.datasrc.jdbc=JDBC\u30c9\u30e9\u30a4\u30d0
+resources.actions.datasrc=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
+resources.actions.datasrc.create=\u65b0\u3057\u3044\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u4f5c\u6210
+resources.actions.datasrc.delete=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u524a\u9664
+resources.actions.datasrc.edit=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u7de8\u96c6
+resources.datasrc.url=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9URL
+resources.datasrc.jdbcclass=JDBC\u30c9\u30e9\u30a4\u30d0\u30af\u30e9\u30b9
+resources.datasrc.connections=\u63a5\u7d9a
+resources.datasrc.active=\u6709\u52b9\u306a\u6700\u5927\u63a5\u7d9a\u6570
+resources.datasrc.idle=\u30a2\u30a4\u30c9\u30eb\u72b6\u614b\u306e\u6700\u5927\u63a5\u7d9a\u6570
+resources.datasrc.wait=\u6700\u5927\u63a5\u7d9a\u5f85\u3061\u6642\u9593
+resources.datasrc.validation=\u30af\u30a8\u30ea\u306e\u691c\u8a3c
+resources.datasrc.jndi=JNDI\u540d
+resources.actions.mailsession=\u30e1\u30fc\u30eb\u30bb\u30c3\u30b7\u30e7\u30f3
+resources.actions.mailsession.create=\u65b0\u3057\u3044\u30e1\u30fc\u30eb\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u4f5c\u6210
+resources.actions.mailsession.delete=\u30e1\u30fc\u30eb\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u524a\u9664
+resources.actions.mailsession.edit=\u30e1\u30fc\u30eb\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u7de8\u96c6
+resources.mailsession.name=\u540d\u524d
+resources.mailsession.mailhost=SMTP\u30db\u30b9\u30c8\u540d
+resources.actions.resourcelk=\u30ea\u30bd\u30fc\u30b9\u30ea\u30f3\u30af
+resources.actions.resourcelk.create=\u65b0\u3057\u3044\u30ea\u30bd\u30fc\u30b9\u30ea\u30f3\u30af\u306e\u4f5c\u6210
+resources.actions.resourcelk.delete=\u30ea\u30bd\u30fc\u30b9\u30ea\u30f3\u30af\u306e\u524a\u9664
+resources.actions.resourcelk.edit=\u30ea\u30bd\u30fc\u30b9\u30ea\u30f3\u30af\u306e\u7de8\u96c6
+resources.resourcelk.name=\u540d\u524d
+resources.resourcelk.global=\u30b0\u30ed\u30fc\u30d0\u30eb
+resources.resourcelk.type=\u578b
+resources.error.name.required=<li>\u540d\u524d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.global.required=<li>\u30b0\u30ed\u30fc\u30d0\u30eb\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.type.required=<li>\u578b\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.value.required=<li>\u5024\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.value.mismatch=<li>\u578b\u3068\u5024\u304c\u9069\u5408\u3057\u307e\u305b\u3093\u3002</li>
+resources.error.entryType.invalid=<li>\u30a8\u30f3\u30c8\u30ea\u30bf\u30a4\u30d7\u3092\u8a8d\u8b58\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.entryType.notimpl=<li>\u3053\u306e\u578b\u306b\u5bfe\u3059\u308b\u59a5\u5f53\u6027\u30c1\u30a7\u30c3\u30ab\u306f\u307e\u3060\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002</li>
+resources.error.url.required=<li>\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9URL\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.driverClass.required=<li>JDBC\u30c9\u30e9\u30a4\u30d0\u30af\u30e9\u30b9\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.active.required=<li>\u6709\u52b9\u306a\u6700\u5927\u63a5\u7d9a\u6570\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.idle.required=<li>\u30a2\u30a4\u30c9\u30eb\u72b6\u614b\u306e\u6700\u5927\u63a5\u7d9a\u6570\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.wait.required=<li>\u6700\u5927\u63a5\u7d9a\u5f85\u3061\u6642\u9593\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.error.mailhost.required=<li>SMTP\u30db\u30b9\u30c8\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093\u3002</li>
+resources.integer.error=<li>\u4e0d\u6b63\u306a\u6574\u6570\u3067\u3059\u3002</li>
+resources.actions.userdb.create=\u65b0\u3057\u3044\u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u4f5c\u6210
+resources.actions.userdb.edit=\u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u7de8\u96c6
+resources.actions.userdb.delete=\u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u524a\u9664
+resources.userdb.location=\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3
+resources.userdb.factory=\u30d5\u30a1\u30af\u30c8\u30ea
+resources.treeBuilder.databases=\u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9
+resources.error.path.required=<li>\u30d1\u30b9\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+resources.error.jndiName.required=<li>JNDI\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+resources.invalid.name=<li>\u7121\u52b9\u306a\u30ea\u30bd\u30fc\u30b9\u540d - \u540d\u524d\u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002</li>
+resources.invalid.env=<li>\u7121\u52b9\u306a\u74b0\u5883\u540d - \u540d\u524d\u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002</li>
+
+# ---------- User Database Module ----------
+users.actions.group.create=\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7\u306e\u4f5c\u6210
+users.actions.group.delete=\u65e2\u5b58\u306e\u30b0\u30eb\u30fc\u30d7\u306e\u524a\u9664
+users.actions.group.list=\u65e2\u5b58\u306e\u30b0\u30eb\u30fc\u30d7\u306e\u4e00\u89a7
+users.actions.role.create=\u65b0\u3057\u3044\u30ed\u30fc\u30eb\u306e\u4f5c\u6210
+users.actions.role.delete=\u65e2\u5b58\u306e\u30ed\u30fc\u30eb\u306e\u524a\u9664
+users.actions.role.list=\u65e2\u5b58\u306e\u30ed\u30fc\u30eb\u306e\u4e00\u89a7
+users.actions.user.create=\u65b0\u3057\u3044\u30e6\u30fc\u30b6\u306e\u4f5c\u6210
+users.actions.user.delete=\u65e2\u5b58\u306e\u30e6\u30fc\u30b6\u306e\u524a\u9664
+users.actions.user.list=\u65e2\u5b58\u306e\u30e6\u30fc\u30b6\u306e\u4e00\u89a7
+users.deleteGroups.title=\u65e2\u5b58\u306e\u30b0\u30eb\u30fc\u30d7\u306e\u524a\u9664
+users.deleteRoles.title=\u65e2\u5b58\u306e\u30ed\u30fc\u30eb\u306e\u524a\u9664
+users.deleteUsers.title=\u65e2\u5b58\u306e\u30e6\u30fc\u30b6\u306e\u524a\u9664
+users.error.attribute.get=\u5c5e\u6027\u306e\u56de\u5fa9\u6642\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f {0}
+users.error.attribute.set=\u5c5e\u6027\u306e\u5909\u66f4\u6642\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f {0}
+users.error.invoke=\u30aa\u30da\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u8d77\u52d5\u6642\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f {0}
+users.error.groupname.required=\u30b0\u30eb\u30fc\u30d7\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093
+users.error.password.required=\u30d1\u30b9\u30ef\u30fc\u30c9\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093
+users.error.quotes=\u30c0\u30d6\u30eb\u30af\u30a9\u30fc\u30c8\u306f\u3001\u30d5\u30a3\u30fc\u30eb\u30c9\u5024\u306b\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+users.error.rolename.required=\u30ed\u30fc\u30eb\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093
+users.error.select=\u7ba1\u7406\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u9078\u629e\u6642\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+users.error.token=\u4e0d\u9069\u5207\u306a\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u304c\u767a\u884c\u3055\u308c\u307e\u3057\u305f
+users.error.username.required=<li>\u30e6\u30fc\u30b6\u540d\u306f\u7701\u7565\u3067\u304d\u307e\u305b\u3093</li>
+users.group.newGroup=\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u4f5c\u6210
+users.group.oldGroup=\u65e2\u5b58\u306e\u30b0\u30eb\u30fc\u30d7\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u7de8\u96c6
+users.group.properties=\u30b0\u30eb\u30fc\u30d7\u30d7\u30ed\u30d1\u30c6\u30a3
+users.list.description=\u8aac\u660e
+users.list.fullName=\u30d5\u30eb\u30cd\u30fc\u30e0
+users.list.groupname=\u30b0\u30eb\u30fc\u30d7\u540d
+users.list.rolename=\u30ed\u30fc\u30eb\u540d
+users.list.username=\u30e6\u30fc\u30b6\u540d
+users.listGroups.title=\u30b0\u30eb\u30fc\u30d7\u4e00\u89a7
+users.listRoles.title=\u30ed\u30fc\u30eb\u4e00\u89a7
+users.listUsers.title=\u30e6\u30fc\u30b6\u4e00\u89a7
+users.prompt.description=\u8aac\u660e:
+users.prompt.fullName=\u30d5\u30eb\u30cd\u30fc\u30e0:
+users.prompt.groupname=\u30b0\u30eb\u30fc\u30d7\u540d:
+users.prompt.password=\u30d1\u30b9\u30ef\u30fc\u30c9:
+users.prompt.rolename=\u30ed\u30fc\u30eb\u540d:
+users.prompt.username=\u30e6\u30fc\u30b6\u540d:
+users.role.newRole=\u65b0\u3057\u3044\u30ed\u30fc\u30eb\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u4f5c\u6210
+users.role.oldRole=\u65e2\u5b58\u306e\u30ed\u30fc\u30eb\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u7de8\u96c6
+users.role.properties=\u30ed\u30fc\u30eb\u30d7\u30ed\u30d1\u30c6\u30a3
+users.treeBuilder.groupsNode=\u30b0\u30eb\u30fc\u30d7
+users.treeBuilder.rolesNode=\u30ed\u30fc\u30eb
+users.treeBuilder.subtreeNode=\u30e6\u30fc\u30b6\u5b9a\u7fa9
+users.treeBuilder.usersNode=\u30e6\u30fc\u30b6
+users.user.newUser=\u65b0\u3057\u3044\u30e6\u30fc\u30b6\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u4f5c\u6210
+users.user.oldUser=\u65e2\u5b58\u306e\u30e6\u30fc\u30b6\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u7de8\u96c6
+users.user.properties=\u30e6\u30fc\u30b6\u30d7\u30ed\u30d1\u30c6\u30a3
+# ---------- -------------------- ----------
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationServlet.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationServlet.java
new file mode 100644
index 0000000..0bd34e3
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/ApplicationServlet.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+import java.text.DateFormat;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import javax.management.MBeanServer;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import org.apache.commons.modeler.Registry;
+import org.apache.struts.action.ActionServlet;
+import org.apache.struts.util.MessageResources;
+
+
+/**
+ * Subclass of ActionServlet that adds caching of the supported locales in the
+ * ApplicationLocales class.
+ *
+ * @author Patrick Luby
+ * @version $Revision$ $Date$
+ */
+
+public class ApplicationServlet extends ActionServlet {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The application scope key under which we store our
+     * <code>ApplicationLocales</code> instance.
+     */
+    public static final String LOCALES_KEY = "applicationLocales";
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The managed beans Registry used to look up metadata.
+     */
+    protected Registry registry = null;
+
+
+    /**
+     * The JMX MBeanServer we will use to look up management beans.
+     */
+    protected MBeanServer server = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Convenience method to make the managed beans Registry available.
+     *
+     * @exception ServletException if the Registry is not available
+     */
+    public Registry getRegistry() throws ServletException {
+
+        if (registry == null)
+            initRegistry();
+        return (this.registry);
+
+    }
+
+
+    /**
+     * Convenience method to make the JMX MBeanServer available.
+     *
+     * @exception ServletException if the MBeanServer is not available
+     */
+    public MBeanServer getServer() throws ServletException {
+
+        if (server == null)
+            initServer();
+        return (this.server);
+
+    }
+
+
+    /**
+     * Initialize this servlet.
+     *
+     * @exception ServletException if an initialization error occurs.
+     */
+    public void init() throws javax.servlet.ServletException {
+        super.init();
+        initApplicationLocales();
+    }
+
+
+    // ---------------------------------------------------- Protected Methods
+
+
+    /**
+     * Create and initialize the ApplicationLocales object, and make it
+     * available as a servlet context attribute.
+     */
+    protected void initApplicationLocales() {
+
+        ApplicationLocales locales = new ApplicationLocales(this);
+        getServletContext().setAttribute(LOCALES_KEY, locales);
+
+    }
+
+
+    /**
+     * Validate the existence of the Registry that should have been
+     * provided to us by an instance of
+     * <code>org.apache.catalina.mbean.ServerLifecycleListener</code>
+     * enabled at startup time.
+     *
+     * @exception ServletException if we cannot find the Registry
+     */
+    protected void initRegistry() throws ServletException {
+
+        registry = Registry.getRegistry();
+        //(Registry) getServletContext().getAttribute
+        //    ("org.apache.catalina.Registry");
+        if (registry == null)
+            throw new UnavailableException("Registry is not available");
+
+    }
+
+
+    /**
+     * Validate the existence of the MBeanServer that should have been
+     * provided to us by an instance of
+     * <code>org.apache.catalina.mbean.ServerLifecycleListener</code>
+     * enabled at startup time.
+     *
+     * @exception ServletException if we cannot find the MBeanServer
+     */
+    protected void initServer() throws ServletException {
+
+        server = Registry.getRegistry().getMBeanServer();
+        //(MBeanServer) getServletContext().getAttribute
+        //    ("org.apache.catalina.MBeanServer");
+        if (server == null)
+            throw new UnavailableException("MBeanServer is not available");
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/AttributeTag.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/AttributeTag.java
new file mode 100644
index 0000000..fb7b58f
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/AttributeTag.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.TagSupport;
+import org.apache.commons.beanutils.PropertyUtils;
+
+
+
+/**
+ * Custom tag that retrieves a JMX MBean attribute value, and writes it
+ * out to the current output stream.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class AttributeTag extends TagSupport {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The attribute name on the JMX MBean to be retrieved.
+     */
+    protected String attribute = null;
+
+    public String getAttribute() {
+        return (this.attribute);
+    }
+
+    public void setAttribute(String attribute) {
+        this.attribute = attribute;
+    }
+
+
+    /**
+     * The bean name to be retrieved.
+     */
+    protected String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The property name to be retrieved.
+     */
+    protected String property = null;
+
+    public String getProperty() {
+        return (this.property);
+    }
+
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+
+    /**
+     * The scope in which the bean should be searched.
+     */
+    protected String scope = null;
+
+    public String getScope() {
+        return (this.scope);
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Render the JMX MBean attribute identified by this tag
+     *
+     * @exception JspException if a processing exception occurs
+     */
+    public int doEndTag() throws JspException {
+
+        // Retrieve the object name identified by our attributes
+        Object bean = null;
+        if (scope == null) {
+            bean = pageContext.findAttribute(name);
+        } else if ("page".equalsIgnoreCase(scope)) {
+            bean = pageContext.getAttribute(name, PageContext.PAGE_SCOPE);
+        } else if ("request".equalsIgnoreCase(scope)) {
+            bean = pageContext.getAttribute(name, PageContext.REQUEST_SCOPE);
+        } else if ("session".equalsIgnoreCase(scope)) {
+            bean = pageContext.getAttribute(name, PageContext.SESSION_SCOPE);
+        } else if ("application".equalsIgnoreCase(scope)) {
+            bean = pageContext.getAttribute(name,
+                                            PageContext.APPLICATION_SCOPE);
+        } else {
+            throw new JspException("Invalid scope value '" + scope + "'");
+        }
+        if (bean == null) {
+            throw new JspException("No bean '" + name + "' found");
+        }
+        if (property != null) {
+            try {
+                bean = PropertyUtils.getProperty(bean, property);
+            } catch (Throwable t) {
+                throw new JspException
+                    ("Exception retrieving property '" + property + "': " + t);
+            }
+            if (bean == null) {
+                throw new JspException("No property '" + property + "' found");
+            }
+        }
+
+        // Convert to an object name as necessary
+        ObjectName oname = null;
+        try {
+            if (bean instanceof ObjectName) {
+                oname = (ObjectName) bean;
+            } else if (bean instanceof String) {
+                oname = new ObjectName((String) bean);
+            } else {
+                oname = new ObjectName(bean.toString());
+            }
+        } catch (Throwable t) {
+            throw new JspException("Exception creating object name for '" +
+                                   bean + "': " + t);
+        }
+
+        // Acquire a reference to our MBeanServer
+        MBeanServer mserver =
+            (MBeanServer) pageContext.getAttribute
+            ("org.apache.catalina.MBeanServer", PageContext.APPLICATION_SCOPE);
+        if (mserver == null)
+            throw new JspException("MBeanServer is not available");
+
+        // Retrieve the specified attribute from the specified MBean
+        Object value = null;
+        try {
+            value = mserver.getAttribute(oname, attribute);
+        } catch (Throwable t) {
+            throw new JspException("Exception retrieving attribute '" +
+                                   attribute + "' from mbean '" +
+                                   oname.toString() + "'");
+        }
+
+        // Render this value to our current output writer
+        if (value != null) {
+            JspWriter out = pageContext.getOut();
+            try {
+                out.print(value);
+            } catch (IOException e) {
+                throw new JspException("IOException: " + e);
+            }
+        }
+
+        // Evaluate the remainder of this page
+        return (EVAL_PAGE);
+
+    }
+
+
+    /**
+     * Release all current state.
+     */
+    public void release() {
+
+        attribute = null;
+        name = null;
+        property = null;
+        scope = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/CommitChangesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/CommitChangesAction.java
new file mode 100644
index 0000000..f2fc379
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/CommitChangesAction.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+import org.apache.struts.util.MessageResources;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+
+/**
+ * Implementation of <strong>Action</strong> that saves the current settings
+ * and writes them out to server.xml
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class CommitChangesAction extends Action {
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+
+       // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+       ObjectName sname = null;    
+        try {
+           sname = new ObjectName(TomcatTreeBuilder.DEFAULT_DOMAIN +
+                                    TomcatTreeBuilder.SERVER_TYPE);
+        } catch (Exception e) {
+            String message = "Could not get Server Object";
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+        
+       String operation = "storeConfig";
+       try {           
+            mBServer.invoke(sname, operation, null, null);            
+        } catch (Throwable t) {
+            getServlet().log
+            (resources.getMessage(locale, "users.error.invoke",
+                                  operation), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                resources.getMessage(locale, "users.error.invoke",
+                                     operation));
+            return (null);            
+        }
+ 
+
+        getServlet().log("Debugging -- changes saved to conf/server.xml");
+        // Forward control back to the banner
+        return (mapping.findForward("Banner"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/DataTag.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/DataTag.java
new file mode 100644
index 0000000..b019625
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/DataTag.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+import javax.servlet.jsp.tagext.Tag;
+
+
+/**
+ * <p>Nested tag that represents an individual "data" for a row.  This tag
+ * is valid <strong>only</strong> when nested within a RowTag tag.
+ *
+ * <p>In addition, the body content of this tag is used as the user-visible
+ * data for the action, so that it may be conveniently localized.</p>
+ *
+ * <strong>FIXME</strong> - Internationalize the exception messages!
+ *
+ * @author Manveen Kaur
+ * @version $Revision$
+ */
+
+public class DataTag extends BodyTagSupport {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The data that will be rendered for this table row.
+     */
+    protected String data = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the start of this tag.
+     *
+     * @exception JspException if a JSP exception has occurred
+     */
+    public int doStartTag() throws JspException {
+
+        // Initialize the holder for our data text
+        this.data = null;
+
+        // Do no further processing for now
+        return (EVAL_BODY_TAG);
+
+    }
+
+
+    /**
+     * Process the body text of this tag (if any).
+     *
+     * @exception JspException if a JSP exception has occurred
+     */
+    public int doAfterBody() throws JspException {
+
+        String data = bodyContent.getString();
+        if (data != null) {
+            data = data.trim();
+            if (data.length() > 0)
+                this.data = data;
+        }
+        return (SKIP_BODY);
+
+    }
+
+
+    /**
+     * Record this action with our surrounding ActionsTag instance.
+     *
+     * @exception JspException if a processing error occurs
+     */
+    public int doEndTag() throws JspException {
+
+        // Find our parent ActionsTag instance
+        Tag parent = getParent();
+        if ((parent == null) || !(parent instanceof RowTag))
+            throw new JspException("Must be nested in a rowTag isntance");
+        RowTag row = (RowTag) parent;
+
+        // Register the information for the action represented by
+        // this action
+        HttpServletResponse response =
+            (HttpServletResponse) pageContext.getResponse();
+        row.setData(data);
+        
+        return (EVAL_PAGE);
+
+    }
+
+
+    /**
+     * Release all state information set by this tag.
+     */
+    public void release() {
+
+        this.data = null;
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/DumpRegistryAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/DumpRegistryAction.java
new file mode 100644
index 0000000..251ce88
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/DumpRegistryAction.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Simple debugging action that dumps a list of the managed beans that are
+ * visible in our Registry.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class DumpRegistryAction extends Action {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Create a request attribute with our collection of beans
+        Registry registry = ((ApplicationServlet) getServlet()).getRegistry();
+        String names[] = registry.findManagedBeans();
+        Arrays.sort(names);
+        ManagedBean beans[] = new ManagedBean[names.length];
+        for (int i = 0; i < names.length; i++)
+            beans[i] = registry.findManagedBean(names[i]);
+        request.setAttribute("beans", beans);
+
+        // Forward to the corresponding display page
+        return (mapping.findForward("Dump Registry Results"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/DumpServerAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/DumpServerAction.java
new file mode 100644
index 0000000..d4649b6
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/DumpServerAction.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Simple debugging action that dumps a list of the MBeans that are
+ * visible in our MBeanServer.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class DumpServerAction extends Action {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Create a request attribute with our collection of MBeans
+        MBeanServer server = ((ApplicationServlet) getServlet()).getServer();
+        Iterator names = server.queryNames(null, null).iterator();
+        ArrayList list = new ArrayList();
+        while (names.hasNext()) {
+            list.add(names.next().toString());
+        }
+        Collections.sort(list);
+        request.setAttribute("names", list);
+
+        // Forward to the corresponding display page
+        return (mapping.findForward("Dump Server Results"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/LabelTag.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/LabelTag.java
new file mode 100644
index 0000000..04f7614
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/LabelTag.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+import javax.servlet.jsp.tagext.Tag;
+
+
+/**
+ * <p>Nested tag that represents an individual "labels" for a row.  This tag
+ * is valid <strong>only</strong> when nested within a RowTag tag.
+ *
+ * <p>In addition, the body content of this tag is used as the user-visible
+ * label for the action, so that it may be conveniently localized.</p>
+ *
+ * <strong>FIXME</strong> - Internationalize the exception messages!
+ *
+ * @author Manveen Kaur
+ * @version $Revision$
+ */
+
+public class LabelTag extends BodyTagSupport {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The label that will be rendered for this action.
+     */
+    protected String label = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the start of this tag.
+     *
+     * @exception JspException if a JSP exception has occurred
+     */
+    public int doStartTag() throws JspException {
+
+        // Initialize the holder for our label text
+        this.label = null;
+
+        // Do no further processing for now
+        return (EVAL_BODY_TAG);
+
+    }
+
+
+    /**
+     * Process the body text of this tag (if any).
+     *
+     * @exception JspException if a JSP exception has occurred
+     */
+    public int doAfterBody() throws JspException {
+
+        String label = bodyContent.getString();
+        if (label != null) {
+            label = label.trim();
+            if (label.length() > 0)
+                this.label = label;
+        }
+        return (SKIP_BODY);
+
+    }
+
+
+    /**
+     * Record this action with our surrounding ActionsTag instance.
+     *
+     * @exception JspException if a processing error occurs
+     */
+    public int doEndTag() throws JspException {
+
+        // Find our parent ActionsTag instance
+        Tag parent = getParent();
+        if ((parent == null) || !(parent instanceof RowTag))
+            throw new JspException("Must be nested in a rowTag isntance");
+        RowTag row = (RowTag) parent;
+
+        // Register the information for the action represented by
+        // this action
+        HttpServletResponse response =
+            (HttpServletResponse) pageContext.getResponse();
+        row.setLabel(label);
+        
+        return (EVAL_PAGE);
+
+    }
+
+
+    /**
+     * Release all state information set by this tag.
+     */
+    public void release() {
+
+        this.label = null;
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/LabelValueBean.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/LabelValueBean.java
new file mode 100644
index 0000000..00de4cb
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/LabelValueBean.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+import java.io.Serializable;
+
+/**
+ * Simple JavaBean to represent label-value pairs for use in collections
+ * that are utilized by the <code>&lt;form:options&gt;</code> tag.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class LabelValueBean implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new LabelValueBean with the specified values.
+     *
+     * @param label The label to be displayed to the user
+     * @param value The value to be returned to the server
+     */
+    public LabelValueBean(String label, String value) {
+        this.label = label;
+        this.value = value;
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The label to be displayed to the user.
+     */
+    protected String label = null;
+
+    public String getLabel() {
+        return (this.label);
+    }
+
+
+    /**
+     * The value to be returned to the server.
+     */
+    protected String value = null;
+
+    public String getValue() {
+        return (this.value);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a string representation of this object.
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer("LabelValueBean[");
+        sb.append(this.label);
+        sb.append(", ");
+        sb.append(this.value);
+        sb.append("]");
+        return (sb.toString());
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/Lists.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/Lists.java
new file mode 100644
index 0000000..e9aa4f8
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/Lists.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+
+/**
+ * General purpose utility methods to create lists of objects that are
+ * commonly required in building the user interface.  In all cases, if there
+ * are no matching elements, a zero-length list (rather than <code>null</code>)
+ * is returned.
+ *
+ * @author Craig R. McClanahan
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public class Lists {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Protected constructor to prevent instantiation.
+     */
+    protected Lists() { }
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * Precomputed list of verbosity level labels and values.
+     */
+    private static List verbosityLevels = new ArrayList();
+
+    static {
+        verbosityLevels.add(new LabelValueBean("0", "0"));
+        verbosityLevels.add(new LabelValueBean("1", "1"));
+        verbosityLevels.add(new LabelValueBean("2", "2"));
+        verbosityLevels.add(new LabelValueBean("3", "3"));
+        verbosityLevels.add(new LabelValueBean("4", "4"));
+    }
+
+    /**
+     * Precomputed list of (true,false) labels and values.
+     */
+    private static List booleanValues = new ArrayList();
+
+    static {
+            booleanValues.add(new LabelValueBean("True", "true"));
+            booleanValues.add(new LabelValueBean("False", "false"));
+    }
+
+    /**
+     * Precomputed list of clientAuth lables and values.
+     */
+    private static List clientAuthValues = new ArrayList();
+
+    static {
+            clientAuthValues.add(new LabelValueBean("True","true"));
+            clientAuthValues.add(new LabelValueBean("False","false"));
+            clientAuthValues.add(new LabelValueBean("Want","want"));
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a <code>List</code> of {@link LabelValueBean}s for the legal
+     * settings for <code>verbosity</code> properties.
+     */
+    public static List getVerbosityLevels() {
+
+        return (verbosityLevels);
+
+    }
+
+    /**
+     * Return a <code>List</code> of {@link LabelValueBean}s for the legal
+     * settings for <code>boolean</code> properties.
+     */
+    public static List getBooleanValues() {
+
+        return (booleanValues);
+
+    }
+    /**
+     * Return a <code>List</code> of {@link LabelValueBean}s for the legal
+     * settings for <code>clientAuth</code> properties.
+     */
+    public static List getClientAuthValues() {
+
+        return (clientAuthValues);
+
+    }
+
+    /**
+     * Return a list of <code>Connector</code> object name strings
+     * for the specified <code>Service</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param service Object name of the service for which to select connectors
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getConnectors(MBeanServer mbserver, ObjectName service)
+        throws Exception {
+
+        StringBuffer sb = new StringBuffer(service.getDomain());
+        sb.append(":type=Connector,*");
+        ObjectName search = new ObjectName(sb.toString());
+        ArrayList connectors = new ArrayList();
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        while (names.hasNext()) {
+            connectors.add(names.next().toString());
+        }
+        Collections.sort(connectors);
+        return (connectors);
+
+    }
+
+
+    /**
+     * Return a list of <code>Connector</code> object name strings
+     * for the specified <code>Service</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param service Object name of the service for which to select connectors
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getConnectors(MBeanServer mbserver, String service)
+        throws Exception {
+
+        return (getConnectors(mbserver, new ObjectName(service)));
+
+    }
+
+
+    /**
+     * Return a list of <code>Context</code> object name strings
+     * for the specified <code>Host</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param host Object name of the host for which to select contexts
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getContexts(MBeanServer mbserver, ObjectName host)
+        throws Exception {
+
+        StringBuffer sb = new StringBuffer(host.getDomain());
+        sb.append(":j2eeType=WebModule,*");
+        ObjectName search = new ObjectName(sb.toString());
+        ArrayList contexts = new ArrayList();
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        String name = null;
+        ObjectName oname = null;
+        String hostPrefix = "//"+host.getKeyProperty("host");
+        String hostAttr = null;
+        while (names.hasNext()) {
+            name = names.next().toString();
+            oname = new ObjectName(name);
+            hostAttr = oname.getKeyProperty("name");
+            if (hostAttr.startsWith(hostPrefix)) {
+                contexts.add(name);
+            }
+        }
+        Collections.sort(contexts);
+        return (contexts);
+
+    }
+
+
+    /**
+     * Return a list of <code>DefaultContext</code> object name strings
+     * for the specified <code>Host</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param container Object name of the container for which to select default
+     * contexts
+     * @param containerType The type of the container for which to select 
+     * default contexts
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getDefaultContexts(MBeanServer mbserver, String 
+        container) throws Exception {
+
+        return (getDefaultContexts(mbserver, new ObjectName(container)));
+
+    }
+    
+    
+    /**
+     * Return a list of <code>DefaultContext</code> object name strings
+     * for the specified <code>Host</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param container Object name of the container for which to select default
+     * contexts
+     * @param containerType The type of the container for which to select 
+     * default contexts
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getDefaultContexts(MBeanServer mbserver, ObjectName 
+        container) throws Exception {
+
+        // FIXME
+        StringBuffer sb = new StringBuffer(container.getDomain());
+        sb.append(":type=DefaultContext");
+        String type = container.getKeyProperty("type");
+        String host = container.getKeyProperty("host");
+        if ("Host".equals(type)) {
+            host = container.getKeyProperty("host");
+        }
+        if (host != null) {
+            sb.append(",host=");
+            sb.append(host);
+        }
+        ObjectName search = new ObjectName(sb.toString());
+        ArrayList defaultContexts = new ArrayList();
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        while (names.hasNext()) {
+            String name = names.next().toString();
+            defaultContexts.add(name);
+        }
+        Collections.sort(defaultContexts);
+        return (defaultContexts);
+
+    }
+
+
+    /**
+     * Return a list of <code>Context</code> object name strings
+     * for the specified <code>Host</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param host Object name of the host for which to select contexts
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getContexts(MBeanServer mbserver, String host)
+        throws Exception {
+
+        return (getContexts(mbserver, new ObjectName(host)));
+
+    }
+
+    /**
+     * Return a list of <code>Host</code> object name strings
+     * for the specified <code>Service</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param service Object name of the service for which to select hosts
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getHosts(MBeanServer mbserver, ObjectName service)
+        throws Exception {
+
+        StringBuffer sb = new StringBuffer(service.getDomain());
+        sb.append(":type=Host,*");
+        ObjectName search = new ObjectName(sb.toString());
+        ArrayList hosts = new ArrayList();
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        while (names.hasNext()) {
+            hosts.add(names.next().toString());
+        }
+        Collections.sort(hosts);
+        return (hosts);
+
+    }
+
+
+    /**
+     * Return a list of <code>Host</code> object name strings
+     * for the specified <code>Service</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param service Object name of the service for which to select hosts
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getHosts(MBeanServer mbserver, String service)
+        throws Exception {
+
+        return (getHosts(mbserver, new ObjectName(service)));
+
+    }
+
+
+    /**
+     * Return a list of <code>Realm</code> object name strings
+     * for the specified container (service, host, or context) object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param container Object name of the container for which to select
+     *                  realms
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getRealms(MBeanServer mbserver, ObjectName container)
+        throws Exception {
+
+        ObjectName search = getSearchObject(container, "Realm");
+        ArrayList realms = new ArrayList();
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        while (names.hasNext()) {
+            realms.add(names.next().toString());
+        }
+        Collections.sort(realms);
+        return (realms);
+
+    }
+
+
+    /**
+     * Return a list of <code>Realm</code> object name strings
+     * for the specified container (service, host, or context) object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param container Object name of the container for which to select
+     *                  realms
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getRealms(MBeanServer mbserver, String container)
+        throws Exception {
+
+        return (getRealms(mbserver, new ObjectName(container)));
+
+    }
+
+    /**
+     * Return a list of <code>Valve</code> object name strings
+     * for the specified container (service, host, or context) object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param container Object name of the container for which to select
+     *                  Valves
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getValves(MBeanServer mbserver, ObjectName container)
+        throws Exception {
+
+        StringBuffer sb = new StringBuffer(container.getDomain());
+        sb.append(":type=Valve");
+        String type = container.getKeyProperty("type");
+        String j2eeType = container.getKeyProperty("j2eeType");
+        sb.append(TomcatTreeBuilder.WILDCARD);
+        String host = "";
+        String path = "";
+        String name = container.getKeyProperty("name");
+        if ((name != null) && (name.length() > 0)) {
+            // parent is context
+            name = name.substring(2);
+            int i = name.indexOf("/");
+            host = name.substring(0,i);
+            path = name.substring(i);
+        } else if ("Host".equals(type)) {
+            // parent is host
+            host = container.getKeyProperty("host");
+        }    
+        
+        ObjectName search = new ObjectName(sb.toString());        
+        ArrayList valves = new ArrayList();
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        while (names.hasNext()) {
+            ObjectName valve = (ObjectName) names.next();
+            String vpath = valve.getKeyProperty("path");            
+            String vhost = valve.getKeyProperty("host");
+            
+            String valveType = null;
+            String className = (String) 
+                    mbserver.getAttribute(valve, "className");
+            int period = className.lastIndexOf(".");
+            if (period >= 0)
+                valveType = className.substring(period + 1);
+
+           // Return only user-configurable valves.
+           if ("AccessLogValve".equalsIgnoreCase(valveType) ||
+               "RemoteAddrValve".equalsIgnoreCase(valveType) ||
+               "RemoteHostValve".equalsIgnoreCase(valveType) || 
+               "RequestDumperValve".equalsIgnoreCase(valveType) ||
+               "SingleSignOn".equalsIgnoreCase(valveType)) {
+            // if service is the container, then the valve name
+            // should not contain path or host                   
+            if ("Service".equalsIgnoreCase(type)) {
+                if ((vpath == null) && (vhost == null)) {
+                    valves.add(valve.toString());
+                }
+            } 
+            
+            if ("Host".equalsIgnoreCase(type)) {
+                if ((vpath == null) && (host.equalsIgnoreCase(vhost))) { 
+                    valves.add(valve.toString());      
+                }
+            }
+            
+            if ("WebModule".equalsIgnoreCase(j2eeType)) {
+                if ((path.equalsIgnoreCase(vpath)) && (host.equalsIgnoreCase(vhost))) {
+                    valves.add(valve.toString());      
+                }
+            }
+           }
+        }        
+        Collections.sort(valves);
+        return (valves);
+    }
+
+    
+    /**
+     * Return a list of <code>Valve</code> object name strings
+     * for the specified container (service, host, or context) object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param container Object name of the container for which to select
+     *                  valves
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getValves(MBeanServer mbserver, String container)
+        throws Exception {
+
+        return (getValves(mbserver, new ObjectName(container)));
+
+    }
+    
+    /**
+     * Return a list of <code>Server</code> object name strings.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getServers(MBeanServer mbserver, String domain)
+        throws Exception {
+
+        ObjectName search = new ObjectName(domain+":type=Server,*");
+        ArrayList servers = new ArrayList();
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        while (names.hasNext()) {
+            servers.add(names.next().toString());
+        }
+        Collections.sort(servers);
+        return (servers);
+
+    }
+
+
+    /**
+     * Return a list of <code>Service</code> object name strings
+     * for the specified <code>Server</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param server Object name of the server for which to select services
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getServices(MBeanServer mbserver, ObjectName server)
+        throws Exception {
+
+        //StringBuffer sb = new StringBuffer(server.getDomain());
+        StringBuffer sb = new StringBuffer("*:type=Service,*");
+        //sb.append(":type=Service,*");
+        ObjectName search = new ObjectName(sb.toString());
+        ArrayList services = new ArrayList();
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        while (names.hasNext()) {
+            services.add(names.next().toString());
+        }
+        Collections.sort(services);
+        return (services);
+
+    }
+
+
+    /**
+     * Return a list of <code>Service</code> object name strings
+     * for the specified <code>Server</code> object name.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param server Object name of the server for which to select services
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static List getServices(MBeanServer mbserver, String server)
+        throws Exception {
+
+        return (getServices(mbserver, new ObjectName(server)));
+
+    }
+
+
+    /**
+     * Return the  <code>Service</code> object name string
+     * that the admin app belongs to.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param request Http request
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static String getAdminAppService
+        (MBeanServer mbserver, String domain, HttpServletRequest request)
+        throws Exception {
+
+        String adminDomain = TomcatTreeBuilder.DEFAULT_DOMAIN;
+        // Get the admin app's service name
+        StringBuffer sb = new StringBuffer(adminDomain);
+        sb.append(":type=Service,*");
+        ObjectName search = new ObjectName(sb.toString());
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        String service = null;
+        while (names.hasNext()) {
+            service = ((ObjectName)names.next()).getKeyProperty("serviceName");
+        }
+        return service;
+
+    }
+
+
+    /**
+     * Return the  <code>Host</code> object name string
+     * that the admin app belongs to.
+     *
+     * @param mbserver MBeanServer from which to retrieve the list
+     * @param request Http request
+     *
+     * @exception Exception if thrown while retrieving the list
+     */
+    public static String getAdminAppHost
+        (MBeanServer mbserver, String domain, HttpServletRequest request)
+        throws Exception {
+        
+        // Get the admin app's host name
+        String adminDomain = TomcatTreeBuilder.DEFAULT_DOMAIN;
+        StringBuffer sb = new StringBuffer(adminDomain);
+        sb.append(":j2eeType=WebModule,*"); 
+        ObjectName search = new ObjectName(sb.toString());
+        Iterator names = mbserver.queryNames(search, null).iterator();
+        String contextPath = request.getContextPath();
+        String host = null;
+        String name = null;
+        ObjectName oname = null;
+        while (names.hasNext()) {       
+            name = names.next().toString();
+            oname = new ObjectName(name);
+            host = oname.getKeyProperty("name");
+            host = host.substring(2);
+            int i = host.indexOf("/");
+            if (contextPath.equals(host.substring(i))) {
+                host = host.substring(0,i);
+                return host;
+            }
+        }
+        return host;
+
+    }
+
+    
+    /**
+     * Return search object name to be used to query.
+     *
+     * @param container object name to query
+     * @param type type of the component
+     *
+     * @exception MalformedObjectNameException if thrown while retrieving the list
+     */
+    public static ObjectName getSearchObject(ObjectName container, String type)  
+            throws Exception {
+        
+        StringBuffer sb = new StringBuffer(container.getDomain());
+        sb.append(":type="+type);
+        String containerType = container.getKeyProperty("type");
+        String name = container.getKeyProperty("name");
+        if ((name != null) && (name.length() > 0)) {
+            // parent is context
+            name = name.substring(2);
+            int i = name.indexOf("/");
+            String host = name.substring(0,i);
+            String path = name.substring(i);
+            sb.append(",path=");
+            sb.append(path);
+            sb.append(",host=");
+            sb.append(host);
+        } else if ("Host".equals(containerType)) {
+            // parent is host
+            String host = container.getKeyProperty("host");
+            sb.append(",host=");
+            sb.append(host);
+        }    
+        
+        return new ObjectName(sb.toString());
+        
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/LogOutAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/LogOutAction.java
new file mode 100644
index 0000000..a9a8a71
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/LogOutAction.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Implementation of <strong>Action</strong> that logs out of the current
+ * session and returns to the main menu (which will trigger the login form).
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class LogOutAction extends Action {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Invalidate the current session and create a new one
+        HttpSession session = request.getSession();
+        session.invalidate();
+        session = request.getSession(true);
+
+        // Forward control back to the main menu
+        return (mapping.findForward("Main Menu"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/RowTag.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/RowTag.java
new file mode 100644
index 0000000..00d2234
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/RowTag.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+import javax.servlet.jsp.tagext.Tag;
+
+
+/**
+ * <p>Nested tag that represents an individual "instant table".  This tag
+ * is valid <strong>only</strong> when nested within an TableTag tag.
+ * This tag has the following user-settable attributes:</p>
+ * <ul>
+ * <li><strong>header</strong> - Is this  a header row?</li>
+ * <li><strong>label</strong> - label to be displayed.</li>
+ * <li><strong>data</strong> - data of the table data element.</li>
+ * <li><strong>labelStyle</strong> - Style to be applied to the
+ * label table data element.</li>
+ * <li><strong>dataStyle</strong> - Style to be applied to the data table
+ * data element.</li>
+ *
+ * </ul>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class RowTag extends BodyTagSupport {
+    
+    /**
+     * Is this the header row?
+     */
+    protected boolean header = false;
+    
+    public boolean getHeader() {
+        return (this.header);
+    }
+    
+    public void setHeader(boolean header) {
+        this.header = header;
+    }    
+    
+    /**
+     * The label that will be rendered for this row's table data element.
+     */
+    protected String label = null;
+   
+    public void setLabel(String label) {
+        this.label = label;
+    }
+    
+    
+    /**
+     * The data of the table data element of this row.
+     */
+    protected String data = null;
+    
+    public void setData(String data) {
+        this.data = data;
+    }
+    
+    /**
+     * The style of the label.
+     */
+    protected String labelStyle = null;
+    
+    public String getLabelStyle() {
+        return (this.labelStyle);
+    }
+    
+    public void setLabelStyle(String labelStyle) {
+        this.labelStyle = labelStyle;
+    }
+    
+    
+    /**
+     * The style of the data.
+     */
+    protected String dataStyle = null;
+    
+    public String getdataStyle() {
+        return (this.dataStyle);
+    }
+    
+    public void setdataStyle(String dataStyle) {
+        this.dataStyle = dataStyle;
+    }
+ 
+    /**
+     * The styleId for the label.
+     */
+    protected String styleId = null;
+    
+    public String getStyleId() {
+        return (this.styleId);
+    }
+    
+    public void setStyleId(String styleId) {
+        this.styleId = styleId;
+    }
+    
+        
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the start of this tag.
+     *
+     * @exception JspException if a JSP exception has occurred
+     */
+    public int doStartTag() throws JspException {
+        
+         // Do no further processing for now
+        return (EVAL_BODY_TAG);
+        
+    }
+    
+    
+    /**
+     * Process the body text of this tag (if any).
+     *
+     * @exception JspException if a JSP exception has occurred
+     */
+    public int doAfterBody() throws JspException {
+       
+        return (SKIP_BODY);
+        
+    }
+    
+    
+    /**
+     * Record this action with our surrounding ActionsTag instance.
+     *
+     * @exception JspException if a processing error occurs
+     */
+    public int doEndTag() throws JspException {
+        
+        // Find our parent TableTag instance
+        Tag parent = getParent();
+        while ((parent != null) && !(parent instanceof TableTag)) {
+            parent = parent.getParent();
+        }
+        if (parent == null) {
+            throw new JspException("Must be nested in a TableTag instance");
+        }
+        TableTag table = (TableTag) parent;
+        
+        // Register the information for the row represented by
+        // this row
+        HttpServletResponse response =
+        (HttpServletResponse) pageContext.getResponse();
+        table.addRow(header, label, data, labelStyle, dataStyle, styleId);
+        
+        return (EVAL_PAGE);
+        
+    }
+    
+    
+    /**
+     * Release all state information set by this tag.
+     */
+    public void release() {
+        
+        //super.release();
+        
+        this.header= false;
+        this.label = null;
+        this.data = null;
+        this.labelStyle = null;
+        this.dataStyle = null;
+        this.styleId = null;
+        
+    }
+    
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/SetLocaleAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/SetLocaleAction.java
new file mode 100644
index 0000000..424d659
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/SetLocaleAction.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.Globals;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Implementation of <strong>Action</strong> that sets the current Locale
+ * to the one specified by the <code>locale</code> request parameter.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class SetLocaleAction extends Action {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // What locale does the user want to switch to?
+        String requestedLocale = ((SetLocaleForm) form).getLocale();
+
+        // Switch to the specified locale, if it exists
+        if (requestedLocale != null) {
+            ApplicationLocales locales = (ApplicationLocales)
+                getServlet().getServletContext().getAttribute
+                (ApplicationServlet.LOCALES_KEY);
+            Iterator iterator = locales.getSupportedLocales().iterator();
+            Locale currentLocale = null;
+            while (iterator.hasNext()) {
+                currentLocale = (Locale) iterator.next();
+                if (requestedLocale.equals(currentLocale.toString())) {
+                    HttpSession session = request.getSession();
+                    session.setAttribute(Globals.LOCALE_KEY, currentLocale);
+                    // Remove form bean so it will get recreated next time
+                    session.removeAttribute(mapping.getAttribute());
+                    break;
+                }
+            }
+        }
+
+        // Forward control back to the main menu
+        return (mapping.findForward("Main Menu"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/SetLocaleForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/SetLocaleForm.java
new file mode 100644
index 0000000..0f39e71
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/SetLocaleForm.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.util.Locale;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import org.apache.struts.Globals;
+import org.apache.struts.action.ActionForm;
+
+
+/**
+ * Form bean for the set locale page.  The actual value is copied when this
+ * bean is added as a session attribute.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class SetLocaleForm
+    extends ActionForm
+    implements HttpSessionBindingListener {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The name of the locale we are currently running.
+     */
+    String locale = null;
+
+    public String getLocale() {
+        return (this.locale);
+    }
+
+    public void setLocale(String locale) {
+        this.locale = locale;
+    }
+
+
+    // ------------------------------------- HttpSessionBindingListener Methods
+
+
+    /**
+     * When this form bean is bound into the session, pick up the user's
+     * current Locale object and set the current value.
+     */
+    public void valueBound(HttpSessionBindingEvent event) {
+
+        HttpSession session = event.getSession();
+        Locale current = (Locale) session.getAttribute(Globals.LOCALE_KEY);
+        if (current != null)
+            locale = current.toString();
+
+    }
+
+
+    /**
+     * No action is required when this object is unbound.
+     */
+    public void valueUnbound(HttpSessionBindingEvent event) {
+
+        locale = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/SetUpTreeAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/SetUpTreeAction.java
new file mode 100644
index 0000000..07c697f
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/SetUpTreeAction.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Test <code>Action</code> sets up  tree control data structure
+ * for tree widget
+ *
+ * @author Jazmin Jonson
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class SetUpTreeAction extends Action {
+
+    public static final String DOMAIN_KEY = "domain";
+    public static final int INIT_PLUGIN_MAX = 10;
+    public static final String TREEBUILDER_KEY = "treebuilders";
+    public static final String ROOTNODENAME_KEY = "rootnodename";
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        ApplicationServlet servlet = (ApplicationServlet)getServlet();
+
+        // Getting init parms from web.xml
+
+        // Get the string to be displayed as root node while rendering the tree
+        String rootnodeName = 
+            (String)servlet.getServletConfig().getInitParameter(ROOTNODENAME_KEY);
+        
+        String treeBuildersStr  =
+            (String)servlet.getServletConfig().getInitParameter(TREEBUILDER_KEY);
+        
+        String domain  =
+            (String)servlet.getServletConfig().getInitParameter(DOMAIN_KEY);
+        
+        
+        // Make the root node and tree control
+        
+        // The root node gets rendered only if its value 
+        // is set as an init-param in web.xml
+        
+        TreeControlNode root =
+            new TreeControlNode("ROOT-NODE",
+                                null, rootnodeName,
+                                "setUpTree.do?select=ROOT-NODE",
+                                "content", true, domain);
+                
+        TreeControl control = new TreeControl(root);
+        
+        if(treeBuildersStr != null) {
+            Class treeBuilderImpl;
+            TreeBuilder treeBuilderBase;
+
+            ArrayList treeBuilders = new ArrayList(INIT_PLUGIN_MAX);
+            int i = 0;
+            StringTokenizer st = new StringTokenizer(treeBuildersStr, ",");
+            while (st.hasMoreTokens()) {
+                treeBuilders.add(st.nextToken().trim());
+            }
+
+            if(treeBuilders.size() == 0)
+                treeBuilders.add(treeBuildersStr.trim());
+
+            for(i = 0; i < treeBuilders.size(); i++) {
+
+                try{
+                    treeBuilderImpl = Class.forName((String)treeBuilders.get(i));
+                    treeBuilderBase =
+                        (TreeBuilder)treeBuilderImpl.newInstance();
+                    treeBuilderBase.buildTree(control, servlet, request);
+                }catch(Throwable t){
+                    t.printStackTrace(System.out);
+                }
+            }
+        }
+
+        HttpSession session = request.getSession();
+        session.setAttribute("treeControlTest", control);
+
+         String  name = request.getParameter("select");
+         if (name != null) {
+            control.selectNode(name);
+            // Forward back to the Blank page
+            return (mapping.findForward("Blank"));
+        }
+
+         return (mapping.findForward("Tree Control Test"));
+
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TableTag.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TableTag.java
new file mode 100644
index 0000000..2dc5320
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TableTag.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+
+/**
+ * <p>JSP custom tag that renders an "instant table" control.  To the user,
+ * it appears as an HTML &lt;table&gt; element
+ * This tag has the following user-settable attributes:</p>
+ * <ul>
+ * <li><strong>columns</strong> - (Integer) number of columns in the table.
+ * If not specified, one two columns will be created.</li>
+ * <li><strong>table-class</strong> - The CSS style class to be applied to the
+ *     entire rendered output of the entire table, if any.</li>
+ * <li><strong>header-row-class</strong> - The CSS style class to be applied to the
+ *     entire rendered output of the table header-row, if any.</li>
+ *
+ * </ul>
+ *
+ * <strong>FIXME</strong> - Internationalize the exception messages!
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class TableTag extends BodyTagSupport {
+    
+    
+    // ----------------------------------------------------- Manifest Constants
+    
+    
+    /**
+     * Attribute name used to indicate that we have generated the JavaScript
+     * function already on the current page.  The data stored for this
+     * attribute is arbitrary - only its existence is relevant.
+     */
+    //   protected static final String FUNCTION_TAG =
+    //       "org.apache.webapp.admin.TableTag.FUNCTION_TAG";
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * The set of labels for the rows displayed by this control.
+     */
+    protected ArrayList labels = new ArrayList();
+    
+    
+    /**
+     * The set of datas for the rows displayed by this control.
+     */
+    protected ArrayList datas = new ArrayList();
+    
+    
+    /**
+     * The set of labelStyles for the rows displayed by this control.
+     */
+    protected ArrayList labelStyles = new ArrayList();
+    
+    
+    /**
+     * The set of dataStyles for the rows displayed by this control.
+     */
+    protected ArrayList dataStyles = new ArrayList();
+    
+    /**
+     * The set of "headers" flags for rows displayed by this control.
+     */
+    protected ArrayList headers = new ArrayList();
+    
+    /**
+     * The set of styleIds for the rows displayed by this control.
+     */
+    protected ArrayList styleIds = new ArrayList();
+        
+    
+    // ------------------------------------------------------------- Properties
+    
+    
+    /**
+     * The number of elements that will be displayed to the user.
+     */
+    protected int columns = 2;
+    
+    public int getColumns() {
+        return (this.columns);
+    }
+    
+    public void setColumns(int columns) {
+        this.columns = columns;
+    }
+    
+    
+    /**
+     * The CSS style class to be applied to the entire rendered output
+     * of this "instant table" object.
+     */
+    protected String tableStyle = null;
+    
+    public String getTableStyle() {
+        return (this.tableStyle);
+    }
+    
+    public void setTableStyle(String tableStyle) {
+        this.tableStyle = tableStyle;
+    }
+    
+    /**
+     * The CSS Style for the lines between table rows.
+     */
+    protected String lineStyle = null;
+    
+    public String getLineStyle() {
+        return (this.lineStyle);
+    }
+    
+    public void setLineStyle(String lineStyle) {
+        this.lineStyle = lineStyle;
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    public int doStartTag() throws JspException {
+ 
+        this.headers.clear();
+        this.labels.clear();
+        this.datas.clear();
+        this.labelStyles.clear();
+        this.dataStyles.clear();
+        this.styleIds.clear();
+        
+        return (EVAL_BODY_TAG);
+ 
+     }    
+
+    
+    /**
+     * Render this instant actions control.
+     *
+     * @exception JspException if a processing error occurs
+     */
+    public int doEndTag() throws JspException {
+        
+        JspWriter out = pageContext.getOut();
+        
+        try {
+            
+            // Render the beginning of this element
+            out.println();
+            out.print("<table ");
+            if (columns > 2) {
+                out.print(" columns=\"");
+                out.print(columns);
+                out.print("\"");
+            }
+            if (tableStyle != null) {
+                out.print(" class=\"");
+                out.print(tableStyle);
+                out.print("\"");
+                out.print(" border=\"1\" cellspacing=\"0\" ");
+                out.print(" cellpadding=\"0\" width=\"100%\" ");
+            }
+            out.println(">");
+            
+            
+            // Render each defined row
+            int n = labels.size();
+            for (int i = 0; i < n; i++) {
+                String label = (String) labels.get(i);
+                boolean header = ((Boolean) headers.get(i)).booleanValue();
+                String data = (String) datas.get(i);
+                String labelStyle = (String) labelStyles.get(i);
+                String dataStyle = (String) dataStyles.get(i);
+                String styleId = (String) styleIds.get(i);
+                
+                if (header) {
+                    out.println("<tr class=\"header-row\" >");
+                    out.println("  <th scope=\"col\" width=\"27%\"> ");
+                
+                    out.print("    <div align=\"left\"");
+                    if (labelStyle != null)
+                        out.print( " class=\"" + labelStyle +"\"");
+                    out.print(">");
+                    if (styleId != null) {
+                        out.print("<label for=\"" + styleId + "\">");
+                    }
+                    out.print(label);
+                    if (styleId != null) {
+                        out.print("</label>");
+                    }
+                    out.println("    </div>");
+                    out.println("  </th>");
+                
+                    out.println("  <th scope=\"col\" width=\"73%\"> ");
+                    out.print("    <div align=\"left\"" );
+                    if (dataStyle != null)
+                        out.print(" class=\"" + dataStyle + "\"");
+                    out.print(">");
+                    out.print(data);
+                    out.println("    </div>");
+                    out.print("  </th>");
+                    out.println("</tr>");
+                } else {
+                    out.println("<tr>");
+                
+                    out.println("  <td scope=\"row\" width=\"27%\"> ");
+                
+                    out.print("    <div align=\"left\"");
+                    if (labelStyle != null)
+                        out.print( " class=\"" + labelStyle +"\"");
+                    out.print(">");
+                    if (styleId != null) {
+                        out.print("<label for=\"" + styleId + "\">");
+                    }
+                    out.print(label);
+                    if (styleId != null) {
+                        out.print("</label>");
+                    }
+                    out.println("    </div>");
+                    out.println("  </td>");
+                
+                    out.println("  <td width=\"73%\"> ");
+                    out.print("    <div align=\"left\"" );
+                    if (dataStyle != null)
+                        out.print(" class=\"" + dataStyle + "\"");
+                    out.print(">");
+                    out.print(data);
+                    out.println("    </div>");
+                    out.print("  </td>");
+                    out.println("</tr>");
+                }
+                
+                /*
+                if (!header) {
+                    out.println("<tr height=\"1\">");
+                    out.println("  <td class=\""+ lineStyle + "\" colspan=\"2\">");
+                    out.println("    <img src=\"\" alt=\"\" width=\"1\" height=\"1\" border=\"0\">");
+                    out.println("  </td>");
+                    out.println("</tr>");
+                }
+                 */
+            }
+            
+            // Render the end of this element
+            out.println("</table>");
+            out.println();
+            
+        } catch (IOException e) {
+            throw new JspException(e);
+        }
+        
+        return (EVAL_PAGE);
+        
+    }
+    
+    
+    /**
+     * Release all state information set by this tag.
+     */
+    public void release() {
+        
+        this.headers.clear();
+        this.labels.clear();
+        this.datas.clear();
+        this.labelStyles.clear();
+        this.dataStyles.clear();
+        this.columns = 2;
+        this.tableStyle = null;
+        this.lineStyle = null;
+        this.styleIds.clear();
+        
+    }
+    
+    
+    // -------------------------------------------------------- Package Methods
+    
+    
+    /**
+     * Add a new Action to the set that will be rendered by this control.
+     *
+     * @param label Localized label visible to the user
+     * @param selected Initial selected state of this option
+     * @param url URL to which control should be transferred if selected
+     */
+    
+    void addRow(boolean header, String label, String data,
+    String labelStyle, String dataStyle, String styleId) {
+        
+        headers.add(new Boolean(header));
+        labels.add(label);
+        datas.add(data);
+        labelStyles.add(labelStyle);
+        dataStyles.add(dataStyle);
+        styleIds.add(styleId);
+        
+    }
+    
+    // ------------------------------------------------------ Protected Methods
+    
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TomcatTreeBuilder.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TomcatTreeBuilder.java
new file mode 100644
index 0000000..25a6b27
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TomcatTreeBuilder.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Locale;
+import java.net.URLEncoder;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.Registry;
+import org.apache.struts.Globals;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import javax.management.AttributeNotFoundException;
+import javax.management.MalformedObjectNameException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
+
+/**
+ * <p> Implementation of TreeBuilder interface for Tomcat Tree Controller
+ *     to build plugin components into the tree
+ *
+ * @author Jazmin Jonson
+ * @author Manveen Kaur
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+
+public class TomcatTreeBuilder implements TreeBuilder{
+    
+    // This SERVER_LABEL needs to be localized
+    private final static String SERVER_LABEL = "Tomcat Server";
+    
+    public final static String DEFAULT_DOMAIN = "Catalina";
+    public final static String SERVER_TYPE = ":type=Server";
+    public final static String FACTORY_TYPE = 
+                        DEFAULT_DOMAIN + ":type=MBeanFactory";
+    public final static String SERVICE_TYPE = ":type=Service";
+    public final static String ENGINE_TYPE = ":type=Engine";
+    public final static String CONNECTOR_TYPE = ":type=Connector";
+    public final static String HOST_TYPE = ":type=Host";
+    public final static String CONTEXT_TYPE = ":type=Context";
+    public final static String LOADER_TYPE = ":type=Loader";
+    public final static String MANAGER_TYPE = ":type=Manager";
+    public final static String LOGGER_TYPE = ":type=Logger";
+    public final static String REALM_TYPE = ":type=Realm";
+    public final static String VALVE_TYPE = ":type=Valve";
+
+    public final static String WILDCARD = ",*";
+
+    public final static String URL_ENCODING="UTF-8";
+    
+    private static MBeanServer mBServer = null;
+    private MessageResources resources = null;
+    private Locale locale = null;
+
+    public void buildTree(TreeControl treeControl,
+                          ApplicationServlet servlet,
+                          HttpServletRequest request) {
+
+        try {
+            HttpSession session = request.getSession();
+            locale = (Locale) session.getAttribute(Globals.LOCALE_KEY);
+            mBServer = servlet.getServer();
+            TreeControlNode root = treeControl.getRoot();
+            resources = (MessageResources)
+                servlet.getServletContext().getAttribute(Globals.MESSAGES_KEY);
+            getServers(root);
+        } catch(Throwable t){
+            t.printStackTrace(System.out);
+        }
+
+    }
+    
+    public static ObjectName getMBeanFactory() 
+            throws MalformedObjectNameException {
+        
+        return new ObjectName(FACTORY_TYPE);
+    }
+    
+
+    /**
+     * Append nodes for all defined servers.
+     *
+     * @param rootNode Root node for the tree control 
+     * @param resources The MessageResources for our localized messages
+     *  messages
+     *
+     * @exception Exception if an exception occurs building the tree
+     */
+    public void getServers(TreeControlNode rootNode) throws Exception {
+        
+        String domain = rootNode.getDomain();
+        Iterator serverNames =
+            Lists.getServers(mBServer,domain).iterator();
+        while (serverNames.hasNext()) {
+            String serverName = (String) serverNames.next();
+            ObjectName objectName = new ObjectName(serverName);
+            String nodeLabel = SERVER_LABEL;
+            TreeControlNode serverNode =
+                new TreeControlNode(serverName,
+                                    "Server.gif",
+                                    nodeLabel,
+                                    "EditServer.do?select=" +
+                                    URLEncoder.encode(serverName,URL_ENCODING) +
+                                    "&nodeLabel=" +
+                                    URLEncoder.encode(nodeLabel,URL_ENCODING),
+                                    "content",
+                                    true, domain);
+            rootNode.addChild(serverNode);
+            getServices(serverNode, serverName);
+        }
+        
+    }
+    
+
+    /**
+     * Append nodes for all defined services for the specified server.
+     *
+     * @param serverNode Server node for the tree control
+     * @param serverName Object name of the parent server
+     * @param resources The MessageResources for our localized messages
+     *  messages
+     *
+     * @exception Exception if an exception occurs building the tree
+     */
+    public void getServices(TreeControlNode serverNode, String serverName) 
+        throws Exception {
+
+        String domain = serverNode.getDomain();
+        Iterator serviceNames =
+            Lists.getServices(mBServer, serverName).iterator();
+        while (serviceNames.hasNext()) {
+            String serviceName = (String) serviceNames.next();
+            ObjectName objectName = new ObjectName(serviceName);
+            String nodeLabel =
+                resources.getMessage(locale, 
+                    "server.service.treeBuilder.subtreeNode") + " (" +
+                    objectName.getKeyProperty("serviceName") + ")";
+            TreeControlNode serviceNode =
+                new TreeControlNode(serviceName,
+                                    "Service.gif",
+                                    nodeLabel,
+                                    "EditService.do?select=" +
+                                    URLEncoder.encode(serviceName,URL_ENCODING) +
+                                    "&nodeLabel=" +
+                                    URLEncoder.encode(nodeLabel,URL_ENCODING),
+                                    "content",
+                                    false, domain);
+            serverNode.addChild(serviceNode);
+            getConnectors(serviceNode, serviceName);
+            getHosts(serviceNode, serviceName);
+            getRealms(serviceNode, serviceName);
+            getValves(serviceNode, serviceName);
+        }
+
+    }
+    
+
+    /**
+     * Append nodes for all defined connectors for the specified service.
+     *
+     * @param serviceNode Service node for the tree control
+     * @param serviceName Object name of the parent service
+     *
+     * @exception Exception if an exception occurs building the tree
+     */
+    public void getConnectors(TreeControlNode serviceNode, String serviceName)
+                        throws Exception{
+        
+        String domain = serviceNode.getDomain();
+        Iterator connectorNames =
+            Lists.getConnectors(mBServer, serviceName).iterator();
+        while (connectorNames.hasNext()) {
+            String connectorName = (String) connectorNames.next();
+            ObjectName objectName = new ObjectName(connectorName);
+            String nodeLabel =
+                resources.getMessage(locale, 
+                    "server.service.treeBuilder.connector") + " (" +  
+                    objectName.getKeyProperty("port") + ")";
+            TreeControlNode connectorNode =
+                new TreeControlNode(connectorName,
+                                    "Connector.gif",
+                                    nodeLabel,
+                                    "EditConnector.do?select=" +
+                                    URLEncoder.encode(connectorName,URL_ENCODING) +
+                                    "&nodeLabel=" +
+                                    URLEncoder.encode(nodeLabel,URL_ENCODING),
+                                    "content",
+                                    false, domain);
+            serviceNode.addChild(connectorNode);
+        }
+    }
+    
+
+    /**
+     * Append nodes for all defined hosts for the specified service.
+     *
+     * @param serviceNode Service node for the tree control
+     * @param serviceName Object name of the parent service
+     * @param resources The MessageResources for our localized messages
+     *  messages
+     *
+     * @exception Exception if an exception occurs building the tree
+     */
+    public void getHosts(TreeControlNode serviceNode, String serviceName) 
+        throws Exception {
+        
+        String domain = serviceNode.getDomain();
+        Iterator hostNames =
+            Lists.getHosts(mBServer, serviceName).iterator();
+        while (hostNames.hasNext()) {
+            String hostName = (String) hostNames.next();
+            ObjectName objectName = new ObjectName(hostName);
+            String nodeLabel =
+                resources.getMessage(locale, 
+                    "server.service.treeBuilder.host") + " (" +
+                    objectName.getKeyProperty("host") + ")";
+            TreeControlNode hostNode =
+                new TreeControlNode(hostName,
+                                    "Host.gif",
+                                    nodeLabel,
+                                    "EditHost.do?select=" +
+                                    URLEncoder.encode(hostName,URL_ENCODING) +
+                                    "&nodeLabel=" +
+                                    URLEncoder.encode(nodeLabel,URL_ENCODING),
+                                    "content",
+                                    false, domain);
+            serviceNode.addChild(hostNode);
+            getContexts(hostNode, hostName);            
+            getRealms(hostNode, hostName);
+            getValves(hostNode, hostName);
+        }
+
+    }    
+
+    
+    /**
+     * Append nodes for all defined contexts for the specified host.
+     *
+     * @param hostNode Host node for the tree control
+     * @param hostName Object name of the parent host
+     * @param resources The MessageResources for our localized messages
+     *  messages
+     *
+     * @exception Exception if an exception occurs building the tree
+     */
+    public void getContexts(TreeControlNode hostNode, String hostName) 
+        throws Exception {
+        
+        String domain = hostNode.getDomain();
+        Iterator contextNames =
+            Lists.getContexts(mBServer, hostName).iterator();
+        while (contextNames.hasNext()) {
+            String contextName = (String) contextNames.next();
+            ObjectName objectName = new ObjectName(contextName);
+            String name = objectName.getKeyProperty("name");
+            name = name.substring(2);
+            int i = name.indexOf("/");
+            String path = name.substring(i);
+            String nodeLabel =
+                resources.getMessage(locale, 
+                    "server.service.treeBuilder.context") + " (" + path + ")";
+            TreeControlNode contextNode =
+                new TreeControlNode(contextName,
+                                    "Context.gif",
+                                    nodeLabel,
+                                    "EditContext.do?select=" +
+                                    URLEncoder.encode(contextName,URL_ENCODING) +
+                                    "&nodeLabel=" +
+                                    URLEncoder.encode(nodeLabel,URL_ENCODING),
+                                    "content",
+                                    false, domain);
+            hostNode.addChild(contextNode);
+            getResources(contextNode, contextName);
+            getRealms(contextNode, contextName);
+            getValves(contextNode, contextName);
+        }
+    }
+    
+    /**
+     * Append nodes for any defined realms for the specified container.
+     *
+     * @param containerNode Container node for the tree control
+     * @param containerName Object name of the parent container
+     *
+     * @exception Exception if an exception occurs building the tree
+     */
+    public void getRealms(TreeControlNode containerNode,
+                          String containerName) throws Exception {
+
+        String domain = containerNode.getDomain();
+        Iterator realmNames =
+            Lists.getRealms(mBServer, containerName).iterator();
+        while (realmNames.hasNext()) {
+            String realmName = (String) realmNames.next();
+	    ObjectName objectName = new ObjectName(realmName);
+            // Create tree nodes for non JAASRealm only
+            try {
+                mBServer.getAttribute(objectName, "validate");
+            } catch (AttributeNotFoundException e) {
+                String nodeLabel = resources.getMessage(locale, 
+                    "server.service.treeBuilder.realmFor", 
+                    containerNode.getLabel());
+	        TreeControlNode realmNode =
+		    new TreeControlNode(realmName,
+                                    "Realm.gif",
+                                    nodeLabel,
+                                    "EditRealm.do?select=" +
+                                    URLEncoder.encode(realmName,URL_ENCODING) +
+                                    "&nodeLabel=" +
+                                    URLEncoder.encode(nodeLabel,URL_ENCODING),
+                                    "content",
+                                    false, domain);
+                containerNode.addChild(realmNode);
+            }
+        }
+        
+    }   
+        
+    
+    /**
+     * Append nodes for any define resources for the specified Context.
+     *
+     * @param containerNode Container node for the tree control
+     * @param containerName Object name of the parent container
+     * @param resources The MessageResources for our localized messages
+     *  messages
+     */
+    public void getResources(TreeControlNode containerNode, String containerName) 
+        throws Exception {
+
+        String domain = containerNode.getDomain();
+        ObjectName oname = new ObjectName(containerName);
+        String type = oname.getKeyProperty("type");
+        if (type == null) {
+            type = oname.getKeyProperty("j2eeType");
+            if (type.equals("WebModule")) {
+                type = "Context";
+            } else {
+                type = "";
+            }
+        }
+        String path = "";
+        String host = "";
+        String name = oname.getKeyProperty("name");
+        if ((name != null) && (name.length() > 0)) {
+            // context resource
+            name = name.substring(2);
+            int i = name.indexOf("/");
+            host = name.substring(0,i);
+            path = name.substring(i);
+        }     
+        TreeControlNode subtree = new TreeControlNode
+            ("Context Resource Administration " + containerName,
+             "folder_16_pad.gif",
+             resources.getMessage(locale, "resources.treeBuilder.subtreeNode"),
+             null,
+             "content",
+             true, domain);        
+        containerNode.addChild(subtree);
+        TreeControlNode datasources = new TreeControlNode
+            ("Context Data Sources " + containerName,
+            "Datasource.gif",
+            resources.getMessage(locale, "resources.treeBuilder.datasources"),
+            "resources/listDataSources.do?resourcetype=" + 
+                URLEncoder.encode(type,URL_ENCODING) + "&path=" +
+                URLEncoder.encode(path,URL_ENCODING) + "&host=" + 
+                URLEncoder.encode(host,URL_ENCODING) + "&domain=" + 
+                URLEncoder.encode(domain,URL_ENCODING) + "&forward=" +
+                URLEncoder.encode("DataSources List Setup",URL_ENCODING),
+            "content",
+            false, domain);
+        TreeControlNode mailsessions = new TreeControlNode
+            ("Context Mail Sessions " + containerName,
+            "Mailsession.gif",
+            resources.getMessage(locale, "resources.treeBuilder.mailsessions"),
+            "resources/listMailSessions.do?resourcetype=" + 
+                URLEncoder.encode(type,URL_ENCODING) + "&path=" +
+                URLEncoder.encode(path,URL_ENCODING) + "&host=" + 
+                URLEncoder.encode(host,URL_ENCODING) + "&domain=" + 
+                URLEncoder.encode(domain,URL_ENCODING) + "&forward=" +
+                URLEncoder.encode("MailSessions List Setup",URL_ENCODING),
+            "content",
+            false, domain);
+        TreeControlNode resourcelinks = new TreeControlNode
+            ("Resource Links " + containerName,
+            "ResourceLink.gif",
+            resources.getMessage(locale, "resources.treeBuilder.resourcelinks"),
+            "resources/listResourceLinks.do?resourcetype=" + 
+                URLEncoder.encode(type,URL_ENCODING) + "&path=" +
+                URLEncoder.encode(path,URL_ENCODING) + "&host=" + 
+                URLEncoder.encode(host,URL_ENCODING) + "&domain=" + 
+                URLEncoder.encode(domain,URL_ENCODING) + "&forward=" +
+                URLEncoder.encode("ResourceLinks List Setup",URL_ENCODING),
+            "content",
+            false, domain);
+        TreeControlNode envs = new TreeControlNode
+            ("Context Environment Entries "+ containerName,
+            "EnvironmentEntries.gif",
+            resources.getMessage(locale, "resources.env.entries"),
+            "resources/listEnvEntries.do?resourcetype=" + 
+                URLEncoder.encode(type,URL_ENCODING) + "&path=" +
+                URLEncoder.encode(path,URL_ENCODING) + "&host=" + 
+                URLEncoder.encode(host,URL_ENCODING) + "&domain=" + 
+                URLEncoder.encode(domain,URL_ENCODING) + "&forward=" +
+                URLEncoder.encode("EnvEntries List Setup",URL_ENCODING),
+            "content",
+            false, domain);
+        subtree.addChild(datasources);
+        subtree.addChild(mailsessions);
+        subtree.addChild(resourcelinks);
+        subtree.addChild(envs);
+    }
+    
+    
+   /**
+     * Append nodes for any defined valves for the specified container.
+     *
+     * @param containerNode Container node for the tree control
+     * @param containerName Object name of the parent container
+     *
+     * @exception Exception if an exception occurs building the tree
+     */
+    public void getValves(TreeControlNode containerNode,
+                          String containerName) throws Exception {
+
+        String domain = containerNode.getDomain();
+        Iterator valveNames =
+                Lists.getValves(mBServer, containerName).iterator();        
+        while (valveNames.hasNext()) {
+            String valveName = (String) valveNames.next();
+            ObjectName objectName = new ObjectName(valveName);
+            String nodeLabel = "Valve for " + containerNode.getLabel();
+            TreeControlNode valveNode =
+                new TreeControlNode(valveName,
+                                    "Valve.gif",
+                                    nodeLabel,
+                                    "EditValve.do?select=" +
+                                    URLEncoder.encode(valveName,URL_ENCODING) +
+                                    "&nodeLabel=" +
+                                    URLEncoder.encode(nodeLabel,URL_ENCODING) +
+                                    "&parent=" +
+                                    URLEncoder.encode(containerName,URL_ENCODING),
+                                    "content",
+                                    false, domain);
+            containerNode.addChild(valveNode);
+        }
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeBuilder.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeBuilder.java
new file mode 100644
index 0000000..2e5b733
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeBuilder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin;
+
+/**
+ * <p> Interface for Admin Tree Controller to build plugin components
+ * into the tree
+ *
+ * @author Jazmin Jonson
+ * @version
+ */
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface TreeBuilder {
+
+    public void buildTree(TreeControl treeControl,
+                          ApplicationServlet servlet,
+                          HttpServletRequest request);
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControl.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControl.java
new file mode 100644
index 0000000..d3584b0
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControl.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+
+/**
+ * <p>The overall data structure representing a <em>tree control</em>
+ * that can be rendered by the <code>TreeControlTag</code> custom tag.
+ * Each node of the tree is represented by an instance of
+ * <code>TreeControlNode</code>.</p>
+ *
+ * @author Jazmin Jonson
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TreeControl implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance with no predefined root node.
+     */
+    public TreeControl() {
+
+        super();
+        setRoot(null);
+
+    }
+
+
+    /**
+     * Construct a new instance with the specified root node.
+     *
+     * @param root The new root node
+     */
+    public TreeControl(TreeControlNode root) {
+
+        super();
+        setRoot(root);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The collection of nodes that represent this tree, keyed by name.
+     */
+    protected HashMap registry = new HashMap();
+
+
+    /**
+     * The most recently selected node.
+     */
+    protected TreeControlNode selected = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The root node of the entire tree.
+     */
+    protected TreeControlNode root = null;
+
+    public TreeControlNode getRoot() {
+        return (this.root);
+    }
+
+    protected void setRoot(TreeControlNode root) {
+        if (this.root != null)
+            removeNode(this.root);
+        if (root != null)
+            addNode(root);
+        root.setLast(true);
+        this.root = root;
+    }
+
+
+    /**
+     * The current displayable "width" of this tree (that is, the maximum
+     * depth of the visible part of the tree).
+     */
+    public int getWidth() {
+
+        if (root == null)
+            return (0);
+        else
+            return (getWidth(root));
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Find and return the <code>TreeControlNode</code> for the specified
+     * node name, if it exists; otherwise, return <code>null</code>.
+     *
+     * @param name Name of the <code>TreeControlNode</code> to be returned
+     */
+    public TreeControlNode findNode(String name) {
+
+        synchronized (registry) {
+            return ((TreeControlNode) registry.get(name));
+        }
+
+    }
+
+
+    /**
+     * Mark the specified node as the one-and-only currently selected one,
+     * deselecting any previous node that was so marked.
+     *
+     * @param node Name of the node to mark as selected, or <code>null</code>
+     *  if there should be no currently selected node
+     */
+    public void selectNode(String name) {
+
+        if (selected != null) {
+            selected.setSelected(false);
+            selected = null;
+        }
+        selected = findNode(name);
+        if (selected != null)
+            selected.setSelected(true);
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Register the specified node in our registry of the complete tree.
+     *
+     * @param node The <code>TreeControlNode</code> to be registered
+     *
+     * @exception IllegalArgumentException if the name of this node
+     *  is not unique
+     */
+    void addNode(TreeControlNode node) throws IllegalArgumentException {
+
+        synchronized (registry) {
+            String name = node.getName();
+            if (registry.containsKey(name))
+                throw new IllegalArgumentException("Name '" + name +
+                                                   "' is not unique");
+            node.setTree(this);
+            registry.put(name, node);
+        }
+
+    }
+
+
+    /**
+     * Calculate the width of the subtree below the specified node.
+     *
+     * @param node The node for which to calculate the width
+     */
+    int getWidth(TreeControlNode node) {
+
+        int width = node.getWidth();
+        if (!node.isExpanded())
+            return (width);
+        TreeControlNode children[] = node.findChildren();
+        for (int i = 0; i < children.length; i++) {
+            int current = getWidth(children[i]);
+            if (current > width)
+                width = current;
+        }
+        return (width);
+
+    }
+
+
+    /**
+     * Deregister the specified node, as well as all child nodes of this
+     * node, from our registry of the complete tree.  If this node is not
+     * present, no action is taken.
+     *
+     * @param node The <code>TreeControlNode</code> to be deregistered
+     */
+    void removeNode(TreeControlNode node) {
+
+        synchronized (registry) {
+            TreeControlNode children[] = node.findChildren();
+            for (int i = 0; i < children.length; i++)
+                removeNode(children[i]);
+            TreeControlNode parent = node.getParent();
+            if (parent != null) {
+                parent.removeChild(node);
+            }
+            node.setParent(null);
+            node.setTree(null);
+            if (node == this.root) {
+                this.root = null;
+            }
+            registry.remove(node.getName());
+        }
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControlNode.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControlNode.java
new file mode 100644
index 0000000..d45c4fc
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControlNode.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+
+/**
+ * <p>An individual node of a tree control represented by an instance of
+ * <code>TreeControl</code>, and rendered by an instance of
+ * <code>TreeControlTag</code>.</p>
+ *
+ * @author Jazmin Jonson
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TreeControlNode implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new TreeControlNode with the specified parameters.
+     *
+     * @param name Internal name of this node (must be unique within
+     *  the entire tree)
+     * @param icon Pathname of the image file for the icon to be displayed
+     *  when this node is visible, relative to the image directory
+     *  for our images
+     * @param label The label that will be displayed to the user if
+     *  this node is visible
+     * @param action The hyperlink to be selected if the user
+     *  selects this node, or <code>null</code> if this node's label should
+     *  not be a hyperlink
+     * @param target The window target in which the <code>action</code>
+     *  hyperlink's results will be displayed, or <code>null</code> for
+     *  the current window
+     * @param expanded Should this node be expanded?
+     */
+    public TreeControlNode(String name,
+                           String icon, String label,
+                           String action, String target,
+                           boolean expanded, String domain) {
+
+        super();
+        this.name = name;
+        this.icon = icon;
+        this.label = label;
+        this.action = action;
+        this.target = target;
+        this.expanded = expanded;
+        this.domain = domain;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of child <code>TreeControlNodes</code> for this node, in the
+     * order that they should be displayed.
+     */
+    protected ArrayList children = new ArrayList();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The hyperlink to which control will be directed if this node
+     * is selected by the user.
+     */
+    protected String action = null;
+
+    public String getAction() {
+        return (this.action);
+    }
+
+    /**
+     * The domain of this node.
+     */
+    protected String domain = null;
+
+    public String getDomain() {
+        return (this.domain);
+    }
+
+    /**
+     * Is this node currently expanded?
+     */
+    protected boolean expanded = false;
+
+    public boolean isExpanded() {
+        return (this.expanded);
+    }
+
+    public void setExpanded(boolean expanded) {
+        this.expanded = expanded;
+    }
+
+
+    /**
+     * The pathname to the icon file displayed when this node is visible,
+     * relative to the image directory for our images.
+     */
+    protected String icon = null;
+
+    public String getIcon() {
+        return (this.icon);
+    }
+
+
+    /**
+     * The label that will be displayed when this node is visible.
+     */
+    protected String label = null;
+
+    public String getLabel() {
+        return (this.label);
+    }
+
+
+    /**
+     * Is this the last node in the set of children for our parent node?
+     */
+    protected boolean last = false;
+
+    public boolean isLast() {
+        return (this.last);
+    }
+
+    void setLast(boolean last) {
+        this.last = last;
+    }
+
+
+    /**
+     * Is this a "leaf" node (i.e. one with no children)?
+     */
+    public boolean isLeaf() {
+        synchronized (children) {
+            return (children.size() < 1);
+        }
+    }
+
+
+    /**
+     * The unique (within the entire tree) name of this node.
+     */
+    protected String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+
+    /**
+     * The parent node of this node, or <code>null</code> if this
+     * is the root node.
+     */
+    protected TreeControlNode parent = null;
+
+    public TreeControlNode getParent() {
+        return (this.parent);
+    }
+
+    void setParent(TreeControlNode parent) {
+        this.parent = parent;
+        if (parent == null)
+            width = 1;
+        else
+            width = parent.getWidth() + 1;
+    }
+
+
+    /**
+     * Is this node currently selected?
+     */
+    protected boolean selected = false;
+
+    public boolean isSelected() {
+        return (this.selected);
+    }
+
+    public void setSelected(boolean selected) {
+        this.selected = selected;
+    }
+
+
+    /**
+     * The window target for the hyperlink identified by the
+     * <code>action</code> property, if this node is selected
+     * by the user.
+     */
+    protected String target = null;
+
+    public String getTarget() {
+        return (this.target);
+    }
+
+
+    /**
+     * The <code>TreeControl</code> instance representing the
+     * entire tree.
+     */
+    protected TreeControl tree = null;
+
+    public TreeControl getTree() {
+        return (this.tree);
+    }
+
+    void setTree(TreeControl tree) {
+        this.tree = tree;
+    }
+
+
+    /**
+     * The display width necessary to display this item (if it is visible).
+     * If this item is not visible, the calculated width will be that of our
+     * most immediately visible parent.
+     */
+    protected int width = 0;
+
+    public int getWidth() {
+        return (this.width);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new child node to the end of the list.
+     *
+     * @param child The new child node
+     *
+     * @exception IllegalArgumentException if the name of the new child
+     *  node is not unique
+     */
+    public void addChild(TreeControlNode child)
+        throws IllegalArgumentException {
+
+        tree.addNode(child);
+        child.setParent(this);
+        synchronized (children) {
+            int n = children.size();
+            if (n > 0) {
+                TreeControlNode node = (TreeControlNode) children.get(n - 1);
+                node.setLast(false);
+            }
+            child.setLast(true);
+            children.add(child);
+        }
+
+    }
+
+
+    /**
+     * Add a new child node at the specified position in the child list.
+     *
+     * @param offset Zero-relative offset at which the new node
+     *  should be inserted
+     * @param child The new child node
+     *
+     * @exception IllegalArgumentException if the name of the new child
+     *  node is not unique
+     */
+    public void addChild(int offset, TreeControlNode child)
+        throws IllegalArgumentException {
+
+        tree.addNode(child);
+        child.setParent(this);
+        synchronized (children) {
+            children.add(offset, child);
+        }
+
+    }
+
+
+    /**
+     * Return the set of child nodes for this node.
+     */
+    public TreeControlNode[] findChildren() {
+
+        synchronized (children) {
+            TreeControlNode results[] = new TreeControlNode[children.size()];
+            return ((TreeControlNode[]) children.toArray(results));
+        }
+
+    }
+
+
+    /**
+     * Remove this node from the tree.
+     */
+    public void remove() {
+
+        if (tree != null) {
+            tree.removeNode(this);
+        }
+
+    }
+
+
+    /**
+     * Remove the child node (and all children of that child) at the
+     * specified position in the child list.
+     *
+     * @param offset Zero-relative offset at which the existing
+     *  node should be removed
+     */
+    public void removeChild(int offset) {
+
+        synchronized (children) {
+            TreeControlNode child =
+                (TreeControlNode) children.get(offset);
+            tree.removeNode(child);
+            child.setParent(null);
+            children.remove(offset);
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Remove the specified child node.  It is assumed that all of the
+     * children of this child node have already been removed.
+     *
+     * @param child Child node to be removed
+     */
+    void removeChild(TreeControlNode child) {
+
+        if (child == null) {
+            return;
+        }
+        synchronized (children) {
+            int n = children.size();
+            for (int i = 0; i < n; i++) {
+                if (child == (TreeControlNode) children.get(i)) {
+                    children.remove(i);
+                    return;
+                }
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControlTag.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControlTag.java
new file mode 100644
index 0000000..4537e3c
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControlTag.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.TagSupport;
+
+
+/**
+ * <p>JSP custom tag that renders a tree control represented by the
+ * <code>TreeControl</code> and <code>TreeControlNode</code> classes.
+ * This tag has the following user-settable attributes:</p>
+ * <ul>
+ * <li><strong>action</strong> - Hyperlink to which expand/contract actions
+ *     should be sent, with a string "<code>{name}</code> marking where
+ *     the node name of the affected node should be included.</li>
+ * <li><strong>images</strong> - Name of the directory containing the images
+ *     for our icons, relative to the page including this tag.  If not
+ *     specified, defaults to "images".</li>
+ * <li><strong>scope</strong> - Attribute scope in which the <code>tree</code>
+ *     attribute is to be found (page, request, session, application).  If
+ *     not specified, the attribute is searched for in all scopes.</li>
+ * <li><strong>style</strong> - CSS style <code>class</code> to be applied
+ *     to be applied to the entire rendered output of the tree control.
+ *     If not specified, no style class is applied.</li>
+ * <li><strong>styleSelected</strong> - CSS style <code>class</code> to be
+ *     applied to the text of any element that is currently selected.  If not
+ *     specified, no additional style class is applied.</li>
+ * <li><strong>styleUnselected</strong> - CSS style <code>class</code> to be
+ *     applied to the text of any element that is not currently selected.
+ *     If not specified, no additional style class is applied.</li>
+ * <li><strong>tree</strong> - Attribute name under which the
+ *     <code>TreeControl</code> bean of the tree we are rendering
+ *     is stored, in the scope specified by the <code>scope</code>
+ *     attribute.  This attribute is required.</li>
+ * </ul>
+ *
+ * <strong>FIXME</strong> - Internationalize the exception messages!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TreeControlTag extends TagSupport {
+
+
+    /**
+     * The default directory name for icon images.
+     */
+    static final String DEFAULT_IMAGES = "images";
+
+
+    /**
+     * The names of tree state images that we need.
+     */
+    static final String IMAGE_HANDLE_DOWN_LAST =    "handledownlast.gif";
+    static final String IMAGE_HANDLE_DOWN_MIDDLE =  "handledownmiddle.gif";
+    static final String IMAGE_HANDLE_RIGHT_LAST =   "handlerightlast.gif";
+    static final String IMAGE_HANDLE_RIGHT_MIDDLE = "handlerightmiddle.gif";
+    static final String IMAGE_LINE_LAST =           "linelastnode.gif";
+    static final String IMAGE_LINE_MIDDLE =         "linemiddlenode.gif";
+    static final String IMAGE_LINE_VERTICAL =       "linevertical.gif";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The hyperlink to be used for submitting requests to expand and
+     * contract tree nodes.  The placeholder "<code>{name}</code>" will
+     * be replaced by the <code>name</code> property of the current
+     * tree node.
+     */
+    protected String action = null;
+
+    public String getAction() {
+        return (this.action);
+    }
+
+    public void setAction(String action) {
+        this.action = action;
+    }
+
+
+    /**
+     * The name of the directory containing the images for our icons,
+     * relative to the page including this tag.
+     */
+    protected String images = DEFAULT_IMAGES;
+
+    public String getImages() {
+        return (this.images);
+    }
+
+    public void setImages(String images) {
+        this.images = images;
+    }
+
+
+    /**
+     * The name of the scope in which to search for the <code>tree</code>
+     * attribute.  Must be "page", "request", "session", or "application"
+     * (or <code>null</code> for an ascending-visibility search).
+     */
+    protected String scope = null;
+
+    public String getScope() {
+        return (this.scope);
+    }
+
+    public void setScope(String scope) {
+        if (!"page".equals(scope) &&
+            !"request".equals(scope) &&
+            !"session".equals(scope) &&
+            !"application".equals(scope))
+            throw new IllegalArgumentException("Invalid scope '" +
+                                               scope + "'");
+        this.scope = scope;
+    }
+
+
+    /**
+     * The CSS style <code>class</code> to be applied to the entire tree.
+     */
+    protected String style = null;
+
+    public String getStyle() {
+        return (this.style);
+    }
+
+    public void setStyle(String style) {
+        this.style = style;
+    }
+
+
+    /**
+     * The CSS style <code>class</code> to be applied to the text
+     * of selected nodes.
+     */
+    protected String styleSelected = null;
+
+    public String getStyleSelected() {
+        return (this.styleSelected);
+    }
+
+    public void setStyleSelected(String styleSelected) {
+        this.styleSelected = styleSelected;
+    }
+
+
+    /**
+     * The CSS style <code>class</code> to be applied to the text
+     * of unselected nodes.
+     */
+    protected String styleUnselected = null;
+
+    public String getStyleUnselected() {
+        return (this.styleUnselected);
+    }
+
+    public void setStyleUnselected(String styleUnselected) {
+        this.styleUnselected = styleUnselected;
+    }
+
+
+    /**
+     * The name of the attribute (in the specified scope) under which our
+     * <code>TreeControl</code> instance is stored.
+     */
+    protected String tree = null;
+
+    public String getTree() {
+        return (this.tree);
+    }
+
+    public void setTree(String tree) {
+        this.tree = tree;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Render this tree control.
+     *
+     * @exception JspException if a processing error occurs
+     */
+    public int doEndTag() throws JspException {
+
+        TreeControl treeControl = getTreeControl();
+        JspWriter out = pageContext.getOut();
+        try {
+            out.print
+                ("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\"");
+            if (style != null) {
+                out.print(" class=\"");
+                out.print(style);
+                out.print("\"");
+            }
+            out.println(">");
+            int level = 0;
+            TreeControlNode node = treeControl.getRoot();
+            render(out, node, level, treeControl.getWidth(), true);
+            out.println("</table>");
+        } catch (IOException e) {
+            throw new JspException(e);
+        }
+
+        return (EVAL_PAGE);
+
+    }
+
+
+    /**
+     * Release all state information set by this tag.
+     */
+    public void release() {
+
+        this.action = null;
+        this.images = DEFAULT_IMAGES;
+        this.scope = null;
+        this.style = null;
+        this.styleSelected = null;
+        this.styleUnselected = null;
+        this.tree = null;
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return the <code>TreeControl</code> instance for the tree control that
+     * we are rendering.
+     *
+     * @exception JspException if no TreeControl instance can be found
+     */
+    protected TreeControl getTreeControl() throws JspException {
+
+        Object treeControl = null;
+        if (scope == null)
+            treeControl = pageContext.findAttribute(tree);
+        else if ("page".equals(scope))
+            treeControl =
+                pageContext.getAttribute(tree, PageContext.PAGE_SCOPE);
+        else if ("request".equals(scope))
+            treeControl =
+                pageContext.getAttribute(tree, PageContext.REQUEST_SCOPE);
+        else if ("session".equals(scope))
+            treeControl =
+                pageContext.getAttribute(tree, PageContext.SESSION_SCOPE);
+        else if ("application".equals(scope))
+            treeControl =
+                pageContext.getAttribute(tree, PageContext.APPLICATION_SCOPE);
+        if (treeControl == null)
+            throw new JspException("Cannot find tree control attribute '" +
+                                   tree + "'");
+        else if (!(treeControl instanceof TreeControl))
+            throw new JspException("Invalid tree control attribute '" +
+                                   tree + "'");
+        else
+            return ((TreeControl) treeControl);
+
+    }
+
+
+    /**
+     * Render the specified node, as controlled by the specified parameters.
+     *
+     * @param out The <code>JspWriter</code> to which we are writing
+     * @param node The <code>TreeControlNode</code> we are currently
+     *  rendering
+     * @param level The indentation level of this node in the tree
+     * @param width Total displayable width of the tree
+     * @param last Is this the last node in a list?
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected void render(JspWriter out, TreeControlNode node,
+                          int level, int width, boolean last)
+        throws IOException {
+
+        HttpServletResponse response =
+            (HttpServletResponse) pageContext.getResponse();
+    
+        // if the node is root node and the label value is
+        // null, then do not render root node in the tree.
+        
+        if ("ROOT-NODE".equalsIgnoreCase(node.getName()) &&
+        (node.getLabel() == null)) {
+            // Render the children of this node
+            TreeControlNode children[] = node.findChildren();
+            int lastIndex = children.length - 1;
+            int newLevel = level + 1;
+            for (int i = 0; i < children.length; i++) {
+                render(out, children[i], newLevel, width, i == lastIndex);
+            }
+            return;
+        }
+        
+        // Render the beginning of this node
+        out.println("  <tr valign=\"middle\">");
+
+        // Create the appropriate number of indents
+        for (int i = 0; i < level; i++) {
+            int levels = level - i;
+            TreeControlNode parent = node;
+            for (int j = 1; j <= levels; j++)
+                parent = parent.getParent();
+            if (parent.isLast())
+                out.print("    <td></td>");
+            else {
+                out.print("    <td><img src=\"");
+                out.print(images);
+                out.print("/");
+                out.print(IMAGE_LINE_VERTICAL);
+                out.print("\" alt=\"\" border=\"0\"></td>");
+            }
+            out.println();
+        }
+
+        // Render the tree state image for this node
+
+        // HACK to take into account special characters like = and &
+        // in the node name, could remove this code if encode URL
+        // and later request.getParameter() could deal with = and &
+        // character in parameter values. 
+        String encodedNodeName = URLEncoder.encode(node.getName(),TomcatTreeBuilder.URL_ENCODING);
+
+        String action = replace(getAction(), "{name}", encodedNodeName);
+
+        
+        String updateTreeAction =
+            replace(getAction(), "tree={name}", "select=" + encodedNodeName);
+        updateTreeAction =
+            ((HttpServletResponse) pageContext.getResponse()).
+            encodeURL(updateTreeAction);
+
+        out.print("    <td>");
+        if ((action != null) && !node.isLeaf()) {
+            out.print("<a href=\"");
+            out.print(response.encodeURL(action));
+            out.print("\">");
+        }
+        out.print("<img src=\"");
+        out.print(images);
+        out.print("/");
+        if (node.isLeaf()) {
+            if (node.isLast())
+                out.print(IMAGE_LINE_LAST);
+            else
+                out.print(IMAGE_LINE_MIDDLE);
+            out.print("\" alt=\"");
+        } else if (node.isExpanded()) {
+            if (node.isLast())
+                out.print(IMAGE_HANDLE_DOWN_LAST);
+            else
+                out.print(IMAGE_HANDLE_DOWN_MIDDLE);
+            out.print("\" alt=\"close node");
+        } else {
+            if (node.isLast())
+                out.print(IMAGE_HANDLE_RIGHT_LAST);
+            else
+                out.print(IMAGE_HANDLE_RIGHT_MIDDLE);
+            out.print("\" alt=\"expand node");
+        }
+        out.print("\" border=\"0\">");
+        if ((action != null) && !node.isLeaf())
+            out.print("</a>");
+        out.println("</td>");
+
+        // Calculate the hyperlink for this node (if any)
+        String hyperlink = null;
+        if (node.getAction() != null)
+            hyperlink = ((HttpServletResponse) pageContext.getResponse()).
+                encodeURL(node.getAction());
+
+        // Render the icon for this node (if any)
+        out.print("    <td colspan=\"");
+        out.print(width - level + 1);
+        out.print("\">");
+        if (node.getIcon() != null) {
+            if (hyperlink != null) {
+                out.print("<a href=\"");
+                out.print(hyperlink);
+                out.print("\"");
+                String target = node.getTarget();
+                if(target != null) {
+                    out.print(" target=\"");
+                    out.print(target);
+                    out.print("\"");
+                }
+                // to refresh the tree in the same 'self' frame
+                out.print(" onclick=\"");
+                out.print("self.location.href='" + updateTreeAction + "'");
+                out.print("\"");
+                out.print(">");
+            }
+            out.print("<img src=\"");
+            out.print(images);
+            out.print("/");
+            out.print(node.getIcon());
+            out.print("\" alt=\"");
+            out.print("\" border=\"0\">");
+            if (hyperlink != null)
+                out.print("</a>");
+        }
+
+        // Render the label for this node (if any)
+
+        if (node.getLabel() != null) {
+            String labelStyle = null;
+            if (node.isSelected() && (styleSelected != null))
+                labelStyle = styleSelected;
+            else if (!node.isSelected() && (styleUnselected != null))
+                labelStyle = styleUnselected;
+            if (hyperlink != null) {
+                // Note the leading space so that the text has some space
+                // between it and any preceding images
+                out.print(" <a href=\"");
+                out.print(hyperlink);
+                out.print("\"");
+                String target = node.getTarget();
+                if(target != null) {
+                    out.print(" target=\"");
+                    out.print(target);
+                    out.print("\"");
+                }
+                if (labelStyle != null) {
+                    out.print(" class=\"");
+                    out.print(labelStyle);
+                    out.print("\"");
+                }
+                // to refresh the tree in the same 'self' frame
+                out.print(" onclick=\"");
+                out.print("self.location.href='" + updateTreeAction + "'");
+                out.print("\"");
+                out.print(">");
+            } else if (labelStyle != null) {
+                out.print("<span class=\"");
+                out.print(labelStyle);
+                out.print("\">");
+            }
+            out.print(node.getLabel());
+            if (hyperlink != null)
+                out.print("</a>");
+            else if (labelStyle != null)
+                out.print("</span>");
+        }
+        out.println("</td>");
+
+        // Render the end of this node
+        out.println("  </tr>");
+
+        // Render the children of this node
+        if (node.isExpanded()) {
+            TreeControlNode children[] = node.findChildren();
+            int lastIndex = children.length - 1;
+            int newLevel = level + 1;
+            for (int i = 0; i < children.length; i++) {
+                render(out, children[i], newLevel, width, i == lastIndex);
+            }
+        }
+
+    }
+
+
+    /**
+     * Replace any occurrence of the specified placeholder in the specified
+     * template string with the specified replacement value.
+     *
+     * @param template Pattern string possibly containing the placeholder
+     * @param placeholder Placeholder expression to be replaced
+     * @param value Replacement value for the placeholder
+     */
+    protected String replace(String template, String placeholder,
+                             String value) {
+
+        if (template == null)
+            return (null);
+        if ((placeholder == null) || (value == null))
+            return (template);
+        while (true) {
+            int index = template.indexOf(placeholder);
+            if (index < 0)
+                break;
+            StringBuffer temp = new StringBuffer(template.substring(0, index));
+            temp.append(value);
+            temp.append(template.substring(index + placeholder.length()));
+            template = temp.toString();
+        }
+        return (template);
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControlTestAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControlTestAction.java
new file mode 100644
index 0000000..6a683d1
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/TreeControlTestAction.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin;
+
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.net.URLDecoder;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Test <code>Action</code> that handles events from the tree control test
+ * page.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TreeControlTestAction extends Action {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        getServlet().log("Entered TreeControlTestAction:perform()");
+
+        String name = null;
+        HttpSession session = request.getSession();
+        TreeControl control =
+            (TreeControl) session.getAttribute("treeControlTest");
+
+        // Handle a tree expand/contract event
+        name = request.getParameter("tree");
+
+        if (name != null) {
+            getServlet().log("Tree expand/contract on " + name);
+
+            TreeControlNode node = control.findNode(name);
+
+            if (node != null){
+                getServlet().log("Found Node: " + name);
+                node.setExpanded(!node.isExpanded());
+            }
+        }else{
+            getServlet().log("tree param is null");
+        }
+
+        // Handle a select item event
+        name = request.getParameter("select");
+        if (name != null) {
+            getServlet().log("Select event on " + name);
+            control.selectNode(name);
+        }
+
+        // Forward back to the test page
+        return (mapping.findForward("Tree Control Test"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/AddConnectorAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/AddConnectorAction.java
new file mode 100644
index 0000000..4f09338
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/AddConnectorAction.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.connector;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+
+/**
+ * The <code>Action</code> that sets up <em>Add Connector</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class AddConnectorAction extends Action {
+    
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+    ActionForm form,
+    HttpServletRequest request,
+    HttpServletResponse response)
+    throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        
+        // the service Name is needed to retrieve the engine mBean to
+        // which the new connector mBean will be added.
+        String serviceName = request.getParameter("select");
+        
+        // Fill in the form values for display and editing
+        ConnectorForm connectorFm = new ConnectorForm();
+        session.setAttribute("connectorForm", connectorFm);
+        connectorFm.setAdminAction("Create");
+        connectorFm.setObjectName("");
+        connectorFm.setConnectorName("");
+        String type = request.getParameter("type");
+        if (type == null)
+            type = "HTTP";    // default type is HTTP
+        connectorFm.setConnectorType(type);
+        connectorFm.setServiceName(serviceName);
+        if ("HTTPS".equalsIgnoreCase(type)) {
+            connectorFm.setScheme("https");
+        } else {
+            connectorFm.setScheme("http");       
+        }
+        connectorFm.setAcceptCountText("10");
+        connectorFm.setCompression("off");
+        connectorFm.setConnLingerText("-1");
+        connectorFm.setConnTimeOutText("60000");
+        connectorFm.setConnUploadTimeOutText("300000");
+        connectorFm.setBufferSizeText("2048");
+        connectorFm.setDisableUploadTimeout("false");
+        connectorFm.setEnableLookups("true");
+        connectorFm.setAddress("");
+        connectorFm.setPortText("");
+        connectorFm.setRedirectPortText("-1");
+        connectorFm.setMinProcessorsText("5");
+        connectorFm.setMaxProcessorsText("20");
+        connectorFm.setMaxKeepAliveText("100");
+        connectorFm.setMaxSpare("50");
+        connectorFm.setMaxThreads("200");
+        connectorFm.setMinSpare("4");
+        connectorFm.setThreadPriority(String.valueOf(Thread.NORM_PRIORITY));
+        connectorFm.setSecure("false");
+        connectorFm.setTcpNoDelay("true");
+        connectorFm.setXpoweredBy("false");
+
+        //supported only by HTTPS
+        connectorFm.setAlgorithm("SunX509");
+        connectorFm.setClientAuthentication("false");
+        connectorFm.setCiphers("");
+        connectorFm.setKeyStoreFileName("");
+        connectorFm.setKeyStorePassword("");
+        connectorFm.setKeyStoreType("JKS");
+        connectorFm.setSslProtocol("TLS");
+                       
+        // supported only by Coyote connectors
+        connectorFm.setProxyName("");
+        connectorFm.setProxyPortText("0");        
+        
+        connectorFm.setBooleanVals(Lists.getBooleanValues());                
+        connectorFm.setClientAuthVals(Lists.getClientAuthValues());
+        
+        String schemeTypes[]= new String[3];
+        schemeTypes[0] = "HTTP";
+        schemeTypes[1] = "HTTPS";                
+        schemeTypes[2] = "AJP";
+        
+        ArrayList types = new ArrayList();    
+        // the first element in the select list should be the type selected
+        types.add(new LabelValueBean(type,
+                "AddConnector.do?select=" + 
+                URLEncoder.encode(serviceName,TomcatTreeBuilder.URL_ENCODING) 
+                + "&type=" + type));        
+         for (int i=0; i< schemeTypes.length; i++) {
+            if (!type.equalsIgnoreCase(schemeTypes[i])) {
+                types.add(new LabelValueBean(schemeTypes[i],
+                "AddConnector.do?select=" + 
+                URLEncoder.encode(serviceName,TomcatTreeBuilder.URL_ENCODING)
+                + "&type=" + schemeTypes[i]));        
+            }
+        }
+        connectorFm.setConnectorTypeVals(types);
+        
+        // Forward to the connector display page
+        return (mapping.findForward("Connector"));
+        
+    }        
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/ConnectorForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/ConnectorForm.java
new file mode 100644
index 0000000..f671e64
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/ConnectorForm.java
@@ -0,0 +1,1205 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.connector;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.net.InetAddress;
+import java.util.List;
+
+/**
+ * Form bean for the connector page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class ConnectorForm extends ActionForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    
+     /**
+     * The administrative action represented by this form.
+     */
+    private String adminAction = "Edit";
+
+    /**
+     * The object name of the Connector this bean refers to.
+     */
+    private String objectName = null;
+    
+    /**
+     * The object name of the service this connector belongs to.
+     */
+    private String serviceName = null;
+   
+    /**
+     * The text for the scheme.
+     */
+    private String scheme = null;
+
+    /**
+     * The text for the connector type. 
+     * Specifies if it is a CoyoteConnector or AJP13Connector etc.
+     */
+    private String connectorType = null;    
+    
+     /**
+     * The text for the node label.
+     */
+    private String nodeLabel = null;
+    
+    /**
+     * The text for the accept Count.
+     */
+    private String acceptCountText = null;
+    
+    /**
+     * The text for the algorithm.
+     */
+    private String algorithm = null;
+    
+    /**
+     * The text for the ciphers.
+     */
+    private String ciphers = null;
+    
+    /**
+     * The text for the Connection Linger.
+     */
+    private String connLingerText = null;
+    
+    /**
+     * The text for the Connection Time Out.
+     */
+    private String connTimeOutText = null;
+    
+    /**
+     * The text for the Connection Upload Time Out.
+     */
+    private String connUploadTimeOutText = null;
+    
+    /**
+     * The text for the buffer size.
+     */
+    private String bufferSizeText = null;
+    
+    /**
+     * The value of disable upload timeout.
+     */
+    private String disableUploadTimeout = "false";
+    
+    /**
+     * The value of enable Lookups.
+     */
+    private String enableLookups = "false";
+    
+    /**
+     * The value of compression.
+     */
+    private String compression = "off";
+    
+    /**
+     * The text for the address.
+     */
+    private String address = null;
+    
+    /**
+     * The text for the minProcessors.
+     */
+    private String minProcessorsText = null;
+    
+    /**
+     * The text for the max Processors.
+     */
+    private String maxProcessorsText = null;
+    
+    /**
+     * The text for the maxKeepAlive.
+     */
+    private String maxKeepAliveText = null;
+    
+    /**
+     * The text for the maxSpare.
+     */
+    private String maxSpare = null;
+    
+    /**
+     * The text for the maxThreads.
+     */
+    private String maxThreads = null;
+    
+    /**
+     * The text for the minSpare.
+     */
+    private String minSpare = null;
+
+    /**
+     * The text for the threadPriority.
+     */
+    private String threadPriority = null;
+    
+    /**
+     * The text for the URIEncoding.
+     */
+    private String uriEncodingText = null;
+    
+    /**
+     * The value of useBodyEncodingForURI.
+     */
+    private String useBodyEncodingForURI = "false";
+
+    /**
+     * The value of allowTrace.
+     */
+    private String allowTrace = "false";
+
+    /**
+     * The text for the port.
+     */
+    private String portText = null;
+    
+    /**
+     * The text for the redirect port.
+     */
+    private String redirectPortText = null;
+    
+    /**
+     * The text for the proxyName.
+     */
+    private String proxyName = null;
+    
+    /**
+     * The text for the proxy Port Number.
+     */
+    private String proxyPortText = null;
+    
+    
+    /**
+     * The text for the connectorName.
+     */
+    private String connectorName = null;
+        
+    /**
+     * Whether client authentication is supported.
+     */
+    private String clientAuthentication = "false";
+        
+    /**
+     * The keyStore Filename.
+     */
+    private String keyStoreFileName = null;
+        
+    /**
+     * The keyStore Password.
+     */
+    private String keyStorePassword = null;
+    
+    /**
+     * The keyStore Type.
+     */
+    private String keyStoreType = null;
+
+    /**
+     * The text for the Ssl Protocol.
+     */
+    private String sslProtocol= null;
+    
+    /*
+     * Represent boolean (true, false) values for enableLookups etc.
+     */    
+    private List booleanVals = null;
+
+    /*
+     * Represent supported connector types.
+     */    
+    private List connectorTypeVals = null;
+
+    /**
+     * Represent supported clientAuth values.
+     */
+    private List clientAuthVals = null;
+
+    /**
+     * The value of secure.
+     */
+    private String secure = "false";
+    /**
+     * The value of tcpNoDelay.
+     */
+    private String tcpNoDelay = "true";
+    
+    /**
+     * The value of xpoweredBy.
+     */
+    private String xpoweredBy = "false";
+    
+    // ------------------------------------------------------------- Properties
+    
+   /**
+     * Return the administrative action represented by this form.
+     */
+    public String getAdminAction() {
+
+        return this.adminAction;
+
+    }
+
+
+    /**
+     * Set the administrative action represented by this form.
+     */
+    public void setAdminAction(String adminAction) {
+
+        this.adminAction = adminAction;
+
+    }
+
+    /**
+     * Return the object name of the Connector this bean refers to.
+     */
+    public String getObjectName() {
+
+        return this.objectName;
+
+    }
+
+
+    /**
+     * Set the object name of the Connector this bean refers to.
+     */
+    public void setObjectName(String objectName) {
+
+        this.objectName = objectName;
+
+    }
+    
+      /**
+     * Return the object name of the service this connector belongs to.
+     */
+    public String getServiceName() {
+
+        return this.serviceName;
+
+    }
+
+
+    /**
+     * Set the object name of the Service this connector belongs to.
+     */
+    public void setServiceName(String serviceName) {
+
+        this.serviceName = serviceName;
+
+    }
+    
+    /**
+     * Return the Scheme.
+     */
+    public String getScheme() {
+        
+        return this.scheme;
+        
+    }
+    
+    /**
+     * Set the Scheme.
+     */
+    public void setScheme(String scheme) {
+        
+        this.scheme = scheme;
+        
+    }
+    
+    /**
+     * Return the Connector type.
+     */
+    public String getConnectorType() {
+        
+        return this.connectorType;
+        
+    }
+    
+    /**
+     * Set the Connector type.
+     */
+    public void setConnectorType(String connectorType) {
+        
+        this.connectorType = connectorType;
+        
+    }
+    
+    /**
+     * Return the label of the node that was clicked.
+     */
+    public String getNodeLabel() {
+        
+        return this.nodeLabel;
+        
+    }
+    
+    /**
+     * Set the node label.
+     */
+    public void setNodeLabel(String nodeLabel) {
+        
+        this.nodeLabel = nodeLabel;
+        
+    }
+    
+    /**
+     * Return the acceptCountText.
+     */
+    public String getAcceptCountText() {
+        
+        return this.acceptCountText;
+        
+    }
+    
+    
+    /**
+     * Set the acceptCountText.
+     */
+    
+    public void setAcceptCountText(String acceptCountText) {
+        
+        this.acceptCountText = acceptCountText;
+        
+    }
+    
+    /**
+     * Return the algorithm.
+     */
+    public String getAlgorithm() {
+        
+        return this.algorithm;
+        
+    }
+    
+    
+    /**
+     * Set the algorithm.
+     */
+    
+    public void setAlgorithm(String algorithm) {
+        
+        this.algorithm = algorithm;
+        
+    }
+    
+    /**
+     * Return the ciphers.
+     */
+    public String getCiphers() {
+        
+        return this.ciphers;
+        
+    }
+    
+    /**
+     * Set the ciphers.
+     */
+    
+    public void setCiphers(String ciphers) {
+        
+        this.ciphers = ciphers;
+        
+    }
+    
+    /**
+     * Return the connLingerText.
+     */
+    public String getConnLingerText() {
+        
+        return this.connLingerText;
+        
+    }
+    
+    /**
+     * Set the connLingerText.
+     */
+    
+    public void setConnLingerText(String connLingerText) {
+        
+        this.connLingerText = connLingerText;
+        
+    }
+    
+    /**
+     * Return the connTimeOutText.
+     */
+    public String getConnTimeOutText() {
+        
+        return this.connTimeOutText;
+        
+    }
+    
+    /**
+     * Set the connTimeOutText.
+     */
+    
+    public void setConnTimeOutText(String connTimeOutText) {
+        
+        this.connTimeOutText = connTimeOutText;
+        
+    }
+       
+    /**
+     * Return the connUploadTimeOutText.
+     */
+    public String getConnUploadTimeOutText() {
+        
+        return this.connUploadTimeOutText;
+        
+    }
+    
+    /**
+     * Set the connUploadTimeOutText.
+     */
+    
+    public void setConnUploadTimeOutText(String connUploadTimeOutText) {
+        
+        this.connUploadTimeOutText = connUploadTimeOutText;
+        
+    }
+    /**
+     * Return the bufferSizeText.
+     */
+    public String getBufferSizeText() {
+        
+        return this.bufferSizeText;
+        
+    }
+    
+    /**
+     * Set the bufferSizeText.
+     */
+    
+    public void setBufferSizeText(String bufferSizeText) {
+        
+        this.bufferSizeText = bufferSizeText;
+        
+    }
+    
+    /**
+     * Return the address.
+     */
+    public String getAddress() {
+        
+        return this.address;
+        
+    }
+    
+    /**
+     * Set the address.
+     */
+    
+    public void setAddress(String address) {
+        
+        this.address = address;
+        
+    }
+    
+    
+    /**
+     * Return the proxy Name.
+     */
+    public String getProxyName() {
+        
+        return this.proxyName;
+        
+    }
+    
+    /**
+     * Set the proxy Name.
+     */
+    
+    public void setProxyName(String proxyName) {
+        
+        this.proxyName = proxyName;
+        
+    }
+    
+    /**
+     * Return the proxy Port NumberText.
+     */
+    public String getProxyPortText() {
+        
+        return this.proxyPortText;
+        
+    }
+    
+    /**
+     * Set the proxy Port NumberText.
+     */
+    
+    public void setProxyPortText(String proxyPortText) {
+        
+        this.proxyPortText = proxyPortText;
+        
+    }
+
+   /**
+     * Return the true/false value of client authentication.
+     */
+    public String getClientAuthentication() {
+
+        return this.clientAuthentication;
+
+    }
+
+
+    /**
+     * Set whether client authentication is supported or not.
+     */
+    public void setClientAuthentication(String clientAuthentication) {
+
+        this.clientAuthentication = clientAuthentication;
+
+    }
+
+    /**
+     * Return the object name of the service this connector belongs to.
+     */
+    public String getKeyStoreFileName() {
+
+        return this.keyStoreFileName;
+
+    }
+
+
+    /**
+     * Set the object name of the Service this connector belongs to.
+     */
+    public void setKeyStoreFileName(String keyStoreFileName) {
+
+        this.keyStoreFileName = keyStoreFileName;
+
+    }
+
+    /**
+     * Return the object name of the service this connector belongs to.
+     */
+    public String getKeyStorePassword() {
+
+        return this.keyStorePassword;
+
+    }
+
+
+    /**
+     * Set the object name of the Service this connector belongs to.
+     */
+    public void setKeyStorePassword(String keyStorePassword) {
+
+        this.keyStorePassword = keyStorePassword;
+
+    }
+
+    /**
+     * Return the keystore type.
+     */
+    public String getKeyStoreType() {
+
+        return this.keyStoreType;
+
+    }
+
+
+    /**
+     * Set the keystore type.
+     */
+    public void setKeyStoreType(String keyStoreType) {
+
+        this.keyStoreType = keyStoreType;
+
+    }
+    /**
+     * Return the sslProtocol
+     */
+    public String getSslProtocol() {
+
+        return this.sslProtocol;
+
+    }
+
+
+    /**
+     * Set the sslProtocol.
+     */
+    public void setSslProtocol(String sslProtocol) {
+
+        this.sslProtocol = sslProtocol;
+
+    }
+    
+    /**
+     * Return the Enable lookup Text.
+     */
+    
+    public String getEnableLookups() {
+        
+        return this.enableLookups;
+        
+    }
+    
+    /**
+     * Set the Enable Lookup Text.
+     */
+    public void setEnableLookups(String enableLookups) {
+        
+        this.enableLookups = enableLookups;
+        
+    }
+    
+    /**
+     * Return the disableUploadTimeout.
+     */
+    
+    public String getDisableUploadTimeout() {
+        
+        return this.disableUploadTimeout;
+        
+    }
+    
+    /**
+     * Set the disableUploadTimeout.
+     */
+    public void setDisableUploadTimeout(String disableUploadTimeout) {
+        
+        this.disableUploadTimeout = disableUploadTimeout;
+        
+    }
+    
+    /**
+     * Return the compression Text.
+     */
+    
+    public String getCompression() {
+        
+        return this.compression;
+        
+    }
+    
+    /**
+     * Set the Compression Text.
+     */
+    public void setCompression(String compression) {
+        
+        this.compression = compression;
+        
+    }
+    
+    /**
+     * Return the booleanVals.
+     */
+    public List getBooleanVals() {
+        
+        return this.booleanVals;
+        
+    }
+    
+    /**
+     * Set the debugVals.
+     */
+    public void setBooleanVals(List booleanVals) {
+        
+        this.booleanVals = booleanVals;
+        
+    }
+
+    /**
+     * Return the clientAuth values.
+     */
+    public List getClientAuthVals() {
+        return clientAuthVals;
+    }
+    /**
+     * Set the clientAuth vaues.
+     */
+    public void setClientAuthVals(List clientAuthVals) {
+        this.clientAuthVals = clientAuthVals;
+    }
+    
+    /**
+     * Return the min Processors Text.
+     */
+    public String getMinProcessorsText() {
+        
+        return this.minProcessorsText;
+        
+    }
+    
+    /**
+     * Set the minProcessors Text.
+     */
+    public void setMinProcessorsText(String minProcessorsText) {
+        
+        this.minProcessorsText = minProcessorsText;
+        
+    }
+    
+    /**
+     * Return the max processors Text.
+     */
+    public String getMaxProcessorsText() {
+        
+        return this.maxProcessorsText;
+        
+    }
+    
+    /**
+     * Set the Max Processors Text.
+     */
+    public void setMaxProcessorsText(String maxProcessorsText) {
+        
+        this.maxProcessorsText = maxProcessorsText;
+        
+    }
+    
+    /**
+     * Return the maxKeepAliveText.
+     */
+    public String getMaxKeepAliveText() {
+        
+        return this.maxKeepAliveText;
+        
+    }
+    
+    /**
+     * Set the maxKeepAliveText.
+     */
+    
+    public void setMaxKeepAliveText(String maxKeepAliveText) {
+        
+        this.maxKeepAliveText = maxKeepAliveText;
+        
+    }
+    
+    /**
+     * Return the maxSpare.
+     */
+    public String getMaxSpare() {
+        
+        return this.maxSpare;
+        
+    }
+    
+    /**
+     * Set the maxSpare.
+     */
+    
+    public void setMaxSpare(String maxSpare) {
+        
+        this.maxSpare = maxSpare;
+        
+    } 
+    
+    /**
+     * Return the maxThreads.
+     */
+    public String getMaxThreads() {
+        
+        return this.maxThreads;
+        
+    }
+    
+    /**
+     * Set the maxThreads.
+     */
+    
+    public void setMaxThreads(String maxThreads) {
+        
+        this.maxThreads = maxThreads;
+        
+    } 
+    
+    /**
+     * Return the minSpare.
+     */
+    public String getMinSpare() {
+        
+        return this.minSpare;
+        
+    }
+    
+    /**
+     * Set the minSpare.
+     */
+    
+    public void setMinSpare(String minSpare) {
+        
+        this.minSpare = minSpare;
+        
+    }
+
+    /**
+     * Return the threadPriority.
+     */
+    public String getThreadPriority() {
+
+      return this.threadPriority;
+
+    }
+
+    /**
+     * Set the threadPriority.
+     */
+    
+    public void setThreadPriority(String threadPriority) {
+      
+      this.threadPriority = threadPriority;
+    
+    }
+    
+    /**
+     * Return the URIEncoding text.
+     */
+    public String getURIEncodingText() {
+        
+        return this.uriEncodingText;
+        
+    }
+    
+    /**
+     * Set the URIEncoding Text.
+     */
+    public void setURIEncodingText(String uriEncodingText) {
+        
+        this.uriEncodingText = uriEncodingText;
+        
+    }
+    
+    /**
+     * Return the useBodyEncodingForURI Text.
+     */
+    public String getUseBodyEncodingForURIText() {
+        
+        return this.useBodyEncodingForURI;
+        
+    }
+    
+    /**
+     * Set the useBodyEncodingForURI Text.
+     */
+    public void setUseBodyEncodingForURIText(String useBodyEncodingForURI) {
+        
+        this.useBodyEncodingForURI = useBodyEncodingForURI;
+        
+    }    
+    
+    /**
+     * Return the allowTrace Text.
+     */
+    public String getAllowTraceText() {
+        
+        return this.allowTrace;
+        
+    }
+    
+    /**
+     * Set the allowTrace Text.
+     */
+    public void setAllowTraceText(String allowTrace) {
+        
+        this.allowTrace = allowTrace;
+        
+    }    
+    
+    /**
+     * Return the port text.
+     */
+    public String getPortText() {
+        
+        return this.portText;
+        
+    }
+    
+    /**
+     * Set the port Text.
+     */
+    public void setPortText(String portText) {
+        
+        this.portText = portText;
+        
+    }
+    
+    
+    /**
+     * Return the port.
+     */
+    public String getRedirectPortText() {
+        
+        return this.redirectPortText;
+        
+    }
+    
+    /**
+     * Set the Redirect Port Text.
+     */
+    public void setRedirectPortText(String redirectPortText) {
+        
+        this.redirectPortText = redirectPortText;
+        
+    }
+    
+    /**
+     * Return the Service Name.
+     */
+    public String getConnectorName() {
+        
+        return this.connectorName;
+        
+    }
+    
+    /**
+     * Set the Service Name.
+     */
+    public void setConnectorName(String connectorName) {
+        
+        this.connectorName = connectorName;
+        
+    }
+    
+    /**
+     * Return the connectorTypeVals.
+     */
+    public List getConnectorTypeVals() {
+        
+        return this.connectorTypeVals;
+        
+    }
+    
+    /**
+     * Set the connectorTypeVals.
+     */
+    public void setConnectorTypeVals(List connectorTypeVals) {
+        
+        this.connectorTypeVals = connectorTypeVals;
+        
+    }
+    
+     /**
+     * Return the secure Text.
+     */
+    public String getSecure() {
+        
+        return this.secure;
+        
+    }
+    
+    /**
+     * Set the secure Text.
+     */
+    public void setSecure(String secure) {
+        
+        this.secure = secure;
+        
+    }    
+    
+    /**
+     * Return the tcpNoDelay Text.
+     */
+    public String getTcpNoDelay() {
+        
+        return this.tcpNoDelay;
+        
+    }
+    
+    /**
+     * Set the tcpNoDelay Text.
+     */
+    public void setTcpNoDelay(String tcpNoDelay) {
+        
+        this.tcpNoDelay = tcpNoDelay;
+        
+    }   
+    
+    /**
+     * Return the xpoweredBy Text.
+     */
+    public String getXpoweredBy() {
+        
+        return this.xpoweredBy;
+        
+    }
+    
+    /**
+     * Set the xpoweredBy Text.
+     */
+    public void setXpoweredBy(String xpoweredBy) {
+        
+        this.xpoweredBy = xpoweredBy;
+        
+    }
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+    
+        this.objectName = null;
+        this.connectorType = null;
+        this.portText = null;
+        this.acceptCountText = null;
+        this.connLingerText = null;
+        this.connTimeOutText = null;
+        this.connUploadTimeOutText = null;
+        this.bufferSizeText = null;
+        this.address = null;
+        this.enableLookups = "false";
+        this.compression = "off";
+        this.minProcessorsText = null;
+        this.maxProcessorsText = null;
+        this.maxKeepAliveText = null;
+        this.maxSpare = null;
+        this.maxThreads = null;
+        this.minSpare = null;
+        this.threadPriority = null;
+        this.uriEncodingText = null;
+        this.useBodyEncodingForURI = "false";
+        this.allowTrace = "false";
+        this.portText = null;
+        this.redirectPortText = null;
+        this.proxyName = null;
+        this.proxyPortText = null;
+        this.keyStoreFileName = null;
+        this.keyStorePassword = null;        
+        this.clientAuthentication = "false";
+        this.secure = "false";
+        this.tcpNoDelay = "false";
+        this.xpoweredBy = "false";
+        
+    }
+    
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    private ActionErrors errors;
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+    
+        errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        // front end validation when save is clicked.
+        //if (submit != null) {
+  
+            /* The IP address can also be null -- which means open the
+             server socket on *all* IP addresses for this host */
+            if ((address.length() > 0) && !address.equalsIgnoreCase(" ")) {
+                try {
+                    InetAddress.getByName(address);
+                } catch (Exception e) {
+                    errors.add("address", new ActionError("error.address.invalid"));
+                }
+            } else {
+                address = " ";
+            }
+            
+            /* ports */
+            numberCheck("portNumber",  portText, true, 1, 65535);
+            numberCheck("redirectPortText",  redirectPortText, true, -1, 65535);
+            
+            /* processors*/
+            //numberCheck("minProcessorsText",  minProcessorsText, true, 1, 512);
+            //try {
+                // if min is a valid integer, then check that max >= min
+                //int min = Integer.parseInt(minProcessorsText);
+                //numberCheck("maxProcessorsText",  maxProcessorsText, true, min, 512);
+            //} catch (Exception e) {
+                // check for the complete range
+                //numberCheck("maxProcessorsText",  maxProcessorsText, true, 1, 512);
+            //}
+            
+            // proxy                  
+            if ((proxyName!= null) && (proxyName.length() > 0)) {
+                try {
+                    InetAddress.getByName(proxyName);
+                } catch (Exception e) {
+                    errors.add("proxyName", new ActionError("error.proxyName.invalid"));
+                }
+            }   
+            
+            // supported only by Coyote HTTP and HTTPS connectors
+            if (!("AJP".equalsIgnoreCase(connectorType))) {
+                numberCheck("acceptCountText", acceptCountText, true, 0, 128);
+                //numberCheck("connTimeOutText", connTimeOutText, true, -1, 60000);
+                numberCheck("bufferSizeText", bufferSizeText, true, 1, 8192);
+                numberCheck("proxyPortText",  proxyPortText, true, 0, 65535);  
+            }
+        //}
+        
+        return errors;
+    }
+    
+    /*
+     * Helper method to check that it is a required number and
+     * is a valid integer within the given range. (min, max).
+     *
+     * @param  field  The field name in the form for which this error occured.
+     * @param  numText  The string representation of the number.
+     * @param rangeCheck  Boolean value set to true of reange check should be performed.
+     *
+     * @param  min  The lower limit of the range
+     * @param  max  The upper limit of the range
+     *
+     */
+    
+    public void numberCheck(String field, String numText, boolean rangeCheck,
+    int min, int max) {
+        
+        /* Check for 'is required' */
+        if ((numText == null) || (numText.length() < 1)) {
+            errors.add(field, new ActionError("error."+field+".required"));
+        } else {
+            
+        /*check for 'must be a number' in the 'valid range'*/
+            try {
+                int num = Integer.parseInt(numText);
+                // perform range check only if required
+                if (rangeCheck) {
+                    if ((num < min) || (num > max ))
+                        errors.add( field,
+                        new ActionError("error."+ field +".range"));
+                }
+            } catch (NumberFormatException e) {
+                errors.add(field,
+                new ActionError("error."+ field + ".format"));
+            }
+        }
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/ConnectorsForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/ConnectorsForm.java
new file mode 100644
index 0000000..41d4e97
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/ConnectorsForm.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.connector;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for deleting connectors.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class ConnectorsForm extends ActionForm {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the connectors to be deleted.
+     */
+    private String connectors[] = new String[0];
+
+    public String[] getConnectors() {
+        return (this.connectors);
+    }
+
+    public void setConnectors(String connectors[]) {
+        this.connectors = connectors;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.connectors = new String[0];
+
+    }
+        
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/DeleteConnectorAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/DeleteConnectorAction.java
new file mode 100644
index 0000000..0ea2651
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/DeleteConnectorAction.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.connector;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.TreeSet;
+import java.util.Set;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * The <code>Action</code> that sets up <em>Delete Connectors</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteConnectorAction extends Action {
+        
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    
+    
+    
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+    ActionForm form,
+    HttpServletRequest request,
+    HttpServletResponse response)
+    throws IOException, ServletException {
+        
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);    
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        String domain = null;
+        // Set up a form bean containing the currently selected
+        // objects to be deleted
+        ConnectorsForm connectorsForm = new ConnectorsForm();
+        String select = request.getParameter("select");
+        if (select != null) {
+            String connectors[] = new String[1];
+            connectors[0] = select;
+            connectorsForm.setConnectors(connectors);
+                        
+            // get the service Name this selected host belongs to
+            try {
+                domain = (new ObjectName(select)).getDomain();
+            } catch (Exception e) {
+                throw new ServletException
+                ("Error extracting service name from the connector to be deleted", e);
+            }        
+        }
+        request.setAttribute("connectorsForm", connectorsForm);
+        
+        // Accumulate a list of all available connectors
+        ArrayList list = new ArrayList();
+         try {
+            String pattern = domain + TomcatTreeBuilder.CONNECTOR_TYPE +
+                TomcatTreeBuilder.WILDCARD;          
+            Iterator items =
+                mBServer.queryNames(new ObjectName(pattern), null).iterator();
+            while (items.hasNext()) {
+                Object item = items.next();
+                list.add(item.toString());
+            }
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.select"));
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.select"));
+            return (null);
+        }      
+        Collections.sort(list);
+        request.setAttribute("connectorsList", list);
+        
+        // Forward to the list display page
+        return (mapping.findForward("Connectors"));        
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/DeleteConnectorsAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/DeleteConnectorsAction.java
new file mode 100644
index 0000000..b71f9e6
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/DeleteConnectorsAction.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.connector;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ObjectInstance;
+import javax.management.modelmbean.ModelMBean;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+/**
+ * The <code>Action</code> that completes <em>Delete Connectors</em>
+ * transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteConnectorsAction extends Action {
+
+
+    /**
+     * Signature for the <code>removeConnector</code> operation.
+     */
+    private String removeConnectorTypes[] =
+    { "java.lang.String",      // Object name
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        
+        // Look up the components we will be using as needed
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Delete the specified Connectors
+        String connectors[]  = ((ConnectorsForm) form).getConnectors();
+        String values[] = new String[1];
+        String operation = "removeConnector";
+        try {
+
+            // Look up our tree control data structure
+            TreeControl control = (TreeControl)
+                session.getAttribute("treeControlTest");
+                
+            // Look up our MBeanFactory MBean
+            ObjectName fname = null;
+            String domain = null;
+            TreeControlNode node = null;
+
+            // Remove the specified connectors
+            for (int i = 0; i < connectors.length; i++) {
+                values[0] = connectors[i];
+                if (control != null) {
+                    control.selectNode(null);
+                    node = control.findNode(connectors[i]);
+                    domain = node.getDomain();
+                    // Look up our MBeanFactory MBean
+                    fname = TomcatTreeBuilder.getMBeanFactory();
+                    mBServer.invoke(fname, operation,
+                                values, removeConnectorTypes);
+                    if (node != null) {
+                        node.remove();
+                    } else {
+                        getServlet().log("Missing TreeControlNode for " +
+                                         connectors[i]);
+                    }
+                } else {
+                    getServlet().log("Missing TreeControl attribute");
+                }
+            }
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      operation), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      operation));
+            return (null);
+
+        }
+
+        // Report successful completion of this transaction
+        return (mapping.findForward("Save Successful"));
+
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/EditConnectorAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/EditConnectorAction.java
new file mode 100644
index 0000000..3055843
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/EditConnectorAction.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.connector;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * The <code>Action</code> that sets up <em>Edit Connector</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class EditConnectorAction extends Action {
+    
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Set up the object names of the MBeans we are manipulating
+        ObjectName cname = null;
+        StringBuffer sb = null;
+        try {
+            cname = new ObjectName(request.getParameter("select"));
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.connectorName.bad",
+                                     request.getParameter("select"));
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+
+        // Fill in the form values for display and editing
+        ConnectorForm connectorFm = new ConnectorForm();
+        session.setAttribute("connectorForm", connectorFm);
+        connectorFm.setAdminAction("Edit");
+        connectorFm.setObjectName(cname.toString());
+        sb = new StringBuffer();
+        sb.append(resources.getMessage(locale, "server.service.treeBuilder.connector"));
+        sb.append(" (");
+        sb.append(cname.getKeyProperty("port"));
+        sb.append(")");
+        connectorFm.setNodeLabel(sb.toString());
+        connectorFm.setBooleanVals(Lists.getBooleanValues());        
+        connectorFm.setClientAuthVals(Lists.getClientAuthValues());
+        
+        String attribute = null;
+        try {
+
+            // Copy scalar properties
+            // General properties
+            attribute = "scheme";
+            String scheme = (String) mBServer.getAttribute(cname, attribute);
+            connectorFm.setScheme(scheme);
+
+            attribute = "protocolHandlerClassName";
+            String handlerClassName = 
+                (String) mBServer.getAttribute(cname, attribute);
+            int period = handlerClassName.lastIndexOf('.');
+            String connType = handlerClassName.substring(period + 1);
+            String connectorType = "HTTPS";
+            if ("JkCoyoteHandler".equalsIgnoreCase(connType)) {
+                connectorType = "AJP";
+            } else if ("Http11Protocol".equalsIgnoreCase(connType) && 
+                      ("http".equalsIgnoreCase(scheme))) {
+                connectorType = "HTTP";
+            }             
+            connectorFm.setConnectorType(connectorType);            
+            
+            attribute = "acceptCount";
+            connectorFm.setAcceptCountText
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));          
+            attribute = "compression";
+            connectorFm.setCompression
+                ((String) mBServer.getAttribute(cname, attribute));          
+            attribute = "connectionLinger";
+            connectorFm.setConnLingerText
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));            
+            attribute = "connectionTimeout";
+            connectorFm.setConnTimeOutText
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));             
+            attribute = "connectionUploadTimeout";
+            connectorFm.setConnUploadTimeOutText
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));              
+            attribute = "disableUploadTimeout";
+            connectorFm.setDisableUploadTimeout
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));       
+            attribute = "bufferSize";
+            connectorFm.setBufferSizeText
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));            
+            attribute = "enableLookups";
+            connectorFm.setEnableLookups
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));            
+            attribute = "address";
+            connectorFm.setAddress
+                ((String) mBServer.getAttribute(cname, attribute));          
+            attribute = "maxKeepAliveRequests";
+            connectorFm.setMaxKeepAliveText
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));       
+            attribute = "maxSpareThreads";
+            connectorFm.setMaxSpare
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));         
+            attribute = "maxThreads";
+            connectorFm.setMaxThreads
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));       
+            attribute = "minSpareThreads";
+            connectorFm.setMinSpare
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));        
+            attribute = "threadPriority";
+            connectorFm.setThreadPriority
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));
+            attribute = "secure";
+            connectorFm.setSecure
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            attribute = "tcpNoDelay";
+            connectorFm.setTcpNoDelay
+                (String.valueOf(mBServer.getAttribute(cname, attribute)));
+            attribute = "xpoweredBy";
+            connectorFm.setXpoweredBy
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            attribute = "URIEncoding";
+            connectorFm.setURIEncodingText
+                ((String) mBServer.getAttribute(cname, attribute));
+            attribute = "useBodyEncodingForURI";
+            connectorFm.setUseBodyEncodingForURIText
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            attribute = "allowTrace";
+            connectorFm.setAllowTraceText
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+          
+            // Ports
+            attribute = "port";
+            connectorFm.setPortText
+                (((Integer) mBServer.getAttribute(cname, attribute)).toString());            
+            attribute = "redirectPort";
+            connectorFm.setRedirectPortText
+                (((Integer) mBServer.getAttribute(cname, attribute)).toString());            
+            
+            // Supported by HTTP and HTTPS only
+            if (!("AJP".equalsIgnoreCase(connectorType))) {
+                attribute = "proxyName";
+                connectorFm.setProxyName
+                    ((String) mBServer.getAttribute(cname, attribute));
+                attribute = "proxyPort";
+                connectorFm.setProxyPortText
+                    (((Integer) mBServer.getAttribute(cname, attribute)).toString());            
+            }
+            
+            if ("HTTPS".equalsIgnoreCase(connectorType)) {
+                // Initialize rest of variables. 
+                // These are set only for SSL connectors.
+                attribute = "algorithm";
+                connectorFm.setAlgorithm
+                    ((String) mBServer.getAttribute(cname, attribute));
+                attribute = "clientAuth";
+                connectorFm.setClientAuthentication
+                    (((String) mBServer.getAttribute(cname, attribute)));
+                attribute = "ciphers";
+                connectorFm.setCiphers
+                    ((String) mBServer.getAttribute(cname, attribute));   
+                attribute = "keystoreFile";
+                connectorFm.setKeyStoreFileName
+                    ((String) mBServer.getAttribute(cname, attribute));
+                attribute = "keystorePass";
+                connectorFm.setKeyStorePassword
+                    ((String) mBServer.getAttribute(cname, attribute));     
+                attribute = "keystoreType";
+                connectorFm.setKeyStoreType
+                    ((String) mBServer.getAttribute(cname, attribute));   
+                attribute = "sslProtocol";
+                connectorFm.setSslProtocol
+                    ((String) mBServer.getAttribute(cname, attribute));          
+            }     
+                
+                        
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+            return (null);
+        }
+        
+        // Forward to the connector display page
+        return (mapping.findForward("Connector"));
+        
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/SaveConnectorAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/SaveConnectorAction.java
new file mode 100644
index 0000000..8eb7619
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/connector/SaveConnectorAction.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.connector;
+
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+/**
+ * The <code>Action</code> that completes <em>Add Connector</em> and
+ * <em>Edit Connector</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveConnectorAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Signature for the <code>createStandardConnector</code> operation.
+     */
+    private String createStandardConnectorTypes[] =
+    { "java.lang.String",    // parent
+      "java.lang.String",    // address
+      "int"                  // port      
+    };
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }    
+        
+        // Identify the requested action
+        ConnectorForm cform = (ConnectorForm) form;
+        String adminAction = cform.getAdminAction();
+        String cObjectName = cform.getObjectName();
+        String connectorType = cform.getConnectorType();
+        ObjectName coname = null;
+
+        // Perform a "Create Connector" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            String operation = null;
+            Object values[] = null;
+
+            try {
+                // get service name which is same as domain
+                String serviceName = cform.getServiceName();
+                ObjectName soname = new ObjectName(serviceName);
+                String domain = soname.getDomain();
+                StringBuffer sb = new StringBuffer(domain);
+                StringBuffer searchSB = new StringBuffer("*");
+                sb.append(TomcatTreeBuilder.CONNECTOR_TYPE);
+                searchSB.append(TomcatTreeBuilder.CONNECTOR_TYPE);
+                sb.append(",port=" + cform.getPortText());
+                searchSB.append(",port=" + cform.getPortText());
+                
+                ObjectName search = new ObjectName(searchSB.toString()+",*");
+                
+                String address = cform.getAddress();
+                if ((address!=null) && (address.length()>0) && 
+                        (!address.equalsIgnoreCase(" "))) {
+                    sb.append(",address=" + address);
+                } else {
+                    address = null;
+                }
+                ObjectName oname = new ObjectName(sb.toString());
+                                                
+                // Ensure that the requested connector name and port is unique
+                if (mBServer.isRegistered(oname) ||
+                    (!mBServer.queryNames(search, null).isEmpty())) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("connectorName",
+                               new ActionError("error.connectorName.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }
+
+                // Look up our MBeanFactory MBean
+                ObjectName fname = TomcatTreeBuilder.getMBeanFactory();
+
+                // Create a new Connector object
+                values = new Object[3];                
+                values[0] = serviceName;  //service parent object name
+                values[1] = address;
+                values[2] = new Integer(cform.getPortText());
+
+                if ("HTTP".equalsIgnoreCase(connectorType)) {
+                        operation = "createHttpConnector"; // HTTP
+                } else if ("HTTPS".equalsIgnoreCase(connectorType)) { 
+                        operation = "createHttpsConnector";   // HTTPS
+                } else {
+                        operation = "createAjpConnector";   // AJP(HTTP)                  
+                }
+                
+                cObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createStandardConnectorTypes);
+                
+                // Add the new Connector to our tree control node
+                TreeControl control = (TreeControl)
+                    session.getAttribute("treeControlTest");
+                if (control != null) {
+                    String parentName = serviceName;
+                    TreeControlNode parentNode = control.findNode(parentName);
+                    if (parentNode != null) {
+                        String nodeLabel = resources.getMessage(locale, 
+                            "server.service.treeBuilder.connector") + " (" + 
+                            cform.getPortText() + ")";
+                        String encodedName =
+                            URLEncoder.encode(cObjectName,TomcatTreeBuilder.URL_ENCODING);
+                        TreeControlNode childNode =
+                            new TreeControlNode(cObjectName,
+                                                "Connector.gif",
+                                                nodeLabel,
+                                                "EditConnector.do?select=" +
+                                                encodedName,
+                                                "content",
+                                                true, domain);
+                        // FIXME--the node should be next to the rest of 
+                        // the Connector nodes..
+                        parentNode.addChild(childNode);
+                        // FIXME - force a redisplay
+                    } else {
+                        getServlet().log
+                            ("Cannot find parent node '" + parentName + "'");
+                    }
+                } else {
+                    getServlet().log
+                        ("Cannot find TreeControlNode!");
+                }
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          operation), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          operation));
+                return (null);
+
+            }
+
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+
+            coname = new ObjectName(cObjectName);
+
+            attribute = "acceptCount";
+            int acceptCount = 60000;
+            try {
+                acceptCount = Integer.parseInt(cform.getAcceptCountText());
+            } catch (Throwable t) {
+                acceptCount = 60000;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("acceptCount", new Integer(acceptCount)));    
+            attribute = "compression";  
+            String compression = cform.getCompression();
+            if ((compression != null) && (compression.length()>0)) { 
+                mBServer.setAttribute(coname,
+                                      new Attribute("compression", compression));
+            }        
+            attribute = "connectionLinger";
+            int connectionLinger = -1;
+            try {
+                connectionLinger = Integer.parseInt(cform.getConnLingerText());
+            } catch (Throwable t) {
+                connectionLinger = 0;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("connectionLinger", new Integer(connectionLinger))); 
+            attribute = "connectionTimeout";
+            int connectionTimeout = 0;
+            try {
+                connectionTimeout = Integer.parseInt(cform.getConnTimeOutText());
+            } catch (Throwable t) {
+                connectionTimeout = 0;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("connectionTimeout", new Integer(connectionTimeout)));            
+            attribute = "connectionUploadTimeout";
+            int connectionUploadTimeout = 0;
+            try {
+                connectionUploadTimeout = Integer.parseInt(cform.getConnUploadTimeOutText());
+            } catch (Throwable t) {
+                connectionUploadTimeout = 0;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("connectionUploadTimeout", new Integer(connectionUploadTimeout)));        
+            attribute = "bufferSize";
+            int bufferSize = 2048;
+            try {
+                bufferSize = Integer.parseInt(cform.getBufferSizeText());
+            } catch (Throwable t) {
+                bufferSize = 2048;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("bufferSize", new Integer(bufferSize)));    
+            attribute = "disableUploadTimeout";
+            mBServer.setAttribute(coname,
+                                  new Attribute("disableUploadTimeout", new Boolean(cform.getDisableUploadTimeout())));                        
+            attribute = "enableLookups";
+            mBServer.setAttribute(coname,
+                                  new Attribute("enableLookups", new Boolean(cform.getEnableLookups())));                        
+
+            attribute = "redirectPort";
+            int redirectPort = 0;
+            try {
+                redirectPort = Integer.parseInt(cform.getRedirectPortText());
+            } catch (Throwable t) {
+                redirectPort = 0;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("redirectPort", new Integer(redirectPort))); 
+            attribute = "minProcessors";
+            int minProcessors = 5;
+            try {
+                minProcessors = Integer.parseInt(cform.getMinProcessorsText());
+            } catch (Throwable t) {
+                minProcessors = 5;
+            }
+            //mBServer.setAttribute(coname,
+            //                      new Attribute("minProcessors", new Integer(minProcessors))); 
+            attribute = "maxProcessors";
+            int maxProcessors = 20;
+            try {
+                maxProcessors = Integer.parseInt(cform.getMaxProcessorsText());
+            } catch (Throwable t) {
+                maxProcessors = 20;
+            }
+            //mBServer.setAttribute(coname,
+            //                      new Attribute("maxProcessors", new Integer(maxProcessors))); 
+       
+            attribute = "maxKeepAliveRequests";
+            int maxKeepAliveRequests = 100;
+            try {
+                maxKeepAliveRequests = Integer.parseInt(cform.getMaxKeepAliveText());
+            } catch (Throwable t) {
+                maxKeepAliveRequests = 100;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("maxKeepAliveRequests", new Integer(maxKeepAliveRequests))); 
+            attribute = "maxSpareThreads";
+            int maxSpare = 50;
+            try {
+                maxSpare = Integer.parseInt(cform.getMaxSpare());
+            } catch (Throwable t) {
+                maxSpare = 50;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute(attribute, (new Integer(maxSpare)).toString())); 
+            attribute = "maxThreads";
+            int maxThreads = 200;
+            try {
+                maxThreads = Integer.parseInt(cform.getMaxThreads());
+            } catch (Throwable t) {
+                maxThreads = 200;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute(attribute, (new Integer(maxThreads)).toString())); 
+			
+            attribute = "minSpareThreads";
+            int minSpare = 4;
+            try {
+                minSpare = Integer.parseInt(cform.getMinSpare());
+            } catch (Throwable t) {
+                minSpare = 4;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute(attribute, (new Integer(minSpare)).toString())); 
+
+            attribute = "threadPriority";
+            int threadPriority = Thread.NORM_PRIORITY;
+            try {
+                threadPriority = Integer.parseInt(cform.getThreadPriority());
+            } catch (Throwable t) {
+                threadPriority = Thread.NORM_PRIORITY;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute(attribute, (new Integer(threadPriority))));
+				  
+            attribute = "secure";
+            mBServer.setAttribute(coname,
+                                  new Attribute("secure", new Boolean(cform.getSecure())));    
+            attribute = "tcpNoDelay";
+            mBServer.setAttribute(coname,
+                                  new Attribute("tcpNoDelay", new Boolean(cform.getTcpNoDelay())));    
+            
+            attribute = "xpoweredBy";
+            mBServer.setAttribute(coname,
+                                  new Attribute("xpoweredBy", new Boolean(cform.getXpoweredBy())));                        
+
+            attribute = "URIEncoding";
+            String uriEnc = cform.getURIEncodingText();
+            if ((uriEnc != null) && (uriEnc.length()==0)) {
+                uriEnc = null;
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute(attribute, uriEnc));            
+
+            attribute = "useBodyEncodingForURI";
+            mBServer.setAttribute(coname,
+                                  new Attribute(attribute, new Boolean(cform.getUseBodyEncodingForURIText())));
+
+            attribute = "allowTrace";
+            mBServer.setAttribute(coname,
+                                  new Attribute(attribute, new Boolean(cform.getAllowTraceText())));
+
+            // proxy name and port do not exist for AJP connector
+            if (!("AJP".equalsIgnoreCase(connectorType))) {
+                attribute = "proxyName";  
+                String proxyName = cform.getProxyName();
+                if ((proxyName != null) && (proxyName.length()>0)) { 
+                    mBServer.setAttribute(coname,
+                                  new Attribute("proxyName", proxyName));
+                }
+                
+                attribute = "proxyPort";
+                int proxyPort = 0;
+                try {
+                    proxyPort = Integer.parseInt(cform.getProxyPortText());
+                } catch (Throwable t) {
+                    proxyPort = 0;
+                }
+                mBServer.setAttribute(coname,
+                              new Attribute("proxyPort", new Integer(proxyPort))); 
+            }
+            
+            // HTTPS specific properties
+            if("HTTPS".equalsIgnoreCase(connectorType)) {
+                attribute = "algorithm";
+                String algorithm = cform.getAlgorithm();
+                if ((algorithm != null) && (algorithm.length()>0)) 
+                    mBServer.setAttribute(coname,
+                              new Attribute("algorithm", algorithm));  
+                
+                attribute = "clientAuth";              
+                mBServer.setAttribute(coname,
+                              new Attribute("clientAuth", 
+                                             cform.getClientAuthentication()));   
+                
+                attribute = "ciphers";
+                String ciphers = cform.getCiphers();
+                if ((ciphers != null) && (ciphers.length()>0)) 
+                    mBServer.setAttribute(coname,
+                              new Attribute("ciphers", ciphers));           
+                
+                attribute = "keystoreFile";
+                String keyFile = cform.getKeyStoreFileName();
+                if ((keyFile != null) && (keyFile.length()>0)) 
+                    mBServer.setAttribute(coname,
+                              new Attribute("keystoreFile", keyFile));            
+                
+                attribute = "keystorePass";
+                String keyPass = cform.getKeyStorePassword();
+                if ((keyPass != null) && (keyPass.length()>0)) 
+                    mBServer.setAttribute(coname,
+                              new Attribute("keystorePass", keyPass));                 
+                // request.setAttribute("warning", "connector.keyPass.warning");  
+                
+                attribute = "keystoreType";
+                String keyType = cform.getKeyStoreType();
+                if ((keyType != null) && (keyType.length()>0)) 
+                    mBServer.setAttribute(coname,
+                              new Attribute("keystoreType", keyType));   
+                
+                attribute = "sslProtocol";
+                String sslProtocol = cform.getSslProtocol();
+                if ((sslProtocol != null) && (sslProtocol.length()>0)) 
+                    mBServer.setAttribute(coname,
+                              new Attribute("sslProtocol", sslProtocol));                    
+             }
+ 
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+        
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/AddContextAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/AddContextAction.java
new file mode 100644
index 0000000..59a1269
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/AddContextAction.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.context;
+
+import java.io.IOException;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.Lists;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+/**
+ * The <code>Action</code> that sets up <em>Add Context</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class AddContextAction extends Action {
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+    ActionForm form,
+    HttpServletRequest request,
+    HttpServletResponse response)
+    throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Fill in the form values for display and editing
+        ContextForm contextFm = new ContextForm();
+        session.setAttribute("contextForm", contextFm);
+        contextFm.setAdminAction("Create");
+        contextFm.setObjectName("");
+        String parent = request.getParameter("parent");
+        contextFm.setParentObjectName(parent);
+        int i = parent.indexOf(":");
+        String domain = parent.substring(0, i);
+        int position = parent.indexOf(",");
+        String loader = domain + TomcatTreeBuilder.LOADER_TYPE + 
+                parent.substring(position, parent.length());
+        String manager = domain + TomcatTreeBuilder.MANAGER_TYPE + 
+                parent.substring(position, parent.length());
+        contextFm.setLoaderObjectName(loader);
+        contextFm.setManagerObjectName(manager); 
+        contextFm.setNodeLabel("");
+        contextFm.setCookies("");
+        contextFm.setCrossContext("false");
+        contextFm.setDocBase("");
+        contextFm.setOverride("false");
+        contextFm.setPrivileged("false");
+        contextFm.setPath("");
+        contextFm.setReloadable("false");
+        contextFm.setSwallowOutput("false");
+        contextFm.setUseNaming("false");
+        contextFm.setWorkDir("");        
+        contextFm.setPath("");
+        //loader initialization
+        //contextFm.setLdrCheckInterval("15");
+        contextFm.setLdrReloadable("false");
+        //manager initialization
+        //contextFm.setMgrCheckInterval("60");
+        contextFm.setMgrMaxSessions("-1");
+        contextFm.setMgrSessionIDInit("");
+        
+        contextFm.setBooleanVals(Lists.getBooleanValues());        
+        
+        // Forward to the context display page
+        return (mapping.findForward("Context"));
+        
+    }    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/ContextForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/ContextForm.java
new file mode 100644
index 0000000..55a9486
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/ContextForm.java
@@ -0,0 +1,780 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.context;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.util.List;
+
+/**
+ * Form bean for the context page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class ContextForm extends ActionForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    
+   /**
+     * The administrative action represented by this form.
+     */
+    private String adminAction = "Edit";
+
+    /**
+     * The object name of the Context this bean refers to.
+     */
+    private String objectName = null;
+   
+    /**
+     * The object name of the parent of this Context.
+     */
+    private String parentObjectName = null;
+   
+   /**
+     * The object name of the loader of this Context.
+     */
+    private String loaderObjectName = null;
+   
+    /**
+     * The object name of the manager of this Context.
+     */
+    private String managerObjectName = null;
+   
+    /**
+     * The text for the node label.
+     */
+    private String nodeLabel = null;
+    
+    /**
+     * The value of cookies.
+     */
+    private String cookies = "false";
+    
+    /**
+     * The value of cross context.
+     */
+    private String crossContext = "false";
+    
+    /**
+     * The text for the document Base.
+     */
+    private String docBase = null;
+    
+    /**
+     * The text for override boolean.
+     */
+    private String override = "false";
+    
+    /**
+     * The text for privileged boolean.
+     */
+    private String privileged = "false";
+    
+    /**
+     * The text for the context path for this context.
+     */
+    private String path = null;
+    
+    /**
+     * The text for reloadable boolean.
+     */
+    private String reloadable = "false";
+
+    /**
+     * The text for swallowOutput boolean.
+     */
+    private String swallowOutput = "false";
+
+    /**
+     * The text for use naming boolean.
+     */
+    private String useNaming = "false";
+    
+    /**
+     * The text for the working directory for this context.
+     */
+    private String workDir = null;
+    
+    /**
+     * The text for the loader check interval.
+     */
+    private String ldrCheckInterval = "15";
+    
+    /**
+     * The text for the boolean value of loader reloadable.
+     */
+    private String ldrReloadable = "false";
+    
+    /**
+     * The text for the session manager check interval.
+     */
+    private String mgrCheckInterval = "60";
+    
+    
+    /**
+     * The text for the session mgr session ID initializer.
+     */
+    private String mgrSessionIDInit = "";
+    
+    /**
+     * The text for the session mgr max active sessions.
+     */
+    private String mgrMaxSessions = "0";
+    
+    /**
+     * The text for the anti resource locking flag.
+     */
+    private String antiResourceLocking = "false";
+
+    /**
+     * The text for the anti jar locking flag.
+     */
+    private String antiJarLocking = "false";
+
+    /*
+     * Represent boolean (true, false) values for cookies etc.
+     */
+    private List booleanVals = null;
+    
+    // ------------------------------------------------------------- Properties
+     
+   /**
+     * Return the administrative action represented by this form.
+     */
+    public String getAdminAction() {
+
+        return this.adminAction;
+
+    }
+
+
+    /**
+     * Set the administrative action represented by this form.
+     */
+    public void setAdminAction(String adminAction) {
+
+        this.adminAction = adminAction;
+
+    }
+
+    /**
+     * Return the object name of the Context this bean refers to.
+     */
+    public String getObjectName() {
+
+        return this.objectName;
+
+    }
+
+    /**
+     * Set the object name of the Context this bean refers to.
+     */
+    public void setObjectName(String objectName) {
+
+        this.objectName = objectName;
+
+    }    
+    
+    /**
+     * Return the parent object name of the Context this bean refers to.
+     */
+    public String getParentObjectName() {
+
+        return this.parentObjectName;
+
+    }
+
+    /**
+     * Set the parent object name of the Context this bean refers to.
+     */
+    public void setParentObjectName(String parentObjectName) {
+
+        this.parentObjectName = parentObjectName;
+
+    }
+    
+      /**
+     * Return the loader object name of the Context this bean refers to.
+     */
+    public String getLoaderObjectName() {
+
+        return this.loaderObjectName;
+
+    }
+
+    /**
+     * Set the loader object name of the Context this bean refers to.
+     */
+    public void setLoaderObjectName(String loaderObjectName) {
+
+        this.loaderObjectName = loaderObjectName;
+
+    }
+    
+      /**
+     * Return the manager object name of the Context this bean refers to.
+     */
+    public String getManagerObjectName() {
+
+        return this.managerObjectName;
+
+    }
+
+    /**
+     * Set the manager object name of the Context this bean refers to.
+     */
+    public void setManagerObjectName(String managerObjectName) {
+
+        this.managerObjectName = managerObjectName;
+
+    }
+    
+    /**
+     * Return the label of the node that was clicked.
+     */
+    public String getNodeLabel() {
+        
+        return this.nodeLabel;
+        
+    }
+    
+    /**
+     * Set the node label.
+     */
+    public void setNodeLabel(String nodeLabel) {
+        
+        this.nodeLabel = nodeLabel;
+        
+    }
+    
+    
+    /**
+     * Return the booleanVals.
+     */
+    public List getBooleanVals() {
+        
+        return this.booleanVals;
+        
+    }
+    
+    /**
+     * Set the debugVals.
+     */
+    public void setBooleanVals(List booleanVals) {
+        
+        this.booleanVals = booleanVals;
+        
+    }
+    
+    
+    /**
+     * Return the Cookies.
+     */
+    
+    public String getCookies() {
+        
+        return this.cookies;
+        
+    }
+    
+    /**
+     * Set the Cookies.
+     */
+    public void setCookies(String cookies) {
+        
+        this.cookies = cookies;
+        
+    }
+    
+    /**
+     * Return the Cross Context.
+     */
+    
+    public String getCrossContext() {
+        
+        return this.crossContext;
+        
+    }
+    
+    /**
+     * Set the Cross Context.
+     */
+    public void setCrossContext(String crossContext) {
+        
+        this.crossContext = crossContext;
+        
+    }
+    
+    
+    /**
+     * Return the Document Base Text.
+     */
+    
+    public String getDocBase() {
+        
+        return this.docBase;
+        
+    }
+    
+    /**
+     * Set the document Base text.
+     */
+    public void setDocBase(String docBase) {
+        
+        this.docBase = docBase;
+        
+    }
+    
+    
+    /**
+     * Return the Override boolean value.
+     */
+    
+    public String getOverride() {
+        
+        return this.override;
+        
+    }
+    
+    /**
+     * Set the override value.
+     */
+    public void setOverride(String override) {
+        
+        this.override = override;
+        
+    }
+    
+    
+    /**
+     * Return the privileged boolean value.
+     */
+    
+    public String getPrivileged() {
+        
+        return this.privileged;
+        
+    }
+    
+    /**
+     * Set the privileged value.
+     */
+    public void setPrivileged(String privileged) {
+        
+        this.privileged = privileged;
+        
+    }
+    
+    
+    /**
+     * Return the context path.
+     */
+    
+    public String getPath() {
+        
+        return this.path;
+        
+    }
+    
+    /**
+     * Set the context path text.
+     */
+    public void setPath(String path) {
+        
+        this.path = path;
+        
+    }
+    
+    
+    /**
+     * Return the reloadable boolean value.
+     */
+    
+    public String getReloadable() {
+        
+        return this.reloadable;
+        
+    }
+    
+    /**
+     * Set the reloadable value.
+     */
+    public void setReloadable(String reloadable) {
+        
+        this.reloadable = reloadable;
+        
+    }
+    
+    /**
+     * Return the swallowOutput boolean value.
+     */
+
+    public String getSwallowOutput() {
+
+        return this.swallowOutput;
+
+    }
+
+    /**
+     * Set the swallowOutput value.
+     */
+    public void setSwallowOutput(String swallowOutput) {
+
+        this.swallowOutput = swallowOutput;
+
+    }
+
+    /**
+     * Return the use naming boolean value.
+     */
+    
+    public String getUseNaming() {
+        
+        return this.useNaming;
+        
+    }
+    
+    /**
+     * Set the useNaming value.
+     */
+    public void setUseNaming(String useNaming) {
+        
+        this.useNaming = useNaming;
+        
+    }
+    
+    /**
+     * Return the Working Directory.
+     */
+    public String getWorkDir() {
+        
+        return this.workDir;
+        
+    }
+    
+    /**
+     * Set the working directory.
+     */
+    public void setWorkDir(String workDir) {
+        
+        this.workDir = workDir;
+        
+    }
+    
+    
+    /**
+     * Return the loader check interval.
+     */
+    public String getLdrCheckInterval() {
+        
+        return this.ldrCheckInterval;
+        
+    }
+    
+    /**
+     * Set the loader Check Interval.
+     */
+    public void setLdrCheckInterval(String ldrCheckInterval) {
+        
+        this.ldrCheckInterval = ldrCheckInterval;
+        
+    }
+    
+    
+    /**
+     * Return the loader reloadable boolean value.
+     */
+    public String getLdrReloadable() {
+        
+        return this.ldrReloadable;
+        
+    }
+    
+    /**
+     * Set the loader reloadable value.
+     */
+    public void setLdrReloadable(String ldrReloadable) {
+        
+        this.ldrReloadable = ldrReloadable;
+        
+    }
+    
+    /**
+     * Return the session manager check interval.
+     */
+    public String getMgrCheckInterval() {
+        
+        return this.mgrCheckInterval;
+        
+    }
+    
+    /**
+     * Set the session manager Check Interval.
+     */
+    public void setMgrCheckInterval(String mgrCheckInterval) {
+        
+        this.mgrCheckInterval = mgrCheckInterval;
+        
+    }
+    
+    /**
+     * Return the session ID initializer.
+     */
+    public String getMgrSessionIDInit() {
+        
+        return this.mgrSessionIDInit;
+        
+    }
+    
+    /**
+     * Set the mgr Session ID Initizializer.
+     */
+    public void setMgrSessionIDInit(String mgrSessionIDInit) {
+        
+        this.mgrSessionIDInit = mgrSessionIDInit;
+        
+    }
+    
+    /**
+     * Return the Session mgr maximum active sessions.
+     */
+    
+    public String getMgrMaxSessions() {
+        
+        return this.mgrMaxSessions;
+        
+    }
+    
+    /**
+     * Set the Session mgr maximum active sessions.
+     */
+    public void setMgrMaxSessions(String mgrMaxSessions) {
+        
+        this.mgrMaxSessions = mgrMaxSessions;
+        
+    }
+
+    /**
+     * Get the anti resouce locking flag
+     */
+    public String getAntiResourceLocking() {
+        return antiResourceLocking;
+    }
+
+    /**
+     * Set the anti resource locking flag
+     */
+    public void setAntiResourceLocking(String arl) {
+	antiResourceLocking = arl;
+    }
+
+
+    /**
+     * Get the anti jar locking flag
+     */
+    public String getAntiJarLocking() {
+        return antiJarLocking;
+    }
+
+    /**
+     * Set the anti jar locking flag
+     */
+    public void setAntiJarLocking(String ajl) {
+        antiJarLocking = ajl;
+    }
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+        
+        this.objectName = null;
+        this.parentObjectName = null;
+        this.loaderObjectName = null;
+        this.managerObjectName = null;
+        
+        // context properties
+        this.cookies = "false";
+        this.crossContext = "false";
+        this.docBase = null;
+        this.override= "false";
+        this.path = null;
+        this.reloadable = "false";
+        this.swallowOutput = "false";
+        this.antiResourceLocking = "false";
+        this.antiJarLocking = "false";
+
+        // loader properties
+        this.ldrCheckInterval = "15";
+        this.ldrReloadable = "true";
+        
+        // session manager properties
+        this.mgrCheckInterval = "60";
+        this.mgrSessionIDInit = "0";
+        this.mgrMaxSessions = "-1";
+    }
+    
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ContextForm[adminAction=");
+        sb.append(adminAction);
+        sb.append(",docBase=");
+        sb.append(docBase);
+        sb.append(",path=");
+        sb.append(path);
+        sb.append(",cookies=");
+        sb.append(cookies);
+        sb.append(",crossContext=");
+        sb.append(crossContext);
+        sb.append(",override=");
+        sb.append(override);
+        sb.append(",reloadable=");
+        sb.append(reloadable);
+        sb.append(",swallowOutput=");
+        sb.append(swallowOutput);
+
+        // loader properties
+        sb.append(",ldrCheckInterval=");
+        sb.append(ldrCheckInterval);        
+        sb.append(",ldrReloadable=");
+        sb.append(ldrReloadable);
+        // manager properties
+        sb.append(",mgrCheckInterval=");
+        sb.append(mgrCheckInterval);
+        sb.append(",mgrSessionIDInit=");
+        sb.append(mgrSessionIDInit);
+        sb.append(",mgrMaxSessions=");
+        sb.append(mgrMaxSessions);
+        // object names
+        sb.append("',objectName='");
+        sb.append(objectName);
+        sb.append("',parentObjectName=");
+        sb.append(parentObjectName);
+        sb.append("',loaderObjectName=");
+        sb.append(loaderObjectName);
+        sb.append("',managerObjectName=");
+        sb.append(managerObjectName);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    private ActionErrors errors;
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        
+        // front end validation when save is clicked.
+        //if (submit != null) {
+            
+            // docBase cannot be null
+            if ((docBase == null) || (docBase.length() < 1)) {
+                errors.add("docBase", new ActionError("error.docBase.required"));
+            }
+            
+            // if path is empty, it's root context
+            // validate context starting with "/" only at the time of context creation.
+            if ("Create".equalsIgnoreCase(adminAction) && !path.startsWith("/")) {
+                errors.add("path", new ActionError("error.path.prefix"));                
+            }
+                        
+            //if ((workDir == null) || (workDir.length() < 1)) {
+            //    errors.add("workDir", new ActionError("error.workDir.required"));
+            //}
+            
+            // loader properties
+            // FIXME-- verify if these ranges are ok.
+            numberCheck("ldrCheckInterval", ldrCheckInterval  , true, 0, 10000);
+            
+            // session manager properties            
+            numberCheck("mgrCheckInterval",  mgrCheckInterval, true, 0, 10000);
+            numberCheck("mgrMaxSessions",  mgrMaxSessions, false, -1, 100);
+            
+            //if ((mgrSessionIDInit == null) || (mgrSessionIDInit.length() < 1)) {
+            //    errors.add("mgrSessionIDInit", new ActionError("error.mgrSessionIDInit.required"));
+            //}
+        //}
+        
+        return errors;
+    }
+    
+    /*
+     * Helper method to check that it is a required number and
+     * is a valid integer within the given range. (min, max).
+     *
+     * @param  field  The field name in the form for which this error occured.
+     * @param  numText  The string representation of the number.
+     * @param rangeCheck  Boolean value set to true of reange check should be performed.
+     *
+     * @param  min  The lower limit of the range
+     * @param  max  The upper limit of the range
+     *
+     */
+    
+    private void numberCheck(String field, String numText, boolean rangeCheck,
+    int min, int max) {
+        
+        // Check for 'is required'
+        if ((numText == null) || (numText.length() < 1)) {
+            errors.add(field, new ActionError("error."+field+".required"));
+        } else {
+            
+            // check for 'must be a number' in the 'valid range'
+            try {
+                int num = Integer.parseInt(numText);
+                // perform range check only if required
+                if (rangeCheck) {
+                    if ((num < min) || (num > max ))
+                        errors.add( field,
+                        new ActionError("error."+ field +".range"));
+                }
+            } catch (NumberFormatException e) {
+                errors.add(field,
+                new ActionError("error."+ field + ".format"));
+            }
+        }
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/ContextsForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/ContextsForm.java
new file mode 100644
index 0000000..5ccf2fa
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/ContextsForm.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.context;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for deleting contexts.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class ContextsForm extends ActionForm {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the contexts to be deleted.
+     */
+    private String contexts[] = new String[0];
+
+    public String[] getContexts() {
+        return (this.contexts);
+    }
+
+    public void setContexts(String contexts[]) {
+        this.contexts = contexts;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.contexts = new String[0];
+
+    }
+        
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/DeleteContextAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/DeleteContextAction.java
new file mode 100644
index 0000000..ae68890
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/DeleteContextAction.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.context;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+
+/**
+ * The <code>Action</code> that sets up <em>Delete Contexts</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteContextAction extends Action {
+    
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // object name that forms the basis of the search pattern
+        // to get list of all available contexts
+        String patternObject = null;
+        
+        // Set up a form bean containing the currently selected
+        // objects to be deleted
+        ContextsForm contextsForm = new ContextsForm();
+        String select = request.getParameter("select");
+        if (select != null) {
+            String contexts[] = new String[1];
+            contexts[0] = select;
+            contextsForm.setContexts(contexts);
+            patternObject = select;
+        }        
+        request.setAttribute("contextsForm", contextsForm);
+                
+        // get the parent host object name
+        String parent = request.getParameter("parent");
+        if (parent != null) {                
+            patternObject = parent;
+        } 
+        
+        // Accumulate a list of all available contexts
+        ArrayList list = new ArrayList();
+        try {
+            ObjectName poname = new ObjectName(patternObject);
+            String domain = poname.getDomain();
+            StringBuffer sb = new StringBuffer(domain);
+            sb.append(":j2eeType=WebModule,*");
+            ObjectName search = new ObjectName(sb.toString());
+            // get all available contexts only for this host
+            Iterator items =
+                mBServer.queryNames(search, null).iterator();
+            String item = null;
+            String host = poname.getKeyProperty("host");
+            if (host==null) {
+                String name = poname.getKeyProperty("name");
+                if ((name != null) && (name.length() > 0)) {
+                    name = name.substring(2);
+                    int i = name.indexOf("/");
+                    host = name.substring(0,i);
+                }
+            }
+            String hostPrefix = "//"+host;
+            String hostAttr = null;
+            while (items.hasNext()) {
+                item = items.next().toString();
+                ObjectName oname = new ObjectName(item);
+                hostAttr = oname.getKeyProperty("name");
+                if (hostAttr.startsWith(hostPrefix)) {
+                    list.add(item);
+                }
+            }
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.select"));
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.select"));
+            return (null);
+        }
+        Collections.sort(list);
+        request.setAttribute("contextsList", list);
+        
+        // Forward to the list display page
+        return (mapping.findForward("Contexts"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/DeleteContextsAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/DeleteContextsAction.java
new file mode 100644
index 0000000..190480c
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/DeleteContextsAction.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.context;
+
+import java.io.IOException;
+import java.util.Locale;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+/**
+ * The <code>Action</code> that completes <em>Delete Contexts</em>
+ * transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteContextsAction extends Action {
+
+
+    /**
+     * Signature for the <code>removeContext</code> operation.
+     */
+    private String removeContextTypes[] =
+    { "java.lang.String",      // Object name
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        
+        // Look up the components we will be using as needed
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Delete the specified Contexts
+        String contexts[]  = ((ContextsForm) form).getContexts();
+        String values[] = new String[1];
+        String operation = "removeContext";
+
+        try {
+
+            // Look up our tree control data structure
+            TreeControl control = (TreeControl)
+                session.getAttribute("treeControlTest");
+
+            // Remove the specified contexts
+            for (int i = 0; i < contexts.length; i++) {
+                values[0] = contexts[i];
+                if (control != null) {
+                    control.selectNode(null);
+                    TreeControlNode node = control.findNode(contexts[i]);
+                    String domain = node.getDomain();
+                    ObjectName fname = TomcatTreeBuilder.getMBeanFactory();
+                    mBServer.invoke(fname, operation,
+                                values, removeContextTypes);
+                    if (node != null) {
+                        node.remove();
+                    } else {
+                        getServlet().log("Missing TreeControlNode for " +
+                                         contexts[i]);
+                    }
+                } else {
+                    getServlet().log("Missing TreeControl attribute");
+                }
+            }
+
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      operation), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      operation));
+            return (null);
+
+        }
+
+        // Report successful completion of this transaction
+        return (mapping.findForward("Save Successful"));
+
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/EditContextAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/EditContextAction.java
new file mode 100644
index 0000000..b175181
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/EditContextAction.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.context;
+
+import java.io.IOException;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.Lists;
+
+/**
+ * The <code>Action</code> that sets up <em>Edit Context</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class EditContextAction extends Action {
+    
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Set up the object names of the MBeans we are manipulating
+        // Context mBean
+        ObjectName cname = null;
+        // Loader mBean
+        ObjectName lname = null;
+        // Manager mBean 
+        ObjectName mname = null;
+        
+        StringBuffer sb = null;
+        try {
+            cname = new ObjectName(request.getParameter("select"));
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.contextName.bad",
+                                     request.getParameter("select"));
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+        String name = cname.getKeyProperty("name");
+        name = name.substring(2);
+        int i = name.indexOf("/");
+        String host = name.substring(0,i);
+        String path = name.substring(i);
+        // Get the corresponding loader
+        try {
+            sb = new StringBuffer(cname.getDomain());
+            sb.append(":type=Loader");
+            sb.append(",path="+path);
+            sb.append(",host="+host);
+            lname = new ObjectName(sb.toString());
+         } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.managerName.bad",
+                                 sb.toString());
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+
+        // Session manager properties
+        // Get the corresponding Session Manager mBean
+        try {
+            sb = new StringBuffer(cname.getDomain());
+            sb.append(":type=Manager");
+            sb.append(",path="+path);
+            sb.append(",host="+host);
+            mname = new ObjectName(sb.toString());
+        } catch (Exception e) {
+            String message =
+                resources.getMessage("error.managerName.bad",
+                                 sb.toString());
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+
+        // Fill in the form values for display and editing
+        ContextForm contextFm = new ContextForm();
+        session.setAttribute("contextForm", contextFm);
+        contextFm.setAdminAction("Edit");
+        contextFm.setObjectName(cname.toString());
+        contextFm.setLoaderObjectName(lname.toString());
+        contextFm.setManagerObjectName(mname.toString());
+        sb = new StringBuffer();
+        sb.append(resources.getMessage(locale, "server.service.treeBuilder.context"));
+        sb.append(" (");
+        sb.append(path);
+        sb.append(")");
+        contextFm.setNodeLabel(sb.toString());
+        contextFm.setBooleanVals(Lists.getBooleanValues());
+       
+        String attribute = null;
+        try {
+
+            // Copy scalar properties
+            attribute = "path";
+            contextFm.setPath
+                ((String) mBServer.getAttribute(cname, attribute));
+            attribute = "cookies";
+            contextFm.setCookies
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            attribute = "crossContext";
+            contextFm.setCrossContext
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            attribute = "docBase";
+            contextFm.setDocBase
+                ((String) mBServer.getAttribute(cname, attribute));
+            attribute = "workDir";
+            contextFm.setWorkDir
+                ((String) mBServer.getAttribute(cname, attribute));
+            attribute = "useNaming";
+            contextFm.setUseNaming
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            attribute = "reloadable";
+            contextFm.setReloadable
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            attribute = "swallowOutput";
+            contextFm.setSwallowOutput
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            attribute = "override";
+            contextFm.setOverride
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            attribute = "privileged";
+            contextFm.setPrivileged
+                (((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+
+	    attribute = "antiJARLocking";
+	    contextFm.setAntiJarLocking
+		(((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+	    attribute = "antiResourceLocking";
+	    contextFm.setAntiResourceLocking
+		(((Boolean) mBServer.getAttribute(cname, attribute)).toString());
+            // loader properties
+            //attribute = "checkInterval";
+            //contextFm.setLdrCheckInterval
+            //    (((Integer) mBServer.getAttribute(lname, attribute)).toString());
+            attribute = "reloadable";
+            contextFm.setLdrReloadable
+                (((Boolean) mBServer.getAttribute(lname, attribute)).toString());
+
+            // manager properties
+            attribute = "entropy";
+            contextFm.setMgrSessionIDInit
+                ((String) mBServer.getAttribute(mname, attribute));
+            attribute = "maxActiveSessions";
+            contextFm.setMgrMaxSessions
+                (((Integer) mBServer.getAttribute(mname, attribute)).toString());
+            //attribute = "checkInterval";
+            //contextFm.setMgrCheckInterval
+            //    (((Integer) mBServer.getAttribute(mname, attribute)).toString());
+
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+            return (null);
+        }
+        
+        // Forward to the context display page
+        return (mapping.findForward("Context"));
+        
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/SaveContextAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/SaveContextAction.java
new file mode 100644
index 0000000..a6f27a8
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/context/SaveContextAction.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.context;
+
+
+import java.net.URLEncoder;
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.commons.modeler.Registry;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+
+/**
+ * The <code>Action</code> that completes <em>Add Context</em> and
+ * <em>Edit Context</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveContextAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Signature for the <code>createStandardContext</code> operation.
+     */
+    private String createStandardContextTypes[] =
+    { "java.lang.String",     // parent
+      "java.lang.String",     // path
+      "java.lang.String",     // docBase
+    };
+
+   /**
+     * Signature for the <code>createStandardLoader</code> operation.
+     */
+    private String createStandardLoaderTypes[] =
+    { "java.lang.String",     // parent
+    };
+
+   /**
+     * Signature for the <code>createStandardManager</code> operation.
+     */
+    private String createStandardManagerTypes[] =
+    { "java.lang.String",     // parent
+    };
+
+    /**
+     * Signature for the <code>removeContext</code> operation.
+     */
+    private String removeContextTypes[] =
+    { "java.lang.String",      // Object name
+    };
+        
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        ContextForm cform = (ContextForm) form;
+        String adminAction = cform.getAdminAction();
+        String cObjectName = cform.getObjectName();
+        String lObjectName = cform.getLoaderObjectName();
+        String mObjectName = cform.getManagerObjectName();
+        if ((cform.getPath() == null) || (cform.getPath().length()<1)) {
+            cform.setPath("/");
+        }
+       
+        // Perform a "Create Context" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            String operation = null;
+            Object values[] = null;
+            
+            try {                
+                // get the parent host name
+                String parentName = cform.getParentObjectName();
+                ObjectName honame = new ObjectName(parentName);
+                
+                // Ensure that the requested context name is unique
+                ObjectName oname = 
+                        new ObjectName(honame.getDomain() + 
+                                    ":j2eeType=WebModule,name=//" +
+                                    honame.getKeyProperty("host") + 
+                                    cform.getPath() +
+                                    // FIXME set J2EEApplication and J2EEServer
+                                    ",J2EEApplication=none,J2EEServer=none");                   
+                
+                if (mBServer.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("contextName",
+                               new ActionError("error.contextName.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }
+                
+                // Look up our MBeanFactory MBean
+                ObjectName fname = 
+                    TomcatTreeBuilder.getMBeanFactory();
+
+                // Create a new StandardContext object
+                values = new Object[3];
+                values[0] = parentName;
+                values[1] = cform.getPath();
+                values[2] = cform.getDocBase();
+                
+                operation = "createStandardContext";
+                cObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createStandardContextTypes);
+                // Create a new Loader object
+                values = new String[1];
+                // parent of loader is the newly created context
+                values[0] = cObjectName.toString();
+                operation = "createWebappLoader";
+                lObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createStandardLoaderTypes);                
+                
+                // Create a new StandardManager object
+                values = new String[1];
+                // parent of manager is the newly created Context
+                values[0] = cObjectName.toString();
+                operation = "createStandardManager";
+                mObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createStandardManagerTypes);
+                                                                       
+                if (mObjectName==null) {
+                    operation = "removeLoader";
+                    values[0] = lObjectName;
+                    mBServer.invoke(fname, operation, values, 
+                        removeContextTypes);
+                    operation = "removeContext";
+                    values[0] = cObjectName;
+                    mBServer.invoke(fname, operation, values, 
+                        removeContextTypes);
+                    Registry.getRegistry().unregisterComponent(new ObjectName(cObjectName));
+                    request.setAttribute("warning", "error.context.directory");
+                    return (mapping.findForward("Save Unsuccessful"));
+                }
+                
+                // Add the new Context to our tree control node
+                addToTreeControlNode(oname, cObjectName, parentName, 
+                                    resources, session, locale);                     
+
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          operation), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          operation));
+                return (null);
+
+            }
+
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+
+            ObjectName coname = new ObjectName(cObjectName);
+            ObjectName loname = new ObjectName(lObjectName);
+            ObjectName moname = new ObjectName(mObjectName);
+
+            attribute = "path";
+            String path = "";
+            try {
+                path = cform.getPath();
+            } catch (Throwable t) {
+                path = "";
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("path", path));
+            
+            attribute = "workDir";
+            String workDir = "";
+            workDir = cform.getWorkDir();
+            if ((workDir!=null) && (workDir.length()>=1)) {
+                mBServer.setAttribute(coname,
+                                  new Attribute("workDir", workDir));
+            }
+ 
+            attribute = "cookies";
+            String cookies = "false";
+            try {
+                cookies = cform.getCookies();
+            } catch (Throwable t) {
+                cookies = "false";
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("cookies", new Boolean(cookies)));
+
+            attribute = "crossContext";
+            String crossContext = "false";
+            try {
+                crossContext = cform.getCrossContext();
+            } catch (Throwable t) {
+                crossContext = "false";
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("crossContext", new Boolean(crossContext)));
+
+            attribute = "override";
+            String override = "false";
+            try {
+                override = cform.getOverride();
+            } catch (Throwable t) {
+                override = "false";
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("override", new Boolean(override)));
+
+            attribute = "privileged";
+            String privileged = "false";
+            try {
+                privileged = cform.getPrivileged();
+            } catch (Throwable t) {
+                privileged = "false";
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("privileged", new Boolean(privileged)));
+
+            attribute = "reloadable";
+            String reloadable = "false";
+            try {
+                reloadable = cform.getReloadable();
+            } catch (Throwable t) {
+                reloadable = "false";
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("reloadable", new Boolean(reloadable)));
+
+            attribute = "swallowOutput";
+            String swallowOutput = "false";
+            try {
+                swallowOutput = cform.getSwallowOutput();
+            } catch (Throwable t) {
+                swallowOutput = "false";
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("swallowOutput", new Boolean(swallowOutput)));
+
+            attribute = "useNaming";
+            String useNaming = "false";
+            try {
+                useNaming = cform.getUseNaming();
+            } catch (Throwable t) {
+                useNaming = "false";
+            }
+            mBServer.setAttribute(coname,
+                                  new Attribute("useNaming", new Boolean(useNaming)));
+
+            attribute = "antiJARLocking";
+            String antiJarLocking = cform.getAntiJarLocking();
+            mBServer.setAttribute(coname,
+                                  new Attribute("antiJARLocking", new Boolean(antiJarLocking)));
+
+            attribute = "antiResourceLocking";
+            String antiResourceLocking = cform.getAntiResourceLocking();
+            mBServer.setAttribute(coname,
+                                  new Attribute("antiResourceLocking", new Boolean(antiResourceLocking)));
+
+	    
+            // Loader properties            
+            attribute = "reloadable";
+            try {
+                reloadable = cform.getLdrReloadable();
+            } catch (Throwable t) {
+                reloadable = "false";
+            }
+            mBServer.setAttribute(loname,
+                                  new Attribute("reloadable", new Boolean(reloadable)));
+            
+            //attribute = "checkInterval";
+            //int checkInterval = 15;
+            //try {
+            //    checkInterval = Integer.parseInt(cform.getLdrCheckInterval());
+            //} catch (Throwable t) {
+            //    checkInterval = 15;
+            //}
+            //mBServer.setAttribute(loname,
+            //                      new Attribute("checkInterval", new Integer(checkInterval)));
+
+            // Manager properties            
+            attribute = "entropy";
+            String entropy = cform.getMgrSessionIDInit();
+            if ((entropy!=null) && (entropy.length()>=1)) {
+                mBServer.setAttribute(moname,
+                                  new Attribute("entropy",entropy));
+            }
+            
+            //attribute = "checkInterval";
+            //try {
+            //    checkInterval = Integer.parseInt(cform.getMgrCheckInterval());
+            //} catch (Throwable t) {
+            //    checkInterval = 60;
+            //}
+            //mBServer.setAttribute(moname,
+            //                      new Attribute("checkInterval", new Integer(checkInterval)));
+            
+            attribute = "maxActiveSessions";
+            int maxActiveSessions = -1;
+            try {
+                maxActiveSessions = Integer.parseInt(cform.getMgrMaxSessions());
+            } catch (Throwable t) {
+                maxActiveSessions = -1;
+            }
+            mBServer.setAttribute(moname,
+                                  new Attribute("maxActiveSessions", new Integer(maxActiveSessions)));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+        
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+        
+    }
+    
+    
+    /**
+     * Append nodes for any define resources for the specified Context.
+     *
+     * @param containerNode Container node for the tree control
+     * @param containerName Object name of the parent container
+     * @param resources The MessageResources for our localized messages
+     *  messages
+     */
+    public void addToTreeControlNode(ObjectName oname, String containerName, 
+                                    String parentName, MessageResources resources, 
+                                    HttpSession session, Locale locale) 
+        throws Exception {
+                              
+        String domain = oname.getDomain();
+        TreeControl control = (TreeControl) session.getAttribute("treeControlTest");
+        if (control != null) {
+            TreeControlNode parentNode = control.findNode(parentName);
+            if (parentNode != null) {
+                String type = "Context";
+                String path = "";
+                String host = "";
+                String name = oname.getKeyProperty("name");
+                if ((name != null) && (name.length() > 0)) {
+                    name = name.substring(2);
+                    int i = name.indexOf("/");
+                    host = name.substring(0,i);
+                    path = name.substring(i); 
+                }
+                String nodeLabel = 
+                    resources.getMessage(locale, "server.service.treeBuilder.context") + 
+                    " (" + path + ")";
+                String encodedName = URLEncoder.encode(oname.toString(),TomcatTreeBuilder.URL_ENCODING);
+                TreeControlNode childNode = 
+                    new TreeControlNode(oname.toString(),
+                                        "Context.gif",
+                                        nodeLabel,
+                                        "EditContext.do?select=" +
+                                        encodedName,
+                                        "content",
+                                        true, domain);
+                parentNode.addChild(childNode);
+        
+                // FIXME - force a redisplay
+                TreeControlNode subtree = new TreeControlNode
+                    ("Context Resource Administration " + containerName,
+                    "folder_16_pad.gif",
+                    resources.getMessage(locale, "resources.treeBuilder.subtreeNode"),
+                    null,
+                    "content",
+                    true, domain);        
+                childNode.addChild(subtree);
+                TreeControlNode datasources = new TreeControlNode
+                    ("Context Data Sources " + containerName,
+                    "Datasource.gif",
+                    resources.getMessage(locale, "resources.treeBuilder.datasources"),
+                    "resources/listDataSources.do?resourcetype=" + 
+                    URLEncoder.encode(type,TomcatTreeBuilder.URL_ENCODING) + "&path=" +
+                    URLEncoder.encode(path,TomcatTreeBuilder.URL_ENCODING) + "&host=" + 
+                    URLEncoder.encode(host,TomcatTreeBuilder.URL_ENCODING) + "&forward=" +
+                    URLEncoder.encode("DataSources List Setup",TomcatTreeBuilder.URL_ENCODING),
+                    "content",
+                    false, domain);
+                TreeControlNode mailsessions = new TreeControlNode
+                    ("Context Mail Sessions " + containerName,
+                    "Mailsession.gif",
+                    resources.getMessage(locale, "resources.treeBuilder.mailsessions"),
+                    "resources/listMailSessions.do?resourcetype=" + 
+                    URLEncoder.encode(type,TomcatTreeBuilder.URL_ENCODING) + "&path=" +
+                    URLEncoder.encode(path,TomcatTreeBuilder.URL_ENCODING) + "&host=" + 
+                    URLEncoder.encode(host,TomcatTreeBuilder.URL_ENCODING) + "&forward=" +
+                    URLEncoder.encode("MailSessions List Setup",TomcatTreeBuilder.URL_ENCODING),
+                    "content",
+                    false, domain);
+                TreeControlNode resourcelinks = new TreeControlNode
+                    ("Resource Links " + containerName,
+                    "ResourceLink.gif",
+                    resources.getMessage(locale, "resources.treeBuilder.resourcelinks"),
+                    "resources/listResourceLinks.do?resourcetype=" + 
+                    URLEncoder.encode(type,TomcatTreeBuilder.URL_ENCODING) + "&path=" +
+                    URLEncoder.encode(path,TomcatTreeBuilder.URL_ENCODING) + "&host=" + 
+                    URLEncoder.encode(host,TomcatTreeBuilder.URL_ENCODING) + "&forward=" +
+                    URLEncoder.encode("ResourceLinks List Setup",TomcatTreeBuilder.URL_ENCODING),
+                    "content",
+                    false, domain);
+                TreeControlNode envs = new TreeControlNode
+                    ("Context Environment Entries "+ containerName,
+                    "EnvironmentEntries.gif",
+                    resources.getMessage(locale ,"resources.env.entries"),
+                    "resources/listEnvEntries.do?resourcetype=" + 
+                    URLEncoder.encode(type,TomcatTreeBuilder.URL_ENCODING) + "&path=" +
+                    URLEncoder.encode(path,TomcatTreeBuilder.URL_ENCODING) + "&host=" + 
+                    URLEncoder.encode(host,TomcatTreeBuilder.URL_ENCODING) + "&forward=" +
+                    URLEncoder.encode("EnvEntries List Setup",TomcatTreeBuilder.URL_ENCODING),
+                    "content",
+                    false, domain);
+                subtree.addChild(datasources);
+                subtree.addChild(mailsessions);
+                subtree.addChild(resourcelinks);
+                subtree.addChild(envs);                    
+            } else {
+                    getServlet().log
+                        ("Cannot find parent node '" + parentName + "'");
+            } 
+        }else {
+            getServlet().log("Cannot find TreeControlNode!");
+        }                              
+    }    
+        
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/filters/SetCharacterEncodingFilter.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/filters/SetCharacterEncodingFilter.java
new file mode 100644
index 0000000..4654857
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/filters/SetCharacterEncodingFilter.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.filters;
+
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.UnavailableException;
+
+
+/**
+ * <p>Example filter that sets the character encoding to be used in parsing the
+ * incoming request, either unconditionally or only if the client did not
+ * specify a character encoding.  Configuration of this filter is based on
+ * the following initialization parameters:</p>
+ * <ul>
+ * <li><strong>encoding</strong> - The character encoding to be configured
+ *     for this request, either conditionally or unconditionally based on
+ *     the <code>ignore</code> initialization parameter.  This parameter
+ *     is required, so there is no default.</li>
+ * <li><strong>ignore</strong> - If set to "true", any character encoding
+ *     specified by the client is ignored, and the value returned by the
+ *     <code>selectEncoding()</code> method is set.  If set to "false,
+ *     <code>selectEncoding()</code> is called <strong>only</strong> if the
+ *     client has not already specified an encoding.  By default, this
+ *     parameter is set to "true".</li>
+ * </ul>
+ *
+ * <p>Although this filter can be used unchanged, it is also easy to
+ * subclass it and make the <code>selectEncoding()</code> method more
+ * intelligent about what encoding to choose, based on characteristics of
+ * the incoming request (such as the values of the <code>Accept-Language</code>
+ * and <code>User-Agent</code> headers, or a value stashed in the current
+ * user's session.</p>
+ *
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SetCharacterEncodingFilter implements Filter {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The default character encoding to set for requests that pass through
+     * this filter.
+     */
+    protected String encoding = null;
+
+
+    /**
+     * The filter configuration object we are associated with.  If this value
+     * is null, this filter instance is not currently configured.
+     */
+    protected FilterConfig filterConfig = null;
+
+
+    /**
+     * Should a character encoding specified by the client be ignored?
+     */
+    protected boolean ignore = true;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Take this filter out of service.
+     */
+    public void destroy() {
+
+        this.encoding = null;
+        this.filterConfig = null;
+
+    }
+
+
+    /**
+     * Select and set (if specified) the character encoding to be used to
+     * interpret request parameters for this request.
+     *
+     * @param request The servlet request we are processing
+     * @param result The servlet response we are creating
+     * @param chain The filter chain we are processing
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain)
+	throws IOException, ServletException {
+
+        // Conditionally select and set the character encoding to be used
+        if (ignore || (request.getCharacterEncoding() == null)) {
+            String encoding = selectEncoding(request);
+            if (encoding != null)
+                request.setCharacterEncoding(encoding);
+        }
+
+	// Pass control on to the next filter
+        chain.doFilter(request, response);
+
+    }
+
+
+    /**
+     * Place this filter into service.
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+	this.filterConfig = filterConfig;
+        this.encoding = filterConfig.getInitParameter("encoding");
+        String value = filterConfig.getInitParameter("ignore");
+        if (value == null)
+            this.ignore = true;
+        else if (value.equalsIgnoreCase("true"))
+            this.ignore = true;
+        else if (value.equalsIgnoreCase("yes"))
+            this.ignore = true;
+        else
+            this.ignore = false;
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Select an appropriate character encoding to be used, based on the
+     * characteristics of the current request and/or filter initialization
+     * parameters.  If no character encoding should be set, return
+     * <code>null</code>.
+     * <p>
+     * The default implementation unconditionally returns the value configured
+     * by the <strong>encoding</strong> initialization parameter for this
+     * filter.
+     *
+     * @param request The servlet request we are processing
+     */
+    protected String selectEncoding(ServletRequest request) {
+
+        return (this.encoding);
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AddAliasAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AddAliasAction.java
new file mode 100644
index 0000000..b96a730
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AddAliasAction.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.host;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Arrays;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.management.MBeanServer;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+import javax.management.ObjectName;
+
+/**
+ * The <code>Action</code> that sets up <em>Add Alias</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class AddAliasAction extends Action {
+    
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+    ActionForm form,
+    HttpServletRequest request,
+    HttpServletResponse response)
+    throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+
+        // the host Name is needed to retrieve the existing aliases
+        // and add new aliases to
+        String hostName = request.getParameter("hostName");
+        // Fill in the form values for display and editing
+        AliasForm aliasFm = new AliasForm();
+        session.setAttribute("aliasForm", aliasFm);
+        
+        // retrieve all aliases
+        String operation = null;
+        try {
+            ObjectName hname = new ObjectName(hostName);
+
+            operation = "findAliases";
+            String aliases[] = 
+                (String[]) mBServer.invoke(hname, operation, null, null);
+            
+            aliasFm.setAliasVals(new ArrayList(Arrays.asList(aliases)));
+
+        } catch (Throwable t) {
+            getServlet().log
+            (resources.getMessage(locale, "users.error.invoke",
+                                  operation), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                resources.getMessage(locale, "users.error.invoke",
+                                     operation));
+            return (null);            
+        }
+        
+        aliasFm.setAliasName("");
+        aliasFm.setHostName(hostName);
+
+        // Forward to the host display page
+        return (mapping.findForward("Alias"));
+        
+    }
+        
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AddHostAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AddHostAction.java
new file mode 100644
index 0000000..195a722
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AddHostAction.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.host;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+
+/**
+ * The <code>Action</code> that sets up <em>Add Host</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class AddHostAction extends Action {
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+    ActionForm form,
+    HttpServletRequest request,
+    HttpServletResponse response)
+    throws IOException, ServletException {
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // the service Name is needed to retrieve the engine mBean to
+        // which the new host mBean will be added.
+        String serviceName = request.getParameter("select");
+
+        // Fill in the form values for display and editing
+        HostForm hostFm = new HostForm();
+        session.setAttribute("hostForm", hostFm);
+        hostFm.setAdminAction("Create");
+        hostFm.setObjectName("");
+        hostFm.setHostName("");
+        hostFm.setServiceName(serviceName);
+        hostFm.setAppBase("");
+        hostFm.setAutoDeploy("true");
+        hostFm.setDeployXML("true");
+        hostFm.setDeployOnStartup("true");
+        hostFm.setUnpackWARs("true");   
+        hostFm.setXmlNamespaceAware("false");
+        hostFm.setXmlValidation("false");
+        hostFm.setBooleanVals(Lists.getBooleanValues());
+
+        // Forward to the host display page
+        return (mapping.findForward("Host"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AliasForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AliasForm.java
new file mode 100644
index 0000000..567fe5d
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AliasForm.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.host;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.util.List;
+
+/**
+ * Form bean for the alias page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class AliasForm extends ActionForm {
+    
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The text for the hostName.
+     */
+    private String hostName = null;
+
+    /**
+     * The text for the aliasName.
+     */
+    private String aliasName = null;
+
+    /*
+     * Represent aliases as a List.
+     */    
+    private List aliasVals = null;
+   
+    // ------------------------------------------------------------- Properties
+    
+    /**
+     * Return the host name.
+     */
+    public String getHostName() {
+        
+        return this.hostName;
+        
+    }
+    
+    /**
+     * Set the host name.
+     */
+    public void setHostName(String hostName) {
+        
+        this.hostName = hostName;
+        
+    }
+
+    /**
+     * Return the alias name.
+     */
+    public String getAliasName() {
+        
+        return this.aliasName;
+        
+    }
+    
+    /**
+     * Set the alias name.
+     */
+    public void setAliasName(String aliasName) {
+        
+        this.aliasName = aliasName;
+        
+    }
+
+    /**
+     * Return the List of alias Vals.
+     */
+    public List getAliasVals() {
+        
+        return this.aliasVals;
+        
+    }
+    
+    /**
+     * Set the alias Vals.
+     */
+    public void setAliasVals(List aliasVals) {
+        
+        this.aliasVals = aliasVals;
+        
+    }
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+        
+        this.aliasName = null;
+        this.hostName = null;
+
+    }
+    
+     /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("AliasForm[hostName=");
+        sb.append(hostName);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        
+        // front end validation when save is clicked.
+        //if (submit != null) {
+            
+            // aliasName cannot be null
+            if ((aliasName== null) || (aliasName.length() < 1)) {
+                errors.add("aliasName", new ActionError("error.aliasName.required"));
+            }
+                        
+        //}        
+        return errors;       
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AliasesForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AliasesForm.java
new file mode 100644
index 0000000..4cb72b3
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/AliasesForm.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.host;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for deleting aliases.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class AliasesForm extends ActionForm {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the aliases to be deleted.
+     */
+    private String aliases[] = new String[0];
+
+    public String[] getAliases() {
+        return (this.aliases);
+    }
+
+    public void setAliases(String aliases[]) {
+        this.aliases = aliases;
+    }
+
+    /**
+     * The text for the hostName.
+     */
+    private String hostName = null;
+
+    /**
+     * Return the host name.
+     */
+    public String getHostName() {
+        
+        return this.hostName;
+        
+    }
+    
+    /**
+     * Set the host name.
+     */
+    public void setHostName(String hostName) {
+        
+        this.hostName = hostName;
+        
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.aliases = new String[0];
+        this.hostName = null;        
+    }
+        
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteAliasAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteAliasAction.java
new file mode 100644
index 0000000..513ca3d
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteAliasAction.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.host;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.TreeSet;
+import java.util.Set;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * The <code>Action</code> that sets up <em>Delete Aliases</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteAliasAction extends Action {
+    
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Set up a form bean containing the currently selected
+        // objects to be deleted
+        AliasesForm aliasesFm = new AliasesForm();
+        ArrayList aliasesList = null;
+        
+        String hostName = request.getParameter("hostName");
+        aliasesFm.setHostName(hostName);
+        
+        // retrieve all aliases
+        String operation = null;
+        try {
+            ObjectName hname = new ObjectName(hostName);
+
+            operation = "findAliases";
+            String aliases[] = 
+                (String[]) mBServer.invoke(hname, operation, null, null);
+            aliasesFm.setAliases(aliases);
+            aliasesList = new ArrayList(Arrays.asList(aliases));
+
+        } catch (Throwable t) {
+            getServlet().log
+            (resources.getMessage(locale, "users.error.invoke",
+                                  operation), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                resources.getMessage(locale, "users.error.invoke",
+                                     operation));
+            return (null);            
+        }
+                
+        Collections.sort(aliasesList);        
+        request.setAttribute("aliasesForm", aliasesFm);        
+        request.setAttribute("aliasesList", aliasesList);
+        
+        // Forward to the list display page
+        return (mapping.findForward("Aliases"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteAliasForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteAliasForm.java
new file mode 100644
index 0000000..b6189d7
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteAliasForm.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.host;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+/**
+ * Form bean for the "Delete Alias" page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteAliasForm extends ActionForm {
+    
+  // No extensions needed
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteAliasesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteAliasesAction.java
new file mode 100644
index 0000000..61a1082
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteAliasesAction.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.host;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ObjectInstance;
+import javax.management.modelmbean.ModelMBean;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+
+/**
+ * The <code>Action</code> that completes <em>Delete Aliases</em>
+ * transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteAliasesAction extends Action {
+
+
+    /**
+     * Signature for the <code>removeAlias</code> operation.
+     */
+    private String removeAliasTypes[] =
+    { "java.lang.String",      // Object name
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        
+        // Look up the components we will be using as needed
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        AliasesForm aliasesFm = (AliasesForm) form;
+        // the host Name is needed to delete the existing aliases from
+        String hostName = aliasesFm.getHostName();
+        
+        // Delete the specified Aliases
+        String aliases[]  = aliasesFm.getAliases();
+        String values[] = new String[1];
+        String operation = "removeAlias";
+
+        try {
+            
+            ObjectName hname = new ObjectName(hostName);
+
+            // Remove the specified hosts
+            for (int i = 0; i < aliases.length; i++) {
+                values[0] = aliases[i];         
+                mBServer.invoke(hname, operation, values, removeAliasTypes);
+            }
+
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      operation), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      operation));
+            return (null);
+
+        }
+
+        // Report successful completion of this transaction
+        return (mapping.findForward("Save Successful"));
+
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteHostAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteHostAction.java
new file mode 100644
index 0000000..ae959e9
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteHostAction.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.host;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.TreeSet;
+import java.util.Set;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.Lists;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * The <code>Action</code> that sets up <em>Delete Hosts</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteHostAction extends Action {
+    
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Set up a form bean containing the currently selected
+        // objects to be deleted
+        HostsForm hostsForm = new HostsForm();
+        String select = request.getParameter("select");
+        String domain = null;
+        if (select != null) {
+            String hosts[] = new String[1];
+            hosts[0] = select;
+            hostsForm.setHosts(hosts);
+                        
+            try {
+                domain = (new ObjectName(select)).getDomain();
+            } catch (Exception e) {
+                throw new ServletException
+                ("Error extracting service name from the host to be deleted", e);
+            }        
+        }
+        String adminHost = null;
+        // Get the host name the admin app runs on
+        // this host cannot be deleted from the admin tool
+        try {
+            adminHost = Lists.getAdminAppHost(
+                                  mBServer, "domain" ,request);
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.hostName.bad",
+                                        adminHost);
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+        request.setAttribute("adminAppHost", adminHost);       
+        request.setAttribute("hostsForm", hostsForm);
+        
+        // Accumulate a list of all available hosts
+        ArrayList list = new ArrayList();
+        try {
+            String pattern = domain + TomcatTreeBuilder.HOST_TYPE +
+                TomcatTreeBuilder.WILDCARD;         
+            Iterator items =
+                mBServer.queryNames(new ObjectName(pattern), null).iterator();
+            while (items.hasNext()) {
+                list.add(items.next().toString());
+            }
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.select"));
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.select"));
+            return (null);
+        }
+        Collections.sort(list);
+        request.setAttribute("hostsList", list);
+        
+        // Forward to the list display page
+        return (mapping.findForward("Hosts"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteHostsAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteHostsAction.java
new file mode 100644
index 0000000..9cd7097
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/DeleteHostsAction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.host;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ObjectInstance;
+import javax.management.modelmbean.ModelMBean;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+/**
+ * The <code>Action</code> that completes <em>Delete Hosts</em>
+ * transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteHostsAction extends Action {
+
+
+    /**
+     * Signature for the <code>removeHost</code> operation.
+     */
+    private String removeHostTypes[] =
+    { "java.lang.String",      // Object name
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        
+        // Look up the components we will be using as needed
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+               
+        // Delete the specified Hosts
+        String hosts[]  = ((HostsForm) form).getHosts();
+        String values[] = new String[1];
+        String operation = "removeHost";
+        
+        getServlet().log("enter DeleteHosts " + hosts);
+
+        try {
+
+            // Look up our tree control data structure
+            TreeControl control = (TreeControl)
+                session.getAttribute("treeControlTest");
+
+            // Remove the specified hosts
+            for (int i = 0; i < hosts.length; i++) {
+                values[0] = hosts[i];
+                getServlet().log("remove host " + hosts[i]);
+                if (control != null) {
+                    control.selectNode(null);
+                    TreeControlNode node = control.findNode(hosts[i]);
+                    String domain = node.getDomain();
+                    ObjectName fname = TomcatTreeBuilder.getMBeanFactory();
+                    mBServer.invoke(fname, operation,
+                                values, removeHostTypes);
+                    if (node != null) {
+                        node.remove();
+                    } else {
+                        getServlet().log("Missing TreeControlNode for " +
+                                         hosts[i]);
+                    }
+                } else {
+                    getServlet().log("Missing TreeControl attribute");
+                }
+            }
+
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      operation), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      operation));
+            return (null);
+
+        }
+
+        // Report successful completion of this transaction
+        return (mapping.findForward("Save Successful"));
+
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/EditHostAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/EditHostAction.java
new file mode 100644
index 0000000..6951766
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/EditHostAction.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.host;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Arrays;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * The <code>Action</code> that sets up <em>Edit Host</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class EditHostAction extends Action {
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+
+        // Set up the object names of the MBeans we are manipulating
+        ObjectName hname = null;
+        StringBuffer sb = null;
+        try {
+            hname = new ObjectName(request.getParameter("select"));
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.hostName.bad",
+                                     request.getParameter("select"));
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+        
+        String adminHost = null;
+        String domain = hname.getDomain();
+        // Get the host name the admin app runs on
+        // this host cannot be deleted from the admin tool
+        try {
+            adminHost = Lists.getAdminAppHost(
+                                  mBServer, domain ,request);
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.hostName.bad",
+                                        adminHost);
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+        request.setAttribute("adminAppHost", adminHost);
+
+        // Fill in the form values for display and editing
+        HostForm hostFm = new HostForm();
+        session.setAttribute("hostForm", hostFm);
+        hostFm.setAdminAction("Edit");
+        hostFm.setObjectName(hname.toString());
+        sb = new StringBuffer();
+        sb.append(resources.getMessage(locale, "server.service.treeBuilder.host"));
+        sb.append(" (");
+        sb.append(hname.getKeyProperty("host"));
+        sb.append(")");
+        hostFm.setNodeLabel(sb.toString());
+        hostFm.setBooleanVals(Lists.getBooleanValues());
+
+        String attribute = null;
+        try {
+
+            // Copy scalar properties
+            attribute = "name";
+            hostFm.setHostName
+                ((String) mBServer.getAttribute(hname, attribute));
+
+            attribute = "appBase";
+            hostFm.setAppBase
+                ((String) mBServer.getAttribute(hname, attribute));
+            attribute = "autoDeploy";
+            hostFm.setAutoDeploy
+                (((Boolean) mBServer.getAttribute(hname, attribute)).toString());
+            attribute = "deployXML";
+            hostFm.setDeployXML
+                (((Boolean) mBServer.getAttribute(hname, attribute)).toString());
+            attribute = "deployOnStartup";
+            hostFm.setDeployOnStartup
+                (((Boolean) mBServer.getAttribute(hname, attribute)).toString());                
+            attribute = "unpackWARs";
+            hostFm.setUnpackWARs
+                (((Boolean) mBServer.getAttribute(hname, attribute)).toString());
+            attribute = "xmlNamespaceAware";
+            hostFm.setXmlNamespaceAware
+                (((Boolean) mBServer.getAttribute(hname, attribute)).toString());
+            attribute = "xmlValidation";
+            hostFm.setXmlValidation
+                (((Boolean) mBServer.getAttribute(hname, attribute)).toString());
+
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+            return (null);
+        }
+
+        // retrieve all aliases
+        String operation = null;
+        try {
+            operation = "findAliases";
+            String aliases[] =
+                (String[]) mBServer.invoke(hname, operation, null, null);
+
+            hostFm.setAliasVals(new ArrayList(Arrays.asList(aliases)));
+
+        } catch (Throwable t) {
+            getServlet().log
+            (resources.getMessage(locale, "users.error.invoke",
+                                  operation), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                resources.getMessage(locale, "users.error.invoke",
+                                     operation));
+            return (null);
+        }
+
+        // Forward to the host display page
+        return (mapping.findForward("Host"));
+
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/HostForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/HostForm.java
new file mode 100644
index 0000000..ed7e7d3
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/HostForm.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.host;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.util.List;
+
+/**
+ * Form bean for the host page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class HostForm extends ActionForm {
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The administrative action represented by this form.
+     */
+    private String adminAction = "Edit";
+
+    /**
+     * The object name of this Host bean refers to.
+     */
+    private String objectName = null;
+
+    /**
+     * The text for the node label. This is of the form 'Host(name)'
+     * and is picked up from the node of the tree that is clicked on.
+     */
+    private String nodeLabel = null;
+
+    /**
+     * The text for the hostName.
+     */
+    private String hostName = null;
+
+    /**
+     * The object name of the service this host belongs to.
+     */
+    private String serviceName = null;
+
+    /**
+     * The directory for the appBase.
+     */
+    private String appBase = null;
+
+    /**
+     * Boolean for autoDeploy.
+     */
+    private String autoDeploy = "true";
+
+    /**
+     * Boolean for deployXML.
+     */
+    private String deployXML = "true";
+
+    /**
+     * Boolean for deployOnStartup.
+     */
+    private String deployOnStartup = "true";
+    
+    /**
+     * Boolean for unpack WARs.
+     */
+    private String unpackWARs = "false";
+
+    /**
+     * The text for the port. -- TBD
+     */
+    private String findAliases = null;
+
+    /*
+     * Represent boolean (true, false) values for unpackWARs etc.
+     */
+    private List booleanVals = null;
+
+    /*
+     * Represent aliases as a List.
+     */
+    private List aliasVals = null;
+
+    /**
+     * Boolean for xmlNamespaceAware.
+     */
+    private String xmlNamespaceAware = "false";
+
+    /**
+     * Boolean for xmlValidation.
+     */
+    private String xmlValidation = "false";
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the administrative action represented by this form.
+     */
+    public String getAdminAction() {
+
+        return this.adminAction;
+
+    }
+
+
+    /**
+     * Set the administrative action represented by this form.
+     */
+    public void setAdminAction(String adminAction) {
+
+        this.adminAction = adminAction;
+
+    }
+
+    /**
+     * Return the object name of the Host this bean refers to.
+     */
+    public String getObjectName() {
+
+        return this.objectName;
+
+    }
+
+
+    /**
+     * Set the object name of the Host this bean refers to.
+     */
+    public void setObjectName(String objectName) {
+
+        this.objectName = objectName;
+
+    }
+    
+
+    /**
+     * Return the object name of the service this host belongs to.
+     */
+    public String getServiceName() {
+
+        return this.serviceName;
+
+    }
+
+
+    /**
+     * Set the object name of the Service this host belongs to.
+     */
+    public void setServiceName(String serviceName) {
+
+        this.serviceName = serviceName;
+
+    }
+
+    /**
+     * Return the label of the node that was clicked.
+     */
+    public String getNodeLabel() {
+
+        return this.nodeLabel;
+
+    }
+
+    /**
+     * Set the node label.
+     */
+    public void setNodeLabel(String nodeLabel) {
+
+        this.nodeLabel = nodeLabel;
+
+    }
+
+    /**
+     * Return the host name.
+     */
+    public String getHostName() {
+
+        return this.hostName;
+
+    }
+
+    /**
+     * Set the host name.
+     */
+    public void setHostName(String hostName) {
+
+        this.hostName = hostName;
+
+    }
+
+    /**
+     * Return the appBase.
+     */
+    public String getAppBase() {
+
+        return this.appBase;
+
+    }
+    
+    /**
+     * Return the autoDeploy.
+     */
+    public String getAutoDeploy() {
+        
+        return this.autoDeploy;
+        
+    }
+    
+    /**
+     * Set the autoDeploy.
+     */
+    
+    public void setAutoDeploy(String autoDeploy) {
+        
+        this.autoDeploy = autoDeploy;
+        
+    }
+
+    /**
+     * Return the deployXML.
+     */
+    public String getDeployXML() {
+        
+        return this.deployXML;
+        
+    }
+    
+    /**
+     * Set the deployXML.
+     */
+    
+    public void setDeployXML(String deployXML) {
+        
+        this.deployXML = deployXML;
+        
+    }
+
+    /**
+     * Return the deployOnStartup.
+     */
+    public String getDeployOnStartup() {
+        
+        return this.deployOnStartup;
+        
+    }
+    
+    /**
+     * Set the deployOnStartup.
+     */
+    
+    public void setDeployOnStartup(String deployOnStartup) {
+        
+        this.deployOnStartup = deployOnStartup;
+        
+    }
+
+    /**
+     * Set the appBase.
+     */
+
+    public void setAppBase(String appBase) {
+
+        this.appBase = appBase;
+
+    }
+
+    /**
+     * Return the unpackWARs.
+     */
+    public String getUnpackWARs() {
+
+        return this.unpackWARs;
+
+    }
+
+    /**
+     * Set the unpackWARs.
+     */
+
+    public void setUnpackWARs(String unpackWARs) {
+
+        this.unpackWARs = unpackWARs;
+
+    }
+
+    /**
+     * Return the booleanVals.
+     */
+    public List getBooleanVals() {
+
+        return this.booleanVals;
+
+    }
+
+    /**
+     * Set the booleanVals.
+     */
+    public void setBooleanVals(List booleanVals) {
+
+        this.booleanVals = booleanVals;
+
+    }
+
+    /**
+     * Return the List of alias Vals.
+     */
+    public List getAliasVals() {
+
+        return this.aliasVals;
+
+    }
+
+    /**
+     * Set the alias Vals.
+     */
+    public void setAliasVals(List aliasVals) {
+
+        this.aliasVals = aliasVals;
+
+    }
+
+    /**
+     * Return the xmlNamespaceAware.
+     */
+    public String getXmlNamespaceAware() {
+
+        return this.xmlNamespaceAware;
+
+    }
+
+    /**
+     * Set the xmlNamespaceAware.
+     */
+
+    public void setXmlNamespaceAware(String xmlNamespaceAware) {
+
+        this.xmlNamespaceAware = xmlNamespaceAware;
+
+    }
+
+    /**
+     * Return the xmlValidation.
+     */
+    public String getXmlValidation() {
+
+        return this.xmlValidation;
+
+    }
+
+    /**
+     * Set the xmlValidation.
+     */
+
+    public void setXmlValidation(String xmlValidation) {
+
+        this.xmlValidation = xmlValidation;
+
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.objectName = null;        
+        this.serviceName = null;
+        this.hostName = null;
+        this.appBase = null;
+        this.autoDeploy = "true";
+        this.deployXML = "true";
+        this.deployOnStartup = "true";
+        this.unpackWARs = "true";
+
+    }
+
+     /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("HostForm[adminAction=");
+        sb.append(adminAction);
+        sb.append(",appBase=");
+        sb.append(appBase);
+        sb.append(",autoDeploy=");
+        sb.append(autoDeploy);
+        sb.append(",deployXML=");
+        sb.append(deployXML);
+        sb.append(",deployOnStartup=");
+        sb.append(deployOnStartup);
+        sb.append(",unpackWARs=");
+        sb.append(unpackWARs);
+        sb.append("',objectName='");
+        sb.append(objectName);
+        sb.append("',hostName=");
+        sb.append(hostName);
+        sb.append("',serviceName=");
+        sb.append(serviceName);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+
+        ActionErrors errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+
+        // front end validation when save is clicked.
+        //if (submit != null) {
+
+            // hostName cannot be null
+            if ((hostName== null) || (hostName.length() < 1)) {
+                errors.add("hostName", new ActionError("error.hostName.required"));
+            }
+
+            // appBase cannot be null
+            if ((appBase == null) || (appBase.length() < 1)) {
+                errors.add("appBase", new ActionError("error.appBase.required"));
+            }
+
+        //}
+        return errors;
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/HostsForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/HostsForm.java
new file mode 100644
index 0000000..221c4fc
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/HostsForm.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.host;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for deleting hosts.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class HostsForm extends ActionForm {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the hosts to be deleted.
+     */
+    private String hosts[] = new String[0];
+
+    public String[] getHosts() {
+        return (this.hosts);
+    }
+
+    public void setHosts(String hosts[]) {
+        this.hosts = hosts;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.hosts = new String[0];
+
+    }
+        
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/SaveAliasAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/SaveAliasAction.java
new file mode 100644
index 0000000..7b2216f
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/SaveAliasAction.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.host;
+
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.List;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+
+/**
+ * The <code>Action</code> that completes <em>Add Alias</em> and
+ * <em>Edit Alias</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveAliasAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Signature for the <code>createStandardAlias</code> operation.
+     */
+    private String createStandardAliasTypes[] =
+    { "java.lang.String",     // host
+    };
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        AliasForm aform = (AliasForm) form;
+
+        // Perform a "Create Alias" transaction (if requested)
+        String operation = "addAlias";
+        
+        //alias to be added
+        Object values[] = new String[1];
+        values[0] = aform.getAliasName();
+
+        String hostName = aform.getHostName();
+
+        // validate if this alias already exists.
+        List aliasVals = aform.getAliasVals();
+        if (aliasVals.contains(values[0])) {
+           ActionErrors errors = new ActionErrors();
+            errors.add("aliasName",
+                       new ActionError("error.aliasName.exists"));
+            saveErrors(request, errors);
+            return (new ActionForward(mapping.getInput()));
+        }
+        
+        try {
+            
+            ObjectName hname = new ObjectName(hostName);
+            mBServer.invoke(hname, operation, values, createStandardAliasTypes);
+
+        } catch (Throwable t) {
+            getServlet().log
+            (resources.getMessage(locale, "users.error.invoke",
+                                  operation), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                resources.getMessage(locale, "users.error.invoke",
+                                     operation));
+            return (null);            
+        }
+                        
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+        
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/SaveHostAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/SaveHostAction.java
new file mode 100644
index 0000000..a179605
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/host/SaveHostAction.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.host;
+
+
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+
+/**
+ * The <code>Action</code> that completes <em>Add Host</em> and
+ * <em>Edit Host</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveHostAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Signature for the <code>createStandardHost</code> operation.
+     */
+    private String createStandardHostTypes[] =
+    { "java.lang.String",     // parent
+      "java.lang.String",     // name
+      "java.lang.String",     // appBase
+      "boolean",              // autoDeploy
+      "boolean",              // deployOnStartup
+      "boolean",              // deployXML
+      "boolean",              // unpackWARs
+      "boolean",              // xmlNamespaceAware
+      "boolean",              // xmlValidation
+    };
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+
+        // Identify the requested action
+        HostForm hform = (HostForm) form;
+        String adminAction = hform.getAdminAction();
+        String hObjectName = hform.getObjectName();
+        ObjectName honame = null;
+
+        // Perform a "Create Host" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            String operation = null;
+            Object values[] = null;
+
+            try {
+                String serviceName = hform.getServiceName();
+                ObjectName soname = new ObjectName(serviceName);
+                String domain = soname.getDomain();
+                // Ensure that the requested host name is unique
+                ObjectName oname =
+                    new ObjectName(domain + 
+                                   TomcatTreeBuilder.HOST_TYPE +
+                                   ",host=" + hform.getHostName());
+                if (mBServer.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("hostName",
+                               new ActionError("error.hostName.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }
+
+                // Look up our MBeanFactory MBean
+                ObjectName fname = TomcatTreeBuilder.getMBeanFactory();
+
+                // Create a new StandardHost object
+                values = new Object[9];
+                values[0] = domain + TomcatTreeBuilder.ENGINE_TYPE;
+                values[1] = hform.getHostName();
+                values[2] = hform.getAppBase();
+                values[3] = new Boolean(hform.getAutoDeploy());
+                values[4] = new Boolean(hform.getDeployOnStartup());
+                values[5] = new Boolean(hform.getDeployXML());
+                values[6] = new Boolean(hform.getUnpackWARs());
+                values[7] = new Boolean(hform.getXmlNamespaceAware());
+                values[8] = new Boolean(hform.getXmlValidation());
+
+
+                operation = "createStandardHost";
+                hObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createStandardHostTypes);
+
+                // Add the new Host to our tree control node
+                TreeControl control = (TreeControl)
+                    session.getAttribute("treeControlTest");
+                if (control != null) {
+                    String parentName = serviceName;
+                    TreeControlNode parentNode = control.findNode(parentName);
+                    if (parentNode != null) {
+                        String nodeLabel =
+                            resources.getMessage(locale, "server.service.treeBuilder.host") +
+                            " (" + hform.getHostName() + ")";
+                        String encodedName =
+                            URLEncoder.encode(hObjectName,TomcatTreeBuilder.URL_ENCODING);
+                        TreeControlNode childNode =
+                            new TreeControlNode(hObjectName,
+                                                "Host.gif",
+                                                nodeLabel,
+                                                "EditHost.do?select=" +
+                                                encodedName,
+                                                "content",
+                                                true, domain);
+                        parentNode.addChild(childNode);
+                        // FIXME - force a redisplay
+                    } else {
+                        getServlet().log
+                            ("Cannot find parent node '" + parentName + "'");
+                    }
+                } else {
+                    getServlet().log
+                        ("Cannot find TreeControlNode!");
+                }
+
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          operation), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          operation));
+                return (null);
+
+            }
+
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+
+            honame = new ObjectName(hObjectName);
+
+            attribute = "appBase";
+            String appBase = "";
+            try {
+                appBase = hform.getAppBase();
+            } catch (Throwable t) {
+                appBase = "";
+            }
+            mBServer.setAttribute(honame,
+                                  new Attribute("appBase", appBase));
+
+            attribute = "autoDeploy";
+            String autoDeploy = "true";
+            try {
+                autoDeploy = hform.getAutoDeploy();
+            } catch (Throwable t) {
+                autoDeploy = "true";
+            }
+            mBServer.setAttribute(honame,
+                                  new Attribute("autoDeploy", new Boolean(autoDeploy)));
+
+            attribute = "deployXML";
+            String deployXML = "true";
+            try {
+                deployXML = hform.getDeployXML();
+            } catch (Throwable t) {
+                deployXML = "true";
+            }
+            mBServer.setAttribute(honame,
+                                  new Attribute("deployXML", new Boolean(deployXML)));
+
+            attribute = "deployOnStartup";
+            String deployOnStartup = "true";
+            try {
+                deployOnStartup = hform.getDeployOnStartup();
+            } catch (Throwable t) {
+                deployOnStartup = "true";
+            }
+            mBServer.setAttribute(honame,
+                                  new Attribute("deployOnStartup", new Boolean(deployOnStartup)));
+                                  
+            attribute = "unpackWARs";
+            String unpackWARs = "false";
+            try {
+                unpackWARs = hform.getUnpackWARs();
+            } catch (Throwable t) {
+                unpackWARs = "false";
+            }
+            mBServer.setAttribute(honame,
+                                  new Attribute("unpackWARs", new Boolean(unpackWARs)));
+
+            attribute = "xmlNamespaceAware";
+            String xmlNamespaceAware = "false";
+            try {
+                xmlNamespaceAware = hform.getXmlNamespaceAware();
+            } catch (Throwable t) {
+                xmlNamespaceAware = "false";
+            }
+            mBServer.setAttribute(honame,
+                                  new Attribute("xmlNamespaceAware", new Boolean(xmlNamespaceAware)));
+
+            attribute = "xmlValidation";
+            String xmlValidation = "false";
+            try {
+                xmlValidation = hform.getXmlValidation();
+            } catch (Throwable t) {
+                xmlValidation = "false";
+            }
+            mBServer.setAttribute(honame,
+                                  new Attribute("xmlValidation", new Boolean(xmlValidation)));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/AddRealmAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/AddRealmAction.java
new file mode 100644
index 0000000..c414462
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/AddRealmAction.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+
+/**
+ * The <code>Action</code> that sets up <em>Add Realm</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class AddRealmAction extends Action {
+
+    // the list for types of realms
+    private ArrayList types = null;
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        // Fill in the form values for display and editing
+
+        String realmTypes[] = new String[5];
+        realmTypes[0] = "UserDatabaseRealm";
+        realmTypes[1] = "JNDIRealm";
+        realmTypes[2] = "MemoryRealm";
+        realmTypes[3] = "JDBCRealm";
+        realmTypes[4] = "DataSourceRealm";
+
+        String parent = request.getParameter("parent");
+        String type = request.getParameter("type");
+        if (type == null)
+            type = "UserDatabaseRealm";    // default type is UserDatabaseRealm
+        
+        types = new ArrayList();
+        // the first element in the select list should be the type selected
+        types.add(new LabelValueBean(type,
+                "AddRealm.do?parent=" + 
+                URLEncoder.encode(parent,TomcatTreeBuilder.URL_ENCODING)
+                + "&type=" + type));
+        for (int i=0; i< realmTypes.length; i++) {
+            if (!type.equalsIgnoreCase(realmTypes[i])) {
+                types.add(new LabelValueBean(realmTypes[i],
+                "AddRealm.do?parent=" + 
+                URLEncoder.encode(parent,TomcatTreeBuilder.URL_ENCODING)
+                + "&type=" + realmTypes[i]));
+            }
+        }
+
+        if ("UserDatabaseRealm".equalsIgnoreCase(type)) {
+            createUserDatabaseRealm(session, parent);
+        } else if ("JNDIRealm".equalsIgnoreCase(type)) {
+            createJNDIRealm(session, parent);
+        } else if ("MemoryRealm".equalsIgnoreCase(type)) {
+            createMemoryRealm(session, parent);
+        } else if ("JDBCRealm".equalsIgnoreCase(type)){
+            createJDBCRealm(session, parent);
+        } else if ("DataSourceRealm".equalsIgnoreCase(type)) {
+            createDataSourceRealm(session, parent);
+        }
+        // Forward to the realm display page
+        return (mapping.findForward(type));
+
+    }
+
+    private void createUserDatabaseRealm(HttpSession session, String parent) {
+
+        UserDatabaseRealmForm realmFm = new UserDatabaseRealmForm();
+        session.setAttribute("userDatabaseRealmForm", realmFm);
+        realmFm.setAdminAction("Create");
+        realmFm.setObjectName("");
+        realmFm.setParentObjectName(parent);
+        String realmType = "UserDatabaseRealm";
+        realmFm.setNodeLabel("Realm (" + realmType + ")");
+        realmFm.setRealmType(realmType);
+        realmFm.setResource("");
+        realmFm.setRealmTypeVals(types);
+    }
+
+    private void createJNDIRealm(HttpSession session, String parent) {
+
+        JNDIRealmForm realmFm = new JNDIRealmForm();
+        session.setAttribute("jndiRealmForm", realmFm);
+        realmFm.setAdminAction("Create");
+        realmFm.setObjectName("");
+        realmFm.setParentObjectName(parent);
+        String realmType = "JNDIRealm";
+        realmFm.setNodeLabel("Realm (" + realmType + ")");
+        realmFm.setRealmType(realmType);
+        realmFm.setDigest("");
+        realmFm.setRoleBase("");
+        realmFm.setUserSubtree("false");
+        realmFm.setRoleSubtree("false");
+        realmFm.setRolePattern("");
+        realmFm.setUserRoleName("");
+        realmFm.setRoleName("");
+        realmFm.setRoleBase("");
+        realmFm.setContextFactory("");
+        realmFm.setUserPattern("");
+        realmFm.setUserSearch("");
+        realmFm.setUserPassword("");
+        realmFm.setConnectionName("");
+        realmFm.setConnectionPassword("");
+        realmFm.setConnectionURL("");
+        realmFm.setSearchVals(Lists.getBooleanValues());
+        realmFm.setRealmTypeVals(types);
+    }
+
+    private void createMemoryRealm(HttpSession session, String parent) {
+
+        MemoryRealmForm realmFm = new MemoryRealmForm();
+        session.setAttribute("memoryRealmForm", realmFm);
+        realmFm.setAdminAction("Create");
+        realmFm.setObjectName("");
+        realmFm.setParentObjectName(parent);
+        String realmType = "MemoryRealm";
+        realmFm.setNodeLabel("Realm (" + realmType + ")");
+        realmFm.setRealmType(realmType);
+        realmFm.setPathName("");
+        realmFm.setRealmTypeVals(types);
+    }
+
+    private void createJDBCRealm(HttpSession session, String parent) {
+
+        JDBCRealmForm realmFm = new JDBCRealmForm();
+        session.setAttribute("jdbcRealmForm", realmFm);
+        realmFm.setAdminAction("Create");
+        realmFm.setObjectName("");
+        realmFm.setParentObjectName(parent);
+        String realmType = "JDBCRealm";
+        realmFm.setNodeLabel("Realm (" + realmType + ")");
+        realmFm.setRealmType(realmType);
+        realmFm.setDigest("");
+        realmFm.setDriver("");
+        realmFm.setRoleNameCol("");
+        realmFm.setPasswordCol("");
+        realmFm.setUserTable("");
+        realmFm.setRoleTable("");
+        realmFm.setConnectionName("");
+        realmFm.setConnectionPassword("");
+        realmFm.setConnectionURL("");
+        realmFm.setRealmTypeVals(types);
+    }
+    
+    private void createDataSourceRealm(HttpSession session, String parent) {
+
+        DataSourceRealmForm realmFm = new DataSourceRealmForm();
+        session.setAttribute("dataSourceRealmForm", realmFm);
+        realmFm.setAdminAction("Create");
+        realmFm.setObjectName("");
+        realmFm.setParentObjectName(parent);
+        String realmType = "DataSourceRealm";
+        realmFm.setNodeLabel("Realm (" + realmType + ")");
+        realmFm.setRealmType(realmType);
+        realmFm.setDataSourceName("");
+        realmFm.setDigest("");
+        realmFm.setLocalDataSource("false");
+        realmFm.setRoleNameCol("");
+        realmFm.setUserCredCol("");
+        realmFm.setUserNameCol("");
+        realmFm.setUserRoleTable("");
+        realmFm.setUserTable("");
+        realmFm.setRealmTypeVals(types);
+        realmFm.setBooleanVals(Lists.getBooleanValues());
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/DataSourceRealmForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/DataSourceRealmForm.java
new file mode 100644
index 0000000..8d67685
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/DataSourceRealmForm.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.net.InetAddress;
+import java.util.List;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the datasource realm page.
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public final class DataSourceRealmForm extends RealmForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    /**
+     * The text for the JNDI named JDBC DataSource for your database.
+     */
+    private String dataSourceName = null;
+      
+    /**
+     * The text for the digest.
+     */
+    private String digest = null;
+    
+    /** 
+     * The text for if the DataSource is local to the webapp.
+     */
+    private String localDataSource = "false";
+    
+    /**
+     * The text for the roleNameCol.
+     */
+    private String roleNameCol = null;
+    
+    /**
+     * The text for the userCredCol.
+     */
+    private String userCredCol = null;
+    
+    /**
+     * The text for the userNameCol.
+     */
+    private String userNameCol = null;
+        
+    /**
+     * The text for the userRoleTable.
+     */
+    private String userRoleTable = null;
+    
+    /**
+     * The text for the user table.
+     */
+    private String userTable = null;
+        
+    /*
+     * Represent boolean (true, false) values for unpackWARs etc.
+     */
+    private List booleanVals = null;
+    
+    // ------------------------------------------------------------- Properties
+    
+    
+    /**
+     * Return the dataSourceName.
+     */
+    public String getDataSourceName() {
+        
+        return this.dataSourceName;
+        
+    }
+    
+    /**
+     * Set the dataSourceName.
+     */
+    public void setDataSourceName(String dataSourceName) {
+        
+        this.dataSourceName = dataSourceName;
+        
+    }
+    
+    /**
+     * Return the digest.
+     */
+    public String getDigest() {
+        
+        return this.digest;
+        
+    }
+    
+    /**
+     * Set the digest.
+     */
+    public void setDigest(String digest) {
+        
+        this.digest = digest;
+        
+    }
+    
+    /**
+     * Return the localDataSource.
+     */
+    public String getLocalDataSource() {
+        
+        return this.localDataSource;
+        
+    }
+    
+    /**
+     * Set the localDataSource.
+     */
+    public void setLocalDataSource(String localDataSource) {
+        
+        this.localDataSource = localDataSource;
+        
+    }
+    
+    /**
+     * Return the roleNameCol.
+     */
+    public String getRoleNameCol() {
+        
+        return this.roleNameCol;
+        
+    }
+    
+    /**
+     * Set the roleNameCol.
+     */
+    public void setRoleNameCol(String roleNameCol) {
+        
+        this.roleNameCol = roleNameCol;
+        
+    }
+    
+    /**
+     * Return the userCredCol.
+     */
+    public String getUserCredCol() {
+        
+        return this.userCredCol;
+        
+    }
+    
+    /**
+     * Set the userCredCol.
+     */
+    public void setUserCredCol(String userCredCol) {
+        
+        this.userCredCol = userCredCol;
+        
+    }
+    
+    /**
+     * Return the userNameCol.
+     */
+    public String getUserNameCol() {
+        
+        return this.userNameCol;
+        
+    }
+    
+    /**
+     * Set the userNameCol.
+     */
+    public void setUserNameCol(String userNameCol) {
+        
+        this.userNameCol = userNameCol;
+        
+    }
+    
+    /**
+     * Return the user role table.
+     */
+    public String getUserRoleTable() {
+        
+        return this.userRoleTable;
+        
+    }
+    
+    /**
+     * Set the user role table.
+     */
+    public void setUserRoleTable(String userRoleTable) {
+        
+        this.userRoleTable = userRoleTable;
+        
+    }
+    
+    /**
+     * Return the user table.
+     */
+    public String getUserTable() {
+        
+        return this.userTable;
+        
+    }
+    
+    /**
+     * Set the user Table.
+     */
+    public void setUserTable(String userTable) {
+        
+        this.userTable = userTable;
+        
+    }
+    
+    /**
+     * Return the booleanVals.
+     */
+    public List getBooleanVals() {
+
+        return this.booleanVals;
+
+    }
+
+    /**
+     * Set the booleanVals.
+     */
+    public void setBooleanVals(List booleanVals) {
+
+        this.booleanVals = booleanVals;
+
+    }
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+        
+        super.reset(mapping, request);   
+        this.dataSourceName = null;
+        this.digest = null;
+        this.localDataSource = "false";
+        
+        this.roleNameCol = null;
+        this.userCredCol = null;
+        this.userNameCol = null;
+        this.userTable = null;
+        this.userRoleTable = null;
+        
+    }
+    
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("DataSourceRealmForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append(",dataSourceName=");
+        sb.append(dataSourceName);
+        sb.append(",digest=");
+        sb.append(digest);
+        sb.append("',localDataSource='");
+        sb.append(localDataSource);
+        sb.append("',roleNameCol=");
+        sb.append(roleNameCol);
+        sb.append("',userCredCol=");
+        sb.append(userCredCol);
+        sb.append("',userNameCol=");
+        sb.append(userNameCol);
+        sb.append("',userRoleTable=");
+        sb.append(userRoleTable);
+        sb.append("',userTable='");
+        sb.append(userTable);
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("',realmType=");
+        sb.append(getRealmType());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+    
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        //String type = request.getParameter("realmType");
+        
+        // front end validation when save is clicked.        
+         //if (submit != null) {
+             // the following fields are required.
+            
+            if ((dataSourceName == null) || (dataSourceName.length() < 1)) {
+                errors.add("dataSourceName",
+                new ActionError("error.dataSourceName.required"));
+            }
+         
+            if ((roleNameCol == null) || (roleNameCol.length() < 1)) {
+                errors.add("roleNameCol",
+                new ActionError("error.roleNameCol.required"));
+            }
+
+            if ((userCredCol == null) || (userCredCol.length() < 1)) {
+                errors.add("userCredCol",
+                new ActionError("error.userCredCol.required"));
+            }
+        
+            if ((userNameCol == null) || (userNameCol.length() < 1)) {
+                errors.add("userNameCol",
+                new ActionError("error.userNameCol.required"));
+            }
+            
+            if ((userRoleTable == null) || (userRoleTable.length() < 1)) {
+                errors.add("userRoleTable",
+                new ActionError("error.userRoleTable.required"));
+            }
+        
+            if ((userTable == null) || (userTable.length() < 1)) {
+                errors.add("userTable",
+                new ActionError("error.userTable.required"));
+            }
+            
+        //}
+                 
+        return errors;
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/DeleteRealmAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/DeleteRealmAction.java
new file mode 100644
index 0000000..c6b532b
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/DeleteRealmAction.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.realm;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.TreeSet;
+import java.util.Set;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.valve.ValveUtil;
+
+/**
+ * The <code>Action</code> that sets up <em>Delete Realms</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteRealmAction extends Action {
+    
+    
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+    ActionForm form,
+    HttpServletRequest request,
+    HttpServletResponse response)
+    throws IOException, ServletException {
+        
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        String pattern = null;
+        // Set up a form bean containing the currently selected
+        // objects to be deleted
+        RealmsForm realmsForm = new RealmsForm();
+        String select = request.getParameter("select");
+        if (select != null) {
+            String realms[] = new String[1];
+            realms[0] = select;
+            realmsForm.setRealms(realms);
+            pattern = select;
+        }
+        request.setAttribute("realmsForm", realmsForm);
+        
+        // Accumulate a list of all available realms
+        ArrayList list = new ArrayList();
+        String parent = request.getParameter("parent");
+        
+        if (parent != null) {
+            try {
+                pattern = ValveUtil.getObjectName(
+                             parent,TomcatTreeBuilder.REALM_TYPE);
+            } catch (Exception e) {
+                getServlet().log
+                (resources.getMessage(locale, "users.error.select"));
+                response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                resources.getMessage(locale, "users.error.select"));
+                return (null);
+            }
+        }
+        
+        try {
+            Iterator items =
+            mBServer.queryNames(new ObjectName(pattern), null).iterator();
+            while (items.hasNext()) {
+                list.add(items.next().toString());
+            }
+        } catch (Exception e) {
+            getServlet().log
+            (resources.getMessage(locale, "users.error.select"));
+            response.sendError
+            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+            resources.getMessage(locale, "users.error.select"));
+            return (null);
+        }
+        
+        Collections.sort(list);
+        request.setAttribute("realmsList", list);
+        
+        // Forward to the list display page
+        return (mapping.findForward("Realms"));
+        
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/DeleteRealmsAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/DeleteRealmsAction.java
new file mode 100644
index 0000000..1d70135
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/DeleteRealmsAction.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.realm;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ObjectInstance;
+import javax.management.modelmbean.ModelMBean;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+/**
+ * The <code>Action</code> that completes <em>Delete Realms</em>
+ * transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteRealmsAction extends Action {
+
+
+    /**
+     * Signature for the <code>removeRealm</code> operation.
+     */
+    private String removeRealmTypes[] =
+    { "java.lang.String",      // Object name
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        
+        // Look up the components we will be using as needed
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Delete the specified Realms
+        String realms[]  = ((RealmsForm) form).getRealms();
+        String values[] = new String[1];
+        String operation = "removeRealm";
+        try {
+
+            // Look up our tree control data structure
+            TreeControl control = (TreeControl)
+                session.getAttribute("treeControlTest");
+
+            // Remove the specified realms
+            for (int i = 0; i < realms.length; i++) {
+                values[0] = realms[i];
+                if (control != null) {
+                    control.selectNode(null);
+                    TreeControlNode node = control.findNode(realms[i]);
+                    String domain = node.getDomain();
+                    ObjectName fname = 
+                        TomcatTreeBuilder.getMBeanFactory();
+                    mBServer.invoke(fname, operation,
+                                values, removeRealmTypes);
+                    if (node != null) {
+                        node.remove();
+                    } else {
+                        getServlet().log("Missing TreeControlNode for " +
+                                         realms[i]);
+                    }
+                } else {
+                    getServlet().log("Missing TreeControl attribute");
+                }
+            }
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      operation), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      operation));
+            return (null);
+
+        }
+
+        // Report successful completion of this transaction
+        return (mapping.findForward("Save Successful"));
+
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/EditRealmAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/EditRealmAction.java
new file mode 100644
index 0000000..2be33bb
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/EditRealmAction.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * A generic <code>Action</code> that sets up <em>Edit
+ * Realm </em> transactions, based on the type of Realm.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class EditRealmAction extends Action {
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+
+        // Set up the object names of the MBeans we are manipulating
+        ObjectName rname = null;
+        StringBuffer sb = null;
+        try {
+            rname = new ObjectName(request.getParameter("select"));
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.realmName.bad",
+                                     request.getParameter("select"));
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+
+       String realmType = null;
+       String attribute = null;
+
+       // Find what type of Realm this is
+       try {
+            attribute = "className";
+            String className = (String)
+                mBServer.getAttribute(rname, attribute);
+            int period = className.lastIndexOf(".");
+            if (period >= 0)
+                realmType = className.substring(period + 1);
+        } catch (Throwable t) {
+          getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+            return (null);
+        }
+
+        // Forward to the appropriate realm display page
+
+        if ("UserDatabaseRealm".equalsIgnoreCase(realmType)) {
+               setUpUserDatabaseRealm(rname, request, response);
+        } else if ("MemoryRealm".equalsIgnoreCase(realmType)) {
+               setUpMemoryRealm(rname, request, response);
+        } else if ("JDBCRealm".equalsIgnoreCase(realmType)) {
+               setUpJDBCRealm(rname, request, response);
+        } else if ("JNDIRealm".equalsIgnoreCase(realmType)) {
+               setUpJNDIRealm(rname, request, response);
+        } else if ("DataSourceRealm".equalsIgnoreCase(realmType)) {
+                setUpDataSourceRealm(rname, request, response);
+        }
+
+        return (mapping.findForward(realmType));
+
+    }
+
+    private void setUpUserDatabaseRealm(ObjectName rname, 
+                                        HttpServletRequest request,
+                                        HttpServletResponse response)
+    throws IOException {
+        // Fill in the form values for display and editing
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        UserDatabaseRealmForm realmFm = new UserDatabaseRealmForm();
+        session.setAttribute("userDatabaseRealmForm", realmFm);
+        realmFm.setAdminAction("Edit");
+        realmFm.setObjectName(rname.toString());
+        String realmType = "UserDatabaseRealm";
+        StringBuffer sb = new StringBuffer("");
+        String host = rname.getKeyProperty("host");
+        String context = rname.getKeyProperty("path");
+        if (host!=null) {
+            sb.append("Host (" + host + ") > ");
+        }
+        if (context!=null) {
+            sb.append("Context (" + context + ") > ");
+        }
+        sb.append(resources.getMessage(locale, "server.service.treeBuilder.realm"));
+        realmFm.setNodeLabel(sb.toString());
+        realmFm.setRealmType(realmType);
+        realmFm.setAllowDeletion(allowDeletion(rname,request));
+
+        String attribute = null;
+        try {
+
+            // Copy scalar properties
+            attribute = "resourceName";
+            realmFm.setResource
+                ((String) mBServer.getAttribute(rname, attribute));
+
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+        }
+    }
+
+    private void setUpMemoryRealm(ObjectName rname, HttpServletRequest request,
+                                        HttpServletResponse response)
+    throws IOException {
+        // Fill in the form values for display and editing
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MemoryRealmForm realmFm = new MemoryRealmForm();
+        session.setAttribute("memoryRealmForm", realmFm);
+        realmFm.setAdminAction("Edit");
+        realmFm.setObjectName(rname.toString());
+        String realmType = "MemoryRealm";
+        StringBuffer sb = new StringBuffer();
+        sb.append(resources.getMessage(locale, "server.service.treeBuilder.realm"));
+        sb.append(" (");
+        sb.append(realmType);
+        sb.append(")");
+        realmFm.setNodeLabel(sb.toString());
+        realmFm.setRealmType(realmType);
+        realmFm.setAllowDeletion(allowDeletion(rname,request));
+
+        String attribute = null;
+        try {
+
+            // Copy scalar properties
+            attribute = "pathname";
+            realmFm.setPathName
+                ((String) mBServer.getAttribute(rname, attribute));
+
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+        }
+    }
+
+    private void setUpJDBCRealm(ObjectName rname, HttpServletRequest request,
+                                        HttpServletResponse response)
+    throws IOException {
+        // Fill in the form values for display and editing
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        JDBCRealmForm realmFm = new JDBCRealmForm();
+        session.setAttribute("jdbcRealmForm", realmFm);
+        realmFm.setAdminAction("Edit");
+        realmFm.setObjectName(rname.toString());
+        String realmType = "JDBCRealm";
+        StringBuffer sb = new StringBuffer();
+        sb.append(resources.getMessage(locale, "server.service.treeBuilder.realm"));
+        sb.append(" (");
+        sb.append(realmType);
+        sb.append(")");
+        realmFm.setNodeLabel(sb.toString());
+        realmFm.setRealmType(realmType);
+        realmFm.setAllowDeletion(allowDeletion(rname,request));
+
+        String attribute = null;
+        try {
+
+            // Copy scalar properties
+            attribute = "digest";
+            realmFm.setDigest
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "driverName";
+            realmFm.setDriver
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "roleNameCol";
+            realmFm.setRoleNameCol
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userNameCol";
+            realmFm.setUserNameCol
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userCredCol";
+            realmFm.setPasswordCol
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userTable";
+            realmFm.setUserTable
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userRoleTable";
+            realmFm.setRoleTable
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "connectionName";
+            realmFm.setConnectionName
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "connectionPassword";
+            realmFm.setConnectionPassword
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "connectionURL";
+            realmFm.setConnectionURL
+                ((String) mBServer.getAttribute(rname, attribute));
+
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+        }
+    }
+
+    private void setUpJNDIRealm(ObjectName rname, HttpServletRequest request,
+                                        HttpServletResponse response)
+    throws IOException {
+        // Fill in the form values for display and editing
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        JNDIRealmForm realmFm = new JNDIRealmForm();
+        session.setAttribute("jndiRealmForm", realmFm);
+        realmFm.setAdminAction("Edit");
+        realmFm.setObjectName(rname.toString());
+        String realmType = "JNDIRealm";
+        StringBuffer sb = new StringBuffer();
+        sb.append(resources.getMessage(locale, "server.service.treeBuilder.realm"));
+        sb.append(" (");
+        sb.append(realmType);
+        sb.append(")");
+        realmFm.setNodeLabel(sb.toString());
+        realmFm.setRealmType(realmType);
+        realmFm.setSearchVals(Lists.getBooleanValues());
+        realmFm.setAllowDeletion(allowDeletion(rname,request));
+
+        String attribute = null;
+        try {
+
+            // Copy scalar properties
+            attribute = "digest";
+            realmFm.setDigest
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userSubtree";
+            realmFm.setUserSubtree
+                    (((Boolean) mBServer.getAttribute(rname, attribute)).toString());
+            attribute = "roleSubtree";
+            realmFm.setRoleSubtree
+                    (((Boolean) mBServer.getAttribute(rname, attribute)).toString());
+            attribute = "userRoleName";
+            realmFm.setUserRoleName
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "roleName";
+            realmFm.setRoleName
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "roleBase";
+            realmFm.setRoleBase
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "roleSearch";
+            realmFm.setRolePattern
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "contextFactory";
+            realmFm.setContextFactory
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userPassword";
+            realmFm.setUserPassword
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userPattern";
+            realmFm.setUserPattern
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userSearch";
+            realmFm.setUserSearch
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "connectionName";
+            realmFm.setConnectionName
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "connectionPassword";
+            realmFm.setConnectionPassword
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "connectionURL";
+            realmFm.setConnectionURL
+                ((String) mBServer.getAttribute(rname, attribute));
+
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+        }
+    }
+
+    private void setUpDataSourceRealm(ObjectName rname, HttpServletRequest request,
+                                        HttpServletResponse response)
+    throws IOException {
+        // Fill in the form values for display and editing
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        DataSourceRealmForm realmFm = new DataSourceRealmForm();
+        session.setAttribute("dataSourceRealmForm", realmFm);
+        realmFm.setAdminAction("Edit");
+        realmFm.setObjectName(rname.toString());
+        String realmType = "DataSourceRealm";
+        StringBuffer sb = new StringBuffer();
+        sb.append(resources.getMessage(locale, "server.service.treeBuilder.realm"));
+        sb.append(" (");
+        sb.append(realmType);
+        sb.append(")");
+        realmFm.setNodeLabel(sb.toString());
+        realmFm.setRealmType(realmType);
+        realmFm.setAllowDeletion(allowDeletion(rname,request));
+        realmFm.setBooleanVals(Lists.getBooleanValues());
+
+        String attribute = null;
+        try {
+
+            // Copy scalar properties
+            attribute = "dataSourceName";
+            realmFm.setDataSourceName
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "digest";
+            realmFm.setDigest
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "localDataSource";
+            realmFm.setLocalDataSource
+                (((Boolean) mBServer.getAttribute(rname, attribute)).toString());
+            attribute = "roleNameCol";
+            realmFm.setRoleNameCol
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userCredCol";
+            realmFm.setUserCredCol
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userNameCol";
+            realmFm.setUserNameCol
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userRoleTable";
+            realmFm.setUserRoleTable
+                ((String) mBServer.getAttribute(rname, attribute));
+            attribute = "userTable";
+            realmFm.setUserTable
+                ((String) mBServer.getAttribute(rname, attribute));
+
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+        }
+    }
+
+    /*
+     * Check if "delete this realm" operation should be enabled.
+     * this operation is not allowed in case the realm is under service,
+     * host or context that the admin app runs on.
+     * return "true" if deletion is allowed.
+     */
+
+    private String allowDeletion(ObjectName rname, HttpServletRequest request) {
+
+     boolean retVal = true;
+     try{
+        // admin app's values
+        String adminService = Lists.getAdminAppService(
+                              mBServer, rname.getDomain(),request);
+        String adminHost = request.getServerName();
+        String adminContext = request.getContextPath();
+
+        //String thisService = rname.getKeyProperty("service");
+        String domain = rname.getDomain();
+        String thisHost = rname.getKeyProperty("host");
+        String thisContext = rname.getKeyProperty("path");
+
+        // realm is under context
+        if (thisContext!=null) {
+            retVal = !(thisContext.equalsIgnoreCase(adminContext));
+        } else if (thisHost != null) {
+            // realm is under host
+            retVal = !(thisHost.equalsIgnoreCase(adminHost));
+        } else {
+            // XXX FIXME
+            // realm is under service
+            return "false";
+            //retVal = !(thisService.equalsIgnoreCase(adminService));
+        }
+
+     } catch (Exception e) {
+           getServlet().log("Error getting admin service, host or context", e);
+     }
+        return new Boolean(retVal).toString();
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/JDBCRealmForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/JDBCRealmForm.java
new file mode 100644
index 0000000..4cf8222
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/JDBCRealmForm.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.net.InetAddress;
+import java.util.List;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the jdbc realm page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class JDBCRealmForm extends RealmForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    /**
+     * The text for the realm name, used to retrieve
+     * the corresponding realm mBean.
+     */
+    private String realmName = null;
+      
+    /**
+     * The text for the digest.
+     */
+    private String digest = null;
+    
+    /**
+     * The text for the roleNameCol.
+     */
+    private String roleNameCol = null;
+
+    /**
+     * The text for the userNameCol.
+     */
+    private String userNameCol = null;
+
+    /**
+     * The text for the passwordCol.
+     */
+    private String passwordCol = null;
+    
+    /**
+     * The text for the driver.
+     */
+    private String driver = null;
+        
+    /**
+     * The text for the role table.
+     */
+    private String roleTable = null;
+    
+    /**
+     * The text for the user table.
+     */
+    private String userTable = null;
+        
+    /**
+     * The text for the connection user name.
+     */
+    private String connectionName = null;
+    
+    /**
+     * The text for the connection Password.
+     */
+    private String connectionPassword = null;
+    
+    /**
+     * The text for the connection URL.
+     */
+    private String connectionURL = null;
+    
+    // ------------------------------------------------------------- Properties
+    
+    
+    /**
+     * Return the digest.
+     */
+    public String getDigest() {
+        
+        return this.digest;
+        
+    }
+    
+    /**
+     * Set the digest.
+     */
+    public void setDigest(String digest) {
+        
+        this.digest = digest;
+        
+    }
+    
+    /**
+     * Return the roleNameCol.
+     */
+    public String getRoleNameCol() {
+        
+        return this.roleNameCol;
+        
+    }
+    
+    /**
+     * Set the roleNameCol.
+     */
+    public void setRoleNameCol(String roleNameCol) {
+        
+        this.roleNameCol = roleNameCol;
+        
+    }
+    
+    /**
+     * Return the userNameCol.
+     */
+    public String getUserNameCol() {
+        
+        return this.userNameCol;
+        
+    }
+    
+    /**
+     * Set the userNameCol.
+     */
+    public void setUserNameCol(String userNameCol) {
+        
+        this.userNameCol = userNameCol;
+        
+    }
+    /**
+     * Return the driver.
+     */
+    public String getDriver() {
+        
+        return this.driver;
+        
+    }
+    
+    /**
+     * Set the driver.
+     */
+    public void setDriver(String driver) {
+        
+        this.driver = driver;
+        
+    }
+    
+    /**
+     * Return the role table.
+     */
+    public String getRoleTable() {
+        
+        return this.roleTable;
+        
+    }
+    
+    /**
+     * Set the roleTable.
+     */
+    public void setRoleTable(String roleTable) {
+        
+        this.roleTable = roleTable;
+        
+    }
+    
+    /**
+     * Return the user table.
+     */
+    public String getUserTable() {
+        
+        return this.userTable;
+        
+    }
+    
+    /**
+     * Set the user Table.
+     */
+    public void setUserTable(String userTable) {
+        
+        this.userTable = userTable;
+        
+    }
+    
+    /**
+     * Return the passwordCol.
+     */
+    public String getPasswordCol() {
+        
+        return this.passwordCol;
+        
+    }
+    
+    /**
+     * Set the passwordCol.
+     */
+    public void setPasswordCol(String passwordCol) {
+        
+        this.passwordCol = passwordCol;
+        
+    }
+    
+    
+    /**
+     * Return the connection name.
+     */
+    public String getConnectionName() {
+        
+        return this.connectionName;
+        
+    }
+    
+    /**
+     * Set the connectionName.
+     */
+    public void setConnectionName(String connectionName) {
+        
+        this.connectionName = connectionName;
+        
+    }
+    
+    
+    /**
+     * Return the connection password.
+     */
+    public String getConnectionPassword() {
+        
+        return this.connectionPassword;
+        
+    }
+    
+    /**
+     * Set the connection password.
+     */
+    public void setConnectionPassword(String connectionPassword) {
+        
+        this.connectionPassword = connectionPassword;
+        
+    }
+    
+    
+    /**
+     * Return the connection URL.
+     */
+    public String getConnectionURL() {
+        
+        return this.connectionURL;
+        
+    }
+    
+    /**
+     * Set the connectionURL.
+     */
+    public void setConnectionURL(String connectionURL) {
+        
+        this.connectionURL = connectionURL;
+        
+    }    
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+        
+        super.reset(mapping, request);   
+        this.digest = null;
+        this.driver = null;
+        
+        this.roleNameCol = null;
+        this.userNameCol = null;
+        this.passwordCol = null;
+        this.userTable = null;
+        this.roleTable = null;
+        
+        this.connectionName = null;
+        this.connectionPassword = null;
+        this.connectionURL = null;
+        
+    }
+    
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("UserDatabaseRealmForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append(",digest=");
+        sb.append(digest);
+        sb.append("',driver='");
+        sb.append(driver);
+        sb.append("',roleNameCol=");
+        sb.append(roleNameCol);
+        sb.append("',userNameCol=");
+        sb.append(userNameCol);
+        sb.append(",passwordCol=");
+        sb.append(passwordCol);
+        sb.append("',userTable='");
+        sb.append(userTable);
+        sb.append("',roleTable=");
+        sb.append(roleTable);
+        sb.append(",connectionName=");
+        sb.append(connectionName);        
+        sb.append("',connectionPassword=");
+        sb.append(connectionPassword);
+        sb.append(",connectionURL=");
+        sb.append(connectionURL);
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("',realmType=");
+        sb.append(getRealmType());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+    
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        //String type = request.getParameter("realmType");
+        
+        // front end validation when save is clicked.        
+         //if (submit != null) {
+             // the following fields are required.
+            
+            if ((driver == null) || (driver.length() < 1)) {
+                errors.add("driver",
+                new ActionError("error.driver.required"));
+            }
+         
+            if ((roleNameCol == null) || (roleNameCol.length() < 1)) {
+                errors.add("roleNameCol",
+                new ActionError("error.roleNameCol.required"));
+            }
+
+            if ((userNameCol == null) || (userNameCol.length() < 1)) {
+                errors.add("userNameCol",
+                new ActionError("error.userNameCol.required"));
+            }
+
+             if ((passwordCol == null) || (passwordCol.length() < 1)) {
+                errors.add("passwordCol",
+                new ActionError("error.passwordCol.required"));
+            }
+            
+            if ((userTable == null) || (userTable.length() < 1)) {
+                errors.add("userTable",
+                new ActionError("error.userTable.required"));
+            }
+            
+            if ((roleTable == null) || (roleTable.length() < 1)) {
+                errors.add("roleTable",
+                new ActionError("error.roleTable.required"));
+            }
+            
+            if ((connectionName == null) || (connectionName.length() < 1)) {
+                errors.add("connectionName",
+                new ActionError("error.connectionName.required"));
+            }
+            
+            if ((connectionPassword == null) || (connectionPassword.length() < 1)) {
+                errors.add("connectionPassword",
+                new ActionError("error.connectionPassword.required"));
+            }
+            
+             if ((connectionURL == null) || (connectionURL.length() < 1)) {
+                errors.add("connectionURL",
+                new ActionError("error.connectionURL.required"));
+            }
+        //}
+                 
+        return errors;
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/JNDIRealmForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/JNDIRealmForm.java
new file mode 100644
index 0000000..beb5f65
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/JNDIRealmForm.java
@@ -0,0 +1,598 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.net.InetAddress;
+import java.util.List;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the JNDI realm page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class JNDIRealmForm extends RealmForm {
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The text for the connection user name.
+     */
+    private String connectionName = null;
+
+    /**
+     * The text for the connection Password.
+     */
+    private String connectionPassword = null;
+
+    /**
+     * The text for the connection URL.
+     */
+    private String connectionURL = null;
+
+    /**
+     * The text for the context Factory.
+     */
+    private String contextFactory = null;
+
+    /**
+     * The text for the digest algorithm.
+     */
+    private String digest = null;
+
+    /**
+     * The text for the role Base.
+     */
+    private String roleBase = null;
+
+    /**
+     * The text for the role name.
+     */
+    private String roleName = null;
+
+    /**
+     * The text for the role Pattern.
+     */
+    private String rolePattern = null;
+
+    /**
+     * Should we search the entire subtree for matching roles?
+     */
+    private String roleSubtree = "false";
+
+    /**
+     * The text for the user Base.
+     */
+    private String userBase = null;
+
+    /**
+     * The text for the user Password.
+     */
+    private String userPassword = null;
+
+    /**
+     * The text for the user Pattern.
+     */
+    private String userPattern = null;
+
+    /**
+     * The text for the user role name.
+     */
+    private String userRoleName = null;
+
+    /**
+     * The text for the user Search.
+     */
+    private String userSearch = null;
+
+    /**
+     * Should we search the entire subtree for matching users?
+     */
+    private String userSubtree = "false";
+
+    /**
+     * Set of valid values for search subtrees(true/false).
+     */
+    private List searchVals = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the search Vals.
+     */
+    public List getSearchVals() {
+
+        return this.searchVals;
+
+    }
+
+    /**
+     * Set the search Vals.
+     */
+    public void setSearchVals(List searchVals) {
+
+        this.searchVals = searchVals;
+
+    }
+
+    /**
+     * Return the roleSubtree boolean Text.
+     */
+    public String getRoleSubtree() {
+
+        return this.roleSubtree;
+
+    }
+
+    /**
+     * Set the roleSubtree Text.
+     */
+    public void setRoleSubtree(String roleSubtree) {
+
+        this.roleSubtree = roleSubtree;
+
+    }
+
+    /**
+     * Return the userSubtree boolean Text.
+     */
+    public String getUserSubtree() {
+
+        return this.userSubtree;
+
+    }
+
+    /**
+     * Set the userSubtree Text.
+     */
+    public void setUserSubtree(String userSubtree) {
+
+        this.userSubtree = userSubtree;
+
+    }
+
+    /**
+     * Return the digest.
+     */
+    public String getDigest() {
+
+        return this.digest;
+
+    }
+
+    /**
+     * Set the digest.
+     */
+    public void setDigest(String digest) {
+
+        this.digest = digest;
+
+    }
+
+    /**
+     * Return the roleBase .
+     */
+    public String getRoleBase() {
+
+        return this.roleBase ;
+
+    }
+
+    /**
+     * Set the roleBase .
+     */
+    public void setRoleBase(String roleBase ) {
+
+        this.roleBase  = roleBase ;
+
+    }
+
+    /**
+     * Return the role name.
+     */
+    public String getRoleName() {
+
+        return this.roleName ;
+
+    }
+
+    /**
+     * Set the role name Attribute .
+     */
+    public void setRoleName(String roleName) {
+
+        this.roleName  = roleName ;
+
+    }
+
+    /**
+     * Return the userBase.
+     */
+    public String getUserBase() {
+
+        return this.userBase ;
+
+    }
+
+    /**
+     * Set the userBase.
+     */
+    public void setUserBase(String userBase ) {
+
+        this.userBase  = userBase ;
+
+    }
+
+    /**
+     * Return the user role name.
+     */
+    public String getUserRoleName() {
+
+        return this.userRoleName ;
+
+    }
+
+    /**
+     * Set the user role name Attribute .
+     */
+    public void setUserRoleName(String userRoleName) {
+
+        this.userRoleName  = userRoleName ;
+
+    }
+
+    /**
+     * Return the role Pattern
+     */
+    public String getRolePattern() {
+
+        return this.rolePattern ;
+
+    }
+
+    /**
+     * Set the role Pattern.
+     */
+    public void setRolePattern(String rolePattern ) {
+
+        this.rolePattern  = rolePattern ;
+
+    }
+
+    /**
+     * Return the user Password .
+     */
+    public String getUserPassword() {
+
+        return this.userPassword ;
+
+    }
+
+    /**
+     * Set the user Password .
+     */
+    public void setUserPassword(String userPassword ) {
+
+        this.userPassword  = userPassword ;
+
+    }
+
+
+    /**
+     * Return the user Pattern .
+     */
+    public String getUserPattern() {
+
+        return this.userPattern  ;
+
+    }
+
+    /**
+     * Set the user user Pattern  .
+     */
+    public void setUserPattern(String userPattern) {
+
+        this.userPattern   = userPattern  ;
+
+    }
+
+    /**
+     * Return the user Search.
+     */
+    public String getUserSearch() {
+
+        return this.userSearch;
+
+    }
+
+    /**
+     * Set the user user Search.
+     */
+    public void setUserSearch(String userSearch) {
+
+        this.userSearch  = userSearch;
+
+    }
+
+    /**
+     * Return the connection name.
+     */
+    public String getConnectionName() {
+
+        return this.connectionName;
+
+    }
+
+    /**
+     * Set the connectionName.
+     */
+    public void setConnectionName(String connectionName) {
+
+        this.connectionName = connectionName;
+
+    }
+
+
+    /**
+     * Return the connection password.
+     */
+    public String getConnectionPassword() {
+
+        return this.connectionPassword;
+
+    }
+
+    /**
+     * Set the connection password.
+     */
+    public void setConnectionPassword(String connectionPassword) {
+
+        this.connectionPassword = connectionPassword;
+
+    }
+
+
+    /**
+     * Return the connection URL.
+     */
+    public String getConnectionURL() {
+
+        return this.connectionURL;
+
+    }
+
+    /**
+     * Set the connectionURL.
+     */
+    public void setConnectionURL(String connectionURL) {
+
+        this.connectionURL = connectionURL;
+
+    }
+
+    /**
+     * Return the context Factory .
+     */
+    public String getContextFactory() {
+
+        return this.contextFactory ;
+
+    }
+
+    /**
+     * Set the context Factory .
+     */
+    public void setContextFactory(String contextFactory ) {
+
+        this.contextFactory  = contextFactory ;
+
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.roleSubtree="false";
+        this.userSubtree="false";
+
+        this.digest = null;
+        this.roleName = null;
+        this.userRoleName = null;
+
+        this.connectionName = null;
+        this.connectionPassword = null;
+        this.connectionURL = null;
+
+        this.rolePattern = null;
+        this.roleBase = null;
+        this.userBase = null;
+        this.userPassword = null;
+        this.userPattern = null;
+        this.userSearch = null;
+        this.contextFactory = null;
+    }
+
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("UserDatabaseRealmForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append(",userSubtree=");
+        sb.append(userSubtree);
+        sb.append(",roleSubtree=");
+        sb.append(roleSubtree);
+        sb.append(",digest=");
+        sb.append(digest);
+        sb.append("',userRoleName='");
+        sb.append(userRoleName);
+        sb.append("',roleName='");
+        sb.append(roleName);
+        sb.append("',connectionName=");
+        sb.append(connectionName);
+        sb.append(",connectionPassword=");
+        sb.append(connectionPassword);
+        sb.append("',connectionURL='");
+        sb.append(connectionURL);
+        sb.append("',rolePattern=");
+        sb.append(rolePattern);
+        sb.append(",roleBase=");
+        sb.append(roleBase);
+        sb.append("',userPassword='");
+        sb.append(userPassword);
+        sb.append(",userBase=");
+        sb.append(userBase);
+        sb.append("',userPattern=");
+        sb.append(userPattern);
+        sb.append("',userSearch=");
+        sb.append(userSearch);
+        sb.append(",contextFactory=");
+        sb.append(contextFactory);
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("',realmType=");
+        sb.append(getRealmType());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+
+        ActionErrors errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+
+        // front end validation when save is clicked.
+        //if (submit != null) {
+            // the following fields are required.
+
+            if ((connectionURL == null) || (connectionURL.length() < 1)) {
+                errors.add("connectionURL",
+                new ActionError("error.connURL.required"));
+            }
+
+            // Either userPattern or userSearch should be specified not both
+            boolean isUserPatternSpecified = false;
+            boolean isUserSearchSpecified = false;
+            if ((userPattern != null) && (userPattern.length() > 0)) {
+                isUserPatternSpecified = true;
+            }
+
+            if ((userSearch != null) && (userSearch.length() > 0)) {
+                isUserSearchSpecified = true;
+            }
+
+            if (isUserPatternSpecified && isUserSearchSpecified) {
+                errors.add("userPattern" ,
+                new ActionError("error.userPattern.userSearch.defined"));
+            }
+
+            /*if ((digest == null) || (digest.length() < 1)) {
+                errors.add("digest",
+                new ActionError("error.digest.required"));
+            } */
+
+            /*if ((roleName == null) || (roleName.length() < 1)) {
+                errors.add("roleName",
+                new ActionError("error.roleName.required"));
+            }
+
+            if ((userRoleName == null) || (userRoleName.length() < 1)) {
+                errors.add("userRoleName",
+                new ActionError("error.userRoleName.required"));
+            }
+
+            if ((rolePattern == null) || (rolePattern.length() < 1)) {
+                errors.add("rolePattern",
+                new ActionError("error.rolePattern.required"));
+            }
+
+            if ((roleBase == null) || (roleBase.length() < 1)) {
+                errors.add("roleBase",
+                new ActionError("error.roleBase.required"));
+            }
+
+            if ((userBase == null) || (userBase.length() < 1)) {
+                errors.add("userBase",
+                new ActionError("error.userBase.required"));
+            }
+
+            if ((userPassword == null) || (userPassword.length() < 1)) {
+                errors.add("userPassword",
+                new ActionError("error.userPassword.required"));
+            }
+
+            if ((userPattern == null) || (userPattern.length() < 1)) {
+                errors.add("userPattern",
+                new ActionError("error.userPattern.required"));
+            }
+
+            if ((userSearch == null) || (userSearch.length() < 1)) {
+                errors.add("userSearch",
+                new ActionError("error.userSearch.required"));
+            }
+
+            if ((connectionName == null) || (connectionName.length() < 1)) {
+                errors.add("connectionName",
+                new ActionError("error.connName.required"));
+            }
+
+            if ((connectionPassword == null) || (connectionPassword.length() < 1)) {
+                errors.add("connectionPassword",
+                new ActionError("error.connPassword.required"));
+            }
+
+            if ((contextFactory == null) || (contextFactory.length() < 1)) {
+                errors.add("contextFactory",
+                new ActionError("error.contextFactory.required"));
+            } */
+        //}
+
+        return errors;
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/MemoryRealmForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/MemoryRealmForm.java
new file mode 100644
index 0000000..19b7010
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/MemoryRealmForm.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.util.List;
+import java.util.Enumeration;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the memory realm page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class MemoryRealmForm extends RealmForm {
+    
+    // ----------------------------------------------------- Instance Variables
+        
+    /**
+     * The text for the path Name.
+     */
+    private String pathName = null;
+       
+    // ------------------------------------------------------------- Properties
+        
+    /**
+     * Return the path Name.
+     */
+    public String getPathName() {
+        
+        return this.pathName;
+        
+    }
+    
+    /**
+     * Set the path Name.
+     */
+    public void setPathName(String pathName) {
+        
+        this.pathName = pathName;
+        
+    }
+        
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+   
+        super.reset(mapping, request);
+        this.pathName = null;
+        
+    }
+    
+   /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("UserDatabaseRealmForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append(",pathname=");
+        sb.append(pathName);
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("',realmType=");
+        sb.append(getRealmType());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+        
+        // front end validation when save is clicked.
+        //if (submit != null) {
+            if ((pathName == null) || (pathName.length()<1)) {
+                errors.add("pathName",
+                new ActionError("error.pathName.required"));
+            }
+        //}        
+        return errors;
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/RealmForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/RealmForm.java
new file mode 100644
index 0000000..915fe75
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/RealmForm.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.net.InetAddress;
+import java.util.List;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the generic realm page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class RealmForm extends ActionForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    
+   /**
+     * The administrative action represented by this form.
+     */
+    private String adminAction = "Edit";
+
+    /**
+     * The object name of the realm this bean refers to.
+     */
+    private String objectName = null;
+    
+    /**
+     * The text for the realm type.
+     */
+    private String realmType = null;
+        
+    /**
+     * The text for the node label.
+     */
+    private String nodeLabel = null;
+    
+    /**
+     * The object name of the parent of this realm.
+     */
+    private String parentObjectName = null;
+        
+    /**
+     * Set of valid values for realms.
+     */
+    private List realmTypeVals = null;
+
+    /**
+     * The text for whether "delete this realm" operation is allowed
+     * on the realm or not.
+     */
+    private String allowDeletion = null;
+
+    // ------------------------------------------------------------- Properties
+
+   /**
+     * Return the administrative action represented by this form.
+     */
+    public String getAdminAction() {
+
+        return this.adminAction;
+
+    }
+
+    /**
+     * Set the administrative action represented by this form.
+     */
+    public void setAdminAction(String adminAction) {
+
+        this.adminAction = adminAction;
+
+    }
+
+    /**
+     * Return the Object Name.
+     */
+    public String getObjectName() {
+        
+        return this.objectName;
+        
+    }
+    
+    /**
+     * Set the Object Name.
+     */
+    public void setObjectName(String objectName) {
+        
+        this.objectName = objectName;
+        
+    }
+    
+    /**
+     * Return the realm type.
+     */
+    public String getRealmType() {
+        
+        return this.realmType;
+        
+    }
+    
+    /**
+     * Set the realm type.
+     */
+    public void setRealmType(String realmType) {
+        
+        this.realmType = realmType;
+        
+    }
+    
+    /**
+     * Return the label of the node that was clicked.
+     */
+    public String getNodeLabel() {
+        
+        return this.nodeLabel;
+        
+    }
+    
+    /**
+     * Set the node label.
+     */
+    public void setNodeLabel(String nodeLabel) {
+        
+        this.nodeLabel = nodeLabel;
+        
+    }
+    
+    /**
+     * Return the parent object name of the realm this bean refers to.
+     */
+    public String getParentObjectName() {
+
+        return this.parentObjectName;
+
+    }
+
+
+    /**
+     * Set the parent object name of the realm this bean refers to.
+     */
+    public void setParentObjectName(String parentObjectName) {
+
+        this.parentObjectName = parentObjectName;
+
+    }
+    
+        
+   /**
+     * Return the realmTypeVals.
+     */
+    public List getRealmTypeVals() {
+        
+        return this.realmTypeVals;
+        
+    }
+    
+    /**
+     * Set the realmTypeVals.
+     */
+    public void setRealmTypeVals(List realmTypeVals) {
+        
+        this.realmTypeVals = realmTypeVals;
+        
+    }
+    
+    /**
+     * Return the allow deletion value.
+     */
+    public String getAllowDeletion() {
+        
+        return this.allowDeletion;
+        
+    }
+    
+    /**
+     * Set the allow Deletion value.
+     */
+    public void setAllowDeletion(String allowDeletion) {
+        
+        this.allowDeletion = allowDeletion;
+        
+    }
+   
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+        
+        objectName = null;
+        adminAction = "Edit";
+        
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/RealmsForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/RealmsForm.java
new file mode 100644
index 0000000..597e59f
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/RealmsForm.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for deleting realms.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class RealmsForm extends ActionForm {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the realms to be deleted.
+     */
+    private String realms[] = new String[0];
+
+    public String[] getRealms() {
+        return (this.realms);
+    }
+
+    public void setRealms(String realms[]) {
+        this.realms = realms;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.realms = new String[0];
+
+    }
+        
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveDataSourceRealmAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveDataSourceRealmAction.java
new file mode 100644
index 0000000..da63975
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveDataSourceRealmAction.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+import org.apache.webapp.admin.valve.ValveUtil;
+
+/**
+ * The <code>Action</code> that completes <em>Add Realm</em> and
+ * <em>Edit Realm</em> transactions for DataSource realm.
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveDataSourceRealmAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Signature for the <code>createDataSourceRealm</code> operation.
+     */
+    private String createDataSourceRealmTypes[] =
+    { "java.lang.String",     // parent
+      "java.lang.String",     // dataSourceName
+      "java.lang.String",     // roleNameCol
+      "java.lang.String",     // userCredCol
+      "java.lang.String",     // userNameCol
+      "java.lang.String",     // userRoleTable
+      "java.lang.String",     // userTable
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        DataSourceRealmForm rform = (DataSourceRealmForm) form;
+        String adminAction = rform.getAdminAction();
+        String rObjectName = rform.getObjectName();
+
+        // Perform a "Create DataSource Realm" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            String operation = null;
+            String values[] = null;
+
+            try {
+
+                String parent = rform.getParentObjectName();                
+                String objectName = ValveUtil.getObjectName(parent,
+                                    TomcatTreeBuilder.REALM_TYPE);
+                
+                ObjectName pname = new ObjectName(parent);
+                StringBuffer sb = new StringBuffer(pname.getDomain());                    
+                
+                // For service, create the corresponding Engine mBean  
+                // Parent in this case needs to be the container mBean for the service 
+                try {                                                        
+                    if ("Service".equalsIgnoreCase(pname.getKeyProperty("type"))) {
+                        sb.append(":type=Engine");
+                        parent = sb.toString();
+                    }
+                } catch (Exception e) {
+                    String message =
+                        resources.getMessage(locale, "error.engineName.bad",
+                                         sb.toString());
+                    getServlet().log(message);
+                    response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+                    return (null);
+                }
+                                                
+                // Ensure that the requested user database name is unique
+                ObjectName oname =
+                    new ObjectName(objectName);
+                if (mBServer.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("realmName",
+                               new ActionError("error.realmName.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }
+
+                String domain = oname.getDomain();
+                // Look up our MBeanFactory MBean
+                ObjectName fname = 
+                    TomcatTreeBuilder.getMBeanFactory();
+
+                // Create a new DataSourceRealm object
+                values = new String[7];
+                values[0] = parent;
+		values[1] = rform.getDataSourceName();
+		values[2] = rform.getRoleNameCol();
+		values[3] = rform.getUserCredCol();
+		values[4] = rform.getUserNameCol();
+		values[5] = rform.getUserRoleTable();
+                values[6] = rform.getUserTable();
+                operation = "createDataSourceRealm";
+                rObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createDataSourceRealmTypes);
+                                    
+                if (rObjectName==null) {
+                    request.setAttribute("warning", "error.datasourcerealm");
+                    return (mapping.findForward("Save Unsuccessful"));
+                }
+
+                // Add the new Realm to our tree control node
+                TreeControl control = (TreeControl)
+                    session.getAttribute("treeControlTest");
+                if (control != null) {
+                    TreeControlNode parentNode = control.findNode(rform.getParentObjectName());
+                    if (parentNode != null) {
+                        String nodeLabel = rform.getNodeLabel();                        
+                        String encodedName =
+                            URLEncoder.encode(rObjectName,TomcatTreeBuilder.URL_ENCODING);
+                        TreeControlNode childNode =
+                            new TreeControlNode(rObjectName,
+                                                "Realm.gif",
+                                                nodeLabel,
+                                                "EditRealm.do?select=" +
+                                                encodedName,
+                                                "content",
+                                                true, domain);
+                        parentNode.addChild(childNode);
+                        // FIXME - force a redisplay
+                    } else {
+                        getServlet().log
+                            ("Cannot find parent node '" + parent + "'");
+                    }
+                } else {
+                    getServlet().log
+                        ("Cannot find TreeControlNode!");
+                }
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          operation), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          operation));
+                return (null);
+
+            }
+
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+
+            ObjectName roname = new ObjectName(rObjectName);
+
+            attribute = "dataSourceName";
+            mBServer.setAttribute(roname,
+                                  new Attribute(attribute, rform.getDataSourceName()));
+
+            attribute = "digest";
+            mBServer.setAttribute(roname,
+                                  new Attribute("digest",  rform.getDigest()));
+
+            attribute = "roleNameCol";
+            mBServer.setAttribute(roname,
+                                  new Attribute("roleNameCol",  rform.getRoleNameCol()));
+
+            attribute = "userCredCol";
+            mBServer.setAttribute(roname,
+                                  new Attribute("userCredCol",  rform.getUserCredCol()));
+
+            attribute = "userNameCol";
+            mBServer.setAttribute(roname,
+                                  new Attribute("userNameCol",  rform.getUserNameCol()));
+
+            attribute = "userRoleTable";
+            mBServer.setAttribute(roname,
+                                  new Attribute("userRoleTable",  rform.getUserRoleTable()));
+
+            attribute = "userTable";
+            mBServer.setAttribute(roname,
+                                  new Attribute("userTable",  rform.getUserTable()));
+            
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+        
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+        
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveJDBCRealmAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveJDBCRealmAction.java
new file mode 100644
index 0000000..d8baa37
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveJDBCRealmAction.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+import org.apache.webapp.admin.valve.ValveUtil;
+
+/**
+ * The <code>Action</code> that completes <em>Add Realm</em> and
+ * <em>Edit Realm</em> transactions for JDBC realm.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveJDBCRealmAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Signature for the <code>createStandardRealm</code> operation.
+     */
+    private String createStandardRealmTypes[] =
+    { "java.lang.String",     // parent
+      "java.lang.String",     // driverName
+      "java.lang.String",     // connectionName
+      "java.lang.String",     // connectionPassword
+      "java.lang.String",     // connectionURL
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        JDBCRealmForm rform = (JDBCRealmForm) form;
+        String adminAction = rform.getAdminAction();
+        String rObjectName = rform.getObjectName();
+
+        // Perform a "Create JDBC Realm" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            String operation = null;
+            String values[] = null;
+
+            try {
+
+                String parent = rform.getParentObjectName();                
+                String objectName = ValveUtil.getObjectName(parent,
+                                    TomcatTreeBuilder.REALM_TYPE);
+                
+                ObjectName pname = new ObjectName(parent);
+                StringBuffer sb = new StringBuffer(pname.getDomain());                    
+                
+                // For service, create the corresponding Engine mBean  
+                // Parent in this case needs to be the container mBean for the service 
+                try {                                                        
+                    if ("Service".equalsIgnoreCase(pname.getKeyProperty("type"))) {
+                        sb.append(":type=Engine");
+                        parent = sb.toString();
+                    }
+                } catch (Exception e) {
+                    String message =
+                        resources.getMessage(locale, "error.engineName.bad",
+                                         sb.toString());
+                    getServlet().log(message);
+                    response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+                    return (null);
+                }
+                                                
+                // Ensure that the requested user database name is unique
+                ObjectName oname =
+                    new ObjectName(objectName);
+                if (mBServer.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("realmName",
+                               new ActionError("error.realmName.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }
+
+                String domain = oname.getDomain();
+                // Look up our MBeanFactory MBean
+                ObjectName fname = 
+                    TomcatTreeBuilder.getMBeanFactory();
+
+                // Create a new StandardRealm object
+                values = new String[5];
+                values[0] = parent;
+		values[1] = rform.getDriver();
+		values[2] = rform.getConnectionName();
+		values[3] = rform.getConnectionPassword();
+		values[4] = rform.getConnectionURL();
+                operation = "createJDBCRealm";
+                rObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createStandardRealmTypes);
+                                    
+                if (rObjectName==null) {
+                    request.setAttribute("warning", "error.jdbcrealm");
+                    return (mapping.findForward("Save Unsuccessful"));
+                }
+
+                // Add the new Realm to our tree control node
+                TreeControl control = (TreeControl)
+                    session.getAttribute("treeControlTest");
+                if (control != null) {
+                    TreeControlNode parentNode = control.findNode(rform.getParentObjectName());
+                    if (parentNode != null) {
+                        String nodeLabel = rform.getNodeLabel();                        
+                        String encodedName =
+                            URLEncoder.encode(rObjectName,TomcatTreeBuilder.URL_ENCODING);
+                        TreeControlNode childNode =
+                            new TreeControlNode(rObjectName,
+                                                "Realm.gif",
+                                                nodeLabel,
+                                                "EditRealm.do?select=" +
+                                                encodedName,
+                                                "content",
+                                                true, domain);
+                        parentNode.addChild(childNode);
+                        // FIXME - force a redisplay
+                    } else {
+                        getServlet().log
+                            ("Cannot find parent node '" + parent + "'");
+                    }
+                } else {
+                    getServlet().log
+                        ("Cannot find TreeControlNode!");
+                }
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          operation), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          operation));
+                return (null);
+
+            }
+
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        String value = null;
+
+        try {
+
+            ObjectName roname = new ObjectName(rObjectName);
+
+            attribute = "digest";
+            value = rform.getDigest();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+            attribute = "driverName";
+            value = rform.getDriver();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+            attribute = "roleNameCol";
+            value = rform.getRoleNameCol();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+            attribute = "userNameCol";
+            value = rform.getUserNameCol();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+            attribute = "userCredCol";
+            value = rform.getPasswordCol();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+            attribute = "userTable";
+            value = rform.getUserTable();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+            attribute = "userRoleTable";
+            value = rform.getRoleTable();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+            attribute = "connectionName";
+            value = rform.getConnectionName();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+            attribute = "connectionURL";
+            value = rform.getConnectionURL();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+            attribute = "connectionPassword";
+            value = rform.getConnectionPassword();
+            setAttributeIfPresent(mBServer, roname, attribute, value);
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+        
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+        
+    }
+
+    /**
+     * Sets the given attribute to the given value
+     * in the given server's object name, if the value
+     * is not null or empty.
+     *
+     * @param theServer The server
+     * @param roname The object name
+     * @param attribute The attribute name
+     * @param value The attribute value
+     * @throws JMException If a JMX error occurs
+     */
+    protected void setAttributeIfPresent(MBeanServer mBServer, ObjectName roname, String attribute, String value)
+        throws JMException {
+
+        if((mBServer == null) || (roname == null) || (attribute == null)) {
+            throw new IllegalArgumentException("MBeanServer, ObjectName, attribute required.");
+        }
+
+        if((value != null) && (value.trim().length() > 0)) {
+            mBServer.setAttribute(roname,
+                                  new Attribute(attribute,  value));
+        }
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveJNDIRealmAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveJNDIRealmAction.java
new file mode 100644
index 0000000..7c50544
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveJNDIRealmAction.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+import org.apache.webapp.admin.valve.ValveUtil;
+
+/**
+ * The <code>Action</code> that completes <em>Add Realm</em> and
+ * <em>Edit Realm</em> transactions for JNDI realm.
+ *
+ * @author Manveen Kaur
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveJNDIRealmAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Signature for the <code>createStandardRealm</code> operation.
+     */
+    private String createStandardRealmTypes[] =
+    { "java.lang.String",     // parent
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+
+        // Identify the requested action
+        JNDIRealmForm rform = (JNDIRealmForm) form;
+        String adminAction = rform.getAdminAction();
+        String rObjectName = rform.getObjectName();
+
+        // Perform a "Create JNDI Realm" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            String operation = null;
+            String values[] = null;
+
+            try {
+
+                String parent = rform.getParentObjectName();
+                String objectName = ValveUtil.getObjectName(parent,
+                                    TomcatTreeBuilder.REALM_TYPE);
+
+                ObjectName pname = new ObjectName(parent);
+                StringBuffer sb = new StringBuffer(pname.getDomain());
+
+                // For service, create the corresponding Engine mBean
+                // Parent in this case needs to be the container mBean for the service
+                try {
+                    if ("Service".equalsIgnoreCase(pname.getKeyProperty("type"))) {
+                        sb.append(":type=Engine");
+                        parent = sb.toString();
+                    }
+                } catch (Exception e) {
+                    String message =
+                        resources.getMessage("error.engineName.bad",
+                                         sb.toString());
+                    getServlet().log(message);
+                    response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+                    return (null);
+                }
+
+                // Ensure that the requested user database name is unique
+                ObjectName oname =
+                    new ObjectName(objectName);
+                if (mBServer.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("realmName",
+                               new ActionError("error.realmName.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }
+
+                String domain = oname.getDomain();
+                // Look up our MBeanFactory MBean
+                ObjectName fname = 
+                    TomcatTreeBuilder.getMBeanFactory();
+
+                // Create a new StandardRealm object
+                values = new String[1];
+                values[0] = parent;
+                operation = "createJNDIRealm";
+                rObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createStandardRealmTypes);
+
+                if (rObjectName==null) {
+                    request.setAttribute("warning", "error.jndirealm");
+                    return (mapping.findForward("Save Unsuccessful"));
+                }
+                
+                // Add the new Realm to our tree control node
+                TreeControl control = (TreeControl)
+                    session.getAttribute("treeControlTest");
+                if (control != null) {
+                    TreeControlNode parentNode = control.findNode(rform.getParentObjectName());
+                    if (parentNode != null) {
+                        String nodeLabel = rform.getNodeLabel();
+                        String encodedName =
+                            URLEncoder.encode(rObjectName,TomcatTreeBuilder.URL_ENCODING);
+                        TreeControlNode childNode =
+                            new TreeControlNode(rObjectName,
+                                                "Realm.gif",
+                                                nodeLabel,
+                                                "EditRealm.do?select=" +
+                                                encodedName,
+                                                "content",
+                                                true, domain);
+                        parentNode.addChild(childNode);
+                        // FIXME - force a redisplay
+                    } else {
+                        getServlet().log
+                            ("Cannot find parent node '" + parent + "'");
+                    }
+                } else {
+                    getServlet().log
+                        ("Cannot find TreeControlNode!");
+                }
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          operation), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          operation));
+                return (null);
+
+            }
+
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+
+            ObjectName roname = new ObjectName(rObjectName);
+
+            attribute = "connectionName";
+            String connectionName = rform.getConnectionName();
+            if ((connectionName != null) && (connectionName.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("connectionName",  connectionName));
+            }
+
+            attribute = "connectionPassword";
+            String connectionPassword = rform.getConnectionPassword();
+            if ((connectionPassword != null) && (connectionPassword.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("connectionPassword",  connectionPassword));
+            }
+
+            attribute = "connectionURL";
+            String connectionURL = rform.getConnectionURL();
+            if ((connectionURL != null) && (connectionURL.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("connectionURL",  connectionURL));
+            }
+
+            attribute = "contextFactory";
+            String contextFactory = rform.getContextFactory();
+            if ((contextFactory != null) && (contextFactory.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("contextFactory",  contextFactory));
+            }
+
+            attribute = "digest";
+            String digest = rform.getDigest();
+            if ((digest != null) && (digest.length()>0)) {
+                mBServer.setAttribute(roname,
+                                        new Attribute("digest", digest));
+            }
+
+            attribute = "roleBase";
+            String roleBase = rform.getRoleBase();
+            if ((roleBase != null) && (roleBase.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("roleBase",  roleBase));
+            }
+
+            attribute = "roleName";
+            String roleName = rform.getRoleName();
+            if ((roleName != null) && (roleName.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("roleName",  roleName));
+            }
+
+            attribute = "roleSearch";
+            String rolePattern = rform.getRolePattern();
+            if ((rolePattern != null) && (rolePattern.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("roleSearch",  rolePattern));
+            }
+
+            attribute = "roleSubtree";
+            String roleSubtree = rform.getRoleSubtree();
+            if ((roleSubtree != null) && (roleSubtree.length()>0)) {
+                mBServer.setAttribute(roname,
+                    new Attribute("roleSubtree",  new Boolean(roleSubtree)));
+            }
+
+            attribute = "userBase";
+            String userBase = rform.getUserBase();
+            if ((userBase != null) && (userBase.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("userBase",  userBase));
+            }
+
+            attribute = "userPassword";
+            String userPassword = rform.getUserPassword();
+            if ((userPassword != null) && (userPassword.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("userPassword",  userPassword));
+            }
+
+            attribute = "userPattern";
+            String userPattern = rform.getUserPattern();
+            if ((userPattern != null) && (userPattern.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("userPattern",  userPattern));
+            }
+
+            attribute = "userRoleName";
+            String userRoleName = rform.getUserRoleName();
+            if ((userRoleName != null) && (userRoleName.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("userRoleName",  userRoleName));
+            }
+
+            attribute = "userSearch";
+            String userSearch = rform.getUserSearch();
+            if ((userSearch != null) && (userSearch.length()>0)) {
+                mBServer.setAttribute(roname,
+                        new Attribute("userSearch",  userSearch));
+            }
+
+            attribute = "userSubtree";
+            String userSubtree = rform.getUserSubtree();
+            if ((userSubtree != null) && (userSubtree.length()>0)) {
+                mBServer.setAttribute(roname,
+                    new Attribute("userSubtree",  new Boolean(userSubtree)));
+            }
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveMemoryRealmAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveMemoryRealmAction.java
new file mode 100644
index 0000000..6cca29d
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveMemoryRealmAction.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+import org.apache.webapp.admin.valve.ValveUtil;
+
+/**
+ * The <code>Action</code> that completes <em>Add Realm</em> and
+ * <em>Edit Realm</em> transactions for Memory realm.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveMemoryRealmAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Signature for the <code>createStandardRealm</code> operation.
+     */
+    private String createStandardRealmTypes[] =
+    { "java.lang.String",     // parent
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        MemoryRealmForm rform = (MemoryRealmForm) form;
+        String adminAction = rform.getAdminAction();
+        String rObjectName = rform.getObjectName();
+
+        // Perform a "Create Memory Realm" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            String operation = null;
+            String values[] = null;
+
+            try {
+
+                String parent = rform.getParentObjectName();                
+                String objectName = ValveUtil.getObjectName(parent,
+                                    TomcatTreeBuilder.REALM_TYPE);
+                
+                ObjectName pname = new ObjectName(parent);
+                StringBuffer sb = new StringBuffer(pname.getDomain());                    
+                
+                // For service, create the corresponding Engine mBean  
+                // Parent in this case needs to be the container mBean for the service 
+                try {                                                        
+                    if ("Service".equalsIgnoreCase(pname.getKeyProperty("type"))) {
+                        sb.append(":type=Engine");
+                        parent = sb.toString();
+                    }
+                } catch (Exception e) {
+                    String message =
+                        resources.getMessage(locale, "error.engineName.bad",
+                                         sb.toString());
+                    getServlet().log(message);
+                    response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+                    return (null);
+                }
+                                                
+                // Ensure that the requested user database name is unique
+                ObjectName oname =
+                    new ObjectName(objectName);
+                if (mBServer.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("realmName",
+                               new ActionError("error.realmName.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }
+
+                String domain = oname.getDomain();
+                // Look up our MBeanFactory MBean
+                ObjectName fname = TomcatTreeBuilder.getMBeanFactory();
+
+                // Create a new StandardRealm object
+                values = new String[1];
+                values[0] = parent;
+                operation = "createMemoryRealm";
+                rObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createStandardRealmTypes);
+                                    
+                // Add the new Realm to our tree control node
+                TreeControl control = (TreeControl)
+                    session.getAttribute("treeControlTest");
+                if (control != null) {
+                    TreeControlNode parentNode = control.findNode(rform.getParentObjectName());
+                    if (parentNode != null) {
+                        String nodeLabel = rform.getNodeLabel();                        
+                        String encodedName =
+                            URLEncoder.encode(rObjectName,TomcatTreeBuilder.URL_ENCODING);
+                        TreeControlNode childNode =
+                            new TreeControlNode(rObjectName,
+                                                "Realm.gif",
+                                                nodeLabel,
+                                                "EditRealm.do?select=" +
+                                                encodedName,
+                                                "content",
+                                                true, domain);
+                        parentNode.addChild(childNode);
+                        // FIXME - force a redisplay
+                    } else {
+                        getServlet().log
+                            ("Cannot find parent node '" + parent + "'");
+                    }
+                } else {
+                    getServlet().log
+                        ("Cannot find TreeControlNode!");
+                }
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          operation), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          operation));
+                return (null);
+
+            }
+
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+
+            ObjectName roname = new ObjectName(rObjectName);
+
+            attribute = "pathname";
+            mBServer.setAttribute(roname,
+                                  new Attribute("pathname",  rform.getPathName()));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+        
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+        
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveUserDatabaseRealmAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveUserDatabaseRealmAction.java
new file mode 100644
index 0000000..163439e
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/SaveUserDatabaseRealmAction.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+import org.apache.webapp.admin.valve.ValveUtil;
+
+/**
+ * The <code>Action</code> that completes <em>Add Realm</em> and
+ * <em>Edit Realm</em> transactions for UserDatabase realm.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveUserDatabaseRealmAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Signature for the <code>createUserDatabaseRealm</code> operation.
+     */
+    private String createUserDatabaseRealmTypes[] =
+    { "java.lang.String",     // parent
+      "java.lang.String",     // name
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        UserDatabaseRealmForm rform = (UserDatabaseRealmForm) form;
+        String adminAction = rform.getAdminAction();
+        String rObjectName = rform.getObjectName();
+
+        // Perform a "Create UserDatabase Realm" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            String operation = null;
+            String values[] = null;
+
+            try {
+
+                String parent = rform.getParentObjectName();                
+                String objectName = ValveUtil.getObjectName(parent,
+                                    TomcatTreeBuilder.REALM_TYPE);
+                
+                ObjectName pname = new ObjectName(parent);
+                StringBuffer sb = new StringBuffer(pname.getDomain());                    
+                
+                // For service, create the corresponding Engine mBean  
+                // Parent in this case needs to be the container mBean for the service 
+                try {                                                        
+                    if ("Service".equalsIgnoreCase(pname.getKeyProperty("type"))) {
+                        sb.append(":type=Engine");
+                        parent = sb.toString();
+                    }
+                } catch (Exception e) {
+                    String message =
+                        resources.getMessage(locale, "error.engineName.bad",
+                                         sb.toString());
+                    getServlet().log(message);
+                    response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+                    return (null);
+                }
+                                                
+                // Ensure that the requested user database name is unique
+                ObjectName oname =
+                    new ObjectName(objectName);
+                if (mBServer.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("realmName",
+                               new ActionError("error.realmName.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }
+                
+                String domain = oname.getDomain();
+                // Look up our MBeanFactory MBean
+                ObjectName fname = TomcatTreeBuilder.getMBeanFactory();
+
+                // Create a new StandardRealm object
+                values = new String[2];
+                values[0] = parent;
+                values[1] = rform.getResource();
+                operation = "createUserDatabaseRealm";
+                rObjectName = (String)
+                    mBServer.invoke(fname, operation,
+                                    values, createUserDatabaseRealmTypes);
+                if (rObjectName==null) {
+                    request.setAttribute("warning", "error.userdbrealm");
+                    return (mapping.findForward("Save Unsuccessful"));
+                }
+
+                // Add the new Realm to our tree control node
+                TreeControl control = (TreeControl)
+                    session.getAttribute("treeControlTest");
+                if (control != null) {
+                    TreeControlNode parentNode = control.findNode(rform.getParentObjectName());
+                    if (parentNode != null) {
+                        String nodeLabel = rform.getNodeLabel();                        
+                        String encodedName =
+                            URLEncoder.encode(rObjectName,TomcatTreeBuilder.URL_ENCODING);
+                        TreeControlNode childNode =
+                            new TreeControlNode(rObjectName,
+                                                "Realm.gif",
+                                                nodeLabel,
+                                                "EditRealm.do?select=" +
+                                                encodedName,
+                                                "content",
+                                                true, domain);
+                        parentNode.addChild(childNode);
+                        // FIXME - force a redisplay
+                    } else {
+                        getServlet().log
+                            ("Cannot find parent node '" + parent + "'");
+                    }
+                } else {
+                    getServlet().log
+                        ("Cannot find TreeControlNode!");
+                }
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          operation), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          operation));
+                return (null);
+
+            }
+
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+
+            ObjectName roname = new ObjectName(rObjectName);
+
+            attribute = "resourceName";
+            mBServer.setAttribute(roname,
+                                  new Attribute("resourceName",  rform.getResource()));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+        
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+        
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/UserDatabaseRealmForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/UserDatabaseRealmForm.java
new file mode 100644
index 0000000..7312395
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/realm/UserDatabaseRealmForm.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.realm;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.net.InetAddress;
+import java.util.List;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the User Database realm page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class UserDatabaseRealmForm extends RealmForm {
+    
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The text for the resource name.
+     */
+    private String resource = null;
+        
+    // ------------------------------------------------------------- Properties
+    
+    
+    /**
+     * Return the resource Name.
+     */
+    public String getResource() {
+        
+        return this.resource;
+        
+    }
+    
+    /**
+     * Set the resource Name.
+     */
+    public void setResource(String resource) {
+        
+        this.resource = resource;
+        
+    }
+        
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+        
+        super.reset(mapping, request);
+        this.resource = null;
+        
+    }
+    
+   /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("UserDatabaseRealmForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append(",resource=");
+        sb.append(getResource());
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("',realmType=");
+        sb.append(getRealmType());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        
+        // front end validation when save is clicked.
+        //if (submit != null) {
+            if ((resource == null) || (resource.length() < 1)) {
+                errors.add("resource",
+                new ActionError("error.resource.required"));
+            }
+        //}
+        return errors;
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/BaseForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/BaseForm.java
new file mode 100644
index 0000000..5647a2b
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/BaseForm.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+
+import javax.management.ObjectName;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Base class for form beans for the resource administration.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class BaseForm extends ActionForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * The node label to be displayed in the user interface.
+     */
+    private String nodeLabel = null;
+
+    public String getNodeLabel() {
+        return (this.nodeLabel);
+    }
+
+    public void setNodeLabel(String nodeLabel) {
+        this.nodeLabel = nodeLabel;
+    }
+
+
+    /**
+     * The MBean object name of this object.  A null or zero-length
+     * value indicates that this is a new object.
+     */
+    private String objectName = null;
+
+    public String getObjectName() {
+        return (this.objectName);
+    }
+
+    public void setObjectName(String objectName) {
+        if ((objectName != null) && (objectName.length() < 1)) {
+            this.objectName = null;
+        } else {
+            this.objectName = objectName;
+        }
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        nodeLabel = null;
+        objectName = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DataSourceForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DataSourceForm.java
new file mode 100644
index 0000000..60b05c7
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DataSourceForm.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the individual data source page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DataSourceForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The url of the data source.
+     */
+    private String url = null;
+
+    public String getUrl() {
+        return (this.url);
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    /**
+     * The JNDI name of the data source.
+     */
+    private String jndiName = null;
+
+    public String getJndiName() {
+        return (this.jndiName);
+    }
+
+    public void setJndiName(String jndiName) {
+        this.jndiName = jndiName;
+    }
+    
+    /**
+     * The JDBC driver class of the data source.
+     */
+    private String driverClass = null;
+
+    public String getDriverClass() {
+        return (this.driverClass);
+    }
+
+    public void setDriverClass(String driverClass) {
+        this.driverClass = driverClass;
+    }
+
+    
+    /**
+     * The username of the databse corresponding to the data source.
+     */
+    private String username = null;
+
+    public String getUsername() {
+        return (this.username);
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    
+    /**
+     * The password of the database corresponding to the data source.
+     */
+    private String password = null;
+
+    public String getPassword() {
+        return (this.password);
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    
+    /**
+     * The max number of active sessions to the data source.
+     */
+    private String active = null;
+
+    public String getActive() {
+        return (this.active);
+    }
+
+    public void setActive(String active) {
+        this.active = active;
+    }
+
+    /**
+     * The max number of idle connections to the data source.
+     */
+    private String idle = null;
+
+    public String getIdle() {
+        return (this.idle);
+    }
+
+    public void setIdle(String idle) {
+        this.idle = idle;
+    }
+
+    /**
+     * The maximum wait for a connection to the data source.
+     */
+    private String wait = null;
+
+    public String getWait() {
+        return (this.wait);
+    }
+
+    public void setWait(String wait) {
+        this.wait = wait;
+    }
+
+    /**
+     * The resource type of this data source.
+     */
+    private String resourcetype = null;
+    
+    /**
+     * Return the resource type of the data source this bean refers to.
+     */
+    public String getResourcetype() {
+        return this.resourcetype;
+    }
+
+    /**
+     * Set the resource type of the data source this bean refers to.
+     */
+    public void setResourcetype(String resourcetype) {
+        this.resourcetype = resourcetype;
+    }
+       
+    /**
+     * The path of this data source.
+     */
+    private String path = null;
+    
+    /**
+     * Return the path of the data source this bean refers to.
+     */
+    public String getPath() {
+        return this.path;
+    }
+
+    /**
+     * Set the path of the data source this bean refers to.
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+       
+    /**
+     * The host of this data source.
+     */
+    private String host = null;
+    
+    /**
+     * Return the host of the data source this bean refers to.
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Set the host of the data source this bean refers to.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }    
+    
+       
+    /**
+     * The domain of this data source.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the data source this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the data source this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    
+    /**
+     * The validation query to the data source.
+     */
+    private String query = null;
+
+    public String getQuery() {
+        return (this.query);
+    }
+
+    public void setQuery(String query) {
+        this.query = query;
+    }
+    
+    /**
+     * The type of the resource.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        url = null;        
+        jndiName = null;
+        driverClass = null;
+        username = null;
+        password = null;
+        type = null;
+    
+        active = null;
+        idle = null;
+        wait = null;
+        query = null;
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    private ActionErrors errors = null;
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+
+        errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+
+        //if (submit != null) {
+
+            // url is a required field
+            if ((url == null) || (url.length() < 1)) {
+                errors.add("url",
+                           new ActionError("resources.error.url.required"));
+            }
+
+            // jndiName is a required field
+            if (( jndiName == null) || (jndiName.length() < 1)) {
+                errors.add("jndiName",
+                           new ActionError("resources.error.jndiName.required"));
+            }
+
+            // driverClass is a required field
+            if ((driverClass == null) || (driverClass.length() < 1)) {
+                errors.add("driverClass",
+                           new ActionError("resources.error.driverClass.required"));
+            }
+            
+            // username is a required field
+            if ((username == null) || (username.length() < 1)) {
+                errors.add("username",
+                           new ActionError("users.error.username.required"));
+            }
+            
+            // commented out password can be an empty string
+            // password is a required field
+            //if ((password == null) || (password.length() < 1)) {
+            //    errors.add("password",
+            //               new ActionError("error.userPassword.required"));
+            //
+            
+            // FIX ME -- need to do a range check
+            numberCheck("active", active , false, 0, 10000);
+            numberCheck("idle", idle , false, 0, 10000);
+            numberCheck("wait", wait , false, 0, 10000);
+
+            // Quotes not allowed in username
+            if ((username != null) && (username.indexOf('"') >= 0)) {
+                errors.add("username",
+                           new ActionError("users.error.quotes"));
+            }
+
+            // Quotes not allowed in password
+            if ((password != null) && (password.indexOf('"') > 0)) {
+                errors.add("password",
+                           new ActionError("users.error.quotes"));
+            }
+         //}
+        return (errors);
+    }
+ 
+    /*
+     * Helper method to check that it is a required number and
+     * is a valid integer within the given range. (min, max).
+     *
+     * @param  field  The field name in the form for which this error occured.
+     * @param  numText  The string representation of the number.
+     * @param rangeCheck  Boolean value set to true of reange check should be performed.
+     *
+     * @param  min  The lower limit of the range
+     * @param  max  The upper limit of the range
+     *
+     */
+    
+    private void numberCheck(String field, String numText, boolean rangeCheck,
+                             int min, int max) {
+        
+        // Check for 'is required'
+        if ((numText == null) || (numText.length() < 1)) {
+            errors.add(field, new ActionError("resources.error."+field+".required"));
+        } else {
+            
+            // check for 'must be a number' in the 'valid range'
+            try {
+                int num = Integer.parseInt(numText);
+                // perform range check only if required
+                if (rangeCheck) {
+                    if ((num < min) || (num > max ))
+                        errors.add( field,
+                        new ActionError("resources.error."+ field +".range"));
+                }
+            } catch (NumberFormatException e) {
+                errors.add(field,
+                new ActionError("resources.integer.error"));
+            }
+        }
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DataSourcesForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DataSourcesForm.java
new file mode 100644
index 0000000..abfe949
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DataSourcesForm.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the delete data sources page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DataSourcesForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the specified environment entries.
+     */
+    private String dataSources[] = null;
+
+    public String[] getDataSources() {
+        return (this.dataSources);
+    }
+
+    public void setDataSources(String dataSources[]) {
+        this.dataSources = dataSources;
+    }
+
+    /**
+     * The resource type of this data source.
+     */
+    private String resourcetype = null;
+    
+    /**
+     * Return the resource type of the data source this bean refers to.
+     */
+    public String getResourcetype() {
+        return this.resourcetype;
+    }
+
+    /**
+     * Set the resource type of the data source this bean refers to.
+     */
+    public void setResourcetype(String resourcetype) {
+        this.resourcetype = resourcetype;
+    }
+       
+    /**
+     * The path of this data source.
+     */
+    private String path = null;
+    
+    /**
+     * Return the path of the data source this bean refers to.
+     */
+    public String getPath() {
+        return this.path;
+    }
+
+    /**
+     * Set the path of the data source this bean refers to.
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+       
+    /**
+     * The host of this data source.
+     */
+    private String host = null;
+    
+    /**
+     * Return the host of the data source this bean refers to.
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Set the host of the data source this bean refers to.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }    
+    
+       
+    /**
+     * The domain of this data source.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the data source this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the data source this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.dataSources = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteDataSourcesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteDataSourcesAction.java
new file mode 100644
index 0000000..1a30756
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteDataSourcesAction.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that deletes the
+ * specified set of dataSource entries.</p>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DeleteDataSourcesAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List DataSources Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        DataSourcesForm dataSourcesForm = (DataSourcesForm) form;
+        String dataSources[] = dataSourcesForm.getDataSources();
+        if (dataSources == null) {
+            dataSources = new String[0];
+        }
+
+        // Perform "Delete EnvEntry" transactions as required
+        try {
+            
+            String resourcetype = dataSourcesForm.getResourcetype();
+            String path = dataSourcesForm.getPath();
+            String host = dataSourcesForm.getHost();
+            //String domain = dataSourcesForm.getDomain();
+            
+            ObjectName dname = null;
+
+            String signature[] = new String[1];
+            signature[0] = "java.lang.String";
+            Object params[] = new String[1];
+             
+            for (int i = 0; i < dataSources.length; i++) {
+                ObjectName oname = new ObjectName(dataSources[i]);
+                String domain = oname.getDomain();
+                dname = ResourceUtils.getNamingResourceObjectName(domain,
+                            resourcetype, path, host);
+                params[0] = oname.getKeyProperty("name");
+                mserver.invoke(dname, "removeResource",
+                               params, signature);
+            }
+          
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "removeResource"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "removeResource"));
+            return (null);
+
+        }
+
+        // Proceed to the list envEntrys screen
+        return (mapping.findForward("DataSources List Setup"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteEnvEntriesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteEnvEntriesAction.java
new file mode 100644
index 0000000..6f872db
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteEnvEntriesAction.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that deletes the
+ * specified set of env entries.</p>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DeleteEnvEntriesAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List EnvEntries Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        EnvEntriesForm envEntriesForm = (EnvEntriesForm) form;
+        String envEntries[] = envEntriesForm.getEnvEntries();
+        String resourcetype = envEntriesForm.getResourcetype();
+        String path = envEntriesForm.getPath();
+        String host = envEntriesForm.getHost();
+        
+        if (envEntries == null) {
+            envEntries = new String[0];
+        }
+
+        // Perform "Delete EnvEntry" transactions as required
+        try {
+            ObjectName dname = null;
+            
+            String signature[] = new String[1];
+            signature[0] = "java.lang.String";
+            Object params[] = new String[1];
+
+            for (int i = 0; i < envEntries.length; i++) {
+                ObjectName oname = new ObjectName(envEntries[i]);
+                String domain = oname.getDomain();
+                dname = ResourceUtils.getNamingResourceObjectName(domain,
+                            resourcetype, path, host);
+                params[0] = oname.getKeyProperty("name");
+                mserver.invoke(dname, "removeEnvironment",
+                               params, signature);
+            }
+            
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "removeEnvironment"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "removeEnvironment"));
+            return (null);
+
+        }
+
+        // Proceed to the list envEntrys screen
+        return (mapping.findForward("EnvEntries List Setup"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteMailSessionsAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteMailSessionsAction.java
new file mode 100644
index 0000000..1c97d10
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteMailSessionsAction.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that deletes the
+ * specified set of mailSession entries.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DeleteMailSessionsAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List MailSessions Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        MailSessionsForm mailSessionsForm = (MailSessionsForm) form;
+        String mailSessions[] = mailSessionsForm.getMailSessions();
+        if (mailSessions == null) {
+            mailSessions = new String[0];
+        }
+
+        // Perform "Delete EnvEntry" transactions as required
+        try {
+            
+            String resourcetype = mailSessionsForm.getResourcetype();
+            String path = mailSessionsForm.getPath();
+            String host = mailSessionsForm.getHost();
+            
+            ObjectName dname = null;
+
+            String signature[] = new String[1];
+            signature[0] = "java.lang.String";
+            Object params[] = new String[1];
+             
+            for (int i = 0; i < mailSessions.length; i++) {
+                ObjectName oname = new ObjectName(mailSessions[i]);
+                String domain = oname.getDomain();
+                dname = ResourceUtils.getNamingResourceObjectName(domain,
+                            resourcetype, path, host);
+                params[0] = oname.getKeyProperty("name");
+                mserver.invoke(dname, "removeResource",
+                               params, signature);
+            }
+          
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "removeResource"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "removeResource"));
+            return (null);
+
+        }
+
+        // Proceed to the list envEntrys screen
+        return (mapping.findForward("MailSessions List Setup"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteResourceLinksAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteResourceLinksAction.java
new file mode 100644
index 0000000..2669423
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteResourceLinksAction.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that deletes the
+ * specified set of resource links entries.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DeleteResourceLinksAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List Resource Links Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        ResourceLinksForm resourceLinksForm = (ResourceLinksForm) form;
+        String resourceLinks[] = resourceLinksForm.getResourceLinks();
+        if (resourceLinks == null) {
+            resourceLinks = new String[0];
+        }
+
+        // Perform "Delete Resource Link" transactions as required
+        try {
+            
+            String resourcetype = resourceLinksForm.getResourcetype();
+            String path = resourceLinksForm.getPath();
+            String host = resourceLinksForm.getHost();
+            
+            ObjectName dname = null;
+
+            String signature[] = new String[1];
+            signature[0] = "java.lang.String";
+            Object params[] = new String[1];
+            
+            for (int i = 0; i < resourceLinks.length; i++) {
+                ObjectName oname = new ObjectName(resourceLinks[i]);
+                String domain = oname.getDomain();  
+                dname = ResourceUtils.getNamingResourceObjectName(domain,
+                            resourcetype, path, host);
+                params[0] = oname.getKeyProperty("name");
+                mserver.invoke(dname, "removeResourceLink",
+                               params, signature);
+            }
+
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "removeResource"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "removeResource"));
+            return (null);
+
+        }
+
+        // Proceed to the list envEntrys screen
+        return (mapping.findForward("ResourceLinks List Setup"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteUserDatabasesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteUserDatabasesAction.java
new file mode 100644
index 0000000..4bd8512
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/DeleteUserDatabasesAction.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that deletes the
+ * specified set of user databases.</p>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DeleteUserDatabasesAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List UserDatabases Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        UserDatabasesForm userDatabasesForm = (UserDatabasesForm) form;
+        String userDatabases[] = userDatabasesForm.getUserDatabases();
+        if (userDatabases == null) {
+            userDatabases = new String[0];
+        }
+
+        // Perform "Delete User Database" transactions as required
+        try {
+
+            String signature[] = new String[1];
+            signature[0] = "java.lang.String";
+            Object params[] = new String[1];
+
+            for (int i = 0; i < userDatabases.length; i++) {
+                ObjectName oname = new ObjectName(userDatabases[i]);
+                // Construct the MBean Name for the naming source
+                ObjectName dname = new ObjectName(oname.getDomain() +
+                                    ResourceUtils.NAMINGRESOURCES_TYPE + 
+                                    ResourceUtils.GLOBAL_TYPE);
+                params[0] = oname.getKeyProperty("name");
+                mserver.invoke(dname, "removeResource",
+                               params, signature);
+            }
+
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "removeResource"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "removeResource"));
+            return (null);
+
+        }
+
+        // Proceed to the list envEntrys screen
+        return (mapping.findForward("UserDatabases List Setup"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/EnvEntriesForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/EnvEntriesForm.java
new file mode 100644
index 0000000..d84c539
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/EnvEntriesForm.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the delete env entries page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class EnvEntriesForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the specified environment entries.
+     */
+    private String envEntries[] = null;
+
+    public String[] getEnvEntries() {
+        return (this.envEntries);
+    }
+
+    public void setEnvEntries(String envEntries[]) {
+        this.envEntries = envEntries;
+    }
+    
+    /**
+     * The resource type of this environment entry.
+     */
+    private String resourcetype = null;
+    
+    /**
+     * Return the resource type of the environment entry this bean refers to.
+     */
+    public String getResourcetype() {
+        return this.resourcetype;
+    }
+
+    /**
+     * Set the resource type of the environment entry this bean refers to.
+     */
+    public void setResourcetype(String resourcetype) {
+        this.resourcetype = resourcetype;
+    }
+       
+    /**
+     * The path of this environment entry.
+     */
+    private String path = null;
+    
+    /**
+     * Return the path of the environment entry this bean refers to.
+     */
+    public String getPath() {
+        return this.path;
+    }
+
+    /**
+     * Set the path of the environment entry this bean refers to.
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+       
+    /**
+     * The host of this environment entry.
+     */
+    private String host = null;
+    
+    /**
+     * Return the host of the environment entry this bean refers to.
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Set the host of the environment entry this bean refers to.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }    
+    
+       
+    /**
+     * The domain of this environment entry.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the environment entry this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the environment entry this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.envEntries = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/EnvEntryForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/EnvEntryForm.java
new file mode 100644
index 0000000..440ded3
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/EnvEntryForm.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.apache.webapp.admin.LabelValueBean;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Form bean for the individual environment entry page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class EnvEntryForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The name of the associated entry.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * The type of the associated entry.
+     */
+    private String entryType = null;
+
+    public String getEntryType() {
+        return (this.entryType);
+    }
+
+    public void setEntryType(String entryType) {
+        this.entryType = entryType;
+    }
+
+
+    /**
+     * The value of the associated entry.
+     */
+    private String value = null;
+
+    public String getValue() {
+        return (this.value);
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    /**
+     * The description of the associated entry.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * The value of override appl level entries.
+     */
+    private boolean override = true;
+
+    public boolean getOverride() {
+        return (this.override);
+    }
+
+    public void setOverride(boolean override) {
+        this.override = override;
+    }
+    
+    /**
+     * The resource type of this environment entry.
+     */
+    private String resourcetype = null;
+    
+    /**
+     * Return the resource type of the environment entry this bean refers to.
+     */
+    public String getResourcetype() {
+        return this.resourcetype;
+    }
+
+    /**
+     * Set the resource type of the environment entry this bean refers to.
+     */
+    public void setResourcetype(String resourcetype) {
+        this.resourcetype = resourcetype;
+    }
+       
+    /**
+     * The path of this environment entry.
+     */
+    private String path = null;
+    
+    /**
+     * Return the path of the environment entry this bean refers to.
+     */
+    public String getPath() {
+        return this.path;
+    }
+
+    /**
+     * Set the path of the environment entry this bean refers to.
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+       
+    /**
+     * The host of this environment entry.
+     */
+    private String host = null;
+    
+    /**
+     * Return the host of the environment entry this bean refers to.
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Set the host of the environment entry this bean refers to.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }    
+    
+       
+    /**
+     * The domain of this environment entry.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the environment entry this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the environment entry this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    
+    /**
+     * Precomputed list of entry type labels and values.
+     */
+    private static List typeVals = new ArrayList();
+
+    static {
+        typeVals.add(new LabelValueBean("java.lang.Boolean", "java.lang.Boolean"));
+        typeVals.add(new LabelValueBean("java.lang.Byte", "java.lang.Byte"));
+        typeVals.add(new LabelValueBean("java.lang.Character", "java.lang.Character"));
+        typeVals.add(new LabelValueBean("java.lang.Double", "java.lang.Double"));
+        typeVals.add(new LabelValueBean("java.lang.Float", "java.lang.Float"));
+        typeVals.add(new LabelValueBean("java.lang.Integer", "java.lang.Integer"));    
+        typeVals.add(new LabelValueBean("java.lang.Long", "java.lang.Long"));
+        typeVals.add(new LabelValueBean("java.lang.Short", "java.lang.Short"));        
+        typeVals.add(new LabelValueBean("java.lang.String", "java.lang.String"));           
+        
+    }
+
+    /**
+     * Return the typeVals.
+     */
+    public List getTypeVals() {
+        
+        return this.typeVals;
+        
+    }
+    
+    /**
+     * Set the typeVals.
+     */
+    public void setTypeVals(List typeVals) {
+        
+        this.typeVals = typeVals;
+        
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        name = null;
+        entryType = null;
+        value = null;
+        description = null;
+        override = false;
+
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    private ActionErrors errors = null;
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+
+        errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+        //if (submit != null) {
+
+            // name is a required field
+            if ((name == null) || (name.length() < 1)) {
+                errors.add("name",
+                           new ActionError("resources.error.name.required"));
+            }
+
+            // value is a required field
+            if ((value == null) || (value.length() < 1)) {
+                errors.add("value",
+                           new ActionError("resources.error.value.required"));
+            }
+
+            // Quotes not allowed in name
+            if ((name != null) && (name.indexOf('"') >= 0)) {
+                errors.add("name",
+                           new ActionError("users.error.quotes"));
+            }
+
+            // Quotes not allowed in value
+            if ((value != null) && (value.indexOf('"') > 0)) {
+                errors.add("value",
+                           new ActionError("users.error.quotes"));
+            }
+
+            // Quotes not allowed in description
+            if ((description != null) && (description.indexOf('"') > 0)) {
+                errors.add("description",
+                           new ActionError("users.error.quotes"));
+            }
+            
+            // if cehcked, override will be sent as a request parameter
+            override = (request.getParameter("override") != null);
+            
+            if (validateType(entryType, value)) {
+                   errors.add("value",
+                           new ActionError("resources.error.value.mismatch"));
+            }
+        //}
+        return (errors);
+    }
+
+    /**
+     * Entry type must match type of value.
+     */
+    private boolean validateType(String entryType, String value) {
+        Class cls = null;
+        boolean mismatch = false;
+        try {
+            cls = Class.forName(entryType);
+            
+            if (Character.class.isAssignableFrom(cls)) {
+                // Special handling is needed because the UI returns
+                // a string even if it is a character (single length string).
+                if (value.length() != 1) {
+                    mismatch = true;
+                }
+            } else if (Boolean.class.isAssignableFrom(cls)) {
+                // Special handling is needed because Boolean
+                // string constructor accepts anything other than
+                // true to be false
+                if (!("true".equalsIgnoreCase(value) ||
+                "false".equalsIgnoreCase(value))) {
+                    mismatch = true;
+                }
+            } else if (Number.class.isAssignableFrom(cls)) {
+                // all numbers throw NumberFormatException if they are
+                // constructed with an incorrect number string
+                // We use the general string constructor to do this job
+                try {
+                    Class[] parameterTypes = {String.class};
+                    Constructor ct = cls.getConstructor(parameterTypes);
+                    Object arglist1[] = {value};
+                    Object retobj = ct.newInstance(arglist1);
+                } catch (Exception e) {
+                    mismatch = true;
+                }
+            } else if (String.class.isAssignableFrom(cls)) {
+                // all strings are allowed
+            } else {
+                // validation for other types not implemented yet
+               errors.add("entryType",
+                       new ActionError("resources.error.entryType.notimpl"));
+            }
+        } catch (ClassNotFoundException cnfe) {
+            // entry type has an invalid entry
+           errors.add("entryType",
+                       new ActionError("resources.error.entryType.invalid"));
+         }        
+        return mismatch;
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListDataSourcesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListDataSourcesAction.java
new file mode 100644
index 0000000..b319800
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListDataSourcesAction.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Locale;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Retrieve the set of MBean names for all currently defined data sources,
+ * and expose them in a request attribute named "dataSourcesForm".  This action
+ * requires the following request parameters to be set:</p>
+ * <ul>
+ * <li><strong>forward</strong> - Global forward to which we should
+ *     go after stashing the dataSources list.</li>
+ * </ul>
+ *
+ * @author Manveen Kaur
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ListDataSourcesAction extends Action {
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        
+        String resourcetype = request.getParameter("resourcetype");
+        String path = request.getParameter("path");
+        String host = request.getParameter("host");
+        String domain = request.getParameter("domain");
+        
+        if (resourcetype != null) {
+            resourcetype = URLDecoder.decode(resourcetype,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (path != null) {
+            path = URLDecoder.decode(path,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (host != null) {
+            host = URLDecoder.decode(host,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (domain != null) {
+            domain = URLDecoder.decode(domain,TomcatTreeBuilder.URL_ENCODING);
+        }
+        
+        // Create a form bean containing the requested MBean Names
+        DataSourcesForm dataSourcesForm = null;
+        try {
+              dataSourcesForm = 
+                ResourceUtils.getDataSourcesForm(mserver, resourcetype,
+                                        path, host, domain);
+        } catch (Exception e) {
+            getServlet().log(resources.getMessage
+                             (locale,
+                              "users.error.attribute.get", "resources"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "resources"));
+        }
+
+        // Stash the results in request scope
+        request.setAttribute("dataSourcesForm", dataSourcesForm);
+        saveToken(request);
+        String forward =
+            URLDecoder.decode(request.getParameter("forward"),TomcatTreeBuilder.URL_ENCODING);
+        
+        return (mapping.findForward(forward));
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListEnvEntriesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListEnvEntriesAction.java
new file mode 100644
index 0000000..06e89dc
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListEnvEntriesAction.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Locale;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Retrieve the set of MBean names for all currently defined environment entries,
+ * and expose them in a request attribute named "enventriesForm".  This action
+ * requires the following request parameters to be set:</p>
+ * <ul>
+ * <li><strong>forward</strong> - Global forward to which we should
+ *     go after stashing the env entries list.</li>
+ * </ul>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ListEnvEntriesAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        
+        String resourcetype = request.getParameter("resourcetype");
+        String path = request.getParameter("path");
+        String host = request.getParameter("host");
+        String domain = request.getParameter("domain");
+        
+        if (resourcetype != null) {
+            resourcetype = URLDecoder.decode(resourcetype,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (path != null) {
+            path = URLDecoder.decode(path,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (host != null) {
+            host = URLDecoder.decode(host,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (domain != null) {
+            domain = URLDecoder.decode(domain,TomcatTreeBuilder.URL_ENCODING);
+        }
+        // Create a form bean containing the requested MBean Names
+        EnvEntriesForm envEntriesForm = null;
+        try {
+           envEntriesForm = 
+                    ResourceUtils.getEnvEntriesForm(mserver, resourcetype,
+                                        path, host, domain);
+        } catch (Exception e) {
+            getServlet().log(resources.getMessage
+                             (locale,
+                              "users.error.attribute.get", "environments"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "environments"));
+        }
+
+        // Stash the results in request scope
+        request.setAttribute("envEntriesForm", envEntriesForm);
+        saveToken(request);
+        String forward =
+            URLDecoder.decode(request.getParameter("forward"),TomcatTreeBuilder.URL_ENCODING);
+        
+        return (mapping.findForward(forward));
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListMailSessionsAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListMailSessionsAction.java
new file mode 100644
index 0000000..39ecc07
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListMailSessionsAction.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Locale;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Retrieve the set of MBean names for all currently defined mail sessions,
+ * and expose them in a request attribute named "mailSessionsForm".  This action
+ * requires the following request parameters to be set:</p>
+ * <ul>
+ * <li><strong>forward</strong> - Global forward to which we should
+ *     go after stashing the mailSessions list.</li>
+ * </ul>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ListMailSessionsAction extends Action {
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        
+        String resourcetype = request.getParameter("resourcetype");
+        String path = request.getParameter("path");
+        String host = request.getParameter("host");
+        String domain = request.getParameter("domain");
+        
+        if (resourcetype != null) {
+            resourcetype = URLDecoder.decode(resourcetype,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (path != null) {
+            path = URLDecoder.decode(path,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (host != null) {
+            host = URLDecoder.decode(host,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (domain != null) {
+            domain = URLDecoder.decode(domain,TomcatTreeBuilder.URL_ENCODING);
+        }
+        
+        // Create a form bean containing the requested MBean Names
+        MailSessionsForm mailSessionsForm = null;
+        try {
+              mailSessionsForm = 
+                ResourceUtils.getMailSessionsForm(mserver, resourcetype,
+                                        path, host, domain);
+        } catch (Exception e) {
+            getServlet().log(resources.getMessage
+                             (locale,
+                              "users.error.attribute.get", "resources"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "resources"));
+        }
+
+        // Stash the results in request scope
+        request.setAttribute("mailSessionsForm", mailSessionsForm);
+        saveToken(request);
+        String forward =
+            URLDecoder.decode(request.getParameter("forward"),TomcatTreeBuilder.URL_ENCODING);
+        
+        return (mapping.findForward(forward));
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListResourceLinksAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListResourceLinksAction.java
new file mode 100644
index 0000000..c583bcc
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListResourceLinksAction.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Locale;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Retrieve the set of MBean names for all currently defined resource links,
+ * and expose them in a request attribute named "resourceLinksForm".  This action
+ * requires the following request parameters to be set:</p>
+ * <ul>
+ * <li><strong>forward</strong> - Global forward to which we should
+ *     go after stashing the resourceLinks list.</li>
+ * </ul>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ListResourceLinksAction extends Action {
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        
+        String resourcetype = request.getParameter("resourcetype");
+        String path = request.getParameter("path");
+        String host = request.getParameter("host");
+        String domain = request.getParameter("domain");
+            
+        if (resourcetype != null) {
+            resourcetype = URLDecoder.decode(resourcetype,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (path != null) {
+            path = URLDecoder.decode(path,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (host != null) {
+            host = URLDecoder.decode(host,TomcatTreeBuilder.URL_ENCODING);
+        }
+        if (domain != null) {
+            domain = URLDecoder.decode(domain,TomcatTreeBuilder.URL_ENCODING);
+        }
+        
+        // Create a form bean containing the requested MBean Names
+        ResourceLinksForm resourceLinksForm = null;
+        try {
+              resourceLinksForm = 
+                ResourceUtils.getResourceLinksForm(mserver, resourcetype,
+                                        path, host, domain);
+        } catch (Exception e) {
+            getServlet().log(resources.getMessage
+                             (locale,
+                              "users.error.attribute.get", "resources"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "resources"));
+        }
+
+        // Stash the results in request scope
+        request.setAttribute("resourceLinksForm", resourceLinksForm);
+        saveToken(request);
+        String forward =
+            URLDecoder.decode(request.getParameter("forward"),TomcatTreeBuilder.URL_ENCODING);
+        
+        return (mapping.findForward(forward));
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListUserDatabasesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListUserDatabasesAction.java
new file mode 100644
index 0000000..664f504
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ListUserDatabasesAction.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Locale;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Retrieve the set of MBean names for all currently defined environment entries,
+ * and expose them in a request attribute named "enventriesForm".  This action
+ * requires the following request parameters to be set:</p>
+ * <ul>
+ * <li><strong>forward</strong> - Global forward to which we should
+ *     go after stashing the env entries list.</li>
+ * </ul>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ListUserDatabasesAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        String domain = request.getParameter("domain");
+        if (domain != null) {
+            domain = URLDecoder.decode(domain,TomcatTreeBuilder.URL_ENCODING);
+        }
+        // Create a form bean containing the requested MBean Names
+        UserDatabasesForm userDatabasesForm = null;
+        try {
+              userDatabasesForm = 
+                        ResourceUtils.getUserDatabasesForm(mserver, domain);
+              userDatabasesForm.setDomain(domain);
+        } catch (Exception e) {
+            getServlet().log(resources.getMessage
+                             (locale,
+                              "users.error.attribute.get", "resources"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "resources"));
+        }
+        
+        // Stash the results in request scope
+        request.setAttribute("userDatabasesForm", userDatabasesForm);
+        saveToken(request);
+        String forward =
+            URLDecoder.decode(request.getParameter("forward"),TomcatTreeBuilder.URL_ENCODING);
+        return (mapping.findForward(forward));
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/MailSessionForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/MailSessionForm.java
new file mode 100644
index 0000000..37ecd48
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/MailSessionForm.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the individual mail session page.
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class MailSessionForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+    
+    /**
+     * The name of the mail session.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+    
+    /**
+     * The mail.smtp.host of the mail session.
+     */
+    private String mailhost = null;
+
+    public String getMailhost() {
+        return (this.mailhost);
+    }
+
+    public void setMailhost(String mailhost) {
+        this.mailhost = mailhost;
+    }
+
+    /**
+     * The resource type of this mail session.
+     */
+    private String resourcetype = null;
+    
+    /**
+     * Return the resource type of the mail session this bean refers to.
+     */
+    public String getResourcetype() {
+        return this.resourcetype;
+    }
+
+    /**
+     * Set the resource type of the mail session this bean refers to.
+     */
+    public void setResourcetype(String resourcetype) {
+        this.resourcetype = resourcetype;
+    }
+       
+    /**
+     * The path of this mail session.
+     */
+    private String path = null;
+    
+    /**
+     * Return the path of the mail session this bean refers to.
+     */
+    public String getPath() {
+        return this.path;
+    }
+
+    /**
+     * Set the path of the mail session this bean refers to.
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+       
+    /**
+     * The host of this mail session.
+     */
+    private String host = null;
+    
+    /**
+     * Return the host of the mail session this bean refers to.
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Set the host of the mail session this bean refers to.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }    
+    
+       
+    /**
+     * The domain of this mail session.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the mail session this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the mail session this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    
+    /**
+     * The type of the resource.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        mailhost = null;   
+        type = null;
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    private ActionErrors errors = null;
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+
+        errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+
+        //if (submit != null) {
+
+            // mailSmtpHost is a required field
+            if ((mailhost == null) || (mailhost.length() < 1)) {
+                errors.add("mailhost",
+                      new ActionError("resources.error.mailhost.required"));
+            }
+         //}
+        
+        return (errors);
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/MailSessionsForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/MailSessionsForm.java
new file mode 100644
index 0000000..1492524
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/MailSessionsForm.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the delete mail sessions page.
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class MailSessionsForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the specified environment entries.
+     */
+    private String mailSessions[] = null;
+
+    public String[] getMailSessions() {
+        return (this.mailSessions);
+    }
+
+    public void setMailSessions(String mailSessions[]) {
+        this.mailSessions = mailSessions;
+    }
+
+    /**
+     * The resource type of this mail session.
+     */
+    private String resourcetype = null;
+    
+    /**
+     * Return the resource type of the mail session this bean refers to.
+     */
+    public String getResourcetype() {
+        return this.resourcetype;
+    }
+
+    /**
+     * Set the resource type of the mail session this bean refers to.
+     */
+    public void setResourcetype(String resourcetype) {
+        this.resourcetype = resourcetype;
+    }
+       
+    /**
+     * The path of this mail session.
+     */
+    private String path = null;
+    
+    /**
+     * Return the path of the mail session this bean refers to.
+     */
+    public String getPath() {
+        return this.path;
+    }
+
+    /**
+     * Set the path of the mail session this bean refers to.
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+       
+    /**
+     * The host of this mail session.
+     */
+    private String host = null;
+    
+    /**
+     * Return the host of the mail session this bean refers to.
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Set the host of the mail session this bean refers to.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }    
+    
+       
+    /**
+     * The domain of this mail session.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the mail session this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the mail session this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.mailSessions = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourceLinkForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourceLinkForm.java
new file mode 100644
index 0000000..452b2c8
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourceLinkForm.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the individual resource link page.
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class ResourceLinkForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The name of the resource link.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * The global name of the resource link.
+     */
+    private String global = null;
+
+    public String getGlobal() {
+        return (this.global);
+    }
+
+    public void setGlobal(String global) {
+        this.global = global;
+    }
+    
+    /**
+     * The resource type of this resource link.
+     */
+    private String resourcetype = null;
+    
+    /**
+     * Return the resource type of the resource link this bean refers to.
+     */
+    public String getResourcetype() {
+        return this.resourcetype;
+    }
+
+    /**
+     * Set the resource type of the resource link this bean refers to.
+     */
+    public void setResourcetype(String resourcetype) {
+        this.resourcetype = resourcetype;
+    }
+       
+    /**
+     * The path of this resource link.
+     */
+    private String path = null;
+    
+    /**
+     * Return the path of the resource link this bean refers to.
+     */
+    public String getPath() {
+        return this.path;
+    }
+
+    /**
+     * Set the path of the resource link this bean refers to.
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+       
+    /**
+     * The host of this resource link.
+     */
+    private String host = null;
+    
+    /**
+     * Return the host of the resource link this bean refers to.
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Set the host of the resource link this bean refers to.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }    
+    
+       
+    /**
+     * The domain of this resource link.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the resource link this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the resource link this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    
+    /**
+     * The type of the resource link link.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        name = null;
+        global = null;
+        type = null;
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    private ActionErrors errors = null;
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+
+        //if (submit != null) {
+
+            // name is a required field
+            if ((name == null) || (name.length() < 1)) {
+                errors.add("name",
+                           new ActionError("resources.error.name.required"));
+            }
+
+            // global is a required field
+            if (( global == null) || (global.length() < 1)) {
+                errors.add("global",
+                           new ActionError("resources.error.global.required"));
+            }
+            
+            // type is a required field
+            if ((type == null) || (type.length() < 1)) {
+                errors.add("type",
+                           new ActionError("resources.error.type.required"));
+            }
+            
+         //}
+
+        return (errors);
+
+    }
+ 
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourceLinksForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourceLinksForm.java
new file mode 100644
index 0000000..52841b5
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourceLinksForm.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the delete resource links page.
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class ResourceLinksForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the specified resource links.
+     */
+    private String resourceLinks[] = null;
+
+    public String[] getResourceLinks() {
+        return (this.resourceLinks);
+    }
+
+    public void setResourceLinks(String resourceLinks[]) {
+        this.resourceLinks = resourceLinks;
+    }
+    
+    
+    /**
+     * The resource type of this resource link.
+     */
+    private String resourcetype = null;
+    
+    /**
+     * Return the resource type of the resource link this bean refers to.
+     */
+    public String getResourcetype() {
+        return this.resourcetype;
+    }
+
+    /**
+     * Set the resource type of the resource link this bean refers to.
+     */
+    public void setResourcetype(String resourcetype) {
+        this.resourcetype = resourcetype;
+    }
+       
+    /**
+     * The path of this resource link.
+     */
+    private String path = null;
+    
+    /**
+     * Return the path of the resource link this bean refers to.
+     */
+    public String getPath() {
+        return this.path;
+    }
+
+    /**
+     * Set the path of the resource link this bean refers to.
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+       
+    /**
+     * The host of this resource link.
+     */
+    private String host = null;
+    
+    /**
+     * Return the host of the resource link this bean refers to.
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Set the host of the resource link this bean refers to.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }    
+    
+       
+    /**
+     * The domain of this resource link.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the resource link this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the resource link this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.resourceLinks = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourceUtils.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourceUtils.java
new file mode 100644
index 0000000..96d7973
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourceUtils.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collections;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ObjectInstance;
+
+
+/**
+ * <p>Shared utility methods for the resource administration module.</p>
+ *
+ * @author Manveen Kaur
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ResourceUtils {
+
+    public final static String ENVIRONMENT_TYPE = ":type=Environment";
+    public final static String RESOURCE_TYPE = ":type=Resource";
+    public final static String RESOURCELINK_TYPE = ":type=ResourceLink";
+    public final static String NAMINGRESOURCES_TYPE = ":type=NamingResources";
+    public final static String GLOBAL_TYPE = ",resourcetype=Global";
+    public final static String CONTEXT_TYPE = ",resourcetype=Context";
+    public final static String SERVICE_DEFAULTCONTEXT_TYPE = 
+                                                ",resourcetype=ServiceDefaultContext";
+    public final static String HOST_DEFAULTCONTEXT_TYPE = 
+                                                ",resourcetype=HostDefaultContext";                                                
+    
+    // resource class names
+    public final static String USERDB_CLASS = "org.apache.catalina.UserDatabase";
+    public final static String DATASOURCE_CLASS = "javax.sql.DataSource";
+    public final static String MAILSESSION_CLASS = "javax.mail.Session";
+
+    // --------------------------------------------------------- Public Methods
+
+
+    public static ObjectName getNamingResourceObjectName(String domain,
+            String resourcetype, String path, String host) throws Exception {
+
+        ObjectName oname = null;
+        if ((resourcetype==null) || (domain==null)) {
+            return null;
+        }
+        // Construct the MBean Name for the naming source
+        if (resourcetype.equals("Global")) {
+            oname = new ObjectName(domain + NAMINGRESOURCES_TYPE +
+                                GLOBAL_TYPE);
+        } else if (resourcetype.equals("Context")) {
+            oname = new ObjectName(domain + NAMINGRESOURCES_TYPE +
+                                CONTEXT_TYPE + 
+                                ",path=" + path + ",host=" + host);
+        } else if (resourcetype.equals("DefaultContext")) {
+            if (host.length() > 0) {
+                oname = new ObjectName(domain + NAMINGRESOURCES_TYPE +
+                                        HOST_DEFAULTCONTEXT_TYPE + 
+                                        ",host=" + host);
+            } else {
+                oname = new ObjectName(domain + NAMINGRESOURCES_TYPE +
+                                        SERVICE_DEFAULTCONTEXT_TYPE);            
+            }
+        }
+        
+        return oname;
+        
+    }
+    
+    /**
+     * Construct and return a ResourcesForm identifying all currently defined
+     * resources in the specified resource database.
+     *
+     * @param mserver MBeanServer to be consulted
+     *
+     * @exception Exception if an error occurs
+     */
+    public static EnvEntriesForm getEnvEntriesForm(MBeanServer mserver, 
+        String resourcetype, String path, String host, String domain) 
+        throws Exception {
+                           
+        ObjectName ename = null;
+        StringBuffer sb = null;        
+        if (resourcetype!=null) {
+            if (resourcetype.equals("Global")) {
+                ename = new ObjectName( domain + ENVIRONMENT_TYPE + 
+                                        GLOBAL_TYPE + ",*");
+            } else if (resourcetype.equals("Context")) {
+                ename = new ObjectName ( domain + ENVIRONMENT_TYPE + 
+                                        CONTEXT_TYPE + ",path=" + path + 
+                                        ",host=" + host + ",*");
+            } else if (resourcetype.equals("DefaultContext")) {
+                if (host.length() > 0) {
+                    ename = new ObjectName( domain + ENVIRONMENT_TYPE + 
+                        HOST_DEFAULTCONTEXT_TYPE + ",host=" + host + ",*");
+                } else {
+                    ename = new ObjectName( domain + ENVIRONMENT_TYPE + 
+                        SERVICE_DEFAULTCONTEXT_TYPE + ",*");
+                }
+            }
+        }
+        
+        Iterator iterator = (mserver.queryMBeans(ename, null).iterator());
+        
+        ArrayList results = new ArrayList();        
+        while (iterator.hasNext()) {
+            ObjectInstance instance = (ObjectInstance) iterator.next(); 
+            results.add(instance.getObjectName().toString());
+        }
+
+        Collections.sort(results);        
+        
+        EnvEntriesForm envEntriesForm = new EnvEntriesForm();
+        envEntriesForm.setEnvEntries((String[]) 
+                        results.toArray(new String[results.size()]));
+        
+        if (resourcetype != null) {
+            envEntriesForm.setResourcetype(resourcetype);
+        } else {
+            envEntriesForm.setResourcetype("");
+        }
+         if (path != null) {
+            envEntriesForm.setPath(path);
+        } else {
+            envEntriesForm.setPath("");
+        }        
+        if (host != null) {
+            envEntriesForm.setHost(host);
+        } else {
+            envEntriesForm.setHost("");
+        }          
+        if (domain != null) {
+            envEntriesForm.setDomain(domain);
+        } else {
+            envEntriesForm.setDomain("");
+        }          
+        
+        return (envEntriesForm);
+
+    }
+
+    /**
+     * Construct and return a DataSourcesForm identifying all currently defined
+     * datasources in the specified resource database.
+     *
+     * @param mserver MBeanServer to be consulted
+     *
+     * @exception Exception if an error occurs
+     */
+    public static DataSourcesForm getDataSourcesForm(MBeanServer mserver, 
+        String resourcetype, String path, String host, String domain) 
+        throws Exception {
+                            
+        ObjectName rname = null;
+        if (resourcetype!=null) {
+            if (resourcetype.equals("Global")) {
+                rname = new ObjectName( domain + RESOURCE_TYPE + GLOBAL_TYPE + 
+                                        ",class=" + DATASOURCE_CLASS + ",*");
+            } else if (resourcetype.equals("Context")) {
+                rname = new ObjectName ( domain + RESOURCE_TYPE + CONTEXT_TYPE + 
+                    ",path=" + path + ",host=" + host + ",class=" + 
+                    DATASOURCE_CLASS + ",*");
+            } else if (resourcetype.equals("DefaultContext")) {
+                if (host.length() > 0) {
+                    rname = new ObjectName( domain + RESOURCE_TYPE + 
+                        HOST_DEFAULTCONTEXT_TYPE + ",host=" + host + 
+                        ",class=" + DATASOURCE_CLASS + ",*");
+                } else {
+                    rname = new ObjectName( domain + RESOURCE_TYPE + 
+                        SERVICE_DEFAULTCONTEXT_TYPE +  
+                        ",class=" + DATASOURCE_CLASS + ",*");
+                }
+            }
+        }
+        Iterator iterator = (mserver.queryMBeans(rname, null).iterator());
+        
+        ArrayList results = new ArrayList();        
+        while (iterator.hasNext()) {
+            
+            ObjectInstance instance = (ObjectInstance) iterator.next(); 
+            ObjectName oname = instance.getObjectName();
+            try {
+                // only add resource mbean if definition exists
+                mserver.getAttribute(oname, "driverClassName");
+                results.add(oname.toString());
+            } catch (AttributeNotFoundException ex) {
+                mserver.setAttribute(oname, 
+                    new Attribute("driverClassName", ""));
+            }
+        }
+
+        Collections.sort(results);        
+        DataSourcesForm dataSourcesForm = new DataSourcesForm();
+        dataSourcesForm.setDataSources((String[]) 
+                        results.toArray(new String[results.size()]));        
+        
+        if (resourcetype != null) {
+            dataSourcesForm.setResourcetype(resourcetype);
+        } else {
+            dataSourcesForm.setResourcetype("");
+        }
+         if (path != null) {
+            dataSourcesForm.setPath(path);
+        } else {
+            dataSourcesForm.setPath("");
+        }        
+        if (host != null) {
+            dataSourcesForm.setHost(host);
+        } else {
+            dataSourcesForm.setHost("");
+        }        
+        if (domain != null) {
+            dataSourcesForm.setDomain(domain);
+        } else {
+            dataSourcesForm.setDomain("");
+        }          
+        return (dataSourcesForm);
+
+    }
+    
+    /**
+     * Construct and return a MailSessionsForm identifying all currently defined
+     * mailsessions in the specified resource database.
+     *
+     * @param mserver MBeanServer to be consulted
+     *
+     * @exception Exception if an error occurs
+     */
+    public static MailSessionsForm getMailSessionsForm(MBeanServer mserver, 
+        String resourcetype, String path, String host, String domain) 
+        throws Exception {
+                            
+        ObjectName rname = null;
+        if (resourcetype!=null) {
+            if (resourcetype.equals("Global")) {
+                rname = new ObjectName( domain + RESOURCE_TYPE + GLOBAL_TYPE + 
+                                        ",class=" + MAILSESSION_CLASS + ",*");
+            } else if (resourcetype.equals("Context")) {
+                rname = new ObjectName (domain + RESOURCE_TYPE + CONTEXT_TYPE + 
+                    ",path=" + path + ",host=" + host + ",class=" + 
+                    MAILSESSION_CLASS + ",*");
+            } else if (resourcetype.equals("DefaultContext")) {
+                if (host.length() > 0) {
+                    rname = new ObjectName(domain + RESOURCE_TYPE + 
+                        HOST_DEFAULTCONTEXT_TYPE + ",host=" + host + 
+                        ",class=" + MAILSESSION_CLASS + ",*");
+                } else {
+                    rname = new ObjectName(domain + RESOURCE_TYPE + 
+                        SERVICE_DEFAULTCONTEXT_TYPE +
+                        ",class=" + MAILSESSION_CLASS + ",*");
+                }
+            }
+        }
+       
+        Iterator iterator = (mserver.queryMBeans(rname, null).iterator());
+        
+        ArrayList results = new ArrayList();        
+        while (iterator.hasNext()) {
+            
+            ObjectInstance instance = (ObjectInstance) iterator.next(); 
+            results.add(instance.getObjectName().toString());
+        }
+
+        Collections.sort(results);        
+        MailSessionsForm mailSessionsForm = new MailSessionsForm();
+        mailSessionsForm.setMailSessions((String[]) 
+                        results.toArray(new String[results.size()]));        
+        
+        if (resourcetype != null) {
+            mailSessionsForm.setResourcetype(resourcetype);
+        } else {
+            mailSessionsForm.setResourcetype("");
+        }
+         if (path != null) {
+            mailSessionsForm.setPath(path);
+        } else {
+            mailSessionsForm.setPath("");
+        }        
+        if (host != null) {
+            mailSessionsForm.setHost(host);
+        } else {
+            mailSessionsForm.setHost("");
+        }        
+        if (domain != null) {
+            mailSessionsForm.setDomain(domain);
+        } else {
+            mailSessionsForm.setDomain("");
+        }          
+        
+        return (mailSessionsForm);
+
+    }
+    
+    /**
+     * Construct and return a ResourceLinksForm identifying all currently defined
+     * resourcelinks in the specified resource database.
+     *
+     * @param mserver MBeanServer to be consulted
+     *
+     * @exception Exception if an error occurs
+     */
+    public static ResourceLinksForm getResourceLinksForm(MBeanServer mserver, 
+        String resourcetype, String path, String host, String domain) 
+        throws Exception {
+
+        ObjectName rname = null;
+        if (resourcetype!=null) {
+            if (resourcetype.equals("Global")) {
+                rname = new ObjectName( domain + RESOURCELINK_TYPE + 
+                                        GLOBAL_TYPE + ",*");
+            } else if (resourcetype.equals("Context")) {
+                rname = new ObjectName ( domain + RESOURCELINK_TYPE + 
+                            CONTEXT_TYPE + ",path=" + path + 
+                            ",host=" + host + ",*");
+            } else if (resourcetype.equals("DefaultContext")) {
+                if (host.length() > 0) {
+                    rname = new ObjectName( domain + RESOURCELINK_TYPE + 
+                                HOST_DEFAULTCONTEXT_TYPE + 
+                                ",host=" + host + ",*");
+                } else {
+                    rname = new ObjectName( domain + RESOURCELINK_TYPE + 
+                                SERVICE_DEFAULTCONTEXT_TYPE + ",*");
+                }
+            }
+        }
+       
+        Iterator iterator = (mserver.queryMBeans(rname, null).iterator());
+        
+        ArrayList results = new ArrayList();        
+        while (iterator.hasNext()) {
+            ObjectInstance instance = (ObjectInstance) iterator.next(); 
+            results.add(instance.getObjectName().toString());
+        }
+
+        Collections.sort(results);        
+        ResourceLinksForm resourceLinksForm = new ResourceLinksForm();
+        resourceLinksForm.setResourceLinks((String[]) 
+                        results.toArray(new String[results.size()]));        
+        
+        if (resourcetype != null) {
+            resourceLinksForm.setResourcetype(resourcetype);
+        } else {
+            resourceLinksForm.setResourcetype("");
+        }
+         if (path != null) {
+            resourceLinksForm.setPath(path);
+        } else {
+            resourceLinksForm.setPath("");
+        }        
+        if (host != null) {
+            resourceLinksForm.setHost(host);
+        } else {
+            resourceLinksForm.setHost("");
+        }        
+        if (domain != null) {
+            resourceLinksForm.setDomain(domain);
+        } else {
+            resourceLinksForm.setDomain("");
+        }          
+        
+        return (resourceLinksForm);
+        
+    }
+    
+    /**
+     * Construct and return a UserDatabaseForm identifying all currently defined
+     * user databases in the specified resource database.
+     *
+     * @param mserver MBeanServer to be consulted
+     *
+     * @exception Exception if an error occurs
+     */
+    public static UserDatabasesForm getUserDatabasesForm(MBeanServer mserver,String domain)
+        throws Exception {
+            
+        ObjectName rname = new ObjectName( domain + RESOURCE_TYPE + GLOBAL_TYPE +
+                            ",class=" + USERDB_CLASS + ",*");
+        
+        Iterator iterator = (mserver.queryMBeans(rname, null).iterator());
+        
+        ArrayList results = new ArrayList();        
+        while (iterator.hasNext()) {
+            ObjectInstance instance = (ObjectInstance) iterator.next(); 
+            results.add(instance.getObjectName().toString());
+        }
+
+        Collections.sort(results);
+
+        UserDatabasesForm userDatabasesForm = new UserDatabasesForm();
+        userDatabasesForm.setUserDatabases((String[]) 
+                        results.toArray(new String[results.size()]));  
+        return (userDatabasesForm);
+
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourcesTreeBuilder.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourcesTreeBuilder.java
new file mode 100644
index 0000000..90d9e90
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/ResourcesTreeBuilder.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.net.URLEncoder;
+import java.util.Locale;
+import java.io.UnsupportedEncodingException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.Globals;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * Implementation of <code>TreeBuilder</code> that adds the nodes required
+ * for administering the resources (data sources).
+ *
+ * @author Manveen Kaur
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ResourcesTreeBuilder implements TreeBuilder {
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ---------------------------------------------------- TreeBuilder Methods
+
+
+    /**
+     * Add the required nodes to the specified <code>treeControl</code>
+     * instance.
+     *
+     * @param treeControl The <code>TreeControl</code> to which we should
+     *  add our nodes
+     * @param servlet The controller servlet for the admin application
+     * @param request The servlet request we are processing
+     */
+    public void buildTree(TreeControl treeControl,
+                          ApplicationServlet servlet,
+                          HttpServletRequest request) {
+
+        MessageResources resources = (MessageResources)
+            servlet.getServletContext().getAttribute(Globals.MESSAGES_KEY);
+        HttpSession session = request.getSession();
+        Locale locale = (Locale) session.getAttribute(Globals.LOCALE_KEY);
+        addSubtree(treeControl.getRoot(), resources, locale);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Add the subtree of nodes required for user administration.
+     *
+     * @param root The root node of our tree control
+     * @param resources The MessageResources for our localized messages
+     *  messages
+     */
+    protected void addSubtree(TreeControlNode root, MessageResources resources,
+                              Locale locale) {
+        try {
+            String domain = root.getDomain();
+            TreeControlNode subtree = new TreeControlNode
+                ("Global Resource Administration",
+                 "folder_16_pad.gif",
+                 resources.getMessage(locale, "resources.treeBuilder.subtreeNode"),
+                 null,
+                 "content",
+                 true, domain);        
+            TreeControlNode datasources = new TreeControlNode
+                ("Globally Administer Data Sources",
+                 "Datasource.gif",
+                 resources.getMessage(locale, "resources.treeBuilder.datasources"),
+                 "resources/listDataSources.do?resourcetype=Global&domain=" +
+                 domain + "&forward=" + 
+                 URLEncoder.encode("DataSources List Setup",TomcatTreeBuilder.URL_ENCODING),
+                 "content",
+                 false, domain);
+            TreeControlNode mailsessions = new TreeControlNode
+                ("Globally Administer Mail Sessions ",
+                 "Mailsession.gif",
+                 resources.getMessage(locale, "resources.treeBuilder.mailsessions"),
+                 "resources/listMailSessions.do?resourcetype=Global&domain=" +
+                 domain + "&forward=" + 
+                 URLEncoder.encode("MailSessions List Setup",TomcatTreeBuilder.URL_ENCODING),
+                 "content",
+                 false, domain);
+            TreeControlNode userdbs = new TreeControlNode
+                ("Globally Administer UserDatabase Entries",
+                 "Realm.gif",
+                 resources.getMessage(locale, "resources.treeBuilder.databases"),
+                 "resources/listUserDatabases.do?domain=" + domain + 
+                 "&forward=" + 
+                 URLEncoder.encode("UserDatabases List Setup",TomcatTreeBuilder.URL_ENCODING),
+                 "content",
+                 false, domain);
+            TreeControlNode envs = new TreeControlNode
+                ("Globally Administer Environment Entries",
+                 "EnvironmentEntries.gif",
+                 resources.getMessage(locale, "resources.env.entries"),
+                 "resources/listEnvEntries.do?resourcetype=Global&domain=" +
+                 domain+"&forward="+
+                 URLEncoder.encode("EnvEntries List Setup",TomcatTreeBuilder.URL_ENCODING),
+                 "content",
+                 false, domain);
+            root.addChild(subtree);
+            subtree.addChild(datasources);
+            subtree.addChild(mailsessions);
+            subtree.addChild(envs);
+            subtree.addChild(userdbs);
+        } catch(UnsupportedEncodingException ex) {
+            // can't happen
+        }
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveDataSourceAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveDataSourceAction.java
new file mode 100644
index 0000000..a6ca115
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveDataSourceAction.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that saves a new or
+ * updated data source entry.</p>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SaveDataSourceAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List DataSources Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        DataSourceForm dataSourceForm = (DataSourceForm) form;
+        String objectName = dataSourceForm.getObjectName();
+
+        // Perform an "Add DataSource" transaction
+        if (objectName == null) {
+
+            String signature[] = new String[2];
+            signature[0] = "java.lang.String";
+            signature[1] = "java.lang.String";
+
+            Object params[] = new Object[2];
+            params[0] = dataSourceForm.getJndiName();
+            params[1] = ResourceUtils.DATASOURCE_CLASS;
+            String encodedJndiName = URLEncoder.encode(params[0].toString(), 
+                                                       "UTF-8");
+
+            String resourcetype = dataSourceForm.getResourcetype();
+            String path = dataSourceForm.getPath();
+            String host = dataSourceForm.getHost();
+            String domain = dataSourceForm.getDomain();
+
+            ObjectName oname = null;
+            ObjectName encodedOName = null;
+
+            try {
+            
+                if (resourcetype.equals("Global")) {
+                    oname = new ObjectName( domain + ResourceUtils.RESOURCE_TYPE + 
+                                            ResourceUtils.GLOBAL_TYPE + 
+                                            ",class=" + params[1] + 
+                                            ",name=" + params[0]);
+                    encodedOName = new ObjectName( domain + 
+                                            ResourceUtils.RESOURCE_TYPE + 
+                                            ResourceUtils.GLOBAL_TYPE + 
+                                            ",class=" + params[1] + 
+                                            ",name=" + encodedJndiName);
+                } else if (resourcetype.equals("Context")) {
+                    oname = new ObjectName( domain + ResourceUtils.RESOURCE_TYPE + 
+                                            ResourceUtils.CONTEXT_TYPE + 
+                                            ",path=" + path + ",host=" + host + 
+                                            ",class=" + params[1] + 
+                                            ",name=" + params[0]);
+                    encodedOName = new ObjectName( domain + 
+                                            ResourceUtils.RESOURCE_TYPE + 
+                                            ResourceUtils.CONTEXT_TYPE + 
+                                            ",path=" + path + ",host=" + host + 
+                                            ",class=" + params[1] + 
+                                            ",name=" + encodedJndiName);
+                }
+                
+                if (mserver.isRegistered(oname) || 
+                                        mserver.isRegistered(encodedOName)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("jndiName",
+                               new ActionError("resources.invalid.name"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }        
+                
+                oname = ResourceUtils.getNamingResourceObjectName(domain,
+                            resourcetype, path, host);                                
+                            
+                // Create the new object and associated MBean
+                objectName = (String) mserver.invoke(oname, "addResource",
+                                                     params, signature);
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          "addResource"), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          "addResource"));
+                return (null);
+            }
+
+        }
+
+        // Perform an "Update User database" transaction
+        String attribute = null;
+        try {
+
+            ObjectName oname = new ObjectName(objectName);
+
+            attribute = "url";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, dataSourceForm.getUrl()));
+            attribute = "driverClassName";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, dataSourceForm.getDriverClass()));
+            attribute = "username";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, dataSourceForm.getUsername()));
+            attribute = "password";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, dataSourceForm.getPassword()));
+            attribute = "maxActive";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, dataSourceForm.getActive()));
+            attribute = "maxIdle";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, dataSourceForm.getIdle()));
+            attribute = "maxWait";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, dataSourceForm.getWait()));
+            attribute = "validationQuery";
+            String validationQuery = dataSourceForm.getQuery();
+            if ((validationQuery != null) && (validationQuery.length()>0)) {
+                mserver.setAttribute(oname,
+                                new Attribute(attribute, validationQuery));
+            }
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute));
+            return (null);
+
+        }
+
+        // Proceed to the list entries screen
+        return (mapping.findForward("DataSources List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveEnvEntryAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveEnvEntryAction.java
new file mode 100644
index 0000000..fc410b3
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveEnvEntryAction.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that saves a new or
+ * updated Env Entry.</p>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SaveEnvEntryAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List EnvEntries Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        EnvEntryForm envEntryForm = (EnvEntryForm) form;
+        String objectName = envEntryForm.getObjectName();
+
+        // Perform an "Add Entry" transaction
+        if (objectName == null) {
+
+            String signature[] = new String[3];
+            signature[0] = "java.lang.String";
+            signature[1] = "java.lang.String";
+            signature[2] = "java.lang.String";
+
+            Object params[] = new Object[3];
+            params[0] = envEntryForm.getName();
+            params[1] = envEntryForm.getEntryType();
+            params[2] = envEntryForm.getValue();
+            
+            String resourcetype = envEntryForm.getResourcetype();
+            String path = envEntryForm.getPath();
+            String host = envEntryForm.getHost();
+            String domain = envEntryForm.getDomain();
+            
+            ObjectName oname = null;
+
+            try {
+            
+                if (resourcetype.equals("Global")) {
+                    oname = new ObjectName( domain + 
+                                            ResourceUtils.ENVIRONMENT_TYPE + 
+                                            ResourceUtils.GLOBAL_TYPE + 
+                                            ",name=" + params[0]);
+                } else if (resourcetype.equals("Context")) {
+                    oname = new ObjectName( domain + 
+                                            ResourceUtils.ENVIRONMENT_TYPE + 
+                                            ResourceUtils.CONTEXT_TYPE + 
+                                            ",path=" + path + ",host=" + host + 
+                                            ",name=" + params[0]);
+                }         
+                            
+                if (mserver.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("name",
+                               new ActionError("resources.invalid.env"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }  
+                
+                oname = ResourceUtils.getNamingResourceObjectName(domain,
+                            resourcetype, path, host);
+                
+                // Create the new object and associated MBean
+                objectName = (String) mserver.invoke(oname, "addEnvironment",
+                                                     params, signature);
+                                                                     
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          "addEnvironment"), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          "addEnvironment"));
+                return (null);
+            }
+
+        }
+
+        // Perform an "Update Environment Entry" transaction
+        String attribute = null;
+        try {
+            
+            // Construct an object name for this object
+            ObjectName oname = new ObjectName(objectName);
+
+            // Update the specified env entry
+            attribute = "override";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, new Boolean(envEntryForm.getOverride())));
+            attribute = "description";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, envEntryForm.getDescription()));
+            attribute = "type";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, envEntryForm.getEntryType()));
+            attribute = "value";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, envEntryForm.getValue()));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute));
+            return (null);
+
+        }
+        
+        // Proceed to the list entries screen
+        return (mapping.findForward("EnvEntries List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveMailSessionAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveMailSessionAction.java
new file mode 100644
index 0000000..0c9ca78
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveMailSessionAction.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that saves a new or
+ * updated mail session entry.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SaveMailSessionAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List MailSessions Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        MailSessionForm mailSessionForm = (MailSessionForm) form;
+        String objectName = mailSessionForm.getObjectName();
+
+        // Perform an "Add MailSession" transaction
+        if (objectName == null) {
+
+            String signature[] = new String[2];
+            signature[0] = "java.lang.String";
+            signature[1] = "java.lang.String";
+
+            Object params[] = new Object[2];
+            params[0] = mailSessionForm.getName();
+            params[1] = ResourceUtils.MAILSESSION_CLASS;     
+            
+            String resourcetype = mailSessionForm.getResourcetype();
+            String path = mailSessionForm.getPath();
+            String host = mailSessionForm.getHost();
+            String domain = mailSessionForm.getDomain();
+            
+            ObjectName oname = null;
+
+            try {
+            
+                if (resourcetype.equals("Global")) {
+                    oname = new ObjectName( domain + ResourceUtils.RESOURCE_TYPE + 
+                                            ResourceUtils.GLOBAL_TYPE + 
+                                            ",class=" + params[1] + 
+                                            ",name=" + params[0]);
+                } else if (resourcetype.equals("Context")) {
+                    oname = new ObjectName( domain + ResourceUtils.RESOURCE_TYPE + 
+                                            ResourceUtils.CONTEXT_TYPE + 
+                                            ",path=" + path + ",host=" + host + 
+                                            ",class=" + params[1] + 
+                                            ",name=" + params[0]);
+                }         
+                            
+                if (mserver.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("name",
+                               new ActionError("resources.invalid.name"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }   
+                
+                oname = ResourceUtils.getNamingResourceObjectName(domain,
+                            resourcetype, path, host);
+                            
+                // Create the new object and associated MBean
+                objectName = (String) mserver.invoke(oname, "addResource",
+                                                     params, signature);
+                                     
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          "addResource"), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          "addResource"));
+                return (null);
+            }
+
+        }
+        
+        // Perform an "Update User database" transaction
+        String attribute = null;
+        try {
+            
+            ObjectName oname = new ObjectName(objectName);
+
+            attribute = "mail.smtp.host";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, mailSessionForm.getMailhost()));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute));
+            return (null);
+
+        }
+        
+        // Proceed to the list entries screen
+        return (mapping.findForward("MailSessions List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveResourceLinkAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveResourceLinkAction.java
new file mode 100644
index 0000000..e70255b
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveResourceLinkAction.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that saves a new or
+ * updated resource link entry.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SaveResourceLinkAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List ResourceLinks Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        ResourceLinkForm resourceLinkForm = (ResourceLinkForm) form;
+        String objectName = resourceLinkForm.getObjectName();
+     
+        // Perform an "Add ResourceLink" transaction
+        if (objectName == null) {
+
+            String signature[] = new String[2];
+            signature[0] = "java.lang.String";
+            signature[1] = "java.lang.String";
+
+            Object params[] = new Object[2];
+            params[0] = resourceLinkForm.getName();
+            params[1] = resourceLinkForm.getType();   
+            
+            String resourcetype = resourceLinkForm.getResourcetype();
+            String path = resourceLinkForm.getPath();
+            String host = resourceLinkForm.getHost();
+            String domain = resourceLinkForm.getDomain();
+                       
+            ObjectName oname = null;
+
+            try {
+            
+                if (resourcetype.equals("Global")) {
+                    oname = new ObjectName( domain + 
+                                            ResourceUtils.RESOURCELINK_TYPE + 
+                                            ResourceUtils.GLOBAL_TYPE + 
+                                            ",name=" + params[0]);
+                } else if (resourcetype.equals("Context")) {
+                    oname = new ObjectName( domain + 
+                                            ResourceUtils.RESOURCELINK_TYPE + 
+                                            ResourceUtils.CONTEXT_TYPE + 
+                                            ",path=" + path + ",host=" + host + 
+                                            ",name=" + params[0]);
+                }         
+                            
+                if (mserver.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("name",
+                               new ActionError("resources.invalid.name"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }   
+                
+                oname = ResourceUtils.getNamingResourceObjectName(domain,
+                            resourcetype, path, host);
+                            
+                // Create the new object and associated MBean
+                objectName = (String) mserver.invoke(oname, "addResourceLink",
+                                                     params, signature);
+                                     
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          "addResourceLink"), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          "addResourceLink"));
+                return (null);
+            }
+
+        }
+        
+        // Perform an "Update User database" transaction
+        String attribute = null;
+        try {
+            
+            ObjectName oname = new ObjectName(objectName);
+            
+            attribute = "global";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, resourceLinkForm.getGlobal()));
+            attribute = "type";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, resourceLinkForm.getType()));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute));
+            return (null);
+
+        }
+        
+        // Proceed to the list entries screen
+        return (mapping.findForward("ResourceLinks List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveUserDatabaseAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveUserDatabaseAction.java
new file mode 100644
index 0000000..8bec05c
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SaveUserDatabaseAction.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that saves a new or
+ * updated User database entry.</p>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SaveUserDatabaseAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+    /*
+     * Only one implementation of factory allowed to start with.
+     */
+    public static String USERDB_FACTORY = "org.apache.catalina.users.MemoryUserDatabaseFactory";
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List UserDatabases Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        UserDatabaseForm userDatabaseForm = (UserDatabaseForm) form;
+        String objectName = userDatabaseForm.getObjectName();
+
+        // Perform an "Add UserDatabase" transaction
+        if (objectName == null) {
+
+            String signature[] = new String[2];
+            signature[0] = "java.lang.String";
+            signature[1] = "java.lang.String";
+
+            Object params[] = new Object[2];
+            params[0] = userDatabaseForm.getName();
+            params[1] = ResourceUtils.USERDB_CLASS;     
+           
+            ObjectName oname = null;
+
+            try {
+                String domain = userDatabaseForm.getDomain();
+                oname = new ObjectName( domain + 
+                            ResourceUtils.RESOURCE_TYPE + 
+                            ResourceUtils.GLOBAL_TYPE +
+                            ",class=" + ResourceUtils.USERDB_CLASS + 
+                            ",name=" + params[0]);
+                            
+                if (mserver.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("name",
+                               new ActionError("resources.invalid.name"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }   
+                
+                // Construct the MBean Name for the naming source
+                oname = new ObjectName(domain + 
+                            ResourceUtils.NAMINGRESOURCES_TYPE + 
+                            ResourceUtils.GLOBAL_TYPE);
+ 
+                // Create the new object and associated MBean
+                objectName = (String) mserver.invoke(oname, "addResource",
+                                                     params, signature);
+                                     
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          "addResource"), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          "addResource"));
+                return (null);
+            }
+
+        }
+        
+        // Perform an "Update User database" transaction
+        String attribute = null;
+        try {
+            
+            ObjectName oname = new ObjectName(objectName);
+
+            attribute = "pathname";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, userDatabaseForm.getPath()));
+            attribute = "factory";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, userDatabaseForm.getFactory()));
+            attribute = "description";
+            mserver.setAttribute
+                (oname,
+                 new Attribute(attribute, userDatabaseForm.getDescription()));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.set.attribute",
+                                      attribute));
+            return (null);
+
+        }
+        
+        // Proceed to the list entries screen
+        return (mapping.findForward("UserDatabases List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpDataSourceAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpDataSourceAction.java
new file mode 100644
index 0000000..76782ac
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpDataSourceAction.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that sets up and stashes
+ * a <code>DataSourceForm</code> bean in request scope.  The form bean will have
+ * a null <code>objectName</code> property if this form represents a DataSource
+ * being added, or a non-null value for an existing DataSource.</p>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SetUpDataSourceAction extends Action {
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Set up the form bean based on the creating or editing state
+        String objectName = request.getParameter("objectName");
+        String resourcetype = request.getParameter("resourcetype");
+        String path = request.getParameter("path");
+        String host = request.getParameter("host");
+        String domain = request.getParameter("domain");
+
+        DataSourceForm dataSourceForm = new DataSourceForm();
+        dataSourceForm.setResourcetype(resourcetype);
+        dataSourceForm.setPath(path);
+        dataSourceForm.setHost(host);
+        dataSourceForm.setDomain(domain);
+        dataSourceForm.setType(ResourceUtils.DATASOURCE_CLASS);
+
+        if (objectName == null) {
+            dataSourceForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.datasrc.create"));
+            dataSourceForm.setObjectName(null);
+            dataSourceForm.setActive("4");
+            dataSourceForm.setIdle("2");
+            dataSourceForm.setWait("5000");
+            dataSourceForm.setType(ResourceUtils.DATASOURCE_CLASS);
+
+        } else {
+            dataSourceForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.datasrc.edit"));
+            dataSourceForm.setObjectName(objectName);
+
+            String attribute = null;
+            try {
+                ObjectName oname = new ObjectName(objectName);
+                try {
+                    attribute = "name";
+                    dataSourceForm.setJndiName
+                        ((String) mserver.getAttribute(oname, attribute));
+                    attribute = "url";
+                    dataSourceForm.setUrl
+                        ((String) mserver.getAttribute(oname, attribute));
+                    attribute = "driverClassName";
+                    dataSourceForm.setDriverClass
+                        ((String) mserver.getAttribute(oname, attribute));
+                    attribute = "username";
+                    dataSourceForm.setUsername
+                        ((String) mserver.getAttribute(oname, attribute));
+                    attribute = "password";
+                    dataSourceForm.setPassword
+                        ((String) mserver.getAttribute(oname, attribute));
+                    attribute = "validationQuery";
+                    dataSourceForm.setQuery
+                        ((String) mserver.getAttribute(oname, attribute));
+                } catch (AttributeNotFoundException ex) {
+                    // disply empty if attribute is not set yet
+                }
+                try {
+                    attribute = "maxActive";
+                    dataSourceForm.setActive
+                        ((String) mserver.getAttribute(oname, attribute));
+                } catch (AttributeNotFoundException e) {
+                    // if maxActive not defined, display default value
+                    dataSourceForm.setActive("4");
+                }
+                try {
+                    attribute = "maxIdle";
+                    dataSourceForm.setIdle
+                        ((String) mserver.getAttribute(oname, attribute));
+                } catch (AttributeNotFoundException e) {
+                    // if maxIdle not defined, display default value
+                    dataSourceForm.setIdle("2");
+                }
+                try {
+                    attribute = "maxWait";
+                    dataSourceForm.setWait
+                        ((String) mserver.getAttribute(oname, attribute));
+                } catch (AttributeNotFoundException e) {
+                    // if maxWait not defined, display default value
+                    dataSourceForm.setWait("5000");
+                }
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale,
+                        "users.error.attribute.get", attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage
+                         (locale, "users.error.attribute.get", attribute));
+                return (null);
+            }
+        }
+
+        // Stash the form bean and forward to the display page
+        saveToken(request);
+        request.setAttribute("dataSourceForm", dataSourceForm);
+        return (mapping.findForward("DataSource"));
+
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpEnvEntryAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpEnvEntryAction.java
new file mode 100644
index 0000000..a940c18
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpEnvEntryAction.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that sets up and stashes
+ * a <code>EnvEntryForm</code> bean in request scope.  The form bean will have
+ * a null <code>objectName</code> property if this form represents a EnvEntry
+ * being added, or a non-null value for an existing EnvEntry.</p>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SetUpEnvEntryAction extends Action {
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Set up the form bean based on the creating or editing state
+        String objectName = request.getParameter("objectName");
+        String resourcetype = request.getParameter("resourcetype");
+        String path = request.getParameter("path");
+        String host = request.getParameter("host");
+        String domain = request.getParameter("domain");
+        
+        EnvEntryForm envEntryForm = new EnvEntryForm();           
+        envEntryForm.setResourcetype(resourcetype);
+        envEntryForm.setPath(path);
+        envEntryForm.setHost(host);
+        envEntryForm.setDomain(domain);
+        if (objectName == null) {
+            envEntryForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.env.create"));
+            envEntryForm.setObjectName(null); 
+        } else {
+            envEntryForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.env.edit"));
+            envEntryForm.setObjectName(objectName);
+            
+            String attribute = null;
+            try {
+                ObjectName oname = new ObjectName(objectName);
+                attribute = "name";
+                envEntryForm.setName
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "type";
+                envEntryForm.setEntryType
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "value";
+                envEntryForm.setValue
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "override";
+                envEntryForm.setOverride(
+                    ((Boolean) mserver.getAttribute(oname, attribute)).booleanValue());
+                attribute = "description";
+                envEntryForm.setDescription
+                    ((String) mserver.getAttribute(oname, attribute));
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale,
+                        "users.error.attribute.get", attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage
+                         (locale, "users.error.attribute.get", attribute));
+                return (null);
+            } 
+        }
+            
+        // Stash the form bean and forward to the display page
+        saveToken(request);
+        request.setAttribute("envEntryForm", envEntryForm);
+        return (mapping.findForward("EnvEntry"));
+
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpMailSessionAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpMailSessionAction.java
new file mode 100644
index 0000000..3b73962
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpMailSessionAction.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that sets up and stashes
+ * a <code>MailSessionForm</code> bean in request scope.  The form bean will have
+ * a null <code>objectName</code> property if this form represents a MailSession
+ * being added, or a non-null value for an existing MailSession.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SetUpMailSessionAction extends Action {
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Set up the form bean based on the creating or editing state
+        String objectName = request.getParameter("objectName");
+        String resourcetype = request.getParameter("resourcetype");
+        String path = request.getParameter("path");
+        String host = request.getParameter("host");
+        String domain = request.getParameter("domain");
+        
+        MailSessionForm mailSessionForm = new MailSessionForm();       
+        mailSessionForm.setResourcetype(resourcetype);
+        mailSessionForm.setPath(path);
+        mailSessionForm.setHost(host);
+        mailSessionForm.setDomain(domain);
+        mailSessionForm.setType(ResourceUtils.MAILSESSION_CLASS);
+
+        if (objectName == null) {
+            mailSessionForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.mailsession.create"));
+            mailSessionForm.setObjectName(null);
+            mailSessionForm.setType(ResourceUtils.MAILSESSION_CLASS);
+            
+        } else {
+            mailSessionForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.mailsession.edit"));
+            mailSessionForm.setObjectName(objectName);
+            
+            String attribute = null;
+            try {
+                
+                ObjectName oname = new ObjectName(objectName);
+                attribute = "name";
+                mailSessionForm.setName
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "mail.smtp.host";
+                mailSessionForm.setMailhost
+                    ((String) mserver.getAttribute(oname, attribute));
+        
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale,
+                        "users.error.attribute.get", attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage
+                         (locale, "users.error.attribute.get", attribute));
+                return (null);
+            } 
+        }
+            
+        // Stash the form bean and forward to the display page
+        saveToken(request);
+        request.setAttribute("mailSessionForm", mailSessionForm);
+        return (mapping.findForward("MailSession"));
+
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpResourceLinkAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpResourceLinkAction.java
new file mode 100644
index 0000000..e79c34c
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpResourceLinkAction.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that sets up and stashes
+ * a <code>ResourceLinkForm</code> bean in request scope.  The form bean will have
+ * a null <code>objectName</code> property if this form represents a ResourceLink
+ * being added, or a non-null value for an existing ResourceLink.</p>
+ *
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SetUpResourceLinkAction extends Action {
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        
+        // Set up the form bean based on the creating or editing state
+        String objectName = request.getParameter("objectName");
+        String resourcetype = request.getParameter("resourcetype");
+        String path = request.getParameter("path");
+        String host = request.getParameter("host");
+        String domain = request.getParameter("domain");
+        
+        ResourceLinkForm resourceLinkForm = new ResourceLinkForm();       
+        resourceLinkForm.setResourcetype(resourcetype);
+        resourceLinkForm.setPath(path);
+        resourceLinkForm.setHost(host);
+        resourceLinkForm.setDomain(domain);
+            
+        if (objectName == null) {
+            resourceLinkForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.resourcelk.create"));
+            resourceLinkForm.setObjectName(null);
+            
+        } else {
+            resourceLinkForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.resourcelk.edit"));
+            resourceLinkForm.setObjectName(objectName);
+            
+            String attribute = null;
+            try {
+                ObjectName oname = new ObjectName(objectName);
+                attribute = "name";
+                resourceLinkForm.setName
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "global";
+                resourceLinkForm.setGlobal
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "type";
+                resourceLinkForm.setType
+                    ((String) mserver.getAttribute(oname, attribute));
+                
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale,
+                        "users.error.attribute.get", attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage
+                         (locale, "users.error.attribute.get", attribute));
+                return (null);
+            } 
+        }
+            
+        // Stash the form bean and forward to the display page
+        saveToken(request);
+        request.setAttribute("resourceLinkForm", resourceLinkForm);
+        return (mapping.findForward("ResourceLink"));
+
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpUserDatabaseAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpUserDatabaseAction.java
new file mode 100644
index 0000000..aa6d7bf
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/SetUpUserDatabaseAction.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that sets up and stashes
+ * a <code>UserDatabaseForm</code> bean in request scope.  The form bean will have
+ * a null <code>objectName</code> property if this form represents a UserDatabase
+ * being added, or a non-null value for an existing UserDatabase.</p>
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SetUpUserDatabaseAction extends Action {
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Set up the form bean based on the creating or editing state
+        String objectName = request.getParameter("objectName");
+        String domain = request.getParameter("domain");
+        
+        UserDatabaseForm userDatabaseForm = new UserDatabaseForm();
+        userDatabaseForm.setFactory
+                            (SaveUserDatabaseAction.USERDB_FACTORY);
+        userDatabaseForm.setType
+                            (ResourceUtils.USERDB_CLASS);  
+        userDatabaseForm.setDomain(domain);               
+
+        if (objectName == null) {
+            userDatabaseForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.userdb.create"));
+            userDatabaseForm.setObjectName(null);
+        } else {
+            userDatabaseForm.setNodeLabel
+                (resources.getMessage(locale, "resources.actions.userdb.edit"));
+            userDatabaseForm.setObjectName(objectName);
+                           
+            String attribute = null;
+            try {
+                ObjectName oname = new ObjectName(objectName);
+                attribute = "name";
+                userDatabaseForm.setName
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "pathname";
+                userDatabaseForm.setPath
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "description";
+                userDatabaseForm.setDescription
+                    ((String) mserver.getAttribute(oname, attribute));
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale,
+                        "users.error.attribute.get", attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage
+                         (locale, "users.error.attribute.get", attribute));
+                return (null);
+            } 
+        }
+            
+        // Stash the form bean and forward to the display page
+        saveToken(request);
+        request.setAttribute("userDatabaseForm", userDatabaseForm);
+        return (mapping.findForward("UserDatabase"));
+
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/UserDatabaseForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/UserDatabaseForm.java
new file mode 100644
index 0000000..59506eb
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/UserDatabaseForm.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.apache.webapp.admin.LabelValueBean;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Form bean for the individual user database page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class UserDatabaseForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * The domain of this data source.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the data source this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the data source this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+
+    /**
+     * The name of the associated entry.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * The path of the associated user database entry.
+     */
+    private String path = null;
+
+    public String getPath() {
+        return (this.path);
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    /**
+     * The type of the resource.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    /**
+     * The factory that implements the user database entry.
+     */
+    private String factory = null;
+
+    public String getFactory() {
+        return (this.factory);
+    }
+
+    public void setFactory(String factory) {
+        this.factory = factory;
+    }
+
+    /**
+     * The description of the associated entry.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        name = null;
+        type = null;
+        path = null;
+        factory = null;
+        description = null;
+
+    }
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    private ActionErrors errors = null;
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+
+        errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+        //if (submit != null) {
+
+            // name is a required field
+            if ((name == null) || (name.length() < 1)) {
+                errors.add("name",
+                           new ActionError("resources.error.name.required"));
+            }
+
+            // path is a required field
+            if ((path == null) || (path.length() < 1)) {
+                errors.add("path",
+                           new ActionError("resources.error.path.required"));
+            }
+
+            // Quotes not allowed in name
+            if ((name != null) && (name.indexOf('"') >= 0)) {
+                errors.add("name",
+                           new ActionError("users.error.quotes"));
+            }
+
+            // Quotes not allowed in path
+            if ((path != null) && (path.indexOf('"') > 0)) {
+                errors.add("path",
+                           new ActionError("users.error.quotes"));
+            }
+
+            // Quotes not allowed in description
+            if ((description != null) && (description.indexOf('"') > 0)) {
+                errors.add("description",
+                           new ActionError("users.error.quotes"));
+            }
+        //}
+        return (errors);
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/UserDatabasesForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/UserDatabasesForm.java
new file mode 100644
index 0000000..4f1f53c
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/resources/UserDatabasesForm.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.resources;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the delete env entries page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class UserDatabasesForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the specified user databases.
+     */
+    private String userDatabases[] = null;
+
+    public String[] getUserDatabases() {
+        return (this.userDatabases);
+    }
+
+    public void setUserDatabases(String userDatabases[]) {
+        this.userDatabases = userDatabases;
+    }
+
+    /**
+     * The domain of this userdatabase.
+     */
+    private String domain = null;
+    
+    /**
+     * Return the domain of the userdatabase this bean refers to.
+     */
+    public String getDomain() {
+        return this.domain;
+    }
+
+    /**
+     * Set the domain of the userdatabase this bean refers to.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.userDatabases = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/server/EditServerAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/server/EditServerAction.java
new file mode 100644
index 0000000..45672b7
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/server/EditServerAction.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.server;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+
+import javax.management.modelmbean.ModelMBean;
+import javax.management.modelmbean.ModelMBeanInfo;
+
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.ApplicationServlet;
+
+/**
+ * Test <code>Action</code> that handles events from the tree control test
+ * page.
+ *
+ * @author Jazmin Jonson
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class EditServerAction extends Action {
+    
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+    ActionForm form,
+    HttpServletRequest request,
+    HttpServletResponse response)
+    throws IOException, ServletException {
+        
+         // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+
+        // label of the node that was clicked on.
+        String nodeLabel = request.getParameter("nodeLabel");  
+        String select = request.getParameter("select");        
+        
+        ServerForm serverFm = new ServerForm();
+        session.setAttribute("serverForm", serverFm);
+        serverFm.setNodeLabel(nodeLabel);        
+        serverFm.setObjectName(select);
+        
+        ObjectName sname = null;    
+        try {
+            sname = new ObjectName(select);
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.serviceName.bad",
+                                     request.getParameter("select"));
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+      
+        String attribute = null;
+        try {
+            // Copy scalar properties
+            attribute = "port";
+            serverFm.setPortNumberText
+                (((Integer) mBServer.getAttribute(sname, attribute)).toString());
+            attribute = "shutdown";
+            serverFm.setShutdownText
+                ((String) mBServer.getAttribute(sname, attribute));
+
+            } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+            return (null);
+        }
+
+        //forward to the server jsp.
+        return (mapping.findForward("Server"));        
+    }
+        
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/server/SaveServerAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/server/SaveServerAction.java
new file mode 100644
index 0000000..56a1c11
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/server/SaveServerAction.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.server;
+
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.struts.util.MessageResources;
+
+/**
+ * Implementation of <strong>Action</strong> that saves server properties.
+ *
+ * @author Jazmin Jonson
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveServerAction extends Action {
+    
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+    ActionForm form,
+    HttpServletRequest request,
+    HttpServletResponse response)
+    throws IOException, ServletException {
+        
+       // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        ActionErrors errors = new ActionErrors();
+        
+        // Report any errors we have discovered back to the original form
+        if (!errors.isEmpty()) {
+            saveErrors(request, errors);
+            return (new ActionForward(mapping.getInput()));
+        }
+        
+        ServerForm sform = (ServerForm) form;
+        String sObjectName = sform.getObjectName();
+        // Acquire a reference to the Server MBean
+        ObjectName soname = null;
+        try {            
+            soname = new ObjectName(sObjectName);
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire Server MBean reference ", t);
+        }
+        
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try{          
+            attribute = "port";
+            int port = 0;
+            try {
+                port = Integer.parseInt(sform.getPortNumberText());
+            } catch (Throwable t) {
+                port = 0;
+            }
+            mBServer.setAttribute(soname,
+                                  new Attribute("port", new Integer(port)));   
+            // set port warning as port < 1024 requires
+            // special software capabilities
+            if (port < 1024) {    
+                request.setAttribute("warning", "server.port.warning");
+            }
+            
+            attribute = "shutdown";
+            mBServer.setAttribute(soname,
+                                  new Attribute("shutdown", sform.getShutdownText()));
+            
+        } catch(Exception e){
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+       }
+        
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));        
+    }  
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/server/ServerForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/server/ServerForm.java
new file mode 100644
index 0000000..fbc2553
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/server/ServerForm.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.server;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+import java.util.List;
+
+/**
+ * Form bean for the server form page.  
+ * @author Patrick Luby
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class ServerForm extends ActionForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    /**
+     * The text for the node label.
+     */
+    private String nodeLabel = null;
+    
+    /**
+     * The text for the port number.
+     */    
+    private String portNumberText = "8080";
+    
+    /**
+     * The text for the shutdown text.
+     */    
+    private String shutdownText = null;
+    
+    /**
+     * The object name of the Connector this bean refers to.
+     */
+    private String objectName = null;
+    
+    // ------------------------------------------------------------- Properties
+    /**
+     * Return the label of the node that was clicked.
+     */
+    public String getNodeLabel() {
+        
+        return this.nodeLabel;
+        
+    }
+    
+    /**
+     * Set the node label.
+     */
+    public void setNodeLabel(String nodeLabel) {
+        
+        this.nodeLabel = nodeLabel;
+        
+    }    
+    
+    /**
+     * Return the portNumberText.
+     */
+    public String getPortNumberText() {
+        
+        return this.portNumberText;
+        
+    }
+    
+    /**
+     * Set the portNumberText.
+     */
+    public void setPortNumberText(String portNumberText) {
+        
+        this.portNumberText = portNumberText;
+        
+    }
+    
+    /**
+     * Return the Shutdown Text.
+     */
+    public String getShutdownText() {
+        
+        return this.shutdownText;
+        
+    }
+    
+    /**
+     * Set the Shut down  Text.
+     */
+    public void setShutdownText(String shutdownText) {
+        
+        this.shutdownText = shutdownText;
+        
+    }
+    
+    /**
+     * Return the object name of the Connector this bean refers to.
+     */
+    public String getObjectName() {
+
+        return this.objectName;
+
+    }
+
+
+    /**
+     * Set the object name of the Connector this bean refers to.
+     */
+    public void setObjectName(String objectName) {
+
+        this.objectName = objectName;
+
+    }
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+        
+        this.portNumberText = null;
+        this.shutdownText = null;
+        
+    }
+    
+    
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        //if (submit != null) {
+            
+            // check for portNumber -- must not be blank, must be in
+            // the range 1 to 65535.
+            
+            if ((portNumberText == null) || (portNumberText.length() < 1)) {
+                errors.add("portNumberText",
+                new ActionError("error.portNumber.required"));
+            } else {
+                try {
+                    int port = Integer.parseInt(portNumberText);
+                    if ((port <= 0) || (port >65535 ))
+                        errors.add("portNumberText", 
+                            new ActionError("error.portNumber.range"));
+                } catch (NumberFormatException e) {
+                    errors.add("portNumberText", 
+                        new ActionError("error.portNumber.format"));
+                }
+            }
+        
+            // shutdown text can be any non-empty string of atleast 6 characters.
+            
+            if ((shutdownText == null) || (shutdownText.length() < 7))
+                errors.add("shutdownText",
+                new ActionError("error.shutdownText.length"));
+            
+        //}
+        
+        return errors;
+        
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/AddServiceAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/AddServiceAction.java
new file mode 100644
index 0000000..aa18207
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/AddServiceAction.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.service;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+
+/**
+ * The <code>Action</code> that sets up <em>Add Service</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class AddServiceAction extends Action {
+        
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        String serverName = request.getParameter("select");
+        
+        // Fill in the form values for display and editing
+        ServiceForm serviceFm = new ServiceForm();
+        session.setAttribute("serviceForm", serviceFm);
+        serviceFm.setAdminAction("Create");
+        serviceFm.setObjectName("");
+        serviceFm.setEngineObjectName("");
+        serviceFm.setServiceName("");
+        serviceFm.setEngineName("");
+        serviceFm.setDefaultHost("localhost");        
+        serviceFm.setAdminServiceName("");
+        serviceFm.setServerObjectName(serverName);
+        ArrayList hosts = new ArrayList();
+        hosts.add(new LabelValueBean
+                  (resources.getMessage(locale, "list.none"), ""));
+        serviceFm.setHostNameVals(hosts);
+        
+        // Forward to the service display page
+        return (mapping.findForward("Service"));
+        
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/DeleteServiceAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/DeleteServiceAction.java
new file mode 100644
index 0000000..446c6ec
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/DeleteServiceAction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.service;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.TreeSet;
+import java.util.Set;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.Lists;
+
+/**
+ * The <code>Action</code> that sets up <em>Delete Services</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteServiceAction extends Action {
+    
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        String select = request.getParameter("select");
+        String adminService = null;
+        ObjectName oname = null;
+        String domain = null;
+        // Get the service name the admin app runs on
+        // this service cannot be deleted from the admin tool
+        try {
+            oname = new ObjectName(select);
+            domain = oname.getDomain();
+            adminService = Lists.getAdminAppService(
+                                  mBServer, domain ,request);
+         } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.serviceName.bad",
+                                 adminService);
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+        request.setAttribute("adminAppService", adminService);
+ 
+        // Set up a form bean containing the currently selected
+        // objects to be deleted
+        ServicesForm servicesForm = new ServicesForm();
+        if (select != null) {
+            String services[] = new String[1];
+            services[0] = select;
+            servicesForm.setServices(services);
+        }
+        request.setAttribute("servicesForm", servicesForm);
+
+        // Accumulate a list of all available services
+        ArrayList list = new ArrayList();
+        try {
+            String pattern = "*" + TomcatTreeBuilder.SERVICE_TYPE +
+                TomcatTreeBuilder.WILDCARD;
+            Iterator items =
+                mBServer.queryNames(new ObjectName(pattern), null).iterator();
+            while (items.hasNext()) {
+                list.add(items.next().toString());
+            }
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.select"));
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.select"));
+            return (null);
+        }
+        Collections.sort(list);
+        request.setAttribute("servicesList", list);    
+        
+        // Forward to the list display page
+        return (mapping.findForward("Services"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/DeleteServicesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/DeleteServicesAction.java
new file mode 100644
index 0000000..aaeefc7
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/DeleteServicesAction.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.service;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ObjectInstance;
+import javax.management.modelmbean.ModelMBean;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+/**
+ * The <code>Action</code> that completes <em>Delete Services</em>
+ * transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteServicesAction extends Action {
+
+
+    /**
+     * Signature for the <code>removeService</code> operation.
+     */
+    private String removeServiceTypes[] =
+    { "java.lang.String",      // Object name
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        
+        // Look up the components we will be using as needed
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Delete the specified Services
+        String services[]  = ((ServicesForm) form).getServices();
+        String values[] = new String[1];
+        String operation = "removeService";
+        try {
+
+            // Look up our tree control data structure
+            TreeControl control = (TreeControl)
+                session.getAttribute("treeControlTest");
+
+            // Remove the specified services
+            for (int i = 0; i < services.length; i++) {
+                values[0] = services[i];
+                ObjectName oname = new ObjectName(services[i]);
+                String domain = oname.getDomain();
+                ObjectName fname = 
+                        TomcatTreeBuilder.getMBeanFactory();
+                mBServer.invoke(fname, operation,
+                                values, removeServiceTypes);
+                if (control != null) {
+                    control.selectNode(null);
+                    TreeControlNode node = control.findNode(services[i]);
+                    if (node != null) {
+                        node.remove();
+                    } else {
+                        getServlet().log("Missing TreeControlNode for " +
+                                         services[i]);
+                    }
+                } else {
+                    getServlet().log("Missing TreeControl attribute");
+                }
+            }
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      operation), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      operation));
+            return (null);
+
+        }
+
+        // Report successful completion of this transaction
+        return (mapping.findForward("Save Successful"));
+
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/EditServiceAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/EditServiceAction.java
new file mode 100644
index 0000000..0030499
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/EditServiceAction.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.service;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * The <code>Action</code> that sets up <em>Edit Service</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class EditServiceAction extends Action {
+    
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Set up the object names of the MBeans we are manipulating
+        ObjectName sname = null;
+        ObjectName ename = null;
+        StringBuffer sb = null;
+        try {
+            sname = new ObjectName(request.getParameter("select"));
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.serviceName.bad",
+                                     request.getParameter("select"));
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+        try {
+            sb = new StringBuffer(sname.getDomain());
+            sb.append(":type=Engine");
+            ename = new ObjectName(sb.toString());
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.engineName.bad",
+                                     sb.toString());
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+
+        String adminService = null;
+        // Get the service name the admin app runs on
+        // this service cannot be deleted from the admin tool
+        try {
+            adminService = Lists.getAdminAppService(
+                                  mBServer, sname.getDomain(),request);
+         } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.serviceName.bad",
+                                 adminService);
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+
+        
+        // Fill in the form values for display and editing
+        ServiceForm serviceFm = new ServiceForm();
+        session.setAttribute("serviceForm", serviceFm);
+        serviceFm.setAdminAction("Edit");
+        serviceFm.setObjectName(sname.toString());
+        serviceFm.setEngineObjectName(ename.toString());
+        sb = new StringBuffer();
+        sb.append(resources.getMessage(locale, "server.service.treeBuilder.subtreeNode"));
+        sb.append(" (");
+        sb.append(sname.getKeyProperty("serviceName"));
+        sb.append(")");
+        serviceFm.setNodeLabel(sb.toString());
+        serviceFm.setAdminServiceName(adminService);
+        String attribute = null;
+        try {
+
+            // Copy scalar properties
+            attribute = "name";
+            serviceFm.setServiceName
+                ((String) mBServer.getAttribute(sname, attribute));
+            attribute = "name";
+            serviceFm.setEngineName
+                ((String) mBServer.getAttribute(ename, attribute));
+            attribute = "defaultHost";
+            serviceFm.setDefaultHost
+                ((String) mBServer.getAttribute(ename, attribute));
+
+            // Build the list of available hosts
+            attribute = "hosts";
+            ArrayList hosts = new ArrayList();
+            hosts.add(new LabelValueBean
+                      (resources.getMessage(locale, "list.none"), ""));
+            Iterator items = Lists.getHosts(mBServer, sname).iterator();
+            while (items.hasNext()) {
+                ObjectName hname = new ObjectName((String) items.next());
+                String name = hname.getKeyProperty("host");
+                if (name!=null) {
+                    hosts.add(new LabelValueBean(name, name));
+                }
+            }
+            serviceFm.setHostNameVals(hosts);
+
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+            return (null);
+        }
+        
+        // Forward to the service display page
+        return (mapping.findForward("Service"));
+        
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/SaveServiceAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/SaveServiceAction.java
new file mode 100644
index 0000000..8dda996
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/SaveServiceAction.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.service;
+
+
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Vector;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.Lists;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+import org.apache.webapp.admin.valve.ValveUtil;
+
+
+
+/**
+ * The <code>Action</code> that completes <em>Add Service</em> and
+ * <em>Edit Service</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @author Amy Roh
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveServiceAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Signature for the <code>createStandardEngine</code> operation.
+     */
+    private String createStandardEngineTypes[] =
+    { "java.lang.String",     // parent
+      "java.lang.String",     // name
+      "java.lang.String",     // defaultHost
+    };
+
+
+    /**
+     * Signature for the <code>createStandardService</code> operation.
+     */
+    private String createStandardServiceTypes[] =
+    { "java.lang.String",     // parent
+      "java.lang.String",     // name
+      "java.lang.String"      // domain
+    };
+
+
+    /**
+     * Signature for the <code>createStandardEngineService</code> operation.
+     */
+    private String createStandardEngineServiceTypes[] =
+    { "java.lang.String",     // parent
+      "java.lang.String",     // engineName
+      "java.lang.String",     // defaultHost
+      "java.lang.String"      // serviceName
+    };
+    
+    
+    /**
+     * Signature for the <code>createUserDatabaseRealm</code> operation.
+     */
+    private String createUserDatabaseRealmTypes[] =
+    { "java.lang.String",     // parent
+      "java.lang.String",     // name
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        ServiceForm sform = (ServiceForm) form;
+        String adminAction = sform.getAdminAction();
+        String sObjectName = sform.getObjectName();
+        String eObjectName = sform.getEngineObjectName();
+        String serverObjectName = sform.getServerObjectName();
+        ObjectName eoname = null;
+        ObjectName soname = null;
+        // Perform a "Create Service" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            String operation = null;
+            String values[] = null;
+
+            try {
+                // engine name is domain
+                String engineName = sform.getEngineName();
+                //String domain = (new ObjectName(serverObjectName)).getDomain();
+                // Ensure that the requested service name is unique
+                ObjectName oname =
+                    new ObjectName("*" + TomcatTreeBuilder.SERVICE_TYPE + 
+                                ",serviceName="+sform.getServiceName());
+                Iterator names = mBServer.queryNames(oname, null).iterator();
+                while (names.hasNext()) {       
+                    if (mBServer.isRegistered((ObjectName)names.next())) {
+                        ActionErrors errors = new ActionErrors();
+                        errors.add("serviceName",
+                               new ActionError("error.serviceName.exists"));
+                        saveErrors(request, errors);
+                        return (new ActionForward(mapping.getInput()));
+                    }
+                }
+                
+                oname = new ObjectName(engineName + TomcatTreeBuilder.ENGINE_TYPE);
+                if (mBServer.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("serviceName",
+                               new ActionError("error.engineName.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                }
+                
+                // Look up our MBeanFactory MBean
+                ObjectName fname = TomcatTreeBuilder.getMBeanFactory();
+
+                // Create a new StandardService and StandardEngine object
+                values = new String[4];
+                values[0] = TomcatTreeBuilder.SERVER_TYPE;
+                values[1] = engineName;
+                values[2] = sform.getDefaultHost();
+                values[3] = sform.getServiceName();
+                operation = "createStandardEngineService";
+                Vector onames = (Vector)
+                    mBServer.invoke(fname, operation,
+                                    values, createStandardEngineServiceTypes);
+                eoname = (ObjectName)onames.get(0);
+                soname = (ObjectName)onames.get(1);
+                sObjectName = soname.toString();
+                eObjectName = eoname.toString();
+                
+                String realmOName = ValveUtil.getObjectName(
+                                    eObjectName, TomcatTreeBuilder.REALM_TYPE);
+            
+                ObjectName roname = new ObjectName(realmOName);
+                if (mBServer.isRegistered(roname)) {
+                    mBServer.unregisterMBean(roname); 
+                }
+                
+                // Create a new UserDatabaseRealm object
+                values = new String[2];
+                values[0] = eObjectName;
+                values[1] = "UserDatabase";
+                operation = "createUserDatabaseRealm";
+                //realmOName = (String)
+                //    mBServer.invoke(fname, operation,
+                //                    values, createUserDatabaseRealmTypes);
+                                    
+                //Enumeration enum = onames.elements();
+                //while (enum.hasMoreElements()) {
+                //    getServlet().log("save service "+enum.nextElement());
+                //}
+                sObjectName = soname.toString();
+                eObjectName = eoname.toString();
+                
+                // Create a new StandardService object
+                //values = new String[3];
+                //values[0] = TomcatTreeBuilder.SERVER_TYPE;
+                //values[1] = sform.getServiceName();
+                //values[2] = engineName;
+                //operation = "createStandardService";
+                //sObjectName = (String)
+                //    mBServer.invoke(fname, operation,
+                //                    values, createStandardServiceTypes);
+
+                // Create a new StandardEngine object
+                //values = new String[3];
+                //values[0] = sObjectName;
+                //values[1] = sform.getEngineName();
+                //values[2] = sform.getDefaultHost();
+                //if ("".equals(values[2])) {
+                //    values[2] = null;
+                //}
+                //operation = "createStandardEngine";
+                //eObjectName = (String)
+                //    mBServer.invoke(fname, operation,
+                //                    values, createStandardEngineTypes);
+
+                // Add the new Service to our tree control node
+                TreeControl control = (TreeControl)
+                    session.getAttribute("treeControlTest");
+                if (control != null) {
+                    String parentName = TomcatTreeBuilder.DEFAULT_DOMAIN + 
+                                            TomcatTreeBuilder.SERVER_TYPE;
+                    TreeControlNode parentNode = control.findNode(parentName);
+                    if (parentNode != null) {
+                        String nodeLabel = resources.getMessage(locale, 
+                            "server.service.treeBuilder.subtreeNode") +" (" +
+                            soname.getKeyProperty("serviceName") + ")";
+                        String encodedName =
+                            URLEncoder.encode(sObjectName,TomcatTreeBuilder.URL_ENCODING);
+                        TreeControlNode childNode =
+                            new TreeControlNode(sObjectName,
+                                                "Service.gif",
+                                                nodeLabel,
+                                                "EditService.do?select=" +
+                                                encodedName,
+                                                "content",
+                                                true, engineName);
+                        parentNode.addChild(childNode);
+                        // update tree to display the newly added realm
+                        //Iterator realmNames =
+                        //    Lists.getRealms(mBServer, sObjectName).iterator();
+                        //while (realmNames.hasNext()) {
+                        //    String realmName = (String) realmNames.next();
+                        //    ObjectName objectName = new ObjectName(realmName);
+                        //    nodeLabel = "Realm for service (" + 
+                        //                        sform.getServiceName() + ")";
+                        //    TreeControlNode realmNode =
+                        //        new TreeControlNode(realmName,
+                        //                            "Realm.gif",
+                        //                            nodeLabel,
+                        //                            "EditRealm.do?select=" +
+                        //                            URLEncoder.encode(realmName) +
+                        //                            "&nodeLabel=" +
+                        //                            URLEncoder.encode(nodeLabel),
+                        //                            "content",
+                        //                            false, engineName);
+                        //    childNode.addChild(realmNode);               
+                        //}         
+                        // FIXME - force a redisplay
+                    } else {
+                        getServlet().log
+                            ("Cannot find parent node '" + parentName + "'");
+                    }
+                } else {
+                    getServlet().log
+                        ("Cannot find TreeControlNode!");
+                }
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          operation), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          operation));
+                return (null);
+
+            }
+
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+        
+            eoname = new ObjectName(eObjectName);
+            soname = new ObjectName(sObjectName);
+
+            attribute = "defaultHost";
+            String defaultHost = sform.getDefaultHost();
+            if ("".equals(defaultHost)) {
+                defaultHost = null;
+            }
+            mBServer.setAttribute(eoname,
+                                  new Attribute("defaultHost", defaultHost));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+        
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+        
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/ServiceForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/ServiceForm.java
new file mode 100644
index 0000000..1a46319
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/ServiceForm.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.service;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.util.List;
+
+/**
+ * Form bean for the service page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class ServiceForm extends ActionForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    
+
+    /**
+     * The administrative action represented by this form.
+     */
+    private String adminAction = "Edit";
+
+
+    /**
+     * The object name of the Engine this bean refers to.
+     */
+    private String engineObjectName = null;
+
+
+    /**
+     * The object name of the Service this bean refers to.
+     */
+    private String objectName = null;
+
+
+    /**
+     * The text for the serviceName.
+     */
+    private String serviceName = null;    
+
+    /**
+     * The text for the serverObjectName.
+     */
+    private String serverObjectName = null; 
+    
+   /**
+     * The text for the node label.
+    */
+    private String nodeLabel = null; 
+    
+    /**
+     * The text for the engine Name.
+     */
+    private String engineName = null;
+    
+    
+    /**
+     * The name of the service the admin app runs on.
+     */
+    private String adminServiceName = null;    
+
+    /**
+     * The text for the defaultHost Name.
+     */
+    private String defaultHost = null;
+    
+    private List hostNameVals = null;
+
+
+    // ------------------------------------------------------------- Properties
+    
+
+    /**
+     * Return the administrative action represented by this form.
+     */
+    public String getAdminAction() {
+
+        return this.adminAction;
+
+    }
+
+
+    /**
+     * Set the administrative action represented by this form.
+     */
+    public void setAdminAction(String adminAction) {
+
+        this.adminAction = adminAction;
+
+    }
+
+
+    /**
+     * Return the object name of the Engine this bean refers to.
+     */
+    public String getEngineObjectName() {
+
+        return this.engineObjectName;
+
+    }
+
+
+    /**
+     * Set the object name of the Engine this bean refers to.
+     */
+    public void setEngineObjectName(String engineObjectName) {
+
+        this.engineObjectName = engineObjectName;
+
+    }
+
+
+    /**
+     * Return the object name of the Service this bean refers to.
+     */
+    public String getObjectName() {
+
+        return this.objectName;
+
+    }
+
+
+    /**
+     * Set the object name of the Service this bean refers to.
+     */
+    public void setObjectName(String objectName) {
+
+        this.objectName = objectName;
+
+    }
+
+
+    /**
+     * Return the label of the node that was clicked.
+     */
+    public String getNodeLabel() {
+        
+        return this.nodeLabel;
+        
+    }
+    
+    /**
+     * Set the node label.
+     */
+    public void setNodeLabel(String nodeLabel) {
+        
+        this.nodeLabel = nodeLabel;
+        
+    }
+        
+    /**
+     * Return the host name values.
+     */
+    public List getHostNameVals() {
+        
+        return this.hostNameVals;
+        
+    }
+    
+    /**
+     * Set the hostName values.
+     */
+    public void setHostNameVals(List hostNameVals) {
+        
+        this.hostNameVals = hostNameVals;
+        
+    }
+    
+    /**
+     * Set the engineName.
+     */
+    
+    public void setEngineName(String engineName) {
+        
+        this.engineName = engineName;
+        
+    }
+    
+    
+    /**
+     * Return the engineName.
+     */
+    
+    public String getEngineName() {
+        
+        return this.engineName;
+        
+    }
+    
+    /**
+     * Return the Server ObjectName.
+     */
+    public String getServerObjectName() {
+        
+        return this.serverObjectName;
+        
+    }
+    
+    /**
+     * Set the Server Name.
+     */
+    public void setServerObjectName(String serverObjectName) {
+        
+        this.serverObjectName = serverObjectName;
+        
+    }
+    
+    /**
+     * Return the Service Name.
+     */
+    public String getServiceName() {
+        
+        return this.serviceName;
+        
+    }
+    
+    /**
+     * Set the Service Name.
+     */
+    public void setServiceName(String serviceName) {
+        
+        this.serviceName = serviceName;
+        
+    }
+
+    /**
+     * Return the name of the service the admin app runs on.
+     */
+    public String getAdminServiceName() {
+
+        return this.adminServiceName;
+
+    }
+
+    /**
+     * Set the name of the service the admin app runs on.
+     */
+    public void setAdminServiceName(String adminServiceName) {
+
+        this.adminServiceName = adminServiceName;
+
+    }
+
+    /**
+     * Return the default Host.
+     */
+    public String getDefaultHost() {
+        
+        return this.defaultHost;
+        
+    }
+    
+    /**
+     * Set the default Host.
+     */
+    public void setDefaultHost(String defaultHost) {
+
+        this.defaultHost = defaultHost;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+        
+        this.engineObjectName = null;
+        this.objectName = null;
+        this.serviceName = null;
+        this.engineName = null;
+        this.adminServiceName = null;
+        this.defaultHost = null;
+    }
+    
+
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ServiceForm[adminAction=");
+        sb.append(adminAction);
+        sb.append(",defaultHost=");
+        sb.append(defaultHost);
+        sb.append(",engineName=");
+        sb.append(engineName);
+        sb.append(",engineObjectName='");
+        sb.append(engineObjectName);
+        sb.append("',objectName='");
+        sb.append(objectName);
+        sb.append("',serviceName=");
+        sb.append(serviceName);
+        sb.append("',serverObjectName=");
+        sb.append(serverObjectName);
+        sb.append("',adminServiceName=");
+        sb.append(adminServiceName);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        String submit = request.getParameter("submit");
+        
+        //if (submit != null) {
+
+            if ((serviceName == null) || (serviceName.length() < 1)) {
+                errors.add("serviceName",
+                           new ActionError("error.serviceName.required"));
+            }
+            
+            if ((engineName == null) || (engineName.length() < 1)) {
+                errors.add("engineName",
+                           new ActionError("error.engineName.required"));
+            }
+
+        //}
+        
+        return errors;
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/ServicesForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/ServicesForm.java
new file mode 100644
index 0000000..19fd88c
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/service/ServicesForm.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.service;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for deleting services.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class ServicesForm extends ActionForm {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the services to be deleted.
+     */
+    private String services[] = new String[0];
+
+    public String[] getServices() {
+        return (this.services);
+    }
+
+    public void setServices(String services[]) {
+        this.services = services;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.services = new String[0];
+
+    }
+        
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/BaseForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/BaseForm.java
new file mode 100644
index 0000000..38c51c3
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/BaseForm.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.users;
+
+
+import javax.management.ObjectName;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Base class for form beans for the user administration
+ * options.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class BaseForm extends ActionForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The MBean Name of UserDatabase containing this object.
+     */
+    private String databaseName = null;
+
+    public String getDatabaseName() {
+        if ((this.databaseName == null) && (this.objectName != null)) {
+            try {
+                ObjectName oname = new ObjectName(this.objectName);
+                this.databaseName = oname.getDomain() + ":" +
+                  "type=UserDatabase,database=" +
+                  oname.getKeyProperty("database");
+            } catch (Throwable t) {
+                this.databaseName = null;
+            }
+        }
+        return (this.databaseName);
+    }
+
+    public void setDatabaseName(String databaseName) {
+        if ((databaseName != null) && (databaseName.length() < 1)) {
+            this.databaseName = null;
+        } else {
+            this.databaseName = databaseName;
+        }
+    }
+
+
+    /**
+     * The node label to be displayed in the user interface.
+     */
+    private String nodeLabel = null;
+
+    public String getNodeLabel() {
+        return (this.nodeLabel);
+    }
+
+    public void setNodeLabel(String nodeLabel) {
+        this.nodeLabel = nodeLabel;
+    }
+
+
+    /**
+     * The MBean object name of this object.  A null or zero-length
+     * value indicates that this is a new object.
+     */
+    private String objectName = null;
+
+    public String getObjectName() {
+        return (this.objectName);
+    }
+
+    public void setObjectName(String objectName) {
+        if ((objectName != null) && (objectName.length() < 1)) {
+            this.objectName = null;
+        } else {
+            this.objectName = objectName;
+        }
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        databaseName = null;
+        nodeLabel = null;
+        objectName = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/DeleteGroupsAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/DeleteGroupsAction.java
new file mode 100644
index 0000000..e4299e1
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/DeleteGroupsAction.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that deletes the
+ * specified set of groups.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DeleteGroupsAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List Groups Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        GroupsForm groupsForm = (GroupsForm) form;
+        String databaseName = groupsForm.getDatabaseName();
+        String groups[] = groupsForm.getGroups();
+        if (groups == null) {
+            groups = new String[0];
+        }
+
+        // Perform "Delete Group" transactions as required
+        try {
+
+            ObjectName dname = new ObjectName(databaseName);
+            String signature[] = new String[1];
+            signature[0] = "java.lang.String";
+            Object params[] = new String[1];
+
+            for (int i = 0; i < groups.length; i++) {
+                ObjectName oname = new ObjectName(groups[i]);
+                params[0] = ObjectName.unquote(oname.getKeyProperty("groupname"));
+                mserver.invoke(dname, "removeGroup",
+                               params, signature);
+            }
+
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "removeGroup"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "removeGroup"));
+            return (null);
+
+        }
+
+        // Save the updated database information
+        try {
+
+            ObjectName dname = new ObjectName(databaseName);
+            mserver.invoke(dname, "save",
+                           new Object[0], new String[0]);
+
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "save"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "save"));
+            return (null);
+
+        }
+
+        // Proceed to the list groups screen
+        return (mapping.findForward("Groups List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/DeleteRolesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/DeleteRolesAction.java
new file mode 100644
index 0000000..32ff6dc
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/DeleteRolesAction.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that deletes the
+ * specified set of roles.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DeleteRolesAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List Roles Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        RolesForm rolesForm = (RolesForm) form;
+        String databaseName = rolesForm.getDatabaseName();
+        String roles[] = rolesForm.getRoles();
+        if (roles == null) {
+            roles = new String[0];
+        }
+
+        // Perform "Delete Role" transactions as required
+        try {
+
+            ObjectName dname = new ObjectName(databaseName);
+            String signature[] = new String[1];
+            signature[0] = "java.lang.String";
+            Object params[] = new String[1];
+
+            for (int i = 0; i < roles.length; i++) {
+                ObjectName oname = new ObjectName(roles[i]);
+                params[0] = oname.getKeyProperty("rolename");
+                mserver.invoke(dname, "removeRole",
+                               params, signature);
+            }
+
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "removeRole"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "removeRole"));
+            return (null);
+
+        }
+
+        // Save the updated database information
+        try {
+
+            ObjectName dname = new ObjectName(databaseName);
+            mserver.invoke(dname, "save",
+                           new Object[0], new String[0]);
+
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "save"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "save"));
+            return (null);
+
+        }
+
+        // Proceed to the list roles screen
+        return (mapping.findForward("Roles List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/DeleteUsersAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/DeleteUsersAction.java
new file mode 100644
index 0000000..2660517
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/DeleteUsersAction.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that deletes the
+ * specified set of users.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class DeleteUsersAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List Users Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        UsersForm usersForm = (UsersForm) form;
+        String databaseName = usersForm.getDatabaseName();
+        String users[] = usersForm.getUsers();
+        if (users == null) {
+            users = new String[0];
+        }
+
+        // Perform "Delete User" transactions as required
+        try {
+
+            ObjectName dname = new ObjectName(databaseName);
+            String signature[] = new String[1];
+            signature[0] = "java.lang.String";
+            Object params[] = new String[1];
+
+            for (int i = 0; i < users.length; i++) {
+                ObjectName oname = new ObjectName(users[i]);
+                params[0] = ObjectName.unquote(oname.getKeyProperty("username"));
+                mserver.invoke(dname, "removeUser",
+                               params, signature);
+            }
+
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "removeUser"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "removeUser"));
+            return (null);
+
+        }
+
+        // Save the updated database information
+        try {
+
+            ObjectName dname = new ObjectName(databaseName);
+            mserver.invoke(dname, "save",
+                           new Object[0], new String[0]);
+
+        } catch (Throwable t) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "save"), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "save"));
+            return (null);
+
+        }
+
+        // Proceed to the list users screen
+        return (mapping.findForward("Users List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/GroupForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/GroupForm.java
new file mode 100644
index 0000000..71f0448
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/GroupForm.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.users;
+
+import java.net.URLDecoder;
+import javax.management.MBeanServer;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * Form bean for the individual group page.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class GroupForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+   /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+    
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this group.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The groupname of this group.
+     */
+    private String groupname = null;
+
+    public String getGroupname() {
+        return (this.groupname);
+    }
+
+    public void setGroupname(String groupname) {
+        this.groupname = groupname;
+    }
+
+
+    /**
+     * The MBean Names of the roles associated with this group.
+     */
+    private String roles[] = new String[0];
+
+    public String[] getRoles() {
+        return (this.roles);
+    }
+
+    public void setRoles(String roles[]) {
+        if (roles == null) {
+            roles = new String[0];
+        }
+        this.roles = roles;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        description = null;
+        groupname = null;
+        roles = new String[0];
+
+    }
+
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        try {
+            // Look up the components we will be using as needed
+            if (mserver == null) {
+                mserver = ((ApplicationServlet) getServlet()).getServer();
+            }
+         
+            // Set up beans containing all possible groups and roles
+            String databaseName =
+                URLDecoder.decode(request.getParameter("databaseName"),TomcatTreeBuilder.URL_ENCODING);
+            request.setAttribute("rolesForm",
+                                 UserUtils.getRolesForm(mserver,
+                                                        databaseName));
+        } catch (Exception e) {
+            // do nothing since the form returns validation error
+        }
+        
+        ActionErrors errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+        //if (submit != null) {
+
+            // groupname is a required field
+            if ((groupname == null) || (groupname.length() < 1)) {
+                errors.add("groupname",
+                           new ActionError("users.error.groupname.required"));
+            }
+
+            // Quotes not allowed in groupname
+            if ((groupname != null) && (groupname.indexOf('"') >= 0)) {
+                errors.add("groupname",
+                           new ActionError("users.error.quotes"));
+            }
+
+            // Quotes not allowed in description
+            if ((description != null) && (description.indexOf('"') > 0)) {
+                errors.add("description",
+                           new ActionError("users.error.quotes"));
+            }
+
+        //}
+
+        return (errors);
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/GroupsForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/GroupsForm.java
new file mode 100644
index 0000000..8f806a5
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/GroupsForm.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.users;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the delete groups page.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class GroupsForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the specified groups.
+     */
+    private String groups[] = null;
+
+    public String[] getGroups() {
+        return (this.groups);
+    }
+
+    public void setGroups(String groups[]) {
+        this.groups = groups;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.groups = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/ListGroupsAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/ListGroupsAction.java
new file mode 100644
index 0000000..e8266d5
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/ListGroupsAction.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Locale;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Retrieve the set of MBean names for all currently defined groups,
+ * and expose them in a request attribute named "groupsForm".  This action
+ * requires the following request parameters to be set:</p>
+ * <ul>
+ * <li><strong>databaseName</strong> - Object name of the UserDatabase
+ *     MBean from which we should retrieve the groups list.</li>
+ * <li><strong>forward</strong> - Global forward to which we should
+ *     go after stashing the groups list.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ListGroupsAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Create a form bean containing the requested MBean Names
+        String databaseName =
+            URLDecoder.decode(request.getParameter("databaseName"),TomcatTreeBuilder.URL_ENCODING);
+        GroupsForm groupsForm = null;
+        try {
+            groupsForm = UserUtils.getGroupsForm(mserver, databaseName);
+        } catch (Exception e) {
+            getServlet().log(resources.getMessage
+                             (locale,
+                              "users.error.attribute.get", "groups"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "groups"));
+            return null;
+        }
+
+        // Stash the results in request scope
+        request.setAttribute("groupsForm", groupsForm);
+        saveToken(request);
+        String forward =
+            URLDecoder.decode(request.getParameter("forward"),TomcatTreeBuilder.URL_ENCODING);
+        return (mapping.findForward(forward));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/ListRolesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/ListRolesAction.java
new file mode 100644
index 0000000..b4002b2
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/ListRolesAction.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Locale;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Retrieve the set of MBean names for all currently defined roles,
+ * and expose them in a request attribute named "rolesForm".  This action
+ * requires the following request parameters to be set:</p>
+ * <ul>
+ * <li><strong>databaseName</strong> - Object name of the UserDatabase
+ *     MBean from which we should retrieve the roles list.</li>
+ * <li><strong>forward</strong> - Global forward to which we should
+ *     go after stashing the roles list.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ListRolesAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+
+        // Create a form bean containing the requested MBean Names
+        String databaseName =
+            URLDecoder.decode(request.getParameter("databaseName"),TomcatTreeBuilder.URL_ENCODING);
+        RolesForm rolesForm = null;
+        try {
+            rolesForm = UserUtils.getRolesForm(mserver, databaseName);
+        } catch (Exception e) {
+            getServlet().log(resources.getMessage
+                             (locale,
+                              "users.error.attribute.get", "roles"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "roles"));
+            return null;
+        }
+
+        // Stash the results in request scope
+        request.setAttribute("rolesForm", rolesForm);
+        saveToken(request);
+        String forward =
+            URLDecoder.decode(request.getParameter("forward"),TomcatTreeBuilder.URL_ENCODING);
+        return (mapping.findForward(forward));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/ListUsersAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/ListUsersAction.java
new file mode 100644
index 0000000..a3ed183
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/ListUsersAction.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Locale;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Retrieve the set of MBean names for all currently defined users,
+ * and expose them in a request attribute named "usersForm".  This action
+ * requires the following request parameters to be set:</p>
+ * <ul>
+ * <li><strong>databaseName</strong> - Object name of the UserDatabase
+ *     MBean from which we should retrieve the users list.</li>
+ * <li><strong>forward</strong> - Global forward to which we should
+ *     go after stashing the users list.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class ListUsersAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+
+        // Create a form bean containing the requested MBean Names
+        String databaseName =
+            URLDecoder.decode(request.getParameter("databaseName"),TomcatTreeBuilder.URL_ENCODING);
+        UsersForm usersForm = null;
+        try {
+            usersForm = UserUtils.getUsersForm(mserver, databaseName);
+        } catch (Exception e) {
+            getServlet().log(resources.getMessage
+                             (locale,
+                              "users.error.attribute.get", "users"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "users"));
+            return null;
+        }
+
+        // Stash the results in request scope
+        request.setAttribute("usersForm", usersForm);
+        saveToken(request);
+        String forward =
+            URLDecoder.decode(request.getParameter("forward"),TomcatTreeBuilder.URL_ENCODING);
+        return (mapping.findForward(forward));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/RoleForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/RoleForm.java
new file mode 100644
index 0000000..d0d7449
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/RoleForm.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.users;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the individual role page.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class RoleForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this role.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The rolename of this role.
+     */
+    private String rolename = null;
+
+    public String getRolename() {
+        return (this.rolename);
+    }
+
+    public void setRolename(String rolename) {
+        this.rolename = rolename;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        description = null;
+        rolename = null;
+
+    }
+
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+
+        ActionErrors errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+        //if (submit != null) {
+
+            // rolename is a required field
+            if ((rolename == null) || (rolename.length() < 1)) {
+                errors.add("rolename",
+                           new ActionError("users.error.rolename.required"));
+            }
+
+            // Quotes not allowed in rolename
+            if ((rolename != null) && (rolename.indexOf('"') >= 0)) {
+                errors.add("rolename",
+                           new ActionError("users.error.quotes"));
+            }
+
+            // Quotes not allowed in description
+            if ((description != null) && (description.indexOf('"') > 0)) {
+                errors.add("description",
+                           new ActionError("users.error.quotes"));
+            }
+
+        //}
+
+        return (errors);
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/RolesForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/RolesForm.java
new file mode 100644
index 0000000..8219e3f
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/RolesForm.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.users;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the delete roles page.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class RolesForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the specified roles.
+     */
+    private String roles[] = null;
+
+    public String[] getRoles() {
+        return (this.roles);
+    }
+
+    public void setRoles(String roles[]) {
+        this.roles = roles;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.roles = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SaveGroupAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SaveGroupAction.java
new file mode 100644
index 0000000..9ddd0d8
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SaveGroupAction.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Implementation of <strong>Action</strong> that saves a new or
+ * updated Group back to the underlying database.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SaveGroupAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List Roles Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        GroupForm groupForm = (GroupForm) form;
+        String databaseName =
+            URLDecoder.decode(groupForm.getDatabaseName(),TomcatTreeBuilder.URL_ENCODING);
+        String objectName = groupForm.getObjectName();
+
+        // Perform an "Add Group" transaction
+        if (objectName == null) {
+
+            String signature[] = new String[2];
+            signature[0] = "java.lang.String";
+            signature[1] = "java.lang.String";
+
+            Object params[] = new Object[2];
+            params[0] = groupForm.getGroupname();
+            params[1] = groupForm.getDescription();
+
+            ObjectName oname = null;
+
+            try {
+
+                // Construct the MBean Name for our UserDatabase
+                oname = new ObjectName(databaseName);
+
+                // Create the new object and associated MBean
+                objectName = (String) mserver.invoke(oname, "createGroup",
+                                                     params, signature);
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          "createGroup"), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          "createGroup"));
+                return (null);
+            }
+
+        }
+
+        // Perform an "Update Group" transaction
+        else {
+
+            ObjectName oname = null;
+            String attribute = null;
+
+            try {
+
+                // Construct an object name for this object
+                oname = new ObjectName(objectName);
+
+                // Update the specified role
+                attribute = "description";
+                mserver.setAttribute
+                    (oname,
+                     new Attribute(attribute, groupForm.getDescription()));
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.set.attribute",
+                                          attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.set.attribute",
+                                          attribute));
+                return (null);
+
+            }
+
+        }
+
+
+        // Reset the roles associated with this group
+        try {
+
+            ObjectName oname = new ObjectName(objectName);
+            mserver.invoke(oname, "removeRoles",
+                           new Object[0], new String[0]);
+            String roles[] = groupForm.getRoles();
+            if (roles == null) {
+                roles = new String[0];
+            }
+            String addsig[] = new String[1];
+            addsig[0] = "java.lang.String";
+            Object addpar[] = new Object[1];
+            for (int i = 0; i < roles.length; i++) {
+                addpar[0] =
+                    (new ObjectName(roles[i])).getKeyProperty("rolename");
+                mserver.invoke(oname, "addRole",
+                               addpar, addsig);
+            }
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "addRole"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "addRole"));
+            return (null);
+
+        }
+
+        // Save the updated database information
+        try {
+
+            ObjectName dname = new ObjectName(databaseName);
+            mserver.invoke(dname, "save",
+                           new Object[0], new String[0]);
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "save"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "save"));
+            return (null);
+
+        }
+
+        // Proceed to the list roles screen
+        return (mapping.findForward("Groups List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SaveRoleAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SaveRoleAction.java
new file mode 100644
index 0000000..6739dec
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SaveRoleAction.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Implementation of <strong>Action</strong> that saves a new or
+ * updated Role back to the underlying database.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SaveRoleAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List Roles Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        RoleForm roleForm = (RoleForm) form;
+        String databaseName =
+            URLDecoder.decode(roleForm.getDatabaseName(),TomcatTreeBuilder.URL_ENCODING);
+        String objectName = roleForm.getObjectName();
+
+        // Perform an "Add Role" transaction
+        if (objectName == null) {
+
+            String signature[] = new String[2];
+            signature[0] = "java.lang.String";
+            signature[1] = "java.lang.String";
+
+            Object params[] = new Object[2];
+            params[0] = roleForm.getRolename();
+            params[1] = roleForm.getDescription();
+
+            ObjectName oname = null;
+
+            try {
+
+                // Construct the MBean Name for our UserDatabase
+                oname = new ObjectName(databaseName);
+
+                // Create the new object and associated MBean
+                mserver.invoke(oname, "createRole",
+                               params, signature);
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          "createRole"), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          "createRole"));
+                return (null);
+            }
+
+        }
+
+        // Perform an "Update Role" transaction
+        else {
+
+            ObjectName oname = null;
+            String attribute = null;
+
+            try {
+
+                // Construct an object name for this object
+                oname = new ObjectName(objectName);
+
+                // Update the specified role
+                attribute = "description";
+                mserver.setAttribute
+                    (oname,
+                     new Attribute(attribute, roleForm.getDescription()));
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.set.attribute",
+                                          attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.set.attribute",
+                                          attribute));
+                return (null);
+
+            }
+
+        }
+
+        // Save the updated database information
+        try {
+
+            ObjectName dname = new ObjectName(databaseName);
+            mserver.invoke(dname, "save",
+                           new Object[0], new String[0]);
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "save"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "save"));
+            return (null);
+
+        }
+
+        // Proceed to the list roles screen
+        return (mapping.findForward("Roles List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SaveUserAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SaveUserAction.java
new file mode 100644
index 0000000..b2c9200
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SaveUserAction.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Implementation of <strong>Action</strong> that saves a new or
+ * updated User back to the underlying database.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SaveUserAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Has this transaction been cancelled?
+        if (isCancelled(request)) {
+            return (mapping.findForward("List Users Setup"));
+        }
+
+        // Check the transaction token
+        if (!isTokenValid(request)) {
+            response.sendError
+                (HttpServletResponse.SC_BAD_REQUEST,
+                 resources.getMessage(locale, "users.error.token"));
+            return (null);
+        }
+
+        // Perform any extra validation that is required
+        UserForm userForm = (UserForm) form;
+        String databaseName =
+            URLDecoder.decode(userForm.getDatabaseName(),TomcatTreeBuilder.URL_ENCODING);
+        String objectName = userForm.getObjectName();
+
+        // Perform an "Add User" transaction
+        if (objectName == null) {
+
+            String signature[] = new String[3];
+            signature[0] = "java.lang.String";
+            signature[1] = "java.lang.String";
+            signature[2] = "java.lang.String";
+
+            Object params[] = new Object[3];
+            params[0] = userForm.getUsername();
+            params[1] = userForm.getPassword();
+            params[2] = userForm.getFullName();
+
+            ObjectName oname = null;
+
+            try {
+
+                // Construct the MBean Name for our UserDatabase
+                oname = new ObjectName(databaseName);
+
+                // Create the new object and associated MBean
+                objectName = (String) mserver.invoke(oname, "createUser",
+                                                     params, signature);
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          "createUser"), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          "createUser"));
+                return (null);
+            }
+
+        }
+
+        // Perform an "Update User" transaction
+        else {
+
+            ObjectName oname = null;
+            String attribute = null;
+
+            try {
+
+                // Construct an object name for this object
+                oname = new ObjectName(objectName);
+
+                // Update the specified user
+                attribute = "fullName";
+                mserver.setAttribute
+                    (oname,
+                     new Attribute(attribute, userForm.getFullName()));
+                attribute = "password";
+                mserver.setAttribute
+                    (oname,
+                     new Attribute(attribute, userForm.getPassword()));
+
+            } catch (Exception e) {
+
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.set.attribute",
+                                          attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.set.attribute",
+                                          attribute));
+                return (null);
+
+            }
+
+        }
+
+        // Reset the groups this user is a member of
+        try {
+
+            ObjectName oname = new ObjectName(objectName);
+            mserver.invoke(oname, "removeGroups",
+                           new Object[0], new String[0]);
+            String groups[] = userForm.getGroups();
+            if (groups == null) {
+                groups = new String[0];
+            }
+            String addsig[] = new String[1];
+            addsig[0] = "java.lang.String";
+            Object addpar[] = new Object[1];
+            for (int i = 0; i < groups.length; i++) {
+                addpar[0] =
+                    (new ObjectName(groups[i])).getKeyProperty("groupname");
+                mserver.invoke(oname, "addGroup",
+                               addpar, addsig);
+            }
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "addGroup"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "addGroup"));
+            return (null);
+
+        }
+
+        // Reset the roles associated with this user
+        try {
+
+            ObjectName oname = new ObjectName(objectName);
+            mserver.invoke(oname, "removeRoles",
+                           new Object[0], new String[0]);
+            String roles[] = userForm.getRoles();
+            if (roles == null) {
+                roles = new String[0];
+            }
+            String addsig[] = new String[1];
+            addsig[0] = "java.lang.String";
+            Object addpar[] = new Object[1];
+            for (int i = 0; i < roles.length; i++) {
+                addpar[0] =
+                    (new ObjectName(roles[i])).getKeyProperty("rolename");
+                mserver.invoke(oname, "addRole",
+                               addpar, addsig);
+            }
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "addRole"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "addRole"));
+            return (null);
+
+        }
+
+        // Save the updated database information
+        try {
+
+            ObjectName dname = new ObjectName(databaseName);
+            mserver.invoke(dname, "save",
+                           new Object[0], new String[0]);
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      "save"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      "save"));
+            return (null);
+
+        }
+
+        // Proceed to the list roles screen
+        return (mapping.findForward("Users List Setup"));
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SetUpGroupAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SetUpGroupAction.java
new file mode 100644
index 0000000..6c002ef
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SetUpGroupAction.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+
+/**
+ * <p>Implementation of <strong>Action</strong> that sets up and stashes
+ * a <code>GroupForm</code> bean in request scope.  The form bean will have
+ * a null <code>objectName</code> property if this form represents a group
+ * being added, or a non-null value for an existing group.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SetUpGroupAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Set up a bean containing all possible roles
+        String databaseName =
+            URLDecoder.decode(request.getParameter("databaseName"),TomcatTreeBuilder.URL_ENCODING);
+        try {
+            request.setAttribute("rolesForm",
+                                 UserUtils.getRolesForm(mserver,
+                                                        databaseName));
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale,
+                                      "users.error.attribute.get",
+                                      "roles"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "roles"));
+            return (null);
+        }
+
+        // Set up the form bean based on the creating or editing state
+        String objectName = request.getParameter("objectName");
+        GroupForm groupForm = new GroupForm();
+        if (objectName == null) {
+            groupForm.setNodeLabel
+                (resources.getMessage(locale, "users.group.newGroup"));
+            groupForm.setObjectName(null);
+        } else {
+            groupForm.setNodeLabel
+                (resources.getMessage(locale, "users.group.oldGroup"));
+            groupForm.setObjectName(objectName);
+            String attribute = null;
+            try {
+                ObjectName oname = new ObjectName(objectName);
+                attribute = "groupname";
+                groupForm.setGroupname
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "description";
+                groupForm.setDescription
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "roles";
+                groupForm.setRoles
+                    ((String[]) mserver.getAttribute(oname, attribute));
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale,
+                        "users.error.attribute.get", attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage
+                         (locale, "users.error.attribute.get", attribute));
+                return (null);
+            }
+        }
+        groupForm.setDatabaseName(databaseName);
+
+        // Stash the form bean and forward to the display page
+        saveToken(request);
+        request.setAttribute("groupForm", groupForm);
+        return (mapping.findForward("Group"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SetUpRoleAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SetUpRoleAction.java
new file mode 100644
index 0000000..7ef6374
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SetUpRoleAction.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Implementation of <strong>Action</strong> that sets up and stashes
+ * a <code>RoleForm</code> bean in request scope.  The form bean will have
+ * a null <code>objectName</code> property if this form represents a role
+ * being added, or a non-null value for an existing role.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SetUpRoleAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        String databaseName =
+            URLDecoder.decode(request.getParameter("databaseName"),TomcatTreeBuilder.URL_ENCODING);
+
+        // Set up the form bean based on the creating or editing state
+        String objectName = request.getParameter("objectName");
+        RoleForm roleForm = new RoleForm();
+        if (objectName == null) {
+            roleForm.setNodeLabel
+                (resources.getMessage(locale, "users.role.newRole"));
+            roleForm.setObjectName(null);
+        } else {
+            roleForm.setNodeLabel
+                (resources.getMessage(locale, "users.role.oldRole"));
+            roleForm.setObjectName(objectName);
+            String attribute = null;
+            try {
+                ObjectName oname = new ObjectName(objectName);
+                attribute = "rolename";
+                roleForm.setRolename
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "description";
+                roleForm.setDescription
+                    ((String) mserver.getAttribute(oname, attribute));
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale,
+                        "users.error.attribute.get", attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage
+                         (locale, "users.error.attribute.get", attribute));
+                return (null);
+            }
+        }
+        roleForm.setDatabaseName(databaseName);
+
+        // Stash the form bean and forward to the display page
+        saveToken(request);
+        request.setAttribute("roleForm", roleForm);
+        return (mapping.findForward("Role"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SetUpUserAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SetUpUserAction.java
new file mode 100644
index 0000000..c4d26c7
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/SetUpUserAction.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Locale;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanInfo;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * <p>Implementation of <strong>Action</strong> that sets up and stashes
+ * a <code>UserForm</code> bean in request scope.  The form bean will have
+ * a null <code>objectName</code> property if this form represents a user
+ * being added, or a non-null value for an existing user.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class SetUpUserAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Look up the components we will be using as needed
+        if (mserver == null) {
+            mserver = ((ApplicationServlet) getServlet()).getServer();
+        }
+        MessageResources resources = getResources(request);
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+
+        // Set up beans containing all possible groups and roles
+        String databaseName =
+            URLDecoder.decode(request.getParameter("databaseName"),TomcatTreeBuilder.URL_ENCODING);
+        try {
+            request.setAttribute("groupsForm",
+                                 UserUtils.getGroupsForm(mserver,
+                                                         databaseName));
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale,
+                                      "users.error.attribute.get",
+                                      "groups"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "groups"));
+            return (null);
+        }
+        try {
+            request.setAttribute("rolesForm",
+                                 UserUtils.getRolesForm(mserver,
+                                                        databaseName));
+        } catch (Exception e) {
+            getServlet().log
+                (resources.getMessage(locale,
+                                      "users.error.attribute.get",
+                                      "roles"), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage
+                 (locale, "users.error.attribute.get", "roles"));
+            return (null);
+        }
+
+        // Set up the form bean based on the creating or editing state
+        String objectName = request.getParameter("objectName");
+        UserForm userForm = new UserForm();
+        if (objectName == null) {
+            userForm.setNodeLabel
+                (resources.getMessage(locale, "users.user.newUser"));
+            userForm.setObjectName(null);
+        } else {
+            userForm.setNodeLabel
+                (resources.getMessage(locale, "users.user.oldUser"));
+            userForm.setObjectName(objectName);
+            String attribute = null;
+            try {
+                ObjectName oname = new ObjectName(objectName);
+                attribute = "username";
+                userForm.setUsername
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "password";
+                userForm.setPassword
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "fullName";
+                userForm.setFullName
+                    ((String) mserver.getAttribute(oname, attribute));
+                attribute = "groups";
+                userForm.setGroups
+                    ((String[]) mserver.getAttribute(oname, attribute));
+                attribute = "roles";
+                userForm.setRoles
+                    ((String[]) mserver.getAttribute(oname, attribute));
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale,
+                        "users.error.attribute.get", attribute), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage
+                         (locale, "users.error.attribute.get", attribute));
+                return (null);
+            }
+        }
+        userForm.setDatabaseName(databaseName);
+
+        // Stash the form bean and forward to the display page
+        saveToken(request);
+        request.setAttribute("userForm", userForm);
+        return (mapping.findForward("User"));
+
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UserForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UserForm.java
new file mode 100644
index 0000000..2b655ff
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UserForm.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.users;
+
+
+import java.net.URLDecoder;
+import javax.management.MBeanServer;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * Form bean for the individual user page.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class UserForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+   /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mserver = null;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * The full name of the associated user.
+     */
+    private String fullName = null;
+
+    public String getFullName() {
+        return (this.fullName);
+    }
+
+    public void setFullName(String fullName) {
+        this.fullName = fullName;
+    }
+
+
+    /**
+     * The MBean Names of the groups associated with this user.
+     */
+    private String groups[] = new String[0];
+
+    public String[] getGroups() {
+        return (this.groups);
+    }
+
+    public void setGroups(String groups[]) {
+        if (groups == null) {
+            groups = new String[0];
+        }
+        this.groups = groups;
+    }
+
+
+    /**
+     * The password of the associated user.
+     */
+    private String password = null;
+
+    public String getPassword() {
+        return (this.password);
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+
+    /**
+     * The MBean Names of the roles associated with this user.
+     */
+    private String roles[] = new String[0];
+
+    public String[] getRoles() {
+        return (this.roles);
+    }
+
+    public void setRoles(String roles[]) {
+        if (roles == null) {
+            roles = new String[0];
+        }
+        this.roles = roles;
+    }
+
+
+    /**
+     * The username of the associated user.
+     */
+    private String username = null;
+
+    public String getUsername() {
+        return (this.username);
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        fullName = null;
+        groups = new String[0];
+        password = null;
+        roles = new String[0];
+        username = null;
+
+    }
+
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        try {
+            // Look up the components we will be using as needed
+            if (mserver == null) {
+                mserver = ((ApplicationServlet) getServlet()).getServer();
+            }
+         
+            // Set up beans containing all possible groups and roles
+            String databaseName =
+                URLDecoder.decode(request.getParameter("databaseName"),TomcatTreeBuilder.URL_ENCODING);
+            request.setAttribute("groupsForm",
+                                 UserUtils.getGroupsForm(mserver,
+                                                         databaseName));
+            request.setAttribute("rolesForm",
+                                 UserUtils.getRolesForm(mserver,
+                                                        databaseName));
+        } catch (Exception e) {
+            // do nothing since the form returns validation error
+        }
+        
+        ActionErrors errors = new ActionErrors();
+
+        String submit = request.getParameter("submit");
+        //if (submit != null) {
+
+            // username is a required field
+            if ((username == null) || (username.length() < 1)) {
+                errors.add("username",
+                           new ActionError("users.error.username.required"));
+            }
+
+            // uassword is a required field
+            if ((password == null) || (username.length() < 1)) {
+                errors.add("password",
+                           new ActionError("users.error.password.required"));
+            }
+
+            // Quotes not allowed in username
+            if ((username != null) && (username.indexOf('"') >= 0)) {
+                errors.add("username",
+                           new ActionError("users.error.quotes"));
+            }
+
+            // Quotes not allowed in password
+            if ((password != null) && (password.indexOf('"') > 0)) {
+                errors.add("description",
+                           new ActionError("users.error.quotes"));
+            }
+
+            // Quotes not allowed in fullName
+            if ((fullName != null) && (fullName.indexOf('"') > 0)) {
+                errors.add("fullName",
+                           new ActionError("users.error.quotes"));
+            }
+
+        //}
+
+        return (errors);
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UserUtils.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UserUtils.java
new file mode 100644
index 0000000..f0380ef
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UserUtils.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.users;
+
+
+import java.util.Arrays;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+
+/**
+ * <p>Shared utility methods for the user database administration module.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class UserUtils {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Construct and return a GroupsForm identifying all currently defined
+     * groups in the specified user database.
+     *
+     * @param mserver MBeanServer to be consulted
+     * @param databaseName MBean Name of the user database to be consulted
+     *
+     * @exception Exception if an error occurs
+     */
+    public static GroupsForm getGroupsForm(MBeanServer mserver,
+                                           String databaseName)
+        throws Exception {
+
+        ObjectName dname = new ObjectName(databaseName);
+        String results[] =
+            (String[]) mserver.getAttribute(dname, "groups");
+        if (results == null) {
+            results = new String[0];
+        }
+        Arrays.sort(results);
+
+        GroupsForm groupsForm = new GroupsForm();
+        groupsForm.setDatabaseName(databaseName);
+        groupsForm.setGroups(results);
+        return (groupsForm);
+
+    }
+
+
+    /**
+     * Construct and return a RolesForm identifying all currently defined
+     * roles in the specified user database.
+     *
+     * @param mserver MBeanServer to be consulted
+     * @param databaseName MBean Name of the user database to be consulted
+     *
+     * @exception Exception if an error occurs
+     */
+    public static RolesForm getRolesForm(MBeanServer mserver,
+                                           String databaseName)
+        throws Exception {
+
+        ObjectName dname = new ObjectName(databaseName);
+        String results[] =
+            (String[]) mserver.getAttribute(dname, "roles");
+        if (results == null) {
+            results = new String[0];
+        }
+        Arrays.sort(results);
+
+        RolesForm rolesForm = new RolesForm();
+        rolesForm.setDatabaseName(databaseName);
+        rolesForm.setRoles(results);
+        return (rolesForm);
+
+    }
+
+
+    /**
+     * Construct and return a UsersForm identifying all currently defined
+     * users in the specified user database.
+     *
+     * @param mserver MBeanServer to be consulted
+     * @param databaseName MBean Name of the user database to be consulted
+     *
+     * @exception Exception if an error occurs
+     */
+    public static UsersForm getUsersForm(MBeanServer mserver,
+                                           String databaseName)
+        throws Exception {
+
+        ObjectName dname = new ObjectName(databaseName);
+        String results[] =
+            (String[]) mserver.getAttribute(dname, "users");
+        if (results == null) {
+            results = new String[0];
+        }
+        Arrays.sort(results);
+
+        UsersForm usersForm = new UsersForm();
+        usersForm.setDatabaseName(databaseName);
+        usersForm.setUsers(results);
+        return (usersForm);
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UsersForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UsersForm.java
new file mode 100644
index 0000000..76a1b3c
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UsersForm.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.users;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the delete users page.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public final class UsersForm extends BaseForm {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the specified users.
+     */
+    private String users[] = null;
+
+    public String[] getUsers() {
+        return (this.users);
+    }
+
+    public void setUsers(String users[]) {
+        this.users = users;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.users = null;
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UsersTreeBuilder.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UsersTreeBuilder.java
new file mode 100644
index 0000000..4d54302
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/users/UsersTreeBuilder.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.users;
+
+
+import java.net.URLEncoder;
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.Globals;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * Implementation of <code>TreeBuilder</code> that adds the nodes required
+ * for administering the user database.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ * @since 4.1
+ */
+
+public class UsersTreeBuilder implements TreeBuilder {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ---------------------------------------------------- TreeBuilder Methods
+
+
+    /**
+     * Add the required nodes to the specified <code>treeControl</code>
+     * instance.
+     *
+     * @param treeControl The <code>TreeControl</code> to which we should
+     *  add our nodes
+     * @param servlet The controller servlet for the admin application
+     * @param request The servlet request we are processing
+     */
+    public void buildTree(TreeControl treeControl,
+                          ApplicationServlet servlet,
+                          HttpServletRequest request) {
+
+        MessageResources resources = (MessageResources)
+            servlet.getServletContext().getAttribute(Globals.MESSAGES_KEY);
+        HttpSession session = request.getSession();
+        Locale locale = (Locale) session.getAttribute(Globals.LOCALE_KEY);
+        addSubtree(treeControl.getRoot(), resources, locale);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Add the subtree of nodes required for user administration.
+     *
+     * @param root The root node of our tree control
+     * @param resources The MessageResources for our localized messages
+     *  messages
+     */
+    protected void addSubtree(TreeControlNode root,
+                              MessageResources resources, Locale locale) {
+
+        try {
+            String databaseName = URLEncoder.encode
+                ("Users:type=UserDatabase,database=UserDatabase",TomcatTreeBuilder.URL_ENCODING);
+
+            TreeControlNode subtree = new TreeControlNode
+                ("Global User and Group Administration",
+                 "folder_16_pad.gif",
+                 resources.getMessage(locale, "users.treeBuilder.subtreeNode"),
+                 null,
+                 "content",
+                 true, "Users");
+            TreeControlNode groups = new TreeControlNode
+                ("Global Administer Groups",
+                 "Groups.gif",
+                 resources.getMessage(locale, "users.treeBuilder.groupsNode"),
+                 "users/listGroups.do?databaseName=" +
+                 URLEncoder.encode(databaseName,TomcatTreeBuilder.URL_ENCODING) +
+                 "&forward=" +
+                 URLEncoder.encode("Groups List Setup",TomcatTreeBuilder.URL_ENCODING),
+                 "content",
+                 false, "Users");
+            TreeControlNode roles = new TreeControlNode
+                ("Global Administer Roles",
+                 "Roles.gif",
+                 resources.getMessage(locale, "users.treeBuilder.rolesNode"),
+                 "users/listRoles.do?databaseName=" +
+                 URLEncoder.encode(databaseName,TomcatTreeBuilder.URL_ENCODING) +
+                 "&forward=" +
+                 URLEncoder.encode("Roles List Setup",TomcatTreeBuilder.URL_ENCODING),
+                 "content",
+                 false, "Users");
+            TreeControlNode users = new TreeControlNode
+                ("Global Administer Users",
+                 "Users.gif",
+                 resources.getMessage(locale, "users.treeBuilder.usersNode"),
+                 "users/listUsers.do?databaseName=" +
+                 URLEncoder.encode(databaseName,TomcatTreeBuilder.URL_ENCODING) +
+                 "&forward=" +
+                 URLEncoder.encode("Users List Setup",TomcatTreeBuilder.URL_ENCODING),
+                 "content",
+                 false, "Users");
+
+            root.addChild(subtree);
+            subtree.addChild(users);
+            subtree.addChild(groups);
+            subtree.addChild(roles);
+        } catch(UnsupportedEncodingException ueex) {
+            // can't happen
+        }
+
+    }
+
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/AccessLogValveForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/AccessLogValveForm.java
new file mode 100644
index 0000000..70816d6
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/AccessLogValveForm.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.util.List;
+
+/**
+ * Form bean for the accesslog valve page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class AccessLogValveForm extends ValveForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    /**
+     * The text for the debug level.
+     */
+    private String debugLvl = "0";
+  
+    /**
+     * Set of valid values for debug level.
+     */
+    private List debugLvlVals = null;
+    
+    /**
+     * The text for the directory.
+     */
+    private String directory = null;
+    
+    /**
+     * The text for the pattern.
+     */
+    private String pattern = null;
+        
+    /**
+     * The text for the prefix.
+     */
+    private String prefix = null;
+    
+    /**
+     * The text for the suffix.
+     */
+    private String suffix = null;
+      
+    /**
+     * The text for the connection URL.
+     */
+    private String resolveHosts = "false";
+      
+    /**
+     * The text for the rotatable.
+     */
+    private String rotatable = "true";    
+       
+    /**
+     * Set of boolean values.
+     */
+    private List booleanVals = null;
+ 
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the debugVals.
+     */
+    public List getDebugLvlVals() {
+        
+        return this.debugLvlVals;
+        
+    }
+    
+    /**
+     * Set the debugVals.
+     */
+    public void setDebugLvlVals(List debugLvlVals) {
+        
+        this.debugLvlVals = debugLvlVals;
+        
+    }
+    
+    /**
+     * Return the booleanVals.
+     */
+    public List getBooleanVals() {
+        
+        return this.booleanVals;
+        
+    }
+    
+    /**
+     * Set the booleanVals.
+     */
+    public void setBooleanVals(List booleanVals) {
+        
+        this.booleanVals = booleanVals;
+        
+    }
+    
+    /**
+     * Return the Debug Level Text.
+     */
+    public String getDebugLvl() {
+        
+        return this.debugLvl;
+        
+    }
+    
+    /**
+     * Set the Debug Level Text.
+     */
+    public void setDebugLvl(String debugLvl) {
+        
+        this.debugLvl = debugLvl;
+        
+    }
+    
+    /**
+     * Return the directory.
+     */
+    public String getDirectory() {
+        
+        return this.directory;
+        
+    }
+    
+    /**
+     * Set the directory.
+     */
+    public void setDirectory(String directory) {
+        
+        this.directory = directory;
+        
+    }
+    
+    /**
+     * Return the pattern.
+     */
+    public String getPattern() {
+        
+        return this.pattern;
+        
+    }
+    
+    /**
+     * Set the pattern.
+     */
+    public void setPattern(String pattern) {
+        
+        this.pattern = pattern;
+        
+    }
+    
+    /**
+     * Return the prefix.
+     */
+    public String getPrefix() {
+        
+        return this.prefix;
+        
+    }
+    
+    /**
+     * Set the prefix.
+     */
+    public void setPrefix(String prefix) {
+        
+        this.prefix = prefix;
+        
+    }
+    
+    /**
+     * Return the suffix.
+     */
+    public String getSuffix() {
+        
+        return this.suffix;
+        
+    }
+    
+    /**
+     * Set the suffix.
+     */
+    public void setSuffix(String suffix) {
+        
+        this.suffix = suffix;
+        
+    }
+            
+    /**
+     * Return the resolve hosts.
+     */
+    public String getResolveHosts() {
+        
+        return this.resolveHosts;
+        
+    }
+    
+    /**
+     * Set the resolveHosts.
+     */
+    public void setResolveHosts(String resolveHosts) {
+        
+        this.resolveHosts = resolveHosts;
+        
+    }  
+    
+    /**
+     * Return the rotatable.
+     */
+    public String getRotatable() {
+        
+        return this.rotatable;
+        
+    }
+    
+    /**
+     * Set the rotatable.
+     */
+    public void setRotatable(String rotatable) {
+        
+        this.rotatable = rotatable;
+        
+    }
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+    
+        super.reset(mapping, request);
+        this.debugLvl = "0";
+        
+        this.directory = null;
+        this.prefix = null;
+        this.suffix = null;
+        this.pattern = null;        
+        this.resolveHosts = "false";
+        this.rotatable = "true";
+        
+    }
+    
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("AccessLogValveForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append("',valveType=");
+        sb.append(getValveType());
+        sb.append(",debugLvl=");
+        sb.append(debugLvl);
+        sb.append(",directory=");
+        sb.append(directory);
+        sb.append("',prefix='");
+        sb.append(prefix);
+        sb.append("',pattern=");
+        sb.append(pattern);
+        sb.append(",resolveHosts=");
+        sb.append(resolveHosts);
+        sb.append(",rotatable=");
+        sb.append(rotatable);
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+    
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        
+        // front end validation when save is clicked.        
+         //if (submit != null) {
+            
+             // if not specified, default is access_log.
+             // to specify no prefix, specify a 0 length string...
+            if ((prefix == null) || (prefix.length() == 0)){
+                prefix = "access_log.";
+            }
+            
+            // default is a 0 length string
+            if ((suffix == null) || (suffix.length() < 1)) {
+                suffix = "";
+            }
+                                    
+            // If no directory attribute is specified, the default
+            // value is "logs".
+            if ((directory == null) || (directory.length() < 1)) {
+                directory = "logs";
+            }
+
+            if ((pattern == null) || (pattern.length() < 1)) {
+                errors.add("pattern",
+                new ActionError("error.pattern.required"));
+            }         
+        //}
+                 
+        return errors;
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/AddValveAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/AddValveAction.java
new file mode 100644
index 0000000..8e528e9
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/AddValveAction.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+
+/**
+ * The <code>Action</code> that sets up <em>Add Valve</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class AddValveAction extends Action {
+        
+    // the list for types of valves
+    private ArrayList types = null;
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Fill in the form values for display and editing
+        
+        String valveTypes[] = new String[5];
+        valveTypes[0] = "AccessLogValve";
+        valveTypes[1] = "RemoteAddrValve";
+        valveTypes[2] = "RemoteHostValve";
+        valveTypes[3] = "RequestDumperValve";       
+        valveTypes[4] = "SingleSignOn";
+                     
+        String parent = request.getParameter("parent");
+        String type = request.getParameter("type");        
+        if (type == null) 
+            type = "AccessLogValve";    // default type is AccessLog
+        
+        types = new ArrayList();    
+        // the first element in the select list should be the type selected
+        types.add(new LabelValueBean(type,
+                "AddValve.do?parent=" + 
+                URLEncoder.encode(parent,TomcatTreeBuilder.URL_ENCODING) 
+                + "&type=" + type));        
+        for (int i=0; i< valveTypes.length; i++) {
+            if (!type.equalsIgnoreCase(valveTypes[i])) {
+                types.add(new LabelValueBean(valveTypes[i],
+                "AddValve.do?parent=" + 
+                URLEncoder.encode(parent,TomcatTreeBuilder.URL_ENCODING) 
+                + "&type=" + valveTypes[i]));        
+            }
+        }
+       
+        if ("AccessLogValve".equalsIgnoreCase(type)) {
+            createAccessLogger(session, parent);
+        } else if ("RemoteAddrValve".equalsIgnoreCase(type)) {
+            createRemoteAddrValve(session, parent);
+        } else if ("RemoteHostValve".equalsIgnoreCase(type)) {
+            createRemoteHostValve(session, parent);
+        } else if ("RequestDumperValve".equalsIgnoreCase(type)) {
+            createRequestDumperValve(session, parent);
+        } else {
+            //SingleSignOn
+            createSingleSignOnValve(session, parent);
+        }
+        // Forward to the valve display page
+        return (mapping.findForward(type));
+        
+    }
+
+    private void createAccessLogger(HttpSession session, String parent) {
+
+        AccessLogValveForm valveFm = new AccessLogValveForm();
+        session.setAttribute("accessLogValveForm", valveFm);
+        valveFm.setAdminAction("Create");
+        valveFm.setObjectName("");
+        valveFm.setParentObjectName(parent);
+        String valveType = "AccessLogValve";
+        valveFm.setNodeLabel("Valve (" + valveType + ")");
+        valveFm.setValveType(valveType);
+        valveFm.setPattern("");
+        valveFm.setDirectory("logs");
+        valveFm.setPrefix("access_log.");
+        valveFm.setSuffix("");
+        valveFm.setResolveHosts("false");
+        valveFm.setRotatable("true");
+        valveFm.setBooleanVals(Lists.getBooleanValues());
+        valveFm.setValveTypeVals(types);        
+    }
+
+    private void createRemoteAddrValve(HttpSession session, String parent) {
+
+        RemoteAddrValveForm valveFm = new RemoteAddrValveForm();
+        session.setAttribute("remoteAddrValveForm", valveFm);
+        valveFm.setAdminAction("Create");
+        valveFm.setObjectName("");
+        valveFm.setParentObjectName(parent);
+        String valveType = "RemoteAddrValve";
+        valveFm.setNodeLabel("Valve (" + valveType + ")");
+        valveFm.setValveType(valveType);
+        valveFm.setAllow("");
+        valveFm.setDeny("");
+        valveFm.setValveTypeVals(types);        
+    }
+
+    private void createRemoteHostValve(HttpSession session, String parent) {
+
+        RemoteHostValveForm valveFm = new RemoteHostValveForm();
+        session.setAttribute("remoteHostValveForm", valveFm);
+        valveFm.setAdminAction("Create");
+        valveFm.setObjectName("");
+        valveFm.setParentObjectName(parent);
+        String valveType = "RemoteHostValve";
+        valveFm.setNodeLabel("Valve (" + valveType + ")");
+        valveFm.setValveType(valveType);
+        valveFm.setAllow("");
+        valveFm.setDeny("");
+        valveFm.setValveTypeVals(types);        
+    }
+
+    private void createRequestDumperValve(HttpSession session, String parent) {
+
+        RequestDumperValveForm valveFm = new RequestDumperValveForm();
+        session.setAttribute("requestDumperValveForm", valveFm);
+        valveFm.setAdminAction("Create");
+        valveFm.setObjectName("");
+        valveFm.setParentObjectName(parent);
+        String valveType = "RequestDumperValve";
+        valveFm.setNodeLabel("Valve (" + valveType + ")");
+        valveFm.setValveType(valveType);
+        valveFm.setValveTypeVals(types);        
+    }
+
+    private void createSingleSignOnValve(HttpSession session, String parent) {
+
+        SingleSignOnValveForm valveFm = new SingleSignOnValveForm();
+        session.setAttribute("singleSignOnValveForm", valveFm);
+        valveFm.setAdminAction("Create");
+        valveFm.setObjectName("");
+        valveFm.setParentObjectName(parent);
+        String valveType = "SingleSignOn";
+        valveFm.setNodeLabel("Valve (" + valveType + ")");
+        valveFm.setValveType(valveType);
+        valveFm.setValveTypeVals(types);        
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/DeleteValveAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/DeleteValveAction.java
new file mode 100644
index 0000000..af1712b
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/DeleteValveAction.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.valve;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.TreeSet;
+import java.util.Set;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.Lists;
+
+/**
+ * The <code>Action</code> that sets up <em>Delete Valves</em> transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteValveAction extends Action {
+    
+    
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+    ActionForm form,
+    HttpServletRequest request,
+    HttpServletResponse response)
+    throws IOException, ServletException {
+        
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        String pattern = null;
+        // Set up a form bean containing the currently selected
+        // objects to be deleted
+        ValvesForm valvesForm = new ValvesForm();
+        String select = request.getParameter("select");
+        if (select != null) {
+            String valves[] = new String[1];
+            valves[0] = select;
+            valvesForm.setValves(valves);
+            pattern = select;
+        }
+        request.setAttribute("valvesForm", valvesForm);
+        
+        // Accumulate a list of all available valves
+        ArrayList list = new ArrayList();
+        String parent = request.getParameter("parent");
+        valvesForm.setParentObjectName(parent);
+        
+        try {
+            Iterator items = (Lists.getValves(mBServer, parent)).iterator();
+            while (items.hasNext()) {
+                list.add(items.next().toString());
+            }
+        } catch (Exception e) {
+            getServlet().log
+            (resources.getMessage(locale, "users.error.select"));
+            response.sendError
+            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+            resources.getMessage(locale, "users.error.select"));
+            return (null);
+        }
+        
+        Collections.sort(list);
+        request.setAttribute("valvesList", list);
+        
+        // Forward to the list display page
+        return (mapping.findForward("Valves"));
+        
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/DeleteValvesAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/DeleteValvesAction.java
new file mode 100644
index 0000000..5ae33e8
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/DeleteValvesAction.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.webapp.admin.valve;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ObjectInstance;
+import javax.management.modelmbean.ModelMBean;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+
+/**
+ * The <code>Action</code> that completes <em>Delete Valves</em>
+ * transactions.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class DeleteValvesAction extends Action {
+
+
+    /**
+     * Signature for the <code>removeValve</code> operation.
+     */
+    private String removeValveTypes[] =
+    { "java.lang.String",      // Object name
+    };
+
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        
+        // Look up the components we will be using as needed
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Delete the specified Valves
+        String valves[]  = ((ValvesForm) form).getValves();
+        String values[] = new String[1];
+        String operation = "removeValve";
+        try {
+
+            // Look up our tree control data structure
+            TreeControl control = (TreeControl)
+                session.getAttribute("treeControlTest");
+
+            // Remove the specified valves
+            for (int i = 0; i < valves.length; i++) {
+                values[0] = valves[i];
+                String domain = (new ObjectName(valves[i])).getDomain();
+                ObjectName fname = TomcatTreeBuilder.getMBeanFactory();
+                mBServer.invoke(fname, operation,
+                                values, removeValveTypes);
+                if (control != null) {
+                    control.selectNode(null);
+                    TreeControlNode node = control.findNode(valves[i]);
+                    if (node != null) {
+                        node.remove();
+                    } else {
+                        getServlet().log("Missing TreeControlNode for " +
+                                         valves[i]);
+                    }
+                } else {
+                    getServlet().log("Missing TreeControl attribute");
+                }
+            }
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.invoke",
+                                      operation), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.invoke",
+                                      operation));
+            return (null);
+
+        }
+
+        // Report successful completion of this transaction
+        return (mapping.findForward("Save Successful"));
+
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/EditValveAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/EditValveAction.java
new file mode 100644
index 0000000..3d5ffeb
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/EditValveAction.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+import org.apache.webapp.admin.Lists;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+
+/**
+ * A generic <code>Action</code> that sets up <em>Edit 
+ * Valve </em> transactions, based on the type of Valve.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class EditValveAction extends Action {
+    
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Set up the object names of the MBeans we are manipulating
+        ObjectName vname = null;
+        StringBuffer sb = null;
+        try {
+            vname = new ObjectName(request.getParameter("select"));
+        } catch (Exception e) {
+            String message =
+                resources.getMessage(locale, "error.valveName.bad",
+                                     request.getParameter("select"));
+            getServlet().log(message);
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+            return (null);
+        }
+        
+       String parent = request.getParameter("parent");
+       String valveType = null;
+       String attribute = null;
+       
+       // Find what type of Valve this is
+       try {    
+            attribute = "className";
+            String className = (String) 
+                mBServer.getAttribute(vname, attribute);
+            int period = className.lastIndexOf(".");
+            if (period >= 0)
+                valveType = className.substring(period + 1);
+        } catch (Throwable t) {
+          getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+            return (null); 
+        }
+
+        // Forward to the appropriate valve display page        
+        if ("AccessLogValve".equalsIgnoreCase(valveType)) {
+               setUpAccessLogValve(vname, request, response);
+        } else if ("RemoteAddrValve".equalsIgnoreCase(valveType)) {
+               setUpRemoteAddrValve(vname, request, response);
+        } else if ("RemoteHostValve".equalsIgnoreCase(valveType)) {
+                setUpRemoteHostValve(vname, request, response);
+        } else if ("RequestDumperValve".equalsIgnoreCase(valveType)) {
+               setUpRequestDumperValve(vname, request, response);
+        } else if ("SingleSignOn".equalsIgnoreCase(valveType)) {
+               setUpSingleSignOnValve(vname, request, response);
+        }
+       
+        
+        return (mapping.findForward(valveType));
+                
+    }
+
+    private void setUpAccessLogValve(ObjectName vname, HttpServletRequest request,
+                                        HttpServletResponse response) 
+    throws IOException {
+        // Fill in the form values for display and editing
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        String parent = request.getParameter("parent");
+        AccessLogValveForm valveFm = new AccessLogValveForm();
+        session.setAttribute("accessLogValveForm", valveFm);
+        valveFm.setAdminAction("Edit");
+        valveFm.setObjectName(vname.toString()); 
+        valveFm.setParentObjectName(parent);
+        String valveType = "AccessLogValve";
+        StringBuffer sb = new StringBuffer("");
+        String host = vname.getKeyProperty("host");
+        String context = vname.getKeyProperty("path");        
+        if (host!=null) {
+            sb.append("Host (" + host + ") > ");
+        }
+        if (context!=null) {
+            sb.append("Context (" + context + ") > ");
+        }
+        sb.append("Valve");
+        valveFm.setNodeLabel(sb.toString());
+        valveFm.setValveType(valveType);
+        valveFm.setBooleanVals(Lists.getBooleanValues());
+        String attribute = null;
+        try {
+            
+            // Copy scalar properties
+            attribute = "directory";
+            valveFm.setDirectory
+                ((String) mBServer.getAttribute(vname, attribute));
+            attribute = "pattern";
+            valveFm.setPattern
+                ((String) mBServer.getAttribute(vname, attribute));
+            attribute = "prefix";
+            valveFm.setPrefix
+                ((String) mBServer.getAttribute(vname, attribute));
+            attribute = "suffix";
+            valveFm.setSuffix
+                ((String) mBServer.getAttribute(vname, attribute));
+            attribute = "resolveHosts";
+            valveFm.setResolveHosts
+                (((Boolean) mBServer.getAttribute(vname, attribute)).toString());
+            attribute = "rotatable";
+            valveFm.setRotatable
+                (((Boolean) mBServer.getAttribute(vname, attribute)).toString());
+
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+        }     
+    }
+
+    private void setUpRequestDumperValve(ObjectName vname, HttpServletRequest request,
+                                        HttpServletResponse response) 
+    throws IOException {
+        // Fill in the form values for display and editing
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        String parent = request.getParameter("parent");
+        RequestDumperValveForm valveFm = new RequestDumperValveForm();
+        session.setAttribute("requestDumperValveForm", valveFm);
+        valveFm.setAdminAction("Edit");
+        valveFm.setObjectName(vname.toString()); 
+        valveFm.setParentObjectName(parent);
+        String valveType = "RequestDumperValve";
+        StringBuffer sb = new StringBuffer("Valve (");
+        sb.append(valveType);
+        sb.append(")");
+        valveFm.setNodeLabel(sb.toString());
+        valveFm.setValveType(valveType);
+        String attribute = null;
+    }
+
+    private void setUpSingleSignOnValve(ObjectName vname, HttpServletRequest request,
+                                        HttpServletResponse response) 
+    throws IOException {
+        // Fill in the form values for display and editing
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        String parent = request.getParameter("parent");
+        SingleSignOnValveForm valveFm = new SingleSignOnValveForm();
+        session.setAttribute("singleSignOnValveForm", valveFm);
+        valveFm.setAdminAction("Edit");
+        valveFm.setObjectName(vname.toString()); 
+        valveFm.setParentObjectName(parent);
+        String valveType = "SingleSignOn";
+        StringBuffer sb = new StringBuffer("Valve (");
+        sb.append(valveType);
+        sb.append(")");
+        valveFm.setNodeLabel(sb.toString());
+        valveFm.setValveType(valveType);
+        String attribute = null;
+    }
+
+
+    private void setUpRemoteAddrValve(ObjectName vname, HttpServletRequest request,
+                                        HttpServletResponse response) 
+    throws IOException {
+        // Fill in the form values for display and editing
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        String parent = request.getParameter("parent");
+        RemoteAddrValveForm valveFm = new RemoteAddrValveForm();
+        session.setAttribute("remoteAddrValveForm", valveFm);
+        valveFm.setAdminAction("Edit");
+        valveFm.setObjectName(vname.toString()); 
+        valveFm.setParentObjectName(parent);
+        String valveType = "RemoteAddrValve";
+        StringBuffer sb = new StringBuffer("Valve (");
+        sb.append(valveType);
+        sb.append(")");
+        valveFm.setNodeLabel(sb.toString());
+        valveFm.setValveType(valveType);
+        String attribute = null;
+        try {
+            
+            // Copy scalar properties
+            attribute = "allow";
+            valveFm.setAllow
+                ((String) mBServer.getAttribute(vname, attribute));
+            attribute = "deny";
+            valveFm.setDeny
+                ((String) mBServer.getAttribute(vname, attribute));
+                        
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+        }     
+    }
+
+    private void setUpRemoteHostValve(ObjectName vname, HttpServletRequest request,
+                                        HttpServletResponse response) 
+    throws IOException {
+        // Fill in the form values for display and editing
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        String parent = request.getParameter("parent");
+        RemoteHostValveForm valveFm = new RemoteHostValveForm();
+        session.setAttribute("remoteHostValveForm", valveFm);
+        valveFm.setAdminAction("Edit");
+        valveFm.setObjectName(vname.toString()); 
+        valveFm.setParentObjectName(parent);
+        String valveType = "RemoteHostValve";
+        StringBuffer sb = new StringBuffer("Valve (");
+        sb.append(valveType);
+        sb.append(")");
+        valveFm.setNodeLabel(sb.toString());
+        valveFm.setValveType(valveType);
+        String attribute = null;
+        try {
+            
+            // Copy scalar properties
+            attribute = "allow";
+            valveFm.setAllow
+                ((String) mBServer.getAttribute(vname, attribute));
+            attribute = "deny";
+            valveFm.setDeny
+                ((String) mBServer.getAttribute(vname, attribute));
+                        
+        } catch (Throwable t) {
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute), t);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.get",
+                                      attribute));
+        }     
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/RemoteAddrValveForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/RemoteAddrValveForm.java
new file mode 100644
index 0000000..6cf91c9
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/RemoteAddrValveForm.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.lang.IllegalArgumentException;
+import java.net.InetAddress;
+import java.util.List;
+import java.util.regex.Pattern;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the remote addr valve page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class RemoteAddrValveForm extends ValveForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * The text for the allow IP addresses.
+     * A comma-separated list of regular expression patterns
+     * that the remote client's IP address is compared to. 
+     */
+    private String allow = "";
+
+    /**
+     * The text for the deny IP addresses.
+     */
+    private String deny = "";
+    
+    /**
+     * The set of <code>allow</code> regular expressions we will evaluate.
+     */
+    private Pattern allows[] = new Pattern[0];
+
+    /**
+     * The set of <code>deny</code> regular expressions we will evaluate.
+     */
+    private Pattern denies[] = new Pattern[0];
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the allow hosts IP adddresses.
+     */
+    public String getAllow() {
+        
+        return this.allow;
+        
+    }
+    
+    /**
+     * Set the allow hosts.
+     */
+    public void setAllow(String allow) {
+        
+        this.allow = allow;
+        
+    }
+    
+    /**
+     * Return the deny hosts IP adddresses.
+     */
+    public String getDeny() {
+        
+        return this.deny;
+        
+    }
+    
+    /**
+     * Set the deny hosts IP addresses.
+     */
+    public void setDeny(String deny) {
+        
+        this.deny = deny;
+        
+    }    
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+                
+        super.reset(mapping, request);
+        this.allow = null;
+        this.deny = null;
+        this.allows = null;
+        this.denies = null;
+        
+    }
+    
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("RemoteAddrValveForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append("',valveType=");
+        sb.append(getValveType());
+        sb.append(",allow=");
+        sb.append(getAllow());
+        sb.append(",deny=");
+        sb.append(getDeny());        
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+    
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        
+        // front end validation when save is clicked.        
+        //if (submit != null) {
+            // validate allow/deny patterns
+            if ((allow == null) || (allow.length() < 1)) {
+                if ((deny == null) || (deny.length() < 1)) {
+                    errors.add("allow",
+                    new ActionError("error.allow.deny.required"));
+                }
+            }                
+        //}
+        
+        try {
+            allows = ValveUtil.precalculate(allow);            
+        } catch (IllegalArgumentException e) {
+            errors.add("allow", new ActionError("error.syntax"));
+            return errors;
+        }
+         
+        try {   
+            denies = ValveUtil.precalculate(deny);
+        } catch (IllegalArgumentException e) {
+            errors.add("allow", new ActionError("error.syntax"));
+            return errors;
+        }
+        
+        String ip = request.getRemoteAddr();
+        
+        if (ip == null) {
+            return errors;
+        }
+        
+        for (int i = 0; i < denies.length; i++) {
+            if (denies[i].matcher(ip).matches()) {
+                if (allows.length < 1) {
+                    errors.add("deny",
+                        new ActionError("error.denyIP"));
+                }
+                for (int j = 0; j < allows.length; j++) {
+                    if (!allows[j].matcher(ip).matches()) { 
+                        errors.add("deny",
+                        new ActionError("error.denyIP"));
+                    }
+                }
+            }    
+        }
+        
+        boolean allowMatch = true;
+        if (allows.length > 0) {
+            allowMatch = false;
+        }
+        for (int i = 0; i < allows.length; i++) {
+            if (allows[i].matcher(ip).matches()) {
+                allowMatch = true;       
+            }
+        }       
+        if (!allowMatch) {
+            errors.add("allow", new ActionError("error.allowIP"));
+        }
+        
+        return errors;
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/RemoteHostValveForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/RemoteHostValveForm.java
new file mode 100644
index 0000000..5151436
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/RemoteHostValveForm.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.lang.IllegalArgumentException;
+import java.net.InetAddress;
+import java.util.List;
+import java.util.regex.Pattern;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the remote host valve page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class RemoteHostValveForm extends ValveForm {
+    
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The text for the allow hosts IP addresses.
+     * A comma-separated list of regular expression patterns
+     * that the remote client's IP address is compared to. 
+     */
+    private String allow = "";
+
+    /**
+     * The text for the deny hosts IP addresses.
+     */
+    private String deny = "";
+
+    /**
+     * The set of <code>allow</code> regular expressions we will evaluate.
+     */
+    private Pattern allows[] = new Pattern[0];
+
+    /**
+     * The set of <code>deny</code> regular expressions we will evaluate.
+     */
+    private Pattern denies[] = new Pattern[0];
+    
+    
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the allow hosts IP adddresses.
+     */
+    public String getAllow() {
+        
+        return this.allow;
+        
+    }
+    
+    /**
+     * Set the allow hosts.
+     */
+    public void setAllow(String allow) {
+        
+        this.allow = allow;
+        
+    }
+    
+    /**
+     * Return the deny hosts IP adddresses.
+     */
+    public String getDeny() {
+        
+        return this.deny;
+        
+    }
+    
+    /**
+     * Set the deny hosts IP addresses.
+     */
+    public void setDeny(String deny) {
+        
+        this.deny = deny;
+        
+    }    
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        super.reset(mapping, request);
+        this.allow = null;
+        this.deny = null;
+        this.allows = null;
+        this.denies = null;
+        
+    }
+    
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("RemoteHostValveForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append("',valveType=");
+        sb.append(getValveType());
+        sb.append(",allow=");
+        sb.append(allow);
+        sb.append(",deny=");
+        sb.append(deny);        
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+    
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        
+        // front end validation when save is clicked.        
+        //if (submit != null) {
+             // TBD
+            // validate allow/deny IPs
+            if ((allow == null) || (allow.length() < 1)) {
+                if ((deny == null) || (deny.length() < 1)) {
+                    errors.add("allow",
+                    new ActionError("error.allow.deny.required"));
+                }
+            }              
+        //}
+        
+        try {
+            allows = ValveUtil.precalculate(allow);            
+        } catch (IllegalArgumentException e) {
+            errors.add("allow", new ActionError("error.syntax"));
+            return errors;
+        }
+         
+        try {   
+            denies = ValveUtil.precalculate(deny);
+        } catch (IllegalArgumentException e) {
+            errors.add("allow", new ActionError("error.syntax"));
+            return errors;
+        }
+                 
+        String host = request.getRemoteHost();
+        // check for IP address also in case DNS is not configured 
+        // to give a host name for the client machine
+        String ip = request.getRemoteAddr();
+    
+        if (host == null) {
+            return errors;
+        }
+        
+        for (int i = 0; i < denies.length; i++) {
+            if (denies[i].matcher(host).matches()) {
+                if (allows.length < 1) {
+                    errors.add("deny",
+                        new ActionError("error.denyHost"));
+                }    
+                for (int j = 0; j < allows.length; j++) {
+                    if (!allows[j].matcher(host).matches()) { 
+                        errors.add("deny",
+                        new ActionError("error.denyHost"));
+                    }
+                }
+            } else if (denies[i].matcher(ip).matches()) {
+                if (allows.length < 1) {
+                    errors.add("deny",
+                        new ActionError("error.denyHost"));
+                }               
+                for (int j = 0; j < allows.length; j++) {
+                    if (!allows[j].matcher(ip).matches()) { 
+                        errors.add("deny",
+                        new ActionError("error.denyHost"));
+                    }
+                }
+            }
+        }
+        
+        boolean allowMatch = true;
+        
+        if ((allows != null) && (allows.length > 0)) {
+            allowMatch = false;
+        }
+        
+        for (int i = 0; i < allows.length; i++) {
+            if (allows[i].matcher(host).matches()) {
+                allowMatch = true;       
+            }
+        }
+        
+        if (!allowMatch) {
+            errors.add("allow", new ActionError("error.allowHost"));
+        }        
+        
+        return errors;
+    }
+    
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/RequestDumperValveForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/RequestDumperValveForm.java
new file mode 100644
index 0000000..778b2ac
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/RequestDumperValveForm.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.util.List;
+
+/**
+ * Form bean for the Request Dumper valve page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class RequestDumperValveForm extends ValveForm {
+        
+    // ----------------------------------------------------- Instance Variables
+
+    
+    // ------------------------------------------------------------- Properties
+    
+        
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+    
+        super.reset(mapping, request);
+    }
+    
+    
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("RequestDumperValveForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append("',valveType=");
+        sb.append(getValveType());
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+    
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        
+        // front end validation when save is clicked.        
+         if (submit != null) {
+         // no validation needed
+        }
+                 
+        return errors;
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveAccessLogValveAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveAccessLogValveAction.java
new file mode 100644
index 0000000..8daa7fb
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveAccessLogValveAction.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+/**
+ * The <code>Action</code> that completes <em>Add Valve</em> and
+ * <em>Edit Valve</em> transactions for AccessLog valve.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveAccessLogValveAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        AccessLogValveForm vform = (AccessLogValveForm) form;
+        String adminAction = vform.getAdminAction();
+        String vObjectName = vform.getObjectName();
+        String parent = vform.getParentObjectName();
+        String valveType = vform.getValveType();
+        
+        // Perform a "Create Valve" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+        
+            vObjectName = ValveUtil.createValve(parent, valveType, 
+                                response, request, mapping, 
+                                (ApplicationServlet) getServlet());
+           
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+        
+            ObjectName voname = new ObjectName(vObjectName);
+            
+            attribute = "directory";
+            mBServer.setAttribute(voname,
+                         new Attribute("directory", vform.getDirectory()));
+            attribute = "pattern";
+            mBServer.setAttribute(voname,
+                        new Attribute("pattern", vform.getPattern()));
+            attribute = "prefix";
+            mBServer.setAttribute(voname,
+                        new Attribute("prefix", vform.getPrefix()));
+            attribute = "suffix";
+            mBServer.setAttribute(voname,
+                        new Attribute("suffix", vform.getSuffix()));
+            attribute = "resolveHosts";
+            mBServer.setAttribute(voname,
+                        new Attribute("resolveHosts", new Boolean(vform.getResolveHosts())));
+            attribute = "rotatable";
+            mBServer.setAttribute(voname,
+                        new Attribute("rotatable", new Boolean(vform.getRotatable())));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+    
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveRemoteAddrValveAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveRemoteAddrValveAction.java
new file mode 100644
index 0000000..9dfb7e3
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveRemoteAddrValveAction.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+/**
+ * The <code>Action</code> that completes <em>Add Valve</em> and
+ * <em>Edit Valve</em> transactions for Remote Addr valve.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveRemoteAddrValveAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        RemoteAddrValveForm vform = (RemoteAddrValveForm) form;
+        String adminAction = vform.getAdminAction();
+        String vObjectName = vform.getObjectName();
+        String parent = vform.getParentObjectName();
+        String valveType = vform.getValveType();
+               
+        // Perform a "Create Valve" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            vObjectName = ValveUtil.createValve(parent, valveType, 
+                                response, request, mapping, 
+                                (ApplicationServlet) getServlet());
+           
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+        
+            ObjectName voname = new ObjectName(vObjectName);
+            
+            attribute = "allow";
+            mBServer.setAttribute(voname,
+                                  new Attribute("allow", vform.getAllow()));
+            attribute = "deny";
+            mBServer.setAttribute(voname,
+                    new Attribute("deny", vform.getDeny()));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+    
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveRemoteHostValveAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveRemoteHostValveAction.java
new file mode 100644
index 0000000..f6254e4
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveRemoteHostValveAction.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+/**
+ * The <code>Action</code> that completes <em>Add Valve</em> and
+ * <em>Edit Valve</em> transactions for Remote Host valve.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveRemoteHostValveAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        RemoteHostValveForm vform = (RemoteHostValveForm) form;
+        String adminAction = vform.getAdminAction();
+        String vObjectName = vform.getObjectName();
+        String parent = vform.getParentObjectName();
+        String valveType = vform.getValveType();
+               
+        // Perform a "Create Valve" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            vObjectName = ValveUtil.createValve(parent, valveType, 
+                                response, request, mapping, 
+                                (ApplicationServlet) getServlet());
+           
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+        try {
+        
+            ObjectName voname = new ObjectName(vObjectName);
+            
+            attribute = "allow";
+            mBServer.setAttribute(voname,
+                                  new Attribute("allow", vform.getAllow()));
+            attribute = "deny";
+            mBServer.setAttribute(voname,
+                    new Attribute("deny", vform.getDeny()));
+
+        } catch (Exception e) {
+
+            getServlet().log
+                (resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute), e);
+            response.sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 resources.getMessage(locale, "users.error.attribute.set",
+                                      attribute));
+            return (null);
+        }
+    
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveRequestDumperValveAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveRequestDumperValveAction.java
new file mode 100644
index 0000000..4d571ef
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveRequestDumperValveAction.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+/**
+ * The <code>Action</code> that completes <em>Add Valve</em> and
+ * <em>Edit Valve</em> transactions for Request Dumper valve.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveRequestDumperValveAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        RequestDumperValveForm vform = (RequestDumperValveForm) form;
+        String adminAction = vform.getAdminAction();
+        String vObjectName = vform.getObjectName();
+        String parent = vform.getParentObjectName();
+        String valveType = vform.getValveType();
+            
+        // Perform a "Create Valve" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+        
+            vObjectName = ValveUtil.createValve(parent, valveType, 
+                                response, request, mapping, 
+                                (ApplicationServlet) getServlet());
+           
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+                
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveSingleSignOnValveAction.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveSingleSignOnValveAction.java
new file mode 100644
index 0000000..cd1b92b
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SaveSingleSignOnValveAction.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.util.Locale;
+import java.io.IOException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+
+/**
+ * The <code>Action</code> that completes <em>Add Valve</em> and
+ * <em>Edit Valve</em> transactions for Single Sign On valve.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SaveSingleSignOnValveAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The MBeanServer we will be interacting with.
+     */
+    private MBeanServer mBServer = null;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param actionForm The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public ActionForward execute(ActionMapping mapping,
+                                 ActionForm form,
+                                 HttpServletRequest request,
+                                 HttpServletResponse response)
+        throws IOException, ServletException {
+        
+        // Acquire the resources that we need
+        HttpSession session = request.getSession();
+        Locale locale = getLocale(request);
+        MessageResources resources = getResources(request);
+        
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = ((ApplicationServlet) getServlet()).getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        // Identify the requested action
+        SingleSignOnValveForm vform = (SingleSignOnValveForm) form;
+        String adminAction = vform.getAdminAction();
+        String vObjectName = vform.getObjectName();
+        String parent = vform.getParentObjectName();
+        String valveType = vform.getValveType();
+               
+        // Perform a "Create Valve" transaction (if requested)
+        if ("Create".equals(adminAction)) {
+
+            try {
+                // Ensure that only one single sign on valve exists
+                ObjectName pname = new ObjectName(parent); 
+                ObjectName oname = 
+                        new ObjectName(pname.getDomain() + 
+                                    ":type=Valve,name=SingleSignOn");               
+                
+                if (mBServer.isRegistered(oname)) {
+                    ActionErrors errors = new ActionErrors();
+                    errors.add("singleSignOnValve",
+                               new ActionError("error.singleSignOn.exists"));
+                    saveErrors(request, errors);
+                    return (new ActionForward(mapping.getInput()));
+                } 
+            } catch (Exception e) {
+                getServlet().log
+                    (resources.getMessage(locale, "users.error.invoke",
+                                          adminAction), e);
+                response.sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                     resources.getMessage(locale, "users.error.invoke",
+                                          adminAction));
+                return (null);
+
+            }
+            
+            vObjectName = ValveUtil.createValve(parent, valveType, 
+                                response, request, mapping, 
+                                (ApplicationServlet) getServlet());
+                      
+        }
+
+        // Perform attribute updates as requested
+        String attribute = null;
+    
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return (mapping.findForward("Save Successful"));
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SingleSignOnValveForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SingleSignOnValveForm.java
new file mode 100644
index 0000000..af60566
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/SingleSignOnValveForm.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.net.InetAddress;
+import java.util.List;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the single sign on valve page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class SingleSignOnValveForm extends ValveForm {
+    
+    // ----------------------------------------------------- Instance Variables
+
+    // ------------------------------------------------------------- Properties
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+    
+        super.reset(mapping, request);
+        
+    }
+    
+    /**
+     * Render this object as a String.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SingleSignOnValveForm[adminAction=");
+        sb.append(getAdminAction());
+        sb.append("',valveType=");
+        sb.append(getValveType());
+        sb.append("',objectName='");
+        sb.append(getObjectName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+    
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    
+    public ActionErrors validate(ActionMapping mapping,
+    HttpServletRequest request) {
+        
+        ActionErrors errors = new ActionErrors();
+        
+        String submit = request.getParameter("submit");
+        
+        // front end validation when save is clicked.        
+         if (submit != null) {
+             // no validation needed
+         }
+                 
+        return errors;
+    }
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/ValveForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/ValveForm.java
new file mode 100644
index 0000000..5815937
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/ValveForm.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import java.net.InetAddress;
+import java.util.List;
+
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.LabelValueBean;
+
+/**
+ * Form bean for the generic valve page.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class ValveForm extends ActionForm {
+    
+    // ----------------------------------------------------- Instance Variables
+    
+   /**
+     * The administrative action represented by this form.
+     */
+    private String adminAction = "Edit";
+
+    /**
+     * The object name of the valve this bean refers to.
+     */
+    private String objectName = null;
+
+    /**
+     * The text for the valve name, used to retrieve
+     * the corresponding valve mBean.
+     */
+    private String valveName = null;
+    
+    /**
+     * The text for the valve type.
+     */
+    private String valveType = null;
+        
+    /**
+     * The text for the node label.
+     */
+    private String nodeLabel = null;
+    
+    /**
+     * The object name of the parent of this valve.
+     */
+    private String parentObjectName = null;
+
+    /**
+     * Set of valid values for valves.
+     */
+    private List valveTypeVals = null;
+    
+    // ------------------------------------------------------------- Properties
+
+       /**
+     * Return the administrative action represented by this form.
+     */
+    public String getAdminAction() {
+
+        return this.adminAction;
+
+    }
+
+    /**
+     * Set the administrative action represented by this form.
+     */
+    public void setAdminAction(String adminAction) {
+
+        this.adminAction = adminAction;
+
+    }
+
+    /**
+     * Return the Object Name.
+     */
+    public String getObjectName() {
+        
+        return this.objectName;
+        
+    }
+    
+    /**
+     * Set the Object Name.
+     */
+    public void setObjectName(String objectName) {
+        
+        this.objectName = objectName;
+        
+    }
+    
+    /**
+     * Return the valve type.
+     */
+    public String getValveType() {
+        
+        return this.valveType;
+        
+    }
+    
+    /**
+     * Set the valve type.
+     */
+    public void setValveType(String valveType) {
+        
+        this.valveType = valveType;
+        
+    }
+    
+    /**
+     * Return the label of the node that was clicked.
+     */
+    public String getNodeLabel() {
+        
+        return this.nodeLabel;
+        
+    }
+    
+    /**
+     * Set the node label.
+     */
+    public void setNodeLabel(String nodeLabel) {
+        
+        this.nodeLabel = nodeLabel;
+        
+    }
+    
+    /**
+     * Return the parent object name of the valve this bean refers to.
+     */
+    public String getParentObjectName() {
+
+        return this.parentObjectName;
+
+    }
+
+
+    /**
+     * Set the parent object name of the valve this bean refers to.
+     */
+    public void setParentObjectName(String parentObjectName) {
+
+        this.parentObjectName = parentObjectName;
+
+    }
+    
+        
+   /**
+     * Return the valveTypeVals.
+     */
+    public List getValveTypeVals() {
+        
+        return this.valveTypeVals;
+        
+    }
+    
+    /**
+     * Set the valveTypeVals.
+     */
+    public void setValveTypeVals(List valveTypeVals) {
+        
+        this.valveTypeVals = valveTypeVals;
+        
+    }
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+        
+        objectName = null;
+        
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/ValveUtil.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/ValveUtil.java
new file mode 100644
index 0000000..c6add88
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/ValveUtil.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2001-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.IOException;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.QueryExp;
+import javax.management.Query;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.JMException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.struts.Globals;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+import org.apache.webapp.admin.ApplicationServlet;
+import org.apache.webapp.admin.TomcatTreeBuilder;
+import org.apache.webapp.admin.TreeControl;
+import org.apache.webapp.admin.TreeControlNode;
+
+/**
+ * A utility class that contains methods common across valves.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public final class ValveUtil {
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    /**
+     * Signature for the <code>createStandardValve</code> operation.
+     */
+    private static String createStandardValveTypes[] =
+    { "java.lang.String",     // parent
+    };
+    
+    
+    // --------------------------------------------------------- Public Methods
+    
+    public static String createValve(String parent, String valveType,
+    HttpServletResponse response, HttpServletRequest request,
+    ActionMapping mapping, ApplicationServlet servlet)
+    throws IOException, ServletException {
+        
+        MessageResources resources = (MessageResources)
+            servlet.getServletContext().getAttribute(Globals.MESSAGES_KEY);
+        HttpSession session = request.getSession();
+        
+        MBeanServer mBServer = null;
+        Locale locale = (Locale) session.getAttribute(Globals.LOCALE_KEY);
+        // Acquire a reference to the MBeanServer containing our MBeans
+        try {
+            mBServer = servlet.getServer();
+        } catch (Throwable t) {
+            throw new ServletException
+            ("Cannot acquire MBeanServer reference", t);
+        }
+        
+        String operation = null;
+        String values[] = null;
+        String vObjectName = null;
+        
+        try {
+            
+            String objectName = ValveUtil.getObjectName(parent,
+            TomcatTreeBuilder.VALVE_TYPE);
+                        
+            String parentNodeName = parent;
+            ObjectName pname = new ObjectName(parent);
+            StringBuffer sb = new StringBuffer(pname.getDomain());
+            
+            // For service, create the corresponding Engine mBean
+            // Parent in this case needs to be the container mBean for the service
+            try {
+                if ("Service".equalsIgnoreCase(pname.getKeyProperty("type"))) {
+                    sb.append(":type=Engine");
+                    parent = sb.toString();
+                }
+            } catch (Exception e) {
+                String message = resources.getMessage("error.engineName.bad",
+                sb.toString());
+                servlet.log(message);
+                response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+                return (null);
+            }    
+            // Ensure that the requested valve name is unique
+            
+            // TBD -- do we need this check?
+            /*
+            ObjectName oname =
+            new ObjectName(objectName);
+            if (mBServer.isRegistered(oname)) {
+                ActionErrors errors = new ActionErrors();
+                errors.add("valveName",
+                    new ActionError("error.valveName.exists"));
+                String message =
+                    resources.getMessage(locale, "error.valveName.exists", sb.toString());
+                response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);                
+                return (new ActionForward(mapping.getInput()));
+            }
+            */
+            
+            String domain = pname.getDomain();
+            // Look up our MBeanFactory MBean
+            ObjectName fname = TomcatTreeBuilder.getMBeanFactory();
+            
+            // Create a new StandardValve object
+            values = new String[1];            
+            values[0] = parent;           
+            
+            operation = "create" + valveType;
+            if ("AccessLogValve".equalsIgnoreCase(valveType))
+                operation = "createAccessLoggerValve";
+                
+            vObjectName = (String)
+                        mBServer.invoke(fname, operation, values, createStandardValveTypes);
+            
+            // Add the new Valve to our tree control node
+            TreeControl control = (TreeControl)
+            session.getAttribute("treeControlTest");
+            if (control != null) {
+                TreeControlNode parentNode = control.findNode(parentNodeName);
+                if (parentNode != null) {
+                    String nodeLabel =
+                    "Valve for " + parentNode.getLabel();
+                    String encodedName =
+                    URLEncoder.encode(vObjectName,TomcatTreeBuilder.URL_ENCODING);
+                    TreeControlNode childNode =
+                    new TreeControlNode(vObjectName,
+                    "Valve.gif",
+                    nodeLabel,
+                    "EditValve.do?select=" + encodedName +
+                    "&nodeLabel=" + URLEncoder.encode(nodeLabel,TomcatTreeBuilder.URL_ENCODING) +
+                    "&parent=" + URLEncoder.encode(parentNodeName,TomcatTreeBuilder.URL_ENCODING),
+                    "content",
+                    true, domain);
+                    parentNode.addChild(childNode);
+                    // FIXME - force a redisplay
+                } else {
+                    servlet.log
+                    ("Cannot find parent node '" + parentNodeName + "'");
+                }
+            } else {
+                servlet.log
+                ("Cannot find TreeControlNode!");
+            }
+            
+        } catch (Exception e) {
+            
+            servlet.log
+            (resources.getMessage(locale, "users.error.invoke",
+            operation), e);
+            response.sendError
+            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+            resources.getMessage(locale, "users.error.invoke",
+            operation));
+            return (null);
+            
+        }
+        
+        // Forward to the success reporting page
+        session.removeAttribute(mapping.getAttribute());
+        return vObjectName;
+    }
+
+    
+    /**
+     * Return an array of regular expression objects initialized from the
+     * specified argument, which must be <code>null</code> or a comma-delimited
+     * list of regular expression patterns.
+     *
+     * @param list The comma-separated list of patterns
+     *
+     * @exception IllegalArgumentException if one of the patterns has
+     *  invalid syntax
+     */
+    public static Pattern[] precalculate(String list) 
+                                    throws IllegalArgumentException {
+
+        if (list == null)
+            return (new Pattern[0]);
+        list = list.trim();
+        if (list.length() < 1)
+            return (new Pattern[0]);
+        list += ",";
+
+        ArrayList reList = new ArrayList();
+        while (list.length() > 0) {
+            int comma = list.indexOf(',');
+            if (comma < 0)
+                break;
+            String pattern = list.substring(0, comma).trim();
+            try {
+                reList.add(Pattern.compile(pattern));
+            } catch (PatternSyntaxException e) {
+                throw new IllegalArgumentException
+                    ("Syntax error in request filter pattern");
+            }
+            list = list.substring(comma + 1);
+        }
+
+        Pattern reArray[] = new Pattern[reList.size()];
+        return ((Pattern[]) reList.toArray(reArray));
+
+    }    
+
+    public static String getObjectName(String parent, String MBeanType)
+    throws Exception{
+        
+        // Form the pattern that gets the logger for this particular
+        // service, host or context.
+        ObjectName poname = new ObjectName(parent);
+        String domain = poname.getDomain();
+        StringBuffer sb = new StringBuffer(domain+MBeanType);
+        String type = poname.getKeyProperty("type");
+        String j2eeType = poname.getKeyProperty("j2eeType");
+        String path = "";
+        String host = "";
+        String name = poname.getKeyProperty("name");
+        if ((name != null) && (name.length() > 0)) {
+            name = name.substring(2);
+            int i = name.indexOf("/");
+            host = name.substring(0,i);
+            path = name.substring(i); 
+        }
+        if ("WebModule".equalsIgnoreCase(j2eeType)) { // container is context            
+            sb.append(",path="+path);
+            sb.append(",host="+host);
+        }
+        if ("Host".equalsIgnoreCase(type)) {    // container is host
+            sb.append(",host=");
+            sb.append(poname.getKeyProperty("host"));
+        }
+        if ("Service".equalsIgnoreCase(type)) {  // container is service
+        }
+        return sb.toString();  
+    }
+
+}
diff --git a/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/ValvesForm.java b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/ValvesForm.java
new file mode 100644
index 0000000..50a237b
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/classes/org/apache/webapp/admin/valve/ValvesForm.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.webapp.admin.valve;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for deleting valves.
+ *
+ * @author Manveen Kaur
+ * @version $Revision$ $Date$
+ */
+
+public class ValvesForm extends ActionForm {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The object names of the valves to be deleted.
+     */
+    private String valves[] = new String[0];
+
+    public String[] getValves() {
+        return (this.valves);
+    }
+
+    public void setValves(String valves[]) {
+        this.valves = valves;
+    }
+
+     /* 
+      * The parent object name of the valve to be deleted. 
+      */
+   
+    private String parentObjectName = null;
+    
+    public String getParentObjectName() {
+        return (this.parentObjectName);
+    }
+    
+    public void setParentObjectName(String parentObjectName) {
+        this.parentObjectName = parentObjectName;
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.valves = new String[0];
+        this.parentObjectName = null;
+    }
+        
+
+}
diff --git a/container/webapps/admin/WEB-INF/controls.tld b/container/webapps/admin/WEB-INF/controls.tld
new file mode 100644
index 0000000..627dfaf
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/controls.tld
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE taglib
+  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
+         "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
+
+<taglib>
+
+  <tlib-version>1.0</tlib-version>
+  <jsp-version>1.2</jsp-version>
+  <short-name>controls</short-name>
+  <description>
+    JSP tag library containing custom GUI controls used in the
+    Tomcat Administrative Application.
+  </description>
+
+  <!-- ========== Instant Table Tag ===================================== -->
+
+  <tag>
+
+    <name>table</name>
+    <tag-class>org.apache.webapp.admin.TableTag</tag-class>
+    <body-content>JSP</body-content>
+    <description>
+      Render a "table" object, which is rendered as an HTML
+      "table" element. 
+      
+      NOTE:  The only valid nested content for this tag is
+      "row" tags from this library.  Anything else will cause
+      the rendered HTML to be invalid.
+
+      NOTE:  To be usable, this tag must be nested inside an
+      HTML &lt;form&gt; element.
+    </description>
+
+    <attribute>
+      <name>columns</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        (Integer) number of columns that the table contains.  If
+        not specified, only two columns will be visible.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>tableStyle</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The CSS style class to be applied to the entire rendered output
+        of the instant table.  If not specified, no overall
+        style class is applied.
+      </description>
+    </attribute>
+ 
+    <attribute>
+      <name>lineStyle</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The CSS style for the lines between rows.
+      </description>
+    </attribute>
+
+  </tag>
+
+    <tag>
+
+    <name>row</name>
+    <tag-class>org.apache.webapp.admin.RowTag</tag-class>
+    <body-content>JSP</body-content>
+    <description>
+      Define a single "instant table row" option for the surrounding "table"
+      tag.  It is not valid to use this tag *except* when nested inside an
+      "table" tag.
+
+      NOTE: This tag can nest only "label" and "data" tags.
+    </description>
+
+    <attribute>
+      <name>header</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        (Boolean) variable set to "true" or "yes" if this row is
+        the header row.
+        </description>
+    </attribute>
+    
+     <attribute>
+      <name>labelStyle</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The style for the label table data element.
+      </description>
+    </attribute>
+    
+     <attribute>
+      <name>dataStyle</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The style for the value of the table data element.
+      </description>
+    </attribute>    
+    
+     <attribute>
+      <name>styleId</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The styleId for the label table data element.
+      </description>
+    </attribute>
+        
+  </tag>
+
+ <tag>
+
+    <name>label</name>
+    <tag-class>org.apache.webapp.admin.LabelTag</tag-class>
+    <body-content>JSP</body-content>
+    <description>
+      Render a "label" object, which is rendered as a label in the row of
+      an HTML "table" element. 
+
+      NOTE:  To be usable, this tag must be nested inside a "row" tag.
+    </description>
+  </tag>
+
+ <tag>
+
+    <name>data</name>
+    <tag-class>org.apache.webapp.admin.DataTag</tag-class>
+    <body-content>JSP</body-content>
+    <description>
+      Render a "data" object, which is rendered as a label in the row of
+      an HTML "table" element. 
+
+      NOTE:  To be usable, this tag must be nested inside a "row" tag.
+    </description>
+  </tag>
+
+  <!-- ========== Instant Actions Tag ===================================== -->
+
+   <tag>
+
+    <name>actions</name>
+    <tag-class>org.apache.webapp.admin.ActionsTag</tag-class>
+    <body-content>JSP</body-content>
+    <description>
+      Render an "instant actions" object, which is rendered as an HTML
+      "select" element, where the selection of a particular element from
+      the list immediately causes a JavaScript function to be executed
+      (with the available elements specified by "action" tag instances
+      nested within the body of the "actions" tag).
+
+      NOTE:  The only valid nested content for this tag is
+      "action" tags from this library.  Anything else will cause
+      the rendered HTML to be invalid.
+
+      NOTE:  To be usable, this tag must be nested inside an
+      HTML &lt;form&gt; element.
+    </description>
+
+    <attribute>
+      <name>size</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        (Integer) number of rows that will be visible to the user.  If
+        not specified, only one row will be visible.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>style</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The CSS style class to be applied to the entire rendered output
+        of the instant actions control.  If not specified, no overall
+        style class is applied.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>label</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        HTML Label tag generation.
+      </description>
+    </attribute>
+    
+  </tag>
+
+  <tag>
+
+    <name>action</name>
+    <tag-class>org.apache.webapp.admin.ActionTag</tag-class>
+    <body-content>JSP</body-content>
+    <description>
+      Define a single "instant action" option for the surrounding "actions"
+      tag.  It is not valid to use this tag *except* when nested inside an
+      "actions" tag.
+
+      NOTE:  The body content of this tag (which should be suitably
+      localized, if required by your application) is used as the
+      user-visible label for this action.
+    </description>
+
+    <attribute>
+      <name>selected</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        (Boolean) variable set to "true" or "yes" if this action should
+        already be selected when the "instant actions" element is
+        initially displayed.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>disabled</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        (Boolean) variable set to "true" or "yes" if the selection
+        for this action should be disabled.        
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>url</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The URL to which the current frame or window will be transferred
+        if the "onchange" event handler of this "instant actions" element
+        is triggered, and this is the currently selected action.  If no
+        URL is specified, no action will be taken (useful for "(None)"
+        options and dividers).
+
+        If this URL starts with a slash, it will be assumed to be
+        context-relative, and will be prefixed with the context path
+        of this request.  Otherwise, it will be used unmodified.
+
+        NOTE:  This URL will be passed through URL rewriting so that it
+        will maintain session identity even in environments were cookies
+        are not being used.
+      </description>
+    </attribute>
+
+  </tag>
+
+
+  <!-- ========== Tree Control Tag ======================================== -->
+
+  <tag>
+
+    <name>tree</name>
+    <tag-class>org.apache.webapp.admin.TreeControlTag</tag-class>
+    <body-content>empty</body-content>
+    <description>
+      Render a "tree" control, based on the current state of a data object
+      of type org.apache.webapp.admin.TreeControl, which is identified
+      by the name specified in the "tree" attribute, in the JSP scope
+      specified by the "scope" attribute.
+    </description>
+
+    <attribute>
+      <name>action</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        Hyperlink to which expand/contract actions should be sent,
+        with a string "${node}" marking where the node name of the
+        affected node should be included (which will usually be as
+        the value of a request parameter).
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>images</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        Name of a directory containing the images for our icons,
+        relative to the page including this tag.  If not specified,
+        defaults to "images".
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>scope</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The JSP scope within which the "tree" attribute is to be found
+        (page, request, session, or application).  If not specified, the
+        "tree" attribute will be searched for in any scope.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>style</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The CSS style class to be applied to the entire rendered output
+        of the tree control.  If not specified, no overall style class
+        is applied.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>styleSelected</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The CSS style class to be applied to the text of any node that
+        is currently selected.  If not specified, no style class will be
+        applied to the text of the selected node.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>styleUnselected</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        The CSS style class to be applied to the text of any node that
+        is *not* currently selected.  If not specified, no style class will
+         be applied to the text of non-selected nodes.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>tree</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        Name of the attribute (in the scope specified by the "scope"
+        attribute, if any) under which an object of type
+        org.apache.webapp.admin.TreeControl is stored.  This object
+        represents the entire current state of the tree, including
+        a representation of the hierarchical representation of the
+        nodes, plus the current expanded/ or contracted state of
+        non-leaf nodes.
+      </description>
+    </attribute>
+
+  </tag>
+
+  <!-- ========== JMX Attribute Display Tag =============================== -->
+
+  <tag>
+
+    <name>attribute</name>
+    <tag-class>org.apache.webapp.admin.AttributeTag</tag-class>
+    <body-content>empty</body-content>
+    <description>
+      Look up an attribute on a JMX MBean, specified by an object name
+      specified by the "name" (and optional "property" and "scope")
+      attributes, and render it to the current JSP writer.  The object name
+      identified by these attributes can be either a java.lang.String version
+      of the name, or a javax.management.ObjectName instance.
+    </description>
+
+    <attribute>
+      <name>attribute</name>
+      <required>true</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        Name of the attribute of the JMX MBean whose value is to be
+        retrieved and written to the current JSP writer.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>name</name>
+      <required>true</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        Name of a bean, optionally in some scope identified by the "scope"
+        attribute.  If the "property" attribute is not specified, this bean
+        must by a String or an ObjectName.  Otherwise, this bean must have
+        a property getter for the property named by "property", which will
+        return the String or ObjectName.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>property</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        Name of a bean property, on the bean identified by the "name"
+        (and optional "scope") attributes, that is either a String or an
+        ObjectName of the JMX MBean whose attribute is to be retrieved.
+      </description>
+    </attribute>
+
+    <attribute>
+      <name>scope</name>
+      <required>false</required>
+      <rtexprvalue>true</rtexprvalue>
+      <description>
+        Name of the scope ("page", "request", "session", or "application")
+        in which the bean identified by the "name" attribute is to be found.
+        If not specified, all scopes will be searched in ascending order.
+      </description>
+    </attribute>
+
+  </tag>
+
+</taglib>
diff --git a/container/webapps/admin/WEB-INF/struts-config.xml b/container/webapps/admin/WEB-INF/struts-config.xml
new file mode 100644
index 0000000..ccd4555
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/struts-config.xml
@@ -0,0 +1,970 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<!DOCTYPE struts-config PUBLIC
+          "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
+          "http://struts.apache.org/dtds/struts-config_1_2.dtd">
+
+
+<struts-config>
+
+
+  <!-- ========== Data Source Configuration =============================== -->
+
+
+  <!-- ========== Form Bean Definitions =================================== -->
+
+  <form-beans>
+
+    <!-- Set Locale form bean -->
+    <form-bean      name="setLocaleForm"
+                    type="org.apache.webapp.admin.SetLocaleForm"/>
+
+    <!-- ============= Server Module ============= -->
+
+    <form-bean      name="serverForm"
+                    type="org.apache.webapp.admin.server.ServerForm"/>
+                    
+    <!-- ============= Service Module ============= -->
+
+    <form-bean      name="serviceForm"
+                    type="org.apache.webapp.admin.service.ServiceForm"/>
+                    
+    <form-bean      name="servicesForm"
+                    type="org.apache.webapp.admin.service.ServicesForm"/>
+
+    <!-- ============= Host Module ============= -->
+
+    <form-bean      name="hostForm"
+                    type="org.apache.webapp.admin.host.HostForm"/>
+                    
+    <form-bean      name="hostsForm"
+                    type="org.apache.webapp.admin.host.HostsForm"/>
+
+    <form-bean      name="aliasForm"
+                    type="org.apache.webapp.admin.host.AliasForm"/>
+                    
+    <form-bean      name="aliasesForm"
+                    type="org.apache.webapp.admin.host.AliasesForm"/>
+
+    <!-- ============= Realm Module ============= -->
+
+    <form-bean      name="dataSourceRealmForm"
+                    type="org.apache.webapp.admin.realm.DataSourceRealmForm"/>
+
+    <form-bean      name="jdbcRealmForm"
+                    type="org.apache.webapp.admin.realm.JDBCRealmForm"/>
+
+    <form-bean      name="jndiRealmForm"
+                    type="org.apache.webapp.admin.realm.JNDIRealmForm"/>
+
+    <form-bean      name="memoryRealmForm"
+                    type="org.apache.webapp.admin.realm.MemoryRealmForm"/>
+
+    <form-bean      name="userDatabaseRealmForm"
+                    type="org.apache.webapp.admin.realm.UserDatabaseRealmForm"/>
+
+    <form-bean      name="realmsForm"
+                    type="org.apache.webapp.admin.realm.RealmsForm"/>
+
+    <!-- ============= Context Module ============= -->
+
+    <form-bean      name="contextForm"
+                    type="org.apache.webapp.admin.context.ContextForm"/>
+                    
+    <form-bean      name="contextsForm"
+                    type="org.apache.webapp.admin.context.ContextsForm"/>
+                    
+    <!-- ============= DefaultContext Module ============= -->
+                                        
+    <!-- ============= Connector Module ============= -->
+
+    <form-bean      name="connectorForm"
+                    type="org.apache.webapp.admin.connector.ConnectorForm"/>
+                    
+    <form-bean      name="connectorsForm"
+                    type="org.apache.webapp.admin.connector.ConnectorsForm"/>
+
+    <!-- ============= Valve Module ============= -->
+
+    <form-bean      name="accessLogValveForm"
+                    type="org.apache.webapp.admin.valve.AccessLogValveForm"/>
+
+    <form-bean      name="remoteAddrValveForm"
+                    type="org.apache.webapp.admin.valve.RemoteAddrValveForm"/>
+
+    <form-bean      name="remoteHostValveForm"
+                    type="org.apache.webapp.admin.valve.RemoteHostValveForm"/>
+
+    <form-bean      name="requestDumperValveForm"
+                    type="org.apache.webapp.admin.valve.RequestDumperValveForm"/>
+
+    <form-bean      name="singleSignOnValveForm"
+                    type="org.apache.webapp.admin.valve.SingleSignOnValveForm"/>
+
+    <form-bean      name="valvesForm"
+                    type="org.apache.webapp.admin.valve.ValvesForm"/>
+
+    <!-- ========== Resources Module ========== -->
+
+    <form-bean      name="envEntryForm"
+                    type="org.apache.webapp.admin.resources.EnvEntryForm"/>
+
+    <form-bean      name="envEntriesForm"
+                    type="org.apache.webapp.admin.resources.EnvEntriesForm"/>
+
+    <form-bean      name="userDatabaseForm"
+                    type="org.apache.webapp.admin.resources.UserDatabaseForm"/>
+
+    <form-bean      name="userDatabasesForm"
+                    type="org.apache.webapp.admin.resources.UserDatabasesForm"/>
+
+    <form-bean      name="dataSourceForm"
+                    type="org.apache.webapp.admin.resources.DataSourceForm"/>
+
+    <form-bean      name="dataSourcesForm"
+                    type="org.apache.webapp.admin.resources.DataSourcesForm"/>
+
+    <form-bean      name="mailSessionForm"
+                    type="org.apache.webapp.admin.resources.MailSessionForm"/>
+
+    <form-bean      name="mailSessionsForm"
+                    type="org.apache.webapp.admin.resources.MailSessionsForm"/>
+
+    <form-bean      name="resourceLinkForm"
+                    type="org.apache.webapp.admin.resources.ResourceLinkForm"/>
+
+    <form-bean      name="resourceLinksForm"
+                    type="org.apache.webapp.admin.resources.ResourceLinksForm"/>
+
+    <!-- ========== User Database Module ========== -->
+
+    <form-bean      name="databaseForm"
+                    type="org.apache.webapp.admin.users.BaseForm"/>
+
+    <form-bean      name="groupForm"
+                    type="org.apache.webapp.admin.users.GroupForm"/>
+
+    <form-bean      name="groupsForm"
+                    type="org.apache.webapp.admin.users.GroupsForm"/>
+
+    <form-bean      name="roleForm"
+                    type="org.apache.webapp.admin.users.RoleForm"/>
+
+    <form-bean      name="rolesForm"
+                    type="org.apache.webapp.admin.users.RolesForm"/>
+
+    <form-bean      name="userForm"
+                    type="org.apache.webapp.admin.users.UserForm"/>
+
+    <form-bean      name="usersForm"
+                    type="org.apache.webapp.admin.users.UsersForm"/>
+
+    <!-- ========== ==================== ========== -->
+
+
+  </form-beans>
+
+
+  <!-- ========== Global Forward Definitions ============================== -->
+
+  <global-forwards>
+
+    <forward        name="Dump Registry Results"
+                    path="/dumpRegistry.jsp"
+                redirect="false"/>
+
+    <forward        name="Dump Server Results"
+                    path="/dumpServer.jsp"
+                redirect="false"/>
+
+    <forward        name="Main Menu"
+                    path="/index.jsp"
+                redirect="false"/>
+
+    <forward        name="Tree Control Test"
+                    path="/tree-control-test.jsp"
+                redirect="false"/>
+                  
+    <forward        name="Save Successful"
+                    path="/saved.jsp"
+                redirect="false"/>
+
+    <forward        name="Save Unsuccessful"
+                    path="/savefail.jsp"
+                redirect="false"/>
+                
+    <forward        name="Blank"
+                    path="/blank.jsp"
+                redirect="false"/>
+
+    <forward        name="User"
+                    path="/user.jsp"
+                redirect="false"/>
+
+    <!-- ============ Server Module ============== -->
+
+    <forward        name="Server"
+                    path="/server/server.jsp"
+                redirect="false"/>
+    
+    <!-- ============ Service Module ============== -->
+
+    <forward        name="Service"
+                    path="/service/service.jsp"
+                redirect="false"/>
+
+    <forward        name="Services"
+                    path="/service/services.jsp"
+                redirect="false"/>
+
+    <!-- ============ Host Module ============== -->
+
+    <forward        name="Host"
+                    path="/host/host.jsp"
+                redirect="false"/>
+
+    <forward        name="Hosts"
+                    path="/host/hosts.jsp"
+                redirect="false"/>
+
+    <forward        name="Alias"
+                    path="/host/alias.jsp"
+                redirect="false"/>
+
+    <forward        name="Aliases"
+                    path="/host/aliases.jsp"
+                redirect="false"/>
+
+
+    <!-- ============ Context Module ============== -->
+
+    <forward        name="Context"
+                    path="/context/context.jsp"
+                redirect="false"/>
+
+    <forward        name="Contexts"
+                    path="/context/contexts.jsp"
+                redirect="false"/>
+
+    <!-- ============ DefaultContext Module ============== -->
+
+    <!-- ============ Connector Module ============== -->
+
+    <forward        name="Connector"
+                    path="/connector/connector.jsp"
+                redirect="false"/>
+
+    <forward        name="Connectors"
+                    path="/connector/connectors.jsp"
+                redirect="false"/>
+
+    <!-- ============ Realm Module ============== -->
+
+    <forward        name="DataSourceRealm"
+                    path="/realm/dataSourceRealm.jsp"
+                redirect="false"/>
+
+    <forward        name="JDBCRealm"
+                    path="/realm/jdbcRealm.jsp"
+                redirect="false"/>
+                
+    <forward        name="JNDIRealm"
+                    path="/realm/jndiRealm.jsp"
+                redirect="false"/>
+                
+    <forward        name="MemoryRealm"
+                    path="/realm/memoryRealm.jsp"
+                redirect="false"/>
+
+    <forward        name="UserDatabaseRealm"
+                    path="/realm/userDatabaseRealm.jsp"
+                redirect="false"/>
+
+    <forward        name="Realms"
+                    path="/realm/realms.jsp"
+                redirect="false"/>
+
+    <!-- ============ Context Module ============== -->
+
+    <forward        name="Context"
+                    path="/context/context.jsp"
+                redirect="false"/>
+
+    <forward        name="Contexts"
+                    path="/context/contexts.jsp"
+                redirect="false"/>
+
+    <!-- ============ Valve Module ============== -->
+
+    <forward        name="AccessLogValve"
+                    path="/valve/accessLogValve.jsp"
+                redirect="false"/>
+                
+    <forward        name="RemoteAddrValve"
+                    path="/valve/remoteAddrValve.jsp"
+                redirect="false"/>
+                
+    <forward        name="RemoteHostValve"
+                    path="/valve/remoteHostValve.jsp"
+                redirect="false"/>
+
+    <forward        name="RequestDumperValve"
+                    path="/valve/requestDumperValve.jsp"
+                redirect="false"/>
+
+    <forward        name="SingleSignOn"
+                    path="/valve/singleSignOnValve.jsp"
+                redirect="false"/>
+
+    <forward        name="Valves"
+                    path="/valve/valves.jsp"
+                redirect="false"/>
+
+    <!-- ========== Resources Module ========== -->
+
+    <forward        name="EnvEntry"
+                    path="/resources/envEntry.jsp"
+                redirect="false"/>
+
+    <forward        name="EnvEntries Delete List"
+                    path="/resources/deleteEnvEntries.jsp"
+                redirect="false"/>
+
+    <forward        name="EnvEntries List"
+                    path="/resources/listEnvEntries.jsp"
+                redirect="false"/>
+
+    <forward        name="EnvEntries List Setup"
+                    path="/resources/listEnvEntries.do?forward=EnvEntries+List"
+                redirect="false"/>
+
+    <forward        name="UserDatabase"
+                    path="/resources/userDatabase.jsp"
+                redirect="false"/>
+
+    <forward        name="UserDatabases Delete List"
+                    path="/resources/deleteUserDatabases.jsp"
+                redirect="false"/>
+
+    <forward        name="UserDatabases List"
+                    path="/resources/listUserDatabases.jsp"
+                redirect="false"/>
+
+    <forward        name="UserDatabases List Setup"
+                    path="/resources/listUserDatabases.do?forward=UserDatabases+List"
+                redirect="false"/>
+                
+    <forward        name="DataSource"
+                    path="/resources/dataSource.jsp"
+                redirect="false"/>
+
+    <forward        name="DataSources Delete List"
+                    path="/resources/deleteDataSources.jsp"
+                redirect="false"/>
+
+    <forward        name="DataSources List"
+                    path="/resources/listDataSources.jsp"
+                redirect="false"/>
+
+    <forward        name="DataSources List Setup"
+                    path="/resources/listDataSources.do?forward=DataSources+List"
+                redirect="false"/>
+
+    <forward        name="MailSession"
+                    path="/resources/mailSession.jsp"
+                redirect="false"/>
+
+    <forward        name="MailSessions Delete List"
+                    path="/resources/deleteMailSessions.jsp"
+                redirect="false"/>
+
+    <forward        name="MailSessions List"
+                    path="/resources/listMailSessions.jsp"
+                redirect="false"/>
+
+    <forward        name="MailSessions List Setup"
+                    path="/resources/listMailSessions.do?forward=MailSessions+List"
+                redirect="false"/>
+
+    <forward        name="ResourceLink"
+                    path="/resources/resourceLink.jsp"
+                redirect="false"/>
+
+    <forward        name="ResourceLinks Delete List"
+                    path="/resources/deleteResourceLinks.jsp"
+                redirect="false"/>
+
+    <forward        name="ResourceLinks List"
+                    path="/resources/listResourceLinks.jsp"
+                redirect="false"/>
+
+    <forward        name="ResourceLinks List Setup"
+                    path="/resources/listResourceLinks.do?forward=ResourceLinks+List"
+                redirect="false"/>
+                        
+    <!-- ========== User Database Module ========== -->
+
+    <forward        name="Group"
+                    path="/users/group.jsp"
+                redirect="false"/>
+
+    <forward        name="Groups Delete List"
+                    path="/users/deleteGroups.jsp"
+                redirect="false"/>
+
+    <forward        name="Groups List"
+                    path="/users/listGroups.jsp"
+                redirect="false"/>
+
+    <forward        name="Groups List Setup"
+                    path="/users/listGroups.do?forward=Groups+List"
+                redirect="false"/>
+
+    <forward        name="Role"
+                    path="/users/role.jsp"
+                redirect="false"/>
+
+    <forward        name="Roles Delete List"
+                    path="/users/deleteRoles.jsp"
+                redirect="false"/>
+
+    <forward        name="Roles List"
+                    path="/users/listRoles.jsp"
+                redirect="false"/>
+
+    <forward        name="Roles List Setup"
+                    path="/users/listRoles.do?forward=Roles+List"
+                redirect="false"/>
+
+    <forward        name="User"
+                    path="/users/user.jsp"
+                redirect="false"/>
+
+    <forward        name="Users Delete List"
+                    path="/users/deleteUsers.jsp"
+                redirect="false"/>
+
+    <forward        name="Users List"
+                    path="/users/listUsers.jsp"
+                redirect="false"/>
+
+    <forward        name="Users List Setup"
+                    path="/users/listUsers.do?forward=Users+List"
+                redirect="false"/>
+
+    <!-- ========== ==================== ========== -->
+
+
+  </global-forwards>
+
+
+  <!-- ========== Action Mapping Definitions ============================== -->
+
+  <action-mappings>
+
+    <!-- Dump registry information (debugging) -->
+    <action    path="/dumpRegistry"
+               type="org.apache.webapp.admin.DumpRegistryAction"/>
+
+    <!-- Dump MBean server information (debugging) -->
+    <action    path="/dumpServer"
+               type="org.apache.webapp.admin.DumpServerAction"/>
+
+    <!-- Set up Tree datastructure -->
+    <action    path="/setUpTree"
+               type="org.apache.webapp.admin.SetUpTreeAction">
+      <forward        name="SetUpTree"
+                      path="/tree-control-test.jsp"
+                  redirect="true"/>
+    </action>
+
+    <!-- Log out of the application -->
+    <action    path="/logOut"
+               type="org.apache.webapp.admin.LogOutAction">
+      <forward        name="Main Menu"
+                      path="/index.jsp"
+                  redirect="true"/>
+    </action>
+
+    <!-- Save current settings to server.xml -->
+    <action    path="/commitChanges"
+               type="org.apache.webapp.admin.CommitChangesAction">
+      <forward        name="Banner"
+                      path="/banner.jsp"
+                  redirect="true"/>
+    </action>
+
+    <!-- Process a set-locale action -->
+    <action    path="/setLocale"
+               type="org.apache.webapp.admin.SetLocaleAction"
+               name="setLocaleForm"
+              scope="session">
+    </action>
+
+    <!-- Tree control test action -->
+    <action    path="/treeControlTest"
+               type="org.apache.webapp.admin.TreeControlTestAction"/>
+
+   <!-- ============= Server Module ============== -->
+
+    <!-- Set up Edit Server transaction -->
+    <action    path="/EditServer"
+               type="org.apache.webapp.admin.server.EditServerAction">
+    </action>
+
+    <!-- Perform Save Server transaction -->
+    <action    path="/SaveServer"
+               type="org.apache.webapp.admin.server.SaveServerAction"
+               name="serverForm"
+              input="/server/server.jsp"
+               scope="session"/>
+
+    <!-- ============= Service Module ============== -->
+
+    <!-- Set up Add Service transaction -->
+    <action    path="/AddService"
+               type="org.apache.webapp.admin.service.AddServiceAction">
+    </action>
+
+    <!-- Set up Delete Services transaction -->
+    <action    path="/DeleteService"
+               type="org.apache.webapp.admin.service.DeleteServiceAction"
+               name="servicesForm"
+               scope="request"/>
+
+    <!-- Perform Delete Services transaction -->
+    <action    path="/DeleteServices"
+               type="org.apache.webapp.admin.service.DeleteServicesAction"
+               name="servicesForm"
+               scope="request"/>
+
+    <!-- Set up Edit Service transaction -->
+    <action    path="/EditService"
+               type="org.apache.webapp.admin.service.EditServiceAction">
+    </action>
+
+    <!-- Perform Save Service transaction -->
+    <action    path="/SaveService"
+               type="org.apache.webapp.admin.service.SaveServiceAction"
+               name="serviceForm"
+              input="/service/service.jsp"
+               scope="session"/>
+
+    <!-- ============= Host Module ============== -->
+
+    <!-- Set up Add Host transaction -->
+    <action    path="/AddHost"
+               type="org.apache.webapp.admin.host.AddHostAction">
+    </action>
+
+    <!-- Set up Delete Hosts transaction -->
+    <action    path="/DeleteHost"
+               type="org.apache.webapp.admin.host.DeleteHostAction"
+               name="hostsForm"
+               scope="request"/>
+
+    <!-- Perform Delete Hosts transaction -->
+    <action    path="/DeleteHosts"
+               type="org.apache.webapp.admin.host.DeleteHostsAction"
+               name="hostsForm"
+               scope="request"/>
+
+    <!-- Set up Edit Host transaction -->
+    <action    path="/EditHost"
+               type="org.apache.webapp.admin.host.EditHostAction">
+    </action>
+
+    <!-- Perform Save Host transaction -->
+    <action    path="/SaveHost"
+               type="org.apache.webapp.admin.host.SaveHostAction"
+               name="hostForm"
+              input="/host/host.jsp"
+               scope="session"/>
+               
+    <!-- Set up Add Alias transaction -->
+    <action    path="/AddAlias"
+               type="org.apache.webapp.admin.host.AddAliasAction">
+    </action>
+
+    <!-- Perform Delete Aliases transaction -->
+    <action    path="/DeleteAlias"
+               type="org.apache.webapp.admin.host.DeleteAliasAction"
+               name="aliasesForm"
+               scope="request"/>
+               
+    <!-- Perform Delete Aliases transaction -->
+    <action    path="/DeleteAliases"
+               type="org.apache.webapp.admin.host.DeleteAliasesAction"
+               name="aliasesForm"
+               scope="request"/>
+               
+    <!-- Perform Save Host transaction -->
+    <action    path="/SaveAlias"
+               type="org.apache.webapp.admin.host.SaveAliasAction"
+               name="aliasForm"
+              input="/host/alias.jsp"
+               scope="session"/>
+
+    <!-- ============= Realm Module ============== -->
+
+    <!-- Set up Add Realm transaction -->
+    <action    path="/AddRealm"
+               type="org.apache.webapp.admin.realm.AddRealmAction">
+    </action>
+
+    <!-- Set up Add Realm transaction -->
+    <action    path="/realm/AddRealm"
+               type="org.apache.webapp.admin.realm.AddRealmAction">
+    </action>
+
+    <!-- Set up Delete Realms transaction -->
+    <action    path="/DeleteRealm"
+               type="org.apache.webapp.admin.realm.DeleteRealmAction"
+               name="realmsForm"
+               scope="request"/>
+
+    <!-- Perform Delete Realms transaction -->
+    <action    path="/DeleteRealms"
+               type="org.apache.webapp.admin.realm.DeleteRealmsAction"
+               name="realmsForm"
+               scope="request"/>
+
+    <!-- Set up Edit Realm transaction (generic) -->
+    <action    path="/EditRealm"
+               type="org.apache.webapp.admin.realm.EditRealmAction">
+    </action>
+
+    <!-- Perform Save UserDatabase Realm transaction -->
+    <action    path="/SaveUserDatabaseRealm"
+               type="org.apache.webapp.admin.realm.SaveUserDatabaseRealmAction"
+               name="userDatabaseRealmForm"
+              input="/realm/userDatabaseRealm.jsp"
+               scope="session"/>
+
+   <!-- Perform Save DataSource Realm transaction -->
+    <action    path="/SaveDataSourceRealm"
+               type="org.apache.webapp.admin.realm.SaveDataSourceRealmAction"
+               name="dataSourceRealmForm"
+              input="/realm/dataSourceRealm.jsp"
+               scope="session"/>
+
+   <!-- Perform Save JDBC Realm transaction -->
+    <action    path="/SaveJDBCRealm"
+               type="org.apache.webapp.admin.realm.SaveJDBCRealmAction"
+               name="jdbcRealmForm"
+              input="/realm/jdbcRealm.jsp"
+               scope="session"/>
+
+    <!-- Perform Save JNDI Realm transaction -->
+    <action    path="/SaveJNDIRealm"
+               type="org.apache.webapp.admin.realm.SaveJNDIRealmAction"
+               name="jndiRealmForm"
+              input="/realm/jndiRealm.jsp"
+               scope="session"/>
+
+    <!-- Perform Save Memory transaction -->
+    <action    path="/SaveMemoryRealm"
+               type="org.apache.webapp.admin.realm.SaveMemoryRealmAction"
+               name="memoryRealmForm"
+              input="/realm/memoryRealm.jsp"
+               scope="session"/>
+               
+   <!-- ============= Context Module ============== -->
+
+    <!-- Set up Add Context transaction -->
+    <action    path="/AddContext"
+               type="org.apache.webapp.admin.context.AddContextAction">
+    </action>
+
+    <!-- Set up Delete Contexts transaction -->
+    <action    path="/DeleteContext"
+               type="org.apache.webapp.admin.context.DeleteContextAction"
+               name="contextsForm"
+               scope="request"/>
+
+    <!-- Perform Delete Contexts transaction -->
+    <action    path="/DeleteContexts"
+               type="org.apache.webapp.admin.context.DeleteContextsAction"
+               name="contextsForm"
+               scope="request"/>
+
+    <!-- Set up Edit Context transaction -->
+    <action    path="/EditContext"
+               type="org.apache.webapp.admin.context.EditContextAction">
+    </action>
+
+    <!-- Perform Save Context transaction -->
+    <action    path="/SaveContext"
+               type="org.apache.webapp.admin.context.SaveContextAction"
+               name="contextForm"
+              input="/context/context.jsp"
+               scope="session"/>
+               
+   <!-- ============= DefaultContext Module ============== -->
+
+   <!-- ============= Connector Module ============== -->
+
+    <!-- Set up Add Connector transaction -->
+    <action    path="/AddConnector"
+               type="org.apache.webapp.admin.connector.AddConnectorAction">
+    </action>
+
+    <!-- Set up Add Connector transaction -->
+    <action    path="/connector/AddConnector"
+               type="org.apache.webapp.admin.connector.AddConnectorAction">
+    </action>
+
+    <!-- Set up Delete Connectors transaction -->
+    <action    path="/DeleteConnector"
+               type="org.apache.webapp.admin.connector.DeleteConnectorAction"
+               name="connectorsForm"
+               scope="request"/>
+
+    <!-- Perform Delete Connectors transaction -->
+    <action    path="/DeleteConnectors"
+               type="org.apache.webapp.admin.connector.DeleteConnectorsAction"
+               name="connectorsForm"
+               scope="request"/>
+
+    <!-- Set up Edit Connector transaction -->
+    <action    path="/EditConnector"
+               type="org.apache.webapp.admin.connector.EditConnectorAction">
+    </action>
+
+    <!-- Perform Save Connector transaction -->
+    <action    path="/SaveConnector"
+               type="org.apache.webapp.admin.connector.SaveConnectorAction"
+               name="connectorForm"
+              input="/connector/connector.jsp"
+               scope="session"/>
+               
+   <!-- ============= Valve Module ============== -->
+
+    <!-- Set up Add Valve transaction -->
+    <action    path="/AddValve"
+               type="org.apache.webapp.admin.valve.AddValveAction">
+    </action>
+
+    <!-- Set up Add Valve transaction -->
+    <action    path="/valve/AddValve"
+               type="org.apache.webapp.admin.valve.AddValveAction">
+    </action>
+
+    <!-- Set up Delete Valves transaction -->
+    <action    path="/DeleteValve"
+               type="org.apache.webapp.admin.valve.DeleteValveAction"
+               name="valvesForm"
+               scope="request"/>
+
+    <!-- Perform Delete Valves transaction -->
+    <action    path="/DeleteValves"
+               type="org.apache.webapp.admin.valve.DeleteValvesAction"
+               name="valvesForm"
+               scope="request"/>
+
+    <!-- Set up Edit Valve transaction (generic) -->
+    <action    path="/EditValve"
+               type="org.apache.webapp.admin.valve.EditValveAction">
+    </action>
+    
+    <!-- Perform Save AccessLog Valve transaction -->
+    <action    path="/SaveAccessLogValve"
+               type="org.apache.webapp.admin.valve.SaveAccessLogValveAction"
+               name="accessLogValveForm"
+              input="/valve/accessLogValve.jsp"
+               scope="session"/>
+
+   <!-- Perform Save Remote Addr Valve transaction -->
+    <action    path="/SaveRemoteAddrValve"
+               type="org.apache.webapp.admin.valve.SaveRemoteAddrValveAction"
+               name="remoteAddrValveForm"
+              input="/valve/remoteAddrValve.jsp"
+               scope="session"/>
+
+    <!-- Perform Save Remote Host Valve transaction -->
+    <action    path="/SaveRemoteHostValve"
+               type="org.apache.webapp.admin.valve.SaveRemoteHostValveAction"
+               name="remoteHostValveForm"
+              input="/valve/remoteHostValve.jsp"
+               scope="session"/>
+
+    <!-- Perform Save Request Dumper Valve transaction -->
+    <action    path="/SaveRequestDumperValve"
+               type="org.apache.webapp.admin.valve.SaveRequestDumperValveAction"
+               name="requestDumperValveForm"
+              input="/valve/requestDumperValve.jsp"
+               scope="session"/>
+
+   <!-- Perform Save Single Sign On Valve transaction -->
+    <action    path="/SaveSingleSignOn"
+               type="org.apache.webapp.admin.valve.SaveSingleSignOnValveAction"
+               name="singleSignOnValveForm"
+              input="/valve/singleSignOnValve.jsp"
+               scope="session"/>
+               
+   <!-- ========== Resources Module ========== -->
+
+   <action    path="/resources/deleteEnvEntries"
+               name="envEntriesForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.DeleteEnvEntriesAction"/>
+
+   <action    path="/resources/deleteUserDatabases"
+               name="userDatabasesForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.DeleteUserDatabasesAction"/>
+      
+    <action    path="/resources/deleteDataSources"
+               name="dataSourcesForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.DeleteDataSourcesAction"/>
+
+    <action    path="/resources/deleteMailSessions"
+               name="mailSessionsForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.DeleteMailSessionsAction"/>
+
+    <action    path="/resources/deleteResourceLinks"
+               name="resourceLinksForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.DeleteResourceLinksAction"/>
+               
+    <action    path="/resources/listEnvEntries"
+               name="envEntriesForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.ListEnvEntriesAction"/>
+               
+    <action    path="/resources/listUserDatabases"
+               name="userDatabasesForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.ListUserDatabasesAction"/>
+
+    <action    path="/resources/listDataSources"
+               name="dataSourcesForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.ListDataSourcesAction"/>
+
+    <action    path="/resources/listMailSessions"
+               name="mailSessionsForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.ListMailSessionsAction"/>
+
+    <action    path="/resources/listResourceLinks"
+               name="resourceLinksForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.ListResourceLinksAction"/>
+
+   <action    path="/resources/saveEnvEntry"
+              input="/resources/envEntry.jsp"
+               name="envEntryForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.SaveEnvEntryAction"/>
+               
+   <action    path="/resources/saveUserDatabase"
+              input="/resources/userDatabase.jsp"
+               name="userDatabaseForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.SaveUserDatabaseAction"/>
+
+    <action    path="/resources/saveDataSource"
+              input="/resources/dataSource.jsp"
+               name="dataSourceForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.SaveDataSourceAction"/>
+
+    <action    path="/resources/saveMailSession"
+              input="/resources/mailSession.jsp"
+               name="mailSessionForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.SaveMailSessionAction"/>
+
+    <action    path="/resources/saveResourceLink"
+              input="/resources/resourceLink.jsp"
+               name="resourceLinkForm"
+              scope="request"
+               type="org.apache.webapp.admin.resources.SaveResourceLinkAction"/>
+
+   <action    path="/resources/setUpEnvEntry"
+               type="org.apache.webapp.admin.resources.SetUpEnvEntryAction"/>
+
+   <action    path="/resources/setUpUserDatabase"
+               type="org.apache.webapp.admin.resources.SetUpUserDatabaseAction"/>
+
+    <action    path="/resources/setUpDataSource"
+               type="org.apache.webapp.admin.resources.SetUpDataSourceAction"/>
+
+    <action    path="/resources/setUpMailSession"
+               type="org.apache.webapp.admin.resources.SetUpMailSessionAction"/>
+
+    <action    path="/resources/setUpResourceLink"
+               type="org.apache.webapp.admin.resources.SetUpResourceLinkAction"/>
+           
+    <!-- ========== User Database Module ========== -->
+
+    <action    path="/users/deleteGroups"
+               name="groupsForm"
+              scope="request"
+               type="org.apache.webapp.admin.users.DeleteGroupsAction"/>
+
+    <action    path="/users/deleteRoles"
+               name="rolesForm"
+              scope="request"
+                type="org.apache.webapp.admin.users.DeleteRolesAction"/>
+
+    <action    path="/users/deleteUsers"
+               name="usersForm"
+              scope="request"
+               type="org.apache.webapp.admin.users.DeleteUsersAction"/>
+
+    <action    path="/users/listGroups"
+               name="groupsForm"
+              scope="request"
+               type="org.apache.webapp.admin.users.ListGroupsAction"/>
+
+    <action    path="/users/listRoles"
+               name="rolesForm"
+              scope="request"
+               type="org.apache.webapp.admin.users.ListRolesAction"/>
+
+    <action    path="/users/listUsers"
+               name="usersForm"
+              scope="request"
+               type="org.apache.webapp.admin.users.ListUsersAction"/>
+
+    <action    path="/users/saveGroup"
+              input="/users/group.jsp"
+               name="groupForm"
+              scope="request"
+               type="org.apache.webapp.admin.users.SaveGroupAction"/>
+
+    <action    path="/users/saveRole"
+              input="/users/role.jsp"
+               name="roleForm"
+              scope="request"
+               type="org.apache.webapp.admin.users.SaveRoleAction"/>
+
+    <action    path="/users/saveUser"
+              input="/users/user.jsp"
+               name="userForm"
+              scope="request"
+               type="org.apache.webapp.admin.users.SaveUserAction"/>
+
+    <action    path="/users/setUpGroup"
+               type="org.apache.webapp.admin.users.SetUpGroupAction"/>
+
+    <action    path="/users/setUpRole"
+               type="org.apache.webapp.admin.users.SetUpRoleAction"/>
+
+    <action    path="/users/setUpUser"
+               type="org.apache.webapp.admin.users.SetUpUserAction"/>
+
+    <!-- ========== ==================== ========== -->
+
+
+  </action-mappings>  
+
+  <controller locale="true" nocache="true" />
+ 
+  <message-resources parameter="org.apache.webapp.admin.ApplicationResources" />
+
+</struts-config>
diff --git a/container/webapps/admin/WEB-INF/web.xml b/container/webapps/admin/WEB-INF/web.xml
new file mode 100644
index 0000000..57b0b10
--- /dev/null
+++ b/container/webapps/admin/WEB-INF/web.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+  <display-name>Tomcat Administration Application</display-name>
+  <description>
+    Tomcat HTML based administration web application.
+  </description>
+
+  <!-- Example filter to set character encoding on each request.
+       Uncomment this filter definition and the mapping to use
+       the filter to decode post and get parameters -->
+
+  <filter>
+    <filter-name>Set Character Encoding</filter-name>
+    <filter-class>org.apache.webapp.admin.filters.SetCharacterEncodingFilter</filter-class>
+    <init-param>
+      <param-name>encoding</param-name>
+      <param-value>UTF8</param-value>
+    </init-param>
+  </filter>
+
+  <!-- Example filter mapping to apply the "Set Character Encoding" filter
+       to *all* requests processed by this web application -->
+
+  <filter-mapping>
+    <filter-name>Set Character Encoding</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
+
+  <!-- Action Servlet Configuration -->
+  <servlet>
+    <servlet-name>action</servlet-name>
+    <servlet-class>
+      org.apache.webapp.admin.ApplicationServlet
+    </servlet-class>
+<!-- Deprecated
+    <init-param>
+      <param-name>application</param-name>
+      <param-value>
+        org.apache.webapp.admin.ApplicationResources
+      </param-value>
+    </init-param>
+-->
+    <init-param>
+      <param-name>config</param-name>
+      <param-value>/WEB-INF/struts-config.xml</param-value>
+    </init-param>
+<!-- Deprecated   
+    <init-param>
+      <param-name>debug</param-name>
+      <param-value>0</param-value>
+    </init-param>
+-->
+    <init-param>
+      <param-name>detail</param-name>
+      <param-value>0</param-value>
+    </init-param>
+<!-- Deprecated
+    <init-param>
+      <param-name>locale</param-name>
+      <param-value>true</param-value>
+    </init-param>
+-->
+<!-- Deprecated
+    <init-param>
+      <param-name>nocache</param-name>
+      <param-value>true</param-value>
+    </init-param>
+-->
+    <init-param>
+      <param-name>validate</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <!-- Label to be displayed for rootnode. If absent, rootnode is not rendered -->
+    <!--
+    <init-param>
+      <param-name>rootnodename</param-name>
+      <param-value>Tomcat Root</param-value>
+    </init-param>
+    -->
+    <init-param>
+      <param-name>treebuilders</param-name>
+      <param-value>
+        org.apache.webapp.admin.TomcatTreeBuilder,
+        org.apache.webapp.admin.resources.ResourcesTreeBuilder,
+        org.apache.webapp.admin.users.UsersTreeBuilder
+    </param-value>
+    </init-param>
+    <init-param>
+      <param-name>domain</param-name>
+      <param-value>Catalina</param-value>
+    </init-param>
+  </servlet>
+
+  <!-- Action Servlet Mapping -->
+  <servlet-mapping>
+    <servlet-name>action</servlet-name>
+    <url-pattern>*.do</url-pattern>
+  </servlet-mapping>
+
+  <!-- Security is active on entire directory -->
+  <security-constraint>
+    <display-name>Tomcat Server Configuration Security Constraint</display-name>
+    <web-resource-collection>
+      <web-resource-name>Protected Area</web-resource-name>
+      <!-- Define the context-relative URL(s) to be protected -->
+      <url-pattern>*.jsp</url-pattern>
+      <url-pattern>*.do</url-pattern>
+      <url-pattern>*.html</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <!-- Anyone with one of the listed roles may access this area -->
+      <role-name>admin</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <!-- Login configuration uses form-based authentication -->
+  <login-config>
+    <auth-method>FORM</auth-method>
+    <realm-name>Tomcat Server Configuration Form-Based Authentication Area</realm-name>
+    <form-login-config>
+      <form-login-page>/login.jsp</form-login-page>
+      <form-error-page>/error.jsp</form-error-page>
+    </form-login-config>
+  </login-config>
+
+  <!-- Security roles referenced by this web application -->
+  <security-role>
+    <description>
+      The role that is required to log in to the Administration Application
+    </description>
+    <role-name>admin</role-name>
+  </security-role>
+
+</web-app>
diff --git a/container/webapps/admin/admin.css b/container/webapps/admin/admin.css
new file mode 100644
index 0000000..c1b7467
--- /dev/null
+++ b/container/webapps/admin/admin.css
@@ -0,0 +1,95 @@
+.masthead-title-text {
+  color: #FFFFFF;
+  margin: 3px 5px 5px 3px;
+  font-size: large;
+  font-family:  Arial, Verdana,Helvetica, Sans-Serif;
+}
+
+.page-title-text {
+  color: #FFFFFF;
+  font-weight: bold;
+  margin: 3px 5px 5px 3px;
+  font-size: normal;
+  font-family: Arial, Verdana, Helvetica, Sans-Serif;
+}
+
+.table-title-text {
+  color: #000000;
+  font-weight: bold;
+  margin: 3px 5px 5px 4px;
+  font-family: Arial, Verdana, Helvetica, Sans-Serif;
+}
+
+.table-header-text {
+  color: #FFFFFF;
+  font-weight: normal;
+  margin: 3px 5px 1px 15px;
+  font-family:  Arial, Verdana,Helvetica, Sans-Serif;
+}
+
+.table-label-text {
+  color: #000000;
+  margin: 3px 5px 3px 15px;
+  font-family: Arial, Verdana, Helvetica, Sans-Serif;
+}
+
+.table-normal-text {
+  color: #000000;
+  margin: 3px 5px 3px 15px;
+  font-family: "Times New Roman", Times, serif;
+}
+
+.back-table {
+  background-color: #9999CC;
+  margin: 0px 5px 3px 5px;
+  font-family: Verdana, Arial, Helvetica, Sans-Serif;
+}
+
+.front-table {
+  background-color: #FFFFFF;
+  font-family: Arial, Verdana, Helvetica, Sans-Serif;
+}
+
+.page-title-row {
+  background-color: #7171A5;
+  text-align: right;
+  font-family: Arial, Verdana, Helvetica, Sans-Serif;
+}
+
+.header-row {
+  background-color: #9999CC;
+  text-align: center;
+  font-family: Arial, Verdana, Helvetica, Sans-Serif;
+}
+
+.sort-row {
+  background-color: #CECEFF;
+  text-align: center;
+  font-family: Arial, Verdana, Helvetica, Sans-Serif;
+}
+
+.line-row {
+  background-color: #CCCCCC;
+  font-family: Arial, Verdana, Helvetica, Sans-Serif;
+}
+
+.button {
+  background-color: #CCCCFF;
+  font-family: Arial, Verdana, Helvetica, Sans-Serif;
+}
+
+a.button-link-text:visited, a.button-link-text:link, a.button-link-text:active {
+  color: #000000;
+  background-color: #CCCCFF;
+  font-weight: bold;
+  font-family: Arial, Verdana, Geneva, Helvetica, Sans-Serif;
+  text-decoration: none;
+}
+
+a.button-link-text:hover {
+  color: #000000;
+  background-color: #CCCCFF;
+  font-weight: bold;
+  font-family: Arial, Verdana, Geneva, Helvetica, Sans-Serif;
+  text-decoration: underline;
+}
diff --git a/container/webapps/admin/admin.xml b/container/webapps/admin/admin.xml
new file mode 100644
index 0000000..b0711ef
--- /dev/null
+++ b/container/webapps/admin/admin.xml
@@ -0,0 +1,20 @@
+<!--
+
+    Context configuration file for the Tomcat Administration Web App
+
+    $Id$
+
+-->
+
+
+<Context docBase="${catalina.home}/server/webapps/admin" privileged="true"
+         antiResourceLocking="false" antiJARLocking="false">
+
+  <!-- Uncomment this Valve to limit access to the Admin app to localhost
+   for obvious security reasons. Allow may be a comma-separated list of
+   hosts (or even regular expressions).
+  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
+    allow="127.0.0.1"/>
+  -->
+
+</Context>
diff --git a/container/webapps/admin/banner.jsp b/container/webapps/admin/banner.jsp
new file mode 100644
index 0000000..b3f9d93
--- /dev/null
+++ b/container/webapps/admin/banner.jsp
@@ -0,0 +1,78 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<!-- Standard Content -->
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+
+<body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0" bgcolor="7171A5" background="images/BlueTile.gif">
+
+<table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr>
+      <td align="left" valign="middle">
+        <div class="masthead-title-text" align="left"><img src="images/TomcatBanner.jpg" alt="Tomcat Web Server Administration Tool" height="120"></div>
+      </td>
+      <form method='post' action='<%=request.getContextPath()%>/commitChanges.do' target='_self'>
+      <td align="right" valign="middle">
+        <html:submit onclick="if(confirm('Are you sure?  Committing changes will restart modified web applications.')) { return true; } else { return false; }">
+          <bean:message key="button.commit"/>
+        </html:submit>
+      </td>
+      </form>
+      <td width="1%">
+        <div class="table-normal-text" align="left">&nbsp </div>
+      </td>
+    <form method='post' action='<%=request.getContextPath()%>/logOut.do' target='_top'>
+      <td align="right" valign="middle">
+        <html:submit>
+          <bean:message key="button.logout"/>
+        </html:submit>
+      </td>
+      <td width="1%">
+        <div class="table-normal-text" align="left">&nbsp </div>
+      </td>
+    </form>
+  </tr>
+</table>
+
+<!-- Select language -->
+<!--
+
+<h2><bean:message key="login.changeLanguage"/></h2>
+
+<html:form action="/setLocale" method="POST" target="_self">
+  <table border="0" cellspacing="5">
+    <tr>
+      <td align="right">
+        <html:select property="locale">
+          <html:options name="applicationLocales"
+                    property="localeValues"
+                   labelName="applicationLocales"
+               labelProperty="localeLabels"/>
+        </html:select>
+      </td>
+      <td align="left">
+        <html:submit>
+          <bean:message key="button.change"/>
+        </html:submit>
+      </td>
+    </tr>
+  </table>
+</html:form>
+-->
+
+</body>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</html:html>
diff --git a/container/webapps/admin/blank.jsp b/container/webapps/admin/blank.jsp
new file mode 100644
index 0000000..16c3296
--- /dev/null
+++ b/container/webapps/admin/blank.jsp
@@ -0,0 +1,24 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<!-- Standard Content -->
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+
+<body bgcolor="white" background="images/PaperTexture.gif">
+
+</body>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</html:html>
diff --git a/container/webapps/admin/build.xml b/container/webapps/admin/build.xml
new file mode 100644
index 0000000..fecff02
--- /dev/null
+++ b/container/webapps/admin/build.xml
@@ -0,0 +1,241 @@
+<project name="admin" default="build-main" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="../../build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <!-- Build Defaults -->
+  <property name="build.compiler"  value="modern"/>
+  <property name="webapps.build"   value="${basedir}/../build"/>
+  <property name="webapps.dist"    value="${basedir}/../dist"/>
+  <property name="webapp.name"     value="admin"/>
+
+  <!-- Dependent JARs and files -->
+  <property name="servlet-api.jar" value="${api.home}/jsr154/dist/lib/servlet-api.jar"/>
+  <property name="jsp-api.jar"     value="${api.home}/jsr152/dist/lib/jsp-api.jar"/>
+  <property name="struts-core.jar" value="${struts.jar}" />
+  <property name="struts-taglib.jar" value="${struts.jar}" />
+
+  <!-- Construct Admin classpath -->
+  <path id="admin.classpath">
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${jsp-api.jar}"/>
+    <pathelement location="${struts-core.jar}"/>
+    <pathelement location="${commons-beanutils.jar}"/>
+    <pathelement location="${commons-collections.jar}"/>
+    <pathelement location="${commons-digester.jar}"/>
+  </path>
+
+
+  <!-- =================== BUILD: Set compile flags ======================= -->
+  <target name="flags">
+
+    <!-- JDK flags -->
+    <available property="jdk.1.2.present" classname="java.util.HashMap" />
+    <available property="jdk.1.3.present"
+     classname="java.lang.reflect.Proxy" />
+    <available property="jdk.1.4.present" classname="java.nio.Buffer" />
+
+    <!-- Ant flags -->
+    <available property="style.available"
+     classname="org.apache.tools.ant.taskdefs.optional.TraXLiaison" />
+
+    <!-- Class availability flags -->
+    <condition property="jaxp.present">
+      <and>
+        <available classname="javax.xml.parsers.SAXParser"
+         classpath="${xerces.jar}" />
+        <available classname="org.xml.sax.ContentHandler"
+         classpath="${xerces.jar}" />
+      </and>
+    </condition>
+    <available property="jmx.present"
+     classname="javax.management.MBeanServer"
+     classpath="${jmx.jar}" />
+    <available property="modeler.present"
+     classname="org.apache.commons.modeler.Registry"
+     classpath="${commons-modeler.jar}:${jmx.jar}"/>
+    <available property="servlet.present"
+     classname="javax.servlet.Servlet"
+     classpath="${servlet-api.jar}" />
+    <available property="jsp.present"
+     classname="javax.servlet.jsp.JspPage"
+     classpath="${jsp-api.jar}" />
+    <available property="struts.present"
+     classname="org.apache.struts.action.ActionForm"
+     classpath="${struts-core.jar}" />
+    <available property="beanutils.present"
+     classname="org.apache.commons.beanutils.PropertyUtils"
+     classpath="${commons-beanutils.jar}" />
+
+
+    <!-- JAR files availability flags -->
+    <available property="jmx.jar.present"   file="${jmx.jar}" />
+    <available property="modeler.jar.present" file="${commons-modeler.jar}" />
+    <available property="servlet-api.jar.present" file="${servlet-api.jar}" />
+    <available property="jsp-api.jar.present" file="${jsp-api.jar}" />
+    <available property="struts.jar.present"  file="${struts-core.jar}" />
+    <available property="beanutils.jar.present" file="${commons-beanutils.jar}" />
+
+    <!-- Conditional compilation flags (determined from the flags above) -->
+    <condition property="compile.admin">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <and>
+          <equals arg1="${struts.present}" arg2="true" />
+          <equals arg1="${jmx.present}" arg2="true" />
+          <equals arg1="${modeler.present}" arg2="true" />
+        </and>
+      </or>
+    </condition>
+
+    <!-- Conditional copy flags (determined from the flags above) -->
+    <condition property="copy.struts.jar">
+      <or>
+        <equals arg1="${full.dist}" arg2="on" />
+        <equals arg1="${struts.jar.present}" arg2="true" />
+      </or>
+    </condition>
+
+  </target>
+
+
+  <!-- =================== BUILD: Set compile flags ======================= -->
+  <target name="flags.display" depends="flags" unless="flags.hide">
+
+    <echo message="--- Build environment for Tomcat Server Configuration Application ---" />
+
+    <echo message="If ${property_name} is displayed, then the property is not set)" />
+
+    <echo message="--- Build options ---" />
+    <echo message="full.dist=${full.dist}" />
+    <echo message="build.sysclasspath=${build.sysclasspath}" />
+    <echo message="compile.debug=${compile.debug}" />
+    <echo message="compile.deprecation=${compile.deprecation}" />
+    <echo message="compile.optimize=${compile.optimize}" />
+
+    <echo message="--- Ant Flags ---" />
+    <echo message="&lt;style&gt; task available (required)=${style.available}" />
+
+    <echo message="--- JDK ---" />
+    <echo message="jdk.1.2.present=${jdk.1.2.present}" />
+    <echo message="jdk.1.3.present=${jdk.1.3.present}" />
+    <echo message="jdk.1.4.present=${jdk.1.4.present}" />
+
+    <echo message="--- Required Libraries ---" />
+    <echo message="jaxp.present=${jaxp.present}" />
+    <echo message="jmx.present=${jmx.present}" />
+    <echo message="modeler.present=${modeler.present}" />
+    <echo message="servlet.present=${servlet.present}" />
+    <echo message="jsp.present=${jsp.present}" />
+
+    <echo message="--- Required JARs ---" />
+    <echo message="jmx.jar.present=${jmx.jar.present}" />
+    <echo message="modeler.jar.present=${modeler.jar.present}" />
+    <echo message="servlet-api.jar.present=${servlet-api.jar.present}" />
+    <echo message="jsp-api.jar.present=${jsp-api.jar.present}" />
+    <echo message="struts.jar.present=${struts.jar.present}" />
+    <echo message="beanutils.jar.present=${beanutils.jar.present}" />
+
+    <echo message="--- Optional JARs ---" />
+
+    <echo message="--- Conditional compilation flags ---" />
+    <echo message="compile.admin=${compile.admin}" />
+
+    <echo message="--- Distribution flags ---" />
+    <echo message="copy.struts.jar=${copy.struts.jar}" />
+
+  </target>
+
+
+  <!-- ======================== BUILD: Copy JARs ========================== -->
+  <target name="copy-struts.jar" if="struts.present">
+    <copy todir="${webapps.build}/${webapp.name}/WEB-INF/lib" file="${struts-core.jar}"/>
+    <copy todir="${webapps.build}/${webapp.name}/WEB-INF/lib" file="${struts-taglib.jar}" />
+  </target>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${webapps.build}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF/classes"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF/lib"/>
+  </target>
+
+
+  <!-- ================ BUILD: Copy Static Files ========================== -->
+  <target name="build-static" depends="flags,flags.display,build-prepare,copy-struts.jar">
+    <copy todir="${webapps.build}/${webapp.name}/WEB-INF/lib" file="${commons-beanutils.jar}"/>
+    <copy tofile="${webapps.build}/${webapp.name}/WEB-INF/lib/commons-collections.jar" 
+    	file="${commons-collections.jar}"/>
+    <copy todir="${webapps.build}/${webapp.name}/WEB-INF/lib" file="${commons-digester.jar}"/>
+    <copy todir="${webapps.build}/${webapp.name}">
+      <fileset dir=".">
+        <exclude name="build.*"/>
+        <exclude name="**/*.java"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <target name="build-main" depends="build-static" if="compile.admin">
+  
+    <javac   srcdir="WEB-INF/classes"
+             destdir="${webapps.build}/${webapp.name}/WEB-INF/classes"
+             debug="${compile.debug}" deprecation="${compile.deprecation}"
+             optimize="${compile.optimize}"
+             excludes="**/CVS/**">
+      <classpath refid="admin.classpath" />
+    </javac>
+
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean and build admin webapp"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${webapps.build}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Directories =================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${webapps.dist}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Distribution Files ============ -->
+  <target name="dist" depends="build-main,dist-prepare"
+   description="Create admin webapp binary distribution">
+      <jar   jarfile="${webapps.dist}/${webapp.name}.war"
+             basedir="${webapps.build}/${webapp.name}" includes="**"/>
+  </target>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <deltree dir="${dist.dir}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
diff --git a/container/webapps/admin/buttons.jsp b/container/webapps/admin/buttons.jsp
new file mode 100644
index 0000000..6a0f5e8
--- /dev/null
+++ b/container/webapps/admin/buttons.jsp
@@ -0,0 +1,16 @@
+   <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td colspan="2" align="right" nowrap>
+        <html:submit styleClass="button">
+           <bean:message key="button.save"/> 
+        </html:submit>          
+        &nbsp;
+        <html:reset styleClass="button">
+            <bean:message key="button.cancel"/> 
+        </html:reset> 
+      </td>
+    </tr>
+</table>
\ No newline at end of file
diff --git a/container/webapps/admin/connector/connector.jsp b/container/webapps/admin/connector/connector.jsp
new file mode 100644
index 0000000..9efcc8e
--- /dev/null
+++ b/container/webapps/admin/connector/connector.jsp
@@ -0,0 +1,439 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveConnector">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="connectorForm" property="objectName"/>
+  <html:hidden property="connectorName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="connectorType"/>
+  <html:hidden property="serviceName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+          <logic:equal name="connectorForm" property="adminAction" value="Create">
+            <bean:message key="actions.connectors.create"/>
+          </logic:equal>
+          <logic:equal name="connectorForm" property="adminAction" value="Edit">
+           <bean:write name="connectorForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Connector Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="connectorForm" property="adminAction" value="Create">
+            <logic:notEqual name="connectorForm" property="portText"
+                            value='<%= Integer.toString(request.getServerPort()) %>'>
+            <controls:action url='<%= "/DeleteConnector.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+            <bean:message key="actions.connectors.delete"/>
+            </controls:action>
+            </logic:notEqual>
+            </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>General</controls:label>
+            <controls:data>&nbsp;</controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="connectorType">
+            <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="connectorForm" property="adminAction" value="Create">
+                    <html:select property="connectorType" onchange="IA_jumpMenu('self',this)" styleId="connectorType">
+                     <bean:define id="connectorTypeVals" name="connectorForm" property="connectorTypeVals"/>
+                     <html:options collection="connectorTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="connectorForm" property="adminAction" value="Edit">
+                  <bean:write name="connectorForm" property="connectorType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+    <%-- do not show scheme while creating a new connector --%>
+    <logic:notEqual name="connectorForm" property="adminAction" value="Create">
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text">
+            <controls:label><bean:message key="connector.scheme"/>:</controls:label>
+            <controls:data>
+              <bean:write name="connectorForm" property="scheme" scope="session"/>
+            </controls:data>
+        </controls:row>
+     </logic:notEqual>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="enableDNS">
+            <controls:label><bean:message key="connector.enable.dns"/>:</controls:label>
+            <controls:data>
+                <html:select property="enableLookups" styleId="enableDNS">
+                     <bean:define id="booleanVals" name="connectorForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="uriencoding">
+            <controls:label><bean:message key="connector.uriencoding"/>:</controls:label>
+            <controls:data>
+               <html:text property="URIEncodingText" size="30" styleId="uriencoding"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="usebodyencoding">
+            <controls:label><bean:message key="connector.useBodyEncodingForURI"/>:</controls:label>
+            <controls:data>
+                <html:select property="useBodyEncodingForURIText" styleId="usebodyencoding">
+                     <bean:define id="booleanVals" name="connectorForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="allowTrace">
+            <controls:label><bean:message key="connector.allowTrace"/>:</controls:label>
+            <controls:data>
+                <html:select property="allowTraceText" styleId="allowTrace">
+                     <bean:define id="booleanVals" name="connectorForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <%-- Input only allowed on create transaction --%>
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="address">
+            <controls:label><bean:message key="connector.address.ip"/>:</controls:label>
+            <controls:data>
+             <logic:equal name="connectorForm" property="adminAction" value="Create">
+               <html:text property="address" size="20" styleId="address"/>
+             </logic:equal>
+             <logic:equal name="connectorForm" property="adminAction" value="Edit">
+               &nbsp;<bean:write name="connectorForm" property="address"/>
+               <html:hidden property="address"/>
+             </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="secure">
+            <controls:label><bean:message key="connector.secure"/>:</controls:label>
+            <controls:data>
+                <html:select property="secure" styleId="secure">
+                     <bean:define id="booleanVals" name="connectorForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <%--controls:row header="true" labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>Processors</controls:label>
+            <controls:data>&nbsp;</controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="minProcessor">
+            <controls:label><bean:message key="connector.min"/>:</controls:label>
+            <controls:data>
+               <html:text property="minProcessorsText" size="5" styleId="minProcessor"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="connectorMax">
+            <controls:label><bean:message key="connector.max"/>:</controls:label>
+            <controls:data>
+               <html:text property="maxProcessorsText" size="5" styleId="connectorMax"/>
+            </controls:data>
+        </controls:row--%>
+
+<%-- The following properties are supported only for Coyote HTTP/S 1.1 Connectors --%>
+
+     <logic:notEqual name="connectorForm" property="connectorType" scope="session"
+                  value="AJP">
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="acceptCount">
+            <controls:label><bean:message key="connector.accept.count"/>:</controls:label>
+            <controls:data>
+              <html:text property="acceptCountText" size="5" maxlength="5" styleId="acceptCount"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="compression">
+            <controls:label><bean:message key="connector.compression"/>:</controls:label>
+            <controls:data>
+               <html:text property="compression" size="10" styleId="compression"/>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="linger">
+            <controls:label><bean:message key="connector.connection.linger"/><br>
+                (<bean:message key="connector.milliseconds"/>) :</controls:label>
+            <controls:data>
+               <html:text property="connLingerText" size="10" styleId="linger"/>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="timeout">
+            <controls:label><bean:message key="connector.connection.timeout"/><br>
+                (<bean:message key="connector.milliseconds"/>) :</controls:label>
+            <controls:data>
+               <html:text property="connTimeOutText" size="10" styleId="timeout"/>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="upload">
+            <controls:label><bean:message key="connector.connection.uploadTimeout"/><br>
+                (<bean:message key="connector.milliseconds"/>) :</controls:label>
+            <controls:data>
+               <html:text property="connUploadTimeOutText" size="10" styleId="upload"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="buffersize">
+            <controls:label><bean:message key="connector.default.buffer"/>:</controls:label>
+            <controls:data>
+               <html:text property="bufferSizeText" size="5" styleId="buffersize"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="disableUpload">
+            <controls:label><bean:message key="connector.connection.disableUploadTimeout"/>:</controls:label>
+            <controls:data>
+                <html:select property="disableUploadTimeout" styleId="disableUpload">
+                     <bean:define id="booleanVals" name="connectorForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="maxkeepalive">
+            <controls:label><bean:message key="connector.maxkeepalive"/>:</controls:label>
+            <controls:data>
+              <html:text property="maxKeepAliveText" size="5" maxlength="5" styleId="maxkeepalive"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="maxspare">
+            <controls:label><bean:message key="connector.maxspare"/>:</controls:label>
+            <controls:data>
+              <html:text property="maxSpare" size="5" maxlength="5" styleId="maxspare"/>
+            </controls:data>
+        </controls:row>
+	
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="maxthreads">
+            <controls:label><bean:message key="connector.maxthreads"/>:</controls:label>
+            <controls:data>
+              <html:text property="maxThreads" size="5" maxlength="5" styleId="maxthreads"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="minspare">
+            <controls:label><bean:message key="connector.minspare"/>:</controls:label>
+            <controls:data>
+              <html:text property="minSpare" size="5" maxlength="5" styleId="minspare"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="threadpriority">
+            <controls:label><bean:message key="connector.threadpriority"/>:</controls:label>
+            <controls:data>
+              <html:text property="threadPriority" size="5" maxlength="5" styleId="threadpriority"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="tcpNoDelay">
+            <controls:label><bean:message key="connector.tcpNoDelay"/>:</controls:label>
+            <controls:data>
+                <html:select property="tcpNoDelay" styleId="tcpNoDelay">
+                     <bean:define id="booleanVals" name="connectorForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="xpoweredby">
+            <controls:label><bean:message key="connector.xpoweredby"/>:</controls:label>
+            <controls:data>
+                <html:select property="xpoweredBy" styleId="xpoweredby">
+                     <bean:define id="booleanVals" name="connectorForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+     </logic:notEqual>
+
+        <controls:row header="true" labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>Ports</controls:label>
+            <controls:data>&nbsp;</controls:data>
+        </controls:row>
+
+        <%-- Input only allowed on create transaction --%>
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="portnumber">
+            <controls:label><bean:message key="server.portnumber"/>:</controls:label>
+            <controls:data>
+             <logic:equal name="connectorForm" property="adminAction" value="Create">
+               <html:text property="portText" size="5" styleId="portnumer"/>
+             </logic:equal>
+             <logic:equal name="connectorForm" property="adminAction" value="Edit">
+               <bean:write name="connectorForm" property="portText"/>
+               <html:hidden property="portText"/>
+             </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="redirectport">
+            <controls:label><bean:message key="connector.redirect.portnumber"/>:</controls:label>
+            <controls:data>
+               <html:text property="redirectPortText" size="5" styleId="redirectport"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row header="true" labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>Proxy</controls:label>
+            <controls:data>&nbsp;</controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="proxyName">
+            <controls:label><bean:message key="connector.proxy.name"/>:</controls:label>
+            <controls:data>
+               <html:text property="proxyName" size="30" styleId="proxyName"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="portNumber">
+            <controls:label><bean:message key="connector.proxy.portnumber"/>:</controls:label>
+            <controls:data>
+                <html:text property="proxyPortText" size="5" styleId="portNumber"/>
+            </controls:data>
+        </controls:row>
+
+<%-- The following properties are supported only on HTTPS Connector --%>
+     <logic:equal name="connectorForm" property="scheme" scope="session"
+                  value="https">
+        <br>
+        <controls:row header="true" labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>Factory Properties:</controls:label>
+            <controls:data>&nbsp;</controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="algorithm">
+            <controls:label><bean:message key="connector.algorithm"/>:</controls:label>
+            <controls:data>
+               <html:text property="algorithm" size="10" styleId="algorithm"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="ciphers">
+            <controls:label><bean:message key="connector.ciphers"/>:</controls:label>
+            <controls:data>
+               <html:text property="ciphers" size="10" styleId="ciphers"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="clientauth">
+            <controls:label><bean:message key="connector.client.auth"/>:</controls:label>
+            <controls:data>
+                <html:select property="clientAuthentication" styleId="clientauth">
+                     <bean:define id="clientAuthVals" name="connectorForm" property="clientAuthVals"/>
+                     <html:options collection="clientAuthVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <%-- Input allowed only on create --%>
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="keystore">
+            <controls:label><bean:message key="connector.keystore.filename"/>:</controls:label>
+            <controls:data>
+            <logic:equal name="connectorForm" property="adminAction" value="Create">
+                <html:text property="keyStoreFileName" size="30" styleId="keystore"/>
+             </logic:equal>
+             <logic:equal name="connectorForm" property="adminAction" value="Edit">
+               <bean:write name="connectorForm" property="keyStoreFileName"/>
+             </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <%-- input password allowed only while creating connector --%>
+        <logic:equal name="connectorForm" property="adminAction" value="Create">
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="password">
+            <controls:label><bean:message key="connector.keystore.password"/>:</controls:label>
+            <controls:data>
+                <html:password property="keyStorePassword" size="30" styleId="password"/>
+                <%--
+                <logic:equal name="connectorForm" property="adminAction" value="Edit">
+                   <bean:write name="connectorForm" property="keyStorePassword"/>
+                </logic:equal>
+                --%>
+            </controls:data>
+        </controls:row>
+        </logic:equal>
+
+        <%-- Input allowed only on create --%>
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="keytype">
+            <controls:label><bean:message key="connector.keystore.type"/>:</controls:label>
+            <controls:data>
+            <logic:equal name="connectorForm" property="adminAction" value="Create">
+                <html:text property="keyStoreType" size="30" styleId="keytype"/>
+             </logic:equal>
+             <logic:equal name="connectorForm" property="adminAction" value="Edit">
+               <bean:write name="connectorForm" property="keyStoreType"/>
+             </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="sslProtocol">
+            <controls:label><bean:message key="connector.sslProtocol"/>:</controls:label>
+            <controls:data>
+               <html:text property="sslProtocol" size="10" styleId="sslProtocol"/>
+            </controls:data>
+        </controls:row>
+
+    </logic:equal>
+   </controls:table>
+
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/connector/connectors.jsp b/container/webapps/admin/connector/connectors.jsp
new file mode 100644
index 0000000..b8702ba
--- /dev/null
+++ b/container/webapps/admin/connector/connectors.jsp
@@ -0,0 +1,99 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="post" action="/DeleteConnectors">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="actions.connectors.delete"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+            <controls:actions label="Connector Actions">
+              <controls:action selected="true">
+                ----<bean:message key="actions.available.actions"/>----
+              </controls:action>
+              <controls:action>
+                ---------------------------------
+              </controls:action>
+            </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+  <br>
+
+  <%-- Connectors List --%>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1"
+         width="100%">
+    <tr><td>
+
+      <table class="front-table" border="1"
+       cellspacing="0" cellpadding="0" width="100%">
+
+        <tr class="header-row">
+          <td><div align="left" class="table-header-text">
+            <bean:message key="actions.delete"/>
+          </div></td>
+          <td><div align="left" class="table-header-text">
+            <bean:message key="host.name"/>
+          </div></td>
+        </tr>
+
+        <logic:iterate name="connectorsList" id="connector">
+          <tr class="line-row">
+            <td><div align="left" class="table-normal-text">&nbsp;
+            <%-- the connector the admin app is running on cannot be deleted
+                 through the tool --%>
+              <logic:match  name="connector" value='<%= Integer.toString(request.getServerPort()) %>'>
+                <font color='red'>*</font>
+              </logic:match>
+            <logic:notMatch name="connector" value='<%= Integer.toString(request.getServerPort()) %>'>
+              <label for="connectors"></label>
+              <html:multibox property="connectors"
+                                value="<%= connector.toString() %>" styleId="connectors"/>
+            </logic:notMatch>
+            </div></td>
+            <td><div align="left" class="table-normal-text">&nbsp;
+              <html:link page='<%= "/EditConnector.do?select=" +
+                         java.net.URLEncoder.encode(connector.toString(),"UTF-8") %>'>
+                Connector (<controls:attribute name="connector" attribute="port"/>)
+              </html:link>
+            </div></td>
+          </tr>
+        </logic:iterate>
+
+      </table>
+
+    </td></tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+
+  <br>
+</html:form>
+
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/context/context.jsp b/container/webapps/admin/context/context.jsp
new file mode 100644
index 0000000..19dfd73
--- /dev/null
+++ b/container/webapps/admin/context/context.jsp
@@ -0,0 +1,339 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveContext">
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="contextForm" property="objectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="loaderObjectName"/>
+  <html:hidden property="managerObjectName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+          <logic:equal name="contextForm" property="adminAction" value="Create">
+            <bean:message key="actions.contexts.create"/>
+          </logic:equal>
+          <logic:equal name="contextForm" property="adminAction" value="Edit">
+            <bean:write name="contextForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Context Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action disabled="true"> --------------------------------- </controls:action>
+            <logic:notEqual name="contextForm" property="adminAction" value="Create">
+            <%-- cannot delete or add the realm of the context of the admin app --%>
+            <logic:notEqual name="contextForm" property="path"
+                            value='<%= request.getContextPath() %>'>
+            <controls:action url='<%= "/AddRealm.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.create"/>
+            </controls:action>
+            <controls:action url='<%= "/DeleteRealm.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.deletes"/>
+            </controls:action>
+            </logic:notEqual>
+            <controls:action disabled="true">  -------------------------------------  </controls:action>
+            <controls:action url='<%= "/AddValve.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+               <bean:message key="actions.valves.create"/>
+            </controls:action>
+            <controls:action url='<%= "/DeleteValve.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+               <bean:message key="actions.valves.deletes"/>
+            </controls:action>
+            <%-- cannot delete the context of the admin app  from the tool --%>
+            <logic:notEqual name="contextForm" property="path"
+                            value='<%= request.getContextPath() %>'>
+            <controls:action disabled="true">  -------------------------------------  </controls:action>
+            <controls:action url='<%= "/DeleteContext.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.contexts.delete"/>
+            </controls:action>
+            </logic:notEqual>
+            </logic:notEqual>
+        </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+ <%-- Context Properties table --%>
+
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td>  <div class="table-title-text">
+            <bean:message key="context.properties"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="cookies">
+            <controls:label><bean:message key="context.cookies"/>:</controls:label>
+            <controls:data>
+                <html:select property="cookies" styleId="cookies">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="crossContext">
+            <controls:label><bean:message key="context.cross.context"/>:</controls:label>
+            <controls:data>
+                <html:select property="crossContext" styleId="crossContext">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+<%-- input only allowed on create transaction --%>
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="docbase">
+            <controls:label><bean:message key="context.docBase"/>:</controls:label>
+            <controls:data>
+              <logic:equal name="contextForm" property="adminAction" value="Create">
+               <html:text property="docBase" size="30" styleId="docbase"/>
+              </logic:equal>
+              <logic:equal name="contextForm" property="adminAction" value="Edit">
+               <bean:write name="contextForm" property="docBase"/>
+               <html:hidden property="docBase"/>
+              </logic:equal>
+            </controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="override">
+            <controls:label><bean:message key="context.override"/>:</controls:label>
+            <controls:data>
+                <html:select property="override" styleId="override">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="privileged">
+            <controls:label><bean:message key="context.privileged"/>:</controls:label>
+            <controls:data>
+                <html:select property="privileged" styleId="privileged">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+<%-- input only allowed on create transaction --%>
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="path">
+            <controls:label><bean:message key="context.path"/>:</controls:label>
+            <controls:data>
+             <logic:equal name="contextForm" property="adminAction" value="Create">
+               <html:text property="path" size="30" styleId="path"/>
+             </logic:equal>
+             <logic:equal name="contextForm" property="adminAction" value="Edit">
+               <bean:write name="contextForm" property="path"/>
+               <html:hidden property="path"/>
+             </logic:equal>
+            </controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="reloadable">
+            <controls:label><bean:message key="context.reloadable"/>:</controls:label>
+            <controls:data>
+                <html:select property="reloadable" styleId="reloadable">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="swallowOutput">
+            <controls:label><bean:message key="context.swallowOutput"/>:</controls:label>
+            <controls:data>
+                <html:select property="swallowOutput" styleId="swallowOutput">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="usernaming">
+            <controls:label><bean:message key="context.usenaming"/>:</controls:label>
+            <controls:data>
+                <html:select property="useNaming" styleId="usernaming">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="antiJarLocking">
+            <controls:label><bean:message key="context.antiJarLocking"/>:</controls:label>
+            <controls:data>
+                <html:select property="antiJarLocking" styleId="antiJarLocking">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="antiResourceLocking">
+            <controls:label><bean:message key="context.antiResourceLocking"/>:</controls:label>
+            <controls:data>
+                <html:select property="antiResourceLocking" styleId="antiResourceLocking">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+<%-- input only allowed on create transaction >
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="workdir">
+            <controls:label><bean:message key="context.workdir"/>:</controls:label>
+            <controls:data>
+             <logic:equal name="contextForm" property="adminAction" value="Create">
+               <html:text property="workDir" size="30" styleId="workdir"/>
+             </logic:equal>
+             <logic:equal name="contextForm" property="adminAction" value="Edit">
+               <bean:write name="contextForm" property="workDir"/>
+               <html:hidden property="workDir"/>
+             </logic:equal>
+            </controls:data>
+        </controls:row--%>
+   </controls:table>
+    </td>
+  </tr>
+</table>
+
+<br>
+
+<%-- Loader Properties table --%>
+
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td>  <div class="table-title-text">
+            <bean:message key="context.loader.properties"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+        <%--controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="checkInterval">
+            <controls:label><bean:message key="context.checkInterval"/>:</controls:label>
+            <controls:data>
+                <html:text property="ldrCheckInterval" size="5" styleId="checkInterval"/>
+            </controls:data>
+        </controls:row--%>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="reloadable">
+            <controls:label><bean:message key="context.reloadable"/>:</controls:label>
+            <controls:data>
+                <html:select property="ldrReloadable" styleId="reloadable">
+                     <bean:define id="booleanVals" name="contextForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+   </controls:table>
+    </td>
+  </tr>
+</table>
+
+<BR>
+<%-- Session Manager Properties table --%>
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td>  <div class="table-title-text">
+            <bean:message key="context.sessionmgr.properties"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+        <%--controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="checkInterval">
+            <controls:label><bean:message key="context.checkInterval"/>:</controls:label>
+            <controls:data>
+                <html:text property="mgrCheckInterval" size="5" styleId="checkInterval"/>
+            </controls:data>
+        </controls:row--%>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="sessionId">
+            <controls:label><bean:message key="context.sessionId"/>:</controls:label>
+            <controls:data>
+               <html:textarea property="mgrSessionIDInit" cols="30" rows="2" styleId="sessionId"/>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="maxSessions">
+            <controls:label><bean:message key="context.max.sessions"/>:</controls:label>
+            <controls:data>
+               <html:text property="mgrMaxSessions" size="5" styleId="maxSessions"/>
+            </controls:data>
+        </controls:row>
+   </controls:table>
+    </td>
+  </tr>
+</table>
+
+    <%@ include file="../buttons.jsp" %>
+
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/context/contexts.jsp b/container/webapps/admin/context/contexts.jsp
new file mode 100644
index 0000000..5ae5b06
--- /dev/null
+++ b/container/webapps/admin/context/contexts.jsp
@@ -0,0 +1,97 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="post" action="/DeleteContexts">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="actions.contexts.delete"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+            <controls:actions label="Context Actions">
+              <controls:action selected="true">
+                ----<bean:message key="actions.available.actions"/>----
+              </controls:action>
+              <controls:action>
+                ---------------------------------
+              </controls:action>
+            </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+  <br>
+
+  <%-- Contexts List --%>
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1"
+         width="100%">
+    <tr><td>
+
+      <table class="front-table" border="1"
+       cellspacing="0" cellpadding="0" width="100%">
+
+        <tr class="header-row">
+          <td><div align="left" class="table-header-text">
+            <bean:message key="actions.delete"/>
+          </div></td>
+          <td><div align="left" class="table-header-text">
+            <bean:message key="host.name"/>
+          </div></td>
+        </tr>
+    
+        <logic:iterate name="contextsList" id="context">
+          <tr class="line-row">
+            <td><div align="left" class="table-normal-text">&nbsp;
+              <%-- admin context cannot be deleted from the tool --%>
+              <logic:match name="context" value='<%= request.getContextPath()+"," %>'>
+                <font color='red'>*</font>
+              </logic:match>
+              <logic:notMatch name="context" value='<%= request.getContextPath()+"," %>'>
+              <label for="contexts"></label>
+              <html:multibox property="contexts"
+                                value="<%= context.toString() %>" styleId="contexts"/>
+              </logic:notMatch>
+            </div></td>
+            <td><div align="left" class="table-normal-text">&nbsp;
+              <html:link page='<%= "/EditContext.do?select=" +
+                         java.net.URLEncoder.encode(context.toString(),"UTF-8") %>'>
+                <controls:attribute name="context" attribute="path"/>
+              </html:link>
+            </div></td>
+          </tr>
+        </logic:iterate>
+
+      </table>
+
+    </td></tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+
+  <br>
+</html:form>
+
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/dumpRegistry.jsp b/container/webapps/admin/dumpRegistry.jsp
new file mode 100644
index 0000000..d2827e3
--- /dev/null
+++ b/container/webapps/admin/dumpRegistry.jsp
@@ -0,0 +1,48 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<!-- Standard Content -->
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+
+<body bgcolor="white">
+
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" colspan="3">
+      Registered Managed Beans
+    </th>
+  </tr>
+
+  <tr>
+    <th align="center">Name</th>
+    <th align="center">Group</th>
+    <th align="center">Description</th>
+  </tr>
+
+  <logic:iterate id="bean" name="beans">
+    <tr>
+      <td><bean:write name="bean" property="name"/></td>
+      <td><bean:write name="bean" property="group"/></td>
+      <td><bean:write name="bean" property="description"/></td>
+    </tr>
+  </logic:iterate>
+
+</table>
+
+</body>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</html:html>
diff --git a/container/webapps/admin/dumpServer.jsp b/container/webapps/admin/dumpServer.jsp
new file mode 100644
index 0000000..5f1cc30
--- /dev/null
+++ b/container/webapps/admin/dumpServer.jsp
@@ -0,0 +1,44 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<!-- Standard Content -->
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+
+<body bgcolor="white">
+
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" colspan="1">
+      Registered MBean Names
+    </th>
+  </tr>
+
+  <tr>
+    <th align="center">Name</th>
+  </tr>
+
+  <logic:iterate id="name" name="names">
+    <tr>
+      <td><bean:write name="name"/></td>
+    </tr>
+  </logic:iterate>
+
+</table>
+
+</body>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</html:html>
diff --git a/container/webapps/admin/error.jsp b/container/webapps/admin/error.jsp
new file mode 100644
index 0000000..64e7e95
--- /dev/null
+++ b/container/webapps/admin/error.jsp
@@ -0,0 +1,37 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<!-- Standard Content -->
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+
+<body bgcolor="white" background="images/PaperTexture.gif">
+
+<center>
+
+<h2>
+  <bean:message key="error.login"/>
+  <br>
+  <bean:message key="error.tryagain"/>
+  <html:link page="/">
+    <bean:message key="error.here"/>
+  </html:link>
+</h2>
+
+</center>
+
+</body>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</html:html>
diff --git a/container/webapps/admin/footer.jsp b/container/webapps/admin/footer.jsp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/container/webapps/admin/footer.jsp
diff --git a/container/webapps/admin/frameset.jsp b/container/webapps/admin/frameset.jsp
new file mode 100644
index 0000000..21415fa
--- /dev/null
+++ b/container/webapps/admin/frameset.jsp
@@ -0,0 +1,31 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<% // Force the initialization of "action" servlet
+   getServletContext().getNamedDispatcher("action").include(request,response);
+%> 
+
+<html:html locale="true">
+
+<!-- Standard Content -->
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<frameset rows="117,685*" cols="*" frameborder="NO" border="3" framespacing="3">
+  <frame name="banner" src='<%= response.encodeURL("banner.jsp") %>' scrolling="no" title="commit and logout banner">
+  <frameset cols="300,*" frameborder="YES" border="2">
+    <frame name="tree" src='<%= response.encodeURL("setUpTree.do") %>' scrolling="auto" title="application navigation tree">
+    <frame name="content" src='<%= response.encodeURL("blank.jsp") %>' scrolling="auto" title="content editing">
+  </frameset>
+</frameset>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</html:html>
diff --git a/container/webapps/admin/header.jsp b/container/webapps/admin/header.jsp
new file mode 100644
index 0000000..3d51356
--- /dev/null
+++ b/container/webapps/admin/header.jsp
@@ -0,0 +1,11 @@
+<!--
+  Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+  reserved.
+-->
+
+<head>
+  <title><bean:message key="application.title"/></title>
+  <html:base/>
+  <link rel="stylesheet" type="text/css" href="tree-control-test.css">
+  <link rel="stylesheet" type="text/css" href="admin.css">
+</head>
diff --git a/container/webapps/admin/host/alias.jsp b/container/webapps/admin/host/alias.jsp
new file mode 100644
index 0000000..28e741f
--- /dev/null
+++ b/container/webapps/admin/host/alias.jsp
@@ -0,0 +1,104 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveAlias">
+
+  <html:hidden property="hostName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text" align="left">
+            <bean:message key="actions.alias.create"/>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<br>
+
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td> <div class="table-title-text">
+        <bean:message key="new.alias"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>
+                <bean:message key="service.property"/>
+            </controls:label>
+            <controls:data>
+                <bean:message key="service.value"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="aliasName">
+            <controls:label>
+                <bean:message key="host.alias.name"/>:
+            </controls:label>
+            <controls:data>
+              <html:text property="aliasName" size="24" maxlength="128" styleId="aliasName"/>
+            </controls:data>
+        </controls:row>
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+<br>
+
+<%-- Aliases List --%>
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td>
+        <div class="table-title-text">
+            <bean:message key="host.aliases"/>
+        </div>
+    </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr> <td>
+        <table class="front-table" border="1" cellspacing="0" cellpadding="0" width="100%">
+          <tr class="header-row">
+            <td width="27%">
+              <div class="table-header-text" align="left"><bean:message key="host.alias.name"/> </div>
+            </td> </tr>
+
+            <logic:iterate id="aliasVal" name="aliasForm" property="aliasVals">
+            <tr> <td width="27%" valign="top" colspan=2>
+                <div class="table-normal-text"> <%= aliasVal %> </div>
+            </td> </tr>
+            </logic:iterate>
+         </table>
+
+    </td> </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/host/aliases.jsp b/container/webapps/admin/host/aliases.jsp
new file mode 100644
index 0000000..1445d6d
--- /dev/null
+++ b/container/webapps/admin/host/aliases.jsp
@@ -0,0 +1,83 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/DeleteAliases">
+
+  <html:hidden property="hostName"/>
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="actions.alias.delete"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+            <controls:actions label="Alias Actions">
+              <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+              <controls:action> --------------------------------- </controls:action>
+            </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+  <br>
+
+  <%-- Aliases List --%>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+        <table class="front-table" border="1"
+         cellspacing="0" cellpadding="0" width="100%">
+          <tr class="header-row">
+            <td><div align="left" class="table-header-text">
+              <bean:message key="actions.delete"/>
+            </div></td>
+            <td><div align="left" class="table-header-text">
+              <bean:message key="host.alias.name"/>
+            </div></td>
+          </tr>
+
+        <logic:iterate name="aliasesList" id="alias">
+          <tr class="line-row">
+            <td><div align="left" class="table-normal-text">&nbsp;
+            <label for="aliases"></label>
+              <html:multibox property="aliases"
+                                value="<%= alias.toString() %>" styleId="aliases"/>
+            </div></td>
+            <td><div align="left" class="table-normal-text">&nbsp;
+                <%= alias.toString() %>
+            </div></td>
+          </tr>
+        </logic:iterate>
+        </table>
+      </td>
+    </tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+
+  <br>
+</html:form>
+
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/host/host.jsp b/container/webapps/admin/host/host.jsp
new file mode 100644
index 0000000..559304a
--- /dev/null
+++ b/container/webapps/admin/host/host.jsp
@@ -0,0 +1,277 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveHost">
+
+  <bean:define id="hostName" name="hostForm" property="hostName"/>
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="hostForm" property="objectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="serviceName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text" align="left">
+          <logic:equal name="hostForm" property="adminAction" value="Create">
+            <bean:message key="actions.hosts.create"/>
+          </logic:equal>
+          <logic:equal name="hostForm" property="adminAction" value="Edit">
+            <bean:write name="hostForm" property="nodeLabel"/>
+          </logic:equal>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+        <controls:actions label="Host Actions">
+            <controls:action selected="true"> -----<bean:message key="actions.available.actions"/>----- </controls:action>
+            <controls:action disabled="true"> ------------------------------------- </controls:action>
+            <logic:notEqual name="hostForm" property="adminAction" value="Create">
+            <controls:action url='<%= "/AddAlias.do?hostName=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.alias.create"/>
+            </controls:action>
+            <controls:action url='<%= "/DeleteAlias.do?hostName=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.alias.delete"/>
+            </controls:action>
+            <controls:action disabled="true"> ------------------------------------- </controls:action>
+            <controls:action url='<%= "/AddContext.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.contexts.create"/>
+            </controls:action>
+            <controls:action url='<%= "/DeleteContext.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.contexts.deletes"/>
+            </controls:action>
+            <controls:action disabled="true"> ------------------------------------- </controls:action>
+            <!--FIXME add/remove defaultcontext-->
+<%--
+            <!--controls:action url='<%= "/AddDefaultContext.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.defaultcontexts.create"/>
+            </controls:action-->
+            <!--controls:action url='<%= "/DeleteDefaultContext.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.defaultcontexts.deletes"/>
+            </controls:action-->                        
+--%>
+            <logic:notEqual name="hostName" value='<%= (String)request.getAttribute("adminAppHost") %>'>
+            <controls:action disabled="true">
+                -------------------------------------
+            </controls:action>
+            <controls:action url='<%= "/AddRealm.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.create"/>
+            </controls:action>
+            <controls:action url='<%= "/DeleteRealm.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.deletes"/>
+            </controls:action>
+            </logic:notEqual>
+            <controls:action disabled="true">
+                -------------------------------------
+            </controls:action>
+            <controls:action url='<%= "/AddValve.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.valves.create"/>
+            </controls:action>
+            <controls:action url='<%= "/DeleteValve.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.valves.deletes"/>
+            </controls:action>
+            <logic:notEqual name="hostName" value='<%= request.getServerName() %>'>
+            <controls:action disabled="true">
+                -------------------------------------
+            </controls:action>
+            <controls:action url='<%= "/DeleteHost.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.hosts.delete"/>
+            </controls:action>
+           </logic:notEqual>
+           </logic:notEqual>
+         </controls:actions>
+       </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+  <br>
+
+ <%-- Host Properties --%>
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td> <div class="table-title-text">
+        <bean:message key="host.properties"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>
+                <bean:message key="service.property"/>
+            </controls:label>
+            <controls:data>
+                <bean:message key="service.value"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="name">
+            <controls:label>
+                <bean:message key="host.name"/>:
+            </controls:label>
+            <controls:data>
+            <%-- input only allowed on create transaction --%>
+             <logic:equal name="hostForm" property="adminAction" value="Create">
+              <html:text property="hostName" size="50" maxlength="50" styleId="name"/>
+             </logic:equal>
+             <logic:equal name="hostForm" property="adminAction" value="Edit">
+              <bean:write name="hostForm" property="hostName"/>
+              <html:hidden property="hostName"/>
+             </logic:equal>
+            </controls:data>
+        </controls:row>
+
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="base">
+            <controls:label><bean:message key="host.base"/>:</controls:label>
+            <controls:data>
+             <logic:equal name="hostForm" property="adminAction" value="Create">
+              <html:text property="appBase" size="24" styleId="base"/>
+             </logic:equal>
+             <logic:equal name="hostForm" property="adminAction" value="Edit">
+              <bean:write name="hostForm" property="appBase"/>
+              <html:hidden property="appBase"/>
+             </logic:equal>
+            </controls:data>
+        </controls:row>
+        
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="autodeploy">
+            <controls:label><bean:message key="host.autoDeploy"/>:</controls:label>
+            <controls:data>
+               <html:select property="autoDeploy" styleId="autodeploy">
+                     <bean:define id="booleanVals" name="hostForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+        
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="deployOnStartup">
+            <controls:label><bean:message key="host.deployOnStartup"/>:</controls:label>
+            <controls:data>
+               <html:select property="deployOnStartup" styleId="deployOnStartup">
+                     <bean:define id="booleanVals" name="hostForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="deployxml">
+            <controls:label><bean:message key="host.deployXML"/>:</controls:label>
+            <controls:data>
+               <html:select property="deployXML" styleId="deployxml">
+                     <bean:define id="booleanVals" name="hostForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+        
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="wars">
+            <controls:label><bean:message key="host.wars"/>:</controls:label>
+            <controls:data>
+               <html:select property="unpackWARs" styleId="wars">
+                     <bean:define id="booleanVals" name="hostForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="xmlnamespace">
+            <controls:label><bean:message key="host.xmlNamespaceAware"/>:</controls:label>
+            <controls:data>
+               <html:select property="xmlNamespaceAware" styleId="xmlnamespace">
+                     <bean:define id="booleanVals" name="hostForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="xmlvalidation">
+            <controls:label><bean:message key="host.xmlValidation"/>:</controls:label>
+            <controls:data>
+               <html:select property="xmlValidation" styleId="xmlvalidation">
+                     <bean:define id="booleanVals" name="hostForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+      </controls:table>
+
+      </td>
+    </tr>
+  </table>
+
+<br>
+<br>
+
+<%-- Aliases List --%>
+ <logic:notEqual name="hostForm" property="adminAction" value="Create">
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td>
+        <div class="table-title-text">
+            <bean:message key="host.aliases"/>
+        </div>
+    </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr> <td>
+        <table class="front-table" border="1" cellspacing="0" cellpadding="0" width="100%">
+          <tr class="header-row">
+            <td width="27%">
+              <div class="table-header-text" align="left"><bean:message key="host.alias.name"/> </div>
+            </td> </tr>
+
+            <logic:iterate id="aliasVal" name="hostForm" property="aliasVals">
+            <tr> <td width="27%" valign="top" colspan=2>
+                <div class="table-normal-text"> <%= aliasVal %> </div>
+            </td> </tr>
+            </logic:iterate>
+         </table>
+
+    </td> </tr>
+  </table>
+ </logic:notEqual>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/host/hosts.jsp b/container/webapps/admin/host/hosts.jsp
new file mode 100644
index 0000000..9ef40b5
--- /dev/null
+++ b/container/webapps/admin/host/hosts.jsp
@@ -0,0 +1,93 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/DeleteHosts">
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="actions.hosts.delete"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+            <controls:actions label="Host Actions">
+              <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+              <controls:action> --------------------------------- </controls:action>
+            </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+  <br>
+
+  <%-- Hosts List --%>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+        <table class="front-table" border="1"
+         cellspacing="0" cellpadding="0" width="100%">
+          <tr class="header-row">
+            <td><div align="left" class="table-header-text">
+              <bean:message key="actions.delete"/>
+            </div></td>
+            <td><div align="left" class="table-header-text">
+              <bean:message key="host.name"/>
+            </div></td>
+          </tr>
+
+        <logic:iterate name="hostsList" id="host">
+          <tr class="line-row">
+            <td><div align="left" class="table-normal-text">&nbsp;
+            
+             <logic:match name="host"
+                        value='<%= (String)request.getAttribute("adminAppHost") %>'>
+             <font color='red'>*</font>
+             </logic:match>
+             <logic:notMatch name="host"
+                        value='<%= (String)request.getAttribute("adminAppHost") %>'>
+              <label for="hosts"></label>          
+              <html:multibox property="hosts"
+                                value="<%= host.toString() %>" styleId="hosts"/>
+              </logic:notMatch>
+              
+            </div></td>
+            <td><div align="left" class="table-normal-text">&nbsp;
+              <html:link page='<%= "/EditHost.do?select=" +
+                         java.net.URLEncoder.encode(host.toString(),"UTF-8") %>'>
+                <controls:attribute name="host" attribute="name"/>
+              </html:link>
+            </div></td>
+          </tr>
+        </logic:iterate>
+        </table>
+      </td>
+    </tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+
+  <br>
+</html:form>
+
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/images/BlueTile.gif b/container/webapps/admin/images/BlueTile.gif
new file mode 100644
index 0000000..dacdfc1
--- /dev/null
+++ b/container/webapps/admin/images/BlueTile.gif
Binary files differ
diff --git a/container/webapps/admin/images/Connector.gif b/container/webapps/admin/images/Connector.gif
new file mode 100644
index 0000000..00b31cc
--- /dev/null
+++ b/container/webapps/admin/images/Connector.gif
Binary files differ
diff --git a/container/webapps/admin/images/Context.gif b/container/webapps/admin/images/Context.gif
new file mode 100644
index 0000000..332356b
--- /dev/null
+++ b/container/webapps/admin/images/Context.gif
Binary files differ
diff --git a/container/webapps/admin/images/Datasource.gif b/container/webapps/admin/images/Datasource.gif
new file mode 100644
index 0000000..20d6751
--- /dev/null
+++ b/container/webapps/admin/images/Datasource.gif
Binary files differ
diff --git a/container/webapps/admin/images/DefaultContext.gif b/container/webapps/admin/images/DefaultContext.gif
new file mode 100644
index 0000000..4ae60ff
--- /dev/null
+++ b/container/webapps/admin/images/DefaultContext.gif
Binary files differ
diff --git a/container/webapps/admin/images/EnvironmentEntries.gif b/container/webapps/admin/images/EnvironmentEntries.gif
new file mode 100644
index 0000000..01c4b0d
--- /dev/null
+++ b/container/webapps/admin/images/EnvironmentEntries.gif
Binary files differ
diff --git a/container/webapps/admin/images/Groups.gif b/container/webapps/admin/images/Groups.gif
new file mode 100644
index 0000000..d6a489e
--- /dev/null
+++ b/container/webapps/admin/images/Groups.gif
Binary files differ
diff --git a/container/webapps/admin/images/Host.gif b/container/webapps/admin/images/Host.gif
new file mode 100644
index 0000000..ab27301
--- /dev/null
+++ b/container/webapps/admin/images/Host.gif
Binary files differ
diff --git a/container/webapps/admin/images/Logger.gif b/container/webapps/admin/images/Logger.gif
new file mode 100644
index 0000000..ee15fe4
--- /dev/null
+++ b/container/webapps/admin/images/Logger.gif
Binary files differ
diff --git a/container/webapps/admin/images/Login.jpg b/container/webapps/admin/images/Login.jpg
new file mode 100644
index 0000000..60bdabd
--- /dev/null
+++ b/container/webapps/admin/images/Login.jpg
Binary files differ
diff --git a/container/webapps/admin/images/LoginBackgroundTile.gif b/container/webapps/admin/images/LoginBackgroundTile.gif
new file mode 100644
index 0000000..62f91f3
--- /dev/null
+++ b/container/webapps/admin/images/LoginBackgroundTile.gif
Binary files differ
diff --git a/container/webapps/admin/images/Mailsession.gif b/container/webapps/admin/images/Mailsession.gif
new file mode 100644
index 0000000..5c8a1bf
--- /dev/null
+++ b/container/webapps/admin/images/Mailsession.gif
Binary files differ
diff --git a/container/webapps/admin/images/PaperTexture.gif b/container/webapps/admin/images/PaperTexture.gif
new file mode 100644
index 0000000..0d24833
--- /dev/null
+++ b/container/webapps/admin/images/PaperTexture.gif
Binary files differ
diff --git a/container/webapps/admin/images/Realm.gif b/container/webapps/admin/images/Realm.gif
new file mode 100644
index 0000000..49684d6
--- /dev/null
+++ b/container/webapps/admin/images/Realm.gif
Binary files differ
diff --git a/container/webapps/admin/images/ResourceLink.gif b/container/webapps/admin/images/ResourceLink.gif
new file mode 100644
index 0000000..51463f8
--- /dev/null
+++ b/container/webapps/admin/images/ResourceLink.gif
Binary files differ
diff --git a/container/webapps/admin/images/Roles.gif b/container/webapps/admin/images/Roles.gif
new file mode 100644
index 0000000..3990969
--- /dev/null
+++ b/container/webapps/admin/images/Roles.gif
Binary files differ
diff --git a/container/webapps/admin/images/Server.gif b/container/webapps/admin/images/Server.gif
new file mode 100644
index 0000000..dbe2f2f
--- /dev/null
+++ b/container/webapps/admin/images/Server.gif
Binary files differ
diff --git a/container/webapps/admin/images/Service.gif b/container/webapps/admin/images/Service.gif
new file mode 100644
index 0000000..657cd3a
--- /dev/null
+++ b/container/webapps/admin/images/Service.gif
Binary files differ
diff --git a/container/webapps/admin/images/Thumbs.db b/container/webapps/admin/images/Thumbs.db
new file mode 100644
index 0000000..078d27b
--- /dev/null
+++ b/container/webapps/admin/images/Thumbs.db
Binary files differ
diff --git a/container/webapps/admin/images/TomcatBanner.jpg b/container/webapps/admin/images/TomcatBanner.jpg
new file mode 100644
index 0000000..b31ce1b
--- /dev/null
+++ b/container/webapps/admin/images/TomcatBanner.jpg
Binary files differ
diff --git a/container/webapps/admin/images/Users.gif b/container/webapps/admin/images/Users.gif
new file mode 100644
index 0000000..b0dafcf
--- /dev/null
+++ b/container/webapps/admin/images/Users.gif
Binary files differ
diff --git a/container/webapps/admin/images/Valve.gif b/container/webapps/admin/images/Valve.gif
new file mode 100644
index 0000000..cf2a68c
--- /dev/null
+++ b/container/webapps/admin/images/Valve.gif
Binary files differ
diff --git a/container/webapps/admin/images/folder_16_pad.gif b/container/webapps/admin/images/folder_16_pad.gif
new file mode 100644
index 0000000..67702a1
--- /dev/null
+++ b/container/webapps/admin/images/folder_16_pad.gif
Binary files differ
diff --git a/container/webapps/admin/images/handledownlast.gif b/container/webapps/admin/images/handledownlast.gif
new file mode 100644
index 0000000..7badb68
--- /dev/null
+++ b/container/webapps/admin/images/handledownlast.gif
Binary files differ
diff --git a/container/webapps/admin/images/handledownmiddle.gif b/container/webapps/admin/images/handledownmiddle.gif
new file mode 100644
index 0000000..5b3cfed
--- /dev/null
+++ b/container/webapps/admin/images/handledownmiddle.gif
Binary files differ
diff --git a/container/webapps/admin/images/handlerightlast.gif b/container/webapps/admin/images/handlerightlast.gif
new file mode 100644
index 0000000..e9d0a92
--- /dev/null
+++ b/container/webapps/admin/images/handlerightlast.gif
Binary files differ
diff --git a/container/webapps/admin/images/handlerightmiddle.gif b/container/webapps/admin/images/handlerightmiddle.gif
new file mode 100644
index 0000000..02eff2b
--- /dev/null
+++ b/container/webapps/admin/images/handlerightmiddle.gif
Binary files differ
diff --git a/container/webapps/admin/images/linelastnode.gif b/container/webapps/admin/images/linelastnode.gif
new file mode 100644
index 0000000..e0ff2f0
--- /dev/null
+++ b/container/webapps/admin/images/linelastnode.gif
Binary files differ
diff --git a/container/webapps/admin/images/linemiddlenode.gif b/container/webapps/admin/images/linemiddlenode.gif
new file mode 100644
index 0000000..009f29b
--- /dev/null
+++ b/container/webapps/admin/images/linemiddlenode.gif
Binary files differ
diff --git a/container/webapps/admin/images/linevertical.gif b/container/webapps/admin/images/linevertical.gif
new file mode 100644
index 0000000..fdec15b
--- /dev/null
+++ b/container/webapps/admin/images/linevertical.gif
Binary files differ
diff --git a/container/webapps/admin/index.jsp b/container/webapps/admin/index.jsp
new file mode 100644
index 0000000..8b8abc2
--- /dev/null
+++ b/container/webapps/admin/index.jsp
@@ -0,0 +1 @@
+<% response.sendRedirect("frameset.jsp"); %>
\ No newline at end of file
diff --git a/container/webapps/admin/login.jsp b/container/webapps/admin/login.jsp
new file mode 100644
index 0000000..7b48a4a
--- /dev/null
+++ b/container/webapps/admin/login.jsp
@@ -0,0 +1,106 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<% // Force the initialization of "action" servlet
+   getServletContext().getNamedDispatcher("action").include(request,response);
+%> 
+
+<html:html locale="true">
+
+<!-- Make sure window is not in a frame -->
+
+<script language="JavaScript" type="text/javascript">
+
+  <!--
+    if (window.self != window.top) {
+      window.open(".", "_top");
+    }
+  // -->
+
+</script>
+
+<!-- Standard Content -->
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+
+<body background="images/LoginBackgroundTile.gif">
+
+<center>
+
+<!-- Login -->
+
+<form method="POST" action='<%= response.encodeURL("j_security_check") %>'
+ name="loginForm">
+  <table border="0" cellspacing="5" background="images/LoginBackgroundTile.gif">
+
+    <tr>
+    <!-- banner -->
+     <td height="183">
+        <div align="center"><img src="images/Login.jpg" alt="Tomcat Web Server Administration Tool" width="490" height="228"></div>
+      </td>
+    </tr>
+
+    <!-- username password prompts fields layout -->
+    <tr>
+    <td background="images/LoginBackgroundTile.gif">
+     <table width="100%" border="0" cellspacing="2" cellpadding="5">
+     <tr>
+      <th align="right">
+        <font color="#FFFFFF"><label for="username"><bean:message key="prompt.username"/></label></font>
+      </th>
+      <td align="left">
+        <input type="text" name="j_username" size="16" id="username"/>
+      </td>
+    </tr>
+    <p>
+    <tr>
+      <th align="right">
+        <font color="#FFFFFF"><label for="password"><bean:message key="prompt.password"/></label></font>
+      </th>
+      <td align="left">
+        <input type="password" name="j_password" size="16" id="password"/>
+      </td>
+    </tr>
+
+    <tr>
+      <td width="50%" valign="top"> <div align="right"></div> </td>
+      <td width="55%" valign="top">&nbsp;</td>
+     </tr>
+
+    <!-- login reset buttons layout -->
+    <tr>
+       <td width="50%" valign="top">
+            <div align="right">
+               <input type="submit" value='<bean:message key="button.login"/>'>&nbsp;&nbsp;
+            </div>
+       </td>
+       <td width="55%" valign="top">
+          &nbsp;&nbsp;<input type="reset" value='<bean:message key="button.reset"/>'>
+       </td>
+     </tr>
+  </table>
+  <p> &nbsp;
+  </td>
+  </tr>
+ </table>
+</form>
+
+<script language="JavaScript" type="text/javascript">
+  <!--
+    document.forms["loginForm"].elements["j_username"].focus()
+  // -->
+</script>
+
+</body>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</html:html>
diff --git a/container/webapps/admin/realm/dataSourceRealm.jsp b/container/webapps/admin/realm/dataSourceRealm.jsp
new file mode 100644
index 0000000..843d930
--- /dev/null
+++ b/container/webapps/admin/realm/dataSourceRealm.jsp
@@ -0,0 +1,155 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveDataSourceRealm">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="dataSourceRealmForm" property="objectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="allowDeletion"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+         <logic:equal name="dataSourceRealmForm" property="adminAction" value="Create">
+            <bean:message key="actions.realms.create"/>
+          </logic:equal>
+          <logic:equal name="dataSourceRealmForm" property="adminAction" value="Edit">
+            <bean:write name="dataSourceRealmForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Realm Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="dataSourceRealmForm" property="adminAction" value="Create">
+            <logic:notEqual name="dataSourceRealmForm" property="allowDeletion" value="false">
+             <controls:action url='<%= "/DeleteRealm.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.delete"/>
+              </controls:action>
+               </logic:notEqual>
+             </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+            <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="dataSourceRealmForm" property="adminAction" value="Create">
+                    <html:select property="realmType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="realmTypeVals" name="dataSourceRealmForm" property="realmTypeVals"/>
+                     <html:options collection="realmTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="dataSourceRealmForm" property="adminAction" value="Edit">
+                  <bean:write name="dataSourceRealmForm" property="realmType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="driver">
+            <controls:label><bean:message key="realm.dataSourceName"/>:</controls:label>
+            <controls:data>
+              <html:text property="dataSourceName" size="30" styleId="dataSourceName"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="digest">
+            <controls:label><bean:message key="realm.digest"/>:</controls:label>
+            <controls:data>
+                <html:text property="digest" size="30" styleId="digest"/>
+            </controls:data>
+        </controls:row>
+        
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="localDataSource">
+            <controls:label><bean:message key="realm.localDataSource"/>:</controls:label>
+            <controls:data>
+               <html:select property="localDataSource" styleId="localDataSource">
+                     <bean:define id="booleanVals" name="dataSourceRealmForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="roleNameCol">
+            <controls:label><bean:message key="realm.roleNameCol"/>:</controls:label>
+            <controls:data>
+                <html:text property="roleNameCol" size="30" styleId="roleNameCol"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userCredCol">
+            <controls:label><bean:message key="realm.userCredCol"/>:</controls:label>
+            <controls:data>
+                <html:text property="userCredCol" size="30" styleId="userCredCol"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userNameCol">
+            <controls:label><bean:message key="realm.userNameCol"/>:</controls:label>
+            <controls:data>
+                <html:text property="userNameCol" size="30" styleId="userNameCol"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userRoleTable">
+            <controls:label><bean:message key="realm.userRoleTable"/>:</controls:label>
+            <controls:data>
+                <html:text property="userRoleTable" size="30" styleId="userRoleTable"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userTable">
+            <controls:label><bean:message key="realm.userTable"/>:</controls:label>
+            <controls:data>
+                <html:text property="userTable" size="30" styleId="userTable"/>
+            </controls:data>
+        </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/realm/jdbcRealm.jsp b/container/webapps/admin/realm/jdbcRealm.jsp
new file mode 100644
index 0000000..3920fe1
--- /dev/null
+++ b/container/webapps/admin/realm/jdbcRealm.jsp
@@ -0,0 +1,165 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveJDBCRealm">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="jdbcRealmForm" property="objectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="allowDeletion"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+         <logic:equal name="jdbcRealmForm" property="adminAction" value="Create">
+            <bean:message key="actions.realms.create"/>
+          </logic:equal>
+          <logic:equal name="jdbcRealmForm" property="adminAction" value="Edit">
+            <bean:write name="jdbcRealmForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Realm Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="jdbcRealmForm" property="adminAction" value="Create">
+            <logic:notEqual name="jdbcRealmForm" property="allowDeletion" value="false">
+             <controls:action url='<%= "/DeleteRealm.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.delete"/>
+              </controls:action>
+               </logic:notEqual>
+             </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+            <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="jdbcRealmForm" property="adminAction" value="Create">
+                    <html:select property="realmType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="realmTypeVals" name="jdbcRealmForm" property="realmTypeVals"/>
+                     <html:options collection="realmTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="jdbcRealmForm" property="adminAction" value="Edit">
+                  <bean:write name="jdbcRealmForm" property="realmType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="driver">
+            <controls:label><bean:message key="realm.driver"/>:</controls:label>
+            <controls:data>
+              <html:text property="driver" size="30" styleId="driver"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="passwd">
+            <controls:label><bean:message key="realm.passwd"/>:</controls:label>
+            <controls:data>
+                <html:text property="connectionPassword" size="30" styleId="passwd"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="url">
+            <controls:label><bean:message key="realm.url"/>:</controls:label>
+            <controls:data>
+                <html:text property="connectionURL" size="30" styleId="url"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="username">
+            <controls:label><bean:message key="realm.userName"/>:</controls:label>
+            <controls:data>
+                <html:text property="connectionName" size="30" styleId="username"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="digest">
+            <controls:label><bean:message key="realm.digest"/>:</controls:label>
+            <controls:data>
+                <html:text property="digest" size="30" styleId="digest"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="passwordCol">
+            <controls:label><bean:message key="realm.passwordCol"/>:</controls:label>
+            <controls:data>
+                <html:text property="passwordCol" size="30" styleId="passwordCol"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="roleNameCol">
+            <controls:label><bean:message key="realm.roleNameCol"/>:</controls:label>
+            <controls:data>
+                <html:text property="roleNameCol" size="30" styleId="roleNameCol"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userNameCol">
+            <controls:label><bean:message key="realm.userNameCol"/>:</controls:label>
+            <controls:data>
+                <html:text property="userNameCol" size="30" styleId="userNameCol"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userRoleTable">
+            <controls:label><bean:message key="realm.userRoleTable"/>:</controls:label>
+            <controls:data>
+                <html:text property="roleTable" size="30" styleId="userRoleTable"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userTable">
+            <controls:label><bean:message key="realm.userTable"/>:</controls:label>
+            <controls:data>
+                <html:text property="userTable" size="30" styleId="userTable"/>
+            </controls:data>
+        </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/realm/jndiRealm.jsp b/container/webapps/admin/realm/jndiRealm.jsp
new file mode 100644
index 0000000..85a18bf
--- /dev/null
+++ b/container/webapps/admin/realm/jndiRealm.jsp
@@ -0,0 +1,208 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveJNDIRealm">
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="jndiRealmForm" property="objectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="allowDeletion"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+         <logic:equal name="jndiRealmForm" property="adminAction" value="Create">
+            <bean:message key="actions.realms.create"/>
+          </logic:equal>
+          <logic:equal name="jndiRealmForm" property="adminAction" value="Edit">
+            <bean:write name="jndiRealmForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Realm Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="jndiRealmForm" property="adminAction" value="Create">
+                <logic:notEqual name="jndiRealmForm" property="allowDeletion" value="false">
+                <controls:action url='<%= "/DeleteRealm.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.delete"/>
+            </controls:action>
+           </logic:notEqual>
+        </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+            <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="jndiRealmForm" property="adminAction" value="Create">
+                    <html:select property="realmType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="realmTypeVals" name="jndiRealmForm" property="realmTypeVals"/>
+                     <html:options collection="realmTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="jndiRealmForm" property="adminAction" value="Edit">
+                  <bean:write name="jndiRealmForm" property="realmType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="connName">
+            <controls:label><bean:message key="realm.connName"/>:</controls:label>
+            <controls:data>
+              <html:text property="connectionName" size="30" styleId="connName"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="connPassword">
+            <controls:label><bean:message key="realm.connPassword"/>:</controls:label>
+            <controls:data>
+                <html:text property="connectionPassword" size="30" styleId="connPassword"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="connURL">
+            <controls:label><bean:message key="realm.connURL"/>:</controls:label>
+            <controls:data>
+                <html:text property="connectionURL" size="30" styleId="connURL"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="connFactory">
+            <controls:label><bean:message key="realm.connFactory"/>:</controls:label>
+            <controls:data>
+                <html:text property="contextFactory" size="30" styleId="connFactory"/>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="digest">
+            <controls:label><bean:message key="realm.digest"/>:</controls:label>
+            <controls:data>
+                <html:text property="digest" size="30" styleId="digest"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="roleBase">
+            <controls:label><bean:message key="realm.roleBase"/>:</controls:label>
+            <controls:data>
+                <html:text property="roleBase" size="30" styleId="roleBase"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="roleName">
+            <controls:label><bean:message key="realm.roleName"/>:</controls:label>
+            <controls:data>
+                <html:text property="roleName" size="30" styleId="roleName"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="pattern">
+            <controls:label><bean:message key="realm.pattern"/>:</controls:label>
+            <controls:data>
+                <html:text property="rolePattern" size="30" styleId="pattern"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="rolesubtree">
+            <controls:label><bean:message key="realm.role.subtree"/>:</controls:label>
+            <controls:data>
+             <html:select property="roleSubtree" styleId="rolesubtree">
+                     <bean:define id="searchVals" name="jndiRealmForm" property="searchVals"/>
+                     <html:options collection="searchVals" property="value"
+                        labelProperty="label"/>
+                </html:select>
+              </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userBase">
+            <controls:label><bean:message key="realm.userBase"/>:</controls:label>
+            <controls:data>
+                <html:text property="userBase" size="30" styleId="userBase"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="roleName">
+            <controls:label><bean:message key="realm.user.roleName"/>:</controls:label>
+            <controls:data>
+                <html:text property="userRoleName" size="30" styleId="roleName"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="usersubtree">
+            <controls:label><bean:message key="realm.user.subtree"/>:</controls:label>
+            <controls:data>
+             <html:select property="userSubtree" styleId="usersubtree">
+                     <bean:define id="searchVals" name="jndiRealmForm" property="searchVals"/>
+                     <html:options collection="searchVals" property="value"
+                        labelProperty="label"/>
+                </html:select>
+              </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userPassword">
+            <controls:label><bean:message key="realm.userPassword"/>:</controls:label>
+            <controls:data>
+                <html:text property="userPassword" size="30" styleId="userPassword"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userPattern">
+            <controls:label><bean:message key="realm.userPattern"/>:</controls:label>
+            <controls:data>
+                <html:text property="userPattern" size="30" styleId="userPattern"/>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="userSearch">
+           <controls:label><bean:message key="realm.userSearch"/>:</controls:label>
+           <controls:data>
+               <html:text property="userSearch" size="30" styleId="userSearch"/>
+           </controls:data>
+       </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/realm/memoryRealm.jsp b/container/webapps/admin/realm/memoryRealm.jsp
new file mode 100644
index 0000000..bcbf340
--- /dev/null
+++ b/container/webapps/admin/realm/memoryRealm.jsp
@@ -0,0 +1,103 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="GET" action="/SaveMemoryRealm">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="memoryRealmForm" property="objectName"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="allowDeletion"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+         <logic:equal name="memoryRealmForm" property="adminAction" value="Create">
+            <bean:message key="actions.realms.create"/>
+          </logic:equal>
+          <logic:equal name="memoryRealmForm" property="adminAction" value="Edit">
+            <bean:write name="memoryRealmForm" property="nodeLabel"/>
+          </logic:equal>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Label Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="memoryRealmForm" property="adminAction" value="Create">
+                <logic:notEqual name="memoryRealmForm" property="allowDeletion" value="false">
+                <controls:action url='<%= "/DeleteRealm.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.delete"/>
+                </controls:action>
+            </logic:notEqual>
+            </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+        <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+        <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="memoryRealmForm" property="adminAction" value="Create">
+                    <html:select property="realmType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="realmTypeVals" name="memoryRealmForm" property="realmTypeVals"/>
+                     <html:options collection="realmTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="memoryRealmForm" property="adminAction" value="Edit">
+                  <bean:write name="memoryRealmForm" property="realmType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="pathName">
+            <controls:label><bean:message key="realm.pathName"/>:</controls:label>
+            <controls:data>
+                <html:text property="pathName" size="25" styleId="pathName"/>
+            </controls:data>
+        </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/realm/realms.jsp b/container/webapps/admin/realm/realms.jsp
new file mode 100644
index 0000000..7f4913a
--- /dev/null
+++ b/container/webapps/admin/realm/realms.jsp
@@ -0,0 +1,92 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="post" action="/DeleteRealms">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="actions.realms.delete"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+            <controls:actions label="Realm Actions">
+              <controls:action selected="true">
+                ----<bean:message key="actions.available.actions"/>----
+              </controls:action>
+              <controls:action disabled="true">
+                ---------------------------------
+              </controls:action>
+            </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+  <br>
+
+  <%-- Realms List --%>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1"
+         width="100%">
+    <tr><td>
+
+      <table class="front-table" border="1"
+       cellspacing="0" cellpadding="0" width="100%">
+
+        <tr class="header-row">
+          <td><div align="left" class="table-header-text">
+            <bean:message key="actions.delete"/>
+          </div></td>
+          <td><div align="left" class="table-header-text">
+            <bean:message key="host.name"/>
+          </div></td>
+        </tr>
+
+        <logic:iterate name="realmsList" id="realm">
+          <tr class="line-row">
+            <td><div align="left" class="table-normal-text">&nbsp;
+            <label for="realms"></label>
+              <html:multibox property="realms"
+                                value="<%= realm.toString() %>" styleId="realms"/>
+            </div></td>
+            <td><div align="left" class="table-normal-text">&nbsp;
+              <html:link page='<%= "/EditRealm.do?select=" +
+                         java.net.URLEncoder.encode(realm.toString(),"UTF-8") %>'>
+                <controls:attribute name="realm" attribute="className"/>
+              </html:link>
+            </div></td>
+          </tr>
+        </logic:iterate>
+
+      </table>
+
+    </td></tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+
+  <br>
+</html:form>
+
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/realm/userDatabaseRealm.jsp b/container/webapps/admin/realm/userDatabaseRealm.jsp
new file mode 100644
index 0000000..f0869d4
--- /dev/null
+++ b/container/webapps/admin/realm/userDatabaseRealm.jsp
@@ -0,0 +1,109 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveUserDatabaseRealm">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="userDatabaseRealmForm" property="objectName"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="allowDeletion"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+         <logic:equal name="userDatabaseRealmForm" property="adminAction" value="Create">
+            <bean:message key="actions.realms.create"/>
+          </logic:equal>
+          <logic:equal name="userDatabaseRealmForm" property="adminAction" value="Edit">
+            <bean:write name="userDatabaseRealmForm" property="nodeLabel"/>
+          </logic:equal>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Realm Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="userDatabaseRealmForm" property="adminAction" value="Create">
+                <logic:notEqual name="userDatabaseRealmForm" property="allowDeletion" value="false">
+                <controls:action url='<%= "/DeleteRealm.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.delete"/>
+            </controls:action>
+           </logic:notEqual>
+           </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+        <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="userDatabaseRealmForm" property="adminAction" value="Create">
+                    <html:select property="realmType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="realmTypeVals" name="userDatabaseRealmForm" property="realmTypeVals"/>
+                     <html:options collection="realmTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="userDatabaseRealmForm" property="adminAction" value="Edit">
+                  <bean:write name="userDatabaseRealmForm" property="realmType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="resource">
+            <controls:label><bean:message key="realm.resource"/>:</controls:label>
+            <controls:data>
+                <logic:equal name="userDatabaseRealmForm" property="adminAction" value="Create">
+                    <html:text property="resource" size="25" maxlength="25" styleId="resource"/>
+                </logic:equal>
+                <logic:equal name="userDatabaseRealmForm" property="adminAction" value="Edit">
+                    <html:hidden property="resource"/>
+                    <bean:write name="userDatabaseRealmForm" property="resource" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/dataSource.jsp b/container/webapps/admin/resources/dataSource.jsp
new file mode 100644
index 0000000..b67e50c
--- /dev/null
+++ b/container/webapps/admin/resources/dataSource.jsp
@@ -0,0 +1,212 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/resources/saveDataSource">
+
+  <html:hidden property="objectName"/>
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="dataSourceForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="dataSourceForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="dataSourceForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="dataSourceForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text">
+          <bean:write name="dataSourceForm" property="nodeLabel"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+          <controls:actions label="Data Source Actions">
+            <controls:action selected="true">
+              ----<bean:message key="actions.available.actions"/>----
+            </controls:action>
+            <controls:action>
+              ---------------------------------
+            </controls:action>
+
+        <controls:action url='<%= "/resources/setUpDataSource.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") %>'>
+                <bean:message key="resources.actions.datasrc.create"/>
+            </controls:action>
+            <controls:action url='<%= "/resources/listDataSources.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") + "&forward=" +
+                            URLEncoder.encode("DataSources Delete List","UTF-8") %>'>
+                <bean:message key="resources.actions.datasrc.delete"/>
+            </controls:action>
+         </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+<br>
+
+  <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr><td><div class="table-title-text">
+        <bean:message key="resources.treeBuilder.datasources"/>
+    </div></td></tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+          <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="jndi">
+            <controls:label>
+              <bean:message key="resources.datasrc.jndi"/>:
+            </controls:label>
+            <controls:data>
+              <logic:present name="dataSourceForm" property="objectName">
+                <bean:write name="dataSourceForm" property="jndiName"/>
+                <html:hidden property="jndiName"/>
+              </logic:present>
+              <logic:notPresent name="dataSourceForm" property="objectName">
+                <html:text property="jndiName" size="35" maxlength="56" styleId="jndi"/>
+              </logic:notPresent>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="url">
+            <controls:label>
+              <bean:message key="resources.datasrc.url"/>:
+            </controls:label>
+            <controls:data>
+                <html:textarea property="url" cols="35" rows="2" styleId="url"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="jdbcclass">
+            <controls:label>
+              <bean:message key="resources.datasrc.jdbcclass"/>:
+            </controls:label>
+            <controls:data>
+              <html:text property="driverClass" size="45" maxlength="256" styleId="jdbcclass"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="username">
+            <controls:label>
+              <bean:message key="users.prompt.username"/>
+            </controls:label>
+            <controls:data>
+              <html:text property="username" size="15" maxlength="25" styleId="username"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="password">
+            <controls:label>
+              <bean:message key="users.prompt.password"/>
+            </controls:label>
+            <controls:data>
+              <html:password property="password" size="15" maxlength="25" styleId="password"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="active">
+            <controls:label>
+              <bean:message key="resources.datasrc.active"/>:
+            </controls:label>
+            <controls:data>
+              <html:text property="active" size="5" maxlength="5" styleId="active"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="idle">
+            <controls:label>
+              <bean:message key="resources.datasrc.idle"/>:
+            </controls:label>
+            <controls:data>
+              <html:text property="idle" size="5" maxlength="5" styleId="idle"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="wait">
+            <controls:label>
+              <bean:message key="resources.datasrc.wait"/>:
+            </controls:label>
+            <controls:data>
+              <html:text property="wait" size="5" maxlength="5" styleId="wait"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="validation">
+            <controls:label>
+              <bean:message key="resources.datasrc.validation"/>:
+            </controls:label>
+            <controls:data>
+              <html:textarea property="query" cols="35" rows="3" styleId="validation"/>
+            </controls:data>
+          </controls:row>
+
+        </controls:table>
+
+      </td>
+
+    </tr>
+
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+
+<!-- Standard Footer -->
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/resources/dataSources.jspf b/container/webapps/admin/resources/dataSources.jspf
new file mode 100644
index 0000000..3f9d03b
--- /dev/null
+++ b/container/webapps/admin/resources/dataSources.jspf
@@ -0,0 +1,50 @@
+<%-- DataSources List --%>
+
+<table class="back-table" border="0" cellspacing="0" cellpadding="1"
+       width="100%"><tr><td> 
+
+  <table class="front-table" border="1"
+   cellspacing="0" cellpadding="0" width="100%">
+    <tr class="header-row">
+      <logic:present name="checkboxes">
+        <td width="5%"><div align="left" class="table-header-text">
+          &nbsp;
+        </td>
+      </logic:present>
+      <th scope="col" width="20%"><div align="left" class="table-header-text">
+        <bean:message key="resources.datasrc.jndi"/>
+      </div></th>
+      <th scope="col"><div align="left" class="table-header-text">
+        <bean:message key="resources.datasrc.jdbcclass"/>
+      </div></th>
+    </tr>
+
+    <logic:iterate name="dataSourcesForm" property="dataSources"
+                     id="dataSource" type="java.lang.String">
+      <tr class="line-row">
+        <logic:present name="checkboxes">
+          <td scope="row"><div align="center" class="table-normal-text">
+            <label for="dataSources"></label>
+            <input type="checkbox" name="dataSources"
+                  value="<%= dataSource %>" styleId="dataSources">
+          </td>
+        </logic:present>
+        <td scope="row"><div align="left" class="table-normal-text">
+          <html:link page='<%= "/resources/setUpDataSource.do?objectName=" + 
+                               URLEncoder.encode(dataSource,"UTF-8") + "&resourcetype=" +
+                               URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                               URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                               URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                               URLEncoder.encode(domainInfo,"UTF-8")  %>'>
+            <controls:attribute name="dataSource" attribute="name"/>
+          </html:link>
+        </div></td>
+        <td scope="row"><div align="left" class="table-normal-text">&nbsp;
+          <controls:attribute name="dataSource" attribute="driverClassName"/>
+        </div></td>
+      </tr>
+    </logic:iterate>
+
+  </table>
+
+</td></tr></table>
diff --git a/container/webapps/admin/resources/deleteDataSources.jsp b/container/webapps/admin/resources/deleteDataSources.jsp
new file mode 100644
index 0000000..b197ad1
--- /dev/null
+++ b/container/webapps/admin/resources/deleteDataSources.jsp
@@ -0,0 +1,86 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listDataSources">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="dataSourcesForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="dataSourcesForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="dataSourcesForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="dataSourcesForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.actions.datasrc.delete"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listDataSources.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+
+<bean:define id="checkboxes" scope="page" value="true"/>
+
+<html:form action="/resources/deleteDataSources">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="dataSourcesForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="dataSourcesForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="dataSourcesForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="dataSourcesForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <%@ include file="../buttons.jsp" %>
+  <br>
+  <%@ include file="dataSources.jspf" %>
+  <%@ include file="../buttons.jsp" %>
+</html:form>
+<br>
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/deleteEnvEntries.jsp b/container/webapps/admin/resources/deleteEnvEntries.jsp
new file mode 100644
index 0000000..35b69cd
--- /dev/null
+++ b/container/webapps/admin/resources/deleteEnvEntries.jsp
@@ -0,0 +1,86 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listEnvEntries">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="envEntriesForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="envEntriesForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="envEntriesForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="envEntriesForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.actions.env.delete"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listEnvEntries.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+
+<bean:define id="checkboxes" scope="page" value="true"/>
+
+<html:form action="/resources/deleteEnvEntries">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="envEntriesForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="envEntriesForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="envEntriesForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="envEntriesForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <%@ include file="../buttons.jsp" %>
+  <br>
+  <%@ include file="envEntries.jspf" %>
+  <%@ include file="../buttons.jsp" %>
+</html:form>
+<br>
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/deleteMailSessions.jsp b/container/webapps/admin/resources/deleteMailSessions.jsp
new file mode 100644
index 0000000..abcab25
--- /dev/null
+++ b/container/webapps/admin/resources/deleteMailSessions.jsp
@@ -0,0 +1,86 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listMailSessions">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="mailSessionsForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="mailSessionsForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="mailSessionsForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="mailSessionsForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.actions.mailsession.delete"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listMailSessions.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+
+<bean:define id="checkboxes" scope="page" value="true"/>
+
+<html:form action="/resources/deleteMailSessions">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="mailSessionsForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="mailSessionsForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="mailSessionsForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="mailSessionsForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <%@ include file="../buttons.jsp" %>
+  <br>
+  <%@ include file="mailSessions.jspf" %>
+  <%@ include file="../buttons.jsp" %>
+</html:form>
+<br>
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/deleteResourceLinks.jsp b/container/webapps/admin/resources/deleteResourceLinks.jsp
new file mode 100644
index 0000000..c4272fc
--- /dev/null
+++ b/container/webapps/admin/resources/deleteResourceLinks.jsp
@@ -0,0 +1,84 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listResourceLinks">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="resourceLinksForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="resourceLinksForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="resourceLinksForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="resourceLinksForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.actions.resourcelk.delete"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listResourceLinks.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+<bean:define id="checkboxes" scope="page" value="true"/>
+<html:form action="/resources/deleteResourceLinks">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="resourceLinksForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="resourceLinksForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="resourceLinksForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="resourceLinksForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <%@ include file="../buttons.jsp" %>
+  <br>
+  <%@ include file="resourceLinks.jspf" %>
+  <%@ include file="../buttons.jsp" %>
+</html:form>
+<br>
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/deleteUserDatabases.jsp b/container/webapps/admin/resources/deleteUserDatabases.jsp
new file mode 100644
index 0000000..9c33b7f
--- /dev/null
+++ b/container/webapps/admin/resources/deleteUserDatabases.jsp
@@ -0,0 +1,62 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listUserDatabases">
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="userDatabasesForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.actions.userdb.delete"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listUserDatabases.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+
+<bean:define id="checkboxes" scope="page" value="true"/>
+
+<html:form action="/resources/deleteUserDatabases">
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="userDatabasesForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <%@ include file="../buttons.jsp" %>
+  <br>
+  <%@ include file="userDatabases.jspf" %>
+  <%@ include file="../buttons.jsp" %>
+</html:form>
+<br>
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/envEntries.jspf b/container/webapps/admin/resources/envEntries.jspf
new file mode 100644
index 0000000..493008c
--- /dev/null
+++ b/container/webapps/admin/resources/envEntries.jspf
@@ -0,0 +1,56 @@
+<%-- Env Entries List --%>
+
+<table class="back-table" border="0" cellspacing="0" cellpadding="1"
+       width="100%"><tr><td> 
+
+  <table class="front-table" border="1"
+   cellspacing="0" cellpadding="0" width="100%">
+    <tr class="header-row">
+      <logic:present name="checkboxes">
+        <td width="5%"><div align="left" class="table-header-text">
+          &nbsp;
+        </td>
+      </logic:present>
+      <th scope="col" width="20%"><div align="left" class="table-header-text">
+        <bean:message key="resources.env.entry"/>
+      </div></th>
+      <th scope="col"><div align="left" class="table-header-text">
+        <bean:message key="service.value"/>
+      </div></th>
+      <th scop="col"><div align="left" class="table-header-text">
+        <bean:message key="users.list.description"/>
+      </div></th>
+    </tr>
+
+    <logic:iterate name="envEntriesForm" property="envEntries"
+                     id="envEntry" type="java.lang.String">
+      <tr class="line-row">
+        <logic:present name="checkboxes">
+          <td scope="row"><div align="center" class="table-normal-text">
+            <label for="envEntries"></label>
+            <input type="checkbox" name="envEntries"
+                  value="<%= envEntry %>" styleId="envEntries">
+          </td>
+        </logic:present>
+        <td><div align="left" class="table-normal-text">
+          <html:link page='<%= "/resources/setUpEnvEntry.do?objectName=" + 
+                               URLEncoder.encode(envEntry,"UTF-8") + "&resourcetype=" +
+                               URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                               URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                               URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                               URLEncoder.encode(domainInfo,"UTF-8") %>'>
+            <controls:attribute name="envEntry" attribute="name"/>
+          </html:link>
+        </div></td>
+        <td><div align="left" class="table-normal-text">&nbsp;
+          <controls:attribute name="envEntry" attribute="value"/>
+        </div></td>
+        <td><div align="left" class="table-normal-text">&nbsp;
+          <controls:attribute name="envEntry" attribute="description"/>
+        </div></td>
+      </tr>
+    </logic:iterate>
+
+  </table>
+
+</td></tr></table>
diff --git a/container/webapps/admin/resources/envEntry.jsp b/container/webapps/admin/resources/envEntry.jsp
new file mode 100644
index 0000000..9f2fc85
--- /dev/null
+++ b/container/webapps/admin/resources/envEntry.jsp
@@ -0,0 +1,176 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/resources/saveEnvEntry">
+
+  <html:hidden property="objectName"/>
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="envEntryForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="envEntryForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="envEntryForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="envEntryForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text">
+          <bean:write name="envEntryForm" property="nodeLabel"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+          <controls:actions label="Environment Entry Actions">
+            <controls:action selected="true">
+              ----<bean:message key="actions.available.actions"/>----
+            </controls:action>
+            <controls:action>
+              ---------------------------------
+            </controls:action>
+
+        <controls:action url='<%= "/resources/setUpEnvEntry.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") %>'>
+                <bean:message key="resources.actions.env.create"/>
+            </controls:action>
+            <controls:action url='<%= "/resources/listEnvEntries.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") + "&forward=" +
+                            URLEncoder.encode("EnvEntries Delete List","UTF-8") %>'>
+                <bean:message key="resources.actions.env.delete"/>
+            </controls:action>
+         </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+<br>
+
+  <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr><td><div class="table-title-text">
+      <bean:message key="resources.env.props"/>
+    </div></td></tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+          <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="name">
+            <controls:label>
+              <bean:message key="service.name"/>:
+            </controls:label>
+            <controls:data>
+              <logic:present name="envEntryForm" property="objectName">
+                <bean:write name="envEntryForm" property="name"/>
+                <html:hidden property="name"/>
+              </logic:present>
+              <logic:notPresent name="envEntryForm" property="objectName">
+                <html:text property="name" size="24" styleId="name"/>
+              </logic:notPresent>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="type">
+            <controls:label>
+              <bean:message key="connector.type"/>:
+            </controls:label>
+            <controls:data>
+              <html:select property="entryType" styleId="type">
+                     <bean:define id="typeVals" name="envEntryForm" property="typeVals"/>
+                     <html:options collection="typeVals" property="value"
+                                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="value">
+            <controls:label>
+              <bean:message key="service.value"/>:
+            </controls:label>
+            <controls:data>
+              <html:text property="value" size="24" styleId="value"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="override">
+            <controls:label>
+              <bean:message key="resources.env.override"/>:
+            </controls:label>
+            <controls:data>
+              <html:checkbox property="override" value="override" styleId="override"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="description">
+            <controls:label>
+              <bean:message key="users.prompt.description"/>
+            </controls:label>
+            <controls:data>
+              <html:text property="description" size="30" styleId="description"/>
+            </controls:data>
+          </controls:row>
+
+        </controls:table>
+
+      </td>
+
+    </tr>
+
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+
+<!-- Standard Footer -->
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/resources/listDataSources.jsp b/container/webapps/admin/resources/listDataSources.jsp
new file mode 100644
index 0000000..da6f4fd
--- /dev/null
+++ b/container/webapps/admin/resources/listDataSources.jsp
@@ -0,0 +1,60 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listDataSources">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="dataSourcesForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="dataSourcesForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="dataSourcesForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="dataSourcesForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.actions.datasrc"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listDataSources.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<br>
+
+<%@ include file="dataSources.jspf" %>
+
+<br>
+</html:form>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/listDataSources.jspf b/container/webapps/admin/resources/listDataSources.jspf
new file mode 100644
index 0000000..acdce2e
--- /dev/null
+++ b/container/webapps/admin/resources/listDataSources.jspf
@@ -0,0 +1,32 @@
+<td align="right" nowrap>
+<div class="page-title-text">
+<controls:actions label="Data Source Actions">
+
+  <controls:action selected="true">
+    ----<bean:message key="actions.available.actions"/>----
+  </controls:action>
+
+  <controls:action disabled="true">
+    ---------------------------------
+  </controls:action>
+
+  <controls:action url='<%= "/resources/setUpDataSource.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8")%>'>
+    <bean:message key="resources.actions.datasrc.create"/>
+  </controls:action>
+
+  <controls:action url='<%= "/resources/listDataSources.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") + "&forward=" + 
+                            URLEncoder.encode("DataSources Delete List","UTF-8") %>'>
+    <bean:message key="resources.actions.datasrc.delete"/>
+  </controls:action>
+
+</controls:actions>
+</div>
+</td>
diff --git a/container/webapps/admin/resources/listEnvEntries.jsp b/container/webapps/admin/resources/listEnvEntries.jsp
new file mode 100644
index 0000000..c35e25c
--- /dev/null
+++ b/container/webapps/admin/resources/listEnvEntries.jsp
@@ -0,0 +1,60 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listEnvEntries">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="envEntriesForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="envEntriesForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="envEntriesForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="envEntriesForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.env.entries"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listEnvEntries.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<br>
+
+<%@ include file="envEntries.jspf" %>
+
+<br>
+</html:form>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/listEnvEntries.jspf b/container/webapps/admin/resources/listEnvEntries.jspf
new file mode 100644
index 0000000..b11001e
--- /dev/null
+++ b/container/webapps/admin/resources/listEnvEntries.jspf
@@ -0,0 +1,32 @@
+<td align="right" nowrap>
+<div class="page-title-text">
+<controls:actions label="Environment Entry Actions">
+
+  <controls:action selected="true">
+    ----<bean:message key="actions.available.actions"/>----
+  </controls:action>
+
+  <controls:action disabled="true">
+    ---------------------------------
+  </controls:action>
+
+  <controls:action url='<%= "/resources/setUpEnvEntry.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") %>'>
+    <bean:message key="resources.actions.env.create"/>
+  </controls:action>
+
+  <controls:action url='<%= "/resources/listEnvEntries.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") + "&forward="+ 
+                            URLEncoder.encode("EnvEntries Delete List","UTF-8") %>'>
+    <bean:message key="resources.actions.env.delete"/>
+  </controls:action>
+
+</controls:actions>
+</div>
+</td>
diff --git a/container/webapps/admin/resources/listMailSessions.jsp b/container/webapps/admin/resources/listMailSessions.jsp
new file mode 100644
index 0000000..5ee4a11
--- /dev/null
+++ b/container/webapps/admin/resources/listMailSessions.jsp
@@ -0,0 +1,60 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listMailSessions">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="mailSessionsForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="mailSessionsForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="mailSessionsForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="mailSessionsForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.actions.mailsession"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listMailSessions.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<br>
+
+<%@ include file="mailSessions.jspf" %>
+
+<br>
+</html:form>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/listMailSessions.jspf b/container/webapps/admin/resources/listMailSessions.jspf
new file mode 100644
index 0000000..67427e5
--- /dev/null
+++ b/container/webapps/admin/resources/listMailSessions.jspf
@@ -0,0 +1,32 @@
+<td align="right" nowrap>
+<div class="page-title-text">
+<controls:actions label="Mail Session Actions">
+
+  <controls:action selected="true">
+    ----<bean:message key="actions.available.actions"/>----
+  </controls:action>
+
+  <controls:action disabled="true">
+    ---------------------------------
+  </controls:action>
+
+  <controls:action url='<%= "/resources/setUpMailSession.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") %>'>
+    <bean:message key="resources.actions.mailsession.create"/>
+  </controls:action>
+
+  <controls:action url='<%= "/resources/listMailSessions.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") + "&forward=" + 
+                            URLEncoder.encode("MailSessions Delete List","UTF-8") %>'>
+    <bean:message key="resources.actions.mailsession.delete"/>
+  </controls:action>
+
+</controls:actions>
+</div>
+</td>
diff --git a/container/webapps/admin/resources/listResourceLinks.jsp b/container/webapps/admin/resources/listResourceLinks.jsp
new file mode 100644
index 0000000..c1d9916
--- /dev/null
+++ b/container/webapps/admin/resources/listResourceLinks.jsp
@@ -0,0 +1,60 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listResourceLinks">
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="resourceLinksForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="resourceLinksForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="resourceLinksForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="resourceLinksForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.actions.resourcelk"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listResourceLinks.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<br>
+
+<%@ include file="resourceLinks.jspf" %>
+
+<br>
+</html:form>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/listResourceLinks.jspf b/container/webapps/admin/resources/listResourceLinks.jspf
new file mode 100644
index 0000000..963036d
--- /dev/null
+++ b/container/webapps/admin/resources/listResourceLinks.jspf
@@ -0,0 +1,32 @@
+<td align="right" nowrap>
+<div class="page-title-text">
+<controls:actions label="Resource Link Actions">
+
+  <controls:action selected="true">
+    ----<bean:message key="actions.available.actions"/>----
+  </controls:action>
+
+  <controls:action disabled="true">
+    ---------------------------------
+  </controls:action>
+
+  <controls:action url='<%= "/resources/setUpResourceLink.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") %>'>
+    <bean:message key="resources.actions.resourcelk.create"/>
+  </controls:action>
+
+  <controls:action url='<%= "/resources/listResourceLinks.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") +"&forward=" + 
+                            URLEncoder.encode("ResourceLinks Delete List","UTF-8") %>'>
+    <bean:message key="resources.actions.resourcelk.delete"/>
+  </controls:action>
+
+</controls:actions>
+</div>
+</td>
diff --git a/container/webapps/admin/resources/listUserDatabases.jsp b/container/webapps/admin/resources/listUserDatabases.jsp
new file mode 100644
index 0000000..92d808a
--- /dev/null
+++ b/container/webapps/admin/resources/listUserDatabases.jsp
@@ -0,0 +1,49 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/resources/listUserDatabases">
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="userDatabasesForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="resources.treeBuilder.databases"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listUserDatabases.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<br>
+
+<%@ include file="userDatabases.jspf" %>
+
+</html:form>
+
+<br>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/resources/listUserDatabases.jspf b/container/webapps/admin/resources/listUserDatabases.jspf
new file mode 100644
index 0000000..a396d4f
--- /dev/null
+++ b/container/webapps/admin/resources/listUserDatabases.jspf
@@ -0,0 +1,26 @@
+<td align="right" nowrap>
+<div class="page-title-text">
+<controls:actions label="User Database Actions">
+
+  <controls:action selected="true">
+    ----<bean:message key="actions.available.actions"/>----
+  </controls:action>
+
+  <controls:action disabled="true">
+    ---------------------------------
+  </controls:action>
+
+  <controls:action url='<%= "/resources/setUpUserDatabase.do?domain=" +
+                        URLEncoder.encode(domainInfo,"UTF-8") %>'>
+    <bean:message key="resources.actions.userdb.create"/>
+  </controls:action>
+
+  <controls:action url='<%= "/resources/listUserDatabases.do?domain=" +
+                        URLEncoder.encode(domainInfo,"UTF-8") + "&forward=" +
+                        URLEncoder.encode("UserDatabases Delete List","UTF-8") %>'>
+    <bean:message key="resources.actions.userdb.delete"/>
+  </controls:action>
+
+</controls:actions>
+</div>
+</td>
diff --git a/container/webapps/admin/resources/mailSession.jsp b/container/webapps/admin/resources/mailSession.jsp
new file mode 100644
index 0000000..0dff75f
--- /dev/null
+++ b/container/webapps/admin/resources/mailSession.jsp
@@ -0,0 +1,142 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/resources/saveMailSession">
+
+  <html:hidden property="objectName"/>
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="mailSessionForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="mailSessionForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="mailSessionForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="mailSessionForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text">
+          <bean:write name="mailSessionForm" property="nodeLabel"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+          <controls:actions label="Mail Session Actions">
+            <controls:action selected="true">
+              ----<bean:message key="actions.available.actions"/>----
+            </controls:action>
+            <controls:action>
+              ---------------------------------
+            </controls:action>
+
+        <controls:action url='<%= "/resources/setUpMailSession.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") %>'>
+                <bean:message key="resources.actions.mailsession.create"/>
+            </controls:action>
+            <controls:action url='<%= "/resources/listMailSessions.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") + "&forward=" +
+                            URLEncoder.encode("MailSessions Delete List","UTF-8") %>'>
+                <bean:message key="resources.actions.mailsession.delete"/>
+            </controls:action>
+         </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+<br>
+
+  <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr><td><div class="table-title-text">
+        <bean:message key="resources.treeBuilder.mailsessions"/>
+    </div></td></tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+          <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="name">
+            <controls:label>
+              <bean:message key="resources.mailsession.name"/>:
+            </controls:label>
+            <controls:data>
+              <logic:present name="mailSessionForm" property="objectName">
+                <bean:write name="mailSessionForm" property="name"/>
+                <html:hidden property="name"/>
+              </logic:present>
+              <logic:notPresent name="mailSessionForm" property="objectName">
+                <html:text property="name" size="35" maxlength="56" styleId="name"/>
+              </logic:notPresent>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="mailhost">
+            <controls:label>
+              <bean:message key="resources.mailsession.mailhost"/>:
+            </controls:label>
+            <controls:data>
+                <html:textarea property="mailhost" cols="35" rows="2" styleId="mailhost"/>
+            </controls:data>
+          </controls:row>
+
+        </controls:table>
+
+      </td>
+
+    </tr>
+
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+
+<!-- Standard Footer -->
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/resources/mailSessions.jspf b/container/webapps/admin/resources/mailSessions.jspf
new file mode 100644
index 0000000..378c3ee
--- /dev/null
+++ b/container/webapps/admin/resources/mailSessions.jspf
@@ -0,0 +1,50 @@
+<%-- MailSessions List --%>
+
+<table class="back-table" border="0" cellspacing="0" cellpadding="1"
+       width="100%"><tr><td> 
+
+  <table class="front-table" border="1"
+   cellspacing="0" cellpadding="0" width="100%">
+    <tr class="header-row">
+      <logic:present name="checkboxes">
+        <td width="5%"><div align="left" class="table-header-text">
+          &nbsp;
+        </td>
+      </logic:present>
+      <th scope="col" width="20%"><div align="left" class="table-header-text">
+        <bean:message key="resources.mailsession.name"/>
+      </div></th>
+      <th scope="col"><div align="left" class="table-header-text">
+        <bean:message key="resources.mailsession.mailhost"/>
+      </div></th>
+    </tr>
+
+    <logic:iterate name="mailSessionsForm" property="mailSessions"
+                     id="mailSession" type="java.lang.String">
+      <tr class="line-row">
+        <logic:present name="checkboxes">
+          <td scope="row"><div align="center" class="table-normal-text">
+            <label for="mailSessions"></label>
+            <input type="checkbox" name="mailSessions"
+                  value="<%= mailSession %>" styleId="mailSessions">
+          </td>
+        </logic:present>
+        <td scope="row"><div align="left" class="table-normal-text">
+          <html:link page='<%= "/resources/setUpMailSession.do?objectName=" + 
+                               URLEncoder.encode(mailSession,"UTF-8") + "&resourcetype=" +
+                               URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                               URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                               URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                               URLEncoder.encode(domainInfo,"UTF-8") %>'>
+            <controls:attribute name="mailSession" attribute="name"/>
+          </html:link>
+        </div></td>
+        <td scope="row"><div align="left" class="table-normal-text">&nbsp;
+          <controls:attribute name="mailSession" attribute="mail.smtp.host"/>
+        </div></td>
+      </tr>
+    </logic:iterate>
+
+  </table>
+
+</td></tr></table>
diff --git a/container/webapps/admin/resources/resourceLink.jsp b/container/webapps/admin/resources/resourceLink.jsp
new file mode 100644
index 0000000..fa92972
--- /dev/null
+++ b/container/webapps/admin/resources/resourceLink.jsp
@@ -0,0 +1,153 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/resources/saveResourceLink">
+
+  <html:hidden property="objectName"/>
+
+  <bean:define id="resourcetypeInfo" type="java.lang.String"
+               name="resourceLinkForm" property="resourcetype"/>
+  <html:hidden property="resourcetype"/>
+
+  <bean:define id="pathInfo" type="java.lang.String"
+               name="resourceLinkForm" property="path"/>
+  <html:hidden property="path"/>
+
+  <bean:define id="hostInfo" type="java.lang.String"
+               name="resourceLinkForm" property="host"/>
+  <html:hidden property="host"/>
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="resourceLinkForm" property="domain"/>
+  <html:hidden property="domain"/>
+
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text">
+          <bean:write name="resourceLinkForm" property="nodeLabel"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+          <controls:actions label="Resource Link Actions">
+            <controls:action selected="true">
+              ----<bean:message key="actions.available.actions"/>----
+            </controls:action>
+            <controls:action>
+              ---------------------------------
+            </controls:action>
+
+            <controls:action url='<%= "/resources/setUpResourceLink.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") %>'>
+                <bean:message key="resources.actions.resourcelk.create"/>
+            </controls:action>
+            <controls:action url='<%= "/resources/listResourceLinks.do?resourcetype=" +
+                            URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                            URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                            URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                            URLEncoder.encode(domainInfo,"UTF-8") + "&forward=" +
+                            URLEncoder.encode("ResourceLinks Delete List","UTF-8") %>'>
+                <bean:message key="resources.actions.resourcelk.delete"/>
+            </controls:action>
+         </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+<br>
+
+  <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr><td><div class="table-title-text">
+        <bean:message key="resources.treeBuilder.resourcelinks"/>
+    </div></td></tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+          <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="name">
+            <controls:label>
+              <bean:message key="resources.resourcelk.name"/>:
+            </controls:label>
+            <controls:data>
+              <logic:present name="resourceLinkForm" property="objectName">
+                <bean:write name="resourceLinkForm" property="name"/>
+                <html:hidden property="name"/>
+              </logic:present>
+              <logic:notPresent name="resourceLinkForm" property="objectName">
+                <html:text property="name" size="35" maxlength="56" styleId="name"/>
+              </logic:notPresent>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="global">
+            <controls:label>
+              <bean:message key="resources.resourcelk.global"/>:
+            </controls:label>
+            <controls:data>
+                <html:text property="global" size="35" maxlength="56" styleId="global"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="type">
+            <controls:label>
+              <bean:message key="resources.resourcelk.type"/>:
+            </controls:label>
+            <controls:data>
+              <html:text property="type" size="45" maxlength="256" styleId="type"/>
+            </controls:data>
+          </controls:row>
+
+    </controls:table>
+
+      </td>
+
+    </tr>
+
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+
+<!-- Standard Footer -->
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/resources/resourceLinks.jspf b/container/webapps/admin/resources/resourceLinks.jspf
new file mode 100644
index 0000000..fcaaa68
--- /dev/null
+++ b/container/webapps/admin/resources/resourceLinks.jspf
@@ -0,0 +1,48 @@
+<%-- ResourceLinks List --%>
+
+<table class="back-table" border="0" cellspacing="0" cellpadding="1"
+       width="100%"><tr><td> 
+
+  <table class="front-table" border="1"
+   cellspacing="0" cellpadding="0" width="100%">
+    <tr class="header-row">
+      <logic:present name="checkboxes">
+        <td width="5%"><div align="left" class="table-header-text">
+          &nbsp;
+        </td>
+      </logic:present>
+      <th scope="col" width="20%"><div align="left" class="table-header-text">
+        <bean:message key="resources.resourcelk.name"/>
+      </div></th>
+      <th scope="col"><div align="left" class="table-header-text">
+        <bean:message key="resources.resourcelk.global"/>
+      </div></th>
+    </tr>
+    <logic:iterate name="resourceLinksForm" property="resourceLinks"
+                     id="resourceLink" type="java.lang.String">
+      <tr class="line-row">
+        <logic:present name="checkboxes">
+          <td scope="row"><div align="center" class="table-normal-text">
+            <label for="resourceLinks"></label>
+            <input type="checkbox" name="resourceLinks"
+                  value="<%= resourceLink %>" styleId="resourceLinks">
+          </td>
+        </logic:present>
+        <td scope="row"><div align="left" class="table-normal-text">
+          <html:link page='<%= "/resources/setUpResourceLink.do?objectName=" + 
+                               URLEncoder.encode(resourceLink,"UTF-8") + "&resourcetype=" +
+                               URLEncoder.encode(resourcetypeInfo,"UTF-8") + "&path="+
+                               URLEncoder.encode(pathInfo,"UTF-8") + "&host="+
+                               URLEncoder.encode(hostInfo,"UTF-8") + "&domain="+
+                               URLEncoder.encode(domainInfo,"UTF-8") %>'>
+            <controls:attribute name="resourceLink" attribute="name"/>
+          </html:link>
+        </div></td>
+        <td scope="row"><div align="left" class="table-normal-text">&nbsp;
+          <controls:attribute name="resourceLink" attribute="global"/>
+        </div></td>
+      </tr>
+    </logic:iterate>
+  </table>
+
+</td></tr></table>
diff --git a/container/webapps/admin/resources/userDatabase.jsp b/container/webapps/admin/resources/userDatabase.jsp
new file mode 100644
index 0000000..00e8558
--- /dev/null
+++ b/container/webapps/admin/resources/userDatabase.jsp
@@ -0,0 +1,145 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/resources/saveUserDatabase">
+
+  <bean:define id="domainInfo" type="java.lang.String"
+               name="userDatabaseForm" property="domain"/>
+  <html:hidden property="domain"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="type"/>
+  <html:hidden property="factory"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text">
+          <bean:write name="userDatabaseForm" property="nodeLabel"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+          <controls:actions label="User Database Actions">
+            <controls:action selected="true">
+              ----<bean:message key="actions.available.actions"/>----
+            </controls:action>
+            <controls:action>
+              ---------------------------------
+            </controls:action>
+            
+            <controls:action url='<%= "/resources/setUpUserDatabase.do?domain=" +
+                                    URLEncoder.encode(domainInfo,"UTF-8") %>'>
+                <bean:message key="resources.actions.userdb.create"/>
+            </controls:action>
+            <controls:action url='<%= "/resources/listUserDatabases.do?domain=" +
+                                    URLEncoder.encode(domainInfo,"UTF-8") + "&forward=" +
+                                    URLEncoder.encode("UserDatabases Delete List","UTF-8") %>'>
+                <bean:message key="resources.actions.userdb.delete"/>
+            </controls:action>
+         </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+<br>
+
+  <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr><td><div class="table-title-text">
+        <bean:message key="resources.treeBuilder.databases"/>
+    </div></td></tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+          <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="name">
+            <controls:label>
+              <bean:message key="service.name"/>:
+            </controls:label>
+            <controls:data>
+              <logic:present name="userDatabaseForm" property="objectName">
+                <bean:write name="userDatabaseForm" property="name"/>
+                <html:hidden property="name"/>
+              </logic:present>
+              <logic:notPresent name="userDatabaseForm" property="objectName">
+                <html:text property="name" size="24" maxlength="32" styleId="name"/>
+              </logic:notPresent>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="location">
+            <controls:label>
+              <bean:message key="resources.userdb.location"/>:
+            </controls:label>
+            <controls:data>
+              <html:text property="path" size="32" maxlength="64" styleId="location"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="factory">
+            <controls:label>
+              <bean:message key="resources.userdb.factory"/>:
+            </controls:label>
+            <controls:data>
+              <bean:write name="userDatabaseForm" property="factory"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="description">
+            <controls:label>
+              <bean:message key="users.prompt.description"/>
+            </controls:label>
+            <controls:data>
+              <html:textarea property="description" cols="32" rows="3" styleId="description"/>
+            </controls:data>
+          </controls:row>
+
+        </controls:table>
+
+      </td>
+
+    </tr>
+
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+
+<!-- Standard Footer -->
+
+<%@ include file="../users/footer.jsp" %>
+
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/resources/userDatabases.jspf b/container/webapps/admin/resources/userDatabases.jspf
new file mode 100644
index 0000000..110e639
--- /dev/null
+++ b/container/webapps/admin/resources/userDatabases.jspf
@@ -0,0 +1,65 @@
+<%-- Env Entries List --%>
+
+<table class="back-table" border="0" cellspacing="0" cellpadding="1"
+       width="100%"><tr><td> 
+
+  <table class="front-table" border="1"
+   cellspacing="0" cellpadding="0" width="100%">
+    <tr class="header-row">
+      <logic:present name="checkboxes">
+        <td width="5%"><div align="left" class="table-header-text">
+          &nbsp;
+        </td>
+      </logic:present>
+      <th scope="col" width="20%"><div align="left" class="table-header-text">
+        <bean:message key="service.name"/>
+      </div></th>
+      <%--
+      <th scope="col"><div align="left" class="table-header-text">
+        <bean:message key="resources.userdb.location"/>
+      </div></th>
+      --%>
+      <th scope="col"><div align="left" class="table-header-text">
+        <bean:message key="users.list.description"/>
+      </div></th>
+    </tr>
+    <logic:iterate name="userDatabasesForm" property="userDatabases"
+                     id="userDatabase" type="java.lang.String">
+      <tr class="line-row">
+        <logic:present name="checkboxes">
+          <td scope="row"><div align="center" class="table-normal-text">
+             <logic:match name="userDatabase"
+                        value="name=UserDatabase">
+             <font color='red'>*</font>
+             </logic:match>
+             <logic:notMatch name="userDatabase"
+                        value="name=UserDatabase">
+              <label for="userDatabases"></label>       
+              <html:multibox property="userDatabases"
+                                value="userDatabase" styleId="userDatabases"/>
+              </logic:notMatch>        
+        
+            <!--input type="checkbox" name="userDatabases"
+                  value="<%= userDatabase %>" styleId="userDatabases"-->
+          </td>
+        </logic:present>
+        <td scope="row"><div align="left" class="table-normal-text">
+          <html:link page='<%= "/resources/setUpUserDatabase.do?objectName=" + 
+                               URLEncoder.encode(userDatabase,"UTF-8") + "&domain=" + 
+                               URLEncoder.encode(domainInfo,"UTF-8") %>'>
+            <controls:attribute name="userDatabase" attribute="name"/>
+          </html:link>
+        </div></td>
+        <%-- FIX ME -- commentred out for now as the page was looking too crowded.
+        <td scope="row"><div align="left" class="table-normal-text">
+          <controls:attribute name="userDatabase" attribute="pathname"/>
+        </div></td>
+        --%>
+        <td scope="row"><div align="left" class="table-normal-text">
+          <controls:attribute name="userDatabase" attribute="description"/>
+        </div></td>
+      </tr>
+    </logic:iterate>
+  </table>
+
+</td></tr></table>
diff --git a/container/webapps/admin/saved.jsp b/container/webapps/admin/saved.jsp
new file mode 100644
index 0000000..9a91e63
--- /dev/null
+++ b/container/webapps/admin/saved.jsp
@@ -0,0 +1,36 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+  <body bgcolor="white" background="images/PaperTexture.gif">
+
+    <%-- Cause our tree control to refresh itself --%>
+    <script language="JavaScript">
+      <!--
+        parent.tree.location='treeControlTest.do';
+      -->
+    </script>
+
+    <%@ include file="header.jsp" %>
+
+    <%-- display warnings if any --%>
+    <logic:present name="warning">
+            <bean:message key="warning.header"/>
+            <bean:message key='<%= (String) request.getAttribute("warning") %>'/>
+            <br>
+    </logic:present>
+
+    <center><h2>
+      <bean:message key="save.success"/>
+    </h2></center>
+
+    <%@ include file="footer.jsp" %>
+
+  </body>
+
+</html:html>
diff --git a/container/webapps/admin/savefail.jsp b/container/webapps/admin/savefail.jsp
new file mode 100644
index 0000000..6e0d27a
--- /dev/null
+++ b/container/webapps/admin/savefail.jsp
@@ -0,0 +1,38 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+  <body bgcolor="white" background="images/PaperTexture.gif">
+
+    <%-- Cause our tree control to refresh itself --%>
+    <script language="JavaScript">
+      <!--
+        parent.tree.location='treeControlTest.do';
+      -->
+    </script>
+
+    <%@ include file="header.jsp" %>
+    <center><h2>
+    <%-- display warnings if any --%>
+    <logic:present name="warning">
+            <bean:message key="warning.header"/>
+    </h2></center>
+    <h3><center>
+            <bean:message key='<%= (String) request.getAttribute("warning") %>'/>
+            <br>
+    </logic:present>
+    </h3></center>
+    <center><h2>
+      <bean:message key="save.fail"/>
+    </h2></center>
+
+    <%@ include file="footer.jsp" %>
+
+  </body>
+
+</html:html>
diff --git a/container/webapps/admin/server/server.jsp b/container/webapps/admin/server/server.jsp
new file mode 100644
index 0000000..72b6bab
--- /dev/null
+++ b/container/webapps/admin/server/server.jsp
@@ -0,0 +1,107 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveServer" focus="portNumberText">
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="serverForm" property="objectName"/>
+  <html:hidden property="objectName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text">
+           <bean:write name="serverForm" property="nodeLabel"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+        <controls:actions label="Server Actions">
+          <controls:action selected="true">
+            ----<bean:message key="actions.available.actions"/>----
+          </controls:action>
+          <controls:action>
+            ---------------------------------
+          </controls:action>
+          <controls:action url='<%= "/AddService.do?select=" +
+                                      URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+            <bean:message key="actions.services.create"/>
+          </controls:action>
+          <controls:action url='<%= "/DeleteService.do?select=" +
+                                      URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+            <bean:message key="actions.services.deletes"/>
+          </controls:action>
+        </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+ <br>
+
+  <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr><td><div class="table-title-text">
+      <bean:message key="server.properties"/>
+    </div></td></tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+         <controls:table tableStyle="front-table" lineStyle="line-row">
+          <controls:row header="true"
+              labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>
+              <bean:message key="service.property"/>
+            </controls:label>
+            <controls:data>
+              <bean:message key="service.value"/>
+            </controls:data>
+          </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="portNumber">
+            <controls:label><bean:message key="server.portnumber"/>:</controls:label>
+            <controls:data>
+              <html:text property="portNumberText" size="24" maxlength="24" styleId="portNumber"/>
+            </controls:data>
+        </controls:row>
+
+       <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="shutdown">
+            <controls:label><bean:message key="server.shutdown"/>:</controls:label>
+            <controls:data>
+               <html:text property="shutdownText" size="24" maxlength="24" styleId="shutdown"/>
+            </controls:data>
+        </controls:row>
+      </controls:table>
+
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+
+<!-- Standard Footer -->
+
+<%@ include file="../footer.jsp" %>
+
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/service/service.jsp b/container/webapps/admin/service/service.jsp
new file mode 100644
index 0000000..8afad09
--- /dev/null
+++ b/container/webapps/admin/service/service.jsp
@@ -0,0 +1,233 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveService">
+
+  <bean:define id="serviceName" name="serviceForm" property="serviceName"/>
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="serviceForm" property="objectName"/>
+  <bean:define id="thisServiceName" type="java.lang.String"
+               name="serviceForm" property="serviceName"/>
+  <html:hidden property="adminServiceName"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="engineObjectName"/>
+  <html:hidden property="adminAction"/>
+  <bean:define id="adminServiceName" type="java.lang.String"
+               name="serviceForm" property="adminServiceName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <logic:equal name="serviceForm" property="adminAction" value="Create">
+            <bean:message key="actions.services.create"/>
+          </logic:equal>
+          <logic:equal name="serviceForm" property="adminAction" value="Edit">
+            <bean:write name="serviceForm" property="nodeLabel"/>
+          </logic:equal>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+          <controls:actions label="Service Actions">
+            <controls:action selected="true">
+              -----<bean:message key="actions.available.actions"/>-----
+            </controls:action>
+            <controls:action disabled="true">
+              -------------------------------------
+            </controls:action>
+            <logic:notEqual name="serviceForm" property="adminAction" value="Create">
+              <controls:action url='<%= "/AddConnector.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.connectors.create"/>
+              </controls:action>
+              <controls:action url='<%= "/DeleteConnector.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8")%>'>
+                <bean:message key="actions.connectors.deletes"/>
+              </controls:action>
+              <controls:action>
+                -------------------------------------
+              </controls:action>
+              <controls:action disabled="true">
+                -------------------------------------
+              </controls:action>
+              <controls:action url='<%= "/AddHost.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.hosts.create"/>
+              </controls:action>
+              <controls:action url='<%= "/DeleteHost.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.hosts.deletes"/>
+              </controls:action>
+              <controls:action disabled="true">
+                -------------------------------------
+              </controls:action>
+               <%-- cannot delete or add a Realm for the service the admin app runs on --%>
+              <logic:notEqual name="serviceName" value='<%= adminServiceName %>'>
+              <controls:action disabled="true">
+                -------------------------------------
+              </controls:action>
+              <%--
+              <controls:action url='<%= "/AddRealm.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.create"/>
+             </controls:action>
+             <controls:action url='<%= "/DeleteRealm.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.realms.deletes"/>
+              </controls:action>
+              --%>
+              </logic:notEqual>
+              <controls:action disabled="true">
+                -------------------------------------
+              </controls:action>
+              <controls:action url='<%= "/AddValve.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.valves.create"/>
+              </controls:action>
+              <controls:action url='<%= "/DeleteValve.do?parent=" +
+                                  URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.valves.deletes"/>
+               </controls:action>
+               <%-- cannot delete the service the admin app runs on --%>
+               <logic:notEqual name="serviceName" value='<%= adminServiceName %>'>
+               <controls:action disabled="true">
+                -------------------------------------
+                </controls:action>
+                 <controls:action url='<%= "/DeleteService.do?select=" +
+                                        URLEncoder.encode(thisObjectName,"UTF-8") %>'>
+                <bean:message key="actions.services.delete"/>
+              </controls:action>
+              </logic:notEqual>
+            </logic:notEqual>
+          </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+  <%-- Service Properties --%>
+
+  <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr><td><div class="table-title-text">
+      <bean:message key="service.properties"/>
+    </div></td></tr>
+  </table>
+
+  <table class="back-table" border="0"
+         cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+          <controls:row header="true"
+              labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>
+              <bean:message key="service.property"/>
+            </controls:label>
+            <controls:data>
+              <bean:message key="service.value"/>
+            </controls:data>
+          </controls:row>
+          <controls:row header="false"
+              labelStyle="table-label-text" dataStyle="table-normal-text" styleId="serviceName">
+            <controls:label>
+              <bean:message key="service.name"/>:
+            </controls:label>
+            <controls:data>
+              <logic:equal name="serviceForm" property="adminAction" value="Create">
+                <html:text property="serviceName" size="50" maxlength="50" styleId="serviceName"/>
+              </logic:equal>
+              <logic:equal name="serviceForm" property="adminAction" value="Edit">
+                <html:hidden property="serviceName"/>
+                <bean:write name="serviceForm" property="serviceName"/>
+              </logic:equal>
+            </controls:data>
+          </controls:row>
+        </controls:table>
+      </td>
+    </tr>
+  </table>
+
+  <br>
+
+  <%-- Engine Properties --%>
+
+  <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr><td><div class="table-title-text">
+      <bean:message key="service.engine.props"/>
+    </div></td></tr>
+  </table>
+
+  <table class="back-table" border="0"
+         cellspacing="0" cellpadding="1" width="100%">
+    <tr>
+      <td>
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+          <controls:row header="true"
+              labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>
+              <bean:message key="service.property"/>
+            </controls:label>
+            <controls:data>
+              <bean:message key="service.value"/>
+            </controls:data>
+          </controls:row>
+          <controls:row header="false"
+              labelStyle="table-label-text" dataStyle="table-normal-text" styleId="engineName">
+            <controls:label>
+              <bean:message key="service.name"/>:
+            </controls:label>
+            <controls:data>
+              <logic:equal name="serviceForm" property="adminAction" value="Create">
+                <html:text property="engineName" size="50" maxlength="50" styleId="engineName"/>
+              </logic:equal>
+              <logic:equal name="serviceForm" property="adminAction" value="Edit">
+                <html:hidden property="engineName"/>
+                <bean:write name="serviceForm" property="engineName"/>
+              </logic:equal>
+            </controls:data>
+          </controls:row>
+          <controls:row header="false"
+              labelStyle="table-label-text" dataStyle="table-normal-text" styleId="hostNameVals">
+            <controls:label>
+              <bean:message key="service.defaulthostname"/>:
+            </controls:label>
+            <controls:data>
+              <bean:define id="hostNameVals" name="serviceForm"
+                           property="hostNameVals"/>
+              <html:select property="defaultHost" styleId="hostNameVals">
+                <html:options collection="hostNameVals" property="value"
+                              labelProperty="label"/>
+              </html:select>
+            </controls:data>
+          </controls:row>
+        </controls:table>
+      </td>
+    </tr>
+  </table>
+
+  <br>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+</body>
+</html:html>
diff --git a/container/webapps/admin/service/services.jsp b/container/webapps/admin/service/services.jsp
new file mode 100644
index 0000000..d832109
--- /dev/null
+++ b/container/webapps/admin/service/services.jsp
@@ -0,0 +1,105 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="post" action="/DeleteServices">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="actions.services.delete"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">        
+            <controls:actions label="Service Actions">
+              <controls:action selected="true">
+                ----<bean:message key="actions.available.actions"/>----
+              </controls:action>
+              <controls:action>
+                ---------------------------------
+              </controls:action>
+            </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+  <br>
+
+  <%-- Services List --%>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1"
+         width="100%">
+    <tr><td>
+
+      <table class="front-table" border="1"
+       cellspacing="0" cellpadding="0" width="100%">
+
+        <tr class="header-row">
+          <td><div align="left" class="table-header-text">
+            <bean:message key="actions.delete"/>
+          </div></td>
+          <td><div align="left" class="table-header-text">
+            <bean:message key="host.name"/>
+          </div></td>
+        </tr>
+
+        <logic:iterate name="servicesList" id="service">
+          <tr class="line-row">
+            <td><div align="left" class="table-normal-text">&nbsp;
+             <logic:match name="service"
+                        value='<%= (String)request.getAttribute("adminAppService") %>'>
+             <font color='red'>*</font>
+             </logic:match>
+             <logic:notMatch name="service"
+                        value='<%= (String)request.getAttribute("adminAppService") %>'>
+              <label for="services"></label>          
+              <html:multibox property="services"
+                                value="<%= service.toString() %>" styleId="services" styleId="services"/>
+              </logic:notMatch>
+            </div></td>
+            <td><div align="left" class="table-normal-text">&nbsp;
+              <html:link page='<%= "/EditService.do?select=" +
+                         java.net.URLEncoder.encode(service.toString(),"UTF-8") %>'>
+                <controls:attribute name="service" attribute="name"/>
+              </html:link>
+            </div></td>
+          </tr>
+        </logic:iterate>
+
+      </table>
+
+    </td></tr>
+  </table>
+
+<br>
+<font color='red'> * </font>
+Cannot delete the service the admin application is running on.
+
+<br>
+
+<%@ include file="../buttons.jsp" %>
+
+  <br>
+</html:form>
+
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/tree-control-test.css b/container/webapps/admin/tree-control-test.css
new file mode 100644
index 0000000..ba5466a
--- /dev/null
+++ b/container/webapps/admin/tree-control-test.css
@@ -0,0 +1,19 @@
+.tree-control {
+  font-family: arial, verdana, geneva, helvetica, sans-serif;
+  font-size: 80%;
+  line-height: 1.0;
+}
+
+.tree-control-selected {
+  color: black;
+  font-weight: bold;
+  text-decoration: none;
+}
+
+.tree-control-unselected {
+  color: black;
+  font-style: normal;
+  font-weight: normal;
+  text-decoration: none;
+}
+
diff --git a/container/webapps/admin/tree-control-test.jsp b/container/webapps/admin/tree-control-test.jsp
new file mode 100644
index 0000000..7387ca6
--- /dev/null
+++ b/container/webapps/admin/tree-control-test.jsp
@@ -0,0 +1,34 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<!-- Standard Content -->
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+
+<body bgcolor="white">
+
+<!-- Tree Component -->
+
+  <controls:tree tree="treeControlTest"
+               action="treeControlTest.do?tree={name}"
+                style="tree-control"
+        styleSelected="tree-control-selected"
+      styleUnselected="tree-control-unselected"
+  />
+
+</body>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</html:html>
diff --git a/container/webapps/admin/users/deleteGroups.jsp b/container/webapps/admin/users/deleteGroups.jsp
new file mode 100644
index 0000000..333edd5
--- /dev/null
+++ b/container/webapps/admin/users/deleteGroups.jsp
@@ -0,0 +1,52 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/users/listGroups">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="users.deleteGroups.title"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listGroups.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+<bean:define id="checkboxes" scope="page" value="true"/>
+<html:form action="/users/deleteGroups">
+  <%@ include file="../buttons.jsp" %>
+  <br>
+  <html:hidden property="databaseName"/>
+  <%@ include file="groups.jspf" %>
+  <%@ include file="../buttons.jsp" %>
+</html:form>
+<br>
+
+<%@ include file="footer.jsp" %>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/users/deleteRoles.jsp b/container/webapps/admin/users/deleteRoles.jsp
new file mode 100644
index 0000000..c70da04
--- /dev/null
+++ b/container/webapps/admin/users/deleteRoles.jsp
@@ -0,0 +1,52 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/users/listRoles">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="users.deleteRoles.title"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listRoles.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+<bean:define id="checkboxes" scope="page" value="true"/>
+<html:form action="/users/deleteRoles">
+  <%@ include file="../buttons.jsp" %>
+  <br>
+  <html:hidden property="databaseName"/>
+  <%@ include file="roles.jspf" %>
+  <%@ include file="../buttons.jsp" %>
+</html:form>
+<br>
+
+<%@ include file="footer.jsp" %>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/users/deleteUsers.jsp b/container/webapps/admin/users/deleteUsers.jsp
new file mode 100644
index 0000000..8c110b0
--- /dev/null
+++ b/container/webapps/admin/users/deleteUsers.jsp
@@ -0,0 +1,52 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/users/listUsers">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="users.deleteUsers.title"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listUsers.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+<bean:define id="checkboxes" scope="page" value="true"/>
+<html:form action="/users/deleteUsers">
+  <%@ include file="../buttons.jsp" %>
+  <br>
+  <html:hidden property="databaseName"/>
+  <%@ include file="users.jspf" %>
+  <%@ include file="../buttons.jsp" %>
+</html:form>
+<br>
+
+<%@ include file="footer.jsp" %>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/users/footer.jsp b/container/webapps/admin/users/footer.jsp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/container/webapps/admin/users/footer.jsp
diff --git a/container/webapps/admin/users/group.jsp b/container/webapps/admin/users/group.jsp
new file mode 100644
index 0000000..f54a588
--- /dev/null
+++ b/container/webapps/admin/users/group.jsp
@@ -0,0 +1,124 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/users/saveGroup"> <!--focus="groupname"-->
+
+  <html:hidden property="databaseName"/>
+  <html:hidden property="objectName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text">
+          <bean:write name="groupForm" property="nodeLabel"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+          <controls:actions label="Group Actions">
+            <controls:action selected="true">
+              ----<bean:message key="actions.available.actions"/>----
+            </controls:action>
+            <controls:action>
+              ---------------------------------
+            </controls:action>
+            <!-- will add the urls later once those screens get implemented -->
+<%--
+            <controls:action url="">
+              <bean:message key="users.actions.group.create"/>
+            </controls:action>
+            <controls:action url="">
+              <bean:message key="users.actions.group.delete"/>
+            </controls:action>
+--%>
+          </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+<br>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1"
+         width="100%">
+    <tr>
+      <td>
+
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+
+          <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>
+              <bean:message key="users.group.properties"/>
+            </controls:label>
+            <controls:data>
+              &nbsp;
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="groupname">
+            <controls:label>
+              <bean:message key="users.prompt.groupname"/>
+            </controls:label>
+            <controls:data>
+              <logic:present name="groupForm" property="objectName">
+                <bean:write name="groupForm" property="groupname"/>
+                <html:hidden property="groupname"/>
+              </logic:present>
+              <logic:notPresent name="groupForm" property="objectName">
+                <html:text property="groupname" size="24" maxlength="32" styleId="groupname"/>
+              </logic:notPresent>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="description">
+            <controls:label>
+              <bean:message key="users.prompt.description"/>
+            </controls:label>
+            <controls:data>
+              <html:text property="description" size="24" maxlength="128" styleId="description"/>
+            </controls:data>
+          </controls:row>
+
+        </controls:table>
+
+      </td>
+
+    </tr>
+
+  </table>
+
+  <bean:define id="checkboxes" scope="page" value="true"/>
+  <br>
+  <%@ include file="roles.jspf" %>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/users/groups.jspf b/container/webapps/admin/users/groups.jspf
new file mode 100644
index 0000000..8d2cd4f
--- /dev/null
+++ b/container/webapps/admin/users/groups.jspf
@@ -0,0 +1,54 @@
+<%-- Groups List --%>
+
+<table class="back-table" border="0" cellspacing="0" cellpadding="1"
+       width="100%"><tr><td> 
+
+  <table class="front-table" border="1"
+   cellspacing="0" cellpadding="0" width="100%">
+    <tr class="header-row">
+      <logic:present name="checkboxes">
+        <th scope="col" width="5%"><div align="left" class="table-header-text">
+          &nbsp;
+        </th>
+      </logic:present>
+      <th scope="col" width="20%"><div align="left" class="table-header-text">
+        <bean:message key="users.list.groupname"/>
+      </div></th>
+      <th scope="col"><div align="left" class="table-header-text">
+        <bean:message key="users.list.description"/>
+      </div></th>
+    </tr>
+    <logic:present name="groupsForm">
+    <logic:iterate name="groupsForm" property="groups"
+                     id="group" type="java.lang.String">
+      <tr class="line-row">
+        <logic:present name="checkboxes">
+          <td scope="row"><div align="center" class="table-normal-text">
+            <logic:present name="userForm">
+             <label for="groups"></label>
+              <html:multibox property="groups" value='<%= group %>' styleId="groups"/>
+            </logic:present>
+            <logic:notPresent name="userForm">
+              <label for="groups"></label>
+              <input type="checkbox" name="groups"
+                    value='<%= group %>' styleId="groups">
+            </logic:notPresent>
+          </td>
+        </logic:present>
+        <td scope="row"><div align="left" class="table-normal-text">&nbsp;
+          <html:link page='<%= "/users/setUpGroup.do?objectName=" + 
+                               URLEncoder.encode(group,"UTF-8") +
+                               "&databaseName=" +
+                               URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") %>'>
+            <controls:attribute name="group" attribute="groupname"/>
+          </html:link>
+        </div></td>
+        <td scope="row"><div align="left" class="table-normal-text">&nbsp;
+          <controls:attribute name="group" attribute="description"/>
+        </div></td>
+      </tr>
+    </logic:iterate>
+    </logic:present>
+  </table>
+
+</td></tr></table>
diff --git a/container/webapps/admin/users/header.jsp b/container/webapps/admin/users/header.jsp
new file mode 100644
index 0000000..cc598d1
--- /dev/null
+++ b/container/webapps/admin/users/header.jsp
@@ -0,0 +1,11 @@
+<!--
+  Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
+  reserved.
+-->
+
+<head>
+  <title><bean:message key="application.title"/></title>
+  <html:base/>
+  <link rel="stylesheet" type="text/css" href="../tree-control-test.css">
+  <link rel="stylesheet" type="text/css" href="../admin.css">
+</head>
diff --git a/container/webapps/admin/users/listGroups.jsp b/container/webapps/admin/users/listGroups.jsp
new file mode 100644
index 0000000..9053c3f
--- /dev/null
+++ b/container/webapps/admin/users/listGroups.jsp
@@ -0,0 +1,43 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/users/listGroups">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="users.listGroups.title"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listGroups.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+<%@ include file="groups.jspf" %>
+<br>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/users/listGroups.jspf b/container/webapps/admin/users/listGroups.jspf
new file mode 100644
index 0000000..2dc0251
--- /dev/null
+++ b/container/webapps/admin/users/listGroups.jspf
@@ -0,0 +1,32 @@
+<td align="right" nowrap>
+<div class="page-title-text">
+<controls:actions label="Group Actions">
+
+  <controls:action selected="true">
+    ----<bean:message key="actions.available.actions"/>----
+  </controls:action>
+
+  <controls:action>
+    ---------------------------------
+  </controls:action>
+
+  <controls:action url='<%= "/users/setUpGroup.do?databaseName=" +
+       URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") %>'>
+    <bean:message key="users.actions.group.create"/>
+  </controls:action>
+
+  <controls:action url='<%= "/users/listGroups.do?databaseName=" +
+       URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") +
+       "&forward=" + URLEncoder.encode("Groups Delete List","UTF-8") %>'>
+    <bean:message key="users.actions.group.delete"/>
+  </controls:action>
+
+  <controls:action url='<%= "/users/listGroups.do?databaseName=" +
+       URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") +
+       "&forward=" + URLEncoder.encode("Groups List","UTF-8") %>'>
+    <bean:message key="users.actions.group.list"/>
+  </controls:action>
+
+</controls:actions>
+</div>
+</td>
diff --git a/container/webapps/admin/users/listRoles.jsp b/container/webapps/admin/users/listRoles.jsp
new file mode 100644
index 0000000..3f60bfc
--- /dev/null
+++ b/container/webapps/admin/users/listRoles.jsp
@@ -0,0 +1,43 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/users/listRoles">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="users.listRoles.title"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listRoles.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+<%@ include file="roles.jspf" %>
+<br>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/users/listRoles.jspf b/container/webapps/admin/users/listRoles.jspf
new file mode 100644
index 0000000..7432adc
--- /dev/null
+++ b/container/webapps/admin/users/listRoles.jspf
@@ -0,0 +1,32 @@
+<td align="right" nowrap>
+<div class="page-title-text">
+<controls:actions label="Role Actions">
+
+  <controls:action selected="true">
+    ----<bean:message key="actions.available.actions"/>----
+  </controls:action>
+
+  <controls:action>
+    ---------------------------------
+  </controls:action>
+
+  <controls:action url='<%= "/users/setUpRole.do?databaseName=" +
+       URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") %>'>
+    <bean:message key="users.actions.role.create"/>
+  </controls:action>
+
+  <controls:action url='<%= "/users/listRoles.do?databaseName=" +
+       URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") +
+       "&forward=" + URLEncoder.encode("Roles Delete List","UTF-8") %>'>
+    <bean:message key="users.actions.role.delete"/>
+  </controls:action>
+
+  <controls:action url='<%= "/users/listRoles.do?databaseName=" +
+       URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") +
+       "&forward=" + URLEncoder.encode("Roles List","UTF-8") %>'>
+    <bean:message key="users.actions.role.list"/>
+  </controls:action>
+
+</controls:actions>
+</div>
+</td>
diff --git a/container/webapps/admin/users/listUsers.jsp b/container/webapps/admin/users/listUsers.jsp
new file mode 100644
index 0000000..bb8f0d7
--- /dev/null
+++ b/container/webapps/admin/users/listUsers.jsp
@@ -0,0 +1,43 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form action="/users/listUsers">
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="users.listUsers.title"/>
+        </div>
+      </td>
+      <td width="19%">
+        <div align="right">
+          <%@ include file="listUsers.jspf" %>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+</html:form>
+
+<br>
+<%@ include file="users.jspf" %>
+<br>
+
+</body>
+</html:html>
diff --git a/container/webapps/admin/users/listUsers.jspf b/container/webapps/admin/users/listUsers.jspf
new file mode 100644
index 0000000..8e98c79
--- /dev/null
+++ b/container/webapps/admin/users/listUsers.jspf
@@ -0,0 +1,32 @@
+<td align="right" nowrap>
+<div class="page-title-text">
+<controls:actions label="User Actions">
+
+  <controls:action selected="true">
+    ----<bean:message key="actions.available.actions"/>----
+  </controls:action>
+
+  <controls:action>
+    ---------------------------------
+  </controls:action>
+
+  <controls:action url='<%= "/users/setUpUser.do?databaseName=" +
+       URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") %>'>
+    <bean:message key="users.actions.user.create"/>
+  </controls:action>
+
+  <controls:action url='<%= "/users/listUsers.do?databaseName=" +
+       URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") +
+       "&forward=" + URLEncoder.encode("Users Delete List","UTF-8") %>'>
+    <bean:message key="users.actions.user.delete"/>
+  </controls:action>
+
+  <controls:action url='<%= "/users/listUsers.do?databaseName=" +
+       URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") +
+       "&forward=" + URLEncoder.encode("Users List","UTF-8") %>'>
+    <bean:message key="users.actions.user.list"/>
+  </controls:action>
+
+</controls:actions>
+</div>
+</td>
diff --git a/container/webapps/admin/users/role.jsp b/container/webapps/admin/users/role.jsp
new file mode 100644
index 0000000..de0db15
--- /dev/null
+++ b/container/webapps/admin/users/role.jsp
@@ -0,0 +1,120 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/users/saveRole"> <!--focus="rolename"-->
+
+  <html:hidden property="databaseName"/>
+  <html:hidden property="objectName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text">
+          <bean:write name="roleForm" property="nodeLabel"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+          <controls:actions label="Role Actions">
+            <controls:action selected="true">
+              ----<bean:message key="actions.available.actions"/>----
+            </controls:action>
+            <controls:action>
+              ---------------------------------
+            </controls:action>
+            <!-- will add the urls later once those screens get implemented -->
+<%--
+            <controls:action url="">
+              <bean:message key="users.actions.role.create"/>
+            </controls:action>
+            <controls:action url="">
+              <bean:message key="users.actions.role.delete"/>
+            </controls:action>
+--%>
+          </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+<br>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1"
+         width="100%">
+    <tr>
+      <td>
+
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+
+          <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>
+              <bean:message key="users.role.properties"/>
+            </controls:label>
+            <controls:data>
+              &nbsp;
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="rolename">
+            <controls:label>
+              <bean:message key="users.prompt.rolename"/>
+            </controls:label>
+            <controls:data>
+              <logic:present name="roleForm" property="objectName">
+                <bean:write name="roleForm" property="rolename"/>
+                <html:hidden property="rolename"/>
+              </logic:present>
+              <logic:notPresent name="roleForm" property="objectName">
+                <html:text property="rolename" size="24" maxlength="32" styleId="rolename"/>
+              </logic:notPresent>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="description">
+            <controls:label>
+              <bean:message key="users.prompt.description"/>:
+            </controls:label>
+            <controls:data>
+              <html:text property="description" size="24" maxlength="128" styleId="description"/>
+            </controls:data>
+          </controls:row>
+
+        </controls:table>
+
+      </td>
+
+    </tr>
+
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/users/roles.jspf b/container/webapps/admin/users/roles.jspf
new file mode 100644
index 0000000..6a5eb87
--- /dev/null
+++ b/container/webapps/admin/users/roles.jspf
@@ -0,0 +1,60 @@
+<%-- Roles List --%>
+
+<table class="back-table" border="0" cellspacing="0" cellpadding="1"
+       width="100%"><tr><td> 
+
+  <table class="front-table" border="1"
+   cellspacing="0" cellpadding="0" width="100%">
+    <tr class="header-row">
+      <logic:present name="checkboxes">
+        <td width="5%"><div align="left" class="table-header-text">
+          &nbsp;
+        </td>
+      </logic:present>
+      <th scope="col" width="20%"><div align="left" class="table-header-text">
+        <bean:message key="users.list.rolename"/>
+      </div></th>
+      <th scope="col"><div align="left" class="table-header-text">
+        <bean:message key="users.list.description"/>
+      </div></th>
+    </tr>
+    <logic:present name="rolesForm">
+    <logic:iterate name="rolesForm" property="roles"
+                     id="role" type="java.lang.String">
+      <tr class="line-row">
+        <logic:present name="checkboxes">
+          <td scope="row"><div align="center" class="table-normal-text">
+            <logic:present name="groupForm">
+              <label for="roles"></label>
+              <html:multibox property="roles" value="<%= role %>" styleId="roles"/>
+            </logic:present>
+            <logic:present name="userForm">
+              <label for="roles"></label>
+              <html:multibox property="roles" value="<%= role %>" styleId="roles"/>
+            </logic:present>
+            <logic:notPresent name="groupForm">
+              <logic:notPresent name="userForm">
+                <label for="roles"></label>
+                <input type="checkbox" name="roles"
+                      value="<%= role %>" styleId="roles">
+              </logic:notPresent>
+            </logic:notPresent>
+          </td>
+        </logic:present>
+        <td scope="row"><div align="left" class="table-normal-text">&nbsp;
+          <html:link page='<%= "/users/setUpRole.do?objectName=" + 
+                               URLEncoder.encode(role,"UTF-8") +
+                               "&databaseName=" +
+                               URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") %>'>
+            <controls:attribute name="role" attribute="rolename"/>
+          </html:link>
+        </div></td>
+        <td scope="row"><div align="left" class="table-normal-text">&nbsp;
+          <controls:attribute name="role" attribute="description"/>
+        </div></td>
+      </tr>
+    </logic:iterate>
+    </logic:present>
+  </table>
+
+</td></tr></table>
diff --git a/container/webapps/admin/users/user.jsp b/container/webapps/admin/users/user.jsp
new file mode 100644
index 0000000..591f7ce
--- /dev/null
+++ b/container/webapps/admin/users/user.jsp
@@ -0,0 +1,137 @@
+<!-- Standard Struts Entries -->
+
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/users/saveUser"> <!--focus="username"-->
+
+  <html:hidden property="databaseName"/>
+  <html:hidden property="objectName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr class="page-title-row">
+      <td align="left" nowrap>
+        <div class="page-title-text">
+          <bean:write name="userForm" property="nodeLabel"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+          <controls:actions label="User Actions">
+            <controls:action selected="true">
+              ----<bean:message key="actions.available.actions"/>----
+            </controls:action>
+            <controls:action>
+              ---------------------------------
+            </controls:action>
+            <controls:action url='<%= "/users/setUpUser.do?databaseName=" +
+            URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") %>'>
+                <bean:message key="users.actions.user.create"/>
+            </controls:action>
+
+            <controls:action url='<%= "/users/listUsers.do?databaseName=" +
+                URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") +
+                "&forward=" + URLEncoder.encode("Users Delete List","UTF-8") %>'>
+                <bean:message key="users.actions.user.delete"/>
+            </controls:action>
+         </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+  <%@ include file="../buttons.jsp" %>
+<br>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1"
+         width="100%">
+    <tr>
+      <td>
+
+        <controls:table tableStyle="front-table" lineStyle="line-row">
+
+          <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label>
+              <bean:message key="users.user.properties"/>
+            </controls:label>
+            <controls:data>
+              &nbsp;
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="username">
+            <controls:label>
+              <bean:message key="users.prompt.username"/>
+            </controls:label>
+            <controls:data>
+              <logic:present name="userForm" property="objectName">
+                <bean:write name="userForm" property="username"/>
+                <html:hidden property="username"/>
+              </logic:present>
+              <logic:notPresent name="userForm" property="objectName">
+                <html:text property="username" size="24" styleId="username"/>
+              </logic:notPresent>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="password">
+            <controls:label>
+              <bean:message key="users.prompt.password"/>
+            </controls:label>
+            <controls:data>
+              <html:password property="password" size="24" styleId="password"/>
+            </controls:data>
+          </controls:row>
+
+          <controls:row labelStyle="table-label-text"
+                         dataStyle="table-normal-text" styleId="fullname">
+            <controls:label>
+              <bean:message key="users.prompt.fullName"/>
+            </controls:label>
+            <controls:data>
+              <html:text property="fullName" size="24" maxlength="64" styleId="fullname"/>
+            </controls:data>
+          </controls:row>
+
+        </controls:table>
+
+      </td>
+
+    </tr>
+
+  </table>
+
+  <bean:define id="checkboxes" scope="page" value="true"/>
+  <br>
+  <%@ include file="groups.jspf" %>
+  <br>
+  <%@ include file="roles.jspf" %>
+
+  <%@ include file="../buttons.jsp" %>
+
+</html:form>
+
+<!-- Standard Footer -->
+
+<%@ include file="footer.jsp" %>
+
+</body>
+
+</html:html>
diff --git a/container/webapps/admin/users/users.jspf b/container/webapps/admin/users/users.jspf
new file mode 100644
index 0000000..e355ebb
--- /dev/null
+++ b/container/webapps/admin/users/users.jspf
@@ -0,0 +1,46 @@
+<%-- Users List --%>
+
+<table class="back-table" border="0" cellspacing="0" cellpadding="1"
+       width="100%"><tr><td> 
+
+  <table class="front-table" border="1"
+   cellspacing="0" cellpadding="0" width="100%">
+    <tr class="header-row">
+      <logic:present name="checkboxes">
+        <td width="5%"><div align="left" class="table-header-text">
+          &nbsp;
+        </td>
+      </logic:present>
+      <th scope="col" width="20%"><div align="left" class="table-header-text">
+        <bean:message key="users.list.username"/>
+      </div></th>
+      <th scope="col"><div align="left" class="table-header-text">
+        <bean:message key="users.list.fullName"/>
+      </div></th>
+    </tr>
+    <logic:iterate name="usersForm" property="users"
+                     id="user" type="java.lang.String">
+      <tr class="line-row">
+        <logic:present name="checkboxes">
+          <td scope="row"><div align="center" class="table-normal-text">
+            <label for="users"></label>
+            <input type="checkbox" name="users"
+                  value='<%= user %>' styleId="users">
+          </td>
+        </logic:present>
+        <td scope="row"><div align="left" class="table-normal-text">&nbsp;
+          <html:link page='<%= "/users/setUpUser.do?objectName=" + 
+                               URLEncoder.encode(user,"UTF-8") +
+                               "&databaseName=" +
+                               URLEncoder.encode(request.getParameter("databaseName"),"UTF-8") %>'>
+            <controls:attribute name="user" attribute="username"/>
+          </html:link>
+        </div></td>
+        <td scope="row"><div align="left" class="table-normal-text">&nbsp;
+          <controls:attribute name="user" attribute="fullName"/>
+        </div></td>
+      </tr>
+    </logic:iterate>
+  </table>
+
+</td></tr></table>
diff --git a/container/webapps/admin/valve/accessLogValve.jsp b/container/webapps/admin/valve/accessLogValve.jsp
new file mode 100644
index 0000000..40ed921
--- /dev/null
+++ b/container/webapps/admin/valve/accessLogValve.jsp
@@ -0,0 +1,153 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveAccessLogValve">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="accessLogValveForm" property="objectName"/>
+  <bean:define id="thisParentName" type="java.lang.String"
+               name="accessLogValveForm" property="parentObjectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="valveType"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+         <logic:equal name="accessLogValveForm" property="adminAction" value="Create">
+            <bean:message key="actions.valves.create"/>
+          </logic:equal>
+          <logic:equal name="accessLogValveForm" property="adminAction" value="Edit">
+            <bean:write name="accessLogValveForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Valve Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="accessLogValveForm" property="adminAction" value="Create">
+             <controls:action url='<%= "/DeleteValve.do?"  +
+                                 "select=" + URLEncoder.encode(thisObjectName,"UTF-8") +
+                                 "&parent="+ URLEncoder.encode(thisParentName,"UTF-8") %>'>
+                <bean:message key="actions.valves.delete"/>
+              </controls:action>
+             </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+ <%-- Access Log Properties --%>
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td> <div class="table-title-text">
+        <bean:message key="valve.access.properties"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+            <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="accessLogValveForm" property="adminAction" value="Create">
+                    <html:select property="valveType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="valveTypeVals" name="accessLogValveForm" property="valveTypeVals"/>
+                     <html:options collection="valveTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="accessLogValveForm" property="adminAction" value="Edit">
+                  <bean:write name="accessLogValveForm" property="valveType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="directory">
+            <controls:label><bean:message key="logger.directory"/>:</controls:label>
+            <controls:data>
+              <html:text property="directory" size="30" styleId="directory"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="pattern">
+            <controls:label><bean:message key="valve.pattern"/>:</controls:label>
+            <controls:data>
+                <html:textarea property="pattern" cols="30" rows="2" styleId="pattern"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="prefix">
+            <controls:label><bean:message key="logger.prefix"/>:</controls:label>
+            <controls:data>
+                <html:text property="prefix" size="30" styleId="prefix"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="resolveHosts">
+            <controls:label><bean:message key="valve.resolveHosts"/>:</controls:label>
+            <controls:data>
+                <html:select property="resolveHosts" styleId="resolveHosts">
+                     <bean:define id="booleanVals" name="accessLogValveForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="rotatable">
+            <controls:label><bean:message key="valve.rotatable"/>:</controls:label>
+            <controls:data>
+                <html:select property="rotatable" styleId="rotatable">
+                     <bean:define id="booleanVals" name="accessLogValveForm" property="booleanVals"/>
+                     <html:options collection="booleanVals" property="value"
+                   labelProperty="label"/>
+                </html:select>
+            </controls:data>
+        </controls:row>
+        
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="suffix">
+            <controls:label><bean:message key="logger.suffix"/>:</controls:label>
+            <controls:data>
+                <html:text property="suffix" size="30" styleId="suffix"/>
+            </controls:data>
+        </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/valve/remoteAddrValve.jsp b/container/webapps/admin/valve/remoteAddrValve.jsp
new file mode 100644
index 0000000..62ae5d3
--- /dev/null
+++ b/container/webapps/admin/valve/remoteAddrValve.jsp
@@ -0,0 +1,117 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveRemoteAddrValve">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="remoteAddrValveForm" property="objectName"/>
+  <bean:define id="thisParentName" type="java.lang.String"
+               name="remoteAddrValveForm" property="parentObjectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="valveType"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+         <logic:equal name="remoteAddrValveForm" property="adminAction" value="Create">
+            <bean:message key="actions.valves.create"/>
+          </logic:equal>
+          <logic:equal name="remoteAddrValveForm" property="adminAction" value="Edit">
+            <bean:write name="remoteAddrValveForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Valve Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="remoteAddrValveForm" property="adminAction" value="Create">
+             <controls:action url='<%= "/DeleteValve.do?"  +
+                                 "select=" + URLEncoder.encode(thisObjectName,"UTF-8") +
+                                 "&parent="+ URLEncoder.encode(thisParentName,"UTF-8") %>'>
+                <bean:message key="actions.valves.delete"/>
+              </controls:action>
+             </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+ <%-- Remote Addr Valve Properties --%>
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td> <div class="table-title-text">
+        <bean:message key="valve.remoteaddress.properties"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+            <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="remoteAddrValveForm" property="adminAction" value="Create">
+                    <html:select property="valveType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="valveTypeVals" name="remoteAddrValveForm" property="valveTypeVals"/>
+                     <html:options collection="valveTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="remoteAddrValveForm" property="adminAction" value="Edit">
+                  <bean:write name="remoteAddrValveForm" property="valveType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="allowIPs">
+            <controls:label><bean:message key="valve.allowIPs"/>:</controls:label>
+            <controls:data>
+                <html:textarea property="allow" cols="30" rows="3" styleId="allowIPs"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="denyIPs">
+            <controls:label><bean:message key="valve.denyIPs"/>:</controls:label>
+            <controls:data>
+                <html:textarea property="deny" cols="30" rows="3" styleId="denyIPs"/>
+            </controls:data>
+        </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/valve/remoteHostValve.jsp b/container/webapps/admin/valve/remoteHostValve.jsp
new file mode 100644
index 0000000..29b8bff
--- /dev/null
+++ b/container/webapps/admin/valve/remoteHostValve.jsp
@@ -0,0 +1,117 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveRemoteHostValve">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="remoteHostValveForm" property="objectName"/>
+  <bean:define id="thisParentName" type="java.lang.String"
+               name="remoteHostValveForm" property="parentObjectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="valveType"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+         <logic:equal name="remoteHostValveForm" property="adminAction" value="Create">
+            <bean:message key="actions.valves.create"/>
+          </logic:equal>
+          <logic:equal name="remoteHostValveForm" property="adminAction" value="Edit">
+            <bean:write name="remoteHostValveForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Valve Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="remoteHostValveForm" property="adminAction" value="Create">
+             <controls:action url='<%= "/DeleteValve.do?"  +
+                                 "select=" + URLEncoder.encode(thisObjectName,"UTF-8") +
+                                 "&parent="+ URLEncoder.encode(thisParentName,"UTF-8") %>'>
+                <bean:message key="actions.valves.delete"/>
+              </controls:action>
+              </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+ <%-- RemoteHost Valve Properties --%>
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td> <div class="table-title-text">
+        <bean:message key="valve.remotehost.properties"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+            <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="remoteHostValveForm" property="adminAction" value="Create">
+                    <html:select property="valveType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="valveTypeVals" name="remoteHostValveForm" property="valveTypeVals"/>
+                     <html:options collection="valveTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="remoteHostValveForm" property="adminAction" value="Edit">
+                  <bean:write name="remoteHostValveForm" property="valveType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="allowHosts">
+            <controls:label><bean:message key="valve.allowHosts"/>:</controls:label>
+            <controls:data>
+                <html:textarea property="allow" cols="30" rows="3" styleId="allowHosts"/>
+            </controls:data>
+        </controls:row>
+
+        <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="denyHosts">
+            <controls:label><bean:message key="valve.denyHosts"/>:</controls:label>
+            <controls:data>
+                <html:textarea property="deny" cols="30" rows="3" styleId="denyHosts"/>
+            </controls:data>
+        </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/valve/requestDumperValve.jsp b/container/webapps/admin/valve/requestDumperValve.jsp
new file mode 100644
index 0000000..4a65032
--- /dev/null
+++ b/container/webapps/admin/valve/requestDumperValve.jsp
@@ -0,0 +1,104 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveRequestDumperValve">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="requestDumperValveForm" property="objectName"/>
+  <bean:define id="thisParentName" type="java.lang.String"
+               name="requestDumperValveForm" property="parentObjectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="valveType"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+         <logic:equal name="requestDumperValveForm" property="adminAction" value="Create">
+            <bean:message key="actions.valves.create"/>
+          </logic:equal>
+          <logic:equal name="requestDumperValveForm" property="adminAction" value="Edit">
+            <bean:write name="requestDumperValveForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Valve Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="requestDumperValveForm" property="adminAction" value="Create">
+             <controls:action url='<%= "/DeleteValve.do?"  +
+                                 "select=" + URLEncoder.encode(thisObjectName,"UTF-8") +
+                                 "&parent="+ URLEncoder.encode(thisParentName,"UTF-8") %>'>
+                <bean:message key="actions.valves.delete"/>
+              </controls:action>
+              </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+ <%-- Request Dumper Valve Properties --%>
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td> <div class="table-title-text">
+        <bean:message key="valve.request.properties"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+            <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="requestDumperValveForm" property="adminAction" value="Create">
+                    <html:select property="valveType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="valveTypeVals" name="requestDumperValveForm" property="valveTypeVals"/>
+                     <html:options collection="valveTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="requestDumperValveForm" property="adminAction" value="Edit">
+                  <bean:write name="requestDumperValveForm" property="valveType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/valve/singleSignOnValve.jsp b/container/webapps/admin/valve/singleSignOnValve.jsp
new file mode 100644
index 0000000..ac04dc7
--- /dev/null
+++ b/container/webapps/admin/valve/singleSignOnValve.jsp
@@ -0,0 +1,103 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="POST" action="/SaveSingleSignOn">
+
+  <bean:define id="thisObjectName" type="java.lang.String"
+               name="singleSignOnValveForm" property="objectName"/>
+  <bean:define id="thisParentName" type="java.lang.String"
+               name="singleSignOnValveForm" property="parentObjectName"/>
+  <html:hidden property="adminAction"/>
+  <html:hidden property="parentObjectName"/>
+  <html:hidden property="objectName"/>
+  <html:hidden property="valveType"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+       <div class="page-title-text" align="left">
+         <logic:equal name="singleSignOnValveForm" property="adminAction" value="Create">
+            <bean:message key="actions.valves.create"/>
+          </logic:equal>
+          <logic:equal name="singleSignOnValveForm" property="adminAction" value="Edit">
+            <bean:write name="singleSignOnValveForm" property="nodeLabel"/>
+          </logic:equal>
+       </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+      <controls:actions label="Valve Actions">
+            <controls:action selected="true"> ----<bean:message key="actions.available.actions"/>---- </controls:action>
+            <controls:action> --------------------------------- </controls:action>
+            <logic:notEqual name="singleSignOnValveForm" property="adminAction" value="Create">
+             <controls:action url='<%= "/DeleteValve.do?"  +
+                                 "select=" + URLEncoder.encode(thisObjectName,"UTF-8") +
+                                 "&parent="+ URLEncoder.encode(thisParentName,"UTF-8") %>'>
+                <bean:message key="actions.valves.delete"/>
+              </controls:action>
+              </logic:notEqual>
+       </controls:actions>
+         </div>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+
+ <%-- Single Sign On Valve Properties --%>
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr> <td> <div class="table-title-text">
+        <bean:message key="valve.single.properties"/>
+    </div> </td> </tr>
+  </table>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="0" width="100%">
+    <tr>
+      <td>
+       <controls:table tableStyle="front-table" lineStyle="line-row">
+            <controls:row header="true"
+                labelStyle="table-header-text" dataStyle="table-header-text">
+            <controls:label><bean:message key="service.property"/></controls:label>
+            <controls:data><bean:message key="service.value"/></controls:data>
+        </controls:row>
+
+      <controls:row labelStyle="table-label-text" dataStyle="table-normal-text" styleId="type">
+            <controls:label><bean:message key="connector.type"/>:</controls:label>
+            <controls:data>
+                 <logic:equal name="singleSignOnValveForm" property="adminAction" value="Create">
+                    <html:select property="valveType" onchange="IA_jumpMenu('self',this)" styleId="type">
+                     <bean:define id="valveTypeVals" name="singleSignOnValveForm" property="valveTypeVals"/>
+                     <html:options collection="valveTypeVals" property="value" labelProperty="label"/>
+                    </html:select>
+                </logic:equal>
+                <logic:equal name="singleSignOnValveForm" property="adminAction" value="Edit">
+                  <bean:write name="singleSignOnValveForm" property="valveType" scope="session"/>
+                </logic:equal>
+            </controls:data>
+        </controls:row>
+
+      </controls:table>
+      </td>
+    </tr>
+  </table>
+    <%@ include file="../buttons.jsp" %>
+  <br>
+  </html:form>
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/admin/valve/valves.jsp b/container/webapps/admin/valve/valves.jsp
new file mode 100644
index 0000000..36183cf
--- /dev/null
+++ b/container/webapps/admin/valve/valves.jsp
@@ -0,0 +1,97 @@
+<!-- Standard Struts Entries -->
+<%@ page language="java" import="java.net.URLEncoder" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
+<%@ taglib uri="/WEB-INF/controls.tld" prefix="controls" %>
+
+<html:html locale="true">
+
+<%@ include file="../users/header.jsp" %>
+
+<!-- Body -->
+<body bgcolor="white" background="../images/PaperTexture.gif">
+
+<!--Form -->
+
+<html:errors/>
+
+<html:form method="post" action="/DeleteValves">
+
+  <bean:define id="thisParentName" type="java.lang.String"
+               name="valvesForm" property="parentObjectName"/>
+  <html:hidden property="parentObjectName"/>
+
+  <table width="100%" border="0" cellspacing="0" cellpadding="0">
+    <tr bgcolor="7171A5">
+      <td width="81%">
+        <div class="page-title-text" align="left">
+          <bean:message key="actions.valves.delete"/>
+        </div>
+      </td>
+      <td align="right" nowrap>
+        <div class="page-title-text">
+            <controls:actions label="Valve Actions">
+              <controls:action selected="true">
+                ----<bean:message key="actions.available.actions"/>----
+              </controls:action>
+              <controls:action disabled="true">
+                ---------------------------------
+              </controls:action>
+            </controls:actions>
+        </div>
+      </td>
+    </tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+  <br>
+
+  <%-- Valves List --%>
+
+  <table class="back-table" border="0" cellspacing="0" cellpadding="1"
+         width="100%">
+    <tr><td>
+
+      <table class="front-table" border="1"
+       cellspacing="0" cellpadding="0" width="100%">
+
+        <tr class="header-row">
+          <td><div align="left" class="table-header-text">
+            <bean:message key="actions.delete"/>
+          </div></td>
+          <td><div align="left" class="table-header-text">
+            <bean:message key="host.name"/>
+          </div></td>
+        </tr>
+
+        <logic:iterate name="valvesList" id="valve">
+          <tr class="line-row">
+            <td><div align="left" class="table-normal-text">&nbsp;
+            <label for="valves"></label>
+              <html:multibox property="valves"
+                                value="<%= valve.toString() %>" styleId="valves"/>
+            </div></td>
+            <td><div align="left" class="table-normal-text">&nbsp;
+              <html:link page='<%= "/EditValve.do?select=" +
+                         java.net.URLEncoder.encode(valve.toString(),"UTF-8") +
+                         "&parent="+ URLEncoder.encode(thisParentName,"UTF-8") %>'>
+                <controls:attribute name="valve" attribute="className"/>
+              </html:link>
+            </div></td>
+          </tr>
+        </logic:iterate>
+
+      </table>
+
+    </td></tr>
+  </table>
+
+<%@ include file="../buttons.jsp" %>
+
+  <br>
+</html:form>
+
+<p>&nbsp;</p>
+</body>
+</html:html>
diff --git a/container/webapps/balancer/META-INF/context.xml b/container/webapps/balancer/META-INF/context.xml
new file mode 100644
index 0000000..b3cf755
--- /dev/null
+++ b/container/webapps/balancer/META-INF/context.xml
@@ -0,0 +1,12 @@
+<!--
+
+    Context configuration file for the Tomcat Balancer Web App
+    This is only needed to keep the distribution small and avoid duplicating
+    commons libraries
+
+    $Id$
+
+-->
+
+
+<Context privileged="true" antiResourceLocking="false" antiJARLocking="false" />
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/BalancerFilter.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/BalancerFilter.java
new file mode 100644
index 0000000..8372a1a
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/BalancerFilter.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.net.URL;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * The balancer filter redirects incoming requests
+ * based on what rules they match.  The rules
+ * are configurable via an XML document whose URL
+ * is specified as an init-param to this filter.
+ *
+ * @author Yoav Shapira
+ */
+public class BalancerFilter implements Filter {
+    /**
+     * The rules this filter consults.
+     */
+    private RuleChain ruleChain;
+
+    /**
+     * The servlet context.
+     */
+    private ServletContext context;
+
+    /**
+     * Returns the rule chain.
+     *
+     * @return The rule chain
+     */
+    protected RuleChain getRuleChain() {
+        return ruleChain;
+    }
+
+    /**
+     * Initialize this filter.
+     *
+     * @param filterConfig The filter config
+     * @throws ServletException If an error occurs
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+        context = filterConfig.getServletContext();
+
+        String configUrlParam = filterConfig.getInitParameter("configUrl");
+
+        if (configUrlParam == null) {
+            throw new ServletException("configUrl is required.");
+        }
+
+        try {
+            InputStream input = context.getResourceAsStream(configUrlParam);
+            RulesParser parser = new RulesParser(input);
+            ruleChain = parser.getResult();
+            context.log(
+                getClass().getName() + ": init(): ruleChain: " + ruleChain);
+        } catch (Exception e) {
+            throw new ServletException(e);
+        }
+    }
+
+    /**
+     * Filter the incoming request.
+     * Consults the rule chain to see if
+     * any rules match this request, and if
+     * so redirects.  Otherwise simply
+     * let request through.
+     *
+     * @param request The request
+     * @param response The response
+     * @param chain The filter chain
+     * @throws IOException If an error occurs
+     * @throws ServletException If an error occurs
+     */
+    public void doFilter(
+        ServletRequest request, ServletResponse response, FilterChain chain)
+        throws IOException, ServletException {
+        if (response.isCommitted()) {
+            context.log(
+                getClass().getName()
+                + ": doFilter(): not inspecting committed response.");
+            chain.doFilter(request, response);
+        } else if (!(request instanceof HttpServletRequest)) {
+            context.log(
+                getClass().getName()
+                + ": doFilter(): not inspecting non-Http request.");
+            chain.doFilter(request, response);
+        } else {
+            HttpServletRequest hreq = (HttpServletRequest) request;
+            HttpServletResponse hres = (HttpServletResponse) response;
+
+            URL redirectUrl = getRuleChain().evaluate(hreq);
+
+            if (redirectUrl != null) {
+                String encoded =
+                    hres.encodeRedirectURL(redirectUrl.toString());
+
+                context.log(
+                    getClass().getName()
+                    + ": doFilter(): redirecting request for "
+                    + hreq.getRequestURL().toString() + " to " + encoded);
+
+                hres.sendRedirect(encoded);
+            } else {
+                chain.doFilter(request, response);
+            }
+        }
+    }
+
+    /**
+     * Destroy this filter.
+     */
+    public void destroy() {
+        context = null;
+        ruleChain = null;
+    }
+}
+
+
+// End of file: BalanceFilter.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/Rule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/Rule.java
new file mode 100644
index 0000000..ab22495
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/Rule.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * The Rule interface is implemented by
+ * load balancing rules.
+ *
+ * @author Yoav Shapira
+ */
+public interface Rule {
+    /**
+     * Determine if the given request
+     * matches the rule.
+     *
+     * @param request The request
+     * @return boolean True if matches, will be redirected.
+     */
+    boolean matches(HttpServletRequest request);
+
+    /**
+     * Returns the redirect URL for
+     * requests that match this rule.
+     *
+     * @return The redirect URL
+     */
+    String getRedirectUrl();
+}
+
+
+// End of file: Rule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/RuleChain.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/RuleChain.java
new file mode 100644
index 0000000..a7a738c
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/RuleChain.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer;
+
+import java.net.URL;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * A RuleChain is a list of rules
+ * considered in order.  The first
+ * rule to succeed stops the evaluation
+ * of rules.
+ *
+ * @author Yoav Shapira
+ */
+public class RuleChain {
+    /**
+     * The list of rules to evaluate.
+     */
+    private List rules;
+
+    /**
+     * Constructor.
+     */
+    public RuleChain() {
+        rules = new ArrayList();
+    }
+
+    /**
+     * Returns the list of rules
+     * to evaluate.
+     *
+     * @return List
+     */
+    protected List getRules() {
+        return rules;
+    }
+
+    /**
+     * Returns an iterator over
+     * the list of rules to evaluate.
+     *
+     * @return Iterator
+     */
+    protected Iterator getRuleIterator() {
+        return getRules().iterator();
+    }
+
+    /**
+     * Adds a rule to evaluate.
+     *
+     * @param theRule The rule to add
+     */
+    public void addRule(Rule theRule) {
+        if (theRule == null) {
+            throw new IllegalArgumentException("The rule cannot be null.");
+        } else {
+            getRules().add(theRule);
+        }
+    }
+
+    /**
+     * Evaluates the given request to see if
+     * any of the rules matches.  Returns the
+     * redirect URL for the first matching
+     * rule.  Returns null if no rules match
+     * the request.
+     *
+     * @param request The request
+     * @return URL The first matching rule URL
+     * @see Rule#matches(HttpServletRequest)
+     */
+    public URL evaluate(HttpServletRequest request) {
+        Iterator iter = getRuleIterator();
+
+        Rule currentRule = null;
+        boolean currentMatches = false;
+
+        while (iter.hasNext()) {
+            currentRule = (Rule) iter.next();
+            currentMatches = currentRule.matches(request);
+
+            if (currentMatches) {
+                try {
+                    return new URL(currentRule.getRedirectUrl());
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        Iterator iter = getRuleIterator();
+        Rule currentRule = null;
+
+        while (iter.hasNext()) {
+            currentRule = (Rule) iter.next();
+            buffer.append(currentRule);
+
+            if (iter.hasNext()) {
+                buffer.append(", ");
+            }
+        }
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: RuleChain.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/RulesParser.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/RulesParser.java
new file mode 100644
index 0000000..6e50490
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/RulesParser.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer;
+
+import org.apache.tomcat.util.digester.Digester;
+
+import java.io.InputStream;
+
+
+/**
+ * The rules parser uses Digester
+ * to parse the rules definition
+ * file and return a RuleChain object.
+ *
+ * @author Yoav Shapira
+ */
+public class RulesParser {
+    /**
+     * The resulting rule chain.
+     */
+    private RuleChain result;
+
+    /**
+     * Constructor.
+     *
+     * @param input To read the configuration
+     */
+    public RulesParser(InputStream input) {
+        try {
+            Digester digester = createDigester();
+            result = (RuleChain) digester.parse(input);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns the parsed rule chain.
+     *
+     * @return The resulting RuleChain
+     */
+    public RuleChain getResult() {
+        return result;
+    }
+
+    /**
+     * Creates the digester instance.
+     *
+     * @return Digester
+     */
+    protected Digester createDigester() {
+        Digester digester = new Digester();
+        digester.setUseContextClassLoader(true);
+
+        String rules = "rules";
+        String rule = "/rule";
+
+        // Construct rule chain
+        digester.addObjectCreate(rules, RuleChain.class);
+
+        // Construct rule
+        digester.addObjectCreate(rules + rule, null, "className");
+
+        // Set rule properties
+        digester.addSetProperties(rules + rule);
+
+        // Add rule to chain
+        digester.addSetNext(rules + rule, "addRule", "org.apache.webapp.balancer.Rule");
+
+        return digester;
+    }
+}
+
+
+// End of class: RulesParser.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/overview.html b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/overview.html
new file mode 100644
index 0000000..e69852f
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/overview.html
@@ -0,0 +1,50 @@
+<html>
+<head>
+<title>Balancer Webapp Overview</title>
+</head>
+<body>
+The balancer webapp is an implementation of a rules-based
+load balancer.  A number of rules are provided and the
+application is written for easy extensibility.  
+
+<p>
+A servlet filter is also provided along with a sample web.xml that
+maps that filter to all requests.  This is only one way
+to use the load balancer.  The user may configure multiple
+filters each with a different rule chain mapped to 
+(possibly multiple) different url-patterns or servlets.  It
+is also possible to use rule chains within a servlet.
+</p>
+
+<p>
+Please note that although this application is being distributed
+with Tomcat, it is compliant with the Servlet Specification v2.3
+and is therefore portable across servlet containers that support
+this specification.
+</p>
+
+<p>
+The balancer webapps uses the 
+<a href="http://jakarta.apache.org/commons/digester">Digester</a> 
+tool from Jakarta Commons.  Digester and its dependencies are
+included in this distribution.
+</p>
+
+<p>
+The balancer webapp uses 
+<a href="http://jalopy.sf.net">Jalopy</a> 
+for source-code formatting and 
+<a href="http://checkstyle.sf.net">CheckStyle</a> 
+for source-code checking.
+<br/>
+The <a href="../checkStyle/checkStyleReport.html">
+CheckStyle Report</a> is available.
+</p>
+
+<p>
+Any suggestions, comments, etc, are welcome: please
+post them to the tomcat-user mailing list.  Enjoy ;)
+</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/package.html b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/package.html
new file mode 100644
index 0000000..90668e3
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/package.html
@@ -0,0 +1,26 @@
+<html>
+<head>
+<title>org.apache.webapp.balancer Package Overview</title>
+</head>
+<body>
+This package contains all classes used by the balancer
+webapp other than actual rule implementations.
+<p>
+The {@link org.apache.webapp.balancer.BalancerFilter Filter} is how the webapp
+is typically used: see the web.xml file in this distribution
+for an example.
+</p>
+
+<p>
+A {@link org.apache.webapp.balancer.RuleChain} is simply a list of rules to be
+evaluated in order.
+</p>
+
+<p>
+The {@link org.apache.webapp.balancer.RulesParser} uses 
+<a href="http://jakarta.apache.org/commons/digester">Digester</a> 
+to parse the rules configuration file.
+</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/AcceptEverythingRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/AcceptEverythingRule.java
new file mode 100644
index 0000000..98f7307
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/AcceptEverythingRule.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * This rule matches every request
+ * passed to it, making it suitable
+ * for use as a catch-all or last
+ * rule in a chain.
+ *
+ * @author Yoav Shapira
+ */
+public class AcceptEverythingRule extends BaseRule {
+    /**
+     * @see org.apache.webapp.balancer.Rule#matches(HttpServletRequest)
+     *
+     * This implementation always matches.
+     */
+    public boolean matches(HttpServletRequest request) {
+        return true;
+    }
+}
+
+
+// End of file: AcceptEverythingRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/BaseRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/BaseRule.java
new file mode 100644
index 0000000..5a8a7e9
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/BaseRule.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer.rules;
+
+import org.apache.webapp.balancer.Rule;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * The BaseRule is an empty rule
+ * implementation which can be
+ * subclassed for extension.
+ *
+ * @author Yoav Shapira
+ */
+public abstract class BaseRule implements Rule {
+    /**
+     * The rule name.
+     */
+    private String name;
+
+    /**
+     * The URL where matching
+     * requested will be redirected.
+     */
+    private String redirectUrl;
+
+    /**
+     * Sets the rule name.
+     *
+     * @param theName The rule name.
+     */
+    public void setName(String theName) {
+        if (theName == null) {
+            throw new IllegalArgumentException("The name cannot be null.");
+        } else {
+            name = theName;
+        }
+    }
+
+    /**
+     * Returns the rule name.
+     *
+     * @return String
+     */
+    protected String getName() {
+        return name;
+    }
+
+    /**
+     * @see Rule#getRedirectUrl
+     */
+    public String getRedirectUrl() {
+        return redirectUrl;
+    }
+
+    /**
+     * Sets the redirect URL.
+     *
+     * @param theRedirectUrl Where matching requests will be redirected
+     */
+    public void setRedirectUrl(String theRedirectUrl) {
+        if (theRedirectUrl == null) {
+            throw new IllegalArgumentException("redirectUrl may not be null.");
+        } else {
+            redirectUrl = theRedirectUrl;
+        }
+    }
+
+    /**
+     * @see Rule#matches(HttpServletRequest)
+     */
+    public abstract boolean matches(HttpServletRequest request);
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        buffer.append("Redirect URL: ");
+        buffer.append(getRedirectUrl());
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: BaseRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/CharacterEncodingRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/CharacterEncodingRule.java
new file mode 100644
index 0000000..c1db326
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/CharacterEncodingRule.java
@@ -0,0 +1,75 @@
+package org.apache.webapp.balancer.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * This rule redirects requests if they
+ * are for a specific character encoding.
+ *
+ * @author Yoav Shapira
+ */
+public class CharacterEncodingRule extends BaseRule {
+    /**
+     * The character encoding.
+     */
+    private String encoding;
+
+    /**
+     * Sets the character encoding.
+     *
+     * @param theEncoding The encoding value
+     */
+    public void setEncoding(String theEncoding) {
+        if (theEncoding == null) {
+            throw new IllegalArgumentException("The encoding cannot be null.");
+        } else {
+            encoding = theEncoding;
+        }
+    }
+
+    /**
+     * Returns the desired encoding.
+     *
+     * @return String
+     */
+    protected String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * @see org.apache.webapp.balancer.Rule#matches(HttpServletRequest request)
+     */
+    public boolean matches(HttpServletRequest request) {
+        String actualEncoding = request.getCharacterEncoding();
+
+        return (getEncoding().compareTo(actualEncoding) == 0);
+    }
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        buffer.append("Target encoding: ");
+        buffer.append(getEncoding());
+        buffer.append(" / ");
+
+        buffer.append("Redirect URL: ");
+        buffer.append(getRedirectUrl());
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: CharacterEncodingRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RemoteAddressRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RemoteAddressRule.java
new file mode 100644
index 0000000..d390578
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RemoteAddressRule.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * The remote access rule
+ * redirects if the request came
+ * from specified remote address.
+ *
+ * @author Yoav Shapira
+ */
+public class RemoteAddressRule extends BaseRule {
+    /**
+     * The target remote address.
+     */
+    private String remoteAddress;
+
+    /**
+     * Sets the target remote address.
+     *
+     * @param theAddress The address
+     */
+    public void setRemoteAddress(String theAddress) {
+        if (theAddress == null) {
+            throw new IllegalArgumentException("The address cannot be null.");
+        } else {
+            remoteAddress = theAddress;
+        }
+    }
+
+    /**
+     * Returns the target remote address.
+     *
+     * @return String
+     */
+    protected String getRemoteAddress() {
+        return remoteAddress;
+    }
+
+    /**
+     * @see org.apache.webapp.balancer.Rule#matches(HttpServletRequest)
+     *
+     * Looks for the request's remote address.
+     */
+    public boolean matches(HttpServletRequest request) {
+        String requestAddr = request.getRemoteAddr();
+
+        return (requestAddr.compareTo(getRemoteAddress()) == 0);
+    }
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        buffer.append("Target remote address: ");
+        buffer.append(getRemoteAddress());
+        buffer.append(" / ");
+
+        buffer.append("Redirect URL: ");
+        buffer.append(getRedirectUrl());
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: RemoteAddressRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RequestAttributeRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RequestAttributeRule.java
new file mode 100644
index 0000000..016a6a8
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RequestAttributeRule.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * This rule accepts or rejects requests
+ * based on the presence of a attribute
+ * in the request.
+ *
+ * @author Yoav Shapira
+ */
+public class RequestAttributeRule extends BaseRule {
+    /**
+     * The target attribute name (attribute
+     * must be present for match to succeed).
+     */
+    private String attributeName;
+
+    /**
+     * The target attribute value.  This
+     * is optional: null means any attribute
+     * value is OK for a match.  A non-null
+     * value will be matches exactly.
+     */
+    private Object attributeValue;
+
+    /**
+     * Sets the target attribute name.
+     *
+     * @param theAttributeName The attribute name
+     */
+    public void setAttributeName(String theAttributeName) {
+        if (theAttributeName == null) {
+            throw new IllegalArgumentException(
+                "attributeName cannot be null.");
+        } else {
+            attributeName = theAttributeName;
+        }
+    }
+
+    /**
+     * Returns the target attribute name.
+     *
+     * @return String The target attribute name.
+     */
+    protected String getAttributeName() {
+        return attributeName;
+    }
+
+    /**
+     * Sets the attribute value, which may be null.
+     *
+     * @param theAttributeValue The attribute value
+     */
+    public void setAttributeValue(Object theAttributeValue) {
+        attributeValue = theAttributeValue;
+    }
+
+    /**
+     * Returns the target attribute value,
+     * which may be null.
+     *
+     * @return Object The target attribute value
+     */
+    protected Object getAttributeValue() {
+        return attributeValue;
+    }
+
+    /**
+     * @see org.apache.webapp.balancer.Rule#matches(HttpServletRequest)
+     */
+    public boolean matches(HttpServletRequest request) {
+        Object actualAttributeValue = request.getAttribute(getAttributeName());
+
+        if (actualAttributeValue == null) {
+            return (getAttributeValue() == null);
+        } else {
+            return (actualAttributeValue.equals(getAttributeValue()));
+        }
+    }
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        buffer.append("Target attribute name: ");
+        buffer.append(getAttributeName());
+        buffer.append(" / ");
+
+        buffer.append("Target attribute value: ");
+        buffer.append(getAttributeValue());
+        buffer.append(" / ");
+
+        buffer.append("Redirect URL: ");
+        buffer.append(getRedirectUrl());
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: RequestAttributeRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RequestHeaderRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RequestHeaderRule.java
new file mode 100644
index 0000000..9f0a698
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RequestHeaderRule.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * This rule checks for the presence
+ * of a specific request header, optionally
+ * with a specific value.  The value may
+ * be null.
+ *
+ * @author Yoav Shapira
+ */
+public class RequestHeaderRule extends BaseRule {
+    /**
+     * The header name, cannot be null.
+     */
+    private String headerName;
+
+    /**
+     * The header value.  This may be
+     * null to indicate any value is OK.
+     */
+    private String headerValue;
+
+    /**
+     * Sets the header name.
+     *
+     * @param theName The name
+     */
+    public void setHeaderName(String theName) {
+        if (theName == null) {
+            throw new IllegalArgumentException(
+                "The header name cannot be null.");
+        } else {
+            headerName = theName;
+        }
+    }
+
+    /**
+     * Returns the header name to match.
+     *
+     * @return The header name
+     */
+    protected String getHeaderName() {
+        return headerName;
+    }
+
+    /**
+     * Sets the header value.
+     *
+     * @param theValue The header value
+     */
+    public void setHeaderValue(String theValue) {
+        headerValue = theValue;
+    }
+
+    /**
+     * Returns the desired header value,
+     * which may be null.
+     *
+     * @return String
+     */
+    protected String getHeaderValue() {
+        return headerValue;
+    }
+
+    /**
+     * @see org.apache.webapp.balancer.Rule#matches(HttpServletRequest request)
+     */
+    public boolean matches(HttpServletRequest request) {
+        String actualHeaderValue = request.getHeader(getHeaderName());
+
+        if (actualHeaderValue == null) {
+            return (getHeaderValue() == null);
+        } else {
+            return (actualHeaderValue.compareTo(getHeaderValue()) == 0);
+        }
+    }
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        buffer.append("Header name: ");
+        buffer.append(getHeaderName());
+        buffer.append(" / ");
+
+        buffer.append("Header value: ");
+        buffer.append(getHeaderValue());
+        buffer.append(" / ");
+
+        buffer.append("Redirect URL: ");
+        buffer.append(getRedirectUrl());
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: RequestHeaderRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RequestParameterRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RequestParameterRule.java
new file mode 100644
index 0000000..714e33c
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/RequestParameterRule.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * This rule accepts or rejects requests
+ * based on the presence of a parameter
+ * in the request.
+ *
+ * @author Yoav Shapira
+ */
+public class RequestParameterRule extends BaseRule {
+    /**
+     * The target parameter name (parameter
+     * must be present for match to succeed).
+     */
+    private String paramName;
+
+    /**
+     * The target parameter value.  This
+     * is optional: null means any parameter
+     * value is OK for a match.  A non-null
+     * value will be matches exactly.
+     */
+    private String paramValue;
+
+    /**
+     * Sets the target parameter name.
+     *
+     * @param theParamName The parameter name
+     */
+    public void setParamName(String theParamName) {
+        if (theParamName == null) {
+            throw new IllegalArgumentException("paramName cannot be null.");
+        } else {
+            paramName = theParamName;
+        }
+    }
+
+    /**
+     * Returns the target parameter name.
+     *
+     * @return String The target parameter name.
+     */
+    protected String getParamName() {
+        return paramName;
+    }
+
+    /**
+     * Sets the parameter value, which may be null.
+     *
+     * @param theParamValue The parameter value
+     */
+    public void setParamValue(String theParamValue) {
+        paramValue = theParamValue;
+    }
+
+    /**
+     * Returns the target parameter value,
+     * which may be null.
+     *
+     * @return String The target parameter value
+     */
+    protected String getParamValue() {
+        return paramValue;
+    }
+
+    /**
+     * @see org.apache.webapp.balancer.Rule#matches(HttpServletRequest)
+     */
+    public boolean matches(HttpServletRequest request) {
+        String actualParamValue = request.getParameter(getParamName());
+
+        if (actualParamValue == null) {
+            return (getParamValue() == null);
+        } else {
+            return (actualParamValue.compareTo(getParamValue()) == 0);
+        }
+    }
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        buffer.append("Target param name: ");
+        buffer.append(getParamName());
+        buffer.append(" / ");
+
+        buffer.append("Target param value: ");
+        buffer.append(getParamValue());
+        buffer.append(" / ");
+
+        buffer.append("Redirect URL: ");
+        buffer.append(getRedirectUrl());
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: RequestParameterRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/SessionAttributeRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/SessionAttributeRule.java
new file mode 100644
index 0000000..221c41e
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/SessionAttributeRule.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer.rules;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+
+/**
+ * This rule accepts or rejects requests
+ * based on the presence of a attribute
+ * in the request.
+ *
+ * @author Yoav Shapira
+ */
+public class SessionAttributeRule extends BaseRule {
+    /**
+     * The target attribute name (attribute
+     * must be present for match to succeed).
+     */
+    private String attributeName;
+
+    /**
+     * The target attribute value.  This
+     * is optional: null means any attribute
+     * value is OK for a match.  A non-null
+     * value will be matches exactly.
+     */
+    private Object attributeValue;
+
+    /**
+     * Sets the target attribute name.
+     *
+     * @param theAttributeName The attribute name
+     */
+    public void setAttributeName(String theAttributeName) {
+        if (theAttributeName == null) {
+            throw new IllegalArgumentException(
+                "attributeName cannot be null.");
+        } else {
+            attributeName = theAttributeName;
+        }
+    }
+
+    /**
+     * Returns the target attribute name.
+     *
+     * @return String The target attribute name.
+     */
+    protected String getAttributeName() {
+        return attributeName;
+    }
+
+    /**
+     * Sets the attribute value, which may be null.
+     *
+     * @param theAttributeValue The attribute value
+     */
+    public void setAttributeValue(Object theAttributeValue) {
+        attributeValue = theAttributeValue;
+    }
+
+    /**
+     * Returns the target attribute value,
+     * which may be null.
+     *
+     * @return Object The target attribute value
+     */
+    protected Object getAttributeValue() {
+        return attributeValue;
+    }
+
+    /**
+     * @see org.apache.webapp.balancer.Rule#matches(HttpServletRequest)
+     */
+    public boolean matches(HttpServletRequest request) {
+        HttpSession session = request.getSession();
+        Object actualAttributeValue = session.getAttribute(getAttributeName());
+
+        if (actualAttributeValue == null) {
+            return (getAttributeValue() == null);
+        } else {
+            return (actualAttributeValue.equals(getAttributeValue()));
+        }
+    }
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        buffer.append("Target attribute name: ");
+        buffer.append(getAttributeName());
+        buffer.append(" / ");
+
+        buffer.append("Target attribute value: ");
+        buffer.append(getAttributeValue());
+        buffer.append(" / ");
+
+        buffer.append("Redirect URL: ");
+        buffer.append(getRedirectUrl());
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: SessionAttributeRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/URLStringMatchRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/URLStringMatchRule.java
new file mode 100644
index 0000000..59a1c6c
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/URLStringMatchRule.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * This rule looks for a specific string
+ * in the URL for a positive match.
+ *
+ * @author Yoav Shapira
+ */
+public class URLStringMatchRule extends BaseRule {
+    /**
+     * The target string that must be present
+     * in the URL, may not be null.
+     */
+    private String targetString;
+
+    /**
+     * Sets the target string that must
+     * be present in the URL.
+     *
+     * @param theTargetString The target string
+     */
+    public void setTargetString(String theTargetString) {
+        if (theTargetString == null) {
+            throw new IllegalArgumentException(
+                "The target string cannot be null.");
+        } else {
+            targetString = theTargetString;
+        }
+    }
+
+    /**
+     * Returns the target string that must
+     * be present in the URL.
+     *
+     * @return The target string
+     */
+    protected String getTargetString() {
+        return targetString;
+    }
+
+    /**
+     * @see org.apache.webapp.balancer.Rule#matches(HttpServletRequest)
+     *
+     * Looks for the target string in the request URL.
+     */
+    public boolean matches(HttpServletRequest request) {
+        String requestUrl = request.getRequestURL().toString();
+        int index = requestUrl.indexOf(getTargetString());
+
+        return (index > -1);
+    }
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        buffer.append("Target string: ");
+        buffer.append(getTargetString());
+        buffer.append(" / ");
+
+        buffer.append("Redirect URL: ");
+        buffer.append(getRedirectUrl());
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: URLStringMatchRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/UserRoleRule.java b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/UserRoleRule.java
new file mode 100644
index 0000000..ed15a76
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/UserRoleRule.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.webapp.balancer.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * This rule redirects the request based
+ * on the user's role.
+ *
+ * @author Yoav Shapira
+ */
+public class UserRoleRule extends BaseRule {
+    /**
+     * The desired role for the user.
+     * If the user is in this role, the
+     * match will succeed.
+     */
+    private String role;
+
+    /**
+     * Sets the desired role.
+     *
+     * @param theRole The desire role
+     */
+    public void setRole(String theRole) {
+        if (theRole == null) {
+            throw new IllegalArgumentException("The role cannot be null.");
+        } else {
+            role = theRole;
+        }
+    }
+
+    /**
+     * Returns the target role.
+     *
+     * @return The desired role
+     */
+    protected String getRole() {
+        return role;
+    }
+
+    /**
+     * @see org.apache.webapp.balancer.Rule#matches(HttpServletRequest)
+     */
+    public boolean matches(HttpServletRequest request) {
+        return request.isUserInRole(getRole());
+    }
+
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append("[");
+        buffer.append(getClass().getName());
+        buffer.append(": ");
+
+        buffer.append("Target role: ");
+        buffer.append(getRole());
+        buffer.append(" / ");
+
+        buffer.append("Redirect URL: ");
+        buffer.append(getRedirectUrl());
+
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+}
+
+
+// End of file: UserRoleRule.java
diff --git a/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/package.html b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/package.html
new file mode 100644
index 0000000..397c7de
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/classes/org/apache/webapp/balancer/rules/package.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<title>org.apache.webapp.balancer.rules Package Overview</title>
+</head>
+<body>
+This package contains all the {@link org.apache.webapp.balancer.Rule} 
+implementations that are distributed with the balancer webapp.  All 
+these rules extend {@link org.apache.webapp.balancer.rules.BaseRule} 
+but users are free to implement the Rule interface as needed.
+</body>
+</html>
diff --git a/container/webapps/balancer/WEB-INF/config/rules.xml b/container/webapps/balancer/WEB-INF/config/rules.xml
new file mode 100644
index 0000000..e49765e
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/config/rules.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rules>
+  <!-- If the URL contains News (case-sensitive), go to CNN.com -->
+  <rule className="org.apache.webapp.balancer.rules.URLStringMatchRule"
+    targetString="News"
+    redirectUrl="http://www.cnn.com" />
+
+  <!-- If the request contains a parameter named paramName whose value
+       is paramValue, go to Yahoo.com. -->
+  <rule className="org.apache.webapp.balancer.rules.RequestParameterRule"
+    paramName="paramName"
+    paramValue="paramValue"
+    redirectUrl="http://www.yahoo.com" />
+
+  <!-- Redirect all requests to jakarta.apache.org. -->
+  <rule className="org.apache.webapp.balancer.rules.AcceptEverythingRule"
+    redirectUrl="http://jakarta.apache.org" />
+</rules>
diff --git a/container/webapps/balancer/WEB-INF/web.xml b/container/webapps/balancer/WEB-INF/web.xml
new file mode 100644
index 0000000..a48c0b5
--- /dev/null
+++ b/container/webapps/balancer/WEB-INF/web.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+  <display-name>Tomcat Simple Load Balancer Example App</display-name>
+  <description>
+    Tomcat Simple Load Balancer Example App
+  </description>
+
+  <!-- BalancerFilter definition -->
+  <filter>
+    <filter-name>BalancerFilter</filter-name>
+    <filter-class>org.apache.webapp.balancer.BalancerFilter</filter-class>
+    <init-param>
+      <param-name>configUrl</param-name>
+      <param-value>/WEB-INF/config/rules.xml</param-value>
+    </init-param>
+  </filter>
+
+  <!-- BalancerFilter mapping -->
+  <filter-mapping>
+    <filter-name>BalancerFilter</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
+</web-app>
\ No newline at end of file
diff --git a/container/webapps/balancer/build.xml b/container/webapps/balancer/build.xml
new file mode 100644
index 0000000..56c45c9
--- /dev/null
+++ b/container/webapps/balancer/build.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0"?>
+
+<!-- 
+This is the build script for the balancer webapp.
+@author Yoav Shapira
+-->
+
+<project name="balancer" default="build-main" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="../../build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="webapps.build"   value="../build"/>
+  <property name="webapps.dist"    value="../dist"/>
+  <property name="webapp.name"     value="balancer"/>
+
+  <!-- Dependent JARs and files -->
+  <property name="servlet-api.jar" value="${api.home}/jsr154/dist/lib/servlet-api.jar"/>
+  <property name="jsp-api.jar"     value="${api.home}/jsr152/dist/lib/jsp-api.jar"/>
+
+  <!-- Compilation classpath -->
+  <path id="balancer.classpath">
+    <!-- Required by Digester -->
+    <pathelement location="${catalina.deploy}/classes"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${commons-logging-api.jar}" />
+  </path>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${webapps.build}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/images"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF/classes"/>
+  </target>
+
+
+  <!-- ================ BUILD: Copy Static Files ========================== -->
+  <target name="build-static" depends="build-prepare">
+    <copy todir="${webapps.build}/${webapp.name}">
+      <fileset dir=".">
+        <exclude name="build.*"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <target name="build-main" depends="build-static">
+
+    <javac   srcdir="WEB-INF/classes" 
+             destdir="${webapps.build}/${webapp.name}/WEB-INF/classes"
+             debug="${compile.debug}" deprecation="${compile.deprecation}"
+             optimize="${compile.optimize}"
+             excludes="**/CVS/**">
+      <classpath refid="balancer.classpath" />
+    </javac>
+
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean and build balancer webapp"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${webapps.build}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Directories =================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${webapps.dist}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Distribution Files ============ -->
+  <target name="dist" depends="build-main,dist-prepare"
+   description="Create balancer webapp binary distribution">
+      <jar   jarfile="${webapps.dist}/${webapp.name}.war"
+             basedir="${webapps.build}/${webapp.name}" includes="**"/>
+  </target>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <deltree dir="${dist.dir}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+
+
+</project>
+<!-- End build.xml -->
+
+
+
+
+
+
diff --git a/container/webapps/build.xml b/container/webapps/build.xml
new file mode 100644
index 0000000..3a36329
--- /dev/null
+++ b/container/webapps/build.xml
@@ -0,0 +1,139 @@
+<project name="Webapps" default="dist" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <!--property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="${user.home}/build.properties"/-->
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="webapps.build"   value="${basedir}/build"/>
+  <property name="webapps.deploy"  value="${basedir}/../build"/>
+  <property name="webapps.dist"    value="${basedir}/dist"/>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+
+    <available classname="junit.framework.TestCase" property="junit.present" />
+
+    <mkdir dir="${webapps.build}"/>
+    <mkdir dir="${webapps.dist}"/>
+
+  </target>
+
+
+  <!-- =================== BUILD: Compile Subprojects ===================== -->
+  <!-- Add a new target for each webapp subproject -->
+
+  <target name="ROOT">
+    <ant dir="${basedir}/ROOT" target="dist"/>
+  </target>
+
+  <target name="admin">
+    <ant dir="${basedir}/admin" target="dist"/>
+  </target>
+
+  <target name="manager">
+    <ant dir="${basedir}/manager" target="dist"/>
+  </target>
+
+	<target name="host-manager">
+	    <ant dir="${basedir}/host-manager" target="dist"/>
+	  </target>
+
+  <target name="docs">
+    <ant dir="${basedir}/docs" target="dist"/>
+  </target>
+
+  <target name="balancer">
+    <ant dir="${basedir}/balancer" target="dist"/>
+  </target>
+
+  <target name="webdav">
+    <ant dir="${basedir}/webdav" target="dist"/>
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <!-- Update the depends list for each subproject -->
+  <target name="build" depends="build-prepare,ROOT,admin,manager,host-manager,docs,balancer,webdav" />
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${webapps.build}"/>
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,dist"/>
+
+
+  <!-- ================= DEPLOY: Deploy Webapps Projects ================== -->
+  <target name="deploy" depends="dist"
+   description="Build and deploy Webapps component">
+
+    <!-- General Purpose Applications -->
+    <mkdir dir="${webapps.deploy}/webapps"/>
+    <copy todir="${webapps.deploy}/webapps">
+      <fileset dir="${webapps.build}" excludes="admin/**,manager/**"/>
+    </copy>
+
+    <!-- Administrative Applications -->
+    <mkdir     dir="${webapps.deploy}/server/webapps"/>
+
+    <copy    todir="${webapps.deploy}/webapps"
+              file="${webapps.build}/admin/admin.xml"/>
+    <mkdir     dir="${webapps.deploy}/server/webapps/admin"/>
+    <copy    todir="${webapps.deploy}/server/webapps/admin">
+      <fileset dir="${webapps.build}/admin" excludes="admin.xml"/>
+    </copy>
+
+    <copy    todir="${webapps.deploy}/webapps"
+              file="${webapps.build}/manager/manager.xml"/>
+    <mkdir     dir="${webapps.deploy}/server/webapps/manager"/>
+    <copy    todir="${webapps.deploy}/server/webapps/manager">
+      <fileset dir="${webapps.build}/manager" excludes="manager.xml"/>
+    </copy>
+
+    <copy    todir="${webapps.deploy}/webapps"
+              file="${webapps.build}/host-manager/host-manager.xml"/>
+    <mkdir     dir="${webapps.deploy}/server/webapps/host-manager"/>
+    <copy    todir="${webapps.deploy}/server/webapps/host-manager">
+      <fileset dir="${webapps.build}/host-manager" excludes="host-manager.xml"/>
+    </copy>
+
+  </target>
+
+
+  <!-- ================= DIST: Create Distribution Files ================== -->
+  <target name="dist" depends="build"/>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <delete dir="${webapps.dist}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+  <!-- ===================== TEST: Compile Unit Tests ===================== -->
+  <target name="build-tests" depends="dist" if="junit.present">
+  </target>
+
+
+  <!-- ===================== TEST: Execute Unit Tests ===================== -->
+  <target name="test" if="junit.present"
+   description="Run all unit test cases">
+  </target>
+
+
+</project>
diff --git a/container/webapps/docs/META-INF/context.xml b/container/webapps/docs/META-INF/context.xml
new file mode 100644
index 0000000..b4cbc46
--- /dev/null
+++ b/container/webapps/docs/META-INF/context.xml
@@ -0,0 +1,12 @@
+<!--
+
+    Context configuration file for the Tomcat Balancer Web App
+    This is only needed to keep the distribution small and avoid duplicating
+    commons libraries
+
+    $Id$
+
+-->
+
+
+<Context antiResourceLocking="false" />
diff --git a/container/webapps/docs/WEB-INF/web.xml b/container/webapps/docs/WEB-INF/web.xml
new file mode 100644
index 0000000..3cc537d
--- /dev/null
+++ b/container/webapps/docs/WEB-INF/web.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+  <display-name>Tomcat Documentation</display-name>
+  <description>
+     Tomcat Documentation.
+  </description>
+</web-app>
diff --git a/container/webapps/docs/appdev/build.xml.txt b/container/webapps/docs/appdev/build.xml.txt
new file mode 100644
index 0000000..1b35ba5
--- /dev/null
+++ b/container/webapps/docs/appdev/build.xml.txt
@@ -0,0 +1,503 @@
+<!--
+     General purpose build script for web applications and web services,
+     including enhanced support for deploying directly to a Tomcat 5
+     based server.
+
+     This build script assumes that the source code of your web application
+     is organized into the following subdirectories underneath the source
+     code directory from which you execute the build script:
+
+        docs                 Static documentation files to be copied to
+                             the "docs" subdirectory of your distribution.
+
+        src                  Java source code (and associated resource files)
+                             to be compiled to the "WEB-INF/classes"
+                             subdirectory of your web applicaiton.
+
+        web                  Static HTML, JSP, and other content (such as
+                             image files), including the WEB-INF subdirectory
+                             and its configuration file contents.
+
+     $Id$
+-->
+
+
+<!-- A "project" describes a set of targets that may be requested
+     when Ant is executed.  The "default" attribute defines the
+     target which is executed if no specific target is requested,
+     and the "basedir" attribute defines the current working directory
+     from which Ant executes the requested task.  This is normally
+     set to the current working directory.
+-->
+
+<project name="My Project" default="compile" basedir=".">
+
+
+
+<!-- ===================== Property Definitions =========================== -->
+
+
+<!--
+
+  Each of the following properties are used in the build script.
+  Values for these properties are set by the first place they are
+  defined, from the following list:
+
+  * Definitions on the "ant" command line (ant -Dfoo=bar compile).
+
+  * Definitions from a "build.properties" file in the top level
+    source directory of this application.
+
+  * Definitions from a "build.properties" file in the developer's
+    home directory.
+
+  * Default definitions in this build.xml file.
+
+  You will note below that property values can be composed based on the
+  contents of previously defined properties.  This is a powerful technique
+  that helps you minimize the number of changes required when your development
+  environment is modified.  Note that property composition is allowed within
+  "build.properties" files as well as in the "build.xml" script.
+
+-->
+
+  <property file="build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+
+<!-- ==================== File and Directory Names ======================== -->
+
+
+<!--
+
+  These properties generally define file and directory names (or paths) that
+  affect where the build process stores its outputs.
+
+  app.name             Base name of this application, used to
+                       construct filenames and directories.
+                       Defaults to "myapp".
+
+  app.path             Context path to which this application should be
+                       deployed (defaults to "/" plus the value of the
+                       "app.name" property).
+
+  app.version          Version number of this iteration of the application.
+
+  build.home           The directory into which the "prepare" and
+                       "compile" targets will generate their output.
+                       Defaults to "build".
+
+  catalina.home        The directory in which you have installed
+                       a binary distribution of Tomcat 5.  This will
+                       be used by the "deploy" target.
+
+  dist.home            The name of the base directory in which
+                       distribution files are created.
+                       Defaults to "dist".
+
+  manager.password     The login password of a user that is assigned the
+                       "manager" role (so that he or she can execute
+                       commands via the "/manager" web application)
+
+  manager.url          The URL of the "/manager" web application on the
+                       Tomcat installation to which we will deploy web
+                       applications and web services.
+
+  manager.username     The login username of a user that is assigned the
+                       "manager" role (so that he or she can execute
+                       commands via the "/manager" web application)
+
+-->
+
+  <property name="app.name"      value="myapp"/>
+  <property name="app.path"      value="/${app.name}"/>
+  <property name="app.version"   value="0.1-dev"/>
+  <property name="build.home"    value="${basedir}/build"/>
+  <property name="catalina.home" value="../../../.."/> <!-- UPDATE THIS! -->
+  <property name="dist.home"     value="${basedir}/dist"/>
+  <property name="docs.home"     value="${basedir}/docs"/>
+  <property name="manager.url"   value="http://localhost:8080/manager"/>
+  <property name="src.home"      value="${basedir}/src"/>
+  <property name="web.home"      value="${basedir}/web"/>
+
+
+<!-- ================== Custom Ant Task Definitions ======================= -->
+
+
+<!--
+
+  These properties define custom tasks for the Ant build tool that interact
+  with the "/manager" web application installed with Tomcat 5.  Before they
+  can be successfully utilized, you must perform the following steps:
+
+  - Copy the file "server/lib/catalina-ant.jar" from your Tomcat 5
+    installation into the "lib" directory of your Ant installation.
+
+  - Create a "build.properties" file in your application's top-level
+    source directory (or your user login home directory) that defines
+    appropriate values for the "manager.password", "manager.url", and
+    "manager.username" properties described above.
+
+  For more information about the Manager web application, and the functionality
+  of these tasks, see <http://localhost:8080/tomcat-docs/manager-howto.html>.
+
+-->
+
+  <taskdef name="deploy"   classname="org.apache.catalina.ant.DeployTask"/>
+  <taskdef name="list"     classname="org.apache.catalina.ant.ListTask"/>
+  <taskdef name="reload"   classname="org.apache.catalina.ant.ReloadTask"/>
+  <taskdef name="undeploy" classname="org.apache.catalina.ant.UndeployTask"/>
+
+
+<!--  ==================== Compilation Control Options ==================== -->
+
+<!--
+
+  These properties control option settings on the Javac compiler when it
+  is invoked using the <javac> task.
+
+  compile.debug        Should compilation include the debug option?
+
+  compile.deprecation  Should compilation include the deprecation option?
+
+  compile.optimize     Should compilation include the optimize option?
+
+-->
+
+  <property name="compile.debug"       value="true"/>
+  <property name="compile.deprecation" value="false"/>
+  <property name="compile.optimize"    value="true"/>
+
+
+
+<!-- ==================== External Dependencies =========================== -->
+
+
+<!--
+
+  Use property values to define the locations of external JAR files on which
+  your application will depend.  In general, these values will be used for
+  two purposes:
+  * Inclusion on the classpath that is passed to the Javac compiler
+  * Being copied into the "/WEB-INF/lib" directory during execution
+    of the "deploy" target.
+
+  Because we will automatically include all of the Java classes that Tomcat 5
+  exposes to web applications, we will not need to explicitly list any of those
+  dependencies.  You only need to worry about external dependencies for JAR
+  files that you are going to include inside your "/WEB-INF/lib" directory.
+
+-->
+
+<!-- Dummy external dependency -->
+<!--
+  <property name="foo.jar"
+           value="/path/to/foo.jar"/>
+-->
+
+
+<!-- ==================== Compilation Classpath =========================== -->
+
+<!--
+
+  Rather than relying on the CLASSPATH environment variable, Ant includes
+  features that makes it easy to dynamically construct the classpath you
+  need for each compilation.  The example below constructs the compile
+  classpath to include the servlet.jar file, as well as the other components
+  that Tomcat makes available to web applications automatically, plus anything
+  that you explicitly added.
+
+-->
+
+  <path id="compile.classpath">
+
+    <!-- Include all JAR files that will be included in /WEB-INF/lib -->
+    <!-- *** CUSTOMIZE HERE AS REQUIRED BY YOUR APPLICATION *** -->
+<!--
+    <pathelement location="${foo.jar}"/>
+-->
+
+    <!-- Include all elements that Tomcat exposes to applications -->
+    <pathelement location="${catalina.home}/common/classes"/>
+    <fileset dir="${catalina.home}/common/endorsed">
+      <include name="*.jar"/>
+    </fileset>
+    <fileset dir="${catalina.home}/common/lib">
+      <include name="*.jar"/>
+    </fileset>
+    <pathelement location="${catalina.home}/shared/classes"/>
+    <fileset dir="${catalina.home}/shared/lib">
+      <include name="*.jar"/>
+    </fileset>
+
+  </path>
+
+
+
+<!-- ==================== All Target ====================================== -->
+
+<!--
+
+  The "all" target is a shortcut for running the "clean" target followed
+  by the "compile" target, to force a complete recompile.
+
+-->
+
+  <target name="all" depends="clean,compile"
+   description="Clean build and dist directories, then compile"/>
+
+
+
+<!-- ==================== Clean Target ==================================== -->
+
+<!--
+
+  The "clean" target deletes any previous "build" and "dist" directory,
+  so that you can be ensured the application can be built from scratch.
+
+-->
+
+  <target name="clean"
+   description="Delete old build and dist directories">
+    <delete dir="${build.home}"/>
+    <delete dir="${dist.home}"/>
+  </target>
+
+
+
+<!-- ==================== Compile Target ================================== -->
+
+<!--
+
+  The "compile" target transforms source files (from your "src" directory)
+  into object files in the appropriate location in the build directory.
+  This example assumes that you will be including your classes in an
+  unpacked directory hierarchy under "/WEB-INF/classes".
+
+-->
+
+  <target name="compile" depends="prepare"
+   description="Compile Java sources">
+
+    <!-- Compile Java classes as necessary -->
+    <mkdir    dir="${build.home}/WEB-INF/classes"/>
+    <javac srcdir="${src.home}"
+          destdir="${build.home}/WEB-INF/classes"
+            debug="${compile.debug}"
+      deprecation="${compile.deprecation}"
+         optimize="${compile.optimize}">
+        <classpath refid="compile.classpath"/>
+    </javac>
+
+    <!-- Copy application resources -->
+    <copy  todir="${build.home}/WEB-INF/classes">
+      <fileset dir="${src.home}" excludes="**/*.java"/>
+    </copy>
+
+  </target>
+
+
+
+<!-- ==================== Dist Target ===================================== -->
+
+
+<!--
+
+  The "dist" target creates a binary distribution of your application
+  in a directory structure ready to be archived in a tar.gz or zip file.
+  Note that this target depends on two others:
+
+  * "compile" so that the entire web application (including external
+    dependencies) will have been assembled
+
+  * "javadoc" so that the application Javadocs will have been created
+
+-->
+
+  <target name="dist" depends="compile,javadoc"
+   description="Create binary distribution">
+
+    <!-- Copy documentation subdirectories -->
+    <mkdir   dir="${dist.home}/docs"/>
+    <copy    todir="${dist.home}/docs">
+      <fileset dir="${docs.home}"/>
+    </copy>
+
+    <!-- Create application JAR file -->
+    <jar jarfile="${dist.home}/${app.name}-${app.version}.war"
+         basedir="${build.home}"/>
+
+    <!-- Copy additional files to ${dist.home} as necessary -->
+
+  </target>
+
+
+
+<!-- ==================== Install Target ================================== -->
+
+<!--
+
+  The "install" target tells the specified Tomcat 5 installation to dynamically
+  install this web application and make it available for execution.  It does
+  *not* cause the existence of this web application to be remembered across
+  Tomcat restarts; if you restart the server, you will need to re-install all
+  this web application.
+
+  If you have already installed this application, and simply want Tomcat to
+  recognize that you have updated Java classes (or the web.xml file), use the
+  "reload" target instead.
+
+  NOTE:  This target will only succeed if it is run from the same server that
+  Tomcat is running on.
+
+  NOTE:  This is the logical opposite of the "remove" target.
+
+-->
+
+  <target name="install" depends="compile"
+   description="Install application to servlet container">
+
+    <deploy url="${manager.url}"
+       username="${manager.username}"
+       password="${manager.password}"
+           path="${app.path}"
+       localWar="file://${build.home}"/>
+
+  </target>
+
+
+<!-- ==================== Javadoc Target ================================== -->
+
+<!--
+
+  The "javadoc" target creates Javadoc API documentation for the Java
+  classes included in your application.  Normally, this is only required
+  when preparing a distribution release, but is available as a separate
+  target in case the developer wants to create Javadocs independently.
+
+-->
+
+  <target name="javadoc" depends="compile"
+   description="Create Javadoc API documentation">
+
+    <mkdir          dir="${dist.home}/docs/api"/>
+    <javadoc sourcepath="${src.home}"
+                destdir="${dist.home}/docs/api"
+           packagenames="*">
+      <classpath refid="compile.classpath"/>
+    </javadoc>
+
+  </target>
+
+
+
+<!-- ====================== List Target =================================== -->
+
+<!--
+
+  The "list" target asks the specified Tomcat 5 installation to list the
+  currently running web applications, either loaded at startup time or
+  installed dynamically.  It is useful to determine whether or not the
+  application you are currently developing has been installed.
+
+-->
+
+  <target name="list"
+   description="List installed applications on servlet container">
+
+    <list    url="${manager.url}"
+        username="${manager.username}"
+        password="${manager.password}"/>
+
+  </target>
+
+
+<!-- ==================== Prepare Target ================================== -->
+
+<!--
+
+  The "prepare" target is used to create the "build" destination directory,
+  and copy the static contents of your web application to it.  If you need
+  to copy static files from external dependencies, you can customize the
+  contents of this task.
+
+  Normally, this task is executed indirectly when needed.
+
+-->
+
+  <target name="prepare">
+
+    <!-- Create build directories as needed -->
+    <mkdir  dir="${build.home}"/>
+    <mkdir  dir="${build.home}/WEB-INF"/>
+    <mkdir  dir="${build.home}/WEB-INF/classes"/>
+
+
+    <!-- Copy static content of this web application -->
+    <copy todir="${build.home}">
+      <fileset dir="${web.home}"/>
+    </copy>
+
+    <!-- Copy external dependencies as required -->
+    <!-- *** CUSTOMIZE HERE AS REQUIRED BY YOUR APPLICATION *** -->
+    <mkdir  dir="${build.home}/WEB-INF/lib"/>
+<!--
+    <copy todir="${build.home}/WEB-INF/lib" file="${foo.jar}"/>
+-->
+
+    <!-- Copy static files from external dependencies as needed -->
+    <!-- *** CUSTOMIZE HERE AS REQUIRED BY YOUR APPLICATION *** -->
+
+  </target>
+
+
+<!-- ==================== Reload Target =================================== -->
+
+<!--
+
+  The "reload" signals the specified application Tomcat 5 to shut itself down
+  and reload. This can be useful when the web application context is not
+  reloadable and you have updated classes or property files in the
+  /WEB-INF/classes directory or when you have added or updated jar files in the
+  /WEB-INF/lib directory.
+
+  NOTE: The /WEB-INF/web.xml web application configuration file is not reread
+  on a reload. If you have made changes to your web.xml file you must stop
+  then start the web application. 
+
+-->
+
+  <target name="reload" depends="compile"
+   description="Reload application on servlet container">
+
+    <reload url="${manager.url}"
+       username="${manager.username}"
+       password="${manager.password}"
+           path="${app.path}"/>
+
+  </target>
+
+
+<!-- ==================== Remove Target =================================== -->
+
+<!--
+
+  The "remove" target tells the specified Tomcat 5 installation to dynamically
+  remove this web application from service.
+
+  NOTE:  This is the logical opposite of the "install" target.
+
+-->
+
+  <target name="remove"
+   description="Remove application on servlet container">
+
+    <undeploy url="${manager.url}"
+         username="${manager.username}"
+         password="${manager.password}"
+             path="${app.path}"/>
+
+  </target>
+
+
+</project>
diff --git a/container/webapps/docs/appdev/deployment.xml b/container/webapps/docs/appdev/deployment.xml
new file mode 100644
index 0000000..8802413
--- /dev/null
+++ b/container/webapps/docs/appdev/deployment.xml
@@ -0,0 +1,254 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="deployment.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>Deployment</title>
+  </properties>
+
+<body>
+
+
+<section name="Background">
+
+<p>Before describing how to organize your source code directories,
+it is useful to examine the runtime organization of a web application.
+Prior to the Servlet API Specification, version 2.2, there was little
+consistency between server platforms.  However, servers that conform
+to the 2.2 (or later) specification are required to accept a
+<em>Web Application Archive</em> in a standard format, which is discussed
+further below.</p>
+
+<p>A web application is defined as a hierarchy of directories and files
+in a standard layout.  Such a hierarchy can be accessed in its "unpacked"
+form, where each directory and file exists in the filesystem separately,
+or in a "packed" form known as a Web ARchive, or WAR file.  The former format
+is more useful during development, while the latter is used when you
+distribute your application to be installed.</p>
+
+<p>The top-level directory of your web application hierarchy is also the
+<em>document root</em> of your application.  Here, you will place the HTML
+files and JSP pages that comprise your application's user interface.  When the
+system administrator deploys your application into a particular server, he
+or she assigns a <em>context path</em> to your application (a later section
+of this manual describes deployment on Tomcat).  Thus, if the
+system administrator assigns your application to the context path
+<code>/catalog</code>, then a request URI referring to
+<code>/catalog/index.html</code> will retrieve the <code>index.html</code>
+file from your document root.</p>
+
+</section>
+
+
+<section name="Standard Directory Layout">
+
+<p>To facilitate creation of a Web Application Archive file in the required
+format, it is convenient to arrange the "executable" files of your web
+application (that is, the files that Tomcat actually uses when executing
+your app) in the same organization as required by the WAR format itself.
+To do this, you will end up with the following contents in your
+application's "document root" directory:</p>
+<ul>
+<li><strong>*.html, *.jsp, etc.</strong> - The HTML and JSP pages, along
+    with other files that must be visible to the client browser (such as
+    JavaScript, stylesheet files, and images) for your application.
+    In larger applications you may choose to divide these files into
+    a subdirectory hierarchy, but for smaller apps, it is generally
+    much simpler to maintain only a single directory for these files.
+    <br/><br/></li>
+<li><strong>/WEB-INF/web.xml</strong> - The <em>Web Application Deployment
+    Descriptor</em> for your application.  This is an XML file describing
+    the servlets and other components that make up your application,
+    along with any initialization parameters and container-managed
+    security constraints that you want the server to enforce for you.
+    This file is discussed in more detail in the following subsection.
+    <br/><br/></li>
+<li><strong>/WEB-INF/classes/</strong> - This directory contains any Java
+    class files (and associated resources) required for your application,
+    including both servlet and non-servlet classes, that are not combined
+    into JAR files.  If your classes are organized into Java packages,
+    you must reflect this in the directory hierarchy under
+    <code>/WEB-INF/classes/</code>.  For example, a Java class named
+    <code>com.mycompany.mypackage.MyServlet</code>
+    would need to be stored in a file named
+    <code>/WEB-INF/classes/com/mycompany/mypackage/MyServlet.class</code>.
+    <br/><br/></li>
+<li><strong>/WEB-INF/lib/</strong> - This directory contains JAR files that
+    contain Java class files (and associated resources) required for your
+    application, such as third party class libraries or JDBC drivers.</li>
+</ul>
+
+<p>When you install an application into Tomcat (or any other
+2.2/2.3-compatible server), the classes in the <code>WEB-INF/classes/</code>
+directory, as well as all classes in JAR files found in the
+<code>WEB-INF/lib/</code> directory, are made visible to other classes
+within your particular web application.  Thus, if
+you include all of the required library classes in one of these places (be
+sure to check licenses for redistribution rights for any third party libraries
+you utilize), you will simplify the installation of your web application --
+no adjustment to the system class path (or installation of global library
+files in your server) will be necessary.</p>
+
+<p>Much of this information was extracted from Chapter 9 of the Servlet
+API Specification, version 2.3, which you should consult for more details.</p>
+
+</section>
+
+
+<section name="Shared Library Files">
+
+<p>Like most servlet containers, Tomcat 5 also supports mechanisms to install
+library JAR files (or unpacked classes) once, and make them visible to all
+installed web applications (without having to be included inside the web
+application itself.  The details of how Tomcat locates and shares such
+classes are described in the
+<a href="../class-loader-howto.html">Class Loader HOW-TO</a> documentation.
+For the purposes of our discussion, there are two locations that are commonly
+used within a Tomcat 5 installation for shared code:</p>
+<ul>
+<li><strong>$CATALINA_HOME/common/lib</strong> - JAR files placed here are
+    visible both to web applications and internal Tomcat code.  This is a
+    good place to put JDBC drivers that are required for both your application
+    and internal Tomcat use (such as for a JDBCRealm).
+    <br/><br/></li>
+<li><strong>$CATALINA_HOME/shared/lib</strong> - JAR files placed here are
+    visible to all web applications, but not to internal Tomcat code.  This
+    is the right place for shared libraries that are specific to your
+    application.<br/><br/></li>
+</ul>
+
+<p>Out of the box, a standard Tomcat 5 installation includes a variety
+of pre-installed shared library files, including:</p>
+<ul>
+<li>The <em>Servlet 2.4</em> and <em>JSP 2.0</em> APIs that are fundamental
+    to writing servlets and JavaServer Pages.<br/><br/></li>
+<li>An <em>XML Parser</em> compliant with the JAXP (version 1.2) APIs, so
+    your application can perform DOM-based or SAX-based processing of
+    XML documents.<br/><br/></li>
+</ul>
+
+</section>
+
+
+<section name="Web Application Deployment Descriptor">
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+<p>As mentioned above, the <code>/WEB-INF/web.xml</code> file contains the
+Web Application Deployment Descriptor for your application.  As the filename
+extension implies, this file is an XML document, and defines everything about
+your application that a server needs to know (except the <em>context path</em>,
+which is assigned by the system administrator when the application is
+deployed).</p>
+
+<p>The complete syntax and semantics for the deployment descriptor is defined
+in Chapter 13 of the Servlet API Specification, version 2.3.  Over time, it
+is expected that development tools will be provided that create and edit the
+deployment descriptor for you.  In the meantime, to provide a starting point,
+a <a href="web.xml.txt" target="_new">basic web.xml file</a>
+is provided.  This file includes comments that describe the purpose of each
+included element.</p>
+
+<p><strong>NOTE</strong> - The Servlet Specification includes a Document
+Type Descriptor (DTD) for the web application deployment descriptor, and
+Tomcat 5 enforces the rules defined here when processing your application's
+<code>/WEB-INF/web.xml</code> file.  In particular, you <strong>must</strong>
+enter your descriptor elements (such as <code>&lt;filter&gt;</code>,
+<code>&lt;servlet&gt;</code>, and <code>&lt;servlet-mapping&gt;</code> in
+the order defined by the DTD (see Section 13.3).</p>
+
+</section>
+
+
+<section name="Tomcat Context Descriptor">
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+<p>A /META-INF/context.xml file can be used to define Tomcat specific
+configuration options, such as loggers, data sources, session manager
+configuration and more. This XML file must contain one Context element, which
+will be considered as if it was the child of the Host element corresponding
+to the Host to which the  The Tomcat configuration documentation contains
+information on the Context element.</p>
+
+</section>
+
+
+<section name="Deployment With Tomcat 5">
+
+<p>In order to be executed, a web application must be deployed on
+a servlet container.  This is true even during development.
+We will describe using Tomcat 5 to provide the execution environment.
+A web application can be deployed in Tomcat by one of the following
+approaches:</p>
+<ul>
+<li><em>Copy unpacked directory hierarchy into a subdirectory in directory
+    <code>$CATALINA_HOME/webapps/</code></em>.  Tomcat will assign a
+    context path to your application based on the subdirectory name you
+    choose.  We will use this technique in the <code>build.xml</code>
+    file that we construct, because it is the quickest and easiest approach
+    during development.  Be sure to restart Tomcat after installing or
+    updating your application.
+    <br/><br/></li>
+<li><em>Copy the web application archive file into directory
+    <code>$CATALINA_HOME/webapps/</code></em>.  When Tomcat is started, it will
+    automatically expand the web application archive file into its unpacked
+    form, and execute the application that way.  This approach would typically
+    be used to install an additional application, provided by a third party
+    vendor or by your internal development staff, into an existing
+    Tomcat installation.  <strong>NOTE</strong> - If you use this approach,
+    and wish to update your application later, you must both replace the
+    web application archive file <strong>AND</strong> delete the expanded
+    directory that Tomcat created, and then restart Tomcat, in order to reflect
+    your changes.
+    <br/><br/></li>
+<li><em>Use the Tomcat 5 "Manager" web application to deploy and undeploy
+    web applications</em>.  Tomcat 5 includes a web application, deployed
+    by default on context path <code>/manager</code>, that allows you to
+    deploy and undeploy applications on a running Tomcat server without
+    restarting it.  See the administrator documentation (TODO: hyperlink)
+    for more information on using the Manager web application.<br/><br/></li>
+<li><em>Use "Manager" Ant Tasks In Your Build Script</em>.  Tomcat 5
+    includes a set of custom task definitions for the <code>Ant</code>
+    build tool that allow you to automate the execution of commands to the
+    "Manager" web application.  These tasks are used in the Tomcat deployer.
+    <br/><br/></li>
+<li><em>Use the Tomcat Deployer</em>.  Tomcat 5 includes a packaged tool
+    bundling the Ant tasks, and can be used to automatically precompile JSPs
+    which are part of the web application before deployment to the server.
+    <br/><br/></li>
+</ul>
+
+<p>Deploying your app on other servlet containers will be specific to each
+container, but all containers compatible with the Servlet API Specification
+(version 2.2 or later) are required to accept a web application archive file.
+Note that other containers are <strong>NOT</strong> required to accept an
+unpacked directory structure (as Tomcat does), or to provide mechanisms for
+shared library files, but these features are commonly available.</p>
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/appdev/index.xml b/container/webapps/docs/appdev/index.xml
new file mode 100644
index 0000000..359c5ed
--- /dev/null
+++ b/container/webapps/docs/appdev/index.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="index.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>Table of Contents</title>
+  </properties>
+
+<body>
+
+
+<section name="Preface">
+
+<p>This manual includes contributions from many members of the Tomcat Project
+developer community.  The following authors have provided significant content:
+</p>
+<ul>
+<li>Craig R. McClanahan
+    (<a href="mailto:craigmcc@apache.org">craigmcc@apache.org</a>)</li>
+</ul>
+
+</section>
+
+
+<section name="Table of Contents">
+
+<p>The information presented is divided into the following sections:</p>
+<ul>
+<li><a href="introduction.html"><strong>Introduction</strong></a> -
+    Briefly describes the information covered here, with
+    links and references to other sources of information.</li>
+<li><a href="installation.html"><strong>Installation</strong></a> -
+    Covers acquiring and installing the required software
+    components to use Tomcat for web application development.</li>
+<li><a href="deployment.html"><strong>Deployment Organization</strong></a> -
+    Discusses the standard directory layout for a web application
+    (defined in the Servlet API Specification), the Web Application
+    Deployment Descriptor, and options for integration with Tomcat
+    in your development environment.</li>
+<li><a href="source.html"><strong>Source Organization</strong></a> -
+    Describes a useful approach to organizing the source code
+    directories for your project, and introduces the
+    <code>build.xml</code> used by Ant to manage compilation.</li>
+<li><a href="processes.html"><strong>Development Processes</strong></a> -
+    Provides brief descriptions of typical development processes
+    utilizing the recommended deployment and source organizations.</li>
+<li><a href="sample/" target="_new"><strong>Example Application</strong></a> -
+    This directory contains a very simple, but functionally complete,
+    "Hello, World" application built according to the principles
+    described in this manual.  You can use this application to
+    practice using the described techniques.</li>
+</ul>
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/appdev/installation.xml b/container/webapps/docs/appdev/installation.xml
new file mode 100644
index 0000000..de9f9b5
--- /dev/null
+++ b/container/webapps/docs/appdev/installation.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="installation.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>Installation</title>
+  </properties>
+
+<body>
+
+
+<section name="Installation">
+
+<p>In order to use Tomcat 5 for developing web applications, you must first
+install it (and the software it depends on).  The required steps are outlined
+in the following subsections.</p>
+
+<subsection name="JDK">
+
+<p>Tomcat 5.5 was designed to run on J2SE 5.0, but it can run on JDK 1.4
+as well, using the compatability package as detailed in the Tomcat 
+installation instructions.
+</p>
+
+<p>Compatible JDKs for many platforms (or links to where they can be found)
+are available at
+<a href="http://java.sun.com/j2se/">http://java.sun.com/j2se/</a>.</p>
+
+</subsection>
+
+<subsection name="Tomcat">
+
+<p>Binary downloads of the <strong>Tomcat</strong> server are available from
+<a href="http://jakarta.apache.org/binindex.cgi">http://jakarta.apache.org/binindex.cgi</a>.
+This manual assumes you are using the most recent release
+of Tomcat 5.  Detailed instructions for downloading and installing
+Tomcat 5 are available <a href="../setup.html">here</a>.</p>
+
+<p>In the remainder of this manual, example shell scripts assume that you have
+set an environment variable <code>CATALINA_HOME</code> that contains the
+pathname to the directory in which Tomcat 5 has been installed.</p>
+
+</subsection>
+
+
+<subsection name="Ant">
+
+<p>Binary downloads of the <strong>Ant</strong> build tool are available from
+<a href="http://ant.apache.org/bindownload.cgi">http://ant.apache.org/bindownload.cgi</a>.
+This manual assumes you are using Ant 1.4 or later.  The instructions should
+also be compatible with later versions, but this has not been tested.</p>
+
+<p>Download and install Ant from the distribution directory mentioned above.
+Then, add the <code>bin</code> directory of the Ant distribution to your
+<code>PATH</code> environment variable, following the standard practices for
+your operating system platform.  Once you have done this, you will be able to
+execute the <code>ant</code> shell command directly.</p>
+
+</subsection>
+
+
+<subsection name="CVS">
+
+<p>Besides the required tools described above, you are strongly encouraged
+to download and install a <em>source code control</em> system, such as the
+<strong>Concurrent Version System</strong> (CVS), to maintain historical
+versions of the source files that make up your web application.  Besides
+the server, you will also need appropriate client
+tools to check out source code files, and check in modified versions.</p>
+
+<p>Detailed instructions for installing and using source code control
+applications is beyond the scope of this manual.  However, CVS server and
+client tools for many platforms (along with documentation) can be downloaded
+from <a href="http://www.cvshome.org">http://www.cvshome.org</a>.</p>
+
+</subsection>
+
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/appdev/introduction.xml b/container/webapps/docs/appdev/introduction.xml
new file mode 100644
index 0000000..43b82b7
--- /dev/null
+++ b/container/webapps/docs/appdev/introduction.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="introduction.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>Introduction</title>
+  </properties>
+
+<body>
+
+
+<section name="Overview">
+
+<p>Congratulations!  You've decided to (or been told to) learn how to
+build web applications using servlets and JSP pages, and picked the
+Tomcat server to use for your learning and development.  But now what
+do you do?</p>
+
+<p>This manual is a primer covering the basic steps of using Tomcat to
+set up a development environment, organize your source code, and then
+build and test your application.  It does not discuss architectures or
+recommended coding practices for web application development,
+or provide in depth instructions on operating the development
+tools that are discussed.  References to sources of additional information
+are included in the following subsections.</p>
+
+<p>The discussion in this manual is aimed at developers who will be using
+a text editor along with command line tools to develop and debug their
+applications.  As such, the recommendations are fairly generic -- but you
+should easily be able to apply them in either a Windows-based or Unix-based
+development environment.  If you are utilizing an Interactive Development
+Environment (IDE) tool, you will need to adapt the advice given here to
+the details of your particular environment.</p>
+
+</section>
+
+
+<section name="Links">
+
+<p>The following links provide access to selected sources of online
+information, documentation, and software that is useful in developing
+web applications with Tomcat.</p>
+<ul>
+<li><a href="http://java.sun.com/products/jsp/download.html">http://java.sun.com/products/jsp/download.html</a> -
+    <i>JavaServer Pages (JSP) Specfication, Version 2.0</i>.  Describes
+    the programming environment provided by standard implementations
+    of the JavaServer Pages (JSP) technology.  In conjunction with
+    the Servlet API Specification (see below), this document describes
+    what a portable API page is allowed to contain.  Specific
+    information on scripting (Chapter 6), tag extensions (Chapter 7),
+    and packaging JSP pages (Appendix A) is useful.  The Javadoc
+    API Documentation is included in the specification, and with the
+    Tomcat download.<br/><br/></li>
+<li><a href="http://java.sun.com/products/servlet/download.html">http://java.sun.com/products/servlet/download.html</a> -
+    <i>Servlet API Specification, Version 2.4</i>.  Describes the
+    programming environment that must be provided by all servlet
+    containers conforming to this specification.  In particular, you
+    will need this document to understand the web application
+    directory structure and deployment file (Chapter 9), methods of
+    mapping request URIs to servlets (Chapter 11), container managed
+    security (Chapter 12), and the syntax of the <code>web.xml</code>
+    Web Application Deployment Descriptor (Chapter 13).  The Javadoc
+    API Documentation is included in the specification, and with the
+    Tomcat download.<br/><br/></li>
+<li><a href="http://java.sun.com/j2ee/blueprints/">http://java.sun.com/j2ee/blueprints/</a> -
+    <i>Sun BluePrints (tm) Design Guidelines for J2EE</i>.  Comprehensive
+    advice and examples on application design for the Java2 Enterprise
+    Edition (J2EE) platform, which includes servlets and JSP pages.  The
+    chapters on servlet and JSP design are useful even when your application
+    does not require other J2EE platform components.
+    <br/><br/></li>
+<li><b>TODO</b> -- Add more entries here!</li>
+</ul>
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/appdev/processes.xml b/container/webapps/docs/appdev/processes.xml
new file mode 100644
index 0000000..e11494e
--- /dev/null
+++ b/container/webapps/docs/appdev/processes.xml
@@ -0,0 +1,299 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="processes.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>Development Processes</title>
+  </properties>
+
+<body>
+
+
+<section name="Development Processes">
+
+<p>Although application development can take many forms, this manual proposes
+a fairly generic process for creating web applications using Tomcat.  The
+following sections highlight the commands and tasks that you, as the developer
+of the code, will perform.  The same basic approach works when you have
+multiple programmers involved, as long as you have an appropriate source code
+control system and internal team rules about who is working on what parts
+of the application at any given time.</p>
+
+<p>The task descriptions below assume that you will be using CVS for source
+code control, and that you have already configured access to the appropriate
+CVS repository.  Instructions for doing this are beyond the scope of this
+manual.  If you are using a different source code control environment, you
+will need to figure out the corresponding commands for your system.</p>
+
+
+<subsection name="One-Time Setup of Ant and Tomcat for Development">
+
+<p>In order to take advantage of the special Ant tasks that interact with the
+<em>Manager</em> web application, you need to perform the following tasks
+once (no matter how many web applications you plan to develop).</p>
+<ul>
+<li><em>Configure the Ant custom tasks</em>.  The implementation code for the
+    Ant custom tasks is in a JAR file named
+    <code>$CATALINA_HOME/server/lib/catalina-ant.jar</code>, which must be
+    copied in to the <code>lib</code> directory of your Ant installation.
+    <br/><br/></li>
+<li><em>Define one or more Tomcat users</em>.  The <em>Manager</em> web
+    application runs under a security constraint that requires a user to be
+    logged in, and have the security role <code>manager</code> assigned to
+    him or her.  How such users are defined depends on which Realm you have
+    configured in Tomcat's <code>conf/server.xml</code> file -- see the
+    <a href="../realm-howto.html">Realm Configuration HOW-TO</a> for more
+    information.  You may define any number of users (with any username
+    and password that you like) with the <code>manager</code> role.
+    <br/><br/></li>
+</ul>
+
+</subsection>
+
+
+<subsection name="Create Project Source Code Directory">
+
+<p>The first step is to create a new project source directory, and customize
+the <code>build.xml</code> and <code>build.properties</code> files you will
+be using.  The directory structure is described in <a href="source.html">the
+previous section</a>, or you can use the
+<a href="sample/">sample application</a> as a starting point.</p>
+
+<p>Create your project source directory, and define it within your CVS
+repository.  This might be done by a series of commands like this, where
+<code>{project}</code> is the name under which your project should be
+stored in the CVS repository, and {username} is your login username:</p>
+<source>
+cd {my home directory}
+mkdir myapp	&lt;-- Assumed "project source directory"
+cd myapp
+mkdir docs
+mkdir src
+mkdir web
+mkdir web/WEB-INF
+cvs import -m "Initial Project Creation" {project} \
+	{username} start
+</source>
+
+<p>Now, to verify that it was created correctly in CVS, we will perform a
+checkout of the new project:</p>
+<source>
+cd ..
+mv myapp myapp.bu
+cvs checkout {project}
+</source>
+
+<p>Next, you will need to create and check in an initial version of the
+<code>build.xml</code> script to be used for development.  For getting
+started quickly and easily, base your <code>build.xml</code> on the
+<a href="build.xml.txt">basic build.xml file</a>, included with this manual,
+or code it from scratch.</p>
+<source>
+cd {my home directory}
+cd myapp
+emacs build.xml		&lt;-- if you want a real editor :-)
+cvs add build.xml
+cvs commit
+</source>
+
+<p>Until you perform the CVS commit, your changes are local to your own
+development directory.  Committing makes those changes visible to other
+developers on your team that are sharing the same CVS repository.</p>
+
+<p>The next step is to customize the Ant <em>properties</em> that are
+named in the <code>build.xml</code> script.  This is done by creating a
+file named <code>build.properties</code> in your project's top-level
+directory.  The supported properties are listed in the comments inside
+the sample <code>build.xml</code> script.  At a minimum, you will generally
+need to define the <code>catalina.home</code> property defining where
+Tomcat 5 is installed, and the manager application username and password.
+You might end up with something like this:</p>
+<source>
+# Context path to install this application on
+app.path=/hello
+
+# Tomcat 5 installation directory
+catalina.home=/usr/local/jakarta-tomcat-5.0
+
+# Manager webapp username and password
+manager.username=myusername
+manager.password=mypassword
+</source>
+
+<p>In general, you will <strong>not</strong> want to check the
+<code>build.properties</code> file in to the CVS repository, because it
+is unique to each developer's environment.</p>
+
+<p>Now, create the initial version of the web application deployment
+descriptor.  You can base <code>web.xml</code> on the
+<a href="web.xml.txt">basic web.xml file</a>, or code it from scratch.</p>
+<source>
+cd {my home directory}
+cd myapp/web/WEB-INF
+emacs web.xml
+cvs add web.xml
+cvs commit
+</source>
+
+Note that this is only an example web.xml file.  The full definition
+of the deployment descriptor file is in the
+<a href="http://java.sun.com/products/servlet">Servlet Specification.</a>
+
+</subsection>
+
+
+<subsection name="Edit Source Code and Pages">
+
+<p>The edit/build/test tasks will generally be your most common activities
+during development and maintenance.  The following general principles apply.
+As described in <a href="source.html">Source Organization</a>, newly created
+source files should be located in the appropriate subdirectory, under your
+project source directory.</p>
+
+<p>Whenever you wish to refresh your development directory to reflect the
+work performed by other developers, you will ask CVS to do it for you:</p>
+<source>
+cd {my home directory}
+cd myapp
+cvs update -dP
+</source>
+
+<p>To create a new file, go to the appropriate directory, create the file,
+and register it with CVS.  When you are satisfied with it's contents (after
+building and testing is successful), commit the new file to the repository.
+For example, to create a new JSP page:</p>
+<source>
+cd {my home directory}
+cd myapp/web		&lt;-- Ultimate destination is document root
+emacs mypage.jsp
+cvs add mypage.jsp
+... build and test the application ...
+cvs commit
+</source>
+
+<p>Java source code that is defined in packages must be organized in a
+directory hierarchy (under the <strong>src/</strong> subdirectory) that
+matches the package names.  For example, a Java class named
+<code>com.mycompany.mypackage.MyClass.java</code> should be stored in file
+<code>src/com/mycompany/mypackage/MyClass.java</code>.
+Whenever you create a new subdirectory, don't forget to
+register it with CVS.</p>
+
+<p>To edit an existing source file, you will generally just start editing
+and testing, then commit the changed file when everything works.  Although
+CVS can be configured to required you to "check out" or "lock" a file you
+are going to be modifying, this is generally not used.</p>
+
+</subsection>
+
+
+<subsection name="Build the Web Application">
+
+<p>When you are ready to compile the application, issue the following
+commands (generally, you will want a shell window open that is set to
+the project source directory, so that only the last command is needed):</p>
+<source>
+cd {my home directory}
+cd myapp		&lt;-- Normally leave a window open here
+ant
+</source>
+
+<p>The Ant tool will be execute the default "compile" target in your
+<code>build.xml</code> file, which will compile any new or updated Java
+code.  If this is the first time you compile after a "build clean",
+it will cause everything to be recompiled.</p>
+
+<p>To force the recompilation of your entire application, do this instead:</p>
+<source>
+cd {my home directory}
+cd myapp
+ant all
+</source>
+
+<p>This is a very good habit immediately before checking in changes, to
+make sure that you have not introduced any subtle problems that Javac's
+conditional checking did not catch.</p>
+
+</subsection>
+
+
+<subsection name="Test Your Web Application">
+
+<p>To test your application, you will want to install it under Tomcat.  The
+quickest way to do that is to use the custom Ant tasks that are included in
+the sample <code>build.xml</code> script.  Using these commands might follow
+a pattern like this:</p>
+<ul>
+<li><em>Start Tomcat 5 if needed</em>.  If Tomcat 5 is not already running,
+    you will need to start it in the usual way.
+    <br/><br/></li>
+<li><em>Compile your application</em>.  Use the <code>ant compile</code>
+    command (or just <code>ant</code>, since this is the default).  Make
+    sure that there are no compilation errors.
+    <br/><br/></li>
+<li><em>Install the application</em>.  Use the <code>ant install</code>
+    command.  This tells Tomcat to immediately start running your app on
+    the context path defined in the <code>app.path</code> build property.
+    Tomcat does <strong>NOT</strong> have to be restarted for this to
+    take effect.<br/><br/></li>
+<li><em>Test the application</em>.  Using your browser or other testing
+    tools, test the functionality of your application.
+    <br/><br/></li>
+<li><em>Modify and rebuild as needed</em>.  As you discover that changes
+    are required, make those changes in the original <strong>source</strong>
+    files, not in the output build directory, and re-issue the
+    <code>ant compile</code> command.  This ensures that your changes will
+    be available to be saved (via <code>cvs commit</code>) later on --
+    the output build directory is deleted and recreated as necessary.
+    <br/><br/></li>
+<li><em>Reload the application</em>.  Tomcat will recognize changes in
+    JSP pages automatically, but it will continue to use the old versions
+    of any servlet or JavaBean classes until the application is reloaded.
+    You can trigger this by executing the <code>ant reload</code> command.
+    <br/><br/></li>
+<li><em>Remove the application when you re done</em>.  When you are through
+    working on this application, you can remove it from live execution by
+    running the <code>ant remove</code> command.</li>
+</ul>
+
+<p>Do not forget to commit your changes to the source code repository when
+you have completed your testing!</p>
+
+</subsection>
+
+
+<subsection name="Creating a Release">
+
+<p>When you are through adding new functionality, and you've tested everything
+(you DO test, don't you :-), it is time to create the distributable version
+of your web application that can be deployed on the production server.  The
+following general steps are required:</p>
+<ul>
+<li>Issue the command <code>ant all</code> from the project source
+    directory, to rebuild everything from scratch one last time.
+    <br/><br/></li>
+<li>Use the <code>cvs tag</code> command to create an identifier for
+    all of the source files utilized to create this release.  This allows
+    you to reliably reconstruct a release (from sources) at a later
+    time.</li>
+<li>Issue the command <code>ant dist</code> to create a distributable
+    web application archive (WAR) file, as well as a JAR file containing
+    the corresponding source code.
+    <br/><br/></li>
+<li>Package the contents of the <code>dist</code> directory using the
+    <strong>tar</strong> or <strong>zip</strong> utility, according to
+    the standard release procedures used by your organization.</li>
+</ul>
+
+</subsection>
+
+
+</section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/appdev/project.xml b/container/webapps/docs/appdev/project.xml
new file mode 100644
index 0000000..7ce8b24
--- /dev/null
+++ b/container/webapps/docs/appdev/project.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Application Developer's Guide"
+        href="http://jakarta.apache.org/tomcat/">
+
+    <title>Application Developer's Guide</title>
+
+    <logo href="/images/tomcat.gif">
+      The Tomcat Servlet/JSP Container
+    </logo>
+
+    
+    <body>
+
+    <menu name="Links">
+        <item name="Docs Home"             href="../index.html"/>
+    </menu>
+
+    <menu name="Contents">
+        <item name="Contents"              href="index.html"/>
+        <item name="Introduction"          href="introduction.html"/>
+        <item name="Installation"          href="installation.html"/>
+        <item name="Deployment"            href="deployment.html"/>
+        <item name="Source Code"           href="source.html"/>
+        <item name="Processes"             href="processes.html"/>
+        <item name="Example App"           href="sample/"/>
+    </menu>
+
+    </body>
+</project>
diff --git a/container/webapps/docs/appdev/sample/docs/README.txt b/container/webapps/docs/appdev/sample/docs/README.txt
new file mode 100644
index 0000000..641c989
--- /dev/null
+++ b/container/webapps/docs/appdev/sample/docs/README.txt
@@ -0,0 +1,2 @@
+This is a dummy README file for the sample
+web application.
diff --git a/container/webapps/docs/appdev/sample/index.html b/container/webapps/docs/appdev/sample/index.html
new file mode 100644
index 0000000..582c970
--- /dev/null
+++ b/container/webapps/docs/appdev/sample/index.html
@@ -0,0 +1,30 @@
+<html>
+<head>
+<meta name="author" content="Ben Souther" />
+<title>Sample Application</title>
+</head>
+<body>
+<h2>Sample Application</h2>
+      <p> 
+        The example app has been packaged as a war file and can be downloaded 
+        <a href="sample.war">here</a> (Note: make sure your browser doesn't 
+        change file extension or append a new one).
+      </p>
+      <p> 
+        The easiest way to run this application is simply to move the war file 
+        to your <b>CATALINA_HOME/webapps</b> directory. Tomcat will automatically 
+        expand and deploy the application for you. You can view it with the 
+        following URL (assuming that you're running tomcat on port 8080 
+        as is the default):
+        <br />
+        <a href="http://localhost:8080/sample">http://localhost:8080/sample</a>
+      </p>
+      <p>
+        If you just want to browse the code you can unpack the war file 
+        with the <b>jar</b> command.
+        <source>
+          jar -xvf sample.war
+        </source>
+      </p>
+</body>
+</html>
\ No newline at end of file
diff --git a/container/webapps/docs/appdev/sample/sample.war b/container/webapps/docs/appdev/sample/sample.war
new file mode 100644
index 0000000..f43f6c0
--- /dev/null
+++ b/container/webapps/docs/appdev/sample/sample.war
Binary files differ
diff --git a/container/webapps/docs/appdev/sample/src/mypackage/Hello.java b/container/webapps/docs/appdev/sample/src/mypackage/Hello.java
new file mode 100644
index 0000000..5301fa8
--- /dev/null
+++ b/container/webapps/docs/appdev/sample/src/mypackage/Hello.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mypackage;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Simple servlet to validate that the Hello, World example can
+ * execute servlets.  In the web application deployment descriptor,
+ * this servlet must be mapped to correspond to the link in the
+ * "index.html" file.
+ *
+ * @author Craig R. McClanahan <Craig.McClanahan@eng.sun.com>
+ */
+
+public final class Hello extends HttpServlet {
+
+
+    /**
+     * Respond to a GET request for the content produced by
+     * this servlet.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are producing
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+      throws IOException, ServletException {
+
+	response.setContentType("text/html");
+	PrintWriter writer = response.getWriter();
+
+	writer.println("<html>");
+	writer.println("<head>");
+	writer.println("<title>Sample Application Servlet Page</title>");
+	writer.println("</head>");
+	writer.println("<body bgcolor=white>");
+
+	writer.println("<table border=\"0\">");
+	writer.println("<tr>");
+	writer.println("<td>");
+	writer.println("<img src=\"images/tomcat.gif\">");
+	writer.println("</td>");
+	writer.println("<td>");
+	writer.println("<h1>Sample Application Servlet</h1>");
+	writer.println("This is the output of a servlet that is part of");
+	writer.println("the Hello, World application.  It displays the");
+	writer.println("request headers from the request we are currently");
+	writer.println("processing.");
+	writer.println("</td>");
+	writer.println("</tr>");
+	writer.println("</table>");
+
+	writer.println("<table border=\"0\" width=\"100%\">");
+	Enumeration names = request.getHeaderNames();
+	while (names.hasMoreElements()) {
+	    String name = (String) names.nextElement();
+	    writer.println("<tr>");
+	    writer.println("  <th align=\"right\">" + name + ":</th>");
+	    writer.println("  <td>" + request.getHeader(name) + "</td>");
+	    writer.println("</tr>");
+	}
+	writer.println("</table>");
+
+	writer.println("</body>");
+	writer.println("</html>");
+
+    }
+
+
+}
diff --git a/container/webapps/docs/appdev/sample/web/WEB-INF/web.xml b/container/webapps/docs/appdev/sample/web/WEB-INF/web.xml
new file mode 100644
index 0000000..95e2974
--- /dev/null
+++ b/container/webapps/docs/appdev/sample/web/WEB-INF/web.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+    <display-name>Hello, World Application</display-name>
+    <description>
+	This is a simple web application with a source code organization
+	based on the recommendations of the Application Developer's Guide.
+    </description>
+
+    <servlet>
+        <servlet-name>HelloServlet</servlet-name>
+        <servlet-class>mypackage.Hello</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>HelloServlet</servlet-name>
+        <url-pattern>/hello</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/container/webapps/docs/appdev/sample/web/hello.jsp b/container/webapps/docs/appdev/sample/web/hello.jsp
new file mode 100644
index 0000000..bf5957c
--- /dev/null
+++ b/container/webapps/docs/appdev/sample/web/hello.jsp
@@ -0,0 +1,44 @@
+<html>
+<head>
+<title>Sample Application JSP Page</title>
+</head>
+<body bgcolor=white>
+
+<table border="0">
+<tr>
+<td align=center>
+<img src="images/tomcat.gif">
+</td>
+<td>
+<h1>Sample Application JSP Page</h1>
+This is the output of a JSP page that is part of the Hello, World
+application.  It displays several useful values from the request
+we are currently processing.
+</td>
+</tr>
+</table>
+
+<table border="0" border="100%">
+<tr>
+  <th align="right">Context Path:</th>
+  <td align="left"><%= request.getContextPath() %></td>
+</tr>
+<tr>
+  <th align="right">Path Information:</th>
+  <td align="left"><%= request.getPathInfo() %></td>
+</tr>
+<tr>
+  <th align="right">Query String:</th>
+  <td align="left"><%= request.getQueryString() %></td>
+</tr>
+<tr>
+  <th align="right">Request Method:</th>
+  <td align="left"><%= request.getMethod() %></td>
+</tr>
+<tr>
+  <th align="right">Servlet Path:</th>
+  <td align="left"><%= request.getServletPath() %></td>
+</tr>
+</table>
+</body>
+</html>
diff --git a/container/webapps/docs/appdev/sample/web/images/tomcat.gif b/container/webapps/docs/appdev/sample/web/images/tomcat.gif
new file mode 100644
index 0000000..32f7d80
--- /dev/null
+++ b/container/webapps/docs/appdev/sample/web/images/tomcat.gif
Binary files differ
diff --git a/container/webapps/docs/appdev/sample/web/index.html b/container/webapps/docs/appdev/sample/web/index.html
new file mode 100644
index 0000000..b5b7a70
--- /dev/null
+++ b/container/webapps/docs/appdev/sample/web/index.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+<title>Sample "Hello, World" Application</title>
+</head>
+<body bgcolor=white>
+
+<table border="0">
+<tr>
+<td>
+<img src="images/tomcat.gif">
+</td>
+<td>
+<h1>Sample "Hello, World" Application</h1>
+<p>This is the home page for a sample application used to illustrate the
+source directory organization of a web application utilizing the principles
+outlined in the Application Developer's Guide.
+</td>
+</tr>
+</table>
+
+<p>To prove that they work, you can execute either of the following links:
+<ul>
+<li>To a <a href="hello.jsp">JSP page</a>.
+<li>To a <a href="hello">servlet</a>.
+</ul>
+
+</body>
+</html>
diff --git a/container/webapps/docs/appdev/source.xml b/container/webapps/docs/appdev/source.xml
new file mode 100644
index 0000000..84e5730
--- /dev/null
+++ b/container/webapps/docs/appdev/source.xml
@@ -0,0 +1,306 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="source.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>Source Organization</title>
+  </properties>
+
+<body>
+
+
+<section name="Directory Structure">
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+<p>A key recommendation of this manual is to separate the directory
+hierarchy containing your source code (described in this section) from
+the directory hierarchy containing your deployable application
+(described in the preceding section).  Maintaining this separation has
+the following advantages:</p>
+<ul>
+<li>The contents of the source directories can be more easily administered,
+    moved, and backed up if the "executable" version of the application
+    is not intermixed.
+    <br/><br/></li>
+<li>Source code control is easier to manage on directories that contain
+    only source files.
+    <br/><br/></li>
+<li>The files that make up an installable distribution of your
+    application are much easier to select when the deployment
+    hierarchy is separate.</li>
+</ul>
+
+<p>As we will see, the <code>ant</code> development tool makes the creation
+and processing of such directory hierarchies nearly painless.</p>
+
+<p>The actual directory and file hierarchy used to contain the source code
+of an application can be pretty much anything you like.  However, the
+following organization has proven to be quite generally applicable, and is
+expected by the example <code>build.xml</code> configuration file that
+is discussed below.  All of these components exist under a top level
+<em>project source directory</em> for your application:</p>
+<ul>
+<li><strong>docs/</strong> - Documentation for your application, in whatever
+    format your development team is using.<br/><br/></li>
+<li><strong>src/</strong> - Java source files that generate the servlets,
+    beans, and other Java classes that are unique to your application.
+    If your source code is organized in packages (<strong>highly</strong>
+    recommended), the package hierarchy should be reflected as a directory
+    structure underneath this directory.<br/><br/></li>
+<li><strong>web/</strong> - The static content of your web site (HTML pages,
+    JSP pages, JavaScript files, CSS stylesheet files, and images) that will
+    be accessible to application clients.  This directory will be the
+    <em>document root</em> of your web application, and any subdirectory
+    structure found here will be reflected in the request URIs required to
+    access those files.<br/><br/></li>
+<li><strong>web/WEB-INF/</strong> - The special configuration files required
+    for your application, including the web application deployment descriptor
+    (<code>web.xml</code>, defined in the 
+    <a href="http://java.sun.com/products/servlet">Servlet Specification</a>), 
+    tag library descriptors for custom tag libraries
+    you have created, and other resource files you wish to include within
+    your web application.  Even though this directory appears to be a
+    subdirectory of your <em>document root</em>, the Servlet Specification
+    prohibits serving the contents of this directory (or any file it contains)
+    directly to a client request.  Therefore, this is a good place to store
+    configuration information that is sensitive (such as database connection
+    usernames and passwords), but is required for your application to
+    operate successfully.</li>
+</ul>
+
+<p>During the development process, two additional directories will be
+created on a temporary basis:</p>
+<ul>
+<li><strong>build/</strong> - When you execute a default build
+    (<code>ant</code>), this directory will contain an exact image
+    of the files in the web application archive for this application.
+    Tomcat 5 allows you to deploy an application in an unpacked
+    directory like this, either by copying it to the
+    <code>$CATALINA_HOME/webapps</code> directory, or by <em>installing</em>
+    it via the "Manager" web application.  The latter approach is very
+    useful during development, and will be illustrated below.
+    <br/><br/></li>
+<li><strong>dist/</strong> - When you execute the <code>ant dist</code>
+    target, this directory will be created.  It will create an exact image
+    of the binary distribution for your web application, including an license
+    information, documentation, and README files that you have prepared.</li>
+</ul>
+
+<p>Note that these two directories should <strong>NOT</strong> be archived in
+your source code control system, because they are deleted and recreated (from
+scratch) as needed during development.  For that reason, you should not edit
+any source files in these directories if you want to maintain a permanent
+record of the changes, because the changes will be lost the next time that a
+build is performed.</p>
+
+  <subsection name="External Dependencies">
+
+  <p>What do you do if your application requires JAR files (or other
+  resources) from external projects or packages?  A common example is that
+  you need to include a JDBC driver in your web application, in order to
+  operate.</p>
+
+  <p>Different developers take different approaches to this problem.
+  Some will encourage checking a copy of the JAR files you depend on into
+  the source code control archives for every application that requires those
+  JAR files.  However, this can cause significant management issues when you
+  use the same JAR in many applications - particular when faced with a need
+  to upgrade to a different version of that JAR file.</p>
+
+  <p>Therefore, this manual recommends that you <strong>NOT</strong> store
+  a copy of the packages you depend on inside the source control archives
+  of your applications.  Instead, the external dependencies should be
+  integrated as part of the process of <strong>building</strong> your
+  application.  In that way, you can always pick up the appropriate version
+  of the JAR files from wherever your development system administrator has
+  installed them, without having to worry about updating your application
+  every time the version of the dependent JAR file is changed.</p>
+
+  <p>In the example Ant <code>build.xml</code> file, we will demonstrate
+  how to define <em>build properties</em> that let you configure the locations
+  of the files to be copied, without having to modify <code>build.xml</code>
+  when these files change.  The build properties used by a particular
+  developer can be customized on a per-application basis, or defaulted to
+  "standard" build properties stored in the developer's home directory.</p>
+
+  <p>In many cases, your development system administrator will have already
+  installed the required JAR files into Tomcat 5's <code>common/lib</code>
+  or <code>shared/lib</code> directories.  If this has been done, you need
+  to take no actions at all - the example <code>build.xml</code> file
+  automatically constructs a compile classpath that includes these files.</p>
+
+  </subsection>
+
+</section>
+
+
+<section name="Source Code Control">
+
+<p>As mentioned earlier, it is highly recommended that you place all of the
+source files that comprise your application under the management of a
+source code control system like the Concurrent Version System (CVS).  If you
+elect to do this, every directory and file in the source hierarchy should be
+registered and saved -- but none of the generated files.  If you register
+binary format files (such as images or JAR libraries), be sure to indicate
+this to your source code control system.</p>
+
+<p>We recommended (in the previous section) that you should not store the
+contents of the <code>build/</code> and <code>dist/</code> directories
+created by your development process in the source code control system.  An
+easy way to tell CVS to ignore these directories is to create a file named
+<code>.cvsignore</code> (note the leading period) in your top-level source
+directory, with the following contents:</p>
+<source>
+build
+dist
+build.properties
+</source>
+
+<p>The reason for mentioning <code>build.properties</code> here will be
+explained in the <a href="processes.html">Processes</a> section.</p>
+
+<p>Detailed instructions for your source code control environment are beyond
+the scope of this manual.  However, the following steps are followed when
+using a command-line CVS client:</p>
+<ul>
+<li>To refresh the state of your source code to that stored in the
+    the source repository, go to your project source directory, and
+    execute <code>cvs update -dP</code>.
+    <br/><br/></li>
+<li>When you create a new subdirectory in the source code hierarchy, register
+    it in CVS with a command like <code>cvs add {subdirname}</code>.
+    <br/><br/></li>
+<li>When you first create a new source code file, navigate to the directory
+    that contains it, and register the new file with a command like
+    <code>cvs add {filename}</code>.
+    <br/><br/></li>
+<li>If you no longer need a particular source code file, navigate to the
+    containing directory and remove the file.  Then, deregister it in CVS
+    with a command like <code>cvs remove {filename}</code>.
+    <br/><br/></li>
+<li>While you are creating, modifying, and deleting source files, changes
+    are not yet reflected in the server repository.  To save your changes in
+    their current state, go to the project source directory
+    and execute <code>cvs commit</code>.  You will be asked to write a brief
+    description of the changes you have just completed, which will be stored
+    with the new version of any updated source file.</li>
+</ul>
+
+<p>CVS, like other source code control systems, has many additional features
+(such as the ability to tag the files that made up a particular release, and
+support for multiple development branches that can later be merged).  See the
+links and references in the <a href="introduction.html">Introduction</a> for
+more information.</p>
+
+</section>
+
+
+<section name="BUILD.XML Configuration File">
+
+<p>We will be using the <strong>ant</strong> tool to manage the compilation of
+our Java source code files, and creation of the deployment hierarchy.  Ant
+operates under the control of a build file, normally called
+<code>build.xml</code>, that defines the processing steps required.  This
+file is stored in the top-level directory of your source code hierarchy, and
+should be checked in to your source code control system.</p>
+
+<p>Like a Makefile, the <code>build.xml</code> file provides several
+"targets" that support optional development activities (such as creating
+the associated Javadoc documentation, erasing the deployment home directory
+so you can build your project from scratch, or creating the web application
+archive file so you can distribute your application.  A well-constructed
+<code>build.xml</code> file will contain internal documentation describing
+the targets that are designed for use by the developer, versus those targets
+used internally.  To ask Ant to display the project documentation, change to
+the directory containing the <code>build.xml</code> flie and type:</p>
+<source>
+ant -projecthelp
+</source>
+
+<p>To give you a head start, a <a href="build.xml.txt">basic build.xml file</a>
+is provided that you can customize and install in the project source directory
+for your application.  This file includes comments that describe the various
+targets that can be executed.  Briefly, the following targets are generally
+provided:</p>
+<ul>
+<li><strong>clean</strong> - This target deletes any existing
+    <code>build</code> and <code>dist</code> directories, so that they
+    can be reconstructed from scratch.  This allows you to guarantee that
+    you have not made source code modifications that will result in
+    problems at runtime due to not recompiling all affected classes.
+    <br/><br/></li>
+<li><strong>compile</strong> - This target is used to compile any source code
+    that has been changed since the last time compilation took place.  The
+    resulting class files are created in the <code>WEB-INF/classes</code>
+    subdirectory of your <code>build</code> directory, exactly where the
+    structure of a web application requires them to be.  Because
+    this command is executed so often during development, it is normally
+    made the "default" target so that a simple <code>ant</code> command will
+    execute it.
+    <br/><br/></li>
+<li><strong>all</strong> - This target is a short cut for running the
+    <code>clean</code> target, followed by the <code>compile</code> target.
+    Thus, it guarantees that you will recompile the entire application, to
+    ensure that you have not unknowingly introduced any incompatible changes.
+    <br/><br/></li>
+<li><strong>javadoc</strong> - This target creates Javadoc API documentation
+    for the Java classes in this web application.  The example
+    <code>build.xml</code> file assumes you want to include the API
+    documentation with your app distribution, so it generates the docs
+    in a subdirectory of the <code>dist</code> directory.  Because you normally
+    do not need to generate the Javadocs on every compilation, this target is
+    usually a dependency of the <code>dist</code> target, but not of the
+    <code>compile</code> target.
+    <br/><br/></li>
+<li><strong>dist</strong> - This target creates a distribution directory for
+    your application, including any required documentation, the Javadocs for
+    your Java classes, and a web application archive (WAR) file that will be
+    delivered to system administrators who wish to install your application.
+    Because this target also depends on the <code>deploy</code> target, the
+    web application archive will have also picked up any external dependencies
+    that were included at deployment time.</li>
+</ul>
+
+<p>For interactive development and testing of your web application using
+Tomcat 5, the following additional targets are defined:</p>
+<ul>
+<li><strong>install</strong> - Tell the currently running Tomcat 5 to make
+    the application you are developing immediately available for execution
+    and testing.  This action does not require Tomcat 5 to be restarted, but
+    it is also not remembered after Tomcat is restarted the next time.
+    <br/><br/></li>
+<li><strong>reload</strong> - Once the application is installed, you can
+    continue to make changes and recompile using the <code>compile</code>
+    target.  Tomcat 5 will automatically recognize changes made to JSP pages,
+    but not to servlet or JavaBean classes - this command will tell Tomcat
+    to restart the currently installed application so that such changes are
+    recognized.
+    <br/><br/></li>
+<li><strong>remove</strong> - When you have completed your development and
+    testing activities, you can optionally tell Tomcat 5 to remove this
+    application from service.
+    </li>
+</ul>
+
+<p>Using the development and testing targets requires some additional
+one-time setup that is described on the next page.</p>
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/appdev/web.xml.txt b/container/webapps/docs/appdev/web.xml.txt
new file mode 100644
index 0000000..e75bfba
--- /dev/null
+++ b/container/webapps/docs/appdev/web.xml.txt
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!DOCTYPE web-app 
+    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
+    "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+
+
+    <!-- General description of your web application -->
+
+    <display-name>My Web Application</display-name>
+    <description>
+      This is version X.X of an application to perform
+      a wild and wonderful task, based on servlets and
+      JSP pages.  It was written by Dave Developer
+      (dave@mycompany.com), who should be contacted for
+      more information.
+    </description>
+
+
+    <!-- Context initialization parameters that define shared
+         String constants used within your application, which
+         can be customized by the system administrator who is
+         installing your application.  The values actually
+         assigned to these parameters can be retrieved in a
+         servlet or JSP page by calling:
+
+             String value =
+               getServletContext().getInitParameter("name");
+
+         where "name" matches the <param-name> element of
+         one of these initialization parameters.
+
+         You can define any number of context initialization
+         parameters, including zero.
+    -->
+
+    <context-param>
+      <param-name>webmaster</param-name>
+      <param-value>myaddress@mycompany.com</param-value>
+      <description>
+        The EMAIL address of the administrator to whom questions
+        and comments about this application should be addressed.
+      </description>
+    </context-param>
+
+
+    <!-- Servlet definitions for the servlets that make up
+         your web application, including initialization
+         parameters.  With Tomcat, you can also send requests
+         to servlets not listed here with a request like this:
+
+           http://localhost:8080/{context-path}/servlet/{classname}
+
+         but this usage is not guaranteed to be portable.  It also
+         makes relative references to images and other resources
+         required by your servlet more complicated, so defining
+         all of your servlets (and defining a mapping to them with
+         a servlet-mapping element) is recommended.
+
+         Servlet initialization parameters can be retrieved in a
+         servlet or JSP page by calling:
+
+             String value =
+               getServletConfig().getInitParameter("name");
+
+         where "name" matches the <param-name> element of
+         one of these initialization parameters.
+
+         You can define any number of servlets, including zero.
+    -->
+
+    <servlet>
+      <servlet-name>controller</servlet-name>
+      <description>
+        This servlet plays the "controller" role in the MVC architecture
+        used in this application.  It is generally mapped to the ".do"
+        filename extension with a servlet-mapping element, and all form
+        submits in the app will be submitted to a request URI like
+        "saveCustomer.do", which will therefore be mapped to this servlet.
+
+        The initialization parameter namess for this servlet are the
+        "servlet path" that will be received by this servlet (after the
+        filename extension is removed).  The corresponding value is the
+        name of the action class that will be used to process this request.
+      </description>
+      <servlet-class>com.mycompany.mypackage.ControllerServlet</servlet-class>
+      <init-param>
+        <param-name>listOrders</param-name>
+        <param-value>com.mycompany.myactions.ListOrdersAction</param-value>
+      </init-param>
+      <init-param>
+        <param-name>saveCustomer</param-name>
+        <param-value>com.mycompany.myactions.SaveCustomerAction</param-value>
+      </init-param>
+      <!-- Load this servlet at server startup time -->
+      <load-on-startup>5</load-on-startup>
+    </servlet>
+
+    <servlet>
+      <servlet-name>graph</servlet-name>
+      <description>
+        This servlet produces GIF images that are dynamically generated
+        graphs, based on the input parameters included on the request.
+        It is generally mapped to a specific request URI like "/graph".
+      </description>
+    </servlet>
+
+
+    <!-- Define mappings that are used by the servlet container to
+         translate a particular request URI (context-relative) to a
+         particular servlet.  The examples below correspond to the
+         servlet descriptions above.  Thus, a request URI like:
+
+           http://localhost:8080/{contextpath}/graph
+
+         will be mapped to the "graph" servlet, while a request like:
+
+           http://localhost:8080/{contextpath}/saveCustomer.do
+
+         will be mapped to the "controller" servlet.
+
+         You may define any number of servlet mappings, including zero.
+         It is also legal to define more than one mapping for the same
+         servlet, if you wish to.
+    -->
+
+    <servlet-mapping>
+      <servlet-name>controller</servlet-name>
+      <url-pattern>*.do</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+      <servlet-name>graph</servlet-name>
+      <url-pattern>/graph</url-pattern>
+    </servlet-mapping>
+
+
+    <!-- Define the default session timeout for your application,
+         in minutes.  From a servlet or JSP page, you can modify
+         the timeout for a particular session dynamically by using
+         HttpSession.getMaxInactiveInterval(). -->
+
+    <session-config>
+      <session-timeout>30</session-timeout>    <!-- 30 minutes -->
+    </session-config>
+
+
+</web-app>
diff --git a/container/webapps/docs/apr.xml b/container/webapps/docs/apr.xml
new file mode 100644
index 0000000..03aa99d
--- /dev/null
+++ b/container/webapps/docs/apr.xml
@@ -0,0 +1,277 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="apr.html">
+
+    &project;
+
+  <properties>
+    <title>Apache Portable Runtime and Tomcat</title>
+    <author>Remy Maucherat</author>
+  </properties>
+
+<body>
+
+  <section name="Introduction">
+
+  <p>
+      Tomcat can use the <a href="http://apr.apache.org/">Apache Portable Runtime</a> to 
+      provide superior scalability, performance, and better integration with native server 
+      technologies. The Apache Portable Runtime is a highly portable library that is at 
+      the heart of Apache HTTP Server 2.x. APR has many uses, including access to advanced IO
+      functionality (such as sendfile, epoll and OpenSSL), OS level functionality (random number
+      generation, system status, etc), and native process handling (shared memory, NT
+      pipes and Unix sockets).
+  </p>
+  
+  <p>
+      These features allows making Tomcat a general purpose webserver, will enable much better 
+      integration with other native web technologies, and overall make Java much more viable as
+      a full fledged webserver platform rather than simply a backend focused technology.
+  </p>
+
+  </section>
+
+  <section name="Installation">
+
+    <p>
+      APR support requires three main native components to be installed:
+      <ul>
+        <li>APR library</li>
+        <li>JNI wrappers for APR used by Tomcat (libtcnative)</li>
+        <li>OpenSSL libraries</li>
+      </ul>
+    </p>
+
+    <subsection name="Windows">
+    
+    <p>
+      Windows binaries are provided for libapr and libtcnative. Windows OpenSSL
+      binaries are linked from the <a href="http://www.openssl.org">Official OpenSSL website</a>
+      (see related/binaries).
+    </p>
+    
+    </subsection>
+    
+    <subsection name="Linux">
+    
+    <p>
+      Most Linux distributions will ship packages for APR and OpenSSL. The JNI wrapper (libtcnative) will 
+      then have to be compiled. It depends on APR, OpenSSL, and the Java headers.
+    </p>
+    
+    <p>
+      Requirements:
+      <ul>
+        <li>APR 1.1+ development headers (libapr1-dev package)</li>
+        <li>OpenSSL 0.9.7+ development headers (libssl-dev package)</li>
+        <li>JNI headers from Java compatible JDK 1.4+</li>
+        <li>GNU development environment (gcc, make)</li>
+      </ul>
+    </p>
+    
+    <p>
+      The wrapper library sources are located in the Tomcat binary bundle, in the 
+      <code>bin/tomcat-native.tar.gz</code> archive.
+      Once the build environment is installed and the source archive is extracted, the wrapper library 
+      can be compiled using (from the folder containing the configure script):
+      <source>./configure &amp;&amp; make &amp;&amp; make install</source>
+    </p>
+    
+    </subsection>
+	
+  </section>
+
+  <section name="APR Components">
+
+  <p>
+    Once the libraries are properly installed and available to Java (if loading fails, the library path
+    will be displayed), the Tomcat connectors will automatically use APR. Configuration of the connectors
+    is similar to the regular connectors, but have a few extra attributes which are used to configure
+    APR components. Note that the defaults should be well tuned for most use cases, and additional
+    tweaking shouldn't be required.
+  </p>
+
+  <p>
+    When APR is enabled, the following features are also enabled in Tomcat:
+    <ul>
+      <li>Secure session ID generation by default on all platforms (platforms other than Linux required
+          random number generation using a configured entropy)</li>
+      <li>OS level statistics on memory usage and CPU usage by the Tomcat process are displayed by
+          the status servlet</li>
+    </ul>
+  </p>
+
+  </section>
+
+  <section name="APR Connectors Configuration">
+
+    <subsection name="HTTP">
+    
+    <p>
+      When APR is enabled, the HTTP connector will use sendfile for hadling large static files (all such
+      files will be sent ansychronously using high performance kernel level calls), and will use 
+      a socket poller for keepalive, increasing scalability of the server.
+    </p>
+
+    <p>
+      The following attributes are supported in the HTTP APR connector in addition to the ones supported
+      in the regular HTTP connector:
+    </p>
+
+    <attributes>
+ 
+    <attribute name="firstReadTimeout" required="false">
+      <p>The first read of a request will be made using the specified timeout. If no data is available
+      after the specified time, the socket will be placed in the poller. Setting this value to 0 will
+      increase scalability, but will have a minor impact on latency (see the related pollTime attribute).
+      The default value is 100 (100ms). Note: on Windows, the actual value of firstReadTimeout will
+      be 500 + the specified value.</p>
+    </attribute>
+
+    <attribute name="pollTime" required="false">
+      <p>Duration of a poll call. Lowering this value will slightly decrease latency of connections 
+      being kept alive in some cases, but will use more CPU as more poll calls are being made. The
+      default value is 5000 (5ms).</p>
+    </attribute>
+
+    <attribute name="pollerSize" required="false">
+      <p>Amount of sockets that the poller responsible for polling kept alive connections can hold at a
+      given time. Extra connections will be closed right away. The default value is 768, corresponding to
+      768 keepalive connections.</p>
+    </attribute>
+
+    <attribute name="useSendfile" required="false">
+      <p>Use kernel level sendfile for certain static files. The default value is true.</p>
+    </attribute>
+
+    <attribute name="sendfileSize" required="false">
+      <p>Amount of sockets that the poller responsible for sending static files asynchronously can hold 
+      at a given time. Extra connections will be closed right away without any data being sent 
+      (resulting in a zero length file on the client side). Note that in most cases, sendfile is a call
+      that will return right away (being taken care of "synchonously" by the kernel), and the sendfile
+      poller will not be used, so the amount of static files which can be sent concurrently is much larger
+      than the specified amount. The default value is 256.</p>
+    </attribute>
+
+    </attributes>
+    
+    </subsection>
+	
+    <subsection name="HTTPS">
+    
+    <p>
+      When APR is enabled, the HTTPS connector will use a socket poller for keepalive, increasing 
+      scalability of the server. It also uses OpenSSL, which may be more optimized than JSSE depending
+      on the processor being used, and can be complemented with many commercial accelerator components.
+      Unlike the HTTP connector, the HTTPS connector cannot use sendfile to optimize static file
+      processing.
+    </p>
+
+    <p>
+      The HTTPS APR connector has the same basic attributes than the HTTP APR connector, but adds 
+      OpenSSL specific ones. For the full details on using OpenSSL, please refer to OpenSSL documentations
+      and the many books available for it (see the <a href="http://www.openssl.org">Official OpenSSL 
+      website</a>). The SSL specific attributes for the connector are:
+    </p>
+    
+    <attributes>
+
+    <attribute name="SSLEngine" required="false">
+    <p>
+      Name of the SSLEngine to use. off: Do not use SSL, on: Use SSL but no specific ENGINE.
+      The default value is off.
+    </p>
+    </attribute>
+    <attribute name="SSLProtocol" required="false">
+    <p>
+      Protocol which may be used for communicating with clients. The default is "all", with
+      other acceptable values being "SSLv2", "SSLv3", "TLSv1", and "SSLv2+SSLv3".
+    </p>
+    </attribute>
+    <attribute name="SSLCipherSuite" required="false">
+    <p>
+      Ciphers which may be used for communicating with clients. The default is "ALL", with
+      other acceptable values being a list of ciphers, with ":" used as the delimiter
+      (see OpenSSL documentation for the list of ciphers supported).
+    </p>
+    </attribute>
+    <attribute name="SSLCertificateFile" required="true">
+    <p>
+      Name of the file that contains the server certificate. The format is PEM-encoded.
+    </p>
+    </attribute>
+    <attribute name="SSLCertificateKeyFile" required="false">
+    <p>
+      Name of the file that contains the server private key. The format is PEM-encoded.
+      The default value is the value of "SSLCertificateFile" and in this case both certificate
+      and private key have to be in this file (NOT RECOMMENDED).
+    </p>
+    </attribute>
+    <attribute name="SSLPassword" required="false">
+    <p>
+      Pass phrase for the encrypted private key. If "SSLPassword" is not provided, the callback fonction
+      should prompt for the pass phrase.
+    </p>
+    </attribute>
+    <attribute name="SSLVerifyClient" required="false">
+    <p>
+      Ask client for certificate. The default is "none", meaning the client will not have the opportunity
+      to submit a certificate. Other acceptable values include "optional", "require" and "optionalNoCA".
+    </p>
+    </attribute>
+    <attribute name="SSLVerifyDepth" required="false">
+    <p>
+      Maximum verification depth for client certificates. The default is "10".
+    </p>
+    </attribute>
+
+    </attributes>
+    </subsection>
+	
+    <subsection name="AJP">
+    
+    <p>
+      When APR is enabled, the AJP connector will use a socket poller for keepalive, increasing 
+      scalability of the server. As AJP is designed around a pool of persistent (or almost
+      persistent) connections, this will reduce significantly the amount of processing threads 
+      needed by Tomcat. Unlike the HTTP connector, the AJP connector cannot use sendfile to optimize
+      static file processing.
+    </p>
+
+    <p>
+      The following attributes are supported in the AJP APR connector in addition to the ones supported
+      in the regular AJP connector:
+    </p>
+
+    <attributes>
+ 
+    <attribute name="firstReadTimeout" required="false">
+      <p>The first read of a request will be made using the specified timeout. If no data is available
+      after the specified time, the socket will be placed in the poller. Setting this value to 0 will
+      increase scalability, but will have a minor impact on latency (see the related pollTime attribute).
+      The default value is 100 (100ms). Note: on Windows, the actual value of firstReadTimeout will
+      be 500 + the specified value.</p>
+    </attribute>
+
+    <attribute name="pollTime" required="false">
+      <p>Duration of a poll call. Lowering this value will slightly decrease latency of connections 
+      being kept alive in some cases, but will use more CPU as more poll calls are being made. The
+      default value is 5000 (5ms).</p>
+    </attribute>
+
+    <attribute name="pollerSize" required="false">
+      <p>Amount of sockets that the poller responsible for polling kept alive connections can hold at a
+      given time. Extra connections will be closed right away. The default value is 768, corresponding to
+      768 keepalive connections.</p>
+    </attribute>
+
+    </attributes>
+    
+    </subsection>
+	
+  </section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/architecture/index.xml b/container/webapps/docs/architecture/index.xml
new file mode 100644
index 0000000..a320ac3
--- /dev/null
+++ b/container/webapps/docs/architecture/index.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="index.html">
+
+  &project;
+
+  <properties>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>Table of Contents</title>
+  </properties>
+
+<body>
+
+
+<section name="Preface">
+
+<p>This section of the Tomcat documentation attempts to explain
+the architecture and design of the Tomcat server.  It includes significant
+contributions from several tomcat developers:
+</p>
+<ul>
+<li>Yoav Shapira
+    (<a href="mailto:yoavs@apache.org">yoavs@apache.org</a>)</li>
+<li>Jeanfrancois Arcand
+    (<a href="mailto:jfarcand@apache.org">jfarcand@apache.org</a>)</li>
+<li>Filip Hanik
+    (<a href="mailto:fhanik@apache.org">fhanik@apache.org</a>)</li>
+</ul>
+
+</section>
+
+
+<section name="Table of Contents">
+
+<p>The information presented is divided into the following sections:</p>
+<ul>
+<li><a href="overview.html"><strong>Overview</strong></a> -
+    An overview of the Tomcat server architecture with key terms
+    and concepts.</li>
+<li><a href="startup.html"><strong>Server Startup</strong></a> -
+    A detailed description, with sequence diagrams, of how the Tomcat
+    server starts up.</li>
+<li><a href="requestProcess.html"><strong>Request Process Flow</strong></a> -
+    A detailed description of how Tomcat handles a request.</li>
+</ul>
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/architecture/overview.xml b/container/webapps/docs/architecture/overview.xml
new file mode 100644
index 0000000..99b14bc
--- /dev/null
+++ b/container/webapps/docs/architecture/overview.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="overview.html">
+
+  &project;
+
+  <properties>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>Architecture Overview</title>
+  </properties>
+
+<body>
+
+
+<section name="Overview">
+<p>
+This page provides an overview of the Tomcat server architecture.
+</p>
+</section>
+
+<section name="Terms">
+
+<subsection name="Server">
+<p>
+In the Tomcat world, a
+<a href="../config/server.html">Server</a> represents the whole container.
+Tomcat provides a default implementation of the 
+<a href="../catalina/docs/api/org/apache/catalina/Server.html">Server interface.</a>,
+and this is rarely customized by users.
+</p>
+</subsection>
+
+<subsection name="Service">
+<p>
+A <a href="../config/service.html">Service</a> is an intermediate component
+which lives inside a Server and ties one or more Connectors to exactly one
+Engine.  The Service element is rarely customized by users, as the default
+implementation is simple and sufficient:
+<a href="../catalina/docs/api/org/apache/catalina/Service.html">Service interface</a>.
+</p>
+</subsection>
+
+<subsection name="Engine">
+<p>
+An
+<a href="../config/engine.html">Engine</a> represents request processing
+pipeline for a specific Service.  As a Service may have multiple Connectors,
+the Engine received and processes all requests from these connectors, handing
+the response back to the appropriate connector for transmission to the client.
+The <a href="../catalina/docs/api/org/apache/catalina/Engine.html">Engine interface</a>
+may be implemented to supply custom Engines, though this is uncommon.
+</p>
+<p>
+Note that the Engine may be used for Tomcat server clustering via the
+jvmRoute parameter.  Read the Clustering documentation for more information.
+</p>
+</subsection>
+
+<subsection name="Host">
+<p>
+A <a href="../config/host.html">Host</a> is an association of a network name,
+e.g. www.yourcompany.com, to the Tomcat server.  An Engine may contain
+multiple hosts, and the Host element also supports network aliases such as
+yourcompany.com and abc.yourcompany.com.  Users rarely create custom
+<a href="../catalina/docs/api/org/apache/catalina/Host.html">Hosts</a>
+because the 
+<a href="../catalina/docs/api/org/apache/catalina/core/StandardHost.html">StandardHost
+implementation</a> provides significant additional functionality.
+</p>
+</subsection>
+
+<subsection name="Connector">
+<p>
+A Connector handles communications with the client.  There are multiple
+connectors available with Tomcat, all of which implement the 
+<a href="../catalina/docs/api/org/apache/catalina/Connector.html">Connector
+interface.</a>  These include the
+<a href="../config/coyote.html">Coyote connector</a> which is used for
+most HTTP traffic, especially when running Tomcat as a standalone server, 
+and the <a href="../config/jk2.html">JK2 connector</a> which implements
+the AJP procotol used when connecting Tomcat to an Apache HTTPD server.
+Creating a customized connector is a significant effort.
+</p>
+</subsection>
+
+<subsection name="Context">
+<p>
+A
+<a href="../config/context.html">Context</a>
+represents a web application.  A Host may contain multiple
+contexts, each with a unique path.  The
+<a href="../catalina/docs/api/org/apache/catalina/Context.html">Context
+interface</a> may be implemented to create custom Contexts, but
+this is rarely the case because the
+<a href="../catalina/docs/api/org/apache/catalina/core/StandardContext.html">
+StandardContext</a> provides significant additional functionality.
+</p>
+</subsection>
+</section>
+
+<section name="Comments">
+<p>
+Tomcat is designed to be a fast and efficient implementation of the
+Servlet Specification.  Tomcat came about as the reference implementation
+of this specification, and has remained rigorous in adhering to the
+specification.  At the same time, significant attention has been paid
+to Tomcat's performance and it is now on par with other servlet containers,
+including commercial ones.
+</p>
+<p>
+In recent releases of Tomcat, mostly starting with Tomcat 5,
+we have begun effots to make more aspects of Tomcat managable via
+JMX.  In addition, the Manager and Admin webapps have been greatly
+enhanced and improved.  Managability is a primary area of concern
+for us as the product matures and the specification becomes more
+stable.
+</p>
+</section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/architecture/project.xml b/container/webapps/docs/architecture/project.xml
new file mode 100644
index 0000000..b7d25b3
--- /dev/null
+++ b/container/webapps/docs/architecture/project.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Tomcat Architecture"
+        href="http://jakarta.apache.org/tomcat/">
+
+    <title>Tomcat Architecture</title>
+
+    <logo href="/images/tomcat.gif">
+      The Tomcat Servlet/JSP Container
+    </logo>
+
+    
+    <body>
+
+    <menu name="Links">
+        <item name="Docs Home"             href="../index.html" />
+    </menu>
+
+    <menu name="Contents">
+        <item name="Contents"              href="index.html" />
+        <item name="Overview"              href="overview.html" />
+        <item name="Server Startup"        href="startup.html" />
+        <item name="Request Process"       href="requestProcess.html" />
+    </menu>
+
+    </body>
+</project>
diff --git a/container/webapps/docs/architecture/requestProcess.xml b/container/webapps/docs/architecture/requestProcess.xml
new file mode 100644
index 0000000..ba359bd
--- /dev/null
+++ b/container/webapps/docs/architecture/requestProcess.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="requestProcess.html">
+
+  &project;
+
+  <properties>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>Request Process Flow</title>
+  </properties>
+
+<body>
+
+
+<section name="Request Process Flow">
+
+<p>
+This page describes the process used by Tomcat to handle
+an incoming request.  This process is largely defined by
+the Servlet Specification, which outlines the order
+of events that must take place.
+</p>
+
+<subsection name="description">
+<p>
+TODO
+</p>
+</subsection>
+
+<subsection name="diagram">
+<p>
+A UML sequence diagram of the request process is available
+<a href="requestProcess/requestProcess.pdf">here.</a>
+</p>
+</subsection>
+
+<subsection name="comments">
+<p>
+The Servlet Specification provides many opportunities for
+listening in (using Listeners) or modiying (using Filters)
+the request handling process even before the request arrives
+at the servlet that will handle it.
+</p>
+
+</subsection>
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/architecture/requestProcess/requestProcess.pdf b/container/webapps/docs/architecture/requestProcess/requestProcess.pdf
new file mode 100644
index 0000000..6a171de
--- /dev/null
+++ b/container/webapps/docs/architecture/requestProcess/requestProcess.pdf
Binary files differ
diff --git a/container/webapps/docs/architecture/requestProcess/roseModel.mdl b/container/webapps/docs/architecture/requestProcess/roseModel.mdl
new file mode 100644
index 0000000..159c2ee
--- /dev/null
+++ b/container/webapps/docs/architecture/requestProcess/roseModel.mdl
@@ -0,0 +1,12921 @@
+
+(object Petal
+    version    	45
+    _written   	"Rose 7.6.0109.2314"
+    charSet    	0)
+
+(object Design "Logical View"
+    is_unit    	TRUE
+    is_loaded  	TRUE
+    quid       	"3DFDF6CE0337"
+    defaults   	(object defaults
+	rightMargin 	0.250000
+	leftMargin 	0.250000
+	topMargin  	0.250000
+	bottomMargin 	0.500000
+	pageOverlap 	0.250000
+	clipIconLabels 	TRUE
+	autoResize 	TRUE
+	snapToGrid 	TRUE
+	gridX      	16
+	gridY      	16
+	defaultFont 	(object Font
+	    size       	10
+	    face       	"Arial"
+	    bold       	FALSE
+	    italics    	FALSE
+	    underline  	FALSE
+	    strike     	FALSE
+	    color      	0
+	    default_color 	TRUE)
+	showMessageNum 	1
+	showClassOfObject 	TRUE
+	notation   	"Unified")
+    root_usecase_package 	(object Class_Category "Use Case View"
+	quid       	"3DFDF6CE0369"
+	exportControl 	"Public"
+	global     	TRUE
+	logical_models 	(list unit_reference_list)
+	logical_presentations 	(list unit_reference_list
+	    (object UseCaseDiagram "Main"
+		quid       	"3DFDF6D201FE"
+		title      	"Main"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	0
+		items      	(list diagram_item_list))))
+    root_category 	(object Class_Category "Logical View"
+	quid       	"3DFDF6CE0338"
+	exportControl 	"Public"
+	global     	TRUE
+	subsystem  	"Component View"
+	quidu      	"3DFDF6CE036A"
+	logical_models 	(list unit_reference_list
+	    (object Class_Category "org.apache.catalina"
+		quid       	"3E42DE8D0082"
+		visible_categories 	(list visibility_relationship_list
+		    (object Visibility_Relationship
+			quid       	"3E42DEF601EB"
+			supplier   	"Logical View::org.apache.tomcat.util"
+			quidu      	"3E42DEDF01F2")
+		    (object Visibility_Relationship
+			quid       	"3E42DF700060"
+			supplier   	"Logical View::org.apache.coyote"
+			quidu      	"3E42DE9F0132")
+		    (object Visibility_Relationship
+			quid       	"3E43D165039C"
+			supplier   	"Logical View::org.apache.naming"
+			quidu      	"3E43D1580339"))
+		exportControl 	"Public"
+		logical_models 	(list unit_reference_list
+		    (object Class_Category "ant"
+			quid       	"3E42DFBB037F"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43CFF7020F"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "authenticator"
+			quid       	"3E42DFC702B4"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D03C0395"
+				supplier   	"Logical View::org.apache.catalina::deploy"
+				quidu      	"3E42DFDC0340")
+			    (object Visibility_Relationship
+				quid       	"3E43D03F01C2"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184")
+			    (object Visibility_Relationship
+				quid       	"3E43D043024A"
+				supplier   	"Logical View::org.apache.catalina::valves"
+				quidu      	"3E42E02D035B"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "connector"
+			quid       	"3E42DFCF036A"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D07E017D"
+				supplier   	"Logical View::org.apache.catalina::session"
+				quidu      	"3E42E00C026D"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "core"
+			quid       	"3E42DFD603BA"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D19E01A9"
+				supplier   	"Logical View::org.apache.catalina::deploy"
+				quidu      	"3E42DFDC0340")
+			    (object Visibility_Relationship
+				quid       	"3E43D1A10185"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184")
+			    (object Visibility_Relationship
+				quid       	"3E43D1CE007C"
+				supplier   	"Logical View::org.apache.catalina::connector"
+				quidu      	"3E42DFCF036A")
+			    (object Visibility_Relationship
+				quid       	"3E43D1D800D0"
+				supplier   	"Logical View::org.apache.catalina::security"
+				quidu      	"3E42E00100D7")
+			    (object Visibility_Relationship
+				quid       	"3E43D25C031F"
+				supplier   	"Logical View::org.apache.catalina::mbean"
+				quidu      	"3E42DFF10188")
+			    (object Visibility_Relationship
+				quid       	"3E43D260028E"
+				supplier   	"Logical View::org.apache.catalina::startup"
+				quidu      	"3E42E01E00EC")
+			    (object Visibility_Relationship
+				quid       	"3E43D26A015C"
+				supplier   	"Logical View::org.apache.catalina::session"
+				quidu      	"3E42E00C026D")
+			    (object Visibility_Relationship
+				quid       	"3E43D2830271"
+				supplier   	"Logical View::org.apache.catalina::valves"
+				quidu      	"3E42E02D035B")
+			    (object Visibility_Relationship
+				quid       	"3E43D2C80248"
+				supplier   	"Logical View::org.apache.catalina::net"
+				quidu      	"3E42DFF70227")
+			    (object Visibility_Relationship
+				quid       	"3E43D2D6002B"
+				supplier   	"Logical View::org.apache.catalina::loader"
+				quidu      	"3E43D2D002D6")
+			    (object Visibility_Relationship
+				quid       	"3E43D3D300F7"
+				supplier   	"Logical View::org.apache.naming"
+				quidu      	"3E43D1580339"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "deploy"
+			quid       	"3E42DFDC0340"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D32001B8"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "launcher"
+			quid       	"3E42DFE2033F"
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "logger"
+			quid       	"3E42DFEC0285"
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "mbean"
+			quid       	"3E42DFF10188"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D49101A5"
+				supplier   	"Logical View::org.apache.catalina::deploy"
+				quidu      	"3E42DFDC0340")
+			    (object Visibility_Relationship
+				quid       	"3E43D4C6027D"
+				supplier   	"Logical View::org.apache.catalina::core"
+				quidu      	"3E42DFD603BA")
+			    (object Visibility_Relationship
+				quid       	"3E43D4FB008F"
+				supplier   	"Logical View::org.apache.catalina::session"
+				quidu      	"3E42E00C026D")
+			    (object Visibility_Relationship
+				quid       	"3E43D50000BE"
+				supplier   	"Logical View::org.apache.catalina::valves"
+				quidu      	"3E42E02D035B")
+			    (object Visibility_Relationship
+				quid       	"3E43D5080278"
+				supplier   	"Logical View::org.apache.catalina::realm"
+				quidu      	"3E42DFFA00AE")
+			    (object Visibility_Relationship
+				quid       	"3E43D55A0258"
+				supplier   	"Logical View::org.apache.catalina::logger"
+				quidu      	"3E42DFEC0285")
+			    (object Visibility_Relationship
+				quid       	"3E43D56000D0"
+				supplier   	"Logical View::org.apache.catalina::authenticator"
+				quidu      	"3E42DFC702B4"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "net"
+			quid       	"3E42DFF70227"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D6390371"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "realm"
+			quid       	"3E42DFFA00AE"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D69F0133"
+				supplier   	"Logical View::org.apache.catalina::core"
+				quidu      	"3E42DFD603BA")
+			    (object Visibility_Relationship
+				quid       	"3E43D6A10353"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184")
+			    (object Visibility_Relationship
+				quid       	"3E43D70E00E2"
+				supplier   	"Logical View::org.apache.naming"
+				quidu      	"3E43D1580339")
+			    (object Visibility_Relationship
+				quid       	"3E43D72302D7"
+				supplier   	"Logical View::org.apache.catalina::deploy"
+				quidu      	"3E42DFDC0340"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "security"
+			quid       	"3E42E00100D7"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D74D007F"
+				supplier   	"Logical View::org.apache.catalina::startup"
+				quidu      	"3E42E01E00EC")
+			    (object Visibility_Relationship
+				quid       	"3E43D76B0371"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "servlets"
+			quid       	"3E42E00502DB"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D82702E5"
+				supplier   	"Logical View::org.apache.tomcat.util"
+				quidu      	"3E42DEDF01F2")
+			    (object Visibility_Relationship
+				quid       	"3E43D82A02CC"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184")
+			    (object Visibility_Relationship
+				quid       	"3E43D82D0244"
+				supplier   	"Logical View::org.apache.naming"
+				quidu      	"3E43D1580339"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "session"
+			quid       	"3E42E00C026D"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D8770344"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "ssi"
+			quid       	"3E42E01002C3"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D8F902B5"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "startup"
+			quid       	"3E42E01E00EC"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D9150251"
+				supplier   	"Logical View::org.apache.catalina::logger"
+				quidu      	"3E42DFEC0285")
+			    (object Visibility_Relationship
+				quid       	"3E43D919018F"
+				supplier   	"Logical View::org.apache.catalina::security"
+				quidu      	"3E42E00100D7")
+			    (object Visibility_Relationship
+				quid       	"3E43D946000D"
+				supplier   	"Logical View::org.apache.catalina::core"
+				quidu      	"3E42DFD603BA")
+			    (object Visibility_Relationship
+				quid       	"3E43D95E012A"
+				supplier   	"Logical View::org.apache.catalina::loader"
+				quidu      	"3E43D2D002D6")
+			    (object Visibility_Relationship
+				quid       	"3E43D9960315"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184")
+			    (object Visibility_Relationship
+				quid       	"3E43D99902BF"
+				supplier   	"Logical View::org.apache.catalina::valves"
+				quidu      	"3E42E02D035B")
+			    (object Visibility_Relationship
+				quid       	"3E43D99C0147"
+				supplier   	"Logical View::org.apache.catalina::deploy"
+				quidu      	"3E42DFDC0340")
+			    (object Visibility_Relationship
+				quid       	"3E43D9DA0114"
+				supplier   	"Logical View::org.apache.catalina::net"
+				quidu      	"3E42DFF70227")
+			    (object Visibility_Relationship
+				quid       	"3E43D9F402F2"
+				supplier   	"Logical View::org.apache.catalina::realm"
+				quidu      	"3E42DFFA00AE"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "user"
+			quid       	"3E42E0220174"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43DB240227"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184")
+			    (object Visibility_Relationship
+				quid       	"3E43DB31009F"
+				supplier   	"Logical View::org.apache.naming"
+				quidu      	"3E43D1580339"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "util"
+			quid       	"3E42E0260184"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43DB85017C"
+				supplier   	"Logical View::org.apache.catalina::core"
+				quidu      	"3E42DFD603BA")
+			    (object Visibility_Relationship
+				quid       	"3E43DB88016C"
+				supplier   	"Logical View::org.apache.naming"
+				quidu      	"3E43D1580339"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "valves"
+			quid       	"3E42E02D035B"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43DC2B0257"
+				supplier   	"Logical View::org.apache.catalina::util"
+				quidu      	"3E42E0260184")
+			    (object Visibility_Relationship
+				quid       	"3E43DD3E0271"
+				supplier   	"Logical View::org.apache.catalina::deploy"
+				quidu      	"3E42DFDC0340")
+			    (object Visibility_Relationship
+				quid       	"3E43DD4102CF"
+				supplier   	"Logical View::org.apache.catalina::connector"
+				quidu      	"3E42DFCF036A")
+			    (object Visibility_Relationship
+				quid       	"3E43DDDE00B8"
+				supplier   	"Logical View::org.apache.catalina::core"
+				quidu      	"3E42DFD603BA"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list))
+		    (object Class_Category "loader"
+			quid       	"3E43D2D002D6"
+			visible_categories 	(list visibility_relationship_list
+			    (object Visibility_Relationship
+				quid       	"3E43D3CF00F2"
+				supplier   	"Logical View::org.apache.naming"
+				quidu      	"3E43D1580339"))
+			exportControl 	"Public"
+			logical_models 	(list unit_reference_list)
+			logical_presentations 	(list unit_reference_list)))
+		logical_presentations 	(list unit_reference_list
+		    (object ClassDiagram "Main"
+			quid       	"3E42DFB6010B"
+			title      	"Main"
+			zoom       	100
+			max_height 	28350
+			max_width  	21600
+			origin_x   	0
+			origin_y   	0
+			items      	(list diagram_item_list
+			    (object CategoryView "Logical View::org.apache.catalina::ant" @1
+				location   	(2208, 1504)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@1
+				    location   	(2064, 1420)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"ant")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFBB037F"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::authenticator" @2
+				location   	(192, 2000)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@2
+				    location   	(48, 1916)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"authenticator")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFC702B4"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::connector" @3
+				location   	(464, 1328)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@3
+				    location   	(320, 1244)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"connector")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFCF036A"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::core" @4
+				location   	(2224, 800)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@4
+				    location   	(2080, 716)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"core")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFD603BA"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::deploy" @5
+				location   	(240, 160)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@5
+				    location   	(96, 76)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"deploy")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFDC0340"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::launcher" @6
+				location   	(1776, 2480)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@6
+				    location   	(1632, 2396)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"launcher")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFE2033F"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::logger" @7
+				location   	(752, 128)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@7
+				    location   	(608, 44)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"logger")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFEC0285"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::mbean" @8
+				location   	(2208, 1216)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@8
+				    location   	(2064, 1132)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"mbean")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFF10188"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::net" @9
+				location   	(1056, 2496)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@9
+				    location   	(912, 2412)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"net")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFF70227"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::realm" @10
+				location   	(1248, 112)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@10
+				    location   	(1104, 28)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"realm")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DFFA00AE"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::security" @11
+				location   	(304, 2496)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@11
+				    location   	(160, 2412)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"security")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42E00100D7"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::servlets" @12
+				location   	(2096, 1888)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@12
+				    location   	(1952, 1804)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"servlets")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42E00502DB"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::session" @13
+				location   	(432, 1696)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@13
+				    location   	(288, 1612)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"session")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42E00C026D"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::ssi" @14
+				location   	(672, 2480)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@14
+				    location   	(528, 2393)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"ssi")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42E01002C3"
+				width      	301
+				height     	187)
+			    (object CategoryView "Logical View::org.apache.catalina::startup" @15
+				location   	(1088, 832)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@15
+				    location   	(944, 748)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"startup")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42E01E00EC"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::user" @16
+				location   	(1424, 2496)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@16
+				    location   	(1280, 2412)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"user")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42E0220174"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::util" @17
+				location   	(1312, 1872)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@17
+				    location   	(1168, 1788)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"util")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42E0260184"
+				width      	300
+				height     	180)
+			    (object CategoryView "Logical View::org.apache.catalina::valves" @18
+				location   	(304, 704)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@18
+				    location   	(160, 620)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"valves")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42E02D035B"
+				width      	300
+				height     	180)
+			    (object ImportView "" @19
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43CFF7020F"
+				client     	@1
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @20
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D07E017D"
+				client     	@3
+				supplier   	@13
+				line_style 	0)
+			    (object CategoryView "Logical View::org.apache.catalina::loader" @21
+				location   	(2240, 416)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@21
+				    location   	(2096, 332)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	288
+				    justify    	0
+				    label      	"loader")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E43D2D002D6"
+				width      	300
+				height     	180)
+			    (object ImportView "" @22
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D32001B8"
+				client     	@5
+				supplier   	@17
+				line_style 	0)
+			    (object CategoryView "Logical View::org.apache.naming" @23
+				location   	(1872, 96)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@23
+				    location   	(1699, 12)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	346
+				    justify    	0
+				    label      	"org.apache.naming")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E43D1580339"
+				width      	358
+				height     	180)
+			    (object ImportView "" @24
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D3CF00F2"
+				client     	@21
+				supplier   	@23
+				line_style 	0)
+			    (object ImportView "" @25
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D6390371"
+				client     	@9
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @26
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D69F0133"
+				client     	@10
+				supplier   	@4
+				line_style 	0)
+			    (object ImportView "" @27
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D6A10353"
+				client     	@10
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @28
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D70E00E2"
+				client     	@10
+				supplier   	@23
+				line_style 	0)
+			    (object ImportView "" @29
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D72302D7"
+				client     	@10
+				supplier   	@5
+				line_style 	0)
+			    (object ImportView "" @30
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D69F0133"
+				client     	@10
+				supplier   	@4
+				line_style 	0)
+			    (object ImportView "" @31
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D74D007F"
+				client     	@11
+				supplier   	@15
+				line_style 	0)
+			    (object ImportView "" @32
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D76B0371"
+				client     	@11
+				supplier   	@17
+				line_style 	0)
+			    (object CategoryView "Logical View::org.apache.tomcat.util" @33
+				location   	(2096, 2224)
+				font       	(object Font
+				    size       	10
+				    face       	"Arial"
+				    bold       	FALSE
+				    italics    	FALSE
+				    underline  	FALSE
+				    strike     	FALSE
+				    color      	0
+				    default_color 	TRUE)
+				label      	(object ItemLabel
+				    Parent_View 	@33
+				    location   	(1923, 2140)
+				    fill_color 	13434879
+				    nlines     	2
+				    max_width  	346
+				    justify    	0
+				    label      	"org.apache.tomcat.util")
+				icon_style 	"Icon"
+				line_color 	3342489
+				fill_color 	13434879
+				quidu      	"3E42DEDF01F2"
+				width      	358
+				height     	180)
+			    (object ImportView "" @34
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D8770344"
+				client     	@13
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @35
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D8F902B5"
+				client     	@14
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @36
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43DB240227"
+				client     	@16
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @37
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43DB31009F"
+				client     	@16
+				supplier   	@23
+				line_style 	0)
+			    (object ImportView "" @38
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43DB85017C"
+				client     	@17
+				supplier   	@4
+				line_style 	0)
+			    (object ImportView "" @39
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43DB88016C"
+				client     	@17
+				supplier   	@23
+				line_style 	0)
+			    (object ImportView "" @40
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D82702E5"
+				client     	@12
+				supplier   	@33
+				line_style 	0)
+			    (object ImportView "" @41
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D82A02CC"
+				client     	@12
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @42
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D82D0244"
+				client     	@12
+				supplier   	@23
+				vertices   	(list Points
+				    (2060, 1743)
+				    (1746, 447)
+				    (1838, 186))
+				line_style 	0)
+			    (object ImportView "" @43
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D919018F"
+				client     	@15
+				supplier   	@11
+				line_style 	0)
+			    (object ImportView "" @44
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D946000D"
+				client     	@15
+				supplier   	@4
+				line_style 	0)
+			    (object ImportView "" @45
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D95E012A"
+				client     	@15
+				supplier   	@21
+				line_style 	0)
+			    (object ImportView "" @46
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D95E012A"
+				client     	@15
+				supplier   	@21
+				line_style 	0)
+			    (object ImportView "" @47
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D9960315"
+				client     	@15
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @48
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D99902BF"
+				client     	@15
+				supplier   	@18
+				line_style 	0)
+			    (object ImportView "" @49
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D99C0147"
+				client     	@15
+				supplier   	@5
+				line_style 	0)
+			    (object ImportView "" @50
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D946000D"
+				client     	@15
+				supplier   	@4
+				line_style 	0)
+			    (object ImportView "" @51
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D9150251"
+				client     	@15
+				supplier   	@7
+				line_style 	0)
+			    (object ImportView "" @52
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D9DA0114"
+				client     	@15
+				supplier   	@9
+				line_style 	0)
+			    (object ImportView "" @53
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D9F402F2"
+				client     	@15
+				supplier   	@10
+				line_style 	0)
+			    (object ImportView "" @54
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D9960315"
+				client     	@15
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @55
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D946000D"
+				client     	@15
+				supplier   	@4
+				line_style 	0)
+			    (object ImportView "" @56
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D99C0147"
+				client     	@15
+				supplier   	@5
+				line_style 	0)
+			    (object ImportView "" @57
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D49101A5"
+				client     	@8
+				supplier   	@5
+				line_style 	0)
+			    (object ImportView "" @58
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D4C6027D"
+				client     	@8
+				supplier   	@4
+				line_style 	0)
+			    (object ImportView "" @59
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D4FB008F"
+				client     	@8
+				supplier   	@13
+				line_style 	0)
+			    (object ImportView "" @60
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D50000BE"
+				client     	@8
+				supplier   	@18
+				vertices   	(list Points
+				    (2057, 1216)
+				    (1278, 1216)
+				    (454, 783))
+				line_style 	0)
+			    (object ImportView "" @61
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D5080278"
+				client     	@8
+				supplier   	@10
+				line_style 	0)
+			    (object ImportView "" @62
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D55A0258"
+				client     	@8
+				supplier   	@7
+				line_style 	0)
+			    (object ImportView "" @63
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D56000D0"
+				client     	@8
+				supplier   	@2
+				line_style 	0)
+			    (object ImportView "" @64
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D19E01A9"
+				client     	@4
+				supplier   	@5
+				line_style 	0)
+			    (object ImportView "" @65
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D1A10185"
+				client     	@4
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @66
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D1CE007C"
+				client     	@4
+				supplier   	@3
+				line_style 	0)
+			    (object ImportView "" @67
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D1D800D0"
+				client     	@4
+				supplier   	@11
+				vertices   	(list Points
+				    (2081, 890)
+				    (959, 1616)
+				    (409, 2351))
+				line_style 	0)
+			    (object ImportView "" @68
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D25C031F"
+				client     	@4
+				supplier   	@8
+				line_style 	0)
+			    (object ImportView "" @69
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D260028E"
+				client     	@4
+				supplier   	@15
+				line_style 	0)
+			    (object ImportView "" @70
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D26A015C"
+				client     	@4
+				supplier   	@13
+				line_style 	0)
+			    (object ImportView "" @71
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D2830271"
+				client     	@4
+				supplier   	@18
+				line_style 	0)
+			    (object ImportView "" @72
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D2C80248"
+				client     	@4
+				supplier   	@9
+				line_style 	0)
+			    (object ImportView "" @73
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D2D6002B"
+				client     	@4
+				supplier   	@21
+				line_style 	0)
+			    (object ImportView "" @74
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D3D300F7"
+				client     	@4
+				supplier   	@23
+				line_style 	0)
+			    (object ImportView "" @75
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D03C0395"
+				client     	@2
+				supplier   	@5
+				vertices   	(list Points
+				    (171, 1855)
+				    (16, 766)
+				    (205, 250))
+				line_style 	0)
+			    (object ImportView "" @76
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D03F01C2"
+				client     	@2
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @77
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43D043024A"
+				client     	@2
+				supplier   	@18
+				line_style 	0)
+			    (object ImportView "" @78
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43DC2B0257"
+				client     	@18
+				supplier   	@17
+				line_style 	0)
+			    (object ImportView "" @79
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43DD3E0271"
+				client     	@18
+				supplier   	@5
+				line_style 	0)
+			    (object ImportView "" @80
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43DD4102CF"
+				client     	@18
+				supplier   	@3
+				line_style 	0)
+			    (object ImportView "" @81
+				stereotype 	TRUE
+				line_color 	3342489
+				quidu      	"3E43DDDE00B8"
+				client     	@18
+				supplier   	@4
+				vertices   	(list Points
+				    (454, 654)
+				    (1293, 381)
+				    (2073, 731))
+				line_style 	0)))))
+	    (object Class_Category "org.apache.coyote"
+		quid       	"3E42DE9F0132"
+		visible_categories 	(list visibility_relationship_list
+		    (object Visibility_Relationship
+			quid       	"3E42DEFC00B3"
+			supplier   	"Logical View::org.apache.tomcat.util"
+			quidu      	"3E42DEDF01F2"))
+		exportControl 	"Public"
+		logical_models 	(list unit_reference_list)
+		logical_presentations 	(list unit_reference_list))
+	    (object Class_Category "org.apache.tomcat.util"
+		quid       	"3E42DEDF01F2"
+		exportControl 	"Public"
+		logical_models 	(list unit_reference_list)
+		logical_presentations 	(list unit_reference_list))
+	    (object Class_Category "org.apache.jasper"
+		quid       	"3E42DEFF0270"
+		exportControl 	"Public"
+		logical_models 	(list unit_reference_list)
+		logical_presentations 	(list unit_reference_list))
+	    (object Class_Category "org.apache.naming"
+		quid       	"3E43D1580339"
+		exportControl 	"Public"
+		logical_models 	(list unit_reference_list)
+		logical_presentations 	(list unit_reference_list))
+	    (object Mechanism @82
+		logical_models 	(list unit_reference_list
+		    (object Object "Bootstrap"
+			quid       	"3DFDF8FD0345"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFDF9210008"
+				supplier   	"Bootstrap"
+				quidu      	"3DFDF8FD0345"
+				messages   	(list Messages
+				    (object Message "initClassLoaders()"
+					quid       	"3DFDF9210009"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1"
+					ordinal    	0
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFDF91A010C"
+				supplier   	"Catalina"
+				quidu      	"3DFDF90A0330"
+				messages   	(list Messages
+				    (object Message "newInstance()"
+					quid       	"3DFDF91A010D"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2"
+					ordinal    	1
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "setParentClassLoader()"
+					quid       	"3DFDF97900C2"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3"
+					ordinal    	2
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "load()"
+					quid       	"3DFDFA3402F2"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"4"
+					ordinal    	3
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "Digester"
+			quid       	"3DFDFAF201A1"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFDFB8400A6"
+				supplier   	"ServerLifecycleListener"
+				quidu      	"3DFDFB4B0217"
+				messages   	(list Messages
+				    (object Message "newInstance()"
+					quid       	"3DFDFB8400A7"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"9.1"
+					ordinal    	9
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFDFB920147"
+				supplier   	"GlobalResourcesLifecycleListener"
+				quidu      	"3DFDFB7A02AB"
+				messages   	(list Messages
+				    (object Message "newInstance()"
+					quid       	"3DFDFB920148"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"9.2"
+					ordinal    	10
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "ServerLifecycleListener"
+			quid       	"3DFDFB4B0217"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "GlobalResourcesLifecycleListener"
+			quid       	"3DFDFB7A02AB"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "SecurityConfig"
+			quid       	"3DFDFBD802BA"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "Catalina"
+			quid       	"3DFDF90A0330"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFDFA8001D9"
+				supplier   	"Catalina"
+				quidu      	"3DFDF90A0330"
+				messages   	(list Messages
+				    (object Message "initDirs()"
+					quid       	"3DFDFA8001DA"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"5"
+					ordinal    	4
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "initNaming()"
+					quid       	"3DFDFA8B0347"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"6"
+					ordinal    	5
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "initialize()"
+					quid       	"3DFDFAAD01AC"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"7"
+					ordinal    	6
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFDFAF800C3"
+				supplier   	"Digester"
+				quidu      	"3DFDFAF201A1"
+				messages   	(list Messages
+				    (object Message "createDigester()"
+					quid       	"3DFDFAF800C4"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8"
+					ordinal    	7
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "parse()"
+					quid       	"3DFDFB0100B2"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"9"
+					ordinal    	8
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFDFBEA00C1"
+				supplier   	"SecurityConfig"
+				quidu      	"3DFDFBD802BA"
+				messages   	(list Messages
+				    (object Message "newInstance()"
+					quid       	"3DFDFBEA00C2"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"10"
+					ordinal    	11
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "setPackageDefinition()"
+					quid       	"3DFDFBF401F2"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"11"
+					ordinal    	12
+					Operation  	"setPackageDefinition"
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "setPackageAccess()"
+					quid       	"3DFDFC1203C2"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"12"
+					ordinal    	13
+					Operation  	"setPackageAccess"
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)))
+	    (object Mechanism @83
+		logical_models 	(list unit_reference_list
+		    (object Object "Catalina"
+			quid       	"3DFDFC8F015F"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFDFD1F0075"
+				supplier   	"StandardServer"
+				quidu      	"3DFDFCCB006B"
+				messages   	(list Messages
+				    (object Message "initialize()"
+					quid       	"3DFDFD1F0076"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1"
+					ordinal    	0
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardServer"
+			quid       	"3DFDFCCB006B"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFDFD3D01C3"
+				supplier   	"StandardService"
+				quidu      	"3DFDFD370020"
+				messages   	(list Messages
+				    (object Message "initialize()"
+					quid       	"3DFDFD3D01C4"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2"
+					ordinal    	1
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardService"
+			quid       	"3DFDFD370020"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFDFE990304"
+				supplier   	"CoyoteConnector"
+				quidu      	"3DFDFE810313"
+				messages   	(list Messages
+				    (object Message "initialize()"
+					quid       	"3DFDFE990305"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.1"
+					ordinal    	2
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "CoyoteConnector"
+			quid       	"3DFDFE810313"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE013D0216"
+				supplier   	"CoyoteAdapter"
+				quidu      	"3DFDFFA00226"
+				messages   	(list Messages
+				    (object Message "new()"
+					quid       	"3DFE013D0217"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.1.1"
+					ordinal    	3
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE0183032F"
+				supplier   	"Http11Protocol"
+				quidu      	"3DFE016601A6"
+				messages   	(list Messages
+				    (object Message "new()"
+					quid       	"3DFE01830330"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.1.2"
+					ordinal    	4
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "init()"
+					quid       	"3DFE0188032C"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.1.3"
+					ordinal    	5
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE01BC038B"
+				supplier   	"JkCoyoteAdapter"
+				quidu      	"3DFE01AD01A8"
+				messages   	(list Messages
+				    (object Message "new()"
+					quid       	"3DFE01BC038C"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.1.4"
+					ordinal    	6
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "init()"
+					quid       	"3DFE01C30164"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.1.5"
+					ordinal    	7
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "CoyoteAdapter"
+			quid       	"3DFDFFA00226"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "Http11Protocol"
+			quid       	"3DFE016601A6"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "JkCoyoteAdapter"
+			quid       	"3DFE01AD01A8"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)))
+	    (object Mechanism @84
+		logical_models 	(list unit_reference_list
+		    (object Object "Bootstrap"
+			quid       	"3DFE027700F5"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE02830373"
+				supplier   	"Catalina"
+				quidu      	"3DFE027D0067"
+				messages   	(list Messages
+				    (object Message "start()"
+					quid       	"3DFE02830374"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1"
+					ordinal    	0
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "Catalina"
+			quid       	"3DFE027D0067"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE02BA0187"
+				supplier   	"StandardServer"
+				quidu      	"3DFE02B30015"
+				messages   	(list Messages
+				    (object Message "start()"
+					quid       	"3DFE02BA0188"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1"
+					ordinal    	1
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardServer"
+			quid       	"3DFE02B30015"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE02D3006B"
+				supplier   	"StandardServer"
+				quidu      	"3DFE02B30015"
+				messages   	(list Messages
+				    (object Message "fireLifecycleEvent(BEFORE_START_EVENT)"
+					quid       	"3DFE02D3006C"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.1"
+					ordinal    	2
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "fireLifecycleEvent(START_EVENT)"
+					quid       	"3DFE02DF02DF"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.2"
+					ordinal    	3
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE030C02B2"
+				supplier   	"StandardService"
+				quidu      	"3DFE030400E3"
+				messages   	(list Messages
+				    (object Message "start()"
+					quid       	"3DFE030C02B3"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.3"
+					ordinal    	4
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardService"
+			quid       	"3DFE030400E3"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE031D0021"
+				supplier   	"StandardService"
+				quidu      	"3DFE030400E3"
+				messages   	(list Messages
+				    (object Message "fireLifecycleEvent(BEFORE_START_EVENT)"
+					quid       	"3DFE031D0022"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.3.1"
+					ordinal    	5
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "fireLifecycleEvent(START_EVENT)"
+					quid       	"3DFE0330019B"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.3.2"
+					ordinal    	6
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE03700189"
+				supplier   	"StandardEngine"
+				quidu      	"3DFE034700C2"
+				messages   	(list Messages
+				    (object Message "start()"
+					quid       	"3DFE0370018A"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.3.3"
+					ordinal    	7
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardEngine"
+			quid       	"3DFE034700C2"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE03750050"
+				supplier   	"StandardEngine"
+				quidu      	"3DFE034700C2"
+				messages   	(list Messages
+				    (object Message "fireLifecycleEvent(BEFORE_START_EVENT)"
+					quid       	"3DFE03750051"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2"
+					ordinal    	8
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "addDefaultMapper()"
+					quid       	"3DFE0389001C"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3"
+					ordinal    	9
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "logger.start()"
+					quid       	"3DFE03980281"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"4"
+					ordinal    	10
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "realm.start()"
+					quid       	"3DFE03A80107"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"5"
+					ordinal    	11
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "findMappers()"
+					quid       	"3DFE03BD000D"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"6"
+					ordinal    	12
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "findChildren()"
+					quid       	"3DFE03E000A4"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"7"
+					ordinal    	13
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE03FB0279"
+				supplier   	"StandardHost"
+				quidu      	"3DFE03F2035D"
+				messages   	(list Messages
+				    (object Message "start()"
+					quid       	"3DFE03FB027A"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8"
+					ordinal    	14
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardHost"
+			quid       	"3DFE03F2035D"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE043B02AD"
+				supplier   	"StandardHost"
+				quidu      	"3DFE03F2035D"
+				messages   	(list Messages
+				    (object Message "fireLifecycleEvent(BEFORE_START_EVENT)"
+					quid       	"3DFE043B02AE"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8.1"
+					ordinal    	15
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "addDefaultMapper()"
+					quid       	"3DFE045C021F"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8.2"
+					ordinal    	16
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "logger.start()"
+					quid       	"3DFE049B000C"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8.3"
+					ordinal    	17
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "findMapper()"
+					quid       	"3DFE04A303BB"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8.4"
+					ordinal    	18
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "findChildren()"
+					quid       	"3DFE04A90342"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8.5"
+					ordinal    	19
+					Operation  	"findChildren"
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE048E00B8"
+				supplier   	"StandardPipeline"
+				quidu      	"3DFE047D006D"
+				messages   	(list Messages
+				    (object Message "start()"
+					quid       	"3DFE048E00B9"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8.6"
+					ordinal    	20
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardPipeline"
+			quid       	"3DFE047D006D"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE05780137"
+				supplier   	"StandardPipeline"
+				quidu      	"3DFE047D006D"
+				messages   	(list Messages
+				    (object Message "fireLifecycleEvent(BEFORE_START_EVENT)"
+					quid       	"3DFE05780138"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8.6.1"
+					ordinal    	21
+					Operation  	"fireLifecycleEvent(AFTER_START_EVENT)"
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "fireLifecycleEvent(START_EVENT)"
+					quid       	"3DFE05A80398"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8.6.2"
+					ordinal    	22
+					Operation  	"fireLifecycleEvent(BEFORE_START_EVENT)"
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "fireLifecycleEvent(AFTER_EVENT)"
+					quid       	"3DFE05BA0196"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"8.6.3"
+					ordinal    	23
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)))
+	    (object Mechanism @85
+		logical_models 	(list unit_reference_list
+		    (object Object "StandardHost"
+			quid       	"3DFE0538017B"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE066C0340"
+				supplier   	"StandardHost"
+				quidu      	"3DFE0538017B"
+				messages   	(list Messages
+				    (object Message "fireLifecycleEvent(START_EVENT)"
+					quid       	"3DFE066C0341"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1"
+					ordinal    	0
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE06D20293"
+				supplier   	"HostConfig"
+				quidu      	"3DFE06A60131"
+				messages   	(list Messages
+				    (object Message "interested[i].lifecycleEvent()"
+					quid       	"3DFE06D20294"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2"
+					ordinal    	1
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "install()"
+					quid       	"3DFE078B03BB"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"ToClientFromSupplier"
+					sequence   	"2.6"
+					ordinal    	7
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "install()"
+					quid       	"3DFE132D0309"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"ToClientFromSupplier"
+					sequence   	"5"
+					ordinal    	13
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE07B100BD"
+				supplier   	"StandardHostDeployer"
+				quidu      	"3DFE079A0055"
+				messages   	(list Messages
+				    (object Message "install()"
+					quid       	"3DFE07B100BE"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3"
+					ordinal    	8
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "install() // same as above"
+					quid       	"3DFE133A036C"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"6"
+					ordinal    	17
+					Operation  	"install()"
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "HostConfig"
+			quid       	"3DFE06A60131"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE06E9028C"
+				supplier   	"HostConfig"
+				quidu      	"3DFE06A60131"
+				messages   	(list Messages
+				    (object Message "setDeployXML()"
+					quid       	"3DFE06E9028D"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.1"
+					ordinal    	2
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "setLiveDeploy()"
+					quid       	"3DFE06F300FF"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.2"
+					ordinal    	3
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "setUnpacksWar()"
+					quid       	"3DFE06FB00D9"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.3"
+					ordinal    	4
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "setXMLValidation()"
+					quid       	"3DFE070C0015"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.4"
+					ordinal    	5
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "deployDescriptors()"
+					quid       	"3DFE073B0031"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2.5"
+					ordinal    	6
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "deployApps()"
+					quid       	"3DFE131F0327"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"4"
+					ordinal    	12
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardHostDeployer"
+			quid       	"3DFE079A0055"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE07D200EC"
+				supplier   	"Digester"
+				quidu      	"3DFE07C9034C"
+				messages   	(list Messages
+				    (object Message "create()"
+					quid       	"3DFE07D200ED"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1"
+					ordinal    	9
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "parse()"
+					quid       	"3DFE07D603D7"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.6"
+					ordinal    	16
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "add(ContextRuleSet)"
+					quid       	"3DFE08FA003D"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.3"
+					ordinal    	11
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE087D01E2"
+				supplier   	"StandardHostDeployer"
+				quidu      	"3DFE079A0055")
+			    (object Link
+				quid       	"3DFE08DA029A"
+				supplier   	"ContextRuleSet"
+				quidu      	"3DFE0834016F"
+				messages   	(list Messages
+				    (object Message "new()"
+					quid       	"3DFE08DA029B"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.2"
+					ordinal    	10
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "add(NamingRuleSet())"
+					quid       	"3DFE0907015F"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.5"
+					ordinal    	15
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE08E00090"
+				supplier   	"NamingRuleSet"
+				quidu      	"3DFE08D00173"
+				messages   	(list Messages
+				    (object Message "new()"
+					quid       	"3DFE08E00091"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.4"
+					ordinal    	14
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "Digester"
+			quid       	"3DFE07C9034C"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "ContextRuleSet"
+			quid       	"3DFE0834016F"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "NamingRuleSet"
+			quid       	"3DFE08D00173"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)))
+	    (object Mechanism @86
+		logical_models 	(list unit_reference_list
+		    (object Object "Digester"
+			quid       	"3DFE095A0371"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE0E7801DA"
+				supplier   	"Digester"
+				quidu      	"3DFE095A0371"
+				messages   	(list Messages
+				    (object Message "parse"
+					quid       	"3DFE0E7801DB"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1"
+					ordinal    	0
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "startElement()"
+					quid       	"3DFE0F2F03D2"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2"
+					ordinal    	1
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE0F400213"
+				supplier   	"Rule"
+				quidu      	"3DFE0E7400D0"
+				messages   	(list Messages
+				    (object Message "begin()"
+					quid       	"3DFE0F400214"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3"
+					ordinal    	2
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "Rule"
+			quid       	"3DFE0E7400D0"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE0FD30265"
+				supplier   	"StandardContext"
+				quidu      	"3DFE0FC502A1"
+				messages   	(list Messages
+				    (object Message "newInstance()"
+					quid       	"3DFE0FD30266"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1"
+					ordinal    	3
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE102002E8"
+				supplier   	"SetPropertiesRule"
+				quidu      	"3DFE100303A4"
+				messages   	(list Messages
+				    (object Message "begin()"
+					quid       	"3DFE102002E9"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.2"
+					ordinal    	6
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE127F0024"
+				supplier   	"Rule"
+				quidu      	"3DFE0E7400D0")
+			    (object Link
+				quid       	"3DFE128501C7"
+				supplier   	"SetNextRule"
+				quidu      	"3DFE12690267"
+				messages   	(list Messages
+				    (object Message "end()"
+					quid       	"3DFE128501C8"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.3"
+					ordinal    	8
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardContext"
+			quid       	"3DFE0FC502A1"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE114A0192"
+				supplier   	"StandardPipeline"
+				quidu      	"3DFE112F003F"
+				messages   	(list Messages
+				    (object Message "setBasic(StandardContextValve)"
+					quid       	"3DFE114A0193"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.2"
+					ordinal    	5
+					Operation  	"setBasic"
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE115E001E"
+				supplier   	"StandardContextValve"
+				quidu      	"3DFE110D0375"
+				messages   	(list Messages
+				    (object Message "new()"
+					quid       	"3DFE115E001F"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1"
+					ordinal    	4
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "SetPropertiesRule"
+			quid       	"3DFE100303A4"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE11D50390"
+				supplier   	"StandardContext"
+				quidu      	"3DFE0FC502A1"
+				messages   	(list Messages
+				    (object Message "//Using BeanUtil, set the object properties (from ex: admin.xml)"
+					quid       	"3DFE11D50391"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.2.1"
+					ordinal    	7
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardContextValve"
+			quid       	"3DFE110D0375"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardPipeline"
+			quid       	"3DFE112F003F"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "SetNextRule"
+			quid       	"3DFE12690267"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)))
+	    (object Mechanism @87
+		logical_models 	(list unit_reference_list
+		    (object Object "StandardContext"
+			quid       	"3DFE196D00D9"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE200603BD"
+				supplier   	"WebappLoader"
+				quidu      	"3DFE1FFA0347"
+				messages   	(list Messages
+				    (object Message "new"
+					quid       	"3DFE200603BE"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.1"
+					ordinal    	5
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE200C0299"
+				supplier   	"StandardContext"
+				quidu      	"3DFE196D00D9"
+				messages   	(list Messages
+				    (object Message "setLoader"
+					quid       	"3DFE200C029A"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.2"
+					ordinal    	6
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "setManager"
+					quid       	"3DFE2032001C"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.4"
+					ordinal    	8
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "fireLifecycleEvent(START_EVENT)"
+					quid       	"3DFE205B01A2"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.5"
+					ordinal    	9
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE202C024F"
+				supplier   	"StandardManager"
+				quidu      	"3DFE201F0105"
+				messages   	(list Messages
+				    (object Message "new"
+					quid       	"3DFE202C0250"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.3"
+					ordinal    	7
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "start()"
+					quid       	"3DFE20B600E5"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.7"
+					ordinal    	12
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE20960002"
+				supplier   	"ContextConfig"
+				quidu      	"3DFE2087028C"
+				messages   	(list Messages
+				    (object Message " // Notify interested LifecycleListeners"
+					quid       	"3DFE20960003"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6"
+					ordinal    	10
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardHostDeployer"
+			quid       	"3DFE1D8A02DC"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE1FAF0014"
+				supplier   	"StandardHost"
+				quidu      	"3DFE1DF20141"
+				messages   	(list Messages
+				    (object Message "addChild"
+					quid       	"3DFE1FB60277"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1"
+					ordinal    	3
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardHost"
+			quid       	"3DFE1DF20141"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE1FC40227"
+				supplier   	"StandardContext"
+				quidu      	"3DFE196D00D9"
+				messages   	(list Messages
+				    (object Message "start()"
+					quid       	"3DFE1FC40228"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1"
+					ordinal    	4
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "WebappLoader"
+			quid       	"3DFE1FFA0347"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardManager"
+			quid       	"3DFE201F0105"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "ContextConfig"
+			quid       	"3DFE2087028C"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE20CF018B"
+				supplier   	"ContextConfig"
+				quidu      	"3DFE2087028C"
+				messages   	(list Messages
+				    (object Message "start()"
+					quid       	"3DFE20CF018C"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6.1"
+					ordinal    	11
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "defaultConfig()"
+					quid       	"3DFE20E303E2"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6.2"
+					ordinal    	13
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "applicationConfig()"
+					quid       	"3DFE211D01A1"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6.3"
+					ordinal    	14
+					Operation  	"applicationConfig"
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE21B60287"
+				supplier   	"Digester"
+				quidu      	"3DFE13960364"
+				messages   	(list Messages
+				    (object Message "create()"
+					quid       	"3DFE21B60288"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6.3.1"
+					ordinal    	15
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "createWarpper() // Invoked by a WebWrapperRule (not Directly by the Digester)"
+					quid       	"3DFE228B03BA"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"ToClientFromSupplier"
+					sequence   	"3.1.1.6.3.1.2"
+					ordinal    	17
+					Operation  	"createWarpper() // Invoked by a Rule (not Directly by the Digester)"
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE22560061"
+				supplier   	"StandardWrapper"
+				quidu      	"3DFE220C0122"
+				messages   	(list Messages
+				    (object Message "new"
+					quid       	"3DFE229A0004"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6.3.1.2.1"
+					ordinal    	18
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "addInstanceListener()"
+					quid       	"3DFE22A700C1"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6.3.2"
+					ordinal    	19
+					Operation  	"addInstanceListener"
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "addLifecycleListener()"
+					quid       	"3DFE22C701CC"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6.3.3"
+					ordinal    	20
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "addContainerListener()"
+					quid       	"3DFE22E80364"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6.3.4"
+					ordinal    	21
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	TRUE
+			multi      	FALSE)
+		    (object Object "Digester"
+			quid       	"3DFE13960364"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE19AE0064"
+				supplier   	"Digester"
+				quidu      	"3DFE13960364"
+				messages   	(list Messages
+				    (object Message "parse"
+					quid       	"3DFE19AE0065"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1"
+					ordinal    	0
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "startElement()"
+					quid       	"3DFE19B102E9"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2"
+					ordinal    	1
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "// Process web.xml * tld.xml"
+					quid       	"3DFE21BE021B"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1.1.6.3.1.1"
+					ordinal    	16
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE1DFB0021"
+				supplier   	"StandardHostDeployer"
+				quidu      	"3DFE1D8A02DC"
+				messages   	(list Messages
+				    (object Message "addChild"
+					quid       	"3DFE1DFB0022"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3"
+					ordinal    	2
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE22190225"
+				supplier   	"StandardWrapper"
+				quidu      	"3DFE220C0122"))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardWrapper"
+			quid       	"3DFE220C0122"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)))
+	    (object Mechanism @88
+		logical_models 	(list unit_reference_list
+		    (object Object "ThreadPool"
+			quid       	"3DFE402B02C5"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE40E701AD"
+				supplier   	"TcpWorkerThread"
+				quidu      	"3DFE403200F8"
+				messages   	(list Messages
+				    (object Message "runIt()"
+					quid       	"3DFE40E701AE"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1"
+					ordinal    	0
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "TcpWorkerThread"
+			quid       	"3DFE403200F8"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE40FC010D"
+				supplier   	"Http11Protocol"
+				quidu      	"3DFE40750177"
+				messages   	(list Messages
+				    (object Message "processConnection"
+					quid       	"3DFE40FC010E"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1"
+					ordinal    	1
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "Http11Protocol"
+			quid       	"3DFE40750177"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE4111029E"
+				supplier   	"Http11Protocol"
+				quidu      	"3DFE40750177"
+				messages   	(list Messages
+				    (object Message "process()"
+					quid       	"3DFE4111029F"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.1"
+					ordinal    	2
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "parseHeaders()"
+					quid       	"3DFE415C0151"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2"
+					ordinal    	3
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "prepareRequest()"
+					quid       	"3DFE41A60161"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3"
+					ordinal    	4
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE41D60106"
+				supplier   	"CoyoteAdapter"
+				quidu      	"3DFE410600DF"
+				messages   	(list Messages
+				    (object Message "service()"
+					quid       	"3DFE41D60107"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"4"
+					ordinal    	5
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "CoyoteAdapter"
+			quid       	"3DFE410600DF"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE422C01F0"
+				supplier   	"CoyoteAdapter"
+				quidu      	"3DFE410600DF"
+				messages   	(list Messages
+				    (object Message "postParseRequest()"
+					quid       	"3DFE422C01F1"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"5"
+					ordinal    	6
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE42800237"
+				supplier   	"StandardEngine"
+				quidu      	"3DFE424B0349"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE42800238"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"6"
+					ordinal    	7
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardEngine"
+			quid       	"3DFE424B0349"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE429A002C"
+				supplier   	"StandardPipeline"
+				quidu      	"3DFE42900045"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE429A002D"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"6.1"
+					ordinal    	8
+					Operation  	"invoke"
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardPipeline"
+			quid       	"3DFE42900045"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE42CE022F"
+				supplier   	"StandardValveContext"
+				quidu      	"3DFE42C002B1"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE42CE0230"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"6.1.1"
+					ordinal    	9
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardValveContext"
+			quid       	"3DFE42C002B1"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)))
+	    (object Mechanism @89
+		logical_models 	(list unit_reference_list
+		    (object Object "StandardContextValve"
+			quid       	"3DFE4307001E"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE434C019A"
+				supplier   	"StandardEngineValve"
+				quidu      	"3DFE432801F3"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE434C019B"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1"
+					ordinal    	0
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE43C203A3"
+				supplier   	"ErrorReportValve"
+				quidu      	"3DFE438C028D"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE43C203A4"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3"
+					ordinal    	4
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "invokeNext()"
+					quid       	"3DFE46330293"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"ToClientFromSupplier"
+					sequence   	"3.2"
+					ordinal    	6
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE46E70025"
+				supplier   	"ErrorDispatcherValve"
+				quidu      	"3DFE451F01EC"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE46E70026"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"4"
+					ordinal    	7
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "invokeNext"
+					quid       	"3DFE475D03A0"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"ToClientFromSupplier"
+					sequence   	"4.1"
+					ordinal    	8
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE476503C9"
+				supplier   	"StandardHostValve"
+				quidu      	"3DFE47310130"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE476503CA"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"5"
+					ordinal    	9
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardEngineValve"
+			quid       	"3DFE432801F3"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE436C009C"
+				supplier   	"StandardHost"
+				quidu      	"3DFE436503BD"
+				messages   	(list Messages
+				    (object Message "map()"
+					quid       	"3DFE436C009D"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1"
+					ordinal    	1
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "invoke()"
+					quid       	"3DFE43830063"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.2"
+					ordinal    	2
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE437F0143"
+				supplier   	"StandardEngineValve"
+				quidu      	"3DFE432801F3"))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardHost"
+			quid       	"3DFE436503BD"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE43B903BE"
+				supplier   	"StandardContextValve"
+				quidu      	"3DFE4307001E"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE43B903BF"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"2"
+					ordinal    	3
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "ErrorReportValve"
+			quid       	"3DFE438C028D"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE442501B0"
+				supplier   	"ErrorReportValve"
+				quidu      	"3DFE438C028D"
+				messages   	(list Messages
+				    (object Message "report()"
+					quid       	"3DFE442501B1"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"3.1"
+					ordinal    	5
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE452A00F7"
+				supplier   	"ErrorDispatcherValve"
+				quidu      	"3DFE451F01EC"))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "ErrorDispatcherValve"
+			quid       	"3DFE451F01EC"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE47500148"
+				supplier   	"StandardHostValve"
+				quidu      	"3DFE47310130")
+			    (object Link
+				quid       	"3DFE47580335"
+				supplier   	"ErrorDispatcherValve"
+				quidu      	"3DFE451F01EC"))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardHostValve"
+			quid       	"3DFE47310130"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE47CD0166"
+				supplier   	"StandardHostValve"
+				quidu      	"3DFE47310130"
+				messages   	(list Messages
+				    (object Message "map() //Context"
+					quid       	"3DFE47CD0167"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"5.1"
+					ordinal    	10
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE47D500B3"
+				supplier   	"StandardContext"
+				quidu      	"3DFE47C100F1"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE47D500B4"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"5.2"
+					ordinal    	11
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardContext"
+			quid       	"3DFE47C100F1"
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)))
+	    (object Mechanism @90
+		logical_models 	(list unit_reference_list
+		    (object Object "StandardContext"
+			quid       	"3DFE48B001D1"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE48BE0267"
+				supplier   	"StandardPipeline"
+				quidu      	"3DFE48B80088"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE48BE0268"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1"
+					ordinal    	0
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardPipeline"
+			quid       	"3DFE48B80088"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE48EA0039"
+				supplier   	"StandardValveContext"
+				quidu      	"3DFE48D000DC"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE48EA003A"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1"
+					ordinal    	1
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "invoke()"
+					quid       	"3DFE4976015D"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.2"
+					ordinal    	6
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardValveContext"
+			quid       	"3DFE48D000DC"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE491102D5"
+				supplier   	"StandardContextValve"
+				quidu      	"3DFE490303A7"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE491102D6"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.1"
+					ordinal    	2
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE4993023B"
+				supplier   	"StandardWrapperValve"
+				quidu      	"3DFE49890056"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE4993023C"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.2.1"
+					ordinal    	7
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardContextValve"
+			quid       	"3DFE490303A7"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE492F033C"
+				supplier   	"StandardContextValve"
+				quidu      	"3DFE490303A7"
+				messages   	(list Messages
+				    (object Message "map //return Wrapper"
+					quid       	"3DFE492F033D"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.1.1"
+					ordinal    	3
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE494A0150"
+				supplier   	"StandardWrapper"
+				quidu      	"3DFE49370351"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE494A0151"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.1.2"
+					ordinal    	4
+					Operation  	"invoke"
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardWrapper"
+			quid       	"3DFE49370351"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE495F0287"
+				supplier   	"StandardPipeline"
+				quidu      	"3DFE48B80088"
+				messages   	(list Messages
+				    (object Message "invoke()"
+					quid       	"3DFE495F0288"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.1.1.2.1"
+					ordinal    	5
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "StandardWrapperValve"
+			quid       	"3DFE49890056"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE49DB018A"
+				supplier   	"StandardWrapperValve"
+				quidu      	"3DFE49890056")
+			    (object Link
+				quid       	"3DFE49EC004E"
+				supplier   	"StandardWrapper"
+				quidu      	"3DFE49370351"
+				messages   	(list Messages
+				    (object Message "allocate()"
+					quid       	"3DFE49EC004F"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.2.1.1"
+					ordinal    	8
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "return servlet"
+					quid       	"3DFE4A200067"
+					frequency  	"Aperiodic"
+					synchronization 	"Return"
+					dir        	"ToClientFromSupplier"
+					sequence   	"1.2.1.1.1"
+					ordinal    	9
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE4A29027D"
+				supplier   	"ApplicationFilterChain"
+				quidu      	"3DFE4A1500B2"
+				messages   	(list Messages
+				    (object Message "createFilterChain()"
+					quid       	"3DFE4A29027E"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.2.1.1.1.1"
+					ordinal    	10
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "doFilter()"
+					quid       	"3DFE4A490283"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.2.1.2"
+					ordinal    	11
+					Operation  	"doFilter"
+					quidu      	"000000000000"
+					creation   	FALSE)
+				    (object Message "return"
+					quid       	"3DFE4CB4025B"
+					frequency  	"Aperiodic"
+					synchronization 	"Return"
+					dir        	"ToClientFromSupplier"
+					sequence   	"1.2.1.2.3"
+					ordinal    	14
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "ApplicationFilterChain"
+			quid       	"3DFE4A1500B2"
+			collaborators 	(list link_list
+			    (object Link
+				quid       	"3DFE4C2701C2"
+				supplier   	"ApplicationFilterChain"
+				quidu      	"3DFE4A1500B2"
+				messages   	(list Messages
+				    (object Message "internalDoFilter()"
+					quid       	"3DFE4C2701C3"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.2.1.2.1"
+					ordinal    	12
+					quidu      	"000000000000"
+					creation   	FALSE)))
+			    (object Link
+				quid       	"3DFE4CA502BE"
+				supplier   	"$UNNAMED$0"
+				quidu      	"3DFE4BAE0056"
+				messages   	(list Messages
+				    (object Message "service()"
+					quid       	"3DFE4CA502BF"
+					frequency  	"Aperiodic"
+					synchronization 	"Simple"
+					dir        	"FromClientToSupplier"
+					sequence   	"1.2.1.2.2"
+					ordinal    	13
+					quidu      	"000000000000"
+					creation   	FALSE))))
+			persistence 	"Transient"
+			creationObj 	FALSE
+			multi      	FALSE)
+		    (object Object "$UNNAMED$0"
+			quid       	"3DFE4BAE0056"
+			stereotype 	"Servlet"
+			persistence 	"Transient"
+			creationObj 	TRUE
+			multi      	FALSE))))
+	logical_presentations 	(list unit_reference_list
+	    (object ClassDiagram "Main"
+		quid       	"3DFDF6D2021B"
+		title      	"Main"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	0
+		items      	(list diagram_item_list))
+	    (object ClassDiagram "high level packaging"
+		quid       	"3E42DE75004B"
+		title      	"high level packaging"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	0
+		items      	(list diagram_item_list
+		    (object CategoryView "Logical View::org.apache.catalina" @91
+			location   	(1024, 752)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@91
+			    location   	(780, 668)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	488
+			    justify    	0
+			    label      	"org.apache.catalina")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3E42DE8D0082"
+			width      	500
+			height     	181)
+		    (object CategoryView "Logical View::org.apache.coyote" @92
+			location   	(512, 1184)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@92
+			    location   	(237, 1090)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	550
+			    justify    	0
+			    label      	"org.apache.coyote")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3E42DE9F0132"
+			width      	563
+			height     	200)
+		    (object CategoryView "Logical View::org.apache.tomcat.util" @93
+			location   	(1920, 1104)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@93
+			    location   	(1670, 1020)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	500
+			    justify    	0
+			    label      	"org.apache.tomcat.util")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3E42DEDF01F2"
+			width      	512
+			height     	181)
+		    (object ImportView "" @94
+			stereotype 	TRUE
+			line_color 	3342489
+			quidu      	"3E42DEF601EB"
+			client     	@91
+			supplier   	@93
+			line_style 	0)
+		    (object ImportView "" @95
+			stereotype 	TRUE
+			line_color 	3342489
+			quidu      	"3E42DEFC00B3"
+			client     	@92
+			supplier   	@93
+			line_style 	0)
+		    (object CategoryView "Logical View::org.apache.jasper" @96
+			location   	(1728, 624)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@96
+			    location   	(1437, 540)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	582
+			    justify    	0
+			    label      	"org.apache.jasper")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3E42DEFF0270"
+			width      	594
+			height     	181)
+		    (object ImportView "" @97
+			stereotype 	TRUE
+			line_color 	3342489
+			quidu      	"3E42DF700060"
+			client     	@91
+			supplier   	@92
+			line_style 	0)
+		    (object NoteView @98
+			location   	(1200, 208)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@98
+			    location   	(847, 143)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	671
+			    label      	"High Level package dependencies")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	731
+			height     	143)
+		    (object CategoryView "Logical View::org.apache.naming" @99
+			location   	(352, 304)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@99
+			    location   	(83, 220)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	538
+			    justify    	0
+			    label      	"org.apache.naming")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3E43D1580339"
+			width      	550
+			height     	181)
+		    (object ImportView "" @100
+			stereotype 	TRUE
+			line_color 	3342489
+			quidu      	"3E43D165039C"
+			client     	@91
+			supplier   	@99
+			line_style 	0)))
+	    (object InteractionDiagram "1. catalina_load"
+		mechanism_ref 	@82
+		quid       	"3DFDF8EE0267"
+		title      	"1. catalina_load"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	519
+		items      	(list diagram_item_list
+		    (object InterObjView "Bootstrap" @101
+			location   	(224, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@101
+			    location   	(224, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Bootstrap")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDF8FD0345"
+			width      	300
+			height     	1972
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @102
+			    location   	(224, 368)
+			    line_color 	3342489
+			    InterObjView 	@101
+			    height     	1738
+			    y_coord    	1678
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @103
+			    location   	(224, 368)
+			    line_color 	3342489
+			    InterObjView 	@101
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "Digester" @104
+			location   	(896, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@104
+			    location   	(896, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Digester")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDFAF201A1"
+			width      	300
+			height     	1972
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @105
+			    location   	(896, 1232)
+			    line_color 	3342489
+			    InterObjView 	@104
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @106
+			    location   	(896, 1312)
+			    line_color 	3342489
+			    InterObjView 	@104
+			    height     	264
+			    y_coord    	204
+			    Nested     	FALSE))
+		    (object InterObjView "ServerLifecycleListener" @107
+			location   	(1232, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@107
+			    location   	(1232, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"ServerLifecycleListener")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDFB4B0217"
+			width      	300
+			height     	1972
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @108
+			    location   	(1232, 1328)
+			    line_color 	3342489
+			    InterObjView 	@107
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "GlobalResourcesLifecycleListener" @109
+			location   	(1568, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@109
+			    location   	(1568, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	322
+			    justify    	0
+			    label      	"GlobalResourcesLifecycleListener")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDFB7A02AB"
+			width      	340
+			height     	1972
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @110
+			    location   	(1568, 1456)
+			    line_color 	3342489
+			    InterObjView 	@109
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "SecurityConfig" @111
+			location   	(1920, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@111
+			    location   	(1920, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"SecurityConfig")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDFBD802BA"
+			width      	300
+			height     	1972
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @112
+			    location   	(1920, 1600)
+			    line_color 	3342489
+			    InterObjView 	@111
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @113
+			    location   	(1920, 1680)
+			    line_color 	3342489
+			    InterObjView 	@111
+			    height     	146
+			    y_coord    	86
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @114
+			    location   	(1920, 1760)
+			    line_color 	3342489
+			    InterObjView 	@111
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object SelfMessView "" @115
+			location   	(16, 368)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @116
+			    Parent_View 	@115
+			    location   	(315, 324)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDF9210009"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	340
+			    justify    	0
+			    label      	"initClassLoaders()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@101
+			supplier   	@101
+			Focus_Src  	@102
+			Focus_Entry 	@103
+			origin     	(240, 368)
+			terminus   	(390, 368)
+			ordinal    	0)
+		    (object NoteView @117
+			location   	(1152, 1072)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@117
+			    location   	(1014, 1012)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	240
+			    label      	"parse server.xml")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	300
+			height     	132)
+		    (object NoteView @118
+			location   	(1376, 80)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@118
+			    location   	(1238, 20)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	240
+			    label      	"MBeans")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	300
+			height     	132)
+		    (object AttachView "" @119
+			stereotype 	TRUE
+			line_color 	3342489
+			client     	@118
+			supplier   	@107
+			line_style 	0)
+		    (object AttachView "" @120
+			stereotype 	TRUE
+			line_color 	3342489
+			client     	@109
+			supplier   	@118
+			line_style 	0)
+		    (object NoteView @121
+			location   	(2160, 2176)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@121
+			    location   	(1947, 2113)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	390
+			    label      	"#1Catalina.load()")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	450
+			height     	138)
+		    (object InterObjView "Catalina" @122
+			location   	(560, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@122
+			    location   	(560, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Catalina")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDF90A0330"
+			width      	300
+			height     	1972
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @123
+			    location   	(560, 464)
+			    line_color 	3342489
+			    InterObjView 	@122
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @124
+			    location   	(560, 608)
+			    line_color 	3342489
+			    InterObjView 	@122
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @125
+			    location   	(560, 720)
+			    line_color 	3342489
+			    InterObjView 	@122
+			    height     	1326
+			    y_coord    	1266
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @126
+			    location   	(560, 896)
+			    line_color 	3342489
+			    InterObjView 	@122
+			    height     	194
+			    y_coord    	134
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @127
+			    location   	(560, 896)
+			    InterObjView 	@122
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @128
+			    location   	(560, 1024)
+			    line_color 	3342489
+			    InterObjView 	@122
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @129
+			    location   	(560, 1024)
+			    InterObjView 	@122
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @130
+			    location   	(560, 1152)
+			    line_color 	3342489
+			    InterObjView 	@122
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @131
+			    location   	(560, 1152)
+			    InterObjView 	@122
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object SelfMessView "" @132
+			location   	(16, 896)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @133
+			    Parent_View 	@132
+			    location   	(651, 852)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFA8001DA"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	160
+			    justify    	0
+			    label      	"initDirs()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@122
+			supplier   	@122
+			Focus_Src  	@127
+			Focus_Entry 	@126
+			origin     	(576, 896)
+			terminus   	(726, 896)
+			ordinal    	4)
+		    (object SelfMessView "" @134
+			location   	(16, 1024)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @135
+			    Parent_View 	@134
+			    location   	(701, 981)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFA8B0347"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	228
+			    justify    	0
+			    label      	"initNaming()"
+			    pctDist    	0.840000
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@122
+			supplier   	@122
+			Focus_Src  	@129
+			Focus_Entry 	@128
+			origin     	(576, 1024)
+			terminus   	(726, 1024)
+			ordinal    	5)
+		    (object SelfMessView "" @136
+			location   	(16, 1152)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @137
+			    Parent_View 	@136
+			    location   	(686, 1109)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFAAD01AC"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	180
+			    justify    	0
+			    label      	"initialize()"
+			    pctDist    	0.733333
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@122
+			supplier   	@122
+			Focus_Src  	@131
+			Focus_Entry 	@130
+			origin     	(576, 1152)
+			terminus   	(726, 1152)
+			ordinal    	6)
+		    (object InterMessView "" @138
+			location   	(16, 464)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @139
+			    Parent_View 	@138
+			    location   	(389, 437)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDF91A010D"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	265
+			    justify    	0
+			    label      	"newInstance()"
+			    pctDist    	0.495082
+			    height     	28
+			    orientation 	0)
+			line_color 	3342489
+			client     	@101
+			supplier   	@122
+			Focus_Src  	@102
+			Focus_Entry 	@123
+			origin     	(239, 464)
+			terminus   	(544, 464)
+			ordinal    	1)
+		    (object InterMessView "" @140
+			location   	(16, 608)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @141
+			    Parent_View 	@140
+			    location   	(456, 565)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDF97900C2"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	445
+			    justify    	0
+			    label      	"setParentClassLoader()"
+			    pctDist    	0.711475
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@101
+			supplier   	@122
+			Focus_Src  	@102
+			Focus_Entry 	@124
+			origin     	(239, 608)
+			terminus   	(544, 608)
+			ordinal    	2)
+		    (object InterMessView "" @142
+			location   	(16, 720)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @143
+			    Parent_View 	@142
+			    location   	(391, 676)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFA3402F2"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	108
+			    justify    	0
+			    label      	"load()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@101
+			supplier   	@122
+			Focus_Src  	@102
+			Focus_Entry 	@125
+			origin     	(239, 720)
+			terminus   	(544, 720)
+			ordinal    	3)
+		    (object InterMessView "" @144
+			location   	(16, 1232)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @145
+			    Parent_View 	@144
+			    location   	(727, 1188)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFAF800C4"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	302
+			    justify    	0
+			    label      	"createDigester()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@122
+			supplier   	@104
+			Focus_Src  	@125
+			Focus_Entry 	@105
+			origin     	(575, 1232)
+			terminus   	(880, 1232)
+			ordinal    	7)
+		    (object InterMessView "" @146
+			location   	(16, 1312)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @147
+			    Parent_View 	@146
+			    location   	(727, 1268)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFB0100B2"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	136
+			    justify    	0
+			    label      	"parse()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@122
+			supplier   	@104
+			Focus_Src  	@125
+			Focus_Entry 	@106
+			origin     	(575, 1312)
+			terminus   	(880, 1312)
+			ordinal    	8)
+		    (object AttachView "" @148
+			stereotype 	TRUE
+			line_color 	3342489
+			client     	@147
+			supplier   	@117
+			line_style 	0)
+		    (object InterMessView "" @149
+			location   	(16, 1328)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @150
+			    Parent_View 	@149
+			    location   	(1063, 1284)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFB8400A7"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	265
+			    justify    	0
+			    label      	"newInstance()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@104
+			supplier   	@107
+			Focus_Src  	@106
+			Focus_Entry 	@108
+			origin     	(911, 1328)
+			terminus   	(1216, 1328)
+			ordinal    	9)
+		    (object InterMessView "" @151
+			location   	(16, 1456)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @152
+			    Parent_View 	@151
+			    location   	(1231, 1412)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFB920148"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	265
+			    justify    	0
+			    label      	"newInstance()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@104
+			supplier   	@109
+			Focus_Src  	@106
+			Focus_Entry 	@110
+			origin     	(911, 1456)
+			terminus   	(1552, 1456)
+			ordinal    	10)
+		    (object InterMessView "" @153
+			location   	(16, 1600)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @154
+			    Parent_View 	@153
+			    location   	(1239, 1556)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFBEA00C2"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	265
+			    justify    	0
+			    label      	"newInstance()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@122
+			supplier   	@111
+			Focus_Src  	@125
+			Focus_Entry 	@112
+			origin     	(575, 1600)
+			terminus   	(1904, 1600)
+			ordinal    	11)
+		    (object InterMessView "" @155
+			location   	(16, 1680)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @156
+			    Parent_View 	@155
+			    location   	(1239, 1636)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFBF401F2"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	425
+			    justify    	0
+			    label      	"setPackageDefinition()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@122
+			supplier   	@111
+			Focus_Src  	@125
+			Focus_Entry 	@113
+			origin     	(575, 1680)
+			terminus   	(1904, 1680)
+			ordinal    	12)
+		    (object InterMessView "" @157
+			location   	(16, 1760)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @158
+			    Parent_View 	@157
+			    location   	(1239, 1716)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFC1203C2"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	386
+			    justify    	0
+			    label      	"setPackageAccess()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@122
+			supplier   	@111
+			Focus_Src  	@125
+			Focus_Entry 	@114
+			origin     	(575, 1760)
+			terminus   	(1904, 1760)
+			ordinal    	13)))
+	    (object InteractionDiagram "2. catalina_initliaze"
+		mechanism_ref 	@83
+		quid       	"3DFDFC44002A"
+		title      	"2. catalina_initliaze"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	87
+		items      	(list diagram_item_list
+		    (object InterObjView "Catalina" @159
+			location   	(176, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@159
+			    location   	(176, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Catalina")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDFC8F015F"
+			width      	300
+			height     	1180
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @160
+			    location   	(176, 400)
+			    line_color 	3342489
+			    InterObjView 	@159
+			    height     	914
+			    y_coord    	854
+			    Nested     	FALSE))
+		    (object InterObjView "StandardServer" @161
+			location   	(496, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@161
+			    location   	(496, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardServer")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDFCCB006B"
+			width      	300
+			height     	1180
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @162
+			    location   	(496, 400)
+			    line_color 	3342489
+			    InterObjView 	@161
+			    height     	854
+			    y_coord    	794
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @163
+			    location   	(496, 480)
+			    line_color 	3342489
+			    InterObjView 	@161
+			    height     	768
+			    y_coord    	708
+			    Nested     	TRUE))
+		    (object InterObjView "StandardService" @164
+			location   	(832, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@164
+			    location   	(832, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardService")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDFD370020"
+			width      	300
+			height     	1180
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @165
+			    location   	(832, 480)
+			    line_color 	3342489
+			    InterObjView 	@164
+			    height     	708
+			    y_coord    	648
+			    Nested     	FALSE))
+		    (object InterObjView "CoyoteConnector" @166
+			location   	(1168, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@166
+			    location   	(1168, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"CoyoteConnector")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDFE810313"
+			width      	300
+			height     	1180
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @167
+			    location   	(1168, 528)
+			    line_color 	3342489
+			    InterObjView 	@166
+			    height     	600
+			    y_coord    	540
+			    Nested     	FALSE))
+		    (object InterObjView "CoyoteAdapter" @168
+			location   	(1504, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@168
+			    location   	(1504, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"CoyoteAdapter")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFDFFA00226"
+			width      	300
+			height     	1180
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @169
+			    location   	(1504, 576)
+			    line_color 	3342489
+			    InterObjView 	@168
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "Http11Protocol" @170
+			location   	(1808, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@170
+			    location   	(1808, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Http11Protocol")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE016601A6"
+			width      	300
+			height     	1180
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @171
+			    location   	(1808, 704)
+			    line_color 	3342489
+			    InterObjView 	@170
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @172
+			    location   	(1808, 832)
+			    line_color 	3342489
+			    InterObjView 	@170
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "JkCoyoteAdapter" @173
+			location   	(2144, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@173
+			    location   	(2144, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"JkCoyoteAdapter")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE01AD01A8"
+			width      	300
+			height     	1180
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @174
+			    location   	(2144, 928)
+			    line_color 	3342489
+			    InterObjView 	@173
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @175
+			    location   	(2144, 1008)
+			    line_color 	3342489
+			    InterObjView 	@173
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterMessView "" @176
+			location   	(16, 400)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @177
+			    Parent_View 	@176
+			    location   	(335, 356)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFD1F0076"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	180
+			    justify    	0
+			    label      	"initialize()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@159
+			supplier   	@161
+			Focus_Src  	@160
+			Focus_Entry 	@162
+			origin     	(191, 400)
+			terminus   	(480, 400)
+			ordinal    	0)
+		    (object InterMessView "" @178
+			location   	(16, 480)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @179
+			    Parent_View 	@178
+			    location   	(663, 436)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFD3D01C4"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	180
+			    justify    	0
+			    label      	"initialize()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@161
+			supplier   	@164
+			Focus_Src  	@163
+			Focus_Entry 	@165
+			origin     	(511, 480)
+			terminus   	(816, 480)
+			ordinal    	1)
+		    (object InterMessView "" @180
+			location   	(16, 528)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @181
+			    Parent_View 	@180
+			    location   	(999, 484)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFDFE990305"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	180
+			    justify    	0
+			    label      	"initialize()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@164
+			supplier   	@166
+			Focus_Src  	@165
+			Focus_Entry 	@167
+			origin     	(847, 528)
+			terminus   	(1152, 528)
+			ordinal    	2)
+		    (object InterMessView "" @182
+			location   	(16, 576)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @183
+			    Parent_View 	@182
+			    location   	(1335, 532)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE013D0217"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	106
+			    justify    	0
+			    label      	"new()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@166
+			supplier   	@168
+			Focus_Src  	@167
+			Focus_Entry 	@169
+			origin     	(1183, 576)
+			terminus   	(1488, 576)
+			ordinal    	3)
+		    (object InterMessView "" @184
+			location   	(1504, 704)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @185
+			    Parent_View 	@184
+			    location   	(1487, 660)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE01830330"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	106
+			    justify    	0
+			    label      	"new()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@166
+			supplier   	@170
+			Focus_Src  	@167
+			Focus_Entry 	@171
+			origin     	(1183, 704)
+			terminus   	(1792, 704)
+			ordinal    	4)
+		    (object InterMessView "" @186
+			location   	(1504, 832)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @187
+			    Parent_View 	@186
+			    location   	(1487, 788)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE0188032C"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	80
+			    justify    	0
+			    label      	"init()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@166
+			supplier   	@170
+			Focus_Src  	@167
+			Focus_Entry 	@172
+			origin     	(1183, 832)
+			terminus   	(1792, 832)
+			ordinal    	5)
+		    (object InterMessView "" @188
+			location   	(16, 928)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @189
+			    Parent_View 	@188
+			    location   	(1655, 884)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE01BC038C"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	106
+			    justify    	0
+			    label      	"new()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@166
+			supplier   	@173
+			Focus_Src  	@167
+			Focus_Entry 	@174
+			origin     	(1183, 928)
+			terminus   	(2128, 928)
+			ordinal    	6)
+		    (object InterMessView "" @190
+			location   	(16, 1008)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @191
+			    Parent_View 	@190
+			    location   	(1655, 964)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE01C30164"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	80
+			    justify    	0
+			    label      	"init()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@166
+			supplier   	@173
+			Focus_Src  	@167
+			Focus_Entry 	@175
+			origin     	(1183, 1008)
+			terminus   	(2128, 1008)
+			ordinal    	7)
+		    (object NoteView @192
+			location   	(2144, 2016)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@192
+			    location   	(1947, 1957)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	359
+			    label      	"#2 Catalina.initialize()")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	419
+			height     	131)))
+	    (object InteractionDiagram "3. catalina_start"
+		mechanism_ref 	@84
+		quid       	"3DFE026D02D1"
+		title      	"3. catalina_start"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	2481
+		items      	(list diagram_item_list
+		    (object InterObjView "Bootstrap" @193
+			location   	(192, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@193
+			    location   	(192, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Bootstrap")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE027700F5"
+			width      	300
+			height     	2912
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @194
+			    location   	(192, 384)
+			    line_color 	3342489
+			    InterObjView 	@193
+			    height     	2662
+			    y_coord    	2602
+			    Nested     	FALSE))
+		    (object InterObjView "Catalina" @195
+			location   	(480, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@195
+			    location   	(480, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Catalina")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE027D0067"
+			width      	300
+			height     	2912
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @196
+			    location   	(480, 384)
+			    line_color 	3342489
+			    InterObjView 	@195
+			    height     	2602
+			    y_coord    	2542
+			    Nested     	FALSE))
+		    (object InterObjView "StandardServer" @197
+			location   	(784, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@197
+			    location   	(784, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardServer")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE02B30015"
+			width      	300
+			height     	2912
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @198
+			    location   	(784, 416)
+			    line_color 	3342489
+			    InterObjView 	@197
+			    height     	2510
+			    y_coord    	2450
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @199
+			    location   	(784, 480)
+			    line_color 	3342489
+			    InterObjView 	@197
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @200
+			    location   	(784, 592)
+			    line_color 	3342489
+			    InterObjView 	@197
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardService" @201
+			location   	(1088, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@201
+			    location   	(1088, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardService")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE030400E3"
+			width      	300
+			height     	2912
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @202
+			    location   	(1088, 704)
+			    line_color 	3342489
+			    InterObjView 	@201
+			    height     	2162
+			    y_coord    	2102
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @203
+			    location   	(1088, 752)
+			    line_color 	3342489
+			    InterObjView 	@201
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @204
+			    location   	(1088, 864)
+			    line_color 	3342489
+			    InterObjView 	@201
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardEngine" @205
+			location   	(1424, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@205
+			    location   	(1424, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	332
+			    justify    	0
+			    label      	"StandardEngine")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE034700C2"
+			width      	350
+			height     	2912
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @206
+			    location   	(1424, 976)
+			    line_color 	3342489
+			    InterObjView 	@205
+			    height     	1830
+			    y_coord    	1770
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @207
+			    location   	(1424, 1056)
+			    line_color 	3342489
+			    InterObjView 	@205
+			    height     	1744
+			    y_coord    	1684
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @208
+			    location   	(1424, 1056)
+			    line_color 	3342489
+			    InterObjView 	@205
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @209
+			    location   	(1424, 1168)
+			    line_color 	3342489
+			    InterObjView 	@205
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @210
+			    location   	(1424, 1296)
+			    line_color 	3342489
+			    InterObjView 	@205
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @211
+			    location   	(1424, 1408)
+			    line_color 	3342489
+			    InterObjView 	@205
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @212
+			    location   	(1424, 1536)
+			    line_color 	3342489
+			    InterObjView 	@205
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @213
+			    location   	(1424, 1648)
+			    line_color 	3342489
+			    InterObjView 	@205
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardHost" @214
+			location   	(1760, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@214
+			    location   	(1760, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardHost")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE03F2035D"
+			width      	300
+			height     	2912
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @215
+			    location   	(1760, 1760)
+			    line_color 	3342489
+			    InterObjView 	@214
+			    height     	980
+			    y_coord    	920
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @216
+			    location   	(1760, 1808)
+			    line_color 	3342489
+			    InterObjView 	@214
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @217
+			    location   	(1760, 1920)
+			    line_color 	3342489
+			    InterObjView 	@214
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @218
+			    location   	(1760, 2032)
+			    line_color 	3342489
+			    InterObjView 	@214
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @219
+			    location   	(1760, 2144)
+			    line_color 	3342489
+			    InterObjView 	@214
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @220
+			    location   	(1760, 2256)
+			    line_color 	3342489
+			    InterObjView 	@214
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardPipeline" @221
+			location   	(2080, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@221
+			    location   	(2080, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	326
+			    justify    	0
+			    label      	"StandardPipeline")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE047D006D"
+			width      	344
+			height     	2912
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @222
+			    location   	(2080, 2368)
+			    line_color 	3342489
+			    InterObjView 	@221
+			    height     	312
+			    y_coord    	252
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @223
+			    location   	(2080, 2416)
+			    line_color 	3342489
+			    InterObjView 	@221
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @224
+			    location   	(2080, 2480)
+			    line_color 	3342489
+			    InterObjView 	@221
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @225
+			    location   	(2080, 2560)
+			    line_color 	3342489
+			    InterObjView 	@221
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterMessView "" @226
+			location   	(16, 384)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @227
+			    Parent_View 	@226
+			    location   	(335, 340)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE02830374"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	110
+			    justify    	0
+			    label      	"start()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@193
+			supplier   	@195
+			Focus_Src  	@194
+			Focus_Entry 	@196
+			origin     	(207, 384)
+			terminus   	(464, 384)
+			ordinal    	0)
+		    (object InterMessView "" @228
+			location   	(16, 416)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @229
+			    Parent_View 	@228
+			    location   	(631, 372)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE02BA0188"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	110
+			    justify    	0
+			    label      	"start()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@195
+			supplier   	@197
+			Focus_Src  	@196
+			Focus_Entry 	@198
+			origin     	(495, 416)
+			terminus   	(768, 416)
+			ordinal    	1)
+		    (object SelfMessView "" @230
+			location   	(16, 480)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @231
+			    Parent_View 	@230
+			    location   	(1244, 437)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE02D3006C"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	854
+			    justify    	0
+			    label      	"fireLifecycleEvent(BEFORE_START_EVENT)"
+			    pctDist    	2.960000
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@197
+			supplier   	@197
+			Focus_Src  	@198
+			Focus_Entry 	@199
+			origin     	(800, 480)
+			terminus   	(950, 480)
+			ordinal    	2)
+		    (object SelfMessView "" @232
+			location   	(16, 592)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @233
+			    Parent_View 	@232
+			    location   	(1146, 549)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE02DF02DF"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	658
+			    justify    	0
+			    label      	"fireLifecycleEvent(START_EVENT)"
+			    pctDist    	2.313333
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@197
+			supplier   	@197
+			Focus_Src  	@198
+			Focus_Entry 	@200
+			origin     	(800, 592)
+			terminus   	(950, 592)
+			ordinal    	3)
+		    (object InterMessView "" @234
+			location   	(16, 704)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @235
+			    Parent_View 	@234
+			    location   	(935, 660)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE030C02B3"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	110
+			    justify    	0
+			    label      	"start()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@197
+			supplier   	@201
+			Focus_Src  	@198
+			Focus_Entry 	@202
+			origin     	(799, 704)
+			terminus   	(1072, 704)
+			ordinal    	4)
+		    (object SelfMessView "" @236
+			location   	(16, 752)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @237
+			    Parent_View 	@236
+			    location   	(1531, 708)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE031D0022"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	854
+			    justify    	0
+			    label      	"fireLifecycleEvent(BEFORE_START_EVENT)"
+			    pctDist    	2.853333
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@201
+			supplier   	@201
+			Focus_Src  	@202
+			Focus_Entry 	@203
+			origin     	(1104, 752)
+			terminus   	(1254, 752)
+			ordinal    	5)
+		    (object SelfMessView "" @238
+			location   	(16, 864)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @239
+			    Parent_View 	@238
+			    location   	(1449, 821)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE0330019B"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	658
+			    justify    	0
+			    label      	"fireLifecycleEvent(START_EVENT)"
+			    pctDist    	2.306667
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@201
+			supplier   	@201
+			Focus_Src  	@202
+			Focus_Entry 	@204
+			origin     	(1104, 864)
+			terminus   	(1254, 864)
+			ordinal    	6)
+		    (object InterMessView "" @240
+			location   	(16, 976)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @241
+			    Parent_View 	@240
+			    location   	(1255, 932)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE0370018A"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	110
+			    justify    	0
+			    label      	"start()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@201
+			supplier   	@205
+			Focus_Src  	@202
+			Focus_Entry 	@206
+			origin     	(1103, 976)
+			terminus   	(1408, 976)
+			ordinal    	7)
+		    (object SelfMessView "" @242
+			location   	(16, 1056)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @243
+			    Parent_View 	@242
+			    location   	(1865, 1014)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE03750051"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	854
+			    justify    	0
+			    label      	"fireLifecycleEvent(BEFORE_START_EVENT)"
+			    pctDist    	2.840000
+			    height     	43
+			    orientation 	0)
+			line_color 	3342489
+			client     	@205
+			supplier   	@205
+			Focus_Src  	@207
+			Focus_Entry 	@208
+			origin     	(1440, 1056)
+			terminus   	(1590, 1056)
+			ordinal    	8)
+		    (object SelfMessView "" @244
+			location   	(16, 1168)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @245
+			    Parent_View 	@244
+			    location   	(1639, 1141)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE0389001C"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	373
+			    justify    	0
+			    label      	"addDefaultMapper()"
+			    pctDist    	1.326667
+			    height     	28
+			    orientation 	0)
+			line_color 	3342489
+			client     	@205
+			supplier   	@205
+			Focus_Src  	@207
+			Focus_Entry 	@209
+			origin     	(1440, 1168)
+			terminus   	(1590, 1168)
+			ordinal    	9)
+		    (object SelfMessView "" @246
+			location   	(16, 1296)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @247
+			    Parent_View 	@246
+			    location   	(1592, 1268)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE03980281"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	238
+			    justify    	0
+			    label      	"logger.start()"
+			    pctDist    	1.020000
+			    height     	29
+			    orientation 	0)
+			line_color 	3342489
+			client     	@205
+			supplier   	@205
+			Focus_Src  	@207
+			Focus_Entry 	@210
+			origin     	(1440, 1296)
+			terminus   	(1590, 1296)
+			ordinal    	10)
+		    (object SelfMessView "" @248
+			location   	(16, 1408)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @249
+			    Parent_View 	@248
+			    location   	(1593, 1380)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE03A80107"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	226
+			    justify    	0
+			    label      	"realm.start()"
+			    pctDist    	1.026667
+			    height     	29
+			    orientation 	0)
+			line_color 	3342489
+			client     	@205
+			supplier   	@205
+			Focus_Src  	@207
+			Focus_Entry 	@211
+			origin     	(1440, 1408)
+			terminus   	(1590, 1408)
+			ordinal    	11)
+		    (object SelfMessView "" @250
+			location   	(16, 1536)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @251
+			    Parent_View 	@250
+			    location   	(1608, 1508)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE03BD000D"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	259
+			    justify    	0
+			    label      	"findMappers()"
+			    pctDist    	1.120000
+			    height     	29
+			    orientation 	0)
+			line_color 	3342489
+			client     	@205
+			supplier   	@205
+			Focus_Src  	@207
+			Focus_Entry 	@212
+			origin     	(1440, 1536)
+			terminus   	(1590, 1536)
+			ordinal    	12)
+		    (object SelfMessView "" @252
+			location   	(16, 1648)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @253
+			    Parent_View 	@252
+			    location   	(1515, 1604)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE03E000A4"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	251
+			    justify    	0
+			    label      	"findChildren()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@205
+			supplier   	@205
+			Focus_Src  	@207
+			Focus_Entry 	@213
+			origin     	(1440, 1648)
+			terminus   	(1590, 1648)
+			ordinal    	13)
+		    (object InterMessView "" @254
+			location   	(1664, 1760)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @255
+			    Parent_View 	@254
+			    location   	(1591, 1716)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE03FB027A"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	110
+			    justify    	0
+			    label      	"start()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@205
+			supplier   	@214
+			Focus_Src  	@207
+			Focus_Entry 	@215
+			origin     	(1439, 1760)
+			terminus   	(1744, 1760)
+			ordinal    	14)
+		    (object SelfMessView "" @256
+			location   	(16, 1808)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @257
+			    Parent_View 	@256
+			    location   	(1606, 1784)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE043B02AE"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	854
+			    justify    	0
+			    label      	"fireLifecycleEvent(BEFORE_START_EVENT)"
+			    pctDist    	-1.133333
+			    height     	24
+			    orientation 	0)
+			line_color 	3342489
+			client     	@214
+			supplier   	@214
+			Focus_Src  	@215
+			Focus_Entry 	@216
+			origin     	(1776, 1808)
+			terminus   	(1926, 1808)
+			ordinal    	15)
+		    (object SelfMessView "" @258
+			location   	(16, 1920)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @259
+			    Parent_View 	@258
+			    location   	(1963, 1877)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE045C021F"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	373
+			    justify    	0
+			    label      	"addDefaultMapper()"
+			    pctDist    	1.253333
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@214
+			supplier   	@214
+			Focus_Src  	@215
+			Focus_Entry 	@217
+			origin     	(1776, 1920)
+			terminus   	(1926, 1920)
+			ordinal    	16)
+		    (object InterMessView "" @260
+			location   	(2000, 2368)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @261
+			    Parent_View 	@260
+			    location   	(1919, 2324)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE048E00B9"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	110
+			    justify    	0
+			    label      	"start()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@214
+			supplier   	@221
+			Focus_Src  	@215
+			Focus_Entry 	@222
+			origin     	(1775, 2368)
+			terminus   	(2064, 2368)
+			ordinal    	20)
+		    (object SelfMessView "" @262
+			location   	(16, 2032)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @263
+			    Parent_View 	@262
+			    location   	(1916, 2004)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE049B000C"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	238
+			    justify    	0
+			    label      	"logger.start()"
+			    pctDist    	0.933333
+			    height     	29
+			    orientation 	0)
+			line_color 	3342489
+			client     	@214
+			supplier   	@214
+			Focus_Src  	@215
+			Focus_Entry 	@218
+			origin     	(1776, 2032)
+			terminus   	(1926, 2032)
+			ordinal    	17)
+		    (object SelfMessView "" @264
+			location   	(16, 2144)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @265
+			    Parent_View 	@264
+			    location   	(1916, 2117)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE04A303BB"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	238
+			    justify    	0
+			    label      	"findMapper()"
+			    pctDist    	0.933333
+			    height     	28
+			    orientation 	0)
+			line_color 	3342489
+			client     	@214
+			supplier   	@214
+			Focus_Src  	@215
+			Focus_Entry 	@219
+			origin     	(1776, 2144)
+			terminus   	(1926, 2144)
+			ordinal    	18)
+		    (object SelfMessView "" @266
+			location   	(16, 2256)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @267
+			    Parent_View 	@266
+			    location   	(1916, 2228)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE04A90342"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	251
+			    justify    	0
+			    label      	"findChildren()"
+			    pctDist    	0.933333
+			    height     	29
+			    orientation 	0)
+			line_color 	3342489
+			client     	@214
+			supplier   	@214
+			Focus_Src  	@215
+			Focus_Entry 	@220
+			origin     	(1776, 2256)
+			terminus   	(1926, 2256)
+			ordinal    	19)
+		    (object NoteView @268
+			location   	(2128, 1488)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@268
+			    location   	(1915, 1422)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	390
+			    label      	"#1 Catalina.start()")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	450
+			height     	144)
+		    (object SelfMessView "" @269
+			location   	(16, 2416)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @270
+			    Parent_View 	@269
+			    location   	(1644, 2498)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE05780138"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	854
+			    justify    	0
+			    label      	"fireLifecycleEvent(BEFORE_START_EVENT)"
+			    pctDist    	-3.020000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@221
+			supplier   	@221
+			Focus_Src  	@222
+			Focus_Entry 	@223
+			origin     	(2096, 2416)
+			terminus   	(2246, 2416)
+			ordinal    	21)
+		    (object SelfMessView "" @271
+			location   	(16, 2480)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @272
+			    Parent_View 	@271
+			    location   	(1705, 2582)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE05A80398"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	658
+			    justify    	0
+			    label      	"fireLifecycleEvent(START_EVENT)"
+			    pctDist    	-2.613333
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@221
+			supplier   	@221
+			Focus_Src  	@222
+			Focus_Entry 	@224
+			origin     	(2096, 2480)
+			terminus   	(2246, 2480)
+			ordinal    	22)
+		    (object SelfMessView "" @273
+			location   	(16, 2560)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @274
+			    Parent_View 	@273
+			    location   	(1737, 2423)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE05BA0196"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	658
+			    justify    	0
+			    label      	"fireLifecycleEvent(AFTER_EVENT)"
+			    pctDist    	-2.393333
+			    height     	138
+			    orientation 	0)
+			line_color 	3342489
+			client     	@221
+			supplier   	@221
+			Focus_Src  	@222
+			Focus_Entry 	@225
+			origin     	(2096, 2560)
+			terminus   	(2246, 2560)
+			ordinal    	23)
+		    (object NoteView @275
+			location   	(960, 1680)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@275
+			    location   	(635, 1571)
+			    fill_color 	13434879
+			    nlines     	4
+			    max_width  	615
+			    label      	"All StandardX will fire these events.")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	675
+			height     	231)
+		    (object AttachView "" @276
+			stereotype 	TRUE
+			line_color 	3342489
+			client     	@275
+			supplier   	@272
+			line_style 	0)
+		    (object AttachView "" @277
+			stereotype 	TRUE
+			line_color 	3342489
+			client     	@275
+			supplier   	@270
+			line_style 	0)
+		    (object AttachView "" @278
+			stereotype 	TRUE
+			line_color 	3342489
+			client     	@275
+			supplier   	@274
+			line_style 	0)))
+	    (object InteractionDiagram "4. catalina_start_2"
+		mechanism_ref 	@85
+		quid       	"3DFE050900BF"
+		title      	"4. catalina_start_2"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	1087
+		items      	(list diagram_item_list
+		    (object InterObjView "StandardHost" @279
+			location   	(208, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@279
+			    location   	(208, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardHost")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE0538017B"
+			width      	300
+			height     	2114
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @280
+			    location   	(208, 384)
+			    line_color 	3342489
+			    InterObjView 	@279
+			    height     	1864
+			    y_coord    	1804
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @281
+			    location   	(208, 384)
+			    line_color 	3342489
+			    InterObjView 	@279
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @282
+			    location   	(208, 1088)
+			    line_color 	3342489
+			    InterObjView 	@279
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @283
+			    location   	(208, 1616)
+			    line_color 	3342489
+			    InterObjView 	@279
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "HostConfig" @284
+			location   	(544, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@284
+			    location   	(544, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"HostConfig")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE06A60131"
+			width      	300
+			height     	2114
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @285
+			    location   	(544, 512)
+			    line_color 	3342489
+			    InterObjView 	@284
+			    height     	696
+			    y_coord    	636
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @286
+			    location   	(544, 576)
+			    line_color 	3342489
+			    InterObjView 	@284
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @287
+			    location   	(544, 688)
+			    line_color 	3342489
+			    InterObjView 	@284
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @288
+			    location   	(544, 784)
+			    line_color 	3342489
+			    InterObjView 	@284
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @289
+			    location   	(544, 896)
+			    line_color 	3342489
+			    InterObjView 	@284
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @290
+			    location   	(544, 1008)
+			    line_color 	3342489
+			    InterObjView 	@284
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @291
+			    location   	(544, 1536)
+			    line_color 	3342489
+			    InterObjView 	@284
+			    height     	200
+			    y_coord    	140
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @292
+			    location   	(544, 1536)
+			    line_color 	3342489
+			    InterObjView 	@284
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardHostDeployer" @293
+			location   	(944, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@293
+			    location   	(944, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	426
+			    justify    	0
+			    label      	"StandardHostDeployer")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE079A0055"
+			width      	444
+			height     	2114
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @294
+			    location   	(944, 1280)
+			    line_color 	3342489
+			    InterObjView 	@293
+			    height     	824
+			    y_coord    	764
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @295
+			    location   	(944, 2128)
+			    line_color 	3342489
+			    InterObjView 	@293
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "Digester" @296
+			location   	(1328, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@296
+			    location   	(1328, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Digester")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE07C9034C"
+			width      	300
+			height     	2114
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @297
+			    location   	(1328, 1280)
+			    line_color 	3342489
+			    InterObjView 	@296
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @298
+			    location   	(1328, 1488)
+			    line_color 	3342489
+			    InterObjView 	@296
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @299
+			    location   	(1328, 1984)
+			    line_color 	3342489
+			    InterObjView 	@296
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "ContextRuleSet" @300
+			location   	(1648, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@300
+			    location   	(1648, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"ContextRuleSet")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE0834016F"
+			width      	300
+			height     	2114
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @301
+			    location   	(1648, 1408)
+			    line_color 	3342489
+			    InterObjView 	@300
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @302
+			    location   	(1648, 1888)
+			    line_color 	3342489
+			    InterObjView 	@300
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "NamingRuleSet" @303
+			location   	(1968, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@303
+			    location   	(1968, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"NamingRuleSet")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE08D00173"
+			width      	300
+			height     	2114
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @304
+			    location   	(1968, 1792)
+			    line_color 	3342489
+			    InterObjView 	@303
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object SelfMessView "" @305
+			location   	(0, 384)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @306
+			    Parent_View 	@305
+			    location   	(555, 342)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE066C0341"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	651
+			    justify    	0
+			    label      	"fireLifecycleEvent(START_EVENT)"
+			    pctDist    	2.206667
+			    height     	43
+			    orientation 	0)
+			line_color 	3342489
+			client     	@279
+			supplier   	@279
+			Focus_Src  	@280
+			Focus_Entry 	@281
+			origin     	(224, 384)
+			terminus   	(374, 384)
+			ordinal    	0)
+		    (object InterMessView "" @307
+			location   	(384, 512)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @308
+			    Parent_View 	@307
+			    location   	(486, 468)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE06D20294"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	507
+			    justify    	0
+			    label      	"interested[i].lifecycleEvent()"
+			    pctDist    	0.865574
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@279
+			supplier   	@284
+			Focus_Src  	@280
+			Focus_Entry 	@285
+			origin     	(223, 512)
+			terminus   	(528, 512)
+			ordinal    	1)
+		    (object SelfMessView "" @309
+			location   	(16, 576)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @310
+			    Parent_View 	@309
+			    location   	(713, 537)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE06E9028D"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	297
+			    justify    	0
+			    label      	"setDeployXML()"
+			    pctDist    	1.026667
+			    height     	40
+			    orientation 	0)
+			line_color 	3342489
+			client     	@284
+			supplier   	@284
+			Focus_Src  	@285
+			Focus_Entry 	@286
+			origin     	(560, 576)
+			terminus   	(710, 576)
+			ordinal    	2)
+		    (object SelfMessView "" @311
+			location   	(16, 688)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @312
+			    Parent_View 	@311
+			    location   	(714, 645)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE06F300FF"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	288
+			    justify    	0
+			    label      	"setLiveDeploy()"
+			    pctDist    	1.033333
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@284
+			supplier   	@284
+			Focus_Src  	@285
+			Focus_Entry 	@287
+			origin     	(560, 688)
+			terminus   	(710, 688)
+			ordinal    	3)
+		    (object SelfMessView "" @313
+			location   	(16, 784)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @314
+			    Parent_View 	@313
+			    location   	(732, 756)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE06FB00D9"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	326
+			    justify    	0
+			    label      	"setUnpacksWar()"
+			    pctDist    	1.153333
+			    height     	29
+			    orientation 	0)
+			line_color 	3342489
+			client     	@284
+			supplier   	@284
+			Focus_Src  	@285
+			Focus_Entry 	@288
+			origin     	(560, 784)
+			terminus   	(710, 784)
+			ordinal    	4)
+		    (object SelfMessView "" @315
+			location   	(16, 896)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @316
+			    Parent_View 	@315
+			    location   	(747, 868)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE070C0015"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	350
+			    justify    	0
+			    label      	"setXMLValidation()"
+			    pctDist    	1.246667
+			    height     	29
+			    orientation 	0)
+			line_color 	3342489
+			client     	@284
+			supplier   	@284
+			Focus_Src  	@285
+			Focus_Entry 	@289
+			origin     	(560, 896)
+			terminus   	(710, 896)
+			ordinal    	5)
+		    (object SelfMessView "" @317
+			location   	(16, 1008)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @318
+			    Parent_View 	@317
+			    location   	(762, 980)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE073B0031"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	359
+			    justify    	0
+			    label      	"deployDescriptors()"
+			    pctDist    	1.346667
+			    height     	29
+			    orientation 	0)
+			line_color 	3342489
+			client     	@284
+			supplier   	@284
+			Focus_Src  	@285
+			Focus_Entry 	@290
+			origin     	(560, 1008)
+			terminus   	(710, 1008)
+			ordinal    	6)
+		    (object InterMessView "" @319
+			location   	(16, 1088)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @320
+			    Parent_View 	@319
+			    location   	(376, 1044)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE078B03BB"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	136
+			    justify    	0
+			    label      	"install()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@284
+			supplier   	@279
+			Focus_Src  	@285
+			Focus_Entry 	@282
+			origin     	(528, 1088)
+			terminus   	(224, 1088)
+			ordinal    	7)
+		    (object InterMessView "" @321
+			location   	(576, 1280)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @322
+			    Parent_View 	@321
+			    location   	(575, 1236)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE07B100BE"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	136
+			    justify    	0
+			    label      	"install()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@279
+			supplier   	@293
+			Focus_Src  	@280
+			Focus_Entry 	@294
+			origin     	(223, 1280)
+			terminus   	(928, 1280)
+			ordinal    	8)
+		    (object InterMessView "" @323
+			location   	(1152, 1280)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @324
+			    Parent_View 	@323
+			    location   	(1135, 1236)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE07D200ED"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	144
+			    justify    	0
+			    label      	"create()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@293
+			supplier   	@296
+			Focus_Src  	@294
+			Focus_Entry 	@297
+			origin     	(959, 1280)
+			terminus   	(1312, 1280)
+			ordinal    	9)
+		    (object InterMessView "" @325
+			location   	(1136, 1984)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @326
+			    Parent_View 	@325
+			    location   	(1135, 1940)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE07D603D7"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	136
+			    justify    	0
+			    label      	"parse()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@293
+			supplier   	@296
+			Focus_Src  	@294
+			Focus_Entry 	@299
+			origin     	(959, 1984)
+			terminus   	(1312, 1984)
+			ordinal    	16)
+		    (object InterMessView "" @327
+			location   	(1296, 1408)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @328
+			    Parent_View 	@327
+			    location   	(1295, 1364)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE08DA029B"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	106
+			    justify    	0
+			    label      	"new()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@293
+			supplier   	@300
+			Focus_Src  	@294
+			Focus_Entry 	@301
+			origin     	(959, 1408)
+			terminus   	(1632, 1408)
+			ordinal    	10)
+		    (object InterMessView "" @329
+			location   	(1456, 1792)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @330
+			    Parent_View 	@329
+			    location   	(1455, 1748)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE08E00091"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	106
+			    justify    	0
+			    label      	"new()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@293
+			supplier   	@303
+			Focus_Src  	@294
+			Focus_Entry 	@304
+			origin     	(959, 1792)
+			terminus   	(1952, 1792)
+			ordinal    	14)
+		    (object InterMessView "" @331
+			location   	(16, 1488)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @332
+			    Parent_View 	@331
+			    location   	(1182, 1445)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE08FA003D"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	387
+			    justify    	0
+			    label      	"add(ContextRuleSet)"
+			    pctDist    	0.631728
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@293
+			supplier   	@296
+			Focus_Src  	@294
+			Focus_Entry 	@298
+			origin     	(959, 1488)
+			terminus   	(1312, 1488)
+			ordinal    	11)
+		    (object InterMessView "" @333
+			location   	(1296, 1888)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @334
+			    Parent_View 	@333
+			    location   	(1295, 1844)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE0907015F"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	416
+			    justify    	0
+			    label      	"add(NamingRuleSet())"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@293
+			supplier   	@300
+			Focus_Src  	@294
+			Focus_Entry 	@302
+			origin     	(959, 1888)
+			terminus   	(1632, 1888)
+			ordinal    	15)
+		    (object NoteView @335
+			location   	(2096, 2384)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@335
+			    location   	(1893, 2315)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	371
+			    label      	"#2 Catalina.start()")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	431
+			height     	150)
+		    (object SelfMessView "" @336
+			location   	(16, 1536)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @337
+			    Parent_View 	@336
+			    location   	(697, 1493)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE131F0327"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	244
+			    justify    	0
+			    label      	"deployApps()"
+			    pctDist    	0.913333
+			    height     	43
+			    orientation 	0)
+			line_color 	3342489
+			client     	@284
+			supplier   	@284
+			Focus_Src  	@291
+			Focus_Entry 	@292
+			origin     	(560, 1536)
+			terminus   	(710, 1536)
+			ordinal    	12)
+		    (object InterMessView "" @338
+			location   	(16, 1616)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @339
+			    Parent_View 	@338
+			    location   	(376, 1572)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE132D0309"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	136
+			    justify    	0
+			    label      	"install()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@284
+			supplier   	@279
+			Focus_Src  	@291
+			Focus_Entry 	@283
+			origin     	(528, 1616)
+			terminus   	(224, 1616)
+			ordinal    	13)
+		    (object InterMessView "" @340
+			location   	(576, 2128)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @341
+			    Parent_View 	@340
+			    location   	(575, 2084)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE133A036C"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	463
+			    justify    	0
+			    label      	"install() // same as above"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@279
+			supplier   	@293
+			Focus_Src  	@280
+			Focus_Entry 	@295
+			origin     	(223, 2128)
+			terminus   	(928, 2128)
+			ordinal    	17)))
+	    (object InteractionDiagram "5. catalina_start_3"
+		mechanism_ref 	@86
+		quid       	"3DFE094A0346"
+		title      	"5. catalina_start_3"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	0
+		items      	(list diagram_item_list
+		    (object InterObjView "Digester" @342
+			location   	(176, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@342
+			    location   	(176, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Digester")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE095A0371"
+			width      	300
+			height     	1214
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @343
+			    location   	(176, 352)
+			    line_color 	3342489
+			    InterObjView 	@342
+			    height     	996
+			    y_coord    	936
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @344
+			    location   	(176, 352)
+			    line_color 	3342489
+			    InterObjView 	@342
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @345
+			    location   	(176, 448)
+			    line_color 	3342489
+			    InterObjView 	@342
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "Rule" @346
+			location   	(480, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@346
+			    location   	(480, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Rule")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE0E7400D0"
+			width      	300
+			height     	1214
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @347
+			    location   	(480, 560)
+			    line_color 	3342489
+			    InterObjView 	@346
+			    height     	728
+			    y_coord    	668
+			    Nested     	FALSE))
+		    (object InterObjView "StandardContext" @348
+			location   	(816, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@348
+			    location   	(816, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	332
+			    justify    	0
+			    label      	"StandardContext")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE0FC502A1"
+			width      	350
+			height     	1214
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @349
+			    location   	(816, 592)
+			    line_color 	3342489
+			    InterObjView 	@348
+			    height     	264
+			    y_coord    	204
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @350
+			    location   	(816, 1008)
+			    line_color 	3342489
+			    InterObjView 	@348
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "StandardPipeline" @351
+			location   	(1184, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@351
+			    location   	(1184, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	363
+			    justify    	0
+			    label      	"StandardPipeline")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE112F003F"
+			width      	381
+			height     	1214
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @352
+			    location   	(1184, 736)
+			    line_color 	3342489
+			    InterObjView 	@351
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "StandardContextValve" @353
+			location   	(1552, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@353
+			    location   	(1552, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	300
+			    justify    	0
+			    label      	"StandardContextValve")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE110D0375"
+			width      	318
+			height     	1214
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @354
+			    location   	(1552, 624)
+			    line_color 	3342489
+			    InterObjView 	@353
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "SetPropertiesRule" @355
+			location   	(1920, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@355
+			    location   	(1920, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	363
+			    justify    	0
+			    label      	"SetPropertiesRule")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE100303A4"
+			width      	381
+			height     	1214
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @356
+			    location   	(1920, 928)
+			    line_color 	3342489
+			    InterObjView 	@355
+			    height     	200
+			    y_coord    	140
+			    Nested     	FALSE))
+		    (object InterObjView "SetNextRule" @357
+			location   	(2272, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@357
+			    location   	(2272, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"SetNextRule")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE12690267"
+			width      	300
+			height     	1214
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @358
+			    location   	(2272, 1168)
+			    line_color 	3342489
+			    InterObjView 	@357
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object SelfMessView "" @359
+			location   	(0, 352)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @360
+			    Parent_View 	@359
+			    location   	(267, 308)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE0E7801DB"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	108
+			    justify    	0
+			    label      	"parse"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@342
+			supplier   	@342
+			Focus_Src  	@343
+			Focus_Entry 	@344
+			origin     	(192, 352)
+			terminus   	(342, 352)
+			ordinal    	0)
+		    (object SelfMessView "" @361
+			location   	(16, 448)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @362
+			    Parent_View 	@361
+			    location   	(345, 420)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE0F2F03D2"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	267
+			    justify    	0
+			    label      	"startElement()"
+			    pctDist    	1.020000
+			    height     	28
+			    orientation 	0)
+			line_color 	3342489
+			client     	@342
+			supplier   	@342
+			Focus_Src  	@343
+			Focus_Entry 	@345
+			origin     	(192, 448)
+			terminus   	(342, 448)
+			ordinal    	1)
+		    (object InterMessView "" @363
+			location   	(336, 560)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @364
+			    Parent_View 	@363
+			    location   	(327, 516)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE0F400214"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	132
+			    justify    	0
+			    label      	"begin()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@342
+			supplier   	@346
+			Focus_Src  	@343
+			Focus_Entry 	@347
+			origin     	(191, 560)
+			terminus   	(464, 560)
+			ordinal    	2)
+		    (object InterMessView "" @365
+			location   	(16, 592)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @366
+			    Parent_View 	@365
+			    location   	(647, 548)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE0FD30266"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	265
+			    justify    	0
+			    label      	"newInstance()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@346
+			supplier   	@348
+			Focus_Src  	@347
+			Focus_Entry 	@349
+			origin     	(495, 592)
+			terminus   	(800, 592)
+			ordinal    	3)
+		    (object InterMessView "" @367
+			location   	(864, 928)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @368
+			    Parent_View 	@367
+			    location   	(1199, 884)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE102002E9"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	132
+			    justify    	0
+			    label      	"begin()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@346
+			supplier   	@355
+			Focus_Src  	@347
+			Focus_Entry 	@356
+			origin     	(495, 928)
+			terminus   	(1904, 928)
+			ordinal    	6)
+		    (object InterMessView "" @369
+			location   	(1008, 736)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @370
+			    Parent_View 	@369
+			    location   	(1139, 693)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE114A0193"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	610
+			    justify    	0
+			    label      	"setBasic(StandardContextValve)"
+			    pctDist    	0.915014
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@348
+			supplier   	@351
+			Focus_Src  	@349
+			Focus_Entry 	@352
+			origin     	(831, 736)
+			terminus   	(1168, 736)
+			ordinal    	5)
+		    (object InterMessView "" @371
+			location   	(16, 624)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @372
+			    Parent_View 	@371
+			    location   	(1183, 580)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE115E001F"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	106
+			    justify    	0
+			    label      	"new()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@348
+			supplier   	@353
+			Focus_Src  	@349
+			Focus_Entry 	@354
+			origin     	(831, 624)
+			terminus   	(1536, 624)
+			ordinal    	4)
+		    (object InterMessView "" @373
+			location   	(1440, 1008)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @374
+			    Parent_View 	@373
+			    location   	(1368, 964)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE11D50391"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	1190
+			    justify    	0
+			    label      	"//Using BeanUtil, set the object properties (from ex: admin.xml)"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@355
+			supplier   	@348
+			Focus_Src  	@356
+			Focus_Entry 	@350
+			origin     	(1904, 1008)
+			terminus   	(832, 1008)
+			ordinal    	7)
+		    (object InterMessView "" @375
+			location   	(1392, 1168)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @376
+			    Parent_View 	@375
+			    location   	(1375, 1124)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE128501C8"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	99
+			    justify    	0
+			    label      	"end()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@346
+			supplier   	@357
+			Focus_Src  	@347
+			Focus_Entry 	@358
+			origin     	(495, 1168)
+			terminus   	(2256, 1168)
+			ordinal    	8)
+		    (object NoteView @377
+			location   	(1216, 80)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@377
+			    location   	(900, 15)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	596
+			    label      	"HostConfig.deployDescriptor()")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	656
+			height     	143)
+		    (object NoteView @378
+			location   	(2128, 1888)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@378
+			    location   	(1947, 1822)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	327
+			    label      	"#3 Catalina.start()")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	387
+			height     	144)))
+	    (object InteractionDiagram "6. catalina_start_4"
+		mechanism_ref 	@87
+		quid       	"3DFE13890008"
+		title      	"6. catalina_start_4"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	1818
+		items      	(list diagram_item_list
+		    (object InterObjView "Digester" @379
+			location   	(176, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@379
+			    location   	(176, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Digester")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE13960364"
+			width      	300
+			height     	2446
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @380
+			    location   	(176, 336)
+			    line_color 	3342489
+			    InterObjView 	@379
+			    height     	1228
+			    y_coord    	1168
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @381
+			    location   	(176, 336)
+			    line_color 	3342489
+			    InterObjView 	@379
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @382
+			    location   	(176, 480)
+			    line_color 	3342489
+			    InterObjView 	@379
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @383
+			    location   	(176, 1616)
+			    line_color 	3342489
+			    InterObjView 	@379
+			    height     	580
+			    y_coord    	520
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @384
+			    location   	(176, 1728)
+			    line_color 	3342489
+			    InterObjView 	@379
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardHostDeployer" @385
+			location   	(480, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@385
+			    location   	(480, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	301
+			    justify    	0
+			    label      	"StandardHostDeployer")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE1D8A02DC"
+			width      	319
+			height     	2446
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @386
+			    location   	(480, 576)
+			    line_color 	3342489
+			    InterObjView 	@385
+			    height     	928
+			    y_coord    	868
+			    Nested     	FALSE))
+		    (object InterObjView "StandardHost" @387
+			location   	(800, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@387
+			    location   	(800, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardHost")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE1DF20141"
+			width      	300
+			height     	2446
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @388
+			    location   	(800, 592)
+			    line_color 	3342489
+			    InterObjView 	@387
+			    height     	852
+			    y_coord    	792
+			    Nested     	FALSE))
+		    (object InterObjView "StandardContext" @389
+			location   	(1120, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@389
+			    location   	(1120, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	295
+			    justify    	0
+			    label      	"StandardContext")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE196D00D9"
+			width      	313
+			height     	2446
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @390
+			    location   	(1120, 624)
+			    line_color 	3342489
+			    InterObjView 	@389
+			    height     	760
+			    y_coord    	700
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @391
+			    location   	(1120, 800)
+			    line_color 	3342489
+			    InterObjView 	@389
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @392
+			    location   	(1120, 976)
+			    line_color 	3342489
+			    InterObjView 	@389
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @393
+			    location   	(1120, 1072)
+			    line_color 	3342489
+			    InterObjView 	@389
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "WebappLoader" @394
+			location   	(1440, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@394
+			    location   	(1440, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	295
+			    justify    	0
+			    label      	"WebappLoader")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE1FFA0347"
+			width      	313
+			height     	2446
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @395
+			    location   	(1440, 640)
+			    line_color 	3342489
+			    InterObjView 	@394
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "StandardManager" @396
+			location   	(1760, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@396
+			    location   	(1760, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	301
+			    justify    	0
+			    label      	"StandardManager")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE201F0105"
+			width      	319
+			height     	2446
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @397
+			    location   	(1760, 832)
+			    line_color 	3342489
+			    InterObjView 	@396
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @398
+			    location   	(1760, 1264)
+			    line_color 	3342489
+			    InterObjView 	@396
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterObjView "ContextConfig" @399
+			location   	(1952, 352)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@399
+			    location   	(1952, 352)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"ContextConfig")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE2087028C"
+			width      	300
+			height     	2318
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @400
+			    location   	(1952, 412)
+			    InterObjView 	@399
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @401
+			    location   	(1952, 1136)
+			    line_color 	3342489
+			    InterObjView 	@399
+			    height     	1444
+			    y_coord    	1384
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @402
+			    location   	(1952, 1264)
+			    line_color 	3342489
+			    InterObjView 	@399
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @403
+			    location   	(1952, 1456)
+			    line_color 	3342489
+			    InterObjView 	@399
+			    height     	1070
+			    y_coord    	1010
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @404
+			    location   	(1952, 1568)
+			    line_color 	3342489
+			    InterObjView 	@399
+			    height     	952
+			    y_coord    	892
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @405
+			    location   	(1952, 1984)
+			    line_color 	3342489
+			    InterObjView 	@399
+			    height     	152
+			    y_coord    	92
+			    Nested     	TRUE))
+		    (object SelfMessView "" @406
+			location   	(16, 336)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @407
+			    Parent_View 	@406
+			    location   	(267, 292)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE19AE0065"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	108
+			    justify    	0
+			    label      	"parse"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@379
+			supplier   	@379
+			Focus_Src  	@380
+			Focus_Entry 	@381
+			origin     	(192, 336)
+			terminus   	(342, 336)
+			ordinal    	0)
+		    (object SelfMessView "" @408
+			location   	(16, 480)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @409
+			    Parent_View 	@408
+			    location   	(328, 437)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE19B102E9"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	267
+			    justify    	0
+			    label      	"startElement()"
+			    pctDist    	0.906667
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@379
+			supplier   	@379
+			Focus_Src  	@380
+			Focus_Entry 	@382
+			origin     	(192, 480)
+			terminus   	(342, 480)
+			ordinal    	1)
+		    (object InterMessView "" @410
+			location   	(16, 576)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @411
+			    Parent_View 	@410
+			    location   	(327, 552)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE1DFB0022"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	165
+			    justify    	0
+			    label      	"addChild"
+			    pctDist    	0.498645
+			    height     	25
+			    orientation 	0)
+			line_color 	3342489
+			client     	@379
+			supplier   	@385
+			Focus_Src  	@380
+			Focus_Entry 	@386
+			origin     	(191, 576)
+			terminus   	(464, 576)
+			ordinal    	2)
+		    (object InterMessView "" @412
+			location   	(16, 592)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @413
+			    Parent_View 	@412
+			    location   	(639, 548)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE1FB60277"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	165
+			    justify    	0
+			    label      	"addChild"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@385
+			supplier   	@387
+			Focus_Src  	@386
+			Focus_Entry 	@388
+			origin     	(495, 592)
+			terminus   	(784, 592)
+			ordinal    	3)
+		    (object InterMessView "" @414
+			location   	(16, 624)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @415
+			    Parent_View 	@414
+			    location   	(959, 580)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE1FC40228"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	110
+			    justify    	0
+			    label      	"start()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@387
+			supplier   	@389
+			Focus_Src  	@388
+			Focus_Entry 	@390
+			origin     	(815, 624)
+			terminus   	(1104, 624)
+			ordinal    	4)
+		    (object InterMessView "" @416
+			location   	(16, 640)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @417
+			    Parent_View 	@416
+			    location   	(1279, 596)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE200603BE"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	82
+			    justify    	0
+			    label      	"new"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@389
+			supplier   	@394
+			Focus_Src  	@390
+			Focus_Entry 	@395
+			origin     	(1135, 640)
+			terminus   	(1424, 640)
+			ordinal    	5)
+		    (object SelfMessView "" @418
+			location   	(16, 800)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @419
+			    Parent_View 	@418
+			    location   	(1224, 756)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE200C029A"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	186
+			    justify    	0
+			    label      	"setLoader"
+			    pctDist    	0.593333
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@389
+			supplier   	@389
+			Focus_Src  	@390
+			Focus_Entry 	@391
+			origin     	(1136, 800)
+			terminus   	(1286, 800)
+			ordinal    	6)
+		    (object InterMessView "" @420
+			location   	(16, 832)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @421
+			    Parent_View 	@420
+			    location   	(1439, 788)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE202C0250"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	82
+			    justify    	0
+			    label      	"new"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@389
+			supplier   	@396
+			Focus_Src  	@390
+			Focus_Entry 	@397
+			origin     	(1135, 832)
+			terminus   	(1744, 832)
+			ordinal    	7)
+		    (object SelfMessView "" @422
+			location   	(16, 976)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @423
+			    Parent_View 	@422
+			    location   	(1260, 933)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE2032001C"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	221
+			    justify    	0
+			    label      	"setManager"
+			    pctDist    	0.833333
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@389
+			supplier   	@389
+			Focus_Src  	@390
+			Focus_Entry 	@392
+			origin     	(1136, 976)
+			terminus   	(1286, 976)
+			ordinal    	8)
+		    (object SelfMessView "" @424
+			location   	(16, 1072)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @425
+			    Parent_View 	@424
+			    location   	(1481, 1043)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE205B01A2"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	658
+			    justify    	0
+			    label      	"fireLifecycleEvent(START_EVENT)"
+			    pctDist    	2.306667
+			    height     	30
+			    orientation 	0)
+			line_color 	3342489
+			client     	@389
+			supplier   	@389
+			Focus_Src  	@390
+			Focus_Entry 	@393
+			origin     	(1136, 1072)
+			terminus   	(1286, 1072)
+			ordinal    	9)
+		    (object InterMessView "" @426
+			location   	(16, 1136)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @427
+			    Parent_View 	@426
+			    location   	(1535, 1092)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE20960003"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	745
+			    justify    	0
+			    label      	" // Notify interested LifecycleListeners"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@389
+			supplier   	@399
+			Focus_Src  	@390
+			Focus_Entry 	@401
+			origin     	(1135, 1136)
+			terminus   	(1936, 1136)
+			ordinal    	10)
+		    (object SelfMessView "" @428
+			location   	(16, 1264)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @429
+			    Parent_View 	@428
+			    location   	(2043, 1220)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE20CF018C"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	110
+			    justify    	0
+			    label      	"start()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@399
+			supplier   	@399
+			Focus_Src  	@401
+			Focus_Entry 	@402
+			origin     	(1968, 1264)
+			terminus   	(2118, 1264)
+			ordinal    	11)
+		    (object SelfMessView "" @430
+			location   	(16, 1456)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @431
+			    Parent_View 	@430
+			    location   	(2027, 1413)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE20E303E2"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	275
+			    justify    	0
+			    label      	"defaultConfig()"
+			    pctDist    	0.393333
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@399
+			supplier   	@399
+			Focus_Src  	@401
+			Focus_Entry 	@403
+			origin     	(1968, 1456)
+			terminus   	(2118, 1456)
+			ordinal    	13)
+		    (object SelfMessView "" @432
+			location   	(16, 1568)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @433
+			    Parent_View 	@432
+			    location   	(2043, 1524)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE211D01A1"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	349
+			    justify    	0
+			    label      	"applicationConfig()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@399
+			supplier   	@399
+			Focus_Src  	@401
+			Focus_Entry 	@404
+			origin     	(1968, 1568)
+			terminus   	(2118, 1568)
+			ordinal    	14)
+		    (object InterMessView "" @434
+			location   	(1664, 1264)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @435
+			    Parent_View 	@434
+			    location   	(1439, 1220)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE20B600E5"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	110
+			    justify    	0
+			    label      	"start()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@389
+			supplier   	@396
+			Focus_Src  	@390
+			Focus_Entry 	@398
+			origin     	(1135, 1264)
+			terminus   	(1744, 1264)
+			ordinal    	12)
+		    (object InterMessView "" @436
+			location   	(16, 1616)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @437
+			    Parent_View 	@436
+			    location   	(1064, 1572)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE21B60288"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	145
+			    justify    	0
+			    label      	"create()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@399
+			supplier   	@379
+			Focus_Src  	@404
+			Focus_Entry 	@383
+			origin     	(1936, 1616)
+			terminus   	(192, 1616)
+			ordinal    	15)
+		    (object SelfMessView "" @438
+			location   	(16, 1728)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @439
+			    Parent_View 	@438
+			    location   	(457, 1701)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE21BE021B"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	530
+			    justify    	0
+			    label      	"// Process web.xml * tld.xml"
+			    pctDist    	1.773333
+			    height     	28
+			    orientation 	0)
+			line_color 	3342489
+			client     	@379
+			supplier   	@379
+			Focus_Src  	@383
+			Focus_Entry 	@384
+			origin     	(192, 1728)
+			terminus   	(342, 1728)
+			ordinal    	16)
+		    (object InterObjView "StandardWrapper" @440
+			location   	(2208, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@440
+			    location   	(2208, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardWrapper")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE220C0122"
+			width      	300
+			height     	2446
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @441
+			    location   	(2208, 2016)
+			    line_color 	3342489
+			    InterObjView 	@440
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @442
+			    location   	(2208, 2176)
+			    line_color 	3342489
+			    InterObjView 	@440
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @443
+			    location   	(2208, 2288)
+			    line_color 	3342489
+			    InterObjView 	@440
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @444
+			    location   	(2208, 2400)
+			    line_color 	3342489
+			    InterObjView 	@440
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterMessView "" @445
+			location   	(16, 1984)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @446
+			    Parent_View 	@445
+			    location   	(1063, 1940)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE228B03BA"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	1478
+			    justify    	0
+			    label      	"createWarpper() // Invoked by a WebWrapperRule (not Directly by the Digester)"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@379
+			supplier   	@399
+			Focus_Src  	@383
+			Focus_Entry 	@405
+			origin     	(191, 1984)
+			terminus   	(1936, 1984)
+			ordinal    	17)
+		    (object InterMessView "" @447
+			location   	(16, 2016)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @448
+			    Parent_View 	@447
+			    location   	(2079, 1972)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE229A0004"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	82
+			    justify    	0
+			    label      	"new"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@399
+			supplier   	@440
+			Focus_Src  	@405
+			Focus_Entry 	@441
+			origin     	(1967, 2016)
+			terminus   	(2192, 2016)
+			ordinal    	18)
+		    (object InterMessView "" @449
+			location   	(16, 2176)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @450
+			    Parent_View 	@449
+			    location   	(2116, 2134)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE22A700C1"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	405
+			    justify    	0
+			    label      	"addInstanceListener()"
+			    pctDist    	0.662295
+			    height     	43
+			    orientation 	0)
+			line_color 	3342489
+			client     	@399
+			supplier   	@440
+			Focus_Src  	@404
+			Focus_Entry 	@442
+			origin     	(1967, 2176)
+			terminus   	(2192, 2176)
+			ordinal    	19)
+		    (object InterMessView "" @451
+			location   	(2496, 2288)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @452
+			    Parent_View 	@451
+			    location   	(2116, 2245)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE22C701CC"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	410
+			    justify    	0
+			    label      	"addLifecycleListener()"
+			    pctDist    	0.662295
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@399
+			supplier   	@440
+			Focus_Src  	@404
+			Focus_Entry 	@443
+			origin     	(1967, 2288)
+			terminus   	(2192, 2288)
+			ordinal    	20)
+		    (object InterMessView "" @453
+			location   	(16, 2400)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @454
+			    Parent_View 	@453
+			    location   	(2124, 2357)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE22E80364"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	428
+			    justify    	0
+			    label      	"addContainerListener()"
+			    pctDist    	0.701639
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@399
+			supplier   	@440
+			Focus_Src  	@404
+			Focus_Entry 	@444
+			origin     	(1967, 2400)
+			terminus   	(2192, 2400)
+			ordinal    	21)
+		    (object NoteView @455
+			location   	(1216, 80)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@455
+			    location   	(825, 14)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	746
+			    label      	"Deploy App.")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	806
+			height     	144)
+		    (object NoteView @456
+			location   	(2144, 2704)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@456
+			    location   	(1953, 2641)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	347
+			    label      	"#4 Catalina.start()")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	407
+			height     	138)))
+	    (object InteractionDiagram "1. catalina_request"
+		mechanism_ref 	@88
+		quid       	"3DFE3B5001C3"
+		title      	"1. catalina_request"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	0
+		items      	(list diagram_item_list
+		    (object InterObjView "ThreadPool" @457
+			location   	(176, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@457
+			    location   	(176, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"ThreadPool")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE402B02C5"
+			width      	300
+			height     	1276
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @458
+			    location   	(176, 384)
+			    line_color 	3342489
+			    InterObjView 	@457
+			    height     	304
+			    y_coord    	244
+			    Nested     	FALSE))
+		    (object InterObjView "TcpWorkerThread" @459
+			location   	(512, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@459
+			    location   	(512, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	332
+			    justify    	0
+			    label      	"TcpWorkerThread")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE403200F8"
+			width      	350
+			height     	1276
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @460
+			    location   	(512, 384)
+			    line_color 	3342489
+			    InterObjView 	@459
+			    height     	244
+			    y_coord    	184
+			    Nested     	FALSE))
+		    (object InterObjView "Http11Protocol" @461
+			location   	(848, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@461
+			    location   	(848, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"Http11Protocol")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE40750177"
+			width      	300
+			height     	1276
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @462
+			    location   	(848, 400)
+			    line_color 	3342489
+			    InterObjView 	@461
+			    height     	168
+			    y_coord    	108
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @463
+			    location   	(848, 448)
+			    line_color 	3342489
+			    InterObjView 	@461
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @464
+			    location   	(848, 592)
+			    line_color 	3342489
+			    InterObjView 	@461
+			    height     	120
+			    y_coord    	60
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @465
+			    location   	(848, 592)
+			    line_color 	3342489
+			    InterObjView 	@461
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @466
+			    location   	(848, 736)
+			    line_color 	3342489
+			    InterObjView 	@461
+			    height     	674
+			    y_coord    	614
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @467
+			    location   	(848, 736)
+			    line_color 	3342489
+			    InterObjView 	@461
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "CoyoteAdapter" @468
+			location   	(1168, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@468
+			    location   	(1168, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"CoyoteAdapter")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE410600DF"
+			width      	300
+			height     	1276
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @469
+			    location   	(1168, 848)
+			    line_color 	3342489
+			    InterObjView 	@468
+			    height     	502
+			    y_coord    	442
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @470
+			    location   	(1168, 944)
+			    line_color 	3342489
+			    InterObjView 	@468
+			    height     	352
+			    y_coord    	292
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @471
+			    location   	(1168, 944)
+			    line_color 	3342489
+			    InterObjView 	@468
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardEngine" @472
+			location   	(1520, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@472
+			    location   	(1520, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	363
+			    justify    	0
+			    label      	"StandardEngine")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE424B0349"
+			width      	381
+			height     	1276
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @473
+			    location   	(1520, 1008)
+			    line_color 	3342489
+			    InterObjView 	@472
+			    height     	228
+			    y_coord    	168
+			    Nested     	FALSE))
+		    (object InterObjView "StandardPipeline" @474
+			location   	(1872, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@474
+			    location   	(1872, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardPipeline")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE42900045"
+			width      	300
+			height     	1276
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @475
+			    location   	(1872, 1040)
+			    line_color 	3342489
+			    InterObjView 	@474
+			    height     	136
+			    y_coord    	76
+			    Nested     	FALSE))
+		    (object InterObjView "StandardValveContext" @476
+			location   	(2192, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@476
+			    location   	(2192, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardValveContext")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE42C002B1"
+			width      	300
+			height     	1276
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @477
+			    location   	(2192, 1056)
+			    line_color 	3342489
+			    InterObjView 	@476
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterMessView "" @478
+			location   	(16, 384)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @479
+			    Parent_View 	@478
+			    location   	(343, 340)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE40E701AE"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	112
+			    justify    	0
+			    label      	"runIt()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@457
+			supplier   	@459
+			Focus_Src  	@458
+			Focus_Entry 	@460
+			origin     	(191, 384)
+			terminus   	(496, 384)
+			ordinal    	0)
+		    (object InterMessView "" @480
+			location   	(16, 400)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @481
+			    Parent_View 	@480
+			    location   	(679, 356)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE40FC010E"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	359
+			    justify    	0
+			    label      	"processConnection"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@459
+			supplier   	@461
+			Focus_Src  	@460
+			Focus_Entry 	@462
+			origin     	(527, 400)
+			terminus   	(832, 400)
+			ordinal    	1)
+		    (object SelfMessView "" @482
+			location   	(16, 448)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @483
+			    Parent_View 	@482
+			    location   	(969, 405)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE4111029F"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	175
+			    justify    	0
+			    label      	"process()"
+			    pctDist    	0.706667
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@461
+			supplier   	@461
+			Focus_Src  	@462
+			Focus_Entry 	@463
+			origin     	(864, 448)
+			terminus   	(1014, 448)
+			ordinal    	2)
+		    (object SelfMessView "" @484
+			location   	(16, 592)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @485
+			    Parent_View 	@484
+			    location   	(1048, 549)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE415C0151"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	291
+			    justify    	0
+			    label      	"parseHeaders()"
+			    pctDist    	1.226667
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@461
+			supplier   	@461
+			Focus_Src  	@464
+			Focus_Entry 	@465
+			origin     	(864, 592)
+			terminus   	(1014, 592)
+			ordinal    	3)
+		    (object SelfMessView "" @486
+			location   	(16, 736)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @487
+			    Parent_View 	@486
+			    location   	(1052, 692)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE41A60161"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	328
+			    justify    	0
+			    label      	"prepareRequest()"
+			    pctDist    	1.253333
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@461
+			supplier   	@461
+			Focus_Src  	@466
+			Focus_Entry 	@467
+			origin     	(864, 736)
+			terminus   	(1014, 736)
+			ordinal    	4)
+		    (object InterMessView "" @488
+			location   	(992, 848)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @489
+			    Parent_View 	@488
+			    location   	(1007, 804)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE41D60107"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	162
+			    justify    	0
+			    label      	"service()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@461
+			supplier   	@468
+			Focus_Src  	@466
+			Focus_Entry 	@469
+			origin     	(863, 848)
+			terminus   	(1152, 848)
+			ordinal    	5)
+		    (object SelfMessView "" @490
+			location   	(16, 944)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @491
+			    Parent_View 	@490
+			    location   	(1372, 916)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE422C01F1"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	373
+			    justify    	0
+			    label      	"postParseRequest()"
+			    pctDist    	1.253333
+			    height     	28
+			    orientation 	0)
+			line_color 	3342489
+			client     	@468
+			supplier   	@468
+			Focus_Src  	@470
+			Focus_Entry 	@471
+			origin     	(1184, 944)
+			terminus   	(1334, 944)
+			ordinal    	6)
+		    (object InterMessView "" @492
+			location   	(1344, 1008)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @493
+			    Parent_View 	@492
+			    location   	(1343, 964)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE42800238"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	149
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@468
+			supplier   	@472
+			Focus_Src  	@470
+			Focus_Entry 	@473
+			origin     	(1183, 1008)
+			terminus   	(1504, 1008)
+			ordinal    	7)
+		    (object InterMessView "" @494
+			location   	(16, 1040)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @495
+			    Parent_View 	@494
+			    location   	(1695, 996)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE429A002D"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	149
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@472
+			supplier   	@474
+			Focus_Src  	@473
+			Focus_Entry 	@475
+			origin     	(1535, 1040)
+			terminus   	(1856, 1040)
+			ordinal    	8)
+		    (object InterMessView "" @496
+			location   	(16, 1056)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @497
+			    Parent_View 	@496
+			    location   	(2031, 1012)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE42CE0230"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	149
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@474
+			supplier   	@476
+			Focus_Src  	@475
+			Focus_Entry 	@477
+			origin     	(1887, 1056)
+			terminus   	(2176, 1056)
+			ordinal    	9)
+		    (object NoteView @498
+			location   	(2000, 2016)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@498
+			    location   	(1862, 1956)
+			    fill_color 	13434879
+			    nlines     	2
+			    max_width  	240
+			    label      	"See next diagram")
+			line_color 	3342489
+			fill_color 	13434879
+			width      	300
+			height     	132)
+		    (object AttachView "" @499
+			stereotype 	TRUE
+			line_color 	3342489
+			client     	@498
+			supplier   	@476
+			line_style 	0)))
+	    (object InteractionDiagram "2. catalina_request_2"
+		mechanism_ref 	@89
+		quid       	"3DFE42F7024C"
+		title      	"2. catalina_request_2"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	0
+		items      	(list diagram_item_list
+		    (object InterObjView "StandardContextValve" @500
+			location   	(224, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@500
+			    location   	(224, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	401
+			    justify    	0
+			    label      	"StandardContextValve")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE4307001E"
+			width      	419
+			height     	1678
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @501
+			    location   	(224, 384)
+			    line_color 	3342489
+			    InterObjView 	@500
+			    height     	386
+			    y_coord    	326
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @502
+			    location   	(224, 704)
+			    line_color 	3342489
+			    InterObjView 	@500
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @503
+			    location   	(224, 896)
+			    line_color 	3342489
+			    InterObjView 	@500
+			    height     	916
+			    y_coord    	856
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @504
+			    location   	(224, 1024)
+			    line_color 	3342489
+			    InterObjView 	@500
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @505
+			    location   	(224, 1280)
+			    line_color 	3342489
+			    InterObjView 	@500
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardEngineValve" @506
+			location   	(592, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@506
+			    location   	(592, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardEngineValve")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE432801F3"
+			width      	300
+			height     	1678
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @507
+			    location   	(592, 384)
+			    line_color 	3342489
+			    InterObjView 	@506
+			    height     	264
+			    y_coord    	204
+			    Nested     	FALSE))
+		    (object InterObjView "StandardHost" @508
+			location   	(912, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@508
+			    location   	(912, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardHost")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE436503BD"
+			width      	300
+			height     	1678
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @509
+			    location   	(912, 416)
+			    line_color 	3342489
+			    InterObjView 	@508
+			    height     	431
+			    y_coord    	371
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @510
+			    location   	(912, 528)
+			    line_color 	3342489
+			    InterObjView 	@508
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @511
+			    location   	(912, 704)
+			    line_color 	3342489
+			    InterObjView 	@508
+			    height     	120
+			    y_coord    	60
+			    Nested     	TRUE))
+		    (object InterObjView "ErrorReportValve" @512
+			location   	(1264, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@512
+			    location   	(1264, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	294
+			    justify    	0
+			    label      	"ErrorReportValve")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE438C028D"
+			width      	312
+			height     	1678
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @513
+			    location   	(1264, 896)
+			    line_color 	3342489
+			    InterObjView 	@512
+			    height     	248
+			    y_coord    	188
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @514
+			    location   	(1264, 944)
+			    line_color 	3342489
+			    InterObjView 	@512
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "ErrorDispatcherValve" @515
+			location   	(1584, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@515
+			    location   	(1584, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	295
+			    justify    	0
+			    label      	"ErrorDispatcherValve")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE451F01EC"
+			width      	313
+			height     	1678
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @516
+			    location   	(1584, 1168)
+			    line_color 	3342489
+			    InterObjView 	@515
+			    height     	232
+			    y_coord    	172
+			    Nested     	FALSE))
+		    (object InterObjView "StandardHostValve" @517
+			location   	(1904, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@517
+			    location   	(1904, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	295
+			    justify    	0
+			    label      	"StandardHostValve")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE47310130"
+			width      	313
+			height     	1678
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @518
+			    location   	(1904, 1472)
+			    line_color 	3342489
+			    InterObjView 	@517
+			    height     	280
+			    y_coord    	220
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @519
+			    location   	(1904, 1536)
+			    line_color 	3342489
+			    InterObjView 	@517
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardContext" @520
+			location   	(2224, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@520
+			    location   	(2224, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardContext")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE47C100F1"
+			width      	300
+			height     	1678
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @521
+			    location   	(2224, 1632)
+			    line_color 	3342489
+			    InterObjView 	@520
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object InterMessView "" @522
+			location   	(16, 384)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @523
+			    Parent_View 	@522
+			    location   	(407, 340)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE434C019B"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	146
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@500
+			supplier   	@506
+			Focus_Src  	@501
+			Focus_Entry 	@507
+			origin     	(239, 384)
+			terminus   	(576, 384)
+			ordinal    	0)
+		    (object InterMessView "" @524
+			location   	(16, 416)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @525
+			    Parent_View 	@524
+			    location   	(751, 372)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE436C009D"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	107
+			    justify    	0
+			    label      	"map()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@506
+			supplier   	@508
+			Focus_Src  	@507
+			Focus_Entry 	@509
+			origin     	(607, 416)
+			terminus   	(896, 416)
+			ordinal    	1)
+		    (object InterMessView "" @526
+			location   	(800, 528)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @527
+			    Parent_View 	@526
+			    location   	(751, 484)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE43830063"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	146
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@506
+			supplier   	@508
+			Focus_Src  	@507
+			Focus_Entry 	@510
+			origin     	(607, 528)
+			terminus   	(896, 528)
+			ordinal    	2)
+		    (object InterMessView "" @528
+			location   	(608, 704)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @529
+			    Parent_View 	@528
+			    location   	(568, 660)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE43B903BF"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	146
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@508
+			supplier   	@500
+			Focus_Src  	@511
+			Focus_Entry 	@502
+			origin     	(896, 704)
+			terminus   	(240, 704)
+			ordinal    	3)
+		    (object InterMessView "" @530
+			location   	(752, 896)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @531
+			    Parent_View 	@530
+			    location   	(743, 852)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE43C203A4"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	146
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@500
+			supplier   	@512
+			Focus_Src  	@503
+			Focus_Entry 	@513
+			origin     	(239, 896)
+			terminus   	(1248, 896)
+			ordinal    	4)
+		    (object SelfMessView "" @532
+			location   	(16, 944)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @533
+			    Parent_View 	@532
+			    location   	(1355, 900)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE442501B1"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	135
+			    justify    	0
+			    label      	"report()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@512
+			supplier   	@512
+			Focus_Src  	@513
+			Focus_Entry 	@514
+			origin     	(1280, 944)
+			terminus   	(1430, 944)
+			ordinal    	5)
+		    (object InterMessView "" @534
+			location   	(16, 1024)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @535
+			    Parent_View 	@534
+			    location   	(744, 980)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE46330293"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	230
+			    justify    	0
+			    label      	"invokeNext()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@512
+			supplier   	@500
+			Focus_Src  	@513
+			Focus_Entry 	@504
+			origin     	(1248, 1024)
+			terminus   	(240, 1024)
+			ordinal    	6)
+		    (object InterMessView "" @536
+			location   	(944, 1168)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @537
+			    Parent_View 	@536
+			    location   	(903, 1124)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE46E70026"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	146
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@500
+			supplier   	@515
+			Focus_Src  	@503
+			Focus_Entry 	@516
+			origin     	(239, 1168)
+			terminus   	(1568, 1168)
+			ordinal    	7)
+		    (object InterMessView "" @538
+			location   	(16, 1280)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @539
+			    Parent_View 	@538
+			    location   	(904, 1236)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE475D03A0"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	206
+			    justify    	0
+			    label      	"invokeNext"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@515
+			supplier   	@500
+			Focus_Src  	@516
+			Focus_Entry 	@505
+			origin     	(1568, 1280)
+			terminus   	(240, 1280)
+			ordinal    	8)
+		    (object InterMessView "" @540
+			location   	(1184, 1472)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @541
+			    Parent_View 	@540
+			    location   	(1063, 1428)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE476503CA"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	146
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@500
+			supplier   	@517
+			Focus_Src  	@503
+			Focus_Entry 	@518
+			origin     	(239, 1472)
+			terminus   	(1888, 1472)
+			ordinal    	9)
+		    (object SelfMessView "" @542
+			location   	(16, 1536)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @543
+			    Parent_View 	@542
+			    location   	(1995, 1492)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE47CD0167"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	295
+			    justify    	0
+			    label      	"map() //Context"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@517
+			supplier   	@517
+			Focus_Src  	@518
+			Focus_Entry 	@519
+			origin     	(1920, 1536)
+			terminus   	(2070, 1536)
+			ordinal    	10)
+		    (object InterMessView "" @544
+			location   	(16, 1632)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @545
+			    Parent_View 	@544
+			    location   	(2063, 1588)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE47D500B4"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	146
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@517
+			supplier   	@520
+			Focus_Src  	@518
+			Focus_Entry 	@521
+			origin     	(1919, 1632)
+			terminus   	(2208, 1632)
+			ordinal    	11)))
+	    (object InteractionDiagram "3. catalina_request_3"
+		mechanism_ref 	@90
+		quid       	"3DFE48A202AD"
+		title      	"3. catalina_request_3"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	612
+		origin_y   	938
+		items      	(list diagram_item_list
+		    (object InterObjView "StandardContext" @546
+			location   	(160, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@546
+			    location   	(160, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardContext")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE48B001D1"
+			width      	300
+			height     	2226
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @547
+			    location   	(160, 400)
+			    line_color 	3342489
+			    InterObjView 	@546
+			    height     	1960
+			    y_coord    	1900
+			    Nested     	FALSE))
+		    (object InterObjView "StandardPipeline" @548
+			location   	(480, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@548
+			    location   	(480, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardPipeline")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE48B80088"
+			width      	300
+			height     	2226
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @549
+			    location   	(480, 400)
+			    line_color 	3342489
+			    InterObjView 	@548
+			    height     	1900
+			    y_coord    	1840
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @550
+			    location   	(480, 1088)
+			    line_color 	3342489
+			    InterObjView 	@548
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardValveContext" @551
+			location   	(800, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@551
+			    location   	(800, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardValveContext")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE48D000DC"
+			width      	300
+			height     	2226
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @552
+			    location   	(800, 736)
+			    line_color 	3342489
+			    InterObjView 	@551
+			    height     	1510
+			    y_coord    	1450
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @553
+			    location   	(800, 1168)
+			    line_color 	3342489
+			    InterObjView 	@551
+			    height     	1072
+			    y_coord    	1012
+			    Nested     	TRUE))
+		    (object InterObjView "StandardContextValve" @554
+			location   	(1104, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@554
+			    location   	(1104, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardContextValve")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE490303A7"
+			width      	300
+			height     	2226
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @555
+			    location   	(1104, 800)
+			    line_color 	3342489
+			    InterObjView 	@554
+			    height     	468
+			    y_coord    	408
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @556
+			    location   	(1104, 848)
+			    line_color 	3342489
+			    InterObjView 	@554
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "StandardWrapper" @557
+			location   	(1424, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@557
+			    location   	(1424, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	288
+			    justify    	0
+			    label      	"StandardWrapper")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE49370351"
+			width      	306
+			height     	2226
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @558
+			    location   	(1424, 944)
+			    line_color 	3342489
+			    InterObjView 	@557
+			    height     	264
+			    y_coord    	204
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @559
+			    location   	(1424, 1520)
+			    line_color 	3342489
+			    InterObjView 	@557
+			    height     	340
+			    y_coord    	280
+			    Nested     	FALSE))
+		    (object InterObjView "StandardWrapperValve" @560
+			location   	(1744, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@560
+			    location   	(1744, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"StandardWrapperValve")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE49890056"
+			width      	300
+			height     	2226
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @561
+			    location   	(1744, 1440)
+			    line_color 	3342489
+			    InterObjView 	@560
+			    height     	740
+			    y_coord    	680
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @562
+			    location   	(1744, 1616)
+			    line_color 	3342489
+			    InterObjView 	@560
+			    height     	184
+			    y_coord    	124
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @563
+			    location   	(1744, 2000)
+			    line_color 	3342489
+			    InterObjView 	@560
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterObjView "ApplicationFilterChain" @564
+			location   	(2064, 224)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@564
+			    location   	(2064, 224)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"ApplicationFilterChain")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE4A1500B2"
+			width      	300
+			height     	2226
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @565
+			    location   	(2064, 1680)
+			    line_color 	3342489
+			    InterObjView 	@564
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @566
+			    location   	(2064, 1808)
+			    line_color 	3342489
+			    InterObjView 	@564
+			    height     	312
+			    y_coord    	252
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @567
+			    location   	(2064, 1872)
+			    line_color 	3342489
+			    InterObjView 	@564
+			    height     	60
+			    y_coord    	0
+			    Nested     	TRUE))
+		    (object InterMessView "" @568
+			location   	(336, 400)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @569
+			    Parent_View 	@568
+			    location   	(319, 356)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE48BE0268"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	147
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@546
+			supplier   	@548
+			Focus_Src  	@547
+			Focus_Entry 	@549
+			origin     	(175, 400)
+			terminus   	(464, 400)
+			ordinal    	0)
+		    (object InterMessView "" @570
+			location   	(16, 736)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @571
+			    Parent_View 	@570
+			    location   	(639, 692)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE48EA003A"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	147
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@548
+			supplier   	@551
+			Focus_Src  	@549
+			Focus_Entry 	@552
+			origin     	(495, 736)
+			terminus   	(784, 736)
+			ordinal    	1)
+		    (object InterMessView "" @572
+			location   	(16, 800)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @573
+			    Parent_View 	@572
+			    location   	(951, 756)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE491102D6"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	147
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@551
+			supplier   	@554
+			Focus_Src  	@552
+			Focus_Entry 	@555
+			origin     	(815, 800)
+			terminus   	(1088, 800)
+			ordinal    	2)
+		    (object SelfMessView "" @574
+			location   	(16, 848)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @575
+			    Parent_View 	@574
+			    location   	(1322, 821)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE492F033D"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	437
+			    justify    	0
+			    label      	"map //return Wrapper"
+			    pctDist    	1.346667
+			    height     	28
+			    orientation 	0)
+			line_color 	3342489
+			client     	@554
+			supplier   	@554
+			Focus_Src  	@555
+			Focus_Entry 	@556
+			origin     	(1120, 848)
+			terminus   	(1270, 848)
+			ordinal    	3)
+		    (object InterMessView "" @576
+			location   	(1264, 944)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @577
+			    Parent_View 	@576
+			    location   	(1262, 901)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE494A0151"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	147
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.498270
+			    height     	44
+			    orientation 	0)
+			line_color 	3342489
+			client     	@554
+			supplier   	@557
+			Focus_Src  	@555
+			Focus_Entry 	@558
+			origin     	(1119, 944)
+			terminus   	(1408, 944)
+			ordinal    	4)
+		    (object InterMessView "" @578
+			location   	(960, 1088)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @579
+			    Parent_View 	@578
+			    location   	(952, 1044)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE495F0288"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	147
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@557
+			supplier   	@548
+			Focus_Src  	@558
+			Focus_Entry 	@550
+			origin     	(1408, 1088)
+			terminus   	(496, 1088)
+			ordinal    	5)
+		    (object InterMessView "" @580
+			location   	(16, 1168)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @581
+			    Parent_View 	@580
+			    location   	(639, 1124)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE4976015D"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	147
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@548
+			supplier   	@551
+			Focus_Src  	@549
+			Focus_Entry 	@553
+			origin     	(495, 1168)
+			terminus   	(784, 1168)
+			ordinal    	6)
+		    (object InterMessView "" @582
+			location   	(1296, 1440)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @583
+			    Parent_View 	@582
+			    location   	(1271, 1396)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE4993023C"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	147
+			    justify    	0
+			    label      	"invoke()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@551
+			supplier   	@560
+			Focus_Src  	@553
+			Focus_Entry 	@561
+			origin     	(815, 1440)
+			terminus   	(1728, 1440)
+			ordinal    	7)
+		    (object InterMessView "" @584
+			location   	(1616, 1520)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @585
+			    Parent_View 	@584
+			    location   	(1584, 1476)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE49EC004F"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	175
+			    justify    	0
+			    label      	"allocate()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@560
+			supplier   	@557
+			Focus_Src  	@561
+			Focus_Entry 	@559
+			origin     	(1728, 1520)
+			terminus   	(1440, 1520)
+			ordinal    	8)
+		    (object InterMessView "" @586
+			location   	(1616, 1616)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @587
+			    Parent_View 	@586
+			    location   	(1583, 1572)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE4A200067"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	242
+			    justify    	0
+			    label      	"return servlet"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@557
+			supplier   	@560
+			Focus_Src  	@559
+			Focus_Entry 	@562
+			origin     	(1439, 1616)
+			terminus   	(1728, 1616)
+			ordinal    	9)
+		    (object InterMessView "" @588
+			location   	(1936, 1680)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @589
+			    Parent_View 	@588
+			    location   	(1937, 1636)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE4A29027E"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	343
+			    justify    	0
+			    label      	"createFilterChain()"
+			    pctDist    	0.619377
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@560
+			supplier   	@564
+			Focus_Src  	@562
+			Focus_Entry 	@565
+			origin     	(1759, 1680)
+			terminus   	(2048, 1680)
+			ordinal    	10)
+		    (object InterMessView "" @590
+			location   	(16, 1808)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @591
+			    Parent_View 	@590
+			    location   	(1902, 1764)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE4A490283"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	170
+			    justify    	0
+			    label      	"doFilter()"
+			    pctDist    	0.498270
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@560
+			supplier   	@564
+			Focus_Src  	@561
+			Focus_Entry 	@566
+			origin     	(1759, 1808)
+			terminus   	(2048, 1808)
+			ordinal    	11)
+		    (object InterObjView "$UNNAMED$0" @592
+			location   	(2240, 368)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	TRUE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object ItemLabel
+			    Parent_View 	@592
+			    location   	(2240, 368)
+			    fill_color 	13434879
+			    anchor_loc 	1
+			    nlines     	2
+			    max_width  	282
+			    justify    	0
+			    label      	"")
+			stereotype 	(object ItemLabel
+			    Parent_View 	@592
+			    location   	(2240, 368)
+			    fill_color 	13434879
+			    anchor     	10
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	222
+			    justify    	0
+			    label      	"<<Servlet>>")
+			icon_style 	"Icon"
+			line_color 	3342489
+			fill_color 	13434879
+			quidu      	"3DFE4BAE0056"
+			width      	300
+			height     	2082
+			icon_height 	0
+			icon_width 	0
+			icon_y_offset 	0
+			annotation 	1
+			Focus_Of_Control 	(object Focus_Of_Control "" @593
+			    location   	(2240, 428)
+			    InterObjView 	@592
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE)
+			Focus_Of_Control 	(object Focus_Of_Control "" @594
+			    location   	(2240, 1984)
+			    line_color 	3342489
+			    InterObjView 	@592
+			    height     	60
+			    y_coord    	0
+			    Nested     	FALSE))
+		    (object SelfMessView "" @595
+			location   	(16, 1872)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @596
+			    Parent_View 	@595
+			    location   	(2155, 1828)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE4C2701C3"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	308
+			    justify    	0
+			    label      	"internalDoFilter()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@564
+			supplier   	@564
+			Focus_Src  	@566
+			Focus_Entry 	@567
+			origin     	(2080, 1872)
+			terminus   	(2230, 1872)
+			ordinal    	12)
+		    (object InterMessView "" @597
+			location   	(2144, 1984)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @598
+			    Parent_View 	@597
+			    location   	(2151, 1940)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE4CA502BF"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	162
+			    justify    	0
+			    label      	"service()"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	0)
+			line_color 	3342489
+			client     	@564
+			supplier   	@592
+			Focus_Src  	@566
+			Focus_Entry 	@594
+			origin     	(2079, 1984)
+			terminus   	(2224, 1984)
+			ordinal    	13)
+		    (object InterMessView "" @599
+			location   	(16, 2000)
+			font       	(object Font
+			    size       	10
+			    face       	"Arial"
+			    bold       	FALSE
+			    italics    	FALSE
+			    underline  	FALSE
+			    strike     	FALSE
+			    color      	0
+			    default_color 	TRUE)
+			label      	(object SegLabel @600
+			    Parent_View 	@599
+			    location   	(1904, 1956)
+			    font       	(object Font
+				size       	10
+				face       	"Arial"
+				bold       	FALSE
+				italics    	FALSE
+				underline  	FALSE
+				strike     	FALSE
+				color      	0
+				default_color 	TRUE)
+			    quidu      	"3DFE4CB4025B"
+			    anchor_loc 	1
+			    nlines     	1
+			    max_width  	113
+			    justify    	0
+			    label      	"return"
+			    pctDist    	0.500000
+			    height     	45
+			    orientation 	1)
+			line_color 	3342489
+			client     	@564
+			supplier   	@560
+			Focus_Src  	@566
+			Focus_Entry 	@563
+			origin     	(2048, 2000)
+			terminus   	(1760, 2000)
+			ordinal    	14)))))
+    root_subsystem 	(object SubSystem "Component View"
+	quid       	"3DFDF6CE036A"
+	physical_models 	(list unit_reference_list)
+	physical_presentations 	(list unit_reference_list
+	    (object Module_Diagram "Main"
+		quid       	"3DFDF6D201FD"
+		title      	"Main"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	0
+		items      	(list diagram_item_list))))
+    process_structure 	(object Processes
+	quid       	"3DFDF6CE0373"
+	ProcsNDevs 	(list
+	    (object Process_Diagram "Deployment View"
+		quid       	"3DFDF6CE0387"
+		title      	"Deployment View"
+		zoom       	100
+		max_height 	28350
+		max_width  	21600
+		origin_x   	0
+		origin_y   	0
+		items      	(list diagram_item_list))))
+    properties 	(object Properties
+	attributes 	(list Attribute_Set
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"propertyId"
+		value      	"809135966")
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"default__Project"
+		value      	(list Attribute_Set
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"project"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TableCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ViewCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DomainCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"SPPackageCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TriggerCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IndexCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ConstraintCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"StoreProcedureCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"PrimaryKeyCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ForeignKeyCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"JoinCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TableSpaceCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"cONTAINERCounter"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TablePrefix"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ViewPrefix"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DomainPrefix"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TriggerPrefix"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IndexPrefix"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ConstraintPrefix"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"StoreProcedurePrefix"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"PrimaryKeyPrefix"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ForeignKeyPrefix"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TableSpacePrefix"
+			value      	"")))
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"default__Module-Spec"
+		value      	(list Attribute_Set
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmItem"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DMName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsDatabase"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TargetDatabase"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Location"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsTableSpace"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TableSpaceType"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsDeault"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"BufferPool"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ExtentSize"
+			value      	1)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"PrefetchSize"
+			value      	1)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"PageSize"
+			value      	4)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ManagedBy"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ContainerList"
+			value      	"")))
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"default__Category"
+		value      	(list Attribute_Set
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmItem"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DMName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmSchema"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmDomainPackage"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsSchema"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsDomainPackage"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsRootSchema"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsRootDomainPackage"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsSchemaPackage"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DatabaseID"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DBMS"
+			value      	"")))
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"default__Class"
+		value      	(list Attribute_Set
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmItem"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DMName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsTable"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsView"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsDomain"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsSPPackage"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Synonymns"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TableSpaceID"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"SourceId"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"SourceType"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"CorrelationName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"SelectClause"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsUpdateable"
+			value      	TRUE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"CheckOption"
+			value      	"None")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsSnapShot"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsDistinct"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"PersistToServer"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsPackage"
+			value      	FALSE)))
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"default__Attribute"
+		value      	(list Attribute_Set
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmItem"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DMName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Ordinal"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsIdentity"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsUnique"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"NullsAllowed"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Length"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Scale"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ColumnType"
+			value      	"Native")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ForBitData"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DefaultValueType"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DefaultValue"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"SourceId"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"SourceType"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"OID"
+			value      	FALSE)))
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"default__Association"
+		value      	(list Attribute_Set
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmItem"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DMName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsRelationship"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"SourceId"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"SourceType"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"RIMethod"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ParentUpdateRule"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ParentUpdateRuleName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ParentDeleteRule"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ParentDeleteRuleName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ChildInsertRestrict"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ChildInsertRestrictName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ChildMultiplicity"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ChildMultiplicityName"
+			value      	"")))
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"default__Role"
+		value      	(list Attribute_Set
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmItem"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DMName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ConstraintName"
+			value      	"")))
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"default__Operation"
+		value      	(list Attribute_Set
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmItem"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DMName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsConstraint"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ConstraintType"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsIndex"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsTrigger"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsStoredProcedure"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsCluster"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TableSpace"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"FillFactor"
+			value      	0)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"KeyList"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"CheckPredicate"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsUnique"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DeferalMode"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"InitialCheckTime"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"TriggerType"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsInsertEvent"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsUpdateEvent"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsDeleteEvent"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"RefOldTable"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"RefNewTable"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"RefOldRow"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"RefNewRow"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsRow"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"WhenClause"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Language"
+			value      	"SQL")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ProcType"
+			value      	"Procedure")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsDeterministic"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ParameterStyle"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ReturnedNull"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ExternalName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Length"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Scale"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ForBitData"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DefaultValue"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DefaultValueType"
+			value      	"")))
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"default__Parameter"
+		value      	(list Attribute_Set
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"dmItem"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DMName"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsInParameter"
+			value      	TRUE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"IsOutParameter"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Ordinal"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Length"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"Scale"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"ForBitData"
+			value      	FALSE)
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DefaultValueType"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"DefaultValue"
+			value      	"")
+		    (object Attribute
+			tool       	"Data Modeler"
+			name       	"OperationID"
+			value      	"")))
+	    (object Attribute
+		tool       	"Data Modeler"
+		name       	"HiddenTool"
+		value      	FALSE)
+	    (object Attribute
+		tool       	"Data Modeler Communicator"
+		name       	"HiddenTool"
+		value      	FALSE)
+	    (object Attribute
+		tool       	"Deploy"
+		name       	"HiddenTool"
+		value      	FALSE)
+	    (object Attribute
+		tool       	"Rose Model Integrator"
+		name       	"HiddenTool"
+		value      	FALSE)
+	    (object Attribute
+		tool       	"Rose Web Publisher"
+		name       	"HiddenTool"
+		value      	FALSE)
+	    (object Attribute
+		tool       	"Web Modeler"
+		name       	"HiddenTool"
+		value      	FALSE))
+	quid       	"3DFDF6CE0374"))
diff --git a/container/webapps/docs/architecture/startup.xml b/container/webapps/docs/architecture/startup.xml
new file mode 100644
index 0000000..69a7741
--- /dev/null
+++ b/container/webapps/docs/architecture/startup.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="startup.html">
+
+  &project;
+
+  <properties>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>Startup</title>
+  </properties>
+
+<body>
+
+
+<section name="Server Startup">
+
+<p>
+This page describes how the Tomcat server starts up.  There are several
+different ways to start tomcat, including:
+<ul>
+  <li>From the command line.</li>
+  <li>From a Java program as an embedded server.</li>
+  <li>Automatically as a Windows service.</li>
+</ul>
+</p>
+
+<subsection name="description">
+<p>
+A text description of the startup procedure is available
+<a href="startup/serverStartup.txt">here.</a>
+</p>
+</subsection>
+
+<subsection name="diagram">
+<p>
+A UML sequence diagram of the startup procedure is available
+<a href="startup/serverStartup.pdf">here.</a>
+</p>
+</subsection>
+
+<subsection name="comments">
+<p>
+The startup process can be customized in many ways, both
+by modifying Tomcat code and by implementing your own
+LifecycleListeners which are then registered in the server.xml
+configuration file.
+</p>
+
+</subsection>
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/architecture/startup/serverStartup.pdf b/container/webapps/docs/architecture/startup/serverStartup.pdf
new file mode 100644
index 0000000..34aa598
--- /dev/null
+++ b/container/webapps/docs/architecture/startup/serverStartup.pdf
Binary files differ
diff --git a/container/webapps/docs/architecture/startup/serverStartup.txt b/container/webapps/docs/architecture/startup/serverStartup.txt
new file mode 100644
index 0000000..6120b0a
--- /dev/null
+++ b/container/webapps/docs/architecture/startup/serverStartup.txt
@@ -0,0 +1,136 @@
+Tomcat 5 Startup Sequence
+
+Sequence 1. Start from Command Line
+Class: org.apache.catalina.startup.Bootstrap
+What it does:
+	a) Set up classloaders 
+		commonLoader (common)-> System Loader
+		sharedLoader (shared)-> commonLoader -> System Loader
+		catalinaLoader(server) -> commonLoader -> System Loader
+	b) Load startup class (reflection)
+		org.apache.catalina.startup.Catalina
+		setParentClassloader -> sharedLoader
+		Thread.contextClassloader -> catalinaLoader
+	c) Bootstrap.daemon.init() complete
+	
+Sequence 2. Process command line argument (start, startd, stop, stopd)
+Class: org.apache.catalina.startup.Bootstrap (assume command->start)
+What it does: 
+	a) Catalina.setAwait(true);
+	b) Catalina.load()
+		b1) initDirs() -> set properties like 
+		                  catalina.home
+		                  catalina.base == catalina.home (most cases)
+		b2) initNaming
+			setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
+				    org.apache.naming.java.javaURLContextFactory ->default)
+		b3) createStartDigester() 
+			Configures a digester for the main server.xml elements like
+			org.apache.catalina.core.StandardServer (can change of course :)
+			org.apache.catalina.deploy.NamingResources
+				Stores naming resources in the J2EE JNDI tree
+			org.apache.catalina.LifecycleListener
+				implements events for start/stop of major components
+			org.apache.catalina.core.StandardService
+				The single entry for a set of connectors,
+				so that a container can listen to multiple connectors
+				ie, single entry
+			org.apache.coyote.tomcat5.CoyoteConnector
+				Connectors to listen for incoming requests only
+			It also adds the following rulesets to the digester
+				NamingRuleSet
+				EngineRuleSet
+				HostRuleSet
+				ContextRuleSet
+		b4) Load the server.xml and parse it using the digester
+		    Parsing the server.xml using the digester is an automatic
+		    XML-object mapping tool, that will create the objects defined in server.xml
+		    Startup of the actual container has not started yet.
+		b5) Assigns System.out and System.err to the SystemLogHandler class
+		b6) Calls intialize on all components, this makes each object register itself with the 
+		    JMX agent.
+		    During the process call the Connectors also initialize the adapters.
+		    The adapters are the components that do the request pre-processing.
+		    Typical adapters are HTTP1.1 (default if no protocol is specified,
+		    org.apache.coyote.http11.Http11Protocol)
+		    AJP1.3 for mod_jk etc.
+
+	c) Catalina.start()
+		c1) Starts the NamingContext and binds all JNDI references into it
+		c2) Starts the services under <Server> which are:
+			StandardService -> starts Engine (ContainerBase ->Logger,Loader,Realm,Cluster etc)
+		c3) StandardHost (started by the service)
+				Configures a ErrorReportValvem to do proper HTML output for different HTTP 
+				errors codes
+				Starts the Valves in the pipeline (at least the ErrorReportValve)
+				Configures the StandardHostValve, 
+					this valves ties the Webapp Class loader to the thread context
+					it also finds the session for the request
+					and invokes the context pipeline
+				Starts the HostConfig component
+					This component deploys all the webapps
+						(webapps & conf/Catalina/localhost/*.xml)
+					Webapps are installed using the deployer (StandardHostDeployer)
+					The deployer will create a Digester for your context, this digester
+					will then invoke ContextConfig.start()
+						The ContextConfig.start() will process the default web.xml (conf/web.xml)
+						and then process the applications web.xml (WEB-INF/web.xml)
+						
+		c4) During the lifetime of the container (StandardEngine) there is a background thread that 
+		    keeps checking if the context has changed. If a context changes (timestamp of war file, 
+		    context xml file, web.xml) then a reload is issued (stop/remove/deploy/start)
+		    
+	d) Tomcat receives a request on an HTTP port
+	    d1) The request is received by a separate thread which is waiting in the PoolTcpEndPoint 
+	         class. It is waiting for a request in a regular ServerSocket.accept() method.
+	         When a request is received, this thread wakes up.
+	    d2) The PoolTcpEndPoint assigns the a TcpConnection to handle the request. 
+	        It also supplies a JMX object name to the catalina container (not used I believe)
+	    d3) The processor to handle the request in this case is Coyote Http11Processor, 
+	        and the process method is invoked.
+	        This same processor is also continuing to check the input stream of the socket
+	        until the keep alive point is reached or the connection is disconnected.
+	    d4) The HTTP request is parsed using an internal buffer class (Coyote Http11 Internal Buffer)
+	        The buffer class parses the request line, the headers, etc and store the result in a 
+	        Coyote request (not an HTTP request) This request contains all the HTTP info, such
+	        as servername, port, scheme, etc.
+	    d5) The processor contains a reference to an Adapter, in this case it is the 
+	        Coyote Tomcat 5 Adapter. Once the request has been parsed, the Http11 processor
+	        invokes service() on the adapter. In the service method, the Request contains a 
+	        CoyoteRequest and CoyoteRespons (null for the first time)
+	        The CoyoteRequest(Response) implements HttpRequest(Response) and HttpServletRequest(Response)
+	        The adapter parses and associates everything with the request, cookies, the context through a 
+	        Mapper, etc
+	    d6) When the parsing is finished, the CoyoteAdapter invokes its container (StandardEngine)
+	        and invokes the invoke(request,response) method.
+	        This initiates the HTTP request into the Catalina container starting at the engine level
+	    d7) The StandardEngine.invoke() simply invokes the container pipeline.invoke()
+	    d8) By default the engine only has one valve the StandardEngineValve, this valve simply
+	        invokes the invoke() method on the Host pipeline (StandardHost.getPipeLine())
+	    d9) the StandardHost has two valves by default, the StandardHostValve and the ErrorReportValve
+	    d10) The standard host valve associates the correct class loader with the current thread
+	         It also retrives the Manager and the session associated with the request (if there is one)
+	         If there is a session access() is called to keep the session alive
+	    d11) After that the StandardHostValve invokes the pipeline on the context associated
+	         with the request.
+	    d12) The first valve that gets invoked by the Context pipeline is the FormAuthenticator
+	         valve. Then the StandardContextValve gets invoke.
+	         The StandardContextValve invokes any context listeners associated with the context.
+	         Next it invokes the pipeline on the Wrapper component (StandardWrapperValve)
+	    d13) During the invokation of the StandardWrapperValve, the JSP wrapper (Jasper) gets invoked
+	         This results in the actual compilation of the JSP.
+	         And then invokes the actual servlet.
+	e) Invokation of the servlet class
+	         
+	         
+	         
+	    
+	        
+	        
+	        
+	    
+			
+			
+		
+		
+			
\ No newline at end of file
diff --git a/container/webapps/docs/balancer-howto.xml b/container/webapps/docs/balancer-howto.xml
new file mode 100755
index 0000000..88d33a9
--- /dev/null
+++ b/container/webapps/docs/balancer-howto.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="balancer-howto.html">
+
+    &project; 
+
+    <properties>
+        <author email="yoavs@apache.org">Yoav Shapira</author>
+        <author>Remy Maucherat</author>
+        <author>Andy Oliver</author>
+        <title>Load Balancer HOW-TO</title>
+    </properties>
+
+<body>
+
+<section name="Table of Contents">
+<p>
+<a href="#Using the JK 1.2.x native connector">
+Using the JK native connector</a><br />
+<a href="#Using Apache HTTP Server 2.x with mod_proxy">
+Using Apache HTTP Server 2.x and mod_proxy</a><br />
+<a href="#Using the balancer webapp">Using the balancer webapp</a><br />
+</p>
+</section>
+
+<section name="Using the JK 1.2.x native connector">
+
+Please refer to the JK 1.2.x documentation.
+
+</section>
+
+<section name="Using Apache HTTP Server 2.x with mod_proxy">
+
+Please refer to the mod_proxy documentation for Apache HTTP Server 2.2. This supports either
+HTTP or AJP load balancing. This new version of mod_proxy is also useable with
+Apache HTTP Server 2.0, but mod_proxy will have to be compiled separately using the code
+from Apache HTTP Server 2.2.
+
+</section>
+
+<section name="Using the balancer webapp">
+
+<subsection name="Overview">
+
+<p>
+Tomcat 5.0.15 and later ships with a webapp named balancer.  This is
+a simple implemention of a rules-based load balancer.  It was not designed
+as a replacement for other load-balancing mechanisms used for high traffic
+environments.  Rather, it is a simple, pure Java, easily extensible, and fast
+way to direct traffic among multiple servers.
+</p>
+<p>
+Although balancer ships with Tomcat, it is not Tomcat-specific and runs
+on other containers without any modification.  The balancer webapp 
+requires a Servlet Specification 2.3 or later container if you wish 
+to use a filter to redirect traffic.  If you wish to redirect traffic
+using a servlet, you may use any servlet container.
+</p>
+</subsection>
+
+<subsection name="Sample Configuration">
+<p>
+The default balancer installation uses a single filter, BalancerFilter,
+mapped to all requests (url-pattern /*).  The filter reads its rules
+from the location specified in the balancer deployment descriptor
+(web.xml file).  The default rules are:
+<ul>
+  <li>Redirect requests with News in the URL to www.cnn.com</li>
+  <li>Redirect requests with a parameter named paramName whose
+value is paramValue to www.yahoo.com.</li>
+  <li>Redirect all other requests to jakarta.apache.org.</li>
+</ul>
+
+Therefore, when you install tomcat, start it, and point your
+browser to http://localhost:8080/balancer, you will be redirected
+to http://jakarta.apache.org.  If you point your browser to
+http://localhost:8080/balancer/News you will be redirected to
+http://www.cnn.com.  The request for 
+http://localhost:8080/balancer/BlahBlah?paramName=paramValue will
+be redirected to http://www.yahoo.com.
+</p>
+</subsection>
+
+<subsection name="Balancer Rules">
+<p>
+A <i>Rule</i> in the balancer system is a combination of
+a request matching criterion and a redirection URL for
+matching requests.  Rules implement the
+org.apache.webapp.balancer.Rule interface.
+</p>
+
+<p>
+The balancer distribution contains a number of useful
+rules.  The framework is also designed for easy extensibility
+so that you can write your own rules quickly.  Rules
+should be JavaBeans (public no-args constructor, public
+setter method setXXX for property xxx), as they are
+instantiated by Jakarta Commons Digester.  Feel free
+to inquire on the tomcat-user mailing list regarding
+the availability of rules or the inclusion of your rules
+in the distribution.
+</p>
+
+<p>
+Rules are assembled into RuleChains.  Each BalancerFilter
+(or Servlet/JSP) refers to one RuleChain when making its
+redirection decisions.  Note that you are not restricted
+to having one filter mapped to /* as done in the sample
+configuration.  You can configure as many filters as
+desired, using the full filter mapping possibilities defined
+in the Servlet Specification.  Each filter will have
+its own RuleChain.
+</p>
+</subsection>
+
+<subsection name="How it Works">
+<p>
+<ol>
+  <li>You write a rules configuration file containing various
+rules and redirection locations.</li>
+  <li>You define the balancer filter in your web.xml, mapping
+it as desired (/* is a common use-case) and configuring it
+with your rules configuration file.</li>
+  <li>The server is started, initializing the filter.</li>
+  <li>A request comes into the server.  The filter consults
+its rule chain to determine where to redirect the request.  Rules
+are consulted in the order in which they are defined in the rules
+configuration file.  The first matching rule will stop the
+evaluation and cause the request to be redirected.</li>
+</ol>
+</p>
+
+</subsection>
+
+<subsection name="Comments">
+<p>
+Please direct questions, comments, suggestions, etc. to the
+tomcat-user mailing list.  Thank you.
+</p>
+</subsection>
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/build.xml b/container/webapps/docs/build.xml
new file mode 100644
index 0000000..1cd735f
--- /dev/null
+++ b/container/webapps/docs/build.xml
@@ -0,0 +1,236 @@
+<project name="tomcat-docs" default="build-main" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="../../build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="webapps.build"   value="../build"/>
+  <property name="webapps.dist"    value="../dist"/>
+  <property name="webapp.name"     value="tomcat-docs"/>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${webapps.build}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ================ BUILD: Copy Static Files ========================== -->
+  <target name="build-static" depends="build-prepare">
+
+    <!-- Top Level Static Files -->
+    <copy    todir="${webapps.build}/${webapp.name}">
+      <fileset dir="../..">
+        <include name="BUILDING.txt"/>
+        <include name="README.txt"/>
+        <include name="RUNNING.txt"/>
+      </fileset>
+    </copy>
+    <copy    todir="${webapps.build}/${webapp.name}">
+      <fileset dir="." includes="**/*.html"/>
+    </copy>
+
+    <!-- WEB-INF Static Files -->
+    <copy    todir="${webapps.build}/${webapp.name}/WEB-INF">
+      <fileset dir="WEB-INF"/>
+    </copy>
+
+    <!-- Application Developer's Guide Examples -->
+    <copy    todir="${webapps.build}/${webapp.name}/appdev">
+      <fileset dir="appdev" includes="*.txt"/>
+    </copy>
+    <copy    todir="${webapps.build}/${webapp.name}/appdev/sample">
+      <fileset dir="appdev/sample"/>
+    </copy>
+    <copy   tofile="${webapps.build}/${webapp.name}/appdev/sample/build.xml"
+              file="appdev/build.xml.txt"/>
+
+    <!-- Catalina Functional Specifications -->
+    <mkdir     dir="${webapps.build}/${webapp.name}/catalina/funcspecs"/>
+
+    <!-- Architecture -->
+    <copy    todir="${webapps.build}/${webapp.name}/architecture">
+      <fileset dir="architecture" excludes="*.xml"/>
+    </copy>
+
+    <!-- Images Subdirectory -->
+    <mkdir     dir="${webapps.build}/${webapp.name}/images"/>
+    <copy    todir="${webapps.build}/${webapp.name}/images">
+      <fileset dir="images"/>
+    </copy>
+
+    <mkdir     dir="${webapps.build}/${webapp.name}/printer"/>
+    <!-- Top Level Static Files -->
+    <copy    todir="${webapps.build}/${webapp.name}/printer">
+      <fileset dir="../..">
+        <include name="BUILDING.txt"/>
+        <include name="README.txt"/>
+        <include name="RUNNING.txt"/>
+      </fileset>
+    </copy>
+    <style basedir="."
+           destdir="${webapps.build}/${webapp.name}/printer"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="build.xml project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="./.."/>
+      <param name="project-menu" expression="nomenu"/>
+    </style>
+
+    <!-- WEB-INF Subdirectory -->
+    <mkdir     dir="${webapps.build}/${webapp.name}/WEB-INF"/>
+    <copy    todir="${webapps.build}/${webapp.name}/WEB-INF">
+      <fileset dir="WEB-INF"/>
+    </copy>
+
+  </target>
+
+
+  <!-- ================= BUILD: XML-HTML Generation ======================= -->
+  <target name="build-main" depends="build-static">
+
+    <!-- Top Level Directory -->
+    <style basedir="."
+           destdir="${webapps.build}/${webapp.name}"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="build.xml project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="."/>
+    </style>
+
+    <!-- Application Developer's Guide -->
+    <style basedir="appdev"
+           destdir="${webapps.build}/${webapp.name}/appdev"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression=".."/>
+    </style>
+    <mkdir     dir="${webapps.build}/${webapp.name}/appdev/printer"/>
+    <!-- Application Developer's Guide Examples -->
+    <copy    todir="${webapps.build}/${webapp.name}/appdev/printer">
+      <fileset dir="appdev" includes="*.txt"/>
+    </copy>
+    <style basedir="appdev"
+           destdir="${webapps.build}/${webapp.name}/appdev/printer"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="../.."/>
+      <param name="project-menu" expression="nomenu"/>
+    </style>
+
+    <!-- Catalina Functional Specifications -->
+    <mkdir     dir="${webapps.build}/${webapp.name}/catalina"/>
+    <mkdir     dir="${webapps.build}/${webapp.name}/catalina/funcspecs"/>
+    <style basedir="funcspecs"
+           destdir="${webapps.build}/${webapp.name}/catalina/funcspecs"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="../.."/>
+    </style>
+    <mkdir     dir="${webapps.build}/${webapp.name}/catalina/funcspecs/printer"/>
+    <style basedir="funcspecs"
+           destdir="${webapps.build}/${webapp.name}/catalina/funcspecs/printer"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="../../.."/>
+      <param name="project-menu" expression="nomenu"/>
+    </style>
+
+    <!-- Server Configuration Reference -->
+    <style basedir="config"
+           destdir="${webapps.build}/${webapp.name}/config"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression=".."/>
+    </style>
+    <mkdir     dir="${webapps.build}/${webapp.name}/config/printer"/>
+    <style basedir="config"
+           destdir="${webapps.build}/${webapp.name}/config/printer"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="../.."/>
+      <param name="project-menu" expression="nomenu"/>
+    </style>
+
+    <!-- Server Architecture -->
+    <style basedir="architecture"
+           destdir="${webapps.build}/${webapp.name}/architecture"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression=".."/>
+    </style>
+    <mkdir     dir="${webapps.build}/${webapp.name}/architecture/printer"/>
+    <style basedir="architecture"
+           destdir="${webapps.build}/${webapp.name}/architecture/printer"
+         extension=".html"
+             style="tomcat-docs.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="../.."/>
+      <param name="project-menu" expression="nomenu"/>
+    </style>
+
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean and build documentation webapp"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${webapps.build}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Directories =================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${webapps.dist}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Distribution Files ============ -->
+  <target name="dist" depends="build-main,dist-prepare"
+   description="Create documentation webapp binary distribution">
+      <jar   jarfile="${webapps.dist}/${webapp.name}.war"
+             basedir="${webapps.build}/${webapp.name}" includes="**"/>
+  </target>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <delete dir="${webapps.dist}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
diff --git a/container/webapps/docs/building.xml b/container/webapps/docs/building.xml
new file mode 100644
index 0000000..f1f764c
--- /dev/null
+++ b/container/webapps/docs/building.xml
@@ -0,0 +1,216 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document>
+
+    &project;
+
+    <properties>
+      <author>Remy Maucherat</author>
+      <title>Building Tomcat</title>
+    </properties>
+
+<body>
+
+<section name="Introduction">
+
+<p>
+Building Tomcat from CVS is very easy, and is the first step to contributing to
+Tomcat. The following is a step by step TODO list.
+</p>
+
+</section>
+
+<section name="Download and install a Java Development Kit 1.4.x or later">
+
+<p>
+Earlier releases would also work, but are harder to work with due to the need to
+download additional dependencies. Tomcat also runs much faster on 
+the latest 1.4 JDK.
+</p>
+
+<p>
+The Sun JDK can be downloaded <a href="http://java.sun.com/j2se/">here</a>.
+</p>
+
+<p>
+<b>IMPORTANT</b>: Set an environment variable JAVA_HOME to the pathname of the 
+directory into which you installed the JDK release.
+</p>
+
+</section>
+
+<section name="Install Apache Ant 1.6.x">
+
+<p>
+Download a binary distribution of Ant 1.6.x from 
+<a href="http://ant.apache.org/bindownload.cgi">here</a>.
+</p>
+
+<p>
+Unpack the binary distribution into a convenient location so that the
+Ant release resides in its own directory (conventionally named
+"jakarta-ant-1.6.2").  For the purposes of the remainder of this document,
+the symbolic name "${ant.home}" is used to refer to the full pathname of
+ the release directory.
+</p>
+
+<p>
+Create an ANT_HOME environment variable to point the directory ${ant.home}, 
+and modify the PATH environment variable to include directory
+"${ant.home}/bin" in its list.  This makes the "ant" command line script
+available, which will be used to actually perform the build.
+</p>
+
+</section>
+
+<section name="Building Tomcat">
+
+<p>
+Download the main build.xml script from <a href="build.xml">here</a>.
+</p>
+
+<p>
+Create a new directory, and copy the newly download build.xml to it. This
+  directory will be referred to as the ${tomcat.source} directory in the rest
+  of this document.
+</p>
+
+<p>
+Go to that directory, and do:
+<code><br/>
+    cd ${tomcat.source}<br/>
+    ant<br/>
+</code>
+</p>
+
+<p>
+NOTE: Users accessing the Internet through a proxy must use a properties
+  file to indicate to Ant the proxy configuration. Read below.
+</p>
+
+<p>
+WARNING: Running this command will checkout the Tomcat 5 sources from CVS, as
+  well as download binaries to the <code>/usr/share/java</code> directory. 
+  Make sure this is appropriate to do so on your computer. On Windows, 
+  this usually corresponds to the <code>C:\usr\share\java</code> directory, 
+  unless Cygwin is used. Read below to customize the directory used 
+  to download the binaries.
+</p>
+
+<p>
+The build can be controlled by creating a ${tomcat.source}/build.properties
+  file, and adding the following content to it:
+<code><br/>
+    # ----- Proxy setup -----<br/>
+    # Uncomment if using a proxy server.<br/>
+    #proxy.host=proxy.domain<br/>
+    #proxy.port=8080<br/>
+    #proxy.use=on<br/>
+<br/>
+    # ----- Default Base Path for Dependent Packages -----<br/>
+    # Replace this path with the directory path where<br/>
+    # dependencies binaries should be downloaded.<br/>
+    base.path=/usr/share/java<br/>
+</code>
+</p>
+
+</section>
+
+<section name="Updating and rebuilding Tomcat sources">
+
+<p>
+It is recommended to regularly update the downloaded Tomcat 5 sources. 
+To do this, execute the following commands:
+
+<code><br/>
+    cd ${tomcat.source}<br/>
+    ant checkout<br/>
+</code>
+</p>
+
+<p>
+For a quick rebuild of only modified code you can use:
+<code><br/>
+    cd ${tomcat.source}<br/>
+    ant build<br/>
+</code>
+
+</p>
+
+</section>
+
+<section name="Building with Eclipse">
+
+<p>
+<b>Important:</b>
+This is not a supported means of building Tomcat; this information is
+provided without warranty :-).
+The only supported means of building Tomcat is with the "ant build"
+described above.
+However, some developers like to work on Java code with a Java IDE,
+and the following steps have been used by some developers.
+</p>
+
+<p>
+Note that you <b>must</b> complete all the above steps to fetch
+the repositories and build some JAR files the first time.
+After you have completed the above steps, you can set up a
+series of Eclipse 3 projects.
+<b>Note</b> that this will not let you build everything under Eclipse;
+the build process requires use of Ant for the many stages that aren't
+simple Java compilations.
+However, it will allow you to view and edit the Java code,
+get warnings, reformat code, perform refactorings, run Tomcat
+under the IDE, and so on.
+</p>
+
+<p>
+Use File-&gt;New Project to create a new Java project 
+for each of the binaries repository (e.g., /usr/share/java), 
+tomcat-connectors, tomcat-catalina, jasper, servletapi.
+Unless you thought ahead to make the ${tomcat.source} directory be under
+your Workspace folder, tell Eclipse the external location.
+The obvious dependencies will be needed; the "repository" project
+needs to export all its jars, and be referred to by the other projects.
+Eclipse will find all source trees and jars, and
+hopefully compile without problems
+You will need to add ${ant-home}/lib/ant.jar as an "External Jar"
+under Project Properties for some of these projects (notably those that fail
+to compile for want of BuildException).
+</p>
+
+<p>
+To run Tomcat without a special IDE plug-in, you can simply use Run-&gt;Run...
+enter "org.apache.catalina.startup.Catalina" as the main class,
+"start" as program arguments, and
+"-Dcatalina.home=..." (with the name of your build directory) 
+as VM arguments.
+</p>
+
+<p>
+Note also that due to the way the Tomcat source is assembled
+from several CVS projects, you may not be able to use the Eclipse
+CVS client to update (nor to commit, if you are a committer).
+Use the external CVS client of your choice, then use the
+Eclipse PackageExplorer or Navigator "Refresh" context menu item
+to tell Eclipse that you've updated the files.
+</p>
+
+</section>
+
+<section name="Building with other IDEs">
+<p>
+The same caveats apply as for Eclipse, above.
+</p>
+
+<p>
+The same general idea should work in most IDEs; it has been reported
+to work in Idea, for example.
+</p>
+
+</section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/catalina/docs/api/index.html b/container/webapps/docs/catalina/docs/api/index.html
new file mode 100644
index 0000000..4fe8f9a
--- /dev/null
+++ b/container/webapps/docs/catalina/docs/api/index.html
@@ -0,0 +1,17 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html>
+    <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <title>Administration</title>
+</head>
+
+<body>
+
+Tomcat's internal javadoc is no longer installed by default. Download and install 
+the "fulldocs" package to get it.
+
+You can also access the javadoc online in the Tomcat 
+<a href="http://jakarta.apache.org/tomcat/tomcat-5.5-doc/">documentation bundle</a>.
+
+</body>
+</html>
diff --git a/container/webapps/docs/cgi-howto.xml b/container/webapps/docs/cgi-howto.xml
new file mode 100644
index 0000000..6c720b3
--- /dev/null
+++ b/container/webapps/docs/cgi-howto.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="cgi-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="glenn@apache.org">Glenn L. Nielsen</author>
+        <title>CGI How To</title>
+    </properties>
+
+<body>
+
+<section name="Introduction">
+
+<p>The CGI (Common Gateway Interface) defines a way for a web server to
+interact with external content-generating programs, which are often
+referred to as CGI programs or CGI scripts.
+</p>
+
+<p>Within Tomcat, CGI support can be added when you are using Tomcat as your
+HTTP server and require CGI support.  Typically this is done
+during development when you don't want to run a web server like 
+Apache httpd.
+Tomcat's CGI support is largely compatible with Apache httpd's, 
+but there are some limitations (e.g., only one cgi-bin directory).
+</p>
+
+<p>CGI support is implemented using the servlet class
+<code>org.apache.catalina.servlets.CGIServlet</code>.  Traditionally,
+this servlet is mapped to the URL pattern "/cgi-bin/*".</p>
+
+<p>By default CGI support is disabled in Tomcat.</p>
+</section>
+
+<section name="Installation">
+
+<p><strong>CAUTION</strong> - CGI scripts are used to execute programs
+external to the Tomcat JVM. If you are using the Java SecurityManager this
+will bypass your security policy configuration in <code>catalina.policy.</code></p>
+
+<p>Rename <code>$CATALINA_BASE/server/lib/servlets-cgi.renametojar</code>
+to <code>$CATALINA_BASE/server/lib/servlets-cgi.jar</code>.</p>
+
+<p>Remove the XML comments from around the CGI servlet and servlet-mapping
+configuration in <code>$CATALINA_BASE/conf/web.xml</code>.</p>
+</section>
+
+<section name="Configuration">
+
+<p>There are several servlet init parameters which can be used to
+configure the behaviour of the CGI servlet.
+<ul>
+<li><strong>cgiPathPrefix</strong> - The CGI search path will start at
+the web application root directory + File.separator + this prefix.
+The default cgiPathPrefix is <code>WEB-INF/cgi</code></li>
+<li><strong>debug</strong> - Debugging detail level for messages logged
+by this servlet. Default 0.</li>
+<li><strong>executable</strong> - The of the executable to be used to
+run the script. Default is <code>perl</code>.</li>
+<li><strong>parameterEncoding</strong> - Name of the parameter encoding
+to be used with the GCI servlet. Default is
+<code>System.getProperty("file.encoding","UTF-8")</code>.</li>
+<li><strong>passShellEnvironment</strong> - Should the shell environment
+variables (if any) be passed to the CGI script? Default is
+<code>false</code>.</li>
+</ul>
+</p>
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/changelog.xml b/container/webapps/docs/changelog.xml
new file mode 100644
index 0000000..e7d54f1
--- /dev/null
+++ b/container/webapps/docs/changelog.xml
@@ -0,0 +1,1982 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="changelog.html">
+
+  &project;
+
+  <properties>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>Changelog</title>
+  </properties>
+
+<body>
+
+<section name="Preface">
+  <p>
+  This is the Changelog for Tomcat 5.5.x, which was branched based on Tomcat 5.0.27.
+  For changes in Tomcat version 5.0.x, which preceded version 5.5.0, please see
+  <a href="http://jakarta.apache.org/tomcat/tomcat-5.0-doc/changelog.html">The
+  Tomcat 5.0.x Changelog</a>.  As maintenance releases are cut on the 5.0.x branch,
+  most fixes will be ported into a 5.5.x release and noted both here and in the
+  above Changelog.  However, bugs fixed in Tomcat 5.0.28 and earlier are noted
+  only in the above changelog.
+  </p>
+</section>
+
+<section name="Tomcat 5.5.11 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <update>
+        Update to Xerces 2.7.1 (remm)
+      </update>
+      <add>
+        Add ready to build bin/tomcat-native.tar.gz for the APR JNI wrapper library (remm)
+      </add>
+      <fix>
+        <bug>35930</bug>: Bad logging config used by the Tomcat Windows service (remm)
+      </fix>
+      <add>
+        <bug>33261</bug>: Windows installer now checks the user type and warns non-admins as needed. (yoavs)
+      </add>
+      <update>
+        The Windows installer will now optionally download a (32bit) Windows .dll for Tomcat native
+        from HEAnet (remm)
+      </update>
+      <fix>
+        Declaration of jspc Ant task to fix the deployer package (remm)
+      </fix>
+    </changelog>
+  </subsection>
+  
+  <subsection name="Catalina">
+    <changelog>
+      <add>
+        Add concurrency control valve (o.a.c.valves.SemaphoreValve). As the Tomcat distribution 
+        is not built for Java 5, the valve will have to be compiled from the sources 
+        using Java 5 (remm)
+      </add>
+      <fix>
+        <bug>35880</bug>: Ignore JSSE15SocketFactory when generating JavaDoc, as it breaks
+          the JDK 1.4 JavaDoc tool. (yoavs)
+      </fix>
+      <fix>
+        <bug>35865</bug>: setclasspath.sh cannot be excutive under cygwin. (funkman)
+      </fix>
+      <fix>
+        <bug>33267</bug>: Set working path in service installer, as suggested by Dominik
+          Drzewiecki. (yoavs)
+      </fix>
+      <update>
+        <bug>34794</bug>: Update connector documentation to include clientAuth attribute. (yoavs)
+      </update>
+      <fix>
+        <bug>35894</bug>, <bug>36228</bug>: Fix CNFE when starting in a sandbox. (billbarker, remm)
+      </fix>
+      <fix>
+        Add version check for Tomcat native so that incompatible API changes are detected early (remm)
+      </fix>
+      <fix>
+        <bug>36020</bug>: Allow MemoryUserDatabase to work better on write protected mediums,
+        submitted by Rainer Jung (remm)
+      </fix>
+      <fix>
+        <bug>35978</bug>: Bad handling of single range requests greater than 2GB in the DefaultServlet
+        (remm)
+      </fix>
+      <fix>
+        <bug>35984</bug>: Client abort exceptions will now use getCause (remm)
+      </fix>
+      <fix>
+        Fix handling of non-file based includes with SSI, submitted by David Becker (markt)
+      </fix>
+    </changelog>
+  </subsection>
+  
+   <subsection name="Coyote">
+     <changelog>
+      <fix>
+        Fix default ports for http and https which are set in the request when the parsed
+        hostname does not specify the port, and which were inverted (https was set as 80 
+        and http as 443). (remm)
+      </fix>
+      <fix>
+        Add missing tomcatAuthentication attribute to the AJP APR implementation. (remm)
+      </fix>
+      <fix>
+        Check filename sendfile attribute only if sendfile is enabled. (remm)
+      </fix>
+      <fix>
+        Fix output buffering for APR AJP implementation. (remm)
+      </fix>
+      <fix>
+        <bug>35941</bug>: Fix getRemoteAddr for APR AJP implementation. (remm)
+      </fix>
+      <fix>
+        <bug>35942</bug>: Fix NPE retriving cipher suite attribute when no certificate 
+        was submitted (for example with no SSL). (remm)
+      </fix>
+      <fix>
+        Internationalization and code cleanups for APR AJP implementation. (remm)
+      </fix>
+      <fix>
+        Security exception in APR AJP implementation when running with the security
+        manager enabled. (remm)
+      </fix>
+      <fix>
+        <bug>36173</bug>: Add missing sync in FastHttpDateFormat.formatDate, submitted 
+        by Alexei Krainiouk (remm)
+      </fix>
+      <fix>
+        Disable HTTP compression when sendfile is used for a resource (remm)
+      </fix>
+      <fix>
+        AJP secret attribute report only at trace level. (pero)
+      </fix>
+     </changelog>
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        <bug>36127</bug>: Validation compatibility with Xerces 2.7.1, submitted 
+        by Florent Benoit (remm)
+      </fix>
+    </changelog>
+  </subsection>
+  
+  <subsection name="Cluster">
+    <changelog>        
+      <fix>
+        Fix NPE when cluster stops (pero)
+      </fix>
+      <fix>
+        <bug>36218</bug>: MemoryRealm now support also GenericPrincipal, but 
+        JAASRealm with cluster replication still has a problem, detected by Dirk Dekok (pero)
+      </fix>   
+     </changelog>
+  </subsection>
+  
+  <subsection name="Webapps">
+    <changelog>
+    </changelog>
+  </subsection>
+ </section>
+
+<section name="Tomcat 5.5.10 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <add>
+         Add JMX remote ant task to control tomcat MBeanserver via ant scripts.
+         Ant lib is included at "server/lib/catalina-ant-jmx.jar" and documentation
+         is added to <a href="monitoring.html">Monitoring and Managing Tomcat How-To</a> (pero)
+      </add>
+      <fix>
+        <bug>34361</bug>: Integrate better antlib and import support for 
+        catalina manager tasks [Modified patch from Daniel Santos]  (pero)
+      </fix>
+      <fix>
+        StoreConfig save now the Connector.sslProtocol attribute. (pero)
+      </fix>
+      <update>
+        Change log dir at service.bat to "$CATALINA_BASE/logs" for better multi instance support. (pero)
+      </update>
+      <update>
+        <bug>34237</bug>: Added note and links to context and host configuration
+          references in JNDI DataSources HowTo to aid the clueless. (yoavs)
+      </update>
+      <update>
+        <bug>34248</bug>: Update JavaMail download instructions to include JAF. (yoavs)
+      </update>
+      <update>
+        Update to JDT from Eclipse 3.1, with support for Java 5 (remm)
+      </update>
+      <update>
+        Refactoring, redesign and extend the cluster module
+          - Cluster can be configured as subelement from Engine and Host.
+          - Optimized performance and reduce memory usage
+          - Better JMX support
+          - add a lot of JMX stats attribute for better monitoring 
+          - add a single element default cluster configuration
+          - more config options
+             LifecycleListener
+             ClusterListener
+             more than one cluster valves
+          - better subclass support
+          - change a lot of existing cluster API's (pero)
+       </update>
+       <add>
+         Add Apache Portable Runtime JNI wrapper and helper API (mturk)
+       </add>
+       <update>
+         Update JULI to provide support for taking over java.util.logging bootstrap configuration,
+         and move the default properties file to ${catalina.base}/conf/logging.properties (remm)
+       </update>
+       <fix>
+         <bug>34746</bug>: Updated catalina.properties instructions per Bill Edwards' suggestion. (yoavs)
+       </fix>
+       <fix>
+         <bug>35090</bug>: Minor documentation typo fix. (yoavs)
+       </fix>
+       <fix>
+         <bug>34931</bug>: Rewrote ROOT/index.jsp to be XHTML strict compliant, per Richard
+           Beton's patch. (yoavs)
+       </fix>
+    </changelog>
+  </subsection>
+  
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        <bug>20380</bug>: Access log timestamps now take account of Daylight Saving
+        Time (DST). (markt)
+      </fix>
+      <add>
+        <bug>34220</bug>: Provide better error message when server.xml can't be located.
+          [Modified patch from Ralf Hauser] (yoavs)
+      </add>
+      <add>
+          Add MessageListener and LifecylceListener cluster saving to storeconfig module
+          (&lt;Cluster ... &gt;&lt;ClusterListener className="org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListener" &gt;) (pero)
+      </add>  
+      <fix>
+        <bug>33743</bug>: Add additional synchronization in webapp classloader to avoid
+        possible race condition when defining a class (remm)
+      </fix>
+      <fix>
+        <bug>33711</bug>: Add events on passivate and activate to cleanup SSO, and recycle
+        session objects when removing them from a manager (so that anyone keeping references
+        to it would leak a minimal amount of memory) (remm)
+      </fix>
+      <update>
+        Re-add patch causing Session.getId to throw an ISE, and make all internal components
+        use a safe getIdInternal method (remm)
+      </update>
+      <update>
+        Store principal to be exposed for Request.getUserPrincipal inside the GenericPrincipal,
+        to remove hacks from the JAAS realm (remm)
+      </update>
+      <fix>
+        <bug>10385</bug>: SSI Servlet now includes better support for files that use character
+        encodings other than the platform default.(markt)
+      </fix>
+      <fix>
+        Remove CopyParentClassLoader rule, which doesn't seem to be doing anything useful
+        anymore. (remm)
+      </fix>
+      <add>
+        Provide an ServletFilter implementation of Server Side Includes (SSI). This was
+        submitted by David Becker under <bug>33106</bug>. (markt)
+      </add>
+      <add>
+        Add sendfile support to default servlet, with a sendfileSize configuration attribute.
+        (remm)
+      </add>
+      <update>
+        If APR as well as Tomcat's JNI wrapper for APR are present, use APRized protocol handlers
+        instead of the regular ones (remm)
+      </update>
+      <fix>
+        <bug>22617</bug>: When used with an EJB container and a realm that supports the concept
+        of an unauthenticated user (J2EE.3.4.3) BASIC authentication was always authenticating
+        users as the unauthenticated user without giving them a chance to supply a username and
+        password. (markt)
+      </fix>
+      <fix>
+        Prevent facade objects cloning (remm)
+      </fix>
+      <update>
+        Add missing CGI variables to SSI servlet. Patch submitted by Fritz Schneider. (markt)
+      </update>
+      <fix>
+        <bug>34578</bug>: Updated JNDIRealm comment. (yoavs)
+      </fix>
+      <fix>
+        <bug>34273</bug>: Better Bootstrap warning message. [Path from Ralf Hauser] (yoavs)
+      </fix>
+      <update>
+        <bug>34675</bug>: Updated Proxy-HowTo page with Servlet API calls. (yoavs)
+      </update>
+      <fix>
+        <bug>34546</bug>: Fix problem where the "first" Valve couldn't be removed from a Pipeline. (billbarker)
+      </fix>
+      <fix>
+        Fix NPE when POST size exceeds limit defined by maxPostSize. (markt)
+      </fix>
+      <fix>
+        Fix FORM authentication so POSTed parameters are not assumed to be encoded with platform
+        default encoding. A side effect of this fix is that the bodies of POST requests that
+        require FORM authentication are now buffered and made available after a sucessful login. (markt)
+      </fix>
+      <fix>
+        <bug>34840</bug>: Better handling of external WARs redeployment, and ignore docBase specified
+        in context file if within the Host appBase (remm)
+      </fix>
+      <fix>
+        Fix handling of symbolic links when the DefaultServlet is generating directory
+        listings. (markt)
+      </fix>
+      <fix>
+        <bug>35769</bug>: Correct implementation of javax.naming.Context.composeName( Name, Name)
+        in multiple places. Patch provided by Laurent Simon. (markt)
+      </fix>
+      <add>
+        <bug>34805</bug>: Add warning for suspicious security patterns, as suggested by Ralf Hauser. (yoavs)
+      </add>
+      <fix>
+        <bug>35819</bug>: Use getWorkPath for deleting work directory on context destroy, as suggested
+          by Rob Steele. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+  
+   <subsection name="Coyote">
+     <changelog>
+      <update>
+        Add support for using "Smart Cards" as trust/keyStore. (billbarker)
+      </update>
+      <update>
+        Add some Mbean attributes and operations to ChannelSocket (pero)
+      </update>    
+      <add>
+        Apache Portable Runtime based HTTP/1.1 protocol handler, with SSL support (remm)
+      </add>
+      <add>
+        Add support for simple file-based CRLs under JDK 1.5 (billbarker)
+      </add>
+      <add>
+        Add experimental NIO-Socket channel for the AJP/1.3 Connector (billbarker)
+      </add>
+      <add>
+        <bug>34648</bug>: Add configuration option to enable IP-based Virtual Hosts. (billbarker)
+      </add>
+      <update>
+        Refactor the AJP/1.3 Connector to be able to handle more advanced Actions. (billbarker)
+      </update>
+      <fix>
+        Fix connector initialisation so sslProtocol is not required for SSL. (markt)
+      </fix>
+      <add>
+        Add bufferSize option to the AJP/1.3 Java connector to control output buffering. (billbarker)
+      </add>
+      <add>
+        Apache Portable Runtime based AJP/1.3 protocol handler (remm)
+      </add>
+      <fix>
+        Delay reading the inital request body packet by default for the AJP/1.3 Java connector. (billbarker)
+      </fix>
+     </changelog>
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        <bug>18477</bug>: Allow symbolic links when precompiling JSPs (markt)
+      </fix>
+      <add>
+        <bug>34727</bug>: Allow specifying the Option class used by the Jasper engine,
+        submitted by Scott Stark (remm)
+      </add>
+      <add>
+        Support for Java 5.0 in JSPs (remm)
+      </add>
+      <update>
+        Java 5 will be the source and target for JSPs when running on Java 5 (remm)
+      </update>
+      <update>
+        <bug>34652</bug>: Add the ability to get SMAPs when precompiling, submitted by
+        Daryl Robbins (remm)
+      </update>
+      <fix>
+        <bug>34465</bug>: Jspc failure if there is no web.xml (remm)
+      </fix>
+      <fix>
+        <bug>35696</bug>: Make certain that release is called for custom tags 
+         when tag-pooling is disabled. (billbarker)
+      </fix>
+      <fix>
+        <bug>35386</bug>: Make useBean resources use consistent spelling, from Kurt Huwig. (yoavs)
+      </fix>
+      <update>
+        <bug>33522</bug>: Update jasper-howto to reflect use of javac switch. (yoavs)
+      </update>
+      <add>
+        <bug>35114</bug>: Add failOnError flag to JspC, by ziweth. (yoavs)
+      </add>
+      <fix>
+        <bug>35410</bug>: Fixed NPE in JspWriterImpl. (yoavs)
+      </fix>
+      <add>
+        <bug>35571</bug>: JspC resolved uriRoot relative to Ant project basedir, if any, as suggested
+          by Jason Pettiss. (yoavs)
+      </add>
+    </changelog>
+  </subsection>
+  
+  <subsection name="Cluster">
+    <changelog>        
+      <add>
+        Add that cluster can configure as Engine and Host element. (pero)
+      </add>      
+      <add>
+        Add single cluster default configuration element - discussed at JAX 2005 conference Cluster Workshop. (pero)
+      </add>      
+      <fix>
+        Fix resend GET_ALL_SESSIONS when wait ACK failed at receiver side (pero)
+      </fix>  
+      <fix>
+        ClusterValve now remove from container element when cluster stops and added with next start again. (pero)
+      </fix>     
+      <add>
+        Set timestamp only at first time inside SessionMessageImpl (pero)
+      </add>    
+      <add>
+       Set timestamp from findsessions method call, when handling GET_ALL_SESSION
+       to all SEND_SESSION_DATA and TRANSFER complete messages. (pero>
+      </add>
+      <add> 
+       Drop all received message inside GET_ALL_SESSION message queue before state 
+       transfer message timestamp. (pero)
+      </add>      
+      <add>
+        Cluster ping now transfer cluster domain information and DeltaManager only
+        send and receive message from same domain members (pero)
+      </add>      
+      <add>
+        JMX Support for McastService (Membership) (pero)
+      </add>      
+      <add>
+        Redesign SimpleTcpCluster message receiving to ClusterReceiverBase (pero)
+      </add>      
+      <add>
+        Cluster transfer all attributes to the generate session manager at addManager. 
+        Remove some unused attributes at SimpleTcpCluster and ReplicationTransmitter (pero)
+      </add>    
+      <update>
+        Refactor DeltaManager:
+          - createSession call now ManagerBase super class method
+          - extract some long methods
+          - send GET_ALL_SESSION with session blocks
+          - don't sync sessions map when send all sessions (pero)  
+      </update>          
+      <update>
+        Add developer actions at to-do.txt (Proposal of changes) (pero)  
+      </update>          
+      <update>
+        Small refactorings at FastAsyncSocketSender (pero)  
+      </update>          
+      <update>
+        Redesign cluster message sending to lesser cpu and memory usage. 
+        Set at ReplicationTransmitter#compress=false as default. Change API from
+        ClusterSender, ReplicaitonTransmitter, DataSender, SimpleTcpCluster (pero)  
+      </update>          
+      <add>
+        DeltaManager has now JMX expireAllLocalSessions and processExipre operation 
+        for better cluster node shutdown handling (usefull for testing only) (pero)   
+      </add>  
+      <add>
+        DataSender doWaitAckStats for better understanding wait ack problems (pero)   
+      </add>  
+      <update>
+        Refactor DeltaManager and add counter for cluster message send/receive message (pero)  
+      </update>          
+      <fix>
+        <bug>34389</bug>:Porting Clustering fix pack to 5.5.10 code base.
+        Remove synchonized from DataSender.pushMessage(). Very offen the 
+        complete cluster blocking after replicated a bulk of new session messages under heavy load.
+        All cluster node standing for a lot of time and made nothing. 
+        Fix it for pooled, asynchronous and fastasyncqueue replication mode. Very bad thing, sorry! (pero)  
+      </fix>
+      <add>
+        Add notifySessionListenersOnReplication attribute to SimpleTcpCluster to stop notify 
+        event to SessionListener at backup nodes from create and destroy replicated session (pero)
+      </add>
+      <add>
+        Add compress attribute to ClusterSender and ClusterReceiver interface. Now compress config
+        transfer from sender to receiver at SimpleTcpCluster. (pero) 
+      </add>    
+      <add>
+        Add ClusterValve interface and implement it as ReplicationValve and JvmRouteBinderValve. Now both
+        Valves can be directly configured at server.xml Host/Cluster/Valve subelements.
+        Also this configuration are correctly handled with the StoreConfig module. (pero)  
+      </add>    
+      <update>
+        Deactivate DataSender keepAliveMaxRequestCount change default to -1. 
+        Cluster replication sockets are fast and very stable! (pero)  
+      </update>          
+      <update>
+        Setup JvmRouteBinderValve as host valve instead context valve. Refactor the API a little bit. (pero)
+      </update>
+      <fix>
+        Don't increment open socket counter before socket is really open. Add socket open failures counter (pero) 
+      </fix>  
+      <add>
+        Add MessageListener support to cluster server.xml element (ClusterListener) to 
+        register your own cluster message receiver (pero)
+      </add>  
+      <add>
+        Add LifecycleListener support to cluster server.xml element (Listener)
+        and notify those listener from start/stop cluster,
+        add/remove session manager, sending fault and start/stop member  (pero)
+      </add>  
+      <add>
+        Add active backgroundProcess keepAlive timeout and request count socket close check
+        at ReplicationTransmitter.  Check frequency can be change with attribute 
+        processSenderFrequency (default 2). (pero)
+      </add>
+      <add>
+        Remove useless Jdk13ReplicationListener,Jdk13ObjectReader. 
+        Add SocketReplicationListener and SocketObjectReader to have nativ socket ClusterReceiver. 
+        Also extract ClusterReceiverBase superclass for SocketReplicationListener and ReplicationListener (pero) 
+      </add>
+      <update>
+        Add and update some API and the <a href="cluster-howto.html">cluster howto documentation</a> (pero)
+      </update>
+      <update>
+        Refactor ReplicationValve for better understanding and small optimization (pero)
+      </update>
+      <add>
+        Starting a unit test suite for cluster module - very much todo (pero)
+      </add>
+      <fix>
+        Fix ant build.xml to direct compile at cluster module directory (pero)
+      </fix>
+      <fix>
+        Fix some I18N messages, but a lot of work is waiting for fix (pero)
+      </fix>
+      <add>
+        Add ReplicationValve Mbeans stats attribute getter and resetStatistics operation (pero)
+      </add>
+     </changelog>
+  </subsection>
+  
+  <subsection name="Webapps">
+    <changelog>
+      <fix>
+        <bug>35758</bug>: Admin webapp mishandling digest attribute of JDBCDataSourceRealm. (yoavs)
+      </fix>
+      <add>
+        <bug>34250</bug>: Admin webapp Commit Changes button now asks for confirmation. (yoavs)
+      </add>
+      <add>
+        <bug>34818</bug>: Alternating row for apps in HTML manager, as suggested by Jeff
+          Domeyer. (yoavs)
+      </add>
+      <add>
+        <bug>35379</bug>: Added commons-logging to build path of manager and host-manager apps,
+          to make them build with Jikes, as suggested by Aaron Isotton. (yoavs)
+      </add>
+    </changelog>
+  </subsection>
+ </section>
+
+<section name="Tomcat 5.5.9 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <add>
+        Add JULI, a java.util.logging implementation, used to provide sane defaults and
+        configurability equivalent to Tomcat 4.0 for Tomcat 5.5 logging (remm)
+      </add>
+      <docs>
+        Add JULI documentation to the logging page (remm)
+      </docs>
+      <add>
+        Add host manager webapp (remm)
+      </add>
+      <add>
+        Add ant JkStatusUpdateTask for remote status worker handling ( >=mod_jk 1.2.9) (pero)
+      </add>
+      <add>
+        <bug>33739</bug>: Add reference to RUNNING.txt in setup.html. (yoavs)
+      </add>
+      <fix>
+        <bug>33719</bug>: Update reference to Ant download page. (yoavs)
+      </fix>
+      <fix>
+        <bug>33883</bug>: Bad options in SSL-HowTo. (yoavs)
+      </fix>
+      <update>
+        Update to MX4J 3.0.1 (pero)
+      </update>
+      <update>
+        <bug>34139</bug>: Updated Realm-HowTo to specify JMX, Commons-Logging jars for RealmBase. (yoavs)
+      </update>
+      <add>
+        <bug>33325</bug>: Added top-level clean target to Netbuild build.xml file. (yoavs)
+      </add>
+      <update>
+        <bug>33755</bug>: Clarified Postgresql JNDI datasource example. [patch submitted by
+          Tom Witmer] (yoavs)
+      </update>
+    </changelog>
+   </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        Remove some instances of expanded folder removal (remm)
+      </fix>
+      <fix>
+        Don't call mkdirs if we're not going to save the configuration in StandardContext (remm)
+      </fix>
+      <fix>
+        Fix context classloader binding during loader initialization (it was set to null before) (remm)
+      </fix>
+      <fix>
+        The webapp logger should only be retrieved when the context classloader is set to the 
+        webapp's classloader (remm)
+      </fix>
+      <fix>
+        <bug>34170</bug>: Add back retry logic in JDBC realm in case of a connection failure (remm)
+      </fix>
+      <fix>
+        <bug>22041</bug>: Support dynamic proxies as session objects. (markt)
+      </fix>
+      <fix>
+        Fix logger names for wrappers (remm)
+      </fix>
+      <fix>
+        <bug>34006</bug>: If antiResourceLocking was used, HostConfig considered the path as external,
+        and web application resources were not correctly removed or tacked; also simplify the code a lot
+        (remm)
+      </fix>
+      <fix>
+        <bug>34016</bug>: Save and restore docBase when using antiResourceLocking, for compatibility with
+        the admin webapp (remm)
+      </fix>
+      <add>
+        <bug>33636</bug>: Set lastModified attribute when expanding WAR files. (yoavs)
+      </add>
+      <add>
+        <bug>32938</bug>: Allow Salted SHA (SSHA) passwords in JNDIRealm. (yoavs)
+      </add>
+      <add>
+        <bug>31288</bug>: Allow SMTP authentication for JNDI MailSessionFactory. (yoavs)
+      </add>
+      <update>
+        Harmonize processing of the context.xml defaults with the way web.xml is processed
+        (remm)
+      </update>
+      <fix>
+        Ignore ';' if it is in the query string (remm)
+      </fix>
+      <fix>
+        private to protected for the webapp classloader (remm)
+      </fix>
+      <fix>
+        Improve logging of filters and listeners startup errors (remm)
+      </fix>
+      <fix>
+        <bug>33774</bug>: Retry once in JNDI realm authenticate failure regardless of the 
+        exception message (remm)
+      </fix>
+      <fix>
+        <bug>33961</bug>: Don't encode '~' in context paths (remm)
+      </fix>
+      <fix>
+        <bug>32866</bug>: Propagate distributable property from context to manager (yoavs)
+      </fix>
+      <fix>
+        <bug>32867</bug>: Reset distributable attribute in context for clean reload handling (yoavs)
+      </fix>
+      <update>
+          Fix some RealmBase/JNDIRealm log.isXXXEnabled (pero)
+      </update>
+      <fix>
+          <bug>34161</bug>: Harmonize StandardContext.stop with ContainerBase.stop (remm)
+      </fix>
+    </changelog>
+   </subsection>
+   
+   <subsection name="Coyote">
+     <changelog>
+      <fix>
+        <bug>33971</bug>: Set remoteHost to null when Apache doesn't send one. (billbarker)
+      </fix>
+      <fix>
+        Fix calculation of threadRatio for the ms thread pool, and fix setting the updated
+        timeout value (remm)
+      </fix>
+      <update>
+        Update the ms thread pool so that we allocate a worker before accepting a new socket,
+        and wait a little if the pool is exhausted; this should make low maxThreads values work a 
+        lot better (remm)
+      </update>
+      <update>
+        <bug>33857</bug>: Update information on automatic mod_jk configuration in Apache-HowTo (yoavs)
+      </update>
+      <fix>
+        Fix sync block placement in Mapper.addContext (remm)
+      </fix>
+      <fix>
+        <bug>32741</bug>: Fix spelling of "committed" [patch from Ben Souther] (yoavs)
+      </fix>
+      <fix>
+        <bug>34133</bug>: Make setHeader clear multi-valued headers (billbarker)
+      </fix>
+     </changelog>
+   </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        <bug>34034</bug>: Jasper does not respect external entities (billbarker)
+      </fix>
+      <fix>
+        <bug>33810</bug>: Incorrect recycling of BodyContent if close is called (remm)
+      </fix>
+      <update>
+        Per instance loggers in Jasper (remm)
+      </update>
+    </changelog>
+  </subsection>
+   
+   <subsection name="Cluster">
+    <changelog>
+      <fix>
+        Fix JvmRouteBinderValve primary failover attribute to
+        org.apache.catalina.cluster.session.JvmRouteOrignalSessionID (pero)
+      </fix>
+      <fix>
+        Change attribute name waitForAck to sendAck at ReplicationListener (pero)
+      </fix>
+      <add>
+        Integrate new fastasyncqueue cluster sender mode.
+        Support queue size limitation,
+        get all queued objects and send it to the backup node,
+        no queue thread lock contention under high replication load,
+        submitted by Rainer Jung (pero)
+      </add>
+      <add>
+        Add compress attribute to Sender and Receiver to transfer data uncompressed. 
+        At high cluster load this option consume lesser cpu and memory.
+        Implement the compress handling to ReplicationTransmitter, ReplicationListener, 
+        XByteBuffer and Jdk13ReplicationListener (pero)
+      </add>
+      <add>
+        Add doProcessingStats to synchronous, asynchronous and fastqueueasync sender modes
+        to get min, avg, max processing times as IDataSender JMX MBeans (pero)
+      </add>
+      <fix>
+        TcpThreadPool use constant ACK byte array instead create 
+        new 3 byte buffer for every message ack (pero)
+      </fix>
+      <update>
+        Refactor ReplicationTransmitter and ReplicationListener (pero)
+      </update>
+      <update>
+        add getCatalinaCluster() to ClusterReceiver and SimpleTcpCluster (pero)
+      </update>    
+      <update>
+        Update the Api documentation (pero)
+      </update>
+    </changelog>
+   </subsection>
+   <subsection name="Webapps">
+     <changelog>
+       <update>
+         Use the standard struts taglib URIs in admin JSPs. (billbarker)
+       </update>
+      <add>
+        Add more host parameters to create new host with host-manager (pero)
+      </add>
+      <fix>
+        <bug>34033</bug>: Fix quoting related bugs (remm)
+      </fix>
+      <fix>
+        <bug>33713</bug>: Add Struts init code in frameset.jsp as well (remm)
+      </fix>
+     </changelog>
+   </subsection>
+</section>
+
+<section name="Tomcat 5.5.8 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <fix>
+        <bug>33204</bug>: Fixed SSL HowTo page. (yoavs)
+      </fix>
+      <fix>
+        <bug>33351</bug>: Fix silent uninstallation. (remm)
+      </fix>
+      <fix>
+        <bug>33489</bug>: Missing space in uninstaller message. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        Unregister host mbean and all context mbeans at remove a host, s. StandardHost.destroy() and MBeanFactory.createStandardHost/removeHost(,) detected by Thorsten Kamann (pero)
+      </fix>
+      <fix>
+        make it possible to restart connector, now serversocket recreated after stop,start (pero)
+      </fix>
+      <fix>
+        change mbean names from Mapper and ProtocolHandler to connector naming style (pero)
+      </fix>
+      <update>
+        Add some log.isXXXEnabled (pero)
+      </update>
+      <fix>
+        Deregister MapperListener after remove connector (pero)
+      </fix>
+      <fix>
+        Remove host only at own domain with same name at all services, detected by Thorsten Kamann (pero)
+      </fix>
+      <fix>
+        <bug>33187</bug>: Remove any logging of the password in the JAAS realm,
+        submitted by Andrew Jaquith (remm)
+      </fix>
+      <fix>
+        <bug>33033</bug>: Don't do anything to the response in the ErrorReportValve
+        if data has already been written (remm)
+      </fix>
+      <update>
+        Add charset support for the URLs used by the tasks, to remove deprecation (remm)
+      </update>
+      <fix>
+        <bug>26135</bug>: Workaround for memory leak when reloading Struts
+        based web applications by clearing the bean instrospector cache of the JVM on
+        classloader stop, submitted by Tobias Lofstrand. (remm)
+      </fix>
+      <fix>
+         Ensure that if CLASSPATH is declared on startup - it is not used. (funkman)
+      </fix>
+      <fix>
+         Add back use of deployOnStartup in HostConfig (remm)
+      </fix>
+      <docs>
+         Ant tasks docs patches, submitted by Gabriele Garuglieri. (remm)
+      </docs>
+      <update>
+         Use NIO for the raw copying operation, as it is faster (a little under 30%), 
+         and decreases a little the impact of antiResourceLocking. (remm)
+      </update>
+      <fix>
+         <bug>33357</bug>: Fix connection leaks with the DataSourceRealm, as well 
+         as improve efficiency, submitted by Dominik Drzewiecki. (remm)
+      </fix>
+      <update>
+         Improve a little logging of servlet exceptions, which should all log the root cause. (remm)
+      </update>
+      <update>
+         Add new Manager.createSession(sessionId) method, allowing the client to "specify" the session id which should be used using a cookie
+         when using emptySessionPath="true". This fixes session tracking in this case. (remm)
+      </update>
+      <fix>
+         <bug>33368</bug>: Fix memory leak in swallowOutput feature which occurred when the thread pool size is
+         reduced, submitted by Rainer Jung. (remm)
+      </fix>
+	  <fix>
+          StoreConfig: can't save cluster Membership element (pero)
+       </fix>
+	  <add>
+          StoreConfig: suppress default jkHome attribute at connector  (pero)
+      </add>
+	  <add>
+          StoreConfig: Save new dymanic properties from ReplicationTransmitter  (pero)
+      </add>
+      <fix>
+         <bug>33463</bug>: Remove attributes after context destroy. (remm)
+      </fix>
+      <fix>
+         <bug>33572</bug>: context.xml should be a redeploy resource, and add prioritization for
+         redeploy resources. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+  
+  <subsection name="Coyote">
+    <changelog>
+      <fix>
+         PoolTcpEndpoint recreate ServerSocket after start,stop,start connector (pero)
+      </fix>
+      <update>
+        Add some log.isXXXEnabled (pero)
+      </update>
+	  <add>
+	    JkMX: make log4j mbean configurable with attribute log4jEnabled (pero)
+	  </add>
+	  <fix>
+	    When Tomcat runs on Windows and IE is uploading data to the server, the first read 
+	    must be at least 8KB, otherwise upload speed is extremely low, submitted by Noel 
+	    Rocher (remm)
+	  </fix>
+    </changelog>
+  </subsection>
+ 
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        <bug>33223</bug>: pageContext.forward and jsp:include result
+        in StringIndexOutOfBoundsException (luehe)
+      </fix>
+      <fix>
+        <bug>33373</bug>: Fix handling of context classloader in jspc (remm)
+      </fix>
+      <fix>
+        <bug>33538</bug>: Ignore example and tag-extension elements in TagLibraryInfoImpl. (yoavs)
+      </fix>
+      <fix>
+        <bug>33539</bug>: Better error message when an unknown element is encountered in the tag file. (yoavs)
+      </fix>
+      <fix>
+        <bug>33219</bug>: Minor JspServletWrapper code cleanup. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+  
+  <subsection name="Cluster">
+    <changelog>
+       <fix>
+          Add instance based ReplicationValve statistics to Mbean descriptor (pero)
+       </fix>
+       <fix>
+          Better I18N support to cluster session and tcp classes (pero)
+       </fix>
+       <add>
+          Support optional primaryIndicator at ReplicationValve to mark that 
+          request processing to existing session is at primary cluster node. 
+          Easy failover detection, when mark is not at 
+          configurable primaryIndicator attribute, submitted by Rainer Jung (pero)
+       </add>
+       <update>
+          Refactor all implementation from interface IDataSenders (pero)
+       </update>
+      <add>
+          Add some usefull attributes and operations to the all sender MBeans. (pero)
+       </add>
+      <add>
+          Add keepAlive and waitForAck handling to AsyncSocketSender and factor out a DataSender base class.(pero)
+       </add>
+       <add>
+          ReplicationTransmitter: Enable and Disable autoreconnect sender and waitForAck. (pero)
+       </add>
+       <add>
+          ReplicationTransmitter: transfer all properties to socket sender from server.xml configuration. (pero)
+       </add>
+    </changelog>
+  </subsection>
+
+  <subsection name="Webapps">
+    <changelog>
+      <fix>
+        Fix create and remove Host for Admin app. (pero)
+      </fix>
+    </changelog>
+   </subsection>
+</section>
+
+<section name="Tomcat 5.5.7 (remm)">
+  <subsection name="General">
+    <changelog>
+      <add>
+        Add installer for mod_jk on IIS. (mturk)
+      </add>
+      <add>
+        New store config module for better server.xml saving support.<br/>
+        Add &lt;Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener" /&gt; to your server.xml (pero)
+      </add>
+      <update>
+        <bug>32081</bug>: Remove the JDK requirement from the Unix scripts, submitted
+        by Ben Souther (remm)
+      </update>
+      <fix>
+        <bug>32953</bug>: SERVLETAPI: XSS Issues, submitted by Mark Thomas (jfarcand)
+      </fix>
+      <update>
+        Update to commons-digester 1.6, JDT 3.0.1, MX4J 2.1.0, Struts 1.2.6 (remm)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <update>
+         First integration at StoreConfig to StandardServer (pero)
+      </update>
+      <fix>
+        <bug>32714 </bug>: Don't make the AccessLogValve final (funkman)
+      </fix>
+      <fix>
+        <bug>32694</bug>: Fix bad code to make docBase path aboslute in antiLocking
+        method. (remm)
+      </fix>
+      <fix>
+        <bug>32713</bug>: Fix resource-env-ref handling. (remm)
+      </fix>
+      <fix>
+        <bug>31201</bug>: Improve i18n support in DefaultServlet. This was causing
+        problems with JSP include actions and static files. (markt)
+      </fix>
+      <fix>
+        Add some log.isXXXEnabled to o.a.c.core.StandardHost StandardEngine, StandardService (pero)
+      </fix>
+      <add>
+        Feature addition to add Redirector and failOnError support for all Catalina Ant tasks,
+        submitted by Gabriele Garuglieri (remm)
+      </add>
+      <fix>
+        <bug>31198</bug>: Fix FORM and DIGEST authentication for non-ASCII
+        usernames and passwords. (markt)
+      </fix>
+      <fix>
+        Reimplement charset mapper (remm)
+      </fix>
+      <fix>
+        Add logging of exception which could occur when retrieving the password in JDBCRealm (remm)
+      </fix>
+      <fix>
+        <bug>25889</bug>: Don't execute queries twice, submitted by Tom Anderson (remm)
+      </fix>
+      <fix>
+        <bug>32832</bug>: request.getSession(false) fails to return null (luehe)
+      </fix>
+      <fix>
+        <bug>28222</bug>: request.getRequestURL() in forwarded jsp/servlet returns
+        original url rather than new url as per SRV8.4 (markt)
+      </fix>
+      <fix>
+        <bug>33157</bug>: Fix handling of the buffer length for basic authentication parsing (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Coyote">
+    <changelog>
+      <fix>
+        <bug>32708</bug>: Better handling of bad encoding with the string cache. (remm)
+      </fix>
+      <fix>
+        <bug>32781</bug>: Fix bad initialization of the "scheme" field of the request
+        object, which would cause getScheme to return "http" for the first request. (remm)
+      </fix>
+      <fix>
+        Content length should be ignored if there is chunking (remm)
+      </fix>
+      <fix>
+        Remove most deprecation problems for the AJP connector (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        <bug>32746</bug>: Avoid JAR locking when loading classes and improve loading
+        performance by taking advantage of caching, submitted by Dominik Drzewiecki. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Cluster">
+    <changelog>
+       <fix>
+          correct JvmRouteSessionIDBinderListener MBean name to &lt;domain&gt;:type=Listener,name=JvmRouteSessionIDBinderListener,host=&lt;host&gt; (pero)
+       </fix>
+       <add>
+          JMX support to SimpleTcpCluster, ReplicationTransmitter and all senders (pero)
+       </add>
+    </changelog>
+  </subsection>
+
+  <subsection name="Webapps">
+    <changelog>
+      <fix>
+        Fix the webDAV servlet so it can be used via any arbitrary mapping
+        (eg /webdav/*) to edit the contents of a web application. (markt)
+      </fix>
+      <fix>
+        <bug>32729</bug>: Stop is optional and may fail, so it needs to be in a separate try/catch (remm)
+      </fix>
+      <update>
+        Remove the remove method of the manager servlet, and use the undeploy method instead (remm)
+      </update>
+      <fix>
+        <bug>32777</bug>: Fail if application isn't configured properly, submitted by Gabriele Garuglieri
+        (remm)
+      </fix>
+      <fix>
+        <bug>32771</bug>: Cannot undeploy/deploy misconfigured app after tomcat startup,
+        submitted by Gabriele Garuglieri (remm)
+      </fix>
+      <fix>
+        <bug>28867</bug>: Correct manager documentation to document correct way to
+        reference the ROOT context. Submitted by Stephane Bailliez. (markt)
+      </fix>
+      <fix>
+        <bug>33085</bug>: Add support for setting privileged attribute of context
+        to admin webapp. (markt)
+      </fix>
+      <fix>
+        <bug>33117</bug>: Fix Open bugs link broken on default homepage.
+        Patch supplied by Sander Temme. (markt)
+      </fix>
+      <fix>
+        Improve javadoc generation for Catalina. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+
+<section name="Tomcat 5.5.6 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <update>
+        <bug>32532</bug>: updated logging documentation. (yoavs)
+      </update>
+      <update>
+        <bug>32382</bug>: Index page and packaed WAR for sample webapp. (yoavs)
+      </update>
+      <fix>
+        <bug>32603</bug>: Updated host.xml to reflect appBase resolution. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        Add child to the map of the parent before starting it. (remm)
+      </fix>
+      <fix>
+        Decouple usage of the scheme and secure attributes from enabling SSL. (remm)
+      </fix>
+      <fix>
+        <bug>32502</bug>: memory leak in DigestAuthenticator. (yoavs)
+      </fix>
+      <fix>
+        <bug>28709</bug>: javax.servlet.http.HttpServletRequest.isRequestedSessionIdValid() returns true for an invalidated session. (luehe)
+      </fix>
+      <fix>
+        <bug>32137</bug>: Possible thread-safety issue in RealmBase. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Coyote">
+    <changelog>
+      <fix>
+        <bug>32585</bug>: Better handling for content length greater than Integer.MAX_VALUE in response. (markt)
+      </fix>
+      <update>
+        Allow ApacheConfig and friends to live under an Engine. (billbarker)
+      </update>
+      <update>
+        Syncronize access to the Jk Request registration count. (billbarker)
+      </update>
+      <update>
+        Speed the MsgContext on its way to GC. (billbarker)
+      </update>
+      <fix>
+        Keep correct thread counts in Thread pool when thread ends in an exception (billbarker)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <update>
+        Updated Jasper-HowTo section on using Jikes, changed conf/web.xml JSPServlet to refer people to Jasper-HowTo so that we don't have these instructions in two places. (yoavs)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Webapps">
+    <changelog>
+      <fix>
+        <bug>32505</bug>: Fix handling of an empty context parameter (which occurred every time the HTML
+        manager was used to deploy a local war without specifying also a context file). (remm)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+
+<section name="Tomcat 5.5.5 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <update>
+        <bug>32235</bug>: Sync conf/web.xml MIME types with Apache httpd. (yoavs)
+      </update>
+      <fix>
+        <bug>31132</bug>: Better -x/-r support for OS/400 in startup scripts. (yoavs)
+      </fix>
+      <update>
+        <bug>22679</bug>: Added misc note on accessing session ID to SSL-HowTo. (yoavs)
+      </update>
+ <!-- ByteBufferAccessLogValve.java is not inside!!
+      <update>
+        Add an asynchrounous access log valve based on NIO (jfarcand)
+      </update>
+ -->
+       <update>
+        <bug>32249</bug>: Updated logging documentation. (yoavs)
+      </update>
+      <update>
+        <bug>32282</bug>: Modify Windows Uninstaller to only remove webapps/ROOT and webapps if user asks to remove everything. (yoavs)
+      </update>
+      <fix>
+        <bug>32371</bug>: outdated introduction.xml page. (yoavs)
+      </fix>
+      <fix>
+        <bug>32373</bug>: outdated installation.xml page. (yoavs)
+      </fix>
+      <update>
+        <bug>32454</bug>: amended JNDI documentation for JavaMail/JavaActivationFramework usage. (yoavs)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        <bug>32130</bug>: Add safety check to FileStore#keys method. (yoavs)
+      </fix>
+      <update>
+        <bug>32276</bug>: Add developer info to Realm How-To. (yoavs)
+      </update>
+      <fix>
+        <bug>32082</bug>: Added protected getPrincipals method to MemoryRealm for easier extension. (yoavs)
+      </fix>
+      <fix>
+        <bug>32023</bug>: CGIServlet fails to handle post message with multipart/form data. (yoavs)
+      </fix>
+      <fix>
+        <bug>32269</bug>: JNDIRealm fails with InvalidNameException to authenticate users if LDAP distinguished name (DN) contains slash or double quote character(s). (yoavs)
+      </fix>
+     <fix>
+        Move processExpiresFrequency check to ManagerBase and reflect change to all subclasses (StandardManager, PersientManagerBase, DeltaManager). (pero)
+      </fix>
+      <update>
+        Add DIGEST authentication support to the JDBC and DataSource realms. Supports both digested and cleartext passwords. (markt)
+      </update>
+      <fix>
+        <bug>32429</bug>: CGIServlet calculates number of lines received on stderr incorrectly. (markt)
+      </fix>
+      <fix>
+        <bug>32431</bug>: Fix typo in code that passes data to CGI script. (markt)
+      </fix>
+      <fix>
+        <bug>32430</bug>: Class cast exception in toString() method within CGI servlet. (markt)
+      </fix>
+      <fix>
+         Add some log.isXXXEnabled checks at StandardContext and HostConfig (pero)
+      </fix>
+      <fix>
+         Remove the last DefaultContext artifacts (pero)
+      </fix>
+      <fix>
+         <bug>32031</bug>: using createConnector with "http" protocol (remm)
+      </fix>
+      <fix>
+         Add configFile attribute in JMX descriptors (remm)
+      </fix>
+      <fix>
+         Fix autodeployer handling of a war which includes a /META-INF/context.xml, so that it is
+         correctly registered and can be reloaded correctly (remm)
+      </fix>
+      <fix>
+         <bug>32137</bug>: Use of MessageDigest should be synced in DIGEST (remm)
+      </fix>
+      <fix>
+         Add info log when the autodeployer reloads a context (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Coyote">
+    <changelog>
+      <update>
+        Small HTTP/1.1 optimizations: replace usage of Strings with constant byte arrays, and
+        simplify the code converting Strings to bytes (remm)
+      </update>
+      <update>
+        Greatly reduce the amount of recycle method calls on the buffers (remm)
+      </update>
+      <fix>Add null OName check for Request unregistration in Jk, to remove
+           exception under JDK 1.5. (billbarker)
+      </fix>
+      <fix><bug>32292</bug>: Don't send keep-alive header when the protocol
+           can't be parsed. (billbarker)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <update>
+        Updated JspC usage messages to include recently added configurable parameters. (yoavs)
+      </update>
+      <fix>
+        <bug>32330</bug>: JspC changes context classloader. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Cluster">
+    <changelog>
+       <add>
+          JvmRouteBinderValve/JvmRouteSessionIDBinderListener to bind cluster session after primary node failure at first calling backup node.
+          This was an option to have session stickyness after cluster node crashed. Work only with JESSIONID cookies. (pero)
+       </add>
+       <add>
+          Better log support to DeltaManager to see detail information at debug level. (pero)
+       </add>
+       <fix>
+          Fix FarmWarDeployer based on new HostConfig deployer. (pero)
+       </fix>
+       <fix>
+          FarmWarDeployer controlled WarWatcher with engine backgroundProcess call.
+          Added processDeployFrequency attribute to Deployer server.xml element. (pero)
+       </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Webapps">
+    <changelog>
+      <update>
+        <bug>32019</bug>: Remove maxlength=64 restriction on env entry values in admin webapp. (yoavs)
+      </update>
+      <fix>
+        Fix various problems in realm docs, submitted by Phil Mocek. (remm)
+      </fix>
+      <update>
+        Add log4j docs submitted by Allistair Crossley. (remm)
+      </update>
+      <fix><bug>32381</bug>: Fix problem where EL expression is used as a
+        place holder in the admin webapp.
+        Submitted by Allistair Crossley. (billbarker)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+
+<section name="Tomcat 5.5.4 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <update>
+        <bug>31671</bug>: Update web.xml files to 2.4 schema where applicable. (yoavs)
+      </update>
+      <update>
+        <bug>31912</bug>: Add PNG and CSS file types to replication filter default. (yoavs)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        Add processExpiresFrequency to PersistentManagerBase and made some small JDBCStore optimizations (pero)
+      </fix>
+      <fix>
+        Register JSP monitoring mbean for each servlet that declares a jsp-file in web.xml. (luehe)
+      </fix>
+      <fix>
+        <bug>31578</bug>: Update Manager configuration documentation. (yoavs)
+      </fix>
+      <fix>
+        <bug>31273</bug>: Add support for derefaliases in JNDIRealm. (markt)
+      </fix>
+      <fix>
+        <bug>31623</bug>: Better OS400 support in setclasspath.sh. (yoavs)
+      </fix>
+      <add>
+        Extend background processing to most container components. (remm)
+      </add>
+      <fix>
+        Remove all MX4J related code. (remm)
+      </fix>
+      <fix>
+        Update JAR list in TldConfig. (remm)
+      </fix>
+      <add>
+        Register datasources with JMX. With DBCP, this is enough to provide JMX management and monitoring.
+        It might work well with many other data sources which might not register themselves in JMX
+        but do expose their stuff in a java bean fashion. (remm)
+      </add>
+      <update>
+        Add the ability to force session cookies to be set to the root path "/". This should not be used
+        on large servers, otherwise tons of cookies may be sent. (remm)
+      </update>
+      <fix>
+        Workaround for client socket exceptions occurring while running a CGI, which could cause
+        the external process to hang. (remm)
+      </fix>
+      <update>
+        Optimize session cookie IDs conversion to String, since this is an unavoidable and uncacheable
+        operation. (remm)
+      </update>
+      <fix>
+        Add explicit error message if temp dir does not exist, and remove useless calls to initDirs. (remm)
+      </fix>
+      <add>
+        Add an optimized access log valve, supporting hardcoded support for the common and combined patterns,
+        and doing a majority of its write-to-logfile operations asynchronously. (remm)
+      </add>
+      <update>
+        Register an MBean to monitor and manage the StringCache, and allow invoking the reset operation. (remm)
+      </update>
+      <fix>
+        <bug>31677</bug>: Log warning if work dir for context can't be determined. (yoavs)
+      </fix>
+      <fix>
+        <bug>31903</bug>: Fix condition which seems to not have been properly updated after adding
+        entry.binaryContent = null a little below, submitted by Joe Zhou. (remm)
+      </fix>
+      <fix>
+        Prevent silent NPEs during StandardContext.start dealing with JMX registration of realm, submitted
+        by Keith Wannamaker. (remm)
+      </fix>
+      <fix>
+        <bug>31592</bug>: Support other encodings for digests. (yoavs)
+      </fix>
+      <update>
+        <bug>31739</bug>: Minor realm-howto and AJP connector doc updates. (yoavs)
+      </update>
+      <fix>
+        <bug>31753</bug>: Minor inconsistency between JDBC and DataSourceRealm#authenticate. (yoavs)
+      </fix>
+      <update>
+        <bug>31683</bug>: Minor clarifications to realm documentation. (yoavs)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Coyote">
+    <changelog>
+      <fix>
+        Improve i18n in TCP endpoint, and add a better error message when an exception occurs
+        in setSocketOptions. (remm)
+      </fix>
+      <fix>
+        <bug>31663</bug>: Use interval field as the delay for monitor thread. (remm)
+      </fix>
+      <fix>
+        Remove bad shutdown logic for ms pool strategy. (remm)
+      </fix>
+      <fix>
+        Sync with Cookie, by adding ' ' as a special char. If a special char is present,
+        the string will be quoted. If the client doesn't support it, the String will no be quoted anyway
+        and no IAE will be thrown. (remm)
+      </fix>
+      <add>
+        Add an optional String cache for ByteChunk.toString and CharChunk.toString. The cache is
+        unsynchronized during most of its operation, and is static after a training period. An operation
+        is provided to allow resetting the cache. (remm)
+      </add>
+      <update>
+        String caching is enabled by default for ByteChunk. (remm)
+      </update>
+      <fix>
+        <bug>31090</bug>: Use a URL encoded path when setting session cookies. (remm)
+      </fix>
+       <add>
+          Add getAttributeName() to ProtocolHandler to get all attributes at runtime (pero)
+       </add>
+    </changelog>
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <update>
+        Exposed compilerSourceVM and compilerTargetVM options to JspC. (yoavs)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Cluster">
+    <changelog>
+       <fix>
+          DeltaManager and SimpleTcpReplicationManager generate double jvmRoute (pero)
+       </fix>
+       <add>
+          Add some missing Getters and log.isXXXEnableds (pero)
+       </add>
+    </changelog>
+  </subsection>
+
+  <subsection name="Webapps">
+    <changelog>
+      <fix>
+        <bug>31707</bug>: Broken JavaScript confirmation in HTML manager. (yoavs)
+      </fix>
+      <fix>
+        Remove hard-coded admin context path from admin's banner.jsp. (yoavs)
+      </fix>
+      <update>
+        Major connector docs update. (remm)
+      </update>
+      <fix>
+        <bug>31732</bug>: Fix Japanese localization of Manager's list output. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+
+</section>
+
+<section name="Tomcat 5.5.3 (yoavs)">
+
+  <subsection name="General">
+    <changelog>
+      <fix>
+        <bug>30568</bug>: Incomplete setup.html documentation for launching jsvc. (yoavs)
+      </fix>
+      <update>
+        Repackage naming features. (remm)
+      </update>
+      <fix>
+        Fix deployer packaging. (remm)
+      </fix>
+      <fix>
+        Fix embed packaging. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        Fix memory leak when Security Manager is turned on. (jfarcand)
+      </fix>
+      <fix>
+        When checking status codes for error handling, only check if
+        Response.isError() is true. This way, users may use setStatus() to set their own
+        error status without having the error page invoked. (in which case, the user should've
+        use sendError()) (funkman)
+      </fix>
+      <update>
+        Remove Digester code for Xerces workaround. (jfarcand)
+      </update>
+      <fix>
+        Give proper permission to the balancer app when running under the security manager. (jfarcand)
+      </fix>
+      <fix>
+        <bug>30869</bug>: Make sure JAAS realm name is legal. (yoavs)
+      </fix>
+      <update>
+          md5Helper, md5Encoder, and normalize are used by WebdavServlet,
+          not DefaultServelt so move them into WebdavServlet.
+      </update>
+      <fix>
+        <bug>31277</bug>: Clarified automatic application deployment section of Host configuration page. (yoavs)
+      </fix>
+     <fix>
+       <bug>28631</bug>: JAASRealm enhancements to support custom user and role classes  use Commons-Logging. (yoavs)
+     </fix>
+     <fix>
+       <bug>31364</bug>: Missing resource in org.apache.catalina.core.LocalString.properties. (yoavs)
+     </fix>
+     <fix>
+       <bug>31362</bug>: Missing -Xdebug in catalina.bat when launching with JPDA and Security. (yoavs)
+     </fix>
+     <fix>
+       <bug>31356</bug>: Duplicates not counted in session generation. (yoavs)
+     </fix>
+     <fix>
+       <bug>30949</bug>: Make sure ApplicationDispatcher unwraps request/response even if include error occurs. (yoavs)
+     </fix>
+      <fix>
+        Fixed StandardContext.getStartTime() to return actual start time/date instead of time (startupTime) it took to start context. (luehe)
+      </fix>
+      <update>
+        getRequest/getResponse should return the most relevant interface, to avoid casts. (remm)
+      </update>
+      <update>
+        Add check for directory before considering something is a compressed WAR. (remm)
+      </update>
+      <docs>
+        Update the connector documentation. (remm)
+      </docs>
+      <fix>
+        When parsing a context file, ignore the "path" attribute:
+        the only place where it is acceptable is in server.xml. (remm)
+      </fix>
+      <fix>
+        Digester handling fixes: always call reset in a finally block after using a digester. (remm)
+      </fix>
+      <update>
+        Remove many fields from Connector, and tie the creation of the Connector to the
+        creation of the protocol handler. (remm)
+      </update>
+      <update>
+        Remove package triggers from the classloader, which seem useless when using Java 5. (remm)
+      </update>
+      <fix>
+        Realms will now use set attribute to set themselves in their container when using JMX. (remm)
+      </fix>
+      <fix>
+        Fix JMX related operations with the Connector. (remm)
+      </fix>
+      <fix>
+        Fix save-to-XML for naming resources. (remm)
+      </fix>
+      <fix>
+        Remove authenticator "debug" attributes from the descriptors. (remm)
+      </fix>
+      <update>
+        Refactor org.apache.catalina.deploy.ContextXXX to use new super class ResourceBase. (pero)
+      </update>
+      <fix>
+        Enable Connector.findLifecycleListener that we can listen start/stop Connector events and save the listener to xml. (pero)
+      </fix>
+      <update>
+        Remove Watchdog references, as it is no longer used. (yoavs)
+      </update>
+      <fix>
+        <bug>31511</bug>: Don't call setenv.bat if not found, in *using-launcher scripts. (yoavs)
+      </fix>
+      <fix>
+        <bug>31549</bug>: Add name to WebappClassLoader's stopped message. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Coyote">
+    <changelog>
+      <update>
+        Allow customized server header for Standalone. (funkman)
+      </update>
+      <fix>
+        Digester.reset now removes the error handler, the root and calls clear, to prevent
+        any memory leak. (remm)
+      </fix>
+      <update>
+        Remove useless stuff in digester. (remm)
+      </update>
+      <update>
+        In HTTP, add a utility method to convert strings to byte arrays, and output the server header
+        directly as bytes. (remm)
+      </update>
+      <add>
+        Add a master slave thread pool based on the code from Tomcat 4.0. It is less exotic than the
+        default one, and might fare better on some picky systems, such as Redhat 9. The two threadpools
+        will likely be removed once we use the Java 5 API, although more investigation is needed. (remm)
+      </add>
+      <fix>
+        Fix issue with getProperty in IntrospectionUtils. (remm)
+      </fix>
+      <update>
+        Remove attribute translation for SSL in the HTTP protocol handler: it will now be done in the
+        Catalina Connector class. (remm)
+      </update>
+      <fix>
+        Fix handling of the "timeout" attribute of the HTTP protocol handler. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        <bug>31171</bug>: Wrap to avoid ClassCastException in PageContextImpl. (yoavs)
+      </fix>
+      <fix>
+        <bug>31257</bug>: Added specification of endorsed dirs if forking.  Note that this is fairly useless for now in 5.5 since it uses JDT and not javac by default. (yoavs)
+      </fix>
+      <docs>
+        Document new Jasper defaults, and update the production configuration. (remm)
+      </docs>
+      <fix>
+        Copied XML encoding detection logic into JASPER, so we're no longer dependent on Xerces. (luehe)
+      </fix>
+      <fix>
+        Fix cosmetic issue where extra CRLF would be inserted during each precompilation in web.xml. (remm)
+      </fix>
+      <update>
+        Allow configuring the interval following a compilation during which a JSP will not be checked
+        for modifications. (remm)
+      </update>
+      <fix>
+        <bug>31465</bug>: Ensure that the compiler reads the .java file using the same encoding as that with which it was written. (markt)
+      </fix>
+      <fix>
+        <bug>31510</bug>: Null out response in JspWriterImpl#recycle to aid in JBoss memory leak. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Cluster">
+    <changelog>
+      <update>
+        Added flag to the cluster (notifyListenersOnReplication) to enable/disable the
+        notifications of attribute/context listeners upon replication of a session delta
+        Works only with the DeltaManager (fhanik)
+      </update>
+      <update>
+        Added flag to the cluster (Cluster/Sender/ackTimeout) to set the timeout in milliseconds
+        for a synchronous request to go through, defaults to 15000ms (fhanik)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Webapps">
+    <changelog>
+      <fix>
+        <bug>29485</bug>: I broke the HTML manager when adding JavaScript confirmation, fixed now ;) (yoavs)
+      </fix>
+      <fix>
+        <bug>31058</bug>: Ensure StatusTransformer escapes query string for XML. (yoavs)
+      </fix>
+      <update>
+        Added contexts' start time (available from 'startTime' MBean attribute of StandardContext) to status page (luehe)
+      </update>
+      <fix>
+        <bug>31264</bug>: the deploy task should now behave correctly. (remm)
+      </fix>
+      <update>
+        Refactor the manager servlet to make calls to the deployer more robust. (remm)
+      </update>
+      <fix>
+        Use the more robust String.valueOf in the form edit action of the connector. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+
+
+<section name="Tomcat 5.5.2 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <fix>
+        The installer will now use the system's JRE. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        Fix URL generation for classloaders on Windows, causing common/classes and shared/classes
+        to be unusable (remm)
+      </fix>
+      <fix>
+        <bug>31110</bug>: Fix resource packaging bug for servlets (remm)
+      </fix>
+      <fix>
+        Fix 5.5 regression where going through the authenticator would create a session each time. (remm)
+      </fix>
+      <fix>
+        Fix classname of the connector in Embedded, and remove the socket factory. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Coyote">
+    <changelog>
+      <fix>
+        Redo server header handling again. (remm)
+      </fix>
+      <update>
+        Cleanup a little access to the headers using a local variable and
+        use setValue for Server and Date headers. (remm)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        Remove maxTagNesting and curTagNesting since they are unused. (funkman)
+      </fix>
+      <fix>
+        Fix tag files handling with JDT, which were ususable, and refactor the lifecycle handling of
+        the page loader. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Cluster">
+    <changelog>
+    </changelog>
+  </subsection>
+
+  <subsection name="Webapps">
+    <changelog>
+    </changelog>
+  </subsection>
+</section>
+
+<section name="Tomcat 5.5.1 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <update>
+        Tomcat 5.5 can be built on JDK 5.0. (yoavs)
+      </update>
+      <fix>
+        Windows installer polish. (mladen, remm)
+      </fix>
+      <update>
+        Remove dependency on Jakarta regexp. (remm)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        Allow overriding the location of the default context file, similar to the default
+        web.xml. (remm)
+      </fix>
+      <update>
+        Backport if-else logic for SSI servlet from 4.1 (funkman)
+      </update>
+      <fix>
+        Remove DefaultContext elements from the digester rules. (remm)
+      </fix>
+      <fix>
+        Fix ResourceLink handling. (remm)
+      </fix>
+      <fix>
+        Modify the auto deployer to get along with contexts which are statically defined in server.xml. (remm)
+      </fix>
+      <fix>
+        Externalize constant strings defining the location of deployment related resources. (remm)
+      </fix>
+      <fix>
+        <bug>31052</bug>: BeanFactory swallows root cause of exception. (yoavs)
+      </fix>
+      <fix>
+        Allow using deploy Ant task with just config attribute, submitted by Michael Schuerig. (remm)
+      </fix>
+      <add>
+        Added longest time an expired session had been alive to set of monitorable session manager attributes. (luehe)
+      </add>
+      <add>
+        Added average time an expired session had been alive to set of monitorable session manager attributes. (luehe)
+      </add>
+      <fix>
+        Clear a reference in the digester where a context would be referenced for more time than it
+        needed, until the next context deployment operation. (remm)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Coyote">
+    <changelog>
+      <fix>
+        <bug>31018</bug>: Race condition in SystemLogHandler. (yoavs)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        Use the "compiler" parameter to allow specifying that Ant should be used. (remm)
+      </fix>
+      <fix>
+        Ignore JDT compiler warnings. (remm)
+      </fix>
+      <add>
+        Added compilerTargetVM option support, "1.4" default. (yoavs)
+      </add>
+    </changelog>
+  </subsection>
+
+  <subsection name="Cluster">
+    <changelog>
+      <fix>
+        Fix adding the clustering valve, so that session replication actually occurs. (fhanik)
+      </fix>
+    </changelog>
+  </subsection>
+
+  <subsection name="Webapps">
+    <changelog>
+      <update>
+        Major documentation update with current Tomcat 5.5 changes. (remm)
+      </update>
+      <update>
+        Added JavaScript confirmation dialog to "dangerous" Manager servler links. (yoavs)
+      </update>
+    </changelog>
+  </subsection>
+</section>
+
+<section name="Tomcat 5.5.0 (yoavs)">
+  <subsection name="General">
+    <changelog>
+      <update>
+        Many updated and fixed JavaDocs. (yoavs)
+      </update>
+      <update>
+        Designed and tested Tomcat on J2SE 5.0 (aka JDK 1.5). (everyone)
+      </update>
+      <update>
+        Bundled Eclipse JDT (new dependency) to allow Tomcat to run on a JRE only, i.e. no JDK required. (remm)
+      </update>
+      <update>
+        Repackage commons-dbcp and its dependencies as a sigle smaller WAR, with renamed packages. (remm)
+      </update>
+      <update>
+        Removed dependencies on commons-digester, commons-beanutils, and commons-collections.
+        The relevant digester functionality is now merged in tomcat-util. (remm)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Catalina">
+    <changelog>
+      <update>
+        Removed usage of org.apache.catalina.Logger, increased usage of commons-logging everywhere. (remm)
+      </update>
+      <update>
+        Refactored classloader code to better handle JAR and general resource locking. (remm)
+      </update>
+      <update>
+        Written JMX-related code to play nicely with J2SE 5.0 built-in JMX abilities. (remm, costin)
+      </update>
+      <update>
+        Extensively profiled and optimized the server startup performance as well as the request mapping and processing pipeline. (remm)
+      </update>
+      <update>
+        The container will now always process a /META-INF/context.xml resource, unless the webapp has a specified external context file. (remm)
+      </update>
+      <update>
+        New default configuration mechanism for web applications, replacing DefaultContext. This uses a
+        shared context file located in conf/context.xml. (remm)
+      </update>
+      <update>
+        Revamped deployer, alloying full hotdeploy (note: on Windows, this requires the anti file locking
+        features). (remm)
+      </update>
+      <update>
+        Remove verbosity from the JNDI resources configuration, by allowing arbitrary attributes on the Resource element. (remm)
+      </update>
+      <update>
+        Simpler Valve interface, to allow smaller stack traces and reducing the amount of method calls. (remm)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Coyote">
+  </subsection>
+
+  <subsection name="Jasper">
+    <changelog>
+      <update>
+        Eclipse JDT is now the default Java compiler in Jasper. Source dependencies are now loaded from
+        the container classloader, and compilation times are much faster. (remm)
+      </update>
+      <update>
+        Jasper development mode should now have acceptable performance for heavily accessed pages.
+        Precompiling JSPs is still significantly more efficient, however. (remm)
+      </update>
+    </changelog>
+  </subsection>
+
+  <subsection name="Cluster">
+  </subsection>
+
+  <subsection name="Webapps">
+    <changelog>
+    </changelog>
+  </subsection>
+</section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/class-loader-howto.xml b/container/webapps/docs/class-loader-howto.xml
new file mode 100644
index 0000000..8bbe188
--- /dev/null
+++ b/container/webapps/docs/class-loader-howto.xml
@@ -0,0 +1,280 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="class-loader-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+        <author email="yoavs@apache.org">Yoav Shapira</author>
+        <title>Class Loader HOW-TO</title>
+    </properties>
+
+<body>
+
+
+<section name="Quick Start">
+
+<p>The following rules cover about 95% of the decisions that application
+developers and deployers must make about where to place class and resource
+files to make them available to web applications:</p>
+<ul>
+<li>For classes and resources specific to a particular web application,
+    place unpacked classes and resources under <code>/WEB-INF/classes</code>
+    of your web application archive, or place JAR files containing those
+    classes and resources under <code>/WEB-INF/lib</code> of your web
+    application archive.</li>
+<li>For classes and resources that must be shared across all web applications,
+    place unpacked classes and resources under
+    <code>$CATALINA_BASE/shared/classes</code>, or place JAR files
+    containing those classes and resources under
+    <code>$CATALINA_BASE/shared/lib</code>.</li>
+</ul>
+
+</section>
+
+
+<section name="Overview">
+
+<p>Like many server applications, Tomcat 5 installs a variety of class loaders
+(that is, classes that implement <code>java.lang.ClassLoader</code>) to allow
+different portions of the container, and the web applications running on the
+container, to have access to different repositories of available classes and
+resources.  This mechanism is used to provide the functionality defined in the
+Servlet Specification, version 2.4 -- in particular, Sections 9.4 and 9.6.</p>
+
+<p>In a J2SE 2 (that is, J2SE 1.2 or later) environment, class loaders are
+arranged in a parent-child tree.  Normally, when a class loader is asked to
+load a particular class or resource, it delegates the request to a parent
+class loader first, and then looks in its own repositories only if the parent
+class loader(s) cannot find the requested class or resource.  The model for
+web application class loaders differs slightly from this, as discussed below,
+but the main principles are the same.</p>
+
+<p>When Tomcat 5 is started, it creates a set of class loaders that are
+organized into the following parent-child relationships, where the parent
+class loader is above the child class loader:</p>
+
+<source>
+      Bootstrap
+          |
+       System
+          |
+       Common
+      /      \
+ Catalina   Shared
+             /   \
+        Webapp1  Webapp2 ... 
+</source>
+
+<p>The characteristics of each of these class loaders, including the source
+of classes and resources that they make visible, are discussed in detail in
+the following section.</p>
+
+</section>
+
+<section name="Class Loader Definitions">
+
+<p>As indicated in the diagram above, Tomcat 5 creates the following class
+loaders as it is initialized:</p>
+<ul>
+<li><strong>Bootstrap</strong> - This class loader contains the basic runtime
+    classes provided by the Java Virtual Machine, plus any classes from JAR
+    files present in the System Extensions directory
+    (<code>$JAVA_HOME/jre/lib/ext</code>).  <em>NOTE</em> - Some JVMs may
+    implement this as more than one class loader, or it may not be visible
+    (as a class loader) at all.</li>
+<li><strong>System</strong> - This class loader is normally initialized from
+    the contents of the <code>CLASSPATH</code> environment variable.  All such
+    classes are visible to both Tomcat internal classes, and to web
+    applications.  However, the standard Tomcat 5 startup scripts
+    (<code>$CATALINA_HOME/bin/catalina.sh</code> or
+    <code>%CATALINA_HOME%\bin\catalina.bat</code>) totally ignore the contents
+    of the <code>CLASSPATH</code> environment variable itself, and instead
+    build the System class loader from the following repositories:
+    <ul>
+    <li><em>$CATALINA_HOME/bin/bootstrap.jar</em> - Contains the main() method
+        that is used to initialize the Tomcat 5 server, and the class loader
+        implementation classes it depends on.</li>
+    <li><em>$JAVA_HOME/lib/tools.jar</em> - Contains the "javac" compiler used
+        to convert JSP pages into servlet classes.</li>
+    <li><em>$CATALINA_HOME/bin/commons-logging-api.jar</em> - Jakarta commons 
+        logging API.</li>
+    <li><em>$CATALINA_HOME/bin/commons-daemon.jar</em> - Jakarta commons 
+        daemon API.</li>
+    <li><em>jmx.jar</em> - The JMX 1.2 implementation.</li>
+    </ul></li>
+<li><strong>Common</strong> - This class loader contains additional classes
+    that are made visible to both Tomcat internal classes and to all web
+    applications.  Normally, application classes should <strong>NOT</strong>
+    be placed here.  All unpacked classes and resources in
+    <code>$CATALINA_HOME/common/classes</code>, as well as classes and
+    resources in JAR files under the
+    <code>$CATALINA_HOME/commons/endorsed</code>,
+    <code>$CATALINA_HOME/commons/i18n</code> and
+    <code>$CATALINA_HOME/common/lib</code> directories,
+    are made visible through this
+    class loader.  By default, that includes the following:
+    <ul>
+    <li><em>commons-el.jar</em> - Jakarta commons el, implementing the 
+        expression language used by Jasper.</li>
+    <li><em>jasper-compiler.jar</em> - The JSP 2.0 compiler.</li>
+    <li><em>jasper-compiler-jdt.jar</em> - The Eclipse JDT Java compiler.</li>
+    <li><em>jasper-runtime.jar</em> - The JSP 2.0 runtime.</li>
+    <li><em>jsp-api.jar</em> - The JSP 2.0 API.</li>
+    <li><em>naming-common.jar</em> - The JNDI implementation used by Tomcat 5
+        to represent in-memory naming contexts.</li>
+    <li><em>naming-factory.jar</em> - The JNDI implementation used by Tomcat 5
+        to resolve references to enterprise resources (EJB, connection 
+        pools).</li>
+    <li><em>naming-factory-dbcp.jar</em> - Jakarta commons DBCP, providing a
+        JDBC connection pool to web applications. The classes have been moved
+        out of their default org.apache.commons package.</li>
+    <li><em>naming-java.jar</em> - Handler for the java: namespace.</li>
+    <li><em>naming-resources.jar</em> - The specialized JNDI naming context
+        implementation used to represent the static resources of a web
+        application. This is not related to the support of the J2EE ENC, and
+        cannot be removed.</li>
+    <li><em>servlet-api.jar</em> - The Servlet 2.4 API.</li>
+    <li><em>tomcat-i18n-**.jar</em> - Optional JARs containing resource bundles
+        for other languages. As default bundles are also included in each 
+        individual JAR, they can be safely removed if no internationalization
+        of messages is needed.</li>
+    </ul></li>
+<li><strong>Catalina</strong> - This class loader is initialized to include
+    all classes and resources required to implement Tomcat 5 itself.  These
+    classes and resources are <strong>TOTALLY</strong> invisible to web
+    applications.  All unpacked classes and resources in
+    <code>$CATALINA_HOME/server/classes</code>, as well as classes and
+    resources in JAR files under
+    <code>$CATALINA_HOME/server/lib</code>, are made visible through
+    this class loader.  By default, that includes the following:
+    <ul>
+    <li><em>catalina.jar</em> - Implementation of the Catalina servlet
+        container portion of Tomcat 5.</li>
+    <li><em>catalina-ant.jar</em> - Some Ant tasks which can be used to
+        manage Tomcat using the manager web application.</li>
+    <li><em>catalina-optional.jar</em> - Some optional components of
+        Catalina.</li>
+    <li><em>commons-modeler.jar</em> - A model MBeans implementation used
+        by Tomcat to expose its internal objects through JMX.</li>
+    <li><em>servlets-xxxxx.jar</em> - The classes associated with each
+        internal servlet that provides part of Tomcat's functionality.
+        These are separated so that they can be completely removed if the
+        corresponding service is not required, or they can be subject to
+        specialized security manager permissions.</li>
+    <li><em>tomcat-coyote.jar</em> - Coyote API.</li>
+    <li><em>tomcat-http.jar</em> - Standalone Java HTTP/1.1 
+        connector.</li>
+    <li><em>tomcat-ajp.jar</em> - Classes for the Java portion of the
+        <code>AJP</code> web server connector, which allows Tomcat to
+        run behind web servers such as Apache and iPlanet iAS and iWS.</li>
+    <li><em>tomcat-util.jar</em> - Utility classes required by some
+        Tomcat connectors.</li>
+    </ul></li>
+<li><strong>Shared</strong> - This class loader is the place to put classes
+    and resources that you wish to share across <strong>ALL</strong>
+    web applications (unless Tomcat internal classes also need access,
+    in which case you should put them in the <strong>Common</strong>
+    class loader instead).  All unpacked classes and resources in
+    <code>$CATALINA_BASE/shared/classes</code>, as well as classes and
+    resources in JAR files under <code>$CATALINA_BASE/shared/lib</code>, are
+    made visible through this class loader. If multiple Tomcat instances are
+    run from the same binary using the $CATALINA_BASE environment variable,
+    then this classloader repositories are relative to $CATALINA_BASE rather
+    than $CATALINA_HOME.</li>
+<li><strong>WebappX</strong> - A class loader is created for each web
+    application that is deployed in a single Tomcat 5 instance.  All unpacked
+    classes and resources in the <code>/WEB-INF/classes</code> directory of
+    your web application archive, plus classes and resources in JAR files
+    under the <code>/WEB-INF/lib</code> directory of your web application
+    archive, are made visible to the containing web application, but to
+    no others.</li>
+</ul>
+
+<p>As mentioned above, the web application class loader diverges from the
+default Java 2 delegation model (in accordance with the recommendations in the
+Servlet Specification, version 2.3, section 9.7.2 Web Application Classloader).  
+When a request to load a
+class from the web application's <em>WebappX</em> class loader is processed,
+this class loader will look in the local repositories <strong>first</strong>,
+instead of delegating before looking.  There are exceptions. Classes which are
+part of the JRE base classes cannot be overriden. For some classes (such as
+the XML parser components in J2SE 1.4+), the J2SE 1.4 endorsed feature can be 
+used  
+(see the common classloader definition above). In addition, for the following
+class patterns, the classloader will always delegate first 
+(and load the class itself if no parent classloader loads it):
+<ul>
+<li><em>javax.*</em></li>
+<li><em>org.xml.sax.*</em></li>
+<li><em>org.w3c.dom.*</em></li>
+<li><em>org.apache.xerces.*</em></li>
+<li><em>org.apache.xalan.*</em></li>
+</ul>
+Last, any JAR containing servlet API classes will be ignored by the 
+classloader.
+All other class loaders in Tomcat 5 follow the usual delegation pattern.</p>
+
+<p>Therefore, from the perspective of a web application, class or resource
+loading looks in the following repositories, in this order:</p>
+<ul>
+<li>Bootstrap classes of your JVM</li>
+<li>System class loader classses (described above)</li>
+<li><em>/WEB-INF/classes</em> of your web application</li>
+<li><em>/WEB-INF/lib/*.jar</em> of your web application</li>
+<li><em>$CATALINA_HOME/common/classes</em></li>
+<li><em>$CATALINA_HOME/common/endorsed/*.jar</em></li>
+<li><em>$CATALINA_HOME/common/i18n/*.jar</em></li>
+<li><em>$CATALINA_HOME/common/lib/*.jar</em></li>
+<li><em>$CATALINA_BASE/shared/classes</em></li>
+<li><em>$CATALINA_BASE/shared/lib/*.jar</em></li>
+</ul>
+
+</section>
+
+
+<section name="XML Parsers and J2SE 1.4">
+
+<p>Among many other changes, the J2SE 1.4 release packages the JAXP APIs, and
+a version of Xerces, inside the JRE.  This has impacts on applications that
+wish to use their own XML parser.</p>
+
+<p>In previous versions of Tomcat 5, you could simply replace the XML parser
+in the <code>$CATALINA_HOME/common/lib</code> directory to change the parser
+used by all web applications.  However, this technique will not be effective
+when you are running on J2SE 1.4, because the usual class loader delegation
+process will always choose the implementation inside the JDK in preference
+to this one.</p>
+
+<p>JDK 1.4 supports a mechanism called the "Endorsed Standards Override
+Mechanism" to allow replacement of APIs created outside of the JCP (i.e.
+DOM and SAX from W3C).  It can also be used to update the XML parser
+implementation.  For more information, see:
+<a href="http://java.sun.com/j2se/1.4/docs/guide/standards/index.html">
+http://java.sun.com/j2se/1.4/docs/guide/standards/index.html</a>.</p>
+
+<p>Tomcat utilizes this mechanism by including the system property setting
+<code>-Djava.endorsed.dirs=$CATALINA_HOME/common/endorsed</code> in the
+command line that starts the container.  Therefore, you can replace the
+parser that is installed in this directory, and it will get used even on a
+JDK 1.4 system.</p>
+
+</section>
+
+
+<section name="Running under a security manager">
+
+<p>When running under a security manager the locations from which classes
+are permitted to be loaded will also depend on the contents of your policy
+file. See <a href="security-manager-howto.html">Security Manager HOW-TO</a>
+for further information.</p>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/cluster-howto.xml b/container/webapps/docs/cluster-howto.xml
new file mode 100644
index 0000000..c4122f2
--- /dev/null
+++ b/container/webapps/docs/cluster-howto.xml
@@ -0,0 +1,1140 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="cluster-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="fhanik@apache.org">Filip Hanik</author>
+        <author email="pero@apache.org">Peter Rossbach</author>
+        <title>Clustering/Session Replication HOW-TO</title>
+    </properties>
+
+<body>
+
+
+<section name="Quick Start">
+
+<p>To run session replication in your Tomcat 5.5 container, the following steps
+should be completed:</p>
+<ul>
+<li>All your session attributes must implement <code>java.io.Serializable</code></li>
+<li>Uncomment the <code>Cluster</code> element in server.xml</li>
+<li>Uncomment the <code>Valve(ReplicationValve)</code> element in server.xml</li>
+<li>If your Tomcat instances are running on the same machine, make sure the <code>tcpListenPort</code>
+    attribute is unique for each instance.</li>
+<li>Make sure your <code>web.xml</code> has the <code>&lt;distributable/&gt;</code> element 
+    or set at your <code>&lt;Context distributable="true" /&gt;</code></li>
+<li>Make sure that jvmRoute attribute is set at your Engine <code>&lt;Engine name="Catalina" jvmRoute="node1" &gt;</code></li>
+<li>Make sure that all nodes have the same time and sync with NTP service!</li>
+</ul>
+<p>Load balancing can be achieved through many techniques, as seen in the
+<a href="balancer-howto.html">Load Balancing</a> chapter.</p>
+<p>Note: Remember that your session state is tracked by a cookie, so your URL must look the same from the out
+   side otherwise, a new session will be created.</p>
+<p>Note: Clustering support currently requires the JDK version 1.4 or later.</p>
+</section>
+
+
+<section name="Overview">
+
+<p>To enable session replication in Tomcat, three different paths can be followed to achieve the exact same thing:</p>
+<ol>
+  <li>Using session persistence, and saving the session to a shared file system (PersistenceManager + FileStore)</li>
+  <li>Using session persistence, and saving the session to a shared database (PersistenceManager + JDBCStore)</li>
+  <li>Using in-memory-replication, using the SimpleTcpCluster that ships with Tomcat 5 (server/lib/catalina-cluster.jar)</li>
+</ol>
+
+<p>In this release of session replication, Tomcat performs an all-to-all replication of session state.
+
+   This is an algorithm that is only efficient when the clusters are small. For large clusters, the next
+   release will support a primary-secondary session replication where the session will only be stored at one
+   or maybe two backup servers. 
+   Currently you can use the domain worker attribute (mod_jk &gt; 1.2.8) to build cluster partitions
+   with the potential of very scaleable cluster solution.
+   In order to keep the network traffic down in an all-to-all environment, you can split your cluster
+   into smaller groups. This can be easily achieved by using different multicast addresses for the different groups.
+   A very simple setup would look like this:
+   </p>
+
+<source>
+        DNS Round Robin
+               |
+         Load Balancer
+          /           \
+      Cluster1      Cluster2
+      /     \        /     \
+  Tomcat1 Tomcat2  Tomcat3 Tomcat4
+</source>
+
+<p>What is important to mention here, is that session replication is only the beginning of clustering.
+   Another popular concept used to implement clusters is farming, ie, you deploy your apps only to one
+   server, and the cluster will distribute the deployments across the entire cluster.
+   This is all capabilities that can go into with the FarmWarDeployer (s. cluster example at <code>server.xml</code>)</p>
+<p>In the next section will go deeper into how session replication works and how to configure it.</p>
+
+</section>
+
+<section name="How it Works">
+<p>To make it easy to understand how clustering works, We are gonna take you through a series of scenarios.
+   In the scenario we only plan to use two tomcat instances <code>TomcatA</code> and <code>TomcatB</code>.
+   We will cover the following sequence of events:</p>
+
+<ol>
+<li><code>TomcatA</code> starts up</li>
+<li><code>TomcatB</code> starts up (Wait that TomcatA start is complete)</li>
+<li><code>TomcatA</code> receives a request, a session <code>S1</code> is created.</li>
+<li><code>TomcatA</code> crashes</li>
+<li><code>TomcatB</code> receives a request for session <code>S1</code></li>
+<li><code>TomcatA</code> starts up</li>
+<li><code>TomcatA</code> receives a request, invalidate is called on the session (<code>S1</code>)</li>
+<li><code>TomcatB</code> receives a request, for a new session (<code>S2</code>)</li>
+<li><code>TomcatA</code> The session <code>S2</code> expires due to inactivity.</li>
+</ol>
+
+<p>Ok, now that we have a good sequence, we will take you through exactly what happens in the session repliction code</p>
+
+<ol>
+<li><b><code>TomcatA</code> starts up</b>
+    <p>
+        Tomcat starts up using the standard start up sequence. When the Host object is created, a cluster object is associated with it.
+        When the contexts are parsed, if the distributable element is in place in web.xml
+        Tomcat asks the Cluster class (in this case <code>SimpleTcpCluster</code>) to create a manager
+        for the replicated context. So with clustering enabled, distributable set in web.xml
+        Tomcat will create a <code>DeltaManager</code> for that context instead of a <code>StandardManager</code>.
+        The cluster class will start up a membership service (multicast) and a replication service (tcp unicast).
+        More on the architecture further down in this document.
+    </p><p></p>
+</li>
+<li><b><code>TomcatB</code> starts up</b>
+    <p>
+        When TomcatB starts up, it follows the same sequence as TomcatA did with one exception.
+        The cluster is started and will establish a membership (TomcatA,TomcatB).
+        TomcatB will now request the session state from a server that already exists in the cluster,
+        in this case TomcatA. TomcatA responds to the request, and before TomcatB starts listening
+        for HTTP requests, the state has been transferred from TomcatA to TomcatB.
+        In case TomcatA doesn't respond, TomcatB will time out after 60 seconds, and issue a log
+        entry. The session state gets transferred for each web application that has distributable in
+        its web.xml. Note: To use session replication efficiently, all your tomcat instances should be
+        configured the same.
+    </p><p></p>
+</li>
+<li><B><code>TomcatA</code> receives a request, a session <code>S1</code> is created.</B>
+    <p>
+        The request coming in to TomcatA is treated exactly the same way as without session replication.
+        The action happens when the request is completed, the <code>ReplicationValve</code> will intercept
+        the request before the response is returned to the user.
+        At this point it finds that the session has been modified, and it uses TCP to replicata the
+        session to TomcatB. Once the serialized data has been handed off to the operating systems TCP logic,
+        the request returns to the user, back through the valve pipeline.
+        For each request the entire session is replicated, this allows code that modifies attributes
+        in the session without calling setAttribute or removeAttribute to be replicated.
+        a useDirtyFlag configuration parameter can be used to optimize the number of times
+        a session is replicated.
+    </p><p></p>
+
+</li>
+<li><b><code>TomcatA</code> crashes</b>
+    <p>
+        When TomcatA crashes, TomcatB receives a notification that TomcatA has dropped out
+        of the cluster. TomcatB removes TomcatA from its membership list, and TomcatA will no longer
+        be notified of any changes that occurs in TomcatB.
+        The load balancer will redirect the requests from TomcatA to TomcatB and all the sessions
+        are current.
+    </p><p></p>
+</li>
+<li><b><code>TomcatB</code> receives a request for session <code>S1</code></b>
+    <p>Nothing exciting, TomcatB will process the request as any other request.
+    </p><p></p>
+</li>
+<li><b><code>TomcatA</code> starts up</b>
+    <p>Upon start up, before TomcatA starts taking new request and making itself
+    available to it will follow the start up sequence described above 1) 2).
+    It will join the cluster, contact TomcatB for the current state of all the sessions.
+    And once it receives the session state, it finishes loading and opens its HTTP/mod_jk ports.
+    So no requests will make it to TomcatA until it has received the session state from TomcatB.
+    </p><p></p>
+</li>
+<li><b><code>TomcatA</code> receives a request, invalidate is called on the session (<code>S1</code>)</b>
+    <p>The invalidate is call is intercepted, and the session is queued with invalidated sessions.
+        When the request is complete, instead of sending out the session that has changed, it sends out
+        an "expire" message to TomcatB and TomcatB will invalidate the session as well.
+    </p><p></p>
+
+</li>
+<li><b><code>TomcatB</code> receives a request, for a new session (<code>S2</code>)</b>
+    <p>Same scenario as in step 3)
+    </p><p></p>
+
+
+</li>
+<li><code>TomcatA</code> The session <code>S2</code> expires due to inactivity.
+    <p>The invalidate is call is intercepted the same was as when a session is invalidated by the user,
+       and the session is queued with invalidated sessions.
+       At this point, the invalidet session will not be replicated across until
+       another request comes through the system and checks the invalid queue.
+    </p><p></p>
+</li>
+</ol>
+
+<p>Phuuuhh! :)</p>
+
+<p><b>Membership</b>
+    Clustering membership is established using very simple multicast pings.
+    Each Tomcat instance will periodically send out a multicast ping,
+    in the ping message the instance will broad cast its IP and TCP listen port
+    for replication.
+    If an instance has not received such a ping within a given timeframe, the
+    member is considered dead. Very simple, and very effective!
+    Of course, you need to enable multicasting on your system.
+</p>
+
+<p><b>TCP Replication</b>
+    Once a multicast ping has been received, the member is added to the cluster
+    Upon the next replication request, the sending instance will use the host and
+    port info and establish a TCP socket. Using this socket it sends over the serialized data.
+    The reason I choose TCP sockets is because it has built in flow control and guaranteed delivery.
+    So I know, when I send some data, it will make it there :)
+</p>
+
+<p><b>Distributed locking and pages using frames</b>
+    Tomcat does not keep session instances in sync across the cluster.
+    The implementation of such logic would be to much overhead and cause all
+    kinds of problems. If your client accesses the same session
+    simultanously using multiple requests, then the last request
+    will override the other sessions in the cluster.
+</p>
+
+</section>
+
+<section name="Cluster Architecture">
+
+<p><b>Component Levels:</b>
+<source>
+         Server
+           |
+         Service
+           |
+         Engine
+           |  \ 
+           |  --- Cluster --*
+           |
+         Host
+           |
+         ------
+        /      \
+     Cluster    Context(1-N)                 
+        |             \
+        |             -- Manager
+        |                   \
+        |                   -- DeltaManager
+        |
+     -----------------------------
+     |          |         |       \
+   Receiver    Sender   Membership  \
+     \                               -- Valve
+     -- SocketReplicationListener    |      \
+     -- ReplicationListener          |       -- ReplicationValve
+                                     |       -- JvmRouteBinderValve 
+                                     |
+                                     -- LifecycleListener 
+                                     |
+                                     -- ClusterListener 
+                                     |      \
+                                     |       -- ClusterSessionListener
+                                     |       -- JvmRouteSessionIDBinderListener
+                                     |
+                                     -- Deployer 
+                                            \
+                                             -- FarmWarDeployer
+      
+      
+</source>
+<source>
+   Sender
+    \
+    -- ReplicationTransmitter 
+             |
+             ---------
+                      \
+                   IDataSender
+                          \
+                          |
+                          --- (sync)
+                          |  \
+                          |   -- PooledSocketSender   (pooled)
+                          |   -- SockerSender         (synchronous)
+                          |                                
+                          --- (async)
+                             \
+                              -- AsyncSocketSender     (synchronous)
+                              -- FastAsyncSocketSender (fastasyncqueue)         
+</source>
+</p>
+
+</section>
+
+
+<section name="Cluster Configuration">
+<p>The cluster configuration is described in the sample server.xml file.
+What is worth to mention is that the attributes starting with mcastXXX
+are for the membership multicast ping, and the attributes starting with tcpXXX
+are for the actual TCP replication.
+</p>
+<p>
+    The membership is established by all the tomcat instances are sending broadcast messages
+    on the same multicast IP and port.
+    The TCP listen port, is the port where the session replication is received from other members.
+</p>
+<p>
+    The replication valve is used to find out when the request has been completed and initiate the
+    replication.
+</p>
+<p>
+    One of the most important performance considerations is the synchronous (pooled or not pooled) versus asynchronous replication
+    mode. In a synchronous replication mode the request doesn't return until the replicated session has been
+    sent over the wire and reinstantiated on all the other cluster nodes.
+    There are two settings for synchronous replication. Pooled or not pooled.
+    Not pooled (ie replicationMode=&quot;fastasnycqueue&quot; or &quot;synchronous&quot;) means that all the replication request are sent over a single
+    socket.
+    Using synchronous mode can potentially becomes a bottleneck when a lot of messages generated,
+    You can overcome this bottleneck by setting replicationMode=&quot;pooled&quot; but then you worker threads blocks with replication .
+    What is recommended here is to increase the number of threads that handle
+    incoming replication request. This is the tcpThreadCount property in the cluster
+    section of server.xml. The pooled setting means that we are using multiple sockets, hence increases the performance.
+    Asynchronous replication, should be used if you have sticky sessions until fail over, then
+    your replicated data is not time crucial, but the request time is, at this time leave the tcpThreadCount to
+    be number-of-nodes-1.
+    During async replication, the request is returned before the data has been replicated. async replication yields shorter
+    request times, and synchronous replication guarantees the session to be replicated before the request returns.
+</p>
+<p>
+    The parameter &quot;replicationMode&quot; has four different settings: &quot;pooled&quot;, &quot;synchronous&quot;, &quot;asynchronous&quot; and &quot;fastasyncqueue&quot;
+</p>
+
+<section name="Simple Cluster Configuration">
+<p>
+Simple one line configuration<br/>
+<source>
+   &lt;Server                 port="8011" 
+                       shutdown="SHUTDOWN" &gt;
+    &lt;GlobalNamingResources&gt;
+    &lt;Resource              name="UserDatabase" auth="Container"
+                           type="org.apache.catalina.UserDatabase"
+                    description="User database that can be updated and saved"
+                        factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
+                        pathname="conf/tomcat-users.xml" /&gt;
+  &lt;/GlobalNamingResources&gt;
+    &lt;Service              name="Catalina"&gt;
+        &lt;Connector        port="9012" 
+                      protocol="AJP/1.3"
+        &lt;Connector         port="9013"
+                     maxThreads="10"
+                minSpareThreads="4"
+                maxSpareThreads="4"
+        /&gt;
+        &lt;Engine            name="Catalina" 
+                   defaultHost="localhost" 
+                        jvmRoute="node1"&gt;
+        &lt;Realm        className="org.apache.catalina.realm.UserDatabaseRealm"
+                   resourceName="UserDatabase" /&gt;
+            &lt;Host          name="localhost"
+                        appBase="webapps"&gt;
+             &lt;Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"/&gt;
+            &lt;/Host&gt;
+        &lt;/Engine&gt;
+    &lt;/Service&gt;
+&lt;/Server&gt;
+</source>
+<br/>
+The default mode configuration setup a <em>fastasyncmode</em> cluster configuration with following
+parameters:
+<ul>
+    <li>Open Membership receiver at <em>228.0.0.4</em> and send to multicast udp port <em>8012</em></li>
+    <li>Send membership every 1 sec and drop member after 30sec.</li>
+    <li>Open message receiver at default ip interface at first free port between <em>8015</em> and <em>8019</em>.</li>
+    <li>Receiver message with <em>SocketReplicationListener</em> </li>
+    <li>Configure a <em>ReplicationTransmitter</em> with <em>fastasyncmode</em> sender mode.</li>
+    <li>Receiver message with <em>SocketReplicationListener</em>.</li>
+    <li>Add <em>ClusterSessionListener</em> and <em>ReplicationValve</em>.</li>
+</ul> 
+</p>
+<p>
+<b>NOTE</b>: Use this configuration when you need very quick a test cluster with
+at your developer machine. You can change the default attributes from cluster sub elements.
+Use following cluster attribute prefixes <em>sender.</em>,
+<b>receiver.</b>, <b>service.</b>, <b>manager.</b>, <b>valve.</b> and <b>listener.</b>.
+<br/><b>Example</b> configure cluster at windows laptop with network connection and
+change receiver port range<br/>
+<source>
+&lt;Cluster                 className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
+          service.mcastBindAddress="127.0.0.1" 
+            receiver.tcpListenPort="9070" 
+         receiver.tcpListenMaxPort="9075" /&gt;
+</source>    
+<br/>       
+<b>WARNING</b>: When you add you sub elements, there overwrite the defaults complete.
+<br/><b>Example</b> configure cluster with cluster failover jsessionid support. In this
+case you need also the defaultmode Cluster listener <em>ClusterSessionListener</em> and <em>ReplicationValve</em>.<br/>
+<source>
+&lt;Cluster                 className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
+          service.mcastBindAddress="127.0.0.1" 
+            receiver.tcpListenPort="9070" 
+         receiver.tcpListenMaxPort="9075" &gt;
+       &lt;ClusterListener  className="org.apache.catalina.cluster.session.ClusterSessionListener" /&gt;
+       &lt;ClusterListener  className="org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListener" /&gt;
+       &lt;Valve            className="org.apache.catalina.cluster.tcp.ReplicationValve"
+                            filter=".*\.gif;.*\.js;.*\.css;.*\.png;.*\.jpeg;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"
+                  primaryIndicator="true" /&gt;
+	   &lt;Valve            className="org.apache.catalina.cluster.session.JvmRouteBinderValve"
+	                      enabled="true"  /&gt;
+&lt;Cluster/&gt;
+</source> 
+</p>
+</section>
+
+<section name="Simple Engine Cluster Configuration for all hosts">
+<p>
+Simple one line engine configuration<br/>
+<source>
+   &lt;Server                 port="8011" 
+                       shutdown="SHUTDOWN" &gt;
+    &lt;GlobalNamingResources&gt;
+    &lt;Resource              name="UserDatabase" auth="Container"
+                           type="org.apache.catalina.UserDatabase"
+                    description="User database that can be updated and saved"
+                        factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
+                        pathname="conf/tomcat-users.xml" /&gt;
+  &lt;/GlobalNamingResources&gt;
+    &lt;Service              name="Catalina"&gt;
+        &lt;Connector        port="9012" 
+                      protocol="AJP/1.3"
+        &lt;Connector         port="9013"
+                     maxThreads="10"
+                minSpareThreads="4"
+                maxSpareThreads="4"
+        /&gt;
+        &lt;Engine            name="Catalina" 
+                   defaultHost="localhost" 
+                        jvmRoute="node1"&gt;
+        &lt;Realm        className="org.apache.catalina.realm.UserDatabaseRealm"
+                   resourceName="UserDatabase" /&gt;
+        &lt;Cluster      className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"/&gt;
+            &lt;Host          name="localhost"
+                        appBase="webapps"/&gt;
+        &lt;/Engine&gt;
+    &lt;/Service&gt;
+&lt;/Server&gt;
+</source>
+<br/>
+See default mode configuration description as simple host cluster example before.
+</p>
+</section>
+
+<section name="Complex Cluster Configuration">
+<p>
+<br/><b>Example</b> Configure cluster with complete sub elements. Activate this node
+as master farm delopyer. Message receiver is NIO based <em>ReplicationListener</em> with six parallel
+worker threads.
+<br/>
+<source>
+       &lt;Server                 port="8011" 
+                       shutdown="SHUTDOWN" &gt;
+    &lt;GlobalNamingResources&gt;
+    &lt;Resource              name="UserDatabase" auth="Container"
+                           type="org.apache.catalina.UserDatabase"
+                    description="User database that can be updated and saved"
+                        factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
+                        pathname="conf/tomcat-users.xml" /&gt;
+  &lt;/GlobalNamingResources&gt;
+    &lt;Service              name="Catalina"&gt;
+        &lt;Connector        port="9012" 
+                      protocol="AJP/1.3"
+        &lt;Connector         port="9013"
+                     maxThreads="10"
+                minSpareThreads="4"
+                maxSpareThreads="4"
+        /&gt;
+        &lt;Engine            name="Catalina" 
+                   defaultHost="localhost" 
+                        jvmRoute="node1"&gt;
+        &lt;Realm        className="org.apache.catalina.realm.UserDatabaseRealm"
+                   resourceName="UserDatabase" /&gt;
+            &lt;Host          name="localhost"
+                        appBase="webapps"&gt;
+                &lt;Cluster                  className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
+                                       doClusterLog="true"
+                                     clusterLogName="clusterlog"
+                                  manager.className="org.apache.catalina.cluster.session.DeltaManager"
+                   manager.expireSessionsOnShutdown="false"
+               manager.notifyListenersOnReplication="false"
+        manager.notifySessionListenersOnReplication="false"
+                            manager.sendAllSessions="false"
+                        manager.sendAllSessionsSize="500"
+                    manager.sendAllSessionsWaitTime="20"&gt;
+                  &lt;Membership 
+                                          className="org.apache.catalina.cluster.mcast.McastService"
+                                          mcastAddr="228.0.0.4"
+                                   mcastBindAddress="127.0.0.1" 
+                                 mcastClusterDomain="d10" 
+                                          mcastPort="45564"
+                                     mcastFrequency="1000"
+                                      mcastDropTime="30000"/&gt;
+                  &lt;Receiver 
+                                           className="org.apache.catalina.cluster.tcp.ReplicationListener"
+                                    tcpListenAddress="auto"
+                                       tcpListenPort="9015"
+                                  tcpSelectorTimeout="100"
+                                      tcpThreadCount="6"
+                  &lt;Sender
+                                           className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+                                     replicationMode="fastasyncqueue"
+                        doTransmitterProcessingStats="true"
+                                   doProcessingStats="true"
+                                      doWaitAckStats="true"
+                                       queueTimeWait="true"
+                                        queueDoStats="true"
+                                      queueCheckLock="true"
+                                          ackTimeout="15000"
+                                          waitForAck="true"
+                                    keepAliveTimeout="80000"
+                            keepAliveMaxRequestCount="-1"/&gt;
+                  &lt;Valve                   className="org.apache.catalina.cluster.tcp.ReplicationValve"
+                                              filter=".*\.gif;.*\.js;.*\.css;.*\.png;.*\.jpeg;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"
+                                    primaryIndicator="true" /&gt;
+                  &lt;Valve                    className="org.apache.catalina.cluster.session.JvmRouteBinderValve"
+                                             enabled="true" /&gt;	
+                  &lt;ClusterListener         className="org.apache.catalina.cluster.session.ClusterSessionListener" /&gt;
+                  &lt;ClusterListener         className="org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListener" /&gt;
+                  &lt;Deployer                className="org.apache.catalina.cluster.deploy.FarmWarDeployer"
+                                            tempDir="${catalina.base}/war-temp"
+                                          deployDir="${catalina.base}/war-deploy/"
+                                           watchDir="${catalina.base}/war-listen/"
+                                       watchEnabled="true"/&gt;
+                  &lt;/Cluster&gt;
+            &lt;/Host&gt;
+        &lt;/Engine&gt;
+    &lt;/Service&gt;
+&lt;/Server&gt;
+</source>
+</p>
+</section>
+
+<section name="Cluster Configuration for ReplicationTransmitter">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+
+  <tr>
+    <td>replicationMode</td>
+    <td>replication mode (<em>synchronous</em>, <em>pooled</em>, <em>asynchronous</em> or <em>fastasyncqueue</em>)
+    </td>
+    <td><code>pooled</code></td>
+  </tr>
+
+  <tr>
+    <td>processSenderFrequency</td>
+    <td>Control the sender keepalive status and drop sender socket connection after timeout is reached.
+    Check every processSenderFrequency value engine background ticks.
+    </td>
+    <td><code>2</code></td>
+  </tr>
+
+  <tr>
+    <td>compress</td>
+    <td>compress bytes before sending (consume memory, but reduce network traffic - GZIP)</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>ackTimeout</td>
+    <td>acknowledge timeout</td>
+    <td><code>15000</code></td>
+  </tr>
+  
+  <tr>
+    <td>waitForAck</td>
+    <td>Wait for ack after data send</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>autoConnect</td>
+    <td>is sender disabled, fork a new socket</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>doTransmitterProcessingStats</td>
+    <td>create processing time stats</td>
+    <td><code>false</code></td>
+  </tr>
+</table>
+</p>
+<p>
+Example to get statistic information, wait for ack at every message send and transfer at compressed mode<br/>
+<source>
+    &lt;Sender
+      className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+      replicationMode="fastasyncqueue"
+      compress="true"
+      doTransmitterProcessingStats="true"
+      ackTimeout="15000"
+      waitForAck="true"
+      autoConnect="false"/&gt;
+</source>
+</p>  
+</section>
+     
+<section name="Cluster Configuration for ReplicationTransmitter (fastayncqueue - mode)">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+  
+  <tr>
+    <td>keepAliveTimeout</td>
+    <td>active socket keep alive timeout</td>
+    <td><code>60000</code></td>
+  </tr>  
+
+  <tr>
+    <td>keepAliveMaxRequestCount</td>
+    <td>max request over this socket</td>
+    <td><code>-1</code></td>
+  </tr>  
+
+  <tr>
+    <td>doProcessingStats</td>
+    <td>create Processing time stats</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>doWaitAckStats</td>
+    <td>create waitAck time stats</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>resend</td>
+    <td>resend message after failure, can overwrite at message</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>queueDoStats</td>
+    <td>activated queue stats</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>queueCheckLock</td>
+    <td>check to lost locks</td>
+    <td><code>false</code></td>
+  </tr>
+  <tr>
+    <td>queueAddWaitTimeout</td>
+    <td>queue add wait time (tomcat connector thread waits)</td>
+    <td><code>10000</code></td>
+  </tr>
+  <tr>
+    <td>queueRemoveWaitTimeout</td>
+    <td>queue remove wait time (queue thread waits)</td>
+    <td><code>30000</code></td>
+  </tr>
+  
+  <tr>
+    <td>maxQueueLength</td>
+    <td>max queue length (default without limit)</td>
+    <td><code>-1</code></td>
+  </tr>
+  
+  <tr>
+    <td>threadPriority</td>
+    <td>change queue thread priority (1-10 ; 5 is normal)</td>
+    <td><code>5</code></td>
+  </tr>
+</table>
+  
+</p>
+<p>
+Example to get a lot of statistic information and not wait for ACK<br/>
+<source>
+    &lt;Sender
+      className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+      replicationMode="fastasyncqueue"
+      doTransmitterProcessingStats="true"
+      doProcessingStats="true"
+      doWaitAckStats="true"
+      queueTimeWait="true"
+      queueDoStats="true"
+      queueCheckLock="true"
+      waitForAck="false"
+      autoConnect="false"
+      keepAliveTimeout="320000"
+      keepAliveMaxRequestCount="-1"/&gt;
+</source>
+</p>  
+</section>
+
+<section name="Cluster Configuration for ReplicationTransmitter ( asynchronous - mode)">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+  
+  <tr>
+    <td>keepAliveTimeout</td>
+    <td>active socket keep alive timeout</td>
+    <td><code>60000</code></td>
+  </tr>  
+
+  <tr>
+    <td>keepAliveMaxRequestCount</td>
+    <td>max request over this socket</td>
+    <td><code>-1</code></td>
+  </tr>  
+
+  <tr>
+    <td>doProcessingStats</td>
+    <td>create Processing time stats</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>doWaitAckStats</td>
+    <td>create waitAck time stats</td>
+    <td><code>false</code></td>
+  </tr>
+  
+  <tr>
+    <td>resend</td>
+    <td>resend message after failure, can overwrite at message</td>
+    <td><code>false</code></td>
+  </tr>
+
+</table>
+</p>
+<p>
+Example to get a processing statistic information, resend after failure and wait for ACK<br/>
+<source>
+    &lt;Sender
+      className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+      replicationMode="asynchronous"
+      doProcessingStats="true"
+      doWaitAckStats="true"
+      resend="true"
+      keepAliveTimeout="320000"
+      keepAliveMaxRequestCount="-1"/&gt;
+</source>
+</p>  
+</section>
+    
+<section name="Cluster Configuration for ReplicationTransmitter ( synchronous - mode)">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+  
+  <tr>
+    <td>keepAliveTimeout</td>
+    <td>active socket keep alive timeout</td>
+    <td><code>60000</code></td>
+  </tr>  
+
+  <tr>
+    <td>keepAliveMaxRequestCount</td>
+    <td>max request over this socket</td>
+    <td><code>-1</code></td>
+  </tr>  
+
+  <tr>
+    <td>doProcessingStats</td>
+    <td>create Processing time stats</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>doWaitAckStats</td>
+    <td>create waitAck time stats</td>
+    <td><code>true</code></td>
+  </tr>
+
+  <tr>
+    <td>resend</td>
+    <td>resend message after failure, can overwrite at message</td>
+    <td><code>false</code></td>
+  </tr>
+
+</table>
+  
+</p>
+<p>
+Example to get a no processing statistic information, no wait for ACK, after 10000 request renew socket and autoconnect before first request is send.<br/>
+<source>
+    &lt;Sender
+      className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+      replicationMode="synchronous"
+      autoConnect="true"
+      keepAliveTimeout="-1"
+      keepAliveMaxRequestCount="10000"/&gt;
+</source>
+</p>  
+</section>
+
+<section name="Cluster Configuration for ReplicationTransmitter ( pooled - mode)">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+  
+  <tr>
+    <td>keepAliveTimeout</td>
+    <td>active socket keep alive timeout</td>
+    <td><code>60000</code></td>
+  </tr>  
+
+  <tr>
+    <td>keepAliveMaxRequestCount</td>
+    <td>max request over this socket</td>
+    <td><code>-1</code></td>
+  </tr>  
+
+  <tr>
+    <td>maxPoolSocketLimit</td>
+    <td>max pooled sockets (Sender Sockets)</td>
+    <td><code>25</code></td>
+  </tr>
+  
+  <tr>
+    <td>resend</td>
+    <td>resend message after failure, can overwrite at message</td>
+    <td><code>false</code></td>
+  </tr>
+  
+</table>
+  
+</p>
+<p>
+Example to get a no processing statistic information, wait for ACK, after 10000 request renew socket, only 10 SockerSender available and autoconnect before first request is send.<br/>
+<source>
+    &lt;Sender
+      className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+      replicationMode="pooled"
+      autoConnect="true"
+      maxPoolSocketLimit="10"
+      keepAliveTimeout="-1"
+      keepAliveMaxRequestCount="10000"/&gt;
+</source>
+</p>  
+</section>
+
+<section name="Cluster Configuration for ReplicationTransmitter ( DeltaManager Attribute)">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+  
+  <tr>
+    <td>expireSessionsOnShutdown</td>
+    <td>When server stopped, expire all sessions also at backup nodes (only for testing)</td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>maxActiveSessions</td>
+    <td>Number of active sessions. (Default is no limit)</td>
+    <td><code>-1</code></td>
+  </tr>  
+
+  <tr>
+    <td>notifyListenersOnReplication</td>
+    <td>Notify application session listener to session creation 
+    and expiring events at backup nodes</td>
+    <td><code>true</code></td>
+  </tr>
+
+  <tr>
+    <td>notifySessionListenersOnReplication</td>
+    <td>Notify application session listener to attribute changes at backup nodes</td>
+    <td><code>true</code></td>
+  </tr>
+
+  <tr>
+    <td>stateTransferTimeout</td>
+    <td>Timeout that session state transfer is complete. Is attribute <code>stateTransferTimeout == -1</code> 
+        then application wait that other node send the complete session state</td>
+    <td><code>60</code></td>
+  </tr>
+  
+  <tr>
+    <td>sendAllSessions</td>
+    <td>Flag to send sessions as splited blocks</td>
+    <td><code>true</code></td>
+  </tr>
+
+  <tr>
+    <td>sendAllSessionsSize</td>
+    <td>Number of serialize sessions inside a send block session message. Only useful when <code>sendAllSessions==false</code></td>
+    <td><code>1000</code></td>
+  </tr>
+
+  <tr>
+    <td>sendAllSessionsWaitTime</td>
+    <td>wait time between two session send blocks.</td>
+    <td><code>2000</code></td>
+  </tr>
+
+  <tr>
+    <td>sendClusterDomainOnly</td>
+    <td>Send all session messages only to member inside same cluster domain 
+        (value od Membership attribute mcastClusterDomain). Also don't handle
+        session messages from other domains.</td>
+    <td><code>true</code></td>
+  </tr>  
+
+  <tr>
+    <td>stateTimestampDrop</td>
+    <td>DeltaManager queued Sessions messages when send GET_ALL_SESSION to other node.
+    with stateTimestampDrop all messages before state transfer message creation date (find session) are dropped.
+    Only other GET_ALL_SESSION events are handle with date before state transfer message.</td>
+    <td><code>true</code></td>
+  </tr>  
+  
+</table>
+  
+</p>
+<p>
+Example send all sessions at blocks. Serialize and send 100 session inside on block.<br/>
+<source>
+    &lt;Sender
+      className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+      replicationMode="fastasyncqueue"
+      sendAllSessions="false"
+      sendAllSessionsSize="100"
+      keepAliveTimeout="-1"
+      keepAliveMaxRequestCount="-1"/&gt;
+</source>
+</p>
+<p>
+<b>Note:</b><br/>
+As <em>Cluster.defaultMode=true</em> you can configure the manager attributes with prefix <em>manager.</em>. 
+<br/>
+<b>Note:</b><br/>
+With <em>Cluster.setProperty(&lt;String&gt;,&lt;String&gt;)</em> you can modify 
+attributes for all register managers. The method exists as MBeans operation.
+</p>
+</section>
+
+</section>
+
+
+<section name="Monitoring your Cluster with JMX">
+<p>Monitoring is a very important question when you use a cluster. Some of the cluster objects are JMX MBeans </p>
+<p>Add the following parameter to your startup script with Java 5:
+<source>
+set CATALINA_OPTS=\
+-Dcom.sun.management.jmxremote \
+-Dcom.sun.management.jmxremote.port=%my.jmx.port% \
+-Dcom.sun.management.jmxremote.ssl=false \
+-Dcom.sun.management.jmxremote.authenticate=false
+</source>
+</p>
+<p>Activate JMX with JDK 1.4:
+<ol>
+<li>Install the compat package</li>
+<li>Install the mx4j-tools.jar at common/lib (use the same mx4j version as your tomcat release)</li>
+<li>Configure a MX4J JMX HTTP Adaptor at your AJP Connector<p></p>
+<source>
+&lt;Connector port="${AJP.PORT}" handler.list="mx" mx.enabled="true" mx.httpHost="${JMX.HOST}" mx.httpPort="${JMX.PORT}" protocol="AJP/1.3" /&gt;
+</source>
+</li>
+<li>Start your tomcat and look with your browser to http://${JMX.HOST}:${JMX.PORT}</li>
+<li>With the connector parameter <code>mx.authMode="basic" mx.authUser="tomcat" mx.authPassword="strange"</code> you can control the access!</li>
+</ol>
+</p>
+<p>
+List of Cluster Mbeans<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Name</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">MBean ObjectName - Engine</th>
+    <th align="center" bgcolor="aqua">MBean ObjectName - Host</th>
+  </tr>
+
+  <tr>
+    <td>Cluster</td>
+    <td>The complete cluster element</td>
+    <td><code>type=Cluster</code></td>
+    <td><code>type=Cluster,host=${HOST}</code></td>
+  </tr>
+ 
+  <tr>
+    <td>ClusterSender</td>
+    <td>Configuration and stats of the sender infrastructure</td>
+    <td><code>type=ClusterSender</code></td>
+    <td><code>type=ClusterSender,host=${HOST}</code></td>
+  </tr>
+ 
+  <tr>
+    <td>ClusterReceiver</td>
+    <td>Configuration and stats of the recevier infrastructure</td>
+    <td><code>type=ClusterReceiver</code></td>
+    <td><code>type=ClusterReceiver,host=${HOST}</code></td>
+  </tr>
+
+  <tr>
+    <td>ClusterMembership</td>
+    <td>Configuration and stats of the membership infrastructure</td>
+    <td><code>type=ClusterMembership</code></td>
+    <td><code>type=ClusterMembership,host=${HOST}</code></td>
+  </tr>
+
+  <tr>
+    <td>IDataSender</td>
+    <td>For every cluster member it exist a sender mbeans. 
+    It exists speziall MBeans to all replication modes</td>
+    <td><code>type=IDataSender,
+        senderAddress=${MEMBER.SENDER.IP},
+        senderPort=${MEMBER.SENDER.PORT}</code></td>
+    <td><code>type=IDataSender,host=${HOST},
+        senderAddress=${MEMBER.SENDER.IP},
+        senderPort=${MEMBER.SENDER.PORT}</code></td>
+  </tr>
+ 
+  <tr>
+    <td>DeltaManager</td>
+    <td>This manager control the sessions and handle session replication </td>
+    <td><code>type=Manager,path=${APP.CONTEXT.PATH},host=${HOST}</code></td>
+    <td><code>type=Manager,path=${APP.CONTEXT.PATH},host=${HOST}</code></td>
+  </tr>
+
+  <tr>
+    <td>ReplicationValve</td>
+    <td>This valve control the replication to the backup nodes</td>
+    <td><code>type=Valve,name=ReplicationValve</code></td>
+    <td><code>type=Valve,name=ReplicationValve,host=${HOST}</code></td>
+  </tr>
+
+  <tr>
+    <td>JvmRouteBinderValve</td>
+    <td>This is a cluster fallback valve to change the Session ID to the current tomcat jvmroute.</td>
+    <td><code>type=Valve,name=JvmRouteBinderValve,
+              path=${APP.CONTEXT.PATH}</code></td>
+    <td><code>type=Valve,name=JvmRouteBinderValve,host=${HOST},
+              path=${APP.CONTEXT.PATH}</code></td>
+  </tr>
+
+</table>
+</p>
+</section>
+
+<section name="FAQ">
+<p>To be completed once we receive questions about session replication:</p>
+<ol>
+<li>Q: Can I configure as engine level?<p></p>
+
+    A: Since Tomcat 5.5.10 you can configure a cluster as engine and host level.
+    This helps to support clustering at a web hosting szenario.
+  <p></p>
+  </li>
+<li>Q: What is the simples cluster config?<p></p>
+
+    A: Since Tomcat 5.5.10 you can configure a cluster with following: <code>&lt;Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster" defaultMode="true" /&gt;</code>
+  <p></p>
+  </li>
+<li>Q: How can I activated transparent logging?<p></p>
+
+    A: Use "org.apache.catalina.cluster" as logger category and switch to info, debug or trace as log level.
+    A: Configure the <b>clusterLog</b> attribute (logging category) to get and send and receive message log.
+  <p></p>
+  </li>
+<li>Q: How can I used JMX information to monitor the cluster?<p></p>
+
+    A: Yes, it exists a lot ot usefull information to the cluster as MBeans. With Java 5 you can use the
+       jconsole to look inside the runnnig cluster (s. JMX section above).
+       At fastasyncmode replication mode you can got more information with 
+       sender attributes <code>doProcessingStats="true"</code> and <code>queueDoStats="true"</code>.
+       With the new JMX remote ant task you can access the state and call operations. 
+  <p></p>
+  </li>
+<li><p></p>Q: Can I pause the message sending?<p></p>
+
+    A: Yes, the async senders buffer the messages, but make sure the membership ping is active. 
+       With fastasyncqueue mode you can limit the max queue size. 
+  <p></p>
+  </li>
+<li>Q: Can I at more pooled senders?<p></p>
+
+    A: Yes, with sender attribute <code>maxPoolSocketLimit="40"</code> you can have more than the default
+       <code>25</code> sockets to transfer more parallel messages. 
+  <p></p>
+  </li>
+<li>Q: What happens when I pull the network cable?<p></p>
+
+    A: Well, the other members will remove the instance from the cluster,
+       but when you insert the cable again, the Tomcat instance might have completely flipped out.
+       This is because the OS might start going 100% CPU when a multicast message is sent.
+       There has not yet been a good solution for this, I will let you know when I have come up with one.
+       (pero: I test this and I works correct with java 5 and exists when you use the cluster with JDK 1.4.x)
+  <p></p>
+  </li>
+<li>Q: At my windows laptop without network my cluster doesn't work?<p></p>
+
+    A: The Membership attribute <code>mcastBindAddress="127.0.0.1"</code> must be set!  
+  <p></p>
+  </li>
+<li>Q: The cluster dosen't work under linux with two nodes at two boxes?<p></p>
+
+    A: Check the the following topics:
+    <ul>
+    <li>Is your network interface enabled for multicast? <code>ifconfig eth0 MULTICAST</code></li>
+    <li>Exists a multicast route to your network interface? <code>route add -host 228.0.0.4 dev eth0</code></li>
+    <li>Is your firewall active? Then check that multicast port is on your UDP open list
+       and the receiver TCP port is also for both machines open!</li>
+    </ul>   
+  <p></p>
+  </li>
+
+</ol>
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/config/ajp.xml b/container/webapps/docs/config/ajp.xml
new file mode 100644
index 0000000..7071526
--- /dev/null
+++ b/container/webapps/docs/config/ajp.xml
@@ -0,0 +1,297 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="ajp.html">
+
+  &project;
+
+  <properties>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <author email="arjaquith@mindspring.com">Andrew R. Jaquith</author>
+    <title>The AJP Connector</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>The <strong>AJP Connector</strong> element represents a
+  <strong>Connector</strong> component that communicates with a web
+  connector via the <code>AJP</code> protocol.  This is used for cases
+  where you wish to invisibly integrate Tomcat 5 into an existing (or new)
+  Apache installation, and you want Apache to handle the static content
+  contained in the web application, and/or utilize Apache's SSL
+  processing.</p>
+
+  <p>This connector supports load balancing when used in conjunction with
+  the <code>jvmRoute</code> attribute of the 
+  <a href="engine.html">Engine</a>.</p>
+
+<p>The native connectors supported with this Tomcat release are:
+<ul>
+<li>JK 1.2.x with any of the supported servers</li>
+<li>mod_proxy on Apache httpd 2.x (included by default in Apache HTTP Server 2.2), 
+with AJP enabled</li>
+</ul>
+</p>
+
+<p><b>Other native connectors supporting AJP may work, but are no longer supported.</b></p>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+  <p>All implementations of <strong>Connector</strong>
+  support the following attributes:</p>
+
+  <attributes>
+
+    <attribute name="allowTrace" required="false">
+      <p>A boolean value which can be used to enable or disable the TRACE
+      HTTP method. If not specified, this attribute is set to false.</p>
+    </attribute>
+
+    <attribute name="emptySessionPath" required="false">
+      <p>If set to <code>true</code>, all paths for session cookies will be set
+      to <code>/</code>. This can be useful for portlet specification implementations,
+      but will greatly affect performance if many applications are accessed on a given
+      server by the client.
+      If not specified, this attribute is set to <code>false</code>.</p>
+    </attribute>
+
+    <attribute name="enableLookups" required="false">
+      <p>Set to <code>true</code> if you want calls to
+      <code>request.getRemoteHost()</code> to perform DNS lookups in
+      order to return the actual host name of the remote client.  Set
+      to <code>false</code> to skip the DNS lookup and return the IP
+      address in String form instead (thereby improving performance).
+      By default, DNS lookups are enabled.</p>
+    </attribute>
+
+    <attribute name="maxPostSize" required="false">
+      <p>The maximum size in bytes of the POST which will be handled by
+      the container FORM URL parameter parsing. The feature can be disabled by
+      setting this attribute to a value less than or equal to 0.
+      If not specified, this attribute is set to 2097152 (2 megabytes).</p>
+    </attribute>
+
+    <attribute name="maxSavePostSize" required="false">
+      <p>The maximum size in bytes of the POST which will be saved/buffered by
+      the container during FORM or CLIENT-CERT authentication. For both types
+      of authentication, the POST will be saved/buffered before the user is
+      authenticated. For CLIENT-CERT authentication, the POST is buffered for
+      the duration of the SSL handshake and the buffer emptied when the request
+      is processed. For FORM authentication the POST is saved whilst the user
+      is re-directed to the login form and is retained until the user
+      successfully authenticates or the session associated with the
+      authentication request expires. The limit can be disabled by setting this
+      attribute to -1. Setting the attribute to zero will disable the saving of
+      POST data during authentication. If not specified, this attribute is set
+      to 4096 (4 kilobytes).</p>
+    </attribute>
+
+    <attribute name="protocol" required="false">
+      <p>This attribute value must be <code>AJP/1.3</code> to use the AJP
+      handler.</p>
+    </attribute>
+
+    <attribute name="proxyName" required="false">
+      <p>If this <strong>Connector</strong> is being used in a proxy
+      configuration, configure this attribute to specify the server name
+      to be returned for calls to <code>request.getServerName()</code>.
+      See <a href="#Proxy Support">Proxy Support</a> for more
+      information.</p>
+    </attribute>
+
+    <attribute name="proxyPort" required="false">
+      <p>If this <strong>Connector</strong> is being used in a proxy
+      configuration, configure this attribute to specify the server port
+      to be returned for calls to <code>request.getServerPort()</code>.
+      See <a href="#Proxy Support">Proxy Support</a> for more
+      information.</p>
+    </attribute>
+
+    <attribute name="redirectPort" required="false">
+      <p>If this <strong>Connector</strong> is supporting non-SSL
+      requests, and a request is received for which a matching
+      <code>&lt;security-constraint&gt;</code> requires SSL transport,
+      Catalina will automatically redirect the request to the port
+      number specified here.</p>
+    </attribute>
+
+    <attribute name="request.registerRequests" required="false">
+      <p>This attribute controls request registration for JMX monitoring
+      of the Connector.  It is enabled by default, but may be turned
+      it off to save a bit of memory.</p>
+    </attribute>
+
+    <attribute name="scheme" required="false">
+      <p>Set this attribute to the name of the protocol you wish to have
+      returned by calls to <code>request.getScheme()</code>.  For
+      example, you would set this attribute to "<code>https</code>"
+      for an SSL Connector.  The default value is "<code>http</code>".
+      See <a href="#SSL Support">SSL Support</a> for more information.</p>
+    </attribute>
+
+    <attribute name="secure" required="false">
+      <p>Set this attribute to <code>true</code> if you wish to have
+      calls to <code>request.isSecure()</code> to return <code>true</code>
+      for requests received by this Connector (you would want this on an
+      SSL Connector).  The default value is <code>false</code>.</p>
+    </attribute>
+
+    <attribute name="URIEncoding" required="false">
+      <p>This specifies the character encoding used to decode the URI bytes,
+      after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
+      </p>
+    </attribute>
+
+    <attribute name="useBodyEncodingForURI" required="false">
+      <p>This specifies if the encoding specified in contentType should be used
+      for URI query parameters, instead of using the URIEncoding. This
+      setting is present for compatibility with Tomcat 4.1.x, where the
+      encoding specified in the contentType, or explicitely set using
+      Request.setCharacterEncoding method was also used for the parameters from
+      the URL. The default value is <code>false</code>.
+      </p>
+    </attribute>
+
+    <attribute name="useIPVHosts" required="false">
+      <p>Set this attribute to <code>true</code> to cause Tomcat to use
+      the ServerName passed by the native web server to determine the Host
+      to send the request to.  The default value is <code>false</code>.</p>
+    </attribute>
+
+    <attribute name="xpoweredBy" required="false">
+      <p>Set this attribute to <code>true</code> to cause Tomcat to advertise
+      support for the Srevlet specification using the header recommended in the
+      specification.  The default value is <code>false</code>.</p>
+    </attribute>
+
+  </attributes>
+
+  </subsection>
+
+  <subsection name="Standard Implementation">
+
+  <p>To use AJP, you
+  must specify the protocol attribute (see above).</p>
+
+  <p><strong>This implementation supports the AJP 1.3 protocol.</strong></p>
+
+  <p>It supports the following additional attributes (in addition to the
+  common attributes listed above):</p>
+
+  <attributes>
+
+    <attribute name="address" required="false">
+      <p>For servers with more than one IP address, this attribute
+      specifies which address will be used for listening on the specified
+      port.  By default, this port will be used on all IP addresses
+      associated with the server. A value of <code>127.0.0.1</code>
+      indicates that the Connector will only listen on the loopback
+      interface.</p>
+    </attribute>
+
+    <attribute name="backlog" required="false">
+      <p>The maximum queue length for incoming connection requests when
+      all possible request processing threads are in use.  Any requests
+      received when the queue is full will be refused.  The default
+      value is 10.</p>
+    </attribute>
+
+    <attribute name="bufferSize" required="false">
+      <p>The size of the output buffer to use.  If less than or equal to zero,
+         then output buffering is disabled.  The default value is -1
+         (i.e. buffering disabled)</p>
+    </attribute>
+
+    <attribute name="connectionTimeout" required="false">
+      <p>The number of milliseconds this <strong>Connector</strong> will wait,
+      after accepting a connection, for the request URI line to be
+      presented.  The default value is infinite (i.e. no timeout).</p>
+    </attribute>
+
+    <attribute name="minProcessors" required="false">
+      <strong>deprecated</strong>
+      <p>The minimum number of processors to start at initialization time.
+      If not specified, this atttribute is set to 5.</p>
+    </attribute>
+
+    <attribute name="maxProcessors" required="false">
+      <strong>deprecated</strong>
+      <p>The maximum number of processors allowed. This should be
+      set to a value that is greater than or equal to the maximum number
+      of concurrent connections the remote web server can open to Tomcat 
+      simultaneously. For example, if the web server is Apache 1.x or 2.x
+      Tomcat's <code>maxProcessors</code> should be set to the 
+      value of Apache's <code>maxClients</code> directive.</p>
+      <p>A <code>maxProcessors</code> value of zero (0) signifies that 
+      the number of processors is unlimited. If not specified, this
+      atttribute defaults to 20.</p>
+     </attribute>
+
+    <attribute name="maxSpareThreads" required="false">
+      <p>The maximum number of unused request processing threads that
+      will be allowed to exist until the thread pool starts stopping the
+      unnecessary threads.  The default value is 50.</p>
+    </attribute>
+
+    <attribute name="maxThreads" required="false">
+      <p>The maximum number of request processing threads to be created
+      by this <strong>Connector</strong>, which therefore determines the
+      maximum number of simultaneous requests that can be handled.  If
+      not specified, this attribute is set to 200.</p>
+    </attribute>
+
+    <attribute name="minSpareThreads" required="false">
+      <p>The number of request processing threads that will be created
+      when this <strong>Connector</strong> is first started.  The connector
+      will also make sure it has the specified number of idle processing
+      threads available. This attribute should be set to a value smaller
+      than that set for <code>maxThreads</code>.  The default value is 4.</p>
+    </attribute>
+
+    <attribute name="port" required="true">
+      <p>The TCP port number on which this <strong>Connector</strong>
+      will create a server socket and await incoming connections.  Your
+      operating system will allow only one server application to listen
+      to a particular port number on a particular IP address.</p>
+    </attribute>
+
+    <attribute name="tcpNoDelay" required="false">
+      <p>If set to <code>true</code>, the TCP_NO_DELAY option will be
+      set on the server socket, which improves performance under most
+      circumstances.  This is set to <code>true</code> by default.</p>
+    </attribute>
+
+    <attribute name="tomcatAuthentication" required="false">
+      <p>If set to <code>true</code>, the authetication will be done in Tomcat. 
+      Otherwise, the authenticated principal will be propagated from the native
+      webaserver and used for authorization in Tomcat.  
+      The default value is <code>true</code>.</p>
+    </attribute>
+
+  </attributes>
+
+  </subsection>
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>None at this time.</p>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/config/context.xml b/container/webapps/docs/config/context.xml
new file mode 100644
index 0000000..83980cf
--- /dev/null
+++ b/container/webapps/docs/config/context.xml
@@ -0,0 +1,745 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="context.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>The Context Container</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>The <strong>Context</strong> element represents a <em>web
+  application</em>, which is run within a particular virtual host.
+  Each web application is based on a <em>Web Application Archive</em>
+  (WAR) file, or a corresponding directory containing the corresponding
+  unpacked contents, as described in the Servlet Specification (version
+  2.2 or later).  For more information about web application archives,
+  you can download the
+  <a href="http://java.sun.com/products/servlet/download.html">Servlet
+  Specification</a>, and review the Tomcat
+  <a href="../appdev/index.html">Application Developer's Guide</a>.</p>
+
+  <p>The web application used to process each HTTP request is selected
+  by Catalina based on matching the longest possible prefix of the
+  Request URI against the <em>context path</em> of each defined Context.
+  Once selected, that Context will select an appropriate servlet to
+  process the incoming request, according to the servlet mappings defined
+  in the <em>web application deployment descriptor</em> file (which MUST
+  be located at <code>/WEB-INF/web.xml</code> within the web app's
+  directory hierarchy).</p>
+
+  <p>You may define as many <strong>Context</strong> elements as you
+  wish.  Each such Context MUST have a unique
+  context path, which is defined by the <code>path</code> attribute.
+  In addition, you MUST define a Context with a context path equal to
+  a zero-length string.  This Context becomes the <em>default</em>
+  web application for this virtual host, and is used to process all
+  requests that do not match any other Context's context path.</p>
+
+  <p>In addition to nesting <strong>Context</strong> elements inside a
+  <a href="host.html">Host</a> element, you can also store them:</p>
+  <ul>
+  <li>in the individual <code>$CATALINA_HOME/conf/context.xml</code> file: 
+  the Context element information will be loaded by all webapps</li>
+  <li>in the individual 
+  <code>$CATALINA_HOME/conf/[enginename]/[hostname]/context.xml.default</code>
+  file: the Context element information will be loaded by all webapps of that
+  host</li>
+  <li>in individual files (with a ".xml" extension) in the 
+  <code>$CATALINA_HOME/conf/[enginename]/[hostname]/</code> directory</li>
+  <li>if the previous file was not found for this application, in individual file
+  at <code>/META-INF/context.xml</code> inside the application files</li>
+  </ul>
+  <p>See
+  <a href="host.html#Automatic Application Deployment">Automatic
+  Application Deployment</a> for more information. This method allows dynamic
+  reconfiguration of the web application, since the main 
+  <code>conf/server.xml</code> file cannot be reloaded without restarting
+  Tomcat. <b>Please note that for tomcat 5, unlike tomcat 4.x, it is NOT
+  recommended to place &lt;Context&gt; elements directly in the server.xml file.</b>
+  Instead, put them in the META-INF/context.xml directory of your WAR file or
+  the conf directory as described above.
+  </p>
+
+  <p>In addition to explicitly specified Context elements, there are
+  several techniques by which Context elements can be created automatically
+  for you.  See <a href="host.html#Automatic Application Deployment">
+  Automatic Application Deployment</a> and
+  <a href="host.html#User Web Applications">User Web Applications</a>
+  for more information.</p>
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+    <p>All implementations of <strong>Context</strong>
+    support the following attributes:</p>
+
+    <attributes>
+
+      <attribute name="backgroundProcessorDelay" required="false">
+        <p>This value represents the delay in seconds between the 
+        invocation of the backgroundProcess method on this context and 
+        its child containers, including all wrappers. 
+        Child containers will not be invoked if their delay value is not 
+        negative (which would mean they are using their own processing 
+        thread). Setting this to a positive value will cause 
+        a thread to be spawn. After waiting the specified amount of time, 
+        the thread will invoke the backgroundProcess method on this host 
+        and all its child containers. A context will use background 
+        processing to perform session expiration and class monitoring for
+        reloading. If not specified, the default value for this attribute is 
+        -1, which means the context will rely on the background processing 
+        thread of its parent host.</p>
+      </attribute>
+
+      <attribute name="className" required="false">
+        <p>Java class name of the implementation to use.  This class must
+        implement the <code>org.apache.catalina.Context</code> interface.
+        If not specified, the standard value (defined below) will be used.</p>
+      </attribute>
+
+      <attribute name="cookies" required="false">
+        <p>Set to <code>true</code> if you want cookies to be used for
+        session identifier communication if supported by the client (this
+        is the default).  Set to <code>false</code> if you want to disable
+        the use of cookies for session identifier communication, and rely
+        only on URL rewriting by the application.</p>
+      </attribute>
+
+      <attribute name="crossContext" required="false">
+        <p>Set to <code>true</code> if you want calls within this application
+        to <code>ServletContext.getContext()</code> to successfully return a
+        request dispatcher for other web applications running on this virtual
+        host.  Set to <code>false</code> (the default) in security
+        conscious environments, to make <code>getContext()</code> always
+        return <code>null</code>.</p>
+      </attribute>
+
+      <attribute name="docBase" required="true">
+        <p>The <em>Document Base</em> (also known as the <em>Context
+        Root</em>) directory for this web application, or the pathname
+        to the web application archive file (if this web application is
+        being executed directly from the WAR file).    You may specify
+        an absolute pathname for this directory or WAR file, or a pathname
+        that is relative to the <code>appBase</code> directory of the
+        owning <a href="host.html">Host</a>.</p>
+      </attribute>
+
+      <attribute name="override" required="false">
+        <p>Set to <code>true</code> to have explicit settings in this
+        Context element override any corresponding settings in the
+        <a href="defaultcontext.html">DefaultContext</a> element associated
+        with our owning <a href="host.html">Host</a>.  By default, settings
+        in the DefaultContext element will be used.</p>
+      </attribute>
+
+      <attribute name="privileged" required="false">
+        <p>Set to <code>true</code> to allow this context to use container
+        servlets, like the manager servlet.</p>
+      </attribute>
+
+      <attribute name="path" required="false">
+        <p>The <em>context path</em> of this web application, which is
+        matched against the beginning of each request URI to select the
+        appropriate web application for processing.  All of the context paths
+        within a particular <a href="host.html">Host</a> must be unique.
+        If you specify a context path of an empty string (""), you are
+        defining the <em>default</em> web application for this Host, which
+        will process all requests not assigned to other Contexts. The value of
+        this field must not be set except when statically defining a Context in
+        server.xml, as it will be infered from the filenames used for either the 
+        .xml context file or the docBase.</p>
+      </attribute>
+
+      <attribute name="reloadable" required="false">
+        <p>Set to <code>true</code> if you want Catalina to monitor classes in
+        <code>/WEB-INF/classes/</code> and <code>/WEB-INF/lib</code> for
+        changes, and automatically reload the web application if a change
+        is detected.  This feature is very useful during application
+        development, but it requires significant runtime overhead and is
+        not recommended for use on deployed production applications.  That's
+        why the default setting for this attribute is <i>false</i>.  You
+        can use the <a href="../manager-howto.html">Manager</a> web
+        application, however, to trigger reloads of deployed applications
+        on demand.</p>
+      </attribute>
+
+      <attribute name="wrapperClass" required="false">
+        <p>Java class name of the <code>org.apache.catalina.Wrapper</code>
+        implementation class that will be used for servlets managed by this
+        Context.  If not specified, a standard default value will be used.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Standard Implementation">
+
+    <p>The standard implementation of <strong>Context</strong> is
+    <strong>org.apache.catalina.core.StandardContext</strong>.
+    It supports the following additional attributes (in addition to the
+    common attributes listed above):</p>
+
+    <attributes>
+
+      <attribute name="allowLinking" required="false">
+        <p>If the value of this flag is <code>true</code>, symlinks will be
+        allowed inside the web application, pointing to resources outside the
+        web application base path. If not specified, the default value
+        of the flag is <code>false</code>.</p>
+        <p><b>NOTE: This flag MUST NOT be set to true on the Windows platform
+        (or any other OS which does not have a case sensitive filesystem),
+        as it will disable case sensitivity checks, allowing JSP source code
+        disclosure, among other security problems.</b></p>
+      </attribute>
+
+      <attribute name="antiJARLocking" required="false">
+        <p>If true, the Tomcat classloader will take extra measures to avoid
+        JAR file locking when resources are accessed inside JARs through URLs.
+        This will impact startup time of applications, but could prove to be useful
+        on platforms or configurations where file locking can occur.
+        If not specified, the default value is <code>false</code>.</p>
+      </attribute>
+
+      <attribute name="antiResourceLocking" required="false">
+        <p>If true, Tomcat will prevent any file locking.
+        This will significantly impact startup time of applications, 
+        but allows full webapp hot deploy and undeploy on platforms 
+        or configurations where file locking can occur.
+        If not specified, the default value is <code>false</code>.</p>
+      </attribute>
+
+      <attribute name="cacheMaxSize" required="false">
+        <p>Maximum size of the static resource cache in kilobytes. 
+        If not specified, the default value is <code>10240</code>
+        (10 megabytes).</p>
+      </attribute>
+
+      <attribute name="cacheTTL" required="false">
+        <p>Amount of time in milliseconds between cache entries revalidation.
+        If not specified, the default value is <code>5000</code> 
+        (5 seconds).</p>
+      </attribute>
+
+      <attribute name="cachingAllowed" required="false">
+        <p>If the value of this flag is <code>true</code>, the cache for static
+        resources will be used. If not specified, the default value
+        of the flag is <code>true</code>.</p>
+      </attribute>
+
+      <attribute name="caseSensitive" required="false">
+        <p>If the value of this flag is <code>true</code>, all case sensitivity
+        checks will be disabled. If not 
+        specified, the default value of the flag is <code>true</code>.</p>
+        <p><b>NOTE: This flag MUST NOT be set to false on the Windows platform
+        (or any other OS which does not have a case sensitive filesystem),
+        as it will disable case sensitivity checks, allowing JSP source code
+        disclosure, among other security problems.</b></p>
+      </attribute>
+
+      <attribute name="processTlds" required="false">
+        <p>Whether the context should process TLDs on startup.  The default
+        is true.  The false setting is intended for special cases
+        that know in advance TLDs are not part of the webapp.</p>
+      </attribute>
+
+      <attribute name="swallowOutput" required="false">
+        <p>If the value of this flag is <code>true</code>, the bytes output to
+        System.out and System.err by the web application will be redirected to
+        the web application logger. If not specified, the default value
+        of the flag is <code>false</code>.</p>
+      </attribute>
+
+      <attribute name="tldNamespaceAware" required="false">
+        <p>If the value of this flag is <code>true</code>, the TLD files
+        XML validation will be namespace-aware.  If you turn this flag on,
+        you should probably also turn <code>tldValidation</code> on.  The
+        default value for this flag is <code>false</code>, and setting it
+        to true will incur a performance penalty.
+        </p>
+      </attribute>
+
+      <attribute name="tldValidation" required="false">
+        <p>If the value of this flag is <code>true</code>, the TLD files
+        will be XML validated on context startup.  The default value for
+        this flag is <code>false</code>, and setting it to true will incur
+        a performance penalty.</p>
+      </attribute>
+
+      <attribute name="unpackWAR" required="false">
+        <p>If true, Tomcat will unpack all compressed web applications before
+        running them.
+        If not specified, the default value is <code>true</code>.</p>
+      </attribute>
+
+      <attribute name="useNaming" required="false">
+        <p>Set to <code>true</code> (the default) to have Catalina enable a
+        JNDI <code>InitialContext</code> for this web application that is
+        compatible with Java2 Enterprise Edition (J2EE) platform
+        conventions.</p>
+      </attribute>
+
+      <attribute name="workDir" required="false">
+        <p>Pathname to a scratch directory to be provided by this Context
+        for temporary read-write use by servlets within the associated web
+        application.  This directory will be made visible to servlets in the
+        web application by a servlet context attribute (of type
+        <code>java.io.File</code>) named
+        <code>javax.servlet.context.tempdir</code> as described in the
+        Servlet Specification.  If not specified, a suitable directory
+        underneath <code>$CATALINA_HOME/work</code> will be provided.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>You can nest at most one instance of the following utility components
+  by nesting a corresponding element inside your <strong>Context</strong>
+  element:</p>
+  <ul>
+  <li><a href="loader.html"><strong>Loader</strong></a> -
+      Configure the web application class loader that will be used to load
+      servlet and bean classes for this web application.  Normally, the
+      default configuration of the class loader will be sufficient.</li>
+  <li><a href="manager.html"><strong>Manager</strong></a> -
+      Configure the session manager that will be used to create, destroy,
+      and persist HTTP sessions for this web application.  Normally, the
+      default configuration of the session manager will be sufficient.</li>
+  <li><a href="realm.html"><strong>Realm</strong></a> -
+      Configure a realm that will allow its
+      database of users, and their associated roles, to be utilized solely
+      for this particular web application.  If not specified, this web
+      application will utilize the Realm associated with the owning
+      <a href="host.html">Host</a> or <a href="engine.html">Engine</a>.</li>
+  <li><a href="resources.html"><strong>Resources</strong></a> -
+      Configure the resource manager that will be used to access the static
+      resources associated with this web application.  Normally, the
+      default configuration of the resource manager will be sufficient.</li>
+  <li><strong>WatchedResource</strong> - The auto deployer will monitor the 
+      specified static resource of the web application for updates, and will
+      reload the web application if is is updated. The content of this element
+      must be a string.</li>
+  </ul>
+
+</section>
+
+
+<section name="Special Features">
+
+
+  <subsection name="Logging">
+
+    <p>A context is associated with the 
+       <code>org.apache.catalina.core.ContainerBase.[enginename].[hostname].[path]</code>
+       log category.  Note that the brackets are actually part of the name, don't omit them.</p>
+
+  </subsection>
+
+
+  <subsection name="Access Logs">
+
+    <p>When you run a web server, one of the output files normally generated
+    is an <em>access log</em>, which generates one line of information for
+    each request processed by the server, in a standard format.  Catalina
+    includes an optional <a href="valve.html">Valve</a> implementation that
+    can create access logs in the same standard format created by web servers,
+    or in any number of custom formats.</p>
+
+    <p>You can ask Catalina to create an access log for all requests
+    processed by an <a href="engine.html">Engine</a>,
+    <a href="host.html">Host</a>, or <a href="context.html">Context</a>
+    by nesting a <a href="valve.html">Valve</a> element like this:</p>
+
+<source>
+&lt;Context path="/examples" ...&gt;
+  ...
+  &lt;Valve className="org.apache.catalina.valves.AccessLogValve"
+         prefix="localhost_access_log." suffix=".txt"
+         pattern="common"/&gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>See <a href="valve.html#Access Log Valve">Access Log Valve</a>
+    for more information on the configuration attributes that are
+    supported.</p>
+
+  </subsection>
+
+
+  <subsection name="Automatic Context Configuration">
+
+    <p>If you use the standard <strong>Context</strong> implementation,
+    the following configuration steps occur automtically when Catalina
+    is started, or whenever this web application is reloaded.  No special
+    configuration is required to enable this feature.</p>
+    
+    <ul>
+    <li>If you have not declared your own <a href="loader.html">Loader</a>
+       element, a standard web application class loader will be configured.
+       </li>
+    <li>If you have not declared your own <a href="manager.html">Manager</a>
+        element, a standard session manager will be configured.</li>
+    <li>If you have not declared your own <a href="resources.html">Resources</a>
+        element, a standard resources manager will be configured.</li>
+    <li>The web application properties listed in <code>conf/web.xml</code>
+        will be processed as defaults for this web application.  This is used
+        to establish default mappings (such as mapping the <code>*.jsp</code>
+        extension to the corresponding JSP servlet), and other standard
+        features that apply to all web applications.</li>
+    <li>The web application properties listed in the
+        <code>/WEB-INF/web.xml</code> resource for this web application
+        will be processed (if this resource exists).</li>
+    <li>If your web application has specified security constraints that might
+        require user authentication, an appropriate Authenticator that
+        implements the login method you have selected will be configured.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Context Parameters">
+
+    <p>You can configure named values that will be made visible to the
+    web application as servlet context initialization parameters by nesting
+    <code>&lt;Parameter&gt;</code> elements inside this element.  For
+    example, you can create an initialization parameter like this:</p>
+<source>
+&lt;Context ...&gt;
+  ...
+  &lt;Parameter name="companyName" value="My Company, Incorporated"
+         override="false"/&gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>This is equivalent to the inclusion of the following element in the
+    web application deployment descriptor (<code>/WEB-INF/web.xml</code>):
+    </p>
+<source>
+&lt;context-param&gt;
+  &lt;param-name&gt;companyName&lt;/param-name&gt;
+  &lt;param-value&gt;My Company, Incorporated&lt;/param-value&gt;
+&lt;/context-param&gt;
+</source>
+    <p>but does <em>not</em> require modification of the deployment descriptor
+    to customize this value.</p>
+
+    <p>The valid attributes for a <code>&lt;Parameter&gt;</code> element
+    are as follows:</p>
+
+    <attributes>
+
+      <attribute name="description" required="false">
+        <p>Optional, human-readable description of this context
+        initialization parameter.</p>
+      </attribute>
+
+      <attribute name="name" required="true">
+        <p>The name of the context initialization parameter to be created.</p>
+      </attribute>
+
+      <attribute name="override" required="false">
+        <p>Set this to <code>false</code> if you do <strong>not</strong> want
+        a <code>&lt;context-param&gt;</code> for the same parameter name,
+        found in the web application deployment descriptor, to override the
+        value specified here.  By default, overrides are allowed.</p>
+      </attribute>
+
+      <attribute name="value" required="true">
+        <p>The parameter value that will be presented to the application
+        when requested by calling
+        <code>ServletContext.getInitParameter()</code>.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Environment Entries">
+
+    <p>You can configure named values that will be made visible to the
+    web application as environment entry resources, by nesting
+    <code>&lt;Environment&gt;</code> entries inside this element.  For
+    example, you can create an environment entry like this:</p>
+<source>
+&lt;Context ...&gt;
+  ...
+  &lt;Environment name="maxExemptions" value="10"
+         type="java.lang.Integer" override="false"/&gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>This is equivalent to the inclusion of the following element in the
+    web application deployment descriptor (<code>/WEB-INF/web.xml</code>):
+    </p>
+<source>
+&lt;env-entry&gt;
+  &lt;env-entry-name&gt;maxExemptions&lt;/param-name&gt;
+  &lt;env-entry-value&gt;10&lt;/env-entry-value&gt;
+  &lt;env-entry-type&gt;java.lang.Integer&lt;/env-entry-type&gt;
+&lt;/env-entry&gt;
+</source>
+    <p>but does <em>not</em> require modification of the deployment descriptor
+    to customize this value.</p>
+
+    <p>The valid attributes for an <code>&lt;Environment&gt;</code> element
+    are as follows:</p>
+
+    <attributes>
+
+      <attribute name="description" required="false">
+        <p>Optional, human-readable description of this environment entry.</p>
+      </attribute>
+
+      <attribute name="name" required="true">
+        <p>The name of the environment entry to be created, relative to the
+        <code>java:comp/env</code> context.</p>
+      </attribute>
+
+      <attribute name="override" required="false">
+        <p>Set this to <code>false</code> if you do <strong>not</strong> want
+        an <code>&lt;env-entry&gt;</code> for the same environment entry name,
+        found in the web application deployment descriptor, to override the
+        value specified here.  By default, overrides are allowed.</p>
+      </attribute>
+
+      <attribute name="type" required="true">
+        <p>The fully qualified Java class name expected by the web application
+        for this environment entry.  Must be one of the legal values for
+        <code>&lt;env-entry-type&gt;</code> in the web application deployment
+        descriptor:  <code>java.lang.Boolean</code>,
+        <code>java.lang.Byte</code>, <code>java.lang.Character</code>,
+        <code>java.lang.Double</code>, <code>java.lang.Float</code>,
+        <code>java.lang.Integer</code>, <code>java.lang.Long</code>,
+        <code>java.lang.Short</code>, or <code>java.lang.String</code>.</p>
+      </attribute>
+
+      <attribute name="value" required="true">
+        <p>The parameter value that will be presented to the application
+        when requested from the JNDI context.  This value must be convertable
+        to the Java type defined by the <code>type</code> attribute.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Lifecycle Listeners">
+
+    <p>If you have implemented a Java object that needs to know when this
+    <strong>Context</strong> is started or stopped, you can declare it by
+    nesting a <strong>Listener</strong> element inside this element.  The
+    class name you specify must implement the
+    <code>org.apache.catalina.LifecycleListener</code> interface, and
+    it will be notified about the occurrence of the coresponding
+    lifecycle events.  Configuration of such a listener looks like this:</p>
+
+<source>
+&lt;Context path="/examples" ...&gt;
+  ...
+  &lt;Listener className="com.mycompany.mypackage.MyListener" ... &gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>Note that a Listener can have any number of additional properties
+    that may be configured from this element.  Attribute names are matched
+    to corresponding JavaBean property names using the standard property
+    method naming patterns.</p>
+
+  </subsection>
+
+
+  <subsection name="Request Filters">
+
+    <p>You can ask Catalina to check the IP address, or host name, on every
+    incoming request directed to the surrounding
+    <a href="engine.html">Engine</a>, <a href="host.html">Host</a>, or
+    <a href="context.html">Context</a> element.  The remote address or name
+    will be checked against a configured list of "accept" and/or "deny"
+    filters, which are defined using the Regular Expression syntax supported
+    by the <a href="http://jakarta.apache.org/regexp/">Jakarta Regexp</a>
+    regular expression library.  Requests that come from locations that are
+    not accepted will be rejected with an HTTP "Forbidden" error.
+    Example filter declarations:</p>
+
+<source>
+&lt;Context path="/examples" ...&gt;
+  ...
+  &lt;Valve className="org.apache.catalina.valves.RemoteHostValve"
+         allow="*.mycompany.com,www.yourcompany.com"/&gt;
+  &lt;Valve className="org.apache.catalina.valves.RemoteAddrValve"
+         deny="192.168.1.*"/&gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>See <a href="valve.html#Remote Address Filter">Remote Address Filter</a>
+    and <a href="valve.html#Remote Host Filter">Remote Host Filter</a> for
+    more information about the configuration options that are supported.</p>
+
+  </subsection>
+
+
+  <subsection name="Resource Definitions">
+
+    <p>You can declare the characteristics of the resource
+    to be returned for JNDI lookups of <code>&lt;resource-ref&gt;</code> and
+    <code>&lt;resource-env-ref&gt;</code> elements in the web application
+    deployment descriptor.  You <strong>MUST</strong> also define
+    the needed resource parameters as attributes of the <code>Resource</code> 
+    element, to configure the object factory to be used (if not known to Tomcat 
+    already), and the properties used to configure that object factory.</p>
+
+    <p>For example, you can create a resource definition like this:</p>
+<source>
+&lt;Context ...&gt;
+  ...
+  &lt;Resource name="jdbc/EmployeeDB" auth="Container"
+            type="javax.sql.DataSource"
+     description="Employees Database for HR Applications"/&gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>This is equivalent to the inclusion of the following element in the
+    web application deployment descriptor (<code>/WEB-INF/web.xml</code>):</p>
+<source>
+&lt;resource-ref&gt;
+  &lt;description&gt;Employees Database for HR Applications&lt;/description&gt;
+  &lt;res-ref-name&gt;jdbc/EmployeeDB&lt;/res-ref-name&gt;
+  &lt;res-ref-type&gt;javax.sql.DataSource&lt;/res-ref-type&gt;
+  &lt;res-auth&gt;Container&lt;/res-auth&gt;
+&lt;/resource-ref&gt;
+</source>
+
+    <p>but does <em>not</em> require modification of the deployment
+    descriptor to customize this value.</p>
+
+    <p>The valid attributes for a <code>&lt;Resource&gt;</code> element
+    are as follows:</p>
+
+    <attributes>
+
+      <attribute name="auth" required="false">
+        <p>Specify whether the web Application code signs on to the
+        corresponding resource manager programatically, or whether the
+        Container will sign on to the resource manager on behalf of the
+        application.  The value of this attribute must be
+        <code>Application</code> or <code>Container</code>.  This
+        attribute is <strong>required</strong> if the web application
+        will use a <code>&lt;resource-ref&gt;</code> element in the web
+        application deployment descriptor, but is optional if the
+        application uses a <code>&lt;resource-env-ref&gt;</code> instead.</p>
+      </attribute>
+
+      <attribute name="description" required="false">
+        <p>Optional, human-readable description of this resource.</p>
+      </attribute>
+
+      <attribute name="name" required="true">
+        <p>The name of the resource to be created, relative to the
+        <code>java:comp/env</code> context.</p>
+      </attribute>
+
+      <attribute name="scope" required="false">
+        <p>Specify whether connections obtained through this resource
+        manager can be shared.  The value of this attribute must be
+        <code>Shareable</code> or <code>Unshareable</code>.  By default,
+        connections are assumed to be shareable.</p>
+      </attribute>
+
+      <attribute name="type" required="true">
+        <p>The fully qualified Java class name expected by the web
+        application when it performs a lookup for this resource.</p>
+      </attribute>
+
+    </attributes>
+
+
+  </subsection>
+
+
+  <subsection name="Resource Links">
+
+     <p>This element is used to create a link to a global JNDI resource. Doing
+     a JNDI lookup on the link name will then return the linked global 
+     resource.</p>
+
+    <p>For example, you can create a resource link like this:</p>
+<source>
+&lt;Context ...&gt;
+  ...
+  &lt;ResourceLink name="linkToGlobalResource"
+            global="simpleValue"
+            type="java.lang.Integer"
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>The valid attributes for a <code>&lt;ResourceLink&gt;</code> element
+    are as follows:</p>
+
+    <attributes>
+
+      <attribute name="global" required="true">
+        <p>The name of the linked global resource in the 
+        global JNDI context.</p>
+      </attribute>
+
+      <attribute name="name" required="true">
+        <p>The name of the resource link to be created, relative to the
+        <code>java:comp/env</code> context.</p>
+      </attribute>
+
+      <attribute name="type" required="true">
+        <p>The fully qualified Java class name expected by the web
+        application when it performs a lookup for this resource link.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+</section>
+
+
+</body>
+
+
+</document>
diff --git a/container/webapps/docs/config/engine.xml b/container/webapps/docs/config/engine.xml
new file mode 100644
index 0000000..fcd6051
--- /dev/null
+++ b/container/webapps/docs/config/engine.xml
@@ -0,0 +1,240 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="engine.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>The Engine Container</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>The <strong>Engine</strong> element represents the entire request
+  processing machinery associated with a particular Catalina
+  <a href="service.html">Service</a>.  It receives and processes
+  <em>all</em> requests from one or more <strong>Connectors</strong>,
+  and returns the completed response to the Connector for ultimate
+  transmission back to the client.</p>
+
+  <p>Exactly one <strong>Engine</strong> element MUST be nested inside
+  a <a href="service.html">Service</a> element, following all of the
+  corresponding Connector elements associated with this Service.</p>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+    <p>All implementations of <strong>Engine</strong>
+    support the following attributes:</p>
+
+    <attributes>
+
+      <attribute name="backgroundProcessorDelay" required="false">
+        <p>This value represents the delay in seconds between the 
+        invocation of the backgroundProcess method on this engine and 
+        its child containers, including all hosts and contexts. 
+        Child containers will not be invoked if their delay value is not 
+        negative (which would mean they are using their own processing 
+        thread). Setting this to a positive value will cause 
+        a thread to be spawn. After waiting the specified amount of time, 
+        the thread will invoke the backgroundProcess method on this engine 
+        and all its child containers. If not specified, the default value for
+        this attribute is 10, which represent a 10 seconds delay.</p>
+      </attribute>
+
+      <attribute name="className" required="false">
+        <p>Java class name of the implementation to use.  This class must
+        implement the <code>org.apache.catalina.Engine</code> interface.
+        If not specified, the standard value (defined below) will be used.</p>
+      </attribute>
+
+      <attribute name="defaultHost" required="true">
+        <p>The default host name, which identifies the
+        <a href="host.html">Host</a> that will process requests directed
+        to host names on this server, but which are not configured in
+        this configuration file.  This name MUST match the <code>name</code>
+        attributes of one of the <a href="host.html">Host</a> elements
+        nested immediately inside.</p>
+      </attribute>
+
+      <attribute name="jvmRoute" required="false">
+        <p>Identifier which must be used in load balancing scenarios to enable
+        session affinity. The indetifier, which must be unique across all
+        Tomcat 5 servers which participate in the cluster, will be appended to
+        the generated session identifier, therefore allowing the front end
+        proxy to always forward a particular session to the same Tomcat 5
+        instance.</p>
+      </attribute>
+
+      <attribute name="name" required="true">
+        <p>Logical name of this Engine, used in log and error messages.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Standard Implementation">
+
+    <p>The standard implementation of <strong>Engine</strong> is
+    <strong>org.apache.catalina.core.StandardEngine</strong>.
+    It supports the following additional attributes (in addition to the
+    common attributes listed above):</p>
+
+    <attributes>
+
+    </attributes>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>You can nest one or more <a href="host.html">Host</a> elements inside
+  this <strong>Engine</strong> element, each representing a different virtual
+  host associated with this server.  At least one <a href="host.html">Host</a>
+  is required, and one of the nested <a href="host.html">Hosts</a> MUST
+  have a name that matches the name specified for the
+  <code>defaultHost</code> attribute, listed above.</p>
+
+  <p>You can optional nest a <a href="defaultcontext.html">DefaultContext</a>
+  element inside this <strong>Engine</strong> element, to define the default
+  characteristics of web applications that are automatically deployed.</p>
+
+  <p>You can nest at most one instance of the following utility components
+  by nesting a corresponding element inside your <strong>Engine</strong>
+  element:</p>
+  <ul>
+  <li><a href="realm.html"><strong>Realm</strong></a> -
+      Configure a realm that will allow its
+      database of users, and their associated roles, to be shared across all
+      <a href="host.html">Hosts</a> and <a href="context.html">Contexts</a>
+      nested inside this Engine, unless overridden by a
+      <a href="realm.html">Realm</a> configuration at a lower level.</li>
+  </ul>
+
+</section>
+
+
+<section name="Special Features">
+
+
+  <subsection name="Logging">
+
+    <p>An engine is associated with the 
+       <code>org.apache.catalina.core.ContainerBase.[enginename]</code>
+       log category.  Note that the brackets are actually part of the name,
+       don't omit them.</p>
+
+  </subsection>
+
+
+  <subsection name="Access Logs">
+
+    <p>When you run a web server, one of the output files normally generated
+    is an <em>access log</em>, which generates one line of information for
+    each request processed by the server, in a standard format.  Catalina
+    includes an optional <a href="valve.html">Valve</a> implementation that
+    can create access logs in the same standard format created by web servers,
+    or in any number of custom formats.</p>
+
+    <p>You can ask Catalina to create an access log for all requests
+    processed by an <a href="engine.html">Engine</a>,
+    <a href="host.html">Host</a>, or <a href="context.html">Context</a>
+    by nesting a <a href="valve.html">Valve</a> element like this:</p>
+
+<source>
+&lt;Engine name="Standalone" ...&gt;
+  ...
+  &lt;Valve className="org.apache.catalina.valves.AccessLogValve"
+         prefix="catalina_access_log." suffix=".txt"
+         pattern="common"/&gt;
+  ...
+&lt;/Engine&gt;
+</source>
+
+    <p>See <a href="valve.html#Access Log Valve">Access Log Valve</a>
+    for more information on the configuration attributes that are
+    supported.</p>
+
+  </subsection>
+
+
+  <subsection name="Lifecycle Listeners">
+
+    <p>If you have implemented a Java object that needs to know when this
+    <strong>Engine</strong> is started or stopped, you can declare it by
+    nesting a <strong>Listener</strong> element inside this element.  The
+    class name you specify must implement the
+    <code>org.apache.catalina.LifecycleListener</code> interface, and
+    it will be notified about the occurrence of the coresponding
+    lifecycle events.  Configuration of such a listener looks like this:</p>
+
+<source>
+&lt;Engine name="Standalone" ...&gt;
+  ...
+  &lt;Listener className="com.mycompany.mypackage.MyListener" ... &gt;
+  ...
+&lt;/Engine&gt;
+</source>
+
+    <p>Note that a Listener can have any number of additional properties
+    that may be configured from this element.  Attribute names are matched
+    to corresponding JavaBean property names using the standard property
+    method naming patterns.</p>
+
+  </subsection>
+
+
+  <subsection name="Request Filters">
+
+    <p>You can ask Catalina to check the IP address, or host name, on every
+    incoming request directed to the surrounding
+    <a href="engine.html">Engine</a>, <a href="host.html">Host</a>, or
+    <a href="context.html">Context</a> element.  The remote address or name
+    will be checked against a configured list of "accept" and/or "deny"
+    filters, which are defined using the Regular Expression syntax supported
+    by the <a href="http://jakarta.apache.org/regexp/">Jakarta Regexp</a>
+    regular expression library.  Requests that come from locations that are
+    not accepted will be rejected with an HTTP "Forbidden" error.
+    Example filter declarations:</p>
+
+<source>
+&lt;Engine name="Standalone" ...&gt;
+  ...
+  &lt;Valve className="org.apache.catalina.valves.RemoteHostValve"
+         allow="*.mycompany.com,www.yourcompany.com"/&gt;
+  &lt;Valve className="org.apache.catalina.valves.RemoteAddrValve"
+         deny="192.168.1.*"/&gt;
+  ...
+&lt;/Engine&gt;
+</source>
+
+  <p>See <a href="valve.html#Remote Address Filter">Remote Address Filter</a>
+  and <a href="valve.html#Remote Host Filter">Remote Host Filter</a> for
+  more information about the configuration options that are supported.</p>
+
+  </subsection>
+
+
+</section>
+
+
+</body>
+
+
+</document>
diff --git a/container/webapps/docs/config/globalresources.xml b/container/webapps/docs/config/globalresources.xml
new file mode 100644
index 0000000..f7e574a
--- /dev/null
+++ b/container/webapps/docs/config/globalresources.xml
@@ -0,0 +1,232 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="globalresources.html">
+
+  &project;
+
+  <properties>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>The GlobalNamingResources Component</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>The <strong>GlobalNamingResources</strong> element defines the global
+  JNDI resources for the <a href="server.html">Server</a>.</p>
+
+  <p>These resources are listed in the server's global JNDI resource context.
+   This context is distinct from the per-web-application JNDI contexts 
+  described in
+  the <a href="../jndi-resources-howto.html">JNDI Resources HOW-TO</a>.
+  The resources defined in this element are <strong>not</strong> visible in
+  the per-web-application contexts unless you explicitly link them with
+  <a href="context.html#Resource Links">&lt;ResourceLink&gt;</a> elements.
+  </p>
+
+</section>
+
+
+<section name="Attributes">
+
+</section>
+
+
+<section name="Nested Components">
+
+</section>
+
+
+<section name="Special Features">
+
+
+  <subsection name="Environment Entries">
+
+  <p>You can configure named values that will be made visible to all
+    web applications as environment entry resources by nesting
+    <code>&lt;Environment&gt;</code> entries inside this element. For
+    example, you can create an environment entry like this:</p>
+<source>
+&lt;GlobalNamingResources ...&gt;
+  ...
+  &lt;Environment name="maxExemptions" value="10"
+         type="java.lang.Integer" override="false"/&gt;
+  ...
+&lt;/GlobalNamingResources&gt;
+</source>
+
+    <p>This is equivalent to the inclusion of the following element in the
+    web application deployment descriptor (<code>/WEB-INF/web.xml</code>):
+    </p>
+<source>
+&lt;env-entry&gt;
+  &lt;env-entry-name&gt;maxExemptions&lt;/param-name&gt;
+  &lt;env-entry-value&gt;10&lt;/env-entry-value&gt;
+  &lt;env-entry-type&gt;java.lang.Integer&lt;/env-entry-type&gt;
+&lt;/env-entry&gt;
+</source>
+    <p>but does <em>not</em> require modification of the deployment descriptor
+    to customize this value.</p>
+
+    <p>The valid attributes for an <code>&lt;Environment&gt;</code> element
+    are as follows:</p>
+
+    <attributes>
+
+      <attribute name="description" required="false">
+        <p>Optional, human-readable description of this environment entry.</p>
+      </attribute>
+
+      <attribute name="name" required="true">
+        <p>The name of the environment entry to be created, relative to the
+        <code>java:comp/env</code> context.</p>
+      </attribute>
+
+      <attribute name="override" required="false">
+        <p>Set this to <code>false</code> if you do <strong>not</strong> want
+        an <code>&lt;env-entry&gt;</code> for the same environment entry name,
+        found in the web application deployment descriptor, to override the
+        value specified here.  By default, overrides are allowed.</p>
+      </attribute>
+
+      <attribute name="type" required="true">
+        <p>The fully qualified Java class name expected by the web application
+        for this environment entry.  Must be one of the legal values for
+        <code>&lt;env-entry-type&gt;</code> in the web application deployment
+        descriptor:  <code>java.lang.Boolean</code>,
+        <code>java.lang.Byte</code>, <code>java.lang.Character</code>,
+        <code>java.lang.Double</code>, <code>java.lang.Float</code>,
+        <code>java.lang.Integer</code>, <code>java.lang.Long</code>,
+        <code>java.lang.Short</code>, or <code>java.lang.String</code>.</p>
+      </attribute>
+
+      <attribute name="value" required="true">
+        <p>The parameter value that will be presented to the application
+        when requested from the JNDI context.  This value must be convertable
+        to the Java type defined by the <code>type</code> attribute.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Resource Definitions">
+
+    <p>You can declare the characteristics of resources
+    to be returned for JNDI lookups of <code>&lt;resource-ref&gt;</code> and
+    <code>&lt;resource-env-ref&gt;</code> elements in the web application
+    deployment descriptor by defining them in this element and then linking 
+    them with <a href="context.html#Resource Links">&lt;ResourceLink&gt;</a> 
+    elements
+    in the <code><strong>&lt;Context&gt;</strong></code> element.  
+
+    You <strong>MUST</strong> also define any other needed parameters using
+    attributes on the Resource element, to configure
+    the object factory to be used (if not known to Tomcat already), and
+    the properties used to configure that object factory.</p>
+
+    <p>For example, you can create a resource definition like this:</p>
+<source>
+&lt;GlobalNamingResources ...&gt;
+  ...
+  &lt;Resource name="jdbc/EmployeeDB" auth="Container"
+            type="javax.sql.DataSource"
+     description="Employees Database for HR Applications"/&gt;
+  ...
+&lt;/GlobalNamingResources&gt;
+</source>
+
+    <p>This is equivalent to the inclusion of the following element in the
+    web application deployment descriptor (<code>/WEB-INF/web.xml</code>):</p>
+<source>
+&lt;resource-ref&gt;
+  &lt;description&gt;Employees Database for HR Applications&lt;/description&gt;
+  &lt;res-ref-name&gt;jdbc/EmployeeDB&lt;/res-ref-name&gt;
+  &lt;res-ref-type&gt;javax.sql.DataSource&lt;/res-ref-type&gt;
+  &lt;res-auth&gt;Container&lt;/res-auth&gt;
+&lt;/resource-ref&gt;
+</source>
+
+    <p>but does <em>not</em> require modification of the deployment
+    descriptor to customize this value.</p>
+
+    <p>The valid attriutes for a <code>&lt;Resource&gt;</code> element
+    are as follows:</p>
+
+    <attributes>
+
+      <attribute name="auth" required="false">
+        <p>Specify whether the web Application code signs on to the
+        corresponding resource manager programatically, or whether the
+        Container will sign on to the resource manager on behalf of the
+        application.  The value of this attribute must be
+        <code>Application</code> or <code>Container</code>.  This
+        attribute is <strong>required</strong> if the web application
+        will use a <code>&lt;resource-ref&gt;</code> element in the web
+        application deployment descriptor, but is optional if the
+        application uses a <code>&lt;resource-env-ref&gt;</code> instead.</p>
+      </attribute>
+
+      <attribute name="description" required="false">
+        <p>Optional, human-readable description of this resource.</p>
+      </attribute>
+
+      <attribute name="name" required="true">
+        <p>The name of the resource to be created, relative to the
+        <code>java:comp/env</code> context.</p>
+      </attribute>
+
+      <attribute name="scope" required="false">
+        <p>Specify whether connections obtained through this resource
+        manager can be shared.  The value of this attribute must be
+        <code>Shareable</code> or <code>Unshareable</code>.  By default,
+        connections are assumed to be shareable.</p>
+      </attribute>
+
+      <attribute name="type" required="true">
+        <p>The fully qualified Java class name expected by the web
+        application when it performs a lookup for this resource.</p>
+      </attribute>
+
+    </attributes>
+
+
+  </subsection>
+
+  <subsection name="Resource Links">
+    <p>Use <a href="context.html#Resource Links">&lt;ResourceLink&gt;</a> 
+    elements to link resources from the global context into 
+    per-web-application contexts. Here is an example of making a custom 
+    factory available to all applications in the server, based on the example 
+    definition in the 
+    <a href="../jndi-resource-howto.html#Generic JavaBean Resources">
+    JNDI Resource HOW-TO</a>:
+    </p>
+
+    <source>
+      <![CDATA[
+        <DefaultContext>
+          <ResourceLink 
+            name="bean/MyBeanFactory"
+            global="bean/MyBeanFactory"
+            type="com.mycompany.MyBean"
+          />
+        </DefaultContext>
+      ]]>
+    </source>
+
+   </subsection>
+
+</section>
+
+
+</body>
+
+
+</document>
diff --git a/container/webapps/docs/config/host.xml b/container/webapps/docs/config/host.xml
new file mode 100644
index 0000000..fe745e4
--- /dev/null
+++ b/container/webapps/docs/config/host.xml
@@ -0,0 +1,541 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="host.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>The Host Container</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>The <strong>Host</strong> element represents a <em>virtual host</em>,
+  which is an association of a network name for a server (such as
+  "www.mycompany.com" with the particular server on which Catalina is
+  running.  In order to be effective, this name must be registered in the
+  <em>Domain Name Service</em> (DNS) server that manages the Internet
+  domain you belong to - contact your Network Administrator for more
+  information.</p>
+
+  <p>In many cases, System Administrators wish to associate more than
+  one network name (such as <code>www.mycompany.com</code> and
+  <code>company.com</code>) with the same virtual host and applications.
+  This can be accomplished using the <a href="#Host Name Aliases">Host
+  Name Aliases</a> feature discussed below.</p>
+
+  <p>One or more <strong>Host</strong> elements are nested inside an
+  <a href="engine.html">Engine</a> element.  Inside the Host element, you
+  can nest <a href="context.html">Context</a> elements for the web
+  applications associated with this virtual host.  Exactly one of the Hosts
+  associated with each Engine MUST have a name matching the
+  <code>defaultHost</code> attribute of that Engine.</p>
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+    <p>All implementations of <strong>Host</strong>
+    support the following attributes:</p>
+
+    <attributes>
+
+      <attribute name="appBase" required="true">
+        <p>The <em>Application Base</em> directory for this virtual host.
+        This is the pathname of a directory that may contain web applications
+        to be deployed on this virtual host.  You may specify an
+        absolute pathname for this directory, or a pathname that is relative
+        to the <code>$CATALINA_BASE</code> directory.  See
+        <a href="#Automatic Application Deployment">Automatic Application
+        Deployment</a> for more information on automatic recognition and
+        deployment of web applications to be deployed automatically.</p>
+      </attribute>
+
+      <attribute name="autoDeploy" required="false">
+        <p>This flag value indicates if new web applications, dropped in to
+        the <code>appBase</code> directory while Tomcat is running, should
+        be automatically deployed.  The flag's value defaults to true.  See
+        <a href="#Automatic Application Deployment">Automatic Application
+        Deployment</a> for more information.</p>
+      </attribute>
+
+      <attribute name="backgroundProcessorDelay" required="false">
+        <p>This value represents the delay in seconds between the 
+        invocation of the backgroundProcess method on this host and 
+        its child containers, including all contexts. 
+        Child containers will not be invoked if their delay value is not 
+        negative (which would mean they are using their own processing 
+        thread). Setting this to a positive value will cause 
+        a thread to be spawn. After waiting the specified amount of time, 
+        the thread will invoke the backgroundProcess method on this host 
+        and all its child containers. A host will use background processing to
+        perform live web application deployment related tasks. If not 
+        specified, the default value for this attribute is -1, which means 
+        the host will rely on the background processing thread of its parent 
+        engine.</p>
+      </attribute>
+
+      <attribute name="className" required="false">
+        <p>Java class name of the implementation to use.  This class must
+        implement the <code>org.apache.catalina.Host</code> interface.
+        If not specified, the standard value (defined below) will be used.</p>
+      </attribute>
+
+      <attribute name="deployOnStartup" required="false">
+        <p>This flag value indicates if web applications from this host should
+        be automatically deployed by the host configurator.
+        The flag's value defaults to true.  See
+        <a href="#Automatic Application Deployment">Automatic Application
+        Deployment</a> for more information.</p>
+      </attribute>
+
+      <attribute name="name" required="true">
+        <p>Network name of this virtual host, as registered in your
+        <em>Domain Name Service</em> server.  One of the Hosts nested within
+        an <a href="engine.html">Engine</a> MUST have a name that matches the
+        <code>defaultHost</code> setting for that Engine.  See
+        <a href="#Host Name Aliases">Host Name Aliases</a> for information
+        on how to assign more than one network name to the same
+        virtual host.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Standard Implementation">
+
+    <p>The standard implementation of <strong>Host</strong> is
+    <strong>org.apache.catalina.core.StandardHost</strong>.
+    It supports the following additional attributes (in addition to the
+    common attributes listed above):</p>
+
+    <attributes>
+
+      <attribute name="deployXML" required="false">
+        <p>Set to <code>false</code> if you want to disable parsing the context.xml
+        file embedded inside the application (located at <code>/META-INF/context.xml</code>). 
+        Security consious environments should set this to <code>false</code> to prevent
+        applications from interacting with the container's configuration. The 
+        administrator will then be responsible for providing an external context 
+        configuration file, and put it in 
+        <code>$CATALINA_HOME/conf/[enginename]/[hostname]/</code>.
+        The flag's value defaults to <code>true</code>.</p>
+      </attribute>
+
+      <attribute name="errorReportValveClass" required="false">
+        <p>Java class name of the error reporting valve which will be used
+        by this Host. The responsability of this valve is to output error
+        reports. Setting this property allows to customize the look of the
+        error pages which will be generated by Tomcat. This class must
+        implement the
+        <code>org.apache.catalina.Valve</code> interface. If none is specified,
+        the value <code>org.apache.catalina.valves.ErrorReportValve</code>
+        will be used by default.</p>
+      </attribute>
+
+      <attribute name="unpackWARs" required="false">
+        <p>Set to <code>true</code> if you want web applications that are
+        placed in the <code>appBase</code> directory as web application
+        archive (WAR) files to be unpacked into a corresponding disk directory
+        structure, <code>false</code> to run such web applications directly
+        from a WAR file.  See
+        <a href="#Automatic Application Deployment">Automatic Application
+        Deployment</a> for more information.</p>
+      </attribute>
+
+      <attribute name="workDir" required="false">
+        <p>Pathname to a scratch directory to be used by applications for
+        this Host. Each application will have its own sub directory with
+        temporary read-write use.  Configuring a Context workDir will override
+        use of the Host workDir configuration.  This directory will be made
+        visible to servlets in the web application by a servlet context
+        attribute (of type <code>java.io.File</code>) named
+        <code>javax.servlet.context.tempdir</code> as described in the
+        Servlet Specification.  If not specified, a suitable directory
+        underneath <code>$CATALINA_HOME/work</code> will be provided.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>You can nest one or more <a href="context.html">Context</a> elements
+  inside this <strong>Host</strong> element, each representing a different web
+  application associated with this virtual host.  In addition, you can nest a
+  single <a href="defaultcontext.html">DefaultContext</a> element that defines
+  default values for subsequently deployed web applications.</p>
+
+  <p>You can optional nest a <a href="defaultcontext.html">DefaultContext</a>
+  element inside this <strong>Host</strong> element, to define the default
+  characteristics of web applications that are automatically deployed.</p>
+
+  <p>You can nest at most one instance of the following utility components
+  by nesting a corresponding element inside your <strong>Host</strong>
+  element:</p>
+  <ul>
+  <li><a href="realm.html"><strong>Realm</strong></a> -
+      Configure a realm that will allow its
+      database of users, and their associated roles, to be shared across all
+      <a href="context.html">Contexts</a> nested inside this Host (unless
+      overridden by a <a href="realm.html">Realm</a> configuration
+      at a lower level).</li>
+  </ul>
+
+</section>
+
+
+<section name="Special Features">
+
+
+  <subsection name="Logging">
+
+    <p>A host is associated with the 
+       <code>org.apache.catalina.core.ContainerBase.[enginename].[hostname]</code>
+       log category.  Note that the brackets are actuall part of the name,
+       don't omit them.</p>
+
+  </subsection>
+
+
+  <subsection name="Access Logs">
+
+    <p>When you run a web server, one of the output files normally generated
+    is an <em>access log</em>, which generates one line of information for
+    each request processed by the server, in a standard format.  Catalina
+    includes an optional <a href="valve.html">Valve</a> implementation that
+    can create access logs in the same standard format created by web servers,
+    or in any number of custom formats.</p>
+
+    <p>You can ask Catalina to create an access log for all requests
+    processed by an <a href="engine.html">Engine</a>,
+    <a href="host.html">Host</a>, or <a href="context.html">Context</a>
+    by nesting a <a href="valve.html">Valve</a> element like this:</p>
+
+<source>
+&lt;Host name="localhost" ...&gt;
+  ...
+  &lt;Valve className="org.apache.catalina.valves.AccessLogValve"
+         prefix="localhost_access_log." suffix=".txt"
+         pattern="common"/&gt;
+  ...
+&lt;/Host&gt;
+</source>
+
+    <p>See <a href="valve.html#Access Log Valve">Access Log Valve</a>
+    for more information on the configuration attributes that are
+    supported.</p>
+
+  </subsection>
+
+
+  <subsection name="Automatic Application Deployment">
+
+    <p>If you are using the standard <strong>Host</strong> implementation,
+    the following actions take place automatically when Catalina is first
+    started, if the <code>deployOnStartup</code> property is set to
+    <code>true</code> (which is the default value):</p>
+    <ul>
+    <li>Any XML file in the 
+        <code>$CATALINA_HOME/conf/[engine_name]/[host_name]</code> directory is
+        assumed to contain a
+        <a href="context.html">Context</a> element (and its associated
+        subelements) for a single web application.  The <code>docBase</code>
+        attribute of this <code>&lt;Context&gt;</code> element will typically
+        be the absolute pathname to a web application directory, or the
+        absolute pathname of a web application archive (WAR) file (which
+        will not be expanded).</li>
+    <li>Any web application archive file within the application base (appBase)
+        directory that does not have a corresponding
+        directory of the same name (without the ".war" extension) will be
+        automatically expanded, unless the <code>unpackWARs</code> property
+        is set to <code>false</code>.  If you redeploy an updated WAR file,
+        be sure to delete the expanded directory when restarting Tomcat, so
+        that the updated WAR file will be re-expanded (note that the auto
+        deployer will automatically take care of this if it is enabled).</li>
+    <li>Any subdirectory within the <em>application base directory</em>
+        that appears to be an unpacked web application (that is, it contains
+        a <code>/WEB-INF/web.xml</code> file) will receive an automatically
+        generated <a href="context.html">Context</a> element, even if this
+        directory is not mentioned in the <code>conf/server.xml</code> file.
+        This generated Context entry will be configured according to the
+        properties set in any <a href="defaultcontext.html">DefaultContext</a>
+        element nested in this Host element.  The context path for this
+        deployed Context will be a slash character ("/") followed by the
+        directory name, unless the directory name is ROOT, in which case
+        the context path will be an empty string ("").</li>
+    </ul>
+
+    <p>In addition to the automatic deployment that occurs at startup time,
+    you can also request that new XML configuration files, WAR files, or
+    subdirectories (containing web applications) that are dropped in to the
+    <code>appBase</code> (or 
+    <code>$CATALINA_HOME/conf/[engine_name]/[host_name]</code> in the case of
+    an XML configuration file) directory while Tomcat is running will be
+    automatically deployed, according to the rules described above. The 
+    auto deployer will also track web applications for the following changes:
+    <ul>
+        <li>An update to the WEB-INF/web.xml file will trigger a reload of the
+        web application</li>
+        <li>An update to a WAR which has been expanded will trigger 
+        an undeploy (<strong>with a removal of the expanded webapp</strong>), 
+        followed by a deployment</li>
+        <li>An update to a XML configuration file will trigger an undeploy
+        (without the removal of any expanded directory), followed by 
+        a deployment of the associated web application</li>
+    </ul>
+    </p>
+
+    <p>When using automatic deployment, the <code>docBase</code> defined by
+    an XML <a href="context.html">Context</a> file should be outside of the
+    <code>appBase</code> directory. If this is not the case difficulties
+    may be experienced deploying the web application or the application may
+    be deployed twice.</p>
+
+    <p>Finally, note that if you are defining contexts explicitly, you should
+    probably turn off automatic application deployment.  Otherwise, your context
+    will be deployed twice each, and that may cause problems for your app.
+    </p>
+
+  </subsection>
+
+
+  <subsection name="Host Name Aliases">
+
+    <p>In many server environments, Network Administrators have configured
+    more than one network name (in the <em>Domain Name Service</em> (DNS)
+    server), that resolve to the IP address of the same server.  Normally,
+    each such network name would be configured as a separate
+    <strong>Host</strong> element in <code>conf/server.xml</code>, each
+    with its own set of web applications.</p>
+
+    <p>However, in some circumstances, it is desireable that two or more
+    network names should resolve to the <strong>same</strong> virtual host,
+    running the same set of applications.  A common use case for this
+    scenario is a corporate web site, where it is desireable that users
+    be able to utilize either <code>www.mycompany.com</code> or
+    <code>company.com</code> to access exactly the same content and
+    applications.</p>
+
+    <p>This is accomplished by utilizing one or more <strong>Alias</strong>
+    elements nested inside your <strong>Host</strong> element.  For
+    example:</p>
+<source>
+&lt;Host name="www.mycompany.com" ...&gt;
+  ...
+  &lt;Alias&gt;mycompany.com&lt;/Alias&gt;
+  ...
+&lt;/Host&gt;
+</source>
+
+    <p>In order for this strategy to be effective, all of the network names
+    involved must be registered in your DNS server to resolve to the
+    same computer that is running this instance of Catalina.</p>
+
+  </subsection>
+
+
+  <subsection name="Lifecycle Listeners">
+
+    <p>If you have implemented a Java object that needs to know when this
+    <strong>Host</strong> is started or stopped, you can declare it by
+    nesting a <strong>Listener</strong> element inside this element.  The
+    class name you specify must implement the
+    <code>org.apache.catalina.LifecycleListener</code> interface, and
+    it will be notified about the occurrence of the coresponding
+    lifecycle events.  Configuration of such a listener looks like this:</p>
+
+<source>
+&lt;Host name="localhost" ...&gt;
+  ...
+  &lt;Listener className="com.mycompany.mypackage.MyListener" ... &gt;
+  ...
+&lt;/Host&gt;
+</source>
+
+    <p>Note that a Listener can have any number of additional properties
+    that may be configured from this element.  Attribute names are matched
+    to corresponding JavaBean property names using the standard property
+    method naming patterns.</p>
+
+  </subsection>
+
+
+  <subsection name="Request Filters">
+
+    <p>You can ask Catalina to check the IP address, or host name, on every
+    incoming request directed to the surrounding
+    <a href="engine.html">Engine</a>, <a href="host.html">Host</a>, or
+    <a href="context.html">Context</a> element.  The remote address or name
+    will be checked against a configured list of "accept" and/or "deny"
+    filters, which are defined using the Regular Expression syntax supported
+    by the <a href="http://jakarta.apache.org/regexp/">Jakarta Regexp</a>
+    regular expression library.  Requests that come from locations that are
+    not accepted will be rejected with an HTTP "Forbidden" error.
+    Example filter declarations:</p>
+
+<source>
+&lt;Host name="localhost" ...&gt;
+  ...
+  &lt;Valve className="org.apache.catalina.valves.RemoteHostValve"
+         allow="*.mycompany.com,www.yourcompany.com"/&gt;
+  &lt;Valve className="org.apache.catalina.valves.RemoteAddrValve"
+         deny="192.168.1.*"/&gt;
+  ...
+&lt;/Host&gt;
+</source>
+
+  <p>See <a href="valve.html#Remote Address Filter">Remote Address Filter</a>
+  and <a href="valve.html#Remote Host Filter">Remote Host Filter</a> for
+  more information about the configuration options that are supported.</p>
+
+  </subsection>
+
+
+  <subsection name="Single Sign On">
+
+    <p>In many environments, but particularly in portal environments, it
+    is desireable to have a user challenged to authenticate themselves only
+    once over a set of web applications deployed on a particular virtual
+    host.  This can be accomplished by nesting an element like this inside
+    the Host element for this virtual host:</p>
+
+<source>
+&lt;Host name="localhost" ...&gt;
+  ...
+  &lt;Valve className="org.apache.catalina.authenticator.SingleSignOn"
+         debug="0"/&gt;
+  ...
+&lt;/Host&gt;
+</source>
+
+    <p>The Single Sign On facility operates according to the following rules:
+    </p>
+    <ul>
+    <li>All web applications configured for this virtual host must share the
+        same <a href="realm.html">Realm</a>.  In practice, that means you can
+        nest the Realm element inside this Host element (or the surrounding
+        <a href="engine.html">Engine</a> element), but not inside a
+        <a href="context.html">Context</a> element for one of the involved
+        web applications.</li>
+    <li>As long as the user accesses only unprotected resources in any of the
+        web applications on this virtual host, they will not be challenged
+        to authenticate themselves.</li>
+    <li>As soon as the user accesses a protected resource in
+        <strong>any</strong> web application associated with this virtual
+        host, the user will be challenged to authenticate himself or herself,
+        using the login method defined for the web application currently
+        being accessed.</li>
+    <li>Once authenticated, the roles associated with this user will be
+        utilized for access control decisions across <strong>all</strong>
+        of the associated web applications, without challenging the user
+        to authenticate themselves to each application individually.</li>
+    <li>As soon as the user logs out of one web application (for example,
+        by invalidating the corresponding session if form
+        based login is used), the user's sessions in <strong>all</strong>
+        web applications will be invalidated.  Any subsequent attempt to
+        access a protected resource in any application will require the
+        user to authenticate himself or herself again.</li>
+    <li>The Single Sign On feature utilizes HTTP cookies to transmit a token
+        that associates each request with the saved user identity, so it can
+        only be utilized in client environments that support cookies.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="User Web Applications">
+
+    <p>Many web servers can automatically map a request URI starting with
+    a tilde character ("~") and a username to a directory (commonly named
+    <code>public_html</code>) in that user's home directory on the server.
+    You can accomplish the same thing in Catalina by using a special
+    <strong>Listener</strong> element like this (on a Unix system that
+    uses the <code>/etc/passwd</code> file to identify valid users):</p>
+
+<source>
+&lt;Host name="localhost" ...&gt;
+  ...
+  &lt;Listener className="org.apache.catalina.startup.UserConfig"
+            directoryName="public_html"
+            userClass="org.apache.catalina.startup.PasswdUserDatabase"/&gt;
+  ...
+&lt;/Host&gt;
+</source>
+
+    <p>On a server where <code>/etc/passwd</code> is not in use, you can
+    request Catalina to consider all directories found in a specified base
+    directory (such as <code>c:\Homes</code> in this example) to be
+    considered "user home" directories for the purposes of this directive:</p>
+
+<source>
+&lt;Host name="localhost" ...&gt;
+  ...
+  &lt;Listener className="org.apache.catalina.startup.UserConfig"
+            directoryName="public_html"
+            homeBase=c:\Homes"
+            userClass="org.apache.catalina.startup.HomesUserDatabase"/&gt;
+  ...
+&lt;/Host&gt;
+</source>
+
+    <p>If a user home directory has been set up for a user named
+    <code>craigmcc</code>, then its contents will be visible from a
+    client browser by making a request to a URL like:</p>
+
+<source>
+http://www.mycompany.com:8080/~craigmcc
+</source>
+
+    <p>Successful use of this feature requires recognition of the following
+    considerations:</p>
+    <ul>
+    <li>Each user web application will be deployed with characteristics
+        established by any <a href="defaultcontext.html">DefaultContext</a>
+        element you have configured for this Host.</li>
+    <li>It is legal to include more than one instance of this Listener
+        element.  This would only be useful, however, in circumstances
+        where you wanted to configure more than one "homeBase" directory.</li>
+    <li>The operating system username under which Catalina is executed
+        MUST have read access to each user's web application directory,
+        and all of its contents.</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+</body>
+
+
+</document>
diff --git a/container/webapps/docs/config/http.xml b/container/webapps/docs/config/http.xml
new file mode 100644
index 0000000..b9317ca
--- /dev/null
+++ b/container/webapps/docs/config/http.xml
@@ -0,0 +1,476 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="http.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>The HTTP Connector</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>The <strong>HTTP Connector</strong> element represents a
+  <strong>Connector</strong> component that supports the HTTP/1.1 protocol.
+  It enables Catalina to function as a stand-alone web server, in addition
+  to its ability to execute servlets and JSP pages.  A particular instance
+  of this component listens for connections on a specific TCP port number
+  on the server.  One or more such <strong>Connectors</strong> can be
+  configured as part of a single <a href="service.html">Service</a>, each
+  forwarding to the associated <a href="engine.html">Engine</a> to perform
+  request processing and create the response.</p>
+
+  <p>If you wish to configure the <strong>Connector</strong> that is used
+  for connections to web servers using the AJP protocol (such as the
+  <code>mod_jk 1.2.x</code> connector for Apache 1.3), see
+  <a href="ajp.html">here</a> instead.</p>
+
+  <p>At server startup time, this <strong>Connector</strong> will create a
+  number of request processing threads (based on the value configured for
+  the <code>minSpareThreads</code> attribute).  Each incoming request requires
+  a thread for the duration of that request.  If more simultaneous requests
+  are received than can be handled by the currently available request
+  processing threads, additional threads will be created up to the
+  configured maximum (the value of the <code>maxThreads</code> attribute).
+  If still more simultaneous requests are received, they are stacked up
+  inside the server socket created by the <strong>Connector</strong>, up to
+  the configured maximum (the value of the <code>acceptCount</code>
+  attribute.  Any further simultaneous requests will receive "connection
+  refused" errors, until resources are available to process them.</p>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+  <p>All implementations of <strong>Connector</strong>
+  support the following attributes:</p>
+
+  <attributes>
+ 
+    <attribute name="allowTrace" required="false">
+      <p>A boolean value which can be used to enable or disable the TRACE
+      HTTP method. If not specified, this attribute is set to false.</p>
+    </attribute>
+
+    <attribute name="emptySessionPath" required="false">
+      <p>If set to <code>true</code>, all paths for session cookies will be set
+      to <code>/</code>. This can be useful for portlet specification implementations,
+      but will greatly affect performance if many applications are accessed on a given
+      server by the client.
+      If not specified, this attribute is set to <code>false</code>.</p>
+    </attribute>
+
+    <attribute name="enableLookups" required="false">
+      <p>Set to <code>true</code> if you want calls to
+      <code>request.getRemoteHost()</code> to perform DNS lookups in
+      order to return the actual host name of the remote client.  Set
+      to <code>false</code> to skip the DNS lookup and return the IP
+      address in String form instead (thereby improving performance).
+      By default, DNS lookups are enabled.</p>
+    </attribute>
+
+    <attribute name="maxPostSize" required="false">
+      <p>The maximum size in bytes of the POST which will be handled by
+      the container FORM URL parameter parsing. The limit can be disabled by
+      setting this attribute to a value less than or equal to 0.
+      If not specified, this attribute is set to 2097152 (2 megabytes).</p>
+    </attribute>
+
+    <attribute name="maxSavePostSize" required="false">
+      <p>The maximum size in bytes of the POST which will be saved/buffered by
+      the container during FORM or CLIENT-CERT authentication. For both types
+      of authentication, the POST will be saved/buffered before the user is
+      authenticated. For CLIENT-CERT authentication, the POST is buffered for
+      the duration of
+ the SSL handshake and the buffer emptied when the request
+      is processed. For FORM authentication the POST is
+ saved whilst the user
+      is re-directed to the login form and is retained until the user
+      successfully authenticates or the session associated with the
+      authentication request expires. The limit can be disabled by setting this
+      attribute to -1. Setting the attribute to
+ zero will disable the saving of
+      POST data during authentication
+. If not
+ specified, this attribute is set
+      to
+ 4096 (4 kilobytes).</p>
+    </attribute>
+
+    <attribute name="protocol" required="false">
+      <p>This attribute value must be <code>HTTP/1.1</code> to use the HTTP
+      handler, which is the default.</p>
+    </attribute>
+
+    <attribute name="proxyName" required="false">
+      <p>If this <strong>Connector</strong> is being used in a proxy
+      configuration, configure this attribute to specify the server name
+      to be returned for calls to <code>request.getServerName()</code>.
+      See <a href="#Proxy Support">Proxy Support</a> for more
+      information.</p>
+    </attribute>
+
+    <attribute name="proxyPort" required="false">
+      <p>If this <strong>Connector</strong> is being used in a proxy
+      configuration, configure this attribute to specify the server port
+      to be returned for calls to <code>request.getServerPort()</code>.
+      See <a href="#Proxy Support">Proxy Support</a> for more
+      information.</p>
+    </attribute>
+
+    <attribute name="redirectPort" required="false">
+      <p>If this <strong>Connector</strong> is supporting non-SSL
+      requests, and a request is received for which a matching
+      <code>&lt;security-constraint&gt;</code> requires SSL transport,
+      Catalina will automatically redirect the request to the port
+      number specified here.</p>
+    </attribute>
+
+    <attribute name="scheme" required="false">
+      <p>Set this attribute to the name of the protocol you wish to have
+      returned by calls to <code>request.getScheme()</code>.  For
+      example, you would set this attribute to "<code>https</code>"
+      for an SSL Connector.  The default value is "<code>http</code>".
+      See <a href="#SSL Support">SSL Support</a> for more information.</p>
+    </attribute>
+
+    <attribute name="secure" required="false">
+      <p>Set this attribute to <code>true</code> if you wish to have
+      calls to <code>request.isSecure()</code> to return <code>true</code>
+      for requests received by this Connector (you would want this on an
+      SSL Connector).  The default value is <code>false</code>.</p>
+    </attribute>
+
+    <attribute name="URIEncoding" required="false">
+      <p>This specifies the character encoding used to decode the URI bytes,
+      after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
+      </p>
+    </attribute>
+
+    <attribute name="useBodyEncodingForURI" required="false">
+      <p>This specifies if the encoding specified in contentType should be used
+      for URI query parameters, instead of using the URIEncoding. This
+      setting is present for compatibility with Tomcat 4.1.x, where the
+      encoding specified in the contentType, or explicitely set using
+      Request.setCharacterEncoding method was also used for the parameters from
+      the URL. The default value is <code>false</code>.
+      </p>
+    </attribute>
+
+    <attribute name="useIPVHosts" required="false">
+      <p>Set this attribute to <code>true</code> to cause Tomcat to use
+      the IP address that the request was recieved on to determine the Host
+      to send the request to.  The default value is <code>false</code>.</p>
+    </attribute>
+
+    <attribute name="xpoweredBy" required="false">
+      <p>Set this attribute to <code>true</code> to cause Tomcat to advertise
+      support for the Servlet specification using the header recommended in the
+      specification.  The default value is <code>false</code>.</p>
+    </attribute>
+
+
+  </attributes>
+
+  </subsection>
+
+  <subsection name="Standard Implementation">
+
+  <p>
+  HTTP supports the following additional attributes (in addition to the
+  common attributes listed above):</p>
+
+  <attributes>
+
+    <attribute name="acceptCount" required="false">
+      <p>The maximum queue length for incoming connection requests when
+      all possible request processing threads are in use.  Any requests
+      received when the queue is full will be refused.  The default
+      value is 10.</p>
+    </attribute>
+
+    <attribute name="address" required="false">
+      <p>For servers with more than one IP address, this attribute
+      specifies which address will be used for listening on the specified
+      port.  By default, this port will be used on all IP addresses
+      associated with the server.</p>
+    </attribute>
+
+    <attribute name="bufferSize" required="false">
+      <p>The size (in bytes) of the buffer to be provided for input
+      streams created by this connector.  By default, buffers of
+      2048 bytes will be provided.</p>
+    </attribute>
+
+    <attribute name="compressableMimeType" required="false">
+      <p>The value is a comma separated list of MIME types for which HTTP
+      compression may be used.
+      The default value is <code>text/html,text/xml,text/plain</code>.</p>
+    </attribute>
+
+    <attribute name="clientAuth" required="false">
+      <p>
+        Set this value to <code>true</code> if you want Tomcat to require
+        all SSL clients to present a client Certificate in order to use
+        this socket.  Set this value to <code>want</code> if you want Tomcat
+        to request a client Certificate, but not fail if one isn't presented.
+        See the <a href="../ssl-howto.html">SSL HowTo</a> for an example.
+      </p>
+    </attribute>
+
+    <attribute name="compression" required="false">
+      <p>The <strong>Connector</strong> may use HTTP/1.1 GZIP compression in
+      an attempt to save server bandwidth. The acceptable values for the
+      parameter is "off" (disable compression), "on" (allow compression, which
+      causes text data to be compressed), "force" (forces compression in all
+      cases), or a numerical integer value (which is equivalent to "on", but
+      specifies the minimum amount of data before the output is compressed). If
+      the content-length is not known and compression is set to "on" or more
+      aggressive, the output will also be compressed. If not specified, this
+      attribute is set to "off".</p>
+    </attribute>
+
+    <attribute name="connectionLinger" required="false">
+      <p>The number of milliseconds during which the sockets used by this
+      <strong>Connector</strong> will linger when they are closed.
+      The default value is -1 (socket linger is disabled).</p>
+    </attribute>
+
+    <attribute name="connectionTimeout" required="false">
+      <p>The number of milliseconds this <strong>Connector</strong> will wait,
+      after accepting a connection, for the request URI line to be
+      presented.  The default value is 60000 (i.e. 60 seconds).</p>
+    </attribute>
+
+    <attribute name="disableUploadTimeout" required="false">
+      <p>This flag allows the servlet container to use a different, longer
+      connection timeout while a servlet is being executed, which in the end
+      allows either the servlet a longer amount of time to complete its
+      execution, or a longer timeout during data upload. If not specified,
+      this attribute is set to "false".</p>
+    </attribute>
+
+    <attribute name="maxHttpHeaderSize" required="false">
+      <p>The maximum size of the request and response HTTP header, specified
+      in bytes.
+      If not specified, this attribute is set to 4096 (4 KB).</p>
+    </attribute>
+
+    <attribute name="maxKeepAliveRequests" required="false">
+      <p>The maximum number of HTTP requests which can be pipelined until
+      the connection is closed by the server. Setting this attribute to 1 will
+      disable HTTP/1.0 keep-alive, as well as HTTP/1.1 keep-alive and
+      pipelining. Setting this to -1 will allow an unlimited amount of
+      pipelined or keep-alive HTTP requests.
+      If not specified, this attribute is set to 100.</p>
+    </attribute>
+
+    <attribute name="maxSpareThreads" required="false">
+      <p>The maximum number of unused request processing threads that
+      will be allowed to exist until the thread pool starts stopping the
+      unnecessary threads.  The default value is 50.</p>
+    </attribute>
+
+    <attribute name="maxThreads" required="false">
+      <p>The maximum number of request processing threads to be created
+      by this <strong>Connector</strong>, which therefore determines the
+      maximum number of simultaneous requests that can be handled.  If
+      not specified, this attribute is set to 200.</p>
+    </attribute>
+
+    <attribute name="minSpareThreads" required="false">
+      <p>The number of request processing threads that will be created
+      when this <strong>Connector</strong> is first started.  The connector
+      will also make sure it has the specified number of idle processing
+      threads available. This attribute should be set to a value smaller
+      than that set for <code>maxThreads</code>.  The default value is 4.</p>
+    </attribute>
+
+    <attribute name="noCompressionUserAgents" required="false">
+      <p>The value is a comma separated list of regular expressions matching
+      user-agents of HTTP clients for which compression should not be used,
+      because these clients, although they do advertise support for the
+      feature, have a broken implementation.
+      The default value is an empty String (regexp matching disabled).</p>
+    </attribute>
+
+    <attribute name="port" required="true">
+      <p>The TCP port number on which this <strong>Connector</strong>
+      will create a server socket and await incoming connections.  Your
+      operating system will allow only one server application to listen
+      to a particular port number on a particular IP address.</p>
+    </attribute>
+
+    <attribute name="restrictedUserAgents" required="false">
+      <p>The value is a comma separated list of regular expressions matching
+      user-agents of HTTP clients for which HTTP/1.1 or HTTP/1.0 keep alive
+      should not be used, even if the clients advertise support for these
+      features.
+      The default value is an empty String (regexp matching disabled).</p>
+    </attribute>
+
+    <attribute name="server" required="false">
+      <p>The Server header for the http response.
+         Unless your paranoid, you won't need this feature.
+      </p>
+    </attribute>
+
+    <attribute name="socketBuffer" required="false">
+      <p>The size (in bytes) of the buffer to be provided for socket
+      output buffering. -1 can be specified to disable the use of a buffer.
+      By default, a buffers of 9000 bytes will be used.</p>
+    </attribute>
+
+    <attribute name="strategy" required="false">
+      <p>The thread pooling strategy which will be used. The default strategy does 
+      not use a master thread, but a more conventional strategy using a 
+      master listener thread can be used by setting "ms" as this attribute's value. 
+      The master strategy will work significantly better using the threadPriority 
+      attribute, which will apply only to the thread which listens on the server socket.
+      This is set to <code>lf</code> by default.
+      </p>
+    </attribute>
+
+    <attribute name="tcpNoDelay" required="false">
+      <p>If set to <code>true</code>, the TCP_NO_DELAY option will be
+      set on the server socket, which improves performance under most
+      circumstances.  This is set to <code>true</code> by default.</p>
+    </attribute>
+
+    <attribute name="threadPriority" required="false">
+      <p>The priority of the request processing threads within the JVM.
+      The default value is <code>java.lang.Thread#NORM_PRIORITY</code>.
+      See the JavaDoc for the java.lang.Thread class for more details on
+      what this priority means.
+      </p>
+    </attribute>
+
+  </attributes>
+
+  </subsection>
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>None at this time.</p>
+
+</section>
+
+
+<section name="Special Features">
+
+
+  <subsection name="HTTP/1.1 and HTTP/1.0 Support">
+
+  <p>This <strong>Connector</strong> supports all of the required features
+  of the HTTP/1.1 protocol, as described in RFC 2616, including persistent
+  connections, pipelining, expectations and chunked encoding.  If the client
+  (typically a browser) supports only HTTP/1.0, the
+  <strong>Connector</strong> will gracefully fall back to supporting this
+  protocol as well.  No special configuration is required to enable this
+  support. The <strong>Connector</strong> also supports HTTP/1.0
+  keep-alive.</p>
+
+  <p>RFC 2616 requires that HTTP servers always begin their responses with
+  the highest HTTP version that they claim to support.  Therefore, this
+  <strong>Connector</strong> will always return <code>HTTP/1.1</code> at
+  the beginning of its responses.</p>
+
+  </subsection>
+
+
+  <subsection name="Proxy Support">
+
+  <p>The <code>proxyName</code> and <code>proxyPort</code> attributes can
+  be used when Tomcat is run behind a proxy server.  These attributes
+  modify the values returned to web applications that call the
+  <code>request.getServerName()</code> and <code>request.getServerPort()</code>
+  methods, which are often used to construct absolute URLs for redirects.
+  Without configuring these attributes, the values returned would reflect
+  the server name and port on which the connection from the proxy server
+  was received, rather than the server name and port to whom the client
+  directed the original request.</p>
+
+  <p>For more information, see the
+  <a href="../proxy-howto.html">Proxy Support HOW-TO</a>.</p>
+
+  </subsection>
+
+
+  <subsection name="SSL Support">
+
+  <p>You can enable SSL support for a particular instance of this
+  <strong>Connector</strong> by setting the <code>secure</code> attribute to
+  <code>true</code>.  In addition, you may need to configure the following
+  attributes:</p>
+
+  <attributes>
+
+    <attribute name="algorithm" required="false">
+      <p>The certificate encoding algorithm to be used.  If not
+      specified, the default value is <code>SunX509</code>.</p>
+    </attribute>
+
+    <attribute name="clientAuth" required="false">
+      <p>Set to <code>true</code> if you want the SSL stack to
+      require a valid certificate chain from the client before
+      accepting a connection.  A <code>false</code> value (which
+      is the default) will not require a certificate chain unless
+      the client requests a resource protected by a security constraint
+      that uses <code>CLIENT-CERT</code> authentication.</p>
+    </attribute>
+
+    <attribute name="keystoreFile" required="false">
+      <p>The pathname of the keystore file where you have stored the
+      server certificate to be loaded.  By default, the pathname is
+      the file "<code>.keystore</code>" in the operating system home
+      directory of the user that is running Tomcat.</p>
+    </attribute>
+
+    <attribute name="keystorePass" required="false">
+      <p>The password used to access the server certificate from the
+      specified keystore file.  The default value is "<code>changeit</code>".
+      </p>
+    </attribute>
+
+    <attribute name="keystoreType" required="false">
+      <p>The type of keystore file to be used for the server certificate.
+      If not specified, the default value is "<code>JKS</code>".</p>
+    </attribute>
+
+    <attribute name="sslProtocol" required="false">
+      <p>The version of the SSL protocol to use.  If not specified,
+      the default is "<code>TLS</code>".</p>
+    </attribute>
+
+    <attribute name="ciphers" required="false">
+      <p>A comma seperated list of the encryption ciphers that may be used.
+      If not specified, then any available cipher may be used.</p>
+    </attribute>
+
+  </attributes>
+
+  <p>For more information, see the
+  <a href="../ssl-howto.html">SSL Configuration HOW-TO</a>.</p>
+
+  </subsection>
+
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/config/index.xml b/container/webapps/docs/config/index.xml
new file mode 100644
index 0000000..f94de89
--- /dev/null
+++ b/container/webapps/docs/config/index.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="index.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>Overview</title>
+  </properties>
+
+<body>
+
+
+<section name="Overview">
+
+<p>This manual contains reference information about all of the configuration
+directives that can be included in a <code>conf/server.xml</code> file to
+configure the behavior of the Tomcat 5 Servlet/JSP container.  It does not
+attempt to describe which configuration directives should be used to perform
+specific tasks - for that, see the various <em>HOW-TO</em> documents on the
+main index page.</p>
+
+<p>The configuration element descriptions are organized into the following
+major categories:</p>
+<ul>
+<li><strong>Top Level Elements</strong> - <code>&lt;Server&gt;</code> is the
+    root element of the entire configuration file, while
+    <code>&lt;Service&gt;</code> represents a group of Connectors that is
+    associated with an Engine.</li>
+<li><strong>Connectors</strong> - Represent the interface between external
+    clients sending requests to (and receiving responses from) a particular
+    Service.</li>
+<li><strong>Containers</strong> - Represent components whose function is to
+    process incoming requests, and create the corresponding responses.
+    An Engine handles all requests for a Service, a Host handles all requests
+    for a particular virtual host, and a Context handles all requests for a
+    specific web application.</li>
+<li><strong>Nested Components</strong> - Represent elements that can be
+    nested inside the element for a Container.  Some elements can be nested
+    inside any Container, while others can only be nested inside a
+    Context.</li>
+</ul>
+
+<p>For each element, the corresponding documentation follows this general
+outline:</p>
+<ul>
+<li><strong>Introduction</strong> - Overall description of this particular
+    component.  There will be a corresponding Java <em>interface</em> (in
+    the <code>org.apache.catalina</code> pacakge) that is implemented by one
+    or more standard implementations.</li>
+<li><strong>Attributes</strong> - The set of attributes that are legal for
+    this element.  Generally, this will be subdivided into <em>Common</em>
+    attributes that are supported by all implementations of the corresponding
+    Java interface, and <em>Standard Implementation</em> attributes that are
+    specific to a particular Java class that implements this interface.
+    The names of required attributes are <strong>bolded</strong>.</li>
+<li><strong>Nested Components</strong> - Enumerates which of the <em>Nested
+    Components</em> can be legally nested within this element.</li>
+<li><strong>Special Features</strong> - Describes the configuration of a large
+    variety of special features (specific to each element type) that are
+    supported by the standard implementation of this interface.</li>
+</ul>
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/config/loader.xml b/container/webapps/docs/config/loader.xml
new file mode 100644
index 0000000..05a1cbf
--- /dev/null
+++ b/container/webapps/docs/config/loader.xml
@@ -0,0 +1,149 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="loader.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>The Loader Component</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>The <strong>Loader</strong> element represents the <em>web
+  application class loader</em> that will be used to load Java
+  classes and resources for your web application.  Such
+  a class loader must follow the requirements of the Servlet
+  Specification, and load classes from the following locations:</p>
+  <ul>
+  <li>From the <code>/WEB-INF/classes</code> directory inside your
+      web application.</li>
+  <li>From JAR files in the <code>/WEB-INF/lib</code> directory
+      inside your web application.</li>
+  <li>From resources made available by Catalina to all web
+      applications globally.</li>
+  </ul>
+
+  <p>A Loader element MAY be nested inside a <a href="context.html">Context</a>
+  component.  If it is not included, a default Loader configuration will be
+  created automatically, which is sufficient for most requirements.</p>
+
+  <p>For a more in-depth description of the class loader hierarchy
+  that is implemented by Catalina, see <strong>FIXME - Reference</strong>.</p>
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+    <p>All implementations of <strong>Loader</strong>
+    support the following attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="false">
+        <p>Java class name of the implementation to use.  This class must
+        implement the <code>org.apache.catalina.Loader</code> interface.
+        If not specified, the standard value (defined below) will be used.</p>
+      </attribute>
+
+      <attribute name="delegate" required="false">
+        <p>Set to <code>true</code> if you want the class loader to follow
+        the standard Java2 delegation model, and attempt to load classes from
+        parent class loaders <strong>before</strong> looking inside the web
+        application.  Set to <code>false</code> (the default) to have the
+        class loader look inside the web application first, before asking
+        parent class loaders to find requested classes or resources.</p>
+      </attribute>
+
+      <attribute name="reloadable" required="false">
+        <p>Set to <code>true</code> if you want Catalina to monitor classes in
+        <code>/WEB-INF/classes/</code> and <code>/WEB-INF/lib</code> for
+        changes, and automatically reload the web application if a change
+        is detected.  This feature is very useful during application
+        development, but it requires significant runtime overhead and is
+        not recommended for use on deployed production applications.  You
+        can use the <a href="../manager-howto.html">Manager</a> web
+        application, however, to trigger reloads of deployed applications
+        on demand.</p>
+
+        <p><strong>NOTE</strong> - The value for this property will be
+        inherited from the <code>reloadable</code> attribute you set on
+        the surrounding <a href="context.html">Context</a> component,
+        and any value you explicitly set here will be replaced.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Standard Implementation">
+
+    <p>The standard implementation of <strong>Loader</strong> is
+    <strong>org.apache.catalina.loader.WebappLoader</strong>.
+    It supports the following additional attributes (in addition to the
+    common attributes listed above):</p>
+
+    <attributes>
+
+      <attribute name="checkInterval" required="false">
+        <p>The number of seconds between checks for modified classes and
+        resources, if <code>reloadable</code> has been set to
+        <code>true</code>.  The default is 15 seconds.</p>
+      </attribute>
+
+      <attribute name="loaderClass" required="false">
+        <p>Java class name of the <code>java.lang.ClassLoader</code>
+        implementation class to use.  If not specified, the default value is
+        <code>org.apache.catalina.loader.WebappClassLoader</code>.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>No components may be nested inside a <strong>Loader</strong> element.</p>
+
+</section>
+
+
+<section name="Special Features">
+
+  <subsection name="Logging">
+
+    <p>A loader is associated with the log category based on its classname.</p>
+
+  </subsection>
+
+</section>
+
+
+</body>
+
+
+</document>
diff --git a/container/webapps/docs/config/manager.xml b/container/webapps/docs/config/manager.xml
new file mode 100644
index 0000000..3e8503c
--- /dev/null
+++ b/container/webapps/docs/config/manager.xml
@@ -0,0 +1,459 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="manager.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>The Manager Component</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>The <strong>Manager</strong> element represents the <em>session
+  manager</em> that will be used to create and maintain HTTP sessions
+  as requested by the associated web application.</p>
+
+  <p>A Manager element MAY be nested inside a
+  <a href="context.html">Context</a> component.  If it is not included,
+  a default Manager configuration will be created automatically, which
+  is sufficient for most requirements.</p>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+    <p>All implementations of <strong>Manager</strong>
+    support the following attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="false">
+        <p>Java class name of the implementation to use.  This class must
+        implement the <code>org.apache.catalina.Manager</code> interface.
+        If not specified, the standard value (defined below) will be used.</p>
+      </attribute>
+
+      <attribute name="distributable" required="false">
+        <p>Set to <code>true</code> to ask the session manager to enforce
+        the restrictions described in the Servlet Specification on
+        distributable applications (primarily, this would mean that all
+        session attributes must implement <code>java.io.Serializable</code>).
+        Set to <code>false</code> (the default) to not enforce these
+        restrictions.</p>
+
+        <p><strong>NOTE</strong> - The value for this property is inherited
+        automatically based on the presence or absence of the
+        <code>&lt;distributable&gt;</code> element in the web application
+        deployment descriptor (<code>/WEB-INF/web.xml</code>).</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Standard Implementation">
+
+    <p>Tomcat provides two standard implementations of <strong>Manager</strong>
+    for use - the default one stores active sessions, while the optional one
+    stores active sessions that have been swapped out (in addition to saving
+    sessions across a restart of Tomcat) in a storage location that is selected
+    via the use of an appropriate <strong>Store</strong> nested element.</p>
+
+    <h3>Standard Manager Implementation</h3>
+
+    <p>The standard implementation of <strong>Manager</strong> is
+    <strong>org.apache.catalina.session.StandardManager</strong>.
+    It supports the following additional attributes (in addition to the
+    common attributes listed above):</p>
+
+    <attributes>
+
+      <attribute name="algorithm" required="false">
+        <p>Name of the <em>Message Digest</em> algorithm used to calculate
+        session identifiers produced by this Manager.  This value must
+        be supported by the <code>java.security.MessageDigest</code> class.
+        If not specified, the default value is "MD5".</p>
+      </attribute>
+
+      <attribute name="entropy" required="false">
+        <p>A String value that is utilized when seeding the random number
+        generator used to create session identifiers for this Manager.
+        If not specified, a semi-useful value is calculated, but a long
+        String value should be specified in security-conscious
+        environments.</p>
+      </attribute>
+
+      <attribute name="maxActiveSessions" required="false">
+        <p>The maximum number of active sessions that will be created by
+        this Manager, or -1 (the default) for no limit.</p>
+      </attribute>
+
+      <attribute name="maxInactiveInterval" required="false">
+        <p>The initial maximum time interval, in seconds, 
+        between client requests before a session is invalidated. A negative value
+        will result in sessions never timing out. If the attribute is not provided,
+        a default of 60 seconds is used.</p>
+        
+        <p>This attribute provides the initial value whenever a 
+        new session is created, but the interval may be dynamically 
+        varied by a servlet via the 
+        <code>setMaxInactiveInterval</code> method of the <code>HttpSession</code> object.</p>
+      </attribute>
+
+      <attribute name="pathname" required="false">
+        <p>Absolute or relative (to the work directory for this Context)
+        pathname of the file in which session state will be preserved
+        across application restarts, if possible.  The default is
+        "SESSIONS.ser".  See <a href="#Restart Persistence">Restart
+        Persistence</a> for more information. Restart persistence may be 
+        disabled by setting this attribute to an empty string.</p>
+      </attribute>
+
+      <attribute name="processExpiresFrequency" required="false">
+        <p>Frequency of the session expiration, and related manager operations.
+        Manager operations will be done once for the specified amount of
+        backgrondProcess calls (ie, the lower the amount, the more often the
+        checks will occur). The minimum value is 1, and the default value is 6.
+        </p>
+      </attribute>
+
+      <attribute name="randomClass" required="false">
+        <p>Java class name of the <code>java.util.Random</code>
+        implementation class to use.  If not specified, the default value is
+        <code>java.security.SecureRandom</code>.</p>
+      </attribute>
+
+      <attribute name="sessionIdLength" required="false">
+       <p>The length of session ids created by this Manager, excluding any
+        JVM route information used for load balancing. 
+        The default is 16.</p>
+      </attribute>
+
+    </attributes>
+
+    <h3>Persistent Manager Implementation</h3>
+
+    <p><em><strong>WARNING - Use of this Manager implementation
+    has not been thoroughly tested, and should be considered experimental!
+    </strong></em></p>
+
+    <p>The persistent implementation of <strong>Manager</strong> is
+    <strong>org.apache.catalina.session.PersistentManager</strong>.  In
+    addition to the usual operations of creating and deleting sessions, a
+    <code>PersistentManager</code> has the capability to swap active (but
+    idle) sessions out to a persistent storage mechanism, as well as to save
+    all sessions across a normal restart of Tomcat.  The actual persistent
+    storage mechanism used is selected by your choice of a
+    <strong>Store</strong> element nested inside the <strong>Manager</strong>
+    element - this is required for use of <code>PersistentManager</code>.</p>
+
+    <p>This implementation of Manager supports the following attributes in
+    addition to the <a href="#Common Attributes">Common Attributes</a>
+    described earlier.</p>
+
+    <attributes>
+
+      <attribute name="algorithm" required="false">
+        <p>Name of the <em>Message Digest</em> algorithm used to calculate
+        session identifiers produced by this Manager.  This value must
+        be supported by the <code>java.security.MessageDigest</code> class.
+        If not specified, the default value is "MD5".</p>
+      </attribute>
+
+      <attribute name="className" required="false">
+        <p>Java class name of the implementation to use.  This class must
+        implement the <code>org.apache.catalina.Manager</code> interface.
+        You <strong>must</strong> specify
+        <code>org.apache.catalina.session.PersistentManager</code> to use
+        this manager implementation.</p>
+      </attribute>
+
+      <attribute name="entropy" required="false">
+        <p>A String value that is utilized when seeding the random number
+        generator used to create session identifiers for this Manager.
+        If not specified, a semi-useful value is calculated, but a long
+        String value should be specified in security-conscious
+        environments.</p>
+      </attribute>
+
+      <attribute name="maxActiveSessions" required="false">
+        <p>The maximum number of active sessions that will be created by
+        this Manager, or -1 (the default) for no limit.</p>
+      </attribute>
+
+      <attribute name="maxIdleBackup" required="false">
+        <p>The time interval (in seconds) since the last access to a session
+        before it is eligible for being persisted to the session store, or
+        <code>-1</code> to disable this feature.  By default, this feature is
+        disabled.</p>
+      </attribute>
+
+      <attribute name="maxIdleSwap" required="false">
+        <p>The time interval (in seconds) since the last access to a session
+        before it should be persisted to the session store, and
+        passivated out of the server's memory, or <code>-1</code> to disable
+        this feature.  If this feature is enabled, the time interval specified
+        here should be equal to or longer than the value specified for
+        <code>maxIdleBackup</code>.  By default, this feature is disabled.</p>
+      </attribute>
+
+      <attribute name="minIdleSwap" required="false">
+        <p>The time interval (in seconds) since the last access to a session
+        before it will be eligible to be persisted to the session store, and
+        passivated out of the server's memory, or <code>-1</code> for this
+        swapping to be available at any time.  If specified, this value should
+        be less than that specified by <code>maxIdleSwap</code>.  By default,
+        this value is set to <code>-1</code>.</p>
+      </attribute>
+
+      <attribute name="maxInactiveInterval" required="false">
+        <p>The initial maximum time interval, in seconds, 
+        between client requests before a session is invalidated. A negative value
+        will result in sessions never timing out. If the attribute is not provided,
+        a default of 60 seconds is used.</p>
+        
+        <p>This attribute provides the initial value whenever a 
+        new session is created, but the interval may be dynamically 
+        varied by a servlet via the 
+        <code>setMaxInactiveInterval</code>method of the <code>HttpSession</code> object.</p>
+      </attribute>
+
+      <attribute name="randomClass" required="false">
+        <p>Java class name of the <code>java.util.Random</code>
+        implementation class to use.  If not specified, the default value is
+        <code>java.security.SecureRandom</code>.</p>
+      </attribute>
+
+      <attribute name="saveOnRestart" required="false">
+        <p>Should all sessions be persisted and reloaded when Tomcat is shut
+        down and restarted (or when this application is reloaded)?  By default,
+        this attribute is set to <code>true</code>.</p>
+      </attribute>
+
+      <attribute name="sessionIdLength" required="false">
+        <p>The length of session ids created by this Manager, excluding any
+        JVM route information used for load balancing. 
+        The default is 16.</p>
+      </attribute>
+
+    </attributes>
+
+    <p>In order to successfully use a PersistentManager, you must nest inside
+    it a <strong>&lt;Store&gt;</strong> element, as described below.</p>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Nested Components">
+
+  <h3>Standard Manager Implementation</h3>
+
+  <p>If you are using the <em>Standard Manager Implementation</em>
+  as described above, no elements may be nested inside your
+  <strong>&lt;Manager&gt;</strong> element.</p>
+
+  <h3>Persistent Manager Implementation</h3>
+
+  <p>If you are using the <em>Persistent Manager Implementation</em>
+  as described above, you <strong>MUST</strong> nest a
+  <strong>&lt;Store&gt;</strong> element inside, which defines the
+  characteristics of the persistent data storage.  Two implementations
+  of the <code>&lt;Store&gt;</code> element are currently available,
+  with different characteristics, as described belowl</p>
+
+  <h5>File Based Store</h5>
+
+  <p>The <em>File Based Store</em> implementation saves swapped out
+  sessions in individual files (named based on the session identifier)
+  in a configurable directory.  Therefore, you are likely to encounter
+  scalability problems as the number of active sessions increases, and
+  this should primarily be considered a means to easily experiment.</p>
+
+  <p>To configure this, add a <code>&lt;Store&gt;</code> nested inside
+  your <code>&lt;Manager&gt;</code> element with the following attributes:
+  </p>
+
+  <attributes>
+
+    <attribute name="checkInterval" required="false">
+      <p>The interval (in seconds) between checks for expired sessions
+      among those sessions that are currently swapped out.  By default,
+      this interval is set to 60 seconds (one minute).</p>
+    </attribute>
+
+    <attribute name="className" required="true">
+      <p>Java class name of the implementation to use.  This class must
+      implement the <code>org.apache.catalina.Store</code> interface.  You
+      <strong>must</strong> specify
+      <code>org.apache.catalina.session.FileStore</code>
+      to use this implementation.</p>
+    </attribute>
+
+    <attribute name="directory" required="false">
+      <p>Absolute or relative (to the temporary work directory for this web
+      application) pathname of the directory into which individual session
+      files are written.  If not specified, the temporary work directory
+      assigned by the container is utilized.</p>
+    </attribute>
+
+  </attributes>
+
+
+  <h5>JDBC Based Store</h5>
+
+  <p>The <em>JDBC Based Store</em> implementation saves swapped out
+  sessions in individual rows of a preconfigured table in a database
+  that is accessed via a JDBC driver.  With large numbers of swapped out
+  sessions, this implementation will exhibit improved performance over
+  the File Based Store described above.</p>
+
+  <p>To configure this, add a <code>&lt;Store&gt;</code> nested inside
+  your <code>&lt;Manager&gt;</code> element with the following attributes:
+  </p>
+
+  <attributes>
+
+    <attribute name="checkInterval" required="false">
+      <p>The interval (in seconds) between checks for expired sessions
+      among those sessions that are currently swapped out.  By default,
+      this interval is set to 60 seconds (one minute).</p>
+    </attribute>
+
+    <attribute name="className" required="true">
+      <p>Java class name of the implementation to use.  This class must
+      implement the <code>org.apache.catalina.Store</code> interface.  You
+      <strong>must</strong> specify
+      <code>org.apache.catalina.session.JDBCStore</code>
+      to use this implementation.</p>
+    </attribute>
+
+    <attribute name="connectionURL" required="true">
+      <p>The connection URL that will be handed to the configured JDBC
+      driver to establish a connection to the database containing our
+      session table.</p>
+    </attribute>
+
+    <attribute name="driverName" required="true">
+      <p>Java class name of the JDBC driver to be used.</p>
+    </attribute>
+
+    <attribute name="sessionAppCol" required="true">
+      <p>Name of the database column, contained in the specified session
+      table, that contains the Engine, Host, and Web Application Context
+      name in the format <code>/Engine/Host/Context</code>.</p>
+    </attribute>
+
+    <attribute name="sessionDataCol" required="true">
+      <p>Name of the database column, contained in the specified
+      session table, that contains the serialized form of all session
+      attributes for a swapped out session.  The column type must accept
+      a binary object (typically called a BLOB).</p>
+    </attribute>
+
+    <attribute name="sessionIdCol" required="true">
+      <p>Name of the database column, contained in the specified
+      session table, that contains the session identifier of the
+      swapped out session.  The column type must accept character
+      string data of at least as many characters as are contained
+      in session identifiers created by Tomcat (typically 32).</p>
+    </attribute>
+
+    <attribute name="sessionLastAccessedCol" required="true">
+      <p>Name of the database column, contained in the specified
+      session table, that contains the <code>lastAccessedTime</code>
+      property of this session.  The column type must accept a
+      Java <code>long</code> (64 bits).</p>
+    </attribute>
+
+    <attribute name="sessionMaxInactiveCol" required="true">
+      <p>Name of the database column, contained in the specified
+      session table, that contains the <code>maxInactiveInterval</code>
+      property of this session.  The column type must accept a
+      Java <code>integer</code> (32 bits).</p>
+    </attribute>
+
+    <attribute name="sessionTable" required="true">
+      <p>Name of the database table to be used for storing swapped out
+      sessions.  This table must contain (at least) the database columns
+      that are configured by the other attributes of this element.</p>
+    </attribute>
+
+    <attribute name="sessionValidCol" required="true">
+      <p>Name of the database column, contained in the specified
+      session table, that contains a flag indicating whether this
+      swapped out session is still valid or not.  The column type
+      must accept a single character.</p>
+    </attribute>
+
+  </attributes>
+
+  <p>Before attempting to use the JDBC Based Store for the first time,
+  you must create the table that will be used to store swapped out sessions.
+  Detailed SQL commands vary depending on the database you are using, but
+  a script like this will generally be required:</p>
+
+<source>
+create table tomcat_sessions (
+  session_id     varchar(100) not null primary key,
+  valid_session  char(1) not null,
+  max_inactive   int not null,
+  last_access    bigint not null,
+  app_name       varchar(255),
+  session_data   mediumblob,
+  KEY kapp_name(app_name)
+);
+</source>
+
+  <p>In order for the JDBC Based Store to successfully connect to your
+  database, the JDBC driver you configure must be visible to Tomcat's
+  internal class loader.  Generally, that means you must place the JAR
+  file containing this driver into the <code>$CATALINA_HOME/server/lib</code>
+  directory (if your applications do not also need it) or into the
+  <code>$CATALINA_HOME/common/lib</code> directory (if you wish to share
+  this driver with your web applications.</p>
+
+</section>
+
+
+<section name="Special Features">
+
+
+  <subsection name="Restart Persistence">
+
+    <p>Whenver Catalina is shut down normally and restarted, or when an
+    application reload is triggered, the standard Manager implementation
+    will attempt to serialize all currently active sessions to a disk
+    file located via the <code>pathname</code> attribute.  All such saved
+    sessions will then be deserialized and activated (assuming they have
+    not expired in the mean time) when the application reload is completed.</p>
+
+    <p>In order to successfully restore the state of session attributes,
+    all such attributes MUST implement the <code>java.io.Serializable</code>
+    interface.  You MAY cause the Manager to enforce this restriction by
+    including the <code>&lt;distributable&gt;</code> element in your web
+    application deployment descriptor (<code>/WEB-INF/web.xml</code>).</p>
+
+  </subsection>
+
+</section>
+
+
+</body>
+
+
+</document>
diff --git a/container/webapps/docs/config/project.xml b/container/webapps/docs/config/project.xml
new file mode 100644
index 0000000..4712b81
--- /dev/null
+++ b/container/webapps/docs/config/project.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Apache Tomcat Configuration Reference"
+        href="http://jakarta.apache.org/tomcat/">
+
+  <title>Apache Tomcat Configuration Reference</title>
+
+  <logo href="/images/tomcat.gif">
+    The Apache Tomcat Servlet/JSP Container
+  </logo>
+
+    
+  <body>
+
+    <menu name="Links">
+        <item name="Docs Home"             href="../index.html"/>
+        <item name="Config Ref. Home"      href="index.html"/>
+    </menu>
+
+    <menu name="Top Level Elements">
+        <item name="Server"                href="server.html"/>
+        <item name="Service"               href="service.html"/>
+    </menu>
+
+    <menu name="Connectors">
+        <item name="HTTP"                  href="http.html"/>
+        <item name="AJP"                   href="ajp.html"/>
+    </menu>
+
+    <menu name="Containers">
+        <item name="Context"               href="context.html"/>
+        <item name="Engine"                href="engine.html"/>
+        <item name="Host"                  href="host.html"/>
+    </menu>
+
+    <menu name="Nested Components">
+        <item name="Global Resources"      href="globalresources.html"/>
+        <item name="Loader"                href="loader.html"/>
+        <item name="Manager"               href="manager.html"/> 
+        <item name="Realm"                 href="realm.html"/>
+        <item name="Resources"             href="resources.html"/>
+        <item name="Valve"                 href="valve.html"/>
+    </menu>
+
+  </body>
+
+</project>
diff --git a/container/webapps/docs/config/realm.xml b/container/webapps/docs/config/realm.xml
new file mode 100644
index 0000000..a62349c
--- /dev/null
+++ b/container/webapps/docs/config/realm.xml
@@ -0,0 +1,492 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="realm.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>The Realm Component</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>A <strong>Realm</strong> element represents a "database" of usernames,
+  passwords, and <em>roles</em> (similar to Unix <em>groups</em>) assigned
+  to those users.  Different implementations of Realm allow Catalina to be
+  integrated into environments where such authentication information is already
+  being created and maintained, and then utilize that information to implement
+  <em>Container Managed Security</em> as described in the Servlet
+  Specification.</p>
+
+  <p>You may nest a Realm inside any Catalina container
+  <a href="engine.html">Engine</a>, <a href="host.html">Host</a>, or
+  <a href="context.html">Context</a>).  In addition, Realms associated with
+  an Engine or a Host are automatically inherited by lower-level
+  containers, unless explicitly overridden.</p>
+
+  <p>For more in-depth information about container managed security in web
+  applications, see <strong>FIXME - link to "Container Managed Security Guide"
+  in the application developer's section</strong>.  For more in-depth
+  information about configuing and using the standard Realm component
+  implementations, see <strong>FIXME - link to "Realm Configuration HOW-TO"
+  in the administrator's section</strong>.</p>
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+    <p>All implementations of <strong>Realm</strong>
+    support the following attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="true">
+        <p>Java class name of the implementation to use.  This class must
+        implement the <code>org.apache.catalina.Realm</code> interface.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Standard Implementation">
+
+    <p>Unlike most Catalina components, there are several standard
+    <strong>Realm</strong> implementations available.  As a result,
+    the <code>className</code> attribute MUST be used to select the
+    implementation you wish to use.</p>
+
+    <h3>JDBC Database Realm (org.apache.catalina.realm.JDBCRealm)</h3>
+
+    <p>The <strong>JDBC Database Realm</strong> connects Catalina to
+    a relational database, accessed through an appropriate JDBC driver,
+    to perform lookups of usernames, passwords, and their associated
+    roles.  Because the lookup is done each time that it is required,
+    changes to the database will be immediately reflected in the
+    information used to authenticate new logins.</p>
+
+    <p>A rich set of additional attributes lets you configure the required
+    connection to the underlying database, as well as the table and
+    column names used to retrieve the required information:</p>
+
+    <attributes>
+
+      <attribute name="connectionName" required="true">
+        <p>The database username to use when establishing the JDBC
+        connection.</p>
+      </attribute>
+
+      <attribute name="connectionPassword" required="true">
+        <p>The database password to use when establishing the JDBC
+        connection.</p>
+      </attribute>
+
+      <attribute name="connectionURL" required="true">
+        <p>The connection URL to be passed to the JDBC driver when
+        establishing a database connection.</p>
+      </attribute>
+
+      <attribute name="digest" required="false">
+        <p>The name of the <code>MessageDigest</code> algorithm used
+        to encode user passwords stored in the database.  If not specified,
+        user passwords are assumed to be stored in clear-text.</p>
+      </attribute>
+   
+      <attribute name="digestEncoding" required="false">
+        <p>The charset for encoding digests.  If not specified, the platform
+        default will be used.</p>
+      </attribute>
+
+      <attribute name="driverName" required="true">
+        <p>Fully qualified Java class name of the JDBC driver to be
+        used to connect to the authentication database.</p>
+      </attribute>
+
+      <attribute name="roleNameCol" required="true">
+        <p>Name of the column, in the "user roles" table, which contains
+        a role name assigned to the corresponding user.</p>
+      </attribute>
+
+      <attribute name="userCredCol" required="true">
+        <p>Name of the column, in the "users" table, which contains
+        the user's credentials (i.e. password(.  If a value for the
+        <code>digest</code> attribute is specified, this component
+        will assume that the passwords have been encoded with the
+        specified algorithm.  Otherwise, they will be assumed to be
+        in clear text.</p>
+      </attribute>
+
+      <attribute name="userNameCol" required="true">
+        <p>Name of the column, in the "users" and "user roles" table,
+        that contains the user's username.</p>
+      </attribute>
+
+      <attribute name="userRoleTable" required="true">
+        <p>Name of the "user roles" table, which must contain columns
+        named by the <code>userNameCol</code> and <code>roleNameCol</code>
+        attributes.</p>
+      </attribute>
+
+      <attribute name="userTable" required="true">
+        <p>Name of the "users" table, which must contain columns named
+        by the <code>userNameCol</code> and <code>userCredCol</code>
+        attributes.</p>
+      </attribute>
+
+    </attributes>
+
+    <p>See <strong>FIXME - Nested pointer into HOW-TO</strong> for more
+    information on setting up container managed security using the
+    JDBC Database Realm component.</p>
+
+
+    <h3>
+      DataSource Database Realm (org.apache.catalina.realm.DataSourceRealm)
+    </h3>
+
+    <p>The <strong>DataSource Database Realm</strong> connects Catalina to
+    a relational database, accessed through a JNDI named JDBC DataSource
+    to perform lookups of usernames, passwords, and their associated
+    roles.  Because the lookup is done each time that it is required,
+    changes to the database will be immediately reflected in the
+    information used to authenticate new logins.</p>
+
+    <p>The JDBC Realm uses a single db connection. This requires that
+    realm based authentication be synchronized, i.e. only one authentication
+    can be done at a time. This could be a bottleneck for applications
+    with high volumes of realm based authentications.</p>
+
+    <p>The DataSource Database Realm supports simultaneous realm based
+    authentications and allows the underlying JDBC DataSource to
+    handle optimizations like database connection pooling.</p>
+
+    <p>A rich set of additional attributes lets you configure the name
+    of the JNDI JDBC DataSource, as well as the table and
+    column names used to retrieve the required information:</p>
+
+    <attributes>
+
+      <attribute name="dataSourceName" required="true">
+        <p>The name of the JNDI JDBC DataSource for this Realm.</p>
+      </attribute>
+
+      <attribute name="digest" required="false">
+        <p>The name of the <code>MessageDigest</code> algorithm used
+        to encode user passwords stored in the database.  If not specified,
+        user passwords are assumed to be stored in clear-text.</p>
+      </attribute>
+
+      <attribute name="roleNameCol" required="true">
+        <p>Name of the column, in the "user roles" table, which contains
+        a role name assigned to the corresponding user.</p>
+      </attribute>
+
+      <attribute name="userCredCol" required="true">
+        <p>Name of the column, in the "users" table, which contains
+        the user's credentials (i.e. password(.  If a value for the
+        <code>digest</code> attribute is specified, this component
+        will assume that the passwords have been encoded with the
+        specified algorithm.  Otherwise, they will be assumed to be
+        in clear text.</p>
+      </attribute>
+
+      <attribute name="userNameCol" required="true">
+        <p>Name of the column, in the "users" and "user roles" table,
+        that contains the user's username.</p>
+      </attribute>
+
+      <attribute name="userRoleTable" required="true">
+        <p>Name of the "user roles" table, which must contain columns
+        named by the <code>userNameCol</code> and <code>roleNameCol</code>
+        attributes.</p>
+      </attribute>
+
+      <attribute name="userTable" required="true">
+        <p>Name of the "users" table, which must contain columns named
+        by the <code>userNameCol</code> and <code>userCredCol</code>
+        attributes.</p>
+      </attribute>
+
+    </attributes>
+
+    <p>See the <a href="../realm-howto.html#DataSourceRealm">
+    DataSource Realm HOW-TO</a> for more information on setting up container
+    managed security using the DataSource Database Realm component.</p>
+
+
+    <h3>JNDI Directory Realm (org.apache.catalina.realm.JNDIRealm)</h3>
+
+
+    <p>The <strong>JNDI Directory Realm</strong> connects Catalina to
+    an LDAP Directory, accessed through an appropriate JNDI driver,
+    that stores usernames, passwords, and their associated
+    roles. Changes to the directory are immediately reflected in the
+    information used to authenticate new logins.</p>
+
+
+    <p>The directory realm supports a variety of approaches to using
+    LDAP for authentication:</p>
+
+    <ul>
+    <li>The realm can either use a pattern to determine the
+    distinguished name (DN) of the user's directory entry, or search
+    the directory to locate that entry.
+    </li>
+
+    <li>The realm can authenticate the user either by binding to the
+    directory with the DN of the user's entry and the password
+    presented by the user, or by retrieving the password from the
+    user's entry and performing a comparison locally.
+    </li>
+
+    <li>Roles may be represented in the directory as explicit entries
+    found by a directory search (e.g. group entries of which the user
+    is a member), as the values of an attribute in the user's entry,
+    or both.
+    </li>
+    </ul>
+
+    <p> A rich set of additional attributes lets you configure the
+    required behaviour as well as the connection to the underlying
+    directory and the element and attribute names used to retrieve
+    information from the directory:</p>
+
+    <attributes>
+       <attribute name="authentication" required="false">
+         <p>A string specifying the type of authentication to use.
+         "none", "simple", "strong" or a provider specific definition
+         can be used. If no value is given the providers default is used.</p>
+       </attribute>
+
+      <attribute name="connectionName" required="false">
+        <p>The directory username to use when establishing a
+        connection to the directory for LDAP search operations. If not
+        specified an anonymous connection is made, which is often
+        sufficient unless you specify the <code>userPassword</code>
+        property.</p>
+      </attribute>
+
+      <attribute name="connectionPassword" required="false">
+        <p>The directory password to use when establishing a
+        connection to the directory for LDAP search operations. If not
+        specified an anonymous connection is made, which is often
+        sufficient unless you specify the <code>userPassword</code>
+        property.</p>
+      </attribute>
+
+      <attribute name="connectionURL" required="true">
+        <p>The connection URL to be passed to the JNDI driver when
+        establishing a connection to the directory.</p>
+      </attribute>
+
+      <attribute name="contextFactory" required="false">
+        <p>Fully qualified Java class name of the factory class used
+        to acquire our JNDI <code>InitialContext</code>.  By default,
+        assumes that the standard JNDI LDAP provider will be utilized.</p>
+      </attribute>
+      
+      <attribute name="derefAliases" required="false">
+        <p>A string specifying how aliases are to be dereferenced during
+        search operations. The allowed values are "always", "never",
+        "finding" and "searching". If not specified, "always" is used.</p>
+      </attribute>
+
+      <attribute name="protocol" required="false">
+         <p>A string specifying the security protocol to use. If not given
+         the providers default is used.</p>
+      </attribute>
+
+      <attribute name="roleBase" required="false">
+        <p>The base directory entry for performing role searches. If
+        not specified the top-level element in the directory context
+        will be used.</p>
+      </attribute>
+
+      <attribute name="roleName" required="false">
+        <p>The name of the attribute that contains role names in the
+        directory entries found by a role search. In addition you can
+        use the <code>userRoleName</code> property to specify the name
+        of an attribute, in the user's entry, containing additional
+        role names.  If <code>roleName</code> is not specified a role
+        search does not take place, and roles are taken only from the
+        user's entry.</p>
+      </attribute>
+
+      <attribute name="roleSearch" required="false">
+        <p>The LDAP filter expression used for performing role
+        searches.  Use <code>{0}</code> to substitute the
+        distinguished name (DN) of the user, and/or <code>{1}</code> to
+        substitute the username. If not specified a role search does
+        not take place and roles are taken only from the attribute in
+        the user's entry specified by the <code>userRoleName</code>
+        property.</p>
+      </attribute>
+
+      <attribute name="roleSubtree" required="false">
+        <p>Set to <code>true</code> if you want to search the entire
+        subtree of the element specified by the <code>roleBase</code>
+        property for role entries associated with the user. The
+        default value of <code>false</code> causes only the top level
+        to be searched.</p>
+      </attribute>
+
+      <attribute name="userBase" required="false">
+        <p>The base element for user searches performed using the
+        <code>userSearch</code> expression.  Not used if you are using
+        the <code>userPattern</code> expression.</p>
+      </attribute>
+
+      <attribute name="userPassword" required="false">
+        <p>Name of the attribute in the user's entry containing the
+        user's password.  If you specify this value, JNDIRealm will
+        bind to the directory using the values specified by
+        <code>connectionName</code> and
+        <code>connectionPassword</code> properties, and retrieve the
+        corresponding attribute for comparison to the value specified
+        by the user being authenticated.  If you do
+        <strong>not</strong> specify this value, JNDIRealm will
+        attempt a simple bind to the directory using the DN of the
+        user's entry and the password presented by the user, with a
+        successful bind being interpreted as an authenticated
+        user.</p>
+      </attribute>
+
+      <attribute name="userPattern" required="false">
+        <p>Pattern for the distinguished name (DN) of the user's
+        directory entry, with <code>{0}</code> marking where the
+        actual username should be inserted. You can use this property
+        instead of <code>userSearch</code>, <code>userSubtree</code>
+        and <code>userBase</code> when the distinguished name contains
+        the username and is otherwise the same for all users.</p>
+      </attribute>
+
+      <attribute name="userRoleName" required="false">
+        <p>The name of an attribute in the user's directory entry
+        containing zero or more values for the names of roles assigned
+        to this user.  In addition you can use the
+        <code>roleName</code> property to specify the name of an
+        attribute to be retrieved from individual role entries found
+        by searching the directory. If <code>userRoleName</code> is
+        not specified all the roles for a user derive from the role
+        search.</p>
+      </attribute>
+
+      <attribute name="userSearch" required="false">
+        <p>The LDAP filter expression to use when searching for a
+        user's directory entry, with <code>{0}</code> marking where
+        the actual username should be inserted.  Use this property
+        (along with the <code>userBase</code> and
+        <code>userSubtree</code> properties) instead of
+        <code>userPattern</code> to search the directory for the
+        user's entry.</p>
+      </attribute>
+
+      <attribute name="userSubtree" required="false">
+        <p>Set to <code>true</code> if you want to search the entire
+        subtree of the element specified by the <code>userBase</code>
+        property for the user's entry. The default value of
+        <code>false</code> causes only the top level to be searched.
+        Not used if you are using the <code>userPattern</code>
+        expression.</p>
+      </attribute>
+
+    </attributes>
+
+    <p>See <strong>FIXME - Nested pointer into HOW-TO</strong> for more
+    information on setting up container managed security using the
+    JNDI Directory Realm component.</p>
+
+
+    <h3>Memory Based Realm (org.apache.catalina.realm.MemoryRealm)</h3>
+
+    <p>The <strong>Memory Based Realm</strong> is a simple Realm implementation
+    that reads user information from an XML format, and represents it as a
+    collection of Java objects in memory.  This implementation is intended
+    solely to get up and running with container managed security - it is NOT
+    intended for production use.  As such, there are no mechanisms for
+    updating the in-memory collection of users when the content of the
+    underlying data file is changed.</p>
+
+    <p>The Memory Based Realm implementation supports the following
+    additional attributes:</p>
+
+    <attributes>
+
+      <attribute name="pathname" required="false">
+        <p>Absolute or relative (to $CATALINA_HOME) pathname to the XML file
+        containing our user information.  See below for details on the
+        XML element format required.  If no pathname is specified, the
+        default value is <code>conf/tomcat-users.xml</code>.</p>
+      </attribute>
+
+    </attributes>
+
+    <p>The XML document referenced by the <code>pathname</code> attribute must
+    conform to the following requirements:</p>
+    <ul>
+    <li>The root (outer) element must be <code>&lt;tomcat-users&gt;</code>.
+        </li>
+    <li>Each authorized user must be represented by a single XML element
+        <code>&lt;user&gt;</code>, nested inside the root element.</li>
+    <li>Each <code>&lt;user&gt;</code> element must have the following
+        attributes:
+        <ul>
+        <li><strong>name</strong> - Username of this user (must be unique
+            within this file).</li>
+        <li><strong>password</strong> - Password of this user (in
+            clear text).</li>
+        <li><strong>roles</strong> - Comma-delimited list of the role names
+            assigned to this user.</li>
+        </ul></li>
+    </ul>
+
+    <p>See <strong>FIXME - Nested pointer into HOW-TO</strong> for more
+    information on setting up container managed security using the
+    Memory Based Realm component.</p>
+
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>No components may be nested inside a <strong>Realm</strong> element.</p>
+
+</section>
+
+
+<section name="Special Features">
+
+  <p>See <a href="host.html">Single Sign On</a> for information about
+  configuring Single Sign On support for a virtual host.</p>
+
+</section>
+
+
+</body>
+
+
+</document>
diff --git a/container/webapps/docs/config/resources.xml b/container/webapps/docs/config/resources.xml
new file mode 100644
index 0000000..c1745de
--- /dev/null
+++ b/container/webapps/docs/config/resources.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="resources.html">
+
+  &project;
+
+  <properties>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <title>The Resources Component</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>The <strong>Resources</strong> element represents the <em>web
+  application static resources</em>, from which classes will be loaded, 
+  HTML, JSP and the other static files will be served. This allows the webapp
+  to reside on various mediums other than the filesystem, like compressed
+  in a WAR file, in a JDBC database, or in a more advanced versioning
+  repository.</p>
+
+  <p>A unified caching engine is provided for all accesses to the webapp
+  resources made by the servlet container and web applications which use the
+  container provided mechanisms to access such resources, such as class laoder
+  access, access through the <code>ServletContext</code> interface, or native
+  access through the <code>DirectoryContext</code> interface.</p>
+
+  <p><strong>Note: Running a webapp with non-filesystem based 
+  Resources implementations is only possible when the webapp does not 
+  rely on direct filesystem access to its own resources, and uses the methods
+  in the ServletContext interface to access them.</strong></p>
+
+  <p>A Resources element MAY be nested inside a 
+  <a href="context.html">Context</a> component.  If it is not included, 
+  a default filesystem based Resources will be created automatically, 
+  which is sufficient for most requirements.</p>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+    <p>All implementations of <strong>Resources</strong>
+    support the following attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="false">
+        <p>Java class name of the implementation to use.  This class must
+        implement the <code>javax.naming.directory.DirContext</code> interface.
+        It is recommended for optimal functionality and performance, 
+        but not mandatory, that the class extend 
+        <code>org.apache.naming.resources.BaseDirContext</code>, as well as
+        use the special object types provided in the 
+        <code>org.apache.naming.resources</code> for returned objects.
+        If not specified, the standard value (defined below) will be used.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+  <subsection name="Standard Implementation">
+
+    <p>The standard implementation of <strong>Resources</strong> is
+    <strong>org.apache.naming.resources.FileDirContext</strong>, and 
+    is configured by its parent Context element.</p>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>No components may be nested inside a <strong>Resources</strong> element.</p>
+
+</section>
+
+
+<section name="Special Features">
+
+  <p>No special features are associated with a <strong>Resources</strong>
+  element.</p>
+
+</section>
+
+
+</body>
+
+
+</document>
diff --git a/container/webapps/docs/config/server.xml b/container/webapps/docs/config/server.xml
new file mode 100644
index 0000000..f22bdab
--- /dev/null
+++ b/container/webapps/docs/config/server.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="server.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>The Server Component</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>A <strong>Server</strong> element represents the entire Catalina
+  servlet container.  Therefore, it must be the single outermost element
+  in the <code>conf/server.xml</code> configuration file.  Its attributes
+  represent the characteristics of the servlet container as a whole.</p>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+  <p>All implementations of <strong>Server</strong>
+  support the following attributes:</p>
+
+  <attributes>
+
+    <attribute name="className" required="false">
+      <p>Java class name of the implementation to use.  This class must
+      implement the <code>org.apache.catalina.Server</code> interface.
+      If no class name is specified, the standard implementation will
+      be used.</p>
+    </attribute>
+
+    <attribute name="port" required="true">
+      <p>The TCP/IP port number on which this server waits for a shutdown
+      command.  This connection must be initiated from the same server
+      computer that is running this instance of Tomcat.</p>
+    </attribute>
+
+    <attribute name="shutdown" required="true">
+      <p>The command string that must be received via a TCP/IP connection
+      to the specified port number, in order to shut down Tomcat.</p>
+    </attribute>
+
+  </attributes>
+
+  </subsection>
+
+  <subsection name="Standard Implementation">
+
+  <p>The standard implementation of <strong>Server</strong> is
+  <strong>org.apache.catalina.core.StandardServer</strong>.
+  It supports the following additional attributes (in addition to the
+  common attributes listed above):</p>
+
+  <attributes>
+  </attributes>
+
+  </subsection>
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>The following components may be nested inside a <strong>Server</strong>
+  element:</p>
+  <ul>
+  <li><a href="service.html"><strong>Service</strong></a> - 
+      One or more service element.</li>
+  <li><a href="globalresources.html"><strong>GlobalNamingResources</strong></a> - 
+      Configure the JNDI global resources for the server.</li>
+  </ul>
+
+</section>
+
+
+<section name="Special Features">
+
+  <p>There are no special features associated with a <strong>Server</strong>.
+  </p>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/config/service.xml b/container/webapps/docs/config/service.xml
new file mode 100644
index 0000000..c0a9661
--- /dev/null
+++ b/container/webapps/docs/config/service.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="service.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>The Service Component</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>A <strong>Service</strong> element represents the combination of one or
+  more <strong>Connector</strong> components that share a single
+  <a href="engine.html">Engine</a> component for processing incoming
+  requests.  One or more <strong>Service</strong> elements may be nested
+  inside a <a href="server.html">Server</a> element.</p>
+
+</section>
+
+
+<section name="Attributes">
+
+  <subsection name="Common Attributes">
+
+  <p>All implementations of <strong>Service</strong>
+  support the following attributes:</p>
+
+  <attributes>
+
+    <attribute name="className" required="false">
+      <p>Java class name of the implementation to use.  This class must
+      implement the <code>org.apache.catalina.Service</code> interface.
+      If no class name is specified, the standard implementation will
+      be used.</p>
+    </attribute>
+
+    <attribute name="name" required="true">
+      <p>The display name of this <strong>Service</strong>, which will
+      be included in log messages if you utilize standard Catalina
+      components.  The name of each <strong>Service</strong> that is
+      associated with a particular <a href="server.html">Server</a>
+      must be unique.</p>
+    </attribute>
+
+  </attributes>
+
+  </subsection>
+
+  <subsection name="Standard Implementation">
+
+  <p>The standard implementation of <strong>Service</strong> is
+  <strong>org.apache.catalina.core.StandardService</strong>.
+  It supports the following additional attributes (in addition to the
+  common attributes listed above):</p>
+
+  <attributes>
+
+  </attributes>
+
+  </subsection>
+
+</section>
+
+
+<section name="Nested Components">
+
+  <p>The only components that may be nested inside a <strong>Service</strong>
+  element are one or more <strong>Connector</strong> elements,
+  followed by exactly one <a href="engine.html">Engine</a> element.</p>
+
+</section>
+
+
+<section name="Special Features">
+
+  <p>There are no special features associated with a <strong>Service</strong>.
+  </p>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/config/valve.xml b/container/webapps/docs/config/valve.xml
new file mode 100644
index 0000000..5f97d96
--- /dev/null
+++ b/container/webapps/docs/config/valve.xml
@@ -0,0 +1,434 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="valve.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>The Valve Component</title>
+  </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+  <p>A <strong>Valve</strong> element represents a component that will be
+  inserted into the request processing pipeline for the associated
+  Catalina container (<a href="engine.html">Engine</a>,
+  <a href="host.html">Host</a>, or <a href="context.html">Context</a>).
+  Individual Valves have distinct processing capabilities, and are
+  described individually below.</p>
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+</section>
+
+
+<section name="Access Log Valve">
+
+  <subsection name="Introduction">
+
+    <p>The <strong>Access Log Valve</strong> creates log files in the same
+    format as those created by standard web servers.  These logs can later
+    be analyzed by standard log analysis tools to track page hit counts,
+    user session activity, and so on.  The files produces by this <code>Valve</code>
+    are rolled over nightly at midnight.  This <code>Valve</code>
+    may be associated with any Catalina container (<code>Context</code>,
+    <code>Host</code>, or <code>Engine</code>), and
+    will record ALL requests processed by that container.</p>
+
+  </subsection>
+
+  <subsection name="Attributes">
+
+    <p>The <strong>Access Log Valve</strong> supports the following
+    configuration attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="true">
+        <p>Java class name of the implementation to use.  This MUST be set to
+        <strong>org.apache.catalina.valves.AccessLogValve</strong> to use the
+        default access log valve. To use a more optimized access log valve
+        designed for production use, you MUST set this attribute to 
+        <strong>org.apache.catalina.valves.FastCommonAccessLogValve</strong>.
+        In this case, only the <code>common</code> and <code>combined</code>
+        patterns are supported.</p>
+      </attribute>
+
+      <attribute name="directory" required="false">
+        <p>Absolute or relative pathname of a directory in which log files
+        created by this valve will be placed.  If a relative path is
+        specified, it is interpreted as relative to $CATALINA_HOME.  If
+        no directory attribute is specified, the default value is "logs"
+        (relative to $CATALINA_HOME).</p>
+      </attribute>
+
+      <attribute name="pattern" required="false">
+        <p>A formatting layout identifying the various information fields
+        from the request and response to be logged, or the word
+        <code>common</code> or <code>combined</code> to select a
+        standard format.  See below for more information on configuring
+        this attribute. Note that the optimized access does only support
+        <code>common</code> and <code>combined</code> as the value for this
+        attribute.</p>
+      </attribute>
+
+      <attribute name="prefix" required="false">
+        <p>The prefix added to the start of each log file's name.  If not
+        specified, the default value is "access_log.".  To specify no prefix,
+        use a zero-length string.</p>
+      </attribute>
+
+      <attribute name="resolveHosts" required="false">
+        <p>Set to <code>true</code> to convert the IP address of the remote
+        host into the corresponding host name via a DNS lookup.  Set to
+        <code>false</code> to skip this lookup, and report the remote IP
+        address instead.</p>
+      </attribute>
+
+      <attribute name="suffix" required="false">
+        <p>The suffix added to the end of each log file's name.  If not
+        specified, the default value is "".  To specify no suffix,
+        use a zero-length string.</p>
+      </attribute>
+
+      <attribute name="rotatable" required="false">
+        <p>Deafult true. Flag to determine if log rotation should occur.
+           If set to false, then this file is never rotated and
+           <tt>fileDateFormat</tt> is ignored. Use with caution!
+        </p>
+      </attribute>
+
+      <attribute name="condition" required="false">
+        <p>Turns on conditional logging. If set, requests will be
+           logged only if <tt>ServletRequest.getAttribute()</tt> is
+           null. For example, if this value is set to
+           <tt>junk</tt>, then a particular request will only be logged
+           if <tt>ServletRequest.getAttribute("junk") == null</tt>.
+           The use of Filters is an easy way to set/unset the attribute
+           in the ServletRequest on many different requests.
+        </p>
+      </attribute>
+
+      <attribute name="fileDateFormat" required="false">
+        <p>Allows a customized date format in the access log file name.
+           The date format also decides how often the file is rotated.
+           If you wish to rotate every hour, then set this value
+           to: <tt>yyyy-MM-dd.HH</tt>
+        </p>
+      </attribute>
+
+    </attributes>
+
+    <p>Values for the <code>pattern</code> attribute are made up of literal
+    text strings, combined with pattern identifiers prefixed by the "%"
+    character to cause replacement by the corresponding variable value from
+    the current request and response.  The following pattern codes are
+    supported:</p>
+    <ul>
+    <li><b>%a</b> - Remote IP address</li>
+    <li><b>%A</b> - Local IP address</li>
+    <li><b>%b</b> - Bytes sent, excluding HTTP headers, or '-' if zero</li>
+    <li><b>%B</b> - Bytes sent, excluding HTTP headers</li>
+    <li><b>%h</b> - Remote host name (or IP address if
+        <code>resolveHosts</code> is false)</li>
+    <li><b>%H</b> - Request protocol</li>
+    <li><b>%l</b> - Remote logical username from identd (always returns
+        '-')</li>
+    <li><b>%m</b> - Request method (GET, POST, etc.)</li>
+    <li><b>%p</b> - Local port on which this request was received</li>
+    <li><b>%q</b> - Query string (prepended with a '?' if it exists)</li>
+    <li><b>%r</b> - First line of the request (method and request URI)</li>
+    <li><b>%s</b> - HTTP status code of the response</li>
+    <li><b>%S</b> - User session ID</li>
+    <li><b>%t</b> - Date and time, in Common Log Format</li>
+    <li><b>%u</b> - Remote user that was authenticated (if any), else '-'</li>
+    <li><b>%U</b> - Requested URL path</li>
+    <li><b>%v</b> - Local server name</li>
+    <li><b>%D</b> - Time taken to process the request, in millis</li>
+    <li><b>%T</b> - Time taken to process the request, in seconds</li>
+    </ul>
+
+    <p>
+    There is also support to write information from the cookie, incoming
+    header, the Session or something else in the ServletRequest.
+    It is modeled after the apache syntax:
+    <ul>
+    <li><b><code>%{xxx}i</code></b> for incoming headers</li>
+    <li><b><code>%{xxx}c</code></b> for a specific cookie</li>
+    <li><b><code>%{xxx}r</code></b> xxx is an attribute in the ServletRequest</li>
+    <li><b><code>%{xxx}s</code></b> xxx is an attribute in the HttpSession</li>
+    </ul>
+    </p>
+
+
+    <p>The shorthand pattern name <code>common</code> (which is also the
+    default) corresponds to <strong>%h %l %u %t "%r" %s %b"</strong>.</p>
+
+    <p>The shorthand pattern name <code>combined</code> appends the
+    values of the <code>Referer</code> and <code>User-Agent</code> headers,
+    each in double quotes, to the <code>common</code> pattern
+    described in the previous paragraph.</p>
+
+  </subsection>
+
+</section>
+
+
+<section name="Remote Address Filter">
+
+  <subsection name="Introduction">
+
+    <p>The <strong>Remote Address Filter</strong> allows you to compare the
+    IP address of the client that submitted this request against one or more
+    <em>regular expressions</em>, and either allow the request to continue
+    or refuse to process the request from this client.  A Remote Address
+    Filter can be associated with any Catalina container
+    (<a href="engine.html">Engine</a>, <a href="host.html">Host</a>, or
+    <a href="context.html">Context</a>), and must accept any request
+    presented to this container for processing before it will be passed on.</p>
+
+    <p>The syntax for <em>regular expressions</em> is different than that for
+    'standard' wildcard matching. Tomcat uses the
+    <a href="http://jakarta.apache.org/regexp/">Jakarta Regexp</a> library.
+    Please consult the Regexp documentation for details of the expressions
+    supported.</p>
+
+  </subsection>
+
+  <subsection name="Attributes">
+
+    <p>The <strong>Remote Address Filter</strong> supports the following
+    configuration attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="true">
+        <p>Java class name of the implementation to use.  This MUST be set to
+        <strong>org.apache.catalina.valves.RemoteAddrValve</strong>.</p>
+      </attribute>
+
+      <attribute name="allow" required="false">
+        <p>A comma-separated list of <em>regular expression</em> patterns
+        that the remote client's IP address is compared to.  If this attribute
+        is specified, the remote address MUST match for this request to be
+        accepted.  If this attribute is not specified, all requests will be
+        accepted UNLESS the remote address matches a <code>deny</code>
+        pattern.</p>
+      </attribute>
+
+      <attribute name="deny" required="false">
+        <p>A comma-separated list of <em>regular expression</em> patterns
+        that the remote client's IP address is compared to.  If this attribute
+        is specified, the remote address MUST NOT match for this request to be
+        accepted.  If this attribute is not specified, request acceptance is
+        governed solely by the <code>accept</code> attribute.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+</section>
+
+
+<section name="Remote Host Filter">
+
+  <subsection name="Introduction">
+
+    <p>The <strong>Remote Host Filter</strong> allows you to compare the
+    hostname of the client that submitted this request against one or more
+    <em>regular expressions</em>, and either allow the request to continue
+    or refuse to process the request from this client.  A Remote Host
+    Filter can be associated with any Catalina container
+    (<a href="engine.html">Engine</a>, <a href="host.html">Host</a>, or
+    <a href="context.html">Context</a>), and must accept any request
+    presented to this container for processing before it will be passed on.</p>
+
+    <p>The syntax for <em>regular expressions</em> is different than that for
+    'standard' wildcard matching. Tomcat uses the
+    <a href="http://jakarta.apache.org/regexp/">Jakarta Regexp</a> library.
+    Please consult the Regexp documentation for details of the expressions
+    supported.</p>
+
+  </subsection>
+
+  <subsection name="Attributes">
+
+    <p>The <strong>Remote Host Filter</strong> supports the following
+    configuration attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="true">
+        <p>Java class name of the implementation to use.  This MUST be set to
+        <strong>org.apache.catalina.valves.RemoteHostValve</strong>.</p>
+      </attribute>
+
+      <attribute name="allow" required="false">
+        <p>A comma-separated list of <em>regular expression</em> patterns
+        that the remote client's hostname is compared to.  If this attribute
+        is specified, the remote hostname MUST match for this request to be
+        accepted.  If this attribute is not specified, all requests will be
+        accepted UNLESS the remote hostname matches a <code>deny</code>
+        pattern.</p>
+      </attribute>
+
+      <attribute name="deny" required="false">
+        <p>A comma-separated list of <em>regular expression</em> patterns
+        that the remote client's hostname is compared to.  If this attribute
+        is specified, the remote hostname MUST NOT match for this request to be
+        accepted.  If this attribute is not specified, request acceptance is
+        governed solely by the <code>accept</code> attribute.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+</section>
+
+
+<section name="Request Dumper Valve">
+
+
+  <subsection name="Introduction">
+
+    <p>The <em>Request Dumper Valve</em> is a useful tool in debugging
+    interactions with a client application (or browser) that is sending
+    HTTP requests to your Tomcat-based server.  When configured, it causes
+    details about each request processed by its associated <code>Engine</code>, 
+    <code>Host</code>, or <code>Context</code> to be logged according to 
+    the logging configuration for that container.</p>
+
+    <p><strong>WARNING: Using this valve has side-effects.</strong>  The
+    output from this valve includes any parameters included with the request.
+    The parameters will be decoded using the default platform encoding. Any
+    subsequent calls to <code>request.setCharacterEncoding()</code> within
+    the web application will have no effect.</p>
+
+  </subsection>
+
+
+  <subsection name="Attributes">
+
+    <p>The <strong>Request Dumper Valve</strong> supports the following
+    configuration attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="true">
+        <p>Java class name of the implementation to use.  This MUST be set to
+        <strong>org.apache.catalina.valves.RequestDumperValve</strong>.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Single Sign On Valve">
+
+  <subsection name="Introduction">
+
+    <p>The <em>Single Sign On Vale</em> is utilized when you wish to give users
+    the ability to sign on to any one of the web applications associated with
+    your virtual host, and then have their identity recognized by all other
+    web applications on the same virtual host.</p>
+
+    <p>See the <a href="host.html#Single Sign On">Single Sign On</a> special
+    feature on the <strong>Host</strong> element for more information.</p>
+
+  </subsection>
+
+
+  <subsection name="Attributes">
+
+    <p>The <strong>Single Sign On</strong> Valve supports the following
+    configuration attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="true">
+        <p>Java class name of the implementation to use.  This MUST be set to
+        <strong>org.apache.catalina.authenticator.SingleSignOn</strong>.</p>
+      </attribute>
+
+      <attribute name="requireReauthentication" required="false">
+        <p>Default false. Flag to determine whether each request needs to be 
+        reauthenticated to the security <strong>Realm</strong>. If "true", this
+        Valve uses cached security credentials (username and password) to
+        reauthenticate to the <strong>Realm</strong> each request associated 
+        with an SSO session.  If "false", the Valve can itself authenticate 
+        requests based on the presence of a valid SSO cookie, without 
+        rechecking with the <strong>Realm</strong>.</p>
+      </attribute>
+ 
+
+    </attributes>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Form Authenticator Valve">
+
+  <subsection name="Introduction">
+
+    <p>The <strong>Form Authenticator Valve</strong> is automatically added to
+    any <a href="context.html">Context</a> that is configured to use FORM
+    authentication.</p>
+
+    <p>If any non-default settings are required, the valve may be configured
+    within <a href="context.html">Context</a> element with the required
+    values.</p>
+
+  </subsection>
+
+  <subsection name="Attributes">
+
+    <p>The <strong>Form Authenticator Valve</strong> supports the following
+    configuration attributes:</p>
+
+    <attributes>
+
+      <attribute name="className" required="true">
+        <p>Java class name of the implementation to use.  This MUST be set to
+        <strong>org.apache.catalina.authenticator.FormAuthenticator</strong>.</p>
+      </attribute>
+
+      <attribute name="characterEncoding" required="false">
+        <p>Character encoding to use to read the username and password parameters
+        from the request. If not set, the encoding of the request body will be
+        used.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
+</section>
+
+
+</body>
+
+
+</document>
diff --git a/container/webapps/docs/connectors.xml b/container/webapps/docs/connectors.xml
new file mode 100644
index 0000000..e27101b
--- /dev/null
+++ b/container/webapps/docs/connectors.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="connectors.html">
+
+    &project;
+
+    <properties>
+        <author email="remm@apache.org">Remy Maucherat</author>
+        <title>Connectors How To</title>
+    </properties>
+
+<body>
+
+<section name="Introduction">
+
+<p>Choosing a connector to use with Tomcat can be difficult. This page will
+list the connectors which are supported with this Tomcat release, and will
+hopefully help you make the right choice according to your needs.</p>
+
+</section>
+
+<section name="HTTP">
+
+<p>The HTTP connector is setup by default with Tomcat, and is ready to use. This
+connector features the lowest latency and best overall performance.</p>
+
+<p>For clustering, a HTTP load balancer <b>with support for web sessions stickiness</b>
+must be installed to direct the traffic to the Tomcat servers. Tomcat supports mod_proxy
+(on Apache HTTP Server 2.x, and included by default in Apache HTTP Server 2.2) as the load balancer. 
+It should be noted that the performance of HTTP proxying is usually lower than the 
+performance of AJP, so AJP clustering is often preferable.</p>
+
+</section>
+
+<section name="AJP">
+
+<p>When using a single server, the performance when using a native webserver in 
+front of the Tomcat instance is most of the time significantly worse than a
+standalone Tomcat with its default HTTP connector, even if a large part of the web
+application is made of static files. If integration with the native webserver is 
+needed for any reason, an AJP connector will provide faster performance than 
+proxied HTTP. AJP clustering is the most efficient from the Tomcat perspective. 
+It is otherwise functionally equivalent to HTTP clustering.</p>
+
+<p>The native connectors supported with this Tomcat release are:
+<ul>
+<li>JK 1.2.x with any of the supported servers</li>
+<li>mod_proxy on Apache HTTP Server 2.x (included by default in Apache HTTP Server 2.2), 
+with AJP enabled</li>
+</ul>
+</p>
+
+<p><b>Other native connectors supporting AJP may work, but are no longer supported.</b></p>
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/default-servlet.xml b/container/webapps/docs/default-servlet.xml
new file mode 100755
index 0000000..5aae7ad
--- /dev/null
+++ b/container/webapps/docs/default-servlet.xml
@@ -0,0 +1,294 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="default-servlet.html">
+
+    &project;
+
+    <properties>
+        <author email="funkman@apache.org">Tim Funk</author>
+        <title>Default Servlet Reference</title>
+    </properties>
+
+<body>
+
+<section name="Introduction">
+
+This discusses different ways to manipulate the default servlet. Topics are
+<ul>
+  <li><a href="#what">What is the DefaultServlet?</a></li>
+  <li><a href="#where">Where is it declared?</a></li>
+  <li><a href="#change">What can I change?</a></li>
+  <li><a href="#dir">How do I customize directory listings?</a></li>
+  <li><a href="#secure">How do I secure directory listings?</a></li>
+
+</ul>
+
+</section>
+
+<section name="What is the DefaultServlet">
+<a name="what"></a>
+The default servlet is the servlet which serves static resources as well
+as serves the directory listings (if directory listings are enabled).
+
+</section>
+
+<section name="Where is it declared?">
+<a name="where"></a>
+It is declared globally in <i>$CATALINA_HOME/conf/web.xml</i>.
+By default here is it's declaration:
+<source>
+    &lt;servlet&gt;
+        &lt;servlet-name&gt;default&lt;/servlet-name&gt;
+        &lt;servlet-class&gt;
+          org.apache.catalina.servlets.DefaultServlet
+        &lt;/servlet-class&gt;
+        &lt;init-param&gt;
+            &lt;param-name&gt;debug&lt;/param-name&gt;
+            &lt;param-value&gt;0&lt;/param-value&gt;
+        &lt;/init-param&gt;
+        &lt;init-param&gt;
+            &lt;param-name&gt;listings&lt;/param-name&gt;
+            &lt;param-value&gt;true&lt;/param-value&gt;
+        &lt;/init-param&gt;
+        &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
+    &lt;/servlet&gt;
+
+...
+
+    &lt;servlet-mapping&gt;
+        &lt;servlet-name&gt;default&lt;/servlet-name&gt;
+        &lt;url-pattern&gt;/&lt;/url-pattern&gt;
+    &lt;/servlet-mapping&gt;
+
+</source>
+
+So by default, the default servlet is loaded at webapp startup and
+directory listings are enabled and debugging is turned off.
+</section>
+
+<section name="What can I change?">
+<a name="change"></a>
+The DefaultServlet allows the following initParamters:
+
+<table border="1">
+  <tr>
+    <th valign='top'>debug</th>
+    <td valign='top'>
+        Debugging level. It is not very useful unless you are a tomcat
+        developer. As
+        of this writing, useful values are 0, 1, 11, 1000.
+    </td>
+  </tr>
+  <tr>
+    <th valign='top'>listings</th>
+    <td valign='top'>
+        If no welcome file is present, can a directory listing be
+        shown?
+        value may be <b>true</b> or <b>false</b>
+        <br />
+        Welcome files are part of the servlet api.
+    </td>
+  </tr>
+  <tr>
+    <th valign='top'>readmeFile</th>
+    <td valign='top'>
+        If a directory listing is presented, a readme file may also
+        be presented with the listing. This file is inserted as is
+        so it may contain HTML. default value is null
+    </td>
+  </tr>
+  <tr>
+    <th valign='top'>globalXsltFile</th>
+    <td valign='top'>
+        If you wish to customize your directory listing, you
+        can use an XSL transformation. This value is an absolute
+        file name which be used for all direcotory listings.
+        This can be disabled by per webapp by also declaring the
+        default servlet in your local webapp's web.xml. The format
+        of the xml is shown below.
+    </td>
+  </tr>
+  <tr>
+    <th valign='top'>localXsltFile</th>
+    <td valign='top'>
+        You may also customize your directory listing by directory by
+        configuring <code>localXsltFile</code>. This should be a relative
+        file name in the directory where the listing will take place.
+        This overrides <code>globalXsltFile</code>. If this value
+        is present but a file does not exist, then
+        <code>globalXsltFile</code> will be used. If
+        <code>globalXsltFile</code> does not exist, then the default
+        directory listing will be shown.
+    </td>
+  </tr>
+  <tr>
+    <th valign='top'>input</th>
+    <td valign='top'>
+        Input buffer size (in bytes) when reading
+        resources to be served.  [2048]
+    </td>
+  </tr>
+  <tr>
+    <th valign='top'>output</th>
+    <td valign='top'>
+        Output buffer size (in bytes) when writing
+        resources to be served.  [2048]
+    </td>
+  </tr>
+  <tr>
+    <th valign='top'>readonly</th>
+    <td valign='top'>
+        Is this context "read only", so HTTP commands like PUT and
+        DELETE are rejected?  [true]
+    </td>
+  </tr>
+  <tr>
+    <th valign='top'>fileEncoding</th>
+    <td valign='top'>
+        File encoding to be used when reading static resources.
+        [platform default]
+    </td>
+  </tr>
+  <tr>
+    <th valign='top'>sendfileSize</th>
+    <td valign='top'>
+        If the connector used supports sendfile, this represents the minimal 
+        file size in KB for which sendfile will be used. Use a negative value 
+        to always disable sendfile. [48]
+    </td>
+  </tr>
+
+</table>
+</section>
+
+<section name="How do I customize directory listings?">
+<a name="dir"></a>
+<p>You can override DefaultServlet with you own implementation and use that
+in your web.xml declaration. If you
+can undertand what was just said, we will assume yo can read the code
+to DefaultServlet servlet and make the appropriate adjustments. (If not,
+then that method isn't for you)
+</p>
+<p>
+You can use either  <code>localXsltFile</code> or
+<code>globalXsltFile</code> and DefaultServlet will create
+an xml document and run it through an xsl transformation based
+on the values provided in <code>localXsltFile</code> and
+<code>globalXsltFile</code>. <code>localXsltFile</code> is first
+checked, followed by <code>globalXsltFile</code>, then default
+behaviors takes place.
+</p>
+
+<p>
+Format:
+<source>
+    &lt;listing&gt;
+     &lt;entries&gt;
+      &lt;entry type='file|dir' urlPath='aPath' size='###' date='gmt date'&gt;
+        fileName1
+      &lt;/entry&gt;
+      &lt;entry type='file|dir' urlPath='aPath' size='###' date='gmt date'&gt;
+        fileName2
+      &lt;/entry&gt;
+      ...
+     &lt;/entries&gt;
+     &lt;readme&gt;&lt;/readme&gt;
+    &lt;/listing&gt;
+</source>
+<ul>
+  <li>size will be missing if <code>type='dir'</code></li>
+  <li>Readme is a CDATA entry</li>
+</ul>
+</p>
+The following is a sample xsl file which mimics the default tomcat behavior:
+<source>
+&lt;?xml version="1.0"?&gt;
+
+&lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  version="1.0"&gt;
+
+  &lt;xsl:output method="xhtml" encoding="iso-8859-1" indent="no"/&gt;
+
+  &lt;xsl:template match="listing"&gt;
+   &lt;html&gt;
+    &lt;head&gt;
+      &lt;title&gt;
+        Sample Directory Listing For
+        &lt;xsl:value-of select="@directory"/&gt;
+      &lt;/title&gt;
+      &lt;style&gt;
+        h1{color : white;background-color : #0086b2;}
+        h3{color : white;background-color : #0086b2;}
+        body{font-family : sans-serif,Arial,Tahoma;
+             color : black;background-color : white;}
+        b{color : white;background-color : #0086b2;}
+        a{color : black;} HR{color : #0086b2;}
+      &lt;/style&gt;
+    &lt;/head&gt;
+    &lt;body&gt;
+      &lt;h1&gt;Sample Directory Listing For
+            &lt;xsl:value-of select="@directory"/&gt;
+      &lt;/h1&gt;
+      &lt;hr size="1" /&gt;
+      &lt;table cellspacing="0"
+                  width="100%"
+            cellpadding="5"
+                  align="center"&gt;
+        &lt;tr&gt;
+          &lt;th align="left"&gt;Filename&lt;/th&gt;
+          &lt;th align="center"&gt;Size&lt;/th&gt;
+          &lt;th align="right"&gt;Last Modified&lt;/th&gt;
+        &lt;/tr&gt;
+        &lt;xsl:apply-templates select="entries"/&gt;
+        &lt;/table&gt;
+      &lt;xsl:apply-templates select="readme"/&gt;
+      &lt;hr size="1" /&gt;
+      &lt;h3&gt;Apache Tomcat/5.0&lt;/h3&gt;
+    &lt;/body&gt;
+   &lt;/html&gt;
+  &lt;/xsl:template&gt;
+
+
+  &lt;xsl:template match="entries"&gt;
+    &lt;xsl:apply-templates select="entry"/&gt;
+  &lt;/xsl:template&gt;
+
+  &lt;xsl:template match="readme"&gt;
+    &lt;hr size="1" /&gt;
+    &lt;pre&gt;&lt;xsl:apply-templates/&gt;&lt;/pre&gt;
+  &lt;/xsl:template&gt;
+
+  &lt;xsl:template match="entry"&gt;
+    &lt;tr&gt;
+      &lt;td align="left"&gt;
+        &lt;xsl:variable name="urlPath" select="@urlPath"/&gt;
+        &lt;a href="{$urlPath}"&gt;
+          &lt;tt&gt;&lt;xsl:apply-templates/&gt;&lt;/tt&gt;
+        &lt;/a&gt;
+      &lt;/td&gt;
+      &lt;td align="right"&gt;
+        &lt;tt&gt;&lt;xsl:value-of select="@size"/&gt;&lt;/tt&gt;
+      &lt;/td&gt;
+      &lt;td align="right"&gt;
+        &lt;tt&gt;&lt;xsl:value-of select="@date"/&gt;&lt;/tt&gt;
+      &lt;/td&gt;
+    &lt;/tr&gt;
+  &lt;/xsl:template&gt;
+
+&lt;/xsl:stylesheet&gt;
+</source>
+
+</section>
+
+<section name="How do I secure directory listings?">
+<a name="secure"></a>
+Use web.xml in each individual webapp. See the security section of the
+Servlet specification.
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/deployer-howto.xml b/container/webapps/docs/deployer-howto.xml
new file mode 100644
index 0000000..e3fc412
--- /dev/null
+++ b/container/webapps/docs/deployer-howto.xml
@@ -0,0 +1,181 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="deployer-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="remm@apache.org">Remy Maucherat</author>
+        <title>Deployer HOW-TO</title>
+    </properties>
+
+<body>
+
+<section name="Table of Contents">
+
+<p>
+<a href="#Introduction">Introduction</a><br />
+<a href="#Context descriptors">Context XML descriptors</a><br />
+<a href="#Deploying on Tomcat startup">Deploying on Tomcat startup</a><br />
+<a href="#Deploying on a running Tomcat server">Deploying on running Tomcat server</a><br />
+<a href="#Deploying using the Client Deployer Package">Deploying using the Client Deployer Package</a><br />
+<blockquote>
+</blockquote>
+</p>
+
+</section>
+
+<section name="Introduction">
+
+<p>The deployer, as its name implies, allows deploying and undeploying web
+applications to the Tomcat server, either statically (the application is 
+setup before the server is started), or dynamically (in conjunction with the
+Tomcat Manager web application or manipulating already deployed applications).
+</p>
+
+</section>
+
+<section name="Context descriptors">
+
+<p>A Context XML descriptor is a fragment of XML data which contains a valid
+Context element which would normally be found in the main server configuration
+file (conf/server.xml), and allows easy and automated manipulation 
+of web applications by the various management tools available in Tomcat. 
+For a given host, the Context descriptors are located in 
+<code>$CATALINA_HOME/conf/[enginename]/[hostname]/foo.xml</code>. Note that 
+while the name of the file is not tied to the webapp name, Tomcat will create
+Context descriptors which match the webapp name whenever it will generate a
+Context descriptor.
+</p>
+
+<p>Context descriptors allow defining all aspects and configuration parameters
+of a Context, such as naming resources and session manager configuration.
+It should be noted that the docBase specified in the Context element can 
+refer to either the .WAR or the directory which will be created when the
+.WAR is expanded or the .WAR itself.</p>
+
+</section>
+
+<section name="Deploying on Tomcat startup">
+
+<p>The webapps which are present in the host appBase will be deployed if the
+host "deployOnStartup" property is true. The deployment process is 
+the following:
+<ul>
+    <li>The Context XML declarations will be deployed first</li>
+    <li>Expanded web applications not referenced by Context XML declarations
+        will then be deployed; if they have an associated .WAR file and it is
+        newer than the expanded web application, the expanded directory will
+        be removed and the webapp will be redeployed from the .WAR</li>
+    <li>.WAR files will be deployed</li>
+</ul>
+For each deployed web application, a matching Context XML descriptor will be
+created unless one exists already.
+</p>
+
+</section>
+
+<section name="Deploying on a running Tomcat server">
+
+<p>If the host "autoDeploy" property is true, the host will attempt to deploy 
+and update web applications dynamically, as needed. The host will need to
+have background processing enabled for automatic reloading to work, which
+is the default.</p>
+
+<p>This includes:
+<ul>
+    <li>Deployment of WARs which are copied to the host appBase.</li>
+    <li>Deployment of expanded web applications which are copied to the host
+        appBase.</li>
+    <li>Redeployment of a web application which has been deployed from a WAR
+        when the WAR is updated: the expanded web application is removed, 
+        and the WAR is expanded again. This will not happen if the host is
+        configured so that WARs are not expanded, in which case the webapp
+        will be simply redeployed.</li>
+    <li>Redeployment of the web application if the /WEB-INF/web.xml file is
+        updated.</li>
+    <li>Redeployment of the web application if the context XML file from which
+        the web application has been deployed is updated.</li>
+    <li>Redeployment of the web application if a context XML file (with a
+        name corresponding to the context path of the previously deployed
+        application) is added in the 
+        <code>$CATALINA_HOME/conf/[enginename]/[hostname]/</code> folder.</li>
+    <li>Undeployment of the web application if its document base is deleted
+        (on Windows, this assumes that anti locking features are enabled, otherwise
+        it is not possible to delete the resources of a running web application).</li>
+</ul>
+Note: Web application reloading can also be configured in the loader, in which
+case loaded classes will be tracked for changes.
+</p>
+
+</section>
+
+<section name="Deploying using the Client Deployer Package">
+
+<p>The client deployer is a package which can be used to validate, compile,
+and deploy a web application to a production or development server. It should 
+be noted that this feature uses the Tomcat manager for automatic deployment.
+</p>
+
+<p>The deployer includes the Catalina manager Ant tasks, the Jasper page
+compiler for JSP compilation before deployment, as well as a task which
+validates the webapp's deployment descriptor. The validator task (class
+<code>org.apache.catalina.ant.ValidatorTask</code>) allows only one parameter:
+the base path of an expanded web application.</p>
+
+<p>The deployer uses an unpacked web application as input (see the list of the
+properties used by the deployer below). A web application which 
+is programatically deployed with the deployer may include Tomcat specific 
+deployment configuration, by including a Context configuration XML file in 
+<code>/META-INF/context.xml</code>.</p>
+
+<p>The deployer package includes a ready to use Ant script, with the following
+targets:
+<ul>
+    <li><code>compile</code> (default): Compile and validate the web 
+        application. This can be used standalone, and does not need a running
+        Tomcat server. The compiled application will only run on the associated
+        Tomcat 5.5.x server release, and is not guaranteed to work on another
+        Tomcat release, as the code generated by Jasper depends on its runtime
+        component. It should also be noted that this target will also compile
+        automatically any Java source file located in the 
+        <code>/WEB-INF/classes</code> folder of the web application.</li>
+    <li><code>deploy</code>: Deploy a web application (compiled or not) to 
+        a Tomcat server</li>
+    <li><code>undeploy</code>: Undeploy a web application</li>
+    <li><code>start</code>: Start web application</li>
+    <li><code>reload</code>: Reload web application</li>
+    <li><code>stop</code>: Stop web application</li>
+</ul>
+The following properties can be specified, either as system properties, or by
+using a <code>deployer.properties</code> file located in the root folder of the
+deployer package:
+<ul>
+    <li><code>build</code>: The build folder used will be, by default, 
+        <code>${build}/webapp${path}</code>. After the end of the execution
+        of the <code>compile</code> target, the web application WAR will be
+        located at <code>${build}/webapp${path}.war</code>.</li>
+    <li><code>webapp</code>: Folder containing the expanded web application 
+        which will be compiled and validated. By default, the folder is
+        <code>myapp</code>.</li>
+    <li><code>path</code>: Deployed context path of the web application, 
+        by default <code>/myapp</code>.</li>
+    <li><code>url</code>: Absolute URL to the manager web application of a 
+        running Tomcat server, which will be used to deploy and undeploy the
+        web application. By default, the deployer will attempt to access 
+        a Tomcat instance running on localhost, at 
+        <code>http://localhost:8080/manager</code>.</li>
+    <li><code>username</code>: Username to be used to connect to the Tomcat 
+        manager.</li>
+    <li><code>password</code>: Password to be used to connect to the Tomcat
+        manager.</li>
+</ul>
+</p>
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/developers.xml b/container/webapps/docs/developers.xml
new file mode 100644
index 0000000..290b79c
--- /dev/null
+++ b/container/webapps/docs/developers.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="developers.html">
+
+  &project;
+
+  <properties>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>Tomcat Developers</title>
+  </properties>
+
+<body>
+
+  <section name="Active Developers">
+
+    <p>
+      The list indicates the developers' main areas of interest. Feel free to
+      add to the list :) The developers email addresses are 
+      <code>[login]@apache.org</code>. Please <strong>do not</strong> contact 
+      developers directly for any support issues (please post to the 
+      tomcat-users mailing list instead, or one of the other support 
+      resources; some organizations and individual consultants also offer 
+      for pay Tomcat support, as listed on the 
+      <a href="http://jakarta.apache.org/site/vendors.html">vendors page</a>
+      on the Jakarta website).
+    </p>
+
+    <ul>
+      <li>Amy Roh (amyroh): Catalina, Admin webapp</li>
+      <li>Bill Barker (billbarker): Connectors</li>
+      <li>Costin Manolache (costin): Catalina, Connectors</li>
+      <li>Filip Hanik (fhanik): Clustering</li>
+      <li>Glenn Nielsen (glenn): Catalina, Connectors</li>
+      <li>Henri Gomez (hgomez): Connectors</li>
+      <li>Jan Luehe (luehe): Jasper</li>
+      <li>Jean-Francois Arcand (jfarcand): Catalina</li>
+      <li>Jean-Frederic Clere (jfclere): Connectors</li>
+      <li>Kin-Man Chung (kinman): Jasper</li>
+      <li>Mladen Turk (mturk): Connectors</li>
+      <li>Peter Rossbach (pero): Catalina, Clustering, JMX</li>
+      <li>Remy Maucherat (remm): Catalina, Connectors, Docs</li>
+      <li>Tim Funk (funkman): Catalina, Docs</li>
+      <li>Yoav Shapira (yoavs): Docs, JMX, Catalina, balancer, Release Manager</li>
+    </ul>
+
+  </section>
+
+  <section name="Retired Developers">
+
+
+
+  </section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/funcspecs/fs-admin-apps.xml b/container/webapps/docs/funcspecs/fs-admin-apps.xml
new file mode 100644
index 0000000..5c337c9
--- /dev/null
+++ b/container/webapps/docs/funcspecs/fs-admin-apps.xml
@@ -0,0 +1,278 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="fs-admin-apps.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig McClanahan</author>
+    <title>Administrative Apps - Overall Requirements</title>
+    <revision>$Id$</revision>
+  </properties>
+
+<body>
+
+
+<section name="Overview">
+
+
+  <subsection name="Introduction">
+
+    <p>The purpose of this specification is to define high level requirements
+    for administrative applications that can be used to manage the operation
+    of a running Tomcat 5 container.  A variety of <em>Access Methods</em>
+    to the supported administrative functionality shall be supported, to
+    meet varying requirements:</p>
+    <ul>
+    <li><em>As A Scriptable Web Application</em> - The existing
+        <code>Manager</code> web application provides a simple HTTP-based
+        interface for managing Tomcat through commands that are expressed
+        entirely through a request URI.  This is useful in environments
+        where you wish to script administrative commands with tools that
+        can generate HTTP transactions.</li>
+    <li><em>As An HTML-Based Web Application</em> - Use an HTML presentation
+        to provide a GUI-like user interface for humans to interact with the
+        administrative capabilities.</li>
+    <li><em>As SOAP-Based Web Services</em> - The operational commands to
+        administer Tomcat are made available as web services that utilize
+        SOAP message formats.</li>
+    <li><em>As Java Management Extensions (JMX) Commands</em> - The operational
+        commands to administer Tomcat are made available through JMX APIs,
+        for integration into management consoles that utilize them.</li>
+    <li><em>Other Remote Access APIs</em> - Other remote access APIs, such
+        as JINI, RMI, and CORBA can also be utilized to access administrative
+        capabilities.</li>
+    </ul>
+
+    <p>Underlying all of the access methods described above, it is assumed
+    that the actual operations are performed either directly on the
+    corresponding Catalina components (such as calling the
+    <code>Deployer.deploy()</code> method to deploy a new web application),
+    or through a "business logic" layer that can be shared across all of the
+    access methods.  This approach minimizes the cost of adding new
+    administrative capabilities later -- it is only necessary to add the
+    corresponding business logic function, and then write adapters to it for
+    all desired access methods.</p>
+
+    <p>The current status of this functional specification is
+    <strong>PROPOSED</strong>.  It has not yet been discussed and
+    agreed to on the TOMCAT-DEV mailing list.</p>
+
+  </subsection>
+
+
+  <subsection name="External Specifications">
+
+    <p>The implementation of this functionality depends on the following
+    external specifications:</p>
+    <ul>
+    <li><a href="http://java.sun.com/products/jdk/idl/index.html">Java
+        IDL</a> (for CORBA, included in the JDK)</li>
+    <li><a href="http://java.sun.com/products/JavaManagement/index.html">
+        Java Management Extensions</a></li>
+    <li><a href="http://java.sun.com/products/rmi/index.html">Remote
+        Method Invocation</a> (Included in the JDK)</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Implementation Requirements">
+
+    <p>The implementation of this functionality shall conform to the
+    following requirements:</p>
+    <ul>
+    <li>To the maximum extent feasible, all administrative functions,
+        and the access methods that support them, shall run portably
+        on all platforms where Tomcat 5 itself runs.</li>
+    <li>In a default Tomcat distribution, all administrative capabilities
+        shall be disabled.  It shall be necessary for a system
+        administrator to specifically enable the desired access methods
+        (such as by adding a username/password with a specific role to
+        the Tomcat user's database.</li>
+    <li>Administrative functions shall be realized as direct calls to
+        corresponding Catalina APIs, or through a business logic layer
+        that is independent of the access method used to initiate it.</li>
+    <li>The common business logic components shall be implemented in
+        package <code>org.apache.catalina.admin</code>.</li>
+    <li>The common business logic components shall be built as part of the
+        standard Catalina build process, and made visible in the
+        Catalina class loader.</li>
+    <li>The Java components required for each access method shall be
+        implemented in subpackages of <code>org.apache.catalina.admin</code>.
+        </li>
+    <li>The build scripts should treat each access method as optional,
+        so that it will be built only if the corresponding required
+        APIs are present at build time.</li>
+    <li>It shall be possible to save the configured state of the running
+        Tomcat container such that this state can be reproduced when the
+        container is shut down and restarted.</li>
+    <li>Adminstrative commands to start up and shut down the overall
+        Tomcat container are <strong>out of scope</strong> for the
+        purposes of these applications.  It is assumed that other
+        (usually platform-specific) mechanisms will be used for container
+        startup and shutdown.</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Dependencies">
+
+
+  <subsection name="Environmental Dependencies">
+
+    <p>The following environmental dependencies must be met in order for
+    administrative applications to operate correctly:</p>
+    <ul>
+    <li>For access methods that require creation of server sockets, the
+        appropriate ports must be configured and available.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Container Dependencies">
+
+    <p>Correct operation of administrative applications depends on the
+       following specific features of the surrounding container:</p>
+    <ul>
+    <li>To the maximum extent feasible, Catalina components that offer
+        direct administrative APIs and property setters shall support
+        "live" changes to their operation, without requiring a container
+        restart.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="External Technologies">
+
+    <p>The availability of the following technologies can be assumed
+    for the implementation and operation of the various access methods
+    and the corresponding administrative business logic:</p>
+    <ul>
+    <li><a href="http://java.sun.com/j2se/">Java 2 Standard Edition</a>
+        (Version 1.2 or later)</li>
+    <li><a href="http://www.jcp.org/jsr/detail/154.jsp">Servlet 2.4</a>
+        (supported natively by Tomcat 5)</li>
+    <li><a href="http://www.jcp.org/jsr/detail/152.jsp">JavaServer Pages 2.0</a>
+        (supported natively by Tomcat 5)</li>
+    <li><a href="http://jakarta.apache.org/taglibs/doc/standard-doc/intro.html">JavaServer Pages Standard Tag Library 1.0 (Jakarta Taglibs-Standard 1.0.3)</a></li>
+    <li><a href="http://jakarta.apache.org/struts/">Struts Framework</a>
+        (Version 1.0) - MVC Framework for Web Applications</li>
+    <li><strong>TO BE DETERMINED</strong> - Application for hosting SOAP
+        based web services</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Functionality">
+
+
+  <subsection name="Properties of Administered Objects">
+
+  <p>Functional requirements for administrative applications are specified
+  in terms of <em>Administered Objects</em>, whose definitions and detailed
+  properties are listed <a href="fs-admin-objects.html">here</a>.  In general,
+  Administered Objects correspond to components in the Catalina architecture,
+  but these objects are defined separately here for the following reasons:</p>
+  <ul>
+  <li>It is possible that the administrative applications do not expose
+      every possible configurable facet of the underlying components.</li>
+  <li>In some cases, an Administered Object (from the perspective of an
+      administrative operation) is realized by more than one Catalina
+      component, at a finer-grained level of detail.</li>
+  <li>It is necessary to represent the configuration information for a
+      component separately from the component itself (for instance, in
+      order to store that configuration information for later use).</li>
+  <li>It is necessary to represent configuration information (such as
+      a Default Context) when there is no corresponding component instance.
+      </li>
+  <li>Administered Objects, when realized as Java classes, will include
+      methods for administrative operations that have no correspondence
+      to operations performed by the corresponding actual components.</li>
+  </ul>
+
+  <p>It is assumed that the reader is familiar with the overall component
+  architecture of Catalina.  For further information, see the corresponding
+  Developer Documentation.  To distinguish names that are used as both
+  <em>Administered Objects</em> and <code>Components</code>, different
+  font presentations are utilized.  Default values for many properties
+  are listed in [square brackets].</p>
+
+  </subsection>
+
+
+  <subsection name="Supported Administrative Operations">
+
+  <p>The administrative operations that are available are described in terms
+  of the corresponding Administered Objects (as defined above), in a manner
+  that is independent of the access method by which these operations are
+  requested.  In general, such operations are relevant only in the context
+  of a particular Administered Object (and will most likely be realized as
+  method calls on the corresponding Administered Object classes), so they
+  are organized based on the currently "focused" administered object.
+  The available Supported Operations are documented
+  <a href="fs-admin-opers.html">here</a>.</p>
+
+  </subsection>
+
+
+  <subsection name="Access Method Specific Requirements">
+
+  <h5>Scriptable Web Application</h5>
+
+  <p>An appropriate subset of the administrative operations described above
+  shall be implemented as commands that can be performed by the "Manager"
+  web application.  <strong>FIXME</strong> - Enumerate them.</p>
+
+  <p>In addition, this web application shall conform to the following
+  requirements:</p>
+  <ul>
+  <li>All request URIs shall be protected by a security constraint that
+      requires security role <code>manager</code> for processing.</li>
+  <li>The default user database shall <strong>not</strong> contain any
+      user that has been assigned the role <code>manager</code>.</li>
+  </ul>
+
+  <h5>HTML-Based Web Application</h5>
+
+  <p>The entire suite of administrative operations described above shall be
+  made available through a web application designed for human interaction.
+  In addition, this web application shall conform to the following
+  requirements:</p>
+  <ul>
+  <li>Must be implemented using servlet, JSP, and MVC framework technologies
+      described under "External Technologies", above.</li>
+  <li>Prompts and error messages must be internationalizable to multiple
+      languages.</li>
+  <li>Rendered HTML must be compatible with Netscape Navigator (verson 4.7
+      or later) and Internet Explorer (version 5.0 or later).</li>
+  </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Testable Assertions">
+
+  <p><strong>FIXME</strong> - Complete this section.</p>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/fs-admin-objects.xml b/container/webapps/docs/funcspecs/fs-admin-objects.xml
new file mode 100644
index 0000000..364ad90
--- /dev/null
+++ b/container/webapps/docs/funcspecs/fs-admin-objects.xml
@@ -0,0 +1,481 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="fs-admin-objects.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig McClanahan</author>
+    <title>Administrative Apps - Administered Objects</title>
+    <revision>$Id$</revision>
+  </properties>
+
+<body>
+
+
+<section name="Administered Objects Overview">
+
+<p>This document defines the <em>Administered Objects</em> that represent
+the internal architectural components of the Catalina servlet container.
+Associated with each is a set of <a href="fs-admin-opers.html">Supported
+Operations</a> that can be performed when the administrative application is
+"focused" on a particular configurable object.</p>
+
+<p>The following Administered Objects are defined:</p>
+<ul>
+<li><a href="#Access Logger">Access Logger</a></li>
+<li><a href="#Connector">Connector</a></li>
+<li><a href="#Context">Context</a></li>
+<li><a href="#Default Context">Default Context</a></li>
+<li><a href="#Default Deployment Descriptor">Default Deployment Descriptor</a></li>
+<li><a href="#Engine">Engine</a></li>
+<li><a href="#Environment Entry">Environment Entry</a></li>
+<li><a href="#Host">Host</a></li>
+<li><a href="#JDBC Resource">JDBC Resource</a></li>
+<li><a href="#Loader">Loader</a></li>
+<li><a href="#Manager">Manager</a></li>
+<li><a href="#Realm">Realm</a></li>
+<li><a href="#Request Filter">Request Filter</a></li>
+<li><a href="#Server">Server</a></li>
+<li><a href="#Service">Service</a></li>
+</ul>
+
+</section>
+
+
+<section name="Access Logger">
+
+  <p>An <em>Access Logger</em> is an optional <code>Valve</code> that can
+  create request access logs in the same formats as those provided by
+  web servers.  Such access logs are useful input to hit count and user
+  access tracking analysis programs.  An Access Logger can be attached to
+  an <em>Engine</em>, a <em>Host</em>, a <em>Context</em>, or a <em>Default
+  Context</em>.</p>
+
+  <p>The standard component implementing an <em>Access Logger</em> is
+  <code>org.apache.catalina.valves.AccessLogValve</code>.  It supports the
+  following configurable properties:</p>
+  <ul>
+  <li><code>debug</code> - Debugging detail level.  [0]</li>
+  <li><code>directory</code> - Absolute or relative (to $CATALINA_HOME) path
+      of the directory into which access log files are created.
+      [logs].</li>
+  <li><code>pattern</code> - Pattern string defining the fields to be
+      included in the access log output, or "common" for the standard
+      access log pattern.  See
+      <code>org.apache.catalina.valves.AccessLogValve</code> for more
+      information.  [common]</li>
+  <li><code>prefix</code> - Prefix added to the beginning of each log file
+      name created by this access logger.</li>
+  <li><code>resolveHosts</code> - Should IP addresses be resolved to host
+      names in the log?  [false]</li>
+  <li><code>suffix</code> - Suffix added to the end of each log file name
+      created by this access logger.</li>
+  </ul>
+
+</section>
+
+
+<section name="Connector">
+
+  <p>A <em>Connector</em> is the representation of a communications endpoint
+  by which requests are received from (and responses returned to) a Tomcat
+  client.  The administrative applications shall support those connectors
+  that are commonly utilized in Tomcat installations, as described in detail
+  below.</p>
+
+  <p>For standalone use, the standard connector supporting the HTTP/1.1
+  protocol is <code>org.apache.catalina.connectors.http.HttpConnector</code>.
+  It supports the following configurable properties:</p>
+  <ul>
+  <li><code>acceptCount</code> - The maximum queue length of incoming
+      connections that have not yet been accepted.  [10]</li>
+  <li><code>address</code> - For servers with more than one IP address, the
+      address upon which this connector should listen.  [All Addresses]</li>
+  <li><code>bufferSize</code> - Default input buffer size (in bytes) for
+      requests created by this Connector.  [2048]</li>
+  <li><code>debug</code> - Debugging detail level.  [0]</li>
+  <li><code>enableLookups</code> - Should we perform DNS lookups on remote
+      IP addresses when <code>request.getRemoteHost()</code> is called?
+      [true]</li>
+  <li><code>maxProcessors</code> - The maximum number of processor threads
+      supported by this connector.  [20]</li>
+  <li><code>minProcessors</code> - The minimum number of processor threads
+      to be created at container startup.  [5]</li>
+  <li><code>port</code> - TCP/IP port number on which this Connector should
+      listen for incoming requests. [8080]</li>
+  <li><code>proxyName</code> - Host name to be returned when an application
+      calls <code>request.getServerName()</code>.  [Value of Host: header]</li>
+  <li><code>proxyPort</code> - Port number to be returned when an application
+      calls <code>request.getServerPort()</code>.  [Same as <code>port</code>]
+      </li>
+  </ul>
+
+</section>
+
+
+<section name="Context">
+
+  <p>A <em>Context</em> is the representation of an individual web application,
+  which is associated with a corresponding <em>Host</em>.  Note that the
+  administrable properties of a <em>Context</em> do <strong>not</strong>
+  include any settings from inside the web application deployment descriptor
+  for that application.</p>
+
+  <p>The standard component implementing a <em>Context</em> is
+  <code>org.apache.catalina.core.StandardContext</code>.  It supports the
+  following configurable properties:</p>
+  <ul>
+  <li><code>cookies</code> - Should be use cookies for session identifier
+      communication?  [true]</li>
+  <li><code>crossContext</code> - Should calls to
+      <code>ServletContext.getServletContext()</code> return the actual
+      context responsible for the specified path?  [false]</li>
+  <li><code>debug</code> - Debugging detail level.  [0]</li>
+  <li><code>docBase</code> - The absolute or relative (to the
+      <code>appBase</code> of our owning <em>Host</em>) pathname of a
+      directory containing an unpacked web application, or of a web
+      application archive (WAR) file.</li>
+  <li><code>override</code> - Should settings in this <em>Context</em>
+      override corresponding settings in the <em>Default Context</em>?
+      [false]</li>
+  <li><code>path</code> - Context path for this web application, or an empty
+      string for the root application of a <em>Host</em>.  [Inferred from
+      directory or WAR file name]</li>
+  <li><code>reloadable</code> - Should Tomcat monitor classes in the
+      <code>/WEB-INF/classes</code> directory for changes, and reload the
+      application if they occur?  [false]</li>
+  <li><code>useNaming</code> - Should Tomcat provide a JNDI naming context,
+      containing preconfigured entries and resources, corresponding to the
+      requirements of the Java2 Enterprise Edition specification?  [true]</li>
+  <li><code>workDir</code> - Absolute pathname of a scratch directory that is
+      provided to this web application.  [Automatically assigned relative to
+      $CATALINA_HOME/work]</li>
+  </ul>
+
+  <p>Each <em>Context</em> is owned by a parent <em>Host</em>, and is
+  associated with:</p>
+  <ul>
+  <li>An optional <em>Access Logger</em> that logs all requests processed
+      by this web application.</li>
+  <li>Zero or more <em>Environment Entries</em> representing environment
+      entries for the JNDI naming context associated with a web
+      application.</li>
+  <li>Zero or more <em>JDBC Resources</em> representing database connection
+      pools associated with a web application.</li>
+  <li>A <em>Loader</em> representing the web application class loader used
+      by this web application.</li>
+  <li>A <em>Manager</em> representing the session manager used by this
+      web application.</li>
+  <li>An optional <em>Realm</em> used to provide authentication and access
+      control information for this web application.</li>
+  <li>Zero or more <em>Request Filters</em> used to limit access to this
+      web application based on remote host name or IP address.</li>
+  </ul>
+
+</section>
+
+
+<section name="Default Context">
+
+  <p>A <em>Default Context</em> represents a subset of the configurable
+  properties of a <em>Context</em>, and is used to set defaults for those
+  properties when web applications are automatically deployed.  A <em>Default
+  Context</em> object can be associated with an <em>Engine</em> or a
+  <em>Host</em>.  The following configurable properties are supported:</p>
+  <ul>
+  <li><code>cookies</code> - Should be use cookies for session identifier
+      communication?  [true]</li>
+  <li><code>crossContext</code> - Should calls to
+      <code>ServletContext.getServletContext()</code> return the actual
+      context responsible for the specified path?  [false]</li>
+  <li><code>reloadable</code> - Should Tomcat monitor classes in the
+      <code>/WEB-INF/classes</code> directory for changes, and reload the
+      application if they occur?  [false]</li>
+  <li><code>useNaming</code> - Should Tomcat provide a JNDI naming context,
+      containing preconfigured entries and resources, corresponding to the
+      requirements of the Java2 Enterprise Edition specification?  [true]</li>
+  </ul>
+
+  <p>Each <em>Default Context</em> is owned by a parent <em>Engine</em> or
+  <em>Host</em>, and is associated with:</p>
+  <ul>
+  <li>Zero or more <em>Environment Entries</em> representing environment
+      entries for the JNDI naming context associated with a web
+      application.</li>
+  <li>Zero or more <em>JDBC Resources</em> representing database connection
+      pools associated with a web application.</li>
+  <li>An optional <em>Loader</em> representing default configuration
+      properties for the Loader component of deployed web applications.</li>
+  <li>An optional <em>Manager</em> representing default configuration
+      properties for the Manager component fo deployed web applications.</li>
+  </ul>
+
+</section>
+
+
+<section name="Default Deployment Descriptor">
+
+  <p>Default web application characteristics are configured in a special
+  deployment descriptor named <code>$CATALINA_HOME/conf/web.xml</code>.  This
+  section describes the configurable components that may be stored there.</p>
+
+  <p><strong>FIXME</strong> - Complete the description of default servlets,
+  default mappings, default MIME types, and so on.</p>
+
+</section>
+
+
+<section name="Engine">
+
+  <p>An <em>Engine</em> is the representation of the entire Catalina
+  servlet container, and processes all requests for all of the associated
+  virtual hosts and web applications.</p>
+
+  <p>The standard component implementing an <em>Engine</em> is
+  <code>org.apache.catalina.core.StandardEngine</code>.  It supports the
+  following configurable properties:</p>
+  <ul>
+  <li><code>debug</code> - Debugging detail level.  [0]</li>
+  <li><code>defaultHost</code> - Name of the <em>Host</em> to which requests
+      will be directed if the requested host is unknown.  [localhost]</li>
+  <li><code>name</code> - Logical name of this engine. [Tomcat Stand-Alone]
+      </li>
+  </ul>
+
+  <p>Each <em>Engine</em> is owned by a parent <em>Service</em>, and is
+  associated with:</p>
+  <ul>
+  <li>An optional <em>Access Logger</em> that logs all requests processed
+      by the entire container.</li>
+  <li>A <em>Default Context</em>, representing default properties of a
+      <em>Context</em> for automatically deployed applications for all
+      associated <em>Hosts</em> (unless overridden by a subordinate
+      component).</li>
+  <li>One or more <em>Hosts</em> representing individual virtual hosts
+      supported by this container.</li>
+  <li>A <em>Realm</em> used to provide authentication and access control
+      information for all virtual hosts and web applications (unless
+      overridden by a subordinate component).</li>
+  <li>Zero or more <em>Request Filters</em> used to limit access to the
+      entire container based on remote host name or IP address.</li>
+  </ul>
+
+</section>
+
+
+<section name="Environment Entry">
+
+  <p>An <em>Environment Entry</em> is the representation of a
+  <code>&lt;env-entry&gt;</code> element from a web application deployment
+  descriptor.  It will cause the creation of a corresponding entry in the
+  JNDI naming context provided to the corresponding <em>Context</em>.  The
+  following configurable properties are supported:</p>
+  <ul>
+  <li><code>description</code> - Description of this environment entry.</li>
+  <li><code>name</code> - Environment entry name (relative to the
+      <code>java:comp/env</code> context)</li>
+  <li><code>type</code> - Environment entry type (must be one of the fully
+      qualified Java classes listed in the servlet spec).</li>
+  <li><code>value</code> - Environment entry value (must be convertible from
+      String to the specified <code>type</code>.</li>
+  </ul>
+
+</section>
+
+
+<section name="Host">
+
+  <p>A <em>Host</em> is the representation of an individual virtual host,
+  which has a unique set of associated web applications.</p>
+
+  <p>The standard component implementing a <em>Host</em> is
+  <code>org.apache.catalina.core.StandardHost</code>.  It supports the
+  following configurable properties:</p>
+  <ul>
+  <li><code>aliases</code> - Zero or more DNS names that are also associated
+      with this host (for example, a particular host might be named
+      <code>www.mycompany.com</code> with an alias <code>company.com</code>).
+      </li>
+  <li><code>appBase</code> - Absolute or relative (to $CATALINA_HOME) path
+      to a directory from which web applications will be automatically
+      deployed.</li>
+  <li><code>debug</code> - Debugging detail level.  [0]</li>
+  <li><code>name</code> - DNS Name of the virtual host represented by this
+      object.</li>
+  <li><code>unpackWARs</code> - Should web application archive files
+      deployed by this virtual host be unpacked first?  [true]</li>
+  </ul>
+
+  <p>Each <em>Host</em> is owned by a parent <em>Engine</em>, and is
+  associated with:</p>
+  <ul>
+  <li>An optional <em>Access Logger</em> that logs all requests processed
+      by this virtual host.</li>
+  <li>One or more <em>Contexts</em> representing the web applications
+      operating on this <em>Host</em>.</li>
+  <li>A <em>Default Context</em> representing default <em>Context</em>
+      properties for web applications that are automatically deployed
+      by this <em>Host</em>.</li>
+  <li>A optional <em>Realm</em> used to provide authentication and access
+      control information for all web applications associated with this
+      virtual host (unless overridden by a subordinate component).</li>
+  </ul>
+
+  <p><strong>FIXME</strong> - Should we support configuration of the
+  User Web Applications functionality?</p>
+
+</section>
+
+
+<section name="JDBC Resource">
+
+  <p>A <em>JDBC Resources</em> represents a database connection pool (i.e.
+  an implementation of <code>javax.sql.DataSource</code> that will be
+  configured and made available in the JNDI naming context associated with
+  a web application.</p>
+
+  <p><strong>FIXME</strong> - properties of this administered object</p>
+
+</section>
+
+
+<section name="Loader">
+
+  <p>A <em>Loader</em> represents a web application class loader that will
+  be utilized to provide class loading services for a particular
+  <em>Context</em>.</p>
+
+  <p>The standard component implementing a <em>Loader</em> is
+  <code>org.apache.catalina.loader.StandardLoader</code>.  It supports
+  the following configurable properties:</p>
+  <ul>
+  <li><code>checkInterval</code> - Number of seconds between checks for
+      modified classes, if automatic reloading is enabled.  [15]</li>
+  <li><code>debug</code> - Debugging detail level.  [0]</li>
+  <li><code>reloadable</code> - Should this class loader check for modified
+      classes and initiate automatic reloads?  [Set automatically from the
+      <code>reloadable</code> property of the corresponding <em>Context</em>]
+      </li>
+  </ul>
+
+  <p>Each <em>Loader</em> is owned by a parent <em>Context</em>.</p>
+
+</section>
+
+
+<section name="Manager">
+
+  <p>A <em>Manager</em> represents a session manager that will be associated
+  with a particular web application.  <strong>FIXME</strong> - Add support
+  for advanced session managers and their associated Stores.</p>
+
+  <p>The standard component implementing a <em>Manager</em> is
+  <code>org.apache.catalina.session.StandardManager</code>.  It supports
+  the following configurable properties:</p>
+  <ul>
+  <li><code>checkInterval</code> - Number of seconds between checks for
+      expired sessions.  [60]</li>
+  <li><code>debug</code> - Debugging detail level.  [0]</li>
+  <li><code>entropy</code> - String initialization parameter used to increase
+      the entropy (initial randomness) of the random number generator used to
+      create session identifiers.  [Inferred from engine, host, and context]
+      </li>
+  <li><code>maxActiveSessions</code> - The maximum number of active sessions
+      that are allowed, or -1 for no limit.  [-1]</li>
+  </ul>
+
+  <p>Each <em>Manager</em> is owned by a parent <em>Context</em>.</p>
+
+</section>
+
+
+<section name="Realm">
+
+  <p>A <em>Realm</em> represents a "database" of information about authorized
+  users, their passwords, and the security roles assigned to them.  This will
+  be used by the container in the implementation of container-managed security
+  in accordance with the Servlet Specification.  Several alternative
+  implementations are supported.</p>
+
+  <p><code>org.apache.catalina.realm.MemoryRealm</code> initializes its user
+  information from a simple XML file at startup time.  If changes are made
+  to the information in this file, the corresponding web applications using
+  it must be restarted for the changes to take effect.  It supports the
+  following configurable properties:</p>
+  <ul>
+  <li><code>debug</code> - Debugging detail level.  [0]</li>
+  <li><code>pathname</code> - Absolute or relative (to $CATALINA_HOME) path to
+      the XML file containing our user information.  [conf/tomcat-users.xml]
+      </li>
+  </ul>
+
+  <p><code>org.apache.catalina.realm.JDBCRealm</code> uses a relational
+  database (accessed via JDBC APIs) to contain the user information.  Changes
+  in the contents of this database take effect immediately; however, the roles
+  assigned to a particular user are calculated only when the user initially
+  logs on (and not per request).  The following configurable properties
+  are supported:</p>
+  <ul>
+  <li><code>connectionName</code> - Database username to use when establishing
+      a JDBC connection.</li>
+  <li><code>connectionPassword</code> - Database password to use when
+      establishing a JDBC connection.</li>
+  <li><code>connectionURL</code> - Connection URL to use when establishing
+      a JDBC connection.</li>
+  <li><code>debug</code> - Debugging detail level.  [0]</li>
+  <li><code>digest</code> - Name of the <code>MessageDigest</code> algorithm
+      used to encode passwords in the database, or a zero-length string for
+      no encoding.  [Zero-length String]</li>
+  <li><code>driverName</code> - Fully qualified Java class name of the JDBC
+      driver to be utilized.</li>
+  <li><code>roleNameCol</code> - Name of the column, in the User Roles table,
+      which contains the role name.</li>
+  <li><code>userCredCol</code> - Name of the column, in the Users table,
+      which contains the password (encrypted or unencrypted).</li>
+  <li><code>userNameCol</code> - Name of the column, in both the Users and
+      User Roles tables, that contains the username.</li>
+  <li><code>userRoleTable</code> - Name of the User Roles table, which contains
+      one row per security role assigned to a particular user.  This table must
+      contain the columns specified by the <code>userNameCol</code> and
+      <code>roleNameCol</code> properties.</li>
+  <li><code>userTable</code> - Name of the Users table, which contains one row
+      per authorized user.  This table must contain the columns specified by
+      the <code>userNameCol</code> and <code>userCredCol</code> properties.
+      </li>
+  </ul>
+
+  <p><strong>FIXME</strong> - Should we provide mechanisms to edit the contents
+  of a "tomcat-users.xml" file through the admin applications?</p>
+
+  <p>Each <em>Realm</em> is owned by a parent <em>Engine</em>, <em>Host</em>,
+  or <em>Context</em>.</p>
+
+</section>
+
+
+<section name="Request Filter">
+
+  <p><strong>FIXME</strong> - complete this entry</p>
+
+</section>
+
+
+<section name="Server">
+
+  <p><strong>FIXME</strong> - complete this entry</p>
+
+</section>
+
+
+<section name="Service">
+
+  <p><strong>FIXME</strong> - complete this entry</p>
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/fs-admin-opers.xml b/container/webapps/docs/funcspecs/fs-admin-opers.xml
new file mode 100644
index 0000000..658bc29
--- /dev/null
+++ b/container/webapps/docs/funcspecs/fs-admin-opers.xml
@@ -0,0 +1,324 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="fs-admin-opers.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig McClanahan</author>
+    <title>Administrative Apps - Supported Operations</title>
+    <revision>$Id$</revision>
+  </properties>
+
+<body>
+
+
+<section name="Supported Operations Overview">
+
+<p>This document defines the <em>Supported Operations</em> that may
+be performed against the <a href="fs-admin-objects.html">Administered
+Objects</a> that are supported by Tomcat 5 administrative applications.
+Not all operations are required to be available through every administrative
+application that is implemented.  However, if a given operation is available,
+it should operate consistently with the descriptions found here.</p>
+
+<p>Supported Operations are described for the following Administered
+Objects:</p>
+<ul>
+<li><a href="#Access Logger">Access Logger</a></li>
+<li><a href="#Connector">Connector</a></li>
+<li><a href="#Context">Context</a></li>
+<li><a href="#Default Context">Default Context</a></li>
+<li><a href="#Engine">Engine</a></li>
+<li><a href="#Environment Entry">Environment Entry</a></li>
+<li><a href="#Host">Host</a></li>
+<li><a href="#JDBC Resource">JDBC Resource</a></li>
+<li><a href="#Loader">Loader</a></li>
+<li><a href="#Manager">Manager</a></li>
+<li><a href="#Realm">Realm</a></li>
+<li><a href="#Request Filter">Request Filter</a></li>
+<li><a href="#Server">Server</a></li>
+<li><a href="#Service">Service</a></li>
+</ul>
+
+</section>
+
+
+<section name="Access Logger">
+
+  <p>From the perspective of a particular <em>Access Logger</em>, it shall
+  be possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Engine</em>, <em>Host</em>, or
+      <em>Context</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  </ul>
+
+</section>
+
+
+<section name="Connector">
+
+  <p>From the perspective of a particular <em>Connector</em>, it shall be
+  possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Service</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  </ul>
+
+</section>
+
+
+<section name="Context">
+
+  <p>From the perspective of a particular <em>Context</em>, it shall be
+  possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Host</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  <li>Create and configure a new <em>Access Logger</em> associated
+      with this object.</li>
+  <li>Edit the configurable properties of the associated <em>Access
+      Logger</em>.</li>
+  <li>Remove the associated <em>Access Logger</em>.</li>
+  <li>Create and configure a new <em>Environment Entry</em> associated
+      with this object.</li>
+  <li>Select and edit the configurable properties of an associated
+      <em>Environment Entry</em>.</li>
+  <li>Remove an associated <em>Environment Entry</em>.</li>
+  <li>Create and configure a new <em>JDBC Resource</em> associated
+      with this object.</li>
+  <li>Select and edit the configurable properties of an associated
+      <em>JDBC Resource</em>.</li>
+  <li>Remove an associated <em>JDBC Resource</em>.</li>
+  <li>Create and configure a new <em>Loader</em> associated with
+      this object.</li>
+  <li>Edit the configurable properties of the associated <em>Loader</em>.</li>
+  <li>Remove the associated <em>Loader</em>.</li>
+  <li>Create and configure a new <em>Manager</em> associated with
+      this object.</li>
+  <li>Edit the configurable properties of the associated <em>Manager</em>.</li>
+  <li>Remove the associated <em>Manager</em>.</li>
+  <li>Create and configure a new <em>Realm</em> associated with
+      this object.</li>
+  <li>Edit the configurable properties of the associated <em>Realm</em>.</li>
+  <li>Remove the associated <em>Realm</em>.</li>
+  <li>Create and configure a new <em>Request Filter</em> associated with
+      this object.</li>
+  <li>Select and edit the configurable properties of an
+      associated <em>Request Filter</em></li>
+  <li>Remove an associated <em>Request Filter</em>.</li>
+  </ul>
+
+</section>
+
+
+<section name="Default Context">
+
+  <p>From the perspective of a particular <em>Default Context</em>, it shall
+  be possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Engine</em> or <em>Host</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  <li>Create and configure a new <em>Environment Entry</em> associated
+      with this object.</li>
+  <li>Select and edit the configurable properties of an associated
+      <em>Environment Entry</em>.</li>
+  <li>Remove an associated <em>Environment Entry</em>.</li>
+  <li>Create and configure a new <em>JDBC Resource</em> associated
+      with this object.</li>
+  <li>Select and edit the configurable properties of an associated
+      <em>JDBC Resource</em>.</li>
+  <li>Remove an associated <em>JDBC Resource</em>.</li>
+  </ul>
+
+</section>
+
+
+<section name="Engine">
+
+  <p>From the perspective of a particular <em>Engine</em>, it shall be
+  possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Service</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  <li>Create and configure a new <em>Access Logger</em> associated
+      with this object.</li>
+  <li>Edit the configurable properties of the associated <em>Access
+      Logger</em>.</li>
+  <li>Remove the associated <em>Access Logger</em>.</li>
+  <li>Create and configure a new <em>Default Context</em> associated
+      with this object.</li>
+  <li>Edit the configurable properties of the associated <em>Default
+      Context</em>.</li>
+  <li>Remove the associated <em>Default Context</em>.</li>
+  <li>Create and configure a new <em>Host</em> associated with
+      this object.</li>
+  <li>Select and edit the configurable properties of an
+      associated <em>Host</em>.</li>
+  <li>Remove an associated <em>Host</em>.</li>
+  <li>Create and configure a new <em>Realm</em> associated with
+      this object.</li>
+  <li>Edit the configurable properties of the associated <em>Realm</em>.</li>
+  <li>Remove the associated <em>Realm</em>.</li>
+  <li>Create and configure a new <em>Request Filter</em> associated with
+      this object.</li>
+  <li>Select and edit the configurable properties of an
+      associated <em>Request Filter</em></li>
+  <li>Remove an associated <em>Request Filter</em>.</li>
+  </ul>
+
+</section>
+
+
+<section name="Environment Entry">
+
+  <p>From the perspective of a particular <em>Environment Entry</em>, it shall
+  be possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Context</em> or <em>Default Context</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  </ul>
+
+</section>
+
+
+<section name="Host">
+
+  <p>From the perspective of a particular <em>Host</em>, it shall be
+  possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Engine</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  <li>Create and configure a new <em>Access Logger</em> associated
+      with this object.</li>
+  <li>Edit the configurable properties of the associated <em>Access
+      Logger</em>.</li>
+  <li>Remove the associated <em>Access Logger</em>.</li>
+  <li>Create and configure a new <em>Context</em> associated with
+      this object.</li>
+  <li>Select and edit the configurable properties of an associated
+      <em>Context</em>.</li>
+  <li>Remove an associated <em>Context</em>.</li>
+  <li>Create and configure a new <em>Default Context</em> associated
+      with this object.</li>
+  <li>Edit the configurable properties of the associated <em>Default
+      Context</em>.</li>
+  <li>Remove the associated <em>Default Context</em>.</li>
+  <li>Create and configure a new <em>Realm</em> associated with
+      this object.</li>
+  <li>Edit the configurable properties of the associated <em>Realm</em>.</li>
+  <li>Remove the associated <em>Realm</em>.</li>
+  <li>Create and configure a new <em>Request Filter</em> associated with
+      this object.</li>
+  <li>Select and edit the configurable properties of an
+      associated <em>Request Filter</em></li>
+  <li>Remove an associated <em>Request Filter</em>.</li>
+  </ul>
+
+</section>
+
+
+<section name="JDBC Resource">
+
+  <p>From the perspective of a particular <em>JDBC Resource</em>, it shall
+  be possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Context</em> or <em>Default Context</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  </ul>
+
+</section>
+
+
+<section name="Loader">
+
+  <p>From the perspective of a particular <em>Loader</em>, it shall
+  be possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Context</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  </ul>
+
+</section>
+
+
+<section name="Manager">
+
+  <p>From the perspective of a particular <em>Manager</em>, it shall
+  be possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Context</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  </ul>
+
+</section>
+
+
+<section name="Realm">
+
+  <p>From the perspective of a particular <em>Realm</em>, it shall
+  be possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Engine</em>, <em>Host</em>, or
+      <em>Context</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  </ul>
+
+</section>
+
+
+<section name="Request Filter">
+
+  <p>From the perspective of a particular <em>Request Filter</em>, it shall
+  be possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Engine</em>, <em>Host</em>, or
+      <em>Context</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  </ul>
+
+</section>
+
+
+<section name="Server">
+
+  <p>From the perspective of the overall <em>Server</em>, it shall be
+  possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Edit the configurable properties of this object.</li>
+  <li>Create and configure a new <em>Service</em> associated with
+      this object.</li>
+  <li>Select and edit the configurable properties of an associated
+      <em>Service</em>.</li>
+  </ul>
+
+</section>
+
+
+<section name="Service">
+
+  <p>From the perspective of a particular <em>Service</em>, it shall be
+  possible to perform the following administrative operations:</p>
+  <ul>
+  <li>Navigate to the owning <em>Server</em>.</li>
+  <li>Edit the configurable properties of this object.</li>
+  <li>Create and configure a new <em>Connector</em> associated with
+      this object.</li>
+  <li>Select and edit the configurable properties of an associated
+      <em>Connector</em>.</li>
+  <li>Remove an associated <em>Connector</em>.</li>
+  <li>Create and configure a new <em>Engine</em> associated with
+      this object.</li>
+  <li>Edit the configurable properties of the associated <em>Engine</em>.</li>
+  <li>Remove the associated <em>Engine</em>.</li>
+  </ul>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/fs-default.xml b/container/webapps/docs/funcspecs/fs-default.xml
new file mode 100644
index 0000000..23d6163
--- /dev/null
+++ b/container/webapps/docs/funcspecs/fs-default.xml
@@ -0,0 +1,252 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="fs-default.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig McClanahan</author>
+    <title>Default Servlet</title>
+    <revision>$Id$</revision>
+  </properties>
+
+<body>
+
+
+<section name="Overview">
+
+
+  <subsection name="Introduction">
+
+    <p>The purpose of the <strong>Default Servlet</strong> is to serve
+    static resources of a web application in response to client requests.
+    As the name implies, it is generally configured as the "default"
+    servlet for a web application, by being mapped to a URL pattern "/".</p>
+
+  </subsection>
+
+
+  <subsection name="External Specifications">
+
+    <p>The following external specifications have provisions which
+    partially define the correct behavior of the default servlet:</p>
+    <ul>
+    <li><a href="http://java.sun.com/products/servlet/download.html">
+        Servlet Specification</a> (Version 2.3 PFD2)</li>
+    <li><a href="http://www.rfc-editor.org/rfc/rfc2046.txt">Multipurpose
+        Internet Mail Extensions (MIME) Part Two: Media Types</a></li>
+    <li><a href="http://www.rfc-editor.org/rfc/rfc2616.txt">Hypertext
+        Transfer Protocol -- HTTP/1.1</a></li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Implementation Requirements">
+
+    <p>The implementation of this functionality shall conform to the
+    following requirements:</p>
+    <ul>
+    <li>Must be implemented as a servlet.</li>
+    <li>Must support configurable parameters for debugging detail level,
+        input buffer size, output buffer size, whether or not to produce
+        directory listings when no welcome file is present, and whether or not
+        modifications are supported via DELETE and PUT.</li>
+    <li>Log debugging and operational messages (suitably internationalized)
+        via the <code>getServletContext().log()</code> method.</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Dependencies">
+
+
+  <subsection name="Environmental Dependencies">
+
+    <p>The following environmental dependencies must be met in order for
+    the default servlet to operate correctly:</p>
+    <ul>
+    <li>The default servlet must be registered in the application deployment
+        descriptor (or the default deployment descriptor in file
+        <code>$CATALINA_HOME/conf/web.xml</code>) using a "default servlet"
+        servlet mapping, signified by URL pattern "/".</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Container Dependencies">
+
+    <p>Correct operation of the default servlet depends on the following
+    specific features of the surrounding container:</p>
+    <ul>
+    <li>The container shall provide a servlet context attribute that
+        lists the welcome file names that have been defined for this
+        web application.</li>
+    <li>The container shall provide a servlet context attribute that
+        contains a <code>javax.naming.directory.DirContext</code>
+        implementation representing the static resources of this
+        web application.</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Functionality">
+
+
+  <subsection name="Initialization Functionality">
+
+    <p>The following processing must be performed when the <code>init()</code>
+    method of the invoker servlet is called:</p>
+    <ul>
+    <li>Process and sanity check configuration parameters.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Per-Request Functionality">
+
+
+    <p>For all HTTP request methods, the resource path is determined from
+    the path information provided to this request, either as request attribute
+    <code>javax.servlet.include.path_info</code> (for a request dispatcher
+    access to a static resource) or by calling
+    <code>request.getPathInfo()</code> directly.</p>
+
+    <p>On each HTTP DELETE request processed by this servlet, the following
+    processing shall be performed:</p>
+    <ul>
+    <li>If modifications to the static resources are not allowed (set by a
+        configuration parameter), return HTTP status 403 (forbidden).</li>
+    <li>If an attempt is made to delete a resource from <code>/META-INF</code>
+        or <code>/WEB-INF</code>, return HTTP status 403 (forbidden).</li>
+    <li>If the requested resource does not exist, return HTTP status 404
+        (not found)</li>
+    <li>Unbind the resource from the directory context containing the
+        static resources for this web application.  If successful, return
+        HTTP status 204 (no content).  Otherwise, return HTTP status 405
+        (method not allowed).</li>
+    </ul>
+
+
+    <p>On each HTTP GET request processed by this servlet, the following
+    processing shall be performed:</p>
+    <ul>
+    <li>If the request is for a resource under <code>/META-INF</code> or
+        <code>/WEB-INF</code>, return HTTP status 404 (not found).</li>
+    <li>If the requested resource does not exist, return HTTP status 404
+        (not found).</li>
+    <li>If the requested resource is not a directory, but the resource
+        path ends in "/" or "\", return HTTP status 404 (not found).</li>
+    <li>If the requested resource is a directory:
+        <ul>
+        <li>If the request path does not end with "/", redirect to a
+            corresponding path with "/" appended so that relative references
+            in welcome files are resolved correctly.</li>
+        <li>If one of the specified welcome files exists, redirect to the
+            path for that welcome file so that it will be served explicitly.
+            </li>
+        </ul></li>
+    <li>If the request being processed contains an <code>If-Range</code>
+        header, perform the processing described in the HTTP/1.1 specification
+        to determine whether the client's information is up to date.</li>
+    <li>Determine the content type of the response, by looking up the
+        corresponding MIME type in our servlet context.</li>
+    <li>If the requested resource is a directory:
+        <ul>
+        <li>If directory listings are suppressed, return HTTP status 404
+            (not found).</li>
+        <li>Set the content type to <code>text/html</code>.</li>
+        </ul></li>
+    <li>Determine the range(s) to be returned, based on the existence of
+        any <code>If-Range</code> and <code>Range</code> headers.</li>
+    <li>If the requested resource is a directory, include an <code>ETag</code>
+        header in the response, with the value calculated based on the content
+        of the directory.</li>
+    <li>Include a <code>Last-Modified</code> header in the response documenting
+        the date/time that the resource was last modified.</li>
+    <li>Unless we are processing a HEAD request, include the appropriate
+        content (or content ranges) in the response.</li>
+    </ul>
+
+    <p>On each HTTP HEAD request processed by this servlet, the following
+    processing shall be performed:</p>
+    <ul>
+    <li>Processed identically to an HTTP GET request, except that the data
+        content is not transmitted after the headers.</li>
+    </ul>
+
+    <p>On each HTTP POST request processed by this servlet, the following
+    processing shall be performed:</p>
+    <ul>
+    <li>Processed identically to an HTTP GET request.</li>
+    </ul>
+
+
+    <p>On each HTTP PUT request processed by this servlet, the following
+    processing shall be perfomred:</p>
+    <ul>
+    <li>If modifications to the static resources are not allowed (set by a
+        configuration parameter), return HTTP status 403 (forbidden).</li>
+    <li>If an attempt is made to delete a resource from <code>/META-INF</code>
+        or <code>/WEB-INF</code>, return HTTP status 403 (forbidden).</li>
+    <li>Create a new resource from the body of this request.</li>
+    <li>Bind or rebind the specified path to the new resource (depending on
+        whether it currently exists or not).  Return HTTP status as follows:
+        <ul>
+        <li>If binding was unsuccessful, return HTTP status 409 (conflict).
+            </li>
+        <li>If binding was successful and the resource did not previously
+            exist, return HTTP status 201 (created).</li>
+        <li>If binding was successful and the resource previously existed,
+            return HTTP status 204 (no content).</li>
+        </ul></li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Finalization Functionality">
+
+    <p>No specific processing is required when the <code>destroy()</code>
+    method is called:</p>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Testable Assertions">
+
+  <p>In addition the the assertions implied by the functionality requirements
+  listed above, the following additional assertions shall be tested to
+  validate the behavior of the invoker servlet:</p>
+  <ul>
+  <li>Requests for resources that do not exist in the web application must
+      return HTTP status 404 (not found).</li>
+  <li>The default servlet must operate identically for web applications that
+      are run out of a WAR file directly, or from an unpacked directory
+      structure.</li>
+  <li>If the web application is running out of an unpacked directory
+      structure, the default servlet must recognize cases where the resource
+      has been updated through external means.</li>
+  </ul>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/fs-invoker.xml b/container/webapps/docs/funcspecs/fs-invoker.xml
new file mode 100644
index 0000000..6a27407
--- /dev/null
+++ b/container/webapps/docs/funcspecs/fs-invoker.xml
@@ -0,0 +1,247 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="fs-invoker.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig McClanahan</author>
+    <title>Invoker Servlet</title>
+    <revision>$Id$</revision>
+  </properties>
+
+<body>
+
+
+<section name="Overview">
+
+
+  <subsection name="Introduction">
+
+    <p>The purpose of the <strong>Invoker Servlet</strong> is to allow a
+    web application to dynamically register new <em>servlet definitions</em>
+    that correspond with a <code>&lt;servlet&gt;</code> element in the
+    <code>/WEB-INF/web.xml</code> deployment descriptor, and execute
+    requests utilizing the new servlet definitions.  From the perspective
+    of the newly registered servlets, all servlet lifecycle requirements
+    of the Servlet Specification (such as calling <code>init()</code> and
+    <code>destroy()</code> at the correct times) will be respected.</p>
+
+  </subsection>
+
+
+  <subsection name="External Specifications">
+
+    <p>I do not know of any formal specification of the behavior of an
+    invoker servlet that is publicly available.  Anyone know of one?</p>
+
+  </subsection>
+
+
+  <subsection name="Implementation Requirements">
+
+    <p>The implementation of this functionality shall conform to the
+    following requirements:</p>
+    <ul>
+    <li>Implemented as a servlet.</li>
+    <li>Exist in the <code>org.apache.catalina.servlets</code> package
+        so that it can be loaded by the Catalina class loader.</li>
+    <li>Implement the <code>org.apache.catalina.ContainerServlet</code>
+        interface, so that it gains knowledge of the <code>Wrapper</code>
+        that is responsible for itself and, therefore, access to other
+        internal Catalina components.</li>
+    <li>Support a configurable debugging detail level.</li>
+    <li>Log debugging and operational messages (suitably internationalized)
+        via the <code>getServletContext().log()</code> method.</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Dependencies">
+
+
+  <subsection name="Environmental Dependencies">
+
+    <p>The following environmental dependencies must be met in order for
+    the Invoker servlet to operate correctly:</p>
+    <ul>
+    <li>The invoker servlet must be registered in the application deployment
+        descriptor (or the default deployment descriptor in file
+        <code>$CATALINA_HOME/conf/web.xml</code>) using a "path mapped"
+        servlet mapping.  The historical default mapping is to URL pattern
+        "<code>/servlet/*</code>", although the invoker servlet must operate
+        correctly with an arbitrary mapping.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Container Dependencies">
+
+    <p>Correct operation of the invoker servlet depends on the following
+    specific features of the surrounding container:</p>
+    <ul>
+    <li>Correct support for the <code>ContainerServlet</code> interface,
+        including calling <code>setWrapper()</code> <strong>before</strong>
+        the <code>init()</code> method of the invoker servlet is called.</li>
+    <li>The web application class loader must be stored as the context
+        class loader of the request processing thread.</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Functionality">
+
+
+  <subsection name="Initialization Functionality">
+
+    <p>The following processing must be performed when the <code>init()</code>
+    method of the invoker servlet is called:</p>
+    <ul>
+    <li>Ensure that the container has called <code>setWrapper()</code>.  If
+        not, throw a permanent <code>UnavailableException</code>.</li>
+    <li>Look up and cache the <code>Context</code> that corresponds to our
+        <code>Wrapper</code>.  This is the component with which new servlet
+        definitions and mappings will be registered.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Per-Request Functionality">
+
+    <p>On each request, the following processing shall be performed:</p>
+    <ol>
+    <li>Calculate the <code>{ServletPath}</code> for this request, either from
+        request attribute <code>javax.servlet.include.servlet_path</code> or
+        by calling <code>request.getServletPath()</code>.</li>
+    <li>Calculate the <code>{PathInfo}</code> for this request, either from
+        request attribute <code>javax.servlet.include.path_info</code> or
+        by calling <code>request.getPathInfo()</code>.  If the calculated
+        <code>{PathInfo}</code> is null, return HTTP status 400
+        (bad request).</li>
+    <li>Parse the calculated <code>{PathInfo}</code> value as follows:
+        <ol>
+        <li>Ignore the leading slash character.</li>
+        <li>Accumulate characters up to the next '/' (if any) as the
+            <code>{ServletSelector}</code>.</li>
+        <li>If a '/' was encountered, accumulate all characters from that
+            slash (inclusive) to the end of the string as
+            <code>{PathRemainder}</code>.  If no slash was encountered,
+            set <code>{PathRemainder}</code> to a zero-length string.</li>
+        </ol></li>
+    <li>Determine whether <code>{ServletSelector}</code> is the name of an
+        existing servlet definition, and process it as follows:
+        <ol>
+        <li>Ask our associated <code>Context</code> to find and return a
+            child <code>Wrapper</code> named <code>{ServletSelector}</code>.
+            </li>
+        <li>If there is no such child, skip to the next major step.</li>
+        <li>Register a new servlet mapping for this <code>Wrapper</code>,
+            using a URL pattern calculated as follows:
+            <code>{ServletPath}</code> + "/" + <code>{ServletSelector}</code>
+            + "/*"</li>
+        <li>Create a request dispatcher using a path calculated as follows:
+            <code>{ServletPath}</code> + "/" + <code>{ServletSelector}</code>
+            + <code>{PathRemainder}</code></li>
+        <li>Forward this request to the created request dispatcher, and
+            exit from this request.</li>
+        </ol></li>
+    <li>Assume that <code>{ServletSelector}</code> is the fully qualified
+        name of a Java class that implements <code>javax.servlet.Servlet</code>
+        and process it as follows:
+        <ol>
+        <li>Synthesize a new <code>{ServletName}</code> for the servlet
+            definition that will be created.</li>
+        <li>If there is already a child <code>Wrapper</code> associated with
+            this name, return HTTP status 500 (internal server error), because
+            a mapping should have already been created for this servlet.</li>
+        <li>Attempt to load a class named <code>{ServletSelector}</code> from
+            the web application class loader (i.e. the context class loader
+            for our current thread).  If this fails, return HTTP status 404
+            (not found).</li>
+        <li>Instantiate an instance of this class.  If an error occurs,
+            return HTTP status 404 (not found).</li>
+        <li>If this class does not implement the
+            <code>javax.servlet.Servlet</code> interface, return HTTP status
+            404 (not found).</li>
+        <li>Create and register a new <code>Wrapper</code> child with our
+            <code>Context</code>, under name <code>{ServletName}</code>.</li>
+        <li>Register a new servlet mapping for this <code>Wrapper</code>,
+            using a URL pattern calculated as follows:
+            <code>{ServletPath}</code> + "/" + <code>{ServletSelector}</code>
+            + "/*"</li>
+        <li>Create a request dispatcher using a path calculated as follows:
+            <code>{ServletPath}</code> + "/" + <code>{ServletSelector}</code>
+            + <code>{PathRemainder}</code></li>
+        <li>Forward this request to the created request dispatcher, and
+            exit from this request.</li>
+        </ol></li>
+    </ol>
+
+  </subsection>
+
+
+  <subsection name="Finalization Functionality">
+
+    <p>No specific processing is required when the <code>destroy()</code>
+    method is called:</p>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Testable Assertions">
+
+  <p>In addition the the assertions implied by the functionality requirements
+  listed above, the following additional assertions shall be tested to
+  validate the behavior of the invoker servlet:</p>
+  <ul>
+  <li>It is possible to access an existing servlet definition by name
+      through the invoker.  The existing servlet definition can include
+      either a <code>&lt;servlet-class&gt;</code> or
+      <code>&lt;jsp-file&gt;</code> subelement.</li>
+  <li>When an existing servlet definition is accessed by name, the request
+      will be ultimately processed by the same servlet instance that would
+      have processed it had a mapping to that servlet definition been used
+      on the request directly.</li>
+  <li>It is possible to access an anonymous servlet by class name
+      through the invoker.</li>
+  <li>When an anonymous servlet is accessed, the servlet instance is processed
+      according to the lifecycle requirements of the Servlet Specification.
+      </li>
+  <li>When an anonymous servlet is accessed, the servlet instance receives
+      a <code>ServletConfig</code> instance with no servlet initialization
+      parameters.</li>
+  <li>It is possible to utilize the invoker servlet via a direct request.</li>
+  <li>It is possible to utilize the invoker servlet via a call to
+      <code>RequestDispatcher.forward()</code>, or the corresponding
+      <code>&lt;jsp:forward&gt;</code> tag in a JSP page.</li>
+  <li>It is possible to utilize the invoker servlet via a call to
+      <code>RequestDispatcher.include()</code>, or the corresponding
+      <code>&lt;jsp:include&gt;</code> tag in a JSP page.</li>
+  <li>It is possible to use any HTTP method (including GET and POST) that
+      is supported by the Servlet class that is ultimately executed.</li>
+  <li>The invoker servlet should never be asked to process a second or
+      subsequent request for the same <code>{ServletSelector}</code> (because
+      it will have registered an appropriate servlet mapping.</li>
+  </ul>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/fs-jdbc-realm.xml b/container/webapps/docs/funcspecs/fs-jdbc-realm.xml
new file mode 100644
index 0000000..a4acd3b
--- /dev/null
+++ b/container/webapps/docs/funcspecs/fs-jdbc-realm.xml
@@ -0,0 +1,248 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="fs-jdbc-realm.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig McClanahan</author>
+    <title>JDBCRealm</title>
+    <revision>$Id$</revision>
+  </properties>
+
+<body>
+
+
+<section name="Overview">
+
+
+  <subsection name="Introduction">
+
+    <p>The purpose of the <strong>JDBCRealm</strong> implementation is to
+    provide a mechanism by which Tomcat 5 can acquire information needed
+    to authenticate web application users, and define their security roles,
+    from a relational database accessed via JDBC APIs.  For integration
+    with Catalina, the resulting class(es) must implement the
+    <code>org.apache.catalina.Realm</code> interface.</p>
+
+    <p>This specification reflects a combination of functionality that is
+    already present in the <code>org.apache.catalina.realm.JDBCRealm</code>
+    class, as well as requirements for enhancements that have been
+    discussed.  Where appropriate, requirements statements are marked
+    <em>[Current]</em> and <em>[Requested]</em> to distinguish them.</p>
+
+    <p>The current status of this functional specification is
+    <strong>PROPOSED</strong>.  It has not yet been discussed and
+    agreed to on the TOMCAT-DEV mailing list.</p>
+
+  </subsection>
+
+
+  <subsection name="External Specifications">
+
+    <p>The implementation of this functionality depends on the following
+    external specifications:</p>
+    <ul>
+    <li><a href="http://java.sun.com/products/jdbc/">Java Database
+        Connectivity</a> (version 2.0 or later)</li>
+    <li><a href="http://java.sun.com/products/jdbc/">Java Database
+        Connectivity Optional Package</a> (version 2.0 or later)</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Implementation Requirements">
+
+    <p>The implementation of this functionality shall conform to the
+    following requirements:</p>
+    <ul>
+    <li>Be realized in one or more implementation classes.</li>
+    <li>Implement the <code>org.apache.catalina.Realm</code> interface.
+        [Current]</li>
+    <li>Implement the <code>org.apache.catalina.Lifecycle</code>
+        interface.  [Current]</li>
+    <li>Subclass the <code>org.apache.catalina.realm.RealmBase</code>
+        base class.</li>
+    <li>Live in the <code>org.apache.catalina.realm</code> package.
+        [Current]</li>
+    <li>Support a configurable debugging detail level. [Current]</li>
+    <li>Log debugging and operational messages (suitably internationalized)
+        via the <code>getContainer().log()</code> method. [Current]</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Dependencies">
+
+
+  <subsection name="Environmental Dependencies">
+
+    <p>The following environmental dependencies must be met in order for
+    JDBCRealm to operate correctly:</p>
+    <ul>
+    <li>The desire to utilize JDBCRealm must be registered in
+        <code>$CATALINA_HOME/conf/server.xml</code>, in a
+        <code>&lt;Realm&gt;</code> element that is nested inside a
+        corresponding <code>&lt;Engine&gt;</code>, <code>&lt;Host&gt;</code>,
+        or <code>&lt;Context&gt;</code> element.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Container Dependencies">
+
+    <p>Correct operation of JDBCRealm depends on the following
+    specific features of the surrounding container:</p>
+    <ul>
+    <li>Interactions with <code>JDBCRealm</code> will be initiated by
+        the appropriate <code>Authenticator</code> implementation, based
+        on the login method that is selected.</li>
+    <li><code>JDBCRealm</code> must have the JDBC standard API classes
+        available to it.  For a JDK 1.2 or later container, these APIs
+        are included in the standard platform.</li>
+    <li>When connection pooling is implemented, <code>JDBCRealm</code>
+        must have the JDBC Optional Package (version 2.0 or later) APIs
+        available to it.  This library is available as a separate
+        download (and will be included in Tomcat binary distributions).</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Functionality">
+
+
+  <subsection name="Overview of Operation">
+
+    <p>The main purpose of <code>JDBCRealm</code> is to allow Catalina to
+    authenticate users, and look up the corresponding security roles, from
+    the information found in a relational database accessed via JDBC APIs.
+    For maximum flexibility, the details of how this is done (for example,
+    the names of the required tables and columns) should be configurable.</p>
+
+    <p>Each time that Catalina needs to authenticate a user, it will call
+    the <code>authenticate()</code> method of this Realm implementation,
+    passing the username and password that were specified by the user.  If
+    we find the user in the database (and match on the password), we accumulate
+    all of the security roles that are defined for this user, and create a
+    new <code>GenericPrincipal</code> object to be returned.  If the user
+    is not authenticated, we return <code>null</code> instead.  The
+    <code>GenericUser</code> object caches the set of security roles that
+    were owned by this user at the time of authentication, so that calls to
+    <code>isUserInRole()</code> can be answered without going back to the
+    database every time.</p>
+
+  </subsection>
+
+
+  <subsection name="Detailed Functional Requirements">
+
+
+    <h3>Configurable Properties</h3>
+
+    <p>The implementation shall support the following properties
+    that can be configured with JavaBeans property setters:</p>
+    <ul>
+    <li>Configuration parameters defining the JDBC driver to use, the
+        database connection URL to be accessed, and the username/password
+        to use for logging in. [Current]</li>
+    <li>Configuration parameters describing the connection pool to be
+        created to support simultaneous authentications. [Requested]</li>
+    <li>Name of the tables to be searched for users and roles. [Current]</li>
+    <li>Name of the columns to be used for usernames, passwords, and
+        role names.  [Current]</li>
+    </ul>
+
+    <h3>Lifecycle Functionality</h3>
+
+    <p>The following processing must be performed when the <code>start()</code>
+    method is called:</p>
+    <ul>
+    <li>Establish a connection to the configured database, using the
+        configured username and password.  [Current]</li>
+    <li>Configure and establish a connection pool of connections to the
+        database.  [Requested]</li>
+    </ul>
+
+    <p>The following processing must be performed when the <code>stop()</code>
+    method is called:</p>
+    <ul>
+    <li>Close any opened connections to the database.</li>
+    </ul>
+
+
+    <h3>Method authenticate() Functionality</h3>
+
+    <p>When <code>authenticate()</code> is called, the following processing
+    is required:</p>
+    <ul>
+    <li>Acquire the one and only connection [Current] or acquire a connection
+        from the connection pool [Requested].</li>
+    <li>Select the one and only row from the user's table for this user,
+        and retrieve the corresponding password column.  If zero rows (or
+        more than one row) are found, return <code>null</code>.</li>
+    <li>Authenticate the user by comparing the (possibly encrypted) password
+        value that was received against the password presented by the user.
+        If there is no match, return <code>null</code>.</li>
+    <li>Acquire a <code>List</code> of the security roles assigned to the
+        authenticated user by selecting from the roles table.</li>
+    <li>Construct a new instance of class
+        <code>org.apache.catalina.realm.GenericPrincipal</code>, passing as
+        constructor arguments:  this realm instance, the authenticated
+        username, and a <code>List</code> of the security roles associated
+        with this user.</li>
+    <li><strong>WARNING</strong> - Do not attempt to cache and reuse previous
+        <code>GenericPrincipal</code> objects for a particular user, because
+        the information in the directory server might have changed since the
+        last time this user was authenticated.</li>
+    <li>Return the newly constructed <code>GenericPrincipal</code>.</li>
+    </ul>
+
+
+    <h3>Method hasRole() Functionality</h3>
+
+    <p>When <code>hasRole()</code> is called, the following processing
+    is required:</p>
+    <ul>
+    <li>The <code>principal</code> that is passed as an argument SHOULD
+        be one that we returned (instanceof class
+        <code>org.apache.catalina.realm.GenericPrincipal</code>, with a
+        <code>realm</code> property that is equal to our instance.</li>
+    <li>If the passed <code>principal</code> meets these criteria, check
+        the specified role against the list returned by
+        <code>getRoles()</code>, and return <code>true</code> if the
+        specified role is included; otherwise, return <code>false</code>.</li>
+    <li>If the passed <code>principal</code> does not meet these criteria,
+        return <code>false</code>.</li>
+    </ul>
+
+  </subsection>
+
+</section>
+
+
+<section name="Testable Assertions">
+
+  <p>In addition the the assertions implied by the functionality requirements
+  listed above, the following additional assertions shall be tested to
+  validate the behavior of <code>JDBCRealm</code>:</p>
+  <ul>
+  </ul>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/fs-jndi-realm.xml b/container/webapps/docs/funcspecs/fs-jndi-realm.xml
new file mode 100644
index 0000000..429fe0a
--- /dev/null
+++ b/container/webapps/docs/funcspecs/fs-jndi-realm.xml
@@ -0,0 +1,403 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="fs-jndi-realm.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig McClanahan</author>
+    <title>JNDIRealm</title>
+    <revision>$Id$</revision>
+  </properties>
+
+<body>
+
+
+<section name="Overview">
+
+
+  <subsection name="Introduction">
+
+    <p>The purpose of the <strong>JNDIRealm</strong> implementation is to
+    provide a mechanism by which Tomcat 5 can acquire information needed
+    to authenticate web application users, and define their security roles,
+    from a directory server or other service accessed via JNDI APIs.  For
+    integration with Catalina, this class must implement the
+    <code>org.apache.catalina.Realm</code> interface.</p>
+
+    <p>This specification reflects a combination of functionality that is
+    already present in the <code>org.apache.catalina.realm.JNDIRealm</code>
+    class, as well as requirements for enhancements that have been
+    discussed.  Where appropriate, requirements statements are marked
+    <em>[Current]</em> and <em>[Requested]</em> to distinguish them.</p>
+
+    <p>The current status of this functional specification is
+    <strong>PROPOSED</strong>.  It has not yet been discussed and
+    agreed to on the TOMCAT-DEV mailing list.</p>
+
+    <p>The code in the current version of <code>JNDIRealm</code>, and the
+    ideas expressed in this functional specification, are the results of
+    contributions from many individuals, including (alphabetically):</p>
+    <ul>
+    <li>Holman, John &lt;j.g.holman@qmw.ac.uk&gt;</li>
+    <li>Lockhart, Ellen &lt;elockhart@home.com&gt;</li>
+    <li>McClanahan, Craig &lt;craigmcc@apache.org&gt;</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="External Specifications">
+
+    <p>The implementation of this functionality depends on the following
+    external specifications:</p>
+    <ul>
+    <li><a href="http://java.sun.com/products/jndi/">Java Naming and
+        Directory Interface</a> (version 1.2.1 or later)</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Implementation Requirements">
+
+    <p>The implementation of this functionality shall conform to the
+    following requirements:</p>
+    <ul>
+    <li>Be realized in one or more implementation classes.</li>
+    <li>Implement the <code>org.apache.catalina.Realm</code> interface.
+        [Current]</li>
+    <li>Implement the <code>org.apache.catalina.Lifecycle</code>
+        interface.  [Current]</li>
+    <li>Subclass the <code>org.apache.catalina.realm.RealmBase</code>
+        base class.</li>
+    <li>Live in the <code>org.apache.catalina.realm</code> package.
+        [Current]</li>
+    <li>Support a configurable debugging detail level. [Current]</li>
+    <li>Log debugging and operational messages (suitably internationalized)
+        via the <code>getContainer().log()</code> method. [Current]</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Dependencies">
+
+
+  <subsection name="Environmental Dependencies">
+
+    <p>The following environmental dependencies must be met in order for
+    JNDIRealm to operate correctly:</p>
+    <ul>
+    <li>The desire to utilize JNDIRealm must be registered in
+        <code>$CATALINA_HOME/conf/server.xml</code>, in a
+        <code>&lt;Realm&gt;</code> element that is nested inside a
+        corresponding <code>&lt;Engine&gt;</code>, <code>&lt;Host&gt;</code>,
+        or <code>&lt;Context&gt;</code> element.</li>
+    <li>If the <em>Administrator Login</em> operational mode is selected,
+        the configured administrator username and password must be configured
+        in the corresponding directory server.</li>
+    <li>If the <em>Username Login</em> operational mode is selected,
+        the corresponding directory server must be configured to accept
+        logins with the username and password that will be passed to
+        <code>JNDIRealm</code> by the appropriate <code>Authenticator</code>.
+        </li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Container Dependencies">
+
+    <p>Correct operation of JNDIRealm depends on the following
+    specific features of the surrounding container:</p>
+    <ul>
+    <li>Interactions with <code>JNDIRealm</code> will be initiated by
+        the appropriate <code>Authenticator</code> implementation, based
+        on the login method that is selected.</li>
+    <li><code>JNDIRealm</code> must have the JNDI API classes available
+        to it.  For a JDK 1.2 container, that means <code>jndi.jar</code>
+        and the appropriate implementation (such as <code>ldap.jar</code>)
+        must be placed in the <code>server/lib</code> directory.</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Functionality">
+
+
+  <subsection name="Operational Modes">
+
+    <p>The completed <code>JNDIRealm</code> must support two major operational
+    modes in order to support all of the required use cases.  For the purposes
+    of this document, the modes are called <em>administrator login</em> and
+    <em>Username Login</em>.  They are described further in the following
+    paragraphs.</p>
+
+    <p>For <em>Administrator Login</em> mode, <code>JNDIRealm</code> will be
+    configured to establish one or more connections (using a connection pool)
+    to an appropriate directory server, using JNDI APIs, under a "system
+    administrator" username and password.  This is similar to the approach
+    normally used to configure <code>JDBCRealm</code> to access authentication
+    and access control information in a database.  It is assumed that the
+    system administrator username and password that are configured provide
+    sufficient privileges within the directory server to read (but not modify)
+    the username, password, and assigned roles for each valid user of the
+    web application associated with this <code>Realm</code>.  The password
+    can be stored in cleartext, or in one of the digested modes supported by
+    the <code>org.apache.catalina.realm.RealmBase</code> base class.</p>
+
+    <p>For <em>Username Login</em> mode, <code>JNDIRealm</code> does not
+    normally remain connected to the directory server.  Instead, whenever a
+    user is to be authenticated, a connection to the directory server
+    (using the username and password received from the authenticator) is
+    attempted.  If this connection is successful, the user is assumed to be
+    successfully authenticated.  This connection is then utilized to read
+    the corresponding security roles associated with this user, and the
+    connection is then broken.</p>
+
+    <p><strong>NOTE</strong> - <em>Username Login</em> mode cannot be used
+    if you have selected login method <code>DIGEST</code> in your web
+    application deployment descriptor (<code>web.xml</code>) file.  This
+    restriction exists because the cleartext password is never available
+    to the container, so it is not possible to bind to the directory server
+    using the user's username and password.</p>
+
+    <p>Because these operational modes work so differently, the functionality
+    for each mode will be described separately.  Whether or not both modes
+    are actually supported by a single class (versus a class per mode) is
+    an implementation detail left to the designer.</p>
+
+    <p><strong>NOTE</strong> - The current implementation only implements
+    part of the <em>Administrator Lookup</em> mode requirements.  It does
+    not support the <em>Username Lookup</em> mode at all, at this point.</p>
+
+  </subsection>
+
+
+  <subsection name="Administrator Login Mode Functionality">
+
+
+    <h3>Configurable Properties</h3>
+
+    <p>The implementation shall support the following properties
+    that can be configured with JavaBeans property setters:</p>
+    <ul>
+    <li><code>connectionURL</code> - URL of the directory server we will
+        be contacting.</li>
+    <li><code>contextFactory</code> - Fully qualified class name of the JNDI
+        context factory used to retrieve our InitialContext.
+        [com.sun.jndi.ldap.LdapCtxFactory]</li>
+    <li>Additional configuration properties required to establish the
+        appropriate connection.  [Requested]</li>
+    <li>Connection pool configuration properties.  [Requested]</li>
+    <li>Configuration properties defining how a particular user is
+        authenticated.  The following capabilities should be supported:
+        <ul>
+        <li>Substitute the specified username into a string.  [Requested]</li>
+        <li>Retrieve the distinguished name (DN) of an authorized user via an
+            LDAP search string with a replacement placeholder for the
+            username, and comparison of the password to a configurable
+            attribute retrieved from the search result.  [Current]</li>
+        </ul></li>
+    <li>Configuration properties defining how the roles associated with a
+        particular authenticated user can be retrieved.  The following
+        approaches should be supported:
+        <ul>
+        <li>Retrieve a specified attribute (possibly multi-valued)
+            from an LDAP search expression,
+            with a replacement placeholder for the DN of the user.
+            [Current]</li>
+        <li>Retrieve a set of role names that are defined implicitly (by
+            selecting principals that match a search pattern) rather than
+            explicitly (by finding a particular attribute value).
+            [Requested]</li>
+        </ul></li>
+    </ul>
+
+    <h3>Lifecycle Functionality</h3>
+
+    <p>The following processing must be performed when the <code>start()</code>
+    method is called:</p>
+    <ul>
+    <li>Establish a connection to the configured directory server, using the
+        configured system administrator username and password.  [Current]</li>
+    <li>Configure and establish a connection pool of connections to the
+        directory server.  [Requested]</li>
+    </ul>
+
+    <p>The following processing must be performed when the <code>stop()</code>
+    method is called:</p>
+    <ul>
+    <li>Close any opened connections to the directory server.</li>
+    </ul>
+
+
+    <h3>Method authenticate() Functionality</h3>
+
+    <p>When <code>authenticate()</code> is called, the following processing
+    is required:</p>
+    <ul>
+    <li>Acquire the one and only connection [Current] or acquire a connection
+        from the connection pool [Requested].</li>
+    <li>Authenticate the user by retrieving the user's Distinguished Name,
+        based on the specified username and password.</li>
+    <li>If the user was not authenticated, release the allocated connection
+        and return <code>null</code>.</li>
+    <li>Acquire a <code>List</code> of the security roles assigned to the
+        authenticated user.</li>
+    <li>Construct a new instance of class
+        <code>org.apache.catalina.realm.GenericPrincipal</code>, passing as
+        constructor arguments:  this realm instance, the authenticated
+        username, and a <code>List</code> of the security roles associated
+        with this user.</li>
+    <li><strong>WARNING</strong> - Do not attempt to cache and reuse previous
+        <code>GenericPrincipal</code> objects for a particular user, because
+        the information in the directory server might have changed since the
+        last time this user was authenticated.</li>
+    <li>Return the newly constructed <code>GenericPrincipal</code>.</li>
+    </ul>
+
+
+    <h3>Method hasRole() Functionality</h3>
+
+    <p>When <code>hasRole()</code> is called, the following processing
+    is required:</p>
+    <ul>
+    <li>The <code>principal</code> that is passed as an argument SHOULD
+        be one that we returned (instanceof class
+        <code>org.apache.catalina.realm.GenericPrincipal</code>, with a
+        <code>realm</code> property that is equal to our instance.</li>
+    <li>If the passed <code>principal</code> meets these criteria, check
+        the specified role against the list returned by
+        <code>getRoles()</code>, and return <code>true</code> if the
+        specified role is included; otherwise, return <code>false</code>.</li>
+    <li>If the passed <code>principal</code> does not meet these criteria,
+        return <code>false</code>.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Username Login Mode Functionality">
+
+    <h3>Configurable Properties</h3>
+
+    <p>The implementation shall support the following properties
+    that can be configured with JavaBeans property setters:</p>
+    <ul>
+    <li><code>connectionURL</code> - URL of the directory server we will
+        be contacting.</li>
+    <li><code>contextFactory</code> - Fully qualified class name of the JNDI
+        context factory used to retrieve our InitialContext.
+        [com.sun.jndi.ldap.LdapCtxFactory]</li>
+    <li>Additional configuration properties required to establish the
+        appropriate connection.  [Requested]</li>
+    <li>Connection pool configuration properties.  [Requested]</li>
+    <li>Configuration properties defining if and how a user might be looked
+        up before binding to the directory server.  The following approaches
+        should be supported:
+        <ul>
+        <li>No previous lookup is required - username specified by the user
+            is the same as that used to authenticate to the directory
+            server.</li>
+        <li>Substitute the specified username into a string.</li>
+        <li>Search the directory server based on configured criteria to
+            retrieve the distinguished name of the user, then attempt to
+            bind with that distinguished name.</li>
+        </ul></li>
+    <li>Configuration properties defining how the roles associated with a
+        particular authenticated user can be retrieved.  The following
+        approaches should be supported:
+        <ul>
+        <li>Retrieve a specified attribute (possibly multi-valued)
+            from an LDAP search expression,
+            with a replacement placeholder for the DN of the user.
+            [Current]</li>
+        </ul></li>
+    </ul>
+
+    <h3>Lifecycle Functionality</h3>
+
+    <p>The following processing must be performed when the <code>start()</code>
+    method is called:</p>
+    <ul>
+    <li>None required.</li>
+    </ul>
+
+    <p>The following processing must be performed when the <code>stop()</code>
+    method is called:</p>
+    <ul>
+    <li>None required.</li>
+    </ul>
+
+
+    <h3>Method authenticate() Functionality</h3>
+
+    <p>When <code>authenticate()</code> is called, the following processing
+    is required:</p>
+    <ul>
+    <li>Attempt to bind to the directory server, using the username and
+        password provided by the user.</li>
+    <li>If the user was not authenticated, release the allocated connection
+        and return <code>null</code>.</li>
+    <li>Acquire a <code>List</code> of the security roles assigned to the
+        authenticated user.</li>
+    <li>Construct a new instance of class
+        <code>org.apache.catalina.realm.GenericPrincipal</code>, passing as
+        constructor arguments:  this realm instance, the authenticated
+        username, and a <code>List</code> of the security roles associated
+        with this user.</li>
+    <li><strong>WARNING</strong> - Do not attempt to cache and reuse previous
+        <code>GenericPrincipal</code> objects for a particular user, because
+        the information in the directory server might have changed since the
+        last time this user was authenticated.</li>
+    <li>Return the newly constructed <code>GenericPrincipal</code>.</li>
+    </ul>
+
+
+    <h3>Method hasRole() Functionality</h3>
+
+    <p>When <code>hasRole()</code> is called, the following processing
+    is required:</p>
+    <ul>
+    <li>The <code>principal</code> that is passed as an argument SHOULD
+        be one that we returned (instanceof class
+        <code>org.apache.catalina.realm.GenericPrincipal</code>, with a
+        <code>realm</code> property that is equal to our instance.</li>
+    <li>If the passed <code>principal</code> meets these criteria, check
+        the specified role against the list returned by
+        <code>getRoles()</code>, and return <code>true</code> if the
+        specified role is included; otherwise, return <code>false</code>.</li>
+    <li>If the passed <code>principal</code> does not meet these criteria,
+        return <code>false</code>.</li>
+    </ul>
+
+  </subsection>
+
+</section>
+
+
+<section name="Testable Assertions">
+
+  <p>In addition the the assertions implied by the functionality requirements
+  listed above, the following additional assertions shall be tested to
+  validate the behavior of <code>JNDIRealm</code>:</p>
+  <ul>
+  </ul>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/fs-memory-realm.xml b/container/webapps/docs/funcspecs/fs-memory-realm.xml
new file mode 100644
index 0000000..46d8ab6
--- /dev/null
+++ b/container/webapps/docs/funcspecs/fs-memory-realm.xml
@@ -0,0 +1,239 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="fs-memory-realm.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig McClanahan</author>
+    <title>MemoryRealm</title>
+    <revision>$Id$</revision>
+  </properties>
+
+<body>
+
+
+<section name="Overview">
+
+
+  <subsection name="Introduction">
+
+    <p>The purpose of the <strong>MemoryRealm</strong> implementation is to
+    provide a mechanism by which Tomcat 5 can acquire information needed
+    to authenticate web application users, and define their security roles,
+    from a simple text-based configuration file in XML format.  This is
+    intended to simplify the initial installation and operation of Tomcat 5,
+    without the complexity of configuring a database or directory server
+    based Realm.  It is not intended for production use.</p>
+
+    <p>This specification reflects a combination of functionality that is
+    already present in the <code>org.apache.catalina.realm.MemoryRealm</code>
+    class, as well as requirements for enhancements that have been
+    discussed.  Where appropriate, requirements statements are marked
+    <em>[Current]</em> and <em>[Requested]</em> to distinguish them.</p>
+
+    <p>The current status of this functional specification is
+    <strong>PROPOSED</strong>.  It has not yet been discussed and
+    agreed to on the TOMCAT-DEV mailing list.</p>
+
+  </subsection>
+
+
+  <subsection name="External Specifications">
+
+    <p>The implementation of this functionality depends on the following
+    external specifications:</p>
+    <ul>
+    <li>None</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Implementation Requirements">
+
+    <p>The implementation of this functionality shall conform to the
+    following requirements:</p>
+    <ul>
+    <li>Be realized in one or more implementation classes.</li>
+    <li>Implement the <code>org.apache.catalina.Realm</code> interface.
+        [Current]</li>
+    <li>Implement the <code>org.apache.catalina.Lifecycle</code>
+        interface.  [Current]</li>
+    <li>Subclass the <code>org.apache.catalina.realm.RealmBase</code>
+        base class.</li>
+    <li>Live in the <code>org.apache.catalina.realm</code> package.
+        [Current]</li>
+    <li>Support a configurable debugging detail level. [Current]</li>
+    <li>Log debugging and operational messages (suitably internationalized)
+        via the <code>getContainer().log()</code> method. [Current]</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Dependencies">
+
+
+  <subsection name="Environmental Dependencies">
+
+    <p>The following environmental dependencies must be met in order for
+    MemoryRealm to operate correctly:</p>
+    <ul>
+    <li>The desire to utilize MemoryRealm must be registered in
+        <code>$CATALINA_HOME/conf/server.xml</code>, in a
+        <code>&lt;Realm&gt;</code> element that is nested inside a
+        corresponding <code>&lt;Engine&gt;</code>, <code>&lt;Host&gt;</code>,
+        or <code>&lt;Context&gt;</code> element.  (This is already
+        included in the default <code>server.xml</code> file.)</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Container Dependencies">
+
+    <p>Correct operation of MemoryRealm depends on the following
+    specific features of the surrounding container:</p>
+    <ul>
+    <li>Interactions with <code>MemoryRealm</code> will be initiated by
+        the appropriate <code>Authenticator</code> implementation, based
+        on the login method that is selected.</li>
+    <li><code>MemoryRealm</code> must have an XML parser compatible with
+        the JAXP/1.1 APIs available to it.  This is normally accomplished
+        by placing the corresponding JAR files in directory
+        <code>$CATALINA_HOME/server/lib</code> (to make them visible only
+        to internal Catalina classes) or in directory
+        <code>$CATALINA_HOME/common/lib</code> (to make them visible to
+        Catalina internal classes <strong>and</strong> installed web
+        applications).</li>
+    </ul>
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Functionality">
+
+
+  <subsection name="Overview of Operation">
+
+    <p>The main purpose of <code>MemoryRealm</code> is to allow Catalina to
+    authenticate users, and look up the corresponding security roles, from
+    the information found in an XML-format configuration file.  The format
+    of this file is described below.  When a <code>MemoryRealm</code>
+    instance is started, it will read the contents of this XML file and create
+    an "in memory database" of all the valid users and their associated
+    security roles.</p>
+
+    <p>Each time that Catalina needs to authenticate a user, it will call
+    the <code>authenticate()</code> method of this Realm implementation,
+    passing the username and password that were specified by the user.  If
+    we find the user in the database (and match on the password), we accumulate
+    all of the security roles that are defined for this user, and create a
+    new <code>GenericPrincipal</code> object to be returned.  If the user
+    is not authenticated, we return <code>null</code> instead.  The
+    <code>GenericUser</code> object caches the set of security roles that
+    were owned by this user at the time of authentication, so that calls to
+    <code>isUserInRole()</code> can be answered without going back to the
+    database every time.</p>
+
+  </subsection>
+
+
+  <subsection name="Detailed Functional Requirements">
+
+
+    <h3>Configurable Properties</h3>
+
+    <p>The implementation shall support the following properties
+    that can be configured with JavaBeans property setters:</p>
+    <ul>
+    <li>Configurable debugging detail level.</li>
+    <li>Configurable file pathname (absolute or relative to
+        <code>$CATALINA_HOME</code> of the XML file containing our
+        defined users.  [<code>conf/tomcat-users.xml</code>].</li>
+    </ul>
+
+    <h3>Lifecycle Functionality</h3>
+
+    <p>The following processing must be performed when the <code>start()</code>
+    method is called:</p>
+    <ul>
+    <li>Open and parse the specified XML file.</li>
+    <li>Create an in-memory database representation of the XML file
+        contents.</li>
+    <li><strong>NOTE</strong> - There is no requirement to recognize
+        subsequent changes to the contents of the XML file.</li>
+    </ul>
+
+    <p>The following processing must be performed when the <code>stop()</code>
+    method is called:</p>
+    <ul>
+    <li>Release object references to the in-memory database representation.</li>
+    </ul>
+
+
+    <h3>Method authenticate() Functionality</h3>
+
+    <p>When <code>authenticate()</code> is called, the following processing
+    is required:</p>
+    <ul>
+    <li>Select the one and only "user" instance from the in-memory database,
+        based on matching the specified username.  If there is no such
+        instance, return <code>null</code>.</li>
+    <li>Authenticate the user by comparing the (possibly encrypted) password
+        value that was received against the password presented by the user.
+        If there is no match, return <code>null</code>.</li>
+    <li>Construct a new instance of class
+        <code>org.apache.catalina.realm.GenericPrincipal</code> (if not
+        already using this as the internal database representation) that
+        contains the authenticated username and a <code>List</code> of the
+        security roles associated with this user.</li>
+    <li>Return the newly constructed <code>GenericPrincipal</code>.</li>
+    </ul>
+
+
+    <h3>Method hasRole() Functionality</h3>
+
+    <p>When <code>hasRole()</code> is called, the following processing
+    is required:</p>
+    <ul>
+    <li>The <code>principal</code> that is passed as an argument SHOULD
+        be one that we returned (instanceof class
+        <code>org.apache.catalina.realm.GenericPrincipal</code>, with a
+        <code>realm</code> property that is equal to our instance.</li>
+    <li>If the passed <code>principal</code> meets these criteria, check
+        the specified role against the list returned by
+        <code>getRoles()</code>, and return <code>true</code> if the
+        specified role is included; otherwise, return <code>false</code>.</li>
+    <li>If the passed <code>principal</code> does not meet these criteria,
+        return <code>false</code>.</li>
+    </ul>
+
+  </subsection>
+
+</section>
+
+
+<section name="Testable Assertions">
+
+  <p>In addition the the assertions implied by the functionality requirements
+  listed above, the following additional assertions shall be tested to
+  validate the behavior of <code>MemoryRealm</code>:</p>
+  <ul>
+  </ul>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/index.xml b/container/webapps/docs/funcspecs/index.xml
new file mode 100644
index 0000000..0b30679
--- /dev/null
+++ b/container/webapps/docs/funcspecs/index.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="index.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <title>Table of Contents</title>
+  </properties>
+
+<body>
+
+
+<section name="Catalina Functional Specifications">
+
+<p>This documentation area includes <em>functional specifications</em> for
+many features supported by the <strong>Catalina</strong> servlet container
+portion of Tomcat 5.  In most cases, these features are not documented in the
+underlying Servlet or JSP specifications, so a definition of the expected
+correct behavior is important both to implementors of those features, and to
+test writers trying to decide what to test.</p>
+
+<p>The functional specifications are divided into the following categories
+in the menu (to the left):</p>
+<ul>
+<li><em>Administrative Apps</em> - Overall requirements for supporting an
+    ability to configure and operate a Tomcat 5 installation through tools,
+    as well as detailed requirements for the tools themselves.</li>
+<li><em>Internal Servlets</em> - Requirements for Catalina features that are
+    implemented as internal, container-managed, servlets.</li>
+<li><em>Realm Implementations</em> - Requirements for the implementations of
+    the <code>org.apache.catalina.Realm</code> interface (providing access to
+    collections of users, passwords and roles) that are included in the
+    standard Tomcat 5 distribution.</li>
+</ul>
+
+<p><em>NOTE</em> - In some cases, the contents of these functional specs has
+been "reverse engineered" from existing implementations.  This exercise is
+stil useful, because it provides an introduction to <strong>what</strong>
+Catalina does, without being as concerned with <strong>how</strong> this is
+accomplished.</p>
+
+<p><strong>TODO</strong> - Obviously, this area has a long ways to go before
+it is complete.  Contributions are welcome!</p>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/mbean-names.xml b/container/webapps/docs/funcspecs/mbean-names.xml
new file mode 100644
index 0000000..4612c95
--- /dev/null
+++ b/container/webapps/docs/funcspecs/mbean-names.xml
@@ -0,0 +1,870 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="mbean-names.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig McClanahan</author>
+    <author email="amyroh@apache.org">Amy Roh</author>
+    <title>Tomcat MBean Names</title>
+    <revision>$Id$</revision>
+  </properties>
+
+<body>
+
+
+<section name="Background">
+
+    <p>We will be using <em>JMX MBeans</em> as the technology for
+    implementing manageability of Tomcat.</p>
+
+    <p>One of the key concepts of JMX (and JSR-77) is that each management
+    bean has a unique name in the MBeanServer's registry, and that
+    management applications can utilize these names to retrieve the MBean
+    of interest to them for a particular management operation.
+    This document proposes a naming convention for MBeans that allows easy
+    calculation of the name for a particular MBean.  For background
+    information on JMX MBean names, see the <em>Java Management Extensions
+    Instrumentation and Agent Specification</em>, version 1.0, section 6.
+    In particular, we will be discussing the String Representation of
+    <code>ObjectName</code> instances.</p>
+
+</section>
+
+<section name="Catalina Object Hierarchy">
+
+<p>Tomcat's servlet container implementation, called Catalina, can be
+represented as a hierarchy of objects that contain references to each other.
+The object hierarchy can be represented as a tree, or (isomorphically) based
+on the nesting of configuration elements in the <code>conf/server.xml</code>
+file that is traditionally used to configure Tomcat stand-alone.</p>
+
+<p>The valid component nestings for Catalina are depicted in the following
+table, with columns that contain the following values:</p>
+<ul>
+<li><em>Pattern</em> - Nesting pattern of XML elements (in the
+    <code>conf/server.xml</code> file) used to configure this component.</li>
+<li><em>Cardinality</em> - Minimum and maximum number of occurrences of
+    this element at this nesting position, which also corresponds to the
+    minimum and maximum number of Catalina components.</li>
+<li><em>Identifier</em> - Name of the JavaBeans property of this component
+    that represents the unique identifier (within the nested hierarchy),
+    if any.</li>
+<li><em>MBean ObjectName</em> - The portion of the MBean object name that
+    appears <strong>after</strong> the domain name.  For now, it should be
+    assumed that all of these MBeans appear in the default JMX domain.</li>
+</ul>
+
+<p>In the <em>MBean ObjectName</em> descriptions, several types of symbolic
+expressions are utilized to define variable text that is replaced by
+corresponding values:</p>
+<ul>
+<li><em>${GROUP}</em> - One of the standard MBean names of the specified
+    "group" category.  For example, the expression <code>${REALM}</code>
+    represents the values like <code>JDBCRealm</code> and <code>JAASRealm</code>
+    that identify the various MBeans for possible <code>Realm</code> components.</li>
+<li><em>${name}</em> - Replaced by the value of property <code>name</code>
+    from the current component.</li>
+<li><em>${parent.name}</em> - Replaced by the value of property
+    <code>name</code> from a parent of the current component, with the
+    parent's type identified by <em>parent</em>.</li>
+<li><em>${###}</em> - An arbitrary numeric identifier that preserves
+    order but has no other particular meaning.  In general, the server will
+    assign numeric values to existing instances with large gaps into which
+    new items can be configured if desired.</li>
+</ul>
+
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Pattern</th>
+    <th align="center" bgcolor="aqua">Cardinality</th>
+    <th align="center" bgcolor="aqua">Identifier</th>
+    <th align="center" bgcolor="aqua">MBean ObjectName</th>
+  </tr>
+
+  <tr>
+    <td>Server</td>
+    <td align="center">1..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=${SERVER}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Listener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${LISTENER}, sequence=${###}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service</td>
+    <td align="center">1..n</td>
+    <td align="center"><code>name</code></td>
+    <td><code>type=${SERVICE}, name=${name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Connector</td>
+    <td align="center">1..n</td>
+    <td align="center"><code>address, port</code></td>
+    <td><code>type=${CONNECTOR}, service=${service}, port=${port},
+        address=${address}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Connector / Factory</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td>(Only defined explicitly for an SSL connector, but can be treated
+        as part of the connector component)</td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Connector / Listener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${LISTENER}, sequence=${###}, service=${service},
+        port=${connector.port}, address=${connector.address}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine</td>
+    <td align="center">1..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=${ENGINE}, service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / DefaultContext</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=${DEFAULT-CONTEXT}, service=${service.name}</code></td>
+  </tr>
+
+<!--
+  <tr>
+    <td>Server / Service / Engine / DefaultContext / InstanceListener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / DefaultContext / Listener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${LISTENER}, sequence=${###}</code></td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / DefaultContext / Loader</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / DefaultContext / Manager</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / DefaultContext / Realm</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / DefaultContext / Resources</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / DefaultContext / Valve</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / DefaultContext / WrapperLifecycle</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / DefaultContext / WrapperListener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+-->
+
+  <tr>
+    <td>Server / Service / Engine / Host</td>
+    <td align="center">1..n</td>
+    <td align="center"><code>name</code></td>
+    <td><code>type=${HOST}, host=${name},
+        service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context</td>
+    <td align="center">1..n</td>
+    <td align="center"><code>path</code></td>
+    <td><code>type=${CONTEXT}, path=${path}, host=${host.name},
+        service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context / InstanceListener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${INSTANCE-LISTENER}, sequence=${###}, path=${context.path},
+        host=${host.name}, service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context / Listener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${LISTENER}, sequence=${###}, path=${context.path},
+        host=${host.name}, service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context / Loader</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=${LOADER}, path=${context.path}, host=${host.name},
+        service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context / Manager</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=${MANAGER}, path=${context.path}, host=${host.name},
+        service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context / Realm</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=${REALM}, path=${context.path}, host=${host.name},
+        service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context / Resources</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=${RESOURCES}, path=${context.path}, host=${host.name},
+        service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context / Valve</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${VALVE}, sequence=${###}, path=${context.path},
+        host=${host.name}, service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context / WrapperLifecycle</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${WRAPPER-LIFECYCLE}, sequence=${###}, path=${context.path},
+        host=${host.name}, service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Context / WrapperListener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${WRAPPER-LISTENER}, sequence=${###}, path=${context.path},
+        host=${host.name}, service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=DefaultContext, host=${host.name},
+        service=${service.name}</code></td>
+  </tr>
+
+<!--
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext / InstanceListener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext / Listener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext / Loader</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext / Manager</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext / Realm</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext / Resources</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext / Valve</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext / WrapperLifecycle</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / DefaultContext / WrapperListener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td></td>
+  </tr>
+-->
+
+  <tr>
+    <td>Server / Service / Engine / Host / Listener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${LISTENER}, sequence=${###}, host=${host.name},
+        service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Realm</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=${REALM}, host=${host.name},
+        service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Host / Valve</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${VALVE}, sequence=${###},
+        host=${host.name}, service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Listener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${LISTENER}, sequence=${###}</code>
+        (<strong>FIXME</strong> - disambiguate from Server / Service /
+        Listener)</td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Realm</td>
+    <td align="center">0..1</td>
+    <td align="center">(none)</td>
+    <td><code>type=${REALM}, service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Engine / Valve</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${VALVE}, sequence=${###},
+        service=${service.name}</code></td>
+  </tr>
+
+  <tr>
+    <td>Server / Service / Listener</td>
+    <td align="center">0..n</td>
+    <td align="center">(none)</td>
+    <td><code>type=${LISTENER}, sequence=${###}</code>
+        (<strong>FIXME</strong> - disambiguate from Server / Service /
+        Engine / Listener)</td>
+  </tr>
+
+</table>
+
+</section>
+
+<section name="MBean Groups and Names">
+
+<p>The following MBean names shall be defined in the resource file
+<code>/org/apache/catalina/mbeans/mbeans-descriptors.xml</code> (and
+therefore available for use within the Administration/Configuration
+web application for Tomcat):</p>
+
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">MBean Name</th>
+    <th align="center" bgcolor="aqua">Group Name</th>
+    <th align="center" bgcolor="aqua">Catalina Interface</th>
+    <th align="center" bgcolor="aqua">Implementation Class</th>
+  </tr>
+
+  <tr>
+    <td><code>AccessLogValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.valves.AccessLogValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>BasicAuthenticator</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.authenticator.BasicAuthenticator</code></td>
+  </tr>
+
+  <tr>
+    <td><code>CertificatesValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.valves.CertificatesValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>ContextConfig</code></td>
+    <td align="center"><code>LISTENER</code></td>
+    <td><code>org.apache.catalina.LifecycleListener</code></td>
+    <td><code>org.apache.catalina.startup.ContextConfig</code></td>
+  </tr>
+
+  <tr>
+    <td><code>ContextEnvironment</code></td>
+    <td align="center"><code>RESOURCES</code></td>
+    <td><code>org.apache.catalina.deploy.ContextEnvironment</code></td>
+    <td><code>org.apache.catalina.deploy.ContextEnvironment</code></td>
+  </tr>
+
+  <tr>
+    <td><code>ContextResource</code></td>
+    <td align="center"><code>RESOURCES</code></td>
+    <td><code>org.apache.catalina.deploy.ContextResource</code></td>
+    <td><code>org.apache.catalina.deploy.ContextResource</code></td>
+  </tr>
+
+  <tr>
+    <td><code>ContextResourceLink</code></td>
+    <td align="center"><code>RESOURCES</code></td>
+    <td><code>org.apache.catalina.deploy.ContextResourceLink</code></td>
+    <td><code>org.apache.catalina.deploy.ContextResourceLink</code></td>
+  </tr>
+
+  <tr>
+    <td><code>CoyoteConnector</code></td>
+    <td align="center"><code>CONNECTOR</code></td>
+    <td><code>org.apache.catalina.Connector</code></td>
+    <td><code>org.apache.coyote.tomcat4.CoyoteConnector</code></td>
+  </tr>
+
+  <tr>
+    <td><code>DefaultContext</code></td>
+    <td align="center"><code>DEFAULT-CONTEXT</code></td>
+    <td><code>org.apache.catalina.DefaultContext</code></td>
+    <td><code>org.apache.catalina.core.StandardDefaultContext</code></td>
+  </tr>
+
+  <tr>
+    <td><code>DigestAuthenticator</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.authenticator.DigestAuthenticator</code></td>
+  </tr>
+
+  <tr>
+    <td><code>EngineConfig</code></td>
+    <td align="center"><code>LISTENER</code></td>
+    <td><code>org.apache.catalina.LifecycleListener</code></td>
+    <td><code>org.apache.catalina.startup.EngineConfig</code></td>
+  </tr>
+
+  <tr>
+    <td><code>ErrorReportValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.valves.ErrorReportValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>ErrorDispatcherValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.valves.ErrorDispatcherValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>FormAuthenticator</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.authenticator.FormAuthenticator</code></td>
+  </tr>
+
+  <tr>
+    <td><code>Group</code></td>
+    <td align="center"><code>GROUP</code></td>
+    <td><code>org.apache.catalina.Group</code></td>
+    <td><code>org.apache.catalina.Group</code></td>
+  </tr>
+
+  <tr>
+    <td><code>HostConfig</code></td>
+    <td align="center"><code>LISTENER</code></td>
+    <td><code>org.apache.catalina.LifecycleListener</code></td>
+    <td><code>org.apache.catalina.startup.HostConfig</code></td>
+  </tr>
+
+  <tr>
+    <td><code>HttpConnector10</code></td>
+    <td align="center"><code>CONNECTOR</code></td>
+    <td><code>org.apache.catalina.Connector</code></td>
+    <td><code>org.apache.catalina.connector.http10.HttpConnector</code></td>
+  </tr>
+
+  <tr>
+    <td><code>HttpConnector11</code></td>
+    <td align="center"><code>CONNECTOR</code></td>
+    <td><code>org.apache.catalina.Connector</code></td>
+    <td><code>org.apache.catalina.connector.http.HttpConnector</code></td>
+  </tr>
+
+  <tr>
+    <td><code>JAASRealm</code></td>
+    <td align="center"><code>REALM</code></td>
+    <td><code>org.apache.catalina.Realm</code></td>
+    <td><code>org.apache.catalina.realm.JAASRealm</code></td>
+  </tr>
+
+  <tr>
+    <td><code>JDBCRealm</code></td>
+    <td align="center"><code>REALM</code></td>
+    <td><code>org.apache.catalina.Realm</code></td>
+    <td><code>org.apache.catalina.realm.JDBCRealm</code></td>
+  </tr>
+
+  <tr>
+    <td><code>JDBCUserDatabase</code></td>
+    <td align="center"><code>USERDATABASE</code></td>
+    <td><code>org.apache.catalina.users.JDBCUserDatabase</code></td>
+    <td><code>org.apache.catalina.users.JDBCUserDatabase</code></td>
+  </tr>
+
+  <tr>
+    <td><code>JNDIRealm</code></td>
+    <td align="center"><code>REALM</code></td>
+    <td><code>org.apache.catalina.Realm</code></td>
+    <td><code>org.apache.catalina.realm.JNDIRealm</code></td>
+  </tr>
+
+  <tr>
+    <td><code>MBeanFactory</code></td>
+    <td align="center"><code></code></td>
+    <td><code></code></td>
+    <td><code>org.apache.catalina.mbeans.MBeanFactory</code></td>
+  </tr>
+
+  <tr>
+    <td><code>MemoryRealm</code></td>
+    <td align="center"><code>REALM</code></td>
+    <td><code>org.apache.catalina.Realm</code></td>
+    <td><code>org.apache.catalina.realm.MemoryRealm</code></td>
+  </tr>
+
+  <tr>
+    <td><code>MemoryUserDatabase</code></td>
+    <td align="center"><code>USERDATABASE</code></td>
+    <td><code>org.apache.catalina.users.MemoryUserDatabase</code></td>
+    <td><code>org.apache.catalina.users.MemoryUserDatabase</code></td>
+  </tr>
+
+  <tr>
+    <td><code>NamingContextListener</code></td>
+    <td align="center"><code>LISTENER</code></td>
+    <td><code>org.apache.catalina.LifecycleListener</code></td>
+    <td><code>org.apache.catalina.core.NamingContextListener</code></td>
+  </tr>
+
+  <tr>
+    <td><code>NamingResources</code></td>
+    <td align="center"><code>RESOURCES</code></td>
+    <td><code>org.apache.catalina.deploy.NamingResources</code></td>
+    <td><code>org.apache.catalina.deploy.NamingResources</code></td>
+  </tr>
+
+  <tr>
+    <td><code>NonLoginAuthenticator</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.authenticator.NonLoginAuthenticator</code></td>
+  </tr>
+
+  <tr>
+    <td><code>PersistentManager</code></td>
+    <td align="center"><code>MANAGER</code></td>
+    <td><code>org.apache.catalina.Manager</code></td>
+    <td><code>org.apache.catalina.session.PersistentManager</code></td>
+  </tr>
+
+  <tr>
+    <td><code>RemoteAddrValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.valves.RemoteAddrValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>RemoteHostValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.valves.RemoteHostValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>RequestDumperValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.valves.RequestDumperValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>Role</code></td>
+    <td align="center"><code>ROLE</code></td>
+    <td><code>org.apache.catalina.Role</code></td>
+    <td><code>org.apache.catalina.Role</code></td>
+  </tr>
+
+  <tr>
+    <td><code>SingleSignOn</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.valves.SingleSignOn</code></td>
+  </tr>
+
+  <tr>
+    <td><code>SSLAuthenticator</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.authenticator.SSLAuthenticator</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardContext</code></td>
+    <td align="center"><code>CONTEXT</code></td>
+    <td><code>org.apache.catalina.Context</code></td>
+    <td><code>org.apache.catalina.core.StandardContext</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardContextValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.core.StandardContextValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardEngine</code></td>
+    <td align="center"><code>ENGINE</code></td>
+    <td><code>org.apache.catalina.Engine</code></td>
+    <td><code>org.apache.catalina.core.StandardEngine</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardEngineValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.core.StandardEngineValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardHost</code></td>
+    <td align="center"><code>HOST</code></td>
+    <td><code>org.apache.catalina.Host</code></td>
+    <td><code>org.apache.catalina.core.StandardHost</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardHostValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.core.StandardHostValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardManager</code></td>
+    <td align="center"><code>MANAGER</code></td>
+    <td><code>org.apache.catalina.Manager</code></td>
+    <td><code>org.apache.catalina.session.StandardManager</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardServer</code></td>
+    <td align="center"><code>SERVER</code></td>
+    <td><code>org.apache.catalina.Server</code></td>
+    <td><code>org.apache.catalina.core.StandardServer</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardService</code></td>
+    <td align="center"><code>SERVICE</code></td>
+    <td><code>org.apache.catalina.Service</code></td>
+    <td><code>org.apache.catalina.core.StandardService</code></td>
+  </tr>
+
+  <tr>
+    <td><code>StandardWrapperValve</code></td>
+    <td align="center"><code>VALVE</code></td>
+    <td><code>org.apache.catalina.Valve</code></td>
+    <td><code>org.apache.catalina.core.StandardWrapperValve</code></td>
+  </tr>
+
+  <tr>
+    <td><code>User</code></td>
+    <td align="center"><code>USER</code></td>
+    <td><code>org.apache.catalina.User</code></td>
+    <td><code>org.apache.catalina.User</code></td>
+  </tr>
+
+  <tr>
+    <td><code>UserDatabaseRealm</code></td>
+    <td align="center"><code>REALM</code></td>
+    <td><code>org.apache.catalina.Realm</code></td>
+    <td><code>org.apache.catalina.realm.UserDatabaseRealm</code></td>
+  </tr>
+
+  <tr>
+    <td><code>WebappLoader</code></td>
+    <td align="center"><code>LOADER</code></td>
+    <td><code>org.apache.catalina.Loader</code></td>
+    <td><code>org.apache.catalina.loader.WebappLoader</code></td>
+  </tr>
+
+</table>
+
+</section>
+
+<section name="JSR-77 Cross Reference">
+
+<p>The managed objects in the JSR-77 object hierarchy correspond
+to the specified MBean names or groups as follows:</p>
+
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">JSR-77 Managed Object</th>
+    <th align="center" bgcolor="aqua">MBean Name or Group</th>
+    <th align="center" bgcolor="aqua">Comments</th>
+  </tr>
+
+  <tr>
+    <td><code>J2EEServer</code></td>
+    <td><code>${SERVICE}</code></td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td><code>Node</code></td>
+    <td><code>${SERVICE}</code></td>
+    <td>Tomcat supports a single node only.</td>
+  </tr>
+
+  <tr>
+    <td><code>Port</code></td>
+    <td><code>${CONNECTOR}</code></td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td><code>Servlet</code></td>
+    <td><code>${WRAPPER}</code></td>
+    <td><strong>FIXME</strong> - Not yet identified as an MBean</td>
+  </tr>
+
+  <tr>
+    <td><code>WebModule</code></td>
+    <td><code>${CONTEXT}</code></td>
+    <td></td>
+  </tr>
+
+</table>
+
+</section>
+
+<section name="JSR-88 Cross Reference">
+
+<p>The deployment objects in the JSR-88 API object hierarchy correspond
+to the specified MBean names or groups as follows:</p>
+
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">JSR-88 API Object</th>
+    <th align="center" bgcolor="aqua">MBean Name or Group</th>
+    <th align="center" bgcolor="aqua">Comments</th>
+  </tr>
+
+  <tr>
+    <td><code>DeployableObject</code></td>
+    <td><code>${CONTEXT}</code></td>
+    <td>Context deployment info plus the corresponding WAR file</td>
+  </tr>
+
+  <tr>
+    <td><code>Target</code></td>
+    <td><code>${HOST}</code></td>
+    <td></td>
+  </tr>
+
+</table>
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/funcspecs/project.xml b/container/webapps/docs/funcspecs/project.xml
new file mode 100644
index 0000000..b265a07
--- /dev/null
+++ b/container/webapps/docs/funcspecs/project.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Catalina Functional Specifications"
+        href="http://jakarta.apache.org/tomcat/">
+
+    <title>Catalina Functional Specifications</title>
+
+    <logo href="/images/tomcat.gif">
+      Catalina Functional Specifications
+    </logo>
+
+    <body>
+
+    <menu name="Links">
+        <item name="Docs Home"             href="../../index.html"/>
+        <item name="Functional Specs"      href="index.html"/>
+    </menu>
+
+    <menu name="Administrative Apps">
+        <item name="Overall Requirements"  href="fs-admin-apps.html"/>
+	  <item name="Tomcat MBean Names"    href="mbean-names.html"/>
+        <item name="Administered Objects"  href="fs-admin-objects.html"/>
+        <item name="Supported Operations"  href="fs-admin-opers.html"/>
+    </menu>
+
+    <menu name="Internal Servlets">
+        <item name="Default Servlet"       href="fs-default.html"/>
+        <item name="Invoker Servlet"       href="fs-invoker.html"/>
+    </menu>
+
+    <menu name="Realm Implementations">
+        <item name="JDBC Realm"            href="fs-jdbc-realm.html"/>
+        <item name="JNDI Realm"            href="fs-jndi-realm.html"/>
+        <item name="Memory Realm"          href="fs-memory-realm.html"/>
+    </menu>
+
+
+    </body>
+
+</project>
diff --git a/container/webapps/docs/html-manager-howto.xml b/container/webapps/docs/html-manager-howto.xml
new file mode 100644
index 0000000..cf3a747
--- /dev/null
+++ b/container/webapps/docs/html-manager-howto.xml
@@ -0,0 +1,543 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="html-manager-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="glenn@apache.org">Glenn L. Nielsen</author>
+        <title>Tomcat Web Application Manager How To</title>
+    </properties>
+
+<body>
+
+<section name="Introduction">
+
+<p>In many production environments it is very useful to have the capability
+to manage your web applications without having to shut down and restart
+Tomcat.  This document is for the HTML web interface to the web application
+<a href="manager-howto.html">manager</a>.</p>
+
+<p>The interface is divided into five sections:
+<ul>
+  <li><strong>Message</strong> - Displays success and failure messages.</li>
+  <li><strong>Manager</strong> - General manager operations like list and
+      help.</li>
+  <li><strong>Applications</strong> - List of web applications and
+      commands.</li>
+  <li><strong>Deploy</strong> - Deploying web applications.</li>
+  <li><strong>Server Information</strong> - Information about the Tomcat
+      server.</li>
+</ul>
+</p>
+
+</section>
+
+<section name="Message">
+
+<p>
+Displays information about the success or failure of the last web application
+manager command you performed. If it succeeded <strong>OK</strong> is displayed
+and may be followed by a success message. If it failed <strong>FAIL</strong>
+is displayed followed by an error message. Common failure messages are
+documented below for each command.  The complete list of failure messages for
+each command can be found in the <a href="manager-howto.html">manager</a> web
+application documentation.
+</p>
+
+</section>
+
+<section name="Manager">
+
+<p>The Manager section has three links:
+<ul>
+  <li><strong>List Applications</strong> - Redisplay a list of web
+      applications.</li>
+  <li><strong>HTML Manager Help</strong> - A link to this document.</li>
+  <li><strong>Manager Help</strong> - A link to the comprehensive Manager
+      App HOW TO.</li>
+</ul>
+</p>
+
+</section>
+
+<section name="Applications">
+
+<p>The Applications section lists information about all the installed web
+applications and provides links for managing them. For each web application
+the following is displayed:
+<ul>
+  <li><strong>Path</strong> - The web applicaton context path.</li>
+  <li><strong>Display Name</strong> - The display name for the web application
+      if it has one configured in its "web.xml" file.</li>
+  <li><strong>Running</strong> - Whether the web application is running and
+      available (true), or not running and unavailable (false).</li>
+  <li><strong>Sessions</strong> - The number of active sessions for remote
+      users of this web application.  The number of sessions is a link which
+      when submitted displays more details about session usage by the web
+      application in the Message box.</li>
+  <li><strong>Commands</strong> - Lists all commands which can be performed on
+      the web application. Only those commands which can be performed will be
+      listed as a link which can be submitted. No commands can be performed on
+      the manager web application itself. The following commands can be
+      performed:
+      <ul>
+        <li><strong>Start</strong> - Start a web application which had been
+            stopped.</li>
+        <li><strong>Stop</strong> - Stop a web application which is currently
+            running and make it unavailable.</li>
+        <li><strong>Reload</strong> - Reload the web application so that new
+            ".jar" files in <code>/WEB-INF/lib/</code> or new classes in
+            <code>/WEB-INF/classes/</code> can be used.</li>
+        <li><strong>Undeploy</strong> - Stop and then remove this web
+             application from the server.</li>
+      </ul>
+  </li>
+</ul>
+</p>
+
+<subsection name="Start">
+
+<p>Signal a stopped application to restart, and make itself available again.
+Stopping and starting is useful, for example, if the database required by
+your application becomes temporarily unavailable.  It is usually better to
+stop the web application that relies on this database rather than letting
+users continuously encounter database exceptions.</p>
+
+<p>If this command succeeds, you will see a Message like this:</p>
+<source>
+OK - Started application at context path /examples
+</source>
+
+<p>Otherwise, the Message will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:
+<ul>
+<li><em>Encountered exception</em>
+    <blockquote>             
+    <p>An exception was encountered trying to start the web application.
+    Check the Tomcat 5 logs for the details.</p>
+    </blockquote></li>       
+<li><em>Invalid context path was specified</em>
+    <blockquote>             
+    <p>The context path must start with a slash character, unless you are
+    referencing the ROOT web application -- in which case the context path
+    must be a zero-length string.</p>
+    </blockquote></li>       
+<li><em>No context exists for path /foo</em>
+    <blockquote>             
+    <p>There is no deployed application on the context path
+    that you specified.</p>  
+    </blockquote></li>       
+<li><em>No context path was specified</em>
+    <blockquote>             
+    The <code>path</code> parameter is required.
+    </blockquote></li>       
+</ul>
+</p>
+
+</subsection>
+
+<subsection name="Stop">
+
+<p>Signal an existing application to make itself unavailable, but leave it
+deployed.  Any request that comes in while an application is
+stopped will see an HTTP error 404, and this application will show as
+"stopped" on a list applications command.</p>
+                             
+<p>If this command succeeds, you will see a Message like this:</p>
+<source>
+OK - Stopped application at context path /examples
+</source>
+                             
+<p>Otherwise, the Message will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:
+<ul>                         
+<li><em>Encountered exception</em>
+    <blockquote>             
+    <p>An exception was encountered trying to stop the web application.
+    Check the Tomcat 5 logs for the details.</p>
+    </blockquote></li>       
+<li><em>Invalid context path was specified</em>
+    <blockquote>             
+    <p>The context path must start with a slash character, unless you are
+    referencing the ROOT web application -- in which case the context path
+    must be a zero-length string.</p>
+    </blockquote></li>       
+<li><em>No context exists for path /foo</em>
+    <blockquote>             
+    <p>There is no deployed application on the context path
+    that you specified.</p>  
+    </blockquote></li>       
+<li><em>No context path was specified</em>
+    <blockquote>             
+    The <code>path</code> parameter is required.
+    </blockquote></li>       
+</ul>
+</p>
+
+</subsection>
+
+<subsection name="Reload">
+
+<p>Signal an existing application to shut itself down and reload.  This can
+be useful when the web application context is not reloadable and you have
+updated classes or property files in the <code>/WEB-INF/classes</code>
+directory or when you have added or updated jar files in the
+<code>/WEB-INF/lib</code> directory.
+</p>
+<p><strong>NOTE:</strong> The <code>/WEB-INF/web.xml</code>
+web application configuration file is not checked on a reload;
+the previous web.xml configuration is used.
+If you have made changes to your web.xml file you must stop
+then start the web application.
+</p>
+
+<p>If this command succeeds, you will see a Message like this:</p>
+<source>
+OK - Reloaded application at context path /examples
+</source>
+
+<p>Otherwise, the Message will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:
+<ul>
+<li><em>Encountered exception</em>
+    <blockquote>             
+    <p>An exception was encountered trying to restart the web application.
+    Check the Tomcat 5 logs for the details.</p>
+    </blockquote></li>       
+<li><em>Invalid context path was specified</em>
+    <blockquote>             
+    <p>The context path must start with a slash character, unless you are
+    referencing the ROOT web application -- in which case the context path
+    must be a zero-length string.</p>
+    </blockquote></li>       
+<li><em>No context exists for path /foo</em>
+    <blockquote>             
+    <p>There is no deployed application on the context path
+    that you specified.</p>  
+    </blockquote></li>       
+<li><em>No context path was specified</em>
+    <blockquote>             
+    The <code>path</code> parameter is required.
+    </blockquote></li>       
+<li><em>Reload not supported on WAR deployed at path /foo</em>
+    <blockquote>             
+    Currently, application reloading (to pick up changes to the classes or
+    <code>web.xml</code> file) is not supported when a web application is
+    installed directly from a WAR file, which happens when the host is 
+    configured to not unpack WAR files. As it only works when the web 
+    application is installed from an unpacked directory, if you are using 
+    a WAR file, you should <code>undeploy</code> and then <code>deploy</code> 
+    the application again to pick up your changes.
+    </blockquote></li>       
+</ul>
+</p>
+
+</subsection>
+
+<subsection name="Undeploy">
+
+<p><strong><font color="red">WARNING</font> - This command will delete the
+contents of the web application directory and/or ".war" file if it exists within
+the <code>appBase</code> directory (typically "webapps") for this virtual host
+</strong>.  The web application temporary work directory is also deleted.  If
+you simply want to take an application out of service, you should use the
+<code>/stop</code> command instead.</p>
+                             
+<p>Signal an existing application to gracefully shut itself down, and then
+remove it from Tomcat (which also makes this context path available for
+reuse later).  This command is the logical opposite of the
+<code>/deploy</code> Ant command, and the related deploy features available 
+in the HTML manager.</p>
+                             
+<p>If this command succeeds, you will see a Message like this:</p>
+<source>
+OK - Undeployed application at context path /examples
+</source>
+                             
+<p>Otherwise, the Message will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:
+<ul>                         
+<li><em>Encountered exception</em>
+    <blockquote>             
+    <p>An exception was encountered trying to undeploy the web application.
+    Check the Tomcat logs for the details.</p>
+    </blockquote></li>       
+<li><em>Invalid context path was specified</em>
+    <blockquote>             
+    <p>The context path must start with a slash character, unless you are
+    referencing the ROOT web application -- in which case the context path
+    must be a zero-length string.</p>
+    </blockquote></li>       
+<li><em>No context exists for path /foo</em>
+    <blockquote>             
+    <p>There is no deployed application on the context path
+    that you specified.</p>  
+    </blockquote></li>       
+<li><em>No context path was specified</em>
+    <blockquote>             
+    The <code>path</code> parameter is required.
+    </blockquote></li>       
+</ul>
+</p>
+
+</subsection>
+
+</section>
+
+<section name="Deploy">
+
+<p>Web applications can be deployed using files or directories located
+on the Tomcat server or you can upload a web application archive (WAR)
+file to the server.</p>
+
+<p>To install an application, fill in the appropriate fields for the type
+of install you want to do and then submit it using the <i>Install</i>
+button.</p>
+
+<subsection name="Deploy directory or WAR file located on server">
+
+<p>Deploy and start a new web application, attached to the specified <i>Context
+Path:</i> (which must not be in use by any other web application).
+This command is the logical opposite of the <em>Undeploy</em> command.</p>
+
+<p>There are a number of different ways the deploy command can be used.</p>
+
+<h3>Deploy a Directory or WAR by URL</h3>
+
+<p>Install a web application directory or ".war" file located on the Tomcat
+server. If no <i>Context Path</i> is specified, the directory name or the
+war file name without the ".war" extension is used as the path. The
+<i>WAR or Directory URL</i> specifies a URL (including the <code>file:</code>
+scheme) for either a directory or a web application archive (WAR) file. The
+supported syntax for a URL referring to a WAR file is described on the Javadocs
+page for the <code>java.net.JarURLConnection</code> class.  Use only URLs that
+refer to the entire WAR file.</p>
+
+<p>In this example the web application located in the directory
+<code>C:\path\to\foo</code> on the Tomcat server (running on Windows)
+is deployed as the web application context named <code>/footoo</code>.
+<source>
+Context Path: /footoo
+WAR or Directory URL: file:C:/path/to/foo
+</source>
+</p>
+
+<p>In this example the ".war" file <code>/path/to/bar.war</code> on the
+Tomcat server (running on Unix) is deployed as the web application
+context named <code>/bar</code>. Notice that there is no <code>path</code>
+parameter so the context path defaults to the name of the web application
+archive file without the ".war" extension.
+<source>
+WAR or Directory URL: jar:file:/path/to/bar.war!/
+</source>
+</p>
+
+<h3>Deploy a Directory or War from the Host appBase</h3>
+
+<p>Install a web application directory or ".war" file located in your Host
+appBase directory. If no <i>Context Path</i> is specified the directory name
+or the war file name without the ".war" extension is used as the path.</p>
+
+<p>In this example the web application located in a subdirectory named
+<code>foo</code> in the Host appBase directory of the Tomcat server is
+deployed as the web application context named <code>/foo</code>. Notice
+that there is no <code>path</code> parameter so the context path defaults
+to the name of the web application directory.
+<source>
+WAR or Directory URL: foo
+</source>
+</p>
+
+<p>In this example the ".war" file <code>bar.war</code> located in your
+Host appBase directory on the Tomcat server is deployed as the web
+application context named <code>/bartoo</code>.
+<source>
+Context Path: /bartoo
+WAR or Directory URL: bar.war
+</source>
+</p>
+
+<h3>Deploy using a Context configuration ".xml" file</h3>
+
+<p>If the Host deployXML flag is set to true, you can install a web
+application using a Context configuration ".xml" file and an optional
+".war" file or web application directory. The <i>Context Path</i>
+is not used when installing a web application using a context ".xml"
+configuration file.</p>
+
+<p>A Context configuration ".xml" file can contain valid XML for a
+web application Context just as if it were configured in your
+Tomcat <code>server.xml</code> configuration file. Here is an
+example for Tomcat running on Windows:
+<source>
+&lt;Context path="/foobar" docBase="C:\path\to\application\foobar"
+         debug="0"&gt;
+
+  &lt;!-- Link to the user database we will get roles from --&gt;
+  &lt;ResourceLink name="users" global="UserDatabase"
+                type="org.apache.catalina.UserDatabase"/&gt;
+
+&lt;/Context&gt;
+</source>
+</p>
+
+<p>Use of the <i>WAR or Directory URL</i> is optional. When used
+to select a web application ".war" file or directory it overrides any
+docBase configured in the context configuration ".xml" file.</p>
+
+<p>Here is an example of installing an application using a Context
+configuration ".xml" file for Tomcat running on Windows.
+<source>
+XML Configuration file URL: file:C:/path/to/context.xml
+</source>
+</p>
+
+<p>Here is an example of installing an application using a Context
+configuration ".xml" file and a web application ".war" file located
+on the server (Tomcat running on Unix).
+<source>
+XML Configuration file URL: file:/path/to/context.xml
+WAR or Directory URL: jar:file:/path/to/bar.war!/
+</source>
+</p>
+
+</subsection>
+
+<subsection name="Upload a WAR file to install">
+
+<p>Upload a WAR file from your local system and install it into the
+appBase for your Host. The name of the WAR file without the ".war"
+extension is used as the context path name.</p>
+
+<p>Use the <i>Browse</i> button to select a WAR file to upload to the
+server from your local desktop system.</p>
+
+<p>The .WAR file may include Tomcat specific deployment configuration, by 
+including a Context configuration XML file in 
+<code>/META-INF/context.xml</code>.</p>
+
+<p>Upload of a WAR file could fail for the following reasons:</p>
+<ul>
+<li><em>File uploaded must be a .war</em>
+    <blockquote>
+    <p>The upload install will only accept files which have the filename
+    extension of ".war".</p>
+    </blockquote></li>
+<li><em>War file already exists on server</em>
+    <blockquote>
+    <p>If a war file of the same name already exists in your Host's
+    appBase the upload will fail. Either undeploy the existing war file
+    from your Host's appBase or upload the new war file using a different
+    name.</p>
+    </blockquote></li>
+<li><em>File upload failed, no file</em>
+    <blockquote>
+    <p>The file upload failed, no file was received by the server.</p>
+    </blockquote></li>
+<li><em>Install Upload Failed, Exception:</em>
+    <blockquote>
+    <p>The war file upload or install failed with a Java Exception.
+    The exception message will be listed.</p>
+    </blockquote></li>
+</ul>
+
+</subsection>
+
+<subsection name="Deployment Notes">
+
+<p>If the Host is configured with unpackWARs=true and you install a war
+file, the war will be unpacked into a directory in your Host appBase
+directory.</p>
+
+<p>If the application war or directory is deployed in your Host appBase
+directory and either the Host is configured with autoDeploy=true or
+liveDeploy=true, the Context path must match the directory name or
+war file name without the ".war" extension.</p>
+
+<p>For security when untrusted users can manage web applications, the
+Host deployXML flag can be set to false.  This prevents untrusted users
+from installing web applications using a configuration XML file and
+also prevents them from installing application directories or ".war"
+files located outside of their Host appBase.</p>
+
+</subsection>
+
+<subsection name="Deploy Message">
+
+<p>If deployment and startup is successful, you will receive a Message
+like this:</p>
+<source>
+OK - Deployed application at context path /foo
+</source>
+
+<p>Otherwise, the Message will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Application already exists at path /foo</em>
+    <blockquote>
+    <p>The context paths for all currently running web applications must be
+    unique.  Therefore, you must either undeploy the existing web
+    application using this context path, or choose a different context path
+    for the new one.</p>
+    </blockquote></li>
+<li><em>Document base does not exist or is not a readable directory</em>
+    <blockquote>
+    <p>The URL specified by the <i>WAR or Directory URL:</i> field must
+    identify a directory on this server that contains the "unpacked" version
+    of a web application, or the absolute URL of a web application archive
+    (WAR) file that contains this application.  Correct the value entered for
+    the <i>WAR or Directory URL:</i> field.</p>
+    </blockquote></li>
+<li><em>Encountered exception</em>
+    <blockquote>
+    <p>An exception was encountered trying to start the new web application.
+    Check the Tomcat 5 logs for the details, but likely explanations include
+    problems parsing your <code>/WEB-INF/web.xml</code> file, or missing
+    classes encountered when initializing application event listeners and
+    filters.</p>
+    </blockquote></li>
+<li><em>Invalid application URL was specified</em>
+    <blockquote>
+    <p>The URL for the <i>WAR or Directory URL:</i> field that you specified
+    was not valid.  Such URLs must start with <code>file:</code>, and URLs
+    for a WAR file must end in ".war".</p>
+    </blockquote></li>
+<li><em>Invalid context path was specified</em>
+    <blockquote>
+    <p>The context path must start with a slash character, unless you are
+    referencing the ROOT web application -- in which case the context path
+    must be a "/" string.</p>
+    </blockquote></li>
+<li><em>Context path must match the directory or WAR file name:</em>
+    <blockquote>
+    If the application war or directory is deployed in your Host appBase
+    directory and either the Host is configured with autoDeploy=true or
+    liveDeploy=true, the Context path must match the directory name or
+    war file name without the ".war" extension.
+    </blockquote></li>
+<li><em>Only web applications in the Host web application directory can
+     be deployed</em>
+     <blockquote>
+     If the Host deployXML flag is set to false this error will happen
+     if an attempt is made to install a web application directory or
+      ".war" file outside of the Host appBase directory.
+     </blockquote></li>
+</ul>
+
+</subsection>
+</section>
+
+<section name="Server Information">
+
+<p>This section displays information about Tomcat, the operating system of
+the server Tomcat is hosted on, and the Java Virtual Machine Tomcat is
+running in.</p>
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/images/add.gif b/container/webapps/docs/images/add.gif
new file mode 100644
index 0000000..0774d07
--- /dev/null
+++ b/container/webapps/docs/images/add.gif
Binary files differ
diff --git a/container/webapps/docs/images/code.gif b/container/webapps/docs/images/code.gif
new file mode 100644
index 0000000..d27307b
--- /dev/null
+++ b/container/webapps/docs/images/code.gif
Binary files differ
diff --git a/container/webapps/docs/images/design.gif b/container/webapps/docs/images/design.gif
new file mode 100644
index 0000000..f5db0a9
--- /dev/null
+++ b/container/webapps/docs/images/design.gif
Binary files differ
diff --git a/container/webapps/docs/images/docs.gif b/container/webapps/docs/images/docs.gif
new file mode 100644
index 0000000..d64a4a1
--- /dev/null
+++ b/container/webapps/docs/images/docs.gif
Binary files differ
diff --git a/container/webapps/docs/images/fix.gif b/container/webapps/docs/images/fix.gif
new file mode 100644
index 0000000..d59ad64
--- /dev/null
+++ b/container/webapps/docs/images/fix.gif
Binary files differ
diff --git a/container/webapps/docs/images/jakarta-logo.gif b/container/webapps/docs/images/jakarta-logo.gif
new file mode 100644
index 0000000..049cf82
--- /dev/null
+++ b/container/webapps/docs/images/jakarta-logo.gif
Binary files differ
diff --git a/container/webapps/docs/images/printer.gif b/container/webapps/docs/images/printer.gif
new file mode 100644
index 0000000..5021187
--- /dev/null
+++ b/container/webapps/docs/images/printer.gif
Binary files differ
diff --git a/container/webapps/docs/images/tomcat.gif b/container/webapps/docs/images/tomcat.gif
new file mode 100644
index 0000000..6175673
--- /dev/null
+++ b/container/webapps/docs/images/tomcat.gif
Binary files differ
diff --git a/container/webapps/docs/images/update.gif b/container/webapps/docs/images/update.gif
new file mode 100644
index 0000000..31e22ab
--- /dev/null
+++ b/container/webapps/docs/images/update.gif
Binary files differ
diff --git a/container/webapps/docs/images/void.gif b/container/webapps/docs/images/void.gif
new file mode 100644
index 0000000..e565824
--- /dev/null
+++ b/container/webapps/docs/images/void.gif
Binary files differ
diff --git a/container/webapps/docs/index.xml b/container/webapps/docs/index.xml
new file mode 100644
index 0000000..0254c99
--- /dev/null
+++ b/container/webapps/docs/index.xml
@@ -0,0 +1,173 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="index.html">
+
+  &project;
+
+  <properties>
+    <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>Documentation Index</title>
+  </properties>
+
+<body>
+
+<section name="Introduction">
+
+<p>This is the top-level entry point of the documentation bundle for the
+<strong>Apache Tomcat</strong> Servlet/JSP container.  Apache Tomcat version 5.5 
+implements the
+Servlet 2.4 and JavaServer Pages 2.0 specifications from the
+<a href="http://www.jcp.org">Java Community Process</a>, and includes many
+additional features that make it a useful platform for developing and deploying
+web applications and web services.</p>
+
+<p>Select one of the links from the navigation menu (to the left) to drill
+down to the more detailed documentation that is available.  Each available
+manual is described in more detail below.</p>
+
+</section>
+
+
+<section name="Apache Tomcat User Guide">
+
+<p>The following documents will assist you in downloading, installing
+Apache Tomcat 5, and using many of the Apache Tomcat features.</p>
+
+<ol>
+<li><a href="introduction.html"><strong>Introduction</strong></a> - A
+    brief, high level, overview of Apache Tomcat.</li>
+<li><a href="setup.html"><strong>Setup</strong></a> - How to install and run
+    Apache Tomcat on a variety of platforms.</li>
+<li><a href="appdev/index.html"><strong>First web application</strong></a>
+    - An introduction to the concepts of a <em>web application</em> as defined
+    in the <a href="http://java.sun.com/products/servlet/download.html">Servlet
+    2.3 Specification</a>.  Covers basic organization of your web application
+    source tree, the structure of a web application archive, and an
+    introduction to the web application deployment descriptor
+    (<code>/WEB-INF/web.xml</code>).</li>
+<li><a href="deployer-howto.html"><strong>Deployer</strong></a> -
+    Operating the Apache Tomcat Deployer to deploy, precompile, and validate web
+    applications.</li>
+<li><a href="manager-howto.html"><strong>Manager</strong></a> -
+    Operating the <code>Manager</code> web app to deploy, undeploy, and
+    redeploy applications while Apache Tomcat is running.</li>
+<li><a href="realm-howto.html"><strong>Realms and Access Control</strong></a>
+    - Description of how to configure <em>Realms</em> (databases of users,
+    passwords, and their associated roles) for use in web applications that
+    utilize <em>Container Managed Security</em>.</li>
+<li><a href="security-manager-howto.html"><strong>Security Manager</strong></a>
+    - Configuring and using a Java Security Manager to
+    support fine-grained control over the behavior of your web applications.
+    </li>
+<li><a href="jndi-resources-howto.html"><strong>JNDI Resources</strong></a>
+    - Configuring standard and custom resources in the JNDI naming context
+    that is provided to each web application.</li>
+<li><a href="jndi-datasource-examples-howto.html">
+    <strong>JDBC DataSource</strong></a>
+    - Configuring a JNDI DataSoure with a DB connection pool.
+    Examples for many popular databases.</li>
+<li><a href="class-loader-howto.html"><strong>Classloading</strong></a>
+    - Information about class loading in Apache Tomcat 5, including where to place
+    your application classes so that they are visible.</li>
+<li><a href="jasper-howto.html"><strong>JSPs</strong></a>
+    - Information about Jasper configuration, as well as the JSP compiler
+    usage.</li>
+<li><a href="ssl-howto.html"><strong>SSL</strong></a> -
+    Installing and
+    configuring SSL support so that your Apache Tomcat will serve requests using
+    the <code>https</code> protocol.</li>
+<li><a href="ssi-howto.html"><strong>SSI</strong></a> -
+    Using Server Side Includes in Apache Tomcat.</li>
+<li><a href="cgi-howto.html"><strong>CGI</strong></a> -
+    Using CGIs with Apache Tomcat.</li>
+<li><a href="proxy-howto.html"><strong>Proxy Support</strong></a> -
+    Configuring Apache Tomcat 5 to run behind a proxy server (or a web server
+    functioning as a proxy server).</li>
+<li><a href="mbeans-descriptor-howto.html"><strong>MBean Descriptor</strong></a> -
+    Configuring MBean descriptors files for custom components.</li>
+<li><a href="default-servlet.html"><strong>Default Servlet</strong></a> -
+    Configuring the default servlet and customizing directory listings.</li>
+<li><a href="cluster-howto.html"><strong>Apache Tomcat Clustering</strong></a> -
+    Enable session replication in a Apache Tomcat environment.</li>
+<li><a href="balancer-howto.html"><strong>Balancer</strong></a> -
+    Configuring, using, and extending the load balancer application.</li>
+<li><a href="connectors.html"><strong>Connectors</strong></a> -
+    Connectors available in Apache Tomcat, and native web server integration.</li>
+<li><a href="monitoring.html"><strong>Monitoring and Management</strong></a> -
+    Enabling JMX Remote support, and using tools to monitor and manage Apache Tomcat.</li>
+<li><a href="logging.html"><strong>Logging</strong></a> -
+    Confuguring logging in Apache Tomcat.</li>
+
+</ol>
+
+</section>
+
+
+<section name="Reference">
+
+<p>The following documents are aimed at <em>System Administrators</em> who
+are responsible for installing, configuring, and operating a Apache Tomcat 5 server.
+</p>
+<ul>
+<li><a href="RELEASE-NOTES.txt"><strong>Release notes</strong></a>
+    - Known issues in this Apache Tomcat release.
+    </li>
+<li><a href="config/index.html"><strong>Apache Tomcat Server Configuration Reference</strong></a>
+    - Reference manual that documents all available elements and attributes
+      that may be placed into a Apache Tomcat 5 <code>conf/server.xml</code> file.
+    </li>
+<li><a href="http://jakarta.apache.org/tomcat/connectors-doc/index.html"><strong>JK Documentation</strong></a>
+    - Complete documentation and HOWTOs on the JK native webserver connector,
+      used to interface Apache Tomcat with servers like Apache HTTPd, IIS
+      and others.</li>
+<li><a href="servletapi/index.html"><strong>Servlet API Javadocs</strong></a> -
+    The Servlet 2.4 API Javadocs.</li>
+<li><a href="jspapi/index.html"><strong>JSP API Javadocs</strong></a> -
+    The JSP 2.0 API Javadocs.</li>
+</ul>
+
+</section>
+
+
+<section name="Apache Tomcat Developers">
+
+<p>The following documents are for Java developers who wish to contribute to
+the development of the <em>Apache Tomcat</em> project.</p>
+<ul>
+<li><a href="building.html"><strong>Building from Source</strong></a> - 
+    Details the steps necessary to download Apache Tomcat 5 source code (and the 
+    other packages that it depends on), and build a binary distribution from 
+    those sources.
+    </li>
+<li><a href="changelog.html"><strong>Changelog</strong></a> - Details the
+    changes made to Apache Tomcat.
+    </li>
+<li><a href="status.html"><strong>Status</strong></a> - Apache Tomcat development 
+    status.
+    </li>
+<li><a href="developers.html"><strong>Developers</strong></a> - List of active
+    Apache Tomcat contributors.
+    </li>
+<li><a href="catalina/funcspecs/index.html"><strong>Functional Specifications</strong></a>
+    - Requirements specifications for features of the <em>Catalina</em> servlet
+    container portion of Apache Tomcat 5.</li>
+<li><a href="catalina/docs/api/index.html"><strong>Catalina Javadocs</strong></a>
+    - Javadoc API documentation for the <em>Catalina</em> servlet
+    container and its dependencies.</li>
+<li><a href="jasper/docs/api/index.html"><strong>Jasper Javadocs</strong></a>
+    - Javadoc API documentation for the <em>Jasper</em> JSP container
+    portion of Apache Tomcat 5.</li>
+<li><a href="architecture/index.html"><strong>Apache Tomcat Architecture</strong></a>
+    - Documentation of the Apache Tomcat Server Architecture.</li>
+    
+</ul>
+
+</section>
+
+
+</body>
+</document>
diff --git a/container/webapps/docs/introduction.xml b/container/webapps/docs/introduction.xml
new file mode 100644
index 0000000..c9ab051
--- /dev/null
+++ b/container/webapps/docs/introduction.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="introduction.html">
+
+    &project;
+
+    <properties>
+        <author email="rslifka@sfu.ca">Robert Slifka</author>
+        <title>Introduction</title>
+    </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+<p>For administrators and web developers alike, there are some important bits
+of information you should familiarize yourself with before starting out. This
+document serves as a brief introduction to some of the concepts and
+terminology behind the Tomcat container. As well, where to go when you need
+help.</p>
+
+</section>
+
+
+<section name="Terminology">
+
+<p>In the course of reading these documents, you'll run across a number of
+terms; some specific to Tomcat, and others defined by the
+<a href="http://java.sun.com/products/servlet/">Servlet</a> or
+<a href="http://java.sun.com/products/jsp/">JSP</a> specifications.</p>
+
+<ul>
+<li><strong>Context</strong> - In a nutshell, a Context is a
+    web application.</li>
+<li><strong>Term2</strong> - This is it.</li>
+<li><strong>Term3</strong> - This is it!</li>
+</ul>
+
+</section>
+
+
+<section name="Directories and Files">
+
+<p>Throughout the docs, you'll notice there are numerous references to
+<strong>$CATALINA_HOME</strong>. This represents the root of your Tomcat
+installation. When we say, "This information can be found in your
+$CATALINA_HOME/README.txt file" we mean to look at the README.txt file at the
+root of your Tomcat install.</p>
+
+<p>These are some of the key tomcat directories, all relative
+to <strong>$CATALINA_HOME</strong>:</p>
+
+<ul>
+<li><strong>/bin</strong> - Startup, shutdown, and other scripts. The
+    <code>*.sh</code> files (for Unix systems) are functional duplicates of
+    the <code>*.bat</code> files (for Windows systems).  Since the Win32
+    command-line lacks certain functionality, there are some additional
+    files in here.</li>
+<li><strong>/conf</strong> - Configuration files and related DTDs.  The most
+    important file in here is server.xml.  It is the main configuration file
+    for the container.</li>
+<li><strong>/logs</strong> - Log files are here by default.</li>
+<li><strong>/webapps</strong> - This is where your webapps go.</li>
+</ul>
+
+</section>
+
+
+<section name="Configuring Tomcat">
+
+<p>This section will acquaint you with the basic information used during
+the configuration of the container.</p>
+
+<p>All of the information in the configuration files is read at startup,
+meaning that any change to the files necessitates a restart of the container.
+</p>
+
+</section>
+
+
+<section name="Where to Go for Help">
+
+<p>While we've done our best to ensure that these documents are clearly
+written and easy to understand, we may have missed something.  Provided
+below are various web sites and mailing lists in case you get stuck.</p>
+
+<p>As Tomcat 5 is a new release of Tomcat, keep in mind that some of the
+issues and solutions vary between the major versions of Tomcat (4.x versus
+5).  As you search around the web, there will be some documentation that
+is not relevant to Tomcat 5, but 3.x and 4.x.  Doing 3.x or 4.x things to 5
+will probably not work in most cases as the server.xml files are very
+different.</p>
+
+<ul>
+<li>Current document - most documents will list potential hangups. Be sure
+    to fully read the relevant documentation as it will save you much time
+    and effort. There's nothing like scouring the web only to find out that
+    the answer was right in front of you all along!</li>
+<li><a href="http://jakarta.apache.org/tomcat/faq/">Tomcat FAQ</a> as maintained by the developers.</li>
+<li><a href="http://wiki.apache.org/jakarta-tomcat/">Tomcat WIKI</a></li>
+<li>Tomcat FAQ at <a href="http://www.jguru.com/faq/home.jsp?topic=Tomcat">jGuru</a></li>
+<li>Tomcat mailing list archives - numerous sites archive the Tomcat mailing
+    lists. Since the links change over time, clicking here will search
+    <a href="http://www.google.com/search?q=tomcat+mailing+list+archives">Google</a>.
+    </li>
+<li>The TOMCAT-USER mailing list, which you can subscribe to
+    <a href="http://jakarta.apache.org/site/mail.html">here</a>. If you don't
+    get a reply, then there's a good chance that your question was probably
+    answered in the list archives or one of the FAQs.  Although questions
+    about web application development in general are sometimes asked and
+    answered, please focus your questions on Tomcat-specific issues.</li>
+<li>The TOMCAT-DEV mailing list, which you can subscribe to
+    <a href="http://jakarta.apache.org/site/mail.html">here</a>.  This list is
+    <strong>reserved</strong> for discussions about the development of Tomcat
+    itself.  Questions about Tomcat configuration, and the problems you run
+    into while developing and running applications, will normally be more
+    appropriate on the TOMCAT-USER list instead.</li>
+</ul>
+
+<p>And, if you think something should be in the docs, by all means let us know
+on the TOMCAT-DEV list, or send one of the doc authors email.</p>
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/jasper-howto.xml b/container/webapps/docs/jasper-howto.xml
new file mode 100644
index 0000000..fe0f511
--- /dev/null
+++ b/container/webapps/docs/jasper-howto.xml
@@ -0,0 +1,310 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="jasper-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="glenn@apache.org">Glenn L. Nielsen</author>
+        <title>Jasper 2 JSP Engine How To</title>
+    </properties>
+
+<body>
+
+<section name="Table of Contents">
+<p>
+<a href="#Introduction">Introduction</a><br />
+<a href="#Configuration">Configuration</a><br />
+<a href="#Production Configuration">Production Configuration</a><br />
+<a href="#Web Application Compilation">Web Application Compilation</a><br />
+<a href="#Using Jikes">Using Jikes</a><br />
+</p>
+</section>
+
+<section name="Introduction">
+
+<p>Tomcat 5.5 uses the Jasper 2 JSP Engine to implement
+the <a href="http://java.sun.com/products/jsp/">JavaServer Pages 2.0</a>
+specification.</p>
+
+<p>Jasper 2 has been redesigned to significantly improve performance over
+the orignal Jasper.  In addition to general code improvements the following
+changes were made:
+<ul>
+<li><strong>JSP Custom Tag Pooling</strong> - The java objects instantiated
+for JSP Custom Tags can now be pooled and reused.  This significantly boosts
+the performance of JSP pages which use custom tags.</li>
+<li><strong>Background JSP compilation</strong> - If you make a change to
+a JSP page which had already been compiled Jasper 2 can recompile that
+page in the background.  The previously compiled JSP page will still be
+available to serve requests.  Once the new page has been compiled
+successfully it will replace the old page.  This helps improve availablity
+of your JSP pages on a production server.</li>
+<li><strong>Recompile JSP when included page changes</strong> - Jasper 2
+can now detect when a page included at compile time from a JSP has changed
+and then recompile the parent JSP.</li>
+<li><strong>JDT used to compile JSP pages</strong> - The
+Eclipse JDT Java compiler is now used to perform JSP java source code
+compilation. This compiler loads source dependencies from the container
+classloader. Ant and javac can still be used.</li>
+</ul>
+</p>
+
+<p>Jasper is implemented using the servlet class
+<code>org.apache.jasper.servlet.JspServlet</code>.</p>
+
+</section>
+
+<section name="Configuration">
+
+<p>By default Jasper is configured for use when doing web application
+development.  See the section <a href="#Production Configuration">
+Production Configuration</a> for information on configuring Jasper
+for use on a production Tomcat server.</p>
+
+<p>The servlet which implements Jasper is configured using init parameters
+in your global <code>$CATALINA_BASE/conf/web.xml</code>.
+
+<ul>
+<li><strong>checkInterval</strong> - If development is false and reloading is
+true, background compiles are enabled. checkInterval is the time in seconds
+between checks to see if a JSP page needs to be recompiled. Default
+<code>300</code> seconds.</li>
+
+<li><strong>compiler</strong> - Which compiler Ant should use to compile JSP
+pages.  See the Ant documentation for more information. If the value is not set,
+then the default Eclipse JDT Java compiler will be used instead of using Ant. 
+No default value.</li>
+
+<li><strong>classdebuginfo</strong> - Should the class file be compiled with
+debugging information?  <code>true</code> or <code>false</code>, default
+<code>true</code>.
+</li>
+
+<li><strong>classpath</strong> - What class path should I use while compiling
+generated servlets?  By default the classpath is created dynamically based on
+the current web application.</li>
+
+<li><strong>compilerSourceVM</strong> - What JDK version are the source files compatible with? (Default JDK 1.4)</li>
+
+<li><strong>compilerTargetVM</strong> - What JDK version are the generated files compatible with? (Default JDK 1.4)</li>
+
+<li><strong>development</strong> - Is Jasper used in development mode (will
+check for JSP modification on every access)? <code>true</code> or
+<code>false</code>, default <code>true</code>.</li>
+
+<li><strong>enablePooling</strong> - Determines whether tag handler pooling is
+enabled. <code>true</code> or <code>false</code>, default <code>true</code>.
+</li>
+
+<li><strong>engineOptionsClass</strong> - Allows specifying the Options class
+used to configure Jasper. If not present, the default EmbeddedServletOptions
+will be used.
+</li>
+
+<li><strong>ieClassId</strong> - The class-id value to be sent to Internet
+Explorer when using &lt;jsp:plugin&gt; tags.   Default
+<code>clsid:8AD9C840-044E-11D1-B3E9-00805F499D93</code>.</li>
+
+<li><strong>fork</strong> - Have Ant fork JSP page compiles so they are
+performed in a seperate JVM from Tomcat? <code>true</code> or
+<code>false</code>, default <code>true</code>.</li>
+
+<li><strong>javaEncoding</strong> - Java file encoding to use for generating
+java source files. Default <code>UTF8</code>.</li>
+
+<li><strong>genStrAsCharArray</strong> - Should text strings be generated as char
+arrays, to improve performance in some cases? Default <code>false</code>.</li>
+
+<li><strong>keepgenerated</strong> - Should we keep the generated Java source
+code for each page instead of deleting it? <code>true</code> or
+<code>false</code>, default <code>true</code>.</li>
+
+<li><strong>mappedfile</strong> - Should we generate static content with one 
+print statement per input line, to ease debugging?
+<code>true</code> or <code>false</code>, default <code>true</code>.</li>
+
+<li><strong>modificationTestInterval</strong> - Checks for modification for a given
+JSP file (and all its dependent files) will be performed only once every specified amount
+of seconds. Setting this to 0 will cause the JSP to be checked on every access.
+Default is <code>4</code> seconds.</li>
+
+<li><strong>reloading</strong> - Should Jasper check for modified JSPs?
+<code>true</code> or <code>false</code>, default <code>false</code>.</li>
+
+<li><strong>scratchdir</strong> - What scratch directory should we use when
+compiling JSP pages? Default is the work directory for the current web
+application.</li>
+
+<li><strong>trimSpaces</strong> - Should white spaces in template text between
+actions or directives be trimmed ?, default <code>false</code>.</li>
+</ul>
+</p>
+
+<p>The Java compiler from Eclipse JDT in included as the default compiler. It is an
+advanced Java compiler which will load all dependencies from the Tomcat class loader, 
+which will help tremendously when compiling on large installations with tens of JARs.
+On fast servers, this will allow sub-second recompilation cycles for even large JSP 
+pages. This new compiler will be updated to support the Java 5 syntax as soon as
+possible.</p>
+
+<p>Apache Ant, which was used in previous Tomcat releases, can be used instead instead of 
+the new compiler by simply removing the <code>common/lib/jasper-compiler-jdt.jar</code> file, 
+and placing the <code>ant.jar</code> file from the latest Ant distribution in the 
+<code>common/lib</code> folder.  If you do this, you also need to use the "javac"
+argument to catalina.sh.</p>
+
+</section>
+
+<section name="Production Configuration">
+
+<p>The main JSP optimization which can be done is precompilation of JSPs. However,
+this might not be possible (for example, when using the jsp-property-group feature)
+or practical, in which case the configuration of the Jasper servlet becomes critical.</p>
+
+<p>When using Jasper 2 in a production Tomcat server you should consider
+making the following changes from the default configuration.
+<ul>
+<li><strong>development</strong> - To disable on access checks for JSP
+pages compilation set this to <code>false</code>.</li>
+<li><strong>genStrAsCharArray</strong> - To generate slightly more efficient 
+char arrays, set this to <code>true</code>.</li>
+<li><strong>modificationTestInterval</strong> - If development has to be set to
+<code>true</code> for any reason (such as dynamic generation of JSPs), setting
+this to a high value will improve performance a lot.</li>
+<li><strong>trimSpaces</strong> - To remove useless bytes from the response,
+set this to <code>true</code>.</li>
+</ul>
+</p>
+
+</section>
+
+<section name="Web Application Compilation">
+
+<p>Using Ant is the preferred way to compile web applications using JSPC. 
+Use the script given below (a similar script is included in the "deployer" 
+download) to precompile a webapp:
+</p>
+
+<p>
+<source>
+&lt;project name="Webapp Precompilation" default="all" basedir="."&gt; 
+
+  &lt;target name="jspc"&gt; 
+
+    &lt;taskdef classname="org.apache.jasper.JspC" name="jasper2" &gt; 
+      &lt;classpath id="jspc.classpath"&gt; 
+        &lt;pathelement location="${java.home}/../lib/tools.jar"/&gt; 
+        &lt;fileset dir="${tomcat.home}/bin"&gt; 
+          &lt;include name="*.jar"/&gt; 
+        &lt;/fileset&gt; 
+        &lt;fileset dir="${tomcat.home}/server/lib"&gt; 
+          &lt;include name="*.jar"/&gt; 
+        &lt;/fileset&gt; 
+        &lt;fileset dir="${tomcat.home}/common/lib"&gt; 
+          &lt;include name="*.jar"/&gt; 
+        &lt;/fileset&gt; 
+      &lt;/classpath&gt; 
+    &lt;/taskdef&gt; 
+
+    &lt;jasper2 
+             validateXml="false" 
+             uriroot="${webapp.path}" 
+             webXmlFragment="${webapp.path}/WEB-INF/generated_web.xml" 
+             outputDir="${webapp.path}/WEB-INF/src" /&gt; 
+
+  &lt;/target&gt; 
+
+  &lt;target name="compile"&gt;
+
+    &lt;mkdir dir="${webapp.path}/WEB-INF/classes"/&gt;
+    &lt;mkdir dir="${webapp.path}/WEB-INF/lib"/&gt;
+
+    &lt;javac destdir="${webapp.path}/WEB-INF/classes"
+           optimize="off"
+           debug="on" failonerror="false"
+           srcdir="${webapp.path}/WEB-INF/src" 
+	   excludes="**/*.smap"&gt;
+      &lt;classpath&gt;
+        &lt;pathelement location="${webapp.path}/WEB-INF/classes"/&gt;
+        &lt;fileset dir="${webapp.path}/WEB-INF/lib"&gt;
+          &lt;include name="*.jar"/&gt;
+        &lt;/fileset&gt;
+        &lt;pathelement location="${tomcat.home}/common/classes"/&gt;
+        &lt;fileset dir="${tomcat.home}/common/lib"&gt;
+          &lt;include name="*.jar"/&gt;
+        &lt;/fileset&gt;
+        &lt;pathelement location="${tomcat.home}/shared/classes"/&gt;
+        &lt;fileset dir="${tomcat.home}/shared/lib"&gt;
+          &lt;include name="*.jar"/&gt;
+        &lt;/fileset&gt;
+        &lt;fileset dir="${tomcat.home}/bin"&gt; 
+          &lt;include name="*.jar"/&gt; 
+        &lt;/fileset&gt; 
+      &lt;/classpath&gt;
+      &lt;include name="**" /&gt;
+      &lt;exclude name="tags/**" /&gt;
+    &lt;/javac&gt;
+
+  &lt;/target&gt;
+
+  &lt;target name="all" depends="jspc,compile"&gt;
+  &lt;/target&gt;
+
+&lt;/project&gt;
+</source>
+</p>
+
+<p>
+The following command line can be used to run the script
+(replacing the tokens with the Tomcat base path and the path to the webapp 
+which should be precompiled):<br/>
+<source>
+$ANT_HOME/bin/ant -Dtomcat.home=&lt;$TOMCAT_HOME&gt; -Dwebapp.path=&lt;$WEBAPP_PATH&gt;
+</source>
+</p>
+
+<p>
+Then, the declarations and mappings for the servlets which were generated 
+during the precompilation must be added to the web application deployment
+descriptor. Insert the <code>${webapp.path}/WEB-INF/generated_web.xml</code>
+at the right place inside the <code>${webapp.path}/WEB-INF/web.xml</code> file.
+Restart the web application (using the manager) and test it to verify it is 
+running fine with precompiled servlets. An appropriate token placed in the
+web application deployment descriptor may also be used to automatically
+insert the generated servlet declarations and mappings using Ant filtering 
+capabilities. This is actually how all the webapps distributed with Tomcat 
+are automatically compiled as part of the build process.
+</p>
+
+</section>
+
+<section name="Using Jikes">
+
+<p>If you wish to use
+<a href="http://oss.software.ibm.com/developerworks/opensource/jikes/">
+Jikes</a> to compile JSP pages:
+<ul>
+<li>From your <a href="ant.apache.org">Ant</a> installation, copy ant.jar
+and (if it's available: Ant 1.5 and later) ant-launcher.jar to 
+<code>$CATALINA_BASE/common/lib</code>.</li>
+<li>Download and install jikes. jikes must support the -encoding option.
+Execute <code>jikes -help</code> to verify that it was built with support
+for <code>-encoding</code>.</li>
+<li>Set the init parameter <code>compiler</code> to <code>jikes</code>.</li>
+<li>Define the property <code>-Dbuild.compiler.emacs=true</code> when starting
+Tomcat by adding it to your <code>CATALINA_OPTS</code> environment variable.
+This changes how jikes outputs error messages so that it is compatible with
+Jasper.</li>
+<li>If you get an error reporting that jikes can't use UTF8 encoding, try
+setting the init parameter <code>javaEncoding</code> to
+<code>ISO-8859-1</code>.</li>
+</ul>
+</p>
+
+</section>
+</body>
+
+</document>
diff --git a/container/webapps/docs/jasper/docs/api/index.html b/container/webapps/docs/jasper/docs/api/index.html
new file mode 100644
index 0000000..4fe8f9a
--- /dev/null
+++ b/container/webapps/docs/jasper/docs/api/index.html
@@ -0,0 +1,17 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html>
+    <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <title>Administration</title>
+</head>
+
+<body>
+
+Tomcat's internal javadoc is no longer installed by default. Download and install 
+the "fulldocs" package to get it.
+
+You can also access the javadoc online in the Tomcat 
+<a href="http://jakarta.apache.org/tomcat/tomcat-5.5-doc/">documentation bundle</a>.
+
+</body>
+</html>
diff --git a/container/webapps/docs/jndi-datasource-examples-howto.xml b/container/webapps/docs/jndi-datasource-examples-howto.xml
new file mode 100644
index 0000000..80a4926
--- /dev/null
+++ b/container/webapps/docs/jndi-datasource-examples-howto.xml
@@ -0,0 +1,641 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="jndi-datasource-examples-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="leslie.hughes@rubus.com">Les Hughes</author>
+        <author email="david-tomcat@haraburda.com">David Haraburda</author>
+        <author>Glenn Nielsen</author>
+        <author email="yoavs@apache.org">Yoav Shapira</author>
+        <title>JNDI Datasource HOW-TO</title>
+    </properties>
+
+<body>
+
+<section name="Table of Contents">
+<p>
+<a href="#Introduction">Introduction</a><br />
+<a href="#Database Connection Pool (DBCP) Configurations">
+Database Connection Pool (DBCP) Configurations</a><br />
+<a href="#Non DBCP Solutions">Non DBCP Solutions</a><br />
+<a href="#Oracle 8i with OCI client">Oracle 8i with OCI client</a><br />
+<a href="#Common Problems">Common Problems</a><br />
+</p>
+</section>
+
+<section name="Introduction">
+
+<p>JNDI Datasource configuration is covered extensively in the
+JNDI-Resources-HOWTO.  However, feedback from <code>tomcat-user</code> has
+shown that specifics for individual configurations can be rather tricky.</p>
+
+<p>Here then are some example configurations that have been posted to
+tomcat-user for popular databases and some general tips for db useage.</p>
+
+<p>You should be aware that since these notes are derived from configuration
+and/or feedback posted to <code>tomcat-user</code> YMMV :-). Please let us
+know if you have any other tested configurations that you feel may be of use
+to the wider audience, or if you feel we can improve this section in anyway.</p>
+
+<p>
+<b>Please note that JNDI resource configuration has changed somewhat between
+Tomcat 5.0.x and Tomcat 5.5.x.</b>  You will most likely need to modify your JNDI
+resource configurations to match the syntax in the example below in order
+to make them work in Tomcat 5.5.x.
+</p>
+
+<p>
+Also, please note that JNDI DataSource configuration in general, and this 
+tutorial in particular, assumes that you have read and understood the 
+<a href="config/context.html">Context</a> and 
+<a href="config/host.html">Host</a> configuration references, including
+the section about Automatic Application Deployment in the latter reference.
+</p>
+</section>
+
+<section name="Database Connection Pool (DBCP) Configurations">
+
+<p>DBCP provides support for JDBC 2.0.  On systems using a 1.4 JVM DBCP
+will support JDBC 3.0. Please let us know if you have used DBCP and its
+JDBC 3.0 features with a 1.4 JVM.
+</p>
+
+<p>See the <a href="http://jakarta.apache.org/commons/dbcp/api/index.html">
+DBCP Javadocs</a> BasicDataSource class for a complete list
+of configuration parameters.
+</p>
+
+<subsection name="Installation">
+<p>DBCP uses the Jakarta-Commons Database Connection Pool. It relies on
+number of Jakarta-Commons components:
+<ul>
+<li>Jakarta-Commons DBCP</li>
+<li>Jakarta-Commons Collections</li>
+<li>Jakarta-Commons Pool</li>
+</ul>
+These libraries are located in a single JAR at 
+<code>$CATALINA_HOME/common/lib/naming-factory-dbcp.jar</code>. However,
+only the classes needed for connection pooling have been included, and the
+packages have been renamed to avoid interfering with applications.
+</p>
+
+</subsection>
+
+<subsection name="Preventing dB connection pool leaks">
+
+<p>
+A database connection pool creates and manages a pool of connections
+to a database. Recycling and reusing already existing connections
+to a dB is more efficient than opening a new connection.
+</p>
+
+<p>
+There is one problem with connection pooling.  A web application has
+to explicetely close ResultSet's, Statement's, and Connection's.
+Failure of a web application to close these resources can result in
+them never being available again for reuse, a db connection pool "leak".
+This can eventually result in your web application db connections failing
+if there are no more available connections.</p>
+
+<p>
+There is a solution to this problem.  The Jakarta-Commons DBCP can be
+configured to track and recover these abandoned dB connections.  Not
+only can it recover them, but also generate a stack trace for the code
+which opened these resources and never closed them.</p>
+
+<p>
+To configure a DBCP DataSource so that abandoned dB connections are
+removed and recycled add the following attribute to the
+<code>Resource</code> configuration for your DBCP DataSource:
+<source>
+            removeAbandoned="true"
+</source>
+When available db connections run low DBCP will recover and recyle
+any abandoned dB connections it finds. The default is <code>false</code>.
+</p>
+
+<p>
+Use the <code>removeAbandonedTimeout</code> attribute to set the number
+of seconds a dB connection has been idle before it is considered abandoned.
+<source>
+            removeAbandonedTimeout="60"
+</source>
+The default timeout for removing abandoned connections is 300 seconds.
+</p>
+
+<p>
+The <code>logAbandoned</code> attribute can be set to <code>true</code>
+if you want DBCP to log a stack trace of the code which abandoned the
+dB connection resources.
+<source>
+            logAbandoned="true"
+</source>
+The default is <code>false</code>.
+</p>
+
+</subsection>
+
+<subsection name="MySQL DBCP Example">
+
+<h3>0. Introduction</h3>
+<p>Versions of <a href="http://www.mysql.com/products/mysql/index.html">MySQL</a> and JDBC drivers that have been reported to work:
+<ul>
+<li>MySQL 3.23.47, MySQL 3.23.47 using InnoDB,, MySQL 3.23.58,  MySQL 4.0.1alpha</li>
+<li><a href="http://www.mysql.com/products/connector-j">Connector/J</a> 3.0.11-stable (the official JDBC Driver)</li>
+<li><a href="http://mmmysql.sourceforge.net">mm.mysql</a> 2.0.14 (an old 3rd party JDBC Driver)</li>
+</ul>
+</p>
+
+<p>Before you proceed, don't forget to copy the JDBC Driver's jar into <code>$CATALINA_HOME/common/lib</code>.</p>
+
+<h3>1. MySQL configuration</h3>
+<p>
+Ensure that you follow these instructions as variations can cause problems.
+</p>
+
+<p>Create a new test user, a new database and a single test table.
+Your MySQL user <strong>must</strong> have a password assigned. The driver
+will fail if you try to connect with an empty password.
+<source>
+mysql&gt; GRANT ALL PRIVILEGES ON *.* TO javauser@localhost 
+    -&gt;   IDENTIFIED BY 'javadude' WITH GRANT OPTION;
+mysql&gt; create database javatest;
+mysql&gt; use javatest;
+mysql&gt; create table testdata (
+    -&gt;   id int not null auto_increment primary key,
+    -&gt;   foo varchar(25), 
+    -&gt;   bar int);
+</source>
+<blockquote>
+<strong>Note:</strong> the above user should be removed once testing is
+complete!
+</blockquote>
+</p>
+
+<p>Next insert some test data into the testdata table.
+<source>
+mysql&gt; insert into testdata values(null, 'hello', 12345);
+Query OK, 1 row affected (0.00 sec)
+
+mysql> select * from testdata;
++----+-------+-------+
+| ID | FOO   | BAR   |
++----+-------+-------+
+|  1 | hello | 12345 |
++----+-------+-------+
+1 row in set (0.00 sec)
+
+mysql&gt;
+</source>
+</p>
+
+<h3>2. server.xml configuration</h3>
+<p>Configure the JNDI DataSource in Tomcat by adding a declaration for your
+resource to <code>$CATALINA_HOME/conf/server.xml</code>.</p>
+<p>Add this in between the <code>&lt;/Context&gt;</code> tag of the examples
+context and the <code>&lt;/Host&gt;</code> tag closing the localhost definition.
+If there is no such tag, you can add one as illustrated in the 
+<a href="config/context.html">Context</a> and
+<a href="config/host.html">Host</a> configuration references, and repeated below
+for your convenience.
+
+<source>
+&lt;Context path="/DBTest" docBase="DBTest"
+        debug="5" reloadable="true" crossContext="true"&gt;
+
+    &lt;!-- maxActive: Maximum number of dB connections in pool. Make sure you
+         configure your mysqld max_connections large enough to handle
+         all of your db connections. Set to 0 for no limit.
+         --&gt;
+
+    &lt;!-- maxIdle: Maximum number of idle dB connections to retain in pool.
+         Set to -1 for no limit.  See also the DBCP documentation on this
+         and the minEvictableIdleTimeMillis configuration parameter.
+         --&gt;
+
+    &lt;!-- maxWait: Maximum time to wait for a dB connection to become available
+         in ms, in this example 10 seconds. An Exception is thrown if
+         this timeout is exceeded.  Set to -1 to wait indefinitely.
+         --&gt;
+
+    &lt;!-- username and password: MySQL dB username and password for dB connections  --&gt;
+
+    &lt;!-- driverClassName: Class name for the old mm.mysql JDBC driver is
+         org.gjt.mm.mysql.Driver - we recommend using Connector/J though.
+         Class name for the official MySQL Connector/J driver is com.mysql.jdbc.Driver.
+         --&gt;
+    
+    &lt;!-- url: The JDBC connection url for connecting to your MySQL dB.
+         The autoReconnect=true argument to the url makes sure that the
+         mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
+         connection.  mysqld by default closes idle connections after 8 hours.
+         --&gt;
+
+  &lt;Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
+               maxActive="100" maxIdle="30" maxWait="10000"
+               username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver"
+               url="jdbc:mysql://localhost:3306/javatest?autoReconnect=true"/&gt;
+
+&lt;/Context&gt;
+</source>
+</p>
+
+<h3>3. web.xml configuration</h3>
+
+<p>Now create a <code>WEB-INF/web.xml</code> for this test application.
+<source>
+&lt;web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
+http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4"&gt;
+  &lt;description&gt;MySQL Test App&lt;/description&gt;
+  &lt;resource-ref&gt;
+      &lt;description&gt;DB Connection&lt;/description&gt;
+      &lt;res-ref-name&gt;jdbc/TestDB&lt;/res-ref-name&gt;
+      &lt;res-type&gt;javax.sql.DataSource&lt;/res-type&gt;
+      &lt;res-auth&gt;Container&lt;/res-auth&gt;
+  &lt;/resource-ref&gt;
+&lt;/web-app&gt;
+</source>
+</p>
+
+<h3>4. Test code</h3>
+<p>Now create a simple <code>test.jsp</code> page for use later.
+<source>
+&lt;%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %&gt;
+&lt;%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %&gt;
+
+&lt;sql:query var="rs" dataSource="jdbc/TestDB"&gt;
+select id, foo, bar from testdata
+&lt;/sql:query&gt;
+
+&lt;html&gt;
+  &lt;head&gt;
+    &lt;title&gt;DB Test&lt;/title&gt;
+  &lt;/head&gt;
+  &lt;body&gt;
+
+  &lt;h2&gt;Results&lt;/h2&gt;
+  
+&lt;c:forEach var="row" items="${rs.rows}"&gt;
+    Foo ${row.foo}&lt;br/&gt;
+    Bar ${row.bar}&lt;br/&gt;
+&lt;/c:forEach&gt;
+
+  &lt;/body&gt;
+&lt;/html&gt;
+</source>
+</p>
+
+<p>That JSP page makes use of <a href="http://java.sun.com/products/jsp/jstl">JSTL</a>'s SQL and Core taglibs. You can get it from Sun's <a href="http://java.sun.com/webservices/downloads/webservicespack.html">Java Web Services Developer Pack</a> or <a href="http://jakarta.apache.org/taglibs/doc/standard-doc/intro.html">Jakarta Taglib Standard 1.1</a> project - just make sure you get a 1.1.x release. Once you have JSTL, copy <code>jstl.jar</code> and <code>standard.jar</code> to your web app's <code>WEB-INF/lib</code> directory.
+
+</p>
+
+<p>Finally deploy your web app into <code>$CATALINA_HOME/webapps</code> either
+as a warfile called <code>DBTest.war</code> or into a sub-directory called
+<code>DBTest</code></p>
+<p>Once deployed, point a browser at
+<code>http://localhost:8080/DBTest/test.jsp</code> to view the fruits of
+your hard work.</p>
+
+</subsection>
+
+<subsection name="Oracle 8i">
+<h3>0.    Introduction</h3>
+<p><i>We would appreciate comments on this section as I'm not an Oracle DBA :-)</i></p>
+<p>Oracle requires minimal changes from the MySQL configuration except for the usual gotchas :-) Firstly
+by default, Tomcat will only use <code>*.jar</code> files installed in <code>$CATALINA_HOME/common/lib</code>
+ therefore <code>classes111.zip</code> or <code>classes12.zip</code> will need to be renamed with a <code>.jar</code> 
+extension. Since jarfiles are zipfiles, there is no need to unzip and jar these files - a simple rename will suffice.
+Also, you should be aware that some (early) versions of Tomcat 4.0 when used with JDK 1.4 will not load classes12.zip unless
+you unzip the file, remove the <code>javax.sql.*</code> class heirarchy and rejar.</p>
+<h3>1.    server.xml configuration</h3>
+<p>In a similar manner to the mysql config above, you will need to define your Datasource in your server.xml
+file. Here we define a Datasource called myoracle using the thin driver to connect as user scott, password tiger
+to the schema called myschema in the sid called mysid. (Note: with the thin driver this sid is not the same as the tnsname)</p>
+
+<p>Use of the OCI driver should simply involve a changing thin to oci in the URL string.
+<source>
+&lt;Resource name="jdbc/myoracle" auth="Container"
+              type="javax.sql.DataSource" driverClassName="oracle.jdbc.driver.OracleDriver"
+              url="jdbc:oracle:thin:myschema@127.0.0.1:1521:mysid"
+              username="scott" password="tiger" maxActive="20" maxIdle="10"
+              maxWait="-1"/&gt; 
+</source>
+</p>
+
+<h3>2.    web.xml configuration</h3>
+<p>You should ensure that you respect the elemeent ordering defined by the DTD when you
+create you applications web.xml file.</p>
+<source>
+&lt;resource-ref&gt;
+ &lt;description&gt;Oracle Datasource example&lt;/description&gt;
+ &lt;res-ref-name&gt;jdbc/myoracle&lt;/res-ref-name&gt;
+ &lt;res-type&gt;javax.sql.DataSource&lt;/res-type&gt;
+ &lt;res-auth&gt;Container&lt;/res-auth&gt;
+&lt;/resource-ref&gt;
+</source>
+<h3>3.   Code example</h3>
+<p>You can use the same example application as above (asuming you create the required DB
+instance, tables etc.) replacing the Datasource code with something like</p>
+<source>
+Context initContext = new InitialContext();
+Context envContext  = (Context)initContext.lookup("java:/comp/env");
+DataSource ds = (DataSource)envContext.lookup("jdbc/myoracle");
+Connection conn = ds.getConnection();
+//etc.
+</source>
+</subsection>
+
+
+<subsection name="PostgreSQL">
+<h3>0.    Introduction</h3>
+<p>PostgreSQL is configured in a similar manner to Oracle.</p>
+
+<h3>1. Required files </h3>
+<p>
+Copy the Postgres JDBC jar to $CATALINA_HOME/common/lib. As with Oracle, the
+jars need to be in this directory in order for DBCP's Classloader to find
+them. This has to be done regardless of which configuration step you take next.
+</p>
+
+<h3>2. Resource configuration</h3>
+
+<p>
+You have two choices here: define a datasource that is shared across all Tomcat
+applications, or define a datasource specifically for one application.
+</p>
+
+<h4>2a. Shared resource configuration</h4>
+<p>
+Use this option if you wish to define a datasource that is shared across
+multiple Tomcat applications, or if you just prefer defining your datasource
+in this file.
+</p>
+<p><i>This author has not had success here, although others have reported so.
+Clarification would be appreciated here.</i></p>
+
+<source>
+&lt;Resource name="jdbc/postgres" auth="Container"
+          type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
+          url="jdbc:postgresql://127.0.0.1:5432/mydb"
+          username="myuser" password="mypasswd" maxActive="20" maxIdle="10" maxWait="-1"/&gt;
+</source>
+<h4>2b. Application-specific resource configuration</h4>
+
+<p>
+Use this option if you wish to define a datasource specific to your application,
+not visible to other Tomcat applications. This method is less invasive to your
+Tomcat installation.
+</p>
+
+<p>
+Create a resource definition file for your application defining the
+datasource. This file must have the same name as your application, so if
+your application deploys as <code>someApp.war</code>, this filename must
+be <code>someApp.xml</code>. This file should look something like the following.
+</p>
+
+<source>
+&lt;Context path="/someApp" docBase="someApp"
+   crossContext="true" reloadable="true" debug="1"&gt;
+
+&lt;Resource name="jdbc/postgres" auth="Container"
+          type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
+          url="jdbc:postgresql://127.0.0.1:5432/mydb"
+          username="myuser" password="mypasswd" maxActive="20" maxIdle="10"
+maxWait="-1"/&gt;
+&lt;/Context&gt;
+</source>
+
+<h3>3. web.xml configuration</h3>
+<source>
+&lt;resource-ref&gt;
+ &lt;description&gt;postgreSQL Datasource example&lt;/description&gt;
+ &lt;res-ref-name&gt;jdbc/postgres&lt;/res-ref-name&gt;
+ &lt;res-type&gt;javax.sql.DataSource&lt;/res-type&gt;
+ &lt;res-auth&gt;Container&lt;/res-auth&gt;
+&lt;/resource-ref&gt;
+</source>
+
+<h4>4. Accessing the datasource</h4>
+<p>
+When accessing the datasource programmatically, remember to prepend
+<code>java:/comp/env</code> to your JNDI lookup, as in the following snippet of
+code. Note also that "jdbc/postgres" can be replaced with any value you prefer, provided
+you change it in the above resource definition file as well.
+</p>
+
+<source>
+InitialContext cxt = new InitialContext();
+if ( cxt == null ) {
+   throw new Exception("Uh oh -- no context!");
+}
+
+DataSource ds = (DataSource) cxt.lookup( "java:/comp/env/jdbc/postgres" );
+
+if ( ds == null ) {
+   throw new Exception("Data source not found!");
+}
+</source>
+
+</subsection>
+</section>
+
+<section name="Non-DBCP Solutions">
+<p>
+These solutions either utilise a single connection to the database (not recommended for anything other
+than testing!) or some other pooling technology.
+</p>
+</section>
+
+<section name="Oracle 8i with OCI client">
+<subsection name="Introduction">
+<p>Whilst not strictly addressing the creation of a JNDI DataSource using the OCI client, these notes can be combined with the
+Oracle and DBCP solution above.</p>
+<p>
+In order to use OCI driver, you should have an Oracle client installed. You should have installed
+Oracle8i(8.1.7) client from cd,  and download the suitable JDBC/OCI
+driver(Oracle8i 8.1.7.1 JDBC/OCI Driver) from <a href="http://otn.oracle.com/">otn.oracle.com</a>. 
+</p>
+<p>
+After renaming <code>classes12.zip</code> file to <code>classes12.jar</code>
+for Tomcat, copy it into <code>$CATALINA_HOME/common/lib</code>. 
+You may also have to remove the <code>javax.sql.*</code> classes
+from this file depending upon the version of Tomcat and JDK you are using.
+</p>
+</subsection>
+
+<subsection name="Putting it all together">
+<p>
+Ensure that you have the <code>ocijdbc8.dll</code> or <code>.so</code> in your <code>$PATH</code> or <code>LD_LIBRARY_PATH</code>
+ (possibly in <code>$ORAHOME\bin</code>) and also confirm that the native library can be loaded by a simple test program 
+using <code>System.loadLibrary("ocijdbc8");</code>
+</p>
+<p>
+You should next create a simple test servlet or jsp that has these
+<strong>critical lines</strong>:
+</p>
+<source>
+DriverManager.registerDriver(new
+oracle.jdbc.driver.OracleDriver());
+conn =
+DriverManager.getConnection("jdbc:oracle:oci8:@database","username","password");
+</source>
+<p>
+where database is of the form <code>host:port:SID</code> Now if you try to access the URL of your 
+test servlet/jsp and what you get is a 
+<code>ServletException</code> with a root cause of <code>java.lang.UnsatisfiedLinkError:get_env_handle</code>.
+</p>
+<p>
+First, the <code>UnsatisfiedLinkError</code> indicates that you have 
+<ul>
+<li>a mismatch between your JDBC classes file and
+your Oracle client version. The giveaway here is the message stating that a needed library file cannot be
+found. For example, you may be using a classes12.zip file from Oracle Version 8.1.6 with a Version 8.1.5
+Oracle client. The classeXXXs.zip file and Oracle client software versions must match.
+</li>
+<li>A <code>$PATH</code>, <code>LD_LIBRARY_PATH</code> problem.</li>
+<li>It has been reported that ignoring the driver you have downloded from otn and using 
+the classes12.zip file from the directory <code>$ORAHOME\jdbc\lib</code> will also work.
+</li>
+</ul>
+</p>
+<p>
+Next you may experience the error <code>ORA-06401 NETCMN: invalid driver designator</code>
+</p>
+<p>
+The Oracle documentation says : "Cause: The login (connect) string contains an invalid
+driver designator. Action: Correct the string and re-submit."
+
+Change the database connect string (of the form <code>host:port:SID</code>) with this one:
+<code>(description=(address=(host=myhost)(protocol=tcp)(port=1521))(connect_data=(sid=orcl)))</code>
+</p>
+<p>
+<i>Ed. Hmm, I don't think this is really needed if you sort out your TNSNames - but I'm not an Oracle DBA :-)</i>
+</p>
+</subsection>
+</section>
+
+<section name="Common Problems">
+<p>Here are some common problems encountered with a web application which
+uses a database and tips for how to solve them.</p>
+
+<subsection name="Intermittent dB Connection Failures">
+<p>
+Tomcat runs within a JVM.  The JVM periodically performs garbage collection
+(GC) to remove java objects which are no longer being used.  When the JVM
+performs GC execution of code within Tomcat freezes. If the maximum time
+configured for establishment of a dB connection is less than the amount
+of time garbage collection took you can get a db conneciton failure.
+</p>
+
+<p>To collect data on how long garbage collection is taking add the
+<code>-verbose:gc</code> argument to your <code>CATALINA_OPTS</code>
+environment variable when starting Tomcat.  When verbose gc is enabled
+your <code>$CATALINA_BASE/logs/catalina.out</code> log file will include
+data for every garbage collection including how long it took.</p>
+
+<p>When your JVM is tuned correctly 99% of the time a GC will take less
+than one second.  The remainder will only take a few seconds.  Rarely,
+if ever should a GC take more than 10 seconds.</p>
+
+<p>Make sure that the db connection timeout is set to 10-15 seconds.
+For the DBCP you set this using the parameter <code>maxWait</code>.</p>
+
+</subsection>
+
+<subsection name="Random Connection Closed Exceptions">
+<p>
+These can occur when one request gets a db connection from the connection
+pool and closes it twice.  When using a connection pool, closing the
+connection just returns it to the pool for reuse by another request,
+it doesn't close the connection.  And Tomcat uses multiple threads to
+handle concurrent requests. Here is an example of the sequence
+of events which could cause this error in Tomcat:
+<pre>
+  Request 1 running in Thread 1 gets a db connection.
+
+  Request 1 closes the db connection.
+
+  The JVM switches the running thread to Thread 2
+
+  Request 2 running in Thread 2 gets a db connection
+  (the same db connection just closed by Request 1).
+
+  The JVM switches the running thread back to Thread 1
+
+  Request 1 closes the db connection a second time in a finally block.
+
+  The JVM switches the running thread back to Thread 2
+
+  Request 2 Thread 2 tries to use the db connection but fails
+  because Request 1 closed it.
+</pre>
+Here is an example of properly written code to use a db connection
+obtained from a connection pool:
+<pre>
+  Connection conn = null;
+  Statement stmt = null;  // Or PreparedStatement if needed
+  ResultSet rs = null;
+  try {
+    conn = ... get connection from connection pool ...
+    stmt = conn.createStatement("select ...");
+    rs = stmt.executeQuery();
+    ... iterate through the result set ...
+    rs.close();
+    rs = null;
+    stmt.close();
+    stmt = null;
+    conn.close(); // Return to connection pool
+    conn = null;  // Make sure we don't close it twice
+  } catch (SQLException e) {
+    ... deal with errors ...
+  } finally {
+    // Always make sure result sets and statements are closed,
+    // and the connection is returned to the pool
+    if (rs != null) {
+      try { rs.close(); } catch (SQLException e) { ; }
+      rs = null;
+    }
+    if (stmt != null) {
+      try { stmt.close(); } catch (SQLException e) { ; }
+      stmt = null;
+    }
+    if (conn != null) {
+      try { conn.close(); } catch (SQLException e) { ; }
+      conn = null;
+    }
+  }
+</pre>
+</p>
+
+</subsection>
+
+<subsection name="Context versus GlobalNamingResources">
+<p>
+  Please note that although the above instructions place the JNDI declarations in a Context
+  element, it is possible and sometimes desirable to place these declarations in the 
+  <a href="config/globalresources.html">GlobalNamingResources</a> section of the server
+  configuration file.  A resource placed in the GlobalNamingResources section will be shared
+  among the Contexts of the server.
+</p>
+</subsection>
+
+<subsection name="JNDI Resource Naming and Realm Interaction">
+<p>
+  In order to get Realms to work, the realm must refer to the datasource as
+  defined in the &lt;GlobalNamingResources&gt; or &lt;Context&gt; section, not a datasource as renamed
+  using &lt;ResourceLink&gt;.
+</p>
+</subsection> 
+
+</section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/jndi-resources-howto.xml b/container/webapps/docs/jndi-resources-howto.xml
new file mode 100644
index 0000000..e0c75bc
--- /dev/null
+++ b/container/webapps/docs/jndi-resources-howto.xml
@@ -0,0 +1,762 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="jndi-resources-howto.html">
+
+    &project;
+
+    <properties>
+      <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+      <author email="yoavs@apache.org">Yoav Shapira</author>
+      <title>JNDI Resources HOW-TO</title>
+    </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+<p>Tomcat 5 provides a JNDI <strong>InitialContext</strong> implementation
+instance for each web application running under it, in a manner that is 
+compatible with those provided by a 
+<a href="http://java.sun.com/j2ee">Java2 Enterprise Edition</a> application 
+server. 
+
+The J2EE standard provides a standard set of elements in 
+the <code>/WEB-INF/web.xml</code> file to reference resources; resources 
+referenced in these elements must be defined in an application-server-specific configuration. 
+</p>
+
+<p>For Tomcat 5, these entries in per-web-application 
+<code>InitialContext</code> are configured in the 
+<code><strong>&lt;Context&gt;</strong></code> elements that can be specified 
+in either <code>$CATALINA_HOME/conf/server.xml</code> or, preferably, 
+the per-web-application context XML file (either <code>META-INF/context.xml</code>).
+</p>
+
+<p>Tomcat 5 maintains a separate namespace of global resources for the 
+entire server.  These are configured in the 
+<a href="config/globalresources.html">
+<code><strong>&lt;GlobalNameingResources&gt;</strong></code></a> element of 
+<code>$CATALINA_HOME/conf/server.xml</code>. You may expose these resources to 
+web applications by using 
+<code><strong>&lt;ResourceLink&gt;</strong></code> elements.
+</p>
+
+<p>The resources defined in these elements
+may be referenced by the following elements in the web application deployment
+descriptor (<code>/WEB-INF/web.xml</code>) of your web application:</p>
+<ul>
+<li><code><strong>&lt;env-entry&gt;</strong></code> - Environment entry, a
+    single-value parameter that can be used to configure how the application
+    will operate.</li>
+<li><code><strong>&lt;resource-ref&gt;</strong></code> - Resource reference,
+    which is typically to an object factory for resources such as a JDBC
+    <code>DataSource</code>, a JavaMail <code>Session</code>, or custom
+    object factories configured into Tomcat 5.</li>
+<li><code><strong>&lt;resource-env-ref&gt;</strong></code> - Resource
+    environment reference, a new variation of <code>resource-ref</code>
+    added in Servlet 2.4 that is simpler to configure for resources
+    that do not require authentication information.</li>
+</ul>
+
+<p>The <code>InitialContext</code> is configured as a web application is
+initially deployed, and is made available to web application components (for
+read-only access).  All configured entries and resources are placed in
+the <code>java:comp/env</code> portion of the JNDI namespace, so a typical
+access to a resource - in this case, to a JDBC <code>DataSource</code> -
+would look something like this:</p>
+
+<source>
+// Obtain our environment naming context
+Context initCtx = new InitialContext();
+Context envCtx = (Context) initCtx.lookup("java:comp/env");
+
+// Look up our data source
+DataSource ds = (DataSource)
+  envCtx.lookup("jdbc/EmployeeDB");
+
+// Allocate and use a connection from the pool
+Connection conn = ds.getConnection();
+... use this connection to access the database ...
+conn.close();
+</source>
+
+<p>See the following Specifications for more information about programming APIs
+for JNDI, and for the features supported by Java2 Enterprise Edition (J2EE)
+servers, which Tomcat emulates for the services that it provides:</p>
+<ul>
+<li><a href="http://java.sun.com/products/jndi/#download">Java Naming and
+    Directory Interface</a> (included in JDK 1.4, available separately for
+    prior JDK versions)</li>
+<li><a href="http://java.sun.com/j2ee/download.html">J2EE Platform
+    Specification</a> (in particular, see Chapter 5 on <em>Naming</em>)</li>
+</ul>
+
+</section>
+
+
+<section name="Configuring JNDI Resources">
+
+<p>Each available JNDI Resource is configured based on inclusion of the
+following elements in the <code><strong>&lt;Context&gt;</strong></code> or 
+<code><strong>&lt;DefaultContext&gt;</strong></code> elements:</p>
+
+<ul>
+<li><a href="config/context.html#Environment Entries">&lt;Environment&gt;</a> -
+    Configure names and values for scalar environment entries that will be
+    exposed to the web application through the JNDI
+    <code>InitialContext</code> (equivalent to the inclusion of an
+    <code>&lt;env-entry&gt;</code> element in the web application
+    deployment descriptor).</li>
+<li><a href="config/context.html#Resource Definitions">&lt;Resource&gt;</a> -
+    Configure the name and data type of a resource made available to the
+    application (equivalent to the inclusion of a
+    <code>&lt;resource-ref&gt;</code> element in the web application
+    deployment descriptor).</li>
+<li><a href="config/context.html#Resource Links">&lt;ResourceLink&gt;</a> -
+    Add a link to a resource defined in the global JNDI context. Use resource 
+    links to give a web application access to a resource defined in 
+    the<a href="config/globalresources.html">&lt;GlobalNamingResources&gt;</a>
+    child element of the <a href="config/server.html">&lt;Server&gt;</a>
+    element.</li>
+
+</ul>
+
+<p>Any number of these elements may be nested inside a
+<a href="config/context.html">&lt;Context&gt;</a> element (to be associated
+only with that particular web application).</p>
+
+<p>In addition, the names and values of all <code>&lt;env-entry&gt;</code>
+elements included in the web application deployment descriptor
+(<code>/WEB-INF/web.xml</code>) are configured into the initial context as
+well, overriding corresponding values from <code>conf/server.xml</code>
+<strong>only</strong> if allowed by the corresponding
+<code>&lt;Environment&gt;</code> element (by setting the
+<code>override</code> attribute to "true").</p>
+
+<p>Global resources can be defined in the server-wide JNDI context, by adding
+the resource elements described above to the
+<a href="config/globalresources.html">&lt;GlobalNamingResources&gt;</a>
+child element of the <a href="config/server.html">&lt;Server&gt;</a>
+element and using a 
+<a href="config/context.html#Resource Links">&lt;ResourceLink&gt;</a> to
+include it in the per-web-application context.</p>
+
+</section>
+
+
+<section name="Tomcat Standard Resource Factories">
+
+  <p>Tomcat 5 includes a series of standard resource factories that can
+  provide services to your web applications, but give you configuration
+  flexibility (in <code>$CATALINA_HOME/conf/server.xml</code>) without
+  modifying the web application or the deployment descriptor.  Each
+  subsection below details the configuration and usage of the standard
+  resource factories.</p>
+
+  <p>See <a href="#Adding Custom Resource Factories">Adding Custom
+  Resource Factories</a> for information about how to create, install,
+  configure, and use your own custom resource factory classes with
+  Tomcat 5.</p>
+
+  <p><em>NOTE</em> - Of the standard resource factories, only the
+  "JDBC Data Source" and "User Transaction" factories are mandated to
+  be available on other platforms, and then they are required only if
+  the platform implements the Java2 Enterprise Edition (J2EE) specs.
+  All other standard resource factories, plus custom resource factories
+  that you write yourself, are specific to Tomcat and cannot be assumed
+  to be available on other containers.</p>
+
+  <subsection name="Generic JavaBean Resources">
+
+    <h3>0.  Introduction</h3>
+
+    <p>This resource factory can be used to create objects of <em>any</em>
+    Java class that conforms to standard JavaBeans naming conventions (i.e.
+    it has a zero-arguments constructor, and has property setters that
+    conform to the setFoo() naming pattern.  The resource factory will
+    create a new instance of the appropriate bean class every time a
+    <code>lookup()</code> for this entry is made.</p>
+
+    <p>The steps required to use this facility are described below.</p>
+
+    <h3>1.  Create Your JavaBean Class</h3>
+
+    <p>Create the JavaBean class which will be instantiated each time
+    that the resource factory is looked up.  For this example, assume
+    you create a class <code>com.mycompany.MyBean</code>, which looks
+    like this:</p>
+
+<source>
+package com.mycompany;
+
+public class MyBean {
+
+  private String foo = "Default Foo";
+
+  public String getFoo() {
+    return (this.foo);
+  }
+
+  public void setFoo(String foo) {
+    this.foo = foo;
+  }
+
+  private int bar = 0;
+
+  public int getBar() {
+    return (this.bar);
+  }
+
+  public void setBar(int bar) {
+    this.bar = bar;
+  }
+
+
+}
+</source>
+
+  <h3>2.  Declare Your Resource Requirements</h3>
+
+  <p>Next, modify your web application deployment descriptor
+  (<code>/WEB-INF/web.xml</code>) to declare the JNDI name under which
+  you will request new instances of this bean.  The simplest approach is
+  to use a <code>&lt;resource-env-ref&gt;</code> element, like this:</p>
+
+<source>
+&lt;resource-env-ref&gt;
+  &lt;description&gt;
+    Object factory for MyBean instances.
+  &lt;/description&gt;
+  &lt;resource-env-ref-name&gt;
+    bean/MyBeanFactory
+  &lt;/resource-env-ref-name&gt;
+  &lt;resource-env-ref-type&gt;
+    com.mycompany.MyBean
+  &lt;/resource-env-ref-type&gt;
+&lt;/resource-env-ref&gt;
+</source>
+
+    <p><strong>WARNING</strong> - Be sure you respect the element ordering
+    that is required by the DTD for web application deployment descriptors!
+    See the
+    <a href="http://java.sun.com/products/servlet/download.html">Servlet
+    Specification</a> for details.</p>
+
+  <h3>3.  Code Your Application's Use Of This Resource</h3>
+
+  <p>A typical use of this resource environment reference might look
+  like this:</p>
+
+<source>
+Context initCtx = new InitialContext();
+Context envCtx = (Context) initCtx.lookup("java:comp/env");
+MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory");
+
+writer.println("foo = " + bean.getFoo() + ", bar = " +
+               bean.getBar());
+</source>
+
+    <h3>4.  Configure Tomcat's Resource Factory</h3>
+
+    <p>To configure Tomcat's resource factory, add an elements like this to the
+    <code>$CATALINA_HOME/conf/server.xml</code> file, nested inside the
+    <code>Context</code> element for this web application.</p>
+<source>
+&lt;Context ...&gt;
+  ...
+  &lt;Resource name="bean/MyBeanFactory" auth="Container"
+            type="com.mycompany.MyBean"
+            factory="org.apache.naming.factory.BeanFactory"
+            bar="23"/&gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>Note that the resource name (here, <code>bean/MyBeanFactory</code>
+    must match the value specified in the web application deployment
+    descriptor.  We are also initializing the value of the <code>bar</code>
+    property, which will cause <code>setBar(23)</code> to be called before
+    the new bean is returned.  Because we are not initializing the
+    <code>foo</code> property (although we could have), the bean will
+    contain whatever default value is set up by its constructor.</p>
+
+  </subsection>
+
+
+  <subsection name="JavaMail Sessions">
+
+    <h3>0.  Introduction</h3>
+
+    <p>In many web applications, sending electronic mail messages is a
+    required part of the system's functionality.  The
+    <a href="http://java.sun.com/products/javamail">Java Mail</a> API
+    makes this process relatively straightforward, but requires many
+    configuration details that the client application must be aware of
+    (including the name of the SMTP host to be used for message sending).</p>
+
+    <p>Tomcat 5 includes a standard resource factory that will create
+    <code>javax.mail.Session</code> session instances for you, already
+    connected to the SMTP server that is configured in <code>server.xml</code>.
+    In this way, the application is totally insulated from changes in the
+    email server configuration environment - it simply asks for, and receives,
+    a preconfigured session whenever needed.</p>
+
+    <p>The steps required for this are outlined below.</p>
+
+    <h3>1.  Declare Your Resource Requirements</h3>
+
+    <p>The first thing you should do is modify the web application deployment
+    descriptor (<code>/WEB-INF/web.xml</code>) to declare the JNDI name under
+    which you will look up preconfigured sessions.  By convention, all such
+    names should resolve to the <code>mail</code> subcontext (relative to the
+    standard <code>java:comp/env</code> naming context that is the root of
+    all provided resource factories.  A typical <code>web.xml</code> entry
+    might look like this:</p>
+<source>
+&lt;resource-ref&gt;
+  &lt;description&gt;
+    Resource reference to a factory for javax.mail.Session
+    instances that may be used for sending electronic mail
+    messages, preconfigured to connect to the appropriate
+    SMTP server.
+  &lt;/description&gt;
+  &lt;res-ref-name&gt;
+    mail/Session
+  &lt;/res-ref-name&gt;
+  &lt;res-type&gt;
+    javax.mail.Session
+  &lt;/res-type&gt;
+  &lt;res-auth&gt;
+    Container
+  &lt;/res-auth&gt;
+&lt;/resource-ref&gt;
+</source>
+
+    <p><strong>WARNING</strong> - Be sure you respect the element ordering
+    that is required by the DTD for web application deployment descriptors!
+    See the
+    <a href="http://java.sun.com/products/servlet/download.html">Servlet
+    Specification</a> for details.</p>
+
+    <h3>2.  Code Your Application's Use Of This Resource</h3>
+
+    <p>A typical use of this resource reference might look like this:</p>
+<source>
+Context initCtx = new InitialContext();
+Context envCtx = (Context) initCtx.lookup("java:comp/env");
+Session session = (Session) envCtx.lookup("mail/Session");
+
+Message message = new MimeMessage(session);
+message.setFrom(new InternetAddress(request.getParameter("from"));
+InternetAddress to[] = new InternetAddress[1];
+to[0] = new InternetAddress(request.getParameter("to"));
+message.setRecipients(Message.RecipientType.TO, to);
+message.setSubject(request.getParameter("subject"));
+message.setContent(request.getParameter("content"), "text/plain");
+Transport.send(message);
+</source>
+
+    <p>Note that the application uses the same resource reference name
+    that was declared in the web application deployment descriptor.  This
+    is matched up against the resource factory that is configured in
+    <code>$CATALINA_HOME/conf/server.xml</code>, as described below.</p>
+
+    <h3>3.  Configure Tomcat's Resource Factory</h3>
+
+    <p>To configure Tomcat's resource factory, add an elements like this to the
+    <code>$CATALINA_HOME/conf/server.xml</code> file, nested inside the
+    <code>Context</code> element for this web application.</p>
+<source>
+&lt;Context ...&gt;
+  ...
+  &lt;Resource name="mail/Session" auth="Container"
+            type="javax.mail.Session"
+            mail.smtp.host="localhost"/&gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>Note that the resource name (here, <code>mail/Session</code>) must
+    match the value specified in the web application deployment descriptor.
+    Customize the value of the <code>mail.smtp.host</code> parameter to
+    point at the server that provides SMTP service for your network.</p>
+
+    <h3>4.  Install the JavaMail libraries</h3>
+
+    <p><a href="http://java.sun.com/products/javamail/downloads/index.html" target="_blank">
+    Download the JavaMail API</a>.  The JavaMail API requires the Java Activation
+    Framework (JAF) API as well.  The Java Activation Framework can be downloaded
+    from <a href="http://java.sun.com/products/javabeans/glasgow/jaf.html">Sun's site</a>.
+    </p>
+
+    <p>This download includes 2 vital libraries for the configuration; 
+    activation.jar and mail.jar. Unpackage both distributions and place 
+    them into $CATALINA_HOME/common/lib so that they are available to
+    Tomcat during the initialization of the mail Session Resource.
+    <strong>Note:</strong> placing these jars in both common/lib and a 
+    web application's lib folder will cause an error, so ensure you have
+    them in the $CATALINA_HOME/common/lib location only.
+    </p>
+
+    <h3>Example Application</h3>
+
+    <p>The <code>/examples</code> application included with Tomcat contains
+    an example of utilizing this resource factory.  It is accessed via the
+    "JSP Examples" link.  The source code for the servlet that actually
+    sends the mail message is in
+    <code>/WEB-INF/classes/SendMailServlet.java</code>.</p>
+
+    <p><strong>WARNING</strong> - The default configuration assumes that
+    there is an SMTP server listing on port 25 on <code>localhost</code>.
+    If this is not the case, edit the
+    <code>$CATALINA_HOME/conf/server.xml</code> file, and modify the
+    parameter value for the <code>mail.smtp.host</code> parameter to be
+    the host name of an SMTP server on your network.</p>
+
+  </subsection>
+
+  <subsection name="JDBC Data Sources">
+
+    <h3>0.  Introduction</h3>
+
+    <p>Many web applications need to access a database via a JDBC driver,
+    to support the functionality required by that application.  The J2EE
+    Platform Specification requires J2EE Application Servers to make
+    available a <em>DataSource</em> implementation (that is, a connection
+    pool for JDBC connections) for this purpose.  Tomcat 5 offers exactly
+    the same support, so that database-based applications you develop on
+    Tomcat using this service will run unchanged on any J2EE server.</p>
+
+    <p>For information about JDBC, you should consult the following:</p>
+    <ul>
+    <li><a href="http://java.sun.com/products/jdbc/">http://java.sun.com/products/jdbc/</a> -
+        Home page for information about Java Database Connectivity.</li>
+    <li><a href="http://java.sun.com/j2se/1.3/docs/guide/jdbc/spec2/jdbc2.1.frame.html">http://java.sun.com/j2se/1.3/docs/guide/jdbc/spec2/jdbc2.1.frame.html</a> -
+        The JDBC 2.1 API Specification.</li>
+    <li><a href="http://java.sun.com/products/jdbc/jdbc20.stdext.pdf">http://java.sun.com/products/jdbc/jdbc20.stdext.pdf</a> -
+        The JDBC 2.0 Standard Extension API (including the
+        <code>javax.sql.DataSource</code> API).  This package is now known
+        as the "JDBC Optional Package".</li>
+    <li><a href="http://java.sun.com/j2ee/download.html">http://java.sun.com/j2ee/download.html</a> -
+        The J2EE Platform Specification (covers the JDBC facilities that
+        all J2EE platforms must provide to applications).</li>
+    </ul>
+
+    <p><strong>NOTE</strong> - The default data source support in Tomcat
+    is based on the <strong>DBCP</strong> connection pool from the
+    <a href="http://jakarta.apache.org/commons">Jakarta Commons</a>
+    subproject.  However, it is possible to use any other connection pool
+    that implements <code>javax.sql.DataSource</code>, by writing your
+    own custom resource factory, as described
+    <a href="#Adding Custom Resource Factories">below</a>.</p>
+
+    <h3>1.  Install Your JDBC Driver</h3>
+
+    <p>Use of the <em>JDBC Data Sources</em> JNDI Resource Factory requires
+    that you make an appropriate JDBC driver available to both Tomcat internal
+    classes and to your web application.  This is most easily accomplished by
+    installing the driver's JAR file(s) into the
+    <code>$CATALINA_HOME/common/lib</code> directory, which makes the driver
+    available both to the resource factory and to your application.</p>
+
+    <h3>2.  Declare Your Resource Requirements</h3>
+
+    <p>Next, modify the web application deployment descriptor
+    (<code>/WEB-INF/web.xml</code>) to declare the JNDI name under
+    which you will look up preconfigured data source.  By convention, all such
+    names should resolve to the <code>jdbc</code> subcontext (relative to the
+    standard <code>java:comp/env</code> naming context that is the root of
+    all provided resource factories.  A typical <code>web.xml</code> entry
+    might look like this:</p>
+<source>
+&lt;resource-ref&gt;
+  &lt;description&gt;
+    Resource reference to a factory for java.sql.Connection
+    instances that may be used for talking to a particular
+    database that is configured in the server.xml file.
+  &lt;/description&gt;
+  &lt;res-ref-name&gt;
+    jdbc/EmployeeDB
+  &lt;/res-ref-name&gt;
+  &lt;res-type&gt;
+    javax.sql.DataSource
+  &lt;/res-type&gt;
+  &lt;res-auth&gt;
+    Container
+  &lt;/res-auth&gt;
+&lt;/resource-ref&gt;
+</source>
+
+    <p><strong>WARNING</strong> - Be sure you respect the element ordering
+    that is required by the DTD for web application deployment descriptors!
+    See the
+    <a href="http://java.sun.com/products/servlet/download.html">Servlet
+    Specification</a> for details.</p>
+
+    <h3>3.  Code Your Application's Use Of This Resource</h3>
+
+    <p>A typical use of this resource reference might look like this:</p>
+<source>
+Context initCtx = new InitialContext();
+Context envCtx = (Context) initCtx.lookup("java:comp/env");
+DataSource ds = (DataSource)
+  envCtx.lookup("jdbc/EmployeeDB");
+
+Connection conn = ds.getConnection();
+... use this connection to access the database ...
+conn.close();
+</source>
+
+    <p>Note that the application uses the same resource reference name
+    that was declared in the web application deployment descriptor.  This
+    is matched up against the resource factory that is configured in
+    <code>$CATALINA_HOME/conf/server.xml</code>, as described below.</p>
+
+    <h3>4.  Configure Tomcat's Resource Factory</h3>
+
+    <p>To configure Tomcat's resource factory, add an element like this to the
+    <code>/META-INF/context.xml</code> file in the web application.</p>
+<source>
+&lt;Context ...&gt;
+  ...
+  &lt;Resource name="jdbc/EmployeeDB" auth="Container"
+            type="javax.sql.DataSource" username="dbusername" password="dbpassword"
+            driverClassName="org.hsql.jdbcDriver" url="jdbc:HypersonicSQL:database"
+            maxActive="8" maxIdle="4"/&gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>Note that the resource name (here, <code>jdbc/EmployeeDB</code>) must
+    match the value specified in the web application deployment descriptor.</p>
+
+    <p>This example assumes that you are using the HypersonicSQL database
+    JDBC driver.  Customize the <code>driverClassName</code> and
+    <code>driverName</code> parameters to match your actual database's
+    JDBC driver and connection URL.</p>
+
+    <p>The configuration properties for Tomcat's standard data source
+    resource factory
+    (<code>org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory</code>) are
+    as follows:</p>
+    <ul>
+    <li><strong>driverClassName</strong> - Fully qualified Java class name
+        of the JDBC driver to be used.</li>
+    <li><strong>maxActive</strong> - The maximum number of active instances
+        that can be allocated from this pool at the same time.</li>
+    <li><strong>maxIdle</strong> - The maximum number of connections that
+        can sit idle in this pool at the same time.</li>
+    <li><strong>maxWait</strong> - The maximum number of milliseconds that the
+        pool will wait (when there are no available connections) for a
+        connection to be returned before throwing an exception.</li>
+    <li><strong>password</strong> - Database password to be passed to our
+        JDBC driver.</li>
+    <li><strong>url</strong> - Connection URL to be passed to our JDBC driver.
+        (For backwards compatibility, the property <code>driverName</code>
+        is also recognized.)</li>
+    <li><strong>user</strong> - Database username to be passed to our
+        JDBC driver.</li>
+    <li><strong>validationQuery</strong> - SQL query that can be used by the
+        pool to validate connections before they are returned to the
+        application.  If specified, this query MUST be an SQL SELECT
+        statement that returns at least one row.</li>
+    </ul>
+    <p>For more details, please refer to the commons-dbcp documentation.</p>
+
+  </subsection>
+
+</section>
+
+
+<section name="Adding Custom Resource Factories">
+
+  <p>If none of the standard resource factories meet your needs, you can
+  write your own factory and integrate it into Tomcat 5, and then configure
+  the use of this factory in the <code>conf/server.xml</code> configuration
+  file.  In the example below, we will create a factory that only knows how
+  to create <code>com.mycompany.MyBean</code> beans, from the
+  <a href="#Generic JavaBean Resources">Generic JavaBean Resources</a>
+  example, above.</p>
+
+  <h3>1.  Write A Resource Factory Class</h3>
+
+  <p>You must write a class that implements the JNDI service provider
+  <code>javax.naming.spi.ObjectFactory</code> inteface.  Every time your
+  web application calls <code>lookup()</code> on a context entry that is
+  bound to this factory, the <code>getObjectInstance()</code> method is
+  called, with the following arguments:</p>
+  <ul>
+  <li><strong>Object obj</strong> - The (possibly null) object containing
+      location or reference information that can be used in creating an
+      object.  For Tomcat, this will always be an object of type
+      <code>javax.naming.Reference</code>, which contains the class name
+      of this factory class, as well as the configuration properties
+      (from <code>conf/server.xml</code>) to use in creating objects
+      to be returned.</li>
+  <li><strong>Name name</strong> - The name to which this factory is bound
+      relative to <code>nameCtx</code>, or <code>null</code> if no name
+      is specified.</li>
+  <li><strong>Context nameCtx</strong> - The context relative to which the
+      <code>name</code> parameter is specified, or <code>null</code> if
+      <code>name</code> is relative to the default initial context.</li>
+  <li><strong>Hashtable environment</strong> - The (possibly null)
+      environment that is used in creating this object.  This is generally
+      ignored in Tomcat object factories.</li>
+  </ul>
+
+  <p>To create a resource factory that knows how to produce <code>MyBean</code>
+  instances, you might create a class like this:</p>
+
+<source>
+package com.mycompany;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+
+public class MyBeanFactory implements ObjectFactory {
+
+  public Object getObjectInstance(Object obj,
+      Name name, Context nameCtx, Hashtable environment)
+      throws NamingException {
+
+      // Acquire an instance of our specified bean class
+      MyBean bean = new MyBean();
+
+      // Customize the bean properties from our attributes
+      Reference ref = (Reference) obj;
+      Enumeration addrs = ref.getAll();
+      while (addrs.hasMoreElements()) {
+          RefAddr addr = (RefAddr) addrs.nextElement();
+          String name = addr.getType();
+          String value = (String) addr.getContent();
+          if (name.equals("foo")) {
+              bean.setFoo(value);
+          } else if (name.equals("bar")) {
+              try {
+                  bean.setBar(Integer.parseInt(value));
+              } catch (NumberFormatException e) {
+                  throw new NamingException("Invalid 'bar' value " + value);
+              }
+          }
+      }
+
+      // Return the customized instance
+      return (bean);
+
+  }
+
+}
+</source>
+
+  <p>In this example, we are unconditionally creating a new instance of
+  the <code>com.mycompany.MyBean</code> class, and populating its properties
+  based on the parameters included in the <code>&lt;ResourceParams&gt;</code>
+  element that configures this factory (see below).  You should note that any
+  parameter named <code>factory</code> should be skipped - that parameter is
+  used to specify the name of the factory class itself (in this case,
+  <code>com.mycompany.MyBeanFactory</code>) rather than a property of the
+  bean being configured.</p>
+
+  <p>For more information about <code>ObjectFactory</code>, see the
+  <a href="http://java.sun.com/products/jndi/docs.html">JNDI 1.2 Service
+  Provider Interface (SPI) Specification</a>.</p>
+
+  <p>You will need to compile this class against a class path that includes
+  all of the JAR files in the <code>$CATALINA_HOME/common/lib</code> and
+  <code>$CATALINA_HOME/server/lib</code> directories.  When you are through,
+  place the factory class (and the corresponding bean class) unpacked under
+  <code>$CATALINA_HOME/common/classes</code>, or in a JAR file inside
+  <code>$CATALINA_HOME/common/lib</code>.  In this way, the required class
+  files are visible to both Catalina internal resources and your web
+  application.</p>
+
+  <h3>2.  Declare Your Resource Requirements</h3>
+
+  <p>Next, modify your web application deployment descriptor
+  (<code>/WEB-INF/web.xml</code>) to declare the JNDI name under which
+  you will request new instances of this bean.  The simplest approach is
+  to use a <code>&lt;resource-env-ref&gt;</code> element, like this:</p>
+
+<source>
+&lt;resource-env-ref&gt;
+  &lt;description&gt;
+    Object factory for MyBean instances.
+  &lt;/description&gt;
+  &lt;resource-env-ref-name&gt;
+    bean/MyBeanFactory
+  &lt;/resource-env-ref-name&gt;
+  &lt;resource-env-ref-type&gt;
+    com.mycompany.MyBean
+  &lt;/resource-env-ref-type&gt;
+&lt;resource-env-ref&gt;
+</source>
+
+    <p><strong>WARNING</strong> - Be sure you respect the element ordering
+    that is required by the DTD for web application deployment descriptors!
+    See the
+    <a href="http://java.sun.com/products/servlet/download.html">Servlet
+    Specification</a> for details.</p>
+
+  <h3>3.  Code Your Application's Use Of This Resource</h3>
+
+  <p>A typical use of this resource environment reference might look
+  like this:</p>
+
+<source>
+Context initCtx = new InitialContext();
+Context envCtx = (Context) initCtx.lookup("java:comp/env");
+MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory");
+
+writer.println("foo = " + bean.getFoo() + ", bar = " +
+               bean.getBar());
+</source>
+
+    <h3>4.  Configure Tomcat's Resource Factory</h3>
+
+    <p>To configure Tomcat's resource factory, add an elements like this to the
+    <code>$CATALINA_HOME/conf/server.xml</code> file, nested inside the
+    <code>Context</code> element for this web application.</p>
+<source>
+&lt;Context ...&gt;
+  ...
+  &lt;Resource name="bean/MyBeanFactory" auth="Container"
+            type="com.mycompany.MyBean"
+            factory="com.mycompany.MyBeanFactory"
+            bar="23"/&gt;
+  ...
+&lt;/Context&gt;
+</source>
+
+    <p>Note that the resource name (here, <code>bean/MyBeanFactory</code>
+    must match the value specified in the web application deployment
+    descriptor.  We are also initializing the value of the <code>bar</code>
+    property, which will cause <code>setBar(23)</code> to be called before
+    the new bean is returned.  Because we are not initializing the
+    <code>foo</code> property (although we could have), the bean will
+    contain whatever default value is set up by its constructor.</p>
+
+    <p>You will also note that, from the application developer's perspective,
+    the declaration of the resource environment reference, and the programming
+    used to request new instances, is identical to the approach used for the
+    <em>Generic JavaBean Resources</em> example.  This illustrates one of the
+    advantages of using JNDI resources to encapsulate functionality - you can
+    change the underlying implementation without necessarily having to
+    modify applications using the resources, as long as you maintain
+    compatible APIs.</p>
+
+</section>
+
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/logging.xml b/container/webapps/docs/logging.xml
new file mode 100644
index 0000000..8e48984
--- /dev/null
+++ b/container/webapps/docs/logging.xml
@@ -0,0 +1,273 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="logging.html">
+
+    &project;
+
+  <properties>
+    <title>Logging in Tomcat</title>
+    <author>Allistair Crossley</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+  </properties>
+
+<body>
+
+  <section name="Introduction">
+  <p>
+    Tomcat 5.5 uses 
+    <a href="http://jakarta.apache.org/commons/logging">Commons Logging</a>
+    throughout its internal code allowing the 
+    developer to choose a logging configuration that suits their needs, e.g
+    java.util.logging or 
+    <a href="http://logging.apache.org/log4j">Log4J</a>. 
+    Commons Logging provides Tomcat the ability to log
+    hierarchially across various log levels without needing to rely on a particular
+    logging implementation.
+  </p>
+  <p>
+    An important consequence for Tomcat 5.5 is that the &lt;Logger&gt; element found in 
+    previous versions to create a <code>localhost_log</code> is no longer a valid nested element 
+    of &lt;Context&gt;. Instead, the default Tomcat configuration will use java.util.logging. 
+    If the developer wishes to collect detailed internal Tomcat logging (i.e what is happening 
+    within the Tomcat engine), then they should configure a logging system such as java.util.logging 
+    or log4j as detailed next.
+  </p>
+
+  </section>
+
+  <section name="log4j">
+    <p>
+      Tomcat 5.5 has done away with <code>localhost_log</code> which you may be familiar with
+      as the runtime exception/stack trace log. These types of error are usually thrown
+      by uncaught exceptions, but are still valuable to the developer. They can now be
+      found in the <code>stdout</code> log.
+    </p>
+
+    <p>
+      If you need to setup cross-context detailed logging from within Tomcat's code, 
+      then you can use a simple log4j configuration. Note that this logging van be very 
+      verbose depending on the log level you chose to use.  Note also that a log4j logging 
+      configuration is not going to produce stack trace type logging: those stack traces
+      are output to <code>stdout</code> as discussed above.
+    </p>
+
+    <p>
+      Follow the following steps to setup a file named tomcat.log that has internal 
+      Tomcat logging output to it:
+    </p>
+
+    <p>
+      <ol>
+        <li>Create a file called log4j.properties with the following content 
+            and save it into common/classes. </li>
+        <li>
+          <source>
+            log4j.rootLogger=debug, R <br />
+            log4j.appender.R=org.apache.log4j.RollingFileAppender <br />
+            log4j.appender.R.File=${catalina.home}/logs/tomcat.log <br />
+            log4j.appender.R.MaxFileSize=10MB <br />
+            log4j.appender.R.MaxBackupIndex=10 <br />
+            log4j.appender.R.layout=org.apache.log4j.PatternLayout <br />
+            log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n <br />
+            log4j.logger.org.apache.catalina=DEBUG, R
+          </source>
+	</li>
+
+	<li><a href="http://logging.apache.org/log4j">Download Log4J</a> 
+            (v1.2 or later) and place the log4j jar in $CATALINA_HOME/common/lib.</li>
+
+        <li><a href="http://jakarta.apache.org/site/binindex.cgi#commons-logging">
+            Download Commons Logging</a> and place the commons-logging.jar 
+            (not commons-logging-api.jar) in $CATALINA_HOME/common/lib with 
+            the log4j jar.</li>
+
+	<li>Start Tomcat</li>
+      </ol>
+    </p>
+
+    <p>
+      This log4j configuration sets up a file called tomcat.log in your 
+      Tomcat logs folder with a maximum file size of 10MB and
+      up to 10 backups.  DEBUG level is specified which will result in the 
+      most verbose output from Tomcat.
+    </p>
+	
+    <p>
+      You can (and should) be more picky about which packages to include 
+      in the logging. Tomcat 5.5 uses defines loggers by Engine and Host names.
+      For example, for a default Catalina localhost log, add this to the
+      end of the log4j.properties above. Note that there are known issues with 
+      using this naming convention (with square brackets) in log4j XML based
+      configuration files, so we recommend you use a properties file as described
+      until a future version of log4j allows this convention.
+      
+      <ul>
+        <li>log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost]=DEBUG, R</li>
+	<li>log4j.logger.org.apache.catalina.core=DEBUG, R</li>
+	<li>log4j.logger.org.apache.catalina.session=DEBUG, R</li>
+      </ul>
+
+      Be warned a level of DEBUG will produce megabytes of logging and slow startup
+      of Tomcat. This level should be used sparingly when debugging of internal Tomcat
+      operations is required.
+    </p>
+	
+    <p>
+      Your web applications should certainly use their own log4j configuration. 
+      This is valid <i>with</i> the above configuration.  You would place a similar log4j.properties 
+      file in your web application's WEB-INF/classes folder, and log4j1.2.8.jar into
+      WEB-INF/lib. Then specify your package level logging. This is a basic setup of log4j 
+      which does *not* require Commons-Logging, 
+      and you should consult the 
+      <a href="http://logging.apache.org/log4j/docs/documentation.html">log4j documentation</a> 
+      for more options.  This page is intended only as a bootstrapping guide.
+    </p>
+	
+  </section>
+
+  <section name="java.util.logging">
+
+  <p>
+    In order to configure JDK logging you should have JDK 1.4+. Tomcat 5.5 is intended for
+    JDK 5.0, but can be run on JDK 1.4 using a compatibility package.
+  </p>
+  <p>
+    The default implemenatation of java.util.logging provided in the JDK is too limited to be 
+    useful. A limitation of JDK Logging appears to be the inability to have per-web application logging, 
+    as the configuration is per-VM. As a result, Tomcat will, in the default configuration,
+    replace the default LogManager implementation with a container friendly implementation
+    called JULI, which addresses these shortcomings. It supports the same configuration mechanisms 
+    as the standard JDK java.util.logging, using either a programmatic approach, or properties
+    files. The main difference is that per-classloader properties files can be set (which enables easy
+    redeployment friendly webapp configuration), and the properties files support slightly extended
+    constructs which allows more freedom for defining handlers and assigning them to loggers.
+  </p>
+  <p>
+    JULI is enabled by default in Tomcat 5.5, and supports per classloader configuration, in addition to 
+    the regular global java.util.logging configuration. This means that logging can be configured at 
+    the following layers:
+    <ul>
+      <li>In the JDK's logging.properties file. Check
+      your JAVA_HOME environment setting to see which JDK Tomcat is using (or maybe JRE 5.0 as Tomcat
+      can now run on a JRE from version 5.5). The file will be in <code>$JAVA_HOME/jre/lib</code>.
+      Alternately, it can also use a global configuration file located elsewhere by using the 
+      system property <code>java.util.logging.config.file</code>, or programmatic configuration using
+      <code>java.util.logging.config.class</code>.</li>
+      <li>In each classloader using a logging.properties file. This means that it is possible to have a
+      configuration for the Tomcat core, as well as separate configurations for each webapps which will 
+      have the same lifecycle as the webapps.</li>
+    </ul>
+  </p>
+  <p>
+    The default logging.properties specifies a ConsoleHandler for routing logging to stdout and
+    also a FileHandler. A handler's log level threshold can be set using SEVERE, CONFIG, INFO, 
+    WARN, FINE, FINEST or ALL. The logging.properties shipped with JDK is set to INFO. You
+    can also target specific packages to collect logging from and specify a level. Here is how
+    you would set debugging from Tomcat. You would need to ensure the ConsoleHandler's level is also
+    set to collect this threshold, so FINEST or ALL should be set. Please refer to Sun's java.util.logging
+    documentation for the complete details.
+  </p>
+  <p>
+    <source>org.apache.catalina.level=FINEST</source>
+  </p>
+  <p>
+    The configuration used by JULI is extremely similar, but uses a few extensions to allow better 
+    flexibility in assigning loggers. The main differences are:
+    <ul>
+      <li>A prefix may be added to handler names, so that multiple handlers of a single class may be 
+      instantiated. A prefix is a String which starts with a digit, and ends with '.'. For example, 
+      <code>22foobar.</code> is a valid prefix.</li>
+      <li>As in Java 5.0, loggers can define a list of handlers using the <code>loggerName.handlers</code>
+      property.</li>
+      <li>By default, loggers will not delegate to their parent if they have associated handlers. This
+      may be changed per logger using the <code>loggerName.useParentHandlers</code> property, which accepts 
+      a boolean value.</li>
+      <li>The root logger can define its set of handlers using a <code>.handlers</code> property.</li>
+      <li>System property replacement for property values which start with ${sytstemPropertyName}.</li>
+    </ul>
+  </p>
+  <p>
+    Example logging.properties file to be placed in common/classes:
+    <source>
+handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, \
+           3manager.org.apache.juli.FileHandler, 4admin.org.apache.juli.FileHandler, \
+           java.util.logging.ConsoleHandler
+
+.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
+
+############################################################
+# Handler specific properties.
+# Describes specific configuration info for Handlers.
+############################################################
+
+1catalina.org.apache.juli.FileHandler.level = FINE
+1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+1catalina.org.apache.juli.FileHandler.prefix = catalina.
+
+2localhost.org.apache.juli.FileHandler.level = FINE
+2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+2localhost.org.apache.juli.FileHandler.prefix = localhost.
+
+3manager.org.apache.juli.FileHandler.level = FINE
+3manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+3manager.org.apache.juli.FileHandler.prefix = manager.
+
+4admin.org.apache.juli.FileHandler.level = FINE
+4admin.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+4admin.org.apache.juli.FileHandler.prefix = admin.
+
+java.util.logging.ConsoleHandler.level = FINE
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+
+
+############################################################
+# Facility specific properties.
+# Provides extra control for each logger.
+############################################################
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = \
+   2localhost.org.apache.juli.FileHandler
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = \
+   3manager.org.apache.juli.FileHandler
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/admin].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/admin].handlers = \
+   4admin.org.apache.juli.FileHandler
+
+# For example, set the com.xyz.foo logger to only log SEVERE
+# messages:
+#org.apache.catalina.startup.ContextConfig.level = FINE
+#org.apache.catalina.startup.HostConfig.level = FINE
+#org.apache.catalina.session.ManagerBase.level = FINE
+    </source>
+    </p>
+    
+    <p>
+      Example logging.properties for the servlet-examples web application to be placed
+      in WEB-INF/classes inside the web application:
+      <source>
+handlers = org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
+
+############################################################
+# Handler specific properties.
+# Describes specific configuration info for Handlers.
+############################################################
+
+org.apache.juli.FileHandler.level = FINE
+org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+org.apache.juli.FileHandler.prefix = servlet-examples.
+
+java.util.logging.ConsoleHandler.level = FINE
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+      </source>
+    </p>
+
+  </section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/manager-howto.xml b/container/webapps/docs/manager-howto.xml
new file mode 100644
index 0000000..eca541d
--- /dev/null
+++ b/container/webapps/docs/manager-howto.xml
@@ -0,0 +1,1295 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="manager-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+        <title>Manager App HOW-TO</title>
+    </properties>
+
+<body>
+
+<section name="Table of Contents">
+
+<p>
+<a href="#Introduction">Introduction</a><br />
+<a href="#Configuring Manager Application Access">
+Configuring Manager Application Access</a><br />
+<a href="#Supported Manager Commands">Supported Manager Commands</a><br />
+<blockquote>
+<a href="#Deploy A New Application Remotely">Deploy A New Application Remotely</a><br />
+<a href="#Deploy A New Application from a Local Path">Deploy A New Application from a Local Path</a><br />
+<a href="#List Currently Deployed Applications">
+List Currently Deployed Applications</a><br />
+<a href="#Reload An Existing Application">Reload An Existing Application</a><br />
+<a href="#List OS and JVM Properties">List OS and JVM Properties</a><br />
+<a href="#List Available Global JNDI Resources">
+List Available Global JNDI Resources</a><br />
+<a href="#List Available Security Roles">List Available Security Roles</a><br />
+<a href="#Session Statistics">Session Statistics</a><br />
+<a href="#Start an Existing Application">Start an Existing Application</a><br />
+<a href="#Stop an Existing Application">Stop an Existing Application</a><br />
+<a href="#Undeploy an Existing Application">
+Undeploy an Existing Application</a><br />
+</blockquote>
+<a href="#Executing Manager Commands With Ant">
+Executing Manager Commands With Ant</a><br />
+<a href="#Using the JMX Proxy Servlet">
+Using the JMX Proxy Servlet</a><br />
+<blockquote>
+<a href="#What is JMX Proxy Servlet">What is JMX Proxy Servlet?</a><br />
+<a href="#JMX Query command">Query command</a><br />
+<a href="#JMX Set command">Set command</a><br />
+</blockquote>
+</p>
+
+</section>
+
+<section name="Introduction">
+
+<p>In many production environments, it is very useful to have the capability
+to deploy a new web application, or undeploy an existing one, without having
+to shut down and restart the entire container.  In addition, you can request
+an existing application to reload itself, even if you have not declared it
+to be <code>reloadable</code> in the Tomcat 5 server
+configuration file.</p>
+
+<p>To support these capabilities, Tomcat 5 includes a web application
+(installed by default on context path <code>/manager</code>) that supports
+the following functions:</p>
+<ul>
+<li>Deploy a new web application, on a specified context path, from
+    the uploaded contents of a WAR file.</li>
+<li>Deploy a new web application, on a specified context path, from the
+    server file system.</li>
+<li>List the currently deployed web applications, as well as the
+    sessions that are currently active for those web apps.</li>
+<li>Reload an existing web application, to reflect changes in the
+    contents of <code>/WEB-INF/classes</code> or <code>/WEB-INF/lib</code>.
+    </li>
+<li>List the OS and JVM property values.</li>
+<li>List the available global JNDI resources, for use in deployment
+    tools that are preparing <code>&lt;ResourceLink&gt;</code> elements
+    nested in a <code>&lt;Context&gt;</code> deployment description.</li>
+<li>List the available security roles defined in the user database.</li>
+<li>Start a stopped application (thus making it available again).</li>
+<li>Stop an existing application (so that it becomes unavailable), but
+    do not undeploy it.</li>
+<li>Undeploy a deployed web application and delete its document base
+    directory (unless it was deployed from file system).</li>
+</ul>
+
+<p>There are two ways to configure the Manager web application
+<code>Context</code>:
+<ul>
+<li>Install the <code>manager.xml</code> context configuration file
+    in the <code>$CATALINA_HOME/conf/[enginename]/[hostname]</code> folder.
+</li>
+<li>Configure the Manager <code>Context</code> within the
+    <code>Host</code> configuration in your Tomcat <code>server.xml</code>
+    configuration. Here is an example:
+<pre>
+&lt;Context path="/manager" debug="0" privileged="true"
+         docBase="/usr/local/kinetic/tomcat5/server/webapps/manager"&gt;
+&lt;/Context&gt;
+</pre>
+</li>
+</ul>
+</p>
+
+<p>If you have Tomcat configured to support multiple virtual hosts
+(websites) you would need to configure a Manager for each.</p>
+
+<p>There are three ways to use the <code>Manager</code> web application.
+<ul>
+<li>As an application with a user interface you use in your browser.
+Here is an example URL where you can replace <code>localhost</code> with
+your website host name:  <code>http://localhost/manager/html/</code> .</li>
+<li>A minimal version using HTTP requests only which is suitable for use
+by scripts setup by system administrators.  Commands are given as part of the
+request URI, and responses are in the form of simple text that can be easily
+parsed and processed.  See <a href="#Supported Manager Commands">
+Supported Manager Commands</a> for more information.</li>
+<li>A convenient set of task definitions for the <em>Ant</em>
+(version 1.4 or later) build tool.  See
+<a href="#Executing Manager Commands With Ant">Executing Manager Commands
+With Ant</a> for more information.</li>
+</ul>
+</p>
+
+<p>Future versions of Tomcat 5 will include administrative functionality that
+is presented in (at least) the following forms:
+<ul>
+<li>As web services, so that Tomcat administration can be easily integrated
+    into remote and/or non-Java mnagement environments.</li>
+<li>As a web application with a nice user interface (built on top of the
+    web services processing layer) for easy Tomcat administration via a
+    web browser.</li>
+</ul>
+</p>
+
+</section>
+
+<section name="Configuring Manager Application Access">
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+<p>It would be quite unsafe to ship Tomcat with default settings that allowed
+anyone on the Internet to execute the Manager application on your server.
+Therefore, the Manager application is shipped with the requirement that anyone
+who attempts to use it must authenticate themselves, using a username and
+password that have the role <strong>manager</strong> associated with them.
+Further, there is no username in the default users file
+(<conf>$CATALINA_HOME/conf/tomcat-users.xml</conf>) that is assigned this
+role.  Therefore, access to the Manager application is completely disabled
+by default.</p>
+
+<p>To enable access to the Manager web application, you must either create
+a new username/password combination and associate the role name
+<strong>manager</strong> with it, or add the <strong>manager</strong> role
+to some existing username/password combination.  Exactly where this is done
+depends on which <code>Realm</code> implementation you are using:</p>
+<ul>
+<li><em>MemoryRealm</em> - If you have not customized your
+    <code>$CATALINA_HOME/conf/server.xml</code> to select a different one,
+    Tomcat 5 defaults to an XML-format file stored at
+    <code>$CATALINA_HOME/conf/tomcat-users.xml</code>, which can be
+    edited with any text editor.  This file contains an XML
+    <code>&lt;user&gt;</code> for each individual user, which might
+    look something like this:
+<source>
+&lt;user name="craigmcc" password="secret" roles="standard,manager" /&gt;
+</source>
+    which defines the username and password used by this individual to
+    log on, and the role names he or she is associated with.  You can
+    add the <strong>manager</strong> role to the comma-delimited
+    <code>roles</code> attribute for one or more existing users, and/or
+    create new users with that assigned role.</li>
+<li><em>JDBCRealm</em> - Your user and role information is stored in
+    a database accessed via JDBC.  Add the <strong>manager</strong> role
+    to one or more existing users, and/or create one or more new users
+    with this role assigned, following the standard procedures for your
+    environment.</li>
+<li><em>JNDIRealm</em> - Your user and role information is stored in
+    a directory server accessed via LDAP.  Add the <strong>manager</strong>
+    role to one or more existing users, and/or create one or more new users
+    with this role assigned, following the standard procedures for your
+    environment.</li>
+</ul>
+
+<p>The first time you attempt to issue one of the Manager commands
+described in the next section, you will be challenged to log on using
+BASIC authentication.  The username and password you enter do not matter,
+as long as they identify a valid user in the users database who possesses
+the role <strong>manager</strong>.</p>
+
+<p>In addition to the password restrictions the manager web application
+could be restricted by the remote IP address or host by adding a
+<code>RemoteAddrValve</code> or <code>RemoteHostValve</code>.  Here is
+an example of restricting access to the localhost by IP address:
+<pre>
+&lt;Context path="/manager" debug="0" privileged="true"
+         docBase="/usr/local/kinetic/tomcat5/server/webapps/manager"&gt;
+         &lt;Valve className="org.apache.catalina.valves.RemoteAddrValve"
+                allow="127.0.0.1"/&gt;
+&lt;/Context&gt;
+</pre>
+</p>
+</section>
+
+
+<section name="Supported Manager Commands">
+
+<p>All commands that the Manager application knows how to process are
+specified in a single request URI like this:</p>
+<source>
+http://{host}:{port}/manager/{command}?{parameters}
+</source>
+<p>where <code>{host}</code> and <code>{port}</code> represent the hostname
+and port number on which Tomcat is running, <code>{command}</code>
+represents the Manager command you wish to execute, and
+<code>{parameters}</code> represents the query parameters
+that are specific to that command.  In the illustrations below, customize
+the host and port appropriately for your installation.</p>
+
+<p>Most commands accept one or more of the following query parameters:</p>
+<ul>
+<li><strong>path</strong> - The context path (including the leading slash)
+    of the web application you are dealing with.  To select the ROOT web
+    application, specify "/".  <strong>NOTE</strong> -
+    It is not possible to perform administrative commands on the
+    Manager application itself.</li>
+<li><strong>war</strong> - URL of a web application archive (WAR) file,
+    pathname of a directory which contains the web application, or a
+    Context configuration ".xml" file.  You can use URLs in any of the
+    following formats:
+    <ul>
+    <li><strong>file:/absolute/path/to/a/directory</strong> - The absolute
+        path of a directory that contains the unpacked version of a web
+        application.  This directory will be attached to the context path
+        you specify without any changes.</li>
+    <li><strong>file:/absolute/path/to/a/webapp.war</strong> - The absolute
+        path of a web application archive (WAR) file.  This is valid
+        <strong>only</strong> for the <code>/deploy</code> command, and is
+        the only acceptable format to that command.</li>
+    <li><strong>jar:file:/absolute/path/to/a/warfile.war!/</strong> - The
+        URL to a local web application archive (WAR) file.  You can use any
+        syntax that is valid for the <code>JarURLConnection</code> class
+        for reference to an entire JAR file.</li>
+    <li><strong>file:/absolute/path/to/a/context.xml</strong> - The
+        absolute path of a web application Context configuration ".xml"
+        file which contains the Context configuration element.</li>
+    <li><strong>directory</strong> - The directory name for the web
+        applciation context in the Host's application base directory.</li>
+    <li><strong>webapp.war</strong> - The name of a web application war file
+        located in the Host's application base directory.</li>
+    </ul></li>
+</ul>
+
+<p>Each command will return a response in <code>text/plain</code> format
+(i.e. plain ASCII with no HTML markup), making it easy for both humans and
+programs to read).  The first line of the response wil begin with either
+<code>OK</code> or <code>FAIL</code>, indicating whether the requested
+command was successful or not.  In the case of failure, the rest of the first
+line will contain a description of the problem that was encountered.  Some
+commands include additional lines of information as described below.</p>
+
+<p><em>Internationalization Note</em> - The Manager application looks up
+its message strings in resource bundles, so it is possible that the strings
+have been translated for your platform.  The examples below show the English
+version of the messages.</p>
+
+<blockquote><em>
+<p><strong>WARNING:</strong>  the legacy commands <code>/install</code> and 
+<code>/remove</code> are deprecated.
+They are presently equivalent to <code>/deploy</code> and <code>/undeploy</code>,
+but could be removed in a future release.</p>
+</em></blockquote>
+
+<subsection name="Deploy A New Application Remotely">
+
+<source>
+http://localhost:8080/manager/deploy?path=/foo
+</source>
+
+<p>Upload the web application archive (WAR) file that is specified as the
+request data in this HTTP PUT request, install it into the <code>appBase</code>
+directory of our corresponding virtual host, and start it on the context path
+specified by the <code>path</code> request parameter.  If no <code>path</code>
+is specified the directory name or the war file name without the .war extension
+is used as the path.  The application can
+later be undeployed (and the corresponding application directory removed)
+by use of the <code>/undeploy</code>.</p>
+
+<p>The .WAR file may include Tomcat specific deployment configuration, by 
+including a Context configuration XML file in 
+<code>/META-INF/context.xml</code>.</p>
+
+<p>URL parameters include:
+<ul>
+<li><code>update</code>: When set to true, any existing update will be
+    undeployed first. The default value is set to false.</li>
+<li><code>tag</code>: Specifying a tag name, this allows associating the
+    deployed webapp with a version number. The application version can
+    be later redeployed when needed using only the tag.</li>
+</ul>
+</p>
+
+<p><strong>NOTE</strong> - This command is the logical
+opposite of the <code>/undeploy</code> command.</p>
+
+<p>If installation and startup is successful, you will receive a response
+like this:</p>
+<source>
+OK - Deployed application at context path /foo
+</source>
+
+<p>Otherwise, the response will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Application already exists at path /foo</em>
+    <blockquote>
+    <p>The context paths for all currently running web applications must be
+    unique.  Therefore, you must undeploy the existing web
+    application using this context path, or choose a different context path
+    for the new one. The <code>update</code> parameter may be specified as
+    a parameter on the URL, with a value of <code>true</code> to avoid this
+    error. In that case, an undeploy will be performed on an existing
+    application before performing the deployment.</p>
+    </blockquote></li>
+<li><em>Encountered exception</em>
+    <blockquote>
+    <p>An exception was encountered trying to start the new web application.
+    Check the Tomcat 5 logs for the details, but likely explanations include
+    problems parsing your <code>/WEB-INF/web.xml</code> file, or missing
+    classes encountered when initializing application event listeners and
+    filters.</p>
+    </blockquote></li>
+<li><em>Invalid context path was specified</em>
+    <blockquote>
+    <p>The context path must start with a slash character. To reference the
+    ROOT web application use "/".</p>
+    </blockquote></li>
+<li><em>No context path was specified</em>
+    <blockquote>
+    The <code>path</code> parameter is required.
+    </blockquote></li>
+</ul>
+
+</subsection>
+
+<subsection name="Deploy A New Application from a Local Path">
+
+<p>Deploy and start a new web application, attached to the specified context
+<code>path</code> (which must not be in use by any other web application).
+This command is the logical opposite of the <code>/undeploy</code> command.</p>
+
+<p>There are a number of different ways the deploy command can be used.</p>
+
+<h3>Deploy a version of a previously deployed webapp</h3>
+
+<p>This can be used to deploy a previous version of a web application, which
+has been deployed using the <code>tag</code> attribute. Note that the work
+directory for the manager webapp will contain the previously deployed WARs;
+removing it would make the deployment fail.
+<source>
+http://localhost:8080/manager/deploy?path=/footoo&amp;tag=footag
+</source>
+</p>
+
+<h3>Deploy a Directory or WAR by URL</h3>
+
+<p>Deploy a web application directory or ".war" file located on the Tomcat
+server. If no <code>path</code> is specified, the directory name or the war file
+name without the ".war" extension is used as the path. The <code>war</code>
+parameter specifies a URL (including the <code>file:</code> scheme) for either
+a directory or a web application archive (WAR) file. The supported syntax for
+a URL referring to a WAR file is described on the Javadocs page for the
+<code>java.net.JarURLConnection</code> class.  Use only URLs that refer to
+the entire WAR file.</p>
+
+<p>In this example the web application located in the directory
+<code>/path/to/foo</code> on the Tomcat server is deployed as the
+web application context named <code>/footoo</code>.
+<source>
+http://localhost:8080/manager/deploy?path=/footoo&amp;war=file:/path/to/foo
+</source>
+</p>
+
+<p>In this example the ".war" file <code>/path/to/bar.war</code> on the
+Tomcat server is deployed as the web application context named
+<code>/bar</code>. Notice that there is no <code>path</code> parameter
+so the context path defaults to the name of the web application archive
+file without the ".war" extension.
+<source>
+http://localhost:8080/manager/deploy?war=jar:file:/path/to/bar.war!/
+</source>
+</p>
+
+<h3>Deploy a Directory or War from the Host appBase</h3>
+
+<p>Deploy a web application directory or ".war" file located in your Host
+appBase directory. If no <code>path</code> is specified the directory name
+or the war file name without the ".war" extension is used as the path.</p>
+
+<p>In this example the web application located in a sub directory named
+<code>foo</code> in the Host appBase directory of the Tomcat server is
+deployed as the web application context named <code>/foo</code>. Notice
+that there is no <code>path</code> parameter so the context path defaults
+to the name of the web application directory.
+<source>
+http://localhost:8080/manager/deploy?war=foo
+</source>
+</p>
+
+<p>In this example the ".war" file <code>bar.war</code> located in your
+Host appBase directory on the Tomcat server is deployed as the web
+application context named <code>/bartoo</code>.
+<source>
+http://localhost:8080/manager/deploy?path=/bartoo&amp;war=bar.war
+</source>
+</p>
+
+<h3>Deploy using a Context configuration ".xml" file</h3>
+
+<p>If the Host deployXML flag is set to true you can deploy a web
+application using a Context configuration ".xml" file and an optional
+".war" file or web application directory. The context <code>path</code>
+is not used when deploying a web application using a context ".xml"
+configuration file.</p>
+
+<p>A Context configuration ".xml" file can contain valid XML for a
+web application Context just as if it were configured in your
+Tomcat <code>server.xml</code> configuration file. Here is an
+example:
+<source>
+&lt;Context path="/foobar" docBase="/path/to/application/foobar"
+         debug="0"&gt;
+
+  &lt;!-- Link to the user database we will get roles from --&gt;
+  &lt;ResourceLink name="users" global="UserDatabase"
+                type="org.apache.catalina.UserDatabase"/&gt;
+
+&lt;/Context&gt;
+</source>
+</p>
+
+<p>When the optional <code>war</code> parameter is set to the URL
+for a web application ".war" file or directory it overrides any
+docBase configured in the context configuration ".xml" file.</p>
+
+<p>Here is an example of deploying an application using a Context
+configuration ".xml" file.
+<source>
+http://localhost:8080/manager/deploy?config=file:/path/context.xml
+</source>
+</p>
+
+<p>Here is an example of deploying an application using a Context
+configuration ".xml" file and a web application ".war" file located
+on the server.
+<source>
+http://localhost:8080/manager/deploy?config=file:/path/context.xml&amp;war=jar:file:/path/bar.war!/
+</source>
+</p>
+
+<h3>Deployment Notes</h3>
+
+<p>If the Host is configured with unpackWARs=true and you deploy a war
+file, the war will be unpacked into a directory in your Host appBase
+directory.</p>
+
+<p>If the application war or directory is installed in your Host appBase
+directory and either the Host is configured with autoDeploy=true or
+liveDeploy=true, the Context path must match the directory name or
+war file name without the ".war" extension.</p>
+
+<p>For security when untrusted users can manage web applications, the
+Host deployXML flag can be set to false.  This prevents untrusted users
+from deploying web applications using a configuration XML file and
+also prevents them from deploying application directories or ".war"
+files located outside of their Host appBase.</p>
+
+
+<h3>Deploy Response</h3>
+
+<p>If installation and startup is successful, you will receive a response
+like this:</p>
+<source>
+OK - Deployed application at context path /foo
+</source>
+
+<p>Otherwise, the response will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Application already exists at path /foo</em>
+    <blockquote>
+    <p>The context paths for all currently running web applications must be
+    unique.  Therefore, you must undeploy the existing web
+    application using this context path, or choose a different context path
+    for the new one. The <code>update</code> parameter may be specified as
+    a parameter on the URL, with a value of <code>true</code> to avoid this
+    error. In that case, an undeploy will be performed on an existing
+    application before performing the deployment.</p>
+    </blockquote></li>
+<li><em>Document base does not exist or is not a readable directory</em>
+    <blockquote>
+    <p>The URL specified by the <code>war</code> parameter must identify a
+    directory on this server that contains the "unpacked" version of a
+    web application, or the absolute URL of a web application archive (WAR)
+    file that contains this application.  Correct the value specified by
+    the <code>war</code> parameter.</p>
+    </blockquote></li>
+<li><em>Encountered exception</em>
+    <blockquote>
+    <p>An exception was encountered trying to start the new web application.
+    Check the Tomcat 5 logs for the details, but likely explanations include
+    problems parsing your <code>/WEB-INF/web.xml</code> file, or missing
+    classes encountered when initializing application event listeners and
+    filters.</p>
+    </blockquote></li>
+<li><em>Invalid application URL was specified</em>
+    <blockquote>
+    <p>The URL for the directory or web application that you specified
+    was not valid.  Such URLs must start with <code>file:</code>, and URLs
+    for a WAR file must end in ".war".</p>
+    </blockquote></li>
+<li><em>Invalid context path was specified</em>
+    <blockquote>
+    <p>The context path must start with a slash character. To reference the
+    ROOT web application use "/".</p>
+    </blockquote></li>
+<li><em>Context path must match the directory or WAR file name:</em>
+    <blockquote>
+    If the application war or directory is installed in your Host appBase
+    directory and either the Host is configured with autoDeploy=true or
+    liveDeploy=true, the Context path must match the directory name or
+    war file name without the ".war" extension.
+    </blockquote></li>
+<li><em>Only web applications in the Host web application directory can
+     be installed</em>
+     <blockquote>
+     If the Host deployXML flag is set to false this error will happen
+     if an attempt is made to deploy a web application directory or
+      ".war" file outside of the Host appBase directory.
+     </blockquote></li>
+</ul>
+
+</subsection>
+
+<subsection name="List Currently Deployed Applications">
+
+<source>
+http://localhost:8080/manager/list
+</source>
+
+<p>List the context paths, current status (<code>running</code> or
+<code>stopped</code>), and number of active sessions for all currently
+deployed web applications.  A typical response immediately
+after starting Tomcat might look like this:</p>
+<source>
+OK - Listed applications for virtual host localhost
+/webdav:running:0
+/examples:running:0
+/manager:running:0
+/:running:0
+</source>
+
+</subsection>
+
+<subsection name="Reload An Existing Application">
+
+<source>
+http://localhost:8080/manager/reload?path=/examples
+</source>
+
+<p>Signal an existing application to shut itself down and reload.  This can
+be useful when the web application context is not reloadable and you have
+updated classes or property files in the <code>/WEB-INF/classes</code>
+directory or when you have added or updated jar files in the
+<code>/WEB-INF/lib</code> directory.
+</p>
+<p><strong>NOTE:</strong> The <code>/WEB-INF/web.xml</code>
+web application configuration file is not reread on a reload.
+If you have made changes to your web.xml file you must stop
+then start the web application.
+</p>
+
+<p>If this command succeeds, you will see a response like this:</p>
+<source>
+OK - Reloaded application at context path /examples
+</source>
+
+<p>Otherwise, the response will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Encountered exception</em>
+    <blockquote>
+    <p>An exception was encountered trying to restart the web application.
+    Check the Tomcat 5 logs for the details.</p>
+    </blockquote></li>
+<li><em>Invalid context path was specified</em>
+    <blockquote>
+    <p>The context path must start with a slash character. To reference the
+    ROOT web application use "/".</p>
+    </blockquote></li>
+<li><em>No context exists for path /foo</em>
+    <blockquote>
+    <p>There is no deployed application on the context path
+    that you specified.</p>
+    </blockquote></li>
+<li><em>No context path was specified</em>
+    <blockquote>
+    The <code>path</code> parameter is required.
+    </blockquote></li>
+<li><em>Reload not supported on WAR deployed at path /foo</em>
+    <blockquote>
+    Currently, application reloading (to pick up changes to the classes or
+    <code>web.xml</code> file) is not supported when a web application is
+    deployed directly from a WAR file.  It only works when the web application
+    is deployed from an unpacked directory.  If you are using a WAR file,
+    you should <code>undeploy</code> and then <code>deploy</code> or
+    <code>deploy</code> with the <code>update</code> parameter the
+    application again to pick up your changes.
+    </blockquote></li>
+</ul>
+
+</subsection>
+
+<subsection name="List OS and JVM Properties">
+
+<source>
+http://localhost:8080/manager/serverinfo
+</source>
+
+<p>Lists information about the Tomcat version, OS, and JVM properties.</p>
+
+<p>If an error occurs, the response will start with <code>FAIL</code> and
+include an error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Encountered exception</em>
+    <blockquote>
+    <p>An exception was encountered trying to enumerate the system properties.
+    Check the Tomcat 5 logs for the details.</p>
+    </blockquote></li>
+</ul>
+
+</subsection>
+
+<subsection name="List Available Global JNDI Resources">
+
+<source>
+http://localhost:8080/manager/resources[?type=xxxxx]
+</source>
+
+<p>List the global JNDI resources that are available for use in resource
+links for context configuration files.  If you specify the <code>type</code>
+request parameter, the value must be the fully qualified Java class name of
+the resource type you are interested in (for example, you would specify
+<code>javax.sql.DataSource</code> to acquire the names of all available
+JDBC data sources).  If you do not specify the <code>type</code> request
+parameter, resources of all types will be returned.</p>
+
+<p>Depending on whether the <code>type</code> request parameter is specfied
+or not, the first line of a normal response will be:</p>
+<pre>
+  OK - Listed global resources of all types
+</pre>
+<p>or</p>
+<pre>
+  OK - Listed global resources of type xxxxx
+</pre>
+<p>followed by one line for each resource.  Each line is composed of fields
+delimited by colon characters (":"), as follows:</p>
+<ul>
+<li><em>Global Resource Name</em> - The name of this global JNDI resource,
+    which would be used in the <code>global</code> attribute of a
+    <code>&lt;ResourceLink&gt;</code> element.</li>
+<li><em>Global Resource Type</em> - The fully qualified Java class name of
+    this global JNDI resource.</li>
+</ul>
+
+<p>If an error occurs, the response will start with <code>FAIL</code> and
+include an error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Encountered exception</em>
+    <blockquote>
+    <p>An exception was encountered trying to enumerate the global JNDI
+    resources.  Check the Tomcat 5 logs for the details.</p>
+    </blockquote></li>
+<li><em>No global JNDI resources are available</em>
+    <blockquote>
+    <p>The Tomcat server you are running has been configured without
+    global JNDI resources.</p>
+    </blockquote></li>
+</ul>
+
+
+</subsection>
+
+
+<subsection name="List Available Security Roles">
+
+<source>
+http://localhost:8080/manager/roles
+</source>
+
+<p>List the security role names (and corresponding descriptions) that are
+available in the <code>org.apache.catalina.UserDatabase</code> resource that
+is linked to the <code>users</code> resource reference in the web.xml file
+for the Manager web application.  This would typically be used, for example,
+by a deployment tool that wanted to create
+<code>&lt;security-role-ref&gt;</code> elements to map security role names
+used in a web application to the role names actually defined within the
+container.</p>
+
+<p>By default, the <code>users</code> resource reference is pointed at the
+global <code>UserDatabase</code> resource.  If you choose to utilize a
+different user database per virtual host, you should modify the
+<code>&lt;ResourceLink&gt;</code> element in the default
+<code>manager.xml</code> context configuration file to point at the global
+user database resource for this virtual host.</p>
+
+<p>When this command is executed, the first line of the response will be:</p>
+<pre>
+  OK - Listed security roles
+</pre>
+<p>followed by one line for each security role.  Each line is composed of
+fields delimited by colon characters (":") as follows:</p>
+<ul>
+<li><em>Security Role Name</em> - A security role name that is known to Tomcat
+    in the user database.</li>
+<li><em>Description</em> - Description of this security role (useful in
+    creating user interfaces for selecting roles.</li>
+</ul>
+
+<p>If an error occurs, the response will start with <code>FAIL</code> and
+include an error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Cannot resolve user database reference</em> - A JNDI error prevented
+    the successful lookup of the <code>org.apache.catalina.UserDatabase</code>
+    resource.  Check the Tomcat log files for a stack trace associated with
+    this error.</li>
+<li><em>No user database is available</em> - You have not configured a resource
+    reference for the <code>users</code> resource that points at an
+    appropriate user database instance.  Check your <code>manager.xml</code>
+    file and ensure that you have created an appropriate
+    <code>&lt;ResourceLink&gt;</code> or
+    <code>&lt;ResourceParams&gt;</code> element for this resource.</li>
+</ul>
+
+</subsection>
+
+
+<subsection name="Session Statistics">
+
+<source>
+http://localhost:8080/manager/sessions?path=/examples
+</source>
+
+<p>Display the default session timeout for a web application, and the
+number of currently active sessions that fall within ten-minute ranges of
+their actual timeout times.  For example, after restarting Tomcat and then
+executing one of the JSP samples in the <code>/examples</code> web app,
+you might get something like this:</p>
+<source>
+OK - Session information for application at context path /examples
+Default maximum session inactive interval 30 minutes
+30 - &lt;40 minutes:1 sessions
+</source>
+
+</subsection>
+
+
+<subsection name="Start an Existing Application">
+
+<source>
+http://localhost:8080/manager/start?path=/examples
+</source>
+
+<p>Signal a stopped application to restart, and make itself available again.
+Stopping and starting is useful, for example, if the database required by
+your application becomes temporarily unavailable.  It is usually better to
+stop the web application that relies on this database rather than letting
+users continuously encounter database exceptions.</p>
+
+<p>If this command succeeds, you will see a response like this:</p>
+<source>
+OK - Started application at context path /examples
+</source>
+
+<p>Otherwise, the response will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Encountered exception</em>
+    <blockquote>
+    <p>An exception was encountered trying to start the web application.
+    Check the Tomcat 5 logs for the details.</p>
+    </blockquote></li>
+<li><em>Invalid context path was specified</em>
+    <blockquote>
+    <p>The context path must start with a slash character. To reference the
+    ROOT web application use "/".</p>
+    </blockquote></li>
+<li><em>No context exists for path /foo</em>
+    <blockquote>
+    <p>There is no deployed application on the context path
+    that you specified.</p>
+    </blockquote></li>
+<li><em>No context path was specified</em>
+    <blockquote>
+    The <code>path</code> parameter is required.
+    </blockquote></li>
+</ul>
+
+</subsection>
+
+<subsection name="Stop an Existing Application">
+
+<source>
+http://localhost:8080/manager/stop?path=/examples
+</source>
+
+<p>Signal an existing application to make itself unavailable, but leave it
+deployed.  Any request that comes in while an application is
+stopped will see an HTTP error 404, and this application will show as
+"stopped" on a list applications command.</p>
+
+<p>If this command succeeds, you will see a response like this:</p>
+<source>
+OK - Stopped application at context path /examples
+</source>
+
+<p>Otherwise, the response will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Encountered exception</em>
+    <blockquote>
+    <p>An exception was encountered trying to stop the web application.
+    Check the Tomcat 5 logs for the details.</p>
+    </blockquote></li>
+<li><em>Invalid context path was specified</em>
+    <blockquote>
+    <p>The context path must start with a slash character. To reference the
+    ROOT web application use "/".</p>
+    </blockquote></li>
+<li><em>No context exists for path /foo</em>
+    <blockquote>
+    <p>There is no deployed application on the context path
+    that you specified.</p>
+    </blockquote></li>
+<li><em>No context path was specified</em>
+    <blockquote>
+    The <code>path</code> parameter is required.
+    </blockquote></li>
+</ul>
+
+</subsection>
+
+
+<subsection name="Undeploy an Existing Application">
+
+<source>
+http://localhost:8080/manager/undeploy?path=/examples
+</source>
+
+<p><strong><font color="red">WARNING</font> - This command will delete any web 
+application artifacts that exist within <code>appBase</code> directory 
+(typically "webapps") for this virtual host</strong>.
+This will delete the the application .WAR, if present, 
+the application directory resulting either from a deploy in unpacked form 
+or from .WAR expansion as well as the XML Context definition from
+<code>$CATALINA_HOME/conf/[enginename]/[hostname]/</code> directory. 
+If you simply want to take an application
+out of service, you should use the <code>/stop</code> command instead.</p>
+
+<p>Signal an existing application to gracefully shut itself down, and
+remove it from Tomcat (which also makes this context path available for
+reuse later).  In addition, the document root directory is removed, if it
+exists in the <code>appBase</code> directory (typically "webapps") for
+this virtual host.  This command is the logical opposite of the
+<code>/deploy</code> command.</p>
+
+<p>If this command succeeds, you will see a response like this:</p>
+<source>
+OK - Undeployed application at context path /examples
+</source>
+
+<p>Otherwise, the response will start with <code>FAIL</code> and include an
+error message.  Possible causes for problems include:</p>
+<ul>
+<li><em>Encountered exception</em>
+    <blockquote>
+    <p>An exception was encountered trying to undeploy the web application.
+    Check the Tomcat 5 logs for the details.</p>
+    </blockquote></li>
+<li><em>Invalid context path was specified</em>
+    <blockquote>
+    <p>The context path must start with a slash character. To reference the
+    ROOT web application use "/".</p>
+    </blockquote></li>
+<li><em>No context exists for path /foo</em>
+    <blockquote>
+    <p>There is no deployed application on the context path
+    that you specified.</p>
+    </blockquote></li>
+<li><em>No context path was specified</em>
+    <blockquote>
+    The <code>path</code> parameter is required.
+    </blockquote></li>
+</ul>
+
+</subsection>
+
+</section>
+
+<section name="Executing Manager Commands With Ant">
+
+<p>In addition to the ability to execute Manager commands via HTTP requests,
+as documented above, Tomcat 5 includes a convenient set of Task definitions
+for the <em>Ant</em> (version 1.4 or later) build tool.  In order to use these
+commands, you must perform the following setup operations:</p>
+<ul>
+<li>Download the binary distribution of Ant from
+    <a href="http://ant.apache.org">http://ant.apache.org</a>.
+    You must use version <strong>1.4</strong> or later.</li>
+<li>Install the Ant distribution in a convenient directory (called
+    ANT_HOME in the remainder of these instructions).</li>
+<li>Copy the file <code>server/lib/catalina-ant.jar</code> from your Tomcat 5
+    installation into Ant's library directory (<code>$ANT_HOME/lib</code>).
+    </li>
+<li>Add the <code>$ANT_HOME/bin</code> directory to your <code>PATH</code>
+    environment variable.</li>
+<li>Configure at least one username/password combination in your Tomcat
+    user database that includes the <code>manager</code> role.</li>
+</ul>
+
+<p>To use custom tasks within Ant, you must declare them first with a
+<code>&lt;taskdef&gt;</code> element.  Therefore, your <code>build.xml</code>
+file might look something like this:</p>
+
+<table border="1">
+<tr><td><pre>
+&lt;project name="My Application" default="compile" basedir="."&gt;
+
+  &lt;!-- Configure the directory into which the web application is built --&gt;
+  &lt;property name="build"    value="${basedir}/build"/&gt;
+
+  &lt;!-- Configure the context path for this application --&gt;
+  &lt;property name="path"     value="/myapp"/&gt;
+
+  &lt;!-- Configure properties to access the Manager application --&gt;
+  &lt;property name="url"      value="http://localhost:8080/manager"/&gt;
+  &lt;property name="username" value="myusername"/&gt;
+  &lt;property name="password" value="mypassword"/&gt;
+
+  &lt;!-- Configure the custom Ant tasks for the Manager application --&gt;
+  &lt;taskdef name="deploy"    classname="org.apache.catalina.ant.DeployTask"/&gt;
+  &lt;taskdef name="list"      classname="org.apache.catalina.ant.ListTask"/&gt;
+  &lt;taskdef name="reload"    classname="org.apache.catalina.ant.ReloadTask"/&gt;
+  &lt;taskdef name="resources" classname="org.apache.catalina.ant.ResourcesTask"/&gt;
+  &lt;taskdef name="roles"     classname="org.apache.catalina.ant.RolesTask"/&gt;
+  &lt;taskdef name="start"     classname="org.apache.catalina.ant.StartTask"/&gt;
+  &lt;taskdef name="stop"      classname="org.apache.catalina.ant.StopTask"/&gt;
+  &lt;taskdef name="undeploy"  classname="org.apache.catalina.ant.UndeployTask"/&gt;
+
+  &lt;!-- Executable Targets --&gt;
+  &lt;target name="compile" description="Compile web application"&gt;
+    &lt;!-- ... construct web application in ${build} subdirectory, and
+            generated a ${path}.war ... --&gt;
+  &lt;/target&gt;
+
+  &lt;target name="deploy" description="Install web application"
+          depends="compile"&gt;
+    &lt;deploy url="${url}" username="${username}" password="${password}"
+            path="${path}" war="${build}${path}.war"/&gt;
+  &lt;/target&gt;
+
+  &lt;target name="reload" description="Reload web application"
+          depends="compile"&gt;
+    &lt;reload  url="${url}" username="${username}" password="${password}"
+            path="${path}"/&gt;
+  &lt;/target&gt;
+
+  &lt;target name="undeploy" description="Remove web application"&gt;
+    &lt;undeploy url="${url}" username="${username}" password="${password}"
+            path="${path}"/&gt;
+  &lt;/target&gt;
+
+&lt;/project&gt;
+</pre></td></tr>
+</table>
+
+<p>Now, you can execute commands like <code>ant deploy</code> to deploy the
+application to a running instance of Tomcat, or <code>ant reload</code> to
+tell Tomcat to reload it.  Note also that most of the interesting values in
+this <code>build.xml</code> file are defined as replaceable properties, so
+you can override their values from the command line.  For example, you might
+consider it a security risk to include the real manager password in your
+<code>build.xml</code> file's source code.  To avoid this, omit the password
+property, and specify it from the command line:</p>
+<pre>
+  ant -Dpassword=secret deploy
+</pre>
+
+<subsection name="Tasks output capture">
+
+<p>Using <em>Ant</em> version <strong>1.6.2</strong> or later,
+the Catalina tasks offer the option to capture their output in 
+properties or external files. They support directly the following subset of the 
+<code>&lt;redirector&gt;</code> type attributes:
+</p>
+
+<table border="1" cellpadding="2" cellspacing="0">
+<tbody>
+<tr>
+<td valign="top"><b>Attribute</b></td>
+<td valign="top"><b>Description</b></td>
+<td align="center" valign="top"><b>Required</b></td>
+</tr>
+<tr>
+<td valign="top">output</td>
+<td valign="top">Name of a file to which to write the output. If
+the error stream is not also redirected to a file or property, it will
+appear in this output.</td>
+<td align="center" valign="top">No</td>
+</tr>
+<tr>
+<td valign="top">error</td>
+<td valign="top">The file to which the standard error of the
+command should be redirected.</td>
+<td align="center" valign="top">No</td>
+</tr>
+<tr>
+<td valign="top">logError</td>
+<td valign="top">This attribute is used when you wish to see
+error output in Ant's log and you are redirecting output to a
+file/property. The error output will not be included in the output
+file/property. If you redirect error with the <i>error</i> or <i>errorProperty</i>
+attributes, this will have no effect.</td>
+<td align="center" valign="top">No</td>
+</tr>
+<tr>
+<td valign="top">append</td>
+<td valign="top">Whether output and error files should be
+appended to or overwritten. Defaults to <code>false</code>.</td>
+<td align="center" valign="top">No</td>
+</tr>
+<tr>
+<td valign="top">createemptyfiles</td>
+<td valign="top">Whether output and error files should be created
+even when empty. Defaults to <code>true</code>.</td>
+<td align="center" valign="top">No</td>
+</tr>
+<tr>
+<td valign="top">outputproperty</td>
+<td valign="top">The name of a property in which the output of
+the command should be stored. Unless the error stream is redirected to
+a separate file or stream, this property will include the error output.</td>
+<td align="center" valign="top">No</td>
+</tr>
+<tr>
+<td valign="top">errorproperty</td>
+<td valign="top">The name of a property in which the standard
+error of the command should be stored.</td>
+<td align="center" valign="top">No</td>
+</tr>
+</tbody>
+</table>
+
+<p>A couple of additional attributes can also be specified:
+</p>
+<table border="1" cellpadding="2" cellspacing="0">
+<tbody>
+<tr>
+<td valign="top"><b>Attribute</b></td>
+<td valign="top"><b>Description</b></td>
+<td align="center" valign="top"><b>Required</b></td>
+</tr>
+<tr>
+<td valign="top">alwaysLog</td>
+<td valign="top">This attribute is used when you wish to see the
+output you are capturing, appearing also in the Ant's log. It must not be
+used unless you are capturing task output.
+Defaults to <code>false</code>.
+<em>This attribute will be supported directly by <code>&lt;redirector&gt;</code>
+in Ant 1.6.3</em></td>
+<td align="center" valign="top">No</td>
+</tr>
+<tr>
+<td valign="top">failonerror</td>
+<td valign="top">This attribute is used when you wish to avoid that
+any manager command processing error terminates the ant execution. Defaults to <code>true</code>.
+It must be set to <code>false</code>, if you want to capture error output,
+otherwise execution will terminate before anything can be captured.
+<br></br>
+This attribute acts only on manager command execution,
+any wrong or missing command attribute will still cause Ant execution termination.
+</td>
+<td align="center" valign="top">No</td>
+</tr>
+</tbody>
+</table>
+
+<p>They also support the embedded <code>&lt;redirector&gt;</code> element
+in which you can specify
+its full set of attributes, but <code>input</code>, <code>inputstring</code> and 
+<code>inputencoding</code> that, even if accepted, are not used because they have
+no meaning in this context.
+Refer to <a href="http://ant.apache.org">ant manual</a> for details on 
+<code>&lt;redirector&gt;</code> element attributes.
+</p>
+
+<p>
+Here is a sample build file extract that shows how this output redirection support
+can be used:
+</p>
+
+<table border="1">
+<tr><td><pre>
+	&lt;target name="manager.deploy"
+		depends="context.status"
+		if="context.notInstalled"&gt;
+		&lt;deploy url="${mgr.url}"
+			username="${mgr.username}"
+			password="${mgr.password}"
+			path="${mgr.context.path}"
+			config="${mgr.context.descriptor}"/&gt;
+	&lt;/target&gt;
+
+	&lt;target name="manager.deploy.war"
+		depends="context.status"
+		if="context.deployable"&gt;
+		&lt;deploy url="${mgr.url}"
+			username="${mgr.username}"
+			password="${mgr.password}"
+			update="${mgr.update}"
+			path="${mgr.context.path}"
+			war="${mgr.war.file}"/&gt;
+	&lt;/target&gt;
+	
+	&lt;target name="context.status"&gt;
+		&lt;property name="running" value="${mgr.context.path}:running"/&gt;
+		&lt;property name="stopped" value="${mgr.context.path}:stopped"/&gt;
+	
+		&lt;list url="${mgr.url}"
+			outputproperty="ctx.status"
+			username="${mgr.username}"
+			password="${mgr.password}"&gt;
+		&lt;/list&gt;
+		
+		&lt;condition property="context.running"&gt;
+			&lt;contains string="${ctx.status}" substring="${running}"/&gt;
+		&lt;/condition&gt;
+		&lt;condition property="context.stopped"&gt;
+			&lt;contains string="${ctx.status}" substring="${stopped}"/&gt;
+		&lt;/condition&gt;
+		&lt;condition property="context.notInstalled"&gt;
+			&lt;and&gt;
+				&lt;isfalse value="${context.running}"/&gt;
+				&lt;isfalse value="${context.stopped}"/&gt;
+			&lt;/and&gt;
+		&lt;/condition&gt;
+		&lt;condition property="context.deployable"&gt;
+			&lt;or&gt;
+				&lt;istrue value="${context.notInstalled}"/&gt;
+				&lt;and&gt;
+					&lt;istrue value="${context.running}"/&gt;
+					&lt;istrue value="${mgr.update}"/&gt;
+				&lt;/and&gt;
+				&lt;and&gt;
+					&lt;istrue value="${context.stopped}"/&gt;
+					&lt;istrue value="${mgr.update}"/&gt;
+				&lt;/and&gt;
+			&lt;/or&gt;
+		&lt;/condition&gt;
+		&lt;condition property="context.undeployable"&gt;
+			&lt;or&gt;
+				&lt;istrue value="${context.running}"/&gt;
+				&lt;istrue value="${context.stopped}"/&gt;
+			&lt;/or&gt;
+		&lt;/condition&gt;
+	&lt;/target&gt;
+</pre></td></tr>
+</table>
+
+<p><strong>WARNING:</strong> even if it doesn't make many sense, and is always a bad idea,
+calling a Catalina task more than once,
+badly set Ant tasks depends chains may cause that a task be called
+more than once in the same Ant run, even if not intended to. A bit of caution should be exercised when you are
+capturing output from that task, because this could lead to something unexpected:
+<ul>
+<li>when capturing in a property you will find in it only the output from the <em>first</em> call, because
+Ant properties are immutable and once set they cannot be changed,
+</li>
+<li>when capturing in a file, each run will overwrite it and you will find in it only the <em>last</em> call
+output, unless you are using the <code>append="true"</code> attribute, in which case you will
+see the output of each task call appended to the file.
+</li>
+</ul>
+</p>
+
+</subsection>
+
+</section>
+
+<section name="Using the JMX Proxy Servlet">
+
+  <subsection name="What is JMX Proxy Servlet">
+    The JMX Proxy Servlet is a lightweight proxy to get and set the
+    tomcat internals. (Or any class that has been exposed via an MBean)
+    Its usage is not very user friendly but the UI is
+    extremely help for integrating command line scripts for monitoring
+    and changing the internals of tomcat. You can do two things with the proxy:
+    get information and set information. For you to really understand the
+    JMX Proxy Servlet, you should have a general understanding of JMX.
+    If you don't know what JMX is, then prepare to be confused.
+  </subsection>
+
+  <subsection name="JMX Query command">
+    This takes the form:
+<source>
+http://webserver/manager/jmxproxy/?qry=STUFF
+</source>
+    Where <code>STUFF</code> is the JMX query you wish to perform. For example,
+    here are some queries you might wish to run:
+    <ul>
+      <li>
+        <code>qry=*%3Atype%3DRequestProcessor%2C* -->
+         type=RequestProcessor</code> which will locate all
+         workers which can process requests and report
+         their state.
+      </li>
+      <li>
+        <code>qry=*%3Aj2eeType=Servlet%2c* -->
+            j2eeType=Servlet</code> which return all loaded servlets.
+      </li>
+      <li>
+        <code>qry=Catalina%3Atype%3DEnvironment%2Cresourcetype%3DGlobal%2Cname%3DsimpleValue -->
+            Catalina:type=Environment,resourcetype=Global,name=simpleValue</code>
+            which look for a specific MBean by the given name.
+      </li>
+    </ul>
+    You'll need to experiment with this to really understand its capabilites.
+    If you provide no <code>qry</code> parameter, then all of the MBeans will
+    be displayed. We really recommend looking at the tomcat source code and
+    understand the JMX spec to get a better understanding of all the queries
+    you may run.
+  </subsection>
+
+  <subsection name="JMX Set command">
+    Now that you can query an MBean, its time to muck with Tomcat's internals!
+    The general form of the set command is :
+<source>
+http://webserver/manager/jmxproxy/?set=BEANNAME&amp;att=MYATTRIBUTE&amp;val=NEWVALUE
+</source>
+    So you need to provide 3 request parameters:
+    <ol>
+      <li><code>set</code>: The full bean name</li>
+      <li><code>att</code>: The attribute you wish to alter</li>
+      <li><code>val</code>: The new value </li>
+    </ol>
+    If all goes ok, then it will say OK, otherwise an error message will be
+    shown. For example, lets say we wish to turn up debugging on the fly for the
+    <code>ErrorReportValve</code>. The following will set debugging to 10.
+<source>
+http://localhost:8080/manager/jmxproxy/
+?set=Catalina%3Atype%3DValve%2Cname%3DErrorReportValve%2Chost%3Dlocalhost&amp;att=debug&amp;val=10
+</source>
+    and my result is (YMMV):
+<source>
+Result: ok
+</source>
+
+    Here is what I see if I pass in a bad value. Here is the URL I used,
+    I try set debugging equal to 'cowbell':
+<source>
+http://localhost:8080/manager/jmxproxy/
+?set=Catalina%3Atype%3DValve%2Cname%3DErrorReportValve%2Chost%3Dlocalhost&amp;att=debug&amp;val=cowbell
+</source>
+    When I try that, my result is
+<source>
+Error: java.lang.NumberFormatException: For input string: "cowbell"
+</source>
+  </subsection>
+
+
+</section>
+
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/mbeans-descriptor-howto.xml b/container/webapps/docs/mbeans-descriptor-howto.xml
new file mode 100644
index 0000000..87c9ca0
--- /dev/null
+++ b/container/webapps/docs/mbeans-descriptor-howto.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="mbean-descriptor-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="amyroh@apache.org">Amy Roh</author>
+        <title>MBean Descriptor How To</title>
+    </properties>
+
+<body>
+
+<section name="Introduction">
+
+<p>Tomcat 5 uses JMX MBeans as the technology for implementing 
+manageability of Tomcat.</p>
+
+<p>The descriptions of JMX MBeans for Catalina are in the mbeans-descriptor.xml 
+file in each package.</p>
+
+<p>You will need to add MBean descriptions for your custom components 
+in order to avoid a "ManagedBean is not found" exception.</p>
+
+</section>
+
+<section name="Adding MBean descriptions">
+
+<p>You may also add MBean descriptions for custom components in 
+a mbeans-descriptor.xml file, located in the same package as the class files
+it describes.</p>
+
+<source>
+  &lt;mbean         name="LDAPRealm"
+            className="org.apache.catalina.mbeans.ClassNameMBean"
+          description="Custom LDAPRealm"
+               domain="Catalina"
+                group="Realm"
+                 type="com.myfirm.mypackage.LDAPRealm"&gt;
+
+    &lt;attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/&gt;
+
+    &lt;attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/&gt;
+    .
+    .
+    .
+
+  &lt;/mbean&gt;
+</source>
+
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/monitoring.xml b/container/webapps/docs/monitoring.xml
new file mode 100644
index 0000000..29cd6b9
--- /dev/null
+++ b/container/webapps/docs/monitoring.xml
@@ -0,0 +1,1011 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="monitoring.html">
+
+  &project;
+
+  <properties>
+    <author email="pero@apache.org">Peter Rossbach</author>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <title>Monitoring and Managing Tomcat</title>
+  </properties>
+
+<body>
+
+  <section name="Introduction">
+
+  <p>Monitoring is a very important question today. Looking inside the running
+        server, grab some statistic data or reconfigure some aspects are 
+        daliy adminstration tasks.</p>  
+  
+  </section>
+
+  <section name="Enabling JMX Remote">
+
+    <p>The Sun website includes the list of options and how to configure JMX Remote on Java 5:
+        <a href="http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html">
+        http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html</a>.
+    </p>
+    <p>For quick installation you find here a short installation guide:</p>
+    <p>Add the following parameters to your tomcat startup script:
+    <source>
+    set CATALINA_OPTS="-Dcom.sun.management.jmxremote \
+    -Dcom.sun.management.jmxremote.port=%my.jmx.port% \
+    -Dcom.sun.management.jmxremote.ssl=false \
+    -Dcom.sun.management.jmxremote.authenticate=false"
+    </source>
+    </p>
+    <p>
+    <ol>
+    <li>When you think authorisation is a good, add and change this :
+    <source>
+    -Dcom.sun.management.jmxremote.authenticate=true \
+    -Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password \
+    -Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access \
+    </source>
+    </li>
+    <li>edit the access allow file <em>$CATALINA_BASE/conf/jmxremote.access</em> :
+    <source>
+monitorRole readonly
+controlRole readwrite
+    </source>
+    </li>
+    <li>edit the password file <em>$CATALINA_BASE/conf/jmxremote.password</em> :
+    <source>
+monitorRole tomcat
+controlRole tomcat
+    </source>
+    <b>Tipp</b>: Password File must be readonly and not accessable from every 
+    other user! Remove all other users under windows to access this file.
+    </li>
+    </ol>
+    <b>Note:</b>The JSR 160 JMX-Adaptor opens a second data protocol port. That is a problem
+    when you have installed a local firewall.<br/>
+    </p>
+    <p>Activate JMX MX4J Http Adaptor with Java 1.4:
+    <ol>
+      <li>Install the tomcat compat package</li>
+      <li>Install the mx4j-tools.jar at common/lib. Please, use the same MX4j 
+          version as your tomcat release</li>
+      <li>Configure a MX4J JMX HTTP Adaptor at your AJP Connector
+      <p>
+      <source>
+      &lt;Connector port="${AJP.PORT}" 
+            handler.list="mx" 
+            mx.enabled="true" 
+            mx.httpHost="${JMX.HOST}"
+            mx.httpPort="${JMX.PORT}"
+            protocol="AJP/1.3" /&gt;
+      </source>
+      </p>
+      <p><b>Tipp</b>: With <em>${AJP.PORT}=0</em> no ajp connection where started. 
+      </p>
+      <p><b>Note</b>: MX4J JSR 160 RMI Adaptor to support JDK 1.4 currently not integrated.
+      </p>
+      </li>
+      <li>Start your tomcat and look with a browser at http://${JMX.HOST}:${JMX.PORT}</li>
+      <li>With the mx connector parameter <code>mx.authMode="basic" mx.authUser="tomcat" mx.authPassword="strange"</code> 
+          you can control the access!</li>
+      <li>A complete list of all tomcat core MBeans can you find at <a href="http://jakarta.apache.org/tomcat/tomcat-5.5-doc/catalina/funcspecs/mbean-names.html">
+        http://jakarta.apache.org/tomcat/tomcat-5.5-doc/catalina/funcspecs/mbean-names.html</a>.</li>
+    </ol>
+    </p>
+
+  </section>
+
+  <section name="Manage Tomcat with JMX remote Ant Tasks">
+   <p>For simple tomcat ant task usage with ant 1.6.x we have integrate import and antlib support.</p>   
+   <p><b>antlib</b>Copy your catalina-ant.jar from $CATALINA_HOME/server/lib to $ANT_HOME/lib.</p>
+   <p>Following example show the JMX Accessor usage:</p>
+   <table border="1">
+   <tr><td><p><pre>
+&lt;project name="Catalina Ant JMX" 
+        xmlns:jmx="antlib:org.apache.catalina.ant.jmx" 
+        default="state"
+        basedir="."&gt;
+    &lt;property name="jmx.server.name" value="localhost" /&gt;
+    &lt;property name="jmx.server.port" value="9012" /&gt;
+    &lt;property name="cluster.server.address" value="192.168.1.75" /&gt;
+    &lt;property name="cluster.server.port" value="9025" /&gt;
+ 
+    &lt;target name="state" description="Show JMX Cluster state"&gt;
+        &lt;jmx:open
+            host="${jmx.server.name}"
+            port="${jmx.server.port}"
+            username="controlRole"
+            password="tomcat"/&gt;
+        &lt;jmx:get
+            name="Catalina:type=IDataSender,host=localhost,senderAddress=${cluster.server.address},senderPort=${cluster.server.port}" 
+            attribute="connected"
+            resultproperty="IDataSender.backup.connected"
+            echo="false"
+        /&gt;
+       &lt;jmx:get
+            name="Catalina:type=ClusterSender,host=localhost" 
+            attribute="senderObjectNames"
+            resultproperty="senderObjectNames"
+            echo="false"
+        /&gt;
+        &lt;!-- get current maxActiveSession from ClusterTest application
+             echo it to ant output and store at 
+             property &lt;em&gt;clustertest.maxActiveSessions.orginal&lt;/em&gt;
+        --&gt;
+       &lt;jmx:get
+            name="Catalina:type=Manager,path=/ClusterTest,host=localhost" 
+            attribute="maxActiveSessions"
+            resultproperty="clustertest.maxActiveSessions.orginal"
+            echo="true"
+        /&gt;
+        &lt;!-- set maxActiveSession to 100
+        --&gt;
+        &lt;jmx:set
+            name="Catalina:type=Manager,path=/ClusterTest,host=localhost" 
+            attribute="maxActiveSessions"
+            value="100"
+            type="int"
+        /&gt;
+        &lt;!-- get all sessions and split result as delimiter &lt;em&gt;SPACE&lt;/em&gt; for easy
+             access all session ids directly with ant property sessions.[0..n].
+        --&gt;
+        &lt;jmx:invoke
+            name="Catalina:type=Manager,path=/ClusterTest,host=localhost" 
+            operation="listSessionIds"
+            resultproperty="sessions"
+            echo="false"
+            delimiter=" "
+        /&gt;
+        &lt;!-- Access session attribute &lt;em&gt;Hello&lt;/em&gt; from first session.
+        --&gt;
+        &lt;jmx:invoke
+            name="Catalina:type=Manager,path=/ClusterTest,host=localhost" 
+            operation="getSessionAttribute"
+            resultproperty="Hello"
+            echo="false"
+        &gt;
+          &lt;arg value="${sessions.0}"/&gt;
+          &lt;arg value="Hello"/&gt;
+        &lt;/jmx:invoke&gt; 
+        &lt;!-- Query for all application manager.of the server from all hosts
+             and bind all attributes from all found manager mbeans.
+        --&gt;
+        &lt;jmx:query
+            name="Catalina:type=Manager,*" 
+            resultproperty="manager"
+            echo="true"
+            attributebinding="true"
+        /&gt;
+        &lt;!-- echo the create properties --&gt;
+        &lt;echo&gt;
+           senderObjectNames: ${senderObjectNames.0}
+           IDataSender.backup.connected: ${IDataSender.backup.connected}
+           session: ${sessions.0}
+           manager.length: ${manager.length}
+           manager.0.name: ${manager.0.name}
+           manager.1.name: ${manager.1.name}
+           hello: ${Hello}
+           manager.ClusterTest.0.name: ${manager.ClusterTest.0.name}
+           manager.ClusterTest.0.activeSessions: ${manager.ClusterTest.0.activeSessions}
+           manager.ClusterTest.0.counterSend_EVT_SESSION_EXPIRED: ${manager.ClusterTest.0.counterSend_EVT_SESSION_EXPIRED}
+           manager.ClusterTest.0.counterSend_EVT_GET_ALL_SESSIONS: ${manager.ClusterTest.0.counterSend_EVT_GET_ALL_SESSIONS}
+        &lt;/echo&gt;   
+
+    &lt;/target&gt;
+ 
+&lt;/project&gt;
+   </pre></p>
+   </td></tr>
+</table>
+   <p><b>import:</b> Import the JMX Accessor Projekt with 
+   <em>&lt;import file="${CATALINA.HOME}/bin/jmxaccessor-tasks.xml" /&gt;</em> and
+   reference the tasks with <em>jmxOpen</em>, <em>jmxSet</em>, <em>jmxGet</em>,
+    <em>jmxQuery</em>, <em>jmxInvoke</em>,<em>jmxEquals</em> and <em>jmxCondition</em>. </p>
+
+  </section>
+
+<!-- Open ######################################################################### 
+-->
+
+<section name="JMXAccessorOpenTask - jmx open connection task">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+
+  <tr>
+    <td>url</td>
+    <td>Set jmx connection url - <em>service:jmx:rmi:///jndi/rmi://localhost:8050/jmxrmi</em>
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>host</td>
+    <td>Set the host, shortcut the very long url syntax.
+    </td>
+    <td><code>localhost</code></td>
+  </tr>
+
+  <tr>
+    <td>port</td>
+    <td>Set the remote connection port 
+    </td>
+    <td><code>8050</code></td>
+  </tr>
+
+  <tr>
+    <td>username</td>
+    <td>remote jmx connection user name.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>password</td>
+    <td>remote jmx connection password.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>ref</td>
+    <td>Name of the internal connection referenz. With this attribute you can
+        configure more the one connection inside the same ant projekt.
+    </td>
+    <td><code>jmx.server</code></td>
+  </tr>
+
+  <tr>
+    <td>echo</td>
+    <td>Echo the command usage (for analyse access or debugging)
+    </td>
+    <td><code>false</code></td>
+  </tr>
+  
+  <tr>
+    <td>if</td>
+    <td>Only execute if a property of the given name <b>exists</b> in the current project.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>unless</td>
+    <td>Only execute if a property of the given name <b>not exists</b> in the current project.
+    </td>
+    <td></td>
+  </tr>
+
+</table>
+</p>
+<p>
+Example to open a new jmx connection<br/>
+<source>
+    &lt;jmx:open
+            host="${jmx.server.name}"
+            port="${jmx.server.port}"
+    /&gt;
+</source>
+</p>  
+<p>
+Example to open a jmx connection from url, with authorisation and 
+store at other reference <br/>
+<source>
+    &lt;jmx:open
+            url="service:jmx:rmi:///jndi/rmi://localhost:9024/jmxrmi"
+            ref="jmx.server.9024"
+            username="controlRole"
+            password="tomcat"    
+    /&gt;
+</source>
+</p>  
+
+<p>
+Example to open a jmx connection from url, with authorisation and 
+store at other reference, but only when property <em>jmx.if</em> exists and 
+<em>jmx.unless</em> not exists<br/>
+<source>
+    &lt;jmx:open
+            url="service:jmx:rmi:///jndi/rmi://localhost:9024/jmxrmi"
+            ref="jmx.server.9024"
+            username="controlRole"
+            password="tomcat"    
+            if="jmx.if"    
+            unless="jmx.unless"    
+    /&gt;
+</source>
+</p> 
+<p><b>Note</b>: All properties from <em>jmxOpen</em> task also exists at all 
+other tasks and conditions. 
+</p>
+
+</section>
+
+<!-- Get ######################################################################### 
+-->
+
+<section name="JMXAccessorGetTask:  get attribute value ant task">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+
+  <tr>
+    <td>name</td>
+    <td>Full qualified JMX ObjectName -- <em>Catalina:type=Server</em>
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>attribute</td>
+    <td>Existing Mbean attribute (see Tomcat mbean description above)
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>ref</td>
+    <td>JMX Connection reference
+    </td>
+    <td><code>jmx.server</code></td>
+  </tr>
+
+  <tr>
+    <td>echo</td>
+    <td>Echo command usage (access and result)
+    </td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>resultproperty</td>
+    <td>Save result at this project property
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>delimiter</td>
+    <td>Split result with delimiter (java.util.StringTokenizier) 
+        and use resultproperty as prefix to store tokens.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>separatearrayresults</td>
+    <td>When return value is an array, save result as property list 
+    (<em>$resultproperty.[0..N]</em> and <em>$resultproperty.lenght</em>) 
+    </td>
+    <td><code>true</code></td>
+  </tr>
+
+</table>
+</p>
+<p>
+Example to get remote mbean attribute from default jmx connection <br/>
+<source>
+    &lt;jmx:get
+        name="Catalina:type=Manager,path=/servlets-examples,host=localhost" 
+        attribute="maxActiveSessions"
+        resultproperty="servlets-examples.maxActiveSessions"
+    /&gt;
+</source>
+</p>  
+<p>
+Example to get and result array and split it at separate properties<br/>
+<source>
+    &lt;jmx:get
+        name="Catalina:type=ClusterSender,host=localhost" 
+        attribute="senderObjectNames"
+        resultproperty="senderObjectNames"
+    /&gt;
+</source>
+Access the senderObjectNames properties with:
+<source>
+    ${senderObjectNames.lenght} give the number of returned sender list.
+    ${senderObjectNames.[0..N]} found all sender object names
+</source>
+</p>  
+
+<p>
+Example to get IDataSender attribute connected only when cluster is configured.
+<source>
+&lt;jmx:query
+    failonerror="false"
+    name="Catalina:type=Cluster,host=${tomcat.application.host}"
+    resultproperty="cluster"
+/&gt;
+&lt;jmx:get
+    name="Catalina:type=IDataSender,host=${tomcat.application.host},senderAddress=${cluster.backup.address},senderPort=${cluster.backup.port}" 
+    attribute="connected"
+    resultproperty="datasender.connected"
+    if="cluster.0.name" /&gt;
+</source>
+</p>  
+
+</section>
+
+<!-- Set ######################################################################### 
+-->
+
+<section name="JMXAccessorSetTask:  set attribute value ant task">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+
+  <tr>
+    <td>name</td>
+    <td>Full qualified JMX ObjectName -- <em>Catalina:type=Server</em>
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>attribute</td>
+    <td>Existing Mbean attribute (see Tomcat mbean description above)
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>value</td>
+    <td>value that set to attribute 
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>type</td>
+    <td>type of the attribute.
+    </td>
+    <td>java.lang.String</td>
+  </tr>
+
+  <tr>
+    <td>ref</td>
+    <td>JMX Connection reference
+    </td>
+    <td><code>jmx.server</code></td>
+  </tr>
+
+  <tr>
+    <td>echo</td>
+    <td>Echo command usage (access and result)
+    </td>
+    <td><code>false</code></td>
+  </tr>
+
+</table>
+</p>
+<p>
+Example to set remote mbean attribute value<br/>
+<source>
+    &lt;jmx:set
+        name="Catalina:type=Manager,path=/servlets-examples,host=localhost" 
+        attribute="maxActiveSessions"
+        value="500"
+        type="int"
+    /&gt;
+</source>
+</p>  
+
+</section>
+
+<!-- Invoke ######################################################################### 
+-->
+
+<section name="JMXAccessorInvokeTask:  invoke Mbean operation ant task">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+
+  <tr>
+    <td>name</td>
+    <td>Full qualified JMX ObjectName -- <em>Catalina:type=Server</em>
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>operation</td>
+    <td>Existing Mbean operation (see Tomcat 
+        <a href="http://jakarta.apache.org/tomcat/tomcat-5.5-doc/catalina/funcspecs/fs-admin-opers.html">
+        http://jakarta.apache.org/tomcat/tomcat-5.5-doc/catalina/funcspecs/fs-admin-opers.html</a>.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>ref</td>
+    <td>JMX Connection reference
+    </td>
+    <td><code>jmx.server</code></td>
+  </tr>
+
+  <tr>
+    <td>echo</td>
+    <td>Echo command usage (access and result)
+    </td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>resultproperty</td>
+    <td>Save result at this project property
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>delimiter</td>
+    <td>Split result with delimiter (java.util.StringTokenizier) 
+        and use resultproperty as prefix to store tokens.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>separatearrayresults</td>
+    <td>When return value is an array, save result as property list 
+    (<em>$resultproperty.[0..N]</em> and <em>$resultproperty.lenght</em>) 
+    </td>
+    <td><code>true</code></td>
+  </tr>
+
+</table>
+</p>
+<p>
+stop an application <br/>
+<source>
+    &lt;jmx:invoke
+        name="Catalina:type=Manager,path=/servlets-examples,host=localhost" 
+        operation="stop"/&gt;
+</source>
+Now you can find the sessionid at <em>${sessions.[0..N}</em> properties and access the count
+with ${sessions.lenght} property.
+</p>  
+<p>
+Example to get all sessionids <br/>
+<source>
+    &lt;jmx:invoke
+        name="Catalina:type=Manager,path=/servlets-examples,host=localhost" 
+        operation="listSessionIds"
+        resultproperty="sessions"
+        delimiter=" "        
+    /&gt;
+</source>
+Now you can find the sessionid at <em>${sessions.[0..N}</em> properties and access the count
+with ${sessions.lenght} property.
+</p>  
+<p>
+Example to get remote mbean session attribute from session ${sessionid.0}<br/>
+<source>
+    &lt;jmx:invoke
+        name="Catalina:type=Manager,path=/ClusterTest,host=localhost" 
+        operation="getSessionAttribute"
+        resultproperty="hello"&gt;
+         &lt;arg value="${sessionid.0}"/&gt;
+         &lt;arg value="Hello" /&gt;
+ &lt;/jmx:invoke&gt;
+</source>
+</p>
+<p>
+Example to create a new access logger valve at vhost <em>localhost</em>
+<source>
+ &lt;jmx:invoke
+         name="Catalina:type=MBeanFactory" 
+         operation="createAcccesLoggerValve"
+         resultproperty="acccesLoggerObjectName"
+ &gt;
+     &lt;arg value="Catalina:type=Host,host=localhost"/&gt;
+ &lt;/jmx:invoke&gt;
+</source>
+Now you can find new Mbean with name stored at <em>${acccesLoggerObjectName}</em>
+proeprty.
+</p>  
+
+</section>
+
+<!-- Query ######################################################################### 
+-->
+
+<section name="JMXAccessorQueryTask:  query Mbean ant task">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+
+  <tr>
+    <td>name</td>
+    <td>JMX  ObjectName query string -- <em>Catalina:type=Manager,*</em>
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>ref</td>
+    <td>JMX Connection reference
+    </td>
+    <td><code>jmx.server</code></td>
+  </tr>
+
+  <tr>
+    <td>echo</td>
+    <td>Echo command usage (access and result)
+    </td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>resultproperty</td>
+    <td>Prefix project property name to all founded Mbeans (<em>mbeans.[0..N].objectname</em>)
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>attributebinduing</td>
+    <td>bind ALL MBean attributes in addition to <em>name</em>
+    </td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>delimiter</td>
+    <td>Split result with delimiter (java.util.StringTokenizier) 
+        and use resultproperty as prefix to store tokens.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>separatearrayresults</td>
+    <td>When return value is an array, save result as property list 
+    (<em>$resultproperty.[0..N]</em> and <em>$resultproperty.lenght</em>) 
+    </td>
+    <td><code>true</code></td>
+  </tr>
+
+</table>
+</p>
+<p>
+Get all Manager ObjectNames from all services and Hosts <br/>
+<source>
+  &lt;jmx:query
+           name="Catalina:type=Manager,* 
+           resultproperty="manager" /&gt;
+</source>
+Now you can find the Session Manager at <em>${manager.[0..N].name}</em> 
+properties and access the result object counter with ${manager.length} property.
+</p>  
+<p>
+Example to get the Manager from <em>servlet-examples</em> application an bind all mbean properties<br/>
+<source>
+  &lt;jmx:query
+           name="Catalina:type=Manager,path=/servlet-examples,host=localhost*" 
+           attributebinding="true"
+           resultproperty="manager.servletExamples" /&gt;
+</source>
+Now you can find the manager at <em>${manager.servletExamples.0.name}</em> property
+and can access all properties from this manager with <em>${manager.servletExamples.0.[manager attribute names]</em>}.
+The result object counter from MBeans is stored ad ${manager.length} property.
+</p>  
+
+<p>
+Example to get all MBeans from a server and store inside an external xml property file<br/>
+<source>
+&lt;project name="jmx.query"         
+            xmlns:jmx="antlib:org.apache.catalina.ant.jmx"
+            default="query-all" basedir="."&gt;
+&lt;property name="jmx.host" value="localhost"/&gt;
+&lt;property name="jmx.port" value="8050"/&gt;
+&lt;property name="jmx.username" value="controlRole"/&gt;
+&lt;property name="jmx.password" value="tomcat"/&gt;
+
+&lt;target name="query-all" description="Query all MBeans of a server"&gt;
+&lt;!-- Configure connection --&gt;
+&lt;jmx:open 
+    host="${jmx.host}"
+    port="${jmx.port}"
+    ref="jmx.server"
+    username="${jmx.username}"
+    password="${jmx.password}"/&gt;
+&lt;!-- Query MBean list --&gt;
+&lt;jmx:query 
+    name="*:*"
+    resultproperty="mbeans"
+    attributebinding="false"/&gt;
+    
+&lt;echoproperties
+    destfile="mbeans.properties"
+    prefix="mbeans."
+    format="xml"/&gt;
+    
+&lt;!-- Print results --&gt;
+&lt;echo
+    message="Number of MBeans in server ${jmx.host}:${jmx.port} is ${mbeans.length}"/&gt;
+&lt;/target&gt;
+&lt;/project&gt;
+</source>
+Now you can find all MBeans inside the file <em>mbeans.properties</em>.
+</p>  
+
+</section>
+
+<!-- condition ######################################################################### 
+-->
+
+<section name="JMXAccessorCondition:  express condition">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+
+ <tr>
+    <td>url</td>
+    <td>Set jmx connection url - <em>service:jmx:rmi:///jndi/rmi://localhost:8050/jmxrmi</em>
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>host</td>
+    <td>Set the host, shortcut the very long url syntax.
+    </td>
+    <td><code>localhost</code></td>
+  </tr>
+
+  <tr>
+    <td>port</td>
+    <td>Set the remote connection port 
+    </td>
+    <td><code>8050</code></td>
+  </tr>
+
+  <tr>
+    <td>username</td>
+    <td>remote jmx connection user name.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>password</td>
+    <td>remote jmx connection password.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>ref</td>
+    <td>Name of the internal connection reference. With this attribute you can
+        configure more the one connection inside the same ant projekt.
+    </td>
+    <td><code>jmx.server</code></td>
+  </tr>
+
+  <tr>
+    <td>name</td>
+    <td>Full qualified JMX ObjectName -- <em>Catalina:type=Server</em>
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>echo</td>
+    <td>Echo condition usage (access and result)
+    </td>
+    <td><code>false</code></td>
+  </tr>
+
+  <tr>
+    <td>if</td>
+    <td>Only execute if a property of the given name <b>exists</b> in the current project.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>unless</td>
+    <td>Only execute if a property of the given name <b>not exists</b> in the current project.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>value (requiered)</td>
+    <td>Second arg for operation
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>type</td>
+    <td>Value type to express operation (support <em>long</em> and <em>double</em>)
+    </td>
+    <td><code>long</code></td>
+  </tr>
+
+  <tr>
+    <td>operation</td>
+    <td> express one 
+    <ul>
+    <li>==  equals</li>
+    <li>!=  not equals</li>
+    <li>&gt; greater than (&amp;gt;)</li>
+    <li>&gt;= greater than or equals (&amp;gt;=)</li>
+    <li>&lt; lesser than (&amp;lt;)</li>
+    <li>&lt;= lesser than or equals (&amp;lt;=)</li>
+    </ul>         
+    </td>
+    <td><code>==</code></td>
+  </tr>
+
+</table>
+</p>
+<p>
+Wait for server connection and that cluster backup node is accessable<br/>
+<source>
+      &lt;target name="wait"&gt;
+         &lt;waitfor maxwait="${maxwait}" maxwaitunit="second" timeoutproperty="server.timeout" &gt;
+            &lt;and&gt;
+                &lt;socket server="${server.name}" port="${server.port}"/&gt;
+                &lt;http url="${url}"/&gt;
+                &lt;jmx:condition
+                    operation="==" 
+                    host="localhost" 
+                    port="9014"
+                    username="controlRole"
+                    password="tomcat"
+                    name="Catalina:type=IDataSender,host=localhost,senderAddress=192.168.111.1,senderPort=9025"
+                    attribute="connected"
+                    value="true"
+                /&gt;
+            &lt;/and&gt;
+        &lt;/waitfor&gt;
+        &lt;fail if="server.timeout" message="Server ${url} don't answer inside ${maxwait} sec" /&gt;
+        &lt;echo message="Server ${url} alive" /&gt;
+    &lt;/target&gt;
+</source>
+</p>  
+
+</section>
+
+<!-- Equals ######################################################################### 
+-->
+
+<section name="JMXAccessorEqualsCondition:  equals Mbean ant condition">
+<p>
+List of Attributes<br/>
+<table border="1" cellpadding="5">
+
+  <tr>
+    <th align="center" bgcolor="aqua">Attribute</th>
+    <th align="center" bgcolor="aqua">Description</th>
+    <th align="center" bgcolor="aqua">Default value</th>
+  </tr>
+
+ <tr>
+    <td>url</td>
+    <td>Set jmx connection url - <em>service:jmx:rmi:///jndi/rmi://localhost:8050/jmxrmi</em>
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>host</td>
+    <td>Set the host, shortcut the very long url syntax.
+    </td>
+    <td><code>localhost</code></td>
+  </tr>
+
+  <tr>
+    <td>port</td>
+    <td>Set the remote connection port 
+    </td>
+    <td><code>8050</code></td>
+  </tr>
+
+  <tr>
+    <td>username</td>
+    <td>remote jmx connection user name.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>password</td>
+    <td>remote jmx connection password.
+    </td>
+    <td></td>
+  </tr>
+
+  <tr>
+    <td>ref</td>
+    <td>Name of the internal connection referenz. With this attribute you can
+        configure more the one connection inside the same ant projekt.
+    </td>
+    <td><code>jmx.server</code></td>
+  </tr>
+
+  <tr>
+    <td>name</td>
+    <td>Full qualified JMX ObjectName -- <em>Catalina:type=Server</em>
+    </td>
+    <td></td>
+  </tr>
+
+
+  <tr>
+    <td>echo</td>
+    <td>Echo condition usage (access and result)
+    </td>
+    <td><code>false</code></td>
+  </tr>
+
+</table>
+</p>
+<p>
+Wait for server connection and that cluster backup node is accessable<br/>
+<source>
+      &lt;target name="wait"&gt;
+         &lt;waitfor maxwait="${maxwait}" maxwaitunit="second" timeoutproperty="server.timeout" &gt;
+            &lt;and&gt;
+                &lt;socket server="${server.name}" port="${server.port}"/&gt;
+                &lt;http url="${url}"/&gt;
+                &lt;jmx:equals 
+                    host="localhost" 
+                    port="9014"
+                    username="controlRole"
+                    password="tomcat"
+                    name="Catalina:type=IDataSender,host=localhost,senderAddress=192.168.111.1,senderPort=9025"
+                    attribute="connected"
+                    value="true"
+                /&gt;
+            &lt;/and&gt;
+        &lt;/waitfor&gt;
+        &lt;fail if="server.timeout" message="Server ${url} don't answer inside ${maxwait} sec" /&gt;
+        &lt;echo message="Server ${url} alive" /&gt;
+    &lt;/target&gt;
+</source>
+</p>  
+
+</section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/project.xml b/container/webapps/docs/project.xml
new file mode 100644
index 0000000..781d901
--- /dev/null
+++ b/container/webapps/docs/project.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Apache Tomcat Documentation - Top Level Directory"
+        href="http://jakarta.apache.org/tomcat/">
+
+    <title>The Apache Tomcat 5.5 Servlet/JSP Container</title>
+
+    <logo href="/images/tomcat.gif">
+      The Apache Tomcat Servlet/JSP Container
+    </logo>
+
+
+    <body>
+
+    <menu name="Links">
+        <item name="Docs Home"             href="index.html"/>
+        <item name="FAQ"                   href="../faq" />
+    </menu>
+
+    <menu name="User Guide">
+        <item name="1) Introduction"        href="introduction.html"/>
+        <item name="2) Setup"               href="setup.html"/>
+        <item name="3) First webapp"        href="appdev/index.html"/>
+        <item name="4) Deployer"            href="deployer-howto.html"/>
+        <item name="5) Manager"             href="manager-howto.html"/>
+        <item name="6) Realms and AAA"      href="realm-howto.html"/>
+        <item name="7) Security Manager"
+              href="security-manager-howto.html"/>
+        <item name="8) JNDI Resources"      href="jndi-resources-howto.html"/>
+        <item name="9) JDBC DataSources"
+              href="jndi-datasource-examples-howto.html"/>
+        <item name="10) Classloading"       href="class-loader-howto.html"/>
+        <item name="11) JSPs"               href="jasper-howto.html"/>
+        <item name="12) SSL"                href="ssl-howto.html"/>
+        <item name="13) SSI"                href="ssi-howto.html"/>
+        <item name="14) CGI"                href="cgi-howto.html"/>
+        <item name="15) Proxy Support"      href="proxy-howto.html"/>
+        <item name="16) MBean Descriptor"
+              href="mbeans-descriptor-howto.html"/>
+        <item name="17) Default Servlet"    href="default-servlet.html"/>
+        <item name="18) Clustering"         href="cluster-howto.html"/>
+        <item name="19) Load Balancer"      href="balancer-howto.html"/>
+        <item name="20) Connectors"         href="connectors.html"/>
+        <item name="21) Monitoring and Management"         
+              href="monitoring.html"/>
+        <item name="22) Logging"            href="logging.html"/>
+        <item name="23) APR"                href="apr.html"/>
+    </menu>
+
+    <menu name="Reference">
+        <item name="Release Notes"         href="RELEASE-NOTES.txt"/>
+        <item name="Apache Tomcat Configuration"  href="config/index.html"/>
+        <item name="JK 1.2 Documentation"      
+              href="http://jakarta.apache.org/tomcat/connectors-doc/"/>
+        <item name="Servlet API Javadocs"  href="servletapi/index.html"/>
+        <item name="JSP API Javadocs"      href="jspapi/index.html"/>
+    </menu>
+
+    <menu name="Apache Tomcat Development">
+        <item name="Building"              href="building.html"/>
+        <item name="Changelog"             href="changelog.html"/>
+        <item name="Status"                href="status.html"/>
+        <item name="Developers"            href="developers.html"/>
+        <item name="Functional Specs."     href="catalina/funcspecs/index.html"/>
+        <item name="Apache Tomcat Javadocs"       href="catalina/docs/api/index.html"/>
+        <item name="Apache Jasper Javadocs"       href="jasper/docs/api/index.html"/>
+        <item name="Architecture"          href="architecture/index.html" />
+    </menu>
+
+    </body>
+
+</project>
diff --git a/container/webapps/docs/proxy-howto.xml b/container/webapps/docs/proxy-howto.xml
new file mode 100644
index 0000000..0def1c9
--- /dev/null
+++ b/container/webapps/docs/proxy-howto.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="proxy-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+        <title>Proxy Support HOW-TO</title>
+    </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+<p>Using standard configurations of Tomcat, web applications can ask for
+the server name and port number to which the request was directed for
+processing.  When Tomcat is running standalone with the
+<a href="config/coyote.html">Coyote HTTP/1.1 Connector</a>, it will generally
+report the server name specified in the request, and the port number on
+which the <strong>Connector</strong> is listening.  The servlet API
+calls of interest, for this purpose, are:</p>
+<ul>
+<li><code>ServletRequest.getServerName()</code>: Returns the host name of the server to which the request was sent.</li>
+<li><code>ServletRequest.getServerPort()</code>: Returns the host name of the server to which the request was sent.</li>
+<li><code>ServletRequest.getLocalName()</code>: Returns the host name of the Internet Protocol (IP) interface on which the request was received.</li>
+<li><code>ServletRequest.getLocalPort()</code>:  Returns the Internet Protocol (IP) port number of the interface on which the request was received.</li>
+</ul>
+
+<p>When you are running behind a proxy server (or a web server that is
+configured to behave like a proxy server), you will sometimes prefer to
+manage the values returned by these calls.  In particular, you will
+generally want the port number to reflect that specified in the original
+request, not the one on which the <strong>Connector</strong> itself is
+listening.  You can use the <code>proxyName</code> and <code>proxyPort</code>
+attributes on the <code>&lt;Connector&gt;</code> element to configure
+these values.</p>
+
+<p>Proxy support can take many forms.  The following sections describe
+proxy configurations for several common cases.</p>
+
+</section>
+
+<section name="Apache 1.3 Proxy Support">
+
+<p>Apache 1.3 supports an optional module (<code>mod_proxy</code>) that
+configures the web server to act as a proxy server.  This can be used to
+forward requests for a particular web application to a Tomcat 5 instance,
+without having to configure a web connector such as <code>mod_jk</code>.
+To accomplish this, you need to perform the following tasks:</p>
+<ol>
+<li>Configure your copy of Apache so that it includes the
+    <code>mod_proxy</code> module.  If you are building from source,
+    the easiest way to do this is to include the
+    <code>--enable-module=proxy</code> directive on the
+    <code>./configure</code> command line.</li>
+<li>If not already added for you, make sure that you are loading the
+    <code>mod_proxy</code> module at Apache startup time, by using the
+    following directives in your <code>httpd.conf</code> file:
+<source>
+LoadModule proxy_module  {path-to-modules}/mod_proxy.so
+AddModule  mod_proxy.c
+</source></li>
+<li>Include two directives in your <code>httpd.conf</code> file for
+    each web application that you wish to forward to Tomcat 5.  For
+    example, to forward an application at context path <code>/myapp</code>:
+<source>
+ProxyPass         /myapp  http://localhost:8081/myapp
+ProxyPassReverse  /myapp  http://localhost:8081/myapp
+</source>
+    which tells Apache to forward URLs of the form
+    <code>http://localhost/myapp/*</code> to the Tomcat 5 connector
+    listening on port 8081.</li>
+<li>Configure your copy of Tomcat 5 to include a special
+    <code>&lt;Connector&gt;</code> element, with appropriate
+    proxy settings, for example:
+<source>
+&lt;Connector port="8081" ...
+              proxyName="www.mycompany.com"
+              proxyPort="80"/&gt;
+</source>
+    which will cause servlets inside this web application to think that
+    all proxied requests were directed to <code>www.mycompany.com</code>
+    on port 80.</li>
+<li>It is legal to omit the <code>proxyName</code> attribute from the
+    <code>&lt;Connector&gt;</code> element.  If you do so, the value
+    returned by <code>request.getServerName()</code> will by the host
+    name on which Tomcat is running.  In the example above, it would be
+    <code>localhost</code>.</li>
+<li>If you also have a <code>&lt;Connector&gt;</code> listening on port
+    8080 (nested within the same <a href="config/service.html">Service</a>
+    element), the requests to either port will share the same set of
+    virtual hosts and web applications.</li>
+<li>You might wish to use the IP filtering features of your operating
+    system to restrict connections to port 8081 (in this example) to
+    be allowed <strong>only</strong> from the server that is running
+    Apache.</li>
+<li>Alternatively, you can set up a series of web applications that are
+    only available via proxying, as follows:
+    <ul>
+    <li>Configure another <code>&lt;Service&gt;</code> that contains
+        only a <code>&lt;Connector&gt;</code> for the proxy port.</li>
+    <li>Configure appropriate <a href="config/engine.html">Engine</a>,
+        <a href="config/host.html">Host</a>, and
+        <a href="config/context.html">Context</a> elements for the virtual hosts
+        and web applications accessible via proxying.</li>
+    <li>Optionally, protect port 8081 with IP filters as described
+        earlier.</li>
+    </ul></li>
+<li>When requests are proxied by Apache, the web server will be recording
+    these requests in its access log.  Therefore, you will generally want to
+    disable any access logging performed by Tomcat itself.</li>
+</ol>
+
+<p>When requests are proxied in this manner, <strong>all</strong> requests
+for the configured web applications will be processed by Tomcat (including
+requests for static content).  You can improve performance by using the
+<code>mod_jk</code> web connector instead of <code>mod_proxy</code>. 
+<code>mod_jk</code> can be configured so that the web server serves static
+content that is not processed by filters or security constraints defined
+within the web application's deployment descriptor
+(<code>/WEB-INF/web.xml</code>).</p>
+
+</section>
+
+<section name="Apache 2.0 Proxy Support">
+The same instructions hold true as for 1.3. (Except in Apache 2.0,
+you may omit <code>AddModule  mod_proxy.c</code>)
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/realm-howto.xml b/container/webapps/docs/realm-howto.xml
new file mode 100644
index 0000000..1c6d19d
--- /dev/null
+++ b/container/webapps/docs/realm-howto.xml
@@ -0,0 +1,1421 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="realm-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="craigmcc@apache.org">Craig R. McClanahan</author>
+        <author email="yoavs@apache.org">Yoav Shapira</author>
+        <author email="arjaquith@mindspring.com">Andrew R. Jaquith</author>
+        <title>Realm Configuration HOW-TO</title>
+    </properties>
+
+<body>
+
+
+<section name="Table of Contents">
+
+<p>
+<a href="#Quick Start">Quick Start</a><br />
+<blockquote>
+<a href="#What is a Realm?">What is a Realm?</a><br />
+<a href="#Configuring a Realm">Configuring a Realm</a><br />
+</blockquote>
+<a href="#Common Features">Common Features</a><br />
+<blockquote>
+<a href="#Digested Passwords">Digested Passwords</a><br />
+<a href="#Example Application">Example Application</a><br />
+<a href="#Manager Application">Manager Application</a><br />
+<a href="#Realm Logging">Logging Within Realms</a><br />
+</blockquote>
+<a href="#Standard Realm Implementations">
+Standard Realm Implementations</a><br />
+<blockquote>
+<a href="#JDBCRealm">JDBCRealm</a><br />
+<a href="#DataSourceRealm">DataSourceRealm</a><br />
+<a href="#JNDIRealm">JNDIRealm</a><br />
+<a href="#MemoryRealm">MemoryRealm</a><br />
+<a href="#JAASRealm">JAASRealm</a><br />
+</blockquote>
+</p>
+
+</section>
+
+<section name="Quick Start">
+
+<p>This document describes how to configure Tomcat to support <em>container
+managed security</em>, by connecting to an existing "database" of usernames,
+passwords, and user roles.  You only need to care about this if you are using
+a web application that includes one or more
+<code>&lt;security-constraint&gt;</code> elements, and a
+<code>&lt;login-config&gt;</code> element defining how users are required
+to authenticate themselves.  If you are not utilizing these features, you can
+safely skip this document.</p>
+
+<p>For fundamental background information about container managed security,
+see the <a href="http://java.sun.com/products/servlet/download.html">Servlet
+Specification (Version 2.4)</a>, Section 12.</p>
+
+<p>For information about utilizing the <em>Single Sign On</em> feature of
+Tomcat 5 (allowing a user to authenticate themselves once across the entire
+set of web applications associated with a virtual host), see
+<a href="config/host.html#Single Sign On">here</a>.</p>
+
+</section>
+
+
+<section name="Overview">
+
+
+<subsection name="What is a Realm?">
+
+<p>A <strong>Realm</strong> is a "database" of usernames and passwords that
+identify valid users of a web application (or set of web applications), plus
+an enumeration of the list of <em>roles</em> associated with each valid user.
+You can think of roles as similar to <em>groups</em> in Unix-like operating
+systems, because access to specific web application resources is granted to
+all users possessing a particular role (rather than enumerating the list of
+associated usernames).  A particular user can have any number of roles
+associated with their username.</p>
+
+<p>Although the Servlet Specification describes a portable mechanism for
+applications to <em>declare</em> their security requirements (in the
+<code>web.xml</code> deployment descriptor), there is no portable API
+defining the interface between a servlet container and the associated user
+and role information.  In many cases, however, it is desireable to "connect"
+a servlet container to some existing authentication database or mechanism
+that already exists in the production environment.  Therefore, Tomcat 5
+defines a Java interface (<code>org.apache.catalina.Realm</code>) that
+can be implemented by "plug in" components to establish this connection.
+Five standard plug-ins are provided, supporting connections to various
+sources of authentication information:</p>
+<ul>
+<li><a href="#JDBCRealm">JDBCRealm</a> - Accesses authentication information
+    stored in a relational database, accessed via a JDBC driver.</li>
+<li><a href="#DataSourceRealm">DataSourceRealm</a> - Accesses authentication
+    information stored in a relational database, accessed via a named JNDI
+    JDBC DataSource.</li>
+<li><a href="#JNDIRealm">JNDIRealm</a> - Accesses authentication information
+    stored in an LDAP based directory server, accessed via a JNDI provider.
+    </li>
+<li><a href="#MemoryRealm">MemoryRealm</a> - Accesses authentication
+    information stored in an in-memory object collection, which is initialized
+    from an XML document (<code>conf/tomcat-users.xml</code>).</li>
+<li><a href="#JAASRealm">JAASRealm</a> - Accesses authentication information
+    through the Java Authentication &amp; Authorization Service (JAAS)
+    framework.</li>
+</ul>
+
+<p>It is also possible to write your own <code>Realm</code> implementation,
+and integrate it with Tomcat 5.  To do so, you need to:
+<ul>
+  <li>Implement <code>org.apache.catalina.Realm</code>,</li>
+  <li>Place your compiled realm in $CATALINA_HOME/server/lib,</li>
+  <li>Declare your realm as described in the "Configuring a Realm" section below,</li>
+  <li>Declare your realm to the <a href="mbeans-descriptor-howto.html">MBeans Descriptor</a>.</li>
+</ul>
+</p>
+
+</subsection>
+
+
+<subsection name="Configuring a Realm">
+
+<p>Before getting into the details of the standard Realm implementations, it is
+important to understand, in general terms, how a Realm is configured.  In
+general, you will be adding an XML element to your <code>conf/server.xml</code>
+configuration file, that looks something like this:</p>
+
+<source>
+&lt;Realm className="... class name for this implementation"
+       ... other attributes for this implementation .../&gt;
+</source>
+
+<p>The <code>&lt;Realm&gt;</code> element can be nested inside any one of 
+of the following <code>Container</code> elements.  The location of the
+Realm element has a direct impact on the "scope" of that Realm
+(i.e. which web applications will share the same authentication information):
+</p>
+<ul>
+<li><em>Inside an &lt;Engine&gt; element</em> - This Realm will be shared
+    across ALL web applications on ALL virtual hosts, UNLESS it is overridden
+    by a Realm element nested inside a subordinate <code>&lt;Host&gt;</code>
+    or <code>&lt;Context&gt;</code> element.</li>
+<li><em>Inside a &lt;Host&gt; element</em> - This Realm will be shared across
+    ALL web applications for THIS virtual host, UNLESS it is overridden
+    by a Realm element nested inside a subordinate <code>&lt;Context&gt;</code>
+    element.</li>
+<li><em>Inside a &lt;Context&gt; element</em> - This Realm will be used ONLY
+    for THIS web application.</li>
+</ul>
+
+
+</subsection>
+
+
+</section>
+
+
+<section name="Common Features">
+
+
+<subsection name="Digested Passwords">
+
+<p>For each of the standard <code>Realm</code> implementations, the
+user's password (by default) is stored in clear text.  In many
+environments, this is undesireable because casual observers of the
+authentication data can collect enough information to log on
+successfully, and impersonate other users.  To avoid this problem, the
+standard implementations support the concept of <em>digesting</em>
+user passwords.  This allows the stored version of the passwords to be
+encoded (in a form that is not easily reversible), but that the
+<code>Realm</code> implementation can still utilize for
+authentication.</p>
+
+<p>When a standard realm authenticates by retrieving the stored
+password and comparing it with the value presented by the user, you
+can select digested passwords by specifying the <code>digest</code>
+attribute on your <code>&lt;Realm&gt;</code> element.  The value for
+this attribute must be one of the digest algorithms supported by the
+<code>java.security.MessageDigest</code> class (SHA, MD2, or MD5).
+When you select this option, the contents of the password that is
+stored in the <code>Realm</code> must be the cleartext version of the
+password, as digested by the specified algorithm.</p>
+
+<p>When the <code>authenticate()</code> method of the Realm is called, the
+(cleartext) password specified by the user is itself digested by the same
+algorithm, and the result is compared with the value returned by the
+<code>Realm</code>.  An equal match implies that the cleartext version of the
+original password is the same as the one presented by the user, so that this
+user should be authorized.</p>
+
+<p>To calculate the digested value of a cleartext password, two convenience
+techniques are supported:</p>
+<ul>
+<li>If you are writing an application that needs to calculate digested
+    passwords dynamically, call the static <code>Digest()</code> method of the
+    <code>org.apache.catalina.realm.RealmBase</code> class, passing the
+    cleartext password and the digest algorithm name as arguments.  This
+    method will return the digested password.</li>
+<li>If you want to execute a command line utility to calculate the digested
+    password, simply execute
+<source>
+java org.apache.catalina.realm.RealmBase \
+    -a {algorithm} {cleartext-password}
+</source>
+    and the digested version of this cleartext password will be returned to
+    standard output.</li>
+</ul>
+
+<p>If using digested passwords with DIGEST authentication, the cleartext used
+   to generate the digest is different. In the examples above
+   <code>{cleartext-password}</code> must be replaced with 
+   <code>{username}:{realm}:{cleartext-password}</code>. For example, in a
+   development environment this might take the form
+   <code>testUser:localhost:8080:testPassword</code>.</p>
+
+<p>To use either of the above techniques, the
+<code>$CATALINA_HOME/server/lib/catalina.jar</code> file will need to be
+on your class path to make the <code>RealmBase</code> class available.  In 
+addition, you will need the JMX jar and the commons-logging jar (either 
+commons-logging-api.jar or commons-logging.jar).  Both of these are included
+with the Tomcat distribution.
+</p>
+
+<p>Non-ASCII usernames and/or passwords are supported using
+<source>java org.apache.catalina.realm.RealmBase \
+    -a {algorithm} -e {encoding} {input}
+</source>
+but care is required to ensure that the non-ASCII input is
+correctly passed to the digester.
+The digester returns <code>{input}:{digest}</code>. If the input appears
+corrupted in the return, the digest will be invalid.</p>
+
+</subsection>
+
+
+
+<subsection name="Example Application">
+
+<p>The example application shipped with Tomcat 5 includes an area that is
+protected by a security constraint, utilizing form-based login.  To access it,
+point your browser at
+<a href="http://localhost:8080/jsp-examples/security/protected/">http://localhost:8080/jsp-examples/security/protected/</a>
+and log on with one of the usernames and passwords described for the default
+<a href="#MemoryRealm">MemoryRealm</a>.</p>
+
+</subsection>
+
+
+<subsection name="Manager Application">
+
+<p>If you wish to use the <a href="manager-howto.html">Manager Application</a>
+to deploy and undeploy applications in a running Tomcat 5 installation, you
+MUST add the "manager" role to at least one username in your selected Realm
+implementation.  This is because the manager web application itself uses a
+security constraint that requires role "manager" to access ANY request URI
+within that application.</p>
+
+<p>For security reasons, no username in the default Realm (i.e. using
+<code>conf/tomcat-users.xml</code> is assigned the "manager" role.  Therfore,
+no one will be able to utilize the features of this application until the
+Tomcat administrator specifically assigns this role to one or more users.</p>
+
+</subsection>
+
+<subsection name="Realm Logging">
+
+<p>Debugging and exception messages logged by a <code>Realm</code> will
+   be recorded by the logging configuration associated with the container
+   for the realm: its surrounding <a href="context.html">Context</a>,
+   <a href="host.html">Host</a>, or <a href="engine.html">Engine</a>.</p>
+
+</subsection>
+
+</section>
+
+
+<section name="Standard Realm Implementations">
+
+<subsection name="JDBCRealm">
+
+<h3>Introduction</h3>
+
+<p><strong>JDBCRealm</strong> is an implementation of the Tomcat 5
+<code>Realm</code> interface that looks up users in a relational database
+accessed via a JDBC driver.  There is substantial configuration flexibility
+that lets you adapt to existing table and column names, as long as your
+database structure conforms to the following requirements:</p>
+<ul>
+<li>There must be a table, referenced below as the <em>users</em> table,
+    that contains one row for every valid user that this <code>Realm</code>
+    should recognize.</li>
+<li>The <em>users</em> table must contain at least two columns (it may
+    contain more if your existing applications required it):
+    <ul>
+    <li>Username to be recognized by Tomcat when the user logs in.</li>
+    <li>Password to be recognized by Tomcat when the user logs in.
+        This value may in cleartext or digested - see below for more
+        information.</li>
+    </ul></li>
+<li>There must be a table, referenced below as the <em>user roles</em> table,
+    that contains one row for every valid role that is assigned to a
+    particular user.  It is legal for a user to have zero, one, or more than
+    one valid role.</li>
+<li>The <em>user roles</em> table must contain at least two columns (it may
+    contain more if your existing applications required it):
+    <ul>
+    <li>Username to be recognized by Tomcat (same value as is specified
+        in the <em>users</em> table).</li>
+    <li>Role name of a valid role associated with this user.</li>
+    </ul></li>
+</ul>
+
+<h3>Quick Start</h3>
+
+<p>To set up Tomcat to use JDBCRealm, you will need to follow these steps:</p>
+<ol>
+<li>If you have not yet done so, create tables and columns in your database
+    that conform to the requirements described above.</li>
+<li>Configure a database username and password for use by Tomcat, that has
+    at least read only access to the tables described above.  (Tomcat will
+    never attempt to write to these tables.)</li>
+<li>Place a copy of the JDBC driver you will be using inside the
+    <code>$CATALINA_HOME/server/lib</code> directory (if you do not need it
+    visible to web applications) or <code>$CATALINA_HOME/common/lib</code>
+    (if it will be used both by Tomcat 5 <em>and</em> by your apps).
+    Note that <strong>only</strong> JAR files are recognized!</li>
+<li>Set up a <code>&lt;Realm&gt;</code> element, as described below, in your
+    <code>$CATALINA_HOME/conf/server.xml</code> file.</li>
+<li>Restart Tomcat 5 if it is already running.</li>
+</ol>
+
+<h3>Realm Element Attributes</h3>
+
+<p>To configure JDBCRealm, you will create a <code>&lt;Realm&gt;</code>
+element and nest it in your <code>$CATALINA_HOME/conf/server.xml</code> file,
+as described <a href="#Configuring a Realm">above</a>.  The following
+attributes are supported by this implementation:</p>
+
+<attributes>
+
+  <attribute name="className" required="true">
+    <p>The fully qualified Java class name of this Realm implementation.
+    You <strong>MUST</strong> specify the value
+    "<code>org.apache.catalina.realm.JDBCRealm</code>" here.</p>
+  </attribute>
+
+  <attribute name="connectionName" required="true">
+    <p>The database username used to establish a JDBC connection.</p>
+  </attribute>
+
+  <attribute name="connectionPassword" required="true">
+    <p>The database password used to establish a JDBC connection.</p>
+  </attribute>
+
+  <attribute name="connectionURL" required="true">
+    <p>The database URL used to establish a JDBC connection.</p>
+  </attribute>
+
+  <attribute name="digest" required="false">
+    <p>The digest algorithm used to store passwords in non-plaintext formats.
+    Valid values are those accepted for the algorithm name by the
+    <code>java.security.MessageDigest</code> class.  See
+    <a href="#Digested Passwords">Digested Passwords</a> for more
+    information.  If not specified, passwords are stored in clear text.</p>
+  </attribute>
+
+  <attribute name="driverName" required="true">
+    <p>The fully qualified Java class name of the JDBC driver to be used.
+    Consult the documentation for your JDBC driver for the appropriate
+    value.</p>
+  </attribute>
+
+  <attribute name="roleNameCol" required="true">
+    <p>The name of the column, in the <em>user roles</em> table, that
+    contains the name of a role assigned to this user.</p>
+  </attribute>
+
+  <attribute name="userCredCol" required="true">
+    <p>The name of the column, in the <em>users</em> table, that contains
+    the password for this user (either in clear text, or digested if the
+    <code>digest</code> attribute is set).</p>
+  </attribute>
+
+  <attribute name="userNameCol" required="true">
+    <p>The name of the column, in the <em>users</em> and <em>user roles</em>
+    tables, that contains the username of this user.</p>
+  </attribute>
+
+  <attribute name="userRoleTable" required="true">
+    <p>The name of the table that contains one row for each <em>role</em>
+    assigned to a particular <em>username</em>.  This table must include at
+    least the columns named by the <code>userNameCol</code> and
+    <code>roleNameCol</code> attributes.</p>
+  </attribute>
+
+  <attribute name="userTable" required="true">
+    <p>The name of the table that contains one row for each <em>username</em>
+    to be recognized by Tomcat.  This table must include at least the columns
+    named by the <code>userNameCol</code> and <code>userCredCol</code>
+    attributes.</p>
+  </attribute>
+
+</attributes>
+
+<h3>Example</h3>
+
+<p>An example SQL script to create the needed tables might look something
+like this (adapt the syntax as required for your particular database):</p>
+<source>
+create table users (
+  user_name         varchar(15) not null primary key,
+  user_pass         varchar(15) not null
+);
+
+create table user_roles (
+  user_name         varchar(15) not null,
+  role_name         varchar(15) not null,
+  primary key (user_name, role_name)
+);
+</source>
+
+<p>Example <code>Realm</code> elements are included (commented out) in the
+default <code>$CATALINA_HOME/conf/server.xml</code> file.  Here's an example
+for using a MySQL database called "authority", configured with the tables
+described above, and accessed with username "dbuser" and password "dbpass":</p>
+<source>
+&lt;Realm className="org.apache.catalina.realm.JDBCRealm" debug="99"
+      driverName="org.gjt.mm.mysql.Driver"
+   connectionURL="jdbc:mysql://localhost/authority?user=dbuser&amp;amp;password=dbpass"
+       userTable="users" userNameCol="user_name" userCredCol="user_pass"
+   userRoleTable="user_roles" roleNameCol="role_name"/&gt;
+</source>
+
+<h3>Additional Notes</h3>
+
+<p>JDBCRealm operates according to the following rules:</p>
+<ul>
+<li>When a user attempts to access a protected resource for the first time,
+    Tomcat 5 will call the <code>authenticate()</code> method of this
+    <code>Realm</code>.  Thus, any changes you have made to the database
+    directly (new users, changed passwords or roles, etc.) will be immediately
+    reflected.</li>
+<li>Once a user has been authenticated, the user (and his or her associated
+    roles) are cached within Tomcat for the duration of the user's login.
+    (For FORM-based authentication, that means until the session times out or
+    is invalidated; for BASIC authentication, that means until the user
+    closes their browser).  The cached user is <strong>not</strong> saved and
+    restored across sessions serialisations. Any changes to the database
+    information for an already authenticated user will <strong>not</strong> be
+    reflected until the next time that user logs on again.</li>
+<li>Administering the information in the <em>users</em> and <em>user roles</em>
+    table is the responsibility of your own applications.  Tomcat does not
+    provide any built-in capabilities to maintain users and roles.</li>
+</ul>
+
+</subsection>
+
+
+<subsection name="DataSourceRealm">
+
+<h3>Introduction</h3>
+
+<p><strong>DataSourceRealm</strong> is an implementation of the Tomcat 5
+<code>Realm</code> interface that looks up users in a relational database
+accessed via a JNDI named JDBC DataSource.  There is substantial configuration
+flexibility that lets you adapt to existing table and column names, as long
+as your database structure conforms to the following requirements:</p>
+<ul>
+<li>There must be a table, referenced below as the <em>users</em> table,
+    that contains one row for every valid user that this <code>Realm</code>
+    should recognize.</li>
+<li>The <em>users</em> table must contain at least two columns (it may
+    contain more if your existing applications required it):
+    <ul>
+    <li>Username to be recognized by Tomcat when the user logs in.</li>
+    <li>Password to be recognized by Tomcat when the user logs in.
+        This value may in cleartext or digested - see below for more
+        information.</li>
+    </ul></li>    
+<li>There must be a table, referenced below as the <em>user roles</em> table,
+    that contains one row for every valid role that is assigned to a
+    particular user.  It is legal for a user to have zero, one, or more than
+    one valid role.</li>
+<li>The <em>user roles</em> table must contain at least two columns (it may
+    contain more if your existing applications required it):
+    <ul>
+    <li>Username to be recognized by Tomcat (same value as is specified
+        in the <em>users</em> table).</li>
+    <li>Role name of a valid role associated with this user.</li>
+    </ul></li>
+</ul>
+
+<h3>Quick Start</h3>
+                  
+<p>To set up Tomcat to use DataSourceRealm, you will need to follow these steps:</p>
+<ol>              
+<li>If you have not yet done so, create tables and columns in your database
+    that conform to the requirements described above.</li>
+<li>Configure a database username and password for use by Tomcat, that has
+    at least read only access to the tables described above.  (Tomcat will
+    never attempt to write to these tables.)</li>
+<li>Configure a JNDI named JDBC DataSource for your database.  Refer to the
+    <a href="jndi-datasource-examples-howto.html">JNDI DataSource Example HOW-TO</a>
+    for information on how to configure a JNDI named JDBC DataSource.</li>
+<li>Set up a <code>&lt;Realm&gt;</code> element, as described below, in your
+    <code>$CATALINA_HOME/conf/server.xml</code> file.</li>
+<li>Restart Tomcat 5 if it is already running.</li>
+</ol>
+
+<h3>Realm Element Attributes</h3>
+
+<p>To configure DataSourceRealm, you will create a <code>&lt;Realm&gt;</code>
+element and nest it in your <code>$CATALINA_HOME/conf/server.xml</code> file,
+as described <a href="#Configuring a Realm">above</a>.  The following
+attributes are supported by this implementation:</p>
+
+<attributes>
+
+  <attribute name="className" required="true">
+    <p>The fully qualified Java class name of this Realm implementation.
+    You <strong>MUST</strong> specify the value
+    "<code>org.apache.catalina.realm.DataSourceRealm</code>" here.</p>
+  </attribute>
+
+  <attribute name="dataSourceName" required="true">
+    <p>The JNDI named JDBC DataSource for your database. If the DataSource is
+    local to the context, the name is relative to <code>java:/comp/env</code>,
+    and otherwise the name should match the name used to define the global
+    DataSource.</p>
+  </attribute>
+
+  <attribute name="digest" required="false">
+    <p>The digest algorithm used to store passwords in non-plaintext formats.
+    Valid values are those accepted for the algorithm name by the
+    <code>java.security.MessageDigest</code> class.  See
+    <a href="#Digested Passwords">Digested Passwords</a> for more
+    information.  If not specified, passwords are stored in clear text.</p>
+  </attribute>
+    
+  <attribute name="localDataSource" required="false">
+    <p>When the realm is nested inside a Context element, this allows the 
+    realm to use a DataSource defined for the Context rather than a global
+    DataSource.  If not specified, the default is <code>false</code>: use a 
+    global DataSource.</p>
+  </attribute>
+    
+  <attribute name="roleNameCol" required="true">
+    <p>The name of the column, in the <em>user roles</em> table, that
+    contains the name of a role assigned to this user.</p>
+  </attribute>
+    
+  <attribute name="userCredCol" required="true">
+    <p>The name of the column, in the <em>users</em> table, that contains
+    the password for this user (either in clear text, or digested if the
+    <code>digest</code> attribute is set).</p>
+  </attribute>
+    
+  <attribute name="userNameCol" required="true">
+    <p>The name of the column, in the <em>users</em> and <em>user roles</em>
+    tables, that contains the username of this user.</p>
+  </attribute>
+
+  <attribute name="userRoleTable" required="true">
+    <p>The name of the table that contains one row for each <em>role</em>
+    assigned to a particular <em>username</em>.  This table must include at
+    least the columns named by the <code>userNameCol</code> and
+    <code>roleNameCol</code> attributes.</p>
+  </attribute>
+
+  <attribute name="userTable" required="true">
+    <p>The name of the table that contains one row for each <em>username</em>
+    to be recognized by Tomcat.  This table must include at least the columns
+    named by the <code>userNameCol</code> and <code>userCredCol</code>
+    attributes.</p>
+  </attribute>
+
+</attributes>
+
+<h3>Example</h3>
+
+<p>An example SQL script to create the needed tables might look something
+like this (adapt the syntax as required for your particular database):</p>
+<source>
+create table users (
+  user_name         varchar(15) not null primary key,
+  user_pass         varchar(15) not null
+);
+
+create table user_roles (
+  user_name         varchar(15) not null,
+  role_name         varchar(15) not null,
+  primary key (user_name, role_name)
+);
+</source>
+
+<p>Here is an example for using a MySQL database called "authority", configured
+with the tables described above, and accessed with the JNDI JDBC DataSource with
+name "java:/comp/env/jdbc/authority".</p>
+<source>
+&lt;Realm className="org.apache.catalina.realm.DataSourceRealm" debug="99"
+   dataSourceName="jdbc/authority"
+   userTable="users" userNameCol="user_name" userCredCol="user_pass"
+   userRoleTable="user_roles" roleNameCol="role_name"/&gt;
+</source>
+
+<h3>Additional Notes</h3>
+
+<p>DataSourceRealm operates according to the following rules:</p>
+<ul>
+<li>When a user attempts to access a protected resource for the first time,
+    Tomcat 5 will call the <code>authenticate()</code> method of this
+    <code>Realm</code>.  Thus, any changes you have made to the database
+    directly (new users, changed passwords or roles, etc.) will be immediately
+    reflected.</li>
+<li>Once a user has been authenticated, the user (and his or her associated
+    roles) are cached within Tomcat for the duration of the user's login.
+    (For FORM-based authentication, that means until the session times out or
+    is invalidated; for BASIC authentication, that means until the user
+    closes their browser).  The cached user is <strong>not</strong> saved and
+    restored across sessions serialisations. Any changes to the database
+    information for an already authenticated user will <strong>not</strong> be
+    reflected until the next time that user logs on again.</li>
+<li>Administering the information in the <em>users</em> and <em>user roles</em>
+    table is the responsibility of your own applications.  Tomcat does not
+    provide any built-in capabilities to maintain users and roles.</li>
+</ul>
+
+</subsection>
+
+
+<subsection name="JNDIRealm">
+
+<h3>Introduction</h3>
+
+<p><strong>JNDIRealm</strong> is an implementation of the Tomcat 5
+<code>Realm</code> interface that looks up users in an LDAP directory
+server accessed by a JNDI provider (typically, the standard LDAP
+provider that is available with the JNDI API classes). The realm
+supports a variety of approaches to using a directory for
+authentication.</p>
+
+<h4>Connecting to the directory</h4>
+
+<p>The realm's connection to the directory is defined by the
+<strong>connectionURL</strong> configuration attribute. This is a URL
+whose format is defined by the JNDI provider. It is usually an LDAP
+URL that specifies the domain name of the directory server to connect
+to, and optionally the port number and distinguished name (DN) of the
+required root naming context.</p>
+
+<p>If you have more than one provider you can configure an
+<strong>alternateURL</strong>.  If a socket connection can not be
+made to the provider at the <strong>connectionURL</strong> an
+attempt will be made to use the <strong>alternateURL</strong>.</p>
+
+<p>When making a connection in order to search the directory and
+retrieve user and role information, the realm authenticates itself to
+the directory with the username and password specified by the
+<strong>connectionName</strong> and
+<strong>connectionPassword</strong> properties. If these properties
+are not specified the connection is anonymous. This is sufficient in
+many cases.
+</p>
+
+
+<h4>Selecting the user's directory entry</h4>
+
+<p>Each user that can be authenticated must be represented in the
+directory by an individual entry that corresponds to an element in the
+initial <code>DirContext</code> defined by the
+<strong>connectionURL</strong> attribute. This user entry must have an
+attribute containing the username that is presented for
+authentication.</p>
+
+<p>Often the distinguished name of the user's entry contains the
+username presented for authentication but is otherwise the same for
+all users. In this case the <strong>userPattern</strong> attribute may
+be used to specify the DN, with "{0}" marking where
+the username should be substituted.</p>
+
+<p>Otherwise the realm must search the directory to find a unique entry
+containing the username. The following attributes configure this
+search:
+
+     <ul>
+     <li><strong>userBase</strong> - the entry that is the base of
+         the subtree containing users.  If not specified, the search
+         base is the top-level context.</li>
+
+     <li><strong>userSubtree</strong> - the search scope. Set to
+         <code>true</code> if you wish to search the entire subtree
+         rooted at the <strong>userBase</strong> entry. The default value
+         of <code>false</code> requests a single-level search
+         including only the top level.</li>
+
+     <li><strong>userSearch</strong> - pattern specifying the LDAP
+         search filter to use after substitution of the username.</li>
+
+    </ul>
+</p>
+
+
+<h4>Authenticating the user</h4>
+
+<ul>
+<li>
+<p><b>Bind mode</b></p>
+
+<p>By default the realm authenticates a user by binding to
+the directory with the DN of the entry for that user and the password
+presented by the user. If this simple bind succeeds the user is considered to
+be authenticated.</p>
+
+<p>For security reasons a directory may store a digest of the user's
+password rather than the clear text version (see <a href="#Digested
+Passwords">Digested Passwords</a> for more information). In that case,
+as part of the simple bind operation the directory automatically
+computes the correct digest of the plaintext password presented by the
+user before validating it against the stored value. In bind mode,
+therefore, the realm is not involved in digest processing. The
+<strong>digest</strong> attribute is not used, and will be ignored if
+set.</p>
+</li>
+
+<li>
+<p><b>Comparison mode</b></p>
+<p>Alternatively, the realm may retrieve the stored
+password from the directory and compare it explicitly with the value
+presented by the user. This mode is configured by setting the
+<strong>userPassword</strong> attribute to the name of a directory
+attribute in the user's entry that contains the password.</p>
+
+<p>Comparison mode has some disadvantages. First, the
+<strong>connectionName</strong> and
+<strong>connectionPassword</strong> attributes must be configured to
+allow the realm to read users' passwords in the directory. For
+security reasons this is generally undesirable; indeed many directory
+implementations will not allow even the directory manager to read
+these passwords. In addition, the realm must handle password digests
+itself, including variations in the algorithms used and ways of
+representing password hashes in the directory. However, the realm may
+sometimes need access to the stored password, for example to support
+HTTP Digest Access Authentication (RFC 2069). (Note that HTTP digest
+authentication is different from the storage of password digests in
+the repository for user information as discussed above).
+</p>
+</li>
+</ul>
+
+<h4>Assigning roles to the user</h4>
+
+<p>The directory realm supports two approaches to the representation
+of roles in the directory:</p>
+
+<ul>
+<li>
+<p><b>Roles as explicit directory entries</b></p>
+
+<p>Roles may be represented by explicit directory entries. A role
+entry is usually an LDAP group entry with one attribute
+containing the name of the role and another whose values are the
+distinguished names or usernames of the users in that role.  The
+following attributes configure a directory search to
+find the names of roles associated with the authenticated user:</p>
+
+<ul>
+<li><strong>roleBase</strong> - the base entry for the role search.
+    If not specified, the search base is the top-level directory
+    context.</li>
+
+<li><strong>roleSubtree</strong> - the search
+    scope. Set to <code>true</code> if you wish to search the entire
+    subtree rooted at the <code>roleBase</code> entry. The default
+    value of <code>false</code> requests a single-level search
+    including the top level only.</li>
+
+<li><strong>roleSearch</strong> - the LDAP search filter for
+    selecting role entries. It optionally includes pattern
+    replacements "{0}" for the distinguished name and/or "{1}" for the
+    username of the authenticated user.</li>
+
+<li><strong>roleName</strong> - the attribute in a role entry
+     containing the name of that role.</li>
+
+</ul>
+
+</li>
+</ul>
+
+<ul>
+<li>
+<p><b>Roles as an attribute of the user entry</b></p>
+
+<p>Role names may also be held as the values of an attribute in the
+user's directory entry. Use <strong>userRoleName</strong> to specify
+the name of this attribute.</p>
+
+</li>
+</ul>
+<p>A combination of both approaches to role representation may be used.</p>
+
+<h3>Quick Start</h3>
+
+<p>To set up Tomcat to use JNDIRealm, you will need to follow these steps:</p>
+<ol>
+<li>Make sure your directory server is configured with a schema that matches
+    the requirements listed above.</li>
+<li>If required, configure a username and password for use by Tomcat, that has
+    read only access to the information described above.  (Tomcat will
+    never attempt to modify this information.)</li>
+<li>Place a copy of the JNDI driver you will be using (typically
+    <code>ldap.jar</code> available with JNDI) inside the
+    <code>$CATALINA_HOME/server/lib</code> directory (if you do not need it
+    visible to web applications) or <code>$CATALINA_HOME/common/lib</code>
+    (if it will be used both by Tomcat 5 <em>and</em> by your apps).</li>
+<li>Set up a <code>&lt;Realm&gt;</code> element, as described below, in your
+    <code>$CATALINA_HOME/conf/server.xml</code> file.</li>
+<li>Restart Tomcat 5 if it is already running.</li>
+</ol>
+
+<h3>Realm Element Attributes</h3>
+
+<p>To configure JNDIRealm, you will create a <code>&lt;Realm&gt;</code>
+element and nest it in your <code>$CATALINA_HOME/conf/server.xml</code> file,
+as described <a href="#Configuring a Realm">above</a>.  The following
+attributes are supported by this implementation:</p>
+
+<attributes>
+  <attribute name="className" required="true">
+    <p>The fully qualified Java class name of this Realm implementation.
+    You <strong>MUST</strong> specify the value
+    "<code>org.apache.catalina.realm.JNDIRealm</code>" here.</p>
+  </attribute>
+
+
+      <attribute name="connectionName" required="false">
+        <p>The directory username to use when establishing a
+        connection to the directory for LDAP search operations. If not
+        specified an anonymous connection is made, which is often
+        sufficient unless you specify the <code>userPassword</code>
+        property.</p>
+      </attribute>
+
+      <attribute name="connectionPassword" required="false">
+        <p>The directory password to use when establishing a
+        connection to the directory for LDAP search operations. If not
+        specified an anonymous connection is made, which is often
+        sufficient unless you specify the <code>userPassword</code>
+        property.</p>
+      </attribute>
+
+      <attribute name="connectionURL" required="true">
+        <p>The connection URL to be passed to the JNDI driver when
+        establishing a connection to the directory.</p>
+      </attribute>
+
+      <attribute name="contextFactory" required="false">
+        <p>The fully qualified Java class name of the JNDI context
+        factory to be used for this connection.  By default, the standard
+        JNDI LDAP provider is used
+        (<code>com.sun.jndi.ldap.LdapCtxFactory</code>).</p>
+      </attribute>
+
+      <attribute name="digest" required="false">
+        <p>The digest algorithm to apply to the plaintext password offered
+        by the user before comparing it with the value retrieved from the
+        directory.  Valid values are those accepted for the algorithm name
+        by the <code>java.security.MessageDigest</code> class.  See <a
+        href="#Digested Passwords">Digested Passwords</a> for more
+        information. If not specified the plaintext password is assumed to
+        be retrieved. Not required unless <code>userPassword</code> is
+        specified</p>
+      </attribute>
+
+      <attribute name="roleBase" required="false">
+        <p>The base directory entry for performing role searches. If
+        not specified, the top level element in the directory context
+        will be used.</p>
+      </attribute>
+
+      <attribute name="roleName" required="false">
+        <p>The name of the attribute that contains role names in the
+        directory entries found by a role search. In addition you can
+        use the <code>userRoleName</code> property to specify the name
+        of an attribute, in the user's entry, containing additional
+        role names.  If <code>roleName</code> is not specified a role
+        search does not take place, and roles are taken only from the
+        user's entry.</p>
+      </attribute>
+
+      <attribute name="roleSearch" required="false">
+        <p>The LDAP filter expression used for performing role
+        searches, following the syntax supported by the
+        <code>java.text.MessageFormat</code> class.  Use
+        <code>{0}</code> to substitute the distinguished name (DN) of
+        the user, and/or <code>{1}</code> to substitute the
+        username. If not specified a role search does not take place
+        and roles are taken only from the attribute in the user's
+        entry specified by the <code>userRoleName</code> property.</p>
+      </attribute>
+
+      <attribute name="roleSubtree" required="false">
+        <p>Set to <code>true</code> if you want to search the entire
+        subtree of the element specified by the <code>roleBase</code>
+        property for role entries associated with the user. The
+        default value of <code>false</code> causes only the top level
+        to be searched.</p>
+      </attribute>
+
+      <attribute name="userBase" required="false">
+        <p>The base element for user searches performed using the
+        <code>userSearch</code> expression.  If not specified, the top
+        level element in the directory context will be used. Not used
+        if you are using the <code>userPattern</code> expression.</p>
+      </attribute>
+
+      <attribute name="userPassword" required="false">
+        <p>Name of the attribute in the user's entry containing the
+        user's password.  If you specify this value, JNDIRealm will
+        bind to the directory using the values specified by
+        <code>connectionName</code> and
+        <code>connectionPassword</code> properties, and retrieve the
+        corresponding attribute for comparison to the value specified
+        by the user being authenticated.  If the <code>digest</code>
+        attribute is set, the specified digest algorithm is applied to
+        the password offered by the user before comparing it with the
+        value retrieved from the directory.  If you do
+        <strong>not</strong> specify this value, JNDIRealm will
+        attempt a simple bind to the directory using the DN of the
+        user's entry and password specified by the user, with a
+        successful bind being interpreted as an authenticated
+        user.</p>
+      </attribute>
+
+      <attribute name="userPattern" required="false">
+        <p>A pattern for the distinguished name (DN) of the user's
+        directory entry, following the syntax supported by the
+        <code>java.text.MessageFormat</code> class with
+        <code>{0}</code> marking where the actual username should be
+        inserted. You can use this property instead of
+        <code>userSearch</code>, <code>userSubtree</code> and
+        <code>userBase</code> when the distinguished name contains the
+        username and is otherwise the same for all users.</p>
+      </attribute>
+
+      <attribute name="userRoleName" required="false">
+        <p>The name of an attribute in the user's directory entry
+        containing zero or more values for the names of roles assigned
+        to this user.  In addition you can use the
+        <code>roleName</code> property to specify the name of an
+        attribute to be retrieved from individual role entries found
+        by searching the directory. If <code>userRoleName</code> is
+        not specified all the roles for a user derive from the role
+        search.</p>
+      </attribute>
+
+      <attribute name="userSearch" required="false">
+        <p>The LDAP filter expression to use when searching for a
+        user's directory entry, with <code>{0}</code> marking where
+        the actual username should be inserted.  Use this property
+        (along with the <code>userBase</code> and
+        <code>userSubtree</code> properties) instead of
+        <code>userPattern</code> to search the directory for the
+        user's entry.</p>
+      </attribute>
+
+      <attribute name="userSubtree" required="false">
+        <p>Set to <code>true</code> if you want to search the entire
+        subtree of the element specified by the <code>userBase</code>
+        property for the user's entry. The default value of
+        <code>false</code> causes only the top level to be searched.
+        Not used if you are using the <code>userPattern</code>
+        expression.</p>
+      </attribute>
+
+</attributes>
+
+<h3>Example</h3>
+
+<p>Creation of the appropriate schema in your directory server is beyond the
+scope of this document, because it is unique to each directory server
+implementation.  In the examples below, we will assume that you are using a
+distribution of the OpenLDAP directory server (version 2.0.11 or later), which
+can be downloaded from
+<a href="http://www.openldap.org">http://www.openldap.org</a>.  Assume that
+your <code>slapd.conf</code> file contains the following settings
+(among others):</p>
+<source>
+database ldbm
+suffix dc="mycompany",dc="com"
+rootdn "cn=Manager,dc=mycompany,dc=com"
+rootpw secret
+</source>
+
+<p>We will assume for <code>connectionURL</code> that the directory
+server runs on the same machine as Tomcat.  See <a
+href="http://java.sun.com/products/jndi/docs.html">http://java.sun.com/products/jndi/docs.html</a>
+for more information about configuring and using the JNDI LDAP
+provider.</p>
+
+<p>Next, assume that this directory server has been populated with elements
+as shown below (in LDIF format):</p>
+
+<source>
+
+# Define top-level entry
+dn: dc=mycompany,dc=com
+objectClass: dcObject
+dc:mycompany
+
+# Define an entry to contain people
+# searches for users are based on this entry
+dn: ou=people,dc=mycompany,dc=com
+objectClass: organizationalUnit
+ou: people
+
+# Define a user entry for Janet Jones
+dn: uid=jjones,ou=people,dc=mycompany,dc=com
+objectClass: inetOrgPerson
+uid: jjones
+sn: jones
+cn: janet jones
+mail: j.jones@mycompany.com
+userPassword: janet
+
+# Define a user entry for Fred Bloggs
+dn: uid=fbloggs,ou=people,dc=mycompany,dc=com
+objectClass: inetOrgPerson
+uid: fbloggs
+sn: bloggs
+cn: fred bloggs
+mail: f.bloggs@mycompany.com
+userPassword: fred
+
+# Define an entry to contain LDAP groups
+# searches for roles are based on this entry
+dn: ou=groups,dc=mycompany,dc=com
+objectClass: organizationalUnit
+ou: groups
+
+# Define an entry for the "tomcat" role
+dn: cn=tomcat,ou=groups,dc=mycompany,dc=com
+objectClass: groupOfUniqueNames
+cn: tomcat
+uniqueMember: uid=jjones,ou=people,dc=mycompany,dc=com
+uniqueMember: uid=fbloggs,ou=people,dc=mycompany,dc=com
+
+# Define an entry for the "role1" role
+dn: cn=role1,ou=groups,dc=mycompany,dc=com
+objectClass: groupOfUniqueNames
+cn: role1
+uniqueMember: uid=fbloggs,ou=people,dc=mycompany,dc=com
+</source>
+
+<p>An example <code>Realm</code> element for the OpenLDAP directory
+server configured as described above might look like this, assuming
+that users use their uid (e.g. jjones) to login to the
+application and that an anonymous connection is sufficient to search
+the directory and retrieve role information:</p>
+
+<source>
+&lt;Realm   className="org.apache.catalina.realm.JNDIRealm" debug="99"
+     connectionURL="ldap://localhost:389"
+       userPattern="uid={0},ou=people,dc=mycompany,dc=com"
+          roleBase="ou=groups,dc=mycompany,dc=com"
+          roleName="cn"
+        roleSearch="(uniqueMember={0})"
+/&gt;
+</source>
+
+<p>With this configuration, the realm will determine the user's
+distinguished name by substituting the username into the
+<code>userPattern</code>, authenticate by binding to the directory
+with this DN and the password received from the user, and search the
+directory to find the user's roles.</p>
+
+<p>Now suppose that users are expected to enter their email address
+rather than their userid when logging in. In this case the realm must
+search the directory for the user's entry. (A search is also necessary
+when user entries are held in multiple subtrees corresponding perhaps
+to different organizational units or company locations).</p>
+
+<p>Further, suppose that in addition to the group entries you want to
+use an attribute of the user's entry to hold roles. Now the entry for
+Janet Jones might read as follows:</p>
+
+<source>
+dn: uid=jjones,ou=people,dc=mycompany,dc=com
+objectClass: inetOrgPerson
+uid: jjones
+sn: jones
+cn: janet jones
+mail: j.jones@mycompany.com
+memberOf: role2
+memberOf: role3
+userPassword: janet
+</source>
+
+<p> This realm configuration would satisfy the new requirements:</p>
+
+<source>
+&lt;Realm   className="org.apache.catalina.realm.JNDIRealm" debug="99"
+     connectionURL="ldap://localhost:389"
+          userBase="ou=people,dc=mycompany,dc=com"
+        userSearch="(mail={0})"
+      userRoleName="memberOf"
+          roleBase="ou=groups,dc=mycompany,dc=com"
+          roleName="cn"
+        roleSearch="(uniqueMember={0})"
+/&gt;
+</source>
+
+<p>Now when Janet Jones logs in as "j.jones@mycompany.com", the realm
+searches the directory for a unique entry with that value as its mail
+attribute and attempts to bind to the directory as
+<code>uid=jjones,ou=people,dc=mycompany,dc=com</code> with the given
+password. If authentication succeeds, she is assigned three roles:
+"role2" and "role3", the values of the "memberOf" attribute in her
+directory entry, and "tomcat", the value of the "cn" attribute in the
+only group entry of which she is a member.</p>
+
+<p>Finally, to authenticate the user by retrieving
+the password from the directory and making a local comparison in the
+realm, you might use a realm configuration like this:</p>
+
+<source>
+&lt;Realm   className="org.apache.catalina.realm.JNDIRealm" debug="99"
+    connectionName="cn=Manager,dc=mycompany,dc=com"
+connectionPassword="secret"
+     connectionURL="ldap://localhost:389"
+      userPassword="userPassword"
+       userPattern="uid={0},ou=people,dc=mycompany,dc=com"
+          roleBase="ou=groups,dc=mycompany,dc=com"
+          roleName="cn"
+        roleSearch="(uniqueMember={0})"
+/&gt;
+</source>
+
+<p>However, as discussed above, the default bind mode for
+authentication is usually to be preferred.</p>
+
+<h3>Additional Notes</h3>
+
+<p>JNDIRealm operates according to the following rules:</p>
+<ul>
+<li>When a user attempts to access a protected resource for the first time,
+    Tomcat 5 will call the <code>authenticate()</code> method of this
+    <code>Realm</code>.  Thus, any changes you have made to the directory
+    (new users, changed passwords or roles, etc.) will be immediately
+    reflected.</li>
+<li>Once a user has been authenticated, the user (and his or her associated
+    roles) are cached within Tomcat for the duration of the user's login.
+    (For FORM-based authentication, that means until the session times out or
+    is invalidated; for BASIC authentication, that means until the user
+    closes their browser).  The cached user is <strong>not</strong> saved and
+    restored across sessions serialisations. Any changes to the directory
+    information for an already authenticated user will <strong>not</strong> be
+    reflected until the next time that user logs on again.</li>
+<li>Administering the information in the directory server
+    is the responsibility of your own applications.  Tomcat does not
+    provide any built-in capabilities to maintain users and roles.</li>
+</ul>
+
+</subsection>
+
+
+<subsection name="MemoryRealm">
+
+<h3>Introduction</h3>
+
+<p><strong>MemoryRealm</strong> is a simple demonstration implementation of the
+Tomcat 5 <code>Realm</code> interface.  It is not designed for production use.
+At startup time, MemoryRealm loads information about all users, and their
+corresponding roles, from an XML document (by default, this document is loaded from <code>$CATALINA_HOME/conf/tomcat-users.xml</code>).  Changes to the data
+in this file are not recognized until Tomcat is restarted.</p>
+
+<h3>Realm Element Attributes</h3>
+
+<p>To configure MemoryRealm, you will create a <code>&lt;Realm&gt;</code>
+element and nest it in your <code>$CATALINA_HOME/conf/server.xml</code> file,
+as described <a href="#Configuring a Realm">above</a>.  The following
+attributes are supported by this implementation:</p>
+
+<attributes>
+
+  <attribute name="className" required="true">
+    <p>The fully qualified Java class name of this Realm implementation.
+    You <strong>MUST</strong> specify the value
+    "<code>org.apache.catalina.realm.MemoryRealm</code>" here.</p>
+  </attribute>
+
+  <attribute name="digest" required="false">
+    <p>The digest algorithm used to store passwords in non-plaintext formats.
+    Valid values are those accepted for the algorithm name by the
+    <code>java.security.MessageDigest</code> class.  See
+    <a href="#Digested Passwords">Digested Passwords</a> for more
+    information.  If not specified, passwords are stored in clear text.</p>
+  </attribute>
+
+  <attribute name="pathname" required="false">
+    <p>Absolute or relative (to $CATALINA_HOME) pathname of the XML document
+    containing our valid usernames, passwords, and roles.  See below for more
+    information on the format of this file.  If not specified, the value
+    <code>conf/tomcat-users.xml</code> is used.</p>
+  </attribute>
+
+</attributes>
+
+<h3>User File Format</h3>
+
+<p>The users file (by default, <code>conf/tomcat-users.xml</code> must be an
+XML document, with a root element <code>&lt;tomcat-users&gt;</code>.  Nested
+inside the root element will be a <code>&lt;user&gt;</code> element for each
+valid user, consisting of the following attributes:</p>
+<ul>
+<li><strong>name</strong> - Username this user must log on with.</li>
+<li><strong>password</strong> - Password this user must log on with (in
+    clear text if the <code>digest</code> attribute was not set on the
+    <code>&lt;Realm&gt;</code> element, or digested appropriately as
+    described <a href="#Digested Passwords">here</a> otherwise).</li>
+<li><strong>roles</strong> - Comma-delimited list of the role names
+    associated with this user.</li>
+</ul>
+
+<h3>Example</h3>
+
+<p>The default installation of Tomcat 5 is configured with a MemoryRealm
+nested inside the <code>&lt;Engine&gt;</code> element, so that it applies
+to all virtual hosts and web applications.  The default contents of the
+<code>conf/tomcat-users.xml</code> file is:</p>
+<source>
+&lt;tomcat-users&gt;
+  &lt;user name="tomcat" password="tomcat" roles="tomcat" /&gt;
+  &lt;user name="role1"  password="tomcat" roles="role1"  /&gt;
+  &lt;user name="both"   password="tomcat" roles="tomcat,role1" /&gt;
+&lt;/tomcat-users&gt;
+</source>
+
+<h3>Additional Notes</h3>
+
+<p>MemoryRealm operates according to the following rules:</p>
+<ul>
+<li>When Tomcat first starts up, it loads all defined users and their
+    associated information from the users file.  Changes to the data in
+    this file will <strong>not</strong> be recognized until Tomcat is
+    restarted.</li>
+<li>When a user attempts to access a protected resource for the first time,
+    Tomcat 5 will call the <code>authenticate()</code> method of this
+    <code>Realm</code>.</li>
+<li>Once a user has been authenticated, the user (and his or her associated
+    roles) are cached within Tomcat for the duration of the user's login.
+    (For FORM-based authentication, that means until the session times out or
+    is invalidated; for BASIC authentication, that means until the user
+    closes their browser).  The cached user is <strong>not</strong> saved and
+    restored across sessions serialisations.</li>
+<li>Administering the information in the users file is the responsibility
+    of your application.  Tomcat does not
+    provide any built-in capabilities to maintain users and roles.</li>
+</ul>
+
+
+</subsection>
+
+
+<subsection name="JAASRealm">
+
+<h3>Introduction</h3>
+
+        <p><strong>JAASRealm</strong> is an implementation of the Tomcat
+4 <code>Realm</code> interface that authenticates users through the Java
+Authentication &amp; Authorization Service (JAAS) framework, a Java
+package that is available as an optional package in Java 2 SDK 1.3 and
+is fully integrated as of SDK 1.4 .</p>
+        <p>Using JAASRealm gives the developer the ability to combine
+practically any conceivable security realm with Tomcat's CMA. </p>
+        <p>JAASRealm is prototype for Tomcat of the proposed JAAS-based
+J2EE authentication framework for J2EE v1.4, based on the <a
+ href="http://www.jcp.org/en/jsr/detail?id=196">JCP Specification
+Request 196</a> to enhance container-managed security and promote
+'pluggable' authentication mechanisms whose implementations would be
+container-independent.
+        </p>
+        <p>Based on the JAAS login module and principal (see <code>javax.security.auth.spi.LoginModule</code>
+and <code>javax.security.Principal</code>), you can develop your own
+security mechanism or wrap another third-party mechanism for
+integration with the CMA as implemented by Tomcat.
+        </p>
+
+        <h3>Quick Start</h3>
+        <p>To set up Tomcat to use JAASRealm with your own JAAS login module,
+ you will need to follow these steps:</p>
+        <ol>
+          <li>Write your own LoginModule, User and Role classes based
+on JAAS (see 
+<a href="http://java.sun.com/j2se/1.4.1/docs/guide/security/jaas/tutorials/GeneralAcnOnly.html">the
+JAAS Authentication Tutorial</a> and 
+<a href="http://java.sun.com/j2se/1.4.1/docs/guide/security/jaas/JAASLMDevGuide.html">the JAAS Login Module 
+Developer's Guide</a>) to be managed by the JAAS Login
+Context (<code>javax.security.auth.login.LoginContext</code>)
+When developing your LoginModule, note that JAASRealm's built-in <code>CallbackHandler</code>
++only recognizes the <code>NameCallback</code> and <code>PasswordCallback</code> at present.
+          </li>
+          <li>Although not specified in JAAS, you should create
+seperate classes to distinguish between users and roles, extending <code>javax.security.Principal</code>,
+so that Tomcat can tell which Principals returned from your login
+module are users and which are roles (see <code>org.apache.catalina.realm.JAASRealm</code>).
+Regardless, the first Principal returned is <em>always</em> treated as the user Principal.
+          </li>
+          <li>Place the compiled classes on Tomcat's classpath
+          </li>
+          <li>Set up a login.config file for Java (see <a
+ href="http://java.sun.com/j2se/1.4.1/docs/guide/security/jaas/tutorials/LoginConfigFile.html">JAAS
+LoginConfig file</a>) and tell Tomcat where to find it by specifying
+its location to the JVM, for instance by setting the environment
+variable: <code>JAVA_OPTS=-DJAVA_OPTS=-Djava.security.auth.login.config==$CATALINA_HOME/conf/jaas.config</code></li>
+
+          <li>Configure your security-constraints in your web.xml for
+the resources you want to protect</li>
+          <li>Configure the JAASRealm module in your server.xml </li>
+          <li>Restart Tomcat 5 if it is already running.</li>
+        </ol>
+        <h3>Realm Element Attributes</h3>
+        <p>To configure JAASRealm as for step 6 above, you create
+a <code>&lt;Realm&gt;</code> element and nest it in your 
+<code>$CATALINA_HOME/conf/server.xml</code>
+file within your <code>&lt;Engine&gt;</code> node. The following attributes 
+are supported by this implementation:</p>
+
+<attributes>
+
+  <attribute name="className" required="true">
+    <p>The fully qualified Java class name of this Realm implementation.
+    You <strong>MUST</strong> specify the value
+    "<code>org.apache.catalina.realm.JAASRealm</code>" here.</p>
+  </attribute>
+
+  <attribute name="appName" required="true">
+    <p>The name of the application as configured in your login configuration file 
+    (<a href="http://java.sun.com/j2se/1.4.1/docs/guide/security/jaas/tutorials/LoginConfigFile.html">JAAS LoginConfig</a>).</p>
+  </attribute>
+
+  <attribute name="userClassNames" required="true">
+    <p>A comma-seperated list of the names of the classes that you have made 
+    for your user <code>Principals</code>.</p>
+  </attribute>
+
+  <attribute name="roleClassNames" required="false">
+    <p>A comma-seperated list of the names of the classes that you have made 
+    for your role <code>Principals</code>.</p>
+  </attribute>
+
+  <attribute name="useContextClassLoader" required="false">
+    <p>Instructs JAASRealm to use the context class loader for loading the user-specified
+    <code>LoginModule</code> class and associated <code>Principal</code> classes. The
+    default value is <code>true</code>, which is backwards-compatible with the way 
+    Tomcat 4 works. To load classes using the container's classloader, specify
+    <code>false</code>.</p>
+  </attribute>
+
+</attributes>
+
+<h3>Example</h3>
+
+<p>Here is an example of how your server.xml snippet should look.</p>
+
+<source>
+&lt;Realm className="org.apache.catalina.realm.JAASRealm"                 
+                appName="MyFooRealm"       
+    userClassNames="org.foobar.realm.FooUser"       
+     roleClassNames="org.foobar.realm.FooRole" 
+                      debug="99"/&gt;
+</source>
+
+<p>It is the responsibility of your login module to create and save User and 
+Role objects representing Principals for the user 
+(<code>javax.security.auth.Subject</code>). If your login module doesn't 
+create a user object but also doesn't throw a login exception, then the 
+Tomcat CMA will break and you will be left at the 
+http://localhost:8080/myapp/j_security_check URI or at some other 
+unspecified location.</p>
+
+        <p>The flexibility of the JAAS approach is two-fold: </p>
+        <ul>
+          <li>you can carry out whatever processing you require behind
+the scenes in your own login module.</li>
+          <li>you can plug in a completely different LoginModule by changing the configuration 
+and restarting the server, without any code changes to your application.</li>
+        </ul>
+
+        <h3>Additional Notes</h3>
+        <ul>
+          <li>When a user attempts to access a protected resource for
+              the first time, Tomcat 5 will call the <code>authenticate()</code>
+              method of this <code>Realm</code>.  Thus, any changes you have made in
+              the security mechanism directly (new users, changed passwords or
+              roles, etc.) will be immediately reflected.</li>
+          <li>Once a user has been authenticated, the user (and his or
+              her associated roles) are cached within Tomcat for the duration of
+              the user's login.  For FORM-based authentication, that means until
+              the session times out or is invalidated; for BASIC authentication,
+              that means until the user closes their browser.  Any changes to the
+              security information for an already authenticated user will <strong>not</strong>
+              be reflected until the next time that user logs on again.</li>
+          <li>As with other <code>Realm</code> implementations, digested passwords
+              are supported if the <code>&lt;Realm&gt;</code> element in <code>server.xml</code>
+              contains a <code>digest</code> attribute; JAASRealm's <code>CallbackHandler</code>
+              will digest the password prior to passing it back to the <code>LoginModule</code></li>  
+        </ul>
+
+</subsection>
+
+
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/security-manager-howto.xml b/container/webapps/docs/security-manager-howto.xml
new file mode 100644
index 0000000..87efb0c
--- /dev/null
+++ b/container/webapps/docs/security-manager-howto.xml
@@ -0,0 +1,383 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="security-manager-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="glenn@voyager.apg.more.net">Glenn Nielsen</author>
+        <author email="jeanfrancois.arcand@sun.com">Jean-Francois Arcand</author>
+        <title>Security Manager HOW-TO</title>
+    </properties>
+
+<body>
+
+
+<section name="Background">
+
+  <p>The Java <strong>SecurityManager</strong> is what allows a web browser
+  to run an applet in its own sandbox to prevent untrusted code from
+  accessing files on the local file system, connecting to a host other
+  than the one the applet was loaded from, and so on.  In the same way
+  the SecurityManager protects you from an untrusted applet running in
+  your browser, use of a SecurityManager while running Tomcat can protect
+  your server from trojan servlets, JSPs, JSP beans, and tag libraries.
+  Or even inadvertent mistakes.</p>
+
+  <p>Imagine if someone who is authorized to publish JSPs on your site
+  inadvertently included the following in their JSP:</p>
+<source>
+&lt;% System.exit(1); %&gt;
+</source>
+
+  <p>Every time this JSP was executed by Tomcat, Tomcat would exit.
+  Using the Java SecurityManager is just one more line of defense a
+  system administrator can use to keep the server secure and reliable.</p>
+
+  <p><strong>WARNING</strong> - A security audit
+  have been conducted using the Tomcat 5 codebase. Most of the critical
+  package have been protected and a new security package protection mechanism 
+  has been implemented. Still, make sure that you are satisfied with your SecurityManager 
+  configuration before allowing untrusted users to publish web applications, 
+  JSPs, servlets, beans, or tag libraries.  <strong>However, running with a 
+  SecurityManager is definitely better than running without one.</strong></p>
+
+</section>
+
+
+<section name="Permissions">
+
+  <p>Permission classes are used to define what Permissions a class loaded
+  by Tomcat will have.  There are a number of Permission classes that are
+  a standard part of the JDK, and you can create your own Permission class
+  for use in your own web applications.  Both techniques are used in
+  Tomcat 5.</p>
+
+
+  <subsection name="Standard Permissions">
+
+    <p>This is just a short summary of the standard system SecurityManager
+    Permission classes applicable to Tomcat.  See
+    <a href="http://java.sun.com/security/">http://java.sun.com/security/</a>
+    for more information.</p>
+
+    <ul>
+    <li><strong>java.util.PropertyPermission</strong> - Controls read/write
+        access to JVM properties such as <code>java.home</code>.</li>
+    <li><strong>java.lang.RuntimePermission</strong> - Controls use of
+        some System/Runtime functions like <code>exit()</code> and
+        <code>exec()</code>. Also control the package access/definition.</li>
+    <li><strong>java.io.FilePermission</strong> - Controls read/write/execute
+        access to files and directories.</li>
+    <li><strong>java.net.SocketPermission</strong> - Controls use of
+        network sockets.</li>
+    <li><strong>java.net.NetPermission</strong> - Controls use of
+        multicast network connections.</li>
+    <li><strong>java.lang.reflect.ReflectPermission</strong> - Controls
+        use of reflection to do class introspection.</li>
+    <li><strong>java.security.SecurityPermission</strong> - Controls access
+        to Security methods.</li>
+    <li><strong>java.security.AllPermission</strong> - Allows access to all
+        permissions, just as if you were running Tomcat without a
+        SecurityManager.</li>
+    </ul>
+
+  </subsection>
+
+
+  <subsection name="Tomcat Custom Permissions">
+
+    <p>Tomcat utilizes a custom permission class called
+    <strong>org.apache.naming.JndiPermission</strong>.  This permission
+    controls read access to JNDI named file based resources.  The permission
+    name is the JNDI name and there are no actions.  A trailing "*" can be
+    used to do wild card matching for a JNDI named file resource when
+    granting permission.  For example, you might include the following
+    in your policy file:</p>
+<source>
+permission  org.apache.naming.JndiPermission  "jndi://localhost/examples/*";
+</source>
+
+    <p>A Permission entry like this is generated dynamically for each web
+    application that is deployed, to allow it to read its own static resources
+    but disallow it from using file access to read any other files (unless
+    permissions for those files are explicitly granted).</p>
+
+    <p>Also, Tomcat always dynamically creates the following file permission:</p>
+<source>  
+permission java.io.FilePermission "** your application context**", "read";
+</source>  
+    <p>Where **your application context** equals the folder(or WAR file) under which 
+    your application has been deployed. </p>  
+
+  </subsection>
+
+
+</section>
+
+
+<section name="Configuring Tomcat With A SecurityManager">
+
+  <h3>Policy File Format</h3>
+
+  <p>The security policies implemented by the Java SecurityManager are
+  configured in the <code>$CATALINA_HOME/conf/catalina.policy</code> file.
+  This file completely replaces the <code>java.policy</code> file present
+  in your JDK system directories.  The <code>catalina.policy</code> file
+  can be edited by hand, or you can use the
+  <a href="http://java.sun.com/products/jdk/1.2/docs/tooldocs/solaris/policytool.html">policytool</a>
+  application that comes with Java 1.2 or later.</p>
+
+  <p>Entries in the <code>catalina.policy</code> file use the standard
+  <code>java.policy</code> file format, as follows:</p>
+<source>
+// Example policy file entry
+
+grant [signedBy &lt;signer&gt;,] [codeBase &lt;code source&gt;] {
+  permission  &lt;class&gt;  [&lt;name&gt; [, &lt;action list&gt;]];
+};
+</source>
+
+  <p>The <strong>signedBy</strong> and <strong>codeBase</strong> entries are
+  optional when granting permissions.  Comment lines begin with "//" and
+  end at the end of the current line.  The <code>codeBase</code> is in the
+  form of a URL, and for a file URL can use the <code>${java.home}</code>
+  and <code>${catalina.home}</code> properties (which are expanded out to
+  the directory paths defined for them by the <code>JAVA_HOME</code> and
+  <code>CATALINA_HOME</code> environment variables).</p>
+
+  <h3>The Default Policy File</h3>
+
+  <p>The default <code>$CATALINA_HOME/conf/catalina.policy</code> file
+  looks like this:</p>
+<source>
+// ============================================================================
+// catalina.corepolicy - Security Policy Permissions for Tomcat 5
+//
+// This file contains a default set of security policies to be enforced (by the
+// JVM) when Catalina is executed with the "-security" option.  In addition
+// to the permissions granted here, the following additional permissions are
+// granted to the codebase specific to each web application:
+//
+// * Read access to the document root directory
+//
+// $Id$
+// ============================================================================
+
+
+// ========== SYSTEM CODE PERMISSIONS =========================================
+
+
+// These permissions apply to javac
+grant codeBase "file:${java.home}/lib/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to all shared system extensions
+grant codeBase "file:${java.home}/jre/lib/ext/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to javac when ${java.home] points at $JAVA_HOME/jre
+grant codeBase "file:${java.home}/../lib/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to all shared system extensions when
+// ${java.home} points at $JAVA_HOME/jre
+grant codeBase "file:${java.home}/lib/ext/-" {
+        permission java.security.AllPermission;
+};
+
+
+// ========== CATALINA CODE PERMISSIONS =======================================
+
+
+// These permissions apply to the launcher code
+grant codeBase "file:${catalina.home}/bin/commons-launcher.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the server startup code
+grant codeBase "file:${catalina.home}/bin/bootstrap.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the servlet API classes
+// and those that are shared across all class loaders
+// located in the "common" directory
+grant codeBase "file:${catalina.home}/common/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the container's core code, plus any additional
+// libraries installed in the "server" directory
+grant codeBase "file:${catalina.home}/server/-" {
+        permission java.security.AllPermission;
+};
+
+// ========== WEB APPLICATION PERMISSIONS =====================================
+
+
+// These permissions are granted by default to all web applications
+// In addition, a web application will be given a read FilePermission
+// and JndiPermission for all files and directories in its document root.
+grant { 
+        // Required for JNDI lookup of named JDBC DataSource's and
+        // javamail named MimePart DataSource used to send mail
+        permission java.util.PropertyPermission "java.home", "read";
+        permission java.util.PropertyPermission "java.naming.*", "read";
+        permission java.util.PropertyPermission "javax.sql.*", "read";
+
+        // OS Specific properties to allow read access
+	permission java.util.PropertyPermission "os.name", "read";
+	permission java.util.PropertyPermission "os.version", "read";
+	permission java.util.PropertyPermission "os.arch", "read";
+	permission java.util.PropertyPermission "file.separator", "read";
+	permission java.util.PropertyPermission "path.separator", "read";
+	permission java.util.PropertyPermission "line.separator", "read";
+
+        // JVM properties to allow read access
+        permission java.util.PropertyPermission "java.version", "read";
+        permission java.util.PropertyPermission "java.vendor", "read";
+        permission java.util.PropertyPermission "java.vendor.url", "read";
+        permission java.util.PropertyPermission "java.class.version", "read";
+	permission java.util.PropertyPermission "java.specification.version", "read";
+	permission java.util.PropertyPermission "java.specification.vendor", "read";
+	permission java.util.PropertyPermission "java.specification.name", "read";
+
+	permission java.util.PropertyPermission "java.vm.specification.version", "read";
+	permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
+	permission java.util.PropertyPermission "java.vm.specification.name", "read";
+	permission java.util.PropertyPermission "java.vm.version", "read";
+	permission java.util.PropertyPermission "java.vm.vendor", "read";
+	permission java.util.PropertyPermission "java.vm.name", "read";
+
+        // Required for getting BeanInfo
+        permission java.lang.RuntimePermission "accessClassInPackage.sun.beans.*";
+
+        // Required for OpenJMX
+        permission java.lang.RuntimePermission "getAttribute";
+
+	// Allow read of JAXP compliant XML parser debug
+	permission java.util.PropertyPermission "jaxp.debug", "read";
+};
+
+
+// You can assign additional permissions to particular web applications by
+// adding additional "grant" entries here, based on the code base for that
+// application, /WEB-INF/classes/, or /WEB-INF/lib/ jar files.
+//
+// Different permissions can be granted to JSP pages, classes loaded from
+// the /WEB-INF/classes/ directory, all jar files in the /WEB-INF/lib/
+// directory, or even to individual jar files in the /WEB-INF/lib/ directory.
+//
+// For instance, assume that the standard "examples" application
+// included a JDBC driver that needed to establish a network connection to the
+// corresponding database and used the scrape taglib to get the weather from
+// the NOAA web server.  You might create a "grant" entries like this:
+//
+// The permissions granted to the context root directory apply to JSP pages.
+// grant codeBase "file:${catalina.home}/webapps/examples/-" {
+//      permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect";
+//      permission java.net.SocketPermission "*.noaa.gov:80", "connect";
+// };
+//
+// The permissions granted to the context WEB-INF/classes directory
+// grant codeBase "file:${catalina.home}/webapps/examples/WEB-INF/classes/-" {
+// };
+//
+// The permission granted to your JDBC driver
+// grant codeBase "jar:file:${catalina.home}/webapps/examples/WEB-INF/lib/driver.jar!/-" {
+//      permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect";
+// };
+// The permission granted to the scrape taglib
+// grant codeBase "jar:file:${catalina.home}/webapps/examples/WEB-INF/lib/scrape.jar!/-" {
+//      permission java.net.SocketPermission "*.noaa.gov:80", "connect";
+// };
+</source>
+
+  <h3>Starting Tomcat With A SecurityManager</h3>
+
+  <p>Once you have configured the <code>catalina.policy</code> file for use
+  with a SecurityManager, Tomcat can be started with a SecurityManager in
+  place by using the "-security" option:</p>
+<source>
+$CATALINA_HOME/bin/catalina.sh start -security    (Unix)
+%CATALINA_HOME%\bin\catalina start -security      (Windows)
+</source>
+
+</section>
+<section name="Configuring Package Protection in Tomcat">
+  <p>Starting with Tomcat 5, it is now possible to configure which Tomcat
+  internal package are protected againts package definition and access. See
+  <a href="http://java.sun.com/security/seccodeguide.html">
+    http://java.sun.com/security/seccodeguide.html</a>
+    for more information.</p>    
+
+  
+  <p><strong>WARNING</strong>: Be aware that removing the default package protection 
+  could possibly open a security hole</p>
+
+  <h3>The Default Properties File</h3>
+
+  <p>The default <code>$CATALINA_HOME/conf/catalina.properties</code> file
+  looks like this:</p>
+<source>  
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageAccess unless the
+# corresponding RuntimePermission ("accessClassInPackage."+package) has
+# been granted.
+package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,
+org.apache.jasper.
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageDefinition unless the
+# corresponding RuntimePermission ("defineClassInPackage."+package) has
+# been granted.
+#
+# by default, no packages are restricted for definition, and none of
+# the class loaders supplied with the JDK call checkPackageDefinition.
+#
+package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,
+org.apache.tomcat.,org.apache.jasper.
+</source>
+  <p>Once you have configured the <code>catalina.properties</code> file for use
+  with a SecurityManager, remember to re-start Tomcat.</p>
+</section>
+
+<section name="Troubleshooting">
+
+  <p>If your web application attempts to execute an operation that is
+  prohibited by lack of a required Permission, it will throw an
+  <code>AccessControLException</code> or a <code>SecurityException</code>
+  when the SecurityManager detects the violation.  Debugging the permission
+  that is missing can be challenging, and one option is to turn on debug
+  output of all security decisions that are made during execution.  This
+  is done by setting a system property before starting Tomcat.  The easiest
+  way to do this is via the <code>CATALINA_OPTS</code> environment variable.
+  Execute this command:</p>
+<source>
+export CATALINA_OPTS=-Djava.security.debug=all    (Unix)
+set CATALINA_OPTS=-Djava.security.debug=all       (Windows)
+</source>
+
+  <p>before starting Tomcat.</p>
+
+  <p><strong>WARNING</strong> - This will generate <em>many megabytes</em>
+  of output!  However, it can help you track down problems by searching
+  for the word "FAILED" and determining which permission was being checked
+  for.  See the Java security documentation for more options that you can
+  specify here as well.</p>
+
+</section>
+
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/setup.xml b/container/webapps/docs/setup.xml
new file mode 100644
index 0000000..a833b7e
--- /dev/null
+++ b/container/webapps/docs/setup.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="setup.html">
+
+  &project;
+
+  <properties>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <title>Tomcat Setup</title>
+  </properties>
+
+<body>
+
+  <section name="Introduction">
+    <p>
+      This document introduces several ways to set up Tomcat for running
+      on different platforms.  Please note that some advanced setup issues
+      are not covered here: the full distribution (ZIP file or tarball) 
+      includes a file called
+      RUNNING.txt which discusses these issues.  We encourage you to refer
+      to it if the information below does not answer some of your questions.
+    </p>
+  </section>
+
+  <section name="Windows">
+
+    <p>
+      Installing Tomcat on Windows can be done easily using the Windows 
+      installer. Its interface and functionality is similar to other wizard
+      based installers, with only a few items of interest.
+    </p>
+
+    <p>
+      <ul>
+        <li><strong>Installation as a service</strong>: Tomcat will be 
+            installed as a Windows
+            NT/2k/XP service no matter what setting is selected. Using the
+            checkbox on the component page sets the service as "auto"
+            startup, so that Tomcat is automatically started when Windows
+            starts. For optimal security, the service should be run as a
+            separate user, with reduced permissions (see the Windows Services
+            administration tool and its documentation).</li>
+        <li><strong>Java location</strong>: The installer will use the registry
+            or the JAVA_HOME environment variable to determine the base path
+            of a J2SE 5 JRE.
+            </li>
+        <li><strong>Tray icon</strong>: When Tomcat is run as a service, there
+            will not be any tray icon present when Tomcat is running. Note that
+            when choosing to run Tomcat at the end of installation, the tray
+            icon will be used even if Tomcat was installed as a service.</li>
+        <li>Refer to the
+            <a href="windows-service-howto.html">Windows Service HOW-TO</a>
+            for information on how to manage Tomcat as Windows NT service.
+            </li>            
+      </ul>
+    </p>
+
+    <p>The installer will create shortcuts allowing starting and configuring 
+       Tomcat. It is important to note that the Tomcat administration web 
+       application can only be used when Tomcat is running.</p>
+
+    <p>If using a J2SE 1.4 JRE, the compatibility package must be downloaded and
+       expanded inside the folder where Tomcat was installed.</p>
+
+  </section>
+
+  <section name="Unix daemon">
+
+    <p>Tomcat can be run as a daemon using the jsvc tool from the 
+       commons-daemon project. Source tarballs for jsvc are included with the
+       Tomcat binaries, and need to be compiled. Building jsvc requires
+       a C ANSI compiler (such as GCC), GNU Autoconf, and a JDK.</p>
+
+    <p>Before running the script, the <code>JAVA_HOME</code> environment
+       variable should be set to the base path of the JDK. Alternately, when
+       calling the <code>./configure</code> script, the path of the JDK may
+       be specified using the <code>--with-java</code> parameter, such as
+       <code>./configure --with-java=/usr/java</code>.</p>
+
+    <p>Using the following commands should result in a compiled jsvc binary,
+       located in the <code>$CATALINA_HOME/bin</code> folder. This assumes
+       that GNU TAR is used, and that <code>CATALINA_HOME</code> is an 
+       environment variable pointing to the base path of the Tomcat 
+       installation.</p>
+  
+    <p>Please note that you should use the GNU make (gmake) instead of
+       the native BSD make on FreeBSD systems.</p>
+
+
+<source>
+    cd $CATALINA_HOME/bin
+    tar xvfz jsvc.tar.gz
+    cd jsvc-src
+    autoconf
+    ./configure
+    make
+    cp jsvc ..
+    cd ..
+</source>
+
+    <p>Tomcat can then be run as a daemon using the following commands.</p>
+
+<source>
+    cd $CATALINA_HOME
+    ./bin/jsvc -Djava.endorsed.dirs=./common/endorsed -cp ./bin/bootstrap.jar \
+        -outfile ./logs/catalina.out -errfile ./logs/catalina.err \
+        org.apache.catalina.startup.Bootstrap
+</source>
+
+    <p>jsvc has other useful parameters, such as <code>-user</code> which 
+       causes it to switch to another user after the daemon initialization is
+       complete. This allows, for example, running Tomcat as a non privileged
+       user while still being able to use privileged ports. 
+       <code>jsvc --help</code> will return the full jsvc usage 
+       information. In particular, the <code>-debug</code> option is useful
+       to debug issues running jsvc.</p>
+
+    <p>The file <code>$CATALINA_HOME/bin/jsvc/native/tomcat.sh</code> can be 
+       used as a template for starting Tomcat automatically at boot time from 
+       <code>/etc/init.d</code>.  The file is currently setup for running 
+       Tomcat 4.1.x, so it is necessary to edit it and change the classname 
+       from <code>BootstrapService</code> to <code>Bootstrap</code>.</p>
+
+    <p>Note that the Commons-Daemon JAR file must be on your runtime classpath 
+       to run Tomcat in this manner.  The Commons-Daemon JAR file is in the Class-Path 
+       entry of the bootstrap.jar manifest, but if you get a ClassNotFoundException 
+       or a NoClassDefFoundError for a Commons-Daemon class, add the Commons-Daemon 
+       JAR to the -cp argument when launching jsvc.</p> 
+
+  </section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/ssi-howto.xml b/container/webapps/docs/ssi-howto.xml
new file mode 100644
index 0000000..d60d747
--- /dev/null
+++ b/container/webapps/docs/ssi-howto.xml
@@ -0,0 +1,373 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="ssi-howto.html">
+
+&project;
+
+<properties>
+<author email="glenn@apache.org">Glenn L. Nielsen</author>
+<title>SSI How To</title>
+</properties>
+
+<body>
+
+<section name="Introduction">
+
+<p>SSI (Server Side Includes) are directives that are placed in HTML pages,
+and evaluated on the server while the pages are being served. They let you
+add dynamically generated content to an existing HTML page, without having
+to serve the entire page via a CGI program, or other dynamic technology.
+</p>
+
+<p>Within Tomcat SSI support can be added when using Tomcat as your
+HTTP server and you require SSI support.  Typically this is done
+during development when you don't want to run a web server like Apache.</p>
+
+<p>Tomcat SSI support implements the same SSI directives as Apache.  See the
+<a href="http://httpd.apache.org/docs/howto/ssi.html#basicssidirectives">
+Apache Introduction to SSI</a> for information on using SSI directives.</p>
+
+<p>SSI support is available as a servlet and as a filter. You should use one
+or the other to provide SSI support but not both.</p>
+
+<p>Servlet based SSI support is implemented using the class
+<code>org.apache.catalina.ssi.SSIServlet</code>.  Traditionally, this servlet
+is mapped to the URL pattern "*.shtml".</p>
+
+<p>Filter based SSI support is implemented using the class
+<code>org.apache.catalina.ssi.SSIFilter</code>.  Traditionally, this filter
+is mapped to the URL pattern "*.shtml", though it can be mapped to "*" as
+it will selectively enable/disable SSI processing based on mime types.  The
+contentType init param allows you to apply SSI processing to JSP pages,
+javascript, or any other content you wish.</p>
+<p>By default SSI support is disabled in Tomcat.</p>
+</section>
+
+<section name="Installation">
+
+<p><strong>CAUTION</strong> - SSI directives can be used to execute programs
+external to the Tomcat JVM. If you are using the Java SecurityManager this
+will bypass your security policy configuration in <code>catalina.policy.</code>
+</p>
+
+<p>Rename <code>$CATALINA_BASE/server/lib/servlets-ssi.renametojar</code>
+to <code>$CATALINA_BASE/server/lib/servlets-ssi.jar</code>.</p>
+
+<p>To use the SSI servlet, remove the XML comments from around the SSI servlet
+and servlet-mapping configuration in
+<code>$CATALINA_BASE/conf/web.xml</code>.</p>
+
+<p>To use the SSI filter, remove the XML comments from around the SSI filter
+and filter-mapping configuration in
+<code>$CATALINA_BASE/conf/web.xml</code>.</p>
+
+</section>
+
+<section name="Servlet Configuration">
+
+<p>There are several servlet init parameters which can be used to
+configure the behaviour of the SSI servlet.
+<ul>
+<li><strong>buffered</strong> - Should output from this servlet be buffered?
+(0=false, 1=true) Default 0 (false).</li>
+<li><strong>debug</strong> - Debugging detail level for messages logged
+by this servlet. Default 0.</li>
+<li><strong>expires</strong> - The number of seconds before a page with SSI
+directives will expire. Default behaviour is for all SSI directives to be
+evaluated for every request.</li>
+<li><strong>isVirtualWebappRelative</strong> - Should "virtual" SSI directive
+paths be interpreted as relative to the context root, instead of the server
+root? (0=false, 1=true) Default 0 (false).</li>
+<li><strong>inputEncoding</strong> - The encoding to be assumed for SSI
+resources if one cannot be determined from the resource itself. Default is
+the default platform encoding.</li>
+<li><strong>outputEncoding</strong> - The encoding to be used for the result
+of the SSI processing. Default is UTF-8.</li>
+</ul>
+</p>
+
+</section>
+
+<section name="Filter Configuration">
+
+<p>There are several filter init parameters which can be used to
+configure the behaviour of the SSI filter.
+<ul>
+<li><strong>contentType</strong> - A regex pattern that must be matched before
+SSI processing is applied. When crafting your own pattern, don't forget that a
+mime content type may be followed by an optional character set in the form
+"mime/type; charset=set" that you must take into account.  Default is
+"text/x-server-parsed-html(;.*)?".</li>
+<li><strong>debug</strong> - Debugging detail level for messages logged
+by this servlet. Default 0.</li>
+<li><strong>expires</strong> - The number of seconds before a page with SSI
+directives will expire. Default behaviour is for all SSI directives to be
+evaluated for every request.</li>
+<li><strong>isVirtualWebappRelative</strong> - Should "virtual" SSI directive
+paths be interpreted as relative to the context root, instead of the server
+root? (0=false, 1=true) Default 0 (false).</li>
+</ul>
+</p>
+
+</section>
+
+<section name="Directives">
+<p>Server Side Includes are invoked by embedding SSI directives in an HTML document
+ whose type will be processed by the SSI servlet. The directives take the form of an HTML
+ comment. The directive is replaced by the results of interpreting it before sending the
+ page to the client. The general form of a directive is: </p>
+<p> <code>&lt;!--#directive [parm=value] --&gt;</code></p>
+<p>The directives are:
+<ul>
+<li>
+<strong>config</strong> - <code>&lt;!--#config timefmt=&quot;%B %Y&quot; --&gt;</code>
+Used to set the format of dates and other items processed by SSI
+</li>
+<li>
+<strong>echo</strong> -   <code>&lt;!--#echo var=&quot;VARIABLE_NAME&quot; --&gt;</code>
+will be replaced bt the value of the variable.
+</li>
+<li>
+<strong>exec</strong> -  Used to run commands on the host system.
+</li>
+<li>
+<strong>include</strong> -  <code>&lt;!--#include virtual=&quot;file-name&quot; --&gt;</code>
+inserts the contents
+</li>
+<li>
+<strong>flastmod</strong> - <code>&lt;!--#flastmod file=&quot;filename.shtml&quot; --&gt;</code>
+Returns the time that a file was lost modified.
+</li>
+<li>
+<strong>fsize</strong> - <code>&lt;!--#fsize file=&quot;filename.shtml&quot; --&gt;</code>
+Returns the size of a file.
+</li>
+<li>
+<strong>printenv</strong> - <code>&lt;!--#printenv --&gt;</code>
+Returns the list of all the defined variables.
+</li>
+<li>
+<strong>set</strong> - <code>&lt;!--#set var="foo" value="Bar" --&gt;</code>
+is used to assign a value to a user-defind variable.
+</li>
+<li>
+<strong>if elif endif else</strong> - Used to create conditional sections. For example:</li>
+<code>&lt;!--#config timefmt="%A" --&gt;<br />
+  &lt;!--#if expr="$DATE_LOCAL = /Monday/" --&gt;<br />
+  &lt;p&gt;Meeting at 10:00 on Mondays&lt;/p&gt;<br />
+  &lt;!--#elif expr="$DATE_LOCAL = /Friday/" --&gt;<br />
+  &lt;p&gt;Turn in your time card&lt;/p&gt;<br />
+  &lt;!--#else --&gt;<br />
+  &lt;p&gt;Yoga class at noon.&lt;/p&gt;<br />
+  &lt;!--#endif --&gt;</code>
+ </ul>
+</p>
+See the
+<p> <a href="http://httpd.apache.org/docs/howto/ssi.html#basicssidirectives">
+Apache Introduction to SSI</a> for more information on using SSI directives.</p>
+</section>
+
+<section name="Variables">
+<p>The SSI servlet currently implements the following variables:
+</p>
+<table border="1">
+<tr>
+<th>Variable Name</th>
+<th>Description</th>
+</tr>
+
+<tr>
+<td>AUTH_TYPE</td>
+<td>
+  The type of authentication used for this user: BASIC, FORM, etc.</td>
+</tr>
+
+<tr>
+<td>CONTENT_LENGTH</td>
+<td>
+  The length of the data (in bytes or the number of 
+  characters) passed from a form.</td>
+</tr>
+
+<tr>
+<td>CONTENT_TYPE</td>
+<td>
+  The MIME type of the query data, such as &quot;text/html&quot;.</td>
+</tr>
+
+<tr>
+<td>DATE_GMT</td>
+<td>
+Current date and time in GMT</td>
+</tr>
+
+<tr>
+<td>DATE_LOCAL</td>
+<td>
+Current date and time in the local time zone</td>
+</tr>
+<tr>
+<td>DOCUMENT_NAME</td>
+<td>
+The current file</td>
+</tr>
+<tr>
+<td>DOCUMENT_URI</td>
+<td>
+Virtual path to the file</td>
+</tr>
+
+<tr>
+<td>GATEWAY_INTERFACE</td>
+<td>
+  The revision of the Common Gateway Interface that the 
+  server uses if enabled: &quot;CGI/1.1&quot;.</td>
+</tr>
+
+<tr>
+<td>HTTP_ACCEPT</td>
+<td>
+  A list of the MIME types that the client can accept.</td>
+</tr>
+
+<tr>
+<td>HTTP_ACCEPT_ENCODING</td>
+<td>
+  A list of the compression types that the client can accept.</td>
+</tr>
+
+<tr>
+<td>HTTP_ACCEPT_LANGUAGE</td>
+<td>
+  A list of the laguages that the client can accept.</td>
+</tr>
+<tr>
+<td>HTTP_CONNECTION</td>
+<td>
+  The way that the connection from the client is being managed:
+  &quot;Close&quot; or &quot;Keep-Alive&quot;.</td>
+</tr>
+<tr>
+<td>HTTP_HOST</td>
+<td>
+  The web site that the client requested.</td>
+</tr>
+<tr>
+<td>HTTP_REFERER</td>
+<td>
+  The URL of the document that the client linked from.</td>
+</tr>
+<tr>
+<td>HTTP_USER_AGENT</td>
+<td>
+  The browser the client is using to issue the request.</td>
+</tr>
+<tr>
+<td>LAST_MODIFIED</td>
+<td>
+Last modification date and time for current file</td>
+</tr>
+<tr>
+<td>PATH_INFO</td>
+<td>
+  Extra path information passed to a servlet.</td>
+</tr>
+<tr>
+<td>PATH_TRANSLATED</td>
+<td>
+  The translated version of the path given by the
+  variable PATH_INFO.</td>
+</tr>
+<tr>
+<td>QUERY_STRING</td>
+<td>
+The query string that follows the &quot;?&quot; in the URL.
+</td>
+</tr>
+<tr>
+<td>QUERY_STRING_UNESCAPED</td>
+<td>
+Undecoded query string with all shell metacharacters escaped 
+with &quot;\&quot;</td>
+</tr>
+<tr>
+<td>REMOTE_ADDR</td>
+<td>
+  The remote IP address of the user making the request.</td>
+</tr>
+<tr>
+<td>REMOTE_HOST</td>
+<td>
+  The remote hostname of the user making the request.</td>
+</tr>
+<tr>
+<td>REMOTE_PORT</td>
+<td>
+  The port number at remote IP address of the user making the request.</td>
+</tr>
+<tr>
+<td>REMOTE_USER</td>
+<td>
+  The authenticated name of the user.</td>
+</tr>
+<tr>
+<td>REQUEST_METHOD</td>
+<td>
+  The method with which the information request was
+  issued: &quot;GET&quot;, &quot;POST&quot; etc.</td>
+</tr>
+<tr>
+<td>REQUEST_URI</td>
+<td>
+  The web page originally requested by the client.</td>
+</tr>
+<tr>
+<td>SCRIPT_FILENAME</td>
+<td>
+  The location of the current web page on the server.</td>
+</tr>
+<tr>
+<td>SCRIPT_NAME</td>
+<td>
+  The name of the web page.</td>
+</tr>
+<tr>
+<td>SERVER_ADDR</td>
+<td>
+  The server's IP address.</td>
+</tr>
+<tr>
+<td>SERVER_NAME</td>
+<td>
+  The server's hostname or IP address.</td>
+</tr>
+<tr>
+<td>SERVER_PORT</td>
+<td>
+  The port on which the server received the request.</td>
+</tr>
+<tr>
+<td>SERVER_PROTOCOL</td>
+<td>
+  The protocol used by the server. E.g. &quot;HTTP/1.1&quot;.</td>
+</tr>
+<tr>
+<td>SERVER_SOFTWARE</td>
+<td>
+  The name and version of the server software that is 
+  answering the client request.</td>
+</tr>
+<tr>
+<td>UNIQUE_ID</td>
+<td>
+  A token used to identify the current session if one
+  has been established.</td>
+</tr>
+</table>
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/ssl-howto.xml b/container/webapps/docs/ssl-howto.xml
new file mode 100644
index 0000000..b72f947
--- /dev/null
+++ b/container/webapps/docs/ssl-howto.xml
@@ -0,0 +1,525 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="ssl-howto.html">
+
+    &project;
+
+    <properties>
+        <author email="ccain@apache.org">Christopher Cain</author>
+        <author email="yoavs@apache.org">Yoav Shapira</author>
+        <title>SSL Configuration HOW-TO</title>
+    </properties>
+
+<body>
+
+
+<section name="Quick Start">
+
+    <blockquote><em>
+    <p>The description below uses the variable name $CATALINA_HOME
+    to refer to the directory into which you have installed Tomcat 5,
+    and is the base directory against which most relative paths are
+    resolved.  However, if you have configured Tomcat 5 for multiple
+    instances by setting a CATALINA_BASE directory, you should use
+    $CATALINA_BASE instead of $CATALINA_HOME for each of these
+    references.</p>
+    </em></blockquote>
+
+<p>To install and configure SSL support on Tomcat 5, you need to follow
+these simple steps.  For more information, read the rest of this HOW-TO.</p>
+<ol>
+<li>If you are running a 1.3 JVM, download JSSE 1.0.3 (or later) from
+   <a href="http://java.sun.com/products/jsse/">http://java.sun.com/products/jsse/</a>
+   and either make it an <em>installed extension</em> on the system, or else
+   set an environment variable <code>JSSE_HOME</code> that points at the
+   directory into which you installed JSSE.  </li><br/><br/>
+<li>Create a certificate keystore by executing the following command:
+<p>Windows:</p>
+<source>
+%JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA
+</source>
+<p>Unix:</p>
+<source>
+$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA
+</source>
+<p></p>
+    and specify a password value of "changeit".</li><br/><br/>
+<li>Uncomment the "SSL HTTP/1.1 Connector" entry in
+    <code>$CATALINA_HOME/conf/server.xml</code> and tweak as necessary.</li>
+    <br/><br/>
+</ol>
+
+
+</section>
+
+
+<section name="Introduction to SSL">
+
+<p>SSL, or Secure Socket Layer, is a technology which allows web browsers and
+web servers to communicate over a secured connection.  This means that the data
+being sent is encrypted by one side, transmitted, then decrypted by the other
+side before processing.  This is a two-way process, meaning that both the
+server AND the browser encrypt all traffic before sending out data.</p>
+
+<p>Another important aspect of the SSL protocol is Authentication.  This means
+that during your initial attempt to communicate with a web server over a secure
+connection, that server will present your web browser with a set of
+credentials, in the form of a "Certificate", as proof the site is who and what
+it claims to be.  In certain cases, the server may also request a Certificate
+from your web browser, asking for proof that <em>you</em> are who you claim
+to be.  This is known as "Client Authentication," although in practice this is
+used more for business-to-business (B2B) transactions than with individual
+users.  Most SSL-enabled web servers do not request Client Authentication.</p>
+
+</section>
+
+<section name="SSL and Tomcat">
+
+<p>It is important to note that configuring Tomcat to take advantage of
+secure sockets is usually only necessary when running it as a stand-alone
+web server.  When running Tomcat primarily as a Servlet/JSP container behind
+another web server, such as Apache or Microsoft IIS, it is usually necessary
+to configure the primary web server to handle the SSL connections from users.
+Typically, this server will negotiate all SSL-related functionality, then
+pass on any requests destined for the Tomcat container only after decrypting
+those requests.  Likewise, Tomcat will return cleartext responses, that will
+be encrypted before being returned to the user's browser.  In this environment,
+Tomcat knows that communications between the primary web server and the
+client are taking place over a secure connection (because your application
+needs to be able to ask about this), but it does not participate in the
+encryption or decryption itself.</p>
+
+</section>
+
+<section name="Certificates">
+
+<p>In order to implement SSL, a web server must have an associated Certificate
+for each external interface (IP address) that accepts secure connections.
+The theory behind this design is that a server should provide some kind of
+reasonable assurance that its owner is who you think it is, particularly
+before receiving any sensitive information.  While a broader explanation of
+Certificates is beyond the scope of this document, think of a Certificate
+as a "digital driver's license" for an Internet address.  It states what
+company the site is associated with, along with some basic contact
+information about the site owner or administrator.</p>
+
+<p>This "driver's license" is cryptographically signed by its owner, and is
+therefore extremely difficult for anyone else to forge.  For sites involved
+in e-commerce, or any other business transaction in which authentication of
+identity is important, a Certificate is typically purchased from a well-known
+<em>Certificate Authority</em> (CA) such as VeriSign or Thawte.  Such
+certificates can be electronically verified -- in effect, the Certificate
+Authority will vouch for the authenticity of the certificates that it grants,
+so you can believe that that Certificate is valid if you trust the Certificate
+Authority that granted it.</p>
+
+<p>In many cases, however, authentication is not really a concern.  An
+administrator may simply want to ensure that the data being transmitted and
+received by the server is private and cannot be snooped by anyone who may be
+eavesdropping on the connection.  Fortunately, Java provides a relatively
+simple command-line tool, called <code>keytool</code>, which can easily create
+a "self-signed" Certificate.  Self-signed Certificates are simply user
+generated Certificates which have not been officially registered with any
+well-known CA, and are therefore not really guaranteed to be authentic at all.
+Again, this may or may not even be important, depending on your needs.</p>
+
+</section>
+
+<section name="General Tips on Running SSL">
+
+<p>The first time a user attempts to access a secured page on your site,
+he or she is typically presented with a dialog containing the details of
+the certificate (such as the company and contact name), and asked if he or she
+wishes to accept the Certificate as valid and continue with the transaction.
+Some browsers will provide an option for permanently accepting a given
+Certificate as valid, in which case the user will not be bothered with a
+prompt each time they visit your site.  Other browsers do not provide this
+option.  Once approved by the user, a Certificate will be considered valid
+for at least the entire browser session.</p>
+
+<p>Also, while the SSL protocol was designed to be as efficient as securely
+possible, encryption/decryption is a computationally expensive process from
+a performance standpoint.  It is not strictly necessary to run an entire
+web application over SSL, and indeed a developer can pick and choose which
+pages require a secure connection and which do not.  For a reasonably busy
+site, it is customary to only run certain pages under SSL, namely those
+pages where sensitive information could possibly be exchanged.  This would
+include things like login pages, personal information pages, and shopping
+cart checkouts, where credit card information could possibly be transmitted.
+Any page within an application can be requested over a secure socket by
+simply prefixing the address with <code>https:</code> instead of
+<code>http:</code>.  Any pages which absolutely <strong>require</strong>
+a secure connection should check the protocol type associated with the
+page request and take the appropriate action if <code>https</code> is not
+specified.</p>
+
+<p>Finally, using name-based virtual hosts on a secured connection can be
+problematic.  This is a design limitation of the SSL protocol itself.  The SSL
+handshake, where the client browser accepts the server certificate, must occur
+before the HTTP request is accessed.  As a result, the request information
+containing the virtual host name cannot be determined prior to authentication,
+and it is therefore not possible to assign multiple certificates to a single
+IP address.  If all virtual hosts on a single IP address need to authenticate
+against the same certificate, the addition of multiple virtual hosts should not
+interfere with normal SSL operations on the server.  Be aware, however, that
+most client browsers will compare the server's domain name against the domain
+name listed in the certificate, if any (applicable primarily to official,
+CA-signed certificates).  If the domain names do not match, these browsers will
+display a warning to the client user.  In general, only address-based virtual
+hosts are commonly used with SSL in a production environment.</p>
+
+</section>
+
+<section name="Configuration">
+
+<subsection name="Download and Install JSSE (if needed)">
+<p>Note that JSSE is bundled with Sun's JDK 1.4 and later, so if you're using
+JDK 1.4 and later, you can skip this step.</p>
+
+
+<p>Download the <em>Java Secure Socket Extensions</em> (JSSE) package,
+version 1.0.3 or later, from
+<a href="http://java.sun.com/products/jsse/">http://java.sun.com/products/jsse/</a>.
+If you built Tomcat from source, you have probably already downloaded this
+package.</p>
+
+<p>After expanding the package, there are two ways to make it available to
+Tomcat (choose one or the other):</p>
+<ul>
+<li>Make JSSE an <em>installed extension</em> by copying all three JAR files
+    (<code>jcert.jar</code>, <code>jnet.jar</code>, and <code>jsse.jar</code>)
+    into your <code>$JAVA_HOME/jre/lib/ext</code> directory.</li>
+<li>Create a new environment variable <code>JSSE_HOME</code> that contains
+    the absolute path to the directory into which you unpacked the
+    JSSE binary distribution.</li>
+</ul>
+
+</subsection>
+
+<subsection name="Prepare the Certificate Keystore">
+
+<p>Tomcat currently operates only on <code>JKS</code> or <code>PKCS12</code>
+format keystores.  The <code>JKS</code> format
+is Java's standard "Java KeyStore" format, and is the format created by the
+<code>keytool</code> command-line utility.  This tool is included in the JDK.
+The <code>PKCS12</code> format is an internet standard, and can be manipulated
+via (among other things) OpenSSL and Microsoft's Key-Manager.  However, 
+currently there are some limitations on the support for <code>PKCS12</code>.
+</p>
+
+<p>To import an existing certificate into a JKS keystore, please read the
+documentation (in your JDK documentation package) about <code>keytool</code>.
+</p>
+<p>To import an existing certificate signed by your own CA into a PKCS12 
+keystore using OpenSSL you would execute a command like:
+<source>openssl pkcs12 -export -in mycert.crt -inkey mykey.key \
+                        -out mycert.p12 -name tomcat -CAfile myCA.crt \
+                        -caname root -chain
+</source>
+For more advanced cases, consult the <a href="http://www.openssl.org/">OpenSSL
+documententation</a>.
+</p>
+<p>To create a new keystore from scratch, containing a single self-signed
+Certificate, execute the following from a terminal command line:</p>
+<p>Windows:</p>
+<source>
+%JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA
+</source>
+<p>Unix:</p>
+<source>
+$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA
+</source>
+
+<p>(The RSA algorithm should be preferred as a secure algorithm, and this
+also ensures general compatibility with other servers and components.)</p>
+
+<p>This command will create a new file, in the home directory of the user
+under which you run it, named "<code>.keystore</code>".  To specify a
+different location or filename, add the <code>-keystore</code> parameter,
+followed by the complete pathname to your keystore file,
+to the <code>keytool</code> command shown above.  You will also need to
+reflect this new location in the <code>server.xml</code> configuration file,
+as described later.  For example:</p>
+<p>Windows:</p>
+<source>
+%JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA \
+  -keystore \path\to\my\keystore
+</source>
+<p>Unix:</p>
+<source>
+$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA \
+  -keystore /path/to/my/keystore
+</source>
+
+<p>After executing this command, you will first be prompted for the keystore
+password.  The default password used by Tomcat is "<code>changeit</code>"
+(all lower case), although you can specify a custom password if you like.
+You will also need to specify the custom password in the
+<code>server.xml</code> configuration file, as described later.</p>
+
+<p>Next, you will be prompted for general information about this Certificate,
+such as company, contact name, and so on.  This information will be displayed
+to users who attempt to access a secure page in your application, so make
+sure that the information provided here matches what they will expect.</p>
+
+<p>Finally, you will be prompted for the <em>key password</em>, which is the
+password specifically for this Certificate (as opposed to any other
+Certificates stored in the same keystore file).  You <strong>MUST</strong>
+use the same password here as was used for the keystore password itself.
+(Currently, the <code>keytool</code> prompt will tell you that pressing the
+ENTER key does this for you automatically.)</p>
+
+<p>If everything was successful, you now have a keystore file with a
+Certificate that can be used by your server.</p>
+
+</subsection>
+
+<subsection name="Edit the Tomcat Configuration File">
+
+<p>The final step is to configure your secure socket in the
+<code>$CATALINA_HOME/conf/server.xml</code> file, where
+<code>$CATALINA_HOME</code> represents the directory into which you
+installed Tomcat 5.  An example <code>&lt;Connector&gt;</code> element
+for an SSL connector is included in the default <code>server.xml</code>
+file installed with Tomcat.  It will look something like this:</p>
+<source>
+&lt;-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 --&gt;
+&lt;!--
+&lt;Connector 
+           port="8443" minProcessors="5" maxProcessors="75"
+           enableLookups="true" disableUploadTimeout="true"
+           acceptCount="100" debug="0" scheme="https" secure="true";
+           clientAuth="false" sslProtocol="TLS"/&gt;
+--&gt;
+</source>
+
+<p>You will note that the Connector element itself is commented out by default,
+so you will need to remove the comment tags around it.  Then, you can
+customize the specified attributes as necessary.  For detailed information
+about the various options, consult the
+<a href="config/index.html">Server Configuration Reference</a>.  The
+following discussion covers only those attributes of most interest when
+setting up SSL communication.</p>
+
+<p>The <code>port</code> attribute (default value is 8443) is the TCP/IP
+port number on which Tomcat will listen for secure connections.  You can
+change this to any port number you wish (such as to the default port for
+<code>https</code> communications, which is 443).  However, special setup
+(outside the scope of this document) is necessary to run Tomcat on port
+numbers lower than 1024 on many operating systems.</p>
+
+  <blockquote><em>
+  <p>If you change the port number here, you should also change the
+  value specified for the <code>redirectPort</code> attribute on the
+  non-SSL connector.  This allows Tomcat to automatically redirect
+  users who attempt to access a page with a security constraint specifying
+  that SSL is required, as required by the Servlet 2.4 Specification.</p>
+  </em></blockquote>
+
+<p>There are addional option used to configure the SSL protocol.
+  You may need to add or change the following attribute
+values, depending on how you configured your keystore earlier:</p>
+
+<table border="1">
+  <tr>
+    <th>Attribute</th>
+    <th>Description</th>
+  </tr>
+  <tr>
+    <td><code>clientAuth</code></td>
+    <td>Set this value to <code>true</code> if you want Tomcat to require
+        all SSL clients to present a client Certificate in order to use
+        this socket.  Set this value to <code>want</code> if you want Tomcat
+        to request a client Certificate, but not fail if one isn't presented.
+    </td>
+  </tr>
+  <tr>
+    <td><code>keystoreFile</code></td>
+    <td>Add this attribute if the keystore file you created is not in
+        the default place that Tomcat expects (a file named
+        <code>.keystore</code> in the user home directory under
+        which Tomcat is running).  You can specify an absolute pathname,
+        or a relative pathname that is resolved against the
+        <code>$CATALINA_BASE</code> environment variable.</td>
+  </tr>
+  <tr>
+    <td><code>keystorePass</code></td>
+    <td>Add this element if you used a different keystore (and Certificate)
+        password than the one Tomcat expects (<code>changeit</code>).</td>
+  </tr>
+  <tr>
+    <td><code>keystoreType</code></td>
+    <td>Add this element if using a PKCS12 keystore.  The valid values are
+        <code>JKS</code> and <code>PKCS12</code>.</td>
+  </tr>
+  <tr>
+    <td><code>sslProtocol</code></td>
+    <td>The encryption/decryption protocol to be used on this socket.
+        It is not recommended to change this value if you are using Sun's
+        JVM.  It is reported that IBM's 1.4.1 implementation
+        of the TLS protocol is not compatible with some popular browsers.
+        In this case, use the value <code>SSL</code>.</td>
+  </tr>
+  <tr>
+    <td><code>ciphers</code></td>
+    <td>The comma separated list of encryption ciphers that this socket is 
+        allowed to use.  By default, any available cipher is allowed.</td>
+  </tr>
+  <tr>
+    <td><code>algorithm</code></td>
+    <td>The <code>X509</code> algorithm to use.  This defaults to the Sun 
+        implementation (<code>SunX509</code>).  For IBM JVMs you should use
+        the value <code>IbmX509</code>.  For other vendors, consult the JVM
+        documentation for the correct value.
+    </td>
+  </tr>
+  <tr>
+   <td><code>truststoreFile</code></td>
+   <td>The TrustStore file to use to validate client certificates.</td>
+  </tr>
+  <tr>
+   <td><code>truststorePass</code></td>
+   <td>The password to access the TrustStore.  This defaults to the value
+       of <code>keystorePass</code>.</td>
+  </tr>
+  <tr>
+   <td><code>truststoreType</code></td>
+    <td>Add this element if your are using a different format for the 
+        TrustStore then you are using for the KeyStore.  The valid values are
+        <code>JKS</code> and <code>PKCS12</code>.</td>
+  </tr>
+</table>
+
+<p>After completing these configuration changes, you must restart Tomcat as
+you normally do, and you should be in business.  You should be able to access
+any web application supported by Tomcat via SSL.  For example, try:</p>
+<source>
+https://localhost:8443
+</source>
+
+<p>and you should see the usual Tomcat splash page (unless you have modified
+the ROOT web application).  If this does not work, the following section
+contains some troubleshooting tips.</p>
+
+</subsection>
+
+</section>
+
+<section name="Installing a Certificate from a Certificate Authority">
+<p>To obstain and install a Certificate from a Certificate Authority (like verisign.com, thawte.com 
+or trustcenter.de) you should have read the previous section and then follow these instructions:</p>
+
+<subsection name="Create a local Certificate Signing Request (CSR)">
+<p>In order to obtain a Certificate from the Certificate Authority of your choice 
+you have to create a so called Certificate Signing Request (CSR). That CSR will be used 
+by the Certificate Authority to create a Certificate that will identify your website 
+as "secure". To create a CSR follow these steps:</p>
+<ul>
+<li>Create a local Certificate (as described in the previous section):
+	<source>keytool -genkey -alias tomcat -keyalg RSA \
+	-keystore &lt;your_keystore_filename&gt;</source>
+	Note: In some cases you will have to enter the domain of your website (i.e. <code>www.myside.org</code>)
+	in the field "first- and lastname" in order to create a working Certificate. 
+</li>
+<li>The CSR is then created with:
+	<source>keytool -certreq -keyalg RSA -alias tomcat -file certreq.csr \
+	-keystore &lt;your_keystore_filename&gt;</source>
+</li>
+</ul>
+<p>Now you have a file called <code>certreq.csr</code> that you can submit to the Certificate Authority (look at the
+documentation of the Certificate Authority website on how to do this). In return you get a Certificate.</p>
+</subsection>
+
+<subsection name="Importing the Certificate">
+<p>Now that you have your Certificate you can import it into you local keystore. 
+First of all you have to import a so called Chain Certificate or Root Certificate into your keystore. 
+After that you can procede with importing your Certificate.</p>
+
+<ul>
+<li>Download a Chain Certificate from the Certificate Authority you obtained the Certificate from.<br/>
+	For Verisign.com go to: http://www.verisign.com/support/install/intermediate.html<br/>
+	For Trustcenter.de go to: http://www.trustcenter.de/certservices/cacerts/en/en.htm#server<br/>
+	For Thawte.com go to: http://www.thawte.com/certs/trustmap.html<br/>
+</li>
+<li>Import the Chain Certificate into you keystore
+    <source>keytool -import -alias root -keystore &lt;your_keystore_filename&gt; \
+	-trustcacerts -file &lt;filename_of_the_chain_certificate&gt;</source>
+</li>
+<li>And finally import your new Certificate
+	<source>keytool -import -alias tomcat -keystore &lt;your_keystore_filename&gt; \
+	-trustcacerts -file &lt;your_certificate_filename&gt;</source>
+</li>
+</ul>
+</subsection>
+</section>
+
+<section name="Troubleshooting">
+
+<p>Here is a list of common problems that you may encounter when setting up
+SSL communications, and what to do about them.</p>
+
+<ul>
+
+<li>I get "java.security.NoSuchAlgorithmException" errors in my
+    log files.
+    <blockquote>
+    <p>The JVM cannot find the JSSE JAR files.  Follow all of the directions to
+    <a href="#Download and Install JSSE">download and install JSSE</a>.</p>
+    </blockquote></li>
+
+<li>When Tomcat starts up, I get an exception like
+    "java.io.FileNotFoundException: {some-directory}/{some-file} not found".
+    <blockquote>
+    <p>A likely explanation is that Tomcat cannot find the keystore file
+    where it is looking.  By default, Tomcat expects the keystore file to
+    be named <code>.keystore</code> in the user home directory under which
+    Tomcat is running (which may or may not be the same as yours :-).  If
+    the keystore file is anywhere else, you will need to add a
+    <code>keystoreFile</code> attribute to the <code>&lt;Factory&gt;</code>
+    element in the <a href="#Edit the Tomcat Configuration File">Tomcat
+    configuration file</a>.</p>
+    </blockquote></li>
+
+<li>When Tomcat starts up, I get an exception like
+    "java.io.FileNotFoundException:  Keystore was tampered with, or
+    password was incorrect".
+    <blockquote>
+    <p>Assuming that someone has not <em>actually</em> tampered with
+    your keystore file, the most likely cause is that Tomcat is using
+    a different password than the one you used when you created the
+    keystore file.  To fix this, you can either go back and
+    <a href="#Prepare the Certificate Keystore">recreate the keystore
+    file</a>, or you can add or update the <code>keystorePass</code>
+    attribute on the <code>&lt;Factory&gt;</code> element in the
+    <a href="#Edit the Tomcat Configuration File">Tomcat configuration
+    file</a>.  <strong>REMINDER</strong> - Passwords are case sensitive!</p>
+    </blockquote></li>
+
+</ul>
+
+<p>If you are still having problems, a good source of information is the
+<strong>TOMCAT-USER</strong> mailing list.  You can find pointers to archives
+of previous messages on this list, as well as subscription and unsubscription
+information, at
+<a href="http://jakarta.apache.org/site/mail.html">http://jakarta.apache.org/site/mail.html"</a>.</p>
+
+</section>
+
+<section name="Miscellaneous Tips and Bits">
+
+<p>To access the SSL session ID from the request, use:<br />
+
+  <code>
+    String sslID = (String)request.getAttribute("javax.servlet.request.ssl_session");
+  </code>
+<br />
+For additional discussion on this area, please see
+<a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=22679">Bugzilla</a>.
+</p>
+</section>
+
+</body>
+
+</document>
diff --git a/container/webapps/docs/status.xml b/container/webapps/docs/status.xml
new file mode 100644
index 0000000..bb8d884
--- /dev/null
+++ b/container/webapps/docs/status.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="status.html">
+
+  &project;
+
+  <properties>
+    <author email="remm@apache.org">Remy Maucherat</author>
+    <author email="yoavs@apache.org">Yoav Shapira</author>
+    <title>Project Status</title>
+  </properties>
+
+<body>
+  <section name="Preface">
+    <p>
+      This document attempts to convey the current status of Tomcat development
+      in "big picture" terms.  This is not the place to check if an individual
+      bug report has been addressed or when an individual feature will be available.
+    </p>
+    <p>
+      This page is updated roughly once per every couple of Tomcat minor releases,
+      so for day-to-day status you should check the tomcat-user and tomcat-dev mailing
+      lists.  You can always inquire there as to the availability or status of a
+      specific feature or component.
+    </p>
+  </section>
+
+  <section name="Current Status Summary">
+    <p>
+      <b>Tomcat 5.0.27</b> was released on June 17th, 2004.  At that time, the TOMCAT_5_0
+      branch was tagged in CVS, and work on Tomcat 5.5 began.  We have now had several
+      Tomcat 5.5 releases, including a couple of stable ones.  Accordingly, Tomcat 5.5
+      is now the focus on work.  Tomcat 5.0 is in maintenance mode and its releases
+      will become less and less frequent.
+    </p>
+    <p>
+      <b>Tomcat 5.5</b> has several major goals.  They are discussed in the tomcat-dev
+      mailing list's "5.next" thread: 
+      <a href="http://marc.theaimsgroup.com/?l=tomcat-dev&amp;w=2&amp;r=1&amp;s=5.next&amp;q=b">MARC</a>.
+      The status of some of these items is detailed below.  Once 5.5 releases are
+      available, please refer to the Changelog accompanying each release for detailed
+      changes, enhancements, and fixes.
+    </p>
+    <p>
+      <b>Tomcat 4.1.x</b> is no longer actively developed.  It is maintained to address
+      only showstopper, security, and Servlet Specification compliance issues.  Maintenance
+      for Tomcat 4.1.x will likely cease once a stable release or two of Tomcat 5.5 are out.
+      Users of Tomcat 4.1.x are strongly encouraged to upgrade to the latest stable Tomcat
+      5.0 release.
+    </p>
+    <p>
+      <b>Tomcat 4.0.x</b> is relatively old, and not actively maintained or supported.
+      It is strongly recommended that users of these releases upgrade to the latest
+      stable Tomcat 5.0 release or at least the latest stable Tomcat 4.1 release.
+    </p>
+    <p>
+      <b>Tomcat 3.3.x</b> is in roughly the same maintenance mode as Tomcat 4.1.x.
+    </p>
+    <p>
+      <b>Tomcat 3.2</b> and earlier are in roughly the same support state as Tomcat 4.0.x.
+      Users should upgrade to Tomcat 3.3.x or the latest stable Tomcat 5.0.x.
+    </p>
+  </section>
+
+  <section name="How to read the report">
+    
+    <p>
+      The columns in this report contain the following information:
+    <ul>
+      <li><b>Priority</b> - A sense of how important it is to address this 
+        issue in the short term.</li>
+      <li><b>Action Item</b> - Concise description of the action item
+        to be completed.  Where relevant, Java package names of the
+        primary classes involved are listed in [square brackets]</li>
+      <li><b>Volunteers</b> - Login of those developers who
+        have volunteered to assist in the design, implementation, testing, and
+        documentation of this action item's changes to Tomcat.</li>
+    </ul>
+      Developers can nominate
+      themselves to work on particular action items by asking a Committer to 
+      add their name address to those items.  The developers 
+      working on each item should discuss and agree upon the approach to be 
+      used for implementing the item's changes to the project source code 
+      and documentation, prior to completing those changes.  Such discussions 
+      should take place on the tomcat-dev mailing list so that everyone can 
+      stay apprised of what is going on, or chime in if they want to 
+      contribute ideas and suggestions.
+    </p>
+    
+  </section>
+
+  <section name="TODO List">
+
+    <status>
+      <item priority="High" owner="costin">
+        Refactor ClassLoaders for Tomcat 5.5 to allow container plugins.
+      </item>
+      <item priority="Medium" owner="fhanik">
+        Enhance Cluster functionality for Tomcat 5.5.
+      </item>
+      <item priority="Medium" owner="everyone">
+        Continue fixing bugs and updating docs.
+      </item>
+    </status>
+
+  </section>
+
+  <section name="Open bugs">
+    
+    <p>
+      The list of the bugs which are in an unresolved state for Tomcat 5 can be
+      seen 
+      <a href="http://issues.apache.org/bugzilla/buglist.cgi?bug_status=UNCONFIRMED&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;bug_status=RESOLVED&amp;resolution=LATER&amp;resolution=REMIND&amp;resolution=---&amp;email1=&amp;emailtype1=substring&amp;emailassigned_to1=1&amp;email2=&amp;emailtype2=substring&amp;emailreporter2=1&amp;bugidtype=include&amp;bug_id=&amp;changedin=&amp;votes=&amp;chfieldfrom=&amp;chfieldto=Now&amp;chfieldvalue=&amp;product=Tomcat+5&amp;short_desc=&amp;short_desc_type=allwordssubstr&amp;long_desc=&amp;long_desc_type=allwordssubstr&amp;bug_file_loc=&amp;bug_file_loc_type=allwordssubstr&amp;keywords=&amp;keywords_type=anywords&amp;field0-0-0=noop&amp;type0-0-0=noop&amp;value0-0-0=&amp;cmdtype=doit&amp;order=%27Importance%27">here</a>.
+      Aspiring volunteers and others are strongly encouraged to attempt 
+      to comment and help resolve these issues.
+    </p>
+    
+  </section>
+
+</body>
+</document>
diff --git a/container/webapps/docs/tomcat-docs.xsl b/container/webapps/docs/tomcat-docs.xsl
new file mode 100644
index 0000000..dc332e0
--- /dev/null
+++ b/container/webapps/docs/tomcat-docs.xsl
@@ -0,0 +1,429 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- Content Stylesheet for "tomcat-docs" Documentation -->
+
+<!-- $Id$ -->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  version="1.0">
+
+
+  <!-- Output method -->
+  <xsl:output method="html"
+            encoding="iso-8859-1"
+              indent="no"/>
+
+
+  <!-- Defined parameters (overrideable) -->
+  <xsl:param    name="home-name"        select="'The Jakarta Project'"/>
+  <xsl:param    name="home-href"        select="'http://jakarta.apache.org/'"/>
+  <xsl:param    name="home-logo"        select="'/images/jakarta-logo.gif'"/>
+  <xsl:param    name="printer-logo"     select="'/images/printer.gif'"/>
+  <xsl:param    name="relative-path"    select="'.'"/>
+  <xsl:param    name="void-image"       select="'/images/void.gif'"/>
+  <xsl:param    name="project-menu"     select="'menu'"/>
+  <xsl:param    name="standalone"       select="''"/>
+  <xsl:param    name="buglink"          select="'http://issues.apache.org/bugzilla/show_bug.cgi?id='"/>
+
+  <!-- Defined variables (non-overrideable) -->
+  <xsl:variable name="body-bg"          select="'#ffffff'"/>
+  <xsl:variable name="body-fg"          select="'#000000'"/>
+  <xsl:variable name="body-link"        select="'#525D76'"/>
+  <xsl:variable name="banner-bg"        select="'#525D76'"/>
+  <xsl:variable name="banner-fg"        select="'#ffffff'"/>
+  <xsl:variable name="sub-banner-bg"    select="'#828DA6'"/>
+  <xsl:variable name="sub-banner-fg"    select="'#ffffff'"/>
+  <xsl:variable name="source-color"     select="'#023264'"/>
+  <xsl:variable name="attributes-color" select="'#023264'"/>
+  <xsl:variable name="table-th-bg"      select="'#039acc'"/>
+  <xsl:variable name="table-td-bg"      select="'#a0ddf0'"/>
+
+  <!-- Process an entire document into an HTML page -->
+  <xsl:template match="document">
+    <html>
+    <head>
+    <title><xsl:value-of select="project/title"/> - <xsl:value-of select="properties/title"/></title>
+    <xsl:for-each select="properties/author">
+      <xsl:variable name="name">
+        <xsl:value-of select="."/>
+      </xsl:variable>
+      <xsl:variable name="email">
+        <xsl:value-of select="@email"/>
+      </xsl:variable>
+      <meta name="author" value="{$name}"/>
+      <meta name="email" value="{$email}"/>
+    </xsl:for-each>
+    </head>
+
+    <body bgcolor="{$body-bg}" text="{$body-fg}" link="{$body-link}"
+          alink="{$body-link}" vlink="{$body-link}">
+
+    <table border="0" width="100%" cellspacing="4">
+
+      <xsl:comment>PAGE HEADER</xsl:comment>
+      <tr><td colspan="2">
+
+        <xsl:comment>JAKARTA LOGO</xsl:comment>
+        <xsl:variable name="alt">
+          <xsl:value-of select="$home-name"/>
+        </xsl:variable>
+        <xsl:variable name="href">
+          <xsl:value-of select="$home-href"/>
+        </xsl:variable>
+        <xsl:variable name="src">
+          <xsl:value-of select="$relative-path"/><xsl:value-of select="$home-logo"/>
+        </xsl:variable>
+        <a href="{$href}">
+          <img src="{$src}" align="left" alt="{$alt}" border="0"/>
+        </a>
+        <xsl:if test="project/logo">
+          <xsl:variable name="alt">
+            <xsl:value-of select="project/logo"/>
+          </xsl:variable>
+          <xsl:variable name="home">
+            <xsl:value-of select="project/@href"/>
+          </xsl:variable>
+          <xsl:variable name="src">
+            <xsl:value-of select="$relative-path"/><xsl:value-of select="project/logo/@href"/>
+          </xsl:variable>
+
+          <xsl:comment>PROJECT LOGO</xsl:comment>
+          <a href="{$home}">
+            <img src="{$src}" align="right" alt="{$alt}" border="0"/>
+          </a>
+        </xsl:if>
+
+      </td></tr>
+
+      <xsl:comment>HEADER SEPARATOR</xsl:comment>
+      <tr>
+        <td colspan="2">
+          <hr noshade="noshade" size="1"/>
+        </td>
+      </tr>
+
+      <tr>
+
+        <!-- Don't generate a menu if styling printer friendly docs -->
+        <xsl:if test="$project-menu = 'menu'">
+          <xsl:comment>LEFT SIDE NAVIGATION</xsl:comment>
+          <td width="20%" valign="top" nowrap="true">
+            <xsl:apply-templates select="project/body/menu"/>
+          </td>
+        </xsl:if>
+
+        <xsl:comment>RIGHT SIDE MAIN BODY</xsl:comment>
+        <td width="80%" valign="top" align="left">
+          <table border="0" width="100%" cellspacing="4">
+            <tr>
+              <td align="left" valign="top">
+                <h1><xsl:value-of select="project/title"/></h1>
+                <h2><xsl:value-of select="properties/title"/></h2>
+              </td>
+              <td align="right" valign="top" nowrap="true">
+                <!-- Add the printer friendly link for docs with a menu -->
+                <xsl:if test="$project-menu = 'menu'">
+                  <xsl:variable name="src">
+                    <xsl:value-of select="$relative-path"/><xsl:value-of select="$printer-logo"/>
+                  </xsl:variable>
+                  <xsl:variable name="url">
+                    <xsl:value-of select="/document/@url"/>
+                  </xsl:variable>
+                  <small>
+                    <a href="printer/{$url}">
+                      <img src="{$src}" border="0" alt="Printer Friendly Version"/>
+                      <br />print-friendly<br />version
+                    </a>
+                  </small>
+                </xsl:if>
+                <xsl:if test="$project-menu != 'menu'">
+                  <xsl:variable name="void">
+                    <xsl:value-of select="$relative-path"/><xsl:value-of select="$void-image"/>
+                    </xsl:variable>
+                  <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+                </xsl:if>
+              </td>
+            </tr>
+          </table>
+          <xsl:apply-templates select="body/section"/>
+        </td>
+
+      </tr>
+
+      <xsl:comment>FOOTER SEPARATOR</xsl:comment>
+      <tr>
+        <td colspan="2">
+          <hr noshade="noshade" size="1"/>
+        </td>
+      </tr>
+
+      <xsl:comment>PAGE FOOTER</xsl:comment>
+      <tr><td colspan="2">
+        <div align="center"><font color="{$body-link}" size="-1"><em>
+        Copyright &#169; 1999-2005, Apache Software Foundation
+        </em></font></div>
+      </td></tr>
+
+    </table>
+    </body>
+    </html>
+
+  </xsl:template>
+
+
+  <!-- Process a menu for the navigation bar -->
+  <xsl:template match="menu">
+    <p><strong><xsl:value-of select="@name"/></strong></p>
+    <ul>
+      <xsl:apply-templates select="item"/>
+    </ul>
+  </xsl:template>
+
+
+  <!-- Process a menu item for the navigation bar -->
+  <xsl:template match="item">
+    <xsl:variable name="href">
+      <xsl:value-of select="@href"/>
+    </xsl:variable>
+    <li><a href="{$href}"><xsl:value-of select="@name"/></a></li>
+  </xsl:template>
+
+
+  <!-- Process a documentation section -->
+  <xsl:template match="section">
+    <xsl:variable name="name">
+      <xsl:value-of select="@name"/>
+    </xsl:variable>
+    <table border="0" cellspacing="0" cellpadding="2">
+      <!-- Section heading -->
+      <tr><td bgcolor="{$banner-bg}">
+          <font color="{$banner-fg}" face="arial,helvetica.sanserif">
+          <a name="{$name}">
+          <strong><xsl:value-of select="@name"/></strong></a></font>
+      </td></tr>
+      <!-- Section body -->
+      <tr><td><blockquote>
+        <xsl:apply-templates/>
+      </blockquote></td></tr>
+    </table>
+  </xsl:template>
+
+
+  <!-- Process a documentation subsection -->
+  <xsl:template match="subsection">
+    <xsl:variable name="name">
+      <xsl:value-of select="@name"/>
+    </xsl:variable>
+    <table border="0" cellspacing="0" cellpadding="2">
+      <!-- Subsection heading -->
+      <tr><td bgcolor="{$sub-banner-bg}">
+          <font color="{$sub-banner-fg}" face="arial,helvetica.sanserif">
+          <a name="{$name}">
+          <strong><xsl:value-of select="@name"/></strong></a></font>
+      </td></tr>
+      <!-- Subsection body -->
+      <tr><td><blockquote>
+        <xsl:apply-templates/>
+      </blockquote></td></tr>
+    </table>
+  </xsl:template>
+
+
+  <!-- Process a source code example -->
+  <xsl:template match="source">
+    <xsl:variable name="void">
+      <xsl:value-of select="$relative-path"/><xsl:value-of select="$void-image"/>
+    </xsl:variable>
+    <div align="left">
+      <table cellspacing="4" cellpadding="0" border="0">
+        <tr>
+          <td bgcolor="{$source-color}" width="1" height="1">
+            <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+          </td>
+          <td bgcolor="{$source-color}" height="1">
+            <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+          </td>
+          <td bgcolor="{$source-color}" width="1" height="1">
+            <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+          </td>
+        </tr>
+        <tr>
+          <td bgcolor="{$source-color}" width="1">
+            <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+          </td>
+          <td bgcolor="#ffffff" height="1"><pre>
+            <xsl:value-of select="."/>
+          </pre></td>
+          <td bgcolor="{$source-color}" width="1">
+            <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+          </td>
+        </tr>
+        <tr>
+          <td bgcolor="{$source-color}" width="1" height="1">
+            <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+          </td>
+          <td bgcolor="{$source-color}" height="1">
+            <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+          </td>
+          <td bgcolor="{$source-color}" width="1" height="1">
+            <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+          </td>
+        </tr>
+      </table>
+    </div>
+  </xsl:template>
+
+
+  <!-- Process an attributes list with nested attribute elements -->
+  <xsl:template match="attributes">
+    <table border="1" cellpadding="5">
+      <tr>
+        <th width="15%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Attribute</font>
+        </th>
+        <th width="85%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Description</font>
+        </th>
+      </tr>
+      <xsl:for-each select="attribute">
+        <tr>
+          <td align="left" valign="center">
+            <xsl:if test="@required = 'true'">
+              <strong><code><xsl:value-of select="@name"/></code></strong>
+            </xsl:if>
+            <xsl:if test="@required != 'true'">
+              <code><xsl:value-of select="@name"/></code>
+            </xsl:if>
+          </td>
+          <td align="left" valign="center">
+            <xsl:apply-templates/>
+          </td>
+        </tr>
+      </xsl:for-each>
+    </table>
+  </xsl:template>
+
+  <!-- Fix relative links in printer friendly versions of the docs -->
+  <xsl:template match="a">
+    <xsl:variable name="href" select="@href"/>
+    <xsl:choose>
+      <xsl:when test="$standalone = 'standalone'">
+        <xsl:apply-templates/>
+      </xsl:when>
+      <xsl:when test="$project-menu != 'menu' and starts-with(@href,'../')">
+        <a href="../{$href}"><xsl:apply-templates/></a>
+      </xsl:when>
+      <xsl:when test="$project-menu != 'menu' and starts-with(@href,'./') and contains(substring(@href,3),'/')">
+        <a href=".{$href}"><xsl:apply-templates/></a>
+      </xsl:when>
+      <xsl:when test="$project-menu != 'menu' and not(contains(@href,'//')) and not(starts-with(@href,'/')) and not(starts-with(@href,'#')) and contains(@href,'/')">
+        <a href="../{$href}"><xsl:apply-templates/></a>
+      </xsl:when>
+      <xsl:when test="$href != ''">
+        <a href="{$href}"><xsl:apply-templates/></a>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:variable name="name" select="@name"/>
+        <a name="{$name}"><xsl:apply-templates/></a>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!-- Changelog related tags -->
+  <xsl:template match="changelog">
+    <table border="0" cellpadding="2" cellspacing="2">
+      <xsl:apply-templates/>
+    </table>
+  </xsl:template>
+
+  <xsl:template match="changelog/add">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/add.gif</xsl:variable>
+      <td><img alt="add" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/update">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/update.gif</xsl:variable>
+      <td><img alt="update" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/design">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/design.gif</xsl:variable>
+      <td><img alt="design" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/docs">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/docs.gif</xsl:variable>
+      <td><img alt="docs" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/fix">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/fix.gif</xsl:variable>
+      <td><img alt="fix" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/scode">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/code.gif</xsl:variable>
+      <td><img alt="code" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <!-- Process an attributes list with nested attribute elements -->
+  <xsl:template match="status">
+    <table border="1" cellpadding="5">
+      <tr>
+        <th width="15%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Priority</font>
+        </th>
+        <th width="50%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Action Item</font>
+        </th>
+        <th width="25%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Volunteers</font>
+        </th>
+        <xsl:for-each select="item">
+        <tr>
+          <td align="left" valign="center">
+            <xsl:value-of select="@priority"/>
+          </td>
+          <td align="left" valign="center">
+            <xsl:apply-templates/>
+          </td>
+          <td align="left" valign="center">
+            <xsl:value-of select="@owner"/>
+          </td>
+        </tr>
+        </xsl:for-each>
+      </tr>
+    </table>
+  </xsl:template>
+
+  <!-- Link to a bug report -->
+  <xsl:template match="bug">
+      <xsl:variable name="link"><xsl:value-of select="$buglink"/><xsl:value-of select="text()"/></xsl:variable>
+      <a href="{$link}"><xsl:apply-templates/></a>
+  </xsl:template>
+
+  <!-- Process everything else by just passing it through -->
+  <xsl:template match="*|@*">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|*|text()"/>
+    </xsl:copy>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/container/webapps/docs/windows-service-howto.xml b/container/webapps/docs/windows-service-howto.xml
new file mode 100644
index 0000000..82bf048
--- /dev/null
+++ b/container/webapps/docs/windows-service-howto.xml
@@ -0,0 +1,352 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="windows-service-howto.html">
+
+    &project;
+
+    <properties>
+      <author email="mturk@apache.org">Mladen Turk</author>
+      <title>Windows service HOW-TO</title>
+    </properties>
+
+<body>
+ 
+<section name="NOTICE">
+<p>
+    <b>This section of the documentation applies to procrun 1.0, and is now obsolete.</b>
+</p>
+</section>
+<section name="Tomcat5 service application">
+<p>
+    <b>Tomcat5</b> is a service application for running Tomcat5 as NT service.
+</p>
+</section>
+<section name="Tomcat5w monitor application">
+<p>
+    <b>Tomcat5w</b> is a GUI application for monitoring and configuring Tomcat
+    services.
+</p>    
+    <p>The available command line options are:</p>
+<p> 
+    <table>
+    <tr><th>//ES//</th>
+        <td>Edit service configuration</td>
+        <td>This is the default operation. It is called if the no option is
+            provided but the executable is renamed to <b>servicenameW.exe</b></td>
+    </tr>
+    <tr><th>//MS//</th>
+        <td>Monitor service</td>
+        <td>Put the icon in the system try</td>
+    </tr>
+    </table>
+</p>
+</section>
+<section name="Command line arguments">
+<p>
+    Each command line directive is in the form of <b>//XX//ServiceName</b>
+</p>
+    <p>The available command line options are:</p>
+<p> 
+    <table>
+    <tr><th>//TS//</th>
+        <td>Run the service as console application</td>
+        <td>This is the default operation. It is called if the no option is
+            provided. The ServiceName is the name of the executable without
+            exe sufix, meaning Tomcat5</td>
+    </tr>
+    <tr><th>//RS//</th>
+        <td>Run the service</td>
+        <td>Called only from ServiceManager</td>
+    </tr>
+    <tr><th>//SS//</th>
+        <td>Stop the service</td>
+        <td></td>
+    </tr>
+    <tr><th>//US//</th>
+        <td>Update service parameters</td>
+        <td></td>
+    </tr>
+    <tr><th>//IS//</th>
+        <td>Install service</td>
+        <td></td>
+    </tr>
+    <tr><th>//DS//</th>
+        <td>Delete service</td>
+        <td>Stops the service if running</td>
+    </tr>        
+    </table>
+</p> 
+</section>
+<section name="Command line parameters">
+<p>
+    Each command parameter is prefixed with <b>--</b>.
+    If the command line is prefixed with <b>++</b> then it's value will
+    be appended to the existing option.
+    If the environment variable with the same name as command line parameter but
+    prefixed with <code>PR_</code> exists it will take precedence.
+    For example:
+<source>set PR_CLASSPATH=xx.jar</source>
+</p>    
+<p>is equivalent to providing
+<source>--Classpath=xx.jar</source>
+</p>
+<p> as command line parameter.</p>
+<p> 
+    <table>
+    <tr>
+    <th>ParameterName</th>
+	<th>Default</th>
+	<th>Description</th>
+	</tr> 
+    <tr>
+    <td>--Description</td>
+    <td></td>
+    <td>Service name description (maximum 1024 characters)</td>
+    </tr>
+    <tr>
+    <td>--DisplayName</td>
+    <td>ServiceName</td>
+    <td>Service display name</td>
+    </tr>
+    <tr>
+    <td>--Install</td>
+    <td>procrun.exe //RS//ServiceName</td>
+    <td>Install image</td>
+    </tr>
+    <tr>
+    <td>--Startup</td>
+    <td>manual</td>
+    <td>Service startup mode can be either <b>auto</b> or <b>manual</b></td>
+    </tr>
+    <tr>
+    <td>--DependsOn</td>
+    <td></td>
+    <td>List of services that this service depend on. Dependent services
+        are separated using either <b>#</b> or <b>;</b> characters</td>
+    </tr>
+    <tr>
+    <td>--Environment</td>
+    <td></td>
+    <td>List of environment variables that will be provided to the service
+        in the form <b>key=value</b>. They are separated using either
+        <b>#</b> or <b>;</b> characters</td>
+    </tr>
+    <tr>
+    <td>--User</td>
+    <td></td>
+    <td>User account used for running executable. It is used only for
+    	StartMode <b>java</b> or <b>exe</b> and enables running applications
+    	as service under account without LogonAsService privilege.</td>
+    </tr>
+    <tr>
+    <td>--Password</td>
+    <td></td>
+    <td>Password for user account set by --User parameter</td>
+    </tr>
+    <tr>
+    <td>--JavaHome</td>
+    <td>JAVA_HOME</td>
+    <td>Set a different JAVA_HOME then defined by JAVA_HOME environment
+        variable</td>
+    </tr>
+    <tr>
+    <td>--Jvm</td>
+    <td>auto</td>
+    <td>Use either <b>auto</b> or specify the full path to the <b>jvm.dll</b>.
+        You can use the environment variable expansion here.</td>
+    </tr>
+    <tr>
+    <td>--JvmOptions</td>
+    <td>-Xrs</td>
+    <td>List of options in the form of <b>-D</b> or <b>-X</b> that will be
+        passed to the JVM. The options are separated using either
+        <b>#</b> or <b>;</b> characters.</td>
+    </tr>
+    <tr>
+    <td>--Classpath</td>
+    <td></td>
+    <td>Set the Java classpath</td>
+    </tr>
+    <tr>
+    <td>--JvmMs</td>
+    <td></td>
+    <td>Initial memory pool size in MB</td>
+    </tr>
+    <tr>
+    <td>--JvmMx</td>
+    <td></td>
+    <td>Maximum memory pool size in MB</td>
+    </tr>
+    <tr>
+    <td>--JvmSs</td>
+    <td></td>
+    <td>Thread stack size in KB</td>
+    </tr>
+    <tr>
+    <tr>
+    <td>--StartImage</td>
+    <td></td>
+    <td>Executable that will be run.</td>
+    </tr>
+    <tr>
+    <td>--StartPath</td>
+    <td></td>
+    <td>Working path for the start image executable.</td>
+    </tr>
+    <tr>
+    <td>--StartClass</td>
+    <td></td>
+    <td>Class that will be used for startup.</td>
+    </tr>
+    <tr>
+    <td>--StartParams</td>
+    <td></td>
+    <td>List of parameters that will be passed to either StartImage or
+        StartClass. Parameters are separated using either <b>#</b> or
+        <b>;</b> character.</td>
+    </tr>
+    <tr>
+    <td>--StartMethod</td>
+    <td>Main</td>
+    <td>Method name if differs then main</td>
+    </tr>
+    <tr>
+    <td>--StartMode</td>
+    <td>executable</td>
+    <td>Can one of <b>jvm</b> <b>java</b> or <b>exe</b></td>
+    </tr>
+    <td>--StopImage</td>
+    <td></td>
+    <td>Executable that will be run on Stop service signal.</td>
+    </tr>
+    <tr>
+    <td>--StopPath</td>
+    <td></td>
+    <td>Working path for the stop image executable.</td>
+    </tr>
+    <tr>
+    <td>--StopClass</td>
+    <td></td>
+    <td>Class that will be used on Stop service signal.</td>
+    </tr>
+    <tr>
+    <td>--StopParams</td>
+    <td></td>
+    <td>List of parameters that will be passed to either StopImage or
+        StopClass. Parameters are separated using either <b>#</b> or
+        <b>;</b> character.</td>
+    </tr>
+    <tr>
+    <td>--StopMethod</td>
+    <td>Main</td>
+    <td>Method name if differs then main</td>
+    </tr>
+    <tr>
+    <td>--StopMode</td>
+    <td>executable</td>
+    <td>Can one of <b>jvm</b> <b>java</b> or <b>exe</b></td>
+    </tr>
+    <tr>
+    <td>--StopTimeout</td>
+    <td>No Timeout</td>
+    <td>Defines the timeout in seconds that procrun waits for service to
+        exit gracefully.</td>
+    </tr>
+    <tr>
+    <td>--LogPath</td>
+    <td>working path</td>
+    <td>Defines the path for logging</td>
+    </tr>
+    <tr>
+    <td>--LogPrefix</td>
+    <td>jakarta_service</td>
+    <td>Defines the service log filename</td>
+    </tr>
+    <tr>
+    <td>--LogLevel</td>
+    <td>INFO</td>
+    <td>Defines the logging level and can be either <b>error</b>,
+        <b>info</b>, <b>warn</b> or <b>debug</b></td>
+    </tr>
+    <tr>
+    <td>--StdOutput</td>
+    <td></td>
+    <td>Redirected stdout filename</td>
+    </tr>
+    <tr>
+    <td>--StdError</td>
+    <td></td>
+    <td>Redirected stderr filename</td>
+    </tr>    
+    </table>
+</p> 
+</section>
+<section name="Installing services">
+<p>
+The safest way to manually install the service is to use the provided <b>service.bat</b> script.
+</p>
+<p>
+<source>
+Install the service named 'Tomcat5'
+C:\> service.bat install
+</source>
+</p>
+<p>
+If using tomcat5.exe, you need to use the <b>//IS//</b> parameter.
+</p>
+<p>
+<source>
+Install the service named 'Tomcat5'
+C:\> tomcat5 //IS//Tomcat5 --DisplayName="Apache Tomcat 5" \
+C:\> --Install="C:\Program Files\Tomcat\bin\tomcat5.exe" --Jvm=auto \
+C:\> --StartMode=jvm --StopMode=jvm \
+C:\> --StartClass=org.apache.catalina.startup.Bootstrap --StartParams=start \
+C:\> --StopClass=org.apache.catalina.startup.Bootstrap --StopParams=stop
+</source>
+</p>
+</section>
+<section name="Updating services">
+<p>
+To update the service parameters, you need to use the <b>//US//</b> parameter.
+</p>
+<p>
+<source>
+Update the service named 'Tomcat5
+C:\> tomcat5 //US//Tomcat5 --Description="Apache Tomcat Server - http://jakarta.apache.org/tomcat " \
+C:\> --Startup=auto --Classpath=%JAVA_HOME%\lib\tools.jar;%CATALINA_HOME%\bin\bootstrap.jar
+</source>
+</p>
+</section>
+<section name="Removing services">
+<p>
+To remove the service, you need to use the <b>//DS//</b> parameter.<br/>
+If the service is running it will be stopped and then deleted.
+</p>
+<p>
+<source>
+Remove the service named 'Tomcat5'
+C:\> tomcat5 //DS//Tomcat5
+</source>
+</p>
+</section>
+<section name="Debugging services">
+<p>
+To run the service in console mode, you need to use the <b>//TS//</b> parameter.
+The service shutdown can be initiated by pressing <b>CTRL+C</b> or
+<b>CTRL+BREAK</b>.
+If you rename the tomcat5.exe to testservice.exe then you can just execute the
+testservice.exe and this command mode will be executed by default.
+</p>
+<p>
+<source>
+Run the service named 'Tomcat5' in console mode
+C:\> tomcat5 //TS//Tomcat5 [additional arguments]
+Or simply execute:
+C:\> tomcat5
+</source>
+</p>
+</section>
+</body>
+</document> 
diff --git a/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/Constants.java b/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/Constants.java
new file mode 100644
index 0000000..17e02d6
--- /dev/null
+++ b/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/Constants.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.hostmanager;
+
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.hostmanager";
+
+    public static final String HTML_HEADER_SECTION =
+        "<html>\n" +
+        "<head>\n" +
+        "<style>\n" +
+        org.apache.catalina.util.TomcatCSS.TOMCAT_CSS +
+        "  table {\n" +
+        "    width: 100%;\n" +
+        "  }\n" +
+        "  td.page-title {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-weight: bold;\n" +
+        "    background: white;\n" +
+        "    color: black;\n" +
+        "  }\n" +
+        "  td.title {\n" +
+        "    text-align: left;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-style:italic;\n" +
+        "    font-weight: bold;\n" +
+        "    background: #D2A41C;\n" +
+        "  }\n" +
+        "  td.header-left {\n" +
+        "    text-align: left;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-weight: bold;\n" +
+        "    background: #FFDC75;\n" +
+        "  }\n" +
+        "  td.header-center {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-weight: bold;\n" +
+        "    background: #FFDC75;\n" +
+        "  }\n" +
+        "  td.row-left {\n" +
+        "    text-align: left;\n" +
+        "    vertical-align: middle;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    color: black;\n" +
+        "    background: white;\n" +
+        "  }\n" +
+        "  td.row-center {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: middle;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    color: black;\n" +
+        "    background: white;\n" +
+        "  }\n" +
+        "  td.row-right {\n" +
+        "    text-align: right;\n" +
+        "    vertical-align: middle;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    color: black;\n" +
+        "    background: white;\n" +
+        "  }\n" +
+        "  TH {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-weight: bold;\n" +
+        "    background: #FFDC75;\n" +
+        "  }\n" +
+        "  TD {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: middle;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    color: black;\n" +
+        "    background: white;\n" +
+        "  }\n" +
+        "</style>\n";
+
+    public static final String BODY_HEADER_SECTION =
+        "<title>{0}</title>\n" +
+        "</head>\n" +
+        "\n" +
+        "<body bgcolor=\"#FFFFFF\">\n" +
+        "\n" +
+        "<table cellspacing=\"4\" width=\"100%\" border=\"0\">\n" +
+        " <tr>\n" +
+        "  <td colspan=\"2\">\n" +
+        "   <a href=\"http://jakarta.apache.org/\">\n" +
+        "    <img border=\"0\" alt=\"The Jakarta Project\" align=\"left\"\n" +
+        "         src=\"{0}/images/jakarta-logo.gif\">\n" +
+        "   </a>\n" +
+        "   <a href=\"http://jakarta.apache.org/tomcat/\">\n" +
+        "    <img border=\"0\" alt=\"The Tomcat Servlet/JSP Container\"\n" +
+        "         align=\"right\" src=\"{0}/images/tomcat.gif\">\n" +
+        "   </a>\n" +
+        "  </td>\n" +
+        " </tr>\n" +
+        "</table>\n" +
+        "<hr size=\"1\" noshade=\"noshade\">\n" +
+        "<table cellspacing=\"4\" width=\"100%\" border=\"0\">\n" +
+        " <tr>\n" +
+        "  <td class=\"page-title\" bordercolor=\"#000000\" " +
+        "align=\"left\" nowrap>\n" +
+        "   <font size=\"+2\">{1}</font>\n" +
+        "  </td>\n" +
+        " </tr>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+    public static final String MESSAGE_SECTION =
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        " <tr>\n" +
+        "  <td class=\"row-left\" width=\"10%\">" +
+        "<small><strong>{0}</strong></small>&nbsp;</td>\n" +
+        "  <td class=\"row-left\"><pre>{1}</pre></td>\n" +
+        " </tr>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+    public static final String MANAGER_SECTION =
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td colspan=\"4\" class=\"title\">{0}</td>\n" +
+        "</tr>\n" +
+        " <tr>\n" +
+        "  <td class=\"row-left\"><a href=\"{1}\">{2}</a></td>\n" +
+        "  <td class=\"row-center\"><a href=\"{3}\">{4}</a></td>\n" +
+        "  <td class=\"row-center\"><a href=\"{5}\">{6}</a></td>\n" +
+        "  <td class=\"row-right\"><a href=\"{7}\">{8}</a></td>\n" +
+        " </tr>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+    public static final String SERVER_HEADER_SECTION =
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td colspan=\"6\" class=\"title\">{0}</td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"header-center\"><small>{1}</small></td>\n" +
+        " <td class=\"header-center\"><small>{2}</small></td>\n" +
+        " <td class=\"header-center\"><small>{3}</small></td>\n" +
+        " <td class=\"header-center\"><small>{4}</small></td>\n" +
+        " <td class=\"header-center\"><small>{5}</small></td>\n" +
+        " <td class=\"header-center\"><small>{6}</small></td>\n" +
+        "</tr>\n";
+
+    public static final String SERVER_ROW_SECTION =
+        "<tr>\n" +
+        " <td class=\"row-center\"><small>{0}</small></td>\n" +
+        " <td class=\"row-center\"><small>{1}</small></td>\n" +
+        " <td class=\"row-center\"><small>{2}</small></td>\n" +
+        " <td class=\"row-center\"><small>{3}</small></td>\n" +
+        " <td class=\"row-center\"><small>{4}</small></td>\n" +
+        " <td class=\"row-center\"><small>{5}</small></td>\n" +
+        "</tr>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+    public static final String HTML_TAIL_SECTION =
+        "<hr size=\"1\" noshade=\"noshade\">\n" +
+        "<center><font size=\"-1\" color=\"#525D76\">\n" +
+        " <em>Copyright &copy; 1999-2005, Apache Software Foundation</em>" +
+        "</font></center>\n" +
+        "\n" +
+        "</body>\n" +
+        "</html>";
+    public static final String CHARSET="utf-8";
+
+    // FIXME need we this?
+    public static final String XML_DECLARATION =
+        "<?xml version=\"1.0\" encoding=\""+CHARSET+"\"?>";
+		
+    public static final String XML_STYLE =
+        "<?xml-stylesheet type=\"text/xsl\" href=\"/manager/xform.xsl\" ?>";
+
+}
+
diff --git a/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/HTMLHostManagerServlet.java b/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/HTMLHostManagerServlet.java
new file mode 100644
index 0000000..80c1965
--- /dev/null
+++ b/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/HTMLHostManagerServlet.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.hostmanager;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Host;
+import org.apache.catalina.util.ServerInfo;
+
+/**
+* Servlet that enables remote management of the virtual hosts deployed
+* on the server.  Normally, this functionality will be protected by a security
+* constraint in the web application deployment descriptor.  However, 
+* this requirement can be relaxed during testing.
+* <p>
+* The difference between the <code>HostManagerServlet</code> and this
+* Servlet is that this Servlet prints out a HTML interface which
+* makes it easier to administrate.
+* <p>
+* However if you use a software that parses the output of
+* <code>HostManagerServlet</code> you won't be able to upgrade
+* to this Servlet since the output are not in the
+* same format as from <code>HostManagerServlet</code>
+*
+* @author Bip Thelin
+* @author Malcolm Edgar
+* @author Glenn L. Nielsen
+* @author Peter Rossbach
+* @version $Revision$, $Date$
+* @see ManagerServlet
+*/
+
+public final class HTMLHostManagerServlet extends HostManagerServlet {
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Identify the request parameters that we need
+        String command = request.getPathInfo();
+
+        String name = request.getParameter("name");
+ 
+        // Prepare our output writer to generate the response message
+        response.setContentType("text/html; charset=" + Constants.CHARSET);
+
+        String message = "";
+        // Process the requested command
+        if (command == null) {
+        } else if (command.equals("/add")) {
+            message = add(request, name);
+        } else if (command.equals("/remove")) {
+            message = remove(name);
+        } else if (command.equals("/list")) {
+        } else if (command.equals("/start")) {
+            message = start(name);
+        } else if (command.equals("/stop")) {
+            message = stop(name);
+        } else {
+            message =
+                sm.getString("hostManagerServlet.unknownCommand", command);
+        }
+
+        list(request, response, message);
+    }
+
+    
+    /**
+     * Add a host using the specified parameters.
+     *
+     * @param name host name
+     */
+    protected String add(HttpServletRequest request,String name) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.add(request,printWriter,name,true);
+
+        return stringWriter.toString();
+    }
+
+
+    /**
+     * Remove the specified host.
+     *
+     * @param writer Writer to render results to
+     * @param name host name
+     */
+    protected String remove(String name) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.remove(printWriter, name);
+
+        return stringWriter.toString();
+    }
+
+    
+    /**
+     * Start the host with the specified name.
+     *
+     * @param name Host name
+     */
+    protected String start(String name) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.start(printWriter, name);
+
+        return stringWriter.toString();
+    }
+
+    
+    /**
+     * Stop the host with the specified name.
+     *
+     * @param name Host name
+     */
+    protected String stop(String name) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.stop(printWriter, name);
+
+        return stringWriter.toString();
+    }
+
+    
+    /**
+     * Render a HTML list of the currently active Contexts in our virtual host,
+     * and memory and server status information.
+     *
+     * @param request The request
+     * @param response The response
+     * @param message a message to display
+     */
+    public void list(HttpServletRequest request,
+                     HttpServletResponse response,
+                     String message) throws IOException {
+
+        PrintWriter writer = response.getWriter();
+
+        // HTML Header Section
+        writer.print(Constants.HTML_HEADER_SECTION);
+
+        // Body Header Section
+        Object[] args = new Object[2];
+        args[0] = request.getContextPath();
+        args[1] = sm.getString("htmlHostManagerServlet.title");
+        writer.print(MessageFormat.format
+                     (Constants.BODY_HEADER_SECTION, args));
+
+        // Message Section
+        args = new Object[3];
+        args[0] = sm.getString("htmlHostManagerServlet.messageLabel");
+        args[1] = (message == null || message.length() == 0) ? "OK" : message;
+        writer.print(MessageFormat.format(Constants.MESSAGE_SECTION, args));
+
+        // Manager Section
+        args = new Object[9];
+        args[0] = sm.getString("htmlHostManagerServlet.manager");
+        args[1] = response.encodeURL(request.getContextPath() + "/html/list");
+        args[2] = sm.getString("htmlHostManagerServlet.list");
+        args[3] = response.encodeURL
+            (request.getContextPath() + "/" +
+             sm.getString("htmlHostManagerServlet.helpHtmlManagerFile"));
+        args[4] = sm.getString("htmlHostManagerServlet.helpHtmlManager");
+        args[5] = response.encodeURL
+            (request.getContextPath() + "/" +
+             sm.getString("htmlHostManagerServlet.helpManagerFile"));
+        args[6] = sm.getString("htmlHostManagerServlet.helpManager");
+        args[7] = response.encodeURL("/manager/status");
+        args[8] = sm.getString("statusServlet.title");
+        writer.print(MessageFormat.format(Constants.MANAGER_SECTION, args));
+
+         // Hosts Header Section
+        args = new Object[3];
+        args[0] = sm.getString("htmlHostManagerServlet.hostName");
+        args[1] = sm.getString("htmlHostManagerServlet.hostAliases");
+        args[2] = sm.getString("htmlHostManagerServlet.hostTasks");
+        writer.print(MessageFormat.format(HOSTS_HEADER_SECTION, args));
+
+        // Hosts Row Section
+        // Create sorted map of host names.
+        Container[] children = engine.findChildren();
+        String hostNames[] = new String[children.length];
+        for (int i = 0; i < children.length; i++)
+            hostNames[i] = children[i].getName();
+
+        TreeMap sortedHostNamesMap = new TreeMap();
+
+        for (int i = 0; i < hostNames.length; i++) {
+            String displayPath = hostNames[i];
+            sortedHostNamesMap.put(displayPath, hostNames[i]);
+        }
+
+        String hostsStart = sm.getString("htmlHostManagerServlet.hostsStart");
+        String hostsStop = sm.getString("htmlHostManagerServlet.hostsStop");
+        String hostsRemove = sm.getString("htmlHostManagerServlet.hostsRemove");
+
+        Iterator iterator = sortedHostNamesMap.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String hostName = (String) entry.getKey();
+            Host host = (Host) engine.findChild(hostName);
+
+            if (host != null ) {
+                args = new Object[2];
+                args[0] = hostName;
+                String[] aliases = host.findAliases();
+                StringBuffer buf = new StringBuffer();
+                if (aliases.length > 0) {
+                    buf.append(aliases[0]);
+                    for (int j = 1; j < aliases.length; j++) {
+                        buf.append(", ").append(aliases[j]);
+                    }
+                }
+                args[1] = buf.toString();
+                writer.print
+                    (MessageFormat.format(HOSTS_ROW_DETAILS_SECTION, args));
+
+                args = new Object[6];
+                args[0] = response.encodeURL
+                    (request.getContextPath() +
+                     "/html/start?name=" + hostName);
+                args[1] = hostsStart;
+                args[2] = response.encodeURL
+                    (request.getContextPath() +
+                     "/html/stop?name=" + hostName);
+                args[3] = hostsStop;
+                args[4] = response.encodeURL
+                    (request.getContextPath() +
+                     "/html/remove?name=" + hostName);
+                args[5] = hostsRemove;
+                if (host == this.host) {
+                    writer.print(MessageFormat.format(
+                        MANAGER_HOST_ROW_BUTTON_SECTION, args));
+                } else {
+                    writer.print(MessageFormat.format(
+                        HOSTS_ROW_BUTTON_SECTION, args));
+                }
+
+            }
+        }
+
+        // Add Section
+        args = new Object[6];
+        args[0] = sm.getString("htmlHostManagerServlet.addTitle");
+        args[1] = sm.getString("htmlHostManagerServlet.addHost");
+        args[2] = response.encodeURL(request.getContextPath() + "/html/add");
+        args[3] = sm.getString("htmlHostManagerServlet.addName");
+        args[4] = sm.getString("htmlHostManagerServlet.addAliases");
+        args[5] = sm.getString("htmlHostManagerServlet.addAppBase");
+        writer.print(MessageFormat.format(ADD_SECTION_START, args));
+ 
+        args = new Object[3];
+        args[0] = sm.getString("htmlHostManagerServlet.addAutoDeploy");
+        args[1] = "autoDeploy";
+        args[2] = "checked";
+        writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+        args[0] = sm.getString("htmlHostManagerServlet.addDeployOnStartup");
+        args[1] = "deployOnStartup";
+        args[2] = "checked";
+        writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+        args[0] = sm.getString("htmlHostManagerServlet.addDeployXML");
+        args[1] = "deployXML";
+        args[2] = "checked";
+        writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+        args[0] = sm.getString("htmlHostManagerServlet.addUnpackWARs");
+        args[1] = "unpackWARs";
+        args[2] = "checked";
+        writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+        args[0] = sm.getString("htmlHostManagerServlet.addXmlNamespaceAware");
+        args[1] = "xmlNamespaceAware";
+        args[2] = "";
+        writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+        args[0] = sm.getString("htmlHostManagerServlet.addXmlValidation");
+        args[1] = "xmlValidation";
+        args[2] = "";
+        writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+
+        
+        args = new Object[1];
+        args[0] = sm.getString("htmlHostManagerServlet.addButton");
+        writer.print(MessageFormat.format(ADD_SECTION_END, args));
+
+        // Server Header Section
+        args = new Object[7];
+        args[0] = sm.getString("htmlHostManagerServlet.serverTitle");
+        args[1] = sm.getString("htmlHostManagerServlet.serverVersion");
+        args[2] = sm.getString("htmlHostManagerServlet.serverJVMVersion");
+        args[3] = sm.getString("htmlHostManagerServlet.serverJVMVendor");
+        args[4] = sm.getString("htmlHostManagerServlet.serverOSName");
+        args[5] = sm.getString("htmlHostManagerServlet.serverOSVersion");
+        args[6] = sm.getString("htmlHostManagerServlet.serverOSArch");
+        writer.print(MessageFormat.format
+                     (Constants.SERVER_HEADER_SECTION, args));
+
+        // Server Row Section
+        args = new Object[6];
+        args[0] = ServerInfo.getServerInfo();
+        args[1] = System.getProperty("java.runtime.version");
+        args[2] = System.getProperty("java.vm.vendor");
+        args[3] = System.getProperty("os.name");
+        args[4] = System.getProperty("os.version");
+        args[5] = System.getProperty("os.arch");
+        writer.print(MessageFormat.format(Constants.SERVER_ROW_SECTION, args));
+
+        // HTML Tail Section
+        writer.print(Constants.HTML_TAIL_SECTION);
+
+        // Finish up the response
+        writer.flush();
+        writer.close();
+    }
+
+    
+    // ------------------------------------------------------ Private Constants
+
+    // These HTML sections are broken in relatively small sections, because of
+    // limited number of subsitutions MessageFormat can process
+    // (maximium of 10).
+
+    private static final String HOSTS_HEADER_SECTION =
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td colspan=\"5\" class=\"title\">{0}</td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"header-left\"><small>{0}</small></td>\n" +
+        " <td class=\"header-center\"><small>{1}</small></td>\n" +
+        " <td class=\"header-center\"><small>{2}</small></td>\n" +
+        "</tr>\n";
+
+    private static final String HOSTS_ROW_DETAILS_SECTION =
+        "<tr>\n" +
+        " <td class=\"row-left\"><small><a href=\"{0}\">{0}</a>" +
+        "</small></td>\n" +
+        " <td class=\"row-center\"><small>{1}</small></td>\n";
+
+    private static final String MANAGER_HOST_ROW_BUTTON_SECTION =
+        " <td class=\"row-left\">\n" +
+        "  <small>\n" +
+        "  &nbsp;{1}&nbsp;\n" +
+        "  &nbsp;{3}&nbsp;\n" +
+        "  &nbsp;{5}&nbsp;\n" +
+        "  </small>\n" +
+        " </td>\n" +
+        "</tr>\n";
+
+    private static final String HOSTS_ROW_BUTTON_SECTION =
+        " <td class=\"row-left\">\n" +
+        "  <small>\n" +
+        "  &nbsp;<a href=\"{0}\" onclick=\"return(confirm('Are you sure?'))\">{1}</a>&nbsp;\n" +
+        "  &nbsp;<a href=\"{2}\" onclick=\"return(confirm('Are you sure?'))\">{3}</a>&nbsp;\n" +
+        "  &nbsp;<a href=\"{4}\" onclick=\"return(confirm('Are you sure?'))\">{5}</a>&nbsp;\n" +
+        "  </small>\n" +
+        " </td>\n" +
+        "</tr>\n";
+
+    private static final String ADD_SECTION_START =
+        "</table>\n" +
+        "<br>\n" +
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\" class=\"title\">{0}</td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\" class=\"header-left\"><small>{1}</small></td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\">\n" +
+        "<form method=\"get\" action=\"{2}\">\n" +
+        "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  <small>{3}</small>\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"text\" name=\"name\" size=\"20\">\n" +
+        " </td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  <small>{4}</small>\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"text\" name=\"aliases\" size=\"64\">\n" +
+        " </td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  <small>{5}</small>\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"text\" name=\"appBase\" size=\"64\">\n" +
+        " </td>\n" +
+        "</tr>\n" ;
+    
+        private static final String ADD_SECTION_BOOLEAN =
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  <small>{0}</small>\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"checkbox\" name=\"{1}\" {2}>\n" +
+        " </td>\n" +
+        "</tr>\n" ;
+        
+        private static final String ADD_SECTION_END =
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  &nbsp;\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"hidden\" name=\"manager\" value=\"true\">\n" +
+        "  <input type=\"submit\" value=\"{0}\">\n" +
+        " </td>\n" +
+        "</tr>\n" +
+         "</table>\n" +
+        "</form>\n" +
+        "</td>\n" +
+        "</tr>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+}
diff --git a/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/HostManagerServlet.java b/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/HostManagerServlet.java
new file mode 100644
index 0000000..01119b3
--- /dev/null
+++ b/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/HostManagerServlet.java
@@ -0,0 +1,682 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.hostmanager;
+
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.StringTokenizer;
+
+import javax.management.MBeanServer;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerServlet;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.startup.HostConfig;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.modeler.Registry;
+
+
+/**
+ * Servlet that enables remote management of the virtual hosts installed
+ * on the server.  Normally, this functionality will be protected by 
+ * a security constraint in the web application deployment descriptor.  
+ * However, this requirement can be relaxed during testing.
+ * <p>
+ * This servlet examines the value returned by <code>getPathInfo()</code>
+ * and related query parameters to determine what action is being requested.
+ * The following actions and parameters (starting after the servlet path)
+ * are supported:
+ * <ul>
+ * <li><b>/add?name={host-name}&aliases={host-aliases}&manager={manager}</b> -
+ *     Create and add a new virtual host. The <code>host-name</code> attribute
+ *     indicates the name of the new host. The <code>host-aliases</code> 
+ *     attribute is a comma separated list of the host alias names. 
+ *     The <code>manager</code> attribute is a boolean value indicating if the
+ *     webapp manager will be installed in the newly created host (optional, 
+ *     true by default).</li>
+ * <li><b>/remove?name={host-name}</b> - Remove a virtual host. 
+ *     The <code>host-name</code> attribute indicates the name of the host.
+ *     </li>
+ * <li><b>/list</b> - List the virtual hosts installed on the server.
+ *     Each host will be listed with the following format 
+ *     <code>host-name#host-aliases</code>.</li>
+ * <li><b>/start?name={host-name}</b> - Start the virtual host.</li>
+ * <li><b>/stop?name={host-name}</b> - Stop the virtual host.</li>
+ * </ul>
+ * <p>
+ * <b>NOTE</b> - Attempting to stop or remove the host containing
+ * this servlet itself will not succeed.  Therefore, this servlet should
+ * generally be deployed in a separate virtual host.
+ * <p>
+ * <b>NOTE</b> - For security reasons, this application will not operate
+ * when accessed via the invoker servlet.  You must explicitly map this servlet
+ * with a servlet mapping, and you will always want to protect it with
+ * appropriate security constraints as well.
+ * <p>
+ * The following servlet initialization parameters are recognized:
+ * <ul>
+ * <li><b>debug</b> - The debugging detail level that controls the amount
+ *     of information that is logged by this servlet.  Default is zero.
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class HostManagerServlet
+    extends HttpServlet implements ContainerServlet {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Path where context descriptors should be deployed.
+     */
+    protected File configBase = null;
+
+
+    /**
+     * The Context container associated with our web application.
+     */
+    protected Context context = null;
+
+
+    /**
+     * The debugging detail level for this servlet.
+     */
+    protected int debug = 1;
+
+
+    /**
+     * The associated host.
+     */
+    protected Host host = null;
+
+    
+    /**
+     * The associated engine.
+     */
+    protected Engine engine = null;
+
+    
+    /**
+     * MBean server.
+     */
+    protected MBeanServer mBeanServer = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The Wrapper container associated with this servlet.
+     */
+    protected Wrapper wrapper = null;
+
+
+    // ----------------------------------------------- ContainerServlet Methods
+
+
+    /**
+     * Return the Wrapper with which we are associated.
+     */
+    public Wrapper getWrapper() {
+
+        return (this.wrapper);
+
+    }
+
+
+    /**
+     * Set the Wrapper with which we are associated.
+     *
+     * @param wrapper The new wrapper
+     */
+    public void setWrapper(Wrapper wrapper) {
+
+        this.wrapper = wrapper;
+        if (wrapper == null) {
+            context = null;
+            host = null;
+            engine = null;
+        } else {
+            context = (Context) wrapper.getParent();
+            host = (Host) context.getParent();
+            engine = (Engine) host.getParent();
+        }
+
+        // Retrieve the MBean server
+        mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+        
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Finalize this servlet.
+     */
+    public void destroy() {
+
+        ;       // No actions necessary
+
+    }
+
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Verify that we were not accessed using the invoker servlet
+        if (request.getAttribute(Globals.INVOKED_ATTR) != null)
+            throw new UnavailableException
+                (sm.getString("hostManagerServlet.cannotInvoke"));
+
+        // Identify the request parameters that we need
+        String command = request.getPathInfo();
+        if (command == null)
+            command = request.getServletPath();
+        String name = request.getParameter("name");
+  
+        // Prepare our output writer to generate the response message
+        response.setContentType("text/plain; charset=" + Constants.CHARSET);
+        PrintWriter writer = response.getWriter();
+
+        // Process the requested command
+        if (command == null) {
+            writer.println(sm.getString("hostManagerServlet.noCommand"));
+        } else if (command.equals("/add")) {
+            add(request, writer, name, false);
+        } else if (command.equals("/remove")) {
+            remove(writer, name);
+        } else if (command.equals("/list")) {
+            list(writer);
+        } else if (command.equals("/start")) {
+            start(writer, name);
+        } else if (command.equals("/stop")) {
+            stop(writer, name);
+        } else {
+            writer.println(sm.getString("hostManagerServlet.unknownCommand",
+                                        command));
+        }
+
+        // Finish up the response
+        writer.flush();
+        writer.close();
+
+    }
+
+
+    /**
+     * Add host with all parameter
+     * @param request
+     * @param writer
+     * @param name
+     */
+    protected void add(HttpServletRequest request, PrintWriter writer, String name, boolean htmlMode ) {
+        String aliases = request.getParameter("aliases");
+        String appBase = request.getParameter("appBase");
+        boolean manager = booleanParameter(request, "manager", true, htmlMode);
+        boolean autoDeploy = booleanParameter(request, "autoDeploy", true, htmlMode);
+        boolean deployOnStartup = booleanParameter(request, "deployOnStartup", true, htmlMode);
+        boolean deployXML = booleanParameter(request, "deployXML", true, htmlMode);
+        boolean unpackWARs = booleanParameter(request, "unpackWARs", true, htmlMode);
+        boolean xmlNamespaceAware = booleanParameter(request, "xmlNamespaceAware", false, htmlMode);
+        boolean xmlValidation = booleanParameter(request, "xmlValidation", false, htmlMode);
+        add(writer, name, aliases, appBase, manager,
+            autoDeploy,
+            deployOnStartup,
+            deployXML,                                       
+            unpackWARs,
+            xmlNamespaceAware,
+            xmlValidation);
+    }
+
+
+    /**
+     * extract boolean value from checkbox with default
+     * @param request
+     * @param parameter
+     * @param theDefault
+     * @param htmlMode
+     * @return
+     */
+    protected boolean booleanParameter(HttpServletRequest request,
+            String parameter, boolean theDefault, boolean htmlMode) {
+        String value = request.getParameter(parameter);
+        boolean booleanValue = theDefault;
+        if (value != null) {
+            if (htmlMode) {
+                if (value.equals("on")) {
+                    booleanValue = true;
+                }
+            } else if (theDefault) {
+                if (value.equals("false")) {
+                    booleanValue = false;
+                }
+            } else if (value.equals("true")) {
+                booleanValue = true;
+            }
+        } else if (htmlMode)
+            booleanValue = false;
+        return booleanValue;
+    }
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init() throws ServletException {
+
+        // Ensure that our ContainerServlet properties have been set
+        if ((wrapper == null) || (context == null))
+            throw new UnavailableException
+                (sm.getString("hostManagerServlet.noWrapper"));
+
+        // Verify that we were not accessed using the invoker servlet
+        String servletName = getServletConfig().getServletName();
+        if (servletName == null)
+            servletName = "";
+        if (servletName.startsWith("org.apache.catalina.INVOKER."))
+            throw new UnavailableException
+                (sm.getString("hostManagerServlet.cannotInvoke"));
+
+        // Set our properties from the initialization parameters
+        String value = null;
+        try {
+            value = getServletConfig().getInitParameter("debug");
+            debug = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+
+    }
+
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Add a host using the specified parameters.
+     *
+     * @param writer Writer to render results to
+     * @param name host name
+     * @param aliases comma separated alias list
+     * @param appBase application base for the host
+     * @param manager should the manager webapp be deployed to the new host ?
+     */
+    protected synchronized void add
+        (PrintWriter writer, String name, String aliases, String appBase, 
+         boolean manager,
+         boolean autoDeploy,
+         boolean deployOnStartup,
+         boolean deployXML,                                       
+         boolean unpackWARs,
+         boolean xmlNamespaceAware,
+         boolean xmlValidation) {
+        if (debug >= 1) {
+            log("add: Adding host '" + name + "'");
+        }
+
+        // Validate the requested host name
+        if ((name == null) || name.length() == 0) {
+            writer.println(sm.getString("hostManagerServlet.invalidHostName", name));
+            return;
+        }
+
+        // Check if host already exists
+        if (engine.findChild(name) != null) {
+            writer.println
+                (sm.getString("hostManagerServlet.alreadyHost", name));
+            return;
+        }
+
+        // Validate and create appBase
+        File appBaseFile = null;
+        if (appBase == null || appBase.length() == 0) {
+            appBase = name;
+        }
+        File file = new File(appBase);
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"), appBase);
+        try {
+            appBaseFile = file.getCanonicalFile();
+        } catch (IOException e) {
+            appBaseFile = file;
+        }
+        if (!appBaseFile.exists()) {
+            appBaseFile.mkdirs();
+        }
+        
+        // Create base for config files
+        File configBaseFile = getConfigBase(name);
+        
+        // Copy manager.xml if requested
+        if (manager) {
+            InputStream is = null;
+            OutputStream os = null;
+            try {
+                is = getServletContext().getResourceAsStream("/manager.xml");
+                os = new FileOutputStream(new File(configBaseFile, "manager.xml"));
+                byte buffer[] = new byte[512];
+                int len = buffer.length;
+                while (true) {
+                    len = is.read(buffer);
+                    if (len == -1)
+                        break;
+                    os.write(buffer, 0, len);
+                }
+            } catch (IOException e) {
+                writer.println
+                    (sm.getString("hostManagerServlet.managerXml"));
+                return;
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException e) {
+                    }
+                }
+                if (os != null) {
+                    try {
+                        os.close();
+                    } catch (IOException e) {
+                    }
+                }
+            }
+        }
+        
+        StandardHost host = new StandardHost();
+        host.setAppBase(appBase);
+        host.setName(name);
+
+        host.addLifecycleListener(new HostConfig());
+
+        // Add host aliases
+        if ((aliases != null) && !("".equals(aliases))) {
+            StringTokenizer tok = new StringTokenizer(aliases, ",");
+            while (tok.hasMoreTokens()) {
+                host.addAlias(tok.nextToken());
+            }
+        }
+        host.setAutoDeploy(autoDeploy);
+        host.setDeployOnStartup(deployOnStartup);
+        host.setDeployXML(deployXML);
+        host.setUnpackWARs(unpackWARs);
+        host.setXmlNamespaceAware(xmlNamespaceAware);
+        host.setXmlValidation(xmlValidation);
+        
+        // Add new host
+        try {
+            engine.addChild(host);
+        } catch (Exception e) {
+            writer.println(sm.getString("hostManagerServlet.exception",
+                                        e.toString()));
+            return;
+        }
+        
+        host = (StandardHost) engine.findChild(name);
+        if (host != null) {
+            writer.println(sm.getString("hostManagerServlet.add", name));
+        } else {
+            // Something failed
+            writer.println(sm.getString("hostManagerServlet.addFailed", name));
+        }
+        
+    }
+
+
+    /**
+     * Remove the specified host.
+     *
+     * @param writer Writer to render results to
+     * @param name host name
+     */
+    protected synchronized void remove(PrintWriter writer, String name) {
+
+        if (debug >= 1) {
+            log("remove: Removing host '" + name + "'");
+        }
+
+        // Validate the requested host name
+        if ((name == null) || name.length() == 0) {
+            writer.println(sm.getString("hostManagerServlet.invalidHostName", name));
+            return;
+        }
+
+        // Check if host exists
+        if (engine.findChild(name) == null) {
+            writer.println
+                (sm.getString("hostManagerServlet.noHost", name));
+            return;
+        }
+
+        // Prevent removing our own host
+        if (engine.findChild(name) == host) {
+            writer.println
+                (sm.getString("hostManagerServlet.cannotRemoveOwnHost", name));
+            return;
+        }
+
+        // Remove host
+        // Note that the host will not get physically removed
+        try {
+            engine.removeChild(engine.findChild(name));
+        } catch (Exception e) {
+            writer.println(sm.getString("hostManagerServlet.exception",
+                                        e.toString()));
+            return;
+        }
+        
+        Host host = (StandardHost) engine.findChild(name);
+        if (host == null) {
+            writer.println(sm.getString("hostManagerServlet.remove", name));
+        } else {
+            // Something failed
+            writer.println(sm.getString("hostManagerServlet.removeFailed", name));
+        }
+        
+    }
+
+
+    /**
+     * Render a list of the currently active Contexts in our virtual host.
+     *
+     * @param writer Writer to render to
+     */
+    protected void list(PrintWriter writer) {
+
+        if (debug >= 1)
+            log("list: Listing hosts for engine '" 
+                + engine.getName() + "'");
+
+        writer.println(sm.getString("hostManagerServlet.listed",
+                                    engine.getName()));
+        Container[] hosts = engine.findChildren();
+        for (int i = 0; i < hosts.length; i++) {
+            Host host = (Host) hosts[i];
+            String name = host.getName();
+            String[] aliases = host.findAliases();
+            StringBuffer buf = new StringBuffer();
+            if (aliases.length > 0) {
+                buf.append(aliases[0]);
+                for (int j = 1; j < aliases.length; j++) {
+                    buf.append(',').append(aliases[j]);
+                }
+            }
+            writer.println(sm.getString("hostManagerServlet.listitem",
+                                        name, buf.toString()));
+        }
+    }
+
+
+    /**
+     * Start the host with the specified name.
+     *
+     * @param writer Writer to render to
+     * @param name Host name
+     */
+    protected void start(PrintWriter writer, String name) {
+
+        if (debug >= 1)
+            log("start: Starting host with name '" + name + "'");
+
+        // Validate the requested host name
+        if ((name == null) || name.length() == 0) {
+            writer.println(sm.getString("hostManagerServlet.invalidHostName", name));
+            return;
+        }
+
+        // Check if host exists
+        if (engine.findChild(name) == null) {
+            writer.println
+                (sm.getString("hostManagerServlet.noHost", name));
+            return;
+        }
+
+        // Prevent starting our own host
+        if (engine.findChild(name) == host) {
+            writer.println
+                (sm.getString("hostManagerServlet.cannotStartOwnHost", name));
+            return;
+        }
+
+        // Start host
+        try {
+            ((Lifecycle) engine.findChild(name)).start();
+            writer.println
+                (sm.getString("hostManagerServlet.started", name));
+        } catch (Throwable t) {
+            getServletContext().log
+                (sm.getString("hostManagerServlet.startFailed", name), t);
+            writer.println
+                (sm.getString("hostManagerServlet.startFailed", name));
+            writer.println(sm.getString("hostManagerServlet.exception",
+                                        t.toString()));
+            return;
+        }
+        
+    }
+
+
+    /**
+     * Start the host with the specified name.
+     *
+     * @param writer Writer to render to
+     * @param name Host name
+     */
+    protected void stop(PrintWriter writer, String name) {
+
+        if (debug >= 1)
+            log("stop: Stopping host with name '" + name + "'");
+
+        // Validate the requested host name
+        if ((name == null) || name.length() == 0) {
+            writer.println(sm.getString("hostManagerServlet.invalidHostName", name));
+            return;
+        }
+
+        // Check if host exists
+        if (engine.findChild(name) == null) {
+            writer.println
+                (sm.getString("hostManagerServlet.noHost", name));
+            return;
+        }
+
+        // Prevent starting our own host
+        if (engine.findChild(name) == host) {
+            writer.println
+                (sm.getString("hostManagerServlet.cannotStopOwnHost", name));
+            return;
+        }
+
+        // Start host
+        try {
+            ((Lifecycle) engine.findChild(name)).stop();
+            writer.println
+                (sm.getString("hostManagerServlet.stopped", name));
+        } catch (Throwable t) {
+            getServletContext().log
+                (sm.getString("hostManagerServlet.stopFailed", name), t);
+            writer.println
+                (sm.getString("hostManagerServlet.stopFailed", name));
+            writer.println(sm.getString("hostManagerServlet.exception",
+                                        t.toString()));
+            return;
+        }
+        
+    }
+
+
+    // -------------------------------------------------------- Support Methods
+
+
+    /**
+     * Get config base.
+     */
+    protected File getConfigBase(String hostName) {
+        File configBase = 
+            new File(System.getProperty("catalina.base"), "conf");
+        if (!configBase.exists()) {
+            return null;
+        }
+        if (engine != null) {
+            configBase = new File(configBase, engine.getName());
+        }
+        if (host != null) {
+            configBase = new File(configBase, hostName);
+        }
+        configBase.mkdirs();
+        return configBase;
+    }
+
+
+}
diff --git a/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/LocalStrings.properties b/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/LocalStrings.properties
new file mode 100644
index 0000000..ad49f9d
--- /dev/null
+++ b/container/webapps/host-manager/WEB-INF/classes/org/apache/catalina/hostmanager/LocalStrings.properties
@@ -0,0 +1,59 @@
+hostManagerServlet.cannotInvoke=Cannot invoke host manager servlet through invoker
+hostManagerServlet.noCommand=FAIL - No command was specified
+hostManagerServlet.unknownCommand=FAIL - Unknown command {0}
+hostManagerServlet.noWrapper=Container has not called setWrapper() for this servlet
+hostManagerServlet.invalidHostName=FAIL - Invalid host name {0} was specified
+hostManagerServlet.alreadyHost=FAIL - Host already exists with host name {0}
+hostManagerServlet.managerXml=FAIL - Couldn't install manager.xml
+hostManagerServlet.exception=FAIL - Encountered exception {0}
+hostManagerServlet.add=OK - Host {0} added
+hostManagerServlet.addFailed=FAIL - Failed to add host {0}
+hostManagerServlet.cannotRemoveOwnHost=FAIL - Cannot remove own host {0}
+hostManagerServlet.remove=OK - Removed host {0}
+hostManagerServlet.removeFailed=FAIL - Failed to remove host {0}
+hostManagerServlet.listed=OK - Listed hosts
+hostManagerServlet.listitem={0}:{1}
+hostManagerServlet.cannotStartOwnHost=FAIL - Cannot start own host {0}
+hostManagerServlet.started=OK - Host {0} started
+hostManagerServlet.startFailed=FAIL - Failed to start host {0}
+hostManagerServlet.cannotStopOwnHost=FAIL - Cannot stop own host {0}
+hostManagerServlet.stopped=OK - Host {0} stopped
+hostManagerServlet.stopFailed=FAIL - Failed to stop host {0}
+
+htmlHostManagerServlet.title=Tomcat Virtual Host Manager
+htmlHostManagerServlet.messageLabel=Message:
+htmlHostManagerServlet.manager=Host Manager
+htmlHostManagerServlet.list=List Virtual Hosts
+htmlHostManagerServlet.helpHtmlManagerFile=html-host-manager-howto.html
+htmlHostManagerServlet.helpHtmlManager=HTML Host Manager Help
+htmlHostManagerServlet.helpManagerFile=host-manager-howto.html
+htmlHostManagerServlet.helpManager=Host Manager Help
+htmlHostManagerServlet.hostName=Host name
+htmlHostManagerServlet.hostAliases=Host aliases
+htmlHostManagerServlet.hostTasks=Commands
+htmlHostManagerServlet.hostsStart=Start
+htmlHostManagerServlet.hostsStop=Stop
+htmlHostManagerServlet.hostsRemove=Remove
+htmlHostManagerServlet.addTitle=Add Virtual Host
+htmlHostManagerServlet.addHost=Host
+htmlHostManagerServlet.addName=Name:
+htmlHostManagerServlet.addAliases=Aliases:
+htmlHostManagerServlet.addAppBase=App base:
+htmlHostManagerServlet.addManager=Manager:
+htmlHostManagerServlet.addAutoDeploy=AutoDeploy
+htmlHostManagerServlet.addDeployOnStartup=DeployOnStartup
+htmlHostManagerServlet.addDeployXML=DeployXML
+htmlHostManagerServlet.addUnpackWARs=UnpackWARs
+htmlHostManagerServlet.addXmlNamespaceAware=XmlNamespaceAware
+htmlHostManagerServlet.addXmlValidation=XmlValidation
+htmlHostManagerServlet.addButton=Add
+htmlHostManagerServlet.serverTitle=Server Information
+htmlHostManagerServlet.serverVersion=Tomcat Version
+htmlHostManagerServlet.serverJVMVersion=JVM Version
+htmlHostManagerServlet.serverJVMVendor=JVM Vendor
+htmlHostManagerServlet.serverOSName=OS Name
+htmlHostManagerServlet.serverOSVersion=OS Version
+htmlHostManagerServlet.serverOSArch=OS Architecture
+
+statusServlet.title=Server Status
+statusServlet.complete=Complete Server Status
diff --git a/container/webapps/host-manager/WEB-INF/web.xml b/container/webapps/host-manager/WEB-INF/web.xml
new file mode 100644
index 0000000..9c374c4
--- /dev/null
+++ b/container/webapps/host-manager/WEB-INF/web.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!DOCTYPE web-app
+    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+    "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+
+  <display-name>Tomcat Manager Application</display-name>
+  <description>
+    A scriptable management web application for the Tomcat Web Server;
+	Manager lets you view, load/unload/etc particular web applications.
+  </description>
+
+  <!-- Define the Manager Servlet
+       Change servlet-class to: org.apache.catalina.servlets.HTMLManagerServlet
+       to get a Servlet with a more intuitive HTML interface, don't change if you
+       have software that is expected to parse the output from ManagerServlet
+       since they're not compatible.
+   -->
+  <servlet>
+    <servlet-name>HostManager</servlet-name>
+    <servlet-class>org.apache.catalina.hostmanager.HostManagerServlet</servlet-class>
+    <init-param>
+      <param-name>debug</param-name>
+      <param-value>2</param-value>
+    </init-param>
+  </servlet>
+  <servlet>
+    <servlet-name>HTMLHostManager</servlet-name>
+    <servlet-class>org.apache.catalina.hostmanager.HTMLHostManagerServlet</servlet-class>
+    <init-param>
+      <param-name>debug</param-name>
+      <param-value>2</param-value>
+    </init-param>
+  </servlet>
+
+  <!-- Define the Manager Servlet Mapping -->
+  <servlet-mapping>
+    <servlet-name>HostManager</servlet-name>
+    <url-pattern>/list</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>HostManager</servlet-name>
+    <url-pattern>/add</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>HostManager</servlet-name>
+    <url-pattern>/remove</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>HostManager</servlet-name>
+    <url-pattern>/start</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>HostManager</servlet-name>
+    <url-pattern>/stop</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>HTMLHostManager</servlet-name>
+    <url-pattern>/html/*</url-pattern>
+  </servlet-mapping>
+
+  <!-- Define a Security Constraint on this Application -->
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>HTMLHostManager and HostManager commands</web-resource-name>
+      <url-pattern>/html/*</url-pattern>
+      <url-pattern>/list</url-pattern>
+      <url-pattern>/add</url-pattern>
+      <url-pattern>/remove</url-pattern>
+      <url-pattern>/start</url-pattern>
+      <url-pattern>/stop</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+       <!-- NOTE:  This role is not present in the default users file -->
+       <role-name>admin</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <!-- Define the Login Configuration for this Application -->
+  <login-config>
+    <auth-method>BASIC</auth-method>
+    <realm-name>Tomcat Host Manager Application</realm-name>
+  </login-config>
+
+  <!-- Security roles referenced by this web application -->
+  <security-role>
+    <description>
+      The role that is required to log in to the Manager Application
+    </description>
+    <role-name>admin</role-name>
+  </security-role>
+
+</web-app>
diff --git a/container/webapps/host-manager/build.xml b/container/webapps/host-manager/build.xml
new file mode 100644
index 0000000..feaf46a
--- /dev/null
+++ b/container/webapps/host-manager/build.xml
@@ -0,0 +1,126 @@
+<project name="host-manager" default="build-main" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="../../build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="webapps.build"   value="../build"/>
+  <property name="webapps.dist"    value="../dist"/>
+  <property name="webapp.name"     value="host-manager"/>
+
+  <!-- Dependent JARs and files -->
+  <property name="servlet-api.jar" value="${api.home}/jsr154/dist/lib/servlet-api.jar"/>
+  <property name="jsp-api.jar"     value="${api.home}/jsr152/dist/lib/jsp-api.jar"/>
+
+  <!-- Construct Admin classpath -->
+  <path id="host-manager.classpath">
+    <pathelement location="${catalina.deploy}/classes"/>
+    <pathelement location="${commons-fileupload.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>   
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${jsp-api.jar}"/>
+  </path>
+
+
+	  <!-- ======================== BUILD: Copy JARs ========================== -->
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${webapps.build}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/images"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF/classes"/>
+  </target>
+
+
+  <!-- ================ BUILD: Copy Static Files ========================== -->
+  <target name="build-static" depends="build-prepare">
+    <copy todir="${webapps.build}/${webapp.name}">
+      <fileset dir=".">
+        <exclude name="build.*"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <target name="build-main" depends="build-static">
+
+    <!-- Top Level Directory -->
+    <style basedir="../docs"
+           destdir="${webapps.build}/${webapp.name}"
+           extension=".html"
+           style="tomcat-docs.xsl"
+           excludes="build.xml project.xml"
+           includes="host-manager-howto.xml,host-html-manager-howto.xml">
+      <param name="relative-path" expression="."/>
+      <param name="project-menu" expression="nomenu"/>
+      <param name="standalone" expression="standalone"/>
+    </style>
+
+    <!-- Images Subdirectory -->
+    <copy todir="${webapps.build}/${webapp.name}/images">
+      <fileset dir="../docs/images">
+        <exclude name="printer.gif"/>
+      </fileset>
+    </copy>
+
+    <javac   srcdir="WEB-INF/classes" 
+             destdir="${webapps.build}/${webapp.name}/WEB-INF/classes"
+             debug="${compile.debug}" deprecation="${compile.deprecation}"
+             optimize="${compile.optimize}"
+             excludes="**/CVS/**">
+      <classpath refid="host-manager.classpath" />
+    </javac>
+
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean and build host manager webapp"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${webapps.build}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Directories =================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${webapps.dist}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Distribution Files ============ -->
+  <target name="dist" depends="build-main,dist-prepare"
+   description="Create host manager webapp binary distribution">
+      <jar   jarfile="${webapps.dist}/${webapp.name}.war"
+             basedir="${webapps.build}/${webapp.name}" includes="**"/>
+  </target>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <deltree dir="${dist.dir}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
diff --git a/container/webapps/host-manager/host-manager.xml b/container/webapps/host-manager/host-manager.xml
new file mode 100644
index 0000000..931917f
--- /dev/null
+++ b/container/webapps/host-manager/host-manager.xml
@@ -0,0 +1,13 @@
+<!--
+
+    Context configuration file for the Tomcat Host Manager Web App
+
+    $Id$
+
+-->
+
+
+<Context docBase="${catalina.home}/server/webapps/host-manager"
+         privileged="true" antiResourceLocking="false" antiJARLocking="false">
+
+</Context>
diff --git a/container/webapps/host-manager/manager.xml b/container/webapps/host-manager/manager.xml
new file mode 100644
index 0000000..ae8cd6a
--- /dev/null
+++ b/container/webapps/host-manager/manager.xml
@@ -0,0 +1,13 @@
+<!--
+
+    Context configuration file for the Tomcat Manager Web App
+
+    $Id$
+
+-->
+
+
+<Context docBase="${catalina.home}/server/webapps/manager"
+         privileged="true" antiResourceLocking="false" antiJARLocking="false">
+
+</Context>
diff --git a/container/webapps/jmxremote/README b/container/webapps/jmxremote/README
new file mode 100644
index 0000000..e329d78
--- /dev/null
+++ b/container/webapps/jmxremote/README
@@ -0,0 +1,12 @@
+Simple webapp that loads the Jmx remote connector. 
+
+You need to add mx4j-remote.jar to your common loader or use JDK1.5. Any other JMX implementation
+that supports javax.remote should work as well. 
+
+The directory structure is a bit different from manager and the other webapps in catalina. I'm
+using eclipse, set up to use separate output dirs and autocompile. The output dir is set to WEB-INF/classes,
+and I have a symlink to the jmxremote dir in the sources - so basically there is no need to run ant/manually compile, 
+just save the file and reload the app. 
+
+MISSING: user/password, SSL, custom RMI server address - can be easily added using servlet params ( and should be 
+added if this ever gets included in the distro )
diff --git a/container/webapps/jmxremote/WEB-INF/src/org/apache/tomcat/servlets/jmxremote/JmxRemoteServlet.java b/container/webapps/jmxremote/WEB-INF/src/org/apache/tomcat/servlets/jmxremote/JmxRemoteServlet.java
new file mode 100644
index 0000000..63ed082
--- /dev/null
+++ b/container/webapps/jmxremote/WEB-INF/src/org/apache/tomcat/servlets/jmxremote/JmxRemoteServlet.java
@@ -0,0 +1,110 @@
+/*
+ * Created on Jul 14, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.apache.tomcat.servlets.jmxremote;
+
+import java.io.IOException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+// Using o.a.tomcat.servlets because o.a.catalina won't be loaded unless trusted app 
+
+/**
+ * Experimental servlet allowing connectors to be deployed and managed using the 
+ * webapp infrastructure.
+ * 
+ * Connectors can be packaged in a WAR file, deployed and managed using the 
+ * normal tools. Configuration is done using web.xml init-params - while this is 
+ * not as simple as <connector> tags in server.xml, it may be easier to support
+ * in tools and explain to webapp developers.
+ * 
+ * Since webapp class loader is used - start/stop as well as reloading can be 
+ * controlled from the /manager.
+ * 
+ * Issues:
+ *  - may polute the webapps namespace - solution would be to reserve a prefix
+ * or use some invalid/special name.    
+ *
+ * @author Costin Manolache
+ */
+public class JmxRemoteServlet extends HttpServlet {
+    JMXConnectorServer cntorServer = null; 
+    
+    public void init(ServletConfig conf) throws ServletException {
+        // otherwise log doesn't work
+        super.init(conf);
+        
+        MBeanServer mBeanServer = null;
+
+        Registry reg=null;
+        
+        // TODO: use config to get the registry port, url, pass, user
+
+        
+        try {
+            if( reg==null )
+                reg=LocateRegistry.createRegistry(1099);
+        } catch( Throwable t ) {
+            log("Can't start registry - it may be already started: " + t);
+        }
+        
+        try {
+            mBeanServer = null;
+            if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
+                mBeanServer =
+                    (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
+            } else {
+                mBeanServer = MBeanServerFactory.createMBeanServer();
+            }
+        } catch( Throwable t ) {
+            log("Can't get the mbean server " + t);
+            return;
+        }
+        
+        try {
+            JMXServiceURL address = new JMXServiceURL("service:jmx:rmi://rmiHost/jndi/rmi://localhost:1099/jndiPath");
+            cntorServer = 
+                JMXConnectorServerFactory.newJMXConnectorServer(address, null, mBeanServer);
+            cntorServer.start();
+        } catch (Throwable e) {
+            log("Can't register jmx connector ", e);
+        }
+    }
+
+    /** Stop the connector
+     * 
+     */
+    public void destroy() {
+        try {
+            if( cntorServer != null ) cntorServer.stop();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void service(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException
+    {
+        resp.sendError(404);
+    }
+
+    // I don't know why super.log is broken in init 
+//    public void log(String s) {
+//        System.err.println("JMX rem:" + s);
+//    }
+
+}
diff --git a/container/webapps/jmxremote/WEB-INF/web.xml b/container/webapps/jmxremote/WEB-INF/web.xml
new file mode 100755
index 0000000..b3fb9a6
--- /dev/null
+++ b/container/webapps/jmxremote/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+  <display-name>JMX Remote Connector Loader</display-name>
+  <description>
+	Will start Jmx RMI connector if JMX is available.
+  </description>
+
+  <servlet>
+    <servlet-name>JmxRemote</servlet-name>
+    <servlet-class>org.apache.tomcat.servlets.jmxremote.JmxRemoteServlet</servlet-class>
+    <init-param>
+      <param-name>debug</param-name>
+      <param-value>0</param-value>
+    </init-param>
+    <load-on-startup>1</load-on-startup>
+  </servlet>
+
+</web-app>
diff --git a/container/webapps/jmxremote/build.xml b/container/webapps/jmxremote/build.xml
new file mode 100644
index 0000000..9e096b5
--- /dev/null
+++ b/container/webapps/jmxremote/build.xml
@@ -0,0 +1,105 @@
+<project name="manager" default="build-main" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="${user.home}/build.properties"/>
+  <property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="../../build.properties"/>
+  <property file="../../../jakarta-tomcat-5/build.properties"/>
+  <property file="../../../jakarta-tomcat-5/build.properties.default"/>
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="webapps.build"   value="../build"/>
+  <property name="webapps.dist"    value="../dist"/>
+  <property name="webapp.name"     value="jmxremote"/>
+
+  <!-- Dependent JARs and files -->
+  <property name="servlet-api.jar" value="${api.home}/jsr154/dist/lib/servlet-api.jar"/>
+  <property name="jsp-api.jar"     value="${api.home}/jsr152/dist/lib/jsp-api.jar"/>
+
+  <path id="build.classpath">
+    <pathelement location="${catalina.deploy}/classes"/>
+    <pathelement location="${commons-fileupload.jar}"/>
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${jmx-remote.jar}"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${jsp-api.jar}"/>
+  </path>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${webapps.build}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/images"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF/classes"/>
+  </target>
+
+
+  <!-- ================ BUILD: Copy Static Files ========================== -->
+  <target name="build-static" depends="build-prepare">
+    <copy todir="${webapps.build}/${webapp.name}">
+      <fileset dir=".">
+        <exclude name="build.*"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <target name="build-main" depends="build-static">
+
+    <javac   srcdir="WEB-INF/src" 
+             destdir="${webapps.build}/${webapp.name}/WEB-INF/classes"
+             debug="${compile.debug}" deprecation="${compile.deprecation}"
+             optimize="${compile.optimize}"
+             excludes="**/CVS/**">
+      <classpath refid="build.classpath" />
+    </javac>
+
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean and build manager webapp"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${webapps.build}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Directories =================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${webapps.dist}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Distribution Files ============ -->
+  <target name="dist" depends="build-main,dist-prepare"
+   description="Create manager webapp binary distribution">
+      <jar   jarfile="${webapps.dist}/${webapp.name}.war"
+             basedir="${webapps.build}/${webapp.name}" includes="**"/>
+  </target>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <deltree dir="${dist.dir}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/Constants.java b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/Constants.java
new file mode 100644
index 0000000..345e605
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/Constants.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.manager;
+
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.manager";
+
+    public static final String HTML_HEADER_SECTION =
+        "<html>\n" +
+        "<head>\n" +
+        "<style>\n" +
+        org.apache.catalina.util.TomcatCSS.TOMCAT_CSS +
+        "  table {\n" +
+        "    width: 100%;\n" +
+        "  }\n" +
+        "  td.page-title {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-weight: bold;\n" +
+        "    background: white;\n" +
+        "    color: black;\n" +
+        "  }\n" +
+        "  td.title {\n" +
+        "    text-align: left;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-style:italic;\n" +
+        "    font-weight: bold;\n" +
+        "    background: #D2A41C;\n" +
+        "  }\n" +
+        "  td.header-left {\n" +
+        "    text-align: left;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-weight: bold;\n" +
+        "    background: #FFDC75;\n" +
+        "  }\n" +
+        "  td.header-center {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-weight: bold;\n" +
+        "    background: #FFDC75;\n" +
+        "  }\n" +
+        "  td.row-left {\n" +
+        "    text-align: left;\n" +
+        "    vertical-align: middle;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    color: black;\n" +
+        "  }\n" +
+        "  td.row-center {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: middle;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    color: black;\n" +
+        "  }\n" +
+        "  td.row-right {\n" +
+        "    text-align: right;\n" +
+        "    vertical-align: middle;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    color: black;\n" +
+        "  }\n" +
+        "  TH {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: top;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    font-weight: bold;\n" +
+        "    background: #FFDC75;\n" +
+        "  }\n" +
+        "  TD {\n" +
+        "    text-align: center;\n" +
+        "    vertical-align: middle;\n" +
+        "    font-family:sans-serif,Tahoma,Arial;\n" +
+        "    color: black;\n" +
+        "  }\n" +
+        "</style>\n";
+
+    public static final String BODY_HEADER_SECTION =
+        "<title>{0}</title>\n" +
+        "</head>\n" +
+        "\n" +
+        "<body bgcolor=\"#FFFFFF\">\n" +
+        "\n" +
+        "<table cellspacing=\"4\" width=\"100%\" border=\"0\">\n" +
+        " <tr>\n" +
+        "  <td colspan=\"2\">\n" +
+        "   <a href=\"http://jakarta.apache.org/\">\n" +
+        "    <img border=\"0\" alt=\"The Jakarta Project\" align=\"left\"\n" +
+        "         src=\"{0}/images/jakarta-logo.gif\">\n" +
+        "   </a>\n" +
+        "   <a href=\"http://jakarta.apache.org/tomcat/\">\n" +
+        "    <img border=\"0\" alt=\"The Tomcat Servlet/JSP Container\"\n" +
+        "         align=\"right\" src=\"{0}/images/tomcat.gif\">\n" +
+        "   </a>\n" +
+        "  </td>\n" +
+        " </tr>\n" +
+        "</table>\n" +
+        "<hr size=\"1\" noshade=\"noshade\">\n" +
+        "<table cellspacing=\"4\" width=\"100%\" border=\"0\">\n" +
+        " <tr>\n" +
+        "  <td class=\"page-title\" bordercolor=\"#000000\" " +
+        "align=\"left\" nowrap>\n" +
+        "   <font size=\"+2\">{1}</font>\n" +
+        "  </td>\n" +
+        " </tr>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+    public static final String MESSAGE_SECTION =
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        " <tr>\n" +
+        "  <td class=\"row-left\" width=\"10%\">" +
+        "<small><strong>{0}</strong></small>&nbsp;</td>\n" +
+        "  <td class=\"row-left\"><pre>{1}</pre></td>\n" +
+        " </tr>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+    public static final String MANAGER_SECTION =
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td colspan=\"4\" class=\"title\">{0}</td>\n" +
+        "</tr>\n" +
+        " <tr>\n" +
+        "  <td class=\"row-left\"><a href=\"{1}\">{2}</a></td>\n" +
+        "  <td class=\"row-center\"><a href=\"{3}\">{4}</a></td>\n" +
+        "  <td class=\"row-center\"><a href=\"{5}\">{6}</a></td>\n" +
+        "  <td class=\"row-right\"><a href=\"{7}\">{8}</a></td>\n" +
+        " </tr>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+    public static final String SERVER_HEADER_SECTION =
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td colspan=\"6\" class=\"title\">{0}</td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"header-center\"><small>{1}</small></td>\n" +
+        " <td class=\"header-center\"><small>{2}</small></td>\n" +
+        " <td class=\"header-center\"><small>{3}</small></td>\n" +
+        " <td class=\"header-center\"><small>{4}</small></td>\n" +
+        " <td class=\"header-center\"><small>{5}</small></td>\n" +
+        " <td class=\"header-center\"><small>{6}</small></td>\n" +
+        "</tr>\n";
+
+    public static final String SERVER_ROW_SECTION =
+        "<tr>\n" +
+        " <td class=\"row-center\"><small>{0}</small></td>\n" +
+        " <td class=\"row-center\"><small>{1}</small></td>\n" +
+        " <td class=\"row-center\"><small>{2}</small></td>\n" +
+        " <td class=\"row-center\"><small>{3}</small></td>\n" +
+        " <td class=\"row-center\"><small>{4}</small></td>\n" +
+        " <td class=\"row-center\"><small>{5}</small></td>\n" +
+        "</tr>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+    public static final String HTML_TAIL_SECTION =
+        "<hr size=\"1\" noshade=\"noshade\">\n" +
+        "<center><font size=\"-1\" color=\"#525D76\">\n" +
+        " <em>Copyright &copy; 1999-2005, Apache Software Foundation</em>" +
+        "</font></center>\n" +
+        "\n" +
+        "</body>\n" +
+        "</html>";
+    public static final String CHARSET="utf-8";
+
+    public static final String XML_DECLARATION =
+        "<?xml version=\"1.0\" encoding=\""+CHARSET+"\"?>";
+		
+    public static final String XML_STYLE =
+        "<?xml-stylesheet type=\"text/xsl\" href=\"/manager/xform.xsl\" ?>";
+
+}
+
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/HTMLManagerServlet.java b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/HTMLManagerServlet.java
new file mode 100644
index 0000000..30e70b5
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/HTMLManagerServlet.java
@@ -0,0 +1,691 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.manager;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.DiskFileUpload;
+
+/**
+* Servlet that enables remote management of the web applications deployed
+* within the same virtual host as this web application is.  Normally, this
+* functionality will be protected by a security constraint in the web
+* application deployment descriptor.  However, this requirement can be
+* relaxed during testing.
+* <p>
+* The difference between the <code>ManagerServlet</code> and this
+* Servlet is that this Servlet prints out a HTML interface which
+* makes it easier to administrate.
+* <p>
+* However if you use a software that parses the output of
+* <code>ManagerServlet</code you won't be able to upgrade
+* to this Servlet since the output are not in the
+* same format ar from <code>ManagerServlet</code>
+*
+* @author Bip Thelin
+* @author Malcolm Edgar
+* @author Glenn L. Nielsen
+* @version $Revision$, $Date$
+* @see ManagerServlet
+*/
+
+public final class HTMLManagerServlet extends ManagerServlet {
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Identify the request parameters that we need
+        String command = request.getPathInfo();
+
+        String path = request.getParameter("path");
+        String deployPath = request.getParameter("deployPath");
+        String deployConfig = request.getParameter("deployConfig");
+        String deployWar = request.getParameter("deployWar");
+
+        // Prepare our output writer to generate the response message
+        response.setContentType("text/html; charset=" + Constants.CHARSET);
+
+        String message = "";
+        // Process the requested command
+        if (command == null || command.equals("/")) {
+        } else if (command.equals("/deploy")) {
+            message = deployInternal(deployConfig, deployPath, deployWar);
+        } else if (command.equals("/list")) {
+        } else if (command.equals("/reload")) {
+            message = reload(path);
+        } else if (command.equals("/undeploy")) {
+            message = undeploy(path);
+        } else if (command.equals("/sessions")) {
+            message = sessions(path);
+        } else if (command.equals("/start")) {
+            message = start(path);
+        } else if (command.equals("/stop")) {
+            message = stop(path);
+        } else {
+            message =
+                sm.getString("managerServlet.unknownCommand",
+                             RequestUtil.filter(command));
+        }
+
+        list(request, response, message);
+    }
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doPost(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Identify the request parameters that we need
+        String command = request.getPathInfo();
+
+        if (command == null || !command.equals("/upload")) {
+            doGet(request,response);
+            return;
+        }
+
+        // Prepare our output writer to generate the response message
+        response.setContentType("text/html; charset=" + Constants.CHARSET);
+
+        String message = "";
+
+        // Create a new file upload handler
+        DiskFileUpload upload = new DiskFileUpload();
+
+        // Get the tempdir
+        File tempdir = (File) getServletContext().getAttribute
+            ("javax.servlet.context.tempdir");
+        // Set upload parameters
+        upload.setSizeMax(-1);
+        upload.setRepositoryPath(tempdir.getCanonicalPath());
+    
+        // Parse the request
+        String basename = null;
+        String war = null;
+        FileItem warUpload = null;
+        try {
+            List items = upload.parseRequest(request);
+        
+            // Process the uploaded fields
+            Iterator iter = items.iterator();
+            while (iter.hasNext()) {
+                FileItem item = (FileItem) iter.next();
+        
+                if (!item.isFormField()) {
+                    if (item.getFieldName().equals("deployWar") &&
+                        warUpload == null) {
+                        warUpload = item;
+                    } else {
+                        item.delete();
+                    }
+                }
+            }
+            while (true) {
+                if (warUpload == null) {
+                    message = sm.getString
+                        ("htmlManagerServlet.deployUploadNoFile");
+                    break;
+                }
+                war = warUpload.getName();
+                if (!war.toLowerCase().endsWith(".war")) {
+                    message = sm.getString
+                        ("htmlManagerServlet.deployUploadNotWar",war);
+                    break;
+                }
+                // Get the filename if uploaded name includes a path
+                if (war.lastIndexOf('\\') >= 0) {
+                    war = war.substring(war.lastIndexOf('\\') + 1);
+                }
+                if (war.lastIndexOf('/') >= 0) {
+                    war = war.substring(war.lastIndexOf('/') + 1);
+                }
+                // Identify the appBase of the owning Host of this Context
+                // (if any)
+                basename = war.substring(0, war.indexOf(".war"));
+                File file = new File(getAppBase(), war);
+                if (file.exists()) {
+                    message = sm.getString
+                        ("htmlManagerServlet.deployUploadWarExists",war);
+                    break;
+                }
+                String path = null;
+                if (basename.equals("ROOT")) {
+                    path = "";
+                } else {
+                    path = "/" + basename;
+                }
+
+                if (!isServiced(path)) {
+                    addServiced(path);
+                    try {
+                        warUpload.write(file);
+                        // Perform new deployment
+                        check(path);
+                    } finally {
+                        removeServiced(path);
+                    }
+                }
+                break;
+            }
+        } catch(Exception e) {
+            message = sm.getString
+                ("htmlManagerServlet.deployUploadFail", e.getMessage());
+            log(message, e);
+        } finally {
+            if (warUpload != null) {
+                warUpload.delete();
+            }
+            warUpload = null;
+        }
+
+        list(request, response, message);
+    }
+
+    /**
+     * Deploy an application for the specified path from the specified
+     * web application archive.
+     *
+     * @param config URL of the context configuration file to be deployed
+     * @param path Context path of the application to be deployed
+     * @param war URL of the web application archive to be deployed
+     * @return message String
+     */
+    protected String deployInternal(String config, String path, String war) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.deploy(printWriter, config, path, war, false);
+
+        return stringWriter.toString();
+    }
+
+    /**
+     * Render a HTML list of the currently active Contexts in our virtual host,
+     * and memory and server status information.
+     *
+     * @param request The request
+     * @param response The response
+     * @param message a message to display
+     */
+    public void list(HttpServletRequest request,
+                     HttpServletResponse response,
+                     String message) throws IOException {
+
+        if (debug >= 1)
+            log("list: Listing contexts for virtual host '" +
+                host.getName() + "'");
+
+        PrintWriter writer = response.getWriter();
+
+        // HTML Header Section
+        writer.print(Constants.HTML_HEADER_SECTION);
+
+        // Body Header Section
+        Object[] args = new Object[2];
+        args[0] = request.getContextPath();
+        args[1] = sm.getString("htmlManagerServlet.title");
+        writer.print(MessageFormat.format
+                     (Constants.BODY_HEADER_SECTION, args));
+
+        // Message Section
+        args = new Object[3];
+        args[0] = sm.getString("htmlManagerServlet.messageLabel");
+        args[1] = (message == null || message.length() == 0) ? "OK" : message;
+        writer.print(MessageFormat.format(Constants.MESSAGE_SECTION, args));
+
+        // Manager Section
+        args = new Object[9];
+        args[0] = sm.getString("htmlManagerServlet.manager");
+        args[1] = response.encodeURL(request.getContextPath() + "/html/list");
+        args[2] = sm.getString("htmlManagerServlet.list");
+        args[3] = response.encodeURL
+            (request.getContextPath() + "/" +
+             sm.getString("htmlManagerServlet.helpHtmlManagerFile"));
+        args[4] = sm.getString("htmlManagerServlet.helpHtmlManager");
+        args[5] = response.encodeURL
+            (request.getContextPath() + "/" +
+             sm.getString("htmlManagerServlet.helpManagerFile"));
+        args[6] = sm.getString("htmlManagerServlet.helpManager");
+        args[7] = response.encodeURL
+            (request.getContextPath() + "/status");
+        args[8] = sm.getString("statusServlet.title");
+        writer.print(MessageFormat.format(Constants.MANAGER_SECTION, args));
+
+        // Apps Header Section
+        args = new Object[6];
+        args[0] = sm.getString("htmlManagerServlet.appsTitle");
+        args[1] = sm.getString("htmlManagerServlet.appsPath");
+        args[2] = sm.getString("htmlManagerServlet.appsName");
+        args[3] = sm.getString("htmlManagerServlet.appsAvailable");
+        args[4] = sm.getString("htmlManagerServlet.appsSessions");
+        args[5] = sm.getString("htmlManagerServlet.appsTasks");
+        writer.print(MessageFormat.format(APPS_HEADER_SECTION, args));
+
+        // Apps Row Section
+        // Create sorted map of deployed applications context paths.
+        Container children[] = host.findChildren();
+        String contextPaths[] = new String[children.length];
+        for (int i = 0; i < children.length; i++)
+            contextPaths[i] = children[i].getName();
+
+        TreeMap sortedContextPathsMap = new TreeMap();
+
+        for (int i = 0; i < contextPaths.length; i++) {
+            String displayPath = contextPaths[i];
+            sortedContextPathsMap.put(displayPath, contextPaths[i]);
+        }
+
+        String appsStart = sm.getString("htmlManagerServlet.appsStart");
+        String appsStop = sm.getString("htmlManagerServlet.appsStop");
+        String appsReload = sm.getString("htmlManagerServlet.appsReload");
+        String appsUndeploy = sm.getString("htmlManagerServlet.appsUndeploy");
+
+        Iterator iterator = sortedContextPathsMap.entrySet().iterator();
+        boolean isHighlighted = true;
+        String highlightColor = null;
+
+        while (iterator.hasNext()) {
+            // Bugzilla 34818, alternating row colors
+            isHighlighted = !isHighlighted;
+            if(isHighlighted) {
+                highlightColor = "#C3F3C3";
+            } else {
+                highlightColor = "#FFFFFF";
+            }
+
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String displayPath = (String) entry.getKey();
+            String contextPath = (String) entry.getKey();
+            Context context = (Context) host.findChild(contextPath);
+            if (displayPath.equals("")) {
+                displayPath = "/";
+            }
+
+            if (context != null ) {
+                args = new Object[6];
+                args[0] = displayPath;
+                args[1] = context.getDisplayName();
+                if (args[1] == null) {
+                    args[1] = "&nbsp;";
+                }
+                args[2] = new Boolean(context.getAvailable());
+                args[3] = response.encodeURL
+                    (request.getContextPath() +
+                     "/html/sessions?path=" + displayPath);
+                if (context.getManager() != null) {
+                    args[4] = new Integer
+                        (context.getManager().getActiveSessions());
+                } else {
+                    args[4] = new Integer(0);
+                }
+
+                args[5] = highlightColor;
+
+                writer.print
+                    (MessageFormat.format(APPS_ROW_DETAILS_SECTION, args));
+
+                args = new Object[9];
+                args[0] = response.encodeURL
+                    (request.getContextPath() +
+                     "/html/start?path=" + displayPath);
+                args[1] = appsStart;
+                args[2] = response.encodeURL
+                    (request.getContextPath() +
+                     "/html/stop?path=" + displayPath);
+                args[3] = appsStop;
+                args[4] = response.encodeURL
+                    (request.getContextPath() +
+                     "/html/reload?path=" + displayPath);
+                args[5] = appsReload;
+                args[6] = response.encodeURL
+                    (request.getContextPath() +
+                     "/html/undeploy?path=" + displayPath);
+                args[7] = appsUndeploy;
+                
+                args[8] = highlightColor;
+
+                if (context.getPath().equals(this.context.getPath())) {
+                    writer.print(MessageFormat.format(
+                        MANAGER_APP_ROW_BUTTON_SECTION, args));
+                } else if (context.getAvailable()) {
+                    writer.print(MessageFormat.format(
+                        STARTED_APPS_ROW_BUTTON_SECTION, args));
+                } else {
+                    writer.print(MessageFormat.format(
+                        STOPPED_APPS_ROW_BUTTON_SECTION, args));
+                }
+
+            }
+        }
+
+        // Deploy Section
+        args = new Object[7];
+        args[0] = sm.getString("htmlManagerServlet.deployTitle");
+        args[1] = sm.getString("htmlManagerServlet.deployServer");
+        args[2] = response.encodeURL(request.getContextPath() + "/html/deploy");
+        args[3] = sm.getString("htmlManagerServlet.deployPath");
+        args[4] = sm.getString("htmlManagerServlet.deployConfig");
+        args[5] = sm.getString("htmlManagerServlet.deployWar");
+        args[6] = sm.getString("htmlManagerServlet.deployButton");
+        writer.print(MessageFormat.format(DEPLOY_SECTION, args));
+
+        args = new Object[4];
+        args[0] = sm.getString("htmlManagerServlet.deployUpload");
+        args[1] = response.encodeURL(request.getContextPath() + "/html/upload");
+        args[2] = sm.getString("htmlManagerServlet.deployUploadFile");
+        args[3] = sm.getString("htmlManagerServlet.deployButton");
+        writer.print(MessageFormat.format(UPLOAD_SECTION, args));
+
+        // Server Header Section
+        args = new Object[7];
+        args[0] = sm.getString("htmlManagerServlet.serverTitle");
+        args[1] = sm.getString("htmlManagerServlet.serverVersion");
+        args[2] = sm.getString("htmlManagerServlet.serverJVMVersion");
+        args[3] = sm.getString("htmlManagerServlet.serverJVMVendor");
+        args[4] = sm.getString("htmlManagerServlet.serverOSName");
+        args[5] = sm.getString("htmlManagerServlet.serverOSVersion");
+        args[6] = sm.getString("htmlManagerServlet.serverOSArch");
+        writer.print(MessageFormat.format
+                     (Constants.SERVER_HEADER_SECTION, args));
+
+        // Server Row Section
+        args = new Object[6];
+        args[0] = ServerInfo.getServerInfo();
+        args[1] = System.getProperty("java.runtime.version");
+        args[2] = System.getProperty("java.vm.vendor");
+        args[3] = System.getProperty("os.name");
+        args[4] = System.getProperty("os.version");
+        args[5] = System.getProperty("os.arch");
+        writer.print(MessageFormat.format(Constants.SERVER_ROW_SECTION, args));
+
+        // HTML Tail Section
+        writer.print(Constants.HTML_TAIL_SECTION);
+
+        // Finish up the response
+        writer.flush();
+        writer.close();
+    }
+
+    /**
+     * Reload the web application at the specified context path.
+     *
+     * @see ManagerServlet#reload(PrintWriter, String)
+     *
+     * @param path Context path of the application to be restarted
+     * @return message String
+     */
+    protected String reload(String path) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.reload(printWriter, path);
+
+        return stringWriter.toString();
+    }
+
+    /**
+     * Undeploy the web application at the specified context path.
+     *
+     * @see ManagerServlet#undeploy(PrintWriter, String)
+     *
+     * @param path Context path of the application to be undeployd
+     * @return message String
+     */
+    protected String undeploy(String path) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.undeploy(printWriter, path);
+
+        return stringWriter.toString();
+    }
+
+    /**
+     * Display session information and invoke list.
+     *
+     * @see ManagerServlet#sessions(PrintWriter, String)
+     *
+     * @param path Context path of the application to list session information
+     * @return message String
+     */
+    public String sessions(String path) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.sessions(printWriter, path);
+
+        return stringWriter.toString();
+    }
+
+    /**
+     * Start the web application at the specified context path.
+     *
+     * @see ManagerServlet#start(PrintWriter, String)
+     *
+     * @param path Context path of the application to be started
+     * @return message String
+     */
+    public String start(String path) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.start(printWriter, path);
+
+        return stringWriter.toString();
+    }
+
+    /**
+     * Stop the web application at the specified context path.
+     *
+     * @see ManagerServlet#stop(PrintWriter, String)
+     *
+     * @param path Context path of the application to be stopped
+     * @return message String
+     */
+    protected String stop(String path) {
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.stop(printWriter, path);
+
+        return stringWriter.toString();
+    }
+
+    // ------------------------------------------------------ Private Constants
+
+    // These HTML sections are broken in relatively small sections, because of
+    // limited number of subsitutions MessageFormat can process
+    // (maximium of 10).
+
+    private static final String APPS_HEADER_SECTION =
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td colspan=\"5\" class=\"title\">{0}</td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"header-left\"><small>{1}</small></td>\n" +
+        " <td class=\"header-left\"><small>{2}</small></td>\n" +
+        " <td class=\"header-center\"><small>{3}</small></td>\n" +
+        " <td class=\"header-center\"><small>{4}</small></td>\n" +
+        " <td class=\"header-center\"><small>{5}</small></td>\n" +
+        "</tr>\n";
+
+    private static final String APPS_ROW_DETAILS_SECTION =
+        "<tr>\n" +
+        " <td class=\"row-left\" bgcolor=\"{5}\"><small><a href=\"{0}\">{0}</a></small></td>\n" +
+        " <td class=\"row-left\" bgcolor=\"{5}\"><small>{1}</small></td>\n" +
+        " <td class=\"row-center\" bgcolor=\"{5}\"><small>{2}</small></td>\n" +
+        " <td class=\"row-center\" bgcolor=\"{5}\"><small><a href=\"{3}\">{4}</a></small></td>\n";
+
+    private static final String MANAGER_APP_ROW_BUTTON_SECTION =
+        " <td class=\"row-left\" bgcolor=\"{8}\">\n" +
+        "  <small>\n" +
+        "  &nbsp;{1}&nbsp;\n" +
+        "  &nbsp;{3}&nbsp;\n" +
+        "  &nbsp;{5}&nbsp;\n" +
+        "  &nbsp;{7}&nbsp;\n" +
+        "  </small>\n" +
+        " </td>\n" +
+        "</tr>\n";
+
+    private static final String STARTED_APPS_ROW_BUTTON_SECTION =
+        " <td class=\"row-left\" bgcolor=\"{8}\">\n" +
+        "  <small>\n" +
+        "  &nbsp;{1}&nbsp;\n" +
+        "  &nbsp;<a href=\"{2}\" onclick=\"return(confirm('''Are you sure?'''))\">{3}</a>&nbsp;\n" +
+        "  &nbsp;<a href=\"{4}\" onclick=\"return(confirm('''Are you sure?'''))\">{5}</a>&nbsp;\n" +
+        "  &nbsp;<a href=\"{6}\" onclick=\"return(confirm('''Are you sure?'''))\">{7}</a>&nbsp;\n" +
+        "  </small>\n" +
+        " </td>\n" +
+        "</tr>\n";
+
+    private static final String STOPPED_APPS_ROW_BUTTON_SECTION =
+        " <td class=\"row-left\" bgcolor=\"{8}\">\n" +
+        "  <small>\n" +
+        "  &nbsp;<a href=\"{0}\" onclick=\"return(confirm('''Are you sure?'''))\">{1}</a>&nbsp;\n" +
+        "  &nbsp;{3}&nbsp;\n" +
+        "  &nbsp;{5}&nbsp;\n" +
+        "  &nbsp;<a href=\"{6}\" onclick=\"return(confirm('''Are you sure?'''))\">{7}</a>&nbsp;\n" +
+        "  </small>\n" +
+        " </td>\n" +
+        "</tr>\n";
+
+    private static final String DEPLOY_SECTION =
+        "</table>\n" +
+        "<br>\n" +
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\" class=\"title\">{0}</td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\" class=\"header-left\"><small>{1}</small></td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\">\n" +
+        "<form method=\"get\" action=\"{2}\">\n" +
+        "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  <small>{3}</small>\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"text\" name=\"deployPath\" size=\"20\">\n" +
+        " </td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  <small>{4}</small>\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"text\" name=\"deployConfig\" size=\"20\">\n" +
+        " </td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  <small>{5}</small>\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"text\" name=\"deployWar\" size=\"40\">\n" +
+        " </td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  &nbsp;\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"submit\" value=\"{6}\">\n" +
+        " </td>\n" +
+        "</tr>\n" +
+        "</table>\n" +
+        "</form>\n" +
+        "</td>\n" +
+        "</tr>\n";
+
+    private static final String UPLOAD_SECTION =
+        "<tr>\n" +
+        " <td colspan=\"2\" class=\"header-left\"><small>{0}</small></td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\">\n" +
+        "<form action=\"{1}\" method=\"post\" " +
+        "enctype=\"multipart/form-data\">\n" +
+        "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  <small>{2}</small>\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"file\" name=\"deployWar\" size=\"40\">\n" +
+        " </td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td class=\"row-right\">\n" +
+        "  &nbsp;\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"submit\" value=\"{3}\">\n" +
+        " </td>\n" +
+        "</tr>\n" +
+        "</table>\n" +
+        "</form>\n" +
+        "</table>\n" +
+        "<br>\n" +
+        "\n";
+
+}
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/JMXProxyServlet.java b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/JMXProxyServlet.java
new file mode 100644
index 0000000..5099c70
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/JMXProxyServlet.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.manager;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.Set;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.MBeanInfo;
+import javax.management.MBeanAttributeInfo;
+import javax.management.Attribute;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.modeler.Registry;
+
+/**
+ * This servlet will dump JMX attributes in a simple format
+ * and implement proxy services for modeler.
+ *
+ * @author Costin Manolache
+ */
+public class JMXProxyServlet extends HttpServlet  {
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * MBean server.
+     */
+    protected MBeanServer mBeanServer = null;
+    protected Registry registry;
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init() throws ServletException {
+        // Retrieve the MBean server
+        registry = Registry.getRegistry(null, null);
+        mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+    }
+
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+
+        response.setContentType("text/plain");
+
+        PrintWriter writer = response.getWriter();
+        String qryString= request.getQueryString();
+
+        if( mBeanServer==null ) {
+            writer.println("Error - No mbean server");
+            return;
+        }
+
+        String qry=request.getParameter("set");
+        if( qry!= null ) {
+            String name=request.getParameter("att");
+            String val=request.getParameter("val");
+
+            setAttribute( writer, qry, name, val );
+            return;
+        }
+        qry=request.getParameter("get");
+        if( qry!= null ) {
+            String name=request.getParameter("att");
+            getAttribute( writer, qry, name );
+            return;
+        }        
+        qry=request.getParameter("qry");
+        if( qry == null ) {
+            qry = "*:*";
+        }
+
+        listBeans( writer, qry );
+
+    }
+
+    public void getAttribute(PrintWriter writer, String onameStr, String att) {
+        try {
+            ObjectName oname = new ObjectName(onameStr);
+            Object value = mBeanServer.getAttribute(oname, att);
+            writer.println("OK - Attribute get '" + onameStr + "' - " + att + "= " + value.toString() );
+        } catch (Exception ex) {
+            writer.println("Error - " + ex.toString());
+        }
+    }
+
+    public void setAttribute( PrintWriter writer,
+                              String onameStr, String att, String val )
+    {
+        try {
+            ObjectName oname=new ObjectName( onameStr );
+            String type=registry.getType(oname, att);
+            Object valueObj=registry.convertValue(type, val );
+            mBeanServer.setAttribute( oname, new Attribute(att, valueObj));
+            writer.println("OK - Attribute set");
+        } catch( Exception ex ) {
+            writer.println("Error - " + ex.toString());
+        }
+    }
+
+    public void listBeans( PrintWriter writer, String qry )
+    {
+
+        Set names = null;
+        try {
+            names=mBeanServer.queryNames(new ObjectName(qry), null);
+            writer.println("OK - Number of results: " + names.size());
+            writer.println();
+        } catch (Exception e) {
+            writer.println("Error - " + e.toString());
+            return;
+        }
+
+        Iterator it=names.iterator();
+        while( it.hasNext()) {
+            ObjectName oname=(ObjectName)it.next();
+            writer.println( "Name: " + oname.toString());
+
+            try {
+                MBeanInfo minfo=mBeanServer.getMBeanInfo(oname);
+                // can't be null - I thinl
+                String code=minfo.getClassName();
+                if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) {
+                    code=(String)mBeanServer.getAttribute(oname, "modelerType");
+                }
+                writer.println("modelerType: " + code);
+
+                MBeanAttributeInfo attrs[]=minfo.getAttributes();
+                Object value=null;
+
+                for( int i=0; i< attrs.length; i++ ) {
+                    if( ! attrs[i].isReadable() ) continue;
+                    if( ! isSupported( attrs[i].getType() )) continue;
+                    String attName=attrs[i].getName();
+                    if( attName.indexOf( "=") >=0 ||
+                            attName.indexOf( ":") >=0 ||
+                            attName.indexOf( " ") >=0 ) {
+                        continue;
+                    }
+            
+                    try {
+                        value=mBeanServer.getAttribute(oname, attName);
+                    } catch( Throwable t) {
+                        System.out.println("Error getting attribute " + oname +
+                                " " + attName + " " + t.toString());
+                        continue;
+                    }
+                    if( value==null ) continue;
+                    if( "modelerType".equals( attName)) continue;
+                    String valueString=value.toString();
+                    writer.println( attName + ": " + escape(valueString));
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+            writer.println();
+        }
+
+    }
+
+    public String escape(String value) {
+        // The only invalid char is \n
+        // We also need to keep the string short and split it with \nSPACE
+        // XXX TODO
+        int idx=value.indexOf( "\n" );
+        if( idx < 0 ) return value;
+
+        int prev=0;
+        StringBuffer sb=new StringBuffer();
+        while( idx >= 0 ) {
+            appendHead(sb, value, prev, idx-1);
+
+            sb.append( "\\n\n ");
+            prev=idx+1;
+            if( idx==value.length() -1 ) break;
+            idx=value.indexOf('\n', idx+1);
+        }
+        if( prev < value.length() )
+            appendHead( sb, value, prev, value.length());
+        return sb.toString();
+    }
+
+    private void appendHead( StringBuffer sb, String value, int start, int end) {
+        int pos=start;
+        while( end-pos > 78 ) {
+            sb.append( value.substring(pos, pos+78));
+            sb.append( "\n ");
+            pos=pos+78;
+        }
+        sb.append( value.substring(pos,end));
+    }
+
+    public boolean isSupported( String type ) {
+        return true;
+    }
+}
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings.properties b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings.properties
new file mode 100644
index 0000000..feeabb1
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings.properties
@@ -0,0 +1,81 @@
+htmlManagerServlet.appsAvailable=Running
+htmlManagerServlet.appsName=Display Name
+htmlManagerServlet.appsPath=Path
+htmlManagerServlet.appsReload=Reload
+htmlManagerServlet.appsUndeploy=Undeploy
+htmlManagerServlet.appsSessions=Sessions
+htmlManagerServlet.appsStart=Start
+htmlManagerServlet.appsStop=Stop
+htmlManagerServlet.appsTasks=Commands
+htmlManagerServlet.appsTitle=Applications
+htmlManagerServlet.helpHtmlManager=HTML Manager Help
+htmlManagerServlet.helpHtmlManagerFile=html-manager-howto.html
+htmlManagerServlet.helpManager=Manager Help
+htmlManagerServlet.helpManagerFile=manager-howto.html
+htmlManagerServlet.deployButton=Deploy
+htmlManagerServlet.deployConfig=XML Configuration file URL:
+htmlManagerServlet.deployPath=Context Path (optional):
+htmlManagerServlet.deployServer=Deploy directory or WAR file located on server
+htmlManagerServlet.deployTitle=Deploy
+htmlManagerServlet.deployUpload=WAR file to deploy
+htmlManagerServlet.deployUploadFail=FAIL - Deploy Upload Failed, Exception: {0}
+htmlManagerServlet.deployUploadFile=Select WAR file to upload
+htmlManagerServlet.deployUploadNotWar=FAIL - File uploaded \"{0}\" must be a .war
+htmlManagerServlet.deployUploadNoFile=FAIL - File upload failed, no file
+htmlManagerServlet.deployUploadWarExists=FAIL - War file \"{0}\" already exists on server
+htmlManagerServlet.deployWar=WAR or Directory URL:
+htmlManagerServlet.list=List Applications
+htmlManagerServlet.manager=Manager
+htmlManagerServlet.messageLabel=Message:
+htmlManagerServlet.serverJVMVendor=JVM Vendor
+htmlManagerServlet.serverJVMVersion=JVM Version
+htmlManagerServlet.serverOSArch=OS Architecture
+htmlManagerServlet.serverOSName=OS Name
+htmlManagerServlet.serverOSVersion=OS Version
+htmlManagerServlet.serverTitle=Server Information
+htmlManagerServlet.serverVersion=Tomcat Version
+htmlManagerServlet.title=Tomcat Web Application Manager
+managerServlet.alreadyContext=FAIL - Application already exists at path {0}
+managerServlet.alreadyDocBase=FAIL - Directory {0} is already in use
+managerServlet.cannotInvoke=Cannot invoke manager servlet through invoker
+managerServlet.configured=OK - Deployed application from context file {0}
+managerServlet.deployed=OK - Deployed application at context path {0}
+managerServlet.deployFailed=FAIL - Failed to deploy application at context path {0}
+managerServlet.exception=FAIL - Encountered exception {0}
+managerServlet.deployed=OK - Deployed application at context path {0}
+managerServlet.invalidPath=FAIL - Invalid context path {0} was specified
+managerServlet.invalidWar=FAIL - Invalid application URL {0} was specified
+managerServlet.listed=OK - Listed applications for virtual host {0}
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=FAIL - Cannot identify application base for context path {0}
+managerServlet.noCommand=FAIL - No command was specified
+managerServlet.noContext=FAIL - No context exists for path {0}
+managerServlet.noDirectory=FAIL - Non-directory document base for path {0}
+managerServlet.noDocBase=FAIL - Cannot undeploy document base for path {0}
+managerServlet.noGlobal=FAIL - No global JNDI resources are available
+managerServlet.noReload=FAIL - Reload not supported on WAR deployed at path {0}
+managerServlet.noRename=FAIL - Cannot deploy uploaded WAR for path {0}
+managerServlet.noRole=FAIL - User does not possess role {0}
+managerServlet.noSelf=FAIL - The manager can not reload, undeploy, stop, or undeploy itself
+managerServlet.noWrapper=Container has not called setWrapper() for this servlet
+managerServlet.reloaded=OK - Reloaded application at context path {0}
+managerServlet.undeployd=OK - Undeployed application at context path {0}
+managerServlet.resourcesAll=OK - Listed global resources of all types
+managerServlet.resourcesType=OK - Listed global resources of type {0}
+managerServlet.rolesList=OK - Listed security roles
+managerServlet.saveFail=FAIL - Configuration save failed: {0}
+managerServlet.saved=OK - Server configuration saved
+managerServlet.savedContext=OK - Context {0} configuration saved
+managerServlet.sessiondefaultmax=Default maximum session inactive interval {0} minutes
+managerServlet.sessiontimeout={0} minutes:{1} sessions
+managerServlet.sessions=OK - Session information for application at context path {0}
+managerServlet.started=OK - Started application at context path {0}
+managerServlet.startFailed=FAIL - Application at context path {0} could not be started
+managerServlet.stopped=OK - Stopped application at context path {0}
+managerServlet.undeployed=OK - Undeployed application at context path {0}
+managerServlet.unknownCommand=FAIL - Unknown command {0}
+managerServlet.userDatabaseError=FAIL - Cannot resolve user database reference
+managerServlet.userDatabaseMissing=FAIL - No user database is available
+
+statusServlet.title=Server Status
+statusServlet.complete=Complete Server Status
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_de.properties b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_de.properties
new file mode 100644
index 0000000..20ada40
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_de.properties
@@ -0,0 +1,78 @@
+htmlManagerServlet.appsAvailable=Verfügbar
+htmlManagerServlet.appsName=Anzeigename
+htmlManagerServlet.appsPath=Kontext Pfad
+htmlManagerServlet.appsReload=Neu laden
+htmlManagerServlet.appsUndeploy=Entfernen
+htmlManagerServlet.appsSessions=Sitzungen
+htmlManagerServlet.appsStart=Start
+htmlManagerServlet.appsStop=Stop
+htmlManagerServlet.appsTasks=Kommandos
+htmlManagerServlet.appsTitle=Anwendungen
+htmlManagerServlet.helpHtmlManager=Hilfeseite HTML Manager (englisch)
+htmlManagerServlet.helpHtmlManagerFile=html-manager-howto.html
+htmlManagerServlet.helpManager=Hilfeseite Manager (englisch)
+htmlManagerServlet.helpManagerFile=manager-howto.html
+htmlManagerServlet.deployButton=Installieren
+htmlManagerServlet.deployConfig=XML Konfigurationsdatei URL:
+htmlManagerServlet.deployPath=Kontext Pfad (optional):
+htmlManagerServlet.deployServer=Verzeichnis oder WAR Datei auf Server installieren
+htmlManagerServlet.deployTitle=Installieren
+htmlManagerServlet.deployUpload=Lokale WAR Datei zur Installation hochladen
+htmlManagerServlet.deployUploadFail=FEHLER - Hochladen zur Installation fehlgeschlagen, Ausnahme: {0}
+htmlManagerServlet.deployUploadFile=WAR Datei auswählen
+htmlManagerServlet.deployUploadNotWar=FEHLER - Hochgeladene Datei \"{0}\" muss ein .war sein
+htmlManagerServlet.deployUploadNoFile=FEHLER - Hochladen fehlgeschlagen, keine Datei vorhanden
+htmlManagerServlet.deployUploadWarExists=FEHLER - WAR Datei \"{0}\" existiert bereits auf Server
+htmlManagerServlet.deployWar=WAR oder Verzeichnis URL:
+htmlManagerServlet.list=Anwendungen auflisten
+htmlManagerServlet.manager=Manager
+htmlManagerServlet.messageLabel=Nachricht:
+htmlManagerServlet.serverJVMVendor=JVM Hersteller
+htmlManagerServlet.serverJVMVersion=JVM Version
+htmlManagerServlet.serverOSArch=OS Architektur
+htmlManagerServlet.serverOSName=OS Name
+htmlManagerServlet.serverOSVersion=OS Version
+htmlManagerServlet.serverTitle=Server Informationen
+htmlManagerServlet.serverVersion=Tomcat Version
+htmlManagerServlet.title=Tomcat Webanwendungs-Manager
+managerServlet.alreadyContext=FEHLER - Anwendung existiert bereits für Kontext Pfad {0}
+managerServlet.alreadyDocBase=FEHLER - Verzeichnis {0} bereits in Benutzung
+managerServlet.cannotInvoke=Kann Manager-Servlet nicht durch Invoker aufrufen
+managerServlet.configured=OK - Anwendung von Kontext-Datei {0} installiert
+managerServlet.deployed=OK - Anwendung mit Kontext Pfad {0} installiert
+managerServlet.exception=FEHLER - Ausnahme aufgetreten {0}
+managerServlet.deployed=OK - Anwendung mit Kontext Pfad {0} installiert
+managerServlet.invalidPath=FEHLER - Ungültiger Kontext Pfad {0} angegeben
+managerServlet.invalidWar=FEHLER - Ungültige URL {0} für Anwendung angegeben
+managerServlet.listed=OK - Auflistung der Webanwendungen für virtuellen Server {0}
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=FEHLER - Kann Verzeichnis für Kontext Pfad {0} nicht finden
+managerServlet.noCommand=FEHLER - Es wurde kein Kommando angegeben
+managerServlet.noContext=FEHLER - Es existiert kein Kontext für Pfad {0}
+managerServlet.noDirectory=FEHLER - Pfad {0} ist kein Verzeichnis
+managerServlet.noDocBase=FEHLER - Kann Webanwendungs-Verzeichnis nicht entfernen für Kontext Pfad {0}
+managerServlet.noGlobal=FEHLER - Keine globalen JNDI Ressourcen verfügbar
+managerServlet.noReload=FEHLER - Neu laden nicht unterstützt für WAR mit Pfad {0}
+managerServlet.noRename=FEHLER - Kann hochgeladenes WAR mit Pfad {0} nicht installieren
+managerServlet.noRole=FEHLER - Benutzer nicht in Rolle {0}
+managerServlet.noSelf=FEHLER - Manager-Kommandos können nicht auf die Manager-Anwendung selbst angewendet werden
+managerServlet.noWrapper=Container hat setWrapper() für dieses Servlet nicht aufgerufen
+managerServlet.reloaded=OK - Anwendung mit Kontext Pfad {0} neu geladen
+managerServlet.undeployd=OK - Anwendung mit Kontext Pfad {0} entfernt
+managerServlet.resourcesAll=OK - Auflistung globaler Ressourcen (alle Typen)
+managerServlet.resourcesType=OK - Auflistung globaler Ressourcen von Typ {0}
+managerServlet.rolesList=OK - Auflistung der Sicherheits-Rollen
+managerServlet.saveFail=FEHLER - Speichern der Konfiguration fehlgeschlagen: {0}
+managerServlet.sessiondefaultmax=Voreingestellter Sitzungsablauf nach maximal {0} Minuten Inaktivität
+managerServlet.sessiontimeout={0} Minuten: {1} Sitzungen
+managerServlet.sessions=OK - Sitzungs-Informationen für Anwendung mit Kontext Pfad {0}
+managerServlet.started=OK - Anwendung mit Kontext Pfad {0} gestartet
+managerServlet.startFailed=FEHLER - Anwendung mit Kontext Pfad {0} konnte nicht gestartet werden
+managerServlet.stopped=OK - Anwendung mit Kontext Pfad {0} gestoppt
+managerServlet.undeployed=OK - Anwendung mit Kontext Pfad {0} entfernt
+managerServlet.unknownCommand=FEHLER - Unbekanntes Kommando {0}
+managerServlet.userDatabaseError=FEHLER - Kann Referenz auf Benutzerdatendank nicht auflösen
+managerServlet.userDatabaseMissing=FEHLER - Keine Benutzerdatenbank vorhanden
+
+statusServlet.title=Server Status
+statusServlet.complete=Ausführlicher Server Status
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_es.properties b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_es.properties
new file mode 100644
index 0000000..72a2c1b
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_es.properties
@@ -0,0 +1,80 @@
+htmlManagerServlet.appsAvailable=Ejecutándose
+htmlManagerServlet.appsName=Nombre a Mostrar
+htmlManagerServlet.appsPath=Trayectoria
+htmlManagerServlet.appsReload=Recargar
+htmlManagerServlet.appsUndeploy=Replegar
+htmlManagerServlet.appsSessions=Sesiones
+htmlManagerServlet.appsStart=Arrancar
+htmlManagerServlet.appsStop=Parar
+htmlManagerServlet.appsTasks=Comandos
+htmlManagerServlet.appsTitle=Aplicaciones
+htmlManagerServlet.helpHtmlManager=Ayuda HTML de Gestor
+htmlManagerServlet.helpHtmlManagerFile=html-manager-howto.html
+htmlManagerServlet.helpManager=Ayuda de Gestor
+htmlManagerServlet.helpManagerFile=manager-howto.html
+htmlManagerServlet.deployButton=Desplegar
+htmlManagerServlet.deployConfig=URL de archivo de Configuración XML:
+htmlManagerServlet.deployPath=Trayectoria de Contexto (opcional):
+htmlManagerServlet.deployServer=Desplegar directorio o archivo WAR localizado en servidor
+htmlManagerServlet.deployTitle=Desplegar
+htmlManagerServlet.deployUpload=Archivo WAR a desplegar
+htmlManagerServlet.deployUploadFail=FALLO - Falló Carga de Despliegue, Excepción: {0}
+htmlManagerServlet.deployUploadFile=Seleccione archivo WAR a cargar
+htmlManagerServlet.deployUploadNotWar=FALLO - El archivo cargado \"{0}\" debe de ser un .war
+htmlManagerServlet.deployUploadNoFile=FALLO - Falló la carga de archivo, no hay archivo
+htmlManagerServlet.deployUploadWarExists=FALLO - El archivo war \"{0}\" ya existe en el servidor
+htmlManagerServlet.deployWar=URL de WAR o Directorio:
+htmlManagerServlet.list=Listar Aplicaciones
+htmlManagerServlet.manager=Gestor
+htmlManagerServlet.messageLabel=Mensaje:
+htmlManagerServlet.serverJVMVendor=Vendedor JVM
+htmlManagerServlet.serverJVMVersion=Versión JVM
+htmlManagerServlet.serverOSArch=Arquitectura de SO
+htmlManagerServlet.serverOSName=Nombre de SO
+htmlManagerServlet.serverOSVersion=Versión de SO
+htmlManagerServlet.serverTitle=Información de Servidor
+htmlManagerServlet.serverVersion=Versión de Tomcat
+htmlManagerServlet.title=Gestor de Aplicaciones Web de Tomcat
+managerServlet.alreadyContext=FALLO - Ya existe la aplicación en la trayectoria {0}
+managerServlet.alreadyDocBase=FALLO - Directorio {0} ya está siendo usado
+managerServlet.cannotInvoke=No puedo invocar servlet de gestor a través de invocador
+managerServlet.configured=OK - Desplegada aplicación desde archivo de contexto {0}
+managerServlet.deployed=OK - Desplegada aplicación en trayectoria de contexto {0}
+managerServlet.exception=FALLO - Encontrada excepción {0}
+managerServlet.deployed=OK - Desplegada aplicación en trayectoria de contexto {0}
+managerServlet.invalidPath=FALLO - Se ha especificado una trayectoria inválida de contexto {0}
+managerServlet.invalidWar=FALLO - Se ha especificado una URL de aplicación inválida {0}
+managerServlet.listed=OK - Aplicaciones listadas para máquinda virutal {0}
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=FALLO - No puedo identificar aplicación base para trayectoria de contexto {0}
+managerServlet.noCommand=FALLO - No se ha especificado comando
+managerServlet.noContext=FALLO - No existe contexto para trayectoria {0}
+managerServlet.noDirectory=FALLO - Documento base No-directorio para trayectoria {0}
+managerServlet.noDocBase=FALLO - No puedo replegar documento base para trayectoria {0}
+managerServlet.noGlobal=FALLO - No hay disponibles recursos globales JNDI 
+managerServlet.noReload=FALLO - Recarga no soportada en WAR desplegado en trayectoria {0}
+managerServlet.noRename=FALLO - No pudeo desplegar WAR cargado para trayectoria {0}
+managerServlet.noRole=FALLO - El usuario no desempeña el papel de {0}
+managerServlet.noSelf=FALLO - El gestor no puede recargarse, replegarse, pararse o replegarse a sí mismo
+managerServlet.noWrapper=El Contenedor no ha llamado a setWrapper() para este servlet
+managerServlet.reloaded=OK - Recargada aplicación en trayectoria de contexto {0}
+managerServlet.undeployd=OK - Replegada aplicación en trayectoria de contexto {0}
+managerServlet.resourcesAll=OK - Listados recursos globales de todos los tipos
+managerServlet.resourcesType=OK - Listados recursos globales de tipo {0}
+managerServlet.rolesList=OK - Listados papeles de seguridad
+managerServlet.saveFail=FAIL - Fallo al guardar la configuración: {0}
+managerServlet.saved=OK - Configuración de Servidor guardada
+managerServlet.savedContext=OK - Configuración de Contexto {0} guardada
+managerServlet.sessiondefaultmax=Intervalo máximo por defecto de sesión inactiva {0} minutos
+managerServlet.sessiontimeout={0} minutos: {1} sesiones
+managerServlet.sessions=OK - Información de sesión para aplicación en trayectoria de contexto {0}
+managerServlet.started=OK - Arrancada aplicación en trayectoria de contexto {0}
+managerServlet.startFailed=FALLO - No se pudo arrancar la aplicación en trayectoria de contexto {0}
+managerServlet.stopped=OK - Parada aplicación en trayectoria de contexto {0}
+managerServlet.undeployed=OK - Replegada aplicacación en trayectoria de contexto {0}
+managerServlet.unknownCommand=FALLO - Comando desconocido {0}
+managerServlet.userDatabaseError=FALLO - No puedo resolver referencia de base de datos de usuario
+managerServlet.userDatabaseMissing=FALLO - No se encuentra disponible base de datos de usuario
+
+statusServlet.title=Estado de Servidor
+statusServlet.complete=Estado Completo de Servidor
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_fr.properties b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_fr.properties
new file mode 100644
index 0000000..4a2e1f9
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_fr.properties
@@ -0,0 +1,64 @@
+htmlManagerServlet.appsAvailable=Fonctionnant
+htmlManagerServlet.appsName=Nom d''affichage
+htmlManagerServlet.appsPath=Chemin
+htmlManagerServlet.appsReload=Recharger
+htmlManagerServlet.appsRemove=Retirer
+htmlManagerServlet.appsSessions=Sessions
+htmlManagerServlet.appsStart=Démarrer
+htmlManagerServlet.appsStop=Arréter
+htmlManagerServlet.appsTitle=Applications
+htmlManagerServlet.installButton=Installation
+htmlManagerServlet.installConfig=URL de configuration:
+htmlManagerServlet.installPath=Chemin:
+htmlManagerServlet.installTitle=Installation
+htmlManagerServlet.installWar=URL du WAR:
+htmlManagerServlet.messageLabel=Message:
+htmlManagerServlet.serverJVMVendor=Fournisseur de la JVM
+htmlManagerServlet.serverJVMVersion=Version de la JVM
+htmlManagerServlet.serverOSArch=Architecture d''OS
+htmlManagerServlet.serverOSName=Nom d''OS
+htmlManagerServlet.serverOSVersion=Version d''OS
+htmlManagerServlet.serverTitle=Serveur
+htmlManagerServlet.serverVersion=Version de serveur
+htmlManagerServlet.title=Gestionnaire d''applications WEB Tomcat
+managerServlet.alreadyContext=ECHEC - l''application existe déjà dans le chemin {0}
+managerServlet.alreadyDocBase=ECHEC - Le répertoire {0} est déjà utilisé
+managerServlet.cannotInvoke=Impossible d''utiliser le gestionnaire de servlet au travers du délégué (invoker)
+managerServlet.configured=OK - Application configurée depuis le fichier contexte {0}
+managerServlet.deployed=OK - Application déployée pour le chemin de contexte {0}
+managerServlet.exception=ECHEC - L''exception {0} a été rencontrée
+managerServlet.installed=OK - Application installée pour le chemin de contexte {0}
+managerServlet.invalidPath=ECHEC - Un chemin de contexte invalide {0} a été spécifié
+managerServlet.invalidWar=ECHEC - Une URL d''application invalide {0} a été spécifiée
+managerServlet.listed=OK - Applications listées pour l''hôte virtuel (virtual host) {0}
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=ECHEC - Impossible d''identifier la base de l''application base pour le chemin de context {0}
+managerServlet.noCommand=ECHEC - Aucune commande n''a été spécifiée
+managerServlet.noContext=ECHEC - Aucune contexte n''existe pour le chemin {0}
+managerServlet.noDirectory=ECHEC - La base de document n''est pas un répertoire pour le chemin {0}
+managerServlet.noDocBase=ECHEC - Impossible de retirer la base de document pour le chemin {0}
+managerServlet.noGlobal=ECHEC - Aucune ressource JNDI globale n''est disponible
+managerServlet.noReload=ECHEC - Rechargement non supporté par le WAR déployé au chemin {0}
+managerServlet.noRename=ECHEC - Impossible de déployer un WAR téléchargé pour le chemin {0}
+managerServlet.noRole=ECHEC - L''utilisateur ne possède pas le rôle {0}
+managerServlet.noSelf=ECHEC - Le gestionnaire ne peut recharger, retirer, arrêter, ou se déployer lui-même
+managerServlet.noWrapper=Le conteneur n''a pas appelé "setWrapper()" pour cette servlet
+managerServlet.reloaded=OK - Application rechargée au chemin de contexte {0}
+managerServlet.removed=OK - Application retirée au chemin de contexte {0}
+managerServlet.resourcesAll=OK - Liste des ressources globales de tout type
+managerServlet.resourcesType=OK - Liste des ressources globales de type {0}
+managerServlet.rolesList=OK - Liste de rôles de securité
+managerServlet.saveFail=ECHEC - La sauvegarde de la configuration a échoué: {0}
+managerServlet.sessiondefaultmax=Interval par défaut de maximum de session inactive {0} minutes
+managerServlet.sessiontimeout={0} minutes:{1} sessions
+managerServlet.sessions=OK - Information de session pour l''application au chemin de contexte {0}
+managerServlet.started=OK - Application démarrée pour le chemin de contexte {0}
+managerServlet.startFailed=ECHEC - L''application pour le chemin de contexte {0} n''a pas puêtredémarrée
+managerServlet.stopped=OK - Application arrétée pour le chemin de contexte {0}
+managerServlet.undeployed=OK - Application non-déployée pour le chemin de contexte {0}
+managerServlet.unknownCommand=ECHEC - Commande inconnue {0}
+managerServlet.userDatabaseError=ECHEC - Impossible de résoudre la base de données utilisateurs deréférence
+managerServlet.userDatabaseMissing=ECHEC - Aucune base de données utilisateurs n''est disponible
+
+statusServlet.title=Etat du serveur
+statusServlet.complete=Etat complet du serveur
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_ja.properties b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_ja.properties
new file mode 100644
index 0000000..ac307de
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/LocalStrings_ja.properties
@@ -0,0 +1,79 @@
+htmlManagerServlet.appsAvailable=\u5b9f\u884c\u4e2d
+htmlManagerServlet.appsName=\u8868\u793a\u540d
+htmlManagerServlet.appsPath=\u30d1\u30b9
+htmlManagerServlet.appsReload=\u518d\u30ed\u30fc\u30c9
+htmlManagerServlet.appsUndeploy=\u914d\u5099\u89e3\u9664
+htmlManagerServlet.appsSessions=\u30bb\u30c3\u30b7\u30e7\u30f3
+htmlManagerServlet.appsStart=\u8d77\u52d5
+htmlManagerServlet.appsStop=\u505c\u6b62
+htmlManagerServlet.appsTasks=\u30b3\u30de\u30f3\u30c9
+htmlManagerServlet.appsTitle=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3
+htmlManagerServlet.helpHtmlManager=HTML\u30de\u30cd\u30fc\u30b8\u30e3\u30d8\u30eb\u30d7
+htmlManagerServlet.helpHtmlManagerFile=html-manager-howto.html
+htmlManagerServlet.helpManager=\u30de\u30cd\u30fc\u30b8\u30e3\u30d8\u30eb\u30d7
+htmlManagerServlet.helpManagerFile=manager-howto.html
+htmlManagerServlet.deployButton=\u914d\u5099
+htmlManagerServlet.deployConfig=XML\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306eURL:
+htmlManagerServlet.deployPath=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 (\u7701\u7565\u53ef):
+htmlManagerServlet.deployServer=\u30b5\u30fc\u30d0\u4e0a\u306eWAR\u30d5\u30a1\u30a4\u30eb\u53c8\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306e\u914d\u5099
+htmlManagerServlet.deployTitle=\u914d\u5099
+htmlManagerServlet.deployUpload=WAR\u30d5\u30a1\u30a4\u30eb\u306e\u914d\u5099
+htmlManagerServlet.deployUploadFail=FAIL - \u914d\u5099\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3001\u4f8b\u5916: {0}
+htmlManagerServlet.deployUploadFile=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308bWAR\u30d5\u30a1\u30a4\u30eb\u306e\u9078\u629e
+htmlManagerServlet.deployUploadNotWar=FAIL - \u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308b\u30d5\u30a1\u30a4\u30eb \"{0}\" \u306fWAR\u30d5\u30a1\u30a4\u30eb\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+htmlManagerServlet.deployUploadNoFile=FAIL - \u30d5\u30a1\u30a4\u30eb\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3001\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+htmlManagerServlet.deployUploadWarExists=FAIL - WAR\u30d5\u30a1\u30a4\u30eb \"{0}\" \u306f\u65e2\u306b\u30b5\u30fc\u30d0\u4e0a\u306b\u5b58\u5728\u3057\u307e\u3059
+htmlManagerServlet.deployWar=WAR\u30d5\u30a1\u30a4\u30eb\u53c8\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306eURL:
+htmlManagerServlet.list=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4e00\u89a7
+htmlManagerServlet.manager=\u30de\u30cd\u30fc\u30b8\u30e3
+htmlManagerServlet.messageLabel=\u30e1\u30c3\u30bb\u30fc\u30b8
+htmlManagerServlet.serverJVMVendor=JVM\u30d9\u30f3\u30c0
+htmlManagerServlet.serverJVMVersion=JVM\u30d0\u30fc\u30b8\u30e7\u30f3
+htmlManagerServlet.serverOSArch=OS\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3
+htmlManagerServlet.serverOSName=OS\u540d
+htmlManagerServlet.serverOSVersion=OS\u30d0\u30fc\u30b8\u30e7\u30f3
+htmlManagerServlet.serverTitle=\u30b5\u30fc\u30d0\u60c5\u5831
+htmlManagerServlet.serverVersion=Tomcat\u30d0\u30fc\u30b8\u30e7\u30f3
+htmlManagerServlet.title=Tomcat Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30de\u30cd\u30fc\u30b8\u30e3
+managerServlet.alreadyContext=FAIL - \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u3001\u65e2\u306b\u30d1\u30b9 {0} \u306b\u5b58\u5728\u3057\u307e\u3059
+managerServlet.alreadyDocBase=FAIL - \u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u306f\u65e2\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059
+managerServlet.cannotInvoke=\u30a4\u30f3\u30dc\u30fc\u30ab\u3067\u30de\u30cd\u30fc\u30b8\u30e3\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
+managerServlet.configured=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb {0} \u304b\u3089\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u307e\u3057\u305f
+managerServlet.deployed=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u3057\u307e\u3057\u305f
+managerServlet.exception=FAIL - \u4f8b\u5916 {0} \u304c\u767a\u751f\u3057\u307e\u3057\u305f
+managerServlet.deployed=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u307e\u3057\u305f
+managerServlet.invalidPath=FAIL - \u7121\u52b9\u306a\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u304c\u6307\u5b9a\u3055\u308c\u307e\u3057\u305f
+managerServlet.invalidWar=FAIL - \u7121\u52b9\u306a\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306eURL {0} \u304c\u6307\u5b9a\u3055\u308c\u307e\u3057\u305f
+managerServlet.listed=OK - \u30d0\u30fc\u30c1\u30e3\u30eb\u30db\u30b9\u30c8 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u4e00\u89a7\u3067\u3059
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=FAIL - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306b\u5bfe\u3057\u3066\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d9\u30fc\u30b9\u3092\u78ba\u8a8d\u3067\u304d\u307e\u305b\u3093
+managerServlet.noCommand=FAIL - \u30b3\u30de\u30f3\u30c9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+managerServlet.noContext=FAIL - \u30d1\u30b9 {0} \u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+managerServlet.noDirectory=FAIL - \u30d1\u30b9 {0} \u306b\u5bfe\u3059\u308b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093
+managerServlet.noDocBase=FAIL - \u30d1\u30b9 {0} \u306b\u5bfe\u3059\u308b\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093
+managerServlet.noGlobal=FAIL - \u30b0\u30ed\u30fc\u30d0\u30eb\u306aJNDI\u30ea\u30bd\u30fc\u30b9\u304c\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+managerServlet.noReload=FAIL - \u30d1\u30b9 {0} \u306b\u914d\u5099\u3055\u308c\u305fWAR\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u518d\u30ed\u30fc\u30c9\u304c\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+managerServlet.noRename=FAIL - \u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3055\u308c\u305fWAR\u30d5\u30a1\u30a4\u30eb\u3092\u30d1\u30b9 {0} \u306b\u914d\u5099\u3067\u304d\u307e\u305b\u3093
+managerServlet.noRole=FAIL - \u30e6\u30fc\u30b6\u306f\u30ed\u30fc\u30eb {0} \u3092\u6301\u3063\u3066\u3044\u307e\u305b\u3093
+managerServlet.noSelf=FAIL - \u30de\u30cd\u30fc\u30b8\u30e3\u81ea\u8eab\u3092\u518d\u30ed\u30fc\u30c9\u3001\u524a\u9664\u3001\u505c\u6b62\u3001\u53c8\u306f\u914d\u5099\u89e3\u9664\u3067\u304d\u307e\u305b\u3093
+managerServlet.noWrapper=\u30b3\u30f3\u30c6\u30ca\u306f\u3053\u306e\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u306b\u5bfe\u3057\u3066\u547c\u3073\u51fa\u3055\u308c\u305fsetWrapper()\u3092\u6301\u3063\u3066\u3044\u307e\u305b\u3093
+managerServlet.reloaded=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u518d\u30ed\u30fc\u30c9\u3057\u307e\u3057\u305f
+managerServlet.undeployd=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u304b\u3089\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u89e3\u9664\u3057\u307e\u3057\u305f
+managerServlet.resourcesAll=OK - \u3059\u3079\u3066\u306e\u30bf\u30a4\u30d7\u306e\u30b0\u30ed\u30fc\u30d0\u30eb\u30ea\u30bd\u30fc\u30b9\u3092\u5217\u6319\u3057\u307e\u3057\u305f
+managerServlet.resourcesType=OK - \u30bf\u30a4\u30d7 {0} \u306e\u30b0\u30ed\u30fc\u30d0\u30eb\u30ea\u30bd\u30fc\u30b9\u3092\u5217\u6319\u3057\u307e\u3057\u305f
+managerServlet.rolesList=OK - \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ed\u30fc\u30eb\u3092\u5217\u6319\u3057\u307e\u3057\u305f
+managerServlet.saveFail=FAIL - \u8a2d\u5b9a\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+managerServlet.saved=OK - \u30b5\u30fc\u30d0\u306e\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u307e\u3057\u305f
+managerServlet.savedContext=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8 {0} \u306e\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u307e\u3057\u305f
+managerServlet.sessiondefaultmax=\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u6700\u5927\u30bb\u30c3\u30b7\u30e7\u30f3\u505c\u6b62\u9593\u9694\u306f{0}\u5206\u3067\u3059
+managerServlet.sessiontimeout={0}\u5206: {1}\u30bb\u30c3\u30b7\u30e7\u30f3
+managerServlet.sessions=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30bb\u30c3\u30b7\u30e7\u30f3\u60c5\u5831\u3067\u3059
+managerServlet.started=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u307e\u3057\u305f
+managerServlet.startFailed=FAIL - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093
+managerServlet.stopped=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u505c\u6b62\u3057\u307e\u3057\u305f
+managerServlet.undeployed=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u89e3\u9664\u3057\u307e\u3057\u305f
+managerServlet.unknownCommand=FAIL - \u672a\u77e5\u306e\u30b3\u30de\u30f3\u30c9 {0} \u3067\u3059
+managerServlet.userDatabaseError=FAIL - \u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u53c2\u7167\u3092\u89e3\u6c7a\u3067\u304d\u307e\u305b\u3093
+managerServlet.userDatabaseMissing=FAIL - \u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+statusServlet.title=\u30b5\u30fc\u30d0\u306e\u72b6\u614b
+statusServlet.complete=\u30b5\u30fc\u30d0\u306e\u5168\u72b6\u614b
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/ManagerServlet.java b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/ManagerServlet.java
new file mode 100644
index 0000000..4e66885
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/ManagerServlet.java
@@ -0,0 +1,1572 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.manager;
+
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.naming.Binding;
+import javax.naming.InitialContext;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerServlet;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.Role;
+import org.apache.catalina.Server;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.Session;
+import org.apache.catalina.UserDatabase;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.modeler.Registry;
+
+
+/**
+ * Servlet that enables remote management of the web applications installed
+ * within the same virtual host as this web application is.  Normally, this
+ * functionality will be protected by a security constraint in the web
+ * application deployment descriptor.  However, this requirement can be
+ * relaxed during testing.
+ * <p>
+ * This servlet examines the value returned by <code>getPathInfo()</code>
+ * and related query parameters to determine what action is being requested.
+ * The following actions and parameters (starting after the servlet path)
+ * are supported:
+ * <ul>
+ * <li><b>/deploy?config={config-url}</b> - Install and start a new
+ *     web application, based on the contents of the context configuration
+ *     file found at the specified URL.  The <code>docBase</code> attribute
+ *     of the context configuration file is used to locate the actual
+ *     WAR or directory containing the application.</li>
+ * <li><b>/deploy?config={config-url}&war={war-url}/</b> - Install and start
+ *     a new web application, based on the contents of the context
+ *     configuration file found at <code>{config-url}</code>, overriding the
+ *     <code>docBase</code> attribute with the contents of the web
+ *     application archive found at <code>{war-url}</code>.</li>
+ * <li><b>/deploy?path=/xxx&war={war-url}</b> - Install and start a new
+ *     web application attached to context path <code>/xxx</code>, based
+ *     on the contents of the web application archive found at the
+ *     specified URL.</li>
+ * <li><b>/list</b> - List the context paths of all currently installed web
+ *     applications for this virtual host.  Each context will be listed with
+ *     the following format <code>path:status:sessions</code>.
+ *     Where path is the context path.  Status is either running or stopped.
+ *     Sessions is the number of active Sessions.</li>
+ * <li><b>/reload?path=/xxx</b> - Reload the Java classes and resources for
+ *     the application at the specified path.</li>
+ * <li><b>/resources?type=xxxx</b> - Enumerate the available global JNDI
+ *     resources, optionally limited to those of the specified type
+ *     (fully qualified Java class name), if available.</li>
+ * <li><b>/roles</b> - Enumerate the available security role names and
+ *     descriptions from the user database connected to the <code>users</code>
+ *     resource reference.
+ * <li><b>/serverinfo</b> - Display system OS and JVM properties.
+ * <li><b>/sessions?path=/xxx</b> - List session information about the web
+ *     application attached to context path <code>/xxx</code> for this
+ *     virtual host.</li>
+ * <li><b>/start?path=/xxx</b> - Start the web application attached to
+ *     context path <code>/xxx</code> for this virtual host.</li>
+ * <li><b>/stop?path=/xxx</b> - Stop the web application attached to
+ *     context path <code>/xxx</code> for this virtual host.</li>
+ * <li><b>/undeploy?path=/xxx</b> - Shutdown and remove the web application
+ *     attached to context path <code>/xxx</code> for this virtual host,
+ *     and remove the underlying WAR file or document base directory.
+ *     (<em>NOTE</em> - This is only allowed if the WAR file or document
+ *     base is stored in the <code>appBase</code> directory of this host,
+ *     typically as a result of being placed there via the <code>/deploy</code>
+ *     command.</li>
+ * </ul>
+ * <p>Use <code>path=/</code> for the ROOT context.</p>
+ * <p>The syntax of the URL for a web application archive must conform to one
+ * of the following patterns to be successfully deployed:</p>
+ * <ul>
+ * <li><b>file:/absolute/path/to/a/directory</b> - You can specify the absolute
+ *     path of a directory that contains the unpacked version of a web
+ *     application.  This directory will be attached to the context path you
+ *     specify without any changes.</li>
+ * <li><b>jar:file:/absolute/path/to/a/warfile.war!/</b> - You can specify a
+ *     URL to a local web application archive file.  The syntax must conform to
+ *     the rules specified by the <code>JarURLConnection</code> class for a
+ *     reference to an entire JAR file.</li>
+ * <li><b>jar:http://hostname:port/path/to/a/warfile.war!/</b> - You can specify
+ *     a URL to a remote (HTTP-accessible) web application archive file.  The
+ *     syntax must conform to the rules specified by the
+ *     <code>JarURLConnection</code> class for a reference to an entire
+ *     JAR file.</li>
+ * </ul>
+ * <p>
+ * <b>NOTE</b> - Attempting to reload or remove the application containing
+ * this servlet itself will not succeed.  Therefore, this servlet should
+ * generally be deployed as a separate web application within the virtual host
+ * to be managed.
+ * <p>
+ * <b>NOTE</b> - For security reasons, this application will not operate
+ * when accessed via the invoker servlet.  You must explicitly map this servlet
+ * with a servlet mapping, and you will always want to protect it with
+ * appropriate security constraints as well.
+ * <p>
+ * The following servlet initialization parameters are recognized:
+ * <ul>
+ * <li><b>debug</b> - The debugging detail level that controls the amount
+ *     of information that is logged by this servlet.  Default is zero.
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class ManagerServlet
+    extends HttpServlet implements ContainerServlet {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Path where context descriptors should be deployed.
+     */
+    protected File configBase = null;
+
+
+    /**
+     * The Context container associated with our web application.
+     */
+    protected Context context = null;
+
+
+    /**
+     * The debugging detail level for this servlet.
+     */
+    protected int debug = 1;
+
+
+    /**
+     * File object representing the directory into which the deploy() command
+     * will store the WAR and context configuration files that have been
+     * uploaded.
+     */
+    protected File deployed = null;
+
+
+    /**
+     * Path used to store revisions of webapps.
+     */
+    protected File versioned = null;
+
+
+    /**
+     * Path used to store context descriptors.
+     */
+    protected File contextDescriptors = null;
+
+
+    /**
+     * The associated host.
+     */
+    protected Host host = null;
+
+    
+    /**
+     * The host appBase.
+     */
+    protected File appBase = null;
+    
+    
+    /**
+     * MBean server.
+     */
+    protected MBeanServer mBeanServer = null;
+
+
+    /**
+     * The associated deployer ObjectName.
+     */
+    protected ObjectName oname = null;
+    
+
+    /**
+     * The global JNDI <code>NamingContext</code> for this server,
+     * if available.
+     */
+    protected javax.naming.Context global = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The Wrapper container associated with this servlet.
+     */
+    protected Wrapper wrapper = null;
+
+
+    // ----------------------------------------------- ContainerServlet Methods
+
+
+    /**
+     * Return the Wrapper with which we are associated.
+     */
+    public Wrapper getWrapper() {
+
+        return (this.wrapper);
+
+    }
+
+
+    /**
+     * Set the Wrapper with which we are associated.
+     *
+     * @param wrapper The new wrapper
+     */
+    public void setWrapper(Wrapper wrapper) {
+
+        this.wrapper = wrapper;
+        if (wrapper == null) {
+            context = null;
+            host = null;
+            oname = null;
+        } else {
+            context = (Context) wrapper.getParent();
+            host = (Host) context.getParent();
+            Engine engine = (Engine) host.getParent();
+            try {
+                oname = new ObjectName(engine.getName() 
+                        + ":type=Deployer,host=" + host.getName());
+            } catch (Exception e) {
+                // ?
+            }
+        }
+
+        // Retrieve the MBean server
+        mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+        
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Finalize this servlet.
+     */
+    public void destroy() {
+
+        ;       // No actions necessary
+
+    }
+
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Verify that we were not accessed using the invoker servlet
+        if (request.getAttribute(Globals.INVOKED_ATTR) != null)
+            throw new UnavailableException
+                (sm.getString("managerServlet.cannotInvoke"));
+
+        // Identify the request parameters that we need
+        String command = request.getPathInfo();
+        if (command == null)
+            command = request.getServletPath();
+        String config = request.getParameter("config");
+        String path = request.getParameter("path");
+        String type = request.getParameter("type");
+        String war = request.getParameter("war");
+        String tag = request.getParameter("tag");
+        boolean update = false;
+        if ((request.getParameter("update") != null) 
+            && (request.getParameter("update").equals("true"))) {
+            update = true;
+        }
+
+        // Prepare our output writer to generate the response message
+        response.setContentType("text/plain; charset=" + Constants.CHARSET);
+        PrintWriter writer = response.getWriter();
+
+        // Process the requested command (note - "/deploy" is not listed here)
+        if (command == null) {
+            writer.println(sm.getString("managerServlet.noCommand"));
+        } else if (command.equals("/deploy")) {
+            if (war != null || config != null) {
+                deploy(writer, config, path, war, update);
+            } else {
+                deploy(writer, path, tag);
+            }
+        } else if (command.equals("/install")) {
+            // Deprecated
+            deploy(writer, config, path, war, false);
+        } else if (command.equals("/list")) {
+            list(writer);
+        } else if (command.equals("/reload")) {
+            reload(writer, path);
+        } else if (command.equals("/remove")) {
+            // Deprecated
+            undeploy(writer, path);
+        } else if (command.equals("/resources")) {
+            resources(writer, type);
+        } else if (command.equals("/roles")) {
+            roles(writer);
+        } else if (command.equals("/save")) {
+            save(writer, path);
+        } else if (command.equals("/serverinfo")) {
+            serverinfo(writer);
+        } else if (command.equals("/sessions")) {
+            sessions(writer, path);
+        } else if (command.equals("/start")) {
+            start(writer, path);
+        } else if (command.equals("/stop")) {
+            stop(writer, path);
+        } else if (command.equals("/undeploy")) {
+            undeploy(writer, path);
+        } else {
+            writer.println(sm.getString("managerServlet.unknownCommand",
+                                        command));
+        }
+
+        // Finish up the response
+        writer.flush();
+        writer.close();
+
+    }
+
+
+    /**
+     * Process a PUT request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doPut(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Verify that we were not accessed using the invoker servlet
+        if (request.getAttribute(Globals.INVOKED_ATTR) != null)
+            throw new UnavailableException
+                (sm.getString("managerServlet.cannotInvoke"));
+
+        // Identify the request parameters that we need
+        String command = request.getPathInfo();
+        if (command == null)
+            command = request.getServletPath();
+        String path = request.getParameter("path");
+        String tag = request.getParameter("tag");
+        boolean update = false;
+        if ((request.getParameter("update") != null) 
+            && (request.getParameter("update").equals("true"))) {
+            update = true;
+        }
+
+        // Prepare our output writer to generate the response message
+        response.setContentType("text/plain;charset="+Constants.CHARSET);
+        PrintWriter writer = response.getWriter();
+
+        // Process the requested command
+        if (command == null) {
+            writer.println(sm.getString("managerServlet.noCommand"));
+        } else if (command.equals("/deploy")) {
+            deploy(writer, path, tag, update, request);
+        } else {
+            writer.println(sm.getString("managerServlet.unknownCommand",
+                                        command));
+        }
+
+        // Finish up the response
+        writer.flush();
+        writer.close();
+
+    }
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init() throws ServletException {
+
+        // Ensure that our ContainerServlet properties have been set
+        if ((wrapper == null) || (context == null))
+            throw new UnavailableException
+                (sm.getString("managerServlet.noWrapper"));
+
+        // Verify that we were not accessed using the invoker servlet
+        String servletName = getServletConfig().getServletName();
+        if (servletName == null)
+            servletName = "";
+        if (servletName.startsWith("org.apache.catalina.INVOKER."))
+            throw new UnavailableException
+                (sm.getString("managerServlet.cannotInvoke"));
+
+        // Set our properties from the initialization parameters
+        String value = null;
+        try {
+            value = getServletConfig().getInitParameter("debug");
+            debug = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Acquire global JNDI resources if available
+        Server server = ServerFactory.getServer();
+        if ((server != null) && (server instanceof StandardServer)) {
+            global = ((StandardServer) server).getGlobalNamingContext();
+        }
+
+        // Calculate the directory into which we will be deploying applications
+        versioned = (File) getServletContext().getAttribute
+            ("javax.servlet.context.tempdir");
+
+        // Identify the appBase of the owning Host of this Context
+        // (if any)
+        String appBase = ((Host) context.getParent()).getAppBase();
+        deployed = new File(appBase);
+        if (!deployed.isAbsolute()) {
+            deployed = new File(System.getProperty("catalina.base"),
+                                appBase);
+        }
+        configBase = new File(System.getProperty("catalina.base"), "conf");
+        Container container = context;
+        Container host = null;
+        Container engine = null;
+        while (container != null) {
+            if (container instanceof Host)
+                host = container;
+            if (container instanceof Engine)
+                engine = container;
+            container = container.getParent();
+        }
+        if (engine != null) {
+            configBase = new File(configBase, engine.getName());
+        }
+        if (host != null) {
+            configBase = new File(configBase, host.getName());
+        }
+        // Note: The directory must exist for this to work.
+
+        // Log debugging messages as necessary
+        if (debug >= 1) {
+            log("init: Associated with Deployer '" +
+                oname + "'");
+            if (global != null) {
+                log("init: Global resources are available");
+            }
+        }
+
+    }
+
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Store server configuration.
+     * 
+     * @param path Optional context path to save
+     */
+    protected synchronized void save(PrintWriter writer, String path) {
+
+        Server server = ServerFactory.getServer();
+
+        if (!(server instanceof StandardServer)) {
+            writer.println(sm.getString("managerServlet.saveFail", server));
+            return;
+        }
+
+        if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
+            try {
+                ((StandardServer) server).storeConfig();
+                writer.println(sm.getString("managerServlet.saved"));
+            } catch (Exception e) {
+                log("managerServlet.storeConfig", e);
+                writer.println(sm.getString("managerServlet.exception",
+                                            e.toString()));
+                return;
+            }
+        } else {
+            String contextPath = path;
+            if (path.equals("/")) {
+                contextPath = "";
+            }
+            Context context = (Context) host.findChild(contextPath);
+            if (context == null) {
+                writer.println(sm.getString("managerServlet.noContext", path));
+                return;
+            }
+            try {
+                ((StandardServer) server).storeContext(context);
+                writer.println(sm.getString("managerServlet.savedContext", 
+                               path));
+            } catch (Exception e) {
+                log("managerServlet.save[" + path + "]", e);
+                writer.println(sm.getString("managerServlet.exception",
+                                            e.toString()));
+                return;
+            }
+        }
+
+    }
+
+
+    /**
+     * Deploy a web application archive (included in the current request)
+     * at the specified context path.
+     *
+     * @param writer Writer to render results to
+     * @param path Context path of the application to be installed
+     * @param tag Tag to be associated with the webapp
+     * @param request Servlet request we are processing
+     */
+    protected synchronized void deploy
+        (PrintWriter writer, String path,
+         String tag, boolean update, HttpServletRequest request) {
+
+        if (debug >= 1) {
+            log("deploy: Deploying web application at '" + path + "'");
+        }
+
+        // Validate the requested context path
+        if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
+            writer.println(sm.getString("managerServlet.invalidPath", path));
+            return;
+        }
+        String displayPath = path;
+        if( path.equals("/") )
+            path = "";
+        String basename = getDocBase(path);
+
+        // Check if app already exists, or undeploy it if updating
+        Context context = (Context) host.findChild(path);
+        if (update) {
+            if (context != null) {
+                undeploy(writer, displayPath);
+            }
+            context = (Context) host.findChild(path);
+        }
+        if (context != null) {
+            writer.println
+                (sm.getString("managerServlet.alreadyContext",
+                              displayPath));
+            return;
+        }
+
+        // Calculate the base path
+        File deployedPath = deployed;
+        if (tag != null) {
+            deployedPath = new File(versioned, tag);
+            deployedPath.mkdirs();
+        }
+
+        // Upload the web application archive to a local WAR file
+        File localWar = new File(deployedPath, basename + ".war");
+        if (debug >= 2) {
+            log("Uploading WAR file to " + localWar);
+        }
+
+        // Copy WAR to appBase
+        try {
+            if (!isServiced(path)) {
+                addServiced(path);
+                try {
+                    // Upload WAR
+                    uploadWar(request, localWar);
+                    // Copy WAR and XML to the host app base if needed
+                    if (tag != null) {
+                        deployedPath = deployed;
+                        File localWarCopy = new File(deployedPath, basename + ".war");
+                        copy(localWar, localWarCopy);
+                        localWar = localWarCopy;
+                        copy(localWar, new File(getAppBase(), basename + ".war"));
+                    }
+                    // Perform new deployment
+                    check(path);
+                } finally {
+                    removeServiced(path);
+                }
+            }
+        } catch (Exception e) {
+            log("managerServlet.check[" + displayPath + "]", e);
+            writer.println(sm.getString("managerServlet.exception",
+                                        e.toString()));
+            return;
+        }
+        
+        context = (Context) host.findChild(path);
+        if (context != null && context.getConfigured()) {
+            writer.println(sm.getString("managerServlet.deployed", displayPath));
+        } else {
+            // Something failed
+            writer.println(sm.getString("managerServlet.deployFailed", displayPath));
+        }
+        
+    }
+
+
+    /**
+     * Install an application for the specified path from the specified
+     * web application archive.
+     *
+     * @param writer Writer to render results to
+     * @param tag Revision tag to deploy from
+     * @param path Context path of the application to be installed
+     */
+    protected void deploy(PrintWriter writer, String path, String tag) {
+
+        // Validate the requested context path
+        if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
+            writer.println(sm.getString("managerServlet.invalidPath", path));
+            return;
+        }
+        String displayPath = path;
+        if( path.equals("/") )
+            path = "";
+
+        // Calculate the base path
+        File deployedPath = versioned;
+        if (tag != null) {
+            deployedPath = new File(deployedPath, tag);
+        }
+
+        // Find the local WAR file
+        File localWar = new File(deployedPath, getDocBase(path) + ".war");
+        // Find the local context deployment file (if any)
+        File localXml = new File(configBase, getConfigFile(path) + ".xml");
+
+        // Check if app already exists, or undeploy it if updating
+        Context context = (Context) host.findChild(path);
+        if (context != null) {
+            undeploy(writer, displayPath);
+        }
+
+        // Copy WAR to appBase
+        try {
+            if (!isServiced(path)) {
+                addServiced(path);
+                try {
+                    copy(localWar, new File(getAppBase(), getDocBase(path) + ".war"));
+                    // Perform new deployment
+                    check(path);
+                } finally {
+                    removeServiced(path);
+                }
+            }
+        } catch (Exception e) {
+            log("managerServlet.check[" + displayPath + "]", e);
+            writer.println(sm.getString("managerServlet.exception",
+                                        e.toString()));
+            return;
+        }
+        
+        context = (Context) host.findChild(path);
+        if (context != null && context.getConfigured()) {
+            writer.println(sm.getString("managerServlet.deployed", displayPath));
+        } else {
+            // Something failed
+            writer.println(sm.getString("managerServlet.deployFailed", displayPath));
+        }
+        
+    }
+
+
+    /**
+     * Install an application for the specified path from the specified
+     * web application archive.
+     *
+     * @param writer Writer to render results to
+     * @param config URL of the context configuration file to be installed
+     * @param path Context path of the application to be installed
+     * @param war URL of the web application archive to be installed
+     * @param update true to override any existing webapp on the path
+     */
+    protected void deploy(PrintWriter writer, String config,
+            String path, String war, boolean update) {
+        
+        if (config != null && config.length() == 0) {
+            config = null;
+        }
+        if (war != null && war.length() == 0) {
+            war = null;
+        }
+        
+        if (debug >= 1) {
+            if (config != null && config.length() > 0) {
+                if (war != null) {
+                    log("install: Installing context configuration at '" +
+                            config + "' from '" + war + "'");
+                } else {
+                    log("install: Installing context configuration at '" +
+                            config + "'");
+                }
+            } else {
+                if (path != null && path.length() > 0) {
+                    log("install: Installing web application at '" + path +
+                            "' from '" + war + "'");
+                } else {
+                    log("install: Installing web application from '" + war + "'");
+                }
+            }
+        }
+        
+        if (path == null || path.length() == 0 || !path.startsWith("/")) {
+            writer.println(sm.getString("managerServlet.invalidPath",
+                                        RequestUtil.filter(path)));
+            return;
+        }
+        String displayPath = path;
+        if("/".equals(path)) {
+            path = "";
+        }
+        
+        // Check if app already exists, or undeploy it if updating
+        Context context = (Context) host.findChild(path);
+        if (update) {
+            if (context != null) {
+                undeploy(writer, displayPath);
+            }
+            context = (Context) host.findChild(path);
+        }
+        if (context != null) {
+            writer.println
+            (sm.getString("managerServlet.alreadyContext",
+                    displayPath));
+            return;
+        }
+        
+        if (config != null && (config.startsWith("file:"))) {
+            config = config.substring("file:".length());
+        }
+        if (war != null && (war.startsWith("file:"))) {
+            war = war.substring("file:".length());
+        }
+        
+        try {
+            if (!isServiced(path)) {
+                addServiced(path);
+                try {
+                    if (config != null) {
+                        copy(new File(config), 
+                                new File(configBase, getConfigFile(path) + ".xml"));
+                    }
+                    if (war != null) {
+                        if (war.endsWith(".war")) {
+                            copy(new File(war), 
+                                    new File(getAppBase(), getDocBase(path) + ".war"));
+                        } else {
+                            copy(new File(war), 
+                                    new File(getAppBase(), getDocBase(path)));
+                        }
+                    }
+                    // Perform new deployment
+                    check(path);
+                } finally {
+                    removeServiced(path);
+                }
+            }
+            context = (Context) host.findChild(path);
+            if (context != null && context.getConfigured()) {
+                writer.println(sm.getString("managerServlet.deployed", displayPath));
+            } else {
+                // Something failed
+                writer.println(sm.getString("managerServlet.deployFailed", displayPath));
+            }
+        } catch (Throwable t) {
+            log("ManagerServlet.install[" + displayPath + "]", t);
+            writer.println(sm.getString("managerServlet.exception",
+                    t.toString()));
+        }
+        
+    }
+
+
+    /**
+     * Render a list of the currently active Contexts in our virtual host.
+     *
+     * @param writer Writer to render to
+     */
+    protected void list(PrintWriter writer) {
+
+        if (debug >= 1)
+            log("list: Listing contexts for virtual host '" +
+                host.getName() + "'");
+
+        writer.println(sm.getString("managerServlet.listed",
+                                    host.getName()));
+        Container[] contexts = host.findChildren();
+        for (int i = 0; i < contexts.length; i++) {
+            Context context = (Context) contexts[i];
+            String displayPath = context.getPath();
+            if( displayPath.equals("") )
+                displayPath = "/";
+            if (context != null ) {
+                if (context.getAvailable()) {
+                    writer.println(sm.getString("managerServlet.listitem",
+                                                displayPath,
+                                                "running",
+                                      "" + context.getManager().findSessions().length,
+                                                context.getDocBase()));
+                } else {
+                    writer.println(sm.getString("managerServlet.listitem",
+                                                displayPath,
+                                                "stopped",
+                                                "0",
+                                                context.getDocBase()));
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Reload the web application at the specified context path.
+     *
+     * @param writer Writer to render to
+     * @param path Context path of the application to be restarted
+     */
+    protected void reload(PrintWriter writer, String path) {
+
+        if (debug >= 1)
+            log("restart: Reloading web application at '" + path + "'");
+
+        if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
+            writer.println(sm.getString("managerServlet.invalidPath",
+                                        RequestUtil.filter(path)));
+            return;
+        }
+        String displayPath = path;
+        if( path.equals("/") )
+            path = "";
+
+        try {
+            Context context = (Context) host.findChild(path);
+            if (context == null) {
+                writer.println(sm.getString
+                               ("managerServlet.noContext",
+                                   RequestUtil.filter(displayPath)));
+                return;
+            }
+            // It isn't possible for the manager to reload itself
+            if (context.getPath().equals(this.context.getPath())) {
+                writer.println(sm.getString("managerServlet.noSelf"));
+                return;
+            }
+            context.reload();
+            writer.println
+                (sm.getString("managerServlet.reloaded", displayPath));
+        } catch (Throwable t) {
+            log("ManagerServlet.reload[" + displayPath + "]", t);
+            writer.println(sm.getString("managerServlet.exception",
+                                        t.toString()));
+        }
+
+    }
+
+
+    /**
+     * Render a list of available global JNDI resources.
+     *
+     * @param type Fully qualified class name of the resource type of interest,
+     *  or <code>null</code> to list resources of all types
+     */
+    protected void resources(PrintWriter writer, String type) {
+
+        if (debug >= 1) {
+            if (type != null) {
+                log("resources:  Listing resources of type " + type);
+            } else {
+                log("resources:  Listing resources of all types");
+            }
+        }
+
+        // Is the global JNDI resources context available?
+        if (global == null) {
+            writer.println(sm.getString("managerServlet.noGlobal"));
+            return;
+        }
+
+        // Enumerate the global JNDI resources of the requested type
+        if (type != null) {
+            writer.println(sm.getString("managerServlet.resourcesType",
+                                        type));
+        } else {
+            writer.println(sm.getString("managerServlet.resourcesAll"));
+        }
+
+        Class clazz = null;
+        try {
+            if (type != null) {
+                clazz = Class.forName(type);
+            }
+        } catch (Throwable t) {
+            log("ManagerServlet.resources[" + type + "]", t);
+            writer.println(sm.getString("managerServlet.exception",
+                                        t.toString()));
+            return;
+        }
+
+        printResources(writer, "", global, type, clazz);
+
+    }
+
+
+    /**
+     * List the resources of the given context.
+     */
+    protected void printResources(PrintWriter writer, String prefix,
+                                  javax.naming.Context namingContext,
+                                  String type, Class clazz) {
+
+        try {
+            NamingEnumeration items = namingContext.listBindings("");
+            while (items.hasMore()) {
+                Binding item = (Binding) items.next();
+                if (item.getObject() instanceof javax.naming.Context) {
+                    printResources
+                        (writer, prefix + item.getName() + "/",
+                         (javax.naming.Context) item.getObject(), type, clazz);
+                } else {
+                    if ((clazz != null) &&
+                        (!(clazz.isInstance(item.getObject())))) {
+                        continue;
+                    }
+                    writer.print(prefix + item.getName());
+                    writer.print(':');
+                    writer.print(item.getClassName());
+                    // Do we want a description if available?
+                    writer.println();
+                }
+            }
+        } catch (Throwable t) {
+            log("ManagerServlet.resources[" + type + "]", t);
+            writer.println(sm.getString("managerServlet.exception",
+                                        t.toString()));
+        }
+
+    }
+
+
+    /**
+     * Render a list of security role names (and corresponding descriptions)
+     * from the <code>org.apache.catalina.UserDatabase</code> resource that is
+     * connected to the <code>users</code> resource reference.  Typically, this
+     * will be the global user database, but can be adjusted if you have
+     * different user databases for different virtual hosts.
+     *
+     * @param writer Writer to render to
+     */
+    protected void roles(PrintWriter writer) {
+
+        if (debug >= 1) {
+            log("roles:  List security roles from user database");
+        }
+
+        // Look up the UserDatabase instance we should use
+        UserDatabase database = null;
+        try {
+            InitialContext ic = new InitialContext();
+            database = (UserDatabase) ic.lookup("java:comp/env/users");
+        } catch (NamingException e) {
+            writer.println(sm.getString("managerServlet.userDatabaseError"));
+            log("java:comp/env/users", e);
+            return;
+        }
+        if (database == null) {
+            writer.println(sm.getString("managerServlet.userDatabaseMissing"));
+            return;
+        }
+
+        // Enumerate the available roles
+        writer.println(sm.getString("managerServlet.rolesList"));
+        Iterator roles = database.getRoles();
+        if (roles != null) {
+            while (roles.hasNext()) {
+                Role role = (Role) roles.next();
+                writer.print(role.getRolename());
+                writer.print(':');
+                if (role.getDescription() != null) {
+                    writer.print(role.getDescription());
+                }
+                writer.println();
+            }
+        }
+
+
+    }
+
+
+    /**
+     * Writes System OS and JVM properties.
+     * @param writer Writer to render to
+     */
+    protected void serverinfo(PrintWriter writer) {
+        if (debug >= 1)
+            log("serverinfo");
+        try {
+            StringBuffer props = new StringBuffer();
+            props.append("OK - Server info");
+            props.append("\nTomcat Version: ");
+            props.append(ServerInfo.getServerInfo());
+            props.append("\nOS Name: ");
+            props.append(System.getProperty("os.name"));
+            props.append("\nOS Version: ");
+            props.append(System.getProperty("os.version"));
+            props.append("\nOS Architecture: ");
+            props.append(System.getProperty("os.arch"));
+            props.append("\nJVM Version: ");
+            props.append(System.getProperty("java.runtime.version"));
+            props.append("\nJVM Vendor: ");
+            props.append(System.getProperty("java.vm.vendor"));
+            writer.println(props.toString());
+        } catch (Throwable t) {
+            getServletContext().log("ManagerServlet.serverinfo",t);
+            writer.println(sm.getString("managerServlet.exception",
+                                        t.toString()));
+        }
+    }
+
+    /**
+     * Session information for the web application at the specified context path.
+     * Displays a profile of session MaxInactiveInterval timeouts listing number
+     * of sessions for each 10 minute timeout interval up to 10 hours.
+     *
+     * @param writer Writer to render to
+     * @param path Context path of the application to list session information for
+     */
+    protected void sessions(PrintWriter writer, String path) {
+
+        if (debug >= 1)
+            log("sessions: Session information for web application at '" + path + "'");
+
+        if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
+            writer.println(sm.getString("managerServlet.invalidPath",
+                                        RequestUtil.filter(path)));
+            return;
+        }
+        String displayPath = path;
+        if( path.equals("/") )
+            path = "";
+        try {
+            Context context = (Context) host.findChild(path);
+            if (context == null) {
+                writer.println(sm.getString("managerServlet.noContext",
+                                            RequestUtil.filter(displayPath)));
+                return;
+            }
+            writer.println(sm.getString("managerServlet.sessions", displayPath));
+            writer.println(sm.getString("managerServlet.sessiondefaultmax",
+                                "" + context.getManager().getMaxInactiveInterval()/60));
+            Session [] sessions = context.getManager().findSessions();
+            int [] timeout = new int[60];
+            int notimeout = 0;
+            for (int i = 0; i < sessions.length; i++) {
+                int time = sessions[i].getMaxInactiveInterval()/(10*60);
+                if (time < 0)
+                    notimeout++;
+                else if (time >= timeout.length)
+                    timeout[timeout.length-1]++;
+                else
+                    timeout[time]++;
+            }
+            if (timeout[0] > 0)
+                writer.println(sm.getString("managerServlet.sessiontimeout",
+                                            "<10", "" + timeout[0]));
+            for (int i = 1; i < timeout.length-1; i++) {
+                if (timeout[i] > 0)
+                    writer.println(sm.getString("managerServlet.sessiontimeout",
+                                     "" + (i)*10 + " - <" + (i+1)*10,
+                                                "" + timeout[i]));
+            }
+            if (timeout[timeout.length-1] > 0)
+                writer.println(sm.getString("managerServlet.sessiontimeout",
+                                            ">=" + timeout.length*10,
+                                            "" + timeout[timeout.length-1]));
+            if (notimeout > 0)
+                writer.println(sm.getString("managerServlet.sessiontimeout",
+                                            "unlimited","" + notimeout));
+        } catch (Throwable t) {
+            log("ManagerServlet.sessions[" + displayPath + "]", t);
+            writer.println(sm.getString("managerServlet.exception",
+                                        t.toString()));
+        }
+
+    }
+
+
+    /**
+     * Start the web application at the specified context path.
+     *
+     * @param writer Writer to render to
+     * @param path Context path of the application to be started
+     */
+    protected void start(PrintWriter writer, String path) {
+
+        if (debug >= 1)
+            log("start: Starting web application at '" + path + "'");
+
+        if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
+            writer.println(sm.getString("managerServlet.invalidPath",
+                                        RequestUtil.filter(path)));
+            return;
+        }
+        String displayPath = path;
+        if( path.equals("/") )
+            path = "";
+
+        try {
+            Context context = (Context) host.findChild(path);
+            if (context == null) {
+                writer.println(sm.getString("managerServlet.noContext", 
+                                            RequestUtil.filter(displayPath)));
+                return;
+            }
+            ((Lifecycle) context).start();
+            if (context.getAvailable())
+                writer.println
+                    (sm.getString("managerServlet.started", displayPath));
+            else
+                writer.println
+                    (sm.getString("managerServlet.startFailed", displayPath));
+        } catch (Throwable t) {
+            getServletContext().log
+                (sm.getString("managerServlet.startFailed", displayPath), t);
+            writer.println
+                (sm.getString("managerServlet.startFailed", displayPath));
+            writer.println(sm.getString("managerServlet.exception",
+                                        t.toString()));
+        }
+
+    }
+
+
+    /**
+     * Stop the web application at the specified context path.
+     *
+     * @param writer Writer to render to
+     * @param path Context path of the application to be stopped
+     */
+    protected void stop(PrintWriter writer, String path) {
+
+        if (debug >= 1)
+            log("stop: Stopping web application at '" + path + "'");
+
+        if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
+            writer.println(sm.getString("managerServlet.invalidPath",
+                                        RequestUtil.filter(path)));
+            return;
+        }
+        String displayPath = path;
+        if( path.equals("/") )
+            path = "";
+
+        try {
+            Context context = (Context) host.findChild(path);
+            if (context == null) {
+                writer.println(sm.getString("managerServlet.noContext", 
+                                            RequestUtil.filter(displayPath)));
+                return;
+            }
+            // It isn't possible for the manager to stop itself
+            if (context.getPath().equals(this.context.getPath())) {
+                writer.println(sm.getString("managerServlet.noSelf"));
+                return;
+            }
+            ((Lifecycle) context).stop();
+            writer.println(sm.getString("managerServlet.stopped", displayPath));
+        } catch (Throwable t) {
+            log("ManagerServlet.stop[" + displayPath + "]", t);
+            writer.println(sm.getString("managerServlet.exception",
+                                        t.toString()));
+        }
+
+    }
+
+
+    /**
+     * Undeploy the web application at the specified context path.
+     *
+     * @param writer Writer to render to
+     * @param path Context path of the application to be removed
+     */
+    protected void undeploy(PrintWriter writer, String path) {
+
+        if (debug >= 1)
+            log("undeploy: Undeploying web application at '" + path + "'");
+
+        if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
+            writer.println(sm.getString("managerServlet.invalidPath",
+                                        RequestUtil.filter(path)));
+            return;
+        }
+        String displayPath = path;
+        if( path.equals("/") )
+            path = "";
+
+        try {
+
+            // Validate the Context of the specified application
+            Context context = (Context) host.findChild(path);
+            if (context == null) {
+                writer.println(sm.getString("managerServlet.noContext",
+                                            RequestUtil.filter(displayPath)));
+                return;
+            }
+
+            // Identify the appBase of the owning Host of this Context (if any)
+            String appBase = null;
+            File appBaseDir = null;
+            if (context.getParent() instanceof Host) {
+                appBase = ((Host) context.getParent()).getAppBase();
+                appBaseDir = new File(appBase);
+                if (!appBaseDir.isAbsolute()) {
+                    appBaseDir = new File(System.getProperty("catalina.base"),
+                                          appBase);
+                }
+            }
+
+            if (!isServiced(path)) {
+                addServiced(path);
+                try {
+                    // Try to stop the context first to be nicer
+                    ((Lifecycle) context).stop();
+                } catch (Throwable t) {
+                    // Ignore
+                }
+                try {
+                    File war = new File(getAppBase(), getDocBase(path) + ".war");
+                    File dir = new File(getAppBase(), getDocBase(path));
+                    File xml = new File(configBase, getConfigFile(path) + ".xml");
+                    if (war.exists()) {
+                        war.delete();
+                    } else if (dir.exists()) {
+                        undeployDir(dir);
+                    } else {
+                        xml.delete();
+                    }
+                    // Perform new deployment
+                    check(path);
+                } finally {
+                    removeServiced(path);
+                }
+            }
+            writer.println(sm.getString("managerServlet.undeployed",
+                                        displayPath));
+        } catch (Throwable t) {
+            log("ManagerServlet.undeploy[" + displayPath + "]", t);
+            writer.println(sm.getString("managerServlet.exception",
+                                        t.toString()));
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Support Methods
+
+
+    /**
+     * Given a context path, get the config file name.
+     */
+    protected String getConfigFile(String path) {
+        String basename = null;
+        if (path.equals("")) {
+            basename = "ROOT";
+        } else {
+            basename = path.substring(1).replace('/', '#');
+        }
+        return (basename);
+    }
+
+
+    /**
+     * Given a context path, get the config file name.
+     */
+    protected String getDocBase(String path) {
+        String basename = null;
+        if (path.equals("")) {
+            basename = "ROOT";
+        } else {
+            basename = path.substring(1);
+        }
+        return (basename);
+    }
+
+    
+    /**
+     * Return a File object representing the "application root" directory
+     * for our associated Host.
+     */
+    protected File getAppBase() {
+
+        if (appBase != null) {
+            return appBase;
+        }
+
+        File file = new File(host.getAppBase());
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"),
+                            host.getAppBase());
+        try {
+            appBase = file.getCanonicalFile();
+        } catch (IOException e) {
+            appBase = file;
+        }
+        return (appBase);
+
+    }
+
+
+    /**
+     * Invoke the check method on the deployer.
+     */
+    protected void check(String name) 
+        throws Exception {
+        String[] params = { name };
+        String[] signature = { "java.lang.String" };
+        mBeanServer.invoke(oname, "check", params, signature);
+    }
+    
+
+    /**
+     * Invoke the check method on the deployer.
+     */
+    protected boolean isServiced(String name) 
+        throws Exception {
+        String[] params = { name };
+        String[] signature = { "java.lang.String" };
+        Boolean result = 
+            (Boolean) mBeanServer.invoke(oname, "isServiced", params, signature);
+        return result.booleanValue();
+    }
+    
+
+    /**
+     * Invoke the check method on the deployer.
+     */
+    protected void addServiced(String name) 
+        throws Exception {
+        String[] params = { name };
+        String[] signature = { "java.lang.String" };
+        mBeanServer.invoke(oname, "addServiced", params, signature);
+    }
+    
+
+    /**
+     * Invoke the check method on the deployer.
+     */
+    protected void removeServiced(String name) 
+        throws Exception {
+        String[] params = { name };
+        String[] signature = { "java.lang.String" };
+        mBeanServer.invoke(oname, "removeServiced", params, signature);
+    }
+    
+
+    /**
+     * Delete the specified directory, including all of its contents and
+     * subdirectories recursively.
+     *
+     * @param dir File object representing the directory to be deleted
+     */
+    protected void undeployDir(File dir) {
+
+        String files[] = dir.list();
+        if (files == null) {
+            files = new String[0];
+        }
+        for (int i = 0; i < files.length; i++) {
+            File file = new File(dir, files[i]);
+            if (file.isDirectory()) {
+                undeployDir(file);
+            } else {
+                file.delete();
+            }
+        }
+        dir.delete();
+
+    }
+
+
+    /**
+     * Upload the WAR file included in this request, and store it at the
+     * specified file location.
+     *
+     * @param request The servlet request we are processing
+     * @param war The file into which we should store the uploaded WAR
+     *
+     * @exception IOException if an I/O error occurs during processing
+     */
+    protected void uploadWar(HttpServletRequest request, File war)
+        throws IOException {
+
+        war.delete();
+        ServletInputStream istream = null;
+        BufferedOutputStream ostream = null;
+        try {
+            istream = request.getInputStream();
+            ostream =
+                new BufferedOutputStream(new FileOutputStream(war), 1024);
+            byte buffer[] = new byte[1024];
+            while (true) {
+                int n = istream.read(buffer);
+                if (n < 0) {
+                    break;
+                }
+                ostream.write(buffer, 0, n);
+            }
+            ostream.flush();
+            ostream.close();
+            ostream = null;
+            istream.close();
+            istream = null;
+        } catch (IOException e) {
+            war.delete();
+            throw e;
+        } finally {
+            if (ostream != null) {
+                try {
+                    ostream.close();
+                } catch (Throwable t) {
+                    ;
+                }
+                ostream = null;
+            }
+            if (istream != null) {
+                try {
+                    istream.close();
+                } catch (Throwable t) {
+                    ;
+                }
+                istream = null;
+            }
+        }
+
+    }
+
+
+    /**
+     * Copy the specified file or directory to the destination.
+     *
+     * @param src File object representing the source
+     * @param dest File object representing the destination
+     */
+    public static boolean copy(File src, File dest) {
+        return copyInternal(src, dest, new byte[4096]);
+    }
+
+    
+    /**
+     * Copy the specified file or directory to the destination.
+     *
+     * @param src File object representing the source
+     * @param dest File object representing the destination
+     */
+    public static boolean copyInternal(File src, File dest, byte[] buf) {
+        
+        boolean result = true;
+        
+        String files[] = null;
+        if (src.isDirectory()) {
+            files = src.list();
+            result = dest.mkdir();
+        } else {
+            files = new String[1];
+            files[0] = "";
+        }
+        if (files == null) {
+            files = new String[0];
+        }
+        for (int i = 0; (i < files.length) && result; i++) {
+            File fileSrc = new File(src, files[i]);
+            File fileDest = new File(dest, files[i]);
+            if (fileSrc.isDirectory()) {
+                result = copyInternal(fileSrc, fileDest, buf);
+            } else {
+                FileInputStream is = null;
+                FileOutputStream os = null;
+                try {
+                    is = new FileInputStream(fileSrc);
+                    os = new FileOutputStream(fileDest);
+                    int len = 0;
+                    while (true) {
+                        len = is.read(buf);
+                        if (len == -1)
+                            break;
+                        os.write(buf, 0, len);
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    result = false;
+                } finally {
+                    if (is != null) {
+                        try {
+                            is.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                    if (os != null) {
+                        try {
+                            os.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                }
+            }
+        }
+        return result;
+        
+    }
+    
+    
+}
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/StatusManagerServlet.java b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/StatusManagerServlet.java
new file mode 100644
index 0000000..19e5296
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/StatusManagerServlet.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.manager;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.modeler.Registry;
+
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * This servlet will display a complete status of the HTTP/1.1 connector.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class StatusManagerServlet
+    extends HttpServlet implements NotificationListener {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The debugging detail level for this servlet.
+     */
+    private int debug = 0;
+
+
+    /**
+     * MBean server.
+     */
+    protected MBeanServer mBeanServer = null;
+
+
+    /**
+     * Vector of protocol handlers object names.
+     */
+    protected Vector protocolHandlers = new Vector();
+
+
+    /**
+     * Vector of thread pools object names.
+     */
+    protected Vector threadPools = new Vector();
+
+
+    /**
+     * Vector of request processors object names.
+     */
+    protected Vector requestProcessors = new Vector();
+
+
+    /**
+     * Vector of global request processors object names.
+     */
+    protected Vector globalRequestProcessors = new Vector();
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init() throws ServletException {
+
+        // Retrieve the MBean server
+        mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+
+        // Set our properties from the initialization parameters
+        String value = null;
+        try {
+            value = getServletConfig().getInitParameter("debug");
+            debug = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+
+        try {
+
+            // Query protocol handlers
+            String onStr = "*:type=ProtocolHandler,*";
+            ObjectName objectName = new ObjectName(onStr);
+            Set set = mBeanServer.queryMBeans(objectName, null);
+            Iterator iterator = set.iterator();
+            while (iterator.hasNext()) {
+                ObjectInstance oi = (ObjectInstance) iterator.next();
+                protocolHandlers.addElement(oi.getObjectName());
+            }
+
+            // Query Thread Pools
+            onStr = "*:type=ThreadPool,*";
+            objectName = new ObjectName(onStr);
+            set = mBeanServer.queryMBeans(objectName, null);
+            iterator = set.iterator();
+            while (iterator.hasNext()) {
+                ObjectInstance oi = (ObjectInstance) iterator.next();
+                threadPools.addElement(oi.getObjectName());
+            }
+
+            // Query Global Request Processors
+            onStr = "*:type=GlobalRequestProcessor,*";
+            objectName = new ObjectName(onStr);
+            set = mBeanServer.queryMBeans(objectName, null);
+            iterator = set.iterator();
+            while (iterator.hasNext()) {
+                ObjectInstance oi = (ObjectInstance) iterator.next();
+                globalRequestProcessors.addElement(oi.getObjectName());
+            }
+
+            // Query Request Processors
+            onStr = "*:type=RequestProcessor,*";
+            objectName = new ObjectName(onStr);
+            set = mBeanServer.queryMBeans(objectName, null);
+            iterator = set.iterator();
+            while (iterator.hasNext()) {
+                ObjectInstance oi = (ObjectInstance) iterator.next();
+                requestProcessors.addElement(oi.getObjectName());
+            }
+
+            // Register with MBean server
+            onStr = "JMImplementation:type=MBeanServerDelegate";
+            objectName = new ObjectName(onStr);
+            mBeanServer.addNotificationListener(objectName, this, null, null);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+
+    /**
+     * Finalize this servlet.
+     */
+    public void destroy() {
+
+        ;       // No actions necessary
+
+    }
+
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // mode is flag for HTML or XML output
+        int mode = 0;
+        // if ?XML=true, set the mode to XML
+        if (request.getParameter("XML") != null 
+            && request.getParameter("XML").equals("true")) {
+            mode = 1;
+        }
+        StatusTransformer.setContentType(response, mode);
+
+        PrintWriter writer = response.getWriter();
+
+        boolean completeStatus = false;
+        if ((request.getPathInfo() != null) 
+            && (request.getPathInfo().equals("/all"))) {
+            completeStatus = true;
+        }
+        // use StatusTransformer to output status
+        StatusTransformer.writeHeader(writer,mode);
+
+        // Body Header Section
+        Object[] args = new Object[2];
+        args[0] = request.getContextPath();
+        if (completeStatus) {
+            args[1] = sm.getString("statusServlet.complete");
+        } else {
+            args[1] = sm.getString("statusServlet.title");
+        }
+        // use StatusTransformer to output status
+        StatusTransformer.writeBody(writer,args,mode);
+
+        // Manager Section
+        args = new Object[9];
+        args[0] = sm.getString("htmlManagerServlet.manager");
+        args[1] = response.encodeURL(request.getContextPath() + "/html/list");
+        args[2] = sm.getString("htmlManagerServlet.list");
+        args[3] = response.encodeURL
+            (request.getContextPath() + "/" +
+             sm.getString("htmlManagerServlet.helpHtmlManagerFile"));
+        args[4] = sm.getString("htmlManagerServlet.helpHtmlManager");
+        args[5] = response.encodeURL
+            (request.getContextPath() + "/" +
+             sm.getString("htmlManagerServlet.helpManagerFile"));
+        args[6] = sm.getString("htmlManagerServlet.helpManager");
+        if (completeStatus) {
+            args[7] = response.encodeURL
+                (request.getContextPath() + "/status");
+            args[8] = sm.getString("statusServlet.title");
+        } else {
+            args[7] = response.encodeURL
+                (request.getContextPath() + "/status/all");
+            args[8] = sm.getString("statusServlet.complete");
+        }
+        // use StatusTransformer to output status
+        StatusTransformer.writeManager(writer,args,mode);
+
+        // Server Header Section
+        args = new Object[7];
+        args[0] = sm.getString("htmlManagerServlet.serverTitle");
+        args[1] = sm.getString("htmlManagerServlet.serverVersion");
+        args[2] = sm.getString("htmlManagerServlet.serverJVMVersion");
+        args[3] = sm.getString("htmlManagerServlet.serverJVMVendor");
+        args[4] = sm.getString("htmlManagerServlet.serverOSName");
+        args[5] = sm.getString("htmlManagerServlet.serverOSVersion");
+        args[6] = sm.getString("htmlManagerServlet.serverOSArch");
+        // use StatusTransformer to output status
+        StatusTransformer.writePageHeading(writer,args,mode);
+
+        // Server Row Section
+        args = new Object[6];
+        args[0] = ServerInfo.getServerInfo();
+        args[1] = System.getProperty("java.runtime.version");
+        args[2] = System.getProperty("java.vm.vendor");
+        args[3] = System.getProperty("os.name");
+        args[4] = System.getProperty("os.version");
+        args[5] = System.getProperty("os.arch");
+        // use StatusTransformer to output status
+        StatusTransformer.writeServerInfo(writer, args, mode);
+
+        try {
+
+            // Display operating system statistics using APR if available
+            StatusTransformer.writeOSState(writer,mode);
+
+            // Display virtual machine statistics
+            StatusTransformer.writeVMState(writer,mode);
+
+            Enumeration enumeration = threadPools.elements();
+            while (enumeration.hasMoreElements()) {
+                ObjectName objectName = (ObjectName) enumeration.nextElement();
+                String name = objectName.getKeyProperty("name");
+                // use StatusTransformer to output status
+                StatusTransformer.writeConnectorState
+                    (writer, objectName,
+                     name, mBeanServer, globalRequestProcessors,
+                     requestProcessors, mode);
+            }
+
+            if ((request.getPathInfo() != null) 
+                && (request.getPathInfo().equals("/all"))) {
+                // Note: Retrieving the full status is much slower
+                // use StatusTransformer to output status
+                StatusTransformer.writeDetailedState
+                    (writer, mBeanServer, mode);
+            }
+
+        } catch (Exception e) {
+            throw new ServletException(e);
+        }
+
+        // use StatusTransformer to output status
+        StatusTransformer.writeFooter(writer, mode);
+
+    }
+
+    // ------------------------------------------- NotificationListener Methods
+
+
+    public void handleNotification(Notification notification,
+                                   java.lang.Object handback) {
+
+        if (notification instanceof MBeanServerNotification) {
+            ObjectName objectName = 
+                ((MBeanServerNotification) notification).getMBeanName();
+            if (notification.getType().equals
+                (MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
+                String type = objectName.getKeyProperty("type");
+                if (type != null) {
+                    if (type.equals("ProtocolHandler")) {
+                        protocolHandlers.addElement(objectName);
+                    } else if (type.equals("ThreadPool")) {
+                        threadPools.addElement(objectName);
+                    } else if (type.equals("GlobalRequestProcessor")) {
+                        globalRequestProcessors.addElement(objectName);
+                    } else if (type.equals("RequestProcessor")) {
+                        requestProcessors.addElement(objectName);
+                    }
+                }
+            } else if (notification.getType().equals
+                       (MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
+                String type = objectName.getKeyProperty("type");
+                if (type != null) {
+                    if (type.equals("ProtocolHandler")) {
+                        protocolHandlers.removeElement(objectName);
+                    } else if (type.equals("ThreadPool")) {
+                        threadPools.removeElement(objectName);
+                    } else if (type.equals("GlobalRequestProcessor")) {
+                        globalRequestProcessors.removeElement(objectName);
+                    } else if (type.equals("RequestProcessor")) {
+                        requestProcessors.removeElement(objectName);
+                    }
+                }
+                String j2eeType = objectName.getKeyProperty("j2eeType");
+                if (j2eeType != null) {
+                    
+                }
+            }
+        }
+
+    }
+
+
+}
diff --git a/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/StatusTransformer.java b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/StatusTransformer.java
new file mode 100644
index 0000000..db46151
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/classes/org/apache/catalina/manager/StatusTransformer.java
@@ -0,0 +1,933 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.manager;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.servlet.http.HttpServletResponse;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.tomcat.util.compat.JdkCompat;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+/**
+ * This is a refactoring of the servlet to externalize
+ * the output into a simple class. Although we could
+ * use XSLT, that is unnecessarily complex.
+ *
+ * @author Peter Lin
+ * @version $Revision$ $Date$
+ */
+
+public class StatusTransformer {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    public static void setContentType(HttpServletResponse response, 
+                                      int mode) {
+        if (mode == 0){
+            response.setContentType("text/html;charset="+Constants.CHARSET);
+        } else if (mode == 1){
+            response.setContentType("text/xml;charset="+Constants.CHARSET);
+        }
+    }
+
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public static void writeHeader(PrintWriter writer, int mode) {
+        if (mode == 0){
+            // HTML Header Section
+            writer.print(Constants.HTML_HEADER_SECTION);
+        } else if (mode == 1){
+            writer.write(Constants.XML_DECLARATION);
+            writer.write
+                (Constants.XML_STYLE);
+            writer.write("<status>");
+        }
+    }
+
+
+    /**
+     * Write the header body. XML output doesn't bother
+     * to output this stuff, since it's just title.
+     * 
+     * @param writer The output writer
+     * @param args What to write
+     * @param mode 0 means write 
+     */
+    public static void writeBody(PrintWriter writer, Object[] args, int mode) {
+        if (mode == 0){
+            writer.print(MessageFormat.format
+                         (Constants.BODY_HEADER_SECTION, args));
+        }
+    }
+
+
+    /**
+     * Write the manager webapp information.
+     * 
+     * @param writer The output writer
+     * @param args What to write
+     * @param mode 0 means write
+     */
+    public static void writeManager(PrintWriter writer, Object[] args, 
+                                    int mode) {
+        if (mode == 0){
+            writer.print(MessageFormat.format(Constants.MANAGER_SECTION, args));
+        }
+    }
+
+
+    public static void writePageHeading(PrintWriter writer, Object[] args, 
+                                        int mode) {
+        if (mode == 0){
+            writer.print(MessageFormat.format
+                         (Constants.SERVER_HEADER_SECTION, args));
+        }
+    }
+
+
+    public static void writeServerInfo(PrintWriter writer, Object[] args, 
+                                       int mode){
+        if (mode == 0){
+            writer.print(MessageFormat.format(Constants.SERVER_ROW_SECTION, args));
+        }
+    }
+
+
+    /**
+     * 
+     */
+    public static void writeFooter(PrintWriter writer, int mode) {
+        if (mode == 0){
+            // HTML Tail Section
+            writer.print(Constants.HTML_TAIL_SECTION);
+        } else if (mode == 1){
+            writer.write("</status>");
+        }
+    }
+
+
+    /**
+     * Write the OS state. Mode 0 will generate HTML.
+     * Mode 1 will generate XML.
+     */
+    public static void writeOSState(PrintWriter writer, int mode) {
+        long[] result = new long[16];
+        boolean ok = false;
+        try {
+            String methodName = "info";
+            Class paramTypes[] = new Class[1];
+            paramTypes[0] = result.getClass();
+            Object paramValues[] = new Object[1];
+            paramValues[0] = result;
+            Method method = Class.forName("org.apache.tomcat.jni.OS")
+                .getMethod(methodName, paramTypes);
+            method.invoke(null, paramValues);
+            ok = true;
+        } catch (Throwable t) {
+            // Ignore
+        }
+        
+        if (ok) {
+            if (mode == 0){
+                writer.print("<h1>OS</h1>");
+
+                writer.print("<p>");
+                writer.print(" Physical memory: ");
+                writer.print(formatSize(new Long(result[0]), true));
+                writer.print(" Available memory: ");
+                writer.print(formatSize(new Long(result[1]), true));
+                writer.print(" Total page file: ");
+                writer.print(formatSize(new Long(result[2]), true));
+                writer.print(" Free page file: ");
+                writer.print(formatSize(new Long(result[3]), true));
+                writer.print(" Memory load: ");
+                writer.print(new Long(result[6]));
+                writer.print("<br>");
+                writer.print(" Process kernel time: ");
+                writer.print(formatTime(new Long(result[11] / 1000), true));
+                writer.print(" Process user time: ");
+                writer.print(formatTime(new Long(result[12] / 1000), true));
+                writer.print("</p>");
+            } else if (mode == 1){
+            }
+        }
+        
+    }
+    
+    
+    /**
+     * Write the VM state. Mode 0 will generate HTML.
+     * Mode 1 will generate XML.
+     */
+    public static void writeVMState(PrintWriter writer, int mode)
+        throws Exception {
+
+        if (mode == 0){
+            writer.print("<h1>JVM</h1>");
+
+            writer.print("<p>");
+            writer.print(" Free memory: ");
+            writer.print(formatSize
+                         (new Long(Runtime.getRuntime().freeMemory()), true));
+            writer.print(" Total memory: ");
+            writer.print(formatSize
+                         (new Long(Runtime.getRuntime().totalMemory()), true));
+            writer.print(" Max memory: ");
+            writer.print(formatSize
+                         (new Long(JdkCompat.getJdkCompat().getMaxMemory()), 
+                          true));
+            writer.print("</p>");
+        } else if (mode == 1){
+            writer.write("<jvm>");
+
+            writer.write("<memory");
+            writer.write(" free='" + Runtime.getRuntime().freeMemory() + "'");
+            writer.write(" total='" + Runtime.getRuntime().totalMemory() + "'");
+            writer.write(" max='" + JdkCompat.getJdkCompat().getMaxMemory() + "'/>");
+
+            writer.write("</jvm>");
+        }
+
+    }
+
+
+    /**
+     * Write connector state.
+     */
+    public static void writeConnectorState(PrintWriter writer, 
+                                           ObjectName tpName, String name,
+                                           MBeanServer mBeanServer,
+                                           Vector globalRequestProcessors,
+                                           Vector requestProcessors,
+                                           int mode)
+        throws Exception {
+
+        if (mode == 0) {
+            writer.print("<h1>");
+            writer.print(name);
+            writer.print("</h1>");
+
+            writer.print("<p>");
+            writer.print(" Max threads: ");
+            writer.print(mBeanServer.getAttribute(tpName, "maxThreads"));
+            writer.print(" Min spare threads: ");
+            writer.print(mBeanServer.getAttribute(tpName, "minSpareThreads"));
+            writer.print(" Max spare threads: ");
+            writer.print(mBeanServer.getAttribute(tpName, "maxSpareThreads"));
+            writer.print(" Current thread count: ");
+            writer.print(mBeanServer.getAttribute(tpName, "currentThreadCount"));
+            writer.print(" Current thread busy: ");
+            writer.print(mBeanServer.getAttribute(tpName, "currentThreadsBusy"));
+            try {
+                Object value = mBeanServer.getAttribute(tpName, "keepAliveCount");
+                writer.print(" Keeped alive sockets count: ");
+                writer.print(value);
+            } catch (Exception e) {
+                // Ignore
+            }
+            
+            writer.print("<br>");
+
+            ObjectName grpName = null;
+
+            Enumeration enumeration = globalRequestProcessors.elements();
+            while (enumeration.hasMoreElements()) {
+                ObjectName objectName = (ObjectName) enumeration.nextElement();
+                if (name.equals(objectName.getKeyProperty("name"))) {
+                    grpName = objectName;
+                }
+            }
+
+            if (grpName == null) {
+                return;
+            }
+
+            writer.print(" Max processing time: ");
+            writer.print(formatTime(mBeanServer.getAttribute
+                                    (grpName, "maxTime"), false));
+            writer.print(" Processing time: ");
+            writer.print(formatTime(mBeanServer.getAttribute
+                                    (grpName, "processingTime"), true));
+            writer.print(" Request count: ");
+            writer.print(mBeanServer.getAttribute(grpName, "requestCount"));
+            writer.print(" Error count: ");
+            writer.print(mBeanServer.getAttribute(grpName, "errorCount"));
+            writer.print(" Bytes received: ");
+            writer.print(formatSize(mBeanServer.getAttribute
+                                    (grpName, "bytesReceived"), true));
+            writer.print(" Bytes sent: ");
+            writer.print(formatSize(mBeanServer.getAttribute
+                                    (grpName, "bytesSent"), true));
+            writer.print("</p>");
+
+            writer.print("<table border=\"0\"><tr><th>Stage</th><th>Time</th><th>B Sent</th><th>B Recv</th><th>Client</th><th>VHost</th><th>Request</th></tr>");
+
+            enumeration = requestProcessors.elements();
+            while (enumeration.hasMoreElements()) {
+                ObjectName objectName = (ObjectName) enumeration.nextElement();
+                if (name.equals(objectName.getKeyProperty("worker"))) {
+                    writer.print("<tr>");
+                    writeProcessorState(writer, objectName, mBeanServer, mode);
+                    writer.print("</tr>");
+                }
+            }
+
+            writer.print("</table>");
+
+            writer.print("<p>");
+            writer.print("P: Parse and prepare request S: Service F: Finishing R: Ready K: Keepalive");
+            writer.print("</p>");
+        } else if (mode == 1){
+            writer.write("<connector name='" + name + "'>");
+
+            writer.write("<threadInfo ");
+            writer.write(" maxThreads=\"" + mBeanServer.getAttribute(tpName, "maxThreads") + "\"");
+            writer.write(" minSpareThreads=\"" + mBeanServer.getAttribute(tpName, "minSpareThreads") + "\"");
+            writer.write(" maxSpareThreads=\"" + mBeanServer.getAttribute(tpName, "maxSpareThreads") + "\"");
+            writer.write(" currentThreadCount=\"" + mBeanServer.getAttribute(tpName, "currentThreadCount") + "\"");
+            writer.write(" currentThreadsBusy=\"" + mBeanServer.getAttribute(tpName, "currentThreadsBusy") + "\"");
+            writer.write(" />");
+
+            ObjectName grpName = null;
+
+            Enumeration enumeration = globalRequestProcessors.elements();
+            while (enumeration.hasMoreElements()) {
+                ObjectName objectName = (ObjectName) enumeration.nextElement();
+                if (name.equals(objectName.getKeyProperty("name"))) {
+                    grpName = objectName;
+                }
+            }
+
+            if (grpName != null) {
+
+                writer.write("<requestInfo ");
+                writer.write(" maxTime=\"" + mBeanServer.getAttribute(grpName, "maxTime") + "\"");
+                writer.write(" processingTime=\"" + mBeanServer.getAttribute(grpName, "processingTime") + "\"");
+                writer.write(" requestCount=\"" + mBeanServer.getAttribute(grpName, "requestCount") + "\"");
+                writer.write(" errorCount=\"" + mBeanServer.getAttribute(grpName, "errorCount") + "\"");
+                writer.write(" bytesReceived=\"" + mBeanServer.getAttribute(grpName, "bytesReceived") + "\"");
+                writer.write(" bytesSent=\"" + mBeanServer.getAttribute(grpName, "bytesSent") + "\"");
+                writer.write(" />");
+
+                writer.write("<workers>");
+                enumeration = requestProcessors.elements();
+                while (enumeration.hasMoreElements()) {
+                    ObjectName objectName = (ObjectName) enumeration.nextElement();
+                    if (name.equals(objectName.getKeyProperty("worker"))) {
+                        writeProcessorState(writer, objectName, mBeanServer, mode);
+                    }
+                }
+                writer.write("</workers>");
+            }
+
+            writer.write("</connector>");
+        }
+
+    }
+
+
+    /**
+     * Write processor state.
+     */
+    protected static void writeProcessorState(PrintWriter writer, 
+                                              ObjectName pName,
+                                              MBeanServer mBeanServer, 
+                                              int mode)
+        throws Exception {
+
+        Integer stageValue = 
+            (Integer) mBeanServer.getAttribute(pName, "stage");
+        int stage = stageValue.intValue();
+        boolean fullStatus = true;
+        boolean showRequest = true;
+        String stageStr = null;
+
+        switch (stage) {
+
+        case (1/*org.apache.coyote.Constants.STAGE_PARSE*/):
+            stageStr = "P";
+            fullStatus = false;
+            break;
+        case (2/*org.apache.coyote.Constants.STAGE_PREPARE*/):
+            stageStr = "P";
+            fullStatus = false;
+            break;
+        case (3/*org.apache.coyote.Constants.STAGE_SERVICE*/):
+            stageStr = "S";
+            break;
+        case (4/*org.apache.coyote.Constants.STAGE_ENDINPUT*/):
+            stageStr = "F";
+            break;
+        case (5/*org.apache.coyote.Constants.STAGE_ENDOUTPUT*/):
+            stageStr = "F";
+            break;
+        case (7/*org.apache.coyote.Constants.STAGE_ENDED*/):
+            stageStr = "R";
+            fullStatus = false;
+            break;
+        case (6/*org.apache.coyote.Constants.STAGE_KEEPALIVE*/):
+            stageStr = "K";
+            fullStatus = true;
+            showRequest = false;
+            break;
+        case (0/*org.apache.coyote.Constants.STAGE_NEW*/):
+            stageStr = "R";
+            fullStatus = false;
+            break;
+        default:
+            // Unknown stage
+            stageStr = "?";
+            fullStatus = false;
+
+        }
+
+        if (mode == 0) {
+            writer.write("<td><strong>");
+            writer.write(stageStr);
+            writer.write("</strong></td>");
+
+            if (fullStatus) {
+                writer.write("<td>");
+                writer.print(formatTime(mBeanServer.getAttribute
+                                        (pName, "requestProcessingTime"), false));
+                writer.write("</td>");
+                writer.write("<td>");
+                if (showRequest) {
+                    writer.print(formatSize(mBeanServer.getAttribute
+                                            (pName, "requestBytesSent"), false));
+                } else {
+                    writer.write("?");
+                }
+                writer.write("</td>");
+                writer.write("<td>");
+                if (showRequest) {
+                    writer.print(formatSize(mBeanServer.getAttribute
+                                            (pName, "requestBytesReceived"), 
+                                            false));
+                } else {
+                    writer.write("?");
+                }
+                writer.write("</td>");
+                writer.write("<td>");
+                writer.print(filter(mBeanServer.getAttribute
+                                    (pName, "remoteAddr")));
+                writer.write("</td>");
+                writer.write("<td nowrap>");
+                writer.write(filter(mBeanServer.getAttribute
+                                    (pName, "virtualHost")));
+                writer.write("</td>");
+                writer.write("<td nowrap>");
+                if (showRequest) {
+                    writer.write(filter(mBeanServer.getAttribute
+                                        (pName, "method")));
+                    writer.write(" ");
+                    writer.write(filter(mBeanServer.getAttribute
+                                        (pName, "currentUri")));
+                    String queryString = (String) mBeanServer.getAttribute
+                        (pName, "currentQueryString");
+                    if ((queryString != null) && (!queryString.equals(""))) {
+                        writer.write("?");
+                        writer.print(RequestUtil.filter(queryString));
+                    }
+                    writer.write(" ");
+                    writer.write(filter(mBeanServer.getAttribute
+                                        (pName, "protocol")));
+                } else {
+                    writer.write("?");
+                }
+                writer.write("</td>");
+            } else {
+                writer.write("<td>?</td><td>?</td><td>?</td><td>?</td><td>?</td><td>?</td>");
+            }
+        } else if (mode == 1){
+            writer.write("<worker ");
+            writer.write(" stage=\"" + stageStr + "\"");
+
+            if (fullStatus) {
+                writer.write(" requestProcessingTime=\"" 
+                             + mBeanServer.getAttribute
+                             (pName, "requestProcessingTime") + "\"");
+                writer.write(" requestBytesSent=\"");
+                if (showRequest) {
+                    writer.write("" + mBeanServer.getAttribute
+                                 (pName, "requestBytesSent"));
+                } else {
+                    writer.write("0");
+                }
+                writer.write("\"");
+                writer.write(" requestBytesReceived=\"");
+                if (showRequest) {
+                    writer.write("" + mBeanServer.getAttribute
+                                 (pName, "requestBytesReceived"));
+                } else {
+                    writer.write("0");
+                }
+                writer.write("\"");
+                writer.write(" remoteAddr=\"" 
+                             + filter(mBeanServer.getAttribute
+                                      (pName, "remoteAddr")) + "\"");
+                writer.write(" virtualHost=\"" 
+                             + filter(mBeanServer.getAttribute
+                                      (pName, "virtualHost")) + "\"");
+
+                if (showRequest) {
+                    writer.write(" method=\"" 
+                                 + filter(mBeanServer.getAttribute
+                                          (pName, "method")) + "\"");
+                    writer.write(" currentUri=\"" 
+                                 + filter(mBeanServer.getAttribute
+                                          (pName, "currentUri")) + "\"");
+
+                    String queryString = (String) mBeanServer.getAttribute
+                        (pName, "currentQueryString");
+                    if ((queryString != null) && (!queryString.equals(""))) {
+                        writer.write(" currentQueryString=\"" 
+                                     + RequestUtil.filter(queryString) + "\"");
+                    } else {
+                        writer.write(" currentQueryString=\"&#63;\"");
+                    }
+                    writer.write(" protocol=\"" 
+                                 + filter(mBeanServer.getAttribute
+                                          (pName, "protocol")) + "\"");
+                } else {
+                    writer.write(" method=\"&#63;\"");
+                    writer.write(" currentUri=\"&#63;\"");
+                    writer.write(" currentQueryString=\"&#63;\"");
+                    writer.write(" protocol=\"&#63;\"");
+                }
+            } else {
+                writer.write(" requestProcessingTime=\"0\"");
+                writer.write(" requestBytesSent=\"0\"");
+                writer.write(" requestBytesRecieved=\"0\"");
+                writer.write(" remoteAddr=\"&#63;\"");
+                writer.write(" virtualHost=\"&#63;\"");
+                writer.write(" method=\"&#63;\"");
+                writer.write(" currentUri=\"&#63;\"");
+                writer.write(" currentQueryString=\"&#63;\"");
+                writer.write(" protocol=\"&#63;\"");
+            }
+            writer.write(" />");
+        }
+
+    }
+
+
+    /**
+     * Write applications state.
+     */
+    public static void writeDetailedState(PrintWriter writer,
+                                          MBeanServer mBeanServer, int mode)
+        throws Exception {
+
+        if (mode == 0){
+            ObjectName queryHosts = new ObjectName("*:j2eeType=WebModule,*");
+            Set hostsON = mBeanServer.queryNames(queryHosts, null);
+
+            // Navigation menu
+            writer.print("<h1>");
+            writer.print("Application list");
+            writer.print("</h1>");
+
+            writer.print("<p>");
+            int count = 0;
+            Iterator iterator = hostsON.iterator();
+            while (iterator.hasNext()) {
+                ObjectName contextON = (ObjectName) iterator.next();
+                String webModuleName = contextON.getKeyProperty("name");
+                if (webModuleName.startsWith("//")) {
+                    webModuleName = webModuleName.substring(2);
+                }
+                int slash = webModuleName.indexOf("/");
+                if (slash == -1) {
+                    count++;
+                    continue;
+                }
+
+                writer.print("<a href=\"#" + (count++) + ".0\">");
+                writer.print(webModuleName);
+                writer.print("</a>");
+                if (iterator.hasNext()) {
+                    writer.print("<br>");
+                }
+
+            }
+            writer.print("</p>");
+
+            // Webapp list
+            count = 0;
+            iterator = hostsON.iterator();
+            while (iterator.hasNext()) {
+                ObjectName contextON = (ObjectName) iterator.next();
+                writer.print("<a class=\"A.name\" name=\"" 
+                             + (count++) + ".0\">");
+                writeContext(writer, contextON, mBeanServer, mode);
+            }
+
+        } else if (mode == 1){
+            // for now we don't write out the Detailed state in XML
+        }
+
+    }
+
+
+    /**
+     * Write context state.
+     */
+    protected static void writeContext(PrintWriter writer, 
+                                       ObjectName objectName,
+                                       MBeanServer mBeanServer, int mode)
+        throws Exception {
+
+        if (mode == 0){
+            String webModuleName = objectName.getKeyProperty("name");
+            String name = webModuleName;
+            if (name == null) {
+                return;
+            }
+            
+            String hostName = null;
+            String contextName = null;
+            if (name.startsWith("//")) {
+                name = name.substring(2);
+            }
+            int slash = name.indexOf("/");
+            if (slash != -1) {
+                hostName = name.substring(0, slash);
+                contextName = name.substring(slash);
+            } else {
+                return;
+            }
+
+            ObjectName queryManager = new ObjectName
+                (objectName.getDomain() + ":type=Manager,path=" + contextName 
+                 + ",host=" + hostName + ",*");
+            Set managersON = mBeanServer.queryNames(queryManager, null);
+            ObjectName managerON = null;
+            Iterator iterator2 = managersON.iterator();
+            while (iterator2.hasNext()) {
+                managerON = (ObjectName) iterator2.next();
+            }
+
+            ObjectName queryJspMonitor = new ObjectName
+                (objectName.getDomain() + ":type=JspMonitor,WebModule=" +
+                 webModuleName + ",*");
+            Set jspMonitorONs = mBeanServer.queryNames(queryJspMonitor, null);
+
+            // Special case for the root context
+            if (contextName.equals("/")) {
+                contextName = "";
+            }
+
+            writer.print("<h1>");
+            writer.print(name);
+            writer.print("</h1>");
+            writer.print("</a>");
+
+            writer.print("<p>");
+            Object startTime = mBeanServer.getAttribute(objectName,
+                                                        "startTime");
+            writer.print(" Start time: " +
+                         new Date(((Long) startTime).longValue()));
+            writer.print(" Startup time: ");
+            writer.print(formatTime(mBeanServer.getAttribute
+                                    (objectName, "startupTime"), false));
+            writer.print(" TLD scan time: ");
+            writer.print(formatTime(mBeanServer.getAttribute
+                                    (objectName, "tldScanTime"), false));
+            if (managerON != null) {
+                writeManager(writer, managerON, mBeanServer, mode);
+            }
+            if (jspMonitorONs != null) {
+                writeJspMonitor(writer, jspMonitorONs, mBeanServer, mode);
+            }
+            writer.print("</p>");
+
+            String onStr = objectName.getDomain() 
+                + ":j2eeType=Servlet,WebModule=" + webModuleName + ",*";
+            ObjectName servletObjectName = new ObjectName(onStr);
+            Set set = mBeanServer.queryMBeans(servletObjectName, null);
+            Iterator iterator = set.iterator();
+            while (iterator.hasNext()) {
+                ObjectInstance oi = (ObjectInstance) iterator.next();
+                writeWrapper(writer, oi.getObjectName(), mBeanServer, mode);
+            }
+
+        } else if (mode == 1){
+            // for now we don't write out the context in XML
+        }
+
+    }
+
+
+    /**
+     * Write detailed information about a manager.
+     */
+    public static void writeManager(PrintWriter writer, ObjectName objectName,
+                                    MBeanServer mBeanServer, int mode)
+        throws Exception {
+
+        if (mode == 0) {
+            writer.print("<br>");
+            writer.print(" Active sessions: ");
+            writer.print(mBeanServer.getAttribute
+                         (objectName, "activeSessions"));
+            writer.print(" Session count: ");
+            writer.print(mBeanServer.getAttribute
+                         (objectName, "sessionCounter"));
+            writer.print(" Max active sessions: ");
+            writer.print(mBeanServer.getAttribute(objectName, "maxActive"));
+            writer.print(" Rejected session creations: ");
+            writer.print(mBeanServer.getAttribute
+                         (objectName, "rejectedSessions"));
+            writer.print(" Expired sessions: ");
+            writer.print(mBeanServer.getAttribute
+                         (objectName, "expiredSessions"));
+            writer.print(" Longest session alive time: ");
+            writer.print(formatSeconds(mBeanServer.getAttribute(
+                                                    objectName,
+                                                    "sessionMaxAliveTime")));
+            writer.print(" Average session alive time: ");
+            writer.print(formatSeconds(mBeanServer.getAttribute(
+                                                    objectName,
+                                                    "sessionAverageAliveTime")));
+            writer.print(" Processing time: ");
+            writer.print(formatTime(mBeanServer.getAttribute
+                                    (objectName, "processingTime"), false));
+        } else if (mode == 1) {
+            // for now we don't write out the wrapper details
+        }
+
+    }
+
+
+    /**
+     * Write JSP monitoring information.
+     */
+    public static void writeJspMonitor(PrintWriter writer,
+                                       Set jspMonitorONs,
+                                       MBeanServer mBeanServer,
+                                       int mode)
+            throws Exception {
+
+        int jspCount = 0;
+        int jspReloadCount = 0;
+
+        Iterator iter = jspMonitorONs.iterator();
+        while (iter.hasNext()) {
+            ObjectName jspMonitorON = (ObjectName) iter.next();
+            Object obj = mBeanServer.getAttribute(jspMonitorON, "jspCount");
+            jspCount += ((Integer) obj).intValue();
+            obj = mBeanServer.getAttribute(jspMonitorON, "jspReloadCount");
+            jspReloadCount += ((Integer) obj).intValue();
+        }
+
+        if (mode == 0) {
+            writer.print("<br>");
+            writer.print(" JSPs loaded: ");
+            writer.print(jspCount);
+            writer.print(" JSPs reloaded: ");
+            writer.print(jspReloadCount);
+        } else if (mode == 1) {
+            // for now we don't write out anything
+        }
+    }
+
+
+    /**
+     * Write detailed information about a wrapper.
+     */
+    public static void writeWrapper(PrintWriter writer, ObjectName objectName,
+                                    MBeanServer mBeanServer, int mode)
+        throws Exception {
+
+        if (mode == 0) {
+            String servletName = objectName.getKeyProperty("name");
+            
+            String[] mappings = (String[]) 
+                mBeanServer.invoke(objectName, "findMappings", null, null);
+            
+            writer.print("<h2>");
+            writer.print(servletName);
+            if ((mappings != null) && (mappings.length > 0)) {
+                writer.print(" [ ");
+                for (int i = 0; i < mappings.length; i++) {
+                    writer.print(mappings[i]);
+                    if (i < mappings.length - 1) {
+                        writer.print(" , ");
+                    }
+                }
+                writer.print(" ] ");
+            }
+            writer.print("</h2>");
+            
+            writer.print("<p>");
+            writer.print(" Processing time: ");
+            writer.print(formatTime(mBeanServer.getAttribute
+                                    (objectName, "processingTime"), true));
+            writer.print(" Max time: ");
+            writer.print(formatTime(mBeanServer.getAttribute
+                                    (objectName, "maxTime"), false));
+            writer.print(" Request count: ");
+            writer.print(mBeanServer.getAttribute(objectName, "requestCount"));
+            writer.print(" Error count: ");
+            writer.print(mBeanServer.getAttribute(objectName, "errorCount"));
+            writer.print(" Load time: ");
+            writer.print(formatTime(mBeanServer.getAttribute
+                                    (objectName, "loadTime"), false));
+            writer.print(" Classloading time: ");
+            writer.print(formatTime(mBeanServer.getAttribute
+                                    (objectName, "classLoadTime"), false));
+            writer.print("</p>");
+        } else if (mode == 1){
+            // for now we don't write out the wrapper details
+        }
+
+    }
+
+
+    /**
+     * Filter the specified message string for characters that are sensitive
+     * in HTML.  This avoids potential attacks caused by including JavaScript
+     * codes in the request URL that is often reported in error messages.
+     *
+     * @param obj The message string to be filtered
+     */
+    public static String filter(Object obj) {
+
+        if (obj == null)
+            return ("?");
+        String message = obj.toString();
+
+        char content[] = new char[message.length()];
+        message.getChars(0, message.length(), content, 0);
+        StringBuffer result = new StringBuffer(content.length + 50);
+        for (int i = 0; i < content.length; i++) {
+            switch (content[i]) {
+            case '<':
+                result.append("&lt;");
+                break;
+            case '>':
+                result.append("&gt;");
+                break;
+            case '&':
+                result.append("&amp;");
+                break;
+            case '"':
+                result.append("&quot;");
+                break;
+            default:
+                result.append(content[i]);
+            }
+        }
+        return (result.toString());
+
+    }
+
+
+    /**
+     * Display the given size in bytes, either as KB or MB.
+     *
+     * @param mb true to display megabytes, false for kilobytes
+     */
+    public static String formatSize(Object obj, boolean mb) {
+
+        long bytes = -1L;
+
+        if (obj instanceof Long) {
+            bytes = ((Long) obj).longValue();
+        } else if (obj instanceof Integer) {
+            bytes = ((Integer) obj).intValue();
+        }
+
+        if (mb) {
+            long mbytes = bytes / (1024 * 1024);
+            long rest = 
+                ((bytes - (mbytes * (1024 * 1024))) * 100) / (1024 * 1024);
+            return (mbytes + "." + ((rest < 10) ? "0" : "") + rest + " MB");
+        } else {
+            return ((bytes / 1024) + " KB");
+        }
+
+    }
+
+
+    /**
+     * Display the given time in ms, either as ms or s.
+     *
+     * @param seconds true to display seconds, false for milliseconds
+     */
+    public static String formatTime(Object obj, boolean seconds) {
+
+        long time = -1L;
+
+        if (obj instanceof Long) {
+            time = ((Long) obj).longValue();
+        } else if (obj instanceof Integer) {
+            time = ((Integer) obj).intValue();
+        }
+
+        if (seconds) {
+            return ((((float) time ) / 1000) + " s");
+        } else {
+            return (time + " ms");
+        }
+    }
+
+
+    /**
+     * Formats the given time (given in seconds) as a string.
+     *
+     * @param obj Time object to be formatted as string
+     *
+     * @return String formatted time
+     */
+    public static String formatSeconds(Object obj) {
+
+        long time = -1L;
+
+        if (obj instanceof Long) {
+            time = ((Long) obj).longValue();
+        } else if (obj instanceof Integer) {
+            time = ((Integer) obj).intValue();
+        }
+
+        return (time + " s");
+    }
+
+}
diff --git a/container/webapps/manager/WEB-INF/web.xml b/container/webapps/manager/WEB-INF/web.xml
new file mode 100644
index 0000000..95af36e
--- /dev/null
+++ b/container/webapps/manager/WEB-INF/web.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+  <display-name>Tomcat Manager Application</display-name>
+  <description>
+    A scriptable management web application for the Tomcat Web Server;
+	Manager lets you view, load/unload/etc particular web applications.
+  </description>
+
+  <!-- Define the Manager Servlet
+       Change servlet-class to: org.apache.catalina.servlets.HTMLManagerServlet
+       to get a Servlet with a more intuitive HTML interface, don't change if you
+       have software that is expected to parse the output from ManagerServlet
+       since they're not compatible.
+   -->
+  <servlet>
+    <servlet-name>Manager</servlet-name>
+    <servlet-class>org.apache.catalina.manager.ManagerServlet</servlet-class>
+    <init-param>
+      <param-name>debug</param-name>
+      <param-value>2</param-value>
+    </init-param>
+  </servlet>
+  <servlet>
+    <servlet-name>HTMLManager</servlet-name>
+    <servlet-class>org.apache.catalina.manager.HTMLManagerServlet</servlet-class>
+    <init-param>
+      <param-name>debug</param-name>
+      <param-value>2</param-value>
+    </init-param>
+  </servlet>
+  <servlet>
+    <servlet-name>Status</servlet-name>
+    <servlet-class>org.apache.catalina.manager.StatusManagerServlet</servlet-class>
+    <init-param>
+      <param-name>debug</param-name>
+      <param-value>0</param-value>
+    </init-param>
+  </servlet>
+
+  <servlet>
+    <servlet-name>JMXProxy</servlet-name>
+    <servlet-class>org.apache.catalina.manager.JMXProxyServlet</servlet-class>
+  </servlet>
+
+  <!-- Define the Manager Servlet Mapping -->
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/list</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/sessions</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/start</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/stop</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/install</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/remove</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/deploy</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/undeploy</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/reload</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/save</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/serverinfo</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/roles</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/resources</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>Status</servlet-name>
+    <url-pattern>/status/*</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>JMXProxy</servlet-name>
+      <url-pattern>/jmxproxy/*</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>HTMLManager</servlet-name>
+    <url-pattern>/html/*</url-pattern>
+  </servlet-mapping>
+
+  <!-- Define reference to the user database for looking up roles -->
+  <resource-env-ref>
+    <description>
+      Link to the UserDatabase instance from which we request lists of
+      defined role names.  Typically, this will be connected to the global
+      user database with a ResourceLink element in server.xml or the context
+      configuration file for the Manager web application.
+    </description>
+    <resource-env-ref-name>users</resource-env-ref-name>
+    <resource-env-ref-type>
+      org.apache.catalina.UserDatabase
+    </resource-env-ref-type>
+  </resource-env-ref>
+
+  <!-- Define a Security Constraint on this Application -->
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>HTMLManger and Manager command</web-resource-name>
+      <url-pattern>/jmxproxy/*</url-pattern>
+      <url-pattern>/html/*</url-pattern>
+      <url-pattern>/list</url-pattern>
+      <url-pattern>/sessions</url-pattern>
+      <url-pattern>/start</url-pattern>
+      <url-pattern>/stop</url-pattern>
+      <url-pattern>/install</url-pattern>
+      <url-pattern>/remove</url-pattern>
+      <url-pattern>/deploy</url-pattern>
+      <url-pattern>/undeploy</url-pattern>
+      <url-pattern>/reload</url-pattern>
+      <url-pattern>/save</url-pattern>
+      <url-pattern>/serverinfo</url-pattern>
+      <url-pattern>/status/*</url-pattern>
+      <url-pattern>/roles</url-pattern>
+      <url-pattern>/resources</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+       <!-- NOTE:  This role is not present in the default users file -->
+       <role-name>manager</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <!-- Define the Login Configuration for this Application -->
+  <login-config>
+    <auth-method>BASIC</auth-method>
+    <realm-name>Tomcat Manager Application</realm-name>
+  </login-config>
+
+  <!-- Security roles referenced by this web application -->
+  <security-role>
+    <description>
+      The role that is required to log in to the Manager Application
+    </description>
+    <role-name>manager</role-name>
+  </security-role>
+
+</web-app>
diff --git a/container/webapps/manager/build.xml b/container/webapps/manager/build.xml
new file mode 100644
index 0000000..156af90
--- /dev/null
+++ b/container/webapps/manager/build.xml
@@ -0,0 +1,129 @@
+<project name="manager" default="build-main" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="../../build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="webapps.build"   value="../build"/>
+  <property name="webapps.dist"    value="../dist"/>
+  <property name="webapp.name"     value="manager"/>
+
+  <!-- Dependent JARs and files -->
+  <property name="servlet-api.jar" value="${api.home}/jsr154/dist/lib/servlet-api.jar"/>
+  <property name="jsp-api.jar"     value="${api.home}/jsr152/dist/lib/jsp-api.jar"/>
+
+  <!-- Construct Admin classpath -->
+  <path id="manager.classpath">
+    <pathelement location="${catalina.deploy}/classes"/>
+    <pathelement location="${commons-fileupload.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>   
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${jsp-api.jar}"/>
+  </path>
+
+
+	  <!-- ======================== BUILD: Copy JARs ========================== -->
+	  <target name="copy-fileupload.jar">
+	    <copy todir="${webapps.build}/${webapp.name}/WEB-INF/lib" file="${commons-fileupload.jar}"/>
+	  </target>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${webapps.build}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/images"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF/classes"/>
+  </target>
+
+
+  <!-- ================ BUILD: Copy Static Files ========================== -->
+  <target name="build-static" depends="build-prepare,copy-fileupload.jar">
+    <copy todir="${webapps.build}/${webapp.name}">
+      <fileset dir=".">
+        <exclude name="build.*"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <target name="build-main" depends="build-static">
+
+    <!-- Top Level Directory -->
+    <style basedir="../docs"
+           destdir="${webapps.build}/${webapp.name}"
+           extension=".html"
+           style="tomcat-docs.xsl"
+           excludes="build.xml project.xml"
+           includes="manager-howto.xml,html-manager-howto.xml">
+      <param name="relative-path" expression="."/>
+      <param name="project-menu" expression="nomenu"/>
+      <param name="standalone" expression="standalone"/>
+    </style>
+
+    <!-- Images Subdirectory -->
+    <copy todir="${webapps.build}/${webapp.name}/images">
+      <fileset dir="../docs/images">
+        <exclude name="printer.gif"/>
+      </fileset>
+    </copy>
+
+    <javac   srcdir="WEB-INF/classes" 
+             destdir="${webapps.build}/${webapp.name}/WEB-INF/classes"
+             debug="${compile.debug}" deprecation="${compile.deprecation}"
+             optimize="${compile.optimize}"
+             excludes="**/CVS/**">
+      <classpath refid="manager.classpath" />
+    </javac>
+
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean and build manager webapp"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${webapps.build}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Directories =================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${webapps.dist}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Distribution Files ============ -->
+  <target name="dist" depends="build-main,dist-prepare"
+   description="Create manager webapp binary distribution">
+      <jar   jarfile="${webapps.dist}/${webapp.name}.war"
+             basedir="${webapps.build}/${webapp.name}" includes="**"/>
+  </target>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <deltree dir="${dist.dir}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
diff --git a/container/webapps/manager/manager.xml b/container/webapps/manager/manager.xml
new file mode 100644
index 0000000..a2c026e
--- /dev/null
+++ b/container/webapps/manager/manager.xml
@@ -0,0 +1,17 @@
+<!--
+
+    Context configuration file for the Tomcat Manager Web App
+
+    $Id$
+
+-->
+
+
+<Context docBase="${catalina.home}/server/webapps/manager"
+         privileged="true" antiResourceLocking="false" antiJARLocking="false">
+
+  <!-- Link to the user database we will get roles from -->
+  <ResourceLink name="users" global="UserDatabase"
+                type="org.apache.catalina.UserDatabase"/>
+
+</Context>
diff --git a/container/webapps/manager/status.xsd b/container/webapps/manager/status.xsd
new file mode 100644
index 0000000..0f266de
--- /dev/null
+++ b/container/webapps/manager/status.xsd
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- edited with XMLSPY v5 rel. 4 U (http://www.xmlspy.com) by peter lin (consultant) -->
+<!--W3C Schema generated by XMLSPY v5 rel. 4 U (http://www.xmlspy.com)-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
+	<xs:complexType name="connector">
+		<xs:sequence>
+			<xs:element name="threadInfo" type="threadInfo"/>
+			<xs:element name="requestInfo" type="requestInfo"/>
+			<xs:element name="workers" type="workers"/>
+		</xs:sequence>
+		<xs:attribute name="name" type="xs:string" use="required"/>
+	</xs:complexType>
+	<xs:complexType name="jvm">
+		<xs:sequence>
+			<xs:element name="memory" type="memory"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="memory">
+		<xs:attribute name="free" type="xs:long" use="required"/>
+		<xs:attribute name="total" type="xs:long" use="required"/>
+		<xs:attribute name="max" type="xs:long" use="required"/>
+	</xs:complexType>
+	<xs:complexType name="requestInfo">
+		<xs:attribute name="maxTime" type="xs:long" use="required"/>
+		<xs:attribute name="processingTime" type="xs:int" use="required"/>
+		<xs:attribute name="requestCount" type="xs:long" use="required"/>
+		<xs:attribute name="errorCount" type="xs:long" use="required"/>
+		<xs:attribute name="bytesReceived" type="xs:long" use="required"/>
+		<xs:attribute name="bytesSent" type="xs:long" use="required"/>
+	</xs:complexType>
+	<xs:element name="status">
+		<xs:complexType>
+			<xs:sequence>
+				<xs:element name="jvm" type="jvm"/>
+				<xs:element name="connector" type="connector"/>
+			</xs:sequence>
+		</xs:complexType>
+	</xs:element>
+	<xs:complexType name="threadInfo">
+		<xs:attribute name="maxThreads" type="xs:int" use="required"/>
+		<xs:attribute name="minSpareThreads" type="xs:int" use="required"/>
+		<xs:attribute name="maxSpareThreads" type="xs:int" use="required"/>
+		<xs:attribute name="currentThreadCount" type="xs:int" use="required"/>
+		<xs:attribute name="currentThreadsBusy" type="xs:int" use="required"/>
+	</xs:complexType>
+	<xs:complexType name="worker">
+		<xs:attribute name="stage" type="xs:string" use="required"/>
+		<xs:attribute name="requestProcessingTime" type="xs:int" use="required"/>
+		<xs:attribute name="requestBytesSent" type="xs:long" use="required"/>
+		<xs:attribute name="requestBytesRecieved" type="xs:long" use="required"/>
+		<xs:attribute name="remoteAddr" type="xs:string" use="required"/>
+		<xs:attribute name="virtualHost" type="xs:string" use="required"/>
+		<xs:attribute name="method" type="xs:string" use="required"/>
+		<xs:attribute name="currentUri" type="xs:string" use="required"/>
+		<xs:attribute name="currentQueryString" type="xs:string" use="required"/>
+		<xs:attribute name="protocol" type="xs:string" use="required"/>
+	</xs:complexType>
+	<xs:complexType name="workers">
+		<xs:sequence>
+			<xs:element name="worker" type="worker"/>
+		</xs:sequence>
+	</xs:complexType>
+</xs:schema>
diff --git a/container/webapps/manager/xform.xsl b/container/webapps/manager/xform.xsl
new file mode 100644
index 0000000..c5ced3b
--- /dev/null
+++ b/container/webapps/manager/xform.xsl
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  version="1.0">
+
+  <!-- Output method -->
+  <xsl:output encoding="iso-8859-1"
+              indent="no"/>
+
+  <xsl:template match="status">
+    <html>
+    <head>
+    	<TITLE>Tomcat Status</TITLE>
+		<STYLE type="text/css">
+			body, table, tr, td, a, div, span {
+				vertical-align : top;
+			}
+		</STYLE>
+    </head>
+    <body>
+      <div style='font-size:20px;'>Tomcat Status</div>
+
+      <xsl:apply-templates select="jvm"/>
+      <xsl:apply-templates select="connector"/>
+     </body>
+    </html>
+  </xsl:template>
+
+  <xsl:template match="jvm">
+   <xsl:apply-templates select="memory"/>
+  </xsl:template>
+
+  <xsl:template match="memory">
+    <table><tr>
+    		 <td><b>JVM:</b></td>
+    		 <td><b>free:</b> <xsl:value-of select="@free"/></td>
+    		 <td><b>total:</b> <xsl:value-of select="@total"/></td>
+    		 <td><b>max:</b> <xsl:value-of select="@max"/></td>
+    	   </tr>
+    </table><hr />
+  </xsl:template>
+
+  <xsl:template match="connector">
+	 <b>Connector -- </b> <xsl:value-of select="@name"/><br />
+
+  	<xsl:apply-templates select="threadInfo"/>
+  	<xsl:apply-templates select="requestInfo"/>
+  	<xsl:apply-templates select="workers"/>
+  </xsl:template>
+
+  <xsl:template match="threadInfo">
+    <table><tr>
+    		 <td><b>threadInfo </b></td>
+    		 <td><b>maxThreads:</b> <xsl:value-of select="@maxThreads"/></td>
+    		 <td><b>minSpareThreads:</b> <xsl:value-of select="@minSpareThreads"/></td>
+    		 <td><b>maxSpareThreads:</b> <xsl:value-of select="@maxSpareThreads"/></td>
+    		 <td><b>currentThreadCount:</b> <xsl:value-of select="@currentThreadCount"/></td>
+    		 <td><b>currentThreadsBusy:</b> <xsl:value-of select="@currentThreadsBusy"/></td>
+    	   </tr>
+    </table><hr />
+  </xsl:template>
+
+  <xsl:template match="requestInfo">
+    <table><tr>
+    		 <td><b>requestInfo </b></td>
+    		 <td><b>maxTime:</b> <xsl:value-of select="@maxTime"/></td>
+    		 <td><b>processingTime:</b> <xsl:value-of select="@processingTime"/></td>
+    		 <td><b>requestCount:</b> <xsl:value-of select="@requestCount"/></td>
+    		 <td><b>errorCount:</b> <xsl:value-of select="@errorCount"/></td>
+    		 <td><b>bytesReceived:</b> <xsl:value-of select="@bytesReceived"/></td>
+    		 <td><b>bytesSent:</b> <xsl:value-of select="@bytesSent"/></td>
+    	   </tr>
+    </table><hr />
+  </xsl:template>
+
+  <xsl:template match="workers">
+   <table>
+    <tr><th>Stage</th><th>Time</th><th>B Sent</th><th>B Recv</th><th>Client</th><th>VHost</th><th>Request</th></tr>
+  	<xsl:apply-templates select="worker"/>
+
+   </table><hr />
+  </xsl:template>
+
+  <xsl:template match="worker">
+   <tr>
+    <td><xsl:value-of select="@stage"/></td>
+    <td><xsl:value-of select="@requestProcessingTime"/></td>
+    <td><xsl:value-of select="@requestBytesSent"/></td>
+    <td><xsl:value-of select="@requestBytesReceived"/></td>
+    <td><xsl:value-of select="@remoteAddr"/></td>
+    <td><xsl:value-of select="@virtualHost"/></td>
+    <td><xsl:value-of select="@method"/> <xsl:value-of select="@currentUri"/>?<xsl:value-of select="@currentQueryString"/> <xsl:value-of select="@protocol"/></td>
+   </tr>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/container/webapps/webdav/WEB-INF/web.xml b/container/webapps/webdav/WEB-INF/web.xml
new file mode 100644
index 0000000..1a7dd3e
--- /dev/null
+++ b/container/webapps/webdav/WEB-INF/web.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+  <display-name>Webdav Content Management</display-name>
+  <description>
+     Webdav Content Management
+  </description>
+
+  <servlet>
+    <servlet-name>webdav</servlet-name>
+    <servlet-class>org.apache.catalina.servlets.WebdavServlet</servlet-class>
+    <init-param>
+      <param-name>debug</param-name>
+      <param-value>0</param-value>
+    </init-param>
+    <init-param>
+      <param-name>listings</param-name>
+      <param-value>true</param-value>
+    </init-param>
+    <!-- Uncomment this to enable read and write access -->
+<!--
+    <init-param>
+      <param-name>readonly</param-name>
+      <param-value>false</param-value>
+    </init-param>
+-->
+    <!--load-on-startup>1</load-on-startup-->
+  </servlet>
+
+  <!-- The mapping for the webdav servlet -->
+  <!-- Using /* as the mapping ensures that jasper, welcome files etc are
+       over-ridden and all requests are processed by the webdav servlet.
+       This also overcomes a number of issues with some webdav clients
+       (including MS Webfolders) that do not respond correctly
+ to the
+       redirects (302) that result from using a mapping of / -->
+  <servlet-mapping>
+    <servlet-name>webdav</servlet-name>
+    <url-pattern>/*</url-pattern>
+  </servlet-mapping>
+
+  <!-- ================ Security Constraints for Testing =============== -->
+
+<!--
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>The Entire Web Application</web-resource-name>
+      <url-pattern>/*</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>tomcat</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <login-config>
+    <auth-method>BASIC</auth-method>
+    <realm-name>Tomcat Supported Realm</realm-name>
+  </login-config>
+
+  <security-role>
+    <description>
+      An example role defined in "conf/tomcat-users.xml"
+    </description>
+    <role-name>tomcat</role-name>
+  </security-role>
+-->
+
+  <welcome-file-list>
+    <welcome-file/>
+  </welcome-file-list>  
+
+</web-app>
diff --git a/container/webapps/webdav/build.xml b/container/webapps/webdav/build.xml
new file mode 100644
index 0000000..b5fa124
--- /dev/null
+++ b/container/webapps/webdav/build.xml
@@ -0,0 +1,77 @@
+<project name="webdav" default="build-main" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="../../build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="webapps.build"   value="../build"/>
+  <property name="webapps.dist"    value="../dist"/>
+  <property name="webapp.name"     value="webdav"/>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${webapps.build}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}"/>
+    <mkdir dir="${webapps.build}/${webapp.name}/WEB-INF"/>
+  </target>
+
+
+  <!-- ================ BUILD: Copy Static Files ========================== -->
+  <target name="build-static" depends="build-prepare">
+    <copy todir="${webapps.build}/${webapp.name}">
+      <fileset dir=".">
+        <exclude name="build.*"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <target name="build-main" depends="build-static"/>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean and build webdav webapp"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${webapps.build}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Directories =================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${webapps.dist}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Distribution Files ============ -->
+  <target name="dist" depends="build-main,dist-prepare"
+   description="Create webdav webapp binary distribution">
+      <jar   jarfile="${webapps.dist}/${webapp.name}.war"
+             basedir="${webapps.build}/${webapp.name}" includes="**"/>
+  </target>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <delete dir="${dist.dir}/${webapp.name}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
diff --git a/container/webapps/webdav/index.html b/container/webapps/webdav/index.html
new file mode 100644
index 0000000..55334a7
--- /dev/null
+++ b/container/webapps/webdav/index.html
@@ -0,0 +1,76 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Mozilla/4.72 [en] (WinNT; U) [Netscape]">
+<meta name="Author" content="Anil K. Vijendran">
+<title>Tomcat WebDAV support</title>
+</head>
+<body bgcolor="#FFFFFF">
+<img SRC="tomcat.gif" height=92 width=130 align=LEFT><b><font face="Arial, Helvetica, sans-serif"><font size=+3>Tomcat</font></font></b> 
+<br>
+<b><font face="Arial, Helvetica, sans-serif"><font size=-1>WebDAV support</font></font></b>
+<p>This is the home page for the webdav context. This page is located at:
+<ul>
+<li>
+<font face="Courier New, Courier,mono">$TOMCAT_HOME/webapps/webdav/index.html</font></li>
+</ul>
+
+<p>Tomcat includes built-in support for WebDAV level 2, which enables 
+remote authoring of the website. You can test these capabilities using a WebDAV
+client like MS WebFolders (included with IE 4.0 and up), MS Office 2000, DAV
+Explorer (others are listed on the webpages linked below), and point to the
+<b>/webdav</b> path of the server.
+
+<p>This test context is DAV enabled, but has been set up in read-only mode for
+safety reasons. It can be put in read-write mode by editing the web 
+application descriptor file (WEB-INF/web.xml).
+
+<p>To add remote authoring to your web application, you need to make the following
+changes:
+<ul>
+<li>Add the webDAV servlet to your web application. See the web application
+deployment descriptor for an example. Don't forget to make it read/write.
+<li>Add a servlet mapping for the webDAV servlet with a url pattern of "/webdav/*"
+to your web.xml.
+<li>Add an appropriate security constraint to prevent unauthorised changes to your
+web application.
+<li>You can then edit your web application using a webDAV client using a url
+like <font face="Courier New, Courier,mono">http://host:port/webapp/webdav</font></li>
+</ul>
+
+<p>Working WebDAV clients include :
+<ul>
+<li>Adobe GoLive 5.0 (and other WebDAV-enabled Adobe products, like
+  Photoshop)</li>
+<li>Cadaver 0.15</li>
+<li>DAV Explorer 0.60 and 0.70</li>
+<li>Internet Explorer 5 (Windows 2000)</li>
+<li>Internet Explorer 5.5 (Windows 2000)</li>
+<li>Jakarta Slide 1.0 WebDAV client library</li>
+<li>Office 2000 (Windows 2000)</li>
+<li>SkunkDAV 1.0</li>
+<li>Xythos Drive</li>
+</ul>
+
+<p>WebDAV links:</p>
+<ul>
+<li><b><a href="http://www.webdav.org">General info on WebDAV</a></b></li>
+<li><b><a href="http://www.ics.uci.edu/pub/ietf/webdav/">WebDAV working 
+group</a></b></li>
+<li><b><a href="http://www.webdav.org/projects/">WebDAV clients</a></b></li>
+<li><b>
+<a href="http://jakarta.apache.org/slide/">The Jakarta Slide Project</a>
+</b></li>
+</ul>
+
+
+<hr>
+<p align="right"><font size=-1><img src="tomcat-power.gif" width="77" height="80"></font><br>
+&nbsp;
+<font size=-1>Copyright &copy; 1999-2001 Apache Software Foundation</font><br>
+<font size=-1>All Rights Reserved</font> <br>
+&nbsp;</p>
+<p align="right">&nbsp;</p>
+</body>
+</html>
diff --git a/container/webapps/webdav/tomcat-power.gif b/container/webapps/webdav/tomcat-power.gif
new file mode 100644
index 0000000..c1a7404
--- /dev/null
+++ b/container/webapps/webdav/tomcat-power.gif
Binary files differ
diff --git a/container/webapps/webdav/tomcat.gif b/container/webapps/webdav/tomcat.gif
new file mode 100644
index 0000000..6175673
--- /dev/null
+++ b/container/webapps/webdav/tomcat.gif
Binary files differ
diff --git a/jasper/LICENSE b/jasper/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/jasper/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/jasper/jasper2/.cvsignore b/jasper/jasper2/.cvsignore
new file mode 100644
index 0000000..9d0b71a
--- /dev/null
+++ b/jasper/jasper2/.cvsignore
@@ -0,0 +1,2 @@
+build
+dist
diff --git a/jasper/jasper2/BUILDING.txt b/jasper/jasper2/BUILDING.txt
new file mode 100644
index 0000000..3ed0b5b
--- /dev/null
+++ b/jasper/jasper2/BUILDING.txt
@@ -0,0 +1,73 @@
+$Id$
+
+         Building The Tomcat 4.0 Servlet/JSP Container With Jasper2
+         ==========================================================
+
+Jasper2 is a reimplementation of the JSP Container for Tomcat 4.0 that is
+cleaner and easier to extend.  It also forms the basis of the JSP 2.0 
+implementation.  Jasper2 will eventually replace the Jasper module
+built into the Tomcat distribution.  In the meanwhile, you can build Jasper2
+alongside Tomcat 4.0 and replace Jasper.
+
+To build Jasper2, first make sure you have a working build environment for
+Tomcat 4.0.  You can do so by following the instructions in the BUILDING.txt
+file in the Tomcat 4.0 workspace.
+
+Once you have a working build environment for Tomcat 4.0, do the following:
+
+(0) Modify your build.properties file in Tomcat 4.0
+
+* Add a line to your build.properties file for Tomcat 4.0 that
+  points the build script to Jasper2 instead of Jasper, as follows:
+
+      # ----- Jakarta Tomcat Jasper source path -----
+      jasper.home=${base.path}/jakarta-tomcat-jasper/jasper2
+
+  This directory is relative to the location of build.xml for Tomcat.
+
+(1) Use anonymous CVS (as described on the Jakarta web site at
+    <http://jakarta.apache.org/site/cvsindex.html>, or download a source
+    distribution of the "jakarta-taglibs" repository (7/15/2002 or later).
+
+    http://jakarta.apache.org/builds/jakarta-taglibs/nightly/projects/standard/
+
+* Unpack the package into a convenient location so that
+  it resides in its own subdirectory.
+
+* Follow the instructions in BUILDING.txt to set up the correct 
+  build environment.  Make sure you have a valid build.properties file.
+
+* Change directory to "standard" and build the special JSP 2.0 build target
+  for producing the JSP 2.0 Expression Language Evaluator.  The ant target
+  is called jsp20el.dist.
+
+        cd standard
+        ant jsp20el.dist
+
+(2) Customize Build Properties for this subproject
+
+Most Jakarta subprojects allow you to customize Ant properties (with default
+values defined in the "build.xml" file.  This is done by creating a text file
+named "build.properties" in the source distribution directory (for property
+definitions local to this subproject) and/or your user home directory (for
+property definitions shared across subprojects).  You can use the included
+"build.properties.sample" file as a starting point for this.
+
+Jasper2 has external dependencies that are satisfied by configuring
+appropriate values in your build.properties file.  The easiest
+way to satisfy these dependencies is to copy the "build.properties.sample"
+file (in the top-level Tomcat source directory) to "build.properties", and
+then edit it to suit your environment.  On Unix, this would be done as:
+
+  cd ${jasper2.source}
+  cp build.properties.sample build.properties
+  emacs build.properties
+
+NOTE:  Be *sure* that you do not check "build.properties" in to the CVS
+repository.  This file is local to your own development environment, and
+each developer will have their own version.
+
+(3) Build A Binary Distribution
+
+Jasper2 is built as part of Tomcat.  Follow the instructions in 
+BUILDING.txt in the Tomcat project to build Tomcat with Jasper2.
diff --git a/jasper/jasper2/README.txt b/jasper/jasper2/README.txt
new file mode 100644
index 0000000..1f449a0
--- /dev/null
+++ b/jasper/jasper2/README.txt
@@ -0,0 +1,24 @@
+This project is a rewrite of Jasper for tomcat 4.x.
+
+1. Main goals:
+
+   * Cleanup of the current Jasper.
+   * Use for RI work for JSP 1.3
+   * Use for optimization work for generated servlet codes.
+
+2. Status
+
+   * Main jasper engine (parser, attribute validates, code generator)
+     basically complete.
+
+3. Todo
+
+   * Rewrite attribute parser for better spec comformance.
+   * More cleanup, especially jspc parts.
+   * Added large file option.
+   * Error messages.
+   * JSR045 support
+   * Expression language support (JSP1.3)
+   * Optimizations, optimizations, and optimizations
+
+
diff --git a/jasper/jasper2/build.properties.sample b/jasper/jasper2/build.properties.sample
new file mode 100644
index 0000000..899a3ad
--- /dev/null
+++ b/jasper/jasper2/build.properties.sample
@@ -0,0 +1,17 @@
+# -----------------------------------------------------------------------------
+# build.properties.sample
+#
+# This is an example "build.properties" file, used to customize building 
+# Jasper2 for your local environment.  It defines the location of all external
+# modules that Jasper2 depends on.  Copy this file to "build.properties"
+# in the top-level source directory, and customize it as needed.
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+# ----- Pointer to Catalina -----
+catalina.home=../../jakarta-tomcat-5/build
+
+# ----- Pointer to JSP Standard Tag Library distribution -----
+#standard.home=${base.path}/jakarta-taglibs/standard
+#jsp20el.jar=${standard.home}/../dist/jsp20el/jsp20el.jar
diff --git a/jasper/jasper2/build.xml b/jasper/jasper2/build.xml
new file mode 100644
index 0000000..f9c3eb7
--- /dev/null
+++ b/jasper/jasper2/build.xml
@@ -0,0 +1,306 @@
+<project name="Jasper2" default="deploy" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="${user.home}/build.properties"/>
+  <property file="build.properties"/>
+  <property file="build.properties.sample"/>
+  <property file="${catalina.home}/build.properties"/>
+
+  <!-- Build Defaults -->
+  <property name="jasper.build"      value="${basedir}/build"/>
+  <property name="catalina.home"     value="../../jakarta-tomcat-4.1/build"/>
+  <property name="jasper.deploy"     value="${catalina.home}"/>
+  <property name="jasper.dist"       value="${basedir}/dist"/>
+  <property name="test.failonerror"  value="true"/>
+  <property name="test.runner"       value="junit.textui.TestRunner"/>
+  <property name="tools.jar"         value="${java.home}/lib/tools.jar"/>
+  <property name="ant.jar"           value="${ant.home}/lib/ant.jar"/>
+  <property name="servlet-api.jar" value="${api.home}/jsr154/dist/lib/servlet-api.jar"/>
+  <property name="jsp-api.jar" value="${api.home}/jsr152/dist/lib/jsp-api.jar"/>
+
+
+  <!-- Construct Jasper classpath -->
+  <path id="jasper.classpath">
+    <pathelement location="${ant.jar}"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${jsp-api.jar}"/>
+    <pathelement location="${tools.jar}"/>
+    <pathelement location="${jasper-compiler-jdt.jar}"/>
+    <pathelement location="${xerces.jar}"/>
+    <pathelement location="${xercesImpl.jar}"/>
+    <pathelement location="${xml-apis.jar}"/>
+    <pathelement location="${commons-el.jar}"/>
+    <pathelement location="${commons-collections.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${commons-daemon-launcher.jar}"/>
+    <pathelement location="${jasper.build}/shared/classes"/>
+  </path>
+
+  <!-- Construct unit tests classpath -->
+  <path id="test.classpath">
+    <pathelement location="${ant.jar}"/>
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${jsp-api.jar}"/>
+    <pathelement location="${tools.jar}"/>
+    <pathelement location="${xerces.jar}"/>
+    <pathelement location="${xercesImpl.jar}"/>
+    <pathelement location="${xml-apis.jar}"/>
+    <pathelement location="${commons-collections.jar}"/>
+    <pathelement location="${commons-launcher.jar}"/>
+    <pathelement location="${jasper.build}/shared/classes"/>
+    <pathelement location="${jasper.build}/tests"/>
+  </path>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+
+    <available classname="junit.framework.TestCase" property="junit.present" />
+    <available property="launcher.present"
+     classname="org.apache.commons.launcher.Launcher"
+     classpath="${commons-daemon-launcher.jar}"/>
+    <available property="launcher.bootstrap.present"
+     file="${commons-daemon-launcher-bootstrap.class}"/>
+
+    <condition property="copy.launcher.jars">
+      <and>
+        <equals arg1="${launcher.present}" arg2="true" />
+        <equals arg1="${launcher.bootstrap.present}" arg2="true" />
+      </and>
+    </condition>
+
+    <mkdir dir="${jasper.build}"/>
+    <mkdir dir="${jasper.build}/bin"/>
+    <mkdir dir="${jasper.build}/common/classes"/>
+    <mkdir dir="${jasper.build}/common/lib"/>
+    <mkdir dir="${jasper.build}/shared/classes"/>
+    <mkdir dir="${jasper.build}/shared/lib"/>
+
+  </target>
+
+
+  <!-- =================== BUILD: Copy Launcher Files ===================== -->
+  <target name="copy-launcher.jars" if="copy.launcher.jars">
+    <!-- <copy todir="${jasper.build}/common/lib" file="${ant.jar}"/> -->
+    <copy todir="${jasper.build}/bin" file="${commons-daemon-launcher.jar}"/>
+    <copy todir="${jasper.build}/bin" file="${commons-daemon-launcher-bootstrap.class}"/>
+    <copy todir="${jasper.build}/bin">
+      <fileset dir="src/bin" includes="*-using-launcher.*,launcher.properties,jasper.xml" />
+    </copy>
+  </target>
+
+
+  <!-- =================== BUILD: Copy Static Files ======================= -->
+  <target name="build-static" depends="build-prepare,copy-launcher.jars">
+
+    <!-- Executable Commands -->
+    <copy todir="${jasper.build}/bin">
+      <fileset dir="src/bin" excludes="*-using-launcher.*,launcher.properties,jasper.xml" />
+    </copy>
+    <fixcrlf srcdir="${jasper.build}/bin" includes="*.sh" eol="lf"/>
+    <fixcrlf srcdir="${jasper.build}/bin" includes="*.bat" eol="crlf"/>
+    <chmod perm="+x" dir="${jasper.build}/bin" includes="*.sh"/>
+
+  </target>
+
+
+  <!-- ================= BUILD: Compile Server Components ================= -->
+  <target name="build-main" depends="build-static,build-only">
+    <!-- Extra operations: Supporting JAR Files -->
+    <copy todir="${jasper.build}/shared/lib" file="${commons-el.jar}"/>
+  </target>
+
+  <!-- Just build jasper -->
+  <target name="build-only">
+    <property name="jasper.classes" value="${jasper.build}/shared/classes"/>
+    <property name="jasper-compiler.jar" value="${jasper.build}/shared/lib/jasper-compiler.jar"/>
+    <property name="jasper-runtime.jar" value="${jasper.build}/shared/lib/jasper-runtime.jar"/>
+
+    <!-- Compile internal server components -->
+    <javac srcdir="src/share" destdir="${jasper.classes}"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           source="${compile.source}"
+           optimize="${compile.optimize}"
+           excludes="**/CVS/**">
+      <classpath refid="jasper.classpath" />
+    </javac>
+
+    <!-- Copy static resource files -->
+    <copy todir="${jasper.classes}">
+      <fileset dir="src/share">
+        <include name="**/*.properties"/>
+        <include name="**/*.dtd"/>
+      </fileset>
+    </copy>
+
+   <!-- Jasper Compiler JAR File -->
+   <jar  jarfile="${jasper-compiler.jar}">
+      <fileset dir="${jasper.classes}">
+        <include name="org/apache/jasper/compiler/**" />
+        <include name="org/apache/jasper/xmlparser/**" />
+        <include name="org/apache/jasper/servlet/**" />
+        <include name="org/apache/jasper/tagplugins/**" />
+        <exclude name="org/apache/jasper/Constants.class" />
+        <exclude name="org/apache/jasper/JasperException.class" />
+        <include name="org/apache/jasper/*.class" />
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      </fileset>
+    </jar>
+
+    <!-- Jasper Runtime JAR File -->
+    <jar  jarfile="${jasper-runtime.jar}">
+      <fileset dir="${jasper.classes}">
+        <include name="org/apache/jasper/Constants.class" />
+        <include name="org/apache/jasper/JasperException.class" />
+        <include name="org/apache/jasper/compiler/Localizer.class" />
+        <include name="org/apache/jasper/resources/**" />
+        <include name="org/apache/jasper/runtime/**" />
+        <include name="org/apache/jasper/security/**" />
+        <include name="org/apache/jasper/util/**" />
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      </fileset>
+    </jar>
+
+  </target>
+
+
+  <!-- ================ BUILD: Create Jasper Javadocs ===================== -->
+  <target name="javadoc">
+    <delete dir="${jasper.build}/javadoc"/>
+    <mkdir dir="${jasper.build}/javadoc"/>
+    <javadoc packagenames="org.apache.jasper.*"
+     classpathref="jasper.classpath"
+     sourcepath="src/share"
+     destdir="${jasper.build}/javadoc"
+     author="true"
+     version="true"
+     windowtitle="Jasper Internal API Documentation"
+     doctitle="Jasper API"
+     bottom="Copyright &#169; 2000-2004 Apache Software Foundation.  All Rights Reserved."
+     additionalparam="-breakiterator"
+    />
+  </target>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${jasper.build}"/>
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean, build, and deploy Jasper component"/>
+
+
+  <!-- =============== BUILD: Compile Unit Tests ========================== -->
+  <target name="build-tests" depends="build-main" if="junit.present">
+    <mkdir      dir="${jasper.build}/tests"/>
+    <!-- Compile unit test classes -->
+<!--
+    <javac   srcdir="src/test" destdir="${jasper.build}/tests"
+             deprecation="${compile.deprecation}"
+             debug="${compile.debug}"
+             source="${compile.source}"
+             optimize="off"
+             excludes="**/CVS/**">
+      <classpath refid="test.classpath"/>
+    </javac>
+-->
+  </target>
+
+
+  <!-- ==================== BUILD: Execute Unit Tests ===================== -->
+  <target name="test" if="junit.present"
+   description="Run all unit test cases"
+   depends="build-tests">
+  </target>
+
+
+  <!-- ====================== DEPLOY: Create Directories ================== -->
+  <target name="deploy-prepare">
+    <mkdir dir="${jasper.deploy}"/>
+    <mkdir dir="${jasper.deploy}/bin"/>
+    <mkdir dir="${jasper.deploy}/common/lib"/>
+  </target>
+
+
+  <!-- ====================== DEPLOY: Copy Static Files =================== -->
+  <target name="deploy-static" depends="build-main,deploy-prepare">
+
+    <!-- Executable Commands -->
+    <copy todir="${jasper.deploy}/bin">
+      <fileset dir="${jasper.build}/bin" />
+    </copy>
+    <fixcrlf srcdir="${jasper.deploy}/bin" includes="*.sh" eol="lf"/>
+    <fixcrlf srcdir="${jasper.deploy}/bin" includes="*.bat" eol="crlf"/>
+    <chmod perm="+x" file="${jasper.deploy}/bin/jspc.sh"/>
+
+  </target>
+
+
+  <!-- ====================== DEPLOY: Create Jasper JARs ================== -->
+  <target name="deploy" depends="deploy-static,build-main"
+   description="Build and deploy Jasper component">
+    <echo message="Deploy to ${jasper.deploy}/common/lib" /> 
+    <!-- Jasper Compiler JAR File -->
+    <copy file="${jasper.build}/shared/lib/jasper-compiler.jar"
+          tofile="${jasper.deploy}/common/lib/jasper-compiler.jar" />
+
+    <copy file="${jasper.build}/shared/lib/jasper-runtime.jar"
+          tofile="${jasper.deploy}/common/lib/jasper-runtime.jar" />
+
+    <!-- Copy JARs -->
+    <copy todir="${jasper.deploy}/common/lib" file="${jasper-compiler-jdt.jar}"/>
+    <copy todir="${jasper.deploy}/common/lib" file="${jsp-api.jar}"/>
+    <copy todir="${jasper.deploy}/common/lib" file="${commons-el.jar}"/>
+
+  </target>
+
+
+  <!-- ================ DIST: Create Distribution ========================= -->
+  <target name="dist" depends="build-main"
+   description="Create binary distribution">
+
+    <!-- Executable commands -->
+    <mkdir dir="${jasper.dist}/bin"/>
+    <copy todir="${jasper.dist}/bin">
+      <fileset dir="${jasper.build}/bin" />
+    </copy>
+    <fixcrlf srcdir="${jasper.dist}/bin" includes="*.sh" eol="lf"/>
+    <fixcrlf srcdir="${jasper.dist}/bin" includes="*.bat" eol="crlf"/>
+    <chmod perm="+x" file="${jasper.dist}/bin/jspc.sh"/>
+
+    <!-- Jasper Compiler and Runtime JAR Files -->
+    <mkdir dir="${jasper.dist}/shared/lib"/>
+    <copy todir="${jasper.dist}/shared/lib">
+      <fileset dir="${jasper.build}/shared/lib"/>
+    </copy>
+
+
+  </target>
+
+
+  <!-- ======================== DIST: Clean Directory ===================== -->
+  <target name="dist-clean">
+    <delete dir="${jasper.dist}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean, dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
+
diff --git a/jasper/jasper2/doc/jspc.html b/jasper/jasper2/doc/jspc.html
new file mode 100644
index 0000000..3b90d9c
--- /dev/null
+++ b/jasper/jasper2/doc/jspc.html
@@ -0,0 +1,137 @@
+<html>
+<head>
+<title>&lt;jspc&gt;</title>
+</head>
+<body>
+<H1> JspC -- A Command Line JSP Compilation Tool </H1>
+<H3>$Id$</H3>
+
+<H2>OVERVIEW </H2>
+
+Even thought the primary focus of JSP is as a container run environment,
+sometimes it is useful to create a pure servlet out of the JSP page.
+JspC encapsulates the core JSP to servlet translation into a program
+that has no dependencies on a containing servlet engine and allows you
+to translate JSP pages into an equivalent Java servlet.
+
+<H2>GENERAL USE </H2>
+
+The most basic use of JspC is to compile a JSP page in place with the
+jsp page(s) as the arguments.  This will compile the page with the
+resulting java files placed in the directory JspC was called from.  The
+package will be determined from the directories that the JSP page lives
+in.  This will be relative to the web-app it live in (if it exists or is
+specified) or the default package.  The class name will be the name of
+the JSP page without the extension.
+
+<P>To override these default values you can use the <tt>-c</tt> and 
+<tt>-p</tt> options.  <tt>-c &lt;class name&gt;</tt> changes the name of 
+the class of the first compiled JSP page to the class specified.
+Subsequent JSP pages will not be affected by this option.  The 
+<tt>-p &lt;package name&gt;</tt> option sets the package name of all JSP 
+pages compiled by that invocation.  Web-Apps specified for translation 
+will not be affected by either of these options.</P>
+
+<P>The directory that the resulting java files will go into is specified by
+the <TT>-d &lt;dir&gt;</TT> and <tt>-dd &lt;dir&gt;</tt> options.  Both
+of these specify a directory that files will be written into.  When
+using <tt>-d</tt> the java files will be placed in package appropriate
+sub directories while with <tt>-dd</tt> all of the java files will be
+placed literally into the specified directory (without any subdirectory
+structure).</P>
+
+<P>The amount of command line output can be throttled by the <tt>-q</tt> and 
+the <tt>-v&lt;#&gt;</tt> options.  <tt>-q</tt> has the same effect as
+<tt>-v0</tt>, which is to turn off all but fatal error messages.  
+<tt>-v1</tt> builds on <tt>-v0</tt> and it will print out error messages, 
+<tt>-v2</tt> adds warnings, <tt>-v3</tt> adds informational messages
+that have no operational impact, and <tt>-v4</tt> adds various messages
+used in debugging JspC itself.</P>
+
+<P><tt>-ieplugin</tt> is used by the <tt>&lt;jsp:plugin&gt;</tt> tags.
+If the Java Plug-in COM Class-ID you want to use changes then it can be
+specified here.  This should not need to be altered.</P>
+
+<P>As an aid to makefiles, the <tt>-die[#]</tt> option will cause the
+JVM to exit with an error code if a fatal error occurs during the
+translation.   This can be caused by such things as an invalid JSP 
+or an unwritable destination.  There is an optional number that can be 
+specified to specify the specific exit code, but it defaults to 
+<tt>1</tt> if it is not specified or cannot be deciphered.</P>
+
+<P>The <tt>-mapped</tt> option will split the JSP text content into a
+one line per call format.  There are comments above and below the mapped
+write calls to localize where in the JSP file each line of text comes
+from.  This can lead to a minor performance degradation (but it is bound 
+by a linear complexity).  Without this options all adjacent writes are
+concatenated into a single write.</P>
+
+<H2>WEB-APP INTEGRATION</H2>
+<P>JspC is web-app aware.  The package names and all relative uri
+references in JSP elements are rooted to a web-app base that is either
+heuristically determined or specified by the user.<P>
+
+<P><tt>-uriroot &lt;dir&gt;</tt> specifies the root of the web 
+application.  This is where all absolute uris will be resolved from.  
+If it is not specified then the first JSP page will be used to derive 
+it.  To derive it each parent directory of the first JSP page is 
+searched for a <tt>WEB-INF</tt> directory, and the directory closest to 
+the JSP page that has one will be used.  If none can be found then the 
+directory JspC was called from will be used.  This only affects pages 
+translated from an explicitly declared JSP file.</P>
+
+<P><tt>-uribase &lt;uri&gt;</tt> is used to establish the uri context of
+relative URI references in the JSP pages.  If it does not exist then it
+is derived from the location of the file relative to the declared or 
+derived value of <tt>-uriroot</tt>.    This only affects pages 
+translated from an explicitly declared JSP file.</P>
+
+<P> The jsp files option of <tt>-webapp &lt;dir&gt;</tt> is used to
+specify that an entire web-app's JSP files are to be translated.  The
+value of <tt>-uriroot</tt> for that directory becomes the specified
+directory, and all relative uris are resolved against their position in
+the web app.  Currently specifying a war, jar, or zip file is not
+supported.  Each directory is recursively searched and any file with a 
+<tt>.jsp</tt> extension is parsed as though it is a JSP page.  These
+pages obey the <tt>-d</tt>, <tt>-dd</tt>, and <tt>-p</tt> options.
+
+<P>Appropriate entries for the <tt>web.xml</tt> file can be created via
+the <tt>-webinc &lt;file&gt;</tt> and <tt>-webxml &lt;file&gt;</tt>
+options.  All JSP files and web-apps parsed by the single invocation
+will have appropriate <tt>servlet</tt> and <tt>servlet-mapping</tt> 
+elements created for them.  The <tt>webinc</tt> creates only an XML
+fragment suitable for inclusion into an existing <tt>web.xml</tt> file,
+while the <tt>webxml</tt> option creates an entire file with the 
+appropriate headers and root elements suitable for use as a 
+<tt>web.xml</tt> file.</P>
+
+<H2>COMMAND LINE SUMMARY</H2>
+<pre>
+Usage: jspc &lt;options&gt; [--] &lt;jsp files&gt;
+where jsp files is any number of:
+    &lt;file&gt;         A file to be parsed as a jsp page
+    -webapp &lt;dir&gt;  A directory containing a web-app, all jsp pages
+                   will recursively be parsed
+where options include:
+    -q          Quite mode (same as -v0)
+    -v[#]       Verbose mode (optional number is level, default is 2)
+    -d &lt;dir&gt;    Output Directory
+    -dd &lt;dir&gt;   Literal Output Directory.  (package dirs will not be made)
+    -p &lt;name&gt;         Name of target package
+    -c &lt;name&gt;         Name of target class name
+                (only applies to first JSP page)
+    -mapped     Generate separate write() calls for each HTML line in the JSP
+    -die[#]     Generate an error return code (#) on fatal errors.
+                If the number is absent or unparsable it defaults to 1.
+    -uribase &lt;dir&gt;  The uri directory compilations should be relative to
+                    (Default is "/")
+    -uriroot &lt;dir&gt;  The root directory that uri files should be resolved
+                    against, (Default is the directory jspc is invoked from)
+    -webinc &lt;file&gt;  Creates partial servlet mappings for the -webapp option
+    -webxml &lt;file&gt;  Creates a complete web.xml when using the -webapp option.
+    -ieplugin &lt;clsid&gt;  Java Plugin classid for Internet Explorer
+    -sax2 &lt;driverclassname&gt; Driver class name for the SAX 2.0 parser to be used
+</pre>
+
+</body>
+</html>
diff --git a/jasper/jasper2/src/bin/jasper.bat b/jasper/jasper2/src/bin/jasper.bat
new file mode 100755
index 0000000..fd87e5f
--- /dev/null
+++ b/jasper/jasper2/src/bin/jasper.bat
@@ -0,0 +1,71 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+rem ---------------------------------------------------------------------------
+rem Script for Jasper compiler
+rem
+rem Environment Variable Prequisites
+rem
+rem   JASPER_HOME   May point at your Catalina "build" directory.
+rem
+rem   JASPER_OPTS   (Optional) Java runtime options used when the "start",
+rem                 "stop", or "run" command is executed.
+rem
+rem   JAVA_HOME     Must point at your Java Development Kit installation.
+rem
+rem   JAVA_OPTS     (Optional) Java runtime options used when the "start",
+rem                 "stop", or "run" command is executed.
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Guess JASPER_HOME if not defined
+if not "%JASPER_HOME%" == "" goto gotHome
+set JASPER_HOME=.
+if exist "%JASPER_HOME%\bin\jasper.bat" goto okHome
+set JASPER_HOME=..
+:gotHome
+if exist "%JASPER_HOME%\bin\jasper.bat" goto okHome
+echo The JASPER_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+rem Get standard environment variables
+if exist "%JASPER_HOME%\bin\setenv.bat" call "%JASPER_HOME%\bin\setenv.bat"
+
+rem Get standard Java environment variables
+if exist "%JASPER_HOME%\bin\setclasspath.bat" goto okSetclasspath
+echo Cannot find %JASPER_HOME%\bin\setclasspath.bat
+echo This file is needed to run this program
+goto end
+:okSetclasspath
+set BASEDIR=%JASPER_HOME%
+call "%JASPER_HOME%\bin\setclasspath.bat"
+
+rem Add on extra jar files to CLASSPATH
+for %%i in ("%JASPER_HOME%\common\endorsed\*.jar") do call "%JASPER_HOME%\bin\cpappend.bat" %%i
+for %%i in ("%JASPER_HOME%\common\lib\*.jar") do call "%JASPER_HOME%\bin\cpappend.bat" %%i
+for %%i in ("%JASPER_HOME%\shared\lib\*.jar") do call "%JASPER_HOME%\bin\cpappend.bat" %%i
+set CLASSPATH=%CLASSPATH%;%JASPER_HOME%\shared\classes
+
+rem Parse arguments
+if ""%1"" == ""jspc"" goto doJspc
+echo Usage: jasper ( jspc )
+echo Commands:
+echo   jspc - Run the offline JSP compiler
+goto end
+:doJspc
+shift
+
+rem Get remaining unshifted command line arguments and save them in the
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+%_RUNJAVA% %JAVA_OPTS% %JASPER_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djasper.home="%JASPER_HOME%" org.apache.jasper.JspC %CMD_LINE_ARGS%
+
+:end
diff --git a/jasper/jasper2/src/bin/jasper.sh b/jasper/jasper2/src/bin/jasper.sh
new file mode 100644
index 0000000..5ebd9a6
--- /dev/null
+++ b/jasper/jasper2/src/bin/jasper.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+# -----------------------------------------------------------------------------
+# Script for Jasper compiler
+#
+# Environment Variable Prequisites
+#
+#   JASPER_HOME   May point at your Catalina "build" directory.
+#
+#   JASPER_OPTS   (Optional) Java runtime options used when the "start",
+#                 "stop", or "run" command is executed.
+#
+#   JAVA_HOME     Must point at your Java Development Kit installation.
+#
+#   JAVA_OPTS     (Optional) Java runtime options used when the "start",
+#                 "stop", or "run" command is executed.
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false
+os400=false
+case "`uname`" in
+CYGWIN*) cygwin=true;;
+OS400*) os400=true;;
+esac
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ]; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+
+# Get standard environment variables
+PRGDIR=`dirname "$PRG"`
+JASPER_HOME=`cd "$PRGDIR/.." ; pwd`
+if [ -r "$JASPER_HOME"/bin/setenv.sh ]; then
+  . "$JASPER_HOME"/bin/setenv.sh
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin; then
+  [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$JASPER_HOME" ] && JASPER_HOME=`cygpath --unix "$JASPER_HOME"`
+  [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# Get standard Java environment variables
+if [ -r "$JASPER_HOME"/bin/setclasspath.sh ]; then
+  BASEDIR="$JASPER_HOME"
+  . "$JASPER_HOME"/bin/setclasspath.sh
+else
+  echo "Cannot find $JASPER_HOME/bin/setclasspath.sh"
+  echo "This file is needed to run this program"
+  exit 1
+fi
+
+# Add on extra jar files to CLASSPATH
+for i in "$JASPER_HOME"/common/endorsed/*.jar; do
+  CLASSPATH="$CLASSPATH":"$i"
+done
+for i in "$JASPER_HOME"/common/lib/*.jar; do
+  CLASSPATH="$CLASSPATH":"$i"
+done
+for i in "$JASPER_HOME"/shared/lib/*.jar; do
+  CLASSPATH="$CLASSPATH":"$i"
+done
+CLASSPATH="$CLASSPATH":"$JASPER_HOME"/shared/classes
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  JASPER_HOME=`cygpath --path --windows "$JASPER_HOME"`
+  CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+fi
+
+# ----- Execute The Requested Command -----------------------------------------
+
+if [ "$1" = "jspc" ] ; then
+
+  shift
+  exec "$_RUNJAVA" $JAVA_OPTS $JASPER_OPTS \
+    -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+    -Djasper.home="$JASPER_HOME" \
+    -Dcatalina.home="$JASPER_HOME" \
+    org.apache.jasper.JspC "$@"
+
+elif [ "$1" = "debug" ] ; then
+
+  shift
+  exec "$_RUNJDB" $JAVA_OPTS $JASPER_OPTS \
+    -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+    -Djasper.home="$JASPER_HOME" \
+    -Dcatalina.home="$JASPER_HOME" \
+    org.apache.jasper.JspC "$@"
+
+else
+
+  echo "Usage: jasper.sh ( jspc )"
+  echo "Commands:"
+  echo "  jspc - Run the offline JSP compiler"
+  exit 1
+
+fi
diff --git a/jasper/jasper2/src/bin/jasper.xml b/jasper/jasper2/src/bin/jasper.xml
new file mode 100644
index 0000000..dbc1150
--- /dev/null
+++ b/jasper/jasper2/src/bin/jasper.xml
@@ -0,0 +1,97 @@
+<!--
+
+  XML file for launching Jasper applications using the Launcher.
+
+  To run any of the applications in the JDB debugger, execute the Launcher with
+  a "-Ddebug=true" argument.
+
+  To run any of the applications in JPDA mode, execute the Launcher with a
+  "-Djpda=true" argument.
+
+-->
+
+<project name="Jasper Launcher" default="jspc" basedir=".">
+
+  <!-- Set the application home to the parent directory of this directory -->
+  <property name="jasper.home" location="${basedir}/.."/>
+
+  <!-- Import the user's custom properties -->
+  <property file="${jasper.home}/bin/jasper.properties"/>
+
+  <!-- Set user configurable properties -->
+  <property name="jasper.jvm.args" value=""/>
+  <property name="jasper.source.path" value="${jasper.home}/../../jakarta-servletapi-5/src/share:${jasper.home}/../../jakarta-tomcat-jasper/jasper2/src/share"/>
+
+  <!-- Build the classpath relative to the application home -->
+  <path id="base.class.path">
+    <fileset dir="${jasper.home}/common/lib" includes="*.jar"/>
+    <fileset dir="${jasper.home}/common/endorsed" includes="*.jar"/>
+    <fileset dir="${jasper.home}/shared/lib" includes="*.jar"/>
+    <pathelement location="${jasper.home}/shared/classes"/>
+  </path>
+
+  <!-- Build the sysproperties relative to the application home -->
+  <syspropertyset id="base.sys.properties">
+    <sysproperty key="java.endorsed.dirs" file="${jasper.home}/common/endorsed"/>
+    <sysproperty key="jasper.home" file="${jasper.home}"/>
+  </syspropertyset>
+
+  <!-- Build the standard jvmargs -->
+  <jvmargset id="base.jvm.args">
+    <jvmarg line="${jasper.jvm.args}"/>
+    <jvmarg value="-Xdebug" if="jpda.settings"/>
+    <jvmarg value="-Xrunjdwp:${jpda.settings}" if="jpda.settings"/>
+    <jvmarg value="-sourcepath" if="jdb"/>
+    <jvmarg path="${jasper.source.path}" if="jdb"/>
+  </jvmargset>
+
+  <!-- Target that sets JDB properties when the "debug" property is set -->
+  <target name="setjdb" description="Set JDB properties" if="debug">
+
+    <property name="jdb" value="true"/>
+
+  </target>
+
+  <!-- Target that sets JPDA properties when the "jpda" property is set -->
+  <target name="setjpda" description="Set JPDA properties" if="jpda">
+
+    <condition property="jpda.transport" value="dt_shmem">
+      <os family="windows"/>
+    </condition>
+    <condition property="jpda.transport" value="dt_socket">
+      <not>
+        <os family="windows"/>
+      </not>
+    </condition>
+    <condition property="jpda.address" value="jdbconn">
+      <equals arg1="${jpda.transport}" arg2="dt_shmem"/>
+    </condition>
+    <condition property="jpda.address" value="8000">
+      <not>
+        <equals arg1="${jpda.transport}" arg2="dt_shmem"/>
+      </not>
+    </condition>
+    <property name="jpda.suspend" value="y"/>
+    <property name="jpda.settings" value="transport=${jpda.transport},address=${jpda.address},server=y,suspend=${jpda.suspend}"/>
+
+  </target>
+
+  <!-- Target that executes the JSPC compiler-->
+  <target name="jspc" description="Execute JSPC compiler"
+    depends="setjdb,setjpda">
+
+    <!-- Launch JSPC compiler -->
+    <launch classname="org.apache.jasper.JspC"
+      debug="${jdb}"
+      print="${print}"
+      usesystemin="false"
+      requiretools="true">
+        <jvmargset refid="base.jvm.args"/>
+        <arg value="jspc"/>
+        <syspropertyset refid="base.sys.properties"/>
+        <classpath refid="base.class.path"/>
+    </launch>
+
+  </target>
+
+</project>
diff --git a/jasper/jasper2/src/bin/jspc-using-launcher.bat b/jasper/jasper2/src/bin/jspc-using-launcher.bat
new file mode 100755
index 0000000..f527e8e
--- /dev/null
+++ b/jasper/jasper2/src/bin/jspc-using-launcher.bat
@@ -0,0 +1,40 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+
+rem ---------------------------------------------------------------------------
+rem
+rem Script for running JSPC compiler using the Launcher
+rem
+rem ---------------------------------------------------------------------------
+
+rem Get standard environment variables
+set PRG=%0
+if exist %PRG%\..\setenv.bat goto gotCmdPath
+rem %0 must have been found by DOS using the %PATH% so we assume that
+rem setenv.bat will also be found in the %PATH%
+call setenv.bat
+goto doneSetenv
+:gotCmdPath
+call %PRG%\..\setenv.bat
+:doneSetenv
+
+rem Make sure prerequisite environment variables are set
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto end
+:gotJavaHome
+
+rem Get command line arguments and save them with the proper quoting
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+rem Execute the Launcher using the "jspc" target
+"%JAVA_HOME%\bin\java.exe" -classpath %PRG%\..;"%PATH%";. LauncherBootstrap -launchfile jasper.xml -verbose jspc %CMD_LINE_ARGS%
+
+:end
diff --git a/jasper/jasper2/src/bin/jspc-using-launcher.sh b/jasper/jasper2/src/bin/jspc-using-launcher.sh
new file mode 100644
index 0000000..1a8d9ec
--- /dev/null
+++ b/jasper/jasper2/src/bin/jspc-using-launcher.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# -----------------------------------------------------------------------------
+#
+# Script for running JSPC compiler using the Launcher
+#
+# -----------------------------------------------------------------------------
+
+# Resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ]; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+
+# Get standard environment variables
+PRGDIR=`dirname "$PRG"`
+if [ -r "$PRGDIR"/setenv.sh ]; then
+  . "$PRGDIR"/setenv.sh
+fi
+
+# Execute the Launcher using the "jspc" target
+exec "$JAVA_HOME"/bin/java -classpath "$PRGDIR" LauncherBootstrap -launchfile jasper.xml -verbose jspc "$@"
diff --git a/jasper/jasper2/src/bin/jspc.bat b/jasper/jasper2/src/bin/jspc.bat
new file mode 100755
index 0000000..2959cbb
--- /dev/null
+++ b/jasper/jasper2/src/bin/jspc.bat
@@ -0,0 +1,41 @@
+@echo off
+if "%OS%" == "Windows_NT" setlocal
+rem ---------------------------------------------------------------------------
+rem Script to run the Jasper "offline JSP compiler"
+rem
+rem $Id$
+rem ---------------------------------------------------------------------------
+
+rem Guess JASPER_HOME if not defined
+if not "%JASPER_HOME%" == "" goto gotHome
+set JASPER_HOME=.
+if exist "%JASPER_HOME%\bin\jspc.bat" goto okHome
+set JASPER_HOME=..
+:gotHome
+if exist "%JASPER_HOME%\bin\jspc.bat" goto okHome
+echo The JASPER_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+set EXECUTABLE=%JASPER_HOME%\bin\jasper.bat
+
+rem Check that target executable exists
+if exist "%EXECUTABLE%" goto okExec
+echo Cannot find %EXECUTABLE%
+echo This file is needed to run this program
+goto end
+:okExec
+
+rem Get remaining unshifted command line arguments and save them in the
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+call "%EXECUTABLE%" jspc %CMD_LINE_ARGS%
+
+:end
diff --git a/jasper/jasper2/src/bin/jspc.sh b/jasper/jasper2/src/bin/jspc.sh
new file mode 100644
index 0000000..bc26787
--- /dev/null
+++ b/jasper/jasper2/src/bin/jspc.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# -----------------------------------------------------------------------------
+# Script to run the Jasper "offline JSP compiler"
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+ 
+PRGDIR=`dirname "$PRG"`
+EXECUTABLE=jasper.sh
+
+# Check that target executable exists
+if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
+  echo "Cannot find $PRGDIR/$EXECUTABLE"
+  echo "This file is needed to run this program"
+  exit 1
+fi
+
+if [ "$1" = "debug" ]; then
+  shift
+  exec "$PRGDIR"/"$EXECUTABLE" debug "$@"
+else
+  exec "$PRGDIR"/"$EXECUTABLE" jspc "$@"
+fi
diff --git a/jasper/jasper2/src/share/org/apache/jasper/Constants.java b/jasper/jasper2/src/share/org/apache/jasper/Constants.java
new file mode 100644
index 0000000..1017d98
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/Constants.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper;
+
+
+/**
+ * Some constants and other global data that are used by the compiler and the runtime.
+ *
+ * @author Anil K. Vijendran
+ * @author Harish Prabandham
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+public class Constants {
+    /**
+     * The base class of the generated servlets. 
+     */
+    public static final String JSP_SERVLET_BASE = "org.apache.jasper.runtime.HttpJspBase";
+
+    /**
+     * _jspService is the name of the method that is called by 
+     * HttpJspBase.service(). This is where most of the code generated
+     * from JSPs go.
+     */
+    public static final String SERVICE_METHOD_NAME = "_jspService";
+
+    /**
+     * Default servlet content type.
+     */
+    public static final String SERVLET_CONTENT_TYPE = "text/html";
+
+    /**
+     * These classes/packages are automatically imported by the
+     * generated code. 
+     */
+    public static final String[] STANDARD_IMPORTS = { 
+	"javax.servlet.*", 
+	"javax.servlet.http.*", 
+	"javax.servlet.jsp.*"
+    };
+
+    /**
+     * FIXME
+     * ServletContext attribute for classpath. This is tomcat specific. 
+     * Other servlet engines may choose to support this attribute if they 
+     * want to have this JSP engine running on them. 
+     */
+    public static final String SERVLET_CLASSPATH = "org.apache.catalina.jsp_classpath";
+
+    /**
+     * FIXME
+     * Request attribute for <code>&lt;jsp-file&gt;</code> element of a
+     * servlet definition.  If present on a request, this overrides the
+     * value returned by <code>request.getServletPath()</code> to select
+     * the JSP page to be executed.
+     */
+    public static final String JSP_FILE = "org.apache.catalina.jsp_file";
+
+
+    /**
+     * FIXME
+     * ServletContext attribute for class loader. This is tomcat specific. 
+     * Other servlet engines can choose to have this attribute if they 
+     * want to have this JSP engine running on them. 
+     */
+    //public static final String SERVLET_CLASS_LOADER = "org.apache.tomcat.classloader";
+    public static final String SERVLET_CLASS_LOADER = "org.apache.catalina.classloader";
+
+    /**
+     * Default size of the JSP buffer.
+     */
+    public static final int K = 1024;
+    public static final int DEFAULT_BUFFER_SIZE = 8*K;
+
+    /**
+     * Default size for the tag buffers.
+     */
+    public static final int DEFAULT_TAG_BUFFER_SIZE = 512;
+
+    /**
+     * Default tag handler pool size.
+     */
+    public static final int MAX_POOL_SIZE = 5;
+
+    /**
+     * The query parameter that causes the JSP engine to just
+     * pregenerated the servlet but not invoke it. 
+     */
+    public static final String PRECOMPILE = "jsp_precompile";
+
+    /**
+     * The default package name for compiled jsp pages.
+     */
+    public static final String JSP_PACKAGE_NAME = "org.apache.jsp";
+
+    /**
+     * The default package name for tag handlers generated from tag files
+     */
+    public static final String TAG_FILE_PACKAGE_NAME = "org.apache.jsp.tag";
+
+    /**
+     * Servlet context and request attributes that the JSP engine
+     * uses. 
+     */
+    public static final String INC_REQUEST_URI = "javax.servlet.include.request_uri";
+    public static final String INC_SERVLET_PATH = "javax.servlet.include.servlet_path";
+    public static final String TMP_DIR = "javax.servlet.context.tempdir";
+    public static final String FORWARD_SEEN = "javax.servlet.forward.seen";
+
+    // Must be kept in sync with org/apache/catalina/Globals.java
+    public static final String ALT_DD_ATTR = "org.apache.catalina.deploy.alt_dd";
+
+    /**
+     * Public Id and the Resource path (of the cached copy) 
+     * of the DTDs for tag library descriptors. 
+     */
+    public static final String TAGLIB_DTD_PUBLIC_ID_11 = 
+	"-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN";
+    public static final String TAGLIB_DTD_RESOURCE_PATH_11 = 
+	"/javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd";
+    public static final String TAGLIB_DTD_PUBLIC_ID_12 = 
+	"-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN";
+    public static final String TAGLIB_DTD_RESOURCE_PATH_12 = 
+	"/javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd";
+
+    /**
+     * Public Id and the Resource path (of the cached copy) 
+     * of the DTDs for web application deployment descriptors
+     */
+    public static final String WEBAPP_DTD_PUBLIC_ID_22 = 
+	"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN";
+    public static final String WEBAPP_DTD_RESOURCE_PATH_22 = 
+	"/javax/servlet/resources/web-app_2_2.dtd";
+    public static final String WEBAPP_DTD_PUBLIC_ID_23 = 
+	"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN";
+    public static final String WEBAPP_DTD_RESOURCE_PATH_23 = 
+	"/javax/servlet/resources/web-app_2_3.dtd";
+
+    /**
+     * List of the Public IDs that we cache, and their
+     * associated location. This is used by 
+     * an EntityResolver to return the location of the
+     * cached copy of a DTD.
+     */
+    public static final String[] CACHED_DTD_PUBLIC_IDS = {
+	TAGLIB_DTD_PUBLIC_ID_11,
+	TAGLIB_DTD_PUBLIC_ID_12,
+	WEBAPP_DTD_PUBLIC_ID_22,
+	WEBAPP_DTD_PUBLIC_ID_23,
+    };
+    public static final String[] CACHED_DTD_RESOURCE_PATHS = {
+	TAGLIB_DTD_RESOURCE_PATH_11,
+	TAGLIB_DTD_RESOURCE_PATH_12,
+	WEBAPP_DTD_RESOURCE_PATH_22,
+	WEBAPP_DTD_RESOURCE_PATH_23,
+    };
+    
+    /**
+     * Default URLs to download the pluging for Netscape and IE.
+     */
+    public static final String NS_PLUGIN_URL = 
+        "http://java.sun.com/products/plugin/";
+
+    public static final String IE_PLUGIN_URL = 
+        "http://java.sun.com/products/plugin/1.2.2/jinstall-1_2_2-win.cab#Version=1,2,2,0";
+
+    /**
+     * Prefix to use for generated temporary variable names
+     */
+    public static final String TEMP_VARIABLE_NAME_PREFIX =
+        "_jspx_temp";
+
+    /**
+     * A replacement char for "\$".
+     * XXX This is a hack to avoid changing EL interpreter to recognize "\$"
+     */
+    public static final char ESC='\u001b';
+    public static final String ESCStr="'\\u001b'";
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/EmbeddedServletOptions.java b/jasper/jasper2/src/share/org/apache/jasper/EmbeddedServletOptions.java
new file mode 100644
index 0000000..6ddcd8c
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/EmbeddedServletOptions.java
@@ -0,0 +1,643 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper;
+
+import java.io.File;
+import java.util.*;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.apache.jasper.compiler.TldLocationsCache;
+import org.apache.jasper.compiler.JspConfig;
+import org.apache.jasper.compiler.TagPluginManager;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A class to hold all init parameters specific to the JSP engine. 
+ *
+ * @author Anil K. Vijendran
+ * @author Hans Bergsten
+ * @author Pierre Delisle
+ */
+public final class EmbeddedServletOptions implements Options {
+    
+    // Logger
+    private Log log = LogFactory.getLog(EmbeddedServletOptions.class);
+    
+    private Properties settings = new Properties();
+    
+    /**
+     * Is Jasper being used in development mode?
+     */
+    private boolean development = true;
+    
+    /**
+     * Should Ant fork its java compiles of JSP pages.
+     */
+    public boolean fork = true;
+    
+    /**
+     * Do you want to keep the generated Java files around?
+     */
+    private boolean keepGenerated = true;
+    
+    /**
+     * Should white spaces between directives or actions be trimmed?
+     */
+    private boolean trimSpaces = false;
+    
+    /**
+     * Determines whether tag handler pooling is enabled.
+     */
+    private boolean isPoolingEnabled = true;
+    
+    /**
+     * Do you want support for "mapped" files? This will generate
+     * servlet that has a print statement per line of the JSP file.
+     * This seems like a really nice feature to have for debugging.
+     */
+    private boolean mappedFile = true;
+    
+    /**
+     * Do you want stack traces and such displayed in the client's
+     * browser? If this is false, such messages go to the standard
+     * error or a log file if the standard error is redirected. 
+     */
+    private boolean sendErrorToClient = false;
+    
+    /**
+     * Do we want to include debugging information in the class file?
+     */
+    private boolean classDebugInfo = true;
+    
+    /**
+     * Background compile thread check interval in seconds.
+     */
+    private int checkInterval = 0;
+    
+    /**
+     * Is the generation of SMAP info for JSR45 debuggin suppressed?
+     */
+    private boolean isSmapSuppressed = false;
+    
+    /**
+     * Should SMAP info for JSR45 debugging be dumped to a file?
+     */
+    private boolean isSmapDumped = false;
+    
+    /**
+     * Are Text strings to be generated as char arrays?
+     */
+    private boolean genStringAsCharArray = false;
+    
+    private boolean errorOnUseBeanInvalidClassAttribute = true;
+    
+    /**
+     * I want to see my generated servlets. Which directory are they
+     * in?
+     */
+    private File scratchDir;
+    
+    /**
+     * Need to have this as is for versions 4 and 5 of IE. Can be set from
+     * the initParams so if it changes in the future all that is needed is
+     * to have a jsp initParam of type ieClassId="<value>"
+     */
+    private String ieClassId = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93";
+    
+    /**
+     * What classpath should I use while compiling generated servlets?
+     */
+    private String classpath = null;
+    
+    /**
+     * Compiler to use.
+     */
+    private String compiler = null;
+    
+    /**
+     * Compiler target VM.
+     */
+    private String compilerTargetVM = "1.5";
+    
+    /**
+     * The compiler source VM.
+     */
+    private String compilerSourceVM = "1.5";
+    
+    /**
+     * Cache for the TLD locations
+     */
+    private TldLocationsCache tldLocationsCache = null;
+    
+    /**
+     * Jsp config information
+     */
+    private JspConfig jspConfig = null;
+    
+    /**
+     * TagPluginManager
+     */
+    private TagPluginManager tagPluginManager = null;
+    
+    /**
+     * Java platform encoding to generate the JSP
+     * page servlet.
+     */
+    private String javaEncoding = "UTF8";
+    
+    /**
+     * Modification test interval.
+     */
+    private int modificationTestInterval = 4;
+    
+    /**
+     * Is generation of X-Powered-By response header enabled/disabled?
+     */
+    private boolean xpoweredBy;
+    
+    public String getProperty(String name ) {
+        return settings.getProperty( name );
+    }
+    
+    public void setProperty(String name, String value ) {
+        if (name != null && value != null){ 
+            settings.setProperty( name, value );
+        }
+    }
+    
+    /**
+     * Are we keeping generated code around?
+     */
+    public boolean getKeepGenerated() {
+        return keepGenerated;
+    }
+    
+    /**
+     * Should white spaces between directives or actions be trimmed?
+     */
+    public boolean getTrimSpaces() {
+        return trimSpaces;
+    }
+    
+    public boolean isPoolingEnabled() {
+        return isPoolingEnabled;
+    }
+    
+    /**
+     * Are we supporting HTML mapped servlets?
+     */
+    public boolean getMappedFile() {
+        return mappedFile;
+    }
+    
+    /**
+     * Should errors be sent to client or thrown into stderr?
+     */
+    public boolean getSendErrorToClient() {
+        return sendErrorToClient;
+    }
+    
+    /**
+     * Should class files be compiled with debug information?
+     */
+    public boolean getClassDebugInfo() {
+        return classDebugInfo;
+    }
+    
+    /**
+     * Background JSP compile thread check intervall
+     */
+    public int getCheckInterval() {
+        return checkInterval;
+    }
+    
+    /**
+     * Modification test interval.
+     */
+    public int getModificationTestInterval() {
+        return modificationTestInterval;
+    }
+    
+    /**
+     * Is Jasper being used in development mode?
+     */
+    public boolean getDevelopment() {
+        return development;
+    }
+    
+    /**
+     * Is the generation of SMAP info for JSR45 debuggin suppressed?
+     */
+    public boolean isSmapSuppressed() {
+        return isSmapSuppressed;
+    }
+    
+    /**
+     * Should SMAP info for JSR45 debugging be dumped to a file?
+     */
+    public boolean isSmapDumped() {
+        return isSmapDumped;
+    }
+    
+    /**
+     * Are Text strings to be generated as char arrays?
+     */
+    public boolean genStringAsCharArray() {
+        return this.genStringAsCharArray;
+    }
+    
+    /**
+     * Class ID for use in the plugin tag when the browser is IE. 
+     */
+    public String getIeClassId() {
+        return ieClassId;
+    }
+    
+    /**
+     * What is my scratch dir?
+     */
+    public File getScratchDir() {
+        return scratchDir;
+    }
+    
+    /**
+     * What classpath should I use while compiling the servlets
+     * generated from JSP files?
+     */
+    public String getClassPath() {
+        return classpath;
+    }
+    
+    /**
+     * Is generation of X-Powered-By response header enabled/disabled?
+     */
+    public boolean isXpoweredBy() {
+        return xpoweredBy;
+    }
+    
+    /**
+     * Compiler to use.
+     */
+    public String getCompiler() {
+        return compiler;
+    }
+    
+    /**
+     * @see Options#getCompilerTargetVM
+     */
+    public String getCompilerTargetVM() {
+        return compilerTargetVM;
+    }
+    
+    /**
+     * @see Options#getCompilerSourceVM
+     */
+    public String getCompilerSourceVM() {
+        return compilerSourceVM;
+    }
+    
+    public boolean getErrorOnUseBeanInvalidClassAttribute() {
+        return errorOnUseBeanInvalidClassAttribute;
+    }
+    
+    public void setErrorOnUseBeanInvalidClassAttribute(boolean b) {
+        errorOnUseBeanInvalidClassAttribute = b;
+    }
+    
+    public TldLocationsCache getTldLocationsCache() {
+        return tldLocationsCache;
+    }
+    
+    public void setTldLocationsCache( TldLocationsCache tldC ) {
+        tldLocationsCache = tldC;
+    }
+    
+    public String getJavaEncoding() {
+        return javaEncoding;
+    }
+    
+    public boolean getFork() {
+        return fork;
+    }
+    
+    public JspConfig getJspConfig() {
+        return jspConfig;
+    }
+    
+    public TagPluginManager getTagPluginManager() {
+        return tagPluginManager;
+    }
+    
+    /**
+     * Create an EmbeddedServletOptions object using data available from
+     * ServletConfig and ServletContext. 
+     */
+    public EmbeddedServletOptions(ServletConfig config,
+            ServletContext context) {
+        
+        // JVM version numbers
+        try {
+            if (Float.parseFloat(System.getProperty("java.specification.version")) > 1.4) {
+                compilerSourceVM = compilerTargetVM = "1.5";
+            } else {
+                compilerSourceVM = compilerTargetVM = "1.4";
+            }
+        } catch (NumberFormatException e) {
+            // Ignore
+        }
+        
+        Enumeration enumeration=config.getInitParameterNames();
+        while( enumeration.hasMoreElements() ) {
+            String k=(String)enumeration.nextElement();
+            String v=config.getInitParameter( k );
+            setProperty( k, v);
+        }
+        
+        // quick hack
+        String validating=config.getInitParameter( "validating");
+        if( "false".equals( validating )) ParserUtils.validating=false;
+        
+        String keepgen = config.getInitParameter("keepgenerated");
+        if (keepgen != null) {
+            if (keepgen.equalsIgnoreCase("true")) {
+                this.keepGenerated = true;
+            } else if (keepgen.equalsIgnoreCase("false")) {
+                this.keepGenerated = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.keepgen"));
+                }
+            }
+        }
+        
+        
+        String trimsp = config.getInitParameter("trimSpaces"); 
+        if (trimsp != null) {
+            if (trimsp.equalsIgnoreCase("true")) {
+                trimSpaces = true;
+            } else if (trimsp.equalsIgnoreCase("false")) {
+                trimSpaces = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.trimspaces"));
+                }
+            }
+        }
+        
+        this.isPoolingEnabled = true;
+        String poolingEnabledParam
+        = config.getInitParameter("enablePooling"); 
+        if (poolingEnabledParam != null
+                && !poolingEnabledParam.equalsIgnoreCase("true")) {
+            if (poolingEnabledParam.equalsIgnoreCase("false")) {
+                this.isPoolingEnabled = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.enablePooling"));
+                }		       	   
+            }
+        }
+        
+        String mapFile = config.getInitParameter("mappedfile"); 
+        if (mapFile != null) {
+            if (mapFile.equalsIgnoreCase("true")) {
+                this.mappedFile = true;
+            } else if (mapFile.equalsIgnoreCase("false")) {
+                this.mappedFile = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.mappedFile"));
+                }
+            }
+        }
+        
+        String senderr = config.getInitParameter("sendErrToClient");
+        if (senderr != null) {
+            if (senderr.equalsIgnoreCase("true")) {
+                this.sendErrorToClient = true;
+            } else if (senderr.equalsIgnoreCase("false")) {
+                this.sendErrorToClient = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.sendErrToClient"));
+                }
+            }
+        }
+        
+        String debugInfo = config.getInitParameter("classdebuginfo");
+        if (debugInfo != null) {
+            if (debugInfo.equalsIgnoreCase("true")) {
+                this.classDebugInfo  = true;
+            } else if (debugInfo.equalsIgnoreCase("false")) {
+                this.classDebugInfo  = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.classDebugInfo"));
+                }
+            }
+        }
+        
+        String checkInterval = config.getInitParameter("checkInterval");
+        if (checkInterval != null) {
+            try {
+                this.checkInterval = Integer.parseInt(checkInterval);
+                if (this.checkInterval == 0) {
+                    this.checkInterval = 300;
+                    if (log.isWarnEnabled()) {
+                        log.warn(Localizer.getMessage("jsp.warning.checkInterval"));
+                    }
+                }
+            } catch(NumberFormatException ex) {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.checkInterval"));
+                }
+            }
+        }
+        
+        String modificationTestInterval = config.getInitParameter("modificationTestInterval");
+        if (modificationTestInterval != null) {
+            try {
+                this.modificationTestInterval = Integer.parseInt(modificationTestInterval);
+            } catch(NumberFormatException ex) {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.modificationTestInterval"));
+                }
+            }
+        }
+        
+        String development = config.getInitParameter("development");
+        if (development != null) {
+            if (development.equalsIgnoreCase("true")) {
+                this.development = true;
+            } else if (development.equalsIgnoreCase("false")) {
+                this.development = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.development"));
+                }
+            }
+        }
+        
+        String suppressSmap = config.getInitParameter("suppressSmap");
+        if (suppressSmap != null) {
+            if (suppressSmap.equalsIgnoreCase("true")) {
+                isSmapSuppressed = true;
+            } else if (suppressSmap.equalsIgnoreCase("false")) {
+                isSmapSuppressed = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.suppressSmap"));
+                }
+            }
+        }
+        
+        String dumpSmap = config.getInitParameter("dumpSmap");
+        if (dumpSmap != null) {
+            if (dumpSmap.equalsIgnoreCase("true")) {
+                isSmapDumped = true;
+            } else if (dumpSmap.equalsIgnoreCase("false")) {
+                isSmapDumped = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.dumpSmap"));
+                }
+            }
+        }
+        
+        String genCharArray = config.getInitParameter("genStrAsCharArray");
+        if (genCharArray != null) {
+            if (genCharArray.equalsIgnoreCase("true")) {
+                genStringAsCharArray = true;
+            } else if (genCharArray.equalsIgnoreCase("false")) {
+                genStringAsCharArray = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.genchararray"));
+                }
+            }
+        }
+        
+        String errBeanClass =
+            config.getInitParameter("errorOnUseBeanInvalidClassAttribute");
+        if (errBeanClass != null) {
+            if (errBeanClass.equalsIgnoreCase("true")) {
+                errorOnUseBeanInvalidClassAttribute = true;
+            } else if (errBeanClass.equalsIgnoreCase("false")) {
+                errorOnUseBeanInvalidClassAttribute = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.errBean"));
+                }
+            }
+        }
+        
+        String ieClassId = config.getInitParameter("ieClassId");
+        if (ieClassId != null)
+            this.ieClassId = ieClassId;
+        
+        String classpath = config.getInitParameter("classpath");
+        if (classpath != null)
+            this.classpath = classpath;
+        
+        /*
+         * scratchdir
+         */
+        String dir = config.getInitParameter("scratchdir"); 
+        if (dir != null) {
+            scratchDir = new File(dir);
+        } else {
+            // First try the Servlet 2.2 javax.servlet.context.tempdir property
+            scratchDir = (File) context.getAttribute(Constants.TMP_DIR);
+            if (scratchDir == null) {
+                // Not running in a Servlet 2.2 container.
+                // Try to get the JDK 1.2 java.io.tmpdir property
+                dir = System.getProperty("java.io.tmpdir");
+                if (dir != null)
+                    scratchDir = new File(dir);
+            }
+        }      
+        if (this.scratchDir == null) {
+            log.fatal(Localizer.getMessage("jsp.error.no.scratch.dir"));
+            return;
+        }
+        
+        if (!(scratchDir.exists() && scratchDir.canRead() &&
+                scratchDir.canWrite() && scratchDir.isDirectory()))
+            log.fatal(Localizer.getMessage("jsp.error.bad.scratch.dir",
+                    scratchDir.getAbsolutePath()));
+        
+        this.compiler = config.getInitParameter("compiler");
+        
+        String compilerTargetVM = config.getInitParameter("compilerTargetVM");
+        if(compilerTargetVM != null) {
+            this.compilerTargetVM = compilerTargetVM;
+        }
+        
+        String compilerSourceVM = config.getInitParameter("compilerSourceVM");
+        if(compilerSourceVM != null) {
+            this.compilerSourceVM = compilerSourceVM;
+        }
+        
+        String javaEncoding = config.getInitParameter("javaEncoding");
+        if (javaEncoding != null) {
+            this.javaEncoding = javaEncoding;
+        }
+        
+        String fork = config.getInitParameter("fork");
+        if (fork != null) {
+            if (fork.equalsIgnoreCase("true")) {
+                this.fork = true;
+            } else if (fork.equalsIgnoreCase("false")) {
+                this.fork = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.fork"));
+                }
+            }
+        }
+        
+        String xpoweredBy = config.getInitParameter("xpoweredBy"); 
+        if (xpoweredBy != null) {
+            if (xpoweredBy.equalsIgnoreCase("true")) {
+                this.xpoweredBy = true;
+            } else if (xpoweredBy.equalsIgnoreCase("false")) {
+                this.xpoweredBy = false;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.xpoweredBy"));
+                }
+            }
+        }
+        
+        // Setup the global Tag Libraries location cache for this
+        // web-application.
+        tldLocationsCache = new TldLocationsCache(context);
+        
+        // Setup the jsp config info for this web app.
+        jspConfig = new JspConfig(context);
+        
+        // Create a Tag plugin instance
+        tagPluginManager = new TagPluginManager(context);
+    }
+    
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/JasperException.java b/jasper/jasper2/src/share/org/apache/jasper/JasperException.java
new file mode 100644
index 0000000..3135a51
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/JasperException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper;
+
+/**
+ * Base class for all exceptions generated by the JSP engine. Makes it
+ * convienient to catch just this at the top-level. 
+ *
+ * @author Anil K. Vijendran
+ */
+public class JasperException extends javax.servlet.ServletException {
+    
+    public JasperException(String reason) {
+	super(reason);
+    }
+
+    /**
+     * Creates a JasperException with the embedded exception and the reason for
+     * throwing a JasperException
+     */
+    public JasperException (String reason, Throwable exception) {
+   	super(reason, exception);
+    }
+
+    /**
+     * Creates a JasperException with the embedded exception
+     */
+    public JasperException (Throwable exception) {
+   	super(exception);
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/JspC.java b/jasper/jasper2/src/share/org/apache/jasper/JspC.java
new file mode 100644
index 0000000..8fb49b0
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/JspC.java
@@ -0,0 +1,1326 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper;
+
+import java.io.BufferedReader;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jasper.compiler.Compiler;
+import org.apache.jasper.compiler.JspConfig;
+import org.apache.jasper.compiler.JspRuntimeContext;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.compiler.TagPluginManager;
+import org.apache.jasper.compiler.TldLocationsCache;
+import org.apache.jasper.servlet.JspCServletContext;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Shell for the jspc compiler.  Handles all options associated with the
+ * command line and creates compilation contexts which it then compiles
+ * according to the specified options.
+ *
+ * This version can process files from a _single_ webapp at once, i.e.
+ * a single docbase can be specified.
+ *
+ * It can be used as an Ant task using:
+ * <pre>
+ *   &lt;taskdef classname="org.apache.jasper.JspC" name="jasper2" &gt;
+ *      &lt;classpath&gt;
+ *          &lt;pathelement location="${java.home}/../lib/tools.jar"/&gt;
+ *          &lt;fileset dir="${ENV.CATALINA_HOME}/server/lib"&gt;
+ *              &lt;include name="*.jar"/&gt;
+ *          &lt;/fileset&gt;
+ *          &lt;fileset dir="${ENV.CATALINA_HOME}/common/lib"&gt;
+ *              &lt;include name="*.jar"/&gt;
+ *          &lt;/fileset&gt;
+ *          &lt;path refid="myjars"/&gt;
+ *       &lt;/classpath&gt;
+ *  &lt;/taskdef&gt;
+ *
+ *  &lt;jasper2 verbose="0"
+ *           package="my.package"
+ *           uriroot="${webapps.dir}/${webapp.name}"
+ *           webXmlFragment="${build.dir}/generated_web.xml"
+ *           outputDir="${webapp.dir}/${webapp.name}/WEB-INF/src/my/package" /&gt;
+ * </pre>
+ *
+ * @author Danno Ferrin
+ * @author Pierre Delisle
+ * @author Costin Manolache
+ * @author Yoav Shapira
+ */
+public class JspC implements Options {
+
+    public static final String DEFAULT_IE_CLASS_ID =
+            "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93";
+
+    // Logger
+    private static Log log = LogFactory.getLog(JspC.class);
+
+    private static final String SWITCH_VERBOSE = "-v";
+    private static final String SWITCH_HELP = "-help";
+    private static final String SWITCH_QUIET = "-q";
+    private static final String SWITCH_OUTPUT_DIR = "-d";
+    private static final String SWITCH_IE_CLASS_ID = "-ieplugin";
+    private static final String SWITCH_PACKAGE_NAME = "-p";
+    private static final String SWITCH_CLASS_NAME = "-c";
+    private static final String SWITCH_FULL_STOP = "--";
+    private static final String SWITCH_COMPILE = "-compile";
+    private static final String SWITCH_SOURCE = "-source";
+    private static final String SWITCH_TARGET = "-target";
+    private static final String SWITCH_URI_BASE = "-uribase";
+    private static final String SWITCH_URI_ROOT = "-uriroot";
+    private static final String SWITCH_FILE_WEBAPP = "-webapp";
+    private static final String SWITCH_WEBAPP_INC = "-webinc";
+    private static final String SWITCH_WEBAPP_XML = "-webxml";
+    private static final String SWITCH_MAPPED = "-mapped";
+    private static final String SWITCH_XPOWERED_BY = "-xpoweredBy";
+    private static final String SWITCH_TRIM_SPACES = "-trimSpaces";
+    private static final String SWITCH_CLASSPATH = "-classpath";
+    private static final String SWITCH_DIE = "-die";
+    private static final String SWITCH_POOLING = "-poolingEnabled";
+    private static final String SWITCH_ENCODING = "-javaEncoding";
+    private static final String SWITCH_SMAP = "-smap";
+    private static final String SWITCH_DUMP_SMAP = "-dumpsmap";
+
+    private static final String SHOW_SUCCESS ="-s";
+    private static final String LIST_ERRORS = "-l";
+    private static final int NO_WEBXML = 0;
+    private static final int INC_WEBXML = 10;
+    private static final int ALL_WEBXML = 20;
+    private static final int DEFAULT_DIE_LEVEL = 1;
+    private static final int NO_DIE_LEVEL = 0;
+
+    private static final String[] insertBefore =
+    { "</web-app>", "<servlet-mapping>", "<session-config>",
+      "<mime-mapping>", "<welcome-file-list>", "<error-page>", "<taglib>",
+      "<resource-env-ref>", "<resource-ref>", "<security-constraint>",
+      "<login-config>", "<security-role>", "<env-entry>", "<ejb-ref>",
+      "<ejb-local-ref>" };
+
+    private static int die;
+    private String classPath = null;
+    private URLClassLoader loader = null;
+    private boolean trimSpaces = false;
+    private boolean genStringAsCharArray = false;
+    private boolean xpoweredBy;
+    private boolean mappedFile = false;
+    private boolean poolingEnabled = true;
+    private File scratchDir;
+    private String ieClassId = DEFAULT_IE_CLASS_ID;
+    private String targetPackage;
+    private String targetClassName;
+    private String uriBase;
+    private String uriRoot;
+    private Project project;
+    private int dieLevel;
+    private boolean helpNeeded = false;
+    private boolean compile = false;
+    private boolean smapSuppressed = true;
+    private boolean smapDumped = false;
+
+    private String compiler = null;
+
+    private String compilerTargetVM = "1.4";
+    private String compilerSourceVM = "1.4";
+
+    private boolean classDebugInfo = true;
+
+    /**
+     * Throw an exception if there's a compilation error, or swallow it.
+     * Default is true to preserve old behavior.
+     */
+    private boolean failOnError = true;
+
+    /**
+     * The file extensions to be handled as JSP files.
+     * Default list is .jsp and .jspx.
+     */
+    private List extensions;
+
+    private Vector pages = new Vector();
+    private boolean errorOnUseBeanInvalidClassAttribute = true;
+
+    /**
+     * The java file encoding.  Default
+     * is UTF-8.  Added per bugzilla 19622.
+     */
+    private String javaEncoding = "UTF-8";
+
+    // Generation of web.xml fragments
+    private String webxmlFile;
+    private int webxmlLevel;
+    private boolean addWebXmlMappings = false;
+
+    private Writer mapout;
+    private CharArrayWriter servletout;
+    private CharArrayWriter mappingout;
+
+    private JspCServletContext context;
+
+    // Maintain a dummy JspRuntimeContext for compiling tag files
+    private JspRuntimeContext rctxt;
+
+    /**
+     * Cache for the TLD locations
+     */
+    private TldLocationsCache tldLocationsCache = null;
+
+    private JspConfig jspConfig = null;
+    private TagPluginManager tagPluginManager = null;
+
+    private boolean verbose = false;
+    private boolean listErrors = false;
+    private boolean showSuccess = false;
+    private int argPos;
+    private boolean fullstop = false;
+    private String args[];
+
+    public static void main(String arg[]) {
+        if (arg.length == 0) {
+            System.out.println(Localizer.getMessage("jspc.usage"));
+        } else {
+            try {
+                JspC jspc = new JspC();
+                jspc.setArgs(arg);
+                if (jspc.helpNeeded) {
+                    System.out.println(Localizer.getMessage("jspc.usage"));
+                } else {
+                    jspc.execute();
+                }
+            } catch (JasperException je) {
+                System.err.println(je);
+                //System.err.println(je.getMessage());
+                if (die != NO_DIE_LEVEL) {
+                    System.exit(die);
+                }
+            }
+        }
+    }
+
+    public void setArgs(String[] arg) throws JasperException {
+        args = arg;
+        String tok;
+
+        dieLevel = NO_DIE_LEVEL;
+        die = dieLevel;
+
+        while ((tok = nextArg()) != null) {
+            if (tok.equals(SWITCH_VERBOSE)) {
+                verbose = true;
+                showSuccess = true;
+                listErrors = true;
+            } else if (tok.equals(SWITCH_OUTPUT_DIR)) {
+                tok = nextArg();
+                setOutputDir( tok );
+            } else if (tok.equals(SWITCH_PACKAGE_NAME)) {
+                targetPackage = nextArg();
+            } else if (tok.equals(SWITCH_COMPILE)) {
+                compile=true;
+            } else if (tok.equals(SWITCH_CLASS_NAME)) {
+                targetClassName = nextArg();
+            } else if (tok.equals(SWITCH_URI_BASE)) {
+                uriBase=nextArg();
+            } else if (tok.equals(SWITCH_URI_ROOT)) {
+                setUriroot( nextArg());
+            } else if (tok.equals(SWITCH_FILE_WEBAPP)) {
+                setUriroot( nextArg());
+            } else if ( tok.equals( SHOW_SUCCESS ) ) {
+                showSuccess = true;
+            } else if ( tok.equals( LIST_ERRORS ) ) {
+                listErrors = true;
+            } else if (tok.equals(SWITCH_WEBAPP_INC)) {
+                webxmlFile = nextArg();
+                if (webxmlFile != null) {
+                    webxmlLevel = INC_WEBXML;
+                }
+            } else if (tok.equals(SWITCH_WEBAPP_XML)) {
+                webxmlFile = nextArg();
+                if (webxmlFile != null) {
+                    webxmlLevel = ALL_WEBXML;
+                }
+            } else if (tok.equals(SWITCH_MAPPED)) {
+                mappedFile = true;
+            } else if (tok.equals(SWITCH_XPOWERED_BY)) {
+                xpoweredBy = true;
+            } else if (tok.equals(SWITCH_TRIM_SPACES)) {
+                setTrimSpaces(true);
+            } else if (tok.equals(SWITCH_CLASSPATH)) {
+                setClassPath(nextArg());
+            } else if (tok.startsWith(SWITCH_DIE)) {
+                try {
+                    dieLevel = Integer.parseInt(
+                        tok.substring(SWITCH_DIE.length()));
+                } catch (NumberFormatException nfe) {
+                    dieLevel = DEFAULT_DIE_LEVEL;
+                }
+                die = dieLevel;
+            } else if (tok.equals(SWITCH_HELP)) {
+                helpNeeded = true;
+            } else if (tok.equals(SWITCH_POOLING)) {
+                tok = nextArg();
+                if ("false".equals(tok)) {
+                    poolingEnabled = false;
+                } else {
+                    poolingEnabled = true;
+                }
+            } else if (tok.equals(SWITCH_ENCODING)) {
+                setJavaEncoding(nextArg());
+            } else if (tok.equals(SWITCH_SOURCE)) {
+                setCompilerSourceVM(nextArg());
+            } else if (tok.equals(SWITCH_TARGET)) {
+                setCompilerTargetVM(nextArg());
+            } else if (tok.equals(SWITCH_SMAP)) {
+                smapSuppressed = false;
+            } else if (tok.equals(SWITCH_DUMP_SMAP)) {
+                smapDumped = true;
+            } else {
+                if (tok.startsWith("-")) {
+                    throw new JasperException("Unrecognized option: " + tok +
+                        ".  Use -help for help.");
+                }
+                if (!fullstop) {
+                    argPos--;
+                }
+                // Start treating the rest as JSP Pages
+                break;
+            }
+        }
+
+        // Add all extra arguments to the list of files
+        while( true ) {
+            String file = nextFile();
+            if( file==null ) break;
+            pages.addElement( file );
+        }
+    }
+
+    public boolean getKeepGenerated() {
+        // isn't this why we are running jspc?
+        return true;
+    }
+
+    public boolean getTrimSpaces() {
+        return trimSpaces;
+    }
+
+    public void setTrimSpaces(boolean ts) {
+        this.trimSpaces = ts;
+    }
+
+    public boolean isPoolingEnabled() {
+        return poolingEnabled;
+    }
+
+    public void setPoolingEnabled(boolean poolingEnabled) {
+        this.poolingEnabled = poolingEnabled;
+    }
+
+    public boolean isXpoweredBy() {
+        return xpoweredBy;
+    }
+
+    public void setXpoweredBy(boolean xpoweredBy) {
+        this.xpoweredBy = xpoweredBy;
+    }
+
+    public boolean getErrorOnUseBeanInvalidClassAttribute() {
+        return errorOnUseBeanInvalidClassAttribute;
+    }
+
+    public void setErrorOnUseBeanInvalidClassAttribute(boolean b) {
+        errorOnUseBeanInvalidClassAttribute = b;
+    }
+
+    public int getTagPoolSize() {
+        return Constants.MAX_POOL_SIZE;
+    }
+
+    /**
+     * Are we supporting HTML mapped servlets?
+     */
+    public boolean getMappedFile() {
+        return mappedFile;
+    }
+
+    // Off-line compiler, no need for security manager
+    public Object getProtectionDomain() {
+        return null;
+    }
+
+    public boolean getSendErrorToClient() {
+        // implied send to System.err
+        return true;
+    }
+
+    public void setClassDebugInfo( boolean b ) {
+        classDebugInfo=b;
+    }
+
+    public boolean getClassDebugInfo() {
+        // compile with debug info
+        return classDebugInfo;
+    }
+
+    /**
+     * Background compilation check intervals in seconds
+     */
+    public int getCheckInterval() {
+        return 0;
+    }
+
+    /**
+     * Modification test interval.
+     */
+    public int getModificationTestInterval() {
+        return 0;
+    }
+
+    /**
+     * Is Jasper being used in development mode?
+     */
+    public boolean getDevelopment() {
+        return false;
+    }
+
+    /**
+     * Is the generation of SMAP info for JSR45 debuggin suppressed?
+     */
+    public boolean isSmapSuppressed() {
+        return smapSuppressed;
+    }
+
+    /**
+     * Set smapSuppressed flag.
+     */
+    public void setSmapSuppressed(boolean smapSuppressed) {
+        this.smapSuppressed = smapSuppressed;
+    }
+
+    
+    /**
+     * Should SMAP info for JSR45 debugging be dumped to a file?
+     */
+    public boolean isSmapDumped() {
+        return smapDumped;
+    }
+
+    /**
+     * Set smapSuppressed flag.
+     */
+    public void setSmapDumped(boolean smapDumped) {
+        this.smapDumped = smapDumped;
+    }
+
+    
+    /**
+     * Determines whether text strings are to be generated as char arrays,
+     * which improves performance in some cases.
+     *
+     * @param genStringAsCharArray true if text strings are to be generated as
+     * char arrays, false otherwise
+     */
+    public void setGenStringAsCharArray(boolean genStringAsCharArray) {
+        this.genStringAsCharArray = genStringAsCharArray;
+    }
+
+    /**
+     * Indicates whether text strings are to be generated as char arrays.
+     *
+     * @return true if text strings are to be generated as char arrays, false
+     * otherwise
+     */
+    public boolean genStringAsCharArray() {
+        return genStringAsCharArray;
+    }
+
+    /**
+     * Sets the class-id value to be sent to Internet Explorer when using
+     * <jsp:plugin> tags.
+     *
+     * @param ieClassId Class-id value
+     */
+    public void setIeClassId(String ieClassId) {
+        this.ieClassId = ieClassId;
+    }
+
+    /**
+     * Gets the class-id value that is sent to Internet Explorer when using
+     * <jsp:plugin> tags.
+     *
+     * @return Class-id value
+     */
+    public String getIeClassId() {
+        return ieClassId;
+    }
+
+    public File getScratchDir() {
+        return scratchDir;
+    }
+
+    public Class getJspCompilerPlugin() {
+       // we don't compile, so this is meanlingless
+        return null;
+    }
+
+    public String getJspCompilerPath() {
+       // we don't compile, so this is meanlingless
+        return null;
+    }
+
+    /**
+     * Compiler to use.
+     */
+    public String getCompiler() {
+        return compiler;
+    }
+
+    public void setCompiler(String c) {
+        compiler=c;
+    }
+
+    /**
+     * @see Options#getCompilerTargetVM
+     */
+    public String getCompilerTargetVM() {
+        return compilerTargetVM;
+    }
+
+    public void setCompilerTargetVM(String vm) {
+        compilerTargetVM = vm;
+    }
+
+    /**
+     * @see Options#getCompilerSourceVM()
+     */
+     public String getCompilerSourceVM() {
+         return compilerSourceVM;
+     }
+        
+    /**
+     * @see Options#getCompilerSourceVM()
+     */
+    public void setCompilerSourceVM(String vm) {
+        compilerSourceVM = vm;
+    }
+
+    public TldLocationsCache getTldLocationsCache() {
+        return tldLocationsCache;
+    }
+
+    /**
+     * Returns the encoding to use for
+     * java files.  The default is UTF-8.
+     *
+     * @return String The encoding
+     */
+    public String getJavaEncoding() {
+        return javaEncoding;
+    }
+
+    /**
+     * Sets the encoding to use for
+     * java files.
+     *
+     * @param encodingName The name, e.g. "UTF-8"
+     */
+    public void setJavaEncoding(String encodingName) {
+        javaEncoding = encodingName;
+    }
+
+    public boolean getFork() {
+        return false;
+    }
+
+    public String getClassPath() {
+        if( classPath != null )
+            return classPath;
+        return System.getProperty("java.class.path");
+    }
+
+    public void setClassPath(String s) {
+        classPath=s;
+    }
+
+    /**
+     * Returns the list of file extensions
+     * that are treated as JSP files.
+     *
+     * @return The list of extensions
+     */
+    public List getExtensions() {
+        return extensions;
+    }
+
+    /**
+     * Adds the given file extension to the
+     * list of extensions handled as JSP files.
+     *
+     * @param extension The extension to add, e.g. "myjsp"
+     */
+    protected void addExtension(final String extension) {
+        if(extension != null) {
+            if(extensions == null) {
+                extensions = new Vector();
+            }
+
+            extensions.add(extension);
+        }
+    }
+
+    /**
+     * Sets the project.
+     *
+     * @param theProject The project
+     */
+    public void setProject(final Project theProject) {
+        project = theProject;
+    }
+
+    /**
+     * Returns the project: may be null if not running
+     * inside an Ant project.
+     *
+     * @return The project
+     */
+    public Project getProject() {
+        return project;
+    }
+
+    /**
+     * Base dir for the webapp. Used to generate class names and resolve
+     * includes
+     */
+    public void setUriroot( String s ) {
+        if( s==null ) {
+            uriRoot = s;
+            return;
+        }
+        try {
+            uriRoot = resolveFile(s).getCanonicalPath();
+        } catch( Exception ex ) {
+            uriRoot = s;
+        }
+    }
+
+    /*
+     * Parses comma-separated list of JSP files to be processed.
+     *
+     * <p>Each file is interpreted relative to uriroot, unless it is absolute,
+     * in which case it must start with uriroot.
+     *
+     * @param jspFiles Comma-separated list of JSP files to be processed
+     */
+    public void setJspFiles(String jspFiles) {
+        StringTokenizer tok = new StringTokenizer(jspFiles, " ,");
+        while (tok.hasMoreTokens()) {
+            pages.addElement(tok.nextToken());
+        }
+    }
+
+    public void setCompile( boolean b ) {
+        compile=b;
+    }
+
+    public void setVerbose( int level ) {
+        if (level > 0) {
+            verbose = true;
+            showSuccess = true;
+            listErrors = true;
+        }
+    }
+
+    public void setValidateXml( boolean b ) {
+        org.apache.jasper.xmlparser.ParserUtils.validating=b;
+    }
+
+    public void setListErrors( boolean b ) {
+        listErrors = b;
+    }
+
+    public void setOutputDir( String s ) {
+        if( s!= null ) {
+            scratchDir = resolveFile(s).getAbsoluteFile();
+        } else {
+            scratchDir=null;
+        }
+    }
+
+    public void setPackage( String p ) {
+        targetPackage=p;
+    }
+
+    /**
+     * Class name of the generated file ( without package ).
+     * Can only be used if a single file is converted.
+     * XXX Do we need this feature ?
+     */
+    public void setClassName( String p ) {
+        targetClassName=p;
+    }
+
+    /**
+     * File where we generate a web.xml fragment with the class definitions.
+     */
+    public void setWebXmlFragment( String s ) {
+        webxmlFile=resolveFile(s).getAbsolutePath();
+        webxmlLevel=INC_WEBXML;
+    }
+
+    /**
+     * File where we generate a complete web.xml with the class definitions.
+     */
+    public void setWebXml( String s ) {
+        webxmlFile=resolveFile(s).getAbsolutePath();
+        webxmlLevel=ALL_WEBXML;
+    }
+
+    public void setAddWebXmlMappings(boolean b) {
+        addWebXmlMappings = b;
+    }
+
+    /**
+     * Set the option that throws an exception in case of a compilation error.
+     */
+    public void setFailOnError(final boolean b) {
+        failOnError = b;
+    }
+
+    public boolean getFailOnError() {
+        return failOnError;
+    }
+
+    /**
+     * Obtain JSP configuration informantion specified in web.xml.
+     */
+    public JspConfig getJspConfig() {
+        return jspConfig;
+    }
+
+    public TagPluginManager getTagPluginManager() {
+        return tagPluginManager;
+    }
+
+    public void generateWebMapping( String file, JspCompilationContext clctxt )
+        throws IOException
+    {
+        String className = clctxt.getServletClassName();
+        String packageName = clctxt.getServletPackageName();
+
+        String thisServletName;
+        if  ("".equals(packageName)) {
+            thisServletName = className;
+        } else {
+            thisServletName = packageName + '.' + className;
+        }
+
+        if (servletout != null) {
+            servletout.write("\n    <servlet>\n        <servlet-name>");
+            servletout.write(thisServletName);
+            servletout.write("</servlet-name>\n        <servlet-class>");
+            servletout.write(thisServletName);
+            servletout.write("</servlet-class>\n    </servlet>\n");
+        }
+        if (mappingout != null) {
+            mappingout.write("\n    <servlet-mapping>\n        <servlet-name>");
+            mappingout.write(thisServletName);
+            mappingout.write("</servlet-name>\n        <url-pattern>");
+            mappingout.write(file.replace('\\', '/'));
+            mappingout.write("</url-pattern>\n    </servlet-mapping>\n");
+
+        }
+    }
+
+    /**
+     * Include the generated web.xml inside the webapp's web.xml.
+     */
+    protected void mergeIntoWebXml() throws IOException {
+
+        File webappBase = new File(uriRoot);
+        File webXml = new File(webappBase, "WEB-INF/web.xml");
+        File webXml2 = new File(webappBase, "WEB-INF/web2.xml");
+        String insertStartMarker =
+            Localizer.getMessage("jspc.webinc.insertStart");
+        String insertEndMarker =
+            Localizer.getMessage("jspc.webinc.insertEnd");
+
+        BufferedReader reader = new BufferedReader(new FileReader(webXml));
+        BufferedReader fragmentReader =
+            new BufferedReader(new FileReader(webxmlFile));
+        PrintWriter writer = new PrintWriter(new FileWriter(webXml2));
+
+        // Insert the <servlet> and <servlet-mapping> declarations
+        int pos = -1;
+        String line = null;
+        while (true) {
+            line = reader.readLine();
+            if (line == null) {
+                break;
+            }
+            // Skip anything previously generated by JSPC
+            if (line.indexOf(insertStartMarker) >= 0) {
+                while (true) {
+                    line = reader.readLine();
+                    if (line == null) {
+                        return;
+                    }
+                    if (line.indexOf(insertEndMarker) >= 0) {
+                        line = reader.readLine();
+                        line = reader.readLine();
+                        if (line == null) {
+                            return;
+                        }
+                        break;
+                    }
+                }
+            }
+            for (int i = 0; i < insertBefore.length; i++) {
+                pos = line.indexOf(insertBefore[i]);
+                if (pos >= 0)
+                    break;
+            }
+            if (pos >= 0) {
+                writer.print(line.substring(0, pos));
+                break;
+            } else {
+                writer.println(line);
+            }
+        }
+
+        writer.println(insertStartMarker);
+        while (true) {
+            String line2 = fragmentReader.readLine();
+            if (line2 == null) {
+                writer.println();
+                break;
+            }
+            writer.println(line2);
+        }
+        writer.println(insertEndMarker);
+        writer.println();
+
+        for (int i = 0; i < pos; i++) {
+            writer.print(" ");
+        }
+        writer.println(line.substring(pos));
+
+        while (true) {
+            line = reader.readLine();
+            if (line == null) {
+                break;
+            }
+            writer.println(line);
+        }
+        writer.close();
+
+        reader.close();
+        fragmentReader.close();
+
+        FileInputStream fis = new FileInputStream(webXml2);
+        FileOutputStream fos = new FileOutputStream(webXml);
+
+        byte buf[] = new byte[512];
+        while (true) {
+            int n = fis.read(buf);
+            if (n < 0) {
+                break;
+            }
+            fos.write(buf, 0, n);
+        }
+
+        fis.close();
+        fos.close();
+
+        webXml2.delete();
+        (new File(webxmlFile)).delete();
+
+    }
+
+    private void processFile(String file)
+        throws JasperException
+    {
+        ClassLoader originalClassLoader = null;
+
+        try {
+            // set up a scratch/output dir if none is provided
+            if (scratchDir == null) {
+                String temp = System.getProperty("java.io.tmpdir");
+                if (temp == null) {
+                    temp = "";
+                }
+                scratchDir = new File(new File(temp).getAbsolutePath());
+            }
+
+            String jspUri=file.replace('\\','/');
+            JspCompilationContext clctxt = new JspCompilationContext
+                ( jspUri, false,  this, context, null, rctxt );
+
+            /* Override the defaults */
+            if ((targetClassName != null) && (targetClassName.length() > 0)) {
+                clctxt.setServletClassName(targetClassName);
+                targetClassName = null;
+            }
+            if (targetPackage != null) {
+                clctxt.setServletPackageName(targetPackage);
+            }
+
+            originalClassLoader = Thread.currentThread().getContextClassLoader();
+            if( loader==null ) {
+                initClassLoader( clctxt );
+            }
+            Thread.currentThread().setContextClassLoader(loader);
+
+            clctxt.setClassLoader(loader);
+            clctxt.setClassPath(classPath);
+
+            Compiler clc = clctxt.createCompiler();
+
+            // If compile is set, generate both .java and .class, if
+            // .jsp file is newer than .class file;
+            // Otherwise only generate .java, if .jsp file is newer than
+            // the .java file
+            if( clc.isOutDated(compile) ) {
+                clc.compile(compile, true);
+            }
+
+            // Generate mapping
+            generateWebMapping( file, clctxt );
+            if ( showSuccess ) {
+                log.info( "Built File: " + file );
+            }
+
+        } catch (JasperException je) {
+            Throwable rootCause = je;
+            while (rootCause instanceof JasperException
+                    && ((JasperException) rootCause).getRootCause() != null) {
+                rootCause = ((JasperException) rootCause).getRootCause();
+            }
+            if (rootCause != je) {
+                log.error(Localizer.getMessage("jspc.error.generalException",
+                                               file),
+                          rootCause);
+            }
+
+            // Bugzilla 35114.
+            if(getFailOnError()) {
+                throw je;
+            } else {
+                log.error(je.getMessage());
+            }
+
+        } catch (Exception e) {
+            if ((e instanceof FileNotFoundException) && log.isWarnEnabled()) {
+                log.warn(Localizer.getMessage("jspc.error.fileDoesNotExist",
+                                              e.getMessage()));
+            }
+            throw new JasperException(e);
+        } finally {
+            if(originalClassLoader != null) {
+                Thread.currentThread().setContextClassLoader(originalClassLoader);
+            }
+        }
+    }
+
+    /**
+     * Locate all jsp files in the webapp. Used if no explicit
+     * jsps are specified.
+     */
+    public void scanFiles( File base ) throws JasperException {
+        Stack dirs = new Stack();
+        dirs.push(base);
+
+        // Make sure default extensions are always included
+        if ((getExtensions() == null) || (getExtensions().size() < 2)) {
+            addExtension("jsp");
+            addExtension("jspx");
+        }
+
+        while (!dirs.isEmpty()) {
+            String s = dirs.pop().toString();
+            File f = new File(s);
+            if (f.exists() && f.isDirectory()) {
+                String[] files = f.list();
+                String ext;
+                for (int i = 0; (files != null) && i < files.length; i++) {
+                    File f2 = new File(s, files[i]);
+                    if (f2.isDirectory()) {
+                        dirs.push(f2.getPath());
+                    } else {
+                        String path = f2.getPath();
+                        String uri = path.substring(uriRoot.length());
+                        ext = files[i].substring(files[i].lastIndexOf('.') +1);
+                        if (getExtensions().contains(ext) ||
+                            jspConfig.isJspPage(uri)) {
+                            pages.addElement(path);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void execute() throws JasperException {
+
+        try {
+            if (uriRoot == null) {
+                if( pages.size() == 0 ) {
+                    throw new JasperException(
+                        Localizer.getMessage("jsp.error.jspc.missingTarget"));
+                }
+                String firstJsp=(String)pages.elementAt( 0 );
+                File firstJspF = new File( firstJsp );
+                if (!firstJspF.exists()) {
+                    throw new JasperException(
+                        Localizer.getMessage("jspc.error.fileDoesNotExist",
+                                             firstJsp));
+                }
+                locateUriRoot( firstJspF );
+            }
+
+            if (uriRoot == null) {
+                throw new JasperException(
+                    Localizer.getMessage("jsp.error.jspc.no_uriroot"));
+            }
+
+            if( context==null ) {
+                initServletContext();
+            }
+
+            // No explicit pages, we'll process all .jsp in the webapp
+            if (pages.size() == 0) {
+                scanFiles( new File( uriRoot ));
+            }
+
+            File uriRootF = new File(uriRoot);
+            if (!uriRootF.exists() || !uriRootF.isDirectory()) {
+                throw new JasperException(
+                    Localizer.getMessage("jsp.error.jspc.uriroot_not_dir"));
+            }
+
+            initWebXml();
+
+            Enumeration e = pages.elements();
+            while (e.hasMoreElements()) {
+                String nextjsp = e.nextElement().toString();
+                File fjsp = new File(nextjsp);
+                if (!fjsp.isAbsolute()) {
+                    fjsp = new File(uriRootF, nextjsp);
+                }
+                if (!fjsp.exists()) {
+                    if (log.isWarnEnabled()) {
+                        log.warn
+                            (Localizer.getMessage
+                             ("jspc.error.fileDoesNotExist", fjsp.toString()));
+                    }
+                    continue;
+                }
+                String s = fjsp.getAbsolutePath();
+                if (s.startsWith(uriRoot)) {
+                    nextjsp = s.substring(uriRoot.length());
+                }
+                if (nextjsp.startsWith("." + File.separatorChar)) {
+                    nextjsp = nextjsp.substring(2);
+                }
+                processFile(nextjsp);
+            }
+
+            completeWebXml();
+
+            if (addWebXmlMappings) {
+                mergeIntoWebXml();
+            }
+
+        } catch (IOException ioe) {
+            throw new JasperException(ioe);
+
+        } catch (JasperException je) {
+            Throwable rootCause = je;
+            while (rootCause instanceof JasperException
+                    && ((JasperException) rootCause).getRootCause() != null) {
+                rootCause = ((JasperException) rootCause).getRootCause();
+            }
+            if (rootCause != je) {
+                rootCause.printStackTrace();
+            }
+            throw je;
+        } finally {
+            if (loader != null) {
+                LogFactory.release(loader);
+            }
+        }
+    }
+
+    // ==================== Private utility methods ====================
+
+    private String nextArg() {
+        if ((argPos >= args.length)
+            || (fullstop = SWITCH_FULL_STOP.equals(args[argPos]))) {
+            return null;
+        } else {
+            return args[argPos++];
+        }
+    }
+
+    private String nextFile() {
+        if (fullstop) argPos++;
+        if (argPos >= args.length) {
+            return null;
+        } else {
+            return args[argPos++];
+        }
+    }
+
+    private void initWebXml() {
+        try {
+            if (webxmlLevel >= INC_WEBXML) {
+                File fmapings = new File(webxmlFile);
+                mapout = new FileWriter(fmapings);
+                servletout = new CharArrayWriter();
+                mappingout = new CharArrayWriter();
+            } else {
+                mapout = null;
+                servletout = null;
+                mappingout = null;
+            }
+            if (webxmlLevel >= ALL_WEBXML) {
+                mapout.write(Localizer.getMessage("jspc.webxml.header"));
+                mapout.flush();
+            } else if ((webxmlLevel>= INC_WEBXML) && !addWebXmlMappings) {
+                mapout.write(Localizer.getMessage("jspc.webinc.header"));
+                mapout.flush();
+            }
+        } catch (IOException ioe) {
+            mapout = null;
+            servletout = null;
+            mappingout = null;
+        }
+    }
+
+    private void completeWebXml() {
+        if (mapout != null) {
+            try {
+                servletout.writeTo(mapout);
+                mappingout.writeTo(mapout);
+                if (webxmlLevel >= ALL_WEBXML) {
+                    mapout.write(Localizer.getMessage("jspc.webxml.footer"));
+                } else if ((webxmlLevel >= INC_WEBXML) && !addWebXmlMappings) {
+                    mapout.write(Localizer.getMessage("jspc.webinc.footer"));
+                }
+                mapout.close();
+            } catch (IOException ioe) {
+                // noting to do if it fails since we are done with it
+            }
+        }
+    }
+
+    private void initServletContext() {
+        try {
+            context =new JspCServletContext
+                (new PrintWriter(System.out),
+                 new URL("file:" + uriRoot.replace('\\','/') + '/'));
+            tldLocationsCache = new TldLocationsCache(context, true);
+        } catch (MalformedURLException me) {
+            System.out.println("**" + me);
+        }
+        rctxt = new JspRuntimeContext(context, this);
+        jspConfig = new JspConfig(context);
+        tagPluginManager = new TagPluginManager(context);
+    }
+
+    /**
+     * Initializes the classloader as/if needed for the given
+     * compilation context.
+     *
+     * @param clctxt The compilation context
+     * @throws IOException If an error occurs
+     */
+    private void initClassLoader(JspCompilationContext clctxt)
+        throws IOException {
+
+        classPath = getClassPath();
+
+        ClassLoader jspcLoader = getClass().getClassLoader();
+        if (jspcLoader instanceof AntClassLoader) {
+            classPath += File.pathSeparator
+                + ((AntClassLoader) jspcLoader).getClasspath();
+        }
+
+        // Turn the classPath into URLs
+        ArrayList urls = new ArrayList();
+        StringTokenizer tokenizer = new StringTokenizer(classPath,
+                                                        File.pathSeparator);
+        while (tokenizer.hasMoreTokens()) {
+            String path = tokenizer.nextToken();
+            try {
+                File libFile = new File(path);
+                urls.add(libFile.toURL());
+            } catch (IOException ioe) {
+                // Failing a toCanonicalPath on a file that
+                // exists() should be a JVM regression test,
+                // therefore we have permission to freak uot
+                throw new RuntimeException(ioe.toString());
+            }
+        }
+
+        File webappBase = new File(uriRoot);
+        if (webappBase.exists()) {
+            File classes = new File(webappBase, "/WEB-INF/classes");
+            try {
+                if (classes.exists()) {
+                    classPath = classPath + File.pathSeparator
+                        + classes.getCanonicalPath();
+                    urls.add(classes.getCanonicalFile().toURL());
+                }
+            } catch (IOException ioe) {
+                // failing a toCanonicalPath on a file that
+                // exists() should be a JVM regression test,
+                // therefore we have permission to freak out
+                throw new RuntimeException(ioe.toString());
+            }
+            File lib = new File(webappBase, "/WEB-INF/lib");
+            if (lib.exists() && lib.isDirectory()) {
+                String[] libs = lib.list();
+                for (int i = 0; i < libs.length; i++) {
+                    if( libs[i].length() <5 ) continue;
+                    String ext=libs[i].substring( libs[i].length() - 4 );
+                    if (! ".jar".equalsIgnoreCase(ext)) {
+                        if (".tld".equalsIgnoreCase(ext)) {
+                            log.warn("TLD files should not be placed in "
+                                     + "/WEB-INF/lib");
+                        }
+                        continue;
+                    }
+                    try {
+                        File libFile = new File(lib, libs[i]);
+                        classPath = classPath + File.pathSeparator
+                            + libFile.getAbsolutePath();
+                        urls.add(libFile.getAbsoluteFile().toURL());
+                    } catch (IOException ioe) {
+                        // failing a toCanonicalPath on a file that
+                        // exists() should be a JVM regression test,
+                        // therefore we have permission to freak out
+                        throw new RuntimeException(ioe.toString());
+                    }
+                }
+            }
+        }
+
+        // What is this ??
+        urls.add(new File(clctxt.getRealPath("/")).getCanonicalFile().toURL());
+
+        URL urlsA[]=new URL[urls.size()];
+        urls.toArray(urlsA);
+        loader = new URLClassLoader(urlsA, this.getClass().getClassLoader());
+
+    }
+
+    /**
+     * Find the WEB-INF dir by looking up in the directory tree.
+     * This is used if no explicit docbase is set, but only files.
+     * XXX Maybe we should require the docbase.
+     */
+    private void locateUriRoot( File f ) {
+        String tUriBase = uriBase;
+        if (tUriBase == null) {
+            tUriBase = "/";
+        }
+        try {
+            if (f.exists()) {
+                f = new File(f.getAbsolutePath());
+                while (f != null) {
+                    File g = new File(f, "WEB-INF");
+                    if (g.exists() && g.isDirectory()) {
+                        uriRoot = f.getCanonicalPath();
+                        uriBase = tUriBase;
+                        if (log.isInfoEnabled()) {
+                            log.info(Localizer.getMessage(
+                                        "jspc.implicit.uriRoot",
+                                        uriRoot));
+                        }
+                        break;
+                    }
+                    if (f.exists() && f.isDirectory()) {
+                        tUriBase = "/" + f.getName() + "/" + tUriBase;
+                    }
+
+                    String fParent = f.getParent();
+                    if (fParent == null) {
+                        break;
+                    } else {
+                        f = new File(fParent);
+                    }
+
+                    // If there is no acceptible candidate, uriRoot will
+                    // remain null to indicate to the CompilerContext to
+                    // use the current working/user dir.
+                }
+
+                if (uriRoot != null) {
+                    File froot = new File(uriRoot);
+                    uriRoot = froot.getCanonicalPath();
+                }
+            }
+        } catch (IOException ioe) {
+            // since this is an optional default and a null value
+            // for uriRoot has a non-error meaning, we can just
+            // pass straight through
+        }
+    }
+
+    /**
+     * Resolves the relative or absolute pathname correctly
+     * in both Ant and command-line situations.  If Ant launched
+     * us, we should use the basedir of the current project
+     * to resolve relative paths.
+     *
+     * See Bugzilla 35571.
+     *
+     * @param s The file
+     * @return The file resolved
+     */
+     protected File resolveFile(final String s) {
+         if(getProject() == null) {
+             // Note FileUtils.getFileUtils replaces FileUtils.newFileUtils in Ant 1.6.3
+             return FileUtils.newFileUtils().resolveFile(null, s);
+         } else {
+             return FileUtils.newFileUtils().resolveFile(getProject().getBaseDir(), s);
+         }
+     }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/JspCompilationContext.java b/jasper/jasper2/src/share/org/apache/jasper/JspCompilationContext.java
new file mode 100644
index 0000000..b915b1d
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/JspCompilationContext.java
@@ -0,0 +1,698 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Hashtable;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.servlet.jsp.tagext.TagInfo;
+
+import org.apache.jasper.compiler.Compiler;
+import org.apache.jasper.compiler.JspRuntimeContext;
+import org.apache.jasper.compiler.JspUtil;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.compiler.ServletWriter;
+import org.apache.jasper.servlet.JasperLoader;
+import org.apache.jasper.servlet.JspServletWrapper;
+
+/**
+ * A place holder for various things that are used through out the JSP
+ * engine. This is a per-request/per-context data structure. Some of
+ * the instance variables are set at different points.
+ *
+ * Most of the path-related stuff is here - mangling names, versions, dirs,
+ * loading resources and dealing with uris. 
+ *
+ * @author Anil K. Vijendran
+ * @author Harish Prabandham
+ * @author Pierre Delisle
+ * @author Costin Manolache
+ * @author Kin-man Chung
+ */
+public class JspCompilationContext {
+
+    private Hashtable tagFileJarUrls;
+    private boolean isPackagedTagFile;
+
+    private String className;
+    private String jspUri;
+    private boolean isErrPage;
+    private String basePackageName;
+    private String derivedPackageName;
+    private String servletJavaFileName;
+    private String javaPath;
+    private String classFileName;
+    private String contentType;
+    private ServletWriter writer;
+    private Options options;
+    private JspServletWrapper jsw;
+    private Compiler jspCompiler;
+    private String classPath;
+
+    private String baseURI;
+    private String baseOutputDir;
+    private String outputDir;
+    private ServletContext context;
+    private URLClassLoader loader;
+
+    private JspRuntimeContext rctxt;
+
+    private int removed = 0;
+
+    private URLClassLoader jspLoader;
+    private URL baseUrl;
+    private Class servletClass;
+
+    private boolean isTagFile;
+    private boolean protoTypeMode;
+    private TagInfo tagInfo;
+    private URL tagFileJarUrl;
+
+    // jspURI _must_ be relative to the context
+    public JspCompilationContext(String jspUri,
+                                 boolean isErrPage,
+                                 Options options,
+                                 ServletContext context,
+                                 JspServletWrapper jsw,
+                                 JspRuntimeContext rctxt) {
+
+        this.jspUri = canonicalURI(jspUri);
+        this.isErrPage = isErrPage;
+        this.options = options;
+        this.jsw = jsw;
+        this.context = context;
+
+        this.baseURI = jspUri.substring(0, jspUri.lastIndexOf('/') + 1);
+        // hack fix for resolveRelativeURI
+        if (baseURI == null) {
+            baseURI = "/";
+        } else if (baseURI.charAt(0) != '/') {
+            // strip the basde slash since it will be combined with the
+            // uriBase to generate a file
+            baseURI = "/" + baseURI;
+        }
+        if (baseURI.charAt(baseURI.length() - 1) != '/') {
+            baseURI += '/';
+        }
+
+        this.rctxt = rctxt;
+        this.tagFileJarUrls = new Hashtable();
+        this.basePackageName = Constants.JSP_PACKAGE_NAME;
+    }
+
+    public JspCompilationContext(String tagfile,
+                                 TagInfo tagInfo, 
+                                 Options options,
+                                 ServletContext context,
+                                 JspServletWrapper jsw,
+                                 JspRuntimeContext rctxt,
+                                 URL tagFileJarUrl) {
+        this(tagfile, false, options, context, jsw, rctxt);
+        this.isTagFile = true;
+        this.tagInfo = tagInfo;
+        this.tagFileJarUrl = tagFileJarUrl;
+        if (tagFileJarUrl != null) {
+            isPackagedTagFile = true;
+        }
+    }
+
+    /* ==================== Methods to override ==================== */
+    
+    /** ---------- Class path and loader ---------- */
+
+    /**
+     * The classpath that is passed off to the Java compiler. 
+     */
+    public String getClassPath() {
+        if( classPath != null )
+            return classPath;
+        return rctxt.getClassPath();
+    }
+
+    /**
+     * The classpath that is passed off to the Java compiler. 
+     */
+    public void setClassPath(String classPath) {
+        this.classPath = classPath;
+    }
+
+    /**
+     * What class loader to use for loading classes while compiling
+     * this JSP?
+     */
+    public ClassLoader getClassLoader() {
+        if( loader != null )
+            return loader;
+        return rctxt.getParentClassLoader();
+    }
+
+    public void setClassLoader(URLClassLoader loader) {
+        this.loader = loader;
+    }
+
+    public ClassLoader getJspLoader() {
+        if( jspLoader == null ) {
+            jspLoader = new JasperLoader
+            (new URL[] {baseUrl},
+                    getClassLoader(),
+                    rctxt.getPermissionCollection(),
+                    rctxt.getCodeSource());
+        }
+        return jspLoader;
+    }
+
+    /** ---------- Input/Output  ---------- */
+    
+    /**
+     * The output directory to generate code into.  The output directory
+     * is make up of the scratch directory, which is provide in Options,
+     * plus the directory derived from the package name.
+     */
+    public String getOutputDir() {
+	if (outputDir == null) {
+	    createOutputDir();
+	}
+
+        return outputDir;
+    }
+
+    /**
+     * Create a "Compiler" object based on some init param data. This
+     * is not done yet. Right now we're just hardcoding the actual
+     * compilers that are created. 
+     */
+    public Compiler createCompiler() throws JasperException {
+        if (jspCompiler != null ) {
+            return jspCompiler;
+        }
+        jspCompiler = null;
+        if (options.getCompiler() == null) {
+            jspCompiler = createCompiler("org.apache.jasper.compiler.JDTCompiler");
+            if (jspCompiler == null) {
+                jspCompiler = createCompiler("org.apache.jasper.compiler.AntCompiler");
+            }
+        } else {
+            jspCompiler = createCompiler("org.apache.jasper.compiler.AntCompiler");
+            if (jspCompiler == null) {
+                jspCompiler = createCompiler("org.apache.jasper.compiler.JDTCompiler");
+            }
+        }
+        jspCompiler.init(this, jsw);
+        return jspCompiler;
+    }
+
+    private static Compiler createCompiler(String className) {
+        Compiler compiler = null; 
+        try {
+            compiler = (Compiler) Class.forName(className).newInstance();
+        } catch (Throwable t) {
+            // Log ?
+            // FIXME: log
+        }
+        return compiler;
+    }
+    
+    public Compiler getCompiler() {
+        return jspCompiler;
+    }
+
+    /** ---------- Access resources in the webapp ---------- */
+
+    /** 
+     * Get the full value of a URI relative to this compilations context
+     * uses current file as the base.
+     */
+    public String resolveRelativeUri(String uri) {
+        // sometimes we get uri's massaged from File(String), so check for
+        // a root directory deperator char
+        if (uri.startsWith("/") || uri.startsWith(File.separator)) {
+            return uri;
+        } else {
+            return baseURI + uri;
+        }
+    }
+
+    /**
+     * Gets a resource as a stream, relative to the meanings of this
+     * context's implementation.
+     * @return a null if the resource cannot be found or represented 
+     *         as an InputStream.
+     */
+    public java.io.InputStream getResourceAsStream(String res) {
+        return context.getResourceAsStream(canonicalURI(res));
+    }
+
+
+    public URL getResource(String res) throws MalformedURLException {
+        return context.getResource(canonicalURI(res));
+    }
+
+    public Set getResourcePaths(String path) {
+        return context.getResourcePaths(canonicalURI(path));
+    }
+
+    /** 
+     * Gets the actual path of a URI relative to the context of
+     * the compilation.
+     */
+    public String getRealPath(String path) {
+        if (context != null) {
+            return context.getRealPath(path);
+        }
+        return path;
+    }
+
+    /**
+     * Returns the tag-file-name-to-JAR-file map of this compilation unit,
+     * which maps tag file names to the JAR files in which the tag files are
+     * packaged.
+     *
+     * The map is populated when parsing the tag-file elements of the TLDs
+     * of any imported taglibs. 
+     */
+    public Hashtable getTagFileJarUrls() {
+        return this.tagFileJarUrls;
+    }
+
+    /**
+     * Returns the JAR file in which the tag file for which this
+     * JspCompilationContext was created is packaged, or null if this
+     * JspCompilationContext does not correspond to a tag file, or if the
+     * corresponding tag file is not packaged in a JAR.
+     */
+    public URL getTagFileJarUrl() {
+        return this.tagFileJarUrl;
+    }
+
+    /* ==================== Common implementation ==================== */
+
+    /**
+     * Just the class name (does not include package name) of the
+     * generated class. 
+     */
+    public String getServletClassName() {
+
+        if (className != null) {
+            return className;
+        }
+
+        if (isTagFile) {
+            className = tagInfo.getTagClassName();
+            int lastIndex = className.lastIndexOf('.');
+            if (lastIndex != -1) {
+                className = className.substring(lastIndex + 1);
+            }
+        } else {
+            int iSep = jspUri.lastIndexOf('/') + 1;
+            className = JspUtil.makeJavaIdentifier(jspUri.substring(iSep));
+        }
+        return className;
+    }
+
+    public void setServletClassName(String className) {
+        this.className = className;
+    }
+    
+    /**
+     * Path of the JSP URI. Note that this is not a file name. This is
+     * the context rooted URI of the JSP file. 
+     */
+    public String getJspFile() {
+        return jspUri;
+    }
+
+    /**
+     * Are we processing something that has been declared as an
+     * errorpage? 
+     */
+    public boolean isErrorPage() {
+        return isErrPage;
+    }
+
+    public void setErrorPage(boolean isErrPage) {
+        this.isErrPage = isErrPage;
+    }
+
+    public boolean isTagFile() {
+        return isTagFile;
+    }
+
+    public TagInfo getTagInfo() {
+        return tagInfo;
+    }
+
+    public void setTagInfo(TagInfo tagi) {
+        tagInfo = tagi;
+    }
+
+    /**
+     * True if we are compiling a tag file in prototype mode.
+     * ie we only generate codes with class for the tag handler with empty
+     * method bodies.
+     */
+    public boolean isPrototypeMode() {
+        return protoTypeMode;
+    }
+
+    public void setPrototypeMode(boolean pm) {
+        protoTypeMode = pm;
+    }
+
+    /**
+     * Package name for the generated class is make up of the base package
+     * name, which is user settable, and the derived package name.  The
+     * derived package name directly mirrors the file heirachy of the JSP page.
+     */
+    public String getServletPackageName() {
+        if (isTagFile()) {
+            String className = tagInfo.getTagClassName();
+            int lastIndex = className.lastIndexOf('.');
+            String pkgName = "";
+            if (lastIndex != -1) {
+                pkgName = className.substring(0, lastIndex);
+            }
+            return pkgName;
+        } else {
+            String dPackageName = getDerivedPackageName();
+            if (dPackageName.length() == 0) {
+                return basePackageName;
+            }
+            return basePackageName + '.' + getDerivedPackageName();
+        }
+    }
+
+    private String getDerivedPackageName() {
+        if (derivedPackageName == null) {
+            int iSep = jspUri.lastIndexOf('/');
+            derivedPackageName = (iSep > 0) ?
+                    JspUtil.makeJavaPackage(jspUri.substring(1,iSep)) : "";
+        }
+        return derivedPackageName;
+    }
+	    
+    /**
+     * The package name into which the servlet class is generated.
+     */
+    public void setServletPackageName(String servletPackageName) {
+        this.basePackageName = servletPackageName;
+    }
+
+    /**
+     * Full path name of the Java file into which the servlet is being
+     * generated. 
+     */
+    public String getServletJavaFileName() {
+
+        if (servletJavaFileName == null) {
+            servletJavaFileName =
+		getOutputDir() + getServletClassName() + ".java";
+        } else {
+            // Make sure output dir exists
+            makeOutputDir();
+        }
+        return servletJavaFileName;
+    }
+
+    public void setServletJavaFileName(String servletJavaFileName) {
+        this.servletJavaFileName = servletJavaFileName;
+    }
+
+    /**
+     * Get hold of the Options object for this context. 
+     */
+    public Options getOptions() {
+        return options;
+    }
+
+    public ServletContext getServletContext() {
+        return context;
+    }
+
+    public JspRuntimeContext getRuntimeContext() {
+        return rctxt;
+    }
+
+    /**
+     * Path of the Java file relative to the work directory.
+     */
+    public String getJavaPath() {
+
+        if (javaPath != null) {
+            return javaPath;
+        }
+
+        if (isTagFile()) {
+	    String tagName = tagInfo.getTagClassName();
+            javaPath = tagName.replace('.', '/') + ".java";
+        } else {
+            javaPath = getServletPackageName().replace('.', '/') + '/' +
+                       getServletClassName() + ".java";
+	}
+        return javaPath;
+    }
+
+    public String getClassFileName() {
+
+        if (classFileName == null) {
+            classFileName = getOutputDir() + getServletClassName() + ".class";
+        } else {
+            // Make sure output dir exists
+            makeOutputDir();
+        }
+        return classFileName;
+    }
+
+    /**
+     * Get the content type of this JSP.
+     *
+     * Content type includes content type and encoding.
+     */
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    /**
+     * Where is the servlet being generated?
+     */
+    public ServletWriter getWriter() {
+        return writer;
+    }
+
+    public void setWriter(ServletWriter writer) {
+        this.writer = writer;
+    }
+
+    /**
+     * Gets the 'location' of the TLD associated with the given taglib 'uri'.
+     * 
+     * @return An array of two Strings: The first element denotes the real
+     * path to the TLD. If the path to the TLD points to a jar file, then the
+     * second element denotes the name of the TLD entry in the jar file.
+     * Returns null if the given uri is not associated with any tag library
+     * 'exposed' in the web application.
+     */
+    public String[] getTldLocation(String uri) throws JasperException {
+        String[] location = 
+            getOptions().getTldLocationsCache().getLocation(uri);
+        return location;
+    }
+
+    /**
+     * Are we keeping generated code around?
+     */
+    public boolean keepGenerated() {
+        return getOptions().getKeepGenerated();
+    }
+
+    // ==================== Removal ==================== 
+
+    public void incrementRemoved() {
+        if (removed > 1) {
+            jspCompiler.removeGeneratedFiles();
+            if( rctxt != null )
+                rctxt.removeWrapper(jspUri);
+        }
+        removed++;
+    }
+
+    public boolean isRemoved() {
+        if (removed > 1 ) {
+            return true;
+        }
+        return false;
+    }
+
+    // ==================== Compile and reload ====================
+    
+    public void compile() throws JasperException, FileNotFoundException {
+        createCompiler();
+        if (isPackagedTagFile || jspCompiler.isOutDated()) {
+            try {
+                jspLoader = null;
+                jspCompiler.compile();
+                jsw.setReload(true);
+                jsw.setCompilationException(null);
+            } catch (JasperException ex) {
+                // Cache compilation exception
+                jsw.setCompilationException(ex);
+                throw ex;
+            } catch (Exception ex) {
+                ex.printStackTrace();
+                JasperException je = new JasperException(
+                            Localizer.getMessage("jsp.error.unable.compile"),
+                            ex);
+                // Cache compilation exception
+                jsw.setCompilationException(je);
+                throw je;
+            }
+        }
+    }
+
+    // ==================== Manipulating the class ====================
+
+    public Class load() 
+        throws JasperException, FileNotFoundException
+    {
+        try {
+            getJspLoader();
+            
+            String name;
+            if (isTagFile()) {
+                name = tagInfo.getTagClassName();
+            } else {
+                name = getServletPackageName() + "." + getServletClassName();
+            }
+            servletClass = jspLoader.loadClass(name);
+        } catch (ClassNotFoundException cex) {
+            throw new JasperException(Localizer.getMessage("jsp.error.unable.load"),
+                                      cex);
+        } catch (Exception ex) {
+            throw new JasperException(Localizer.getMessage("jsp.error.unable.compile"),
+                                      ex);
+        }
+        removed = 0;
+        return servletClass;
+    }
+
+    // ==================== Private methods ==================== 
+
+    static Object outputDirLock = new Object();
+
+    private void makeOutputDir() {
+        synchronized(outputDirLock) {
+            File outDirFile = new File(outputDir);
+            outDirFile.mkdirs();
+        }
+    }
+
+    private void createOutputDir() {
+        String path = null;
+        if (isTagFile()) {
+	    String tagName = tagInfo.getTagClassName();
+            path = tagName.replace('.', '/');
+	    path = path.substring(0, path.lastIndexOf('/'));
+        } else {
+            path = getServletPackageName().replace('.', '/');
+	}
+
+        try {
+            // Append servlet or tag handler path to scratch dir
+            baseUrl = options.getScratchDir().toURL();
+            String outUrlString = baseUrl.toString() + '/' + path;
+            URL outUrl = new URL(outUrlString);
+            outputDir = outUrl.getFile() + File.separator;
+            makeOutputDir();
+        } catch (Exception e) {
+            throw new IllegalStateException("No output directory: " +
+                                            e.getMessage());
+        }
+    }
+    
+    private static final boolean isPathSeparator(char c) {
+       return (c == '/' || c == '\\');
+    }
+
+    private static final String canonicalURI(String s) {
+       if (s == null) return null;
+       StringBuffer result = new StringBuffer();
+       final int len = s.length();
+       int pos = 0;
+       while (pos < len) {
+           char c = s.charAt(pos);
+           if ( isPathSeparator(c) ) {
+               /*
+                * multiple path separators.
+                * 'foo///bar' -> 'foo/bar'
+                */
+               while (pos+1 < len && isPathSeparator(s.charAt(pos+1))) {
+                   ++pos;
+               }
+
+               if (pos+1 < len && s.charAt(pos+1) == '.') {
+                   /*
+                    * a single dot at the end of the path - we are done.
+                    */
+                   if (pos+2 >= len) break;
+
+                   switch (s.charAt(pos+2)) {
+                       /*
+                        * self directory in path
+                        * foo/./bar -> foo/bar
+                        */
+                   case '/':
+                   case '\\':
+                       pos += 2;
+                       continue;
+
+                       /*
+                        * two dots in a path: go back one hierarchy.
+                        * foo/bar/../baz -> foo/baz
+                        */
+                   case '.':
+                       // only if we have exactly _two_ dots.
+                       if (pos+3 < len && isPathSeparator(s.charAt(pos+3))) {
+                           pos += 3;
+                           int separatorPos = result.length()-1;
+                           while (separatorPos >= 0 && 
+                                  ! isPathSeparator(result
+                                                    .charAt(separatorPos))) {
+                               --separatorPos;
+                           }
+                           if (separatorPos >= 0)
+                               result.setLength(separatorPos);
+                           continue;
+                       }
+                   }
+               }
+           }
+           result.append(c);
+           ++pos;
+       }
+       return result.toString();
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/Options.java b/jasper/jasper2/src/share/org/apache/jasper/Options.java
new file mode 100644
index 0000000..24ec0cd
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/Options.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper;
+
+import java.io.File;
+
+import org.apache.jasper.compiler.JspConfig;
+import org.apache.jasper.compiler.TagPluginManager;
+import org.apache.jasper.compiler.TldLocationsCache;
+
+/**
+ * A class to hold all init parameters specific to the JSP engine. 
+ *
+ * @author Anil K. Vijendran
+ * @author Hans Bergsten
+ * @author Pierre Delisle
+ */
+public interface Options {
+
+    /**
+     * Returns true if Jasper issues a compilation error instead of a runtime
+     * Instantiation error if the class attribute specified in useBean action
+     * is invalid.
+     */
+    public boolean getErrorOnUseBeanInvalidClassAttribute();
+
+    /**
+     * Are we keeping generated code around?
+     */
+    public boolean getKeepGenerated();
+
+    /**
+     * Returns true if tag handler pooling is enabled, false otherwise.
+     */
+    public boolean isPoolingEnabled();
+
+    /**
+     * Are we supporting HTML mapped servlets?
+     */
+    public boolean getMappedFile();
+
+    /**
+     * Should errors be sent to client or thrown into stderr?
+     */
+    public boolean getSendErrorToClient();
+ 
+    /**
+     * Should we include debug information in compiled class?
+     */
+    public boolean getClassDebugInfo();
+
+    /**
+     * Background compile thread check interval in seconds
+     */
+    public int getCheckInterval();
+
+    /**
+     * Is Jasper being used in development mode?
+     */
+    public boolean getDevelopment();
+
+    /**
+     * Is the generation of SMAP info for JSR45 debugging suppressed?
+     */
+    public boolean isSmapSuppressed();
+
+    /**
+     * Indicates whether SMAP info for JSR45 debugging should be dumped to a
+     * file.
+     * Ignored is suppressSmap() is true
+     */
+    public boolean isSmapDumped();
+
+    /**
+     * Should white spaces between directives or actions be trimmed?
+     */
+    public boolean getTrimSpaces();
+
+    /**
+     * Class ID for use in the plugin tag when the browser is IE. 
+     */
+    public String getIeClassId();
+
+    /**
+     * What is my scratch dir?
+     */
+    public File getScratchDir();
+
+    /**
+     * What classpath should I use while compiling the servlets
+     * generated from JSP files?
+     */
+    public String getClassPath();
+
+    /**
+     * Compiler to use.
+     */
+    public String getCompiler();
+
+    /**
+     * The compiler target VM, e.g. 1.1, 1.2, 1.3, 1.4, or 1.5.
+     */
+    public String getCompilerTargetVM();
+
+    /**
+     * Compiler source VM, e.g. 1.3, 1.4, or 1.5.
+     */
+    public String getCompilerSourceVM();   
+
+    /**
+     * The cache for the location of the TLD's
+     * for the various tag libraries 'exposed'
+     * by the web application.
+     * A tag library is 'exposed' either explicitely in 
+     * web.xml or implicitely via the uri tag in the TLD 
+     * of a taglib deployed in a jar file (WEB-INF/lib).
+     *
+     * @return the instance of the TldLocationsCache
+     * for the web-application.
+     */
+    public TldLocationsCache getTldLocationsCache();
+
+    /**
+     * Java platform encoding to generate the JSP
+     * page servlet.
+     */
+    public String getJavaEncoding();
+
+    /**
+     * boolean flag to tell Ant whether to fork JSP page compilations.
+     */
+    public boolean getFork();
+
+    /**
+     * Obtain JSP configuration informantion specified in web.xml.  
+     */
+    public JspConfig getJspConfig();
+
+    /**
+     * Is generation of X-Powered-By response header enabled/disabled?
+     */
+    public boolean isXpoweredBy();
+
+    /**
+     * Obtain a Tag Plugin Manager
+     */
+    public TagPluginManager getTagPluginManager();
+
+    /**
+     * Are Text strings to be generated as char arrays?
+     */
+    public boolean genStringAsCharArray();
+    
+    /**
+     * Modification test interval.
+     */
+    public int getModificationTestInterval();
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/AntCompiler.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/AntCompiler.java
new file mode 100644
index 0000000..abf7768
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/AntCompiler.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+import java.util.StringTokenizer;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.util.SystemLogHandler;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Javac;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+
+/**
+ * Main JSP compiler class. This class uses Ant for compiling.
+ *
+ * @author Anil K. Vijendran
+ * @author Mandar Raje
+ * @author Pierre Delisle
+ * @author Kin-man Chung
+ * @author Remy Maucherat
+ * @author Mark Roth
+ */
+public class AntCompiler extends Compiler {
+
+    
+    // ----------------------------------------------------- Instance Variables
+
+    protected Project project=null;
+    protected JasperAntLogger logger;
+
+    // ------------------------------------------------------------ Constructor
+
+    // Lazy eval - if we don't need to compile we probably don't need the project
+    protected Project getProject() {
+        
+        if( project!=null ) return project;
+        
+        // Initializing project
+        project = new Project();
+        logger = new JasperAntLogger();
+        logger.setOutputPrintStream(System.out);
+        logger.setErrorPrintStream(System.err);
+        logger.setMessageOutputLevel(Project.MSG_INFO);
+        project.addBuildListener( logger);
+        if (System.getProperty("catalina.home") != null) {
+            project.setBasedir( System.getProperty("catalina.home"));
+        }
+        
+        if( options.getCompiler() != null ) {
+            if( log.isDebugEnabled() )
+                log.debug("Compiler " + options.getCompiler() );
+            project.setProperty("build.compiler", options.getCompiler() );
+        }
+        project.init();
+        return project;
+    }
+    
+    class JasperAntLogger extends DefaultLogger {
+        
+        protected StringBuffer reportBuf = new StringBuffer();
+        
+        protected void printMessage(final String message,
+                final PrintStream stream,
+                final int priority) {
+        }
+        
+        protected void log(String message) {
+            reportBuf.append(message);
+            reportBuf.append(System.getProperty("line.separator"));
+        }
+        
+        protected String getReport() {
+            String report = reportBuf.toString();
+            reportBuf.setLength(0);
+            return report;
+        }
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /** 
+     * Compile the servlet from .java file to .class file
+     */
+    protected void generateClass(String[] smap)
+        throws FileNotFoundException, JasperException, Exception {
+        
+        long t1 = 0;
+        if (log.isDebugEnabled()) {
+            t1 = System.currentTimeMillis();
+        }
+
+        String javaEncoding = ctxt.getOptions().getJavaEncoding();
+        String javaFileName = ctxt.getServletJavaFileName();
+        String classpath = ctxt.getClassPath(); 
+        
+        String sep = System.getProperty("path.separator");
+        
+        StringBuffer errorReport = new StringBuffer();
+        
+        StringBuffer info=new StringBuffer();
+        info.append("Compile: javaFileName=" + javaFileName + "\n" );
+        info.append("    classpath=" + classpath + "\n" );
+        
+        // Start capturing the System.err output for this thread
+        SystemLogHandler.setThread();
+        
+        // Initializing javac task
+        getProject();
+        Javac javac = (Javac) project.createTask("javac");
+        
+        // Initializing classpath
+        Path path = new Path(project);
+        path.setPath(System.getProperty("java.class.path"));
+        info.append("    cp=" + System.getProperty("java.class.path") + "\n");
+        StringTokenizer tokenizer = new StringTokenizer(classpath, sep);
+        while (tokenizer.hasMoreElements()) {
+            String pathElement = tokenizer.nextToken();
+            File repository = new File(pathElement);
+            path.setLocation(repository);
+            info.append("    cp=" + repository + "\n");
+        }
+        
+        if( log.isDebugEnabled() )
+            log.debug( "Using classpath: " + System.getProperty("java.class.path") + sep
+                    + classpath);
+        
+        // Initializing sourcepath
+        Path srcPath = new Path(project);
+        srcPath.setLocation(options.getScratchDir());
+        
+        info.append("    work dir=" + options.getScratchDir() + "\n");
+        
+        // Initialize and set java extensions
+        String exts = System.getProperty("java.ext.dirs");
+        if (exts != null) {
+            Path extdirs = new Path(project);
+            extdirs.setPath(exts);
+            javac.setExtdirs(extdirs);
+            info.append("    extension dir=" + exts + "\n");
+        }
+
+        // Add endorsed directories if any are specified and we're forking
+        // See Bugzilla 31257
+        if(ctxt.getOptions().getFork()) {
+            String endorsed = System.getProperty("java.endorsed.dirs");
+            if(endorsed != null) {
+                Javac.ImplementationSpecificArgument endorsedArg = 
+                    javac.createCompilerArg();
+                endorsedArg.setLine("-J-Djava.endorsed.dirs="+endorsed);
+                info.append("    endorsed dir=" + endorsed + "\n");
+            } else {
+                info.append("    no endorsed dirs specified\n");
+            }
+        }
+        
+        // Configure the compiler object
+        javac.setEncoding(javaEncoding);
+        javac.setClasspath(path);
+        javac.setDebug(ctxt.getOptions().getClassDebugInfo());
+        javac.setSrcdir(srcPath);
+        javac.setTempdir(options.getScratchDir());
+        javac.setOptimize(! ctxt.getOptions().getClassDebugInfo() );
+        javac.setFork(ctxt.getOptions().getFork());
+        info.append("    srcDir=" + srcPath + "\n" );
+        
+        // Set the Java compiler to use
+        if (options.getCompiler() != null) {
+            javac.setCompiler(options.getCompiler());
+            info.append("    compiler=" + options.getCompiler() + "\n");
+        }
+
+        if (options.getCompilerTargetVM() != null) {
+            javac.setTarget(options.getCompilerTargetVM());
+            info.append("   compilerTargetVM=" + options.getCompilerTargetVM() + "\n");
+        }
+
+        if (options.getCompilerSourceVM() != null) {
+            javac.setSource(options.getCompilerSourceVM());
+            info.append("   compilerSourceVM=" + options.getCompilerSourceVM() + "\n");
+        }
+        
+        // Build includes path
+        PatternSet.NameEntry includes = javac.createInclude();
+        
+        includes.setName(ctxt.getJavaPath());
+        info.append("    include="+ ctxt.getJavaPath() + "\n" );
+        
+        BuildException be = null;
+        
+        try {
+            if (ctxt.getOptions().getFork()) {
+                javac.execute();
+            } else {
+                synchronized(javacLock) {
+                    javac.execute();
+                }
+            }
+        } catch (BuildException e) {
+            be = e;
+            log.error( "Javac exception ", e);
+            log.error( "Env: " + info.toString());
+        }
+        
+        errorReport.append(logger.getReport());
+        
+        // Stop capturing the System.err output for this thread
+        String errorCapture = SystemLogHandler.unsetThread();
+        if (errorCapture != null) {
+            errorReport.append(System.getProperty("line.separator"));
+            errorReport.append(errorCapture);
+        }
+        
+        if (!ctxt.keepGenerated()) {
+            File javaFile = new File(javaFileName);
+            javaFile.delete();
+        }
+        
+        if (be != null) {
+            String errorReportString = errorReport.toString();
+            log.error("Error compiling file: " + javaFileName + " "
+                    + errorReportString);
+            JavacErrorDetail[] javacErrors = ErrorDispatcher.parseJavacErrors(
+                    errorReportString, javaFileName, pageNodes);
+            if (javacErrors != null) {
+                errDispatcher.javacError(javacErrors);
+            } else {
+                errDispatcher.javacError(errorReportString, be);
+            }
+        }
+        
+        if( log.isDebugEnabled() ) {
+            long t2=System.currentTimeMillis();
+            log.debug("Compiled " + ctxt.getServletJavaFileName() + " "
+                      + (t2-t1) + "ms");
+        }
+        
+        logger = null;
+        project = null;
+        
+        if (ctxt.isPrototypeMode()) {
+            return;
+        }
+        
+        // JSR45 Support
+        if (! options.isSmapSuppressed()) {
+            SmapUtil.installSmap(smap);
+        }
+    }
+
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/BeanRepository.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/BeanRepository.java
new file mode 100644
index 0000000..f31cadb
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/BeanRepository.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.compiler;
+
+
+import java.util.Vector;
+import java.util.Hashtable;
+
+import org.apache.jasper.JasperException;
+
+/**
+ * Repository of {page, request, session, application}-scoped beans 
+ *
+ * @author Mandar Raje
+ */
+class BeanRepository {
+
+    private Vector sessionBeans;
+    private Vector pageBeans;
+    private Vector appBeans;
+    private Vector requestBeans;
+    private Hashtable beanTypes;
+    private ClassLoader loader;
+    private ErrorDispatcher errDispatcher;
+
+    /*
+     * Constructor.
+     */    
+    public BeanRepository(ClassLoader loader, ErrorDispatcher err) {
+
+        this.loader = loader;
+	this.errDispatcher = err;
+
+	sessionBeans = new Vector(11);
+	pageBeans = new Vector(11);
+	appBeans = new Vector(11);
+	requestBeans = new Vector(11);
+	beanTypes = new Hashtable();
+    }
+        
+    public void addBean(Node.UseBean n, String s, String type, String scope)
+	    throws JasperException {
+
+	if (scope == null || scope.equals("page")) {
+	    pageBeans.addElement(s);	
+	} else if (scope.equals("request")) {
+	    requestBeans.addElement(s);
+	} else if (scope.equals("session")) {
+	    sessionBeans.addElement(s);
+	} else if (scope.equals("application")) {
+	    appBeans.addElement(s);
+	} else {
+	    errDispatcher.jspError(n, "jsp.error.usebean.badScope");
+	}
+	
+	putBeanType(s, type);
+    }
+            
+    public Class getBeanType(String bean) throws JasperException {
+	Class clazz = null;
+	try {
+	    clazz = loader.loadClass ((String)beanTypes.get(bean));
+	} catch (ClassNotFoundException ex) {
+	    throw new JasperException (ex);
+	}
+	return clazz;
+    }
+      
+    public boolean checkVariable (String bean) {
+	// XXX Not sure if this is the correct way.
+	// After pageContext is finalised this will change.
+	return (checkPageBean(bean) || checkSessionBean(bean) ||
+		checkRequestBean(bean) || checkApplicationBean(bean));
+    }
+
+
+    private void putBeanType(String bean, String type) {
+	beanTypes.put (bean, type);
+    }
+
+    private boolean checkPageBean (String s) {
+	return pageBeans.contains (s);
+    }
+
+    private boolean checkRequestBean (String s) {
+	return requestBeans.contains (s);
+    }
+
+    private boolean checkSessionBean (String s) {
+	return sessionBeans.contains (s);
+    }
+
+    private boolean checkApplicationBean (String s) {
+	return appBeans.contains (s);
+    }
+
+}
+
+
+
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/Collector.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/Collector.java
new file mode 100644
index 0000000..3f720c8
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/Collector.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import org.apache.jasper.JasperException;
+
+/**
+ * Collect info about the page and nodes, and make them availabe through
+ * the PageInfo object.
+ *
+ * @author Kin-man Chung
+ * @author Mark Roth
+ */
+
+class Collector {
+
+    /**
+     * A visitor for collecting information on the page and the body of
+     * the custom tags.
+     */
+    static class CollectVisitor extends Node.Visitor {
+
+        private boolean scriptingElementSeen = false;
+        private boolean usebeanSeen = false;
+        private boolean includeActionSeen = false;
+        private boolean paramActionSeen = false;
+        private boolean setPropertySeen = false;
+        private boolean hasScriptingVars = false;
+
+        public void visit(Node.ParamAction n) throws JasperException {
+            if (n.getValue().isExpression()) {
+                scriptingElementSeen = true;
+            }
+            paramActionSeen = true;
+        }
+
+        public void visit(Node.IncludeAction n) throws JasperException {
+            if (n.getPage().isExpression()) {
+                scriptingElementSeen = true;
+            }
+            includeActionSeen = true;
+            visitBody(n);
+        }
+
+        public void visit(Node.ForwardAction n) throws JasperException {
+            if (n.getPage().isExpression()) {
+                scriptingElementSeen = true;
+            }
+            visitBody(n);
+        }
+
+        public void visit(Node.SetProperty n) throws JasperException {
+            if (n.getValue() != null && n.getValue().isExpression()) {
+                scriptingElementSeen = true;
+            }
+            setPropertySeen = true;
+        }
+
+        public void visit(Node.UseBean n) throws JasperException {
+            if (n.getBeanName() != null && n.getBeanName().isExpression()) {
+                scriptingElementSeen = true;
+            }
+            usebeanSeen = true;
+                visitBody(n);
+        }
+
+        public void visit(Node.PlugIn n) throws JasperException {
+            if (n.getHeight() != null && n.getHeight().isExpression()) {
+                scriptingElementSeen = true;
+            }
+            if (n.getWidth() != null && n.getWidth().isExpression()) {
+                scriptingElementSeen = true;
+            }
+            visitBody(n);
+        }
+
+        public void visit(Node.CustomTag n) throws JasperException {
+            // Check to see what kinds of element we see as child elements
+            checkSeen( n.getChildInfo(), n );
+        }
+
+        /**
+         * Check all child nodes for various elements and update the given
+         * ChildInfo object accordingly.  Visits body in the process.
+         */
+        private void checkSeen( Node.ChildInfo ci, Node n )
+            throws JasperException
+        {
+            // save values collected so far
+            boolean scriptingElementSeenSave = scriptingElementSeen;
+            scriptingElementSeen = false;
+            boolean usebeanSeenSave = usebeanSeen;
+            usebeanSeen = false;
+            boolean includeActionSeenSave = includeActionSeen;
+            includeActionSeen = false;
+            boolean paramActionSeenSave = paramActionSeen;
+            paramActionSeen = false;
+            boolean setPropertySeenSave = setPropertySeen;
+            setPropertySeen = false;
+            boolean hasScriptingVarsSave = hasScriptingVars;
+            hasScriptingVars = false;
+
+            // Scan attribute list for expressions
+            if( n instanceof Node.CustomTag ) {
+                Node.CustomTag ct = (Node.CustomTag)n;
+                Node.JspAttribute[] attrs = ct.getJspAttributes();
+                for (int i = 0; attrs != null && i < attrs.length; i++) {
+                    if (attrs[i].isExpression()) {
+                        scriptingElementSeen = true;
+                        break;
+                    }
+                }
+            }
+
+            visitBody(n);
+
+            if( (n instanceof Node.CustomTag) && !hasScriptingVars) {
+                Node.CustomTag ct = (Node.CustomTag)n;
+                hasScriptingVars = ct.getVariableInfos().length > 0 ||
+                    ct.getTagVariableInfos().length > 0;
+            }
+
+            // Record if the tag element and its body contains any scriptlet.
+            ci.setScriptless(! scriptingElementSeen);
+            ci.setHasUseBean(usebeanSeen);
+            ci.setHasIncludeAction(includeActionSeen);
+            ci.setHasParamAction(paramActionSeen);
+            ci.setHasSetProperty(setPropertySeen);
+            ci.setHasScriptingVars(hasScriptingVars);
+
+            // Propagate value of scriptingElementSeen up.
+            scriptingElementSeen = scriptingElementSeen || scriptingElementSeenSave;
+            usebeanSeen = usebeanSeen || usebeanSeenSave;
+            setPropertySeen = setPropertySeen || setPropertySeenSave;
+            includeActionSeen = includeActionSeen || includeActionSeenSave;
+            paramActionSeen = paramActionSeen || paramActionSeenSave;
+            hasScriptingVars = hasScriptingVars || hasScriptingVarsSave;
+        }
+
+        public void visit(Node.JspElement n) throws JasperException {
+            if (n.getNameAttribute().isExpression())
+                scriptingElementSeen = true;
+
+            Node.JspAttribute[] attrs = n.getJspAttributes();
+            for (int i = 0; i < attrs.length; i++) {
+                if (attrs[i].isExpression()) {
+                    scriptingElementSeen = true;
+                    break;
+                }
+            }
+            visitBody(n);
+        }
+
+        public void visit(Node.JspBody n) throws JasperException {
+            checkSeen( n.getChildInfo(), n );
+        }
+
+        public void visit(Node.NamedAttribute n) throws JasperException {
+            checkSeen( n.getChildInfo(), n );
+        }
+
+        public void visit(Node.Declaration n) throws JasperException {
+            scriptingElementSeen = true;
+        }
+
+        public void visit(Node.Expression n) throws JasperException {
+            scriptingElementSeen = true;
+        }
+
+        public void visit(Node.Scriptlet n) throws JasperException {
+            scriptingElementSeen = true;
+        }
+
+        public void updatePageInfo(PageInfo pageInfo) {
+            pageInfo.setScriptless(! scriptingElementSeen);
+        }
+    }
+
+
+    public static void collect(Compiler compiler, Node.Nodes page)
+        throws JasperException {
+
+    CollectVisitor collectVisitor = new CollectVisitor();
+        page.visit(collectVisitor);
+        collectVisitor.updatePageInfo(compiler.getPageInfo());
+
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/Compiler.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/Compiler.java
new file mode 100644
index 0000000..cdcd4dd
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/Compiler.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.Options;
+import org.apache.jasper.servlet.JspServletWrapper;
+
+/**
+ * Main JSP compiler class. This class uses Ant for compiling.
+ *
+ * @author Anil K. Vijendran
+ * @author Mandar Raje
+ * @author Pierre Delisle
+ * @author Kin-man Chung
+ * @author Remy Maucherat
+ * @author Mark Roth
+ */
+public abstract class Compiler {
+    protected org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( Compiler.class );
+
+    // ----------------------------------------------------------------- Static
+
+
+    // Some javac are not thread safe; use a lock to serialize compilation, 
+    static Object javacLock = new Object();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected JspCompilationContext ctxt;
+
+    protected ErrorDispatcher errDispatcher;
+    protected PageInfo pageInfo;
+    protected JspServletWrapper jsw;
+    protected TagFileProcessor tfp;
+
+    protected Options options;
+
+    protected Node.Nodes pageNodes;
+    // ------------------------------------------------------------ Constructor
+
+    public void init(JspCompilationContext ctxt, JspServletWrapper jsw) {
+        this.jsw = jsw;
+        this.ctxt = ctxt;
+        this.options = ctxt.getOptions();
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /** 
+     * Compile the jsp file into equivalent servlet in .java file
+     * @return a smap for the current JSP page, if one is generated,
+     *         null otherwise
+     */
+    protected String[] generateJava() throws Exception {
+
+        String[] smapStr = null;
+
+        long t1, t2, t3, t4;
+
+        t1 = t2 = t3 = t4 = 0;
+      
+        if (log.isDebugEnabled()) {
+            t1 = System.currentTimeMillis();
+        }
+
+        // Setup page info area
+        pageInfo = new PageInfo(new BeanRepository(ctxt.getClassLoader(),
+                                                   errDispatcher),
+                                ctxt.getJspFile());
+
+        JspConfig jspConfig = options.getJspConfig();
+        JspConfig.JspProperty jspProperty =
+            jspConfig.findJspProperty(ctxt.getJspFile());
+
+        /*
+         * If the current uri is matched by a pattern specified in
+         * a jsp-property-group in web.xml, initialize pageInfo with
+         * those properties.
+         */
+        pageInfo.setELIgnored(JspUtil.booleanValue(
+                                            jspProperty.isELIgnored()));
+        pageInfo.setScriptingInvalid(JspUtil.booleanValue(
+                                            jspProperty.isScriptingInvalid()));
+        if (jspProperty.getIncludePrelude() != null) {
+            pageInfo.setIncludePrelude(jspProperty.getIncludePrelude());
+        }
+        if (jspProperty.getIncludeCoda() != null) {
+	    pageInfo.setIncludeCoda(jspProperty.getIncludeCoda());
+        }
+
+        String javaFileName = ctxt.getServletJavaFileName();
+        ServletWriter writer = null;
+
+        try {
+            // Setup the ServletWriter
+            String javaEncoding = ctxt.getOptions().getJavaEncoding();
+            OutputStreamWriter osw = null; 
+
+            try {
+                osw = new OutputStreamWriter(
+                            new FileOutputStream(javaFileName), javaEncoding);
+            } catch (UnsupportedEncodingException ex) {
+                errDispatcher.jspError("jsp.error.needAlternateJavaEncoding",
+                                       javaEncoding);
+            }
+
+            writer = new ServletWriter(new PrintWriter(osw));
+            ctxt.setWriter(writer);
+
+            // Reset the temporary variable counter for the generator.
+            JspUtil.resetTemporaryVariableName();
+
+	    // Parse the file
+	    ParserController parserCtl = new ParserController(ctxt, this);
+	    pageNodes = parserCtl.parse(ctxt.getJspFile());
+
+	    if (ctxt.isPrototypeMode()) {
+                // generate prototype .java file for the tag file
+                Generator.generate(writer, this, pageNodes);
+                writer.close();
+                writer = null;
+                return null;
+            }
+
+            // Validate and process attributes
+            Validator.validate(this, pageNodes);
+
+            if (log.isDebugEnabled()) {
+                t2 = System.currentTimeMillis();
+            }
+
+            // Collect page info
+            Collector.collect(this, pageNodes);
+
+            // Compile (if necessary) and load the tag files referenced in
+            // this compilation unit.
+            tfp = new TagFileProcessor();
+            tfp.loadTagFiles(this, pageNodes);
+
+            if (log.isDebugEnabled()) {
+                t3 = System.currentTimeMillis();
+            }
+        
+            // Determine which custom tag needs to declare which scripting vars
+            ScriptingVariabler.set(pageNodes, errDispatcher);
+
+            // Optimizations by Tag Plugins
+            TagPluginManager tagPluginManager = options.getTagPluginManager();
+            tagPluginManager.apply(pageNodes, errDispatcher, pageInfo);
+
+            // Optimization: concatenate contiguous template texts.
+            TextOptimizer.concatenate(this, pageNodes);
+
+            // Generate static function mapper codes.
+            ELFunctionMapper.map(this, pageNodes);
+
+            // generate servlet .java file
+            Generator.generate(writer, this, pageNodes);
+            writer.close();
+            writer = null;
+
+            // The writer is only used during the compile, dereference
+            // it in the JspCompilationContext when done to allow it
+            // to be GC'd and save memory.
+            ctxt.setWriter(null);
+
+            if (log.isDebugEnabled()) {
+                t4 = System.currentTimeMillis();
+                log.debug("Generated "+ javaFileName + " total="
+                          + (t4-t1) + " generate=" + (t4-t3)
+                          + " validate=" + (t2-t1));
+            }
+
+        } catch (Exception e) {
+            if (writer != null) {
+                try {
+                    writer.close();
+                    writer = null;
+                } catch (Exception e1) {
+                    // do nothing
+                }
+            }
+            // Remove the generated .java file
+            new File(javaFileName).delete();
+            throw e;
+        } finally {
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (Exception e2) {
+                    // do nothing
+                }
+            }
+        }
+        
+        // JSR45 Support
+        if (! options.isSmapSuppressed()) {
+            smapStr = SmapUtil.generateSmap(ctxt, pageNodes);
+        }
+
+        // If any proto type .java and .class files was generated,
+        // the prototype .java may have been replaced by the current
+        // compilation (if the tag file is self referencing), but the
+        // .class file need to be removed, to make sure that javac would
+        // generate .class again from the new .java file just generated.
+        tfp.removeProtoTypeFiles(ctxt.getClassFileName());
+
+        return smapStr;
+    }
+
+    /** 
+     * Compile the servlet from .java file to .class file
+     */
+    protected abstract void generateClass(String[] smap)
+        throws FileNotFoundException, JasperException, Exception;
+    
+    
+    /** 
+     * Compile the jsp file from the current engine context
+     */
+    public void compile()
+        throws FileNotFoundException, JasperException, Exception
+    {
+        compile(true);
+    }
+
+    /**
+     * Compile the jsp file from the current engine context.  As an side-
+     * effect, tag files that are referenced by this page are also compiled.
+     * @param compileClass If true, generate both .java and .class file
+     *                     If false, generate only .java file
+     */
+    public void compile(boolean compileClass)
+        throws FileNotFoundException, JasperException, Exception
+    {
+        compile(compileClass, false);
+    }
+
+    /**
+     * Compile the jsp file from the current engine context.  As an side-
+     * effect, tag files that are referenced by this page are also compiled.
+     *
+     * @param compileClass If true, generate both .java and .class file
+     *                     If false, generate only .java file
+     * @param jspcMode true if invoked from JspC, false otherwise
+     */
+    public void compile(boolean compileClass, boolean jspcMode)
+        throws FileNotFoundException, JasperException, Exception
+    {
+        if (errDispatcher == null) {
+            this.errDispatcher = new ErrorDispatcher(jspcMode);
+        }
+
+        try {
+            String[] smap = generateJava();
+            if (compileClass) {
+                generateClass(smap);
+            }
+        } finally {
+            if (tfp != null) {
+                tfp.removeProtoTypeFiles(null);
+            }
+            // Make sure these object which are only used during the
+            // generation and compilation of the JSP page get
+            // dereferenced so that they can be GC'd and reduce the
+            // memory footprint.
+            tfp = null;
+            errDispatcher = null;
+            pageInfo = null;
+            pageNodes = null;
+            if (ctxt.getWriter() != null) {
+                ctxt.getWriter().close();
+                ctxt.setWriter(null);
+            }
+        }
+    }
+
+    /**
+     * This is a protected method intended to be overridden by 
+     * subclasses of Compiler. This is used by the compile method
+     * to do all the compilation. 
+     */
+    public boolean isOutDated() {
+        return isOutDated( true );
+    }
+
+    /**
+     * Determine if a compilation is necessary by checking the time stamp
+     * of the JSP page with that of the corresponding .class or .java file.
+     * If the page has dependencies, the check is also extended to its
+     * dependeants, and so on.
+     * This method can by overidden by a subclasses of Compiler.
+     * @param checkClass If true, check against .class file,
+     *                   if false, check against .java file.
+     */
+    public boolean isOutDated(boolean checkClass) {
+
+        String jsp = ctxt.getJspFile();
+
+        if (jsw != null
+                && (ctxt.getOptions().getModificationTestInterval() > 0)) {
+ 
+            if (jsw.getLastModificationTest()
+                    + (ctxt.getOptions().getModificationTestInterval() * 1000) 
+                    > System.currentTimeMillis()) {
+                return false;
+            } else {
+                jsw.setLastModificationTest(System.currentTimeMillis());
+            }
+        }
+        
+        long jspRealLastModified = 0;
+        try {
+            URL jspUrl = ctxt.getResource(jsp);
+            if (jspUrl == null) {
+                ctxt.incrementRemoved();
+                return false;
+            }
+            URLConnection uc = jspUrl.openConnection();
+            jspRealLastModified = uc.getLastModified();
+            uc.getInputStream().close();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return true;
+        }
+
+        long targetLastModified = 0;
+        File targetFile;
+        
+        if( checkClass ) {
+            targetFile = new File(ctxt.getClassFileName());
+        } else {
+            targetFile = new File(ctxt.getServletJavaFileName());
+        }
+        
+        if (!targetFile.exists()) {
+            return true;
+        }
+
+        targetLastModified = targetFile.lastModified();
+        if (checkClass && jsw != null) {
+            jsw.setServletClassLastModifiedTime(targetLastModified);
+        }   
+        if (targetLastModified < jspRealLastModified) {
+            if( log.isDebugEnabled() ) {
+                log.debug("Compiler: outdated: " + targetFile + " " +
+                    targetLastModified );
+            }
+            return true;
+        }
+
+        // determine if source dependent files (e.g. includes using include
+        // directives) have been changed.
+        if( jsw==null ) {
+            return false;
+        }
+        
+        List depends = jsw.getDependants();
+        if (depends == null) {
+            return false;
+        }
+
+        Iterator it = depends.iterator();
+        while (it.hasNext()) {
+            String include = (String)it.next();
+            try {
+                URL includeUrl = ctxt.getResource(include);
+                if (includeUrl == null) {
+                    return true;
+                }
+
+                URLConnection includeUconn = includeUrl.openConnection();
+                long includeLastModified = includeUconn.getLastModified();
+                includeUconn.getInputStream().close();
+
+                if (includeLastModified > targetLastModified) {
+                    return true;
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+                return true;
+            }
+        }
+
+        return false;
+
+    }
+
+    
+    /**
+     * Gets the error dispatcher.
+     */
+    public ErrorDispatcher getErrorDispatcher() {
+	return errDispatcher;
+    }
+
+
+    /**
+     * Gets the info about the page under compilation
+     */
+    public PageInfo getPageInfo() {
+	return pageInfo;
+    }
+
+
+    public JspCompilationContext getCompilationContext() {
+	return ctxt;
+    }
+
+
+    /**
+     * Remove generated files
+     */
+    public void removeGeneratedFiles() {
+        try {
+            String classFileName = ctxt.getClassFileName();
+            if (classFileName != null) {
+                File classFile = new File(classFileName);
+                if( log.isDebugEnabled() )
+                    log.debug( "Deleting " + classFile );
+                classFile.delete();
+            }
+        } catch (Exception e) {
+            // Remove as much as possible, ignore possible exceptions
+        }
+        try {
+            String javaFileName = ctxt.getServletJavaFileName();
+            if (javaFileName != null) {
+                File javaFile = new File(javaFileName);
+                if( log.isDebugEnabled() )
+                    log.debug( "Deleting " + javaFile );
+                javaFile.delete();
+            }
+        } catch (Exception e) {
+            // Remove as much as possible, ignore possible exceptions
+        }
+    }
+
+    public void removeGeneratedClassFiles() {
+        try {
+            String classFileName = ctxt.getClassFileName();
+            if (classFileName != null) {
+                File classFile = new File(classFileName);
+                if( log.isDebugEnabled() )
+                    log.debug( "Deleting " + classFile );
+                classFile.delete();
+            }
+        } catch (Exception e) {
+            // Remove as much as possible, ignore possible exceptions
+        }
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/DefaultErrorHandler.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/DefaultErrorHandler.java
new file mode 100644
index 0000000..c1b47b5
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/DefaultErrorHandler.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import org.apache.jasper.JasperException;
+
+/**
+ * Default implementation of ErrorHandler interface.
+ *
+ * @author Jan Luehe
+ */
+class DefaultErrorHandler implements ErrorHandler {
+    
+    /*
+     * Processes the given JSP parse error.
+     *
+     * @param fname Name of the JSP file in which the parse error occurred
+     * @param line Parse error line number
+     * @param column Parse error column number
+     * @param errMsg Parse error message
+     * @param exception Parse exception
+     */
+    public void jspError(String fname, int line, int column, String errMsg,
+            Exception ex) throws JasperException {
+        throw new JasperException(fname + "(" + line + "," + column + ")"
+                + " " + errMsg, ex);
+    }
+    
+    /*
+     * Processes the given JSP parse error.
+     *
+     * @param errMsg Parse error message
+     * @param exception Parse exception
+     */
+    public void jspError(String errMsg, Exception ex) throws JasperException {
+        throw new JasperException(errMsg, ex);
+    }
+    
+    /*
+     * Processes the given javac compilation errors.
+     *
+     * @param details Array of JavacErrorDetail instances corresponding to the
+     * compilation errors
+     */
+    public void javacError(JavacErrorDetail[] details) throws JasperException {
+        
+        if (details == null) {
+            return;
+        }
+        
+        Object[] args = null;
+        StringBuffer buf = new StringBuffer();
+        
+        for (int i=0; i < details.length; i++) {
+            if (details[i].getJspBeginLineNumber() >= 0) {
+                args = new Object[] {
+                        new Integer(details[i].getJspBeginLineNumber()), 
+                        details[i].getJspFileName() };
+                buf.append(Localizer.getMessage("jsp.error.single.line.number",
+                        args));
+                buf.append("\n"); 
+            }
+            
+            buf.append(
+                    Localizer.getMessage("jsp.error.corresponding.servlet"));
+            buf.append(details[i].getErrorMessage());
+            buf.append("\n\n");
+        }
+        
+        throw new JasperException(Localizer.getMessage("jsp.error.unable.compile") + "\n\n" + buf);
+    }
+    
+    /**
+     * Processes the given javac error report and exception.
+     *
+     * @param errorReport Compilation error report
+     * @param exception Compilation exception
+     */
+    public void javacError(String errorReport, Exception exception)
+    throws JasperException {
+        
+        throw new JasperException(
+                Localizer.getMessage("jsp.error.unable.compile"), exception);
+    }
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/Dumper.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/Dumper.java
new file mode 100644
index 0000000..ac4ad7f
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/Dumper.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import org.xml.sax.Attributes;
+import org.apache.jasper.JasperException;
+
+class Dumper {
+
+    static class DumpVisitor extends Node.Visitor {
+	private int indent = 0;
+
+	private String getAttributes(Attributes attrs) {
+	    if (attrs == null)
+		return "";
+
+	    StringBuffer buf = new StringBuffer();
+	    for (int i=0; i < attrs.getLength(); i++) {
+		buf.append(" " + attrs.getQName(i) + "=\""
+			   + attrs.getValue(i) + "\"");
+	    }
+	    return buf.toString();
+	}
+
+	private void printString(String str) {
+	    printIndent();
+	    System.out.print(str);
+	}
+
+	private void printString(String prefix, char[] chars, String suffix) {
+	    String str = null;
+	    if (chars != null) {
+		str = new String(chars);
+	    }
+	    printString(prefix, str, suffix);
+	}
+	     
+	private void printString(String prefix, String str, String suffix) {
+	    printIndent();
+	    if (str != null) {
+		System.out.print(prefix + str + suffix);
+	    } else {
+		System.out.print(prefix + suffix);
+	    }
+	}
+
+	private void printAttributes(String prefix, Attributes attrs,
+				     String suffix) {
+	    printString(prefix, getAttributes(attrs), suffix);
+	}
+
+	private void dumpBody(Node n) throws JasperException {
+	    Node.Nodes page = n.getBody();
+	    if (page != null) {
+//		indent++;
+		page.visit(this);
+//		indent--;
+	    }
+        }
+
+        public void visit(Node.PageDirective n) throws JasperException {
+	    printAttributes("<%@ page", n.getAttributes(), "%>");
+        }
+
+        public void visit(Node.TaglibDirective n) throws JasperException {
+	    printAttributes("<%@ taglib", n.getAttributes(), "%>");
+        }
+
+        public void visit(Node.IncludeDirective n) throws JasperException {
+	    printAttributes("<%@ include", n.getAttributes(), "%>");
+	    dumpBody(n);
+        }
+
+        public void visit(Node.Comment n) throws JasperException {
+	    printString("<%--", n.getText(), "--%>");
+        }
+
+        public void visit(Node.Declaration n) throws JasperException {
+	    printString("<%!", n.getText(), "%>");
+        }
+
+        public void visit(Node.Expression n) throws JasperException {
+	    printString("<%=", n.getText(), "%>");
+        }
+
+        public void visit(Node.Scriptlet n) throws JasperException {
+	    printString("<%", n.getText(), "%>");
+        }
+
+        public void visit(Node.IncludeAction n) throws JasperException {
+	    printAttributes("<jsp:include", n.getAttributes(), ">");
+	    dumpBody(n);
+            printString("</jsp:include>");
+        }
+
+        public void visit(Node.ForwardAction n) throws JasperException {
+	    printAttributes("<jsp:forward", n.getAttributes(), ">");
+	    dumpBody(n);
+	    printString("</jsp:forward>");
+        }
+
+        public void visit(Node.GetProperty n) throws JasperException {
+	    printAttributes("<jsp:getProperty", n.getAttributes(), "/>");
+        }
+
+        public void visit(Node.SetProperty n) throws JasperException {
+	    printAttributes("<jsp:setProperty", n.getAttributes(), ">");
+            dumpBody(n);
+            printString("</jsp:setProperty>");
+        }
+
+        public void visit(Node.UseBean n) throws JasperException {
+	    printAttributes("<jsp:useBean", n.getAttributes(), ">");
+	    dumpBody(n);
+	    printString("</jsp:useBean>");
+        }
+	
+        public void visit(Node.PlugIn n) throws JasperException {
+	    printAttributes("<jsp:plugin", n.getAttributes(), ">");
+	    dumpBody(n);
+	    printString("</jsp:plugin>");
+	}
+        
+        public void visit(Node.ParamsAction n) throws JasperException {
+	    printAttributes("<jsp:params", n.getAttributes(), ">");
+	    dumpBody(n);
+	    printString("</jsp:params>");
+        }
+        
+        public void visit(Node.ParamAction n) throws JasperException {
+	    printAttributes("<jsp:param", n.getAttributes(), ">");
+	    dumpBody(n);
+	    printString("</jsp:param>");
+        }
+        
+        public void visit(Node.NamedAttribute n) throws JasperException {
+	    printAttributes("<jsp:attribute", n.getAttributes(), ">");
+	    dumpBody(n);
+	    printString("</jsp:attribute>");
+        }
+
+        public void visit(Node.JspBody n) throws JasperException {
+	    printAttributes("<jsp:body", n.getAttributes(), ">");
+	    dumpBody(n);
+	    printString("</jsp:body>");
+        }
+        
+        public void visit(Node.ELExpression n) throws JasperException {
+	    printString( "${" + new String( n.getText() ) + "}" );
+        }
+
+        public void visit(Node.CustomTag n) throws JasperException {
+	    printAttributes("<" + n.getQName(), n.getAttributes(), ">");
+	    dumpBody(n);
+	    printString("</" + n.getQName() + ">");
+        }
+
+	public void visit(Node.UninterpretedTag n) throws JasperException {
+	    String tag = n.getQName();
+	    printAttributes("<"+tag, n.getAttributes(), ">");
+	    dumpBody(n);
+	    printString("</" + tag + ">");
+        }
+
+	public void visit(Node.TemplateText n) throws JasperException {
+	    printString(new String(n.getText()));
+	}
+
+	private void printIndent() {
+	    for (int i=0; i < indent; i++) {
+		System.out.print("  ");
+	    }
+	}
+    }
+
+    public static void dump(Node n) {
+	try {
+	    n.accept(new DumpVisitor());	
+	} catch (JasperException e) {
+	    e.printStackTrace();
+	}
+    }
+
+    public static void dump(Node.Nodes page) {
+	try {
+	    page.visit(new DumpVisitor());
+	} catch (JasperException e) {
+	    e.printStackTrace();
+	}
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/ELFunctionMapper.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/ELFunctionMapper.java
new file mode 100644
index 0000000..173adb0
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/ELFunctionMapper.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.util.*;
+import javax.servlet.jsp.tagext.FunctionInfo;
+import org.apache.jasper.JasperException;
+
+/**
+ * This class generates functions mappers for the EL expressions in the page.
+ * Instead of a global mapper, a mapper is used for ecah call to EL
+ * evaluator, thus avoiding the prefix overlapping and redefinition
+ * issues.
+ *
+ * @author Kin-man Chung
+ */
+
+public class ELFunctionMapper {
+    static private int currFunc = 0;
+    private ErrorDispatcher err;
+    StringBuffer ds;  // Contains codes to initialize the functions mappers.
+    StringBuffer ss;  // Contains declarations of the functions mappers.
+
+    /**
+     * Creates the functions mappers for all EL expressions in the JSP page.
+     *
+     * @param compiler Current compiler, mainly for accessing error dispatcher.
+     * @param page The current compilation unit.
+     */
+    public static void map(Compiler compiler, Node.Nodes page) 
+		throws JasperException {
+
+	currFunc = 0;
+	ELFunctionMapper map = new ELFunctionMapper();
+	map.err = compiler.getErrorDispatcher();
+	map.ds = new StringBuffer();
+	map.ss = new StringBuffer();
+
+	page.visit(map.new ELFunctionVisitor());
+
+	// Append the declarations to the root node
+	String ds = map.ds.toString();
+	if (ds.length() > 0) {
+	    Node root = page.getRoot();
+	    new Node.Declaration(map.ss.toString(), null, root);
+	    new Node.Declaration("static {\n" + ds + "}\n", null, root);
+	}
+    }
+
+    /**
+     * A visitor for the page.  The places where EL is allowed are scanned
+     * for functions, and if found functions mappers are created.
+     */
+    class ELFunctionVisitor extends Node.Visitor {
+	
+	/**
+	 * Use a global name map to facilitate reuse of function maps.
+	 * The key used is prefix:function:uri.
+	 */
+	private HashMap gMap = new HashMap();
+
+	public void visit(Node.ParamAction n) throws JasperException {
+	    doMap(n.getValue());
+	    visitBody(n);
+	}
+
+	public void visit(Node.IncludeAction n) throws JasperException {
+	    doMap(n.getPage());
+	    visitBody(n);
+	}
+
+	public void visit(Node.ForwardAction n) throws JasperException {
+	    doMap(n.getPage());
+	    visitBody(n);
+	}
+
+        public void visit(Node.SetProperty n) throws JasperException {
+	    doMap(n.getValue());
+	    visitBody(n);
+	}
+
+        public void visit(Node.UseBean n) throws JasperException {
+	    doMap(n.getBeanName());
+	    visitBody(n);
+	}
+
+        public void visit(Node.PlugIn n) throws JasperException {
+	    doMap(n.getHeight());
+	    doMap(n.getWidth());
+	    visitBody(n);
+	}
+
+        public void visit(Node.JspElement n) throws JasperException {
+
+	    Node.JspAttribute[] attrs = n.getJspAttributes();
+	    for (int i = 0; attrs != null && i < attrs.length; i++) {
+		doMap(attrs[i]);
+	    }
+	    doMap(n.getNameAttribute());
+	    visitBody(n);
+	}
+
+        public void visit(Node.UninterpretedTag n) throws JasperException {
+
+	    Node.JspAttribute[] attrs = n.getJspAttributes();
+	    for (int i = 0; attrs != null && i < attrs.length; i++) {
+		doMap(attrs[i]);
+	    }
+	    visitBody(n);
+	}
+
+        public void visit(Node.CustomTag n) throws JasperException {
+	    Node.JspAttribute[] attrs = n.getJspAttributes();
+	    for (int i = 0; attrs != null && i < attrs.length; i++) {
+		doMap(attrs[i]);
+	    }
+	    visitBody(n);
+	}
+
+        public void visit(Node.ELExpression n) throws JasperException {
+	    doMap(n.getEL());
+	}
+
+	private void doMap(Node.JspAttribute attr) 
+		throws JasperException {
+	    if (attr != null) {
+		doMap(attr.getEL());
+	    }
+	}
+
+        /**
+         * Creates function mappers, if needed, from ELNodes
+         */
+	private void doMap(ELNode.Nodes el) 
+		throws JasperException {
+
+            // Only care about functions in ELNode's
+	    class Fvisitor extends ELNode.Visitor {
+		ArrayList funcs = new ArrayList();
+		HashMap keyMap = new HashMap();
+		public void visit(ELNode.Function n) throws JasperException {
+		    String key = n.getPrefix() + ":" + n.getName();
+		    if (! keyMap.containsKey(key)) {
+			keyMap.put(key,"");
+			funcs.add(n);
+		    }
+		}
+	    }
+
+	    if (el == null) {
+		return;
+	    }
+
+	    // First locate all unique functions in this EL
+	    Fvisitor fv = new Fvisitor();
+	    el.visit(fv);
+	    ArrayList functions = fv.funcs;
+
+	    if (functions.size() == 0) {
+		return;
+	    }
+
+	    // Reuse a previous map if possible
+	    String decName = matchMap(functions);
+	    if (decName != null) {
+		el.setMapName(decName);
+		return;
+	    }
+	
+	    // Generate declaration for the map statically
+	    decName = getMapName();
+	    ss.append("static private org.apache.jasper.runtime.ProtectedFunctionMapper " + decName + ";\n");
+
+	    ds.append("  " + decName + "= ");
+	    ds.append("org.apache.jasper.runtime.ProtectedFunctionMapper");
+
+	    // Special case if there is only one function in the map
+	    String funcMethod = null;
+	    if (functions.size() == 1) {
+		funcMethod = ".getMapForFunction";
+	    } else {
+		ds.append(".getInstance();\n");
+		funcMethod = "  " + decName + ".mapFunction";
+	    }
+
+            // Setup arguments for either getMapForFunction or mapFunction
+	    for (int i = 0; i < functions.size(); i++) {
+		ELNode.Function f = (ELNode.Function)functions.get(i);
+		FunctionInfo funcInfo = f.getFunctionInfo();
+		String key = f.getPrefix()+ ":" + f.getName();
+		ds.append(funcMethod + "(\"" + key + "\", " +
+			funcInfo.getFunctionClass() + ".class, " +
+			'\"' + f.getMethodName() + "\", " +
+			"new Class[] {");
+		String params[] = f.getParameters();
+		for (int k = 0; k < params.length; k++) {
+		    if (k != 0) {
+			ds.append(", ");
+		    }
+		    int iArray = params[k].indexOf('[');
+		    if (iArray < 0) {
+			ds.append(params[k] + ".class");
+		    }
+		    else {
+			String baseType = params[k].substring(0, iArray);
+			ds.append("java.lang.reflect.Array.newInstance(");
+			ds.append(baseType);
+			ds.append(".class,");
+
+			// Count the number of array dimension
+			int aCount = 0;
+			for (int jj = iArray; jj < params[k].length(); jj++ ) {
+			    if (params[k].charAt(jj) == '[') {
+				aCount++;
+			    }
+			}
+			if (aCount == 1) {
+			    ds.append("0).getClass()");
+			} else {
+			    ds.append("new int[" + aCount + "]).getClass()");
+			}
+		    }
+		}
+		ds.append("});\n");
+		// Put the current name in the global function map
+		gMap.put(f.getPrefix() + ':' + f.getName() + ':' + f.getUri(),
+			 decName);
+	    }
+	    el.setMapName(decName);
+	}
+
+        /**
+         * Find the name of the function mapper for an EL.  Reuse a
+         * previously generated one if possible.
+         * @param functions An ArrayList of ELNode.Function instances that
+         *                  represents the functions in an EL
+         * @return A previous generated function mapper name that can be used
+         *         by this EL; null if none found.
+         */
+	private String matchMap(ArrayList functions) {
+
+	    String mapName = null;
+	    for (int i = 0; i < functions.size(); i++) {
+		ELNode.Function f = (ELNode.Function)functions.get(i);
+		String temName = (String) gMap.get(f.getPrefix() + ':' +
+					f.getName() + ':' + f.getUri());
+		if (temName == null) {
+		    return null;
+		}
+		if (mapName == null) {
+		    mapName = temName;
+		} else if (!temName.equals(mapName)) {
+		    // If not all in the previous match, then no match.
+		    return null;
+		}
+	    }
+	    return mapName;
+	}
+
+        /*
+         * @return An unique name for a function mapper.
+         */
+	private String getMapName() {
+	    return "_jspx_fnmap_" + currFunc++;
+	}
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/ELNode.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/ELNode.java
new file mode 100644
index 0000000..06f6d6d
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/ELNode.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.util.*;
+import javax.servlet.jsp.tagext.FunctionInfo;
+import org.apache.jasper.JasperException;
+
+/**
+ * This class defines internal representation for an EL Expression
+ *
+ * It currently only defines functions.  It can be expanded to define
+ * all the components of an EL expression, if need to.
+ *
+ * @author Kin-man Chung
+ */
+
+abstract class ELNode {
+
+    abstract public void accept(Visitor v) throws JasperException;
+
+    /**
+     * Child classes
+     */
+
+
+    /**
+     * Represents an EL expression: anything in ${ and }.
+     */
+    public static class Root extends ELNode {
+
+	private ELNode.Nodes expr;
+
+	Root(ELNode.Nodes expr) {
+	    this.expr = expr;
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public ELNode.Nodes getExpression() {
+	    return expr;
+	}
+    }
+
+    /**
+     * Represents text outside of EL expression.
+     */
+    public static class Text extends ELNode {
+
+	private String text;
+
+	Text(String text) {
+	    this.text = text;
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public String getText() {
+	    return text;
+	}
+    }
+
+    /**
+     * Represents anything in EL expression, other than functions, including
+     * function arguments etc
+     */
+    public static class ELText extends ELNode {
+
+	private String text;
+
+	ELText(String text) {
+	    this.text = text;
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public String getText() {
+	    return text;
+	}
+    }
+
+    /**
+     * Represents a function
+     * Currently only include the prefix and function name, but not its
+     * arguments.
+     */
+    public static class Function extends ELNode {
+
+	private String prefix;
+	private String name;
+	private String uri;
+	private FunctionInfo functionInfo;
+	private String methodName;
+	private String[] parameters;
+
+	Function(String prefix, String name) {
+	    this.prefix = prefix;
+	    this.name = name;
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public String getPrefix() {
+	    return prefix;
+	}
+
+	public String getName() {
+	    return name;
+	}
+
+	public void setUri(String uri) {
+	    this.uri = uri;
+	}
+
+	public String getUri() {
+	    return uri;
+	}
+
+	public void setFunctionInfo(FunctionInfo f) {
+	    this.functionInfo = f;
+	}
+
+	public FunctionInfo getFunctionInfo() {
+	    return functionInfo;
+	}
+
+	public void setMethodName(String methodName) {
+	    this.methodName = methodName;
+	}
+
+	public String getMethodName() {
+	    return methodName;
+	}
+
+	public void setParameters(String[] parameters) {
+	    this.parameters = parameters;
+	}
+
+	public String[] getParameters() {
+	    return parameters;
+	}
+    }
+
+    /**
+     * An ordered list of ELNode.
+     */
+    public static class Nodes {
+
+	/* Name used for creating a map for the functions in this
+	   EL expression, for communication to Generator.
+	 */
+	String mapName = null;	// The function map associated this EL
+	private List list;
+
+	public Nodes() {
+	    list = new ArrayList();
+	}
+
+	public void add(ELNode en) {
+	    list.add(en);
+	}
+
+	/**
+	 * Visit the nodes in the list with the supplied visitor
+	 * @param v The visitor used
+	 */
+	public void visit(Visitor v) throws JasperException {
+	    Iterator iter = list.iterator();
+	    while (iter.hasNext()) {
+		ELNode n = (ELNode) iter.next();
+		n.accept(v);
+	    }
+	}
+
+	public Iterator iterator() {
+	    return list.iterator();
+	}
+
+	public boolean isEmpty() {
+	    return list.size() == 0;
+	}
+
+	/**
+	 * @return true if the expression contains a ${...}
+	 */
+	public boolean containsEL() {
+	    Iterator iter = list.iterator();
+	    while (iter.hasNext()) {
+		ELNode n = (ELNode) iter.next();
+		if (n instanceof Root) {
+		    return true;
+		}
+	    }
+	    return false;
+	}
+
+	public void setMapName(String name) {
+	    this.mapName = name;
+	}
+
+	public String getMapName() {
+	    return mapName;
+	}
+    }
+
+    /*
+     * A visitor class for traversing ELNodes
+     */
+    public static class Visitor {
+
+	public void visit(Root n) throws JasperException {
+	    n.getExpression().visit(this);
+	}
+
+	public void visit(Function n) throws JasperException {
+	}
+
+	public void visit(Text n) throws JasperException {
+	}
+
+	public void visit(ELText n) throws JasperException {
+	}
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/ELParser.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/ELParser.java
new file mode 100644
index 0000000..fe13786
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/ELParser.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+/**
+ * This class implements a parser for EL expressions.
+ *
+ * It takes strings of the form xxx${..}yyy${..}zzz etc, and turn it into
+ * a ELNode.Nodes.
+ *
+ * Currently, it only handles text outside ${..} and functions in ${ ..}.
+ *
+ * @author Kin-man Chung
+ */
+
+public class ELParser {
+
+    private Token curToken;	// current token
+    private ELNode.Nodes expr;
+    private ELNode.Nodes ELexpr;
+    private int index;		// Current index of the expression
+    private String expression;	// The EL expression
+    private boolean escapeBS;	// is '\' an escape char in text outside EL?
+
+    private static final String reservedWords[] = {
+        "and", "div", "empty", "eq", "false",
+        "ge", "gt", "instanceof", "le", "lt", "mod",
+        "ne", "not", "null", "or", "true"};
+
+    public ELParser(String expression) {
+	index = 0;
+	this.expression = expression;
+	expr = new ELNode.Nodes();
+    }
+
+    /**
+     * Parse an EL expression
+     * @param expression The input expression string of the form
+     *                   Char* ('${' Char* '}')* Char*
+     * @return Parsed EL expression in ELNode.Nodes
+     */
+    public static ELNode.Nodes parse(String expression) {
+	ELParser parser = new ELParser(expression);
+	while (parser.hasNextChar()) {
+	    String text = parser.skipUntilEL();
+	    if (text.length() > 0) {
+		parser.expr.add(new ELNode.Text(text));
+	    }
+	    ELNode.Nodes elexpr = parser.parseEL();
+	    if (! elexpr.isEmpty()) {
+		parser.expr.add(new ELNode.Root(elexpr));
+	    }
+	}
+	return parser.expr;
+    }
+
+    /**
+     * Parse an EL expression string '${...}'
+     *@return An ELNode.Nodes representing the EL expression
+     * TODO: Currently only parsed into functions and text strings.  This
+     *       should be rewritten for a full parser.
+     */
+    private ELNode.Nodes parseEL() {
+
+	StringBuffer buf = new StringBuffer();
+	ELexpr = new ELNode.Nodes();
+	while (hasNext()) {
+	    curToken = nextToken();
+	    if (curToken instanceof Char) {
+		if (curToken.toChar() == '}') {
+		    break;
+		}
+		buf.append(curToken.toChar());
+	    } else {
+		// Output whatever is in buffer
+		if (buf.length() > 0) {
+		    ELexpr.add(new ELNode.ELText(buf.toString()));
+		}
+		if (!parseFunction()) {
+		    ELexpr.add(new ELNode.ELText(curToken.toString()));
+		}
+	    }
+	}
+	if (buf.length() > 0) {
+	    ELexpr.add(new ELNode.ELText(buf.toString()));
+	}
+
+	return ELexpr;
+    }
+
+    /**
+     * Parse for a function
+     * FunctionInvokation ::= (identifier ':')? identifier '('
+     *			      (Expression (,Expression)*)? ')'
+     * Note: currently we don't parse arguments
+     */
+    private boolean parseFunction() {
+	if (! (curToken instanceof Id) || isELReserved(curToken.toString())) {
+	    return false;
+	}
+	String s1 = null;                 // Function prefix
+	String s2 = curToken.toString();  // Function name
+	int mark = getIndex();
+	if (hasNext()) {
+	    Token t = nextToken();
+	    if (t.toChar() == ':') {
+		if (hasNext()) {
+		    Token t2 = nextToken();
+		    if (t2 instanceof Id) {
+			s1 = s2;
+			s2 = t2.toString();
+			if (hasNext()) {
+			    t = nextToken();
+			}
+		    }
+		}
+	    }
+	    if (t.toChar() == '(') {
+		ELexpr.add(new ELNode.Function(s1, s2));
+		return true;
+	    }
+	}
+	setIndex(mark);
+	return false;
+    }
+
+    /**
+     * Test if an id is a reserved word in EL
+     */
+    private boolean isELReserved(String id) {
+        int i = 0;
+        int j = reservedWords.length;
+        while (i < j) {
+            int k = (i+j)/2;
+            int result = reservedWords[k].compareTo(id);
+            if (result == 0) {
+                return true;
+            }
+            if (result < 0) {
+                i = k+1;
+            } else {
+                j = k;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Skip until an EL expression ('${') is reached, allowing escape sequences
+     * '\\' and '\$'.
+     * @return The text string up to the EL expression
+     */
+    private String skipUntilEL() {
+	char prev = 0;
+	StringBuffer buf = new StringBuffer();
+	while (hasNextChar()) {
+	    char ch = nextChar();
+	    if (prev == '\\') {
+		prev = 0;
+		if (ch == '\\') {
+		    buf.append('\\');
+		    if (!escapeBS)
+			prev = '\\';
+		} else if (ch == '$') {
+		    buf.append('$');
+		}
+		// else error!
+	    } else if (prev == '$') {
+		if (ch == '{') {
+		    prev = 0;
+		    break;
+		} 
+		buf.append('$');
+		buf.append(ch);
+	    } else if (ch == '\\' || ch == '$') {
+		prev = ch;
+	    } else {
+		buf.append(ch);
+	    }
+	}
+	if (prev != 0) {
+	    buf.append(prev);
+	}
+	return buf.toString();
+    }
+
+    /*
+     * @return true if there is something left in EL expression buffer other
+     *         than white spaces.
+     */
+    private boolean hasNext() {
+	skipSpaces();
+	return hasNextChar();
+    }
+
+    /*
+     * @return The next token in the EL expression buffer.
+     */
+    private Token nextToken() {
+	skipSpaces();
+	if (hasNextChar()) {
+	    char ch = nextChar();
+	    if (Character.isJavaIdentifierStart(ch)) {
+		StringBuffer buf = new StringBuffer();
+		buf.append(ch);
+		while ((ch = peekChar()) != -1 &&
+				Character.isJavaIdentifierPart(ch)) {
+		    buf.append(ch);
+		    nextChar();
+		}
+		return new Id(buf.toString());
+	    }
+
+	    if (ch == '\'' || ch == '"') {
+		return parseQuotedChars(ch);
+	    } else {
+		// For now...
+		return new Char(ch);
+	    }
+	}
+	return null;
+    }
+
+    /*
+     * Parse a string in single or double quotes, allowing for escape sequences
+     * '\\', and ('\"', or "\'")
+     */
+    private Token parseQuotedChars(char quote) {
+	StringBuffer buf = new StringBuffer();
+	buf.append(quote);
+	while (hasNextChar()) {
+	    char ch = nextChar();
+	    if (ch == '\\') {
+		ch = nextChar();
+		if (ch == '\\' || ch == quote) {
+		    buf.append(ch);
+		}
+		// else error!
+	    } else if (ch == quote) {
+		buf.append(ch);
+		break;
+	    } else {
+		buf.append(ch);
+	    }
+	}
+	return new QuotedString(buf.toString());
+    }
+
+    /*
+     * A collection of low level parse methods dealing with character in
+     * the EL expression buffer.
+     */
+
+    private void skipSpaces() {
+	while (hasNextChar()) {
+	    if (expression.charAt(index) > ' ')
+		break;
+	    index++;
+	}
+    }
+
+    private boolean hasNextChar() {
+	return index < expression.length();
+    }
+
+    private char nextChar() {
+	if (index >= expression.length()) {
+	    return (char)-1;
+	}
+	return expression.charAt(index++);
+    }
+
+    private char peekChar() {
+	if (index >= expression.length()) {
+	    return (char)-1;
+	}
+	return expression.charAt(index);
+    }
+
+    private int getIndex() {
+	return index;
+    }
+
+    private void setIndex(int i) {
+	index = i;
+    }
+
+    /*
+     * Represents a token in EL expression string
+     */
+    private static class Token {
+
+	char toChar() {
+	    return 0;
+	}
+
+	public String toString() {
+	    return "";
+	}
+    }
+
+    /*
+     * Represents an ID token in EL
+     */
+    private static class Id extends Token {
+	String id;
+
+	Id(String id) {
+	    this.id = id;
+	}
+
+	public String toString() {
+	    return id;
+	}
+    }
+
+    /*
+     * Represents a character token in EL
+     */
+    private static class Char extends Token {
+
+	private char ch;
+
+	Char(char ch) {
+	    this.ch = ch;
+	}
+
+	char toChar() {
+	    return ch;
+	}
+
+	public String toString() {
+	    return (new Character(ch)).toString();
+	}
+    }
+
+    /*
+     * Represents a quoted (single or double) string token in EL
+     */
+    private static class QuotedString extends Token {
+
+	private String value;
+
+	QuotedString(String v) {
+	    this.value = v;
+	}
+
+	public String toString() {
+	    return value;
+	}
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/ErrorDispatcher.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/ErrorDispatcher.java
new file mode 100644
index 0000000..5eecf52
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/ErrorDispatcher.java
@@ -0,0 +1,582 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.compiler;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Vector;
+import java.net.MalformedURLException;
+
+import org.apache.jasper.JasperException;
+import org.xml.sax.SAXException;
+
+/**
+ * Class responsible for dispatching JSP parse and javac compilation errors
+ * to the configured error handler.
+ *
+ * This class is also responsible for localizing any error codes before they
+ * are passed on to the configured error handler.
+ * 
+ * In the case of a Java compilation error, the compiler error message is
+ * parsed into an array of JavacErrorDetail instances, which is passed on to 
+ * the configured error handler.
+ *
+ * @author Jan Luehe
+ * @author Kin-man Chung
+ */
+public class ErrorDispatcher {
+
+    // Custom error handler
+    private ErrorHandler errHandler;
+
+    // Indicates whether the compilation was initiated by JspServlet or JspC
+    private boolean jspcMode = false;
+
+
+    /*
+     * Constructor.
+     *
+     * @param jspcMode true if compilation has been initiated by JspC, false
+     * otherwise
+     */
+    public ErrorDispatcher(boolean jspcMode) {
+	// XXX check web.xml for custom error handler
+	errHandler = new DefaultErrorHandler();
+        this.jspcMode = jspcMode;
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param errCode Error code
+     */
+    public void jspError(String errCode) throws JasperException {
+	dispatch(null, errCode, null, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param where Error location
+     * @param errCode Error code
+     */
+    public void jspError(Mark where, String errCode) throws JasperException {
+	dispatch(where, errCode, null, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param n Node that caused the error
+     * @param errCode Error code
+     */
+    public void jspError(Node n, String errCode) throws JasperException {
+	dispatch(n.getStart(), errCode, null, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param errCode Error code
+     * @param arg Argument for parametric replacement
+     */
+    public void jspError(String errCode, String arg) throws JasperException {
+	dispatch(null, errCode, new Object[] {arg}, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param where Error location
+     * @param errCode Error code
+     * @param arg Argument for parametric replacement
+     */
+    public void jspError(Mark where, String errCode, String arg)
+	        throws JasperException {
+	dispatch(where, errCode, new Object[] {arg}, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param n Node that caused the error
+     * @param errCode Error code
+     * @param arg Argument for parametric replacement
+     */
+    public void jspError(Node n, String errCode, String arg)
+	        throws JasperException {
+	dispatch(n.getStart(), errCode, new Object[] {arg}, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param errCode Error code
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     */
+    public void jspError(String errCode, String arg1, String arg2)
+	        throws JasperException {
+	dispatch(null, errCode, new Object[] {arg1, arg2}, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param errCode Error code
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     * @param arg3 Third argument for parametric replacement
+     */
+    public void jspError(String errCode, String arg1, String arg2, String arg3)
+	        throws JasperException {
+	dispatch(null, errCode, new Object[] {arg1, arg2, arg3}, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param where Error location
+     * @param errCode Error code
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     */
+    public void jspError(Mark where, String errCode, String arg1, String arg2)
+	        throws JasperException {
+	dispatch(where, errCode, new Object[] {arg1, arg2}, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param where Error location
+     * @param errCode Error code
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     * @param arg3 Third argument for parametric replacement
+     */
+
+    public void jspError(Mark where, String errCode, String arg1, String arg2,
+                         String arg3)
+                throws JasperException {
+        dispatch(where, errCode, new Object[] {arg1, arg2, arg3}, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param n Node that caused the error
+     * @param errCode Error code
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     */
+
+    public void jspError(Node n, String errCode, String arg1, String arg2)
+	        throws JasperException {
+	dispatch(n.getStart(), errCode, new Object[] {arg1, arg2}, null);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param n Node that caused the error
+     * @param errCode Error code
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     * @param arg3 Third argument for parametric replacement
+     */
+
+    public void jspError(Node n, String errCode, String arg1, String arg2,
+                         String arg3)
+	        throws JasperException {
+	dispatch(n.getStart(), errCode, new Object[] {arg1, arg2, arg3}, null);
+    }
+
+    /*
+     * Dispatches the given parsing exception to the configured error handler.
+     *
+     * @param e Parsing exception
+     */
+    public void jspError(Exception e) throws JasperException {
+	dispatch(null, null, null, e);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param errCode Error code
+     * @param arg Argument for parametric replacement
+     * @param e Parsing exception
+     */
+    public void jspError(String errCode, String arg, Exception e)
+	        throws JasperException {
+	dispatch(null, errCode, new Object[] {arg}, e);
+    }
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param n Node that caused the error
+     * @param errCode Error code
+     * @param arg Argument for parametric replacement
+     * @param e Parsing exception
+     */
+    public void jspError(Node n, String errCode, String arg, Exception e)
+	        throws JasperException {
+	dispatch(n.getStart(), errCode, new Object[] {arg}, e);
+    }
+
+    /**
+     * Parses the given error message into an array of javac compilation error
+     * messages (one per javac compilation error line number).
+     *
+     * @param errMsg Error message
+     * @param fname Name of Java source file whose compilation failed
+     * @param page Node representation of JSP page from which the Java source
+     * file was generated
+     *
+     * @return Array of javac compilation errors, or null if the given error
+     * message does not contain any compilation error line numbers
+     */
+    public static JavacErrorDetail[] parseJavacErrors(String errMsg,
+                                                      String fname,
+                                                      Node.Nodes page)
+            throws JasperException, IOException {
+
+	return parseJavacMessage(errMsg, fname, page);
+    }
+
+    /*
+     * Dispatches the given javac compilation errors to the configured error
+     * handler.
+     *
+     * @param javacErrors Array of javac compilation errors
+     */
+    public void javacError(JavacErrorDetail[] javacErrors)
+            throws JasperException {
+
+        errHandler.javacError(javacErrors);
+    }
+
+
+    /*
+     * Dispatches the given compilation error report and exception to the
+     * configured error handler.
+     *
+     * @param errorReport Compilation error report
+     * @param e Compilation exception
+     */
+    public void javacError(String errorReport, Exception e)
+                throws JasperException {
+
+        errHandler.javacError(errorReport, e);
+    }
+
+
+    //*********************************************************************
+    // Private utility methods
+
+    /*
+     * Dispatches the given JSP parse error to the configured error handler.
+     *
+     * The given error code is localized. If it is not found in the
+     * resource bundle for localized error messages, it is used as the error
+     * message.
+     *
+     * @param where Error location
+     * @param errCode Error code
+     * @param args Arguments for parametric replacement
+     * @param e Parsing exception
+     */
+    private void dispatch(Mark where, String errCode, Object[] args,
+			  Exception e) throws JasperException {
+	String file = null;
+	String errMsg = null;
+	int line = -1;
+	int column = -1;
+	boolean hasLocation = false;
+
+	// Localize
+	if (errCode != null) {
+	    errMsg = Localizer.getMessage(errCode, args);
+	} else if (e != null) {
+	    // give a hint about what's wrong
+	    errMsg = e.getMessage();
+	}
+
+	// Get error location
+	if (where != null) {
+            if (jspcMode) {
+                // Get the full URL of the resource that caused the error
+                try {
+                    file = where.getURL().toString();
+                } catch (MalformedURLException me) {
+                    // Fallback to using context-relative path
+                    file = where.getFile();
+                }
+            } else {
+                // Get the context-relative resource path, so as to not
+                // disclose any local filesystem details
+                file = where.getFile();
+            }
+	    line = where.getLineNumber();
+	    column = where.getColumnNumber();
+	    hasLocation = true;
+	}
+
+	// Get nested exception
+	Exception nestedEx = e;
+	if ((e instanceof SAXException)
+	        && (((SAXException) e).getException() != null)) {
+	    nestedEx = ((SAXException) e).getException();
+	}
+
+	if (hasLocation) {
+	    errHandler.jspError(file, line, column, errMsg, nestedEx);
+	} else {
+	    errHandler.jspError(errMsg, nestedEx);
+	}
+    }
+
+    /*
+     * Parses the given Java compilation error message, which may contain one
+     * or more compilation errors, into an array of JavacErrorDetail instances.
+     *
+     * Each JavacErrorDetail instance contains the information about a single
+     * compilation error.
+     *
+     * @param errMsg Compilation error message that was generated by the
+     * javac compiler
+     * @param fname Name of Java source file whose compilation failed
+     * @param page Node representation of JSP page from which the Java source
+     * file was generated
+     *
+     * @return Array of JavacErrorDetail instances corresponding to the
+     * compilation errors
+     */
+    private static JavacErrorDetail[] parseJavacMessage(
+                                String errMsg, String fname, Node.Nodes page)
+	        throws IOException, JasperException {
+
+        Vector errVec = new Vector();
+        StringBuffer errMsgBuf = null;
+        int lineNum = -1;
+        JavacErrorDetail javacError = null;
+        
+        BufferedReader reader = new BufferedReader(new StringReader(errMsg));
+        
+        /*
+         * Parse compilation errors. Each compilation error consists of a file
+         * path and error line number, followed by a number of lines describing
+         * the error.
+         */
+        String line = null;
+        while ((line = reader.readLine()) != null) {
+            
+            /*
+             * Error line number is delimited by set of colons.
+             * Ignore colon following drive letter on Windows (fromIndex = 2).
+             * XXX Handle deprecation warnings that don't have line info
+             */
+            int beginColon = line.indexOf(':', 2); 
+            int endColon = line.indexOf(':', beginColon + 1);
+            if ((beginColon >= 0) && (endColon >= 0)) {
+                if (javacError != null) {
+                    // add previous error to error vector
+                    errVec.add(javacError);
+                }
+                
+                String lineNumStr = line.substring(beginColon + 1, endColon);
+                try {
+                    lineNum = Integer.parseInt(lineNumStr);
+                } catch (NumberFormatException e) {
+                    // XXX
+                }
+                
+                errMsgBuf = new StringBuffer();
+                
+                javacError = createJavacError(fname, page, errMsgBuf, lineNum);
+            }
+            
+            // Ignore messages preceding first error
+            if (errMsgBuf != null) {
+                errMsgBuf.append(line);
+                errMsgBuf.append("\n");
+            }
+        }
+        
+        // Add last error to error vector
+        if (javacError != null) {
+            errVec.add(javacError);
+        } 
+        
+        reader.close();
+        
+        JavacErrorDetail[] errDetails = null;
+        if (errVec.size() > 0) {
+            errDetails = new JavacErrorDetail[errVec.size()];
+            errVec.copyInto(errDetails);
+        }
+        
+        return errDetails;
+    }
+
+
+    /**
+     * @param fname
+     * @param page
+     * @param errMsgBuf
+     * @param lineNum
+     * @return JavacErrorDetail The error details
+     * @throws JasperException
+     */
+    public static JavacErrorDetail createJavacError(String fname, Node.Nodes page, 
+            StringBuffer errMsgBuf, int lineNum) throws JasperException {
+        JavacErrorDetail javacError;
+        // Attempt to map javac error line number to line in JSP page
+        ErrorVisitor errVisitor = new ErrorVisitor(lineNum);
+        page.visit(errVisitor);
+        Node errNode = errVisitor.getJspSourceNode();
+        if ((errNode != null) && (errNode.getStart() != null)) {
+            javacError = new JavacErrorDetail(
+                    fname,
+                    lineNum,
+                    errNode.getStart().getFile(),
+                    errNode.getStart().getLineNumber(),
+                    errMsgBuf);
+        } else {
+            /*
+             * javac error line number cannot be mapped to JSP page
+             * line number. For example, this is the case if a 
+             * scriptlet is missing a closing brace, which causes
+             * havoc with the try-catch-finally block that the code
+             * generator places around all generated code: As a result
+             * of this, the javac error line numbers will be outside
+             * the range of begin and end java line numbers that were
+             * generated for the scriptlet, and therefore cannot be
+             * mapped to the start line number of the scriptlet in the
+             * JSP page.
+             * Include just the javac error info in the error detail.
+             */
+            javacError = new JavacErrorDetail(
+                    fname,
+                    lineNum,
+                    errMsgBuf);
+        }
+        return javacError;
+    }
+
+
+    /*
+     * Visitor responsible for mapping a line number in the generated servlet
+     * source code to the corresponding JSP node.
+     */
+    static class ErrorVisitor extends Node.Visitor {
+
+	// Java source line number to be mapped
+	private int lineNum;
+
+	/*
+	 * JSP node whose Java source code range in the generated servlet
+	 * contains the Java source line number to be mapped
+	 */
+	Node found;
+
+	/*
+	 * Constructor.
+	 *
+	 * @param lineNum Source line number in the generated servlet code
+	 */
+	public ErrorVisitor(int lineNum) {
+	    this.lineNum = lineNum;
+	}
+
+	public void doVisit(Node n) throws JasperException {
+	    if ((lineNum >= n.getBeginJavaLine())
+		    && (lineNum < n.getEndJavaLine())) {
+		found = n;
+	    }
+        }
+
+	/*
+	 * Gets the JSP node to which the source line number in the generated
+	 * servlet code was mapped.
+	 *
+	 * @return JSP node to which the source line number in the generated
+	 * servlet code was mapped
+	 */
+	public Node getJspSourceNode() {
+	    return found;
+	}
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/ErrorHandler.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/ErrorHandler.java
new file mode 100644
index 0000000..212d442
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/ErrorHandler.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import org.apache.jasper.JasperException;
+
+/**
+ * Interface for handling JSP parse and javac compilation errors.
+ * 
+ * An implementation of this interface may be registered with the
+ * ErrorDispatcher by setting the XXX initialization parameter in the JSP
+ * page compiler and execution servlet in Catalina's web.xml file to the
+ * implementation's fully qualified class name.
+ *
+ * @author Jan Luehe
+ * @author Kin-man Chung
+ */
+public interface ErrorHandler {
+
+    /**
+     * Processes the given JSP parse error.
+     *
+     * @param fname Name of the JSP file in which the parse error occurred
+     * @param line Parse error line number
+     * @param column Parse error column number
+     * @param msg Parse error message
+     * @param exception Parse exception
+     */
+    public void jspError(String fname, int line, int column, String msg,
+			 Exception exception) throws JasperException;
+
+    /**
+     * Processes the given JSP parse error.
+     *
+     * @param msg Parse error message
+     * @param exception Parse exception
+     */
+    public void jspError(String msg, Exception exception)
+	throws JasperException;
+
+    /**
+     * Processes the given javac compilation errors.
+     *
+     * @param details Array of JavacErrorDetail instances corresponding to the
+     * compilation errors
+     */
+    public void javacError(JavacErrorDetail[] details)
+	throws JasperException;
+
+    /**
+     * Processes the given javac error report and exception.
+     *
+     * @param errorReport Compilation error report
+     * @param exception Compilation exception
+     */
+    public void javacError(String errorReport, Exception exception)
+        throws JasperException;
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/Generator.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/Generator.java
new file mode 100644
index 0000000..2f4ed52
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/Generator.java
@@ -0,0 +1,4023 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagVariableInfo;
+import javax.servlet.jsp.tagext.VariableInfo;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.runtime.JspRuntimeLibrary;
+import org.xml.sax.Attributes;
+
+/**
+ * Generate Java source from Nodes
+ *
+ * @author Anil K. Vijendran
+ * @author Danno Ferrin
+ * @author Mandar Raje
+ * @author Rajiv Mordani
+ * @author Pierre Delisle
+ *
+ * Tomcat 4.1.x and Tomcat 5:
+ * @author Kin-man Chung
+ * @author Jan Luehe
+ * @author Shawn Bayern
+ * @author Mark Roth
+ * @author Denis Benoit
+ */
+
+class Generator {
+
+    private static final Class[] OBJECT_CLASS = { Object.class };
+    private ServletWriter out;
+    private ArrayList methodsBuffered;
+    private FragmentHelperClass fragmentHelperClass;
+    private ErrorDispatcher err;
+    private BeanRepository beanInfo;
+    private JspCompilationContext ctxt;
+    private boolean isPoolingEnabled;
+    private boolean breakAtLF;
+    private PageInfo pageInfo;
+    private Vector tagHandlerPoolNames;
+    private GenBuffer charArrayBuffer;
+
+    /**
+     * @param s the input string
+     * @return quoted and escaped string, per Java rule
+     */
+    static String quote(String s) {
+
+        if (s == null)
+            return "null";
+
+        return '"' + escape(s) + '"';
+    }
+
+    /**
+     * @param s the input string
+     * @return escaped string, per Java rule
+     */
+    static String escape(String s) {
+
+        if (s == null)
+            return "";
+
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '"')
+                b.append('\\').append('"');
+            else if (c == '\\')
+                b.append('\\').append('\\');
+            else if (c == '\n')
+                b.append('\\').append('n');
+            else if (c == '\r')
+                b.append('\\').append('r');
+            else
+                b.append(c);
+        }
+        return b.toString();
+    }
+
+    /**
+     * Single quote and escape a character
+     */
+    static String quote(char c) {
+
+        StringBuffer b = new StringBuffer();
+        b.append('\'');
+        if (c == '\'')
+            b.append('\\').append('\'');
+        else if (c == '\\')
+            b.append('\\').append('\\');
+        else if (c == '\n')
+            b.append('\\').append('n');
+        else if (c == '\r')
+            b.append('\\').append('r');
+        else
+            b.append(c);
+        b.append('\'');
+        return b.toString();
+    }
+
+    /**
+     * Generates declarations.  This includes "info" of the page directive,
+     * and scriptlet declarations.
+     */
+    private void generateDeclarations(Node.Nodes page) throws JasperException {
+
+        class DeclarationVisitor extends Node.Visitor {
+
+            private boolean getServletInfoGenerated = false;
+
+            /*
+             * Generates getServletInfo() method that returns the value of the
+             * page directive's 'info' attribute, if present.
+             *
+             * The Validator has already ensured that if the translation unit
+             * contains more than one page directive with an 'info' attribute,
+             * their values match.
+             */
+            public void visit(Node.PageDirective n) throws JasperException {
+
+                if (getServletInfoGenerated) {
+                    return;
+                }
+
+                String info = n.getAttributeValue("info");
+                if (info == null)
+                    return;
+
+                getServletInfoGenerated = true;
+                out.printil("public String getServletInfo() {");
+                out.pushIndent();
+                out.printin("return ");
+                out.print(quote(info));
+                out.println(";");
+                out.popIndent();
+                out.printil("}");
+                out.println();
+            }
+
+            public void visit(Node.Declaration n) throws JasperException {
+                n.setBeginJavaLine(out.getJavaLine());
+                out.printMultiLn(new String(n.getText()));
+                out.println();
+                n.setEndJavaLine(out.getJavaLine());
+            }
+
+            // Custom Tags may contain declarations from tag plugins.
+            public void visit(Node.CustomTag n) throws JasperException {
+                if (n.useTagPlugin()) {
+                    if (n.getAtSTag() != null) {
+                        n.getAtSTag().visit(this);
+                    }
+                    visitBody(n);
+                    if (n.getAtETag() != null) {
+                        n.getAtETag().visit(this);
+                    }
+                } else {
+                    visitBody(n);
+                }
+            }
+        }
+
+        out.println();
+        page.visit(new DeclarationVisitor());
+    }
+
+    /**
+     * Compiles list of tag handler pool names.
+     */
+    private void compileTagHandlerPoolList(Node.Nodes page)
+        throws JasperException {
+
+        class TagHandlerPoolVisitor extends Node.Visitor {
+
+            private Vector names;
+
+            /*
+             * Constructor
+             *
+             * @param v Vector of tag handler pool names to populate
+             */
+            TagHandlerPoolVisitor(Vector v) {
+                names = v;
+            }
+
+            /*
+             * Gets the name of the tag handler pool for the given custom tag
+             * and adds it to the list of tag handler pool names unless it is
+             * already contained in it.
+             */
+            public void visit(Node.CustomTag n) throws JasperException {
+
+                if (!n.implementsSimpleTag()) {
+                    String name =
+                        createTagHandlerPoolName(
+                            n.getPrefix(),
+                            n.getLocalName(),
+                            n.getAttributes(),
+                            n.hasEmptyBody());
+                    n.setTagHandlerPoolName(name);
+                    if (!names.contains(name)) {
+                        names.add(name);
+                    }
+                }
+                visitBody(n);
+            }
+
+            /*
+             * Creates the name of the tag handler pool whose tag handlers may
+             * be (re)used to service this action.
+             *
+             * @return The name of the tag handler pool
+             */
+            private String createTagHandlerPoolName(
+                String prefix,
+                String shortName,
+                Attributes attrs,
+                boolean hasEmptyBody) {
+                String poolName = null;
+
+                poolName = "_jspx_tagPool_" + prefix + "_" + shortName;
+                if (attrs != null) {
+                    String[] attrNames = new String[attrs.getLength()];
+                    for (int i = 0; i < attrNames.length; i++) {
+                        attrNames[i] = attrs.getQName(i);
+                    }
+                    Arrays.sort(attrNames, Collections.reverseOrder());
+                    for (int i = 0; i < attrNames.length; i++) {
+                        poolName = poolName + "_" + attrNames[i];
+                    }
+                }
+                if (hasEmptyBody) {
+                    poolName = poolName + "_nobody";
+                }
+                return JspUtil.makeXmlJavaIdentifier(poolName);
+            }
+        }
+
+        page.visit(new TagHandlerPoolVisitor(tagHandlerPoolNames));
+    }
+
+    private void declareTemporaryScriptingVars(Node.Nodes page)
+        throws JasperException {
+
+        class ScriptingVarVisitor extends Node.Visitor {
+
+            private Vector vars;
+
+            ScriptingVarVisitor() {
+                vars = new Vector();
+            }
+
+            public void visit(Node.CustomTag n) throws JasperException {
+
+                if (n.getCustomNestingLevel() > 0) {
+                    TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+                    VariableInfo[] varInfos = n.getVariableInfos();
+
+                    if (varInfos.length > 0) {
+                        for (int i = 0; i < varInfos.length; i++) {
+                            String varName = varInfos[i].getVarName();
+                            String tmpVarName =
+                                "_jspx_"
+                                    + varName
+                                    + "_"
+                                    + n.getCustomNestingLevel();
+                            if (!vars.contains(tmpVarName)) {
+                                vars.add(tmpVarName);
+                                out.printin(varInfos[i].getClassName());
+                                out.print(" ");
+                                out.print(tmpVarName);
+                                out.print(" = ");
+                                out.print(null);
+                                out.println(";");
+                            }
+                        }
+                    } else {
+                        for (int i = 0; i < tagVarInfos.length; i++) {
+                            String varName = tagVarInfos[i].getNameGiven();
+                            if (varName == null) {
+                                varName =
+                                    n.getTagData().getAttributeString(
+                                        tagVarInfos[i].getNameFromAttribute());
+                            } else if (
+                                tagVarInfos[i].getNameFromAttribute()
+                                    != null) {
+                                // alias
+                                continue;
+                            }
+                            String tmpVarName =
+                                "_jspx_"
+                                    + varName
+                                    + "_"
+                                    + n.getCustomNestingLevel();
+                            if (!vars.contains(tmpVarName)) {
+                                vars.add(tmpVarName);
+                                out.printin(tagVarInfos[i].getClassName());
+                                out.print(" ");
+                                out.print(tmpVarName);
+                                out.print(" = ");
+                                out.print(null);
+                                out.println(";");
+                            }
+                        }
+                    }
+                }
+
+                visitBody(n);
+            }
+        }
+
+        page.visit(new ScriptingVarVisitor());
+    }
+
+    /**
+     * Generates the _jspInit() method for instantiating the tag handler pools.
+     * For tag file, _jspInit has to be invoked manually, and the ServletConfig
+     * object explicitly passed.
+     */
+    private void generateInit() {
+
+        if (ctxt.isTagFile()) {
+            out.printil("private void _jspInit(ServletConfig config) {");
+        } else {
+            out.printil("public void _jspInit() {");
+        }
+
+        out.pushIndent();
+        for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
+            out.printin((String)tagHandlerPoolNames.elementAt(i));
+            out.print(
+                " = org.apache.jasper.runtime.TagHandlerPool.getTagHandlerPool(");
+            if (ctxt.isTagFile()) {
+                out.print("config");
+            } else {
+                out.print("getServletConfig()");
+            }
+            out.println(");");
+        }
+        out.popIndent();
+        out.printil("}");
+        out.println();
+    }
+
+    /**
+     * Generates the _jspDestroy() method which is responsible for calling the
+     * release() method on every tag handler in any of the tag handler pools.
+     */
+    private void generateDestroy() {
+
+        out.printil("public void _jspDestroy() {");
+        out.pushIndent();
+        for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
+            out.printin((String)tagHandlerPoolNames.elementAt(i));
+            out.println(".release();");
+        }
+        out.popIndent();
+        out.printil("}");
+        out.println();
+    }
+
+    /**
+     * Generate preamble package name
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreamblePackage(String packageName)
+        throws JasperException {
+        if (!"".equals(packageName) && packageName != null) {
+            out.printil("package " + packageName + ";");
+            out.println();
+        }
+    }
+
+    /**
+     * Generate preamble imports
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreambleImports() throws JasperException {
+        Iterator iter = pageInfo.getImports().iterator();
+        while (iter.hasNext()) {
+            out.printin("import ");
+            out.print((String)iter.next());
+            out.println(";");
+        }
+        out.println();
+    }
+
+    /**
+     * Generation of static initializers in preamble.
+     * For example, dependant list, el function map, prefix map.
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreambleStaticInitializers() throws JasperException {
+        // Static data for getDependants()
+        out.printil("private static java.util.List _jspx_dependants;");
+        out.println();
+        List dependants = pageInfo.getDependants();
+        Iterator iter = dependants.iterator();
+        if (!dependants.isEmpty()) {
+            out.printil("static {");
+            out.pushIndent();
+            out.printin("_jspx_dependants = new java.util.ArrayList(");
+            out.print("" + dependants.size());
+            out.println(");");
+            while (iter.hasNext()) {
+                out.printin("_jspx_dependants.add(\"");
+                out.print((String)iter.next());
+                out.println("\");");
+            }
+            out.popIndent();
+            out.printil("}");
+            out.println();
+        }
+    }
+
+    /**
+     * Declare tag handler pools (tags of the same type and with the same
+     * attribute set share the same tag handler pool)
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreambleClassVariableDeclarations(String className)
+        throws JasperException {
+        if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
+            for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
+                out.printil(
+                    "private org.apache.jasper.runtime.TagHandlerPool "
+                        + tagHandlerPoolNames.elementAt(i)
+                        + ";");
+            }
+            out.println();
+        }
+    }
+
+    /**
+     * Declare general-purpose methods
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreambleMethods() throws JasperException {
+        // Method used to get compile time file dependencies
+        out.printil("public Object getDependants() {");
+        out.pushIndent();
+        out.printil("return _jspx_dependants;");
+        out.popIndent();
+        out.printil("}");
+        out.println();
+
+        if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
+            generateInit();
+            generateDestroy();
+        }
+    }
+
+    /**
+     * Generates the beginning of the static portion of the servlet.
+     */
+    private void generatePreamble(Node.Nodes page) throws JasperException {
+
+        String servletPackageName = ctxt.getServletPackageName();
+        String servletClassName = ctxt.getServletClassName();
+        String serviceMethodName = Constants.SERVICE_METHOD_NAME;
+
+        // First the package name:
+        genPreamblePackage(servletPackageName);
+
+        // Generate imports
+        genPreambleImports();
+
+        // Generate class declaration
+        out.printin("public final class ");
+        out.print(servletClassName);
+        out.print(" extends ");
+        out.println(pageInfo.getExtends());
+        out.printin(
+            "    implements org.apache.jasper.runtime.JspSourceDependent");
+        if (!pageInfo.isThreadSafe()) {
+            out.println(",");
+            out.printin("                 SingleThreadModel");
+        }
+        out.println(" {");
+        out.pushIndent();
+
+        // Class body begins here
+        generateDeclarations(page);
+
+        // Static initializations here
+        genPreambleStaticInitializers();
+
+        // Class variable declarations
+        genPreambleClassVariableDeclarations(servletClassName);
+
+        // Constructor
+        //  generateConstructor(className);
+
+        // Methods here
+        genPreambleMethods();
+
+        // Now the service method
+        out.printin("public void ");
+        out.print(serviceMethodName);
+        out.println(
+            "(HttpServletRequest request, HttpServletResponse response)");
+        out.println("        throws java.io.IOException, ServletException {");
+
+        out.pushIndent();
+        out.println();
+
+        // Local variable declarations
+        out.printil("JspFactory _jspxFactory = null;");
+        out.printil("PageContext pageContext = null;");
+        if (pageInfo.isSession())
+            out.printil("HttpSession session = null;");
+
+        if (pageInfo.isErrorPage()) {
+            out.printil(
+                "Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request);");
+            out.printil("if (exception != null) {");
+            out.pushIndent();
+            out.printil(
+                "response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);");
+            out.popIndent();
+            out.printil("}");
+        }
+
+        out.printil("ServletContext application = null;");
+        out.printil("ServletConfig config = null;");
+        out.printil("JspWriter out = null;");
+        out.printil("Object page = this;");
+
+        out.printil("JspWriter _jspx_out = null;");
+        out.printil("PageContext _jspx_page_context = null;");
+        out.println();
+
+        declareTemporaryScriptingVars(page);
+        out.println();
+
+        out.printil("try {");
+        out.pushIndent();
+
+        out.printil("_jspxFactory = JspFactory.getDefaultFactory();");
+
+        out.printin("response.setContentType(");
+        out.print(quote(pageInfo.getContentType()));
+        out.println(");");
+
+        if (ctxt.getOptions().isXpoweredBy()) {
+            out.printil("response.addHeader(\"X-Powered-By\", \"JSP/2.0\");");
+        }
+
+        out.printil(
+            "pageContext = _jspxFactory.getPageContext(this, request, response,");
+        out.printin("\t\t\t");
+        out.print(quote(pageInfo.getErrorPage()));
+        out.print(", " + pageInfo.isSession());
+        out.print(", " + pageInfo.getBuffer());
+        out.print(", " + pageInfo.isAutoFlush());
+        out.println(");");
+        out.printil("_jspx_page_context = pageContext;");
+
+        out.printil("application = pageContext.getServletContext();");
+        out.printil("config = pageContext.getServletConfig();");
+
+        if (pageInfo.isSession())
+            out.printil("session = pageContext.getSession();");
+        out.printil("out = pageContext.getOut();");
+        out.printil("_jspx_out = out;");
+        out.println();
+    }
+
+    /**
+     * Generates an XML Prolog, which includes an XML declaration and
+     * an XML doctype declaration.
+     */
+    private void generateXmlProlog(Node.Nodes page) {
+
+        /*
+         * An XML declaration is generated under the following conditions:
+         *
+         * - 'omit-xml-declaration' attribute of <jsp:output> action is set to
+         *   "no" or "false"
+         * - JSP document without a <jsp:root>
+         */
+        String omitXmlDecl = pageInfo.getOmitXmlDecl();
+        if ((omitXmlDecl != null && !JspUtil.booleanValue(omitXmlDecl))
+            || (omitXmlDecl == null
+                && page.getRoot().isXmlSyntax()
+                && !pageInfo.hasJspRoot()
+                && !ctxt.isTagFile())) {
+            String cType = pageInfo.getContentType();
+            String charSet = cType.substring(cType.indexOf("charset=") + 8);
+            out.printil(
+                "out.write(\"<?xml version=\\\"1.0\\\" encoding=\\\""
+                    + charSet
+                    + "\\\"?>\\n\");");
+        }
+
+        /*
+         * Output a DOCTYPE declaration if the doctype-root-element appears.
+         * If doctype-public appears:
+         *     <!DOCTYPE name PUBLIC "doctypePublic" "doctypeSystem">
+         * else
+         *     <!DOCTYPE name SYSTEM "doctypeSystem" >
+         */
+
+        String doctypeName = pageInfo.getDoctypeName();
+        if (doctypeName != null) {
+            String doctypePublic = pageInfo.getDoctypePublic();
+            String doctypeSystem = pageInfo.getDoctypeSystem();
+            out.printin("out.write(\"<!DOCTYPE ");
+            out.print(doctypeName);
+            if (doctypePublic == null) {
+                out.print(" SYSTEM \\\"");
+            } else {
+                out.print(" PUBLIC \\\"");
+                out.print(doctypePublic);
+                out.print("\\\" \\\"");
+            }
+            out.print(doctypeSystem);
+            out.println("\\\">\\n\");");
+        }
+    }
+
+    /*
+     * Generates the constructor.
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void generateConstructor(String className) {
+        out.printil("public " + className + "() {");
+        out.printil("}");
+        out.println();
+    }
+
+    /**
+     * A visitor that generates codes for the elements in the page.
+     */
+    class GenerateVisitor extends Node.Visitor {
+
+        /*
+         * Hashtable containing introspection information on tag handlers:
+         *   <key>: tag prefix
+         *   <value>: hashtable containing introspection on tag handlers:
+         *              <key>: tag short name
+         *              <value>: introspection info of tag handler for
+         *                       <prefix:shortName> tag
+         */
+        private Hashtable handlerInfos;
+
+        private Hashtable tagVarNumbers;
+        private String parent;
+        private boolean isSimpleTagParent; // Is parent a SimpleTag?
+        private String pushBodyCountVar;
+        private String simpleTagHandlerVar;
+        private boolean isSimpleTagHandler;
+        private boolean isFragment;
+        private boolean isTagFile;
+        private ServletWriter out;
+        private ArrayList methodsBuffered;
+        private FragmentHelperClass fragmentHelperClass;
+        private int methodNesting;
+        private TagInfo tagInfo;
+        private ClassLoader loader;
+        private int charArrayCount;
+        private HashMap textMap;
+
+        /**
+         * Constructor.
+         */
+        public GenerateVisitor(
+            boolean isTagFile,
+            ServletWriter out,
+            ArrayList methodsBuffered,
+            FragmentHelperClass fragmentHelperClass,
+            ClassLoader loader,
+            TagInfo tagInfo) {
+
+            this.isTagFile = isTagFile;
+            this.out = out;
+            this.methodsBuffered = methodsBuffered;
+            this.fragmentHelperClass = fragmentHelperClass;
+            this.loader = loader;
+            this.tagInfo = tagInfo;
+            methodNesting = 0;
+            handlerInfos = new Hashtable();
+            tagVarNumbers = new Hashtable();
+            textMap = new HashMap();
+        }
+
+        /**
+         * Returns an attribute value, optionally URL encoded.  If
+         * the value is a runtime expression, the result is the expression
+         * itself, as a string.  If the result is an EL expression, we insert
+         * a call to the interpreter.  If the result is a Named Attribute
+         * we insert the generated variable name.  Otherwise the result is a
+         * string literal, quoted and escaped.
+         *
+         * @param attr An JspAttribute object
+         * @param encode true if to be URL encoded
+         * @param expectedType the expected type for an EL evaluation
+         *        (ignored for attributes that aren't EL expressions)
+         */
+        private String attributeValue(
+            Node.JspAttribute attr,
+            boolean encode,
+            Class expectedType) {
+            String v = attr.getValue();
+            if (!attr.isNamedAttribute() && (v == null))
+                return "";
+
+            if (attr.isExpression()) {
+                if (encode) {
+                    return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode(String.valueOf("
+                        + v
+                        + "), request.getCharacterEncoding())";
+                }
+                return v;
+            } else if (attr.isELInterpreterInput()) {
+                boolean replaceESC = v.indexOf(Constants.ESC) > 0;
+                v =
+                    JspUtil.interpreterCall(
+                        this.isTagFile,
+                        v,
+                        expectedType,
+                        attr.getEL().getMapName(),
+                        false);
+                // XXX ESC replacement hack
+                if (replaceESC) {
+                    v = "(" + v + ").replace(" + Constants.ESCStr + ", '$')";
+                }
+                if (encode) {
+                    return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("
+                        + v
+                        + ", request.getCharacterEncoding())";
+                }
+                return v;
+            } else if (attr.isNamedAttribute()) {
+                return attr.getNamedAttributeNode().getTemporaryVariableName();
+            } else {
+                if (encode) {
+                    return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("
+                        + quote(v)
+                        + ", request.getCharacterEncoding())";
+                }
+                return quote(v);
+            }
+        }
+
+        /**
+         * Prints the attribute value specified in the param action, in the
+         * form of name=value string.
+         *
+         * @param n the parent node for the param action nodes.
+         */
+        private void printParams(Node n, String pageParam, boolean literal)
+            throws JasperException {
+
+            class ParamVisitor extends Node.Visitor {
+                String separator;
+
+                ParamVisitor(String separator) {
+                    this.separator = separator;
+                }
+
+                public void visit(Node.ParamAction n) throws JasperException {
+
+                    out.print(" + ");
+                    out.print(separator);
+                    out.print(" + ");
+                    out.print(
+                        "org.apache.jasper.runtime.JspRuntimeLibrary."
+                            + "URLEncode("
+                            + quote(n.getTextAttribute("name"))
+                            + ", request.getCharacterEncoding())");
+                    out.print("+ \"=\" + ");
+                    out.print(attributeValue(n.getValue(), true, String.class));
+
+                    // The separator is '&' after the second use
+                    separator = "\"&\"";
+                }
+            }
+
+            String sep;
+            if (literal) {
+                sep = pageParam.indexOf('?') > 0 ? "\"&\"" : "\"?\"";
+            } else {
+                sep = "((" + pageParam + ").indexOf('?')>0? '&': '?')";
+            }
+            if (n.getBody() != null) {
+                n.getBody().visit(new ParamVisitor(sep));
+            }
+        }
+
+        public void visit(Node.Expression n) throws JasperException {
+            n.setBeginJavaLine(out.getJavaLine());
+            out.printin("out.print(");
+            out.printMultiLn(n.getText());
+            out.println(");");
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.Scriptlet n) throws JasperException {
+            n.setBeginJavaLine(out.getJavaLine());
+            out.printMultiLn(n.getText());
+            out.println();
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.ELExpression n) throws JasperException {
+            n.setBeginJavaLine(out.getJavaLine());
+            if (!pageInfo.isELIgnored()) {
+                out.printil(
+                    "out.write("
+                        + JspUtil.interpreterCall(
+                            this.isTagFile,
+                            "${" + new String(n.getText()) + "}",
+                            String.class,
+                            n.getEL().getMapName(),
+                            false)
+                        + ");");
+            } else {
+                out.printil(
+                    "out.write("
+                        + quote("${" + new String(n.getText()) + "}")
+                        + ");");
+            }
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.IncludeAction n) throws JasperException {
+
+            String flush = n.getTextAttribute("flush");
+            Node.JspAttribute page = n.getPage();
+
+            boolean isFlush = false; // default to false;
+            if ("true".equals(flush))
+                isFlush = true;
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            String pageParam;
+            if (page.isNamedAttribute()) {
+                // If the page for jsp:include was specified via
+                // jsp:attribute, first generate code to evaluate
+                // that body.
+                pageParam =
+                    generateNamedAttributeValue(page.getNamedAttributeNode());
+            } else {
+                pageParam = attributeValue(page, false, String.class);
+            }
+
+            // If any of the params have their values specified by
+            // jsp:attribute, prepare those values first.
+            Node jspBody = findJspBody(n);
+            if (jspBody != null) {
+                prepareParams(jspBody);
+            } else {
+                prepareParams(n);
+            }
+
+            out.printin(
+                "org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "
+                    + pageParam);
+            printParams(n, pageParam, page.isLiteral());
+            out.println(", out, " + isFlush + ");");
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        /**
+         * Scans through all child nodes of the given parent for
+         * <param> subelements.  For each <param> element, if its value
+         * is specified via a Named Attribute (<jsp:attribute>),
+         * generate the code to evaluate those bodies first.
+         * <p>
+         * If parent is null, simply returns.
+         */
+        private void prepareParams(Node parent) throws JasperException {
+            if (parent == null)
+                return;
+
+            Node.Nodes subelements = parent.getBody();
+            if (subelements != null) {
+                for (int i = 0; i < subelements.size(); i++) {
+                    Node n = subelements.getNode(i);
+                    if (n instanceof Node.ParamAction) {
+                        Node.Nodes paramSubElements = n.getBody();
+                        for (int j = 0;
+                            (paramSubElements != null)
+                                && (j < paramSubElements.size());
+                            j++) {
+                            Node m = paramSubElements.getNode(j);
+                            if (m instanceof Node.NamedAttribute) {
+                                generateNamedAttributeValue(
+                                    (Node.NamedAttribute)m);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Finds the <jsp:body> subelement of the given parent node.
+         * If not found, null is returned.
+         */
+        private Node.JspBody findJspBody(Node parent) throws JasperException {
+            Node.JspBody result = null;
+
+            Node.Nodes subelements = parent.getBody();
+            for (int i = 0;
+                (subelements != null) && (i < subelements.size());
+                i++) {
+                Node n = subelements.getNode(i);
+                if (n instanceof Node.JspBody) {
+                    result = (Node.JspBody)n;
+                    break;
+                }
+            }
+
+            return result;
+        }
+
+        public void visit(Node.ForwardAction n) throws JasperException {
+            Node.JspAttribute page = n.getPage();
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            out.printil("if (true) {"); // So that javac won't complain about
+            out.pushIndent(); // codes after "return"
+
+            String pageParam;
+            if (page.isNamedAttribute()) {
+                // If the page for jsp:forward was specified via
+                // jsp:attribute, first generate code to evaluate
+                // that body.
+                pageParam =
+                    generateNamedAttributeValue(page.getNamedAttributeNode());
+            } else {
+                pageParam = attributeValue(page, false, String.class);
+            }
+
+            // If any of the params have their values specified by
+            // jsp:attribute, prepare those values first.
+            Node jspBody = findJspBody(n);
+            if (jspBody != null) {
+                prepareParams(jspBody);
+            } else {
+                prepareParams(n);
+            }
+
+            out.printin("_jspx_page_context.forward(");
+            out.print(pageParam);
+            printParams(n, pageParam, page.isLiteral());
+            out.println(");");
+            if (isTagFile || isFragment) {
+                out.printil("throw new SkipPageException();");
+            } else {
+                out.printil((methodNesting > 0) ? "return true;" : "return;");
+            }
+            out.popIndent();
+            out.printil("}");
+
+            n.setEndJavaLine(out.getJavaLine());
+            // XXX Not sure if we can eliminate dead codes after this.
+        }
+
+        public void visit(Node.GetProperty n) throws JasperException {
+            String name = n.getTextAttribute("name");
+            String property = n.getTextAttribute("property");
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            if (beanInfo.checkVariable(name)) {
+                // Bean is defined using useBean, introspect at compile time
+                Class bean = beanInfo.getBeanType(name);
+                String beanName = JspUtil.getCanonicalName(bean);
+                java.lang.reflect.Method meth =
+                    JspRuntimeLibrary.getReadMethod(bean, property);
+                String methodName = meth.getName();
+                out.printil(
+                    "out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString("
+                        + "((("
+                        + beanName
+                        + ")_jspx_page_context.findAttribute("
+                        + "\""
+                        + name
+                        + "\"))."
+                        + methodName
+                        + "())));");
+            } else {
+                // The object could be a custom action with an associated
+                // VariableInfo entry for this name.
+                // Get the class name and then introspect at runtime.
+                out.printil(
+                    "out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString"
+                        + "(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty"
+                        + "(_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\")));");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.SetProperty n) throws JasperException {
+            String name = n.getTextAttribute("name");
+            String property = n.getTextAttribute("property");
+            String param = n.getTextAttribute("param");
+            Node.JspAttribute value = n.getValue();
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            if ("*".equals(property)) {
+                out.printil(
+                    "org.apache.jasper.runtime.JspRuntimeLibrary.introspect("
+                        + "_jspx_page_context.findAttribute("
+                        + "\""
+                        + name
+                        + "\"), request);");
+            } else if (value == null) {
+                if (param == null)
+                    param = property; // default to same as property
+                out.printil(
+                    "org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\", request.getParameter(\""
+                        + param
+                        + "\"), "
+                        + "request, \""
+                        + param
+                        + "\", false);");
+            } else if (value.isExpression()) {
+                out.printil(
+                    "org.apache.jasper.runtime.JspRuntimeLibrary.handleSetProperty("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\",");
+                out.print(attributeValue(value, false, null));
+                out.println(");");
+            } else if (value.isELInterpreterInput()) {
+                // We've got to resolve the very call to the interpreter
+                // at runtime since we don't know what type to expect
+                // in the general case; we thus can't hard-wire the call
+                // into the generated code.  (XXX We could, however,
+                // optimize the case where the bean is exposed with
+                // <jsp:useBean>, much as the code here does for
+                // getProperty.)
+
+                // The following holds true for the arguments passed to
+                // JspRuntimeLibrary.handleSetPropertyExpression():
+                // - 'pageContext' is a VariableResolver.
+                // - 'this' (either the generated Servlet or the generated tag
+                //   handler for Tag files) is a FunctionMapper.
+                out.printil(
+                    "org.apache.jasper.runtime.JspRuntimeLibrary.handleSetPropertyExpression("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\", "
+                        + quote(value.getValue())
+                        + ", "
+                        + "_jspx_page_context, "
+                        + value.getEL().getMapName()
+                        + ");");
+            } else if (value.isNamedAttribute()) {
+                // If the value for setProperty was specified via
+                // jsp:attribute, first generate code to evaluate
+                // that body.
+                String valueVarName =
+                    generateNamedAttributeValue(value.getNamedAttributeNode());
+                out.printil(
+                    "org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\", "
+                        + valueVarName
+                        + ", null, null, false);");
+            } else {
+                out.printin(
+                    "org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\", ");
+                out.print(attributeValue(value, false, null));
+                out.println(", null, null, false);");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.UseBean n) throws JasperException {
+
+            String name = n.getTextAttribute("id");
+            String scope = n.getTextAttribute("scope");
+            String klass = n.getTextAttribute("class");
+            String type = n.getTextAttribute("type");
+            Node.JspAttribute beanName = n.getBeanName();
+
+            if (type == null) // if unspecified, use class as type of bean
+                type = klass;
+
+            String scopename = "PageContext.PAGE_SCOPE"; // Default to page
+            String lock = "_jspx_page_context";
+
+            if ("request".equals(scope)) {
+                scopename = "PageContext.REQUEST_SCOPE";
+                lock = "request";
+            } else if ("session".equals(scope)) {
+                scopename = "PageContext.SESSION_SCOPE";
+                lock = "session";
+            } else if ("application".equals(scope)) {
+                scopename = "PageContext.APPLICATION_SCOPE";
+                lock = "application";
+            }
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Declare bean
+            out.printin(type);
+            out.print(' ');
+            out.print(name);
+            out.println(" = null;");
+
+            // Lock while getting or creating bean
+            out.printin("synchronized (");
+            out.print(lock);
+            out.println(") {");
+            out.pushIndent();
+
+            // Locate bean from context
+            out.printin(name);
+            out.print(" = (");
+            out.print(type);
+            out.print(") _jspx_page_context.getAttribute(");
+            out.print(quote(name));
+            out.print(", ");
+            out.print(scopename);
+            out.println(");");
+
+            // Create bean
+            /*
+             * Check if bean is alredy there
+             */
+            out.printin("if (");
+            out.print(name);
+            out.println(" == null){");
+            out.pushIndent();
+            if (klass == null && beanName == null) {
+                /*
+                 * If both class name and beanName is not specified, the bean
+                 * must be found locally, otherwise it's an error
+                 */
+                out.printin(
+                    "throw new java.lang.InstantiationException(\"bean ");
+                out.print(name);
+                out.println(" not found within scope\");");
+            } else {
+                /*
+                 * Instantiate the bean if it is not in the specified scope.
+                 */
+                boolean generateNew = false;
+                if (beanName == null) {
+                    try {
+                        Class bean = ctxt.getClassLoader().loadClass(klass);
+                        int modifiers = bean.getModifiers();
+                        if (!Modifier.isPublic(modifiers) ||
+                            Modifier.isInterface(modifiers) ||
+                            Modifier.isAbstract(modifiers)) {
+                            throw new Exception("Invalid bean class modifier");
+                        }
+                        // Check that there is a 0 arg constructor
+                        bean.getConstructor(new Class[] {});
+                        generateNew = true;
+                    } catch (Exception e) {
+                        // Cannot instantiate the specified class
+                        if (ctxt.getOptions().getErrorOnUseBeanInvalidClassAttribute()) {
+                            err.jspError(n, "jsp.error.invalid.bean", klass);
+                        }
+                    }
+                }
+                if (!generateNew) {
+                    String className;
+                    if (beanName != null) {
+                        if (beanName.isNamedAttribute()) {
+                            // If the value for beanName was specified via
+                            // jsp:attribute, first generate code to evaluate
+                            // that body.
+                            className =
+                                generateNamedAttributeValue(
+                                    beanName.getNamedAttributeNode());
+                        } else {
+                            className =
+                                attributeValue(beanName, false, String.class);
+                        }
+                    } else {
+                        // Implies klass is not null
+                        className = quote(klass);
+                    }
+                    out.printil("try {");
+                    out.pushIndent();
+                    out.printin(name);
+                    out.print(" = (");
+                    out.print(type);
+                    out.print(") java.beans.Beans.instantiate(");
+                    out.print("this.getClass().getClassLoader(), ");
+                    out.print(className);
+                    out.println(");");
+                    out.popIndent();
+                    /*
+                     * Note: Beans.instantiate throws ClassNotFoundException
+                     * if the bean class is abstract.
+                     */
+                    out.printil("} catch (ClassNotFoundException exc) {");
+                    out.pushIndent();
+                    out.printil(
+                        "throw new InstantiationException(exc.getMessage());");
+                    out.popIndent();
+                    out.printil("} catch (Exception exc) {");
+                    out.pushIndent();
+                    out.printin("throw new ServletException(");
+                    out.print("\"Cannot create bean of class \" + ");
+                    out.print(className);
+                    out.println(", exc);");
+                    out.popIndent();
+                    out.printil("}"); // close of try
+                } else {
+                    // Implies klass is not null
+                    // Generate codes to instantiate the bean class
+                    out.printin(name);
+                    out.print(" = new ");
+                    out.print(klass);
+                    out.println("();");
+                }
+                /*
+                 * Set attribute for bean in the specified scope
+                 */
+                out.printin("_jspx_page_context.setAttribute(");
+                out.print(quote(name));
+                out.print(", ");
+                out.print(name);
+                out.print(", ");
+                out.print(scopename);
+                out.println(");");
+
+                // Only visit the body when bean is instantiated
+                visitBody(n);
+            }
+            out.popIndent();
+            out.printil("}");
+
+            // End of lock block
+            out.popIndent();
+            out.printil("}");
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        /**
+         * @return a string for the form 'attr = "value"'
+         */
+        private String makeAttr(String attr, String value) {
+            if (value == null)
+                return "";
+
+            return " " + attr + "=\"" + value + '\"';
+        }
+
+        public void visit(Node.PlugIn n) throws JasperException {
+
+            /**
+             * A visitor to handle <jsp:param> in a plugin
+             */
+            class ParamVisitor extends Node.Visitor {
+
+                private boolean ie;
+
+                ParamVisitor(boolean ie) {
+                    this.ie = ie;
+                }
+
+                public void visit(Node.ParamAction n) throws JasperException {
+
+                    String name = n.getTextAttribute("name");
+                    if (name.equalsIgnoreCase("object"))
+                        name = "java_object";
+                    else if (name.equalsIgnoreCase("type"))
+                        name = "java_type";
+
+                    n.setBeginJavaLine(out.getJavaLine());
+                    // XXX - Fixed a bug here - value used to be output
+                    // inline, which is only okay if value is not an EL
+                    // expression.  Also, key/value pairs for the
+                    // embed tag were not being generated correctly.
+                    // Double check that this is now the correct behavior.
+                    if (ie) {
+                        // We want something of the form
+                        // out.println( "<PARAM name=\"blah\"
+                        //     value=\"" + ... + "\">" );
+                        out.printil(
+                            "out.write( \"<PARAM name=\\\""
+                                + escape(name)
+                                + "\\\" value=\\\"\" + "
+                                + attributeValue(
+                                    n.getValue(),
+                                    false,
+                                    String.class)
+                                + " + \"\\\">\" );");
+                        out.printil("out.write(\"\\n\");");
+                    } else {
+                        // We want something of the form
+                        // out.print( " blah=\"" + ... + "\"" );
+                        out.printil(
+                            "out.write( \" "
+                                + escape(name)
+                                + "=\\\"\" + "
+                                + attributeValue(
+                                    n.getValue(),
+                                    false,
+                                    String.class)
+                                + " + \"\\\"\" );");
+                    }
+
+                    n.setEndJavaLine(out.getJavaLine());
+                }
+            }
+
+            String type = n.getTextAttribute("type");
+            String code = n.getTextAttribute("code");
+            String name = n.getTextAttribute("name");
+            Node.JspAttribute height = n.getHeight();
+            Node.JspAttribute width = n.getWidth();
+            String hspace = n.getTextAttribute("hspace");
+            String vspace = n.getTextAttribute("vspace");
+            String align = n.getTextAttribute("align");
+            String iepluginurl = n.getTextAttribute("iepluginurl");
+            String nspluginurl = n.getTextAttribute("nspluginurl");
+            String codebase = n.getTextAttribute("codebase");
+            String archive = n.getTextAttribute("archive");
+            String jreversion = n.getTextAttribute("jreversion");
+
+            String widthStr = null;
+            if (width != null) {
+                if (width.isNamedAttribute()) {
+                    widthStr =
+                        generateNamedAttributeValue(
+                            width.getNamedAttributeNode());
+                } else {
+                    widthStr = attributeValue(width, false, String.class);
+                }
+            }
+
+            String heightStr = null;
+            if (height != null) {
+                if (height.isNamedAttribute()) {
+                    heightStr =
+                        generateNamedAttributeValue(
+                            height.getNamedAttributeNode());
+                } else {
+                    heightStr = attributeValue(height, false, String.class);
+                }
+            }
+
+            if (iepluginurl == null)
+                iepluginurl = Constants.IE_PLUGIN_URL;
+            if (nspluginurl == null)
+                nspluginurl = Constants.NS_PLUGIN_URL;
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // If any of the params have their values specified by
+            // jsp:attribute, prepare those values first.
+            // Look for a params node and prepare its param subelements:
+            Node.JspBody jspBody = findJspBody(n);
+            if (jspBody != null) {
+                Node.Nodes subelements = jspBody.getBody();
+                if (subelements != null) {
+                    for (int i = 0; i < subelements.size(); i++) {
+                        Node m = subelements.getNode(i);
+                        if (m instanceof Node.ParamsAction) {
+                            prepareParams(m);
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // XXX - Fixed a bug here - width and height can be set
+            // dynamically.  Double-check if this generation is correct.
+
+            // IE style plugin
+            // <OBJECT ...>
+            // First compose the runtime output string
+            String s0 = "<OBJECT"
+                    + makeAttr("classid", ctxt.getOptions().getIeClassId())
+                    + makeAttr("name", name);
+
+            String s1 = "";
+            if (width != null) {
+                s1 = " + \" width=\\\"\" + " + widthStr + " + \"\\\"\"";
+            }
+
+            String s2 = "";
+            if (height != null) {
+                s2 = " + \" height=\\\"\" + " + heightStr + " + \"\\\"\"";
+            }
+
+            String s3 = makeAttr("hspace", hspace)
+                    + makeAttr("vspace", vspace)
+                    + makeAttr("align", align)
+                    + makeAttr("codebase", iepluginurl)
+                    + '>';
+
+            // Then print the output string to the java file
+            out.printil(
+                "out.write(" + quote(s0) + s1 + s2 + " + " + quote(s3) + ");");
+            out.printil("out.write(\"\\n\");");
+
+            // <PARAM > for java_code
+            s0 = "<PARAM name=\"java_code\"" + makeAttr("value", code) + '>';
+            out.printil("out.write(" + quote(s0) + ");");
+            out.printil("out.write(\"\\n\");");
+
+            // <PARAM > for java_codebase
+            if (codebase != null) {
+                s0 = "<PARAM name=\"java_codebase\""
+                        + makeAttr("value", codebase)
+                        + '>';
+                out.printil("out.write(" + quote(s0) + ");");
+                out.printil("out.write(\"\\n\");");
+            }
+
+            // <PARAM > for java_archive
+            if (archive != null) {
+                s0 = "<PARAM name=\"java_archive\""
+                        + makeAttr("value", archive)
+                        + '>';
+                out.printil("out.write(" + quote(s0) + ");");
+                out.printil("out.write(\"\\n\");");
+            }
+
+            // <PARAM > for type
+            s0 = "<PARAM name=\"type\""
+                    + makeAttr(
+                        "value",
+                        "application/x-java-"
+                            + type
+                            + ";"
+                            + ((jreversion == null)
+                                ? ""
+                                : "version=" + jreversion))
+                    + '>';
+            out.printil("out.write(" + quote(s0) + ");");
+            out.printil("out.write(\"\\n\");");
+
+            /*
+             * generate a <PARAM> for each <jsp:param> in the plugin body
+             */
+            if (n.getBody() != null)
+                n.getBody().visit(new ParamVisitor(true));
+
+            /*
+             * Netscape style plugin part
+             */
+            out.printil("out.write(" + quote("<COMMENT>") + ");");
+            out.printil("out.write(\"\\n\");");
+            s0 = "<EMBED"
+                    + makeAttr(
+                        "type",
+                        "application/x-java-"
+                            + type
+                            + ";"
+                            + ((jreversion == null)
+                                ? ""
+                                : "version=" + jreversion))
+                    + makeAttr("name", name);
+
+            // s1 and s2 are the same as before.
+
+            s3 = makeAttr("hspace", hspace)
+                    + makeAttr("vspace", vspace)
+                    + makeAttr("align", align)
+                    + makeAttr("pluginspage", nspluginurl)
+                    + makeAttr("java_code", code)
+                    + makeAttr("java_codebase", codebase)
+                    + makeAttr("java_archive", archive);
+            out.printil(
+                "out.write(" + quote(s0) + s1 + s2 + " + " + quote(s3) + ");");
+
+            /*
+             * Generate a 'attr = "value"' for each <jsp:param> in plugin body
+             */
+            if (n.getBody() != null)
+                n.getBody().visit(new ParamVisitor(false));
+
+            out.printil("out.write(" + quote("/>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            out.printil("out.write(" + quote("<NOEMBED>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            /*
+             * Fallback
+             */
+            if (n.getBody() != null) {
+                visitBody(n);
+                out.printil("out.write(\"\\n\");");
+            }
+
+            out.printil("out.write(" + quote("</NOEMBED>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            out.printil("out.write(" + quote("</COMMENT>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            out.printil("out.write(" + quote("</OBJECT>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.NamedAttribute n) throws JasperException {
+            // Don't visit body of this tag - we already did earlier.
+        }
+
+        public void visit(Node.CustomTag n) throws JasperException {
+
+            // Use plugin to generate more efficient code if there is one.
+            if (n.useTagPlugin()) {
+                generateTagPlugin(n);
+                return;
+            }
+
+            TagHandlerInfo handlerInfo = getTagHandlerInfo(n);
+
+            // Create variable names
+            String baseVar =
+                createTagVarName(n.getQName(), n.getPrefix(), n.getLocalName());
+            String tagEvalVar = "_jspx_eval_" + baseVar;
+            String tagHandlerVar = "_jspx_th_" + baseVar;
+            String tagPushBodyCountVar = "_jspx_push_body_count_" + baseVar;
+
+            // If the tag contains no scripting element, generate its codes
+            // to a method.
+            ServletWriter outSave = null;
+            Node.ChildInfo ci = n.getChildInfo();
+            if (ci.isScriptless() && !ci.hasScriptingVars()) {
+                // The tag handler and its body code can reside in a separate
+                // method if it is scriptless and does not have any scripting
+                // variable defined.
+
+                String tagMethod = "_jspx_meth_" + baseVar;
+
+                // Generate a call to this method
+                out.printin("if (");
+                out.print(tagMethod);
+                out.print("(");
+                if (parent != null) {
+                    out.print(parent);
+                    out.print(", ");
+                }
+                out.print("_jspx_page_context");
+                if (pushBodyCountVar != null) {
+                    out.print(", ");
+                    out.print(pushBodyCountVar);
+                }
+                out.println("))");
+                out.pushIndent();
+                out.printil((methodNesting > 0) ? "return true;" : "return;");
+                out.popIndent();
+
+                // Set up new buffer for the method
+                outSave = out;
+                /* For fragments, their bodies will be generated in fragment
+                   helper classes, and the Java line adjustments will be done
+                   there, hence they are set to null here to avoid double
+                   adjustments.
+                */
+                GenBuffer genBuffer =
+                    new GenBuffer(n, n.implementsSimpleTag()? null: n.getBody());
+                methodsBuffered.add(genBuffer);
+                out = genBuffer.getOut();
+
+                methodNesting++;
+                // Generate code for method declaration
+                out.println();
+                out.pushIndent();
+                out.printin("private boolean ");
+                out.print(tagMethod);
+                out.print("(");
+                if (parent != null) {
+                    out.print("javax.servlet.jsp.tagext.JspTag ");
+                    out.print(parent);
+                    out.print(", ");
+                }
+                out.print("PageContext _jspx_page_context");
+                if (pushBodyCountVar != null) {
+                    out.print(", int[] ");
+                    out.print(pushBodyCountVar);
+                }
+                out.println(")");
+                out.printil("        throws Throwable {");
+                out.pushIndent();
+
+                // Initilaize local variables used in this method.
+                if (! isTagFile) {
+                    out.printil("PageContext pageContext = _jspx_page_context;");
+                }
+                out.printil("JspWriter out = _jspx_page_context.getOut();");
+                generateLocalVariables(out, n);
+            }
+
+            if (n.implementsSimpleTag()) {
+                generateCustomDoTag(n, handlerInfo, tagHandlerVar);
+            } else {
+                /*
+                 * Classic tag handler: Generate code for start element, body,
+                 * and end element
+                 */
+                generateCustomStart(
+                    n,
+                    handlerInfo,
+                    tagHandlerVar,
+                    tagEvalVar,
+                    tagPushBodyCountVar);
+
+                // visit body
+                String tmpParent = parent;
+                parent = tagHandlerVar;
+                boolean isSimpleTagParentSave = isSimpleTagParent;
+                isSimpleTagParent = false;
+                String tmpPushBodyCountVar = null;
+                if (n.implementsTryCatchFinally()) {
+                    tmpPushBodyCountVar = pushBodyCountVar;
+                    pushBodyCountVar = tagPushBodyCountVar;
+                }
+                boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
+                isSimpleTagHandler = false;
+
+                visitBody(n);
+
+                parent = tmpParent;
+                isSimpleTagParent = isSimpleTagParentSave;
+                if (n.implementsTryCatchFinally()) {
+                    pushBodyCountVar = tmpPushBodyCountVar;
+                }
+                isSimpleTagHandler = tmpIsSimpleTagHandler;
+
+                generateCustomEnd(
+                    n,
+                    tagHandlerVar,
+                    tagEvalVar,
+                    tagPushBodyCountVar);
+            }
+
+            if (ci.isScriptless() && !ci.hasScriptingVars()) {
+                // Generate end of method
+                if (methodNesting > 0) {
+                    out.printil("return false;");
+                }
+                out.popIndent();
+                out.printil("}");
+                out.popIndent();
+
+                methodNesting--;
+
+                // restore previous writer
+                out = outSave;
+            }
+        }
+
+        private static final String SINGLE_QUOTE = "'";
+        private static final String DOUBLE_QUOTE = "\\\"";
+
+        public void visit(Node.UninterpretedTag n) throws JasperException {
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            /*
+             * Write begin tag
+             */
+            out.printin("out.write(\"<");
+            out.print(n.getQName());
+
+            Attributes attrs = n.getNonTaglibXmlnsAttributes();
+            int attrsLen = (attrs == null) ? 0 : attrs.getLength();
+            for (int i = 0; i < attrsLen; i++) {
+                out.print(" ");
+                out.print(attrs.getQName(i));
+                out.print("=");
+                String quote = DOUBLE_QUOTE;
+                String value = attrs.getValue(i);
+                if (value.indexOf('"') != -1) {
+                    quote = SINGLE_QUOTE;
+                }
+                out.print(quote);
+                out.print(value);
+                out.print(quote);
+            }
+
+            attrs = n.getAttributes();
+            attrsLen = (attrs == null) ? 0 : attrs.getLength();
+            Node.JspAttribute[] jspAttrs = n.getJspAttributes();
+            for (int i = 0; i < attrsLen; i++) {
+                out.print(" ");
+                out.print(attrs.getQName(i));
+                out.print("=");
+                if (jspAttrs[i].isELInterpreterInput()) {
+                    out.print("\\\"\" + ");
+                    out.print(attributeValue(jspAttrs[i], false, String.class));
+                    out.print(" + \"\\\"");
+                } else {
+                    String quote = DOUBLE_QUOTE;
+                    String value = attrs.getValue(i);
+                    if (value.indexOf('"') != -1) {
+                        quote = SINGLE_QUOTE;
+                    }
+                    out.print(quote);
+                    out.print(value);
+                    out.print(quote);
+                }
+            }
+
+            if (n.getBody() != null) {
+                out.println(">\");");
+
+                // Visit tag body
+                visitBody(n);
+
+                /*
+                 * Write end tag
+                 */
+                out.printin("out.write(\"</");
+                out.print(n.getQName());
+                out.println(">\");");
+            } else {
+                out.println("/>\");");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.JspElement n) throws JasperException {
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Compute attribute value string for XML-style and named
+            // attributes
+            Hashtable map = new Hashtable();
+            Node.JspAttribute[] attrs = n.getJspAttributes();
+            for (int i = 0; attrs != null && i < attrs.length; i++) {
+                String attrStr = null;
+                if (attrs[i].isNamedAttribute()) {
+                    attrStr =
+                        generateNamedAttributeValue(
+                            attrs[i].getNamedAttributeNode());
+                } else {
+                    attrStr = attributeValue(attrs[i], false, Object.class);
+                }
+                String s =
+                    " + \" "
+                        + attrs[i].getName()
+                        + "=\\\"\" + "
+                        + attrStr
+                        + " + \"\\\"\"";
+                map.put(attrs[i].getName(), s);
+            }
+
+            // Write begin tag, using XML-style 'name' attribute as the
+            // element name
+            String elemName =
+                attributeValue(n.getNameAttribute(), false, String.class);
+            out.printin("out.write(\"<\"");
+            out.print(" + " + elemName);
+
+            // Write remaining attributes
+            Enumeration enumeration = map.keys();
+            while (enumeration.hasMoreElements()) {
+                String attrName = (String)enumeration.nextElement();
+                out.print((String)map.get(attrName));
+            }
+
+            // Does the <jsp:element> have nested tags other than
+            // <jsp:attribute>
+            boolean hasBody = false;
+            Node.Nodes subelements = n.getBody();
+            if (subelements != null) {
+                for (int i = 0; i < subelements.size(); i++) {
+                    Node subelem = subelements.getNode(i);
+                    if (!(subelem instanceof Node.NamedAttribute)) {
+                        hasBody = true;
+                        break;
+                    }
+                }
+            }
+            if (hasBody) {
+                out.println(" + \">\");");
+
+                // Smap should not include the body
+                n.setEndJavaLine(out.getJavaLine());
+
+                // Visit tag body
+                visitBody(n);
+
+                // Write end tag
+                out.printin("out.write(\"</\"");
+                out.print(" + " + elemName);
+                out.println(" + \">\");");
+            } else {
+                out.println(" + \"/>\");");
+                n.setEndJavaLine(out.getJavaLine());
+            }
+        }
+
+        public void visit(Node.TemplateText n) throws JasperException {
+
+            String text = n.getText();
+
+            int textSize = text.length();
+            if (textSize == 0) {
+                return;
+            }
+
+            if (textSize <= 3) {
+               // Special case small text strings
+               n.setBeginJavaLine(out.getJavaLine());
+               int lineInc = 0;
+               for (int i = 0; i < textSize; i++) {
+                   char ch = text.charAt(i);
+                   out.printil("out.write(" + quote(ch) + ");");
+                   if (i > 0) {
+                       n.addSmap(lineInc);
+                   }
+                   if (ch == '\n') {
+                       lineInc++;
+                   }
+               }
+               n.setEndJavaLine(out.getJavaLine());
+               return;
+           }
+
+            if (ctxt.getOptions().genStringAsCharArray()) {
+               // Generate Strings as char arrays, for performance
+                ServletWriter caOut;
+                if (charArrayBuffer == null) {
+                    charArrayBuffer = new GenBuffer();
+                    caOut = charArrayBuffer.getOut();
+                    caOut.pushIndent();
+                    textMap = new HashMap();
+                } else {
+                    caOut = charArrayBuffer.getOut();
+                }
+                String charArrayName = (String) textMap.get(text);
+                if (charArrayName == null) {
+                    charArrayName = "_jspx_char_array_" + charArrayCount++;
+                    textMap.put(text, charArrayName);
+                    caOut.printin("static char[] ");
+                    caOut.print(charArrayName);
+                    caOut.print(" = ");
+                    caOut.print(quote(text));
+                    caOut.println(".toCharArray();");
+                }
+
+                n.setBeginJavaLine(out.getJavaLine());
+                out.printil("out.write(" + charArrayName + ");");
+                n.setEndJavaLine(out.getJavaLine());
+                return;
+            }
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            out.printin();
+            StringBuffer sb = new StringBuffer("out.write(\"");
+            int initLength = sb.length();
+            int count = JspUtil.CHUNKSIZE;
+            int srcLine = 0;    // relative to starting srouce line
+            for (int i = 0; i < text.length(); i++) {
+                char ch = text.charAt(i);
+                --count;
+                switch (ch) {
+                    case '"' :
+                        sb.append('\\').append('\"');
+                        break;
+                    case '\\' :
+                        sb.append('\\').append('\\');
+                        break;
+                    case '\r' :
+                        sb.append('\\').append('r');
+                        break;
+                    case '\n' :
+                        sb.append('\\').append('n');
+                        srcLine++;
+
+                        if (breakAtLF || count < 0) {
+                            // Generate an out.write() when see a '\n' in template
+                            sb.append("\");");
+                            out.println(sb.toString());
+                            if (i < text.length() - 1) {
+                                out.printin();
+                            }
+                            sb.setLength(initLength);
+                            count = JspUtil.CHUNKSIZE;
+                        }
+                        // add a Smap for this line
+                        n.addSmap(srcLine);
+                        break;
+                    case '\t' : // Not sure we need this
+                        sb.append('\\').append('t');
+                        break;
+                    default :
+                        sb.append(ch);
+                }
+            }
+
+            if (sb.length() > initLength) {
+                sb.append("\");");
+                out.println(sb.toString());
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.JspBody n) throws JasperException {
+            if (n.getBody() != null) {
+                if (isSimpleTagHandler) {
+                    out.printin(simpleTagHandlerVar);
+                    out.print(".setJspBody(");
+                    generateJspFragment(n, simpleTagHandlerVar);
+                    out.println(");");
+                } else {
+                    visitBody(n);
+                }
+            }
+        }
+
+        public void visit(Node.InvokeAction n) throws JasperException {
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Copy virtual page scope of tag file to page scope of invoking
+            // page
+            out.printil(
+                "((org.apache.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();");
+            String varReaderAttr = n.getTextAttribute("varReader");
+            String varAttr = n.getTextAttribute("var");
+            if (varReaderAttr != null || varAttr != null) {
+                out.printil("_jspx_sout = new java.io.StringWriter();");
+            } else {
+                out.printil("_jspx_sout = null;");
+            }
+
+            // Invoke fragment, unless fragment is null
+            out.printin("if (");
+            out.print(toGetterMethod(n.getTextAttribute("fragment")));
+            out.println(" != null) {");
+            out.pushIndent();
+            out.printin(toGetterMethod(n.getTextAttribute("fragment")));
+            out.println(".invoke(_jspx_sout);");
+            out.popIndent();
+            out.printil("}");
+
+            // Store varReader in appropriate scope
+            if (varReaderAttr != null || varAttr != null) {
+                String scopeName = n.getTextAttribute("scope");
+                out.printin("_jspx_page_context.setAttribute(");
+                if (varReaderAttr != null) {
+                    out.print(quote(varReaderAttr));
+                    out.print(
+                        ", new java.io.StringReader(_jspx_sout.toString())");
+                } else {
+                    out.print(quote(varAttr));
+                    out.print(", _jspx_sout.toString()");
+                }
+                if (scopeName != null) {
+                    out.print(", ");
+                    out.print(getScopeConstant(scopeName));
+                }
+                out.println(");");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.DoBodyAction n) throws JasperException {
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Copy virtual page scope of tag file to page scope of invoking
+            // page
+            out.printil(
+                "((org.apache.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();");
+
+            // Invoke body
+            String varReaderAttr = n.getTextAttribute("varReader");
+            String varAttr = n.getTextAttribute("var");
+            if (varReaderAttr != null || varAttr != null) {
+                out.printil("_jspx_sout = new java.io.StringWriter();");
+            } else {
+                out.printil("_jspx_sout = null;");
+            }
+            out.printil("if (getJspBody() != null)");
+            out.pushIndent();
+            out.printil("getJspBody().invoke(_jspx_sout);");
+            out.popIndent();
+
+            // Store varReader in appropriate scope
+            if (varReaderAttr != null || varAttr != null) {
+                String scopeName = n.getTextAttribute("scope");
+                out.printin("_jspx_page_context.setAttribute(");
+                if (varReaderAttr != null) {
+                    out.print(quote(varReaderAttr));
+                    out.print(
+                        ", new java.io.StringReader(_jspx_sout.toString())");
+                } else {
+                    out.print(quote(varAttr));
+                    out.print(", _jspx_sout.toString()");
+                }
+                if (scopeName != null) {
+                    out.print(", ");
+                    out.print(getScopeConstant(scopeName));
+                }
+                out.println(");");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.AttributeGenerator n) throws JasperException {
+            Node.CustomTag tag = n.getTag();
+            Node.JspAttribute[] attrs = tag.getJspAttributes();
+            for (int i = 0; attrs != null && i < attrs.length; i++) {
+                if (attrs[i].getName().equals(n.getName())) {
+                    out.print(
+                        evaluateAttribute(
+                            getTagHandlerInfo(tag),
+                            attrs[i],
+                            tag,
+                            null));
+                    break;
+                }
+            }
+        }
+
+        private TagHandlerInfo getTagHandlerInfo(Node.CustomTag n)
+            throws JasperException {
+            Hashtable handlerInfosByShortName =
+                (Hashtable)handlerInfos.get(n.getPrefix());
+            if (handlerInfosByShortName == null) {
+                handlerInfosByShortName = new Hashtable();
+                handlerInfos.put(n.getPrefix(), handlerInfosByShortName);
+            }
+            TagHandlerInfo handlerInfo =
+                (TagHandlerInfo)handlerInfosByShortName.get(n.getLocalName());
+            if (handlerInfo == null) {
+                handlerInfo =
+                    new TagHandlerInfo(n, n.getTagHandlerClass(), err);
+                handlerInfosByShortName.put(n.getLocalName(), handlerInfo);
+            }
+            return handlerInfo;
+        }
+
+        private void generateTagPlugin(Node.CustomTag n)
+            throws JasperException {
+            if (n.getAtSTag() != null) {
+                n.getAtSTag().visit(this);
+            }
+            visitBody(n);
+            if (n.getAtETag() != null) {
+                n.getAtETag().visit(this);
+            }
+        }
+
+        private void generateCustomStart(
+            Node.CustomTag n,
+            TagHandlerInfo handlerInfo,
+            String tagHandlerVar,
+            String tagEvalVar,
+            String tagPushBodyCountVar)
+            throws JasperException {
+
+            Class tagHandlerClass = handlerInfo.getTagHandlerClass();
+
+            out.printin("//  ");
+            out.println(n.getQName());
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Declare AT_BEGIN scripting variables
+            declareScriptingVars(n, VariableInfo.AT_BEGIN);
+            saveScriptingVars(n, VariableInfo.AT_BEGIN);
+
+            String tagHandlerClassName =
+                JspUtil.getCanonicalName(tagHandlerClass);
+            out.printin(tagHandlerClassName);
+            out.print(" ");
+            out.print(tagHandlerVar);
+            out.print(" = ");
+            if (isPoolingEnabled) {
+                out.print("(");
+                out.print(tagHandlerClassName);
+                out.print(") ");
+                out.print(n.getTagHandlerPoolName());
+                out.print(".get(");
+                out.print(tagHandlerClassName);
+                out.println(".class);");
+            } else {
+                out.print("new ");
+                out.print(tagHandlerClassName);
+                out.println("();");
+            }
+
+            generateSetters(n, tagHandlerVar, handlerInfo, false);
+
+            if (n.implementsTryCatchFinally()) {
+                out.printin("int[] ");
+                out.print(tagPushBodyCountVar);
+                out.println(" = new int[] { 0 };");
+                out.printil("try {");
+                out.pushIndent();
+            }
+            out.printin("int ");
+            out.print(tagEvalVar);
+            out.print(" = ");
+            out.print(tagHandlerVar);
+            out.println(".doStartTag();");
+
+            if (!n.implementsBodyTag()) {
+                // Synchronize AT_BEGIN scripting variables
+                syncScriptingVars(n, VariableInfo.AT_BEGIN);
+            }
+
+            if (!n.hasEmptyBody()) {
+                out.printin("if (");
+                out.print(tagEvalVar);
+                out.println(" != javax.servlet.jsp.tagext.Tag.SKIP_BODY) {");
+                out.pushIndent();
+
+                // Declare NESTED scripting variables
+                declareScriptingVars(n, VariableInfo.NESTED);
+                saveScriptingVars(n, VariableInfo.NESTED);
+
+                if (n.implementsBodyTag()) {
+                    out.printin("if (");
+                    out.print(tagEvalVar);
+                    out.println(
+                        " != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE) {");
+                    // Assume EVAL_BODY_BUFFERED
+                    out.pushIndent();
+                    out.printil("out = _jspx_page_context.pushBody();");
+                    if (n.implementsTryCatchFinally()) {
+                        out.printin(tagPushBodyCountVar);
+                        out.println("[0]++;");
+                    } else if (pushBodyCountVar != null) {
+                        out.printin(pushBodyCountVar);
+                        out.println("[0]++;");
+                    }
+                    out.printin(tagHandlerVar);
+                    out.println(
+                        ".setBodyContent((javax.servlet.jsp.tagext.BodyContent) out);");
+                    out.printin(tagHandlerVar);
+                    out.println(".doInitBody();");
+
+                    out.popIndent();
+                    out.printil("}");
+
+                    // Synchronize AT_BEGIN and NESTED scripting variables
+                    syncScriptingVars(n, VariableInfo.AT_BEGIN);
+                    syncScriptingVars(n, VariableInfo.NESTED);
+
+                } else {
+                    // Synchronize NESTED scripting variables
+                    syncScriptingVars(n, VariableInfo.NESTED);
+                }
+
+                if (n.implementsIterationTag()) {
+                    out.printil("do {");
+                    out.pushIndent();
+                }
+            }
+            // Map the Java lines that handles start of custom tags to the
+            // JSP line for this tag
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        private void generateCustomEnd(
+            Node.CustomTag n,
+            String tagHandlerVar,
+            String tagEvalVar,
+            String tagPushBodyCountVar) {
+
+            if (!n.hasEmptyBody()) {
+                if (n.implementsIterationTag()) {
+                    out.printin("int evalDoAfterBody = ");
+                    out.print(tagHandlerVar);
+                    out.println(".doAfterBody();");
+
+                    // Synchronize AT_BEGIN and NESTED scripting variables
+                    syncScriptingVars(n, VariableInfo.AT_BEGIN);
+                    syncScriptingVars(n, VariableInfo.NESTED);
+
+                    out.printil(
+                        "if (evalDoAfterBody != javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_AGAIN)");
+                    out.pushIndent();
+                    out.printil("break;");
+                    out.popIndent();
+
+                    out.popIndent();
+                    out.printil("} while (true);");
+                }
+
+                restoreScriptingVars(n, VariableInfo.NESTED);
+
+                if (n.implementsBodyTag()) {
+                    out.printin("if (");
+                    out.print(tagEvalVar);
+                    out.println(
+                        " != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE)");
+                    out.pushIndent();
+                    out.printil("out = _jspx_page_context.popBody();");
+                    if (n.implementsTryCatchFinally()) {
+                        out.printin(tagPushBodyCountVar);
+                        out.println("[0]--;");
+                    } else if (pushBodyCountVar != null) {
+                        out.printin(pushBodyCountVar);
+                        out.println("[0]--;");
+                    }
+                    out.popIndent();
+                }
+
+                out.popIndent(); // EVAL_BODY
+                out.printil("}");
+            }
+
+            out.printin("if (");
+            out.print(tagHandlerVar);
+            out.println(
+                ".doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {");
+            out.pushIndent();
+            if(!n.implementsTryCatchFinally()) {
+                if(isPoolingEnabled) {
+                    out.printin(n.getTagHandlerPoolName());
+                    out.print(".reuse(");
+                    out.print(tagHandlerVar);
+                    out.println(");");
+                } else {
+                    out.printin(tagHandlerVar);
+                    out.println(".release();");
+                }
+            }
+            if (isTagFile || isFragment) {
+                out.printil("throw new SkipPageException();");
+            } else {
+                out.printil((methodNesting > 0) ? "return true;" : "return;");
+            }
+            out.popIndent();
+            out.printil("}");
+            // Synchronize AT_BEGIN scripting variables
+            syncScriptingVars(n, VariableInfo.AT_BEGIN);
+
+            // TryCatchFinally
+            if (n.implementsTryCatchFinally()) {
+                out.popIndent(); // try
+                out.printil("} catch (Throwable _jspx_exception) {");
+                out.pushIndent();
+
+                out.printin("while (");
+                out.print(tagPushBodyCountVar);
+                out.println("[0]-- > 0)");
+                out.pushIndent();
+                out.printil("out = _jspx_page_context.popBody();");
+                out.popIndent();
+
+                out.printin(tagHandlerVar);
+                out.println(".doCatch(_jspx_exception);");
+                out.popIndent();
+                out.printil("} finally {");
+                out.pushIndent();
+                out.printin(tagHandlerVar);
+                out.println(".doFinally();");
+            }
+
+            if (isPoolingEnabled) {
+                out.printin(n.getTagHandlerPoolName());
+                out.print(".reuse(");
+                out.print(tagHandlerVar);
+                out.println(");");
+            } else {
+                out.printin(tagHandlerVar);
+                out.println(".release();");
+            }
+
+            if (n.implementsTryCatchFinally()) {
+                out.popIndent();
+                out.printil("}");
+            }
+
+            // Declare and synchronize AT_END scripting variables (must do this
+            // outside the try/catch/finally block)
+            declareScriptingVars(n, VariableInfo.AT_END);
+            syncScriptingVars(n, VariableInfo.AT_END);
+
+            restoreScriptingVars(n, VariableInfo.AT_BEGIN);
+        }
+
+        private void generateCustomDoTag(
+            Node.CustomTag n,
+            TagHandlerInfo handlerInfo,
+            String tagHandlerVar)
+            throws JasperException {
+
+            Class tagHandlerClass = handlerInfo.getTagHandlerClass();
+
+            n.setBeginJavaLine(out.getJavaLine());
+            out.printin("//  ");
+            out.println(n.getQName());
+
+            // Declare AT_BEGIN scripting variables
+            declareScriptingVars(n, VariableInfo.AT_BEGIN);
+            saveScriptingVars(n, VariableInfo.AT_BEGIN);
+
+            String tagHandlerClassName =
+                JspUtil.getCanonicalName(tagHandlerClass);
+            out.printin(tagHandlerClassName);
+            out.print(" ");
+            out.print(tagHandlerVar);
+            out.print(" = ");
+            out.print("new ");
+            out.print(tagHandlerClassName);
+            out.println("();");
+
+            generateSetters(n, tagHandlerVar, handlerInfo, true);
+
+            // Set the body
+            if (findJspBody(n) == null) {
+                /*
+                 * Encapsulate body of custom tag invocation in JspFragment
+                 * and pass it to tag handler's setJspBody(), unless tag body
+                 * is empty
+                 */
+                if (!n.hasEmptyBody()) {
+                    out.printin(tagHandlerVar);
+                    out.print(".setJspBody(");
+                    generateJspFragment(n, tagHandlerVar);
+                    out.println(");");
+                }
+            } else {
+                /*
+                 * Body of tag is the body of the <jsp:body> element.
+                 * The visit method for that element is going to encapsulate
+                 * that element's body in a JspFragment and pass it to
+                 * the tag handler's setJspBody()
+                 */
+                String tmpTagHandlerVar = simpleTagHandlerVar;
+                simpleTagHandlerVar = tagHandlerVar;
+                boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
+                isSimpleTagHandler = true;
+                visitBody(n);
+                simpleTagHandlerVar = tmpTagHandlerVar;
+                isSimpleTagHandler = tmpIsSimpleTagHandler;
+            }
+
+            out.printin(tagHandlerVar);
+            out.println(".doTag();");
+
+            restoreScriptingVars(n, VariableInfo.AT_BEGIN);
+
+            // Synchronize AT_BEGIN scripting variables
+            syncScriptingVars(n, VariableInfo.AT_BEGIN);
+
+            // Declare and synchronize AT_END scripting variables
+            declareScriptingVars(n, VariableInfo.AT_END);
+            syncScriptingVars(n, VariableInfo.AT_END);
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        private void declareScriptingVars(Node.CustomTag n, int scope) {
+
+            Vector vec = n.getScriptingVars(scope);
+            if (vec != null) {
+                for (int i = 0; i < vec.size(); i++) {
+                    Object elem = vec.elementAt(i);
+                    if (elem instanceof VariableInfo) {
+                        VariableInfo varInfo = (VariableInfo)elem;
+                        if (varInfo.getDeclare()) {
+                            out.printin(varInfo.getClassName());
+                            out.print(" ");
+                            out.print(varInfo.getVarName());
+                            out.println(" = null;");
+                        }
+                    } else {
+                        TagVariableInfo tagVarInfo = (TagVariableInfo)elem;
+                        if (tagVarInfo.getDeclare()) {
+                            String varName = tagVarInfo.getNameGiven();
+                            if (varName == null) {
+                                varName =
+                                    n.getTagData().getAttributeString(
+                                        tagVarInfo.getNameFromAttribute());
+                            } else if (
+                                tagVarInfo.getNameFromAttribute() != null) {
+                                // alias
+                                continue;
+                            }
+                            out.printin(tagVarInfo.getClassName());
+                            out.print(" ");
+                            out.print(varName);
+                            out.println(" = null;");
+                        }
+                    }
+                }
+            }
+        }
+
+        /*
+         * This method is called as part of the custom tag's start element.
+         *
+         * If the given custom tag has a custom nesting level greater than 0,
+         * save the current values of its scripting variables to
+         * temporary variables, so those values may be restored in the tag's
+         * end element. This way, the scripting variables may be synchronized
+         * by the given tag without affecting their original values.
+         */
+        private void saveScriptingVars(Node.CustomTag n, int scope) {
+            if (n.getCustomNestingLevel() == 0) {
+                return;
+            }
+
+            TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+            VariableInfo[] varInfos = n.getVariableInfos();
+            if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
+                return;
+            }
+
+            if (varInfos.length > 0) {
+                for (int i = 0; i < varInfos.length; i++) {
+                    if (varInfos[i].getScope() != scope)
+                        continue;
+                    // If the scripting variable has been declared, skip codes
+                    // for saving and restoring it.
+                    if (n.getScriptingVars(scope).contains(varInfos[i]))
+                        continue;
+                    String varName = varInfos[i].getVarName();
+                    String tmpVarName =
+                        "_jspx_" + varName + "_" + n.getCustomNestingLevel();
+                    out.printin(tmpVarName);
+                    out.print(" = ");
+                    out.print(varName);
+                    out.println(";");
+                }
+            } else {
+                for (int i = 0; i < tagVarInfos.length; i++) {
+                    if (tagVarInfos[i].getScope() != scope)
+                        continue;
+                    // If the scripting variable has been declared, skip codes
+                    // for saving and restoring it.
+                    if (n.getScriptingVars(scope).contains(tagVarInfos[i]))
+                        continue;
+                    String varName = tagVarInfos[i].getNameGiven();
+                    if (varName == null) {
+                        varName =
+                            n.getTagData().getAttributeString(
+                                tagVarInfos[i].getNameFromAttribute());
+                    } else if (tagVarInfos[i].getNameFromAttribute() != null) {
+                        // alias
+                        continue;
+                    }
+                    String tmpVarName =
+                        "_jspx_" + varName + "_" + n.getCustomNestingLevel();
+                    out.printin(tmpVarName);
+                    out.print(" = ");
+                    out.print(varName);
+                    out.println(";");
+                }
+            }
+        }
+
+        /*
+         * This method is called as part of the custom tag's end element.
+         *
+         * If the given custom tag has a custom nesting level greater than 0,
+         * restore its scripting variables to their original values that were
+         * saved in the tag's start element.
+         */
+        private void restoreScriptingVars(Node.CustomTag n, int scope) {
+            if (n.getCustomNestingLevel() == 0) {
+                return;
+            }
+
+            TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+            VariableInfo[] varInfos = n.getVariableInfos();
+            if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
+                return;
+            }
+
+            if (varInfos.length > 0) {
+                for (int i = 0; i < varInfos.length; i++) {
+                    if (varInfos[i].getScope() != scope)
+                        continue;
+                    // If the scripting variable has been declared, skip codes
+                    // for saving and restoring it.
+                    if (n.getScriptingVars(scope).contains(varInfos[i]))
+                        continue;
+                    String varName = varInfos[i].getVarName();
+                    String tmpVarName =
+                        "_jspx_" + varName + "_" + n.getCustomNestingLevel();
+                    out.printin(varName);
+                    out.print(" = ");
+                    out.print(tmpVarName);
+                    out.println(";");
+                }
+            } else {
+                for (int i = 0; i < tagVarInfos.length; i++) {
+                    if (tagVarInfos[i].getScope() != scope)
+                        continue;
+                    // If the scripting variable has been declared, skip codes
+                    // for saving and restoring it.
+                    if (n.getScriptingVars(scope).contains(tagVarInfos[i]))
+                        continue;
+                    String varName = tagVarInfos[i].getNameGiven();
+                    if (varName == null) {
+                        varName =
+                            n.getTagData().getAttributeString(
+                                tagVarInfos[i].getNameFromAttribute());
+                    } else if (tagVarInfos[i].getNameFromAttribute() != null) {
+                        // alias
+                        continue;
+                    }
+                    String tmpVarName =
+                        "_jspx_" + varName + "_" + n.getCustomNestingLevel();
+                    out.printin(varName);
+                    out.print(" = ");
+                    out.print(tmpVarName);
+                    out.println(";");
+                }
+            }
+        }
+
+        /*
+         * Synchronizes the scripting variables of the given custom tag for
+         * the given scope.
+         */
+        private void syncScriptingVars(Node.CustomTag n, int scope) {
+            TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+            VariableInfo[] varInfos = n.getVariableInfos();
+
+            if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
+                return;
+            }
+
+            if (varInfos.length > 0) {
+                for (int i = 0; i < varInfos.length; i++) {
+                    if (varInfos[i].getScope() == scope) {
+                        out.printin(varInfos[i].getVarName());
+                        out.print(" = (");
+                        out.print(varInfos[i].getClassName());
+                        out.print(") _jspx_page_context.findAttribute(");
+                        out.print(quote(varInfos[i].getVarName()));
+                        out.println(");");
+                    }
+                }
+            } else {
+                for (int i = 0; i < tagVarInfos.length; i++) {
+                    if (tagVarInfos[i].getScope() == scope) {
+                        String name = tagVarInfos[i].getNameGiven();
+                        if (name == null) {
+                            name =
+                                n.getTagData().getAttributeString(
+                                    tagVarInfos[i].getNameFromAttribute());
+                        } else if (
+                            tagVarInfos[i].getNameFromAttribute() != null) {
+                            // alias
+                            continue;
+                        }
+                        out.printin(name);
+                        out.print(" = (");
+                        out.print(tagVarInfos[i].getClassName());
+                        out.print(") _jspx_page_context.findAttribute(");
+                        out.print(quote(name));
+                        out.println(");");
+                    }
+                }
+            }
+        }
+
+        /*
+         * Creates a tag variable name by concatenating the given prefix and
+         * shortName and endcoded to make the resultant string a valid Java
+         * Identifier.
+         */
+        private String createTagVarName(
+            String fullName,
+            String prefix,
+            String shortName) {
+
+            String varName;
+            synchronized (tagVarNumbers) {
+                varName = prefix + "_" + shortName + "_";
+                if (tagVarNumbers.get(fullName) != null) {
+                    Integer i = (Integer)tagVarNumbers.get(fullName);
+                    varName = varName + i.intValue();
+                    tagVarNumbers.put(fullName, new Integer(i.intValue() + 1));
+                } else {
+                    tagVarNumbers.put(fullName, new Integer(1));
+                    varName = varName + "0";
+                }
+            }
+            return JspUtil.makeXmlJavaIdentifier(varName);
+        }
+
+        private String evaluateAttribute(
+            TagHandlerInfo handlerInfo,
+            Node.JspAttribute attr,
+            Node.CustomTag n,
+            String tagHandlerVar)
+            throws JasperException {
+
+            String attrValue = attr.getValue();
+            if (attrValue == null) {
+                if (attr.isNamedAttribute()) {
+                    if (n.checkIfAttributeIsJspFragment(attr.getName())) {
+                        // XXX - no need to generate temporary variable here
+                        attrValue =
+                            generateNamedAttributeJspFragment(
+                                attr.getNamedAttributeNode(),
+                                tagHandlerVar);
+                    } else {
+                        attrValue =
+                            generateNamedAttributeValue(
+                                attr.getNamedAttributeNode());
+                    }
+                } else {
+                    return null;
+                }
+            }
+
+            String localName = attr.getLocalName();
+
+            Method m = null;
+            Class[] c = null;
+            if (attr.isDynamic()) {
+                c = OBJECT_CLASS;
+            } else {
+                m = handlerInfo.getSetterMethod(localName);
+                if (m == null) {
+                    err.jspError(
+                        n,
+                        "jsp.error.unable.to_find_method",
+                        attr.getName());
+                }
+                c = m.getParameterTypes();
+                // XXX assert(c.length > 0)
+            }
+
+            if (attr.isExpression()) {
+                // Do nothing
+            } else if (attr.isNamedAttribute()) {
+                if (!n.checkIfAttributeIsJspFragment(attr.getName())
+                    && !attr.isDynamic()) {
+                    attrValue =
+                        convertString(
+                            c[0],
+                            attrValue,
+                            localName,
+                            handlerInfo.getPropertyEditorClass(localName),
+                            true);
+                }
+            } else if (attr.isELInterpreterInput()) {
+                // run attrValue through the expression interpreter
+                boolean replaceESC = attrValue.indexOf(Constants.ESC) > 0;
+                attrValue =
+                    JspUtil.interpreterCall(
+                        this.isTagFile,
+                        attrValue,
+                        c[0],
+                        attr.getEL().getMapName(),
+                        false);
+                // XXX hack: Replace ESC with '$'
+                if (replaceESC) {
+                    attrValue =
+                        "("
+                            + attrValue
+                            + ").replace("
+                            + Constants.ESCStr
+                            + ", '$')";
+                }
+            } else {
+                attrValue =
+                    convertString(
+                        c[0],
+                        attrValue,
+                        localName,
+                        handlerInfo.getPropertyEditorClass(localName),
+                        false);
+            }
+            return attrValue;
+        }
+
+        /**
+         * Generate code to create a map for the alias variables
+         * @return the name of the map
+         */
+        private String generateAliasMap(Node.CustomTag n, String tagHandlerVar)
+            throws JasperException {
+
+            TagVariableInfo[] tagVars = n.getTagVariableInfos();
+            String aliasMapVar = null;
+
+            boolean aliasSeen = false;
+            for (int i = 0; i < tagVars.length; i++) {
+
+                String nameFrom = tagVars[i].getNameFromAttribute();
+                if (nameFrom != null) {
+                    String aliasedName = n.getAttributeValue(nameFrom);
+                    if (aliasedName == null)
+                        continue;
+
+                    if (!aliasSeen) {
+                        out.printin("java.util.HashMap ");
+                        aliasMapVar = tagHandlerVar + "_aliasMap";
+                        out.print(aliasMapVar);
+                        out.println(" = new java.util.HashMap();");
+                        aliasSeen = true;
+                    }
+                    out.printin(aliasMapVar);
+                    out.print(".put(");
+                    out.print(quote(tagVars[i].getNameGiven()));
+                    out.print(", ");
+                    out.print(quote(aliasedName));
+                    out.println(");");
+                }
+            }
+            return aliasMapVar;
+        }
+
+        private void generateSetters(
+            Node.CustomTag n,
+            String tagHandlerVar,
+            TagHandlerInfo handlerInfo,
+            boolean simpleTag)
+            throws JasperException {
+
+            // Set context
+            if (simpleTag) {
+                // Generate alias map
+                String aliasMapVar = null;
+                if (n.isTagFile()) {
+                    aliasMapVar = generateAliasMap(n, tagHandlerVar);
+                }
+                out.printin(tagHandlerVar);
+                if (aliasMapVar == null) {
+                    out.println(".setJspContext(_jspx_page_context);");
+                } else {
+                    out.print(".setJspContext(_jspx_page_context, ");
+                    out.print(aliasMapVar);
+                    out.println(");");
+                }
+            } else {
+                out.printin(tagHandlerVar);
+                out.println(".setPageContext(_jspx_page_context);");
+            }
+
+            // Set parent
+            if (!simpleTag) {
+                out.printin(tagHandlerVar);
+                out.print(".setParent(");
+                if (parent != null) {
+                    if (isSimpleTagParent) {
+                        out.print("new javax.servlet.jsp.tagext.TagAdapter(");
+                        out.print("(javax.servlet.jsp.tagext.SimpleTag) ");
+                        out.print(parent);
+                        out.println("));");
+                    } else {
+                        out.print("(javax.servlet.jsp.tagext.Tag) ");
+                        out.print(parent);
+                        out.println(");");
+                    }
+                } else {
+                    out.println("null);");
+                }
+            } else {
+                // The setParent() method need not be called if the value being
+                // passed is null, since SimpleTag instances are not reused
+                if (parent != null) {
+                    out.printin(tagHandlerVar);
+                    out.print(".setParent(");
+                    out.print(parent);
+                    out.println(");");
+                }
+            }
+
+            Node.JspAttribute[] attrs = n.getJspAttributes();
+            for (int i = 0; attrs != null && i < attrs.length; i++) {
+                String attrValue =
+                    evaluateAttribute(handlerInfo, attrs[i], n, tagHandlerVar);
+
+                if (attrs[i].isDynamic()) {
+                    out.printin(tagHandlerVar);
+                    out.print(".");
+                    out.print("setDynamicAttribute(");
+                    String uri = attrs[i].getURI();
+                    if ("".equals(uri) || (uri == null)) {
+                        out.print("null");
+                    } else {
+                        out.print("\"" + attrs[i].getURI() + "\"");
+                    }
+                    out.print(", \"");
+                    out.print(attrs[i].getLocalName());
+                    out.print("\", ");
+                    out.print(attrValue);
+                    out.println(");");
+                } else {
+                    out.printin(tagHandlerVar);
+                    out.print(".");
+                    out.print(
+                        handlerInfo
+                            .getSetterMethod(attrs[i].getLocalName())
+                            .getName());
+                    out.print("(");
+                    out.print(attrValue);
+                    out.println(");");
+                }
+            }
+        }
+
+        /*
+         * @param c The target class to which to coerce the given string
+         * @param s The string value
+         * @param attrName The name of the attribute whose value is being
+         * supplied
+         * @param propEditorClass The property editor for the given attribute
+         * @param isNamedAttribute true if the given attribute is a named
+         * attribute (that is, specified using the jsp:attribute standard
+         * action), and false otherwise
+         */
+        private String convertString(
+            Class c,
+            String s,
+            String attrName,
+            Class propEditorClass,
+            boolean isNamedAttribute)
+            throws JasperException {
+
+            String quoted = s;
+            if (!isNamedAttribute) {
+                quoted = quote(s);
+            }
+
+            if (propEditorClass != null) {
+                String className = JspUtil.getCanonicalName(c);
+                return "("
+                    + className
+                    + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromBeanInfoPropertyEditor("
+                    + className
+                    + ".class, \""
+                    + attrName
+                    + "\", "
+                    + quoted
+                    + ", "
+                    + JspUtil.getCanonicalName(propEditorClass)
+                    + ".class)";
+            } else if (c == String.class) {
+                return quoted;
+            } else if (c == boolean.class) {
+                return JspUtil.coerceToPrimitiveBoolean(s, isNamedAttribute);
+            } else if (c == Boolean.class) {
+                return JspUtil.coerceToBoolean(s, isNamedAttribute);
+            } else if (c == byte.class) {
+                return JspUtil.coerceToPrimitiveByte(s, isNamedAttribute);
+            } else if (c == Byte.class) {
+                return JspUtil.coerceToByte(s, isNamedAttribute);
+            } else if (c == char.class) {
+                return JspUtil.coerceToChar(s, isNamedAttribute);
+            } else if (c == Character.class) {
+                return JspUtil.coerceToCharacter(s, isNamedAttribute);
+            } else if (c == double.class) {
+                return JspUtil.coerceToPrimitiveDouble(s, isNamedAttribute);
+            } else if (c == Double.class) {
+                return JspUtil.coerceToDouble(s, isNamedAttribute);
+            } else if (c == float.class) {
+                return JspUtil.coerceToPrimitiveFloat(s, isNamedAttribute);
+            } else if (c == Float.class) {
+                return JspUtil.coerceToFloat(s, isNamedAttribute);
+            } else if (c == int.class) {
+                return JspUtil.coerceToInt(s, isNamedAttribute);
+            } else if (c == Integer.class) {
+                return JspUtil.coerceToInteger(s, isNamedAttribute);
+            } else if (c == short.class) {
+                return JspUtil.coerceToPrimitiveShort(s, isNamedAttribute);
+            } else if (c == Short.class) {
+                return JspUtil.coerceToShort(s, isNamedAttribute);
+            } else if (c == long.class) {
+                return JspUtil.coerceToPrimitiveLong(s, isNamedAttribute);
+            } else if (c == Long.class) {
+                return JspUtil.coerceToLong(s, isNamedAttribute);
+            } else if (c == Object.class) {
+                return "new String(" + quoted + ")";
+            } else {
+                String className = JspUtil.getCanonicalName(c);
+                return "("
+                    + className
+                    + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromPropertyEditorManager("
+                    + className
+                    + ".class, \""
+                    + attrName
+                    + "\", "
+                    + quoted
+                    + ")";
+            }
+        }
+
+        /*
+         * Converts the scope string representation, whose possible values
+         * are "page", "request", "session", and "application", to the
+         * corresponding scope constant.
+         */
+        private String getScopeConstant(String scope) {
+            String scopeName = "PageContext.PAGE_SCOPE"; // Default to page
+
+            if ("request".equals(scope)) {
+                scopeName = "PageContext.REQUEST_SCOPE";
+            } else if ("session".equals(scope)) {
+                scopeName = "PageContext.SESSION_SCOPE";
+            } else if ("application".equals(scope)) {
+                scopeName = "PageContext.APPLICATION_SCOPE";
+            }
+
+            return scopeName;
+        }
+
+        /**
+         * Generates anonymous JspFragment inner class which is passed as an
+         * argument to SimpleTag.setJspBody().
+         */
+        private void generateJspFragment(Node n, String tagHandlerVar)
+            throws JasperException {
+            // XXX - A possible optimization here would be to check to see
+            // if the only child of the parent node is TemplateText.  If so,
+            // we know there won't be any parameters, etc, so we can
+            // generate a low-overhead JspFragment that just echoes its
+            // body.  The implementation of this fragment can come from
+            // the org.apache.jasper.runtime package as a support class.
+            FragmentHelperClass.Fragment fragment =
+                fragmentHelperClass.openFragment(
+                    n,
+                    tagHandlerVar,
+                    methodNesting);
+            ServletWriter outSave = out;
+            out = fragment.getGenBuffer().getOut();
+            String tmpParent = parent;
+            parent = "_jspx_parent";
+            boolean isSimpleTagParentSave = isSimpleTagParent;
+            isSimpleTagParent = true;
+            boolean tmpIsFragment = isFragment;
+            isFragment = true;
+            String pushBodyCountVarSave = pushBodyCountVar;
+            if (pushBodyCountVar != null) {
+                // Use a fixed name for push body count, to simplify code gen
+                pushBodyCountVar = "_jspx_push_body_count";
+            }
+            visitBody(n);
+            out = outSave;
+            parent = tmpParent;
+            isSimpleTagParent = isSimpleTagParentSave;
+            isFragment = tmpIsFragment;
+            pushBodyCountVar = pushBodyCountVarSave;
+            fragmentHelperClass.closeFragment(fragment, methodNesting);
+            // XXX - Need to change pageContext to jspContext if
+            // we're not in a place where pageContext is defined (e.g.
+            // in a fragment or in a tag file.
+            out.print(
+                "new "
+                    + fragmentHelperClass.getClassName()
+                    + "( "
+                    + fragment.getId()
+                    + ", _jspx_page_context, "
+                    + tagHandlerVar
+                    + ", "
+                    + pushBodyCountVar
+                    + ")");
+        }
+
+        /**
+         * Generate the code required to obtain the runtime value of the
+         * given named attribute.
+         *
+         * @return The name of the temporary variable the result is stored in.
+         */
+        public String generateNamedAttributeValue(Node.NamedAttribute n)
+            throws JasperException {
+
+            String varName = n.getTemporaryVariableName();
+
+            // If the only body element for this named attribute node is
+            // template text, we need not generate an extra call to
+            // pushBody and popBody.  Maybe we can further optimize
+            // here by getting rid of the temporary variable, but in
+            // reality it looks like javac does this for us.
+            Node.Nodes body = n.getBody();
+            if (body != null) {
+                boolean templateTextOptimization = false;
+                if (body.size() == 1) {
+                    Node bodyElement = body.getNode(0);
+                    if (bodyElement instanceof Node.TemplateText) {
+                        templateTextOptimization = true;
+                        out.printil(
+                            "String "
+                                + varName
+                                + " = "
+                                + quote(
+                                    new String(
+                                        ((Node.TemplateText)bodyElement)
+                                            .getText()))
+                                + ";");
+                    }
+                }
+
+                // XXX - Another possible optimization would be for
+                // lone EL expressions (no need to pushBody here either).
+
+                if (!templateTextOptimization) {
+                    out.printil("out = _jspx_page_context.pushBody();");
+                    visitBody(n);
+                    out.printil(
+                        "String "
+                            + varName
+                            + " = "
+                            + "((javax.servlet.jsp.tagext.BodyContent)"
+                            + "out).getString();");
+                    out.printil("out = _jspx_page_context.popBody();");
+                }
+            } else {
+                // Empty body must be treated as ""
+                out.printil("String " + varName + " = \"\";");
+            }
+
+            return varName;
+        }
+
+        /**
+         * Similar to generateNamedAttributeValue, but create a JspFragment
+         * instead.
+         *
+         * @param n The parent node of the named attribute
+         * @param tagHandlerVar The variable the tag handler is stored in,
+         *     so the fragment knows its parent tag.
+         * @return The name of the temporary variable the fragment
+         *     is stored in.
+         */
+        public String generateNamedAttributeJspFragment(
+            Node.NamedAttribute n,
+            String tagHandlerVar)
+            throws JasperException {
+            String varName = n.getTemporaryVariableName();
+
+            out.printin(
+                "javax.servlet.jsp.tagext.JspFragment " + varName + " = ");
+            generateJspFragment(n, tagHandlerVar);
+            out.println(";");
+
+            return varName;
+        }
+    }
+
+    private static void generateLocalVariables(ServletWriter out, Node n)
+        throws JasperException {
+        Node.ChildInfo ci;
+        if (n instanceof Node.CustomTag) {
+            ci = ((Node.CustomTag)n).getChildInfo();
+        } else if (n instanceof Node.JspBody) {
+            ci = ((Node.JspBody)n).getChildInfo();
+        } else if (n instanceof Node.NamedAttribute) {
+            ci = ((Node.NamedAttribute)n).getChildInfo();
+        } else {
+            // Cannot access err since this method is static, but at
+            // least flag an error.
+            throw new JasperException("Unexpected Node Type");
+            //err.getString(
+            //    "jsp.error.internal.unexpected_node_type" ) );
+        }
+
+        if (ci.hasUseBean()) {
+            out.printil("HttpSession session = _jspx_page_context.getSession();");
+            out.printil(
+                "ServletContext application = _jspx_page_context.getServletContext();");
+        }
+        if (ci.hasUseBean()
+            || ci.hasIncludeAction()
+            || ci.hasSetProperty()
+            || ci.hasParamAction()) {
+            out.printil(
+                "HttpServletRequest request = (HttpServletRequest)_jspx_page_context.getRequest();");
+        }
+        if (ci.hasIncludeAction()) {
+            out.printil(
+                "HttpServletResponse response = (HttpServletResponse)_jspx_page_context.getResponse();");
+        }
+    }
+
+    /**
+     * Common part of postamble, shared by both servlets and tag files.
+     */
+    private void genCommonPostamble() {
+        // Append any methods that were generated in the buffer.
+        for (int i = 0; i < methodsBuffered.size(); i++) {
+            GenBuffer methodBuffer = (GenBuffer)methodsBuffered.get(i);
+            methodBuffer.adjustJavaLines(out.getJavaLine() - 1);
+            out.printMultiLn(methodBuffer.toString());
+        }
+
+        // Append the helper class
+        if (fragmentHelperClass.isUsed()) {
+            fragmentHelperClass.generatePostamble();
+            fragmentHelperClass.adjustJavaLines(out.getJavaLine() - 1);
+            out.printMultiLn(fragmentHelperClass.toString());
+        }
+
+        // Append char array declarations
+        if (charArrayBuffer != null) {
+            out.printMultiLn(charArrayBuffer.toString());
+        }
+
+        // Close the class definition
+        out.popIndent();
+        out.printil("}");
+    }
+
+    /**
+     * Generates the ending part of the static portion of the servlet.
+     */
+    private void generatePostamble(Node.Nodes page) {
+        out.popIndent();
+        out.printil("} catch (Throwable t) {");
+        out.pushIndent();
+        out.printil(
+            "if (!(t instanceof SkipPageException)){");
+        out.pushIndent();
+        out.printil("out = _jspx_out;");
+        out.printil("if (out != null && out.getBufferSize() != 0)");
+        out.pushIndent();
+        out.printil("out.clearBuffer();");
+        out.popIndent();
+
+        out.printil(
+            "if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);");
+        out.popIndent();
+        out.printil("}");
+        out.popIndent();
+        out.printil("} finally {");
+        out.pushIndent();
+
+        out.printil(
+            "if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);");
+
+        out.popIndent();
+        out.printil("}");
+
+        // Close the service method
+        out.popIndent();
+        out.printil("}");
+
+        // Generated methods, helper classes, etc.
+        genCommonPostamble();
+    }
+
+    /**
+     * Constructor.
+     */
+    Generator(ServletWriter out, Compiler compiler) {
+        this.out = out;
+        methodsBuffered = new ArrayList();
+        charArrayBuffer = null;
+        err = compiler.getErrorDispatcher();
+        ctxt = compiler.getCompilationContext();
+        fragmentHelperClass =
+            new FragmentHelperClass(ctxt.getServletClassName() + "Helper");
+        pageInfo = compiler.getPageInfo();
+
+        /*
+         * Temporary hack. If a JSP page uses the "extends" attribute of the
+         * page directive, the _jspInit() method of the generated servlet class
+         * will not be called (it is only called for those generated servlets
+         * that extend HttpJspBase, the default), causing the tag handler pools
+         * not to be initialized and resulting in a NPE.
+         * The JSP spec needs to clarify whether containers can override
+         * init() and destroy(). For now, we just disable tag pooling for pages
+         * that use "extends".
+         */
+        if (pageInfo.getExtends(false) == null) {
+            isPoolingEnabled = ctxt.getOptions().isPoolingEnabled();
+        } else {
+            isPoolingEnabled = false;
+        }
+        beanInfo = pageInfo.getBeanRepository();
+        breakAtLF = ctxt.getOptions().getMappedFile();
+        if (isPoolingEnabled) {
+            tagHandlerPoolNames = new Vector();
+        }
+    }
+
+    /**
+     * The main entry for Generator.
+     * @param out The servlet output writer
+     * @param compiler The compiler
+     * @param page The input page
+     */
+    public static void generate(
+        ServletWriter out,
+        Compiler compiler,
+        Node.Nodes page)
+        throws JasperException {
+
+        Generator gen = new Generator(out, compiler);
+
+        if (gen.isPoolingEnabled) {
+            gen.compileTagHandlerPoolList(page);
+        }
+        if (gen.ctxt.isTagFile()) {
+            JasperTagInfo tagInfo = (JasperTagInfo)gen.ctxt.getTagInfo();
+            gen.generateTagHandlerPreamble(tagInfo, page);
+
+            if (gen.ctxt.isPrototypeMode()) {
+                return;
+            }
+
+            gen.generateXmlProlog(page);
+            gen.fragmentHelperClass.generatePreamble();
+            page.visit(
+                gen.new GenerateVisitor(
+                    gen.ctxt.isTagFile(),
+                    out,
+                    gen.methodsBuffered,
+                    gen.fragmentHelperClass,
+                    gen.ctxt.getClassLoader(),
+                    tagInfo));
+            gen.generateTagHandlerPostamble(tagInfo);
+        } else {
+            gen.generatePreamble(page);
+            gen.generateXmlProlog(page);
+            gen.fragmentHelperClass.generatePreamble();
+            page.visit(
+                gen.new GenerateVisitor(
+                    gen.ctxt.isTagFile(),
+                    out,
+                    gen.methodsBuffered,
+                    gen.fragmentHelperClass,
+                    gen.ctxt.getClassLoader(),
+                    null));
+            gen.generatePostamble(page);
+        }
+    }
+
+    /*
+     * Generates tag handler preamble.
+     */
+    private void generateTagHandlerPreamble(
+        JasperTagInfo tagInfo,
+        Node.Nodes tag)
+        throws JasperException {
+
+        // Generate package declaration
+        String className = tagInfo.getTagClassName();
+        int lastIndex = className.lastIndexOf('.');
+        if (lastIndex != -1) {
+            String pkgName = className.substring(0, lastIndex);
+            genPreamblePackage(pkgName);
+            className = className.substring(lastIndex + 1);
+        }
+
+        // Generate imports
+        genPreambleImports();
+
+        // Generate class declaration
+        out.printin("public final class ");
+        out.println(className);
+        out.printil("    extends javax.servlet.jsp.tagext.SimpleTagSupport");
+        out.printin(
+            "    implements org.apache.jasper.runtime.JspSourceDependent");
+        if (tagInfo.hasDynamicAttributes()) {
+            out.println(",");
+            out.printin(
+                "               javax.servlet.jsp.tagext.DynamicAttributes");
+        }
+        out.println(" {");
+        out.println();
+        out.pushIndent();
+
+        /*
+         * Class body begins here
+         */
+
+        generateDeclarations(tag);
+
+        // Static initializations here
+        genPreambleStaticInitializers();
+
+        out.printil("private JspContext jspContext;");
+
+        // Declare writer used for storing result of fragment/body invocation
+        // if 'varReader' or 'var' attribute is specified
+        out.printil("private java.io.Writer _jspx_sout;");
+
+        // Class variable declarations
+        genPreambleClassVariableDeclarations(tagInfo.getTagName());
+
+        generateSetJspContext(tagInfo);
+
+        // Tag-handler specific declarations
+        generateTagHandlerAttributes(tagInfo);
+        if (tagInfo.hasDynamicAttributes())
+            generateSetDynamicAttribute();
+
+        // Methods here
+        genPreambleMethods();
+
+        // Now the doTag() method
+        out.printil(
+            "public void doTag() throws JspException, java.io.IOException {");
+
+        if (ctxt.isPrototypeMode()) {
+            out.printil("}");
+            out.popIndent();
+            out.printil("}");
+            return;
+        }
+
+        out.pushIndent();
+
+        /*
+         * According to the spec, 'pageContext' must not be made available as
+         * an implicit object in tag files.
+         * Declare _jspx_page_context, so we can share the code generator with
+         * JSPs.
+         */
+        out.printil("PageContext _jspx_page_context = (PageContext)jspContext;");
+
+        // Declare implicit objects.
+        out.printil(
+            "HttpServletRequest request = "
+                + "(HttpServletRequest) _jspx_page_context.getRequest();");
+        out.printil(
+            "HttpServletResponse response = "
+                + "(HttpServletResponse) _jspx_page_context.getResponse();");
+        out.printil("HttpSession session = _jspx_page_context.getSession();");
+        out.printil(
+            "ServletContext application = _jspx_page_context.getServletContext();");
+        out.printil("ServletConfig config = _jspx_page_context.getServletConfig();");
+        out.printil("JspWriter out = jspContext.getOut();");
+        if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
+            out.printil("_jspInit(config);");
+        }
+        generatePageScopedVariables(tagInfo);
+
+        declareTemporaryScriptingVars(tag);
+        out.println();
+
+        out.printil("try {");
+        out.pushIndent();
+    }
+
+    private void generateTagHandlerPostamble(TagInfo tagInfo) {
+        out.popIndent();
+
+        // Have to catch Throwable because a classic tag handler
+        // helper method is declared to throw Throwable.
+        out.printil("} catch( Throwable t ) {");
+        out.pushIndent();
+        out.printil("if( t instanceof SkipPageException )");
+        out.printil("    throw (SkipPageException) t;");
+        out.printil("if( t instanceof java.io.IOException )");
+        out.printil("    throw (java.io.IOException) t;");
+        out.printil("if( t instanceof IllegalStateException )");
+        out.printil("    throw (IllegalStateException) t;");
+        out.printil("if( t instanceof JspException )");
+        out.printil("    throw (JspException) t;");
+        out.printil("throw new JspException(t);");
+        out.popIndent();
+        out.printil("} finally {");
+        out.pushIndent();
+        out.printil(
+            "((org.apache.jasper.runtime.JspContextWrapper) jspContext).syncEndTagFile();");
+        if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
+            out.printil("_jspDestroy();");
+        }
+        out.popIndent();
+        out.printil("}");
+
+        // Close the doTag method
+        out.popIndent();
+        out.printil("}");
+
+        // Generated methods, helper classes, etc.
+        genCommonPostamble();
+    }
+
+    /**
+     * Generates declarations for tag handler attributes, and defines the
+     * getter and setter methods for each.
+     */
+    private void generateTagHandlerAttributes(TagInfo tagInfo)
+        throws JasperException {
+
+        if (tagInfo.hasDynamicAttributes()) {
+            out.printil(
+                "private java.util.HashMap _jspx_dynamic_attrs = new java.util.HashMap();");
+        }
+
+        // Declare attributes
+        TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
+        for (int i = 0; i < attrInfos.length; i++) {
+            out.printin("private ");
+            if (attrInfos[i].isFragment()) {
+                out.print("javax.servlet.jsp.tagext.JspFragment ");
+            } else {
+                out.print(JspUtil.toJavaSourceType(attrInfos[i].getTypeName()));
+                out.print(" ");
+            }
+            out.print(attrInfos[i].getName());
+            out.println(";");
+        }
+        out.println();
+
+        // Define attribute getter and setter methods
+        if (attrInfos != null) {
+            for (int i = 0; i < attrInfos.length; i++) {
+                // getter method
+                out.printin("public ");
+                if (attrInfos[i].isFragment()) {
+                    out.print("javax.servlet.jsp.tagext.JspFragment ");
+                } else {
+                    out.print(JspUtil.toJavaSourceType(attrInfos[i].getTypeName()));
+                    out.print(" ");
+                }
+                out.print(toGetterMethod(attrInfos[i].getName()));
+                out.println(" {");
+                out.pushIndent();
+                out.printin("return this.");
+                out.print(attrInfos[i].getName());
+                out.println(";");
+                out.popIndent();
+                out.printil("}");
+                out.println();
+
+                // setter method
+                out.printin("public void ");
+                out.print(toSetterMethodName(attrInfos[i].getName()));
+                if (attrInfos[i].isFragment()) {
+                    out.print("(javax.servlet.jsp.tagext.JspFragment ");
+                } else {
+                    out.print("(");
+                    out.print(JspUtil.toJavaSourceType(attrInfos[i].getTypeName()));
+                    out.print(" ");
+                }
+                out.print(attrInfos[i].getName());
+                out.println(") {");
+                out.pushIndent();
+                out.printin("this.");
+                out.print(attrInfos[i].getName());
+                out.print(" = ");
+                out.print(attrInfos[i].getName());
+                out.println(";");
+                out.popIndent();
+                out.printil("}");
+                out.println();
+            }
+        }
+    }
+
+    /*
+     * Generate setter for JspContext so we can create a wrapper and
+     * store both the original and the wrapper.  We need the wrapper
+     * to mask the page context from the tag file and simulate a
+     * fresh page context.  We need the original to do things like
+     * sync AT_BEGIN and AT_END scripting variables.
+     */
+    private void generateSetJspContext(TagInfo tagInfo) {
+
+        boolean nestedSeen = false;
+        boolean atBeginSeen = false;
+        boolean atEndSeen = false;
+
+        // Determine if there are any aliases
+        boolean aliasSeen = false;
+        TagVariableInfo[] tagVars = tagInfo.getTagVariableInfos();
+        for (int i = 0; i < tagVars.length; i++) {
+            if (tagVars[i].getNameFromAttribute() != null
+                && tagVars[i].getNameGiven() != null) {
+                aliasSeen = true;
+                break;
+            }
+        }
+
+        if (aliasSeen) {
+            out.printil(
+                "public void setJspContext(JspContext ctx, java.util.Map aliasMap) {");
+        } else {
+            out.printil("public void setJspContext(JspContext ctx) {");
+        }
+        out.pushIndent();
+        out.printil("super.setJspContext(ctx);");
+        out.printil("java.util.ArrayList _jspx_nested = null;");
+        out.printil("java.util.ArrayList _jspx_at_begin = null;");
+        out.printil("java.util.ArrayList _jspx_at_end = null;");
+
+        for (int i = 0; i < tagVars.length; i++) {
+
+            switch (tagVars[i].getScope()) {
+                case VariableInfo.NESTED :
+                    if (!nestedSeen) {
+                        out.printil(
+                            "_jspx_nested = new java.util.ArrayList();");
+                        nestedSeen = true;
+                    }
+                    out.printin("_jspx_nested.add(");
+                    break;
+
+                case VariableInfo.AT_BEGIN :
+                    if (!atBeginSeen) {
+                        out.printil(
+                            "_jspx_at_begin = new java.util.ArrayList();");
+                        atBeginSeen = true;
+                    }
+                    out.printin("_jspx_at_begin.add(");
+                    break;
+
+                case VariableInfo.AT_END :
+                    if (!atEndSeen) {
+                        out.printil(
+                            "_jspx_at_end = new java.util.ArrayList();");
+                        atEndSeen = true;
+                    }
+                    out.printin("_jspx_at_end.add(");
+                    break;
+            } // switch
+
+            out.print(quote(tagVars[i].getNameGiven()));
+            out.println(");");
+        }
+        if (aliasSeen) {
+            out.printil(
+                "this.jspContext = new org.apache.jasper.runtime.JspContextWrapper(ctx, _jspx_nested, _jspx_at_begin, _jspx_at_end, aliasMap);");
+        } else {
+            out.printil(
+                "this.jspContext = new org.apache.jasper.runtime.JspContextWrapper(ctx, _jspx_nested, _jspx_at_begin, _jspx_at_end, null);");
+        }
+        out.popIndent();
+        out.printil("}");
+        out.println();
+        out.printil("public JspContext getJspContext() {");
+        out.pushIndent();
+        out.printil("return this.jspContext;");
+        out.popIndent();
+        out.printil("}");
+    }
+
+    /*
+     * Generates implementation of
+     * javax.servlet.jsp.tagext.DynamicAttributes.setDynamicAttribute() method,
+     * which saves each dynamic attribute that is passed in so that a scoped
+     * variable can later be created for it.
+     */
+    public void generateSetDynamicAttribute() {
+        out.printil(
+            "public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {");
+        out.pushIndent();
+        /*
+         * According to the spec, only dynamic attributes with no uri are to
+         * be present in the Map; all other dynamic attributes are ignored.
+         */
+        out.printil("if (uri == null)");
+        out.pushIndent();
+        out.printil("_jspx_dynamic_attrs.put(localName, value);");
+        out.popIndent();
+        out.popIndent();
+        out.printil("}");
+    }
+
+    /*
+     * Creates a page-scoped variable for each declared tag attribute.
+     * Also, if the tag accepts dynamic attributes, a page-scoped variable
+     * is made available for each dynamic attribute that was passed in.
+     */
+    private void generatePageScopedVariables(JasperTagInfo tagInfo) {
+
+        // "normal" attributes
+        TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
+        for (int i = 0; i < attrInfos.length; i++) {
+            String attrName = attrInfos[i].getName();
+            out.printil("if( " + toGetterMethod(attrName) + " != null ) ");
+            out.pushIndent();
+            out.printin("_jspx_page_context.setAttribute(");
+            out.print(quote(attrName));
+            out.print(", ");
+            out.print(toGetterMethod(attrName));
+            out.println(");");
+            out.popIndent();
+        }
+
+        // Expose the Map containing dynamic attributes as a page-scoped var
+        if (tagInfo.hasDynamicAttributes()) {
+            out.printin("_jspx_page_context.setAttribute(\"");
+            out.print(tagInfo.getDynamicAttributesMapName());
+            out.print("\", _jspx_dynamic_attrs);");
+        }
+    }
+
+    /*
+     * Generates the getter method for the given attribute name.
+     */
+    private String toGetterMethod(String attrName) {
+        char[] attrChars = attrName.toCharArray();
+        attrChars[0] = Character.toUpperCase(attrChars[0]);
+        return "get" + new String(attrChars) + "()";
+    }
+
+    /*
+     * Generates the setter method name for the given attribute name.
+     */
+    private String toSetterMethodName(String attrName) {
+        char[] attrChars = attrName.toCharArray();
+        attrChars[0] = Character.toUpperCase(attrChars[0]);
+        return "set" + new String(attrChars);
+    }
+
+    /**
+     * Class storing the result of introspecting a custom tag handler.
+     */
+    private static class TagHandlerInfo {
+
+        private Hashtable methodMaps;
+        private Hashtable propertyEditorMaps;
+        private Class tagHandlerClass;
+
+        /**
+         * Constructor.
+         *
+         * @param n The custom tag whose tag handler class is to be
+         * introspected
+         * @param tagHandlerClass Tag handler class
+         * @param err Error dispatcher
+         */
+        TagHandlerInfo(Node n, Class tagHandlerClass, ErrorDispatcher err)
+            throws JasperException {
+            this.tagHandlerClass = tagHandlerClass;
+            this.methodMaps = new Hashtable();
+            this.propertyEditorMaps = new Hashtable();
+
+            try {
+                BeanInfo tagClassInfo =
+                    Introspector.getBeanInfo(tagHandlerClass);
+                PropertyDescriptor[] pd = tagClassInfo.getPropertyDescriptors();
+                for (int i = 0; i < pd.length; i++) {
+                    /*
+                     * FIXME: should probably be checking for things like
+                     *        pageContext, bodyContent, and parent here -akv
+                     */
+                    if (pd[i].getWriteMethod() != null) {
+                        methodMaps.put(pd[i].getName(), pd[i].getWriteMethod());
+                    }
+                    if (pd[i].getPropertyEditorClass() != null)
+                        propertyEditorMaps.put(
+                            pd[i].getName(),
+                            pd[i].getPropertyEditorClass());
+                }
+            } catch (IntrospectionException ie) {
+                err.jspError(
+                    n,
+                    "jsp.error.introspect.taghandler",
+                    tagHandlerClass.getName(),
+                    ie);
+            }
+        }
+
+        /**
+         * XXX
+         */
+        public Method getSetterMethod(String attrName) {
+            return (Method)methodMaps.get(attrName);
+        }
+
+        /**
+         * XXX
+         */
+        public Class getPropertyEditorClass(String attrName) {
+            return (Class)propertyEditorMaps.get(attrName);
+        }
+
+        /**
+         * XXX
+         */
+        public Class getTagHandlerClass() {
+            return tagHandlerClass;
+        }
+    }
+
+    /**
+     * A class for generating codes to a buffer.  Included here are some
+     * support for tracking source to Java lines mapping.
+     */
+    private static class GenBuffer {
+
+        /*
+         * For a CustomTag, the codes that are generated at the beginning of
+         * the tag may not be in the same buffer as those for the body of the
+         * tag.  Two fields are used here to keep this straight.  For codes
+         * that do not corresponds to any JSP lines, they should be null.
+         */
+        private Node node;
+        private Node.Nodes body;
+        private java.io.CharArrayWriter charWriter;
+        protected ServletWriter out;
+
+        GenBuffer() {
+            this(null, null);
+        }
+
+        GenBuffer(Node n, Node.Nodes b) {
+            node = n;
+            body = b;
+            if (body != null) {
+                body.setGeneratedInBuffer(true);
+            }
+            charWriter = new java.io.CharArrayWriter();
+            out = new ServletWriter(new java.io.PrintWriter(charWriter));
+        }
+
+        public ServletWriter getOut() {
+            return out;
+        }
+
+        public String toString() {
+            return charWriter.toString();
+        }
+
+        /**
+         * Adjust the Java Lines.  This is necessary because the Java lines
+         * stored with the nodes are relative the beginning of this buffer
+         * and need to be adjusted when this buffer is inserted into the
+         * source.
+         */
+        public void adjustJavaLines(final int offset) {
+
+            if (node != null) {
+                adjustJavaLine(node, offset);
+            }
+
+            if (body != null) {
+                try {
+                    body.visit(new Node.Visitor() {
+
+                        public void doVisit(Node n) {
+                            adjustJavaLine(n, offset);
+                        }
+
+                        public void visit(Node.CustomTag n)
+                            throws JasperException {
+                            Node.Nodes b = n.getBody();
+                            if (b != null && !b.isGeneratedInBuffer()) {
+                                // Don't adjust lines for the nested tags that
+                                // are also generated in buffers, because the
+                                // adjustments will be done elsewhere.
+                                b.visit(this);
+                            }
+                        }
+                    });
+                } catch (JasperException ex) {
+                }
+            }
+        }
+
+        private static void adjustJavaLine(Node n, int offset) {
+            if (n.getBeginJavaLine() > 0) {
+                n.setBeginJavaLine(n.getBeginJavaLine() + offset);
+                n.setEndJavaLine(n.getEndJavaLine() + offset);
+            }
+        }
+    }
+
+    /**
+     * Keeps track of the generated Fragment Helper Class
+     */
+    private static class FragmentHelperClass {
+
+        private static class Fragment {
+            private GenBuffer genBuffer;
+            private int id;
+
+            public Fragment(int id, Node node) {
+                this.id = id;
+                genBuffer = new GenBuffer(null, node.getBody());
+            }
+
+            public GenBuffer getGenBuffer() {
+                return this.genBuffer;
+            }
+
+            public int getId() {
+                return this.id;
+            }
+        }
+
+        // True if the helper class should be generated.
+        private boolean used = false;
+
+        private ArrayList fragments = new ArrayList();
+
+        private String className;
+
+        // Buffer for entire helper class
+        private GenBuffer classBuffer = new GenBuffer();
+
+        public FragmentHelperClass(String className) {
+            this.className = className;
+        }
+
+        public String getClassName() {
+            return this.className;
+        }
+
+        public boolean isUsed() {
+            return this.used;
+        }
+
+        public void generatePreamble() {
+            ServletWriter out = this.classBuffer.getOut();
+            out.println();
+            out.pushIndent();
+            // Note: cannot be static, as we need to reference things like
+            // _jspx_meth_*
+            out.printil("private class " + className);
+            out.printil(
+                "    extends " + "org.apache.jasper.runtime.JspFragmentHelper");
+            out.printil("{");
+            out.pushIndent();
+            out.printil(
+                "private javax.servlet.jsp.tagext.JspTag _jspx_parent;");
+            out.printil("private int[] _jspx_push_body_count;");
+            out.println();
+            out.printil(
+                "public "
+                    + className
+                    + "( int discriminator, JspContext jspContext, "
+                    + "javax.servlet.jsp.tagext.JspTag _jspx_parent, "
+                    + "int[] _jspx_push_body_count ) {");
+            out.pushIndent();
+            out.printil("super( discriminator, jspContext, _jspx_parent );");
+            out.printil("this._jspx_parent = _jspx_parent;");
+            out.printil("this._jspx_push_body_count = _jspx_push_body_count;");
+            out.popIndent();
+            out.printil("}");
+        }
+
+        public Fragment openFragment(
+            Node parent,
+            String tagHandlerVar,
+            int methodNesting)
+            throws JasperException {
+            Fragment result = new Fragment(fragments.size(), parent);
+            fragments.add(result);
+            this.used = true;
+            parent.setInnerClassName(className);
+
+            ServletWriter out = result.getGenBuffer().getOut();
+            out.pushIndent();
+            out.pushIndent();
+            // XXX - Returns boolean because if a tag is invoked from
+            // within this fragment, the Generator sometimes might
+            // generate code like "return true".  This is ignored for now,
+            // meaning only the fragment is skipped.  The JSR-152
+            // expert group is currently discussing what to do in this case.
+            // See comment in closeFragment()
+            if (methodNesting > 0) {
+                out.printin("public boolean invoke");
+            } else {
+                out.printin("public void invoke");
+            }
+            out.println(result.getId() + "( " + "JspWriter out ) ");
+            out.pushIndent();
+            // Note: Throwable required because methods like _jspx_meth_*
+            // throw Throwable.
+            out.printil("throws Throwable");
+            out.popIndent();
+            out.printil("{");
+            out.pushIndent();
+            generateLocalVariables(out, parent);
+
+            return result;
+        }
+
+        public void closeFragment(Fragment fragment, int methodNesting) {
+            ServletWriter out = fragment.getGenBuffer().getOut();
+            // XXX - See comment in openFragment()
+            if (methodNesting > 0) {
+                out.printil("return false;");
+            } else {
+                out.printil("return;");
+            }
+            out.popIndent();
+            out.printil("}");
+        }
+
+        public void generatePostamble() {
+            ServletWriter out = this.classBuffer.getOut();
+            // Generate all fragment methods:
+            for (int i = 0; i < fragments.size(); i++) {
+                Fragment fragment = (Fragment)fragments.get(i);
+                fragment.getGenBuffer().adjustJavaLines(out.getJavaLine() - 1);
+                out.printMultiLn(fragment.getGenBuffer().toString());
+            }
+
+            // Generate postamble:
+            out.printil("public void invoke( java.io.Writer writer )");
+            out.pushIndent();
+            out.printil("throws JspException");
+            out.popIndent();
+            out.printil("{");
+            out.pushIndent();
+            out.printil("JspWriter out = null;");
+            out.printil("if( writer != null ) {");
+            out.pushIndent();
+            out.printil("out = this.jspContext.pushBody(writer);");
+            out.popIndent();
+            out.printil("} else {");
+            out.pushIndent();
+            out.printil("out = this.jspContext.getOut();");
+            out.popIndent();
+            out.printil("}");
+            out.printil("try {");
+            out.pushIndent();
+            out.printil("switch( this.discriminator ) {");
+            out.pushIndent();
+            for (int i = 0; i < fragments.size(); i++) {
+                out.printil("case " + i + ":");
+                out.pushIndent();
+                out.printil("invoke" + i + "( out );");
+                out.printil("break;");
+                out.popIndent();
+            }
+            out.popIndent();
+            out.printil("}"); // switch
+            out.popIndent();
+            out.printil("}"); // try
+            out.printil("catch( Throwable e ) {");
+            out.pushIndent();
+            out.printil(
+                "if (e instanceof SkipPageException)");
+            out.printil("    throw (SkipPageException) e;");
+            out.printil("throw new JspException( e );");
+            out.popIndent();
+            out.printil("}"); // catch
+            out.printil("finally {");
+            out.pushIndent();
+
+            out.printil("if( writer != null ) {");
+            out.pushIndent();
+            out.printil("this.jspContext.popBody();");
+            out.popIndent();
+            out.printil("}");
+
+            out.popIndent();
+            out.printil("}"); // finally
+            out.popIndent();
+            out.printil("}"); // invoke method
+            out.popIndent();
+            out.printil("}"); // helper class
+            out.popIndent();
+        }
+
+        public String toString() {
+            return classBuffer.toString();
+        }
+
+        public void adjustJavaLines(int offset) {
+            for (int i = 0; i < fragments.size(); i++) {
+                Fragment fragment = (Fragment)fragments.get(i);
+                fragment.getGenBuffer().adjustJavaLines(offset);
+            }
+        }
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java
new file mode 100644
index 0000000..297e7aa
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.util.*;
+import javax.servlet.jsp.tagext.FunctionInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagFileInfo;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.JasperException;
+
+/**
+ * Class responsible for generating an implicit tag library containing tag
+ * handlers corresponding to the tag files in "/WEB-INF/tags/" or a 
+ * subdirectory of it.
+ *
+ * @author Jan Luehe
+ */
+class ImplicitTagLibraryInfo extends TagLibraryInfo {
+
+    private static final String WEB_INF_TAGS = "/WEB-INF/tags";
+    private static final String TAG_FILE_SUFFIX = ".tag";
+    private static final String TAGX_FILE_SUFFIX = ".tagx";
+    private static final String TAGS_SHORTNAME = "tags";
+    private static final String TLIB_VERSION = "1.0";
+    private static final String JSP_VERSION = "2.0";
+
+    // Maps tag names to tag file paths
+    private Hashtable tagFileMap;
+
+    private ParserController pc;
+    private Vector vec;
+
+    /**
+     * Constructor.
+     */
+    public ImplicitTagLibraryInfo(JspCompilationContext ctxt,
+				  ParserController pc,
+				  String prefix,
+				  String tagdir,
+				  ErrorDispatcher err) throws JasperException {
+        super(prefix, null);
+	this.pc = pc;
+	this.tagFileMap = new Hashtable();
+	this.vec = new Vector();
+
+        // Implicit tag libraries have no functions:
+        this.functions = new FunctionInfo[0];
+
+	tlibversion = TLIB_VERSION;
+	jspversion = JSP_VERSION;
+
+	if (!tagdir.startsWith(WEB_INF_TAGS)) {
+	    err.jspError("jsp.error.invalid.tagdir", tagdir);
+	}
+	
+	// Determine the value of the <short-name> subelement of the
+	// "imaginary" <taglib> element
+	if (tagdir.equals(WEB_INF_TAGS)
+	        || tagdir.equals( WEB_INF_TAGS + "/")) {
+	    shortname = TAGS_SHORTNAME;
+	} else {
+	    shortname = tagdir.substring(WEB_INF_TAGS.length());
+	    shortname = shortname.replace('/', '-');
+	}
+
+	// Populate mapping of tag names to tag file paths
+	Set dirList = ctxt.getResourcePaths(tagdir);
+	if (dirList != null) {
+	    Iterator it = dirList.iterator();
+	    while (it.hasNext()) {
+		String path = (String) it.next();
+		if (path.endsWith(TAG_FILE_SUFFIX)
+		        || path.endsWith(TAGX_FILE_SUFFIX)) {
+		    /*
+		     * Use the filename of the tag file, without the .tag or
+		     * .tagx extension, respectively, as the <name> subelement
+		     * of the "imaginary" <tag-file> element
+		     */
+		    String suffix = path.endsWith(TAG_FILE_SUFFIX) ?
+			TAG_FILE_SUFFIX : TAGX_FILE_SUFFIX; 
+		    String tagName = path.substring(path.lastIndexOf("/") + 1);
+		    tagName = tagName.substring(0,
+						tagName.lastIndexOf(suffix));
+		    tagFileMap.put(tagName, path);
+		}
+	    }
+	}
+    }
+
+    /**
+     * Checks to see if the given tag name maps to a tag file path,
+     * and if so, parses the corresponding tag file.
+     *
+     * @return The TagFileInfo corresponding to the given tag name, or null if
+     * the given tag name is not implemented as a tag file
+     */
+    public TagFileInfo getTagFile(String shortName) {
+
+	TagFileInfo tagFile = super.getTagFile(shortName);
+	if (tagFile == null) {
+	    String path = (String) tagFileMap.get(shortName);
+	    if (path == null) {
+		return null;
+	    }
+
+	    TagInfo tagInfo = null;
+	    try {
+		tagInfo = TagFileProcessor.parseTagFileDirectives(pc,
+								  shortName,
+								  path,
+								  this);
+	    } catch (JasperException je) {
+		throw new RuntimeException(je.toString());
+	    }
+
+	    tagFile = new TagFileInfo(shortName, path, tagInfo);
+	    vec.addElement(tagFile);
+
+	    this.tagFiles = new TagFileInfo[vec.size()];
+	    vec.copyInto(this.tagFiles);
+	}
+
+	return tagFile;
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/JDTCompiler.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/JDTCompiler.java
new file mode 100644
index 0000000..b5b5257
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/JDTCompiler.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.jasper.JasperException;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.internal.compiler.ClassFile;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.Compiler;
+import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
+import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
+import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
+import org.eclipse.jdt.internal.compiler.IProblemFactory;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
+
+/**
+ * JDT class compiler. This compiler will load source dependencies from the
+ * context classloader, reducing dramatically disk access during 
+ * the compilation process.
+ *
+ * @author Cocoon2
+ * @author Remy Maucherat
+ */
+public class JDTCompiler extends org.apache.jasper.compiler.Compiler {
+
+    
+    /** 
+     * Compile the servlet from .java file to .class file
+     */
+    protected void generateClass(String[] smap)
+        throws FileNotFoundException, JasperException, Exception {
+
+        long t1 = 0;
+        if (log.isDebugEnabled()) {
+            t1 = System.currentTimeMillis();
+        }
+        
+        final String sourceFile = ctxt.getServletJavaFileName();
+        final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
+        String packageName = ctxt.getServletPackageName();
+        final String targetClassName = 
+            ((packageName.length() != 0) ? (packageName + ".") : "") 
+                    + ctxt.getServletClassName();
+        final ClassLoader classLoader = ctxt.getJspLoader();
+        String[] fileNames = new String[] {sourceFile};
+        String[] classNames = new String[] {targetClassName};
+        final ArrayList problemList = new ArrayList();
+        
+        class CompilationUnit implements ICompilationUnit {
+
+            String className;
+            String sourceFile;
+
+            CompilationUnit(String sourceFile, String className) {
+                this.className = className;
+                this.sourceFile = sourceFile;
+            }
+
+            public char[] getFileName() {
+                return className.toCharArray();
+            }
+            
+            public char[] getContents() {
+                char[] result = null;
+                try {
+                    InputStreamReader isReader =
+                        new InputStreamReader(new FileInputStream(sourceFile),
+                                ctxt.getOptions().getJavaEncoding());
+                    Reader reader = new BufferedReader(isReader);
+                    if (reader != null) {
+                        char[] chars = new char[8192];
+                        StringBuffer buf = new StringBuffer();
+                        int count;
+                        while ((count = reader.read(chars, 0, 
+                                                    chars.length)) > 0) {
+                            buf.append(chars, 0, count);
+                        }
+                        result = new char[buf.length()];
+                        buf.getChars(0, result.length, result, 0);
+                    }
+                } catch (IOException e) {
+                    log.error("Compilation error", e);
+                }
+                return result;
+            }
+            
+            public char[] getMainTypeName() {
+                int dot = className.lastIndexOf('.');
+                if (dot > 0) {
+                    return className.substring(dot + 1).toCharArray();
+                }
+                return className.toCharArray();
+            }
+            
+            public char[][] getPackageName() {
+                StringTokenizer izer = 
+                    new StringTokenizer(className, ".");
+                char[][] result = new char[izer.countTokens()-1][];
+                for (int i = 0; i < result.length; i++) {
+                    String tok = izer.nextToken();
+                    result[i] = tok.toCharArray();
+                }
+                return result;
+            }
+        }
+
+        final INameEnvironment env = new INameEnvironment() {
+
+                public NameEnvironmentAnswer 
+                    findType(char[][] compoundTypeName) {
+                    String result = "";
+                    String sep = "";
+                    for (int i = 0; i < compoundTypeName.length; i++) {
+                        result += sep;
+                        result += new String(compoundTypeName[i]);
+                        sep = ".";
+                    }
+                    return findType(result);
+                }
+
+                public NameEnvironmentAnswer 
+                    findType(char[] typeName, 
+                             char[][] packageName) {
+                        String result = "";
+                        String sep = "";
+                        for (int i = 0; i < packageName.length; i++) {
+                            result += sep;
+                            result += new String(packageName[i]);
+                            sep = ".";
+                        }
+                        result += sep;
+                        result += new String(typeName);
+                        return findType(result);
+                }
+                
+                private NameEnvironmentAnswer findType(String className) {
+
+                    InputStream is = null;
+                    try {
+                        if (className.equals(targetClassName)) {
+                            ICompilationUnit compilationUnit = 
+                                new CompilationUnit(sourceFile, className);
+                            return 
+                                new NameEnvironmentAnswer(compilationUnit, null);
+                        }
+                        String resourceName = 
+                            className.replace('.', '/') + ".class";
+                        is = classLoader.getResourceAsStream(resourceName);
+                        if (is != null) {
+                            byte[] classBytes;
+                            byte[] buf = new byte[8192];
+                            ByteArrayOutputStream baos = 
+                                new ByteArrayOutputStream(buf.length);
+                            int count;
+                            while ((count = is.read(buf, 0, buf.length)) > 0) {
+                                baos.write(buf, 0, count);
+                            }
+                            baos.flush();
+                            classBytes = baos.toByteArray();
+                            char[] fileName = className.toCharArray();
+                            ClassFileReader classFileReader = 
+                                new ClassFileReader(classBytes, fileName, 
+                                                    true);
+                            return 
+                                new NameEnvironmentAnswer(classFileReader, null);
+                        }
+                    } catch (IOException exc) {
+                        log.error("Compilation error", exc);
+                    } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException exc) {
+                        log.error("Compilation error", exc);
+                    } finally {
+                        if (is != null) {
+                            try {
+                                is.close();
+                            } catch (IOException exc) {
+                                // Ignore
+                            }
+                        }
+                    }
+                    return null;
+                }
+
+                private boolean isPackage(String result) {
+                    if (result.equals(targetClassName)) {
+                        return false;
+                    }
+                    String resourceName = result.replace('.', '/') + ".class";
+                    InputStream is = 
+                        classLoader.getResourceAsStream(resourceName);
+                    return is == null;
+                }
+
+                public boolean isPackage(char[][] parentPackageName, 
+                                         char[] packageName) {
+                    String result = "";
+                    String sep = "";
+                    if (parentPackageName != null) {
+                        for (int i = 0; i < parentPackageName.length; i++) {
+                            result += sep;
+                            String str = new String(parentPackageName[i]);
+                            result += str;
+                            sep = ".";
+                        }
+                    }
+                    String str = new String(packageName);
+                    if (Character.isUpperCase(str.charAt(0))) {
+                        if (!isPackage(result)) {
+                            return false;
+                        }
+                    }
+                    result += sep;
+                    result += str;
+                    return isPackage(result);
+                }
+
+                public void cleanup() {
+                }
+
+            };
+
+        final IErrorHandlingPolicy policy = 
+            DefaultErrorHandlingPolicies.proceedWithAllProblems();
+
+        final Map settings = new HashMap();
+        settings.put(CompilerOptions.OPTION_LineNumberAttribute,
+                     CompilerOptions.GENERATE);
+        settings.put(CompilerOptions.OPTION_SourceFileAttribute,
+                     CompilerOptions.GENERATE);
+        settings.put(CompilerOptions.OPTION_ReportDeprecation,
+                     CompilerOptions.IGNORE);
+        if (ctxt.getOptions().getJavaEncoding() != null) {
+            settings.put(CompilerOptions.OPTION_Encoding,
+                    ctxt.getOptions().getJavaEncoding());
+        }
+        if (ctxt.getOptions().getClassDebugInfo()) {
+            settings.put(CompilerOptions.OPTION_LocalVariableAttribute,
+                         CompilerOptions.GENERATE);
+        }
+
+        // Source JVM
+        if(ctxt.getOptions().getCompilerSourceVM() != null) {
+            String opt = ctxt.getOptions().getCompilerSourceVM();
+            if(opt.equals("1.1")) {
+                settings.put(CompilerOptions.OPTION_Source,
+                             CompilerOptions.VERSION_1_1);
+            } else if(opt.equals("1.2")) {
+                settings.put(CompilerOptions.OPTION_Source,
+                             CompilerOptions.VERSION_1_2);
+            } else if(opt.equals("1.3")) { 
+                settings.put(CompilerOptions.OPTION_Source,
+                             CompilerOptions.VERSION_1_3);
+            } else if(opt.equals("1.4")) {
+                settings.put(CompilerOptions.OPTION_Source,
+                             CompilerOptions.VERSION_1_4);
+            } else if(opt.equals("1.5")) {
+                settings.put(CompilerOptions.OPTION_Source,
+                             CompilerOptions.VERSION_1_5);
+            } else {
+                log.warn("Unknown source VM " + opt + " ignored.");
+                settings.put(CompilerOptions.OPTION_Source,
+                        CompilerOptions.VERSION_1_5);
+            }
+        } else {
+            // Default to 1.5
+            settings.put(CompilerOptions.OPTION_Source,
+                    CompilerOptions.VERSION_1_5);
+        }
+        
+        // Target JVM
+        if(ctxt.getOptions().getCompilerTargetVM() != null) {
+            String opt = ctxt.getOptions().getCompilerTargetVM();
+            if(opt.equals("1.1")) {
+                settings.put(CompilerOptions.OPTION_TargetPlatform,
+                             CompilerOptions.VERSION_1_1);
+            } else if(opt.equals("1.2")) {
+                settings.put(CompilerOptions.OPTION_TargetPlatform,
+                             CompilerOptions.VERSION_1_2);
+            } else if(opt.equals("1.3")) { 
+                settings.put(CompilerOptions.OPTION_TargetPlatform,
+                             CompilerOptions.VERSION_1_3);
+            } else if(opt.equals("1.4")) {
+                settings.put(CompilerOptions.OPTION_TargetPlatform,
+                             CompilerOptions.VERSION_1_4);
+            } else if(opt.equals("1.5")) {
+                settings.put(CompilerOptions.OPTION_TargetPlatform,
+                             CompilerOptions.VERSION_1_5);
+            } else {
+                log.warn("Unknown target VM " + opt + " ignored.");
+                settings.put(CompilerOptions.OPTION_TargetPlatform,
+                        CompilerOptions.VERSION_1_5);
+            }
+        } else {
+            // Default to 1.5
+            settings.put(CompilerOptions.OPTION_TargetPlatform,
+                    CompilerOptions.VERSION_1_5);
+        }
+
+        final IProblemFactory problemFactory = 
+            new DefaultProblemFactory(Locale.getDefault());
+        
+        final ICompilerRequestor requestor = new ICompilerRequestor() {
+                public void acceptResult(CompilationResult result) {
+                    try {
+                        if (result.hasProblems()) {
+                            IProblem[] problems = result.getProblems();
+                            for (int i = 0; i < problems.length; i++) {
+                                IProblem problem = problems[i];
+                                if (problem.isError()) {
+                                    String name = 
+                                        new String(problems[i].getOriginatingFileName());
+                                    try {
+                                        problemList.add(ErrorDispatcher.createJavacError
+                                                (name, pageNodes, new StringBuffer(problem.getMessage()), 
+                                                        problem.getSourceLineNumber()));
+                                    } catch (JasperException e) {
+                                        log.error("Error visiting node", e);
+                                    }
+                                }
+                            }
+                        }
+                        if (problemList.isEmpty()) {
+                            ClassFile[] classFiles = result.getClassFiles();
+                            for (int i = 0; i < classFiles.length; i++) {
+                                ClassFile classFile = classFiles[i];
+                                char[][] compoundName = 
+                                    classFile.getCompoundName();
+                                String className = "";
+                                String sep = "";
+                                for (int j = 0; 
+                                     j < compoundName.length; j++) {
+                                    className += sep;
+                                    className += new String(compoundName[j]);
+                                    sep = ".";
+                                }
+                                byte[] bytes = classFile.getBytes();
+                                String outFile = outputDir + "/" + 
+                                    className.replace('.', '/') + ".class";
+                                FileOutputStream fout = 
+                                    new FileOutputStream(outFile);
+                                BufferedOutputStream bos = 
+                                    new BufferedOutputStream(fout);
+                                bos.write(bytes);
+                                bos.close();
+                            }
+                        }
+                    } catch (IOException exc) {
+                        log.error("Compilation error", exc);
+                    }
+                }
+            };
+
+        ICompilationUnit[] compilationUnits = 
+            new ICompilationUnit[classNames.length];
+        for (int i = 0; i < compilationUnits.length; i++) {
+            String className = classNames[i];
+            compilationUnits[i] = new CompilationUnit(fileNames[i], className);
+        }
+        Compiler compiler = new Compiler(env,
+                                         policy,
+                                         settings,
+                                         requestor,
+                                         problemFactory);
+        compiler.compile(compilationUnits);
+
+        if (!ctxt.keepGenerated()) {
+            File javaFile = new File(ctxt.getServletJavaFileName());
+            javaFile.delete();
+        }
+    
+        if (!problemList.isEmpty()) {
+            JavacErrorDetail[] jeds = 
+                (JavacErrorDetail[]) problemList.toArray(new JavacErrorDetail[0]);
+            errDispatcher.javacError(jeds);
+        }
+        
+        if( log.isDebugEnabled() ) {
+            long t2=System.currentTimeMillis();
+            log.debug("Compiled " + ctxt.getServletJavaFileName() + " "
+                      + (t2-t1) + "ms");
+        }
+
+        if (ctxt.isPrototypeMode()) {
+            return;
+        }
+
+        // JSR45 Support
+        if (! options.isSmapSuppressed()) {
+            SmapUtil.installSmap(smap);
+        }
+        
+    }
+    
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/JasperTagInfo.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/JasperTagInfo.java
new file mode 100644
index 0000000..6a67152
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/JasperTagInfo.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import javax.servlet.jsp.tagext.*;
+
+/**
+ * TagInfo extension used by tag handlers that are implemented via tag files.
+ * This class provides access to the name of the Map used to store the
+ * dynamic attribute names and values passed to the custom action invocation.
+ * This information is used by the code generator.
+ */
+class JasperTagInfo extends TagInfo {
+
+    private String dynamicAttrsMapName;
+
+    public JasperTagInfo(String tagName,
+			 String tagClassName,
+			 String bodyContent,
+			 String infoString,
+			 TagLibraryInfo taglib,
+			 TagExtraInfo tagExtraInfo,
+			 TagAttributeInfo[] attributeInfo,
+			 String displayName,
+			 String smallIcon,
+			 String largeIcon,
+			 TagVariableInfo[] tvi,
+			 String mapName) {
+
+	super(tagName, tagClassName, bodyContent, infoString, taglib,
+	      tagExtraInfo, attributeInfo, displayName, smallIcon, largeIcon,
+	      tvi);
+	this.dynamicAttrsMapName = mapName;
+    }
+
+    public String getDynamicAttributesMapName() {
+	return dynamicAttrsMapName;
+    }
+
+    public boolean hasDynamicAttributes() {
+        return dynamicAttrsMapName != null;
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/JavacErrorDetail.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/JavacErrorDetail.java
new file mode 100644
index 0000000..bc7bef3
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/JavacErrorDetail.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+/**
+ * Class providing details about a javac compilation error.
+ *
+ * @author Jan Luehe
+ * @author Kin-man Chung
+ */
+public class JavacErrorDetail {
+
+    private String javaFileName;
+    private int javaLineNum;
+    private String jspFileName;
+    private int jspBeginLineNum;
+    private StringBuffer errMsg;
+
+    /**
+     * Constructor.
+     *
+     * @param javaFileName The name of the Java file in which the 
+     * compilation error occurred
+     * @param javaLineNum The compilation error line number
+     * @param errMsg The compilation error message
+     */
+    public JavacErrorDetail(String javaFileName,
+			    int javaLineNum,
+			    StringBuffer errMsg) {
+
+	this.javaFileName = javaFileName;
+	this.javaLineNum = javaLineNum;
+	this.errMsg = errMsg;
+        this.jspBeginLineNum = -1;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param javaFileName The name of the Java file in which the 
+     * compilation error occurred
+     * @param javaLineNum The compilation error line number
+     * @param jspFileName The name of the JSP file from which the Java source
+     * file was generated
+     * @param jspBeginLineNum The start line number of the JSP element
+     * responsible for the compilation error
+     * @param errMsg The compilation error message
+     */
+    public JavacErrorDetail(String javaFileName,
+			    int javaLineNum,
+			    String jspFileName,
+			    int jspBeginLineNum,
+			    StringBuffer errMsg) {
+
+        this(javaFileName, javaLineNum, errMsg);
+	this.jspFileName = jspFileName;
+	this.jspBeginLineNum = jspBeginLineNum;
+    }
+
+    /**
+     * Gets the name of the Java source file in which the compilation error
+     * occurred.
+     *
+     * @return Java source file name
+     */
+    public String getJavaFileName() {
+	return this.javaFileName;
+    }
+
+    /**
+     * Gets the compilation error line number.
+     * 
+     * @return Compilation error line number
+     */
+    public int getJavaLineNumber() {
+	return this.javaLineNum;
+    }
+
+    /**
+     * Gets the name of the JSP file from which the Java source file was
+     * generated.
+     *
+     * @return JSP file from which the Java source file was generated.
+     */
+    public String getJspFileName() {
+	return this.jspFileName;
+    }
+
+    /**
+     * Gets the start line number (in the JSP file) of the JSP element
+     * responsible for the compilation error.
+     *
+     * @return Start line number of the JSP element responsible for the
+     * compilation error
+     */
+    public int getJspBeginLineNumber() {
+	return this.jspBeginLineNum;
+    }
+
+    /**
+     * Gets the compilation error message.
+     *
+     * @return Compilation error message
+     */
+    public String getErrorMessage() {
+	return this.errMsg.toString();
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/JspConfig.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspConfig.java
new file mode 100644
index 0000000..e60cf5f
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspConfig.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.Vector;
+import java.net.URL;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.apache.jasper.xmlparser.TreeNode;
+import org.xml.sax.InputSource;
+
+/**
+ * Handles the jsp-config element in WEB_INF/web.xml.  This is used
+ * for specifying the JSP configuration information on a JSP page
+ *
+ * @author Kin-man Chung
+ */
+
+public class JspConfig {
+
+    private static final String WEB_XML = "/WEB-INF/web.xml";
+
+    // Logger
+    private Log log = LogFactory.getLog(JspConfig.class);
+
+    private Vector jspProperties = null;
+    private ServletContext ctxt;
+    private boolean initialized = false;
+
+    private String defaultIsXml = null;		// unspecified
+    private String defaultIsELIgnored = null;	// unspecified
+    private String defaultIsScriptingInvalid = "false";
+    private JspProperty defaultJspProperty;
+
+    public JspConfig(ServletContext ctxt) {
+	this.ctxt = ctxt;
+    }
+
+    private void processWebDotXml(ServletContext ctxt) throws JasperException {
+
+        InputStream is = null;
+
+        try {
+            URL uri = ctxt.getResource(WEB_XML);
+            if (uri == null) {
+	        // no web.xml
+                return;
+	    }
+
+            is = uri.openStream();
+            InputSource ip = new InputSource(is);
+            ip.setSystemId(uri.toExternalForm()); 
+
+            ParserUtils pu = new ParserUtils();
+	    TreeNode webApp = pu.parseXMLDocument(WEB_XML, ip);
+
+	    if (webApp == null
+                    || !"2.4".equals(webApp.findAttribute("version"))) {
+	        defaultIsELIgnored = "true";
+	        return;
+	    }
+	    TreeNode jspConfig = webApp.findChild("jsp-config");
+	    if (jspConfig == null) {
+	        return;
+	    }
+
+            jspProperties = new Vector();
+            Iterator jspPropertyList = jspConfig.findChildren("jsp-property-group");
+            while (jspPropertyList.hasNext()) {
+
+                TreeNode element = (TreeNode) jspPropertyList.next();
+                Iterator list = element.findChildren();
+
+                Vector urlPatterns = new Vector();
+                String pageEncoding = null;
+                String scriptingInvalid = null;
+                String elIgnored = null;
+                String isXml = null;
+                Vector includePrelude = new Vector();
+                Vector includeCoda = new Vector();
+
+                while (list.hasNext()) {
+
+                    element = (TreeNode) list.next();
+                    String tname = element.getName();
+
+                    if ("url-pattern".equals(tname))
+                        urlPatterns.addElement( element.getBody() );
+                    else if ("page-encoding".equals(tname))
+                        pageEncoding = element.getBody();
+                    else if ("is-xml".equals(tname))
+                        isXml = element.getBody();
+                    else if ("el-ignored".equals(tname))
+                        elIgnored = element.getBody();
+                    else if ("scripting-invalid".equals(tname))
+                        scriptingInvalid = element.getBody();
+                    else if ("include-prelude".equals(tname))
+                        includePrelude.addElement(element.getBody());
+                    else if ("include-coda".equals(tname))
+                        includeCoda.addElement(element.getBody());
+                }
+
+                if (urlPatterns.size() == 0) {
+                    continue;
+                }
+ 
+                // Add one JspPropertyGroup for each URL Pattern.  This makes
+                // the matching logic easier.
+                for( int p = 0; p < urlPatterns.size(); p++ ) {
+                    String urlPattern = (String)urlPatterns.elementAt( p );
+                    String path = null;
+                    String extension = null;
+ 
+                    if (urlPattern.indexOf('*') < 0) {
+                        // Exact match
+                        path = urlPattern;
+                    } else {
+                        int i = urlPattern.lastIndexOf('/');
+                        String file;
+                        if (i >= 0) {
+                            path = urlPattern.substring(0,i+1);
+                            file = urlPattern.substring(i+1);
+                        } else {
+                            file = urlPattern;
+                        }
+ 
+                        // pattern must be "*", or of the form "*.jsp"
+                        if (file.equals("*")) {
+                            extension = "*";
+                        } else if (file.startsWith("*.")) {
+                            extension = file.substring(file.indexOf('.')+1);
+                        }
+
+                        // The url patterns are reconstructed as the follwoing:
+                        // path != null, extension == null:  / or /foo/bar.ext
+                        // path == null, extension != null:  *.ext
+                        // path != null, extension == "*":   /foo/*
+                        boolean isStar = "*".equals(extension);
+                        if ((path == null && (extension == null || isStar))
+                                || (path != null && !isStar)) {
+                            if (log.isWarnEnabled()) {
+			        log.warn(Localizer.getMessage(
+                                    "jsp.warning.bad.urlpattern.propertygroup",
+                                    urlPattern));
+                            }
+                            continue;
+                        }
+                    }
+
+                    JspProperty property = new JspProperty(isXml,
+                                                           elIgnored,
+                                                           scriptingInvalid,
+                                                           pageEncoding,
+                                                           includePrelude,
+                                                           includeCoda);
+                    JspPropertyGroup propertyGroup =
+                        new JspPropertyGroup(path, extension, property);
+
+                    jspProperties.addElement(propertyGroup);
+                }
+            }
+        } catch (Exception ex) {
+            throw new JasperException(ex);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (Throwable t) {}
+            }
+        }
+    }
+
+    private void init() throws JasperException {
+
+	if (!initialized) {
+	    processWebDotXml(ctxt);
+	    defaultJspProperty = new JspProperty(defaultIsXml,
+						 defaultIsELIgnored,
+						 defaultIsScriptingInvalid,
+						 null, null, null);
+	    initialized = true;
+	}
+    }
+
+    /**
+     * Select the property group that has more restrictive url-pattern.
+     * In case of tie, select the first.
+     */
+    private JspPropertyGroup selectProperty(JspPropertyGroup prev,
+                                            JspPropertyGroup curr) {
+        if (prev == null) {
+            return curr;
+        }
+        if (prev.getExtension() == null) {
+            // exact match
+            return prev;
+        }
+        if (curr.getExtension() == null) {
+            // exact match
+            return curr;
+        }
+        String prevPath = prev.getPath();
+        String currPath = curr.getPath();
+        if (prevPath == null && currPath == null) {
+            // Both specifies a *.ext, keep the first one
+            return prev;
+        }
+        if (prevPath == null && currPath != null) {
+            return curr;
+        }
+        if (prevPath != null && currPath == null) {
+            return prev;
+        }
+        if (prevPath.length() >= currPath.length()) {
+            return prev;
+        }
+        return curr;
+    }
+            
+
+    /**
+     * Find a property that best matches the supplied resource.
+     * @param uri the resource supplied.
+     * @return a JspProperty indicating the best match, or some default.
+     */
+    public JspProperty findJspProperty(String uri) throws JasperException {
+
+	init();
+
+	// JSP Configuration settings do not apply to tag files	    
+	if (jspProperties == null || uri.endsWith(".tag")
+	        || uri.endsWith(".tagx")) {
+	    return defaultJspProperty;
+	}
+
+	String uriPath = null;
+	int index = uri.lastIndexOf('/');
+	if (index >=0 ) {
+	    uriPath = uri.substring(0, index+1);
+	}
+	String uriExtension = null;
+	index = uri.lastIndexOf('.');
+	if (index >=0) {
+	    uriExtension = uri.substring(index+1);
+	}
+
+	Vector includePreludes = new Vector();
+	Vector includeCodas = new Vector();
+
+	JspPropertyGroup isXmlMatch = null;
+	JspPropertyGroup elIgnoredMatch = null;
+	JspPropertyGroup scriptingInvalidMatch = null;
+	JspPropertyGroup pageEncodingMatch = null;
+
+	Iterator iter = jspProperties.iterator();
+	while (iter.hasNext()) {
+
+	    JspPropertyGroup jpg = (JspPropertyGroup) iter.next();
+	    JspProperty jp = jpg.getJspProperty();
+
+             // (arrays will be the same length)
+             String extension = jpg.getExtension();
+             String path = jpg.getPath();
+ 
+             if (extension == null) {
+                 // exact match pattern: /a/foo.jsp
+                 if (!uri.equals(path)) {
+                     // not matched;
+                     continue;
+                 }
+             } else {
+                 // Matching patterns *.ext or /p/*
+                 if (path != null && uriPath != null &&
+                         ! uriPath.startsWith(path)) {
+                     // not matched
+                     continue;
+                 }
+                 if (!extension.equals("*") &&
+                                 !extension.equals(uriExtension)) {
+                     // not matched
+                     continue;
+                 }
+             }
+             // We have a match
+             // Add include-preludes and include-codas
+             if (jp.getIncludePrelude() != null) {
+                 includePreludes.addAll(jp.getIncludePrelude());
+             }
+             if (jp.getIncludeCoda() != null) {
+                 includeCodas.addAll(jp.getIncludeCoda());
+             }
+
+             // If there is a previous match for the same property, remember
+             // the one that is more restrictive.
+             if (jp.isXml() != null) {
+                 isXmlMatch = selectProperty(isXmlMatch, jpg);
+             }
+             if (jp.isELIgnored() != null) {
+                 elIgnoredMatch = selectProperty(elIgnoredMatch, jpg);
+             }
+             if (jp.isScriptingInvalid() != null) {
+                 scriptingInvalidMatch =
+                     selectProperty(scriptingInvalidMatch, jpg);
+             }
+             if (jp.getPageEncoding() != null) {
+                 pageEncodingMatch = selectProperty(pageEncodingMatch, jpg);
+             }
+	}
+
+
+	String isXml = defaultIsXml;
+	String isELIgnored = defaultIsELIgnored;
+	String isScriptingInvalid = defaultIsScriptingInvalid;
+	String pageEncoding = null;
+
+	if (isXmlMatch != null) {
+	    isXml = isXmlMatch.getJspProperty().isXml();
+	}
+	if (elIgnoredMatch != null) {
+	    isELIgnored = elIgnoredMatch.getJspProperty().isELIgnored();
+	}
+	if (scriptingInvalidMatch != null) {
+	    isScriptingInvalid =
+		scriptingInvalidMatch.getJspProperty().isScriptingInvalid();
+	}
+	if (pageEncodingMatch != null) {
+	    pageEncoding = pageEncodingMatch.getJspProperty().getPageEncoding();
+	}
+
+	return new JspProperty(isXml, isELIgnored, isScriptingInvalid,
+			       pageEncoding, includePreludes, includeCodas);
+    }
+
+    /**
+     * To find out if an uri matches an url pattern in jsp config.  If so,
+     * then the uri is a JSP page.  This is used primarily for jspc.
+     */
+    public boolean isJspPage(String uri) throws JasperException {
+
+        init();
+        if (jspProperties == null) {
+            return false;
+        }
+
+        String uriPath = null;
+        int index = uri.lastIndexOf('/');
+        if (index >=0 ) {
+            uriPath = uri.substring(0, index+1);
+        }
+        String uriExtension = null;
+        index = uri.lastIndexOf('.');
+        if (index >=0) {
+            uriExtension = uri.substring(index+1);
+        }
+
+        Iterator iter = jspProperties.iterator();
+        while (iter.hasNext()) {
+
+            JspPropertyGroup jpg = (JspPropertyGroup) iter.next();
+            JspProperty jp = jpg.getJspProperty();
+
+            String extension = jpg.getExtension();
+            String path = jpg.getPath();
+
+            if (extension == null) {
+                if (uri.equals(path)) {
+                    // There is an exact match
+                    return true;
+                }
+            } else {
+                if ((path == null || path.equals(uriPath)) &&
+                    (extension.equals("*") || extension.equals(uriExtension))) {
+                    // Matches *, *.ext, /p/*, or /p/*.ext
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    static class JspPropertyGroup {
+	private String path;
+	private String extension;
+	private JspProperty jspProperty;
+
+	JspPropertyGroup(String path, String extension,
+			 JspProperty jspProperty) {
+	    this.path = path;
+	    this.extension = extension;
+	    this.jspProperty = jspProperty;
+	}
+
+	public String getPath() {
+	    return path;
+	}
+
+	public String getExtension() {
+	    return extension;
+	}
+
+	public JspProperty getJspProperty() {
+	    return jspProperty;
+	}
+    }
+
+    static public class JspProperty {
+
+	private String isXml;
+	private String elIgnored;
+	private String scriptingInvalid;
+	private String pageEncoding;
+	private Vector includePrelude;
+	private Vector includeCoda;
+
+	public JspProperty(String isXml, String elIgnored,
+		    String scriptingInvalid, String pageEncoding,
+		    Vector includePrelude, Vector includeCoda) {
+
+	    this.isXml = isXml;
+	    this.elIgnored = elIgnored;
+	    this.scriptingInvalid = scriptingInvalid;
+	    this.pageEncoding = pageEncoding;
+	    this.includePrelude = includePrelude;
+	    this.includeCoda = includeCoda;
+	}
+
+	public String isXml() {
+	    return isXml;
+	}
+
+	public String isELIgnored() {
+	    return elIgnored;
+	}
+
+	public String isScriptingInvalid() {
+	    return scriptingInvalid;
+	}
+
+	public String getPageEncoding() {
+	    return pageEncoding;
+	}
+
+	public Vector getIncludePrelude() {
+	    return includePrelude;
+	}
+
+	public Vector getIncludeCoda() {
+	    return includeCoda;
+	}
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/JspDocumentParser.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspDocumentParser.java
new file mode 100644
index 0000000..fbb1636
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspDocumentParser.java
@@ -0,0 +1,1417 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.compiler;
+
+import java.io.CharArrayWriter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarFile;
+
+import javax.servlet.jsp.tagext.TagFileInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Class implementing a parser for a JSP document, that is, a JSP page in XML
+ * syntax.
+ *
+ * @author Jan Luehe
+ * @author Kin-man Chung
+ */
+
+class JspDocumentParser
+    extends DefaultHandler
+    implements LexicalHandler, TagConstants {
+
+    private static final String JSP_VERSION = "version";
+    private static final String LEXICAL_HANDLER_PROPERTY =
+        "http://xml.org/sax/properties/lexical-handler";
+    private static final String JSP_URI = "http://java.sun.com/JSP/Page";
+
+    private static final EnableDTDValidationException ENABLE_DTD_VALIDATION_EXCEPTION =
+        new EnableDTDValidationException(
+            "jsp.error.enable_dtd_validation",
+            null);
+
+    private ParserController parserController;
+    private JspCompilationContext ctxt;
+    private PageInfo pageInfo;
+    private String path;
+    private StringBuffer charBuffer;
+
+    // Node representing the XML element currently being parsed
+    private Node current;
+
+    /*
+     * Outermost (in the nesting hierarchy) node whose body is declared to be
+     * scriptless. If a node's body is declared to be scriptless, all its
+     * nested nodes must be scriptless, too.
+     */ 
+    private Node scriptlessBodyNode;
+
+    private Locator locator;
+
+    //Mark representing the start of the current element.  Note
+    //that locator.getLineNumber() and locator.getColumnNumber()
+    //return the line and column numbers for the character
+    //immediately _following_ the current element.  The underlying
+    //XMl parser eats white space that is not part of character
+    //data, so for Nodes that are not created from character data,
+    //this is the best we can do.  But when we parse character data,
+    //we get an accurate starting location by starting with startMark
+    //as set by the previous element, and updating it as we advance
+    //through the characters.
+    private Mark startMark;
+
+    // Flag indicating whether we are inside DTD declarations
+    private boolean inDTD;
+
+    private boolean isValidating;
+
+    private ErrorDispatcher err;
+    private boolean isTagFile;
+    private boolean directivesOnly;
+    private boolean isTop;
+
+    // Nesting level of Tag dependent bodies
+    private int tagDependentNesting = 0;
+    // Flag set to delay incrmenting tagDependentNesting until jsp:body
+    // is first encountered
+    private boolean tagDependentPending = false;
+
+    /*
+     * Constructor
+     */
+    public JspDocumentParser(
+        ParserController pc,
+        String path,
+        boolean isTagFile,
+        boolean directivesOnly) {
+        this.parserController = pc;
+        this.ctxt = pc.getJspCompilationContext();
+        this.pageInfo = pc.getCompiler().getPageInfo();
+        this.err = pc.getCompiler().getErrorDispatcher();
+        this.path = path;
+        this.isTagFile = isTagFile;
+        this.directivesOnly = directivesOnly;
+        this.isTop = true;
+    }
+
+    /*
+     * Parses a JSP document by responding to SAX events.
+     *
+     * @throws JasperException
+     */
+    public static Node.Nodes parse(
+        ParserController pc,
+        String path,
+        JarFile jarFile,
+        Node parent,
+        boolean isTagFile,
+        boolean directivesOnly,
+        String pageEnc,
+        String jspConfigPageEnc,
+        boolean isEncodingSpecifiedInProlog)
+        throws JasperException {
+
+        JspDocumentParser jspDocParser =
+            new JspDocumentParser(pc, path, isTagFile, directivesOnly);
+        Node.Nodes pageNodes = null;
+
+        try {
+
+            // Create dummy root and initialize it with given page encodings
+            Node.Root dummyRoot = new Node.Root(null, parent, true);
+            dummyRoot.setPageEncoding(pageEnc);
+            dummyRoot.setJspConfigPageEncoding(jspConfigPageEnc);
+            dummyRoot.setIsEncodingSpecifiedInProlog(
+                isEncodingSpecifiedInProlog);
+            jspDocParser.current = dummyRoot;
+            if (parent == null) {
+                jspDocParser.addInclude(
+                    dummyRoot,
+                    jspDocParser.pageInfo.getIncludePrelude());
+            } else {
+                jspDocParser.isTop = false;
+            }
+
+            // Parse the input
+            SAXParser saxParser = getSAXParser(false, jspDocParser);
+            InputStream inStream = null;
+            try {
+                inStream = JspUtil.getInputStream(path, jarFile,
+                                                  jspDocParser.ctxt,
+                                                  jspDocParser.err);
+                saxParser.parse(new InputSource(inStream), jspDocParser);
+            } catch (EnableDTDValidationException e) {
+                saxParser = getSAXParser(true, jspDocParser);
+                jspDocParser.isValidating = true;
+                if (inStream != null) {
+                    try {
+                        inStream.close();
+                    } catch (Exception any) {
+                    }
+                }
+                inStream = JspUtil.getInputStream(path, jarFile,
+                                                  jspDocParser.ctxt,
+                                                  jspDocParser.err);
+                saxParser.parse(new InputSource(inStream), jspDocParser);
+            } finally {
+                if (inStream != null) {
+                    try {
+                        inStream.close();
+                    } catch (Exception any) {
+                    }
+                }
+            }
+
+            if (parent == null) {
+                jspDocParser.addInclude(
+                    dummyRoot,
+                    jspDocParser.pageInfo.getIncludeCoda());
+            }
+
+            // Create Node.Nodes from dummy root
+            pageNodes = new Node.Nodes(dummyRoot);
+
+        } catch (IOException ioe) {
+            jspDocParser.err.jspError("jsp.error.data.file.read", path, ioe);
+        } catch (SAXParseException e) {
+            jspDocParser.err.jspError
+                (new Mark(jspDocParser.ctxt, path, e.getLineNumber(),
+                          e.getColumnNumber()),
+                 e.getMessage());
+        } catch (Exception e) {
+            jspDocParser.err.jspError(e);
+        }
+
+        return pageNodes;
+    }
+
+    /*
+     * Processes the given list of included files.
+     *
+     * This is used to implement the include-prelude and include-coda
+     * subelements of the jsp-config element in web.xml
+     */
+    private void addInclude(Node parent, List files) throws SAXException {
+        if (files != null) {
+            Iterator iter = files.iterator();
+            while (iter.hasNext()) {
+                String file = (String)iter.next();
+                AttributesImpl attrs = new AttributesImpl();
+                attrs.addAttribute("", "file", "file", "CDATA", file);
+
+                // Create a dummy Include directive node
+                    Node includeDir =
+                        new Node.IncludeDirective(attrs, null, // XXX
+    parent);
+                processIncludeDirective(file, includeDir);
+            }
+        }
+    }
+
+    /*
+     * Receives notification of the start of an element.
+     *
+     * This method assigns the given tag attributes to one of 3 buckets:
+     * 
+     * - "xmlns" attributes that represent (standard or custom) tag libraries.
+     * - "xmlns" attributes that do not represent tag libraries.
+     * - all remaining attributes.
+     *
+     * For each "xmlns" attribute that represents a custom tag library, the
+     * corresponding TagLibraryInfo object is added to the set of custom
+     * tag libraries.
+     */
+    public void startElement(
+        String uri,
+        String localName,
+        String qName,
+        Attributes attrs)
+        throws SAXException {
+
+        AttributesImpl taglibAttrs = null;
+        AttributesImpl nonTaglibAttrs = null;
+        AttributesImpl nonTaglibXmlnsAttrs = null;
+
+        processChars();
+
+        checkPrefixes(uri, qName, attrs);
+
+        if (directivesOnly &&
+            !(JSP_URI.equals(uri) && localName.startsWith(DIRECTIVE_ACTION))) {
+            return;
+        }
+
+        // jsp:text must not have any subelements
+        if (JSP_URI.equals(uri) && TEXT_ACTION.equals(current.getLocalName())) {
+            throw new SAXParseException(
+                Localizer.getMessage("jsp.error.text.has_subelement"),
+                locator);
+        }
+
+        startMark = new Mark(ctxt, path, locator.getLineNumber(),
+                             locator.getColumnNumber());
+
+        if (attrs != null) {
+            /*
+             * Notice that due to a bug in the underlying SAX parser, the
+             * attributes must be enumerated in descending order. 
+             */
+            boolean isTaglib = false;
+            for (int i = attrs.getLength() - 1; i >= 0; i--) {
+                isTaglib = false;
+                String attrQName = attrs.getQName(i);
+                if (!attrQName.startsWith("xmlns")) {
+                    if (nonTaglibAttrs == null) {
+                        nonTaglibAttrs = new AttributesImpl();
+                    }
+                    nonTaglibAttrs.addAttribute(
+                        attrs.getURI(i),
+                        attrs.getLocalName(i),
+                        attrs.getQName(i),
+                        attrs.getType(i),
+                        attrs.getValue(i));
+                } else {
+                    if (attrQName.startsWith("xmlns:jsp")) {
+                        isTaglib = true;
+                    } else {
+                        String attrUri = attrs.getValue(i);
+                        // TaglibInfo for this uri already established in
+                        // startPrefixMapping
+                        isTaglib = pageInfo.hasTaglib(attrUri);
+                    }
+                    if (isTaglib) {
+                        if (taglibAttrs == null) {
+                            taglibAttrs = new AttributesImpl();
+                        }
+                        taglibAttrs.addAttribute(
+                            attrs.getURI(i),
+                            attrs.getLocalName(i),
+                            attrs.getQName(i),
+                            attrs.getType(i),
+                            attrs.getValue(i));
+                    } else {
+                        if (nonTaglibXmlnsAttrs == null) {
+                            nonTaglibXmlnsAttrs = new AttributesImpl();
+                        }
+                        nonTaglibXmlnsAttrs.addAttribute(
+                            attrs.getURI(i),
+                            attrs.getLocalName(i),
+                            attrs.getQName(i),
+                            attrs.getType(i),
+                            attrs.getValue(i));
+                    }
+                }
+            }
+        }
+
+        Node node = null;
+
+        if (tagDependentPending && JSP_URI.equals(uri) &&
+                     localName.equals(BODY_ACTION)) {
+            tagDependentPending = false;
+            tagDependentNesting++;
+            current =
+                parseStandardAction(
+                    qName,
+                    localName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    startMark,
+                    current);
+            return;
+        }
+
+        if (tagDependentPending && JSP_URI.equals(uri) &&
+                     localName.equals(ATTRIBUTE_ACTION)) {
+            current =
+                parseStandardAction(
+                    qName,
+                    localName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    startMark,
+                    current);
+            return;
+        }
+
+        if (tagDependentPending) {
+            tagDependentPending = false;
+            tagDependentNesting++;
+        }
+
+        if (tagDependentNesting > 0) {
+            node =
+                new Node.UninterpretedTag(
+                    qName,
+                    localName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    startMark,
+                    current);
+        } else if (JSP_URI.equals(uri)) {
+            node =
+                parseStandardAction(
+                    qName,
+                    localName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    startMark,
+                    current);
+        } else {
+            node =
+                parseCustomAction(
+                    qName,
+                    localName,
+                    uri,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    startMark,
+                    current);
+            if (node == null) {
+                node =
+                    new Node.UninterpretedTag(
+                        qName,
+                        localName,
+                        nonTaglibAttrs,
+                        nonTaglibXmlnsAttrs,
+                        taglibAttrs,
+                        startMark,
+                        current);
+            } else {
+                // custom action
+                String bodyType = getBodyType((Node.CustomTag) node);
+
+                if (scriptlessBodyNode == null
+                        && bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)) {
+                    scriptlessBodyNode = node;
+                }
+                else if (TagInfo.BODY_CONTENT_TAG_DEPENDENT.equalsIgnoreCase(bodyType)) {
+                    tagDependentPending = true;
+                }
+            }
+        }
+
+        current = node;
+    }
+
+    /*
+     * Receives notification of character data inside an element.
+     *
+     * The SAX does not call this method with all of the template text, but may
+     * invoke this method with chunks of it.  This is a problem when we try
+     * to determine if the text contains only whitespaces, or when we are
+     * looking for an EL expression string.  Therefore it is necessary to
+     * buffer and concatenate the chunks and process the concatenated text 
+     * later (at beginTag and endTag)
+     *
+     * @param buf The characters
+     * @param offset The start position in the character array
+     * @param len The number of characters to use from the character array
+     *
+     * @throws SAXException
+     */
+    public void characters(char[] buf, int offset, int len) {
+
+        if (charBuffer == null) {
+            charBuffer = new StringBuffer();
+        }
+        charBuffer.append(buf, offset, len);
+    }
+
+    private void processChars() throws SAXException {
+
+        if (charBuffer == null) {
+            return;
+        }
+
+        /*
+         * JSP.6.1.1: All textual nodes that have only white space are to be
+         * dropped from the document, except for nodes in a jsp:text element,
+         * and any leading and trailing white-space-only textual nodes in a
+         * jsp:attribute whose 'trim' attribute is set to FALSE, which are to
+         * be kept verbatim.
+         * JSP.6.2.3 defines white space characters.
+         */
+        boolean isAllSpace = true;
+        if (!(current instanceof Node.JspText)
+            && !(current instanceof Node.NamedAttribute)) {
+            for (int i = 0; i < charBuffer.length(); i++) {
+                if (!(charBuffer.charAt(i) == ' '
+                    || charBuffer.charAt(i) == '\n'
+                    || charBuffer.charAt(i) == '\r'
+                    || charBuffer.charAt(i) == '\t')) {
+                    isAllSpace = false;
+                    break;
+                }
+            }
+        }
+
+        if (!isAllSpace && tagDependentPending) {
+            tagDependentPending = false;
+            tagDependentNesting++;
+        }
+
+        if (tagDependentNesting > 0) {
+            if (charBuffer.length() > 0) {
+                new Node.TemplateText(charBuffer.toString(), startMark, current);
+            }
+            startMark = new Mark(ctxt, path, locator.getLineNumber(),
+                                 locator.getColumnNumber());
+            charBuffer = null;
+            return;
+        }
+
+        if ((current instanceof Node.JspText)
+            || (current instanceof Node.NamedAttribute)
+            || !isAllSpace) {
+
+            int line = startMark.getLineNumber();
+            int column = startMark.getColumnNumber();
+
+            CharArrayWriter ttext = new CharArrayWriter();
+            int lastCh = 0;
+            for (int i = 0; i < charBuffer.length(); i++) {
+
+                int ch = charBuffer.charAt(i);
+                if (ch == '\n') {
+                    column = 1;
+                    line++;
+                } else {
+                    column++;
+                }
+                if (lastCh == '$' && ch == '{') {
+                    if (ttext.size() > 0) {
+                        new Node.TemplateText(
+                            ttext.toString(),
+                            startMark,
+                            current);
+                        ttext = new CharArrayWriter();
+                        //We subtract two from the column number to
+                        //account for the '${' that we've already parsed
+                        startMark = new Mark(ctxt, path, line, column - 2);
+                    }
+                    // following "${" to first unquoted "}"
+                    i++;
+                    boolean singleQ = false;
+                    boolean doubleQ = false;
+                    lastCh = 0;
+                    for (;; i++) {
+                        if (i >= charBuffer.length()) {
+                            throw new SAXParseException(
+                                Localizer.getMessage(
+                                    "jsp.error.unterminated",
+                                    "${"),
+                                locator);
+
+                        }
+                        ch = charBuffer.charAt(i);
+                        if (ch == '\n') {
+                            column = 1;
+                            line++;
+                        } else {
+                            column++;
+                        }
+                        if (lastCh == '\\' && (singleQ || doubleQ)) {
+                            ttext.write(ch);
+                            lastCh = 0;
+                            continue;
+                        }
+                        if (ch == '}') {
+                            new Node.ELExpression(
+                                ttext.toString(),
+                                startMark,
+                                current);
+                            ttext = new CharArrayWriter();
+                            startMark = new Mark(ctxt, path, line, column);
+                            break;
+                        }
+                        if (ch == '"')
+                            doubleQ = !doubleQ;
+                        else if (ch == '\'')
+                            singleQ = !singleQ;
+
+                        ttext.write(ch);
+                        lastCh = ch;
+                    }
+                } else if (lastCh == '\\' && ch == '$') {
+                    ttext.write('$');
+                    ch = 0;  // Not start of EL anymore
+                } else {
+                    if (lastCh == '$' || lastCh == '\\') {
+                        ttext.write(lastCh);
+                    }
+                    if (ch != '$' && ch != '\\') {
+                        ttext.write(ch);
+                    }
+                }
+                lastCh = ch;
+            }
+            if (lastCh == '$' || lastCh == '\\') {
+                ttext.write(lastCh);
+            }
+            if (ttext.size() > 0) {
+                new Node.TemplateText(ttext.toString(), startMark, current);
+            }
+        }
+        startMark = new Mark(ctxt, path, locator.getLineNumber(),
+                             locator.getColumnNumber());
+
+        charBuffer = null;
+    }
+
+    /*
+     * Receives notification of the end of an element.
+     */
+    public void endElement(String uri, String localName, String qName)
+        throws SAXException {
+
+        processChars();
+
+        if (directivesOnly &&
+            !(JSP_URI.equals(uri) && localName.startsWith(DIRECTIVE_ACTION))) {
+            return;
+        }
+
+        if (current instanceof Node.NamedAttribute) {
+            boolean isTrim = ((Node.NamedAttribute)current).isTrim();
+            Node.Nodes subElems = ((Node.NamedAttribute)current).getBody();
+            for (int i = 0; subElems != null && i < subElems.size(); i++) {
+                Node subElem = subElems.getNode(i);
+                if (!(subElem instanceof Node.TemplateText)) {
+                    continue;
+                }
+                // Ignore any whitespace (including spaces, carriage returns,
+                // line feeds, and tabs, that appear at the beginning and at
+                // the end of the body of the <jsp:attribute> action, if the
+                // action's 'trim' attribute is set to TRUE (default).
+                // In addition, any textual nodes in the <jsp:attribute> that
+                // have only white space are dropped from the document, with
+                // the exception of leading and trailing white-space-only
+                // textual nodes in a <jsp:attribute> whose 'trim' attribute
+                // is set to FALSE, which must be kept verbatim.
+                if (i == 0) {
+                    if (isTrim) {
+                        ((Node.TemplateText)subElem).ltrim();
+                    }
+                } else if (i == subElems.size() - 1) {
+                    if (isTrim) {
+                        ((Node.TemplateText)subElem).rtrim();
+                    }
+                } else {
+                    if (((Node.TemplateText)subElem).isAllSpace()) {
+                        subElems.remove(subElem);
+                    }
+                }
+            }
+        } else if (current instanceof Node.ScriptingElement) {
+            checkScriptingBody((Node.ScriptingElement)current);
+        }
+
+        if ( isTagDependent(current)) {
+            tagDependentNesting--;
+        }
+
+        if (scriptlessBodyNode != null
+                && current.equals(scriptlessBodyNode)) {
+            scriptlessBodyNode = null;
+        }
+
+        if (current.getParent() != null) {
+            current = current.getParent();
+        }
+    }
+
+    /*
+     * Receives the document locator.
+     *
+     * @param locator the document locator
+     */
+    public void setDocumentLocator(Locator locator) {
+        this.locator = locator;
+    }
+
+    /*
+     * See org.xml.sax.ext.LexicalHandler.
+     */
+    public void comment(char[] buf, int offset, int len) throws SAXException {
+
+        processChars();  // Flush char buffer and remove white spaces
+
+        // ignore comments in the DTD
+        if (!inDTD) {
+            startMark =
+                new Mark(
+                    ctxt,
+                    path,
+                    locator.getLineNumber(),
+                    locator.getColumnNumber());
+            new Node.Comment(new String(buf, offset, len), startMark, current);
+        }
+    }
+
+    /*
+     * See org.xml.sax.ext.LexicalHandler.
+     */
+    public void startCDATA() throws SAXException {
+
+        processChars();  // Flush char buffer and remove white spaces
+        startMark = new Mark(ctxt, path, locator.getLineNumber(),
+                             locator.getColumnNumber());
+    }
+
+    /*
+     * See org.xml.sax.ext.LexicalHandler.
+     */
+    public void endCDATA() throws SAXException {
+        processChars();  // Flush char buffer and remove white spaces
+    }
+
+    /*
+     * See org.xml.sax.ext.LexicalHandler.
+     */
+    public void startEntity(String name) throws SAXException {
+        // do nothing
+    }
+
+    /*
+     * See org.xml.sax.ext.LexicalHandler.
+     */
+    public void endEntity(String name) throws SAXException {
+        // do nothing
+    }
+
+    /*
+     * See org.xml.sax.ext.LexicalHandler.
+     */
+    public void startDTD(String name, String publicId, String systemId)
+        throws SAXException {
+        if (!isValidating) {
+            fatalError(ENABLE_DTD_VALIDATION_EXCEPTION);
+        }
+
+        inDTD = true;
+    }
+
+    /*
+     * See org.xml.sax.ext.LexicalHandler.
+     */
+    public void endDTD() throws SAXException {
+        inDTD = false;
+    }
+
+    /*
+     * Receives notification of a non-recoverable error.
+     */
+    public void fatalError(SAXParseException e) throws SAXException {
+        throw e;
+    }
+
+    /*
+     * Receives notification of a recoverable error.
+     */
+    public void error(SAXParseException e) throws SAXException {
+        throw e;
+    }
+
+    /*
+     * Receives notification of the start of a Namespace mapping. 
+     */
+    public void startPrefixMapping(String prefix, String uri)
+        throws SAXException {
+        TagLibraryInfo taglibInfo;
+        try {
+            taglibInfo = getTaglibInfo(prefix, uri);
+        } catch (JasperException je) {
+            throw new SAXParseException(
+                Localizer.getMessage("jsp.error.could.not.add.taglibraries"),
+                locator,
+                je);
+        }
+
+        if (taglibInfo != null) {
+            if (pageInfo.getTaglib(uri) == null) {
+                pageInfo.addTaglib(uri, taglibInfo);
+            }
+            pageInfo.pushPrefixMapping(prefix, uri);
+        } else {
+            pageInfo.pushPrefixMapping(prefix, null);
+        }
+    }
+
+    /*
+     * Receives notification of the end of a Namespace mapping. 
+     */
+    public void endPrefixMapping(String prefix) throws SAXException {
+        pageInfo.popPrefixMapping(prefix);
+    }
+
+    //*********************************************************************
+    // Private utility methods
+
+    private Node parseStandardAction(
+        String qName,
+        String localName,
+        Attributes nonTaglibAttrs,
+        Attributes nonTaglibXmlnsAttrs,
+        Attributes taglibAttrs,
+        Mark start,
+        Node parent)
+        throws SAXException {
+
+        Node node = null;
+
+        if (localName.equals(ROOT_ACTION)) {
+            if (!(current instanceof Node.Root)) {
+                throw new SAXParseException(
+                    Localizer.getMessage("jsp.error.nested_jsproot"),
+                    locator);
+            }
+            node =
+                new Node.JspRoot(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+            if (isTop) {
+                pageInfo.setHasJspRoot(true);
+            }
+        } else if (localName.equals(PAGE_DIRECTIVE_ACTION)) {
+            if (isTagFile) {
+                throw new SAXParseException(
+                    Localizer.getMessage(
+                        "jsp.error.action.istagfile",
+                        localName),
+                    locator);
+            }
+            node =
+                new Node.PageDirective(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+            String imports = nonTaglibAttrs.getValue("import");
+            // There can only be one 'import' attribute per page directive
+            if (imports != null) {
+                ((Node.PageDirective)node).addImport(imports);
+            }
+        } else if (localName.equals(INCLUDE_DIRECTIVE_ACTION)) {
+            node =
+                new Node.IncludeDirective(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+            processIncludeDirective(nonTaglibAttrs.getValue("file"), node);
+        } else if (localName.equals(DECLARATION_ACTION)) {
+            if (scriptlessBodyNode != null) {
+                // We're nested inside a node whose body is
+                // declared to be scriptless
+                throw new SAXParseException(
+                    Localizer.getMessage(
+                        "jsp.error.no.scriptlets",
+                        localName),
+                    locator);
+            }
+            node =
+                new Node.Declaration(
+                    qName,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(SCRIPTLET_ACTION)) {
+            if (scriptlessBodyNode != null) {
+                // We're nested inside a node whose body is
+                // declared to be scriptless
+                throw new SAXParseException(
+                    Localizer.getMessage(
+                        "jsp.error.no.scriptlets",
+                        localName),
+                    locator);
+            }
+            node =
+                new Node.Scriptlet(
+                    qName,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(EXPRESSION_ACTION)) {
+            if (scriptlessBodyNode != null) {
+                // We're nested inside a node whose body is
+                // declared to be scriptless
+                throw new SAXParseException(
+                    Localizer.getMessage(
+                        "jsp.error.no.scriptlets",
+                        localName),
+                    locator);
+            }
+            node =
+                new Node.Expression(
+                    qName,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(USE_BEAN_ACTION)) {
+            node =
+                new Node.UseBean(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(SET_PROPERTY_ACTION)) {
+            node =
+                new Node.SetProperty(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(GET_PROPERTY_ACTION)) {
+            node =
+                new Node.GetProperty(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(INCLUDE_ACTION)) {
+            node =
+                new Node.IncludeAction(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(FORWARD_ACTION)) {
+            node =
+                new Node.ForwardAction(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(PARAM_ACTION)) {
+            node =
+                new Node.ParamAction(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(PARAMS_ACTION)) {
+            node =
+                new Node.ParamsAction(
+                    qName,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(PLUGIN_ACTION)) {
+            node =
+                new Node.PlugIn(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(TEXT_ACTION)) {
+            node =
+                new Node.JspText(
+                    qName,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(BODY_ACTION)) {
+            node =
+                new Node.JspBody(
+                    qName,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(ATTRIBUTE_ACTION)) {
+            node =
+                new Node.NamedAttribute(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(OUTPUT_ACTION)) {
+            node =
+                new Node.JspOutput(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(TAG_DIRECTIVE_ACTION)) {
+            if (!isTagFile) {
+                throw new SAXParseException(
+                    Localizer.getMessage(
+                        "jsp.error.action.isnottagfile",
+                        localName),
+                    locator);
+            }
+            node =
+                new Node.TagDirective(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+            String imports = nonTaglibAttrs.getValue("import");
+            // There can only be one 'import' attribute per tag directive
+            if (imports != null) {
+                ((Node.TagDirective)node).addImport(imports);
+            }
+        } else if (localName.equals(ATTRIBUTE_DIRECTIVE_ACTION)) {
+            if (!isTagFile) {
+                throw new SAXParseException(
+                    Localizer.getMessage(
+                        "jsp.error.action.isnottagfile",
+                        localName),
+                    locator);
+            }
+            node =
+                new Node.AttributeDirective(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(VARIABLE_DIRECTIVE_ACTION)) {
+            if (!isTagFile) {
+                throw new SAXParseException(
+                    Localizer.getMessage(
+                        "jsp.error.action.isnottagfile",
+                        localName),
+                    locator);
+            }
+            node =
+                new Node.VariableDirective(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(INVOKE_ACTION)) {
+            if (!isTagFile) {
+                throw new SAXParseException(
+                    Localizer.getMessage(
+                        "jsp.error.action.isnottagfile",
+                        localName),
+                    locator);
+            }
+            node =
+                new Node.InvokeAction(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(DOBODY_ACTION)) {
+            if (!isTagFile) {
+                throw new SAXParseException(
+                    Localizer.getMessage(
+                        "jsp.error.action.isnottagfile",
+                        localName),
+                    locator);
+            }
+            node =
+                new Node.DoBodyAction(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(ELEMENT_ACTION)) {
+            node =
+                new Node.JspElement(
+                    qName,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else if (localName.equals(FALLBACK_ACTION)) {
+            node =
+                new Node.FallBackAction(
+                    qName,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    current);
+        } else {
+            throw new SAXParseException(
+                Localizer.getMessage(
+                    "jsp.error.xml.badStandardAction",
+                    localName),
+                locator);
+        }
+
+        return node;
+    }
+
+    /*
+     * Checks if the XML element with the given tag name is a custom action,
+     * and returns the corresponding Node object.
+     */
+    private Node parseCustomAction(
+        String qName,
+        String localName,
+        String uri,
+        Attributes nonTaglibAttrs,
+        Attributes nonTaglibXmlnsAttrs,
+        Attributes taglibAttrs,
+        Mark start,
+        Node parent)
+        throws SAXException {
+
+        // Check if this is a user-defined (custom) tag
+        TagLibraryInfo tagLibInfo = pageInfo.getTaglib(uri);
+        if (tagLibInfo == null) {
+            return null;
+        }
+
+        TagInfo tagInfo = tagLibInfo.getTag(localName);
+        TagFileInfo tagFileInfo = tagLibInfo.getTagFile(localName);
+        if (tagInfo == null && tagFileInfo == null) {
+            throw new SAXException(
+                Localizer.getMessage("jsp.error.xml.bad_tag", localName, uri));
+        }
+        Class tagHandlerClass = null;
+        if (tagInfo != null) {
+            String handlerClassName = tagInfo.getTagClassName();
+            try {
+                tagHandlerClass =
+                    ctxt.getClassLoader().loadClass(handlerClassName);
+            } catch (Exception e) {
+                throw new SAXException(
+                    Localizer.getMessage("jsp.error.loadclass.taghandler",
+                                         handlerClassName,
+                                         qName),
+                    e);
+            }
+        }
+
+        String prefix = "";
+        int colon = qName.indexOf(':');
+        if (colon != -1) {
+            prefix = qName.substring(0, colon);
+        }
+
+        Node.CustomTag ret = null;
+        if (tagInfo != null) {
+            ret =
+                new Node.CustomTag(
+                    qName,
+                    prefix,
+                    localName,
+                    uri,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    parent,
+                    tagInfo,
+                    tagHandlerClass);
+        } else {
+            ret =
+                new Node.CustomTag(
+                    qName,
+                    prefix,
+                    localName,
+                    uri,
+                    nonTaglibAttrs,
+                    nonTaglibXmlnsAttrs,
+                    taglibAttrs,
+                    start,
+                    parent,
+                    tagFileInfo);
+        }
+
+        return ret;
+    }
+
+    /*
+     * Creates the tag library associated with the given uri namespace, and
+     * returns it.
+     *
+     * @param prefix The prefix of the xmlns attribute
+     * @param uri The uri namespace (value of the xmlns attribute)
+     *
+     * @return The tag library associated with the given uri namespace
+     */
+    private TagLibraryInfo getTaglibInfo(String prefix, String uri)
+        throws JasperException {
+
+        TagLibraryInfo result = null;
+
+        if (uri.startsWith(URN_JSPTAGDIR)) {
+            // uri (of the form "urn:jsptagdir:path") references tag file dir
+            String tagdir = uri.substring(URN_JSPTAGDIR.length());
+            result =
+                new ImplicitTagLibraryInfo(
+                    ctxt,
+                    parserController,
+                    prefix,
+                    tagdir,
+                    err);
+        } else {
+            // uri references TLD file
+            boolean isPlainUri = false;
+            if (uri.startsWith(URN_JSPTLD)) {
+                // uri is of the form "urn:jsptld:path"
+                uri = uri.substring(URN_JSPTLD.length());
+            } else {
+                isPlainUri = true;
+            }
+
+            String[] location = ctxt.getTldLocation(uri);
+            if (location != null || !isPlainUri) {
+                /*
+                 * If the uri value is a plain uri, a translation error must
+                 * not be generated if the uri is not found in the taglib map.
+                 * Instead, any actions in the namespace defined by the uri
+                 * value must be treated as uninterpreted.
+                 */
+                result =
+                    new TagLibraryInfoImpl(
+                        ctxt,
+                        parserController,
+                        prefix,
+                        uri,
+                        location,
+                        err);
+            }
+        }
+
+        return result;
+    }
+
+    /*
+     * Ensures that the given body only contains nodes that are instances of
+     * TemplateText.
+     *
+     * This check is performed only for the body of a scripting (that is:
+     * declaration, scriptlet, or expression) element, after the end tag of a
+     * scripting element has been reached.
+     */
+    private void checkScriptingBody(Node.ScriptingElement scriptingElem)
+        throws SAXException {
+        Node.Nodes body = scriptingElem.getBody();
+        if (body != null) {
+            int size = body.size();
+            for (int i = 0; i < size; i++) {
+                Node n = body.getNode(i);
+                if (!(n instanceof Node.TemplateText)) {
+                    String elemType = SCRIPTLET_ACTION;
+                    if (scriptingElem instanceof Node.Declaration)
+                        elemType = DECLARATION_ACTION;
+                    if (scriptingElem instanceof Node.Expression)
+                        elemType = EXPRESSION_ACTION;
+                    String msg =
+                        Localizer.getMessage(
+                            "jsp.error.parse.xml.scripting.invalid.body",
+                            elemType);
+                    throw new SAXException(msg);
+                }
+            }
+        }
+    }
+
+    /*
+     * Parses the given file included via an include directive.
+     *
+     * @param fname The path to the included resource, as specified by the
+     * 'file' attribute of the include directive
+     * @param parent The Node representing the include directive
+     */
+    private void processIncludeDirective(String fname, Node parent)
+        throws SAXException {
+
+        if (fname == null) {
+            return;
+        }
+
+        try {
+            parserController.parse(fname, parent, null);
+        } catch (FileNotFoundException fnfe) {
+            throw new SAXParseException(
+                Localizer.getMessage("jsp.error.file.not.found", fname),
+                locator,
+                fnfe);
+        } catch (Exception e) {
+            throw new SAXException(e);
+        }
+    }
+
+    /*
+     * Checks an element's given URI, qname, and attributes to see if any
+     * of them hijack the 'jsp' prefix, that is, bind it to a namespace other
+     * than http://java.sun.com/JSP/Page.
+     *
+     * @param uri The element's URI
+     * @param qName The element's qname
+     * @param attrs The element's attributes
+     */
+    private void checkPrefixes(String uri, String qName, Attributes attrs) {
+
+        checkPrefix(uri, qName);
+
+        int len = attrs.getLength();
+        for (int i = 0; i < len; i++) {
+            checkPrefix(attrs.getURI(i), attrs.getQName(i));
+        }
+    }
+
+    /*
+     * Checks the given URI and qname to see if they hijack the 'jsp' prefix,
+     * which would be the case if qName contained the 'jsp' prefix and
+     * uri was different from http://java.sun.com/JSP/Page.
+     *
+     * @param uri The URI to check
+     * @param qName The qname to check
+     */
+    private void checkPrefix(String uri, String qName) {
+
+        int index = qName.indexOf(':');
+        if (index != -1) {
+            String prefix = qName.substring(0, index);
+            pageInfo.addPrefix(prefix);
+            if ("jsp".equals(prefix) && !JSP_URI.equals(uri)) {
+                pageInfo.setIsJspPrefixHijacked(true);
+            }
+        }
+    }
+
+    /*
+     * Gets SAXParser.
+     *
+     * @param validating Indicates whether the requested SAXParser should
+     * be validating
+     * @param jspDocParser The JSP document parser
+     *
+     * @return The SAXParser
+     */
+    private static SAXParser getSAXParser(
+        boolean validating,
+        JspDocumentParser jspDocParser)
+        throws Exception {
+
+        SAXParserFactory factory = SAXParserFactory.newInstance();
+        factory.setNamespaceAware(true);
+
+        // Preserve xmlns attributes
+        factory.setFeature(
+            "http://xml.org/sax/features/namespace-prefixes",
+            true);
+        factory.setValidating(validating);
+        //factory.setFeature(
+        //    "http://xml.org/sax/features/validation",
+        //    validating);
+        
+        // Configure the parser
+        SAXParser saxParser = factory.newSAXParser();
+        XMLReader xmlReader = saxParser.getXMLReader();
+        xmlReader.setProperty(LEXICAL_HANDLER_PROPERTY, jspDocParser);
+        xmlReader.setErrorHandler(jspDocParser);
+
+        return saxParser;
+    }
+
+    /*
+     * Exception indicating that a DOCTYPE declaration is present, but
+     * validation is turned off.
+     */
+    private static class EnableDTDValidationException
+        extends SAXParseException {
+
+        EnableDTDValidationException(String message, Locator loc) {
+            super(message, loc);
+        }
+    }
+
+    private static String getBodyType(Node.CustomTag custom) {
+
+        if (custom.getTagInfo() != null) {
+            return custom.getTagInfo().getBodyContent();
+        }
+
+        return custom.getTagFileInfo().getTagInfo().getBodyContent();
+    }
+
+    private boolean isTagDependent(Node n) {
+
+        if (n instanceof Node.CustomTag) {
+            String bodyType = getBodyType((Node.CustomTag) n);
+            return
+                TagInfo.BODY_CONTENT_TAG_DEPENDENT.equalsIgnoreCase(bodyType);
+        }
+        return false;
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/JspReader.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspReader.java
new file mode 100644
index 0000000..2a0bbfe
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspReader.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.CharArrayWriter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+
+/**
+ * JspReader is an input buffer for the JSP parser. It should allow
+ * unlimited lookahead and pushback. It also has a bunch of parsing
+ * utility methods for understanding htmlesque thingies.
+ *
+ * @author Anil K. Vijendran
+ * @author Anselm Baird-Smith
+ * @author Harish Prabandham
+ * @author Rajiv Mordani
+ * @author Mandar Raje
+ * @author Danno Ferrin
+ * @author Kin-man Chung
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+
+class JspReader {
+
+    // Logger
+    private Log log = LogFactory.getLog(JspReader.class);
+
+    private Mark current;
+    private String master;
+    private Vector sourceFiles;
+    private int currFileId;
+    private int size;
+    private JspCompilationContext context;
+    private ErrorDispatcher err;
+
+    /*
+     * Set to true when using the JspReader on a single file where we read up
+     * to the end and reset to the beginning many times.
+     * (as in ParserController.figureOutJspDocument()).
+     */
+    private boolean singleFile;
+
+    /*
+     * Constructor.
+     */
+    public JspReader(JspCompilationContext ctxt,
+                     String fname,
+                     String encoding,
+                     JarFile jarFile,
+                     ErrorDispatcher err)
+            throws JasperException, FileNotFoundException, IOException {
+
+        this(ctxt, fname, encoding,
+             JspUtil.getReader(fname, encoding, jarFile, ctxt, err),
+             err);
+    }
+
+    /*
+     * Constructor.
+     */
+    public JspReader(JspCompilationContext ctxt,
+                     String fname,
+                     String encoding,
+                     InputStreamReader reader,
+                     ErrorDispatcher err)
+            throws JasperException, FileNotFoundException {
+
+        this.context = ctxt;
+        this.err = err;
+        sourceFiles = new Vector();
+        currFileId = 0;
+        size = 0;
+        singleFile = false;
+        pushFile(fname, encoding, reader);
+    }
+
+    /*
+     * @return JSP compilation context with which this JspReader is 
+     * associated
+     */
+    JspCompilationContext getJspCompilationContext() {
+        return context;
+    }
+    
+    String getFile(int fileid) {
+        return (String) sourceFiles.elementAt(fileid);
+    }
+        
+    boolean hasMoreInput() throws JasperException {
+        if (current.cursor >= current.stream.length) {
+            if (singleFile) return false; 
+            while (popFile()) {
+                if (current.cursor < current.stream.length) return true;
+            }
+            return false;
+        }
+        return true;
+    }
+    
+    int nextChar() throws JasperException {
+        if (!hasMoreInput())
+            return -1;
+        
+        int ch = current.stream[current.cursor];
+
+        current.cursor++;
+        
+        if (ch == '\n') {
+            current.line++;
+            current.col = 0;
+        } else {
+            current.col++;
+        }
+        return ch;
+    }
+
+    /**
+     * Back up the current cursor by one char, assumes current.cursor > 0,
+     * and that the char to be pushed back is not '\n'.
+     */
+    void pushChar() {
+        current.cursor--;
+        current.col--;
+    }
+
+    String getText(Mark start, Mark stop) throws JasperException {
+        Mark oldstart = mark();
+        reset(start);
+        CharArrayWriter caw = new CharArrayWriter();
+        while (!stop.equals(mark()))
+            caw.write(nextChar());
+        caw.close();
+        reset(oldstart);
+        return caw.toString();
+    }
+
+    int peekChar() throws JasperException {
+        if (!hasMoreInput())
+            return -1;
+        return current.stream[current.cursor];
+    }
+
+    Mark mark() {
+        return new Mark(current);
+    }
+
+    void reset(Mark mark) {
+        current = new Mark(mark);
+    }
+
+    boolean matchesIgnoreCase(String string) throws JasperException {
+        Mark mark = mark();
+        int ch = 0;
+        int i = 0;
+        do {
+            ch = nextChar();
+            if (Character.toLowerCase((char) ch) != string.charAt(i++)) {
+                reset(mark);
+                return false;
+            }
+        } while (i < string.length());
+        reset(mark);
+        return true;
+    }
+
+    /**
+     * search the stream for a match to a string
+     * @param string The string to match
+     * @return <strong>true</strong> is one is found, the current position
+     *         in stream is positioned after the search string, <strong>
+     *               false</strong> otherwise, position in stream unchanged.
+     */
+    boolean matches(String string) throws JasperException {
+        Mark mark = mark();
+        int ch = 0;
+        int i = 0;
+        do {
+            ch = nextChar();
+            if (((char) ch) != string.charAt(i++)) {
+                reset(mark);
+                return false;
+            }
+        } while (i < string.length());
+        return true;
+    }
+
+    boolean matchesETag(String tagName) throws JasperException {
+        Mark mark = mark();
+
+        if (!matches("</" + tagName))
+            return false;
+        skipSpaces();
+        if (nextChar() == '>')
+            return true;
+
+        reset(mark);
+        return false;
+    }
+
+    boolean matchesETagWithoutLessThan(String tagName)
+        throws JasperException
+    {
+       Mark mark = mark();
+
+       if (!matches("/" + tagName))
+           return false;
+       skipSpaces();
+       if (nextChar() == '>')
+           return true;
+
+       reset(mark);
+       return false;
+    }
+
+
+    /**
+     * Looks ahead to see if there are optional spaces followed by
+     * the given String.  If so, true is returned and those spaces and
+     * characters are skipped.  If not, false is returned and the
+     * position is restored to where we were before.
+     */
+    boolean matchesOptionalSpacesFollowedBy( String s )
+        throws JasperException
+    {
+        Mark mark = mark();
+
+        skipSpaces();
+        boolean result = matches( s );
+        if( !result ) {
+            reset( mark );
+        }
+
+        return result;
+    }
+
+    int skipSpaces() throws JasperException {
+        int i = 0;
+        while (hasMoreInput() && isSpace()) {
+            i++;
+            nextChar();
+        }
+        return i;
+    }
+
+    /**
+     * Skip until the given string is matched in the stream.
+     * When returned, the context is positioned past the end of the match.
+     *
+     * @param s The String to match.
+     * @return A non-null <code>Mark</code> instance (positioned immediately
+     *         before the search string) if found, <strong>null</strong>
+     *         otherwise.
+     */
+    Mark skipUntil(String limit) throws JasperException {
+        Mark ret = null;
+        int limlen = limit.length();
+        int ch;
+
+    skip:
+        for (ret = mark(), ch = nextChar() ; ch != -1 ;
+                 ret = mark(), ch = nextChar()) {
+            if (ch == limit.charAt(0)) {
+                Mark restart = mark();
+                for (int i = 1 ; i < limlen ; i++) {
+                    if (peekChar() == limit.charAt(i))
+                        nextChar();
+                    else {
+                        reset(restart);
+                        continue skip;
+                    }
+                }
+                return ret;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Skip until the given string is matched in the stream, but ignoring
+     * chars initially escaped by a '\'.
+     * When returned, the context is positioned past the end of the match.
+     *
+     * @param s The String to match.
+     * @return A non-null <code>Mark</code> instance (positioned immediately
+     *         before the search string) if found, <strong>null</strong>
+     *         otherwise.
+     */
+    Mark skipUntilIgnoreEsc(String limit) throws JasperException {
+        Mark ret = null;
+        int limlen = limit.length();
+        int ch;
+        int prev = 'x';        // Doesn't matter
+        
+    skip:
+        for (ret = mark(), ch = nextChar() ; ch != -1 ;
+                 ret = mark(), prev = ch, ch = nextChar()) {            
+            if (ch == '\\' && prev == '\\') {
+                ch = 0;                // Double \ is not an escape char anymore
+            }
+            else if (ch == limit.charAt(0) && prev != '\\') {
+                for (int i = 1 ; i < limlen ; i++) {
+                    if (peekChar() == limit.charAt(i))
+                        nextChar();
+                    else
+                        continue skip;
+                }
+                return ret;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Skip until the given end tag is matched in the stream.
+     * When returned, the context is positioned past the end of the tag.
+     *
+     * @param tag The name of the tag whose ETag (</tag>) to match.
+     * @return A non-null <code>Mark</code> instance (positioned immediately
+     *               before the ETag) if found, <strong>null</strong> otherwise.
+     */
+    Mark skipUntilETag(String tag) throws JasperException {
+        Mark ret = skipUntil("</" + tag);
+        if (ret != null) {
+            skipSpaces();
+            if (nextChar() != '>')
+                ret = null;
+        }
+        return ret;
+    }
+
+    final boolean isSpace() throws JasperException {
+        // Note: If this logic changes, also update Node.TemplateText.rtrim()
+        return peekChar() <= ' ';
+    }
+
+    /**
+     * Parse a space delimited token.
+     * If quoted the token will consume all characters up to a matching quote,
+     * otherwise, it consumes up to the first delimiter character.
+     *
+     * @param quoted If <strong>true</strong> accept quoted strings.
+     */
+    String parseToken(boolean quoted) throws JasperException {
+        StringBuffer stringBuffer = new StringBuffer();
+        skipSpaces();
+        stringBuffer.setLength(0);
+        
+        if (!hasMoreInput()) {
+            return "";
+        }
+
+        int ch = peekChar();
+        
+        if (quoted) {
+            if (ch == '"' || ch == '\'') {
+
+                char endQuote = ch == '"' ? '"' : '\'';
+                // Consume the open quote: 
+                ch = nextChar();
+                for (ch = nextChar(); ch != -1 && ch != endQuote;
+                         ch = nextChar()) {
+                    if (ch == '\\') 
+                        ch = nextChar();
+                    stringBuffer.append((char) ch);
+                }
+                // Check end of quote, skip closing quote:
+                if (ch == -1) {
+                    err.jspError(mark(), "jsp.error.quotes.unterminated");
+                }
+            } else {
+                err.jspError(mark(), "jsp.error.attr.quoted");
+            }
+        } else {
+            if (!isDelimiter()) {
+                // Read value until delimiter is found:
+                do {
+                    ch = nextChar();
+                    // Take care of the quoting here.
+                    if (ch == '\\') {
+                        if (peekChar() == '"' || peekChar() == '\'' ||
+                               peekChar() == '>' || peekChar() == '%')
+                            ch = nextChar();
+                    }
+                    stringBuffer.append((char) ch);
+                } while (!isDelimiter());
+            }
+        }
+
+        return stringBuffer.toString();
+    }
+
+    void setSingleFile(boolean val) {
+        singleFile = val;
+    }
+
+
+    /**
+     * Gets the URL for the given path name.
+     *
+     * @param path Path name
+     *
+     * @return URL for the given path name.
+     *
+     * @exception MalformedURLException if the path name is not given in 
+     * the correct form
+     */
+    URL getResource(String path) throws MalformedURLException {
+        return context.getResource(path);
+    }
+
+
+    /**
+     * Parse utils - Is current character a token delimiter ?
+     * Delimiters are currently defined to be =, &gt;, &lt;, ", and ' or any
+     * any space character as defined by <code>isSpace</code>.
+     *
+     * @return A boolean.
+     */
+    private boolean isDelimiter() throws JasperException {
+        if (! isSpace()) {
+            int ch = peekChar();
+            // Look for a single-char work delimiter:
+            if (ch == '=' || ch == '>' || ch == '"' || ch == '\''
+                    || ch == '/') {
+                return true;
+            }
+            // Look for an end-of-comment or end-of-tag:                
+            if (ch == '-') {
+                Mark mark = mark();
+                if (((ch = nextChar()) == '>')
+                        || ((ch == '-') && (nextChar() == '>'))) {
+                    reset(mark);
+                    return true;
+                } else {
+                    reset(mark);
+                    return false;
+                }
+            }
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Register a new source file.
+     * This method is used to implement file inclusion. Each included file
+     * gets a unique identifier (which is the index in the array of source
+     * files).
+     *
+     * @return The index of the now registered file.
+     */
+    private int registerSourceFile(String file) {
+        if (sourceFiles.contains(file))
+            return -1;
+        sourceFiles.addElement(file);
+        this.size++;
+        return sourceFiles.size() - 1;
+    }
+    
+
+    /**
+     * Unregister the source file.
+     * This method is used to implement file inclusion. Each included file
+     * gets a uniq identifier (which is the index in the array of source
+     * files).
+     *
+     * @return The index of the now registered file.
+     */
+    private int unregisterSourceFile(String file) {
+        if (!sourceFiles.contains(file))
+            return -1;
+        sourceFiles.removeElement(file);
+        this.size--;
+        return sourceFiles.size() - 1;
+    }
+
+    /**
+     * Push a file (and its associated Stream) on the file stack.  THe
+     * current position in the current file is remembered.
+     */
+    private void pushFile(String file, String encoding, 
+                           InputStreamReader reader) 
+                throws JasperException, FileNotFoundException {
+
+        // Register the file
+        String longName = file;
+
+        int fileid = registerSourceFile(longName);
+
+        if (fileid == -1) {
+            err.jspError("jsp.error.file.already.registered", file);
+        }
+
+        currFileId = fileid;
+
+        try {
+            CharArrayWriter caw = new CharArrayWriter();
+            char buf[] = new char[1024];
+            for (int i = 0 ; (i = reader.read(buf)) != -1 ;)
+                caw.write(buf, 0, i);
+            caw.close();
+            if (current == null) {
+                current = new Mark(this, caw.toCharArray(), fileid, 
+                                   getFile(fileid), master, encoding);
+            } else {
+                current.pushStream(caw.toCharArray(), fileid, getFile(fileid),
+                                   longName, encoding);
+            }
+        } catch (Throwable ex) {
+            log.error("Exception parsing file ", ex);
+            // Pop state being constructed:
+            popFile();
+            err.jspError("jsp.error.file.cannot.read", file);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (Exception any) {}
+            }
+        }
+    }
+
+    /**
+     * Pop a file from the file stack.  The field "current" is retored
+     * to the value to point to the previous files, if any, and is set
+     * to null otherwise.
+     * @return true is there is a previous file on the stck.
+     *         false otherwise.
+     */
+    private boolean popFile() throws JasperException {
+
+        // Is stack created ? (will happen if the Jsp file we're looking at is
+        // missing.
+        if (current == null || currFileId < 0) {
+            return false;
+        }
+
+        // Restore parser state:
+        String fName = getFile(currFileId);
+        currFileId = unregisterSourceFile(fName);
+        if (currFileId < -1) {
+            err.jspError("jsp.error.file.not.registered", fName);
+        }
+
+        Mark previous = current.popStream();
+        if (previous != null) {
+            master = current.baseDir;
+            current = previous;
+            return true;
+        }
+        // Note that although the current file is undefined here, "current"
+        // is not set to null just for convience, for it maybe used to
+        // set the current (undefined) position.
+        return false;
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/JspRuntimeContext.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspRuntimeContext.java
new file mode 100644
index 0000000..12abe44
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspRuntimeContext.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilePermission;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSource;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.jsp.JspFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jasper.Constants;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.Options;
+import org.apache.jasper.util.SystemLogHandler;
+import org.apache.jasper.runtime.JspFactoryImpl;
+import org.apache.jasper.security.SecurityClassLoad;
+import org.apache.jasper.servlet.JspServletWrapper;
+
+/**
+ * Class for tracking JSP compile time file dependencies when the
+ * &060;%@include file="..."%&062; directive is used.
+ *
+ * A background thread periodically checks the files a JSP page
+ * is dependent upon.  If a dpendent file changes the JSP page
+ * which included it is recompiled.
+ *
+ * Only used if a web application context is a directory.
+ *
+ * @author Glenn L. Nielsen
+ * @version $Revision$
+ */
+public final class JspRuntimeContext implements Runnable {
+
+    // Logger
+    private Log log = LogFactory.getLog(JspRuntimeContext.class);
+
+    /*
+     * Counts how many times the webapp's JSPs have been reloaded.
+     */
+    private int jspReloadCount;
+
+    /**
+     * Preload classes required at runtime by a JSP servlet so that
+     * we don't get a defineClassInPackage security exception.
+     */
+    static {
+        JspFactoryImpl factory = new JspFactoryImpl();
+        SecurityClassLoad.securityClassLoad(factory.getClass().getClassLoader());
+        JspFactory.setDefaultFactory(factory);
+    }
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Create a JspRuntimeContext for a web application context.
+     *
+     * Loads in any previously generated dependencies from file.
+     *
+     * @param context ServletContext for web application
+     */
+    public JspRuntimeContext(ServletContext context, Options options) {
+
+        System.setErr(new SystemLogHandler(System.err));
+
+        this.context = context;
+        this.options = options;
+
+        // Get the parent class loader
+        parentClassLoader =
+            (URLClassLoader) Thread.currentThread().getContextClassLoader();
+        if (parentClassLoader == null) {
+            parentClassLoader =
+                (URLClassLoader)this.getClass().getClassLoader();
+        }
+
+	if (log.isDebugEnabled()) {
+	    if (parentClassLoader != null) {
+		log.debug(Localizer.getMessage("jsp.message.parent_class_loader_is",
+					       parentClassLoader.toString()));
+	    } else {
+		log.debug(Localizer.getMessage("jsp.message.parent_class_loader_is",
+					       "<none>"));
+	    }
+        }
+
+        initClassPath();
+
+	if (context instanceof org.apache.jasper.servlet.JspCServletContext) {
+	    return;
+	}
+
+        if (System.getSecurityManager() != null) {
+            initSecurity();
+        }
+
+        // If this web application context is running from a
+        // directory, start the background compilation thread
+        String appBase = context.getRealPath("/");         
+        if (!options.getDevelopment()
+                && appBase != null
+                && options.getCheckInterval() > 0) {
+            if (appBase.endsWith(File.separator) ) {
+                appBase = appBase.substring(0,appBase.length()-1);
+            }
+            String directory =
+                appBase.substring(appBase.lastIndexOf(File.separator));
+            threadName = threadName + "[" + directory + "]";
+            threadStart();
+        }                                            
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * This web applications ServletContext
+     */
+    private ServletContext context;
+    private Options options;
+    private URLClassLoader parentClassLoader;
+    private PermissionCollection permissionCollection;
+    private CodeSource codeSource;                    
+    private String classpath;
+
+    /**
+     * Maps JSP pages to their JspServletWrapper's
+     */
+    private Map jsps = Collections.synchronizedMap( new HashMap());
+ 
+
+    /**
+     * The background thread.
+     */
+    private Thread thread = null;
+
+
+    /**
+     * The background thread completion semaphore.
+     */
+    private boolean threadDone = false;
+
+
+    /**
+     * Name to register for the background thread.
+     */
+    private String threadName = "JspRuntimeContext";
+
+    // ------------------------------------------------------ Public Methods
+
+    /**
+     * Add a new JspServletWrapper.
+     *
+     * @param jspUri JSP URI
+     * @param jsw Servlet wrapper for JSP
+     */
+    public void addWrapper(String jspUri, JspServletWrapper jsw) {
+        jsps.remove(jspUri);
+        jsps.put(jspUri,jsw);
+    }
+
+    /**
+     * Get an already existing JspServletWrapper.
+     *
+     * @param jspUri JSP URI
+     * @return JspServletWrapper for JSP
+     */
+    public JspServletWrapper getWrapper(String jspUri) {
+        return (JspServletWrapper) jsps.get(jspUri);
+    }
+
+    /**
+     * Remove a  JspServletWrapper.
+     *
+     * @param jspUri JSP URI of JspServletWrapper to remove
+     */
+    public void removeWrapper(String jspUri) {
+        jsps.remove(jspUri);
+    }
+
+    /**
+     * Returns the number of JSPs for which JspServletWrappers exist, i.e.,
+     * the number of JSPs that have been loaded into the webapp.
+     *
+     * @return The number of JSPs that have been loaded into the webapp
+     */
+    public int getJspCount() {
+        return jsps.size();
+    }
+
+    /**
+     * Get the SecurityManager Policy CodeSource for this web
+     * applicaiton context.
+     *
+     * @return CodeSource for JSP
+     */
+    public CodeSource getCodeSource() {
+        return codeSource;
+    }
+
+    /**
+     * Get the parent URLClassLoader.
+     *
+     * @return URLClassLoader parent
+     */
+    public URLClassLoader getParentClassLoader() {
+        return parentClassLoader;
+    }
+
+    /**
+     * Get the SecurityManager PermissionCollection for this
+     * web application context.
+     *
+     * @return PermissionCollection permissions
+     */
+    public PermissionCollection getPermissionCollection() {
+        return permissionCollection;
+    }
+
+    /**
+     * Process a "destory" event for this web application context.
+     */                                                        
+    public void destroy() {
+
+        if(System.err instanceof SystemLogHandler)
+            System.setErr(((SystemLogHandler)System.err).getWrapped());
+
+        threadStop();
+
+        Iterator servlets = jsps.values().iterator();
+        while (servlets.hasNext()) {
+            ((JspServletWrapper) servlets.next()).destroy();
+        }
+    }
+
+    /**
+     * Increments the JSP reload counter.
+     */
+    public synchronized void incrementJspReloadCount() {
+        jspReloadCount++;
+    }
+
+    /**
+     * Resets the JSP reload counter.
+     *
+     * @param count Value to which to reset the JSP reload counter
+     */
+    public synchronized void setJspReloadCount(int count) {
+        this.jspReloadCount = count;
+    }
+
+    /**
+     * Gets the current value of the JSP reload counter.
+     *
+     * @return The current value of the JSP reload counter
+     */
+    public int getJspReloadCount() {
+        return jspReloadCount;
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+    /**
+     * Method used by background thread to check the JSP dependencies
+     * registered with this class for JSP's.
+     */
+    private void checkCompile() {
+        Object [] wrappers = jsps.values().toArray();
+        for (int i = 0; i < wrappers.length; i++ ) {
+            JspServletWrapper jsw = (JspServletWrapper)wrappers[i];
+            JspCompilationContext ctxt = jsw.getJspEngineContext();
+            // JspServletWrapper also synchronizes on this when
+            // it detects it has to do a reload
+            synchronized(jsw) {
+                try {
+                    ctxt.compile();
+                } catch (FileNotFoundException ex) {
+                    ctxt.incrementRemoved();
+                } catch (Throwable t) {
+                    jsw.getServletContext().log("Background compile failed",
+						t);
+                }
+            }
+        }
+    }
+
+    /**
+     * The classpath that is passed off to the Java compiler.
+     */
+    public String getClassPath() {
+        return classpath;
+    }
+
+    /**
+     * Method used to initialize classpath for compiles.
+     */
+    private void initClassPath() {
+
+        URL [] urls = parentClassLoader.getURLs();
+        StringBuffer cpath = new StringBuffer();
+        String sep = System.getProperty("path.separator");
+
+        for(int i = 0; i < urls.length; i++) {
+            // Tomcat 4 can use URL's other than file URL's,
+            // a protocol other than file: will generate a
+            // bad file system path, so only add file:
+            // protocol URL's to the classpath.
+            
+            if( urls[i].getProtocol().equals("file") ) {
+                cpath.append((String)urls[i].getFile()+sep);
+            }
+        }    
+
+	cpath.append(options.getScratchDir() + sep);
+
+        String cp = (String) context.getAttribute(Constants.SERVLET_CLASSPATH);
+        if (cp == null || cp.equals("")) {
+            cp = options.getClassPath();
+        }
+
+        classpath = cpath.toString() + cp;
+    }
+
+    /**
+     * Method used to initialize SecurityManager data.
+     */
+    private void initSecurity() {
+
+        // Setup the PermissionCollection for this web app context
+        // based on the permissions configured for the root of the
+        // web app context directory, then add a file read permission
+        // for that directory.
+        Policy policy = Policy.getPolicy();
+        if( policy != null ) {
+            try {          
+                // Get the permissions for the web app context
+                String docBase = context.getRealPath("/");
+                if( docBase == null ) {
+                    docBase = options.getScratchDir().toString();
+                }
+                String codeBase = docBase;
+                if (!codeBase.endsWith(File.separator)){
+                    codeBase = codeBase + File.separator;
+                }
+                File contextDir = new File(codeBase);
+                URL url = contextDir.getCanonicalFile().toURL();
+                codeSource = new CodeSource(url,(Certificate[])null);
+                permissionCollection = policy.getPermissions(codeSource);
+
+                // Create a file read permission for web app context directory
+                if (!docBase.endsWith(File.separator)){
+                    permissionCollection.add
+                        (new FilePermission(docBase,"read"));
+                    docBase = docBase + File.separator;
+                } else {
+                    permissionCollection.add
+                        (new FilePermission
+                            (docBase.substring(0,docBase.length() - 1),"read"));
+                }
+                docBase = docBase + "-";
+                permissionCollection.add(new FilePermission(docBase,"read"));
+
+                // Create a file read permission for web app tempdir (work)
+                // directory
+                String workDir = options.getScratchDir().toString();
+                if (!workDir.endsWith(File.separator)){
+                    permissionCollection.add
+                        (new FilePermission(workDir,"read"));
+                    workDir = workDir + File.separator;
+                }
+                workDir = workDir + "-";
+                permissionCollection.add(new FilePermission(workDir,"read"));
+
+                // Allow the JSP to access org.apache.jasper.runtime.HttpJspBase
+                permissionCollection.add( new RuntimePermission(
+                    "accessClassInPackage.org.apache.jasper.runtime") );
+
+                if (parentClassLoader instanceof URLClassLoader) {
+                    URL [] urls = parentClassLoader.getURLs();
+                    String jarUrl = null;
+                    String jndiUrl = null;
+                    for (int i=0; i<urls.length; i++) {
+                        if (jndiUrl == null
+                                && urls[i].toString().startsWith("jndi:") ) {
+                            jndiUrl = urls[i].toString() + "-";
+                        }
+                        if (jarUrl == null
+                                && urls[i].toString().startsWith("jar:jndi:")
+                                ) {
+                            jarUrl = urls[i].toString();
+                            jarUrl = jarUrl.substring(0,jarUrl.length() - 2);
+                            jarUrl = jarUrl.substring(0,
+                                     jarUrl.lastIndexOf('/')) + "/-";
+                        }
+                    }
+                    if (jarUrl != null) {
+                        permissionCollection.add(
+                                new FilePermission(jarUrl,"read"));
+                        permissionCollection.add(
+                                new FilePermission(jarUrl.substring(4),"read"));
+                    }
+                    if (jndiUrl != null)
+                        permissionCollection.add(
+                                new FilePermission(jndiUrl,"read") );
+                }
+            } catch(Exception e) {
+                context.log("Security Init for context failed",e);
+            }
+        }
+    }
+
+
+    // -------------------------------------------------------- Thread Support
+
+    /**
+     * Start the background thread that will periodically check for
+     * changes to compile time included files in a JSP.
+     *
+     * @exception IllegalStateException if we should not be starting
+     *  a background thread now
+     */
+    protected void threadStart() {
+
+        // Has the background thread already been started?
+        if (thread != null) {
+            return;
+        }
+
+        // Start the background thread
+        threadDone = false;
+        thread = new Thread(this, threadName);
+        thread.setDaemon(true);
+        thread.start();
+
+    }
+
+
+    /**
+     * Stop the background thread that is periodically checking for
+     * changes to compile time included files in a JSP.
+     */ 
+    protected void threadStop() {
+
+        if (thread == null) {
+            return;
+        }
+
+        threadDone = true;
+        thread.interrupt();
+        try {
+            thread.join();
+        } catch (InterruptedException e) {
+            ;
+        }
+        
+        thread = null;
+        
+    }
+
+    /**
+     * Sleep for the duration specified by the <code>checkInterval</code>
+     * property.
+     */ 
+    protected void threadSleep() {
+        
+        try {
+            Thread.sleep(options.getCheckInterval() * 1000L);
+        } catch (InterruptedException e) {
+            ;
+        }
+        
+    }   
+    
+    
+    // ------------------------------------------------------ Background Thread
+        
+        
+    /**
+     * The background thread that checks for changes to files
+     * included by a JSP and flags that a recompile is required.
+     */ 
+    public void run() {
+        
+        // Loop until the termination semaphore is set
+        while (!threadDone) {
+
+            // Wait for our check interval
+            threadSleep();
+
+            // Check for included files which are newer than the
+            // JSP which uses them.
+            try {
+                checkCompile();
+            } catch (Throwable t) {
+                t.printStackTrace();
+            }
+        }
+        
+    }
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/JspUtil.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspUtil.java
new file mode 100644
index 0000000..d9a098e
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/JspUtil.java
@@ -0,0 +1,1108 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import javax.servlet.jsp.el.ELException;
+import javax.servlet.jsp.el.ELParseException;
+import javax.servlet.jsp.el.FunctionMapper;
+
+import org.apache.commons.el.ExpressionEvaluatorImpl;
+import org.apache.jasper.Constants;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.xml.sax.Attributes;
+
+/** 
+ * This class has all the utility method(s).
+ * Ideally should move all the bean containers here.
+ *
+ * @author Mandar Raje.
+ * @author Rajiv Mordani.
+ * @author Danno Ferrin
+ * @author Pierre Delisle
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+public class JspUtil {
+
+    private static final String WEB_INF_TAGS = "/WEB-INF/tags/";
+    private static final String META_INF_TAGS = "/META-INF/tags/";
+
+    // Delimiters for request-time expressions (JSP and XML syntax)
+    private static final String OPEN_EXPR  = "<%=";
+    private static final String CLOSE_EXPR = "%>";
+    private static final String OPEN_EXPR_XML  = "%=";
+    private static final String CLOSE_EXPR_XML = "%";
+
+    private static int tempSequenceNumber = 0;
+    private static ExpressionEvaluatorImpl expressionEvaluator
+	= new ExpressionEvaluatorImpl();
+
+    private static final String javaKeywords[] = {
+        "abstract", "assert", "boolean", "break", "byte", "case",
+        "catch", "char", "class", "const", "continue",
+        "default", "do", "double", "else", "enum", "extends",
+        "final", "finally", "float", "for", "goto",
+        "if", "implements", "import", "instanceof", "int",
+        "interface", "long", "native", "new", "package",
+        "private", "protected", "public", "return", "short",
+        "static", "strictfp", "super", "switch", "synchronized",
+        "this", "throws", "transient", "try", "void",
+        "volatile", "while" };
+
+    public static final int CHUNKSIZE = 1024;
+        
+    public static char[] removeQuotes(char []chars) {
+        CharArrayWriter caw = new CharArrayWriter();
+        for (int i = 0; i < chars.length; i++) {
+            if (chars[i] == '%' && chars[i+1] == '\\' &&
+                chars[i+2] == '>') {
+                caw.write('%');
+                caw.write('>');
+                i = i + 2;
+            } else {
+                caw.write(chars[i]);
+            }
+        }
+        return caw.toCharArray();
+    }
+
+    public static char[] escapeQuotes (char []chars) {
+        // Prescan to convert %\> to %>
+        String s = new String(chars);
+        while (true) {
+            int n = s.indexOf("%\\>");
+            if (n < 0)
+                break;
+            StringBuffer sb = new StringBuffer(s.substring(0, n));
+            sb.append("%>");
+            sb.append(s.substring(n + 3));
+            s = sb.toString();
+        }
+        chars = s.toCharArray();
+        return (chars);
+
+
+        // Escape all backslashes not inside a Java string literal
+        /*
+        CharArrayWriter caw = new CharArrayWriter();
+        boolean inJavaString = false;
+        for (int i = 0; i < chars.length; i++) {
+            if (chars[i] == '"') inJavaString = !inJavaString;
+            // escape out the escape character
+            if (!inJavaString && (chars[i] == '\\')) caw.write('\\');
+            caw.write(chars[i]);
+        }
+        return caw.toCharArray();
+        */
+    }
+
+    /**
+     * Checks if the token is a runtime expression.
+     * In standard JSP syntax, a runtime expression starts with '<%' and
+     * ends with '%>'. When the JSP document is in XML syntax, a runtime
+     * expression starts with '%=' and ends with '%'.
+     *
+     * @param token The token to be checked
+     * return whether the token is a runtime expression or not.
+     */
+    public static boolean isExpression(String token, boolean isXml) {
+	String openExpr;
+	String closeExpr;
+	if (isXml) {
+	    openExpr = OPEN_EXPR_XML;
+	    closeExpr = CLOSE_EXPR_XML;
+	} else {
+	    openExpr = OPEN_EXPR;
+	    closeExpr = CLOSE_EXPR;
+	}
+	if (token.startsWith(openExpr) && token.endsWith(closeExpr)) {
+	    return true;
+	} else {
+	    return false;
+	}
+    }
+
+    /**
+     * @return the "expression" part of a runtime expression, 
+     * taking the delimiters out.
+     */
+    public static String getExpr (String expression, boolean isXml) {
+	String returnString;
+	String openExpr;
+	String closeExpr;
+	if (isXml) {
+	    openExpr = OPEN_EXPR_XML;
+	    closeExpr = CLOSE_EXPR_XML;
+	} else {
+	    openExpr = OPEN_EXPR;
+	    closeExpr = CLOSE_EXPR;
+	}
+	int length = expression.length();
+	if (expression.startsWith(openExpr) && 
+                expression.endsWith(closeExpr)) {
+	    returnString = expression.substring(
+                               openExpr.length(), length - closeExpr.length());
+	} else {
+	    returnString = "";
+	}
+	return returnString;
+    }
+
+    /**
+     * Takes a potential expression and converts it into XML form
+     */
+    public static String getExprInXml(String expression) {
+        String returnString;
+        int length = expression.length();
+
+        if (expression.startsWith(OPEN_EXPR) 
+                && expression.endsWith(CLOSE_EXPR)) {
+            returnString = expression.substring (1, length - 1);
+        } else {
+            returnString = expression;
+        }
+
+        return escapeXml(returnString.replace(Constants.ESC, '$'));
+    }
+
+    /**
+     * Checks to see if the given scope is valid.
+     *
+     * @param scope The scope to be checked
+     * @param n The Node containing the 'scope' attribute whose value is to be
+     * checked
+     * @param err error dispatcher
+     *
+     * @throws JasperException if scope is not null and different from
+     * &quot;page&quot;, &quot;request&quot;, &quot;session&quot;, and
+     * &quot;application&quot;
+     */
+    public static void checkScope(String scope, Node n, ErrorDispatcher err)
+            throws JasperException {
+	if (scope != null && !scope.equals("page") && !scope.equals("request")
+		&& !scope.equals("session") && !scope.equals("application")) {
+	    err.jspError(n, "jsp.error.invalid.scope", scope);
+	}
+    }
+
+    /**
+     * Checks if all mandatory attributes are present and if all attributes
+     * present have valid names.  Checks attributes specified as XML-style
+     * attributes as well as attributes specified using the jsp:attribute
+     * standard action. 
+     */
+    public static void checkAttributes(String typeOfTag,
+				       Node n,
+				       ValidAttribute[] validAttributes,
+				       ErrorDispatcher err)
+				throws JasperException {
+        Attributes attrs = n.getAttributes();
+        Mark start = n.getStart();
+	boolean valid = true;
+
+        // AttributesImpl.removeAttribute is broken, so we do this...
+        int tempLength = (attrs == null) ? 0 : attrs.getLength();
+	Vector temp = new Vector(tempLength, 1);
+        for (int i = 0; i < tempLength; i++) {
+            String qName = attrs.getQName(i);
+            if ((!qName.equals("xmlns")) && (!qName.startsWith("xmlns:")))
+                temp.addElement(qName);
+        }
+
+        // Add names of attributes specified using jsp:attribute
+        Node.Nodes tagBody = n.getBody();
+        if( tagBody != null ) {
+            int numSubElements = tagBody.size();
+            for( int i = 0; i < numSubElements; i++ ) {
+                Node node = tagBody.getNode( i );
+                if( node instanceof Node.NamedAttribute ) {
+                    String attrName = node.getAttributeValue( "name" );
+                    temp.addElement( attrName );
+		    // Check if this value appear in the attribute of the node
+		    if (n.getAttributeValue(attrName) != null) {
+			err.jspError(n, "jsp.error.duplicate.name.jspattribute",
+					attrName);
+		    }
+                }
+                else {
+                    // Nothing can come before jsp:attribute, and only
+                    // jsp:body can come after it.
+                    break;
+                }
+            }
+        }
+
+	/*
+	 * First check to see if all the mandatory attributes are present.
+	 * If so only then proceed to see if the other attributes are valid
+	 * for the particular tag.
+	 */
+	String missingAttribute = null;
+
+	for (int i = 0; i < validAttributes.length; i++) {
+	    int attrPos;    
+	    if (validAttributes[i].mandatory) {
+                attrPos = temp.indexOf(validAttributes[i].name);
+		if (attrPos != -1) {
+		    temp.remove(attrPos);
+		    valid = true;
+		} else {
+		    valid = false;
+		    missingAttribute = validAttributes[i].name;
+		    break;
+		}
+	    }
+	}
+
+	// If mandatory attribute is missing then the exception is thrown
+	if (!valid)
+	    err.jspError(start, "jsp.error.mandatory.attribute", typeOfTag,
+			 missingAttribute);
+
+	// Check to see if there are any more attributes for the specified tag.
+        int attrLeftLength = temp.size();
+	if (attrLeftLength == 0)
+	    return;
+
+	// Now check to see if the rest of the attributes are valid too.
+	String attribute = null;
+
+	for (int j = 0; j < attrLeftLength; j++) {
+	    valid = false;
+	    attribute = (String) temp.elementAt(j);
+	    for (int i = 0; i < validAttributes.length; i++) {
+		if (attribute.equals(validAttributes[i].name)) {
+		    valid = true;
+		    break;
+		}
+	    }
+	    if (!valid)
+		err.jspError(start, "jsp.error.invalid.attribute", typeOfTag,
+			     attribute);
+	}
+	// XXX *could* move EL-syntax validation here... (sb)
+    }
+    
+    public static String escapeQueryString(String unescString) {
+	if ( unescString == null )
+	    return null;
+	
+	String escString    = "";
+	String shellSpChars = "\\\"";
+	
+	for(int index=0; index<unescString.length(); index++) {
+	    char nextChar = unescString.charAt(index);
+	    
+	    if( shellSpChars.indexOf(nextChar) != -1 )
+		escString += "\\";
+	    
+	    escString += nextChar;
+	}
+	return escString;
+    }
+ 
+    /**
+     *  Escape the 5 entities defined by XML.
+     */
+    public static String escapeXml(String s) {
+        if (s == null) return null;
+        StringBuffer sb = new StringBuffer();
+        for(int i=0; i<s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '<') {
+                sb.append("&lt;");
+            } else if (c == '>') {
+                sb.append("&gt;");
+            } else if (c == '\'') {
+                sb.append("&apos;");
+            } else if (c == '&') {
+                sb.append("&amp;");
+            } else if (c == '"') {
+                sb.append("&quot;");
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Replaces any occurrences of the character <tt>replace</tt> with the
+     * string <tt>with</tt>.
+     */
+    public static String replace(String name, char replace, String with) {
+	StringBuffer buf = new StringBuffer();
+	int begin = 0;
+	int end;
+	int last = name.length();
+
+	while (true) {
+	    end = name.indexOf(replace, begin);
+	    if (end < 0) {
+		end = last;
+	    }
+	    buf.append(name.substring(begin, end));
+	    if (end == last) {
+		break;
+	    }
+	    buf.append(with);
+	    begin = end + 1;
+	}
+	
+	return buf.toString();
+    }
+
+    public static class ValidAttribute {
+	String name;
+	boolean mandatory;
+	boolean rtexprvalue;	// not used now
+
+	public ValidAttribute (String name, boolean mandatory,
+            boolean rtexprvalue )
+        {
+	    this.name = name;
+	    this.mandatory = mandatory;
+            this.rtexprvalue = rtexprvalue;
+        }
+
+       public ValidAttribute (String name, boolean mandatory) {
+            this( name, mandatory, false );
+	}
+
+	public ValidAttribute (String name) {
+	    this (name, false);
+	}
+    }
+    
+    /**
+     * Convert a String value to 'boolean'.
+     * Besides the standard conversions done by
+     * Boolean.valueOf(s).booleanValue(), the value "yes"
+     * (ignore case) is also converted to 'true'. 
+     * If 's' is null, then 'false' is returned.
+     *
+     * @param s the string to be converted
+     * @return the boolean value associated with the string s
+     */
+    public static boolean booleanValue(String s) {
+	boolean b = false;
+	if (s != null) {
+	    if (s.equalsIgnoreCase("yes")) {
+		b = true;
+	    } else {
+		b = Boolean.valueOf(s).booleanValue();
+	    }
+	}
+	return b;
+    }
+
+    /**
+     * Returns the <tt>Class</tt> object associated with the class or
+     * interface with the given string name.
+     *
+     * <p> The <tt>Class</tt> object is determined by passing the given string
+     * name to the <tt>Class.forName()</tt> method, unless the given string
+     * name represents a primitive type, in which case it is converted to a
+     * <tt>Class</tt> object by appending ".class" to it (e.g., "int.class").
+     */
+    public static Class toClass(String type, ClassLoader loader)
+	    throws ClassNotFoundException {
+
+	Class c = null;
+	int i0 = type.indexOf('[');
+	int dims = 0;
+	if (i0 > 0) {
+	    // This is an array.  Count the dimensions
+	    for (int i = 0; i < type.length(); i++) {
+		if (type.charAt(i) == '[')
+		    dims++;
+	    }
+	    type = type.substring(0, i0);
+	}
+
+	if ("boolean".equals(type))
+	    c = boolean.class;
+	else if ("char".equals(type))
+	    c = char.class;
+	else if ("byte".equals(type))
+	    c =  byte.class;
+	else if ("short".equals(type))
+	    c = short.class;
+	else if ("int".equals(type))
+	    c = int.class;
+	else if ("long".equals(type))
+	    c = long.class;
+	else if ("float".equals(type))
+	    c = float.class;
+	else if ("double".equals(type))
+	    c = double.class;
+	else if (type.indexOf('[') < 0)
+	    c = loader.loadClass(type);
+
+	if (dims == 0)
+	    return c;
+
+	if (dims == 1)
+	    return java.lang.reflect.Array.newInstance(c, 1).getClass();
+
+	// Array of more than i dimension
+	return java.lang.reflect.Array.newInstance(c, new int[dims]).getClass();
+    }
+
+    /**
+     * Produces a String representing a call to the EL interpreter.
+     * @param expression a String containing zero or more "${}" expressions
+     * @param expectedType the expected type of the interpreted result
+     * @param fnmapvar Variable pointing to a function map.
+     * @param XmlEscape True if the result should do XML escaping
+     * @return a String representing a call to the EL interpreter.
+     */
+    public static String interpreterCall(boolean isTagFile,
+					 String expression,
+                                         Class expectedType,
+                                         String fnmapvar,
+                                         boolean XmlEscape ) 
+    {
+        /*
+         * Determine which context object to use.
+         */
+	String jspCtxt = null;
+	if (isTagFile)
+	    jspCtxt = "this.getJspContext()";
+	else
+	    jspCtxt = "_jspx_page_context";
+
+	/*
+         * Determine whether to use the expected type's textual name
+	 * or, if it's a primitive, the name of its correspondent boxed
+	 * type.
+         */
+	String targetType = expectedType.getName();
+	String primitiveConverterMethod = null;
+	if (expectedType.isPrimitive()) {
+	    if (expectedType.equals(Boolean.TYPE)) {
+		targetType = Boolean.class.getName();
+		primitiveConverterMethod = "booleanValue";
+	    } else if (expectedType.equals(Byte.TYPE)) {
+		targetType = Byte.class.getName();
+		primitiveConverterMethod = "byteValue";
+	    } else if (expectedType.equals(Character.TYPE)) {
+		targetType = Character.class.getName();
+		primitiveConverterMethod = "charValue";
+	    } else if (expectedType.equals(Short.TYPE)) {
+		targetType = Short.class.getName();
+		primitiveConverterMethod = "shortValue";
+	    } else if (expectedType.equals(Integer.TYPE)) {
+		targetType = Integer.class.getName();
+		primitiveConverterMethod = "intValue";
+	    } else if (expectedType.equals(Long.TYPE)) {
+		targetType = Long.class.getName();
+		primitiveConverterMethod = "longValue";
+	    } else if (expectedType.equals(Float.TYPE)) {
+		targetType = Float.class.getName();
+		primitiveConverterMethod = "floatValue";
+	    } else if (expectedType.equals(Double.TYPE)) { 
+		targetType = Double.class.getName();
+		primitiveConverterMethod = "doubleValue";
+	    }
+	}
+ 
+	if (primitiveConverterMethod != null) {
+	    XmlEscape = false;
+	}
+
+	/*
+         * Build up the base call to the interpreter.
+         */
+        // XXX - We use a proprietary call to the interpreter for now
+        // as the current standard machinery is inefficient and requires
+        // lots of wrappers and adapters.  This should all clear up once
+        // the EL interpreter moves out of JSTL and into its own project.
+        // In the future, this should be replaced by code that calls
+        // ExpressionEvaluator.parseExpression() and then cache the resulting
+        // expression objects.  The interpreterCall would simply select
+        // one of the pre-cached expressions and evaluate it.
+        // Note that PageContextImpl implements VariableResolver and
+        // the generated Servlet/SimpleTag implements FunctionMapper, so
+        // that machinery is already in place (mroth).
+	targetType = toJavaSourceType(targetType);
+	StringBuffer call = new StringBuffer(
+             "(" + targetType + ") "
+               + "org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate"
+               + "(" + Generator.quote(expression) + ", "
+               +       targetType + ".class, "
+	       +       "(PageContext)" + jspCtxt 
+               +       ", " + fnmapvar
+	       + ", " + XmlEscape
+               + ")");
+ 
+	/*
+         * Add the primitive converter method if we need to.
+         */
+	if (primitiveConverterMethod != null) {
+	    call.insert(0, "(");
+	    call.append(")." + primitiveConverterMethod + "()");
+	}
+ 
+	return call.toString();
+    }
+
+    /**
+     * Validates the syntax of all ${} expressions within the given string.
+     * @param where the approximate location of the expressions in the JSP page
+     * @param expressions a string containing zero or more "${}" expressions
+     * @param err an error dispatcher to use
+     */
+    public static void validateExpressions(Mark where,
+                                           String expressions,
+                                           Class expectedType,
+                                           FunctionMapper functionMapper,
+                                           ErrorDispatcher err)
+            throws JasperException {
+
+        try {
+            JspUtil.expressionEvaluator.parseExpression( expressions, 
+                expectedType, null );
+        }
+        catch( ELParseException e ) {
+            err.jspError(where, "jsp.error.invalid.expression", expressions,
+                e.toString() );
+        }
+        catch( ELException e ) {
+            err.jspError(where, "jsp.error.invalid.expression", expressions,
+                e.toString() );
+        }
+    }
+
+    /**
+     * Resets the temporary variable name.
+     * (not thread-safe)
+     */
+    public static void resetTemporaryVariableName() {
+        tempSequenceNumber = 0;
+    }
+
+    /**
+     * Generates a new temporary variable name.
+     * (not thread-safe)
+     */
+    public static String nextTemporaryVariableName() {
+        return Constants.TEMP_VARIABLE_NAME_PREFIX + (tempSequenceNumber++);
+    }
+
+    public static String coerceToPrimitiveBoolean(String s,
+						  boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToBoolean(" + s + ")";
+	} else {
+	    if (s == null || s.length() == 0)
+		return "false";
+	    else
+		return Boolean.valueOf(s).toString();
+	}
+    }
+
+    public static String coerceToBoolean(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "(Boolean) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Boolean.class)";
+	} else {
+	    if (s == null || s.length() == 0) {
+		return "new Boolean(false)";
+	    } else {
+		// Detect format error at translation time
+		return "new Boolean(" + Boolean.valueOf(s).toString() + ")";
+	    }
+	}
+    }
+
+    public static String coerceToPrimitiveByte(String s,
+					       boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToByte(" + s + ")";
+	} else {
+	    if (s == null || s.length() == 0)
+		return "(byte) 0";
+	    else
+		return "((byte)" + Byte.valueOf(s).toString() + ")";
+	}
+    }
+
+    public static String coerceToByte(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "(Byte) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Byte.class)";
+	} else {
+	    if (s == null || s.length() == 0) {
+		return "new Byte((byte) 0)";
+	    } else {
+		// Detect format error at translation time
+		return "new Byte((byte)" + Byte.valueOf(s).toString() + ")";
+	    }
+	}
+    }
+
+    public static String coerceToChar(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToChar(" + s + ")";
+	} else {
+	    if (s == null || s.length() == 0) {
+		return "(char) 0";
+	    } else {
+		char ch = s.charAt(0);
+		// this trick avoids escaping issues
+		return "((char) " + (int) ch + ")";
+	    }
+	}
+    }
+
+    public static String coerceToCharacter(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "(Character) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Character.class)";
+	} else {
+	    if (s == null || s.length() == 0) {
+		return "new Character((char) 0)";
+	    } else {
+		char ch = s.charAt(0);
+		// this trick avoids escaping issues
+		return "new Character((char) " + (int) ch + ")";
+	    }
+	}
+    }
+
+    public static String coerceToPrimitiveDouble(String s,
+						 boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToDouble(" + s + ")";
+	} else {
+	    if (s == null || s.length() == 0)
+		return "(double) 0";
+	    else
+		return Double.valueOf(s).toString();
+	}
+    }
+
+    public static String coerceToDouble(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "(Double) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Double.class)";
+	} else {
+	    if (s == null || s.length() == 0) {
+		return "new Double(0)";
+	    } else {
+		// Detect format error at translation time
+		return "new Double(" + Double.valueOf(s).toString() + ")";
+	    }
+	}
+    }
+
+    public static String coerceToPrimitiveFloat(String s,
+						boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToFloat(" + s + ")";
+	} else {
+	    if (s == null || s.length() == 0)
+		return "(float) 0";
+	    else
+		return Float.valueOf(s).toString() + "f";
+	}
+    }
+
+    public static String coerceToFloat(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "(Float) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Float.class)";
+	} else {
+	    if (s == null || s.length() == 0) {
+		return "new Float(0)";
+	    } else {
+		// Detect format error at translation time
+		return "new Float(" + Float.valueOf(s).toString() + "f)";
+	    }
+	}
+    }
+
+    public static String coerceToInt(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToInt(" + s + ")";
+	} else {
+	    if (s == null || s.length() == 0)
+		return "0";
+	    else
+		return Integer.valueOf(s).toString();
+	}
+    }
+
+    public static String coerceToInteger(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "(Integer) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Integer.class)";
+	} else {
+	    if (s == null || s.length() == 0) {
+		return "new Integer(0)";
+	    } else {
+		// Detect format error at translation time
+		return "new Integer(" + Integer.valueOf(s).toString() + ")";
+	    }
+	}
+    }
+
+    public static String coerceToPrimitiveShort(String s,
+						boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToShort(" + s + ")";
+	} else {
+	    if (s == null || s.length() == 0)
+		return "(short) 0";
+	    else
+		return "((short) " + Short.valueOf(s).toString() + ")";
+	}
+    }
+    
+    public static String coerceToShort(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "(Short) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Short.class)";
+	} else {
+	    if (s == null || s.length() == 0) {
+		return "new Short((short) 0)";
+	    } else {
+		// Detect format error at translation time
+		return "new Short(\"" + Short.valueOf(s).toString() + "\")";
+	    }
+	}
+    }
+    
+    public static String coerceToPrimitiveLong(String s,
+					       boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToLong(" + s + ")";
+	} else {
+	    if (s == null || s.length() == 0)
+		return "(long) 0";
+	    else
+		return Long.valueOf(s).toString() + "l";
+	}
+    }
+
+    public static String coerceToLong(String s, boolean isNamedAttribute) {
+	if (isNamedAttribute) {
+	    return "(Long) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Long.class)";
+	} else {
+	    if (s == null || s.length() == 0) {
+		return "new Long(0)";
+	    } else {
+		// Detect format error at translation time
+		return "new Long(" + Long.valueOf(s).toString() + "l)";
+	    }
+	}
+    }
+
+    public static InputStream getInputStream(String fname, JarFile jarFile,
+					     JspCompilationContext ctxt,
+					     ErrorDispatcher err)
+		throws JasperException, IOException {
+
+        InputStream in = null;
+
+	if (jarFile != null) {
+	    String jarEntryName = fname.substring(1, fname.length());
+	    ZipEntry jarEntry = jarFile.getEntry(jarEntryName);
+	    if (jarEntry == null) {
+		err.jspError("jsp.error.file.not.found", fname);
+	    }
+	    in = jarFile.getInputStream(jarEntry);
+	} else {
+	    in = ctxt.getResourceAsStream(fname);
+	}
+
+	if (in == null) {
+	    err.jspError("jsp.error.file.not.found", fname);
+	}
+
+	return in;
+    }
+
+    /**
+     * Gets the fully-qualified class name of the tag handler corresponding to
+     * the given tag file path.
+     *
+     * @param path Tag file path
+     * @param err Error dispatcher
+     *
+     * @return Fully-qualified class name of the tag handler corresponding to 
+     * the given tag file path
+     */
+    public static String getTagHandlerClassName(String path,
+						ErrorDispatcher err)
+                throws JasperException {
+
+        String className = null;
+        int begin = 0;
+        int index;
+        
+        index = path.lastIndexOf(".tag");
+        if (index == -1) {
+            err.jspError("jsp.error.tagfile.badSuffix", path);
+        }
+
+        //It's tempting to remove the ".tag" suffix here, but we can't.
+        //If we remove it, the fully-qualified class name of this tag
+        //could conflict with the package name of other tags.
+        //For instance, the tag file
+        //    /WEB-INF/tags/foo.tag
+        //would have fully-qualified class name
+        //    org.apache.jsp.tag.web.foo
+        //which would conflict with the package name of the tag file
+        //    /WEB-INF/tags/foo/bar.tag
+
+        index = path.indexOf(WEB_INF_TAGS);
+        if (index != -1) {
+            className = "org.apache.jsp.tag.web.";
+            begin = index + WEB_INF_TAGS.length();
+        } else {
+	    index = path.indexOf(META_INF_TAGS);
+	    if (index != -1) {
+		className = "org.apache.jsp.tag.meta.";
+		begin = index + META_INF_TAGS.length();
+	    } else {
+		err.jspError("jsp.error.tagfile.illegalPath", path);
+	    }
+	}
+
+        className += makeJavaPackage(path.substring(begin));
+  
+       return className;
+    }
+
+    /**
+     * Converts the given path to a Java package or fully-qualified class name
+     *
+     * @param path Path to convert
+     *
+     * @return Java package corresponding to the given path
+     */
+    public static final String makeJavaPackage(String path) {
+        String classNameComponents[] = split(path,"/");
+        StringBuffer legalClassNames = new StringBuffer();
+        for (int i = 0; i < classNameComponents.length; i++) {
+            legalClassNames.append(makeJavaIdentifier(classNameComponents[i]));
+            if (i < classNameComponents.length - 1) {
+                legalClassNames.append('.');
+            }
+        }
+        return legalClassNames.toString();
+    }
+
+    /**
+     * Splits a string into it's components.
+     * @param path String to split
+     * @param pat Pattern to split at
+     * @return the components of the path
+     */
+    private static final String [] split(String path, String pat) {
+        Vector comps = new Vector();
+        int pos = path.indexOf(pat);
+        int start = 0;
+        while( pos >= 0 ) {
+            if(pos > start ) {
+                String comp = path.substring(start,pos);
+                comps.add(comp);
+            }
+            start = pos + pat.length();
+            pos = path.indexOf(pat,start);
+        }
+        if( start < path.length()) {
+            comps.add(path.substring(start));
+        }
+        String [] result = new String[comps.size()];
+        for(int i=0; i < comps.size(); i++) {
+            result[i] = (String)comps.elementAt(i);
+        }
+        return result;
+    }
+            
+    /**
+     * Converts the given identifier to a legal Java identifier
+     *
+     * @param identifier Identifier to convert
+     *
+     * @return Legal Java identifier corresponding to the given identifier
+     */
+    public static final String makeJavaIdentifier(String identifier) {
+        StringBuffer modifiedIdentifier = 
+            new StringBuffer(identifier.length());
+        if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
+            modifiedIdentifier.append('_');
+        }
+        for (int i = 0; i < identifier.length(); i++) {
+            char ch = identifier.charAt(i);
+            if (Character.isJavaIdentifierPart(ch) && ch != '_') {
+                modifiedIdentifier.append(ch);
+            } else if (ch == '.') {
+                modifiedIdentifier.append('_');
+            } else {
+                modifiedIdentifier.append(mangleChar(ch));
+            }
+        }
+        if (isJavaKeyword(modifiedIdentifier.toString())) {
+            modifiedIdentifier.append('_');
+        }
+        return modifiedIdentifier.toString();
+    }
+    
+    /**
+     * Mangle the specified character to create a legal Java class name.
+     */
+    public static final String mangleChar(char ch) {
+        char[] result = new char[5];
+        result[0] = '_';
+        result[1] = Character.forDigit((ch >> 12) & 0xf, 16);
+        result[2] = Character.forDigit((ch >> 8) & 0xf, 16);
+        result[3] = Character.forDigit((ch >> 4) & 0xf, 16);
+        result[4] = Character.forDigit(ch & 0xf, 16);
+        return new String(result);
+    }
+
+    /**
+     * Test whether the argument is a Java keyword
+     */
+    public static boolean isJavaKeyword(String key) {
+        int i = 0;
+        int j = javaKeywords.length;
+        while (i < j) {
+            int k = (i+j)/2;
+            int result = javaKeywords[k].compareTo(key);
+            if (result == 0) {
+                return true;
+            }
+            if (result < 0) {
+                i = k+1;
+            } else {
+                j = k;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Converts the given Xml name to a legal Java identifier.  This is
+     * slightly more efficient than makeJavaIdentifier in that we only need
+     * to worry about '.', '-', and ':' in the string.  We also assume that
+     * the resultant string is further concatenated with some prefix string
+     * so that we don't have to worry about it being a Java key word.
+     *
+     * @param name Identifier to convert
+     *
+     * @return Legal Java identifier corresponding to the given identifier
+     */
+    public static final String makeXmlJavaIdentifier(String name) {
+        if (name.indexOf('-') >= 0)
+            name = replace(name, '-', "$1");
+        if (name.indexOf('.') >= 0)
+            name = replace(name, '.', "$2");
+        if (name.indexOf(':') >= 0)
+            name = replace(name, ':', "$3");
+        return name;
+    }
+
+    static InputStreamReader getReader(String fname, String encoding,
+				       JarFile jarFile,
+				       JspCompilationContext ctxt,
+				       ErrorDispatcher err)
+		throws JasperException, IOException {
+
+        InputStreamReader reader = null;
+	InputStream in = getInputStream(fname, jarFile, ctxt, err);
+
+	try {
+            reader = new InputStreamReader(in, encoding);
+	} catch (UnsupportedEncodingException ex) {
+	    err.jspError("jsp.error.unsupported.encoding", encoding);
+	}
+
+	return reader;
+    }
+
+    /**
+     * Class.getName() return arrays in the form "[[[<et>", where et,
+     * the element type can be one of ZBCDFIJS or L<classname>;
+     * It is converted into forms that can be understood by javac.
+     */
+    public static String toJavaSourceType(String type) {
+
+	if (type.charAt(0) != '[') {
+	    return type;
+ 	}
+
+	int dims = 1;
+	String t = null;
+	for (int i = 1; i < type.length(); i++) {
+	    if (type.charAt(i) == '[') {
+		dims++;
+	    } else {
+		switch (type.charAt(i)) {
+		case 'Z': t = "boolean"; break;
+		case 'B': t = "byte"; break;
+		case 'C': t = "char"; break;
+		case 'D': t = "double"; break;
+		case 'F': t = "float"; break;
+		case 'I': t = "int"; break;
+		case 'J': t = "long"; break;
+		case 'S': t = "short"; break;
+		case 'L': t = type.substring(i+1, type.indexOf(';')); break;
+		}
+		break;
+	    }
+	}
+	StringBuffer resultType = new StringBuffer(t);
+	for (; dims > 0; dims--) {
+	    resultType.append("[]");
+	}
+	return resultType.toString();
+    }
+
+    /**
+     * Compute the canonical name from a Class instance.  Note that a
+     * simple replacment of '$' with '.' of a binary name would not work,
+     * as '$' is a legal Java Identifier character.
+     * @param c A instance of java.lang.Class
+     * @return  The canonical name of c.
+     */
+    public static String getCanonicalName(Class c) {
+
+        String binaryName = c.getName();
+        c = c.getDeclaringClass();
+
+        if (c == null) {
+            return binaryName;
+        }
+
+        StringBuffer buf = new StringBuffer(binaryName);
+        do {
+            buf.setCharAt(c.getName().length(), '.');
+            c = c.getDeclaringClass();
+        } while ( c != null);
+
+        return buf.toString();
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/Localizer.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/Localizer.java
new file mode 100644
index 0000000..3645561
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/Localizer.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Class responsible for converting error codes to corresponding localized
+ * error messages.
+ *
+ * @author Jan Luehe
+ */
+public class Localizer {
+
+    private static ResourceBundle bundle = null;
+    
+    static {
+        try {
+        bundle = ResourceBundle.getBundle(
+            "org.apache.jasper.resources.LocalStrings");
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    /*
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * 
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode) {
+	String errMsg = errCode;
+	try {
+	    errMsg = bundle.getString(errCode);
+	} catch (MissingResourceException e) {
+	}
+	return errMsg;
+    }
+
+    /* 
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param arg Argument for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, String arg) {
+	return getMessage(errCode, new Object[] {arg});
+    }
+
+    /* 
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, String arg1, String arg2) {
+	return getMessage(errCode, new Object[] {arg1, arg2});
+    }
+    
+    /* 
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     * @param arg3 Third argument for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, String arg1, String arg2,
+				    String arg3) {
+	return getMessage(errCode, new Object[] {arg1, arg2, arg3});
+    }
+
+    /* 
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     * @param arg3 Third argument for parametric replacement
+     * @param arg4 Fourth argument for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, String arg1, String arg2,
+				    String arg3, String arg4) {
+	return getMessage(errCode, new Object[] {arg1, arg2, arg3, arg4});
+    }
+
+    /*
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param args Arguments for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, Object[] args) {
+	String errMsg = errCode;
+	try {
+	    errMsg = bundle.getString(errCode);
+	    if (args != null) {
+		MessageFormat formatter = new MessageFormat(errMsg);
+		errMsg = formatter.format(args);
+	    }
+	} catch (MissingResourceException e) {
+	}
+	
+	return errMsg;
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/Mark.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/Mark.java
new file mode 100644
index 0000000..81ea186
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/Mark.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.compiler;
+
+import java.util.Stack;
+import java.net.URL;
+import java.net.MalformedURLException;
+import org.apache.jasper.JspCompilationContext;
+
+/**
+ * Mark represents a point in the JSP input. 
+ *
+ * @author Anil K. Vijendran
+ */
+final class Mark {
+
+    // position within current stream
+    int cursor, line, col;
+
+    // directory of file for current stream
+    String baseDir;
+
+    // current stream
+    char[] stream = null;
+
+    // fileid of current stream
+    private int fileId;
+
+    // name of the current file
+    private String fileName;
+
+    /*
+     * stack of stream and stream state of streams that have included
+     * current stream
+     */
+    private Stack includeStack = null;
+
+    // encoding of current file
+    private String encoding = null;
+
+    // reader that owns this mark (so we can look up fileid's)
+    private JspReader reader;
+
+    private JspCompilationContext ctxt;
+
+    /**
+     * Constructor
+     *
+     * @param reader JspReader this mark belongs to
+     * @param inStream current stream for this mark
+     * @param fileId id of requested jsp file
+     * @param name JSP file name
+     * @param inBaseDir base directory of requested jsp file
+     * @param inEncoding encoding of current file
+     */
+    Mark(JspReader reader, char[] inStream, int fileId, String name,
+         String inBaseDir, String inEncoding) {
+
+        this.reader = reader;
+        this.ctxt = reader.getJspCompilationContext();
+        this.stream = inStream;
+        this.cursor = 0;
+        this.line = 1;
+        this.col = 1;
+        this.fileId = fileId;
+        this.fileName = name;
+        this.baseDir = inBaseDir;
+        this.encoding = inEncoding;
+        this.includeStack = new Stack();
+    }
+
+
+    /**
+     * Constructor
+     */
+    Mark(Mark other) {
+
+        this.reader = other.reader;
+        this.ctxt = other.reader.getJspCompilationContext();
+        this.stream = other.stream;
+        this.fileId = other.fileId;
+        this.fileName = other.fileName;
+        this.cursor = other.cursor;
+        this.line = other.line;
+        this.col = other.col;
+        this.baseDir = other.baseDir;
+        this.encoding = other.encoding;
+
+        // clone includeStack without cloning contents
+        includeStack = new Stack();
+        for ( int i=0; i < other.includeStack.size(); i++ ) {
+            includeStack.addElement( other.includeStack.elementAt(i) );
+        }
+    }
+
+
+    /**
+     * Constructor
+     */    
+    Mark(JspCompilationContext ctxt, String filename, int line, int col) {
+
+        this.reader = null;
+        this.ctxt = ctxt;
+        this.stream = null;
+        this.cursor = 0;
+        this.line = line;
+        this.col = col;
+        this.fileId = -1;
+        this.fileName = filename;
+        this.baseDir = "le-basedir";
+        this.encoding = "le-endocing";
+        this.includeStack = null;
+    }
+
+
+    /**
+     * Sets this mark's state to a new stream.
+     * It will store the current stream in it's includeStack.
+     *
+     * @param inStream new stream for mark
+     * @param inFileId id of new file from which stream comes from
+     * @param inBaseDir directory of file
+     * @param inEncoding encoding of new file
+     */
+    public void pushStream(char[] inStream, int inFileId, String name,
+                           String inBaseDir, String inEncoding) 
+    {
+        // store current state in stack
+        includeStack.push(new IncludeState(cursor, line, col, fileId,
+                                           fileName, baseDir, 
+					   encoding, stream) );
+
+        // set new variables
+        cursor = 0;
+        line = 1;
+        col = 1;
+        fileId = inFileId;
+        fileName = name;
+        baseDir = inBaseDir;
+        encoding = inEncoding;
+        stream = inStream;
+    }
+
+
+    /**
+     * Restores this mark's state to a previously stored stream.
+     * @return The previous Mark instance when the stream was pushed, or null
+     * if there is no previous stream
+     */
+    public Mark popStream() {
+        // make sure we have something to pop
+        if ( includeStack.size() <= 0 ) {
+            return null;
+        }
+
+        // get previous state in stack
+        IncludeState state = (IncludeState) includeStack.pop( );
+
+        // set new variables
+        cursor = state.cursor;
+        line = state.line;
+        col = state.col;
+        fileId = state.fileId;
+        fileName = state.fileName;
+        baseDir = state.baseDir;
+        stream = state.stream;
+        return this;
+    }
+
+
+    // -------------------- Locator interface --------------------
+
+    public int getLineNumber() {
+        return line;
+    }
+
+    public int getColumnNumber() {
+        return col;
+    }
+
+    public String getSystemId() {
+        return getFile();
+    }
+
+    public String getPublicId() {
+        return null;
+    }
+
+    public String toString() {
+	return getFile()+"("+line+","+col+")";
+    }
+
+    public String getFile() {
+        return this.fileName;
+    }
+
+    /**
+     * Gets the URL of the resource with which this Mark is associated
+     *
+     * @return URL of the resource with which this Mark is associated
+     *
+     * @exception MalformedURLException if the resource pathname is incorrect
+     */
+    public URL getURL() throws MalformedURLException {
+        return ctxt.getResource(getFile());
+    }
+
+    public String toShortString() {
+        return "("+line+","+col+")";
+    }
+
+    public boolean equals(Object other) {
+	if (other instanceof Mark) {
+	    Mark m = (Mark) other;
+	    return this.reader == m.reader && this.fileId == m.fileId 
+		&& this.cursor == m.cursor && this.line == m.line 
+		&& this.col == m.col;
+	} 
+	return false;
+    }
+
+    /**
+     * @return true if this Mark is greather than the <code>other</code>
+     * Mark, false otherwise.
+     */
+    public boolean isGreater(Mark other) {
+
+        boolean greater = false;
+
+        if (this.line > other.line) {
+            greater = true;
+        } else if (this.line == other.line && this.col > other.col) {
+            greater = true;
+        }
+
+        return greater;
+    }
+
+    /**
+     * Keep track of parser before parsing an included file.
+     * This class keeps track of the parser before we switch to parsing an
+     * included file. In other words, it's the parser's continuation to be
+     * reinstalled after the included file parsing is done.
+     */
+    class IncludeState {
+        int cursor, line, col;
+        int fileId;
+        String fileName;
+        String baseDir;
+        String encoding;
+        char[] stream = null;
+
+        IncludeState(int inCursor, int inLine, int inCol, int inFileId, 
+                     String name, String inBaseDir, String inEncoding,
+                     char[] inStream) {
+            cursor = inCursor;
+            line = inLine;
+            col = inCol;
+            fileId = inFileId;
+            fileName = name;
+            baseDir = inBaseDir;
+            encoding = inEncoding;
+            stream = inStream;
+        }
+    }
+
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/Node.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/Node.java
new file mode 100644
index 0000000..877eca4
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/Node.java
@@ -0,0 +1,2369 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+import java.util.ArrayList;
+
+import javax.servlet.jsp.tagext.BodyTag;
+import javax.servlet.jsp.tagext.DynamicAttributes;
+import javax.servlet.jsp.tagext.IterationTag;
+import javax.servlet.jsp.tagext.SimpleTag;
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagData;
+import javax.servlet.jsp.tagext.TagFileInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagVariableInfo;
+import javax.servlet.jsp.tagext.TryCatchFinally;
+import javax.servlet.jsp.tagext.VariableInfo;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.compiler.tagplugin.TagPluginContext;
+import org.xml.sax.Attributes;
+
+
+/**
+ * An internal data representation of a JSP page or a JSP docuement (XML).
+ * Also included here is a visitor class for tranversing nodes.
+ *
+ * @author Kin-man Chung
+ * @author Jan Luehe
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+
+abstract class Node implements TagConstants {
+
+    private static final VariableInfo[] ZERO_VARIABLE_INFO = { };
+    
+    protected Attributes attrs;
+
+    // xmlns attributes that represent tag libraries (only in XML syntax)
+    protected Attributes taglibAttrs;
+
+    /*
+     * xmlns attributes that do not represent tag libraries
+     * (only in XML syntax)
+     */
+    protected Attributes nonTaglibXmlnsAttrs;
+
+    protected Nodes body;
+    protected String text;
+    protected Mark startMark;
+    protected int beginJavaLine;
+    protected int endJavaLine;
+    protected Node parent;
+    protected Nodes namedAttributeNodes; // cached for performance
+    protected String qName;
+    protected String localName;
+    /*
+     * The name of the inner class to which the codes for this node and
+     * its body are generated.  For instance, for <jsp:body> in foo.jsp,
+     * this is "foo_jspHelper".  This is primarily used for communicating
+     * such info from Generator to Smap generator.
+     */
+    protected String innerClassName;
+
+    private boolean isDummy;
+
+    /**
+     * Zero-arg Constructor.
+     */
+    public Node() {
+	this.isDummy = true;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param start The location of the jsp page
+     * @param parent The enclosing node
+     */
+    public Node(Mark start, Node parent) {
+	this.startMark = start;
+	this.isDummy = (start == null);
+	addToParent(parent);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param qName The action's qualified name
+     * @param localName The action's local name
+     * @param start The location of the jsp page
+     * @param parent The enclosing node
+     */
+    public Node(String qName, String localName, Mark start, Node parent) {
+	this.qName = qName;
+	this.localName = localName;
+	this.startMark = start;
+	this.isDummy = (start == null);
+	addToParent(parent);
+    }
+
+    /**
+     * Constructor for Nodes parsed from standard syntax.
+     *
+     * @param qName The action's qualified name
+     * @param localName The action's local name
+     * @param attrs The attributes for this node
+     * @param start The location of the jsp page
+     * @param parent The enclosing node
+     */
+    public Node(String qName, String localName, Attributes attrs, Mark start,
+		Node parent) {
+	this.qName = qName;
+	this.localName = localName;
+	this.attrs = attrs;
+	this.startMark = start;
+	this.isDummy = (start == null);
+	addToParent(parent);
+    }
+
+    /**
+     * Constructor for Nodes parsed from XML syntax.
+     *
+     * @param qName The action's qualified name
+     * @param localName The action's local name
+     * @param attrs The action's attributes whose name does not start with
+     * xmlns
+     * @param nonTaglibXmlnsAttrs The action's xmlns attributes that do not
+     * represent tag libraries
+     * @param taglibAttrs The action's xmlns attributes that represent tag
+     * libraries
+     * @param start The location of the jsp page
+     * @param parent The enclosing node
+     */
+    public Node(String qName, String localName, Attributes attrs,
+		Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs,
+		Mark start, Node parent) {
+	this.qName = qName;
+	this.localName = localName;
+	this.attrs = attrs;
+	this.nonTaglibXmlnsAttrs = nonTaglibXmlnsAttrs;
+	this.taglibAttrs = taglibAttrs;
+	this.startMark = start;
+	this.isDummy = (start == null);
+	addToParent(parent);
+    }
+
+    /*
+     * Constructor.
+     *
+     * @param qName The action's qualified name
+     * @param localName The action's local name
+     * @param text The text associated with this node
+     * @param start The location of the jsp page
+     * @param parent The enclosing node
+     */
+    public Node(String qName, String localName, String text, Mark start,
+		Node parent) {
+	this.qName = qName;
+	this.localName = localName;
+	this.text = text;
+	this.startMark = start;
+	this.isDummy = (start == null);
+	addToParent(parent);
+    }
+
+    public String getQName() {
+	return this.qName;
+    }
+
+    public String getLocalName() {
+	return this.localName;
+    }
+
+    /*
+     * Gets this Node's attributes.
+     *
+     * In the case of a Node parsed from standard syntax, this method returns
+     * all the Node's attributes.
+     *
+     * In the case of a Node parsed from XML syntax, this method returns only
+     * those attributes whose name does not start with xmlns.
+     */
+    public Attributes getAttributes() {
+	return this.attrs;
+    }
+
+    /*
+     * Gets this Node's xmlns attributes that represent tag libraries
+     * (only meaningful for Nodes parsed from XML syntax)
+     */
+    public Attributes getTaglibAttributes() {
+	return this.taglibAttrs;
+    }
+
+    /*
+     * Gets this Node's xmlns attributes that do not represent tag libraries
+     * (only meaningful for Nodes parsed from XML syntax)
+     */
+    public Attributes getNonTaglibXmlnsAttributes() {
+	return this.nonTaglibXmlnsAttrs;
+    }
+
+    public void setAttributes(Attributes attrs) {
+	this.attrs = attrs;
+    }
+
+    public String getAttributeValue(String name) {
+	return (attrs == null) ? null : attrs.getValue(name);
+    }
+
+    /**
+     * Get the attribute that is non request time expression, either
+     * from the attribute of the node, or from a jsp:attrbute 
+     */
+    public String getTextAttribute(String name) {
+
+	String attr = getAttributeValue(name);
+	if (attr != null) {
+	    return attr;
+	}
+
+	NamedAttribute namedAttribute = getNamedAttributeNode(name);
+	if (namedAttribute == null) {
+	    return null;
+	}
+
+	return namedAttribute.getText();
+    }
+
+    /**
+     * Searches all subnodes of this node for jsp:attribute standard
+     * actions with the given name, and returns the NamedAttribute node
+     * of the matching named attribute, nor null if no such node is found.
+     * <p>
+     * This should always be called and only be called for nodes that
+     * accept dynamic runtime attribute expressions.
+     */
+    public NamedAttribute getNamedAttributeNode( String name ) {
+        NamedAttribute result = null;
+        
+        // Look for the attribute in NamedAttribute children
+        Nodes nodes = getNamedAttributeNodes();
+        int numChildNodes = nodes.size();
+        for( int i = 0; i < numChildNodes; i++ ) {
+            NamedAttribute na = (NamedAttribute)nodes.getNode( i );
+	    boolean found = false;
+	    int index = name.indexOf(':');
+	    if (index != -1) {
+		// qualified name
+		found = na.getName().equals(name);
+	    } else {
+		found = na.getLocalName().equals(name);
+	    }
+	    if (found) {
+                result = na;
+                break;
+            }
+        }
+        
+        return result;
+    }
+
+    /**
+     * Searches all subnodes of this node for jsp:attribute standard
+     * actions, and returns that set of nodes as a Node.Nodes object.
+     *
+     * @return Possibly empty Node.Nodes object containing any jsp:attribute
+     * subnodes of this Node
+     */
+    public Node.Nodes getNamedAttributeNodes() {
+
+	if (namedAttributeNodes != null) {
+	    return namedAttributeNodes;
+	}
+
+        Node.Nodes result = new Node.Nodes();
+        
+        // Look for the attribute in NamedAttribute children
+        Nodes nodes = getBody();
+        if( nodes != null ) {
+            int numChildNodes = nodes.size();
+            for( int i = 0; i < numChildNodes; i++ ) {
+                Node n = nodes.getNode( i );
+                if( n instanceof NamedAttribute ) {
+                    result.add( n );
+                }
+                else if (! (n instanceof Comment)) {
+                    // Nothing can come before jsp:attribute, and only
+                    // jsp:body can come after it.
+                    break;
+                }
+            }
+        }
+
+	namedAttributeNodes = result;
+        return result;
+    }
+    
+    public Nodes getBody() {
+	return body;
+    }
+
+    public void setBody(Nodes body) {
+	this.body = body;
+    }
+
+    public String getText() {
+	return text;
+    }
+
+    public Mark getStart() {
+	return startMark;
+    }
+
+    public Node getParent() {
+	return parent;
+    }
+
+    public int getBeginJavaLine() {
+	return beginJavaLine;
+    }
+
+    public void setBeginJavaLine(int begin) {
+	beginJavaLine = begin;
+    }
+
+    public int getEndJavaLine() {
+	return endJavaLine;
+    }
+
+    public void setEndJavaLine(int end) {
+	endJavaLine = end;
+    }
+
+    public boolean isDummy() {
+	return isDummy;
+    }
+
+    public Node.Root getRoot() {
+	Node n = this;
+	while (!(n instanceof Node.Root)) {
+	    n = n.getParent();
+	}
+	return (Node.Root) n;
+    }
+
+    public String getInnerClassName() {
+        return innerClassName;
+    }
+
+    public void setInnerClassName(String icn) {
+        innerClassName = icn;
+    }
+
+    /**
+     * Selects and invokes a method in the visitor class based on the node
+     * type.  This is abstract and should be overrode by the extending classes.
+     * @param v The visitor class
+     */
+    abstract void accept(Visitor v) throws JasperException;
+
+
+    //*********************************************************************
+    // Private utility methods
+
+    /*
+     * Adds this Node to the body of the given parent.
+     */
+    private void addToParent(Node parent) {
+	if (parent != null) {
+	    this.parent = parent;
+	    Nodes parentBody = parent.getBody();
+	    if (parentBody == null) {
+		parentBody = new Nodes();
+		parent.setBody(parentBody);
+	    }
+	    parentBody.add(this);
+	}
+    }
+
+
+    /*********************************************************************
+     * Child classes
+     */
+    
+    /**
+     * Represents the root of a Jsp page or Jsp document
+     */
+    public static class Root extends Node {
+
+	private Root parentRoot;
+	private boolean isXmlSyntax;
+
+	// Source encoding of the page containing this Root
+	private String pageEnc;
+	
+	// Page encoding specified in JSP config element
+	private String jspConfigPageEnc;
+
+	/*
+	 * Flag indicating if the default page encoding is being used (only
+	 * applicable with standard syntax).
+	 *
+	 * True if the page does not provide a page directive with a
+	 * 'contentType' attribute (or the 'contentType' attribute doesn't
+	 * have a CHARSET value), the page does not provide a page directive
+	 * with a 'pageEncoding' attribute, and there is no JSP configuration
+	 * element page-encoding whose URL pattern matches the page.
+	 */
+	private boolean isDefaultPageEncoding;
+
+	/*
+	 * Indicates whether an encoding has been explicitly specified in the
+	 * page's XML prolog (only used for pages in XML syntax).
+	 * This information is used to decide whether a translation error must
+	 * be reported for encoding conflicts.
+	 */
+	private boolean isEncodingSpecifiedInProlog;
+
+	/*
+	 * Constructor.
+	 */
+	Root(Mark start, Node parent, boolean isXmlSyntax) {
+	    super(start, parent);
+	    this.isXmlSyntax = isXmlSyntax;
+	    this.qName = JSP_ROOT_ACTION;
+	    this.localName = ROOT_ACTION;
+
+	    // Figure out and set the parent root
+	    Node r = parent;
+	    while ((r != null) && !(r instanceof Node.Root))
+		r = r.getParent();
+	    parentRoot = (Node.Root) r;
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public boolean isXmlSyntax() {
+	    return isXmlSyntax;
+	}
+
+	/*
+	 * Sets the encoding specified in the JSP config element whose URL
+	 * pattern matches the page containing this Root.
+	 */
+	public void setJspConfigPageEncoding(String enc) {
+	    jspConfigPageEnc = enc;
+	}
+
+	/*
+	 * Gets the encoding specified in the JSP config element whose URL
+	 * pattern matches the page containing this Root.
+	 */
+	public String getJspConfigPageEncoding() {
+	    return jspConfigPageEnc;
+	}
+
+	public void setPageEncoding(String enc) {
+	    pageEnc = enc;
+	}
+
+	public String getPageEncoding() {
+	    return pageEnc;
+	}
+
+	public void setIsDefaultPageEncoding(boolean isDefault) {
+	    isDefaultPageEncoding = isDefault;
+	}
+
+	public boolean isDefaultPageEncoding() {
+	    return isDefaultPageEncoding;
+	}
+	
+	public void setIsEncodingSpecifiedInProlog(boolean isSpecified) {
+	    isEncodingSpecifiedInProlog = isSpecified;
+	}
+
+	public boolean isEncodingSpecifiedInProlog() {
+	    return isEncodingSpecifiedInProlog;
+	}
+
+	/**
+	 * @return The enclosing root to this Root. Usually represents the
+	 * page that includes this one.
+	 */
+	public Root getParentRoot() {
+	    return parentRoot;
+	}
+    }
+    
+    /**
+     * Represents the root of a Jsp document (XML syntax)
+     */
+    public static class JspRoot extends Node {
+
+	public JspRoot(String qName, Attributes attrs,
+		       Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs,
+		       Mark start, Node parent) {
+	    super(qName, ROOT_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a page directive
+     */
+    public static class PageDirective extends Node {
+
+	private Vector imports;
+
+	public PageDirective(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_PAGE_DIRECTIVE_ACTION, attrs, null, null, start, parent);
+	}
+
+	public PageDirective(String qName, Attributes attrs,
+			     Attributes nonTaglibXmlnsAttrs,
+			     Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, PAGE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	    imports = new Vector();
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	/**
+	 * Parses the comma-separated list of class or package names in the
+	 * given attribute value and adds each component to this
+	 * PageDirective's vector of imported classes and packages.
+	 * @param value A comma-separated string of imports.
+	 */
+	public void addImport(String value) {
+	    int start = 0;
+	    int index;
+	    while ((index = value.indexOf(',', start)) != -1) {
+		imports.add(value.substring(start, index).trim());
+		start = index + 1;
+	    }
+	    if (start == 0) {
+		// No comma found
+		imports.add(value.trim());
+	    } else {
+		imports.add(value.substring(start).trim());
+	    }
+	}
+
+	public List getImports() {
+	    return imports;
+	}
+    }
+
+    /**
+     * Represents an include directive
+     */
+    public static class IncludeDirective extends Node {
+
+	public IncludeDirective(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_INCLUDE_DIRECTIVE_ACTION, attrs, null, null, start,
+		 parent);
+	}
+
+	public IncludeDirective(String qName, Attributes attrs,
+				Attributes nonTaglibXmlnsAttrs,
+				Attributes taglibAttrs, Mark start,
+				Node parent) {
+	    super(qName, INCLUDE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a custom taglib directive
+     */
+    public static class TaglibDirective extends Node {
+
+	public TaglibDirective(Attributes attrs, Mark start, Node parent) {
+	    super(JSP_TAGLIB_DIRECTIVE_ACTION, TAGLIB_DIRECTIVE_ACTION, attrs,
+		  start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a tag directive
+     */
+    public static class TagDirective extends Node {
+        private Vector imports;
+
+	public TagDirective(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_TAG_DIRECTIVE_ACTION, attrs, null, null, start, parent);
+	}
+
+	public TagDirective(String qName, Attributes attrs,
+			    Attributes nonTaglibXmlnsAttrs,
+			    Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, TAG_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+            imports = new Vector();
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+        }
+ 
+        /**
+         * Parses the comma-separated list of class or package names in the
+         * given attribute value and adds each component to this
+         * PageDirective's vector of imported classes and packages.
+         * @param value A comma-separated string of imports.
+         */
+        public void addImport(String value) {
+            int start = 0;
+            int index;
+            while ((index = value.indexOf(',', start)) != -1) {
+                imports.add(value.substring(start, index).trim());
+                start = index + 1;
+            }
+            if (start == 0) {
+                // No comma found
+                imports.add(value.trim());
+            } else {
+                imports.add(value.substring(start).trim());
+            }
+        }
+ 
+        public List getImports() {
+            return imports;
+	}
+    }
+
+    /**
+     * Represents an attribute directive
+     */
+    public static class AttributeDirective extends Node {
+
+	public AttributeDirective(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_ATTRIBUTE_DIRECTIVE_ACTION, attrs, null, null, start,
+		 parent);
+	}
+
+	public AttributeDirective(String qName, Attributes attrs,
+				  Attributes nonTaglibXmlnsAttrs,
+				  Attributes taglibAttrs, Mark start,
+				  Node parent) {
+	    super(qName, ATTRIBUTE_DIRECTIVE_ACTION, attrs,
+		  nonTaglibXmlnsAttrs, taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a variable directive
+     */
+    public static class VariableDirective extends Node {
+
+	public VariableDirective(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_VARIABLE_DIRECTIVE_ACTION, attrs, null, null, start,
+		 parent);
+	}
+
+	public VariableDirective(String qName, Attributes attrs,
+				 Attributes nonTaglibXmlnsAttrs,
+				 Attributes taglibAttrs,
+				 Mark start, Node parent) {
+	    super(qName, VARIABLE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a <jsp:invoke> tag file action
+     */
+    public static class InvokeAction extends Node {
+
+	public InvokeAction(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_INVOKE_ACTION, attrs, null, null, start, parent);
+	}
+
+	public InvokeAction(String qName, Attributes attrs,
+			    Attributes nonTaglibXmlnsAttrs,
+			    Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, INVOKE_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a <jsp:doBody> tag file action
+     */
+    public static class DoBodyAction extends Node {
+
+	public DoBodyAction(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_DOBODY_ACTION, attrs, null, null, start, parent);
+	}
+
+	public DoBodyAction(String qName, Attributes attrs,
+			    Attributes nonTaglibXmlnsAttrs,
+			    Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, DOBODY_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a Jsp comment
+     * Comments are kept for completeness.
+     */
+    public static class Comment extends Node {
+
+	public Comment(String text, Mark start, Node parent) {
+	    super(null, null, text, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents an expression, declaration, or scriptlet
+     */
+    public static abstract class ScriptingElement extends Node {
+
+	public ScriptingElement(String qName, String localName, String text,
+				Mark start, Node parent) {
+	    super(qName, localName, text, start, parent);
+	}
+
+	public ScriptingElement(String qName, String localName,
+				Attributes nonTaglibXmlnsAttrs,
+				Attributes taglibAttrs, Mark start,
+				Node parent) {
+	    super(qName, localName, null, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+	}
+
+	/**
+	 * When this node was created from a JSP page in JSP syntax, its text
+	 * was stored as a String in the "text" field, whereas when this node
+	 * was created from a JSP document, its text was stored as one or more
+	 * TemplateText nodes in its body. This method handles either case.
+	 * @return The text string
+	 */
+	public String getText() {
+	    String ret = text;
+	    if ((ret == null) && (body != null)) {
+		StringBuffer buf = new StringBuffer();
+		for (int i=0; i<body.size(); i++) {
+		    buf.append(body.getNode(i).getText());
+		}
+		ret = buf.toString();
+	    }
+	    return ret;
+	}
+
+        /**
+         * For the same reason as above, the source line information in the
+         * contained TemplateText node should be used.
+         */
+        public Mark getStart() {
+            if (text == null && body != null && body.size() > 0) {
+                return body.getNode(0).getStart();
+            } else {
+                return super.getStart();
+            }
+        }
+    }
+
+    /**
+     * Represents a declaration
+     */
+    public static class Declaration extends ScriptingElement {
+
+	public Declaration(String text, Mark start, Node parent) {
+	    super(JSP_DECLARATION_ACTION, DECLARATION_ACTION, text, start,
+		  parent);
+	}
+
+	public Declaration(String qName, Attributes nonTaglibXmlnsAttrs,
+			   Attributes taglibAttrs, Mark start,
+			   Node parent) {
+	    super(qName, DECLARATION_ACTION, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents an expression.  Expressions in attributes are embedded
+     * in the attribute string and not here.
+     */
+    public static class Expression extends ScriptingElement {
+
+	public Expression(String text, Mark start, Node parent) {
+	    super(JSP_EXPRESSION_ACTION, EXPRESSION_ACTION, text, start,
+		  parent);
+	}
+
+	public Expression(String qName, Attributes nonTaglibXmlnsAttrs,
+			  Attributes taglibAttrs, Mark start,
+			  Node parent) {
+	    super(qName, EXPRESSION_ACTION, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a scriptlet
+     */
+    public static class Scriptlet extends ScriptingElement {
+
+	public Scriptlet(String text, Mark start, Node parent) {
+	    super(JSP_SCRIPTLET_ACTION, SCRIPTLET_ACTION, text, start, parent);
+	}
+
+	public Scriptlet(String qName, Attributes nonTaglibXmlnsAttrs,
+			 Attributes taglibAttrs, Mark start,
+			 Node parent) {
+	    super(qName, SCRIPTLET_ACTION, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents an EL expression.  Expressions in attributes are embedded
+     * in the attribute string and not here.
+     */
+    public static class ELExpression extends Node {
+
+	private ELNode.Nodes el;
+
+        public ELExpression(String text, Mark start, Node parent) {
+            super(null, null, text, start, parent);
+        }
+
+        public void accept(Visitor v) throws JasperException {
+            v.visit(this);
+        }
+
+	public void setEL(ELNode.Nodes el) {
+	    this.el = el;
+	}
+
+	public ELNode.Nodes getEL() {
+	    return el;
+	}
+    }
+
+    /**
+     * Represents a param action
+     */
+    public static class ParamAction extends Node {
+
+	JspAttribute value;
+
+	public ParamAction(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_PARAM_ACTION, attrs, null, null, start, parent);
+	}
+
+	public ParamAction(String qName, Attributes attrs,
+			   Attributes nonTaglibXmlnsAttrs,
+			   Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, PARAM_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public void setValue(JspAttribute value) {
+	    this.value = value;
+	}
+
+	public JspAttribute getValue() {
+	    return value;
+	}
+    }
+
+    /**
+     * Represents a params action
+     */
+    public static class ParamsAction extends Node {
+
+	public ParamsAction(Mark start, Node parent) {
+	    this(JSP_PARAMS_ACTION, null, null, start, parent);
+	}
+
+	public ParamsAction(String qName,
+			    Attributes nonTaglibXmlnsAttrs,
+			    Attributes taglibAttrs,
+			    Mark start, Node parent) {
+	    super(qName, PARAMS_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a fallback action
+     */
+    public static class FallBackAction extends Node {
+
+	public FallBackAction(Mark start, Node parent) {
+	    this(JSP_FALLBACK_ACTION, null, null, start, parent);
+	}
+
+	public FallBackAction(String qName,
+			      Attributes nonTaglibXmlnsAttrs,
+			      Attributes taglibAttrs, Mark start,
+			      Node parent) {
+	    super(qName, FALLBACK_ACTION, null, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents an include action
+     */
+    public static class IncludeAction extends Node {
+
+	private JspAttribute page;
+
+	public IncludeAction(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_INCLUDE_ACTION, attrs, null, null, start, parent);
+	}
+
+	public IncludeAction(String qName, Attributes attrs,
+			     Attributes nonTaglibXmlnsAttrs,
+			     Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, INCLUDE_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public void setPage(JspAttribute page) {
+	    this.page = page;
+	}
+
+	public JspAttribute getPage() {
+	    return page;
+	}
+    }
+
+    /**
+     * Represents a forward action
+     */
+    public static class ForwardAction extends Node {
+
+	private JspAttribute page;
+
+	public ForwardAction(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_FORWARD_ACTION, attrs, null, null, start, parent);
+	}
+
+	public ForwardAction(String qName, Attributes attrs,
+			     Attributes nonTaglibXmlnsAttrs,
+			     Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, FORWARD_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public void setPage(JspAttribute page) {
+	    this.page = page;
+	}
+
+	public JspAttribute getPage() {
+	    return page;
+	}
+    }
+
+    /**
+     * Represents a getProperty action
+     */
+    public static class GetProperty extends Node {
+
+	public GetProperty(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_GET_PROPERTY_ACTION, attrs, null, null, start, parent);
+	}
+
+	public GetProperty(String qName, Attributes attrs,
+			   Attributes nonTaglibXmlnsAttrs,
+			   Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, GET_PROPERTY_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start,  parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a setProperty action
+     */
+    public static class SetProperty extends Node {
+
+	private JspAttribute value;
+
+	public SetProperty(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_SET_PROPERTY_ACTION, attrs, null, null, start, parent);
+	}
+
+	public SetProperty(String qName, Attributes attrs,
+			   Attributes nonTaglibXmlnsAttrs,
+			   Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, SET_PROPERTY_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public void setValue(JspAttribute value) {
+	    this.value = value;
+	}
+
+	public JspAttribute getValue() {
+	    return value;
+	}
+    }
+
+    /**
+     * Represents a useBean action
+     */
+    public static class UseBean extends Node {
+
+	JspAttribute beanName;
+
+	public UseBean(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_USE_BEAN_ACTION, attrs, null, null, start, parent);
+	}
+
+	public UseBean(String qName, Attributes attrs,
+		       Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs,
+		       Mark start, Node parent) {
+	    super(qName, USE_BEAN_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public void setBeanName(JspAttribute beanName) {
+	    this.beanName = beanName;
+	}
+
+	public JspAttribute getBeanName() {
+	    return beanName;
+	}
+    }
+
+    /**
+     * Represents a plugin action
+     */
+    public static class PlugIn extends Node {
+
+        private JspAttribute width;
+        private JspAttribute height;
+        
+	public PlugIn(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_PLUGIN_ACTION, attrs, null, null, start, parent);
+	}
+
+	public PlugIn(String qName, Attributes attrs,
+		      Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs,
+		      Mark start, Node parent) {
+	    super(qName, PLUGIN_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public void setHeight(JspAttribute height) {
+	    this.height = height;
+	}
+
+	public void setWidth(JspAttribute width) {
+	    this.width = width;
+	}
+
+	public JspAttribute getHeight() {
+	    return height;
+	}
+
+	public JspAttribute getWidth() {
+	    return width;
+	}
+    }
+
+    /**
+     * Represents an uninterpreted tag, from a Jsp document
+     */
+    public static class UninterpretedTag extends Node {
+
+	private JspAttribute[] jspAttrs;
+
+	public UninterpretedTag(String qName, String localName,
+				Attributes attrs,
+				Attributes nonTaglibXmlnsAttrs,
+				Attributes taglibAttrs,
+				Mark start, Node parent) {
+	    super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public void setJspAttributes(JspAttribute[] jspAttrs) {
+	    this.jspAttrs = jspAttrs;
+	}
+
+	public JspAttribute[] getJspAttributes() {
+	    return jspAttrs;
+	}
+    }
+    
+    /**
+     * Represents a <jsp:element>.
+     */
+    public static class JspElement extends Node {
+
+	private JspAttribute[] jspAttrs;
+	private JspAttribute nameAttr;
+
+	public JspElement(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_ELEMENT_ACTION, attrs, null, null, start, parent);
+	}
+
+	public JspElement(String qName, Attributes attrs,
+			  Attributes nonTaglibXmlnsAttrs,
+			  Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, ELEMENT_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public void setJspAttributes(JspAttribute[] jspAttrs) {
+	    this.jspAttrs = jspAttrs;
+	}
+
+	public JspAttribute[] getJspAttributes() {
+	    return jspAttrs;
+	}
+
+	/*
+	 * Sets the XML-style 'name' attribute
+	 */
+	public void setNameAttribute(JspAttribute nameAttr) {
+	    this.nameAttr = nameAttr;
+	}
+
+	/*
+	 * Gets the XML-style 'name' attribute
+	 */
+	public JspAttribute getNameAttribute() {
+	    return this.nameAttr;
+	}
+    }
+
+    /**
+     * Represents a <jsp:output>.
+     */
+    public static class JspOutput extends Node {
+
+	public JspOutput(String qName, Attributes attrs,
+			 Attributes nonTaglibXmlnsAttrs,
+			 Attributes taglibAttrs,
+			 Mark start, Node parent) {
+	    super(qName, OUTPUT_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Collected information about child elements.  Used by nodes like
+     * CustomTag, JspBody, and NamedAttribute.  The information is 
+     * set in the Collector.
+     */
+    public static class ChildInfo {
+	private boolean scriptless;	// true if the tag and its body
+					// contain no scripting elements.
+	private boolean hasUseBean;
+	private boolean hasIncludeAction;
+	private boolean hasParamAction;
+	private boolean hasSetProperty;
+	private boolean hasScriptingVars;
+
+	public void setScriptless(boolean s) {
+	    scriptless = s;
+	}
+
+	public boolean isScriptless() {
+	    return scriptless;
+	}
+
+	public void setHasUseBean(boolean u) {
+	    hasUseBean = u;
+	}
+
+	public boolean hasUseBean() {
+	    return hasUseBean;
+	}
+
+	public void setHasIncludeAction(boolean i) {
+	    hasIncludeAction = i;
+	}
+
+	public boolean hasIncludeAction() {
+	    return hasIncludeAction;
+	}
+
+	public void setHasParamAction(boolean i) {
+	    hasParamAction = i;
+	}
+
+	public boolean hasParamAction() {
+	    return hasParamAction;
+	}
+
+	public void setHasSetProperty(boolean s) {
+	    hasSetProperty = s;
+	}
+
+	public boolean hasSetProperty() {
+	    return hasSetProperty;
+	}
+        
+	public void setHasScriptingVars(boolean s) {
+	    hasScriptingVars = s;
+	}
+
+	public boolean hasScriptingVars() {
+	    return hasScriptingVars;
+	}
+    }
+
+    /**
+     * Represents a custom tag
+     */
+    public static class CustomTag extends Node {
+
+	private String uri;
+	private String prefix;
+	private JspAttribute[] jspAttrs;
+	private TagData tagData;
+	private String tagHandlerPoolName;
+	private TagInfo tagInfo;
+	private TagFileInfo tagFileInfo;
+	private Class tagHandlerClass;
+	private VariableInfo[] varInfos;
+	private int customNestingLevel;
+        private ChildInfo childInfo;
+	private boolean implementsIterationTag;
+	private boolean implementsBodyTag;
+	private boolean implementsTryCatchFinally;
+	private boolean implementsSimpleTag;
+	private boolean implementsDynamicAttributes;
+	private Vector atBeginScriptingVars;
+	private Vector atEndScriptingVars;
+	private Vector nestedScriptingVars;
+	private Node.CustomTag customTagParent;
+	private Integer numCount;
+	private boolean useTagPlugin;
+	private TagPluginContext tagPluginContext;
+
+	/**
+	 * The following two fields are used for holding the Java
+	 * scriptlets that the tag plugins may generate.  Meaningful
+	 * only if useTagPlugin is true;
+	 * Could move them into TagPluginContextImpl, but we'll need
+	 * to cast tagPluginContext to TagPluginContextImpl all the time...
+	 */
+	private Nodes atSTag;
+	private Nodes atETag;
+
+	/*
+	 * Constructor for custom action implemented by tag handler.
+	 */
+	public CustomTag(String qName, String prefix, String localName,
+			 String uri, Attributes attrs, Mark start, Node parent,
+			 TagInfo tagInfo, Class tagHandlerClass) {
+	    this(qName, prefix, localName, uri, attrs, null, null, start,
+		 parent, tagInfo, tagHandlerClass);
+	}
+
+	/*
+	 * Constructor for custom action implemented by tag handler.
+	 */
+	public CustomTag(String qName, String prefix, String localName,
+			 String uri, Attributes attrs,
+			 Attributes nonTaglibXmlnsAttrs,
+			 Attributes taglibAttrs,
+			 Mark start, Node parent, TagInfo tagInfo,
+			 Class tagHandlerClass) {
+	    super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+
+	    this.uri = uri;
+	    this.prefix = prefix;
+	    this.tagInfo = tagInfo;
+	    this.tagHandlerClass = tagHandlerClass;
+	    this.customNestingLevel = makeCustomNestingLevel();
+            this.childInfo = new ChildInfo();
+
+	    this.implementsIterationTag = 
+		IterationTag.class.isAssignableFrom(tagHandlerClass);
+	    this.implementsBodyTag =
+		BodyTag.class.isAssignableFrom(tagHandlerClass);
+	    this.implementsTryCatchFinally = 
+		TryCatchFinally.class.isAssignableFrom(tagHandlerClass);
+	    this.implementsSimpleTag = 
+		SimpleTag.class.isAssignableFrom(tagHandlerClass);
+	    this.implementsDynamicAttributes = 
+		DynamicAttributes.class.isAssignableFrom(tagHandlerClass);
+	}
+
+	/*
+	 * Constructor for custom action implemented by tag file.
+	 */
+	public CustomTag(String qName, String prefix, String localName,
+			 String uri, Attributes attrs, Mark start, Node parent,
+			 TagFileInfo tagFileInfo) {
+	    this(qName, prefix, localName, uri, attrs, null, null, start,
+		 parent, tagFileInfo);
+	}
+
+	/*
+	 * Constructor for custom action implemented by tag file.
+	 */
+	public CustomTag(String qName, String prefix, String localName,
+			 String uri, Attributes attrs,
+			 Attributes nonTaglibXmlnsAttrs,
+			 Attributes taglibAttrs,
+			 Mark start, Node parent, TagFileInfo tagFileInfo) {
+
+	    super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+
+	    this.uri = uri;
+	    this.prefix = prefix;
+	    this.tagFileInfo = tagFileInfo;
+	    this.tagInfo = tagFileInfo.getTagInfo();
+	    this.customNestingLevel = makeCustomNestingLevel();
+            this.childInfo = new ChildInfo();
+
+	    this.implementsIterationTag = false;
+	    this.implementsBodyTag = false;
+	    this.implementsTryCatchFinally = false;
+	    this.implementsSimpleTag = true;
+	    this.implementsDynamicAttributes = tagInfo.hasDynamicAttributes();
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	/**
+	 * @return The URI namespace that this custom action belongs to
+	 */
+	public String getURI() {
+	    return this.uri;
+	}
+
+	/**
+	 * @return The tag prefix
+	 */
+	public String getPrefix() {
+	    return prefix;
+	}
+
+	public void setJspAttributes(JspAttribute[] jspAttrs) {
+	    this.jspAttrs = jspAttrs;
+	}
+
+	public JspAttribute[] getJspAttributes() {
+	    return jspAttrs;
+	}
+        
+        public ChildInfo getChildInfo() {
+            return childInfo;
+        }
+	
+	public void setTagData(TagData tagData) {
+	    this.tagData = tagData;
+	    this.varInfos = tagInfo.getVariableInfo(tagData);
+	    if (this.varInfos == null) {
+		this.varInfos = ZERO_VARIABLE_INFO;
+	    }
+	}
+
+	public TagData getTagData() {
+	    return tagData;
+	}
+
+	public void setTagHandlerPoolName(String s) {
+	    tagHandlerPoolName = s;
+	}
+
+	public String getTagHandlerPoolName() {
+	    return tagHandlerPoolName;
+	}
+
+	public TagInfo getTagInfo() {
+	    return tagInfo;
+	}
+
+	public TagFileInfo getTagFileInfo() {
+	    return tagFileInfo;
+	}
+
+	/*
+	 * @return true if this custom action is supported by a tag file,
+	 * false otherwise
+	 */
+	public boolean isTagFile() {
+	    return tagFileInfo != null;
+	}
+
+	public Class getTagHandlerClass() {
+	    return tagHandlerClass;
+	}
+
+	public void setTagHandlerClass(Class hc) {
+	    tagHandlerClass = hc;
+	}
+
+	public boolean implementsIterationTag() {
+	    return implementsIterationTag;
+	}
+
+	public boolean implementsBodyTag() {
+	    return implementsBodyTag;
+	}
+
+	public boolean implementsTryCatchFinally() {
+	    return implementsTryCatchFinally;
+	}
+
+	public boolean implementsSimpleTag() {
+	    return implementsSimpleTag;
+	}
+
+	public boolean implementsDynamicAttributes() {
+	    return implementsDynamicAttributes;
+	}
+
+	public TagVariableInfo[] getTagVariableInfos() {
+	    return tagInfo.getTagVariableInfos();
+ 	}
+ 
+	public VariableInfo[] getVariableInfos() {
+	    return varInfos;
+	}
+
+	public void setCustomTagParent(Node.CustomTag n) {
+	    this.customTagParent = n;
+	}
+
+	public Node.CustomTag getCustomTagParent() {
+	    return this.customTagParent;
+	}
+
+	public void setNumCount(Integer count) {
+	    this.numCount = count;
+	}
+
+	public Integer getNumCount() {
+	    return this.numCount;
+	}
+
+	public void setScriptingVars(Vector vec, int scope) {
+	    switch (scope) {
+	    case VariableInfo.AT_BEGIN:
+		this.atBeginScriptingVars = vec;
+		break;
+	    case VariableInfo.AT_END:
+		this.atEndScriptingVars = vec;
+		break;
+	    case VariableInfo.NESTED:
+		this.nestedScriptingVars = vec;
+		break;
+	    }
+	}
+
+	/*
+	 * Gets the scripting variables for the given scope that need to be
+	 * declared.
+	 */
+	public Vector getScriptingVars(int scope) {
+	    Vector vec = null;
+
+	    switch (scope) {
+	    case VariableInfo.AT_BEGIN:
+		vec = this.atBeginScriptingVars;
+		break;
+	    case VariableInfo.AT_END:
+		vec = this.atEndScriptingVars;
+		break;
+	    case VariableInfo.NESTED:
+		vec = this.nestedScriptingVars;
+		break;
+	    }
+
+	    return vec;
+	}
+
+	/*
+	 * Gets this custom tag's custom nesting level, which is given as
+	 * the number of times this custom tag is nested inside itself.
+	 */
+	public int getCustomNestingLevel() {
+	    return customNestingLevel;
+	}
+
+        /**
+         * Checks to see if the attribute of the given name is of type
+	 * JspFragment.
+         */
+        public boolean checkIfAttributeIsJspFragment( String name ) {
+            boolean result = false;
+
+	    TagAttributeInfo[] attributes = tagInfo.getAttributes();
+	    for (int i = 0; i < attributes.length; i++) {
+		if (attributes[i].getName().equals(name) &&
+		            attributes[i].isFragment()) {
+		    result = true;
+		    break;
+		}
+	    }
+            
+            return result;
+        }
+
+	public void setUseTagPlugin(boolean use) {
+	    useTagPlugin = use;
+	}
+
+	public boolean useTagPlugin() {
+	    return useTagPlugin;
+	}
+
+	public void setTagPluginContext(TagPluginContext tagPluginContext) {
+	    this.tagPluginContext = tagPluginContext;
+	}
+
+	public TagPluginContext getTagPluginContext() {
+	    return tagPluginContext;
+	}
+
+	public void setAtSTag(Nodes sTag) {
+	    atSTag = sTag;
+	}
+
+	public Nodes getAtSTag() {
+	    return atSTag;
+	}
+        
+	public void setAtETag(Nodes eTag) {
+	    atETag = eTag;
+	}
+
+	public Nodes getAtETag() {
+	    return atETag;
+	}
+        
+	/*
+	 * Computes this custom tag's custom nesting level, which corresponds
+	 * to the number of times this custom tag is nested inside itself.
+	 *
+	 * Example:
+	 * 
+	 *  <g:h>
+	 *    <a:b> -- nesting level 0
+	 *      <c:d>
+	 *        <e:f>
+	 *          <a:b> -- nesting level 1
+	 *            <a:b> -- nesting level 2
+	 *            </a:b>
+	 *          </a:b>
+	 *          <a:b> -- nesting level 1
+	 *          </a:b>
+	 *        </e:f>
+	 *      </c:d>
+	 *    </a:b>
+	 *  </g:h>
+	 * 
+	 * @return Custom tag's nesting level
+	 */
+	private int makeCustomNestingLevel() {
+	    int n = 0;
+	    Node p = parent;
+	    while (p != null) {
+		if ((p instanceof Node.CustomTag)
+		        && qName.equals(((Node.CustomTag) p).qName)) {
+		    n++;
+		}
+		p = p.parent;
+	    }
+	    return n;
+	}
+
+	/**
+	 * Returns true if this custom action has an empty body, and false
+	 * otherwise.
+	 *
+	 * A custom action is considered to have an empty body if the 
+	 * following holds true:
+	 * - getBody() returns null, or
+	 * - all immediate children are jsp:attribute actions, or
+	 * - the action's jsp:body is empty.
+	 */
+	 public boolean hasEmptyBody() {
+	     boolean hasEmptyBody = true;
+	     Nodes nodes = getBody();
+	     if (nodes != null) {
+		 int numChildNodes = nodes.size();
+		 for (int i=0; i<numChildNodes; i++) {
+		     Node n = nodes.getNode(i);
+		     if (!(n instanceof NamedAttribute)) {
+			 if (n instanceof JspBody) {
+			     hasEmptyBody = (n.getBody() == null);
+			 } else {
+			     hasEmptyBody = false;
+			 }
+			 break;
+		     }
+		 }
+	     }
+
+	     return hasEmptyBody;
+	 }
+    }
+
+    /**
+     * Used as a placeholder for the evaluation code of a custom action
+     * attribute (used by the tag plugin machinery only).
+     */
+    public static class AttributeGenerator extends Node {
+	String name;	// name of the attribute
+	CustomTag tag;	// The tag this attribute belongs to
+
+	public AttributeGenerator(Mark start, String name, CustomTag tag) {
+	    super(start, null);
+	    this.name = name;
+	    this.tag = tag;
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+	public String getName() {
+	    return name;
+	}
+
+	public CustomTag getTag() {
+	    return tag;
+	}
+    }
+
+    /**
+     * Represents the body of a &lt;jsp:text&gt; element
+     */
+    public static class JspText extends Node {
+
+	public JspText(String qName, Attributes nonTaglibXmlnsAttrs,
+		       Attributes taglibAttrs, Mark start, Node parent) {
+	    super(qName, TEXT_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+    }
+
+    /**
+     * Represents a Named Attribute (&lt;jsp:attribute&gt;)
+     */
+    public static class NamedAttribute extends Node {
+
+        // A unique temporary variable name suitable for code generation
+        private String temporaryVariableName;
+
+        // True if this node is to be trimmed, or false otherwise
+        private boolean trim = true;
+        
+        private ChildInfo childInfo;
+	private String name;
+	private String localName;
+	private String prefix;
+
+        public NamedAttribute(Attributes attrs, Mark start, Node parent) {
+	    this(JSP_ATTRIBUTE_ACTION, attrs, null, null, start, parent);
+	}
+
+        public NamedAttribute(String qName, Attributes attrs,
+			      Attributes nonTaglibXmlnsAttrs,
+			      Attributes taglibAttrs,
+			      Mark start, Node parent) {
+
+            super(qName, ATTRIBUTE_ACTION, attrs, nonTaglibXmlnsAttrs,
+		  taglibAttrs, start, parent);
+            temporaryVariableName = JspUtil.nextTemporaryVariableName();
+            if( "false".equals( this.getAttributeValue( "trim" ) ) ) {
+                // (if null or true, leave default of true)
+                trim = false;
+            }
+            childInfo = new ChildInfo();
+	    name = this.getAttributeValue("name");
+            if (name != null) {
+                // Mandatary attribute "name" will be checked in Validator
+	        localName = name;
+	        int index = name.indexOf(':');
+	        if (index != -1) {
+		    prefix = name.substring(0, index);
+		    localName = name.substring(index+1);
+                }
+	    }
+        }
+
+        public void accept(Visitor v) throws JasperException {
+            v.visit(this);
+        }
+
+        public String getName() {
+            return this.name;
+        }
+
+        public String getLocalName() {
+            return this.localName;
+        }
+
+        public String getPrefix() {
+            return this.prefix;
+        }
+        
+        public ChildInfo getChildInfo() {
+            return this.childInfo;
+        }
+
+        public boolean isTrim() {
+            return trim;
+        }
+
+        /**
+         * @return A unique temporary variable name to store the result in.
+         *      (this probably could go elsewhere, but it's convenient here)
+         */
+        public String getTemporaryVariableName() {
+            return temporaryVariableName;
+        }
+
+	/*
+	 * Get the attribute value from this named attribute (<jsp:attribute>).
+	 * Since this method is only for attributes that are not rtexpr,
+	 * we can assume the body of the jsp:attribute is a template text.
+	 */
+	public String getText() {
+
+	    class AttributeVisitor extends Visitor {
+		String attrValue = null;
+		public void visit(TemplateText txt) {
+		    attrValue = new String(txt.getText());
+		}
+		
+		public String getAttrValue() {
+		    return attrValue;
+		}
+	    }
+
+	    // According to JSP 2.0, if the body of the <jsp:attribute>
+	    // action is empty, it is equivalent of specifying "" as the value
+	    // of the attribute.
+	    String text = "";
+	    if (getBody() != null) {
+		AttributeVisitor attributeVisitor = new AttributeVisitor();
+		try {
+		    getBody().visit(attributeVisitor);
+		} catch (JasperException e) {
+		}
+		text = attributeVisitor.getAttrValue();
+	    }
+	    
+	    return text;
+	}
+    }
+
+    /**
+     * Represents a JspBody node (&lt;jsp:body&gt;)
+     */
+    public static class JspBody extends Node {
+
+        private ChildInfo childInfo;
+
+        public JspBody(Mark start, Node parent) {
+            this(JSP_BODY_ACTION, null, null, start, parent);
+        }
+
+        public JspBody(String qName, Attributes nonTaglibXmlnsAttrs,
+		       Attributes taglibAttrs, Mark start, Node parent) {
+            super(qName, BODY_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs,
+		  start, parent);
+            this.childInfo = new ChildInfo();
+        }
+
+        public void accept(Visitor v) throws JasperException {
+            v.visit(this);
+        }
+
+        public ChildInfo getChildInfo() {
+            return childInfo;
+        }
+    }
+
+    /**
+     * Represents a template text string
+     */
+    public static class TemplateText extends Node {
+
+        private ArrayList extraSmap = null;
+
+	public TemplateText(String text, Mark start, Node parent) {
+	    super(null, null, text, start, parent);
+	}
+
+	public void accept(Visitor v) throws JasperException {
+	    v.visit(this);
+	}
+
+        /**
+         * Trim all whitespace from the left of the template text
+         */
+        public void ltrim() {
+	    int index = 0;
+            while ((index < text.length()) && (text.charAt(index) <= ' ')) {
+		index++;
+            }
+            text = text.substring(index);
+        }
+
+        public void setText(String text) {
+            this.text = text;
+        }
+
+        /**
+         * Trim all whitespace from the right of the template text
+         */
+        public void rtrim() {
+            int index = text.length();
+            while( (index > 0) && (text.charAt(index-1) <= ' ') ) {
+                index--;
+            }
+            text = text.substring(0, index);
+        }
+
+	/**
+	 * Returns true if this template text contains whitespace only.
+	 */
+	public boolean isAllSpace() {
+	    boolean isAllSpace = true;
+	    for (int i=0; i<text.length(); i++) {
+		if (!Character.isWhitespace(text.charAt(i))) {
+		    isAllSpace = false;
+		    break;
+		}
+	    }
+	    return isAllSpace;
+	}
+
+        /**
+         * Add a source to Java line mapping
+         * @param srcLine The postion of the source line, relative to the line
+         *        at the start of this node.  The corresponding java line is
+         *        assumed to be consecutive, i.e. one more than the last.
+         */
+        public void addSmap(int srcLine) {
+            if (extraSmap == null) {
+                extraSmap = new ArrayList();
+            }
+            extraSmap.add(new Integer(srcLine));
+        }
+
+        public ArrayList getExtraSmap() {
+            return extraSmap;
+        }
+    }
+
+    /*********************************************************************
+     * Auxillary classes used in Node
+     */
+
+    /**
+     * Represents attributes that can be request time expressions.
+     *
+     * Can either be a plain attribute, an attribute that represents a
+     * request time expression value, or a named attribute (specified using
+     * the jsp:attribute standard action).
+     */
+
+    public static class JspAttribute {
+
+	private String qName;
+	private String uri;
+	private String localName;
+	private String value;
+	private boolean expression;
+	private boolean dynamic;
+        private ELNode.Nodes el;
+
+        // If true, this JspAttribute represents a <jsp:attribute>
+        private boolean namedAttribute;
+        // The node in the parse tree for the NamedAttribute
+        private NamedAttribute namedAttributeNode;
+
+        JspAttribute(String qName, String uri, String localName, String value,
+		     boolean expr, ELNode.Nodes el, boolean dyn ) {
+	    this.qName = qName;
+	    this.uri = uri;
+	    this.localName = localName;
+	    this.value = value;
+            this.namedAttributeNode = null;
+	    this.expression = expr;
+            this.el = el;
+	    this.dynamic = dyn;
+            this.namedAttribute = false;
+	}
+
+        /**
+         * Use this constructor if the JspAttribute represents a
+         * named attribute.  In this case, we have to store the nodes of
+         * the body of the attribute.
+         */
+        JspAttribute(NamedAttribute na, boolean dyn) {
+            this.qName = na.getName();
+	    this.localName = na.getLocalName();
+            this.value = null;
+            this.namedAttributeNode = na;
+            this.expression = false;
+            this.el = null;
+	    this.dynamic = dyn;
+            this.namedAttribute = true;
+        }
+
+	/**
+ 	 * @return The name of the attribute
+	 */
+	public String getName() {
+	    return qName;
+	}
+
+	/**
+ 	 * @return The local name of the attribute
+	 */
+	public String getLocalName() {
+	    return localName;
+	}
+
+	/**
+ 	 * @return The namespace of the attribute, or null if in the default
+	 * namespace
+	 */
+	public String getURI() {
+	    return uri;
+	}
+
+	/**
+         * Only makes sense if namedAttribute is false.
+         *
+         * @return the value for the attribute, or the expression string
+         *         (stripped of "<%=", "%>", "%=", or "%"
+         *          but containing "${" and "}" for EL expressions)
+	 */
+	public String getValue() {
+	    return value;
+	}
+
+        /**
+         * Only makes sense if namedAttribute is true.
+         *
+         * @return the nodes that evaluate to the body of this attribute.
+         */
+        public NamedAttribute getNamedAttributeNode() {
+            return namedAttributeNode;
+        }
+
+	/**
+         * @return true if the value represents a traditional rtexprvalue
+	 */
+	public boolean isExpression() {
+	    return expression;
+	}
+
+        /**
+         * @return true if the value represents a NamedAttribute value.
+         */
+        public boolean isNamedAttribute() {
+            return namedAttribute;
+        }
+
+        /**
+         * @return true if the value represents an expression that should
+         * be fed to the expression interpreter
+         * @return false for string literals or rtexprvalues that should
+         * not be interpreted or reevaluated
+         */
+        public boolean isELInterpreterInput() {
+            return el != null;
+        }
+
+	/**
+	 * @return true if the value is a string literal known at translation
+	 * time.
+	 */
+	public boolean isLiteral() {
+	    return !expression && (el != null) && !namedAttribute;
+	}
+
+	/**
+	 * XXX
+	 */
+	public boolean isDynamic() {
+	    return dynamic;
+	}
+
+	public ELNode.Nodes getEL() {
+	    return el;
+	}
+    }
+
+    /**
+     * An ordered list of Node, used to represent the body of an element, or
+     * a jsp page of jsp document.
+     */
+    public static class Nodes {
+
+	private List list;
+	private Node.Root root;		// null if this is not a page
+	private boolean generatedInBuffer;
+
+	public Nodes() {
+	    list = new Vector();
+	}
+
+	public Nodes(Node.Root root) {
+	    this.root = root;
+	    list = new Vector();
+	    list.add(root);
+	}
+
+	/**
+	 * Appends a node to the list
+	 * @param n The node to add
+	 */
+	public void add(Node n) {
+	    list.add(n);
+	    root = null;
+	}
+
+	/**
+	 * Removes the given node from the list.
+	 * @param n The node to be removed
+	 */
+	public void remove(Node n) {
+	    list.remove(n);
+	}
+
+	/**
+	 * Visit the nodes in the list with the supplied visitor
+	 * @param v The visitor used
+	 */
+	public void visit(Visitor v) throws JasperException {
+	    Iterator iter = list.iterator();
+	    while (iter.hasNext()) {
+		Node n = (Node) iter.next();
+		n.accept(v);
+	    }
+	}
+
+	public int size() {
+	    return list.size();
+	}
+
+	public Node getNode(int index) {
+	    Node n = null;
+	    try {
+		n = (Node) list.get(index);
+	    } catch (ArrayIndexOutOfBoundsException e) {
+	    }
+	    return n;
+	}
+	
+	public Node.Root getRoot() {
+	    return root;
+	}
+
+	public boolean isGeneratedInBuffer() {
+	    return generatedInBuffer;
+	}
+
+	public void setGeneratedInBuffer(boolean g) {
+	    generatedInBuffer = g;
+	}
+    }
+
+    /**
+     * A visitor class for visiting the node.  This class also provides the
+     * default action (i.e. nop) for each of the child class of the Node.
+     * An actual visitor should extend this class and supply the visit
+     * method for the nodes that it cares.
+     */
+    public static class Visitor {
+
+	/**
+	 * This method provides a place to put actions that are common to
+	 * all nodes. Override this in the child visitor class if need to.
+	 */
+	protected void doVisit(Node n) throws JasperException {
+	}
+
+	/**
+	 * Visit the body of a node, using the current visitor
+	 */
+	protected void visitBody(Node n) throws JasperException {
+	    if (n.getBody() != null) {
+		n.getBody().visit(this);
+	    }
+	}
+
+	public void visit(Root n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(JspRoot n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(PageDirective n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(TagDirective n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(IncludeDirective n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(TaglibDirective n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(AttributeDirective n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(VariableDirective n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(Comment n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(Declaration n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(Expression n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(Scriptlet n) throws JasperException {
+	    doVisit(n);
+	}
+
+        public void visit(ELExpression n) throws JasperException {
+            doVisit(n);
+        }
+
+	public void visit(IncludeAction n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(ForwardAction n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(GetProperty n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(SetProperty n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(ParamAction n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(ParamsAction n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(FallBackAction n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(UseBean n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(PlugIn n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(CustomTag n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(UninterpretedTag n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(JspElement n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+	public void visit(JspText n) throws JasperException {
+	    doVisit(n);
+	    visitBody(n);
+	}
+
+        public void visit(NamedAttribute n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(JspBody n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(InvokeAction n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(DoBodyAction n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+	public void visit(TemplateText n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(JspOutput n) throws JasperException {
+	    doVisit(n);
+	}
+
+	public void visit(AttributeGenerator n) throws JasperException {
+	    doVisit(n);
+	}
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/PageDataImpl.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/PageDataImpl.java
new file mode 100644
index 0000000..8f3f04d
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/PageDataImpl.java
@@ -0,0 +1,704 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.compiler;
+
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ListIterator;
+import javax.servlet.jsp.tagext.PageData;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.AttributesImpl;
+import org.apache.jasper.JasperException;
+
+/**
+ * An implementation of <tt>javax.servlet.jsp.tagext.PageData</tt> which
+ * builds the XML view of a given page.
+ *
+ * The XML view is built in two passes:
+ *
+ * During the first pass, the FirstPassVisitor collects the attributes of the
+ * top-level jsp:root and those of the jsp:root elements of any included
+ * pages, and adds them to the jsp:root element of the XML view.
+ * In addition, any taglib directives are converted into xmlns: attributes and
+ * added to the jsp:root element of the XML view.
+ * This pass ignores any nodes other than JspRoot and TaglibDirective.
+ *
+ * During the second pass, the SecondPassVisitor produces the XML view, using
+ * the combined jsp:root attributes determined in the first pass and any
+ * remaining pages nodes (this pass ignores any JspRoot and TaglibDirective
+ * nodes).
+ *
+ * @author Jan Luehe
+ */
+class PageDataImpl extends PageData implements TagConstants {
+
+    private static final String JSP_VERSION = "2.0";
+    private static final String CDATA_START_SECTION = "<![CDATA[\n";
+    private static final String CDATA_END_SECTION = "]]>\n";
+
+    // string buffer used to build XML view
+    private StringBuffer buf;
+
+    /**
+     * Constructor.
+     *
+     * @param page the page nodes from which to generate the XML view
+     */
+    public PageDataImpl(Node.Nodes page, Compiler compiler)
+	        throws JasperException {
+
+	// First pass
+	FirstPassVisitor firstPass = new FirstPassVisitor(page.getRoot(),
+							  compiler.getPageInfo());
+	page.visit(firstPass);
+
+	// Second pass
+	buf = new StringBuffer();
+	SecondPassVisitor secondPass
+	    = new SecondPassVisitor(page.getRoot(), buf, compiler,
+				    firstPass.getJspIdPrefix());
+	page.visit(secondPass);
+    }
+
+    /**
+     * Returns the input stream of the XML view.
+     *
+     * @return the input stream of the XML view
+     */
+    public InputStream getInputStream() {
+	// Turn StringBuffer into InputStream
+        try {
+            return new ByteArrayInputStream(buf.toString().getBytes("UTF-8"));
+        } catch (UnsupportedEncodingException uee) {
+	    // should never happen
+            throw new RuntimeException(uee.toString());
+        }
+    }
+
+    /*
+     * First-pass Visitor for JspRoot nodes (representing jsp:root elements)
+     * and TablibDirective nodes, ignoring any other nodes.
+     *
+     * The purpose of this Visitor is to collect the attributes of the
+     * top-level jsp:root and those of the jsp:root elements of any included
+     * pages, and add them to the jsp:root element of the XML view.
+     * In addition, this Visitor converts any taglib directives into xmlns:
+     * attributes and adds them to the jsp:root element of the XML view.
+     */
+    static class FirstPassVisitor
+	        extends Node.Visitor implements TagConstants {
+
+	private Node.Root root;
+	private AttributesImpl rootAttrs;
+	private PageInfo pageInfo;
+
+	// Prefix for the 'id' attribute
+	private String jspIdPrefix;
+
+	/*
+	 * Constructor
+	 */
+	public FirstPassVisitor(Node.Root root, PageInfo pageInfo) {
+	    this.root = root;
+	    this.pageInfo = pageInfo;
+	    this.rootAttrs = new AttributesImpl();
+	    this.rootAttrs.addAttribute("", "", "version", "CDATA",
+					JSP_VERSION);
+	    this.jspIdPrefix = "jsp";
+	}
+
+	public void visit(Node.Root n) throws JasperException {
+	    visitBody(n);
+	    if (n == root) {
+		/*
+		 * Top-level page.
+		 *
+		 * Add
+		 *   xmlns:jsp="http://java.sun.com/JSP/Page"
+		 * attribute only if not already present.
+		 */
+		if (!JSP_URI.equals(rootAttrs.getValue("xmlns:jsp"))) {
+		    rootAttrs.addAttribute("", "", "xmlns:jsp", "CDATA",
+					   JSP_URI);
+		}
+
+		if (pageInfo.isJspPrefixHijacked()) {
+		    /*
+		     * 'jsp' prefix has been hijacked, that is, bound to a
+		     * namespace other than the JSP namespace. This means that
+		     * when adding an 'id' attribute to each element, we can't
+		     * use the 'jsp' prefix. Therefore, create a new prefix 
+		     * (one that is unique across the translation unit) for use
+		     * by the 'id' attribute, and bind it to the JSP namespace
+		     */
+		    jspIdPrefix += "jsp";
+		    while (pageInfo.containsPrefix(jspIdPrefix)) {
+			jspIdPrefix += "jsp";
+		    }
+		    rootAttrs.addAttribute("", "", "xmlns:" + jspIdPrefix,
+					   "CDATA", JSP_URI);
+		}
+
+		root.setAttributes(rootAttrs);
+	    }
+	}
+
+	public void visit(Node.JspRoot n) throws JasperException {
+	    addAttributes(n.getTaglibAttributes());
+            addAttributes(n.getNonTaglibXmlnsAttributes());
+	    addAttributes(n.getAttributes());
+
+	    visitBody(n);
+	}
+
+	/*
+	 * Converts taglib directive into "xmlns:..." attribute of jsp:root
+	 * element.
+	 */
+	public void visit(Node.TaglibDirective n) throws JasperException {
+	    Attributes attrs = n.getAttributes();
+	    if (attrs != null) {
+		String qName = "xmlns:" + attrs.getValue("prefix");
+		/*
+		 * According to javadocs of org.xml.sax.helpers.AttributesImpl,
+		 * the addAttribute method does not check to see if the
+		 * specified attribute is already contained in the list: This
+		 * is the application's responsibility!
+		 */
+		if (rootAttrs.getIndex(qName) == -1) {
+		    String location = attrs.getValue("uri");
+		    if (location != null) {
+                        if (location.startsWith("/")) {
+                            location = URN_JSPTLD + location;
+                        }
+			rootAttrs.addAttribute("", "", qName, "CDATA",
+					       location);
+		    } else {
+			location = attrs.getValue("tagdir");
+			rootAttrs.addAttribute("", "", qName, "CDATA",
+					       URN_JSPTAGDIR + location);
+		    }
+		}
+	    }
+	}
+
+	public String getJspIdPrefix() {
+	    return jspIdPrefix;
+	}
+
+	private void addAttributes(Attributes attrs) {
+	    if (attrs != null) {
+		int len = attrs.getLength();
+		for (int i=0; i<len; i++) {
+		    if ("version".equals(attrs.getQName(i))) {
+			continue;
+		    }
+		    rootAttrs.addAttribute(attrs.getURI(i),
+					   attrs.getLocalName(i),
+					   attrs.getQName(i),
+					   attrs.getType(i),
+					   attrs.getValue(i));
+		}
+	    }
+	}
+    }
+
+
+    /*
+     * Second-pass Visitor responsible for producing XML view and assigning
+     * each element a unique jsp:id attribute.
+     */
+    static class SecondPassVisitor extends Node.Visitor
+        	implements TagConstants {
+
+	private Node.Root root;
+	private StringBuffer buf;
+	private Compiler compiler;
+	private String jspIdPrefix;
+	private boolean resetDefaultNS = false;
+
+	// Current value of jsp:id attribute
+	private int jspId;
+
+	/*
+	 * Constructor
+	 */
+	public SecondPassVisitor(Node.Root root, StringBuffer buf,
+				 Compiler compiler, String jspIdPrefix) {
+	    this.root = root;
+	    this.buf = buf;
+	    this.compiler = compiler;
+	    this.jspIdPrefix = jspIdPrefix;
+	}
+
+	/*
+	 * Visits root node.
+	 */
+	public void visit(Node.Root n) throws JasperException {
+	    if (n == this.root) {
+		// top-level page
+		appendXmlProlog();
+		appendTag(n);
+	    } else {
+		boolean resetDefaultNSSave = resetDefaultNS;
+		if (n.isXmlSyntax()) {
+		    resetDefaultNS = true;
+		}
+		visitBody(n);
+		resetDefaultNS = resetDefaultNSSave;
+	    }
+	}
+
+	/*
+	 * Visits jsp:root element of JSP page in XML syntax.
+	 *
+	 * Any nested jsp:root elements (from pages included via an
+	 * include directive) are ignored.
+	 */
+	public void visit(Node.JspRoot n) throws JasperException {
+	    visitBody(n);
+	}
+
+	public void visit(Node.PageDirective n) throws JasperException {
+	    appendPageDirective(n);
+	}
+
+	public void visit(Node.IncludeDirective n) throws JasperException {
+	    // expand in place
+	    visitBody(n);
+	}
+
+	public void visit(Node.Comment n) throws JasperException {
+	    // Comments are ignored in XML view
+	}
+
+	public void visit(Node.Declaration n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.Expression n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.Scriptlet n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.JspElement n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.ELExpression n) throws JasperException {
+	    if (!n.getRoot().isXmlSyntax()) {
+		buf.append("<").append(JSP_TEXT_ACTION);
+		buf.append(" ");
+	        buf.append(jspIdPrefix);
+		buf.append(":id=\"");
+		buf.append(jspId++).append("\">");
+	    }
+	    buf.append("${");
+            buf.append(JspUtil.escapeXml(n.getText()));
+	    buf.append("}");
+	    if (!n.getRoot().isXmlSyntax()) {
+		buf.append(JSP_TEXT_ACTION_END);
+	    }
+	    buf.append("\n");
+	}
+
+	public void visit(Node.IncludeAction n) throws JasperException {
+	    appendTag(n);
+	}
+    
+	public void visit(Node.ForwardAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.GetProperty n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.SetProperty n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.ParamAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.ParamsAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.FallBackAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.UseBean n) throws JasperException {
+	    appendTag(n);
+	}
+	
+	public void visit(Node.PlugIn n) throws JasperException {
+	    appendTag(n);
+	}
+
+        public void visit(Node.NamedAttribute n) throws JasperException {
+            appendTag(n);
+        }
+        
+        public void visit(Node.JspBody n) throws JasperException {
+            appendTag(n);
+        }
+
+	public void visit(Node.CustomTag n) throws JasperException {
+	    boolean resetDefaultNSSave = resetDefaultNS;
+	    appendTag(n, resetDefaultNS);
+	    resetDefaultNS = resetDefaultNSSave;
+	}
+
+	public void visit(Node.UninterpretedTag n) throws JasperException {
+	    boolean resetDefaultNSSave = resetDefaultNS;
+	    appendTag(n, resetDefaultNS);
+	    resetDefaultNS = resetDefaultNSSave;
+	}
+
+	public void visit(Node.JspText n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.DoBodyAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+        public void visit(Node.InvokeAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.TagDirective n) throws JasperException {
+	    appendTagDirective(n);
+	}
+
+	public void visit(Node.AttributeDirective n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.VariableDirective n) throws JasperException {
+	    appendTag(n);
+	}
+        
+	public void visit(Node.TemplateText n) throws JasperException {
+	    /*
+	     * If the template text came from a JSP page written in JSP syntax,
+	     * create a jsp:text element for it (JSP 5.3.2).
+	     */
+	    appendText(n.getText(), !n.getRoot().isXmlSyntax());
+	}
+
+	/*
+	 * Appends the given tag, including its body, to the XML view.
+	 */
+	private void appendTag(Node n) throws JasperException {
+	    appendTag(n, false);
+	}
+
+	/*
+	 * Appends the given tag, including its body, to the XML view,
+	 * and optionally reset default namespace to "", if none specified.
+	 */
+	private void appendTag(Node n, boolean addDefaultNS)
+		throws JasperException {
+
+	    Node.Nodes body = n.getBody();
+	    String text = n.getText();
+
+	    buf.append("<").append(n.getQName());
+	    buf.append("\n");
+
+	    printAttributes(n, addDefaultNS);
+	    buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+	    buf.append(jspId++).append("\"\n");
+
+	    if (ROOT_ACTION.equals(n.getLocalName()) || body != null
+		        || text != null) {
+		buf.append(">\n");
+		if (ROOT_ACTION.equals(n.getLocalName())) {
+		    if (compiler.getCompilationContext().isTagFile()) {
+			appendTagDirective();
+		    } else {
+			appendPageDirective();
+		    }
+		}
+		if (body != null) {
+		    body.visit(this);
+		} else {
+		    appendText(text, false);
+		}
+		buf.append("</" + n.getQName() + ">\n");
+	    } else {
+		buf.append("/>\n");
+	    }
+	}
+
+	/*
+	 * Appends the page directive with the given attributes to the XML
+	 * view.
+	 *
+	 * Since the import attribute of the page directive is the only page
+	 * attribute that is allowed to appear multiple times within the same
+	 * document, and since XML allows only single-value attributes,
+	 * the values of multiple import attributes must be combined into one,
+	 * separated by comma.
+	 *
+	 * If the given page directive contains just 'contentType' and/or
+	 * 'pageEncoding' attributes, we ignore it, as we've already appended
+	 * a page directive containing just these two attributes.
+	 */
+	private void appendPageDirective(Node.PageDirective n) {
+	    boolean append = false;
+	    Attributes attrs = n.getAttributes();
+	    int len = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<len; i++) {
+		String attrName = attrs.getQName(i);
+		if (!"pageEncoding".equals(attrName)
+		        && !"contentType".equals(attrName)) {
+		    append = true;
+		    break;
+		}
+	    }
+	    if (!append) {
+		return;
+	    }
+
+	    buf.append("<").append(n.getQName());
+	    buf.append("\n");
+
+	    // append jsp:id
+	    buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+	    buf.append(jspId++).append("\"\n");
+
+	    // append remaining attributes
+	    for (int i=0; i<len; i++) {
+		String attrName = attrs.getQName(i);
+		if ("import".equals(attrName) || "contentType".equals(attrName)
+		        || "pageEncoding".equals(attrName)) {
+		    /*
+		     * Page directive's 'import' attribute is considered
+		     * further down, and its 'pageEncoding' and 'contentType'
+		     * attributes are ignored, since we've already appended
+		     * a new page directive containing just these two
+		     * attributes
+		     */
+		    continue;
+		}
+		String value = attrs.getValue(i);
+		buf.append("  ").append(attrName).append("=\"");
+		buf.append(JspUtil.getExprInXml(value)).append("\"\n");
+	    }
+	    if (n.getImports().size() > 0) {
+		// Concatenate names of imported classes/packages
+		boolean first = true;
+		ListIterator iter = n.getImports().listIterator();
+		while (iter.hasNext()) {
+		    if (first) {
+			first = false;
+			buf.append("  import=\"");
+		    } else {
+			buf.append(",");
+		    }
+		    buf.append(JspUtil.getExprInXml((String) iter.next()));
+		}
+		buf.append("\"\n");
+	    }
+	    buf.append("/>\n");
+	}
+
+	/*
+	 * Appends a page directive with 'pageEncoding' and 'contentType'
+	 * attributes.
+	 *
+	 * The value of the 'pageEncoding' attribute is hard-coded
+	 * to UTF-8, whereas the value of the 'contentType' attribute, which
+	 * is identical to what the container will pass to
+	 * ServletResponse.setContentType(), is derived from the pageInfo.
+	 */
+	private void appendPageDirective() {
+	    buf.append("<").append(JSP_PAGE_DIRECTIVE_ACTION);
+	    buf.append("\n");
+
+	    // append jsp:id
+	    buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+	    buf.append(jspId++).append("\"\n");
+	    buf.append("  ").append("pageEncoding").append("=\"UTF-8\"\n");
+	    buf.append("  ").append("contentType").append("=\"");
+	    buf.append(compiler.getPageInfo().getContentType()).append("\"\n");
+	    buf.append("/>\n");	    
+	}
+
+	/*
+	 * Appends the tag directive with the given attributes to the XML
+	 * view.
+	 *
+	 * If the given tag directive contains just a 'pageEncoding'
+	 * attributes, we ignore it, as we've already appended
+	 * a tag directive containing just this attributes.
+	 */
+	private void appendTagDirective(Node.TagDirective n)
+	        throws JasperException {
+
+	    boolean append = false;
+	    Attributes attrs = n.getAttributes();
+	    int len = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<len; i++) {
+		String attrName = attrs.getQName(i);
+		if (!"pageEncoding".equals(attrName)) {
+		    append = true;
+		    break;
+		}
+	    }
+	    if (!append) {
+		return;
+	    }
+
+	    appendTag(n);
+	}
+
+	/*
+	 * Appends a tag directive containing a single 'pageEncoding'
+	 * attribute whose value is hard-coded to UTF-8.
+	 */
+	private void appendTagDirective() {
+	    buf.append("<").append(JSP_TAG_DIRECTIVE_ACTION);
+	    buf.append("\n");
+
+	    // append jsp:id
+	    buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+	    buf.append(jspId++).append("\"\n");
+	    buf.append("  ").append("pageEncoding").append("=\"UTF-8\"\n");
+	    buf.append("/>\n");	    
+	}
+
+	private void appendText(String text, boolean createJspTextElement) {
+	    if (createJspTextElement) {
+		buf.append("<").append(JSP_TEXT_ACTION);
+		buf.append("\n");
+
+		// append jsp:id
+		buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+		buf.append(jspId++).append("\"\n");
+		buf.append(">\n");
+
+		appendCDATA(text);
+		buf.append(JSP_TEXT_ACTION_END);
+		buf.append("\n");
+	    } else {
+		appendCDATA(text);
+	    }
+	}
+	
+	/*
+	 * Appends the given text as a CDATA section to the XML view, unless
+	 * the text has already been marked as CDATA.
+	 */
+	private void appendCDATA(String text) {
+	    buf.append(CDATA_START_SECTION);
+	    buf.append(escapeCDATA(text));
+	    buf.append(CDATA_END_SECTION);
+	}
+
+	/*
+	 * Escapes any occurrences of "]]>" (by replacing them with "]]&gt;")
+	 * within the given text, so it can be included in a CDATA section.
+	 */
+	private String escapeCDATA(String text) {
+            if( text==null ) return "";
+	    int len = text.length();
+	    CharArrayWriter result = new CharArrayWriter(len);
+	    for (int i=0; i<len; i++) {
+		if (((i+2) < len)
+		        && (text.charAt(i) == ']')
+		        && (text.charAt(i+1) == ']')
+		        && (text.charAt(i+2) == '>')) {
+		    // match found
+		    result.write(']');
+		    result.write(']');
+		    result.write('&');
+		    result.write('g');
+		    result.write('t');
+		    result.write(';');
+		    i += 2;
+		} else {
+		    result.write(text.charAt(i));
+		}
+	    }
+	    return result.toString();
+	}
+
+	/*
+	 * Appends the attributes of the given Node to the XML view.
+	 */
+	private void printAttributes(Node n, boolean addDefaultNS) {
+
+	    /*
+	     * Append "xmlns" attributes that represent tag libraries
+	     */
+	    Attributes attrs = n.getTaglibAttributes();
+	    int len = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<len; i++) {
+		String name = attrs.getQName(i);
+		String value = attrs.getValue(i);
+		buf.append("  ").append(name).append("=\"").append(value).append("\"\n");
+	    }
+
+	    /*
+	     * Append "xmlns" attributes that do not represent tag libraries
+	     */
+	    attrs = n.getNonTaglibXmlnsAttributes();
+	    len = (attrs == null) ? 0 : attrs.getLength();
+	    boolean defaultNSSeen = false;
+	    for (int i=0; i<len; i++) {
+		String name = attrs.getQName(i);
+		String value = attrs.getValue(i);
+		buf.append("  ").append(name).append("=\"").append(value).append("\"\n");
+		defaultNSSeen |= "xmlns".equals(name);
+	    }
+	    if (addDefaultNS && !defaultNSSeen) {
+		buf.append("  xmlns=\"\"\n");
+	    }
+	    resetDefaultNS = false;
+
+	    /*
+	     * Append all other attributes
+	     */
+	    attrs = n.getAttributes();
+	    len = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<len; i++) {
+		String name = attrs.getQName(i);
+		String value = attrs.getValue(i);
+		buf.append("  ").append(name).append("=\"");
+		buf.append(JspUtil.getExprInXml(value)).append("\"\n");
+	    }
+	}
+
+	/*
+	 * Appends XML prolog with encoding declaration.
+	 */
+	private void appendXmlProlog() {
+	    buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+	}
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/PageInfo.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/PageInfo.java
new file mode 100644
index 0000000..ab3efe2
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/PageInfo.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.compiler;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.JasperException;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+
+/**
+ * A repository for various info about the translation unit under compilation.
+ *
+ * @author Kin-man Chung
+ */
+
+class PageInfo {
+
+    private Vector imports;
+    private Vector dependants;
+
+    private BeanRepository beanRepository;
+    private HashMap taglibsMap;
+    private HashMap jspPrefixMapper;
+    private HashMap xmlPrefixMapper;
+    private HashMap nonCustomTagPrefixMap;
+    private String jspFile;
+    private String defaultLanguage = "java";
+    private String language;
+    private String defaultExtends = Constants.JSP_SERVLET_BASE;
+    private String xtends;
+    private String contentType = null;
+    private String session;
+    private boolean isSession = true;
+    private String bufferValue;
+    private int buffer = 8*1024;    // XXX confirm
+    private String autoFlush;
+    private boolean isAutoFlush = true;
+    private String isThreadSafeValue;
+    private boolean isThreadSafe = true;
+    private String isErrorPageValue;
+    private boolean isErrorPage = false;
+    private String errorPage = null;
+    private String info;
+
+    private boolean scriptless = false;
+    private boolean scriptingInvalid = false;
+    private String isELIgnoredValue;
+    private boolean isELIgnored = false;
+    private String omitXmlDecl = null;
+    private String doctypeName = null;
+    private String doctypePublic = null;
+    private String doctypeSystem = null;
+
+    private boolean isJspPrefixHijacked;
+
+    // Set of all element and attribute prefixes used in this translation unit
+    private HashSet prefixes;
+
+    private boolean hasJspRoot = false;
+    private Vector includePrelude;
+    private Vector includeCoda;
+    private Vector pluginDcls;      // Id's for tagplugin declarations
+
+
+    PageInfo(BeanRepository beanRepository, String jspFile) {
+
+        this.jspFile = jspFile;
+        this.beanRepository = beanRepository;
+        this.taglibsMap = new HashMap();
+        this.jspPrefixMapper = new HashMap();
+        this.xmlPrefixMapper = new HashMap();
+        this.nonCustomTagPrefixMap = new HashMap();
+        this.imports = new Vector();
+        this.dependants = new Vector();
+        this.includePrelude = new Vector();
+        this.includeCoda = new Vector();
+        this.pluginDcls = new Vector();
+        this.prefixes = new HashSet();
+
+        // Enter standard imports
+        for(int i = 0; i < Constants.STANDARD_IMPORTS.length; i++)
+            imports.add(Constants.STANDARD_IMPORTS[i]);
+    }
+
+    /**
+     * Check if the plugin ID has been previously declared.  Make a not
+     * that this Id is now declared.
+     * @return true if Id has been declared.
+     */
+    public boolean isPluginDeclared(String id) {
+        if (pluginDcls.contains(id))
+            return true;
+        pluginDcls.add(id);
+        return false;
+    }
+
+    public void addImports(List imports) {
+        this.imports.addAll(imports);
+    }
+
+    public void addImport(String imp) {
+        this.imports.add(imp);
+    }
+
+    public List getImports() {
+        return imports;
+    }
+
+    public String getJspFile() {
+        return jspFile;
+    }
+
+    public void addDependant(String d) {
+        if (!dependants.contains(d) && !jspFile.equals(d))
+                dependants.add(d);
+    }
+
+    public List getDependants() {
+        return dependants;
+    }
+
+    public BeanRepository getBeanRepository() {
+        return beanRepository;
+    }
+
+    public void setScriptless(boolean s) {
+        scriptless = s;
+    }
+
+    public boolean isScriptless() {
+        return scriptless;
+    }
+
+    public void setScriptingInvalid(boolean s) {
+        scriptingInvalid = s;
+    }
+
+    public boolean isScriptingInvalid() {
+        return scriptingInvalid;
+    }
+
+    public List getIncludePrelude() {
+        return includePrelude;
+    }
+
+    public void setIncludePrelude(Vector prelude) {
+        includePrelude = prelude;
+    }
+
+    public List getIncludeCoda() {
+        return includeCoda;
+    }
+
+    public void setIncludeCoda(Vector coda) {
+        includeCoda = coda;
+    }
+
+    public void setHasJspRoot(boolean s) {
+        hasJspRoot = s;
+    }
+
+    public boolean hasJspRoot() {
+        return hasJspRoot;
+    }
+
+    public String getOmitXmlDecl() {
+        return omitXmlDecl;
+    }
+
+    public void setOmitXmlDecl(String omit) {
+        omitXmlDecl = omit;
+    }
+
+    public String getDoctypeName() {
+        return doctypeName;
+    }
+
+    public void setDoctypeName(String doctypeName) {
+        this.doctypeName = doctypeName;
+    }
+
+    public String getDoctypeSystem() {
+        return doctypeSystem;
+    }
+
+    public void setDoctypeSystem(String doctypeSystem) {
+        this.doctypeSystem = doctypeSystem;
+    }
+
+    public String getDoctypePublic() {
+        return doctypePublic;
+    }
+
+    public void setDoctypePublic(String doctypePublic) {
+        this.doctypePublic = doctypePublic;
+    }
+
+    /* Tag library and XML namespace management methods */
+
+    public void setIsJspPrefixHijacked(boolean isHijacked) {
+        isJspPrefixHijacked = isHijacked;
+    }
+
+    public boolean isJspPrefixHijacked() {
+        return isJspPrefixHijacked;
+    }
+
+    /*
+     * Adds the given prefix to the set of prefixes of this translation unit.
+     *
+     * @param prefix The prefix to add
+     */
+    public void addPrefix(String prefix) {
+        prefixes.add(prefix);
+    }
+
+    /*
+     * Checks to see if this translation unit contains the given prefix.
+     *
+     * @param prefix The prefix to check
+     *
+     * @return true if this translation unit contains the given prefix, false
+     * otherwise
+     */
+    public boolean containsPrefix(String prefix) {
+        return prefixes.contains(prefix);
+    }
+
+    /*
+     * Maps the given URI to the given tag library.
+     *
+     * @param uri The URI to map
+     * @param info The tag library to be associated with the given URI
+     */
+    public void addTaglib(String uri, TagLibraryInfo info) {
+        taglibsMap.put(uri, info);
+    }
+
+    /*
+     * Gets the tag library corresponding to the given URI.
+     *
+     * @return Tag library corresponding to the given URI
+     */
+    public TagLibraryInfo getTaglib(String uri) {
+        return (TagLibraryInfo) taglibsMap.get(uri);
+    }
+
+    /*
+     * Gets the collection of tag libraries that are associated with a URI
+     *
+     * @return Collection of tag libraries that are associated with a URI
+     */
+    public Collection getTaglibs() {
+        return taglibsMap.values();
+    }
+
+    /*
+     * Checks to see if the given URI is mapped to a tag library.
+     *
+     * @param uri The URI to map
+     *
+     * @return true if the given URI is mapped to a tag library, false
+     * otherwise
+     */
+    public boolean hasTaglib(String uri) {
+        return taglibsMap.containsKey(uri);
+    }
+
+    /*
+     * Maps the given prefix to the given URI.
+     *
+     * @param prefix The prefix to map
+     * @param uri The URI to be associated with the given prefix
+     */
+    public void addPrefixMapping(String prefix, String uri) {
+        jspPrefixMapper.put(prefix, uri);
+    }
+
+    /*
+     * Pushes the given URI onto the stack of URIs to which the given prefix
+     * is mapped.
+     *
+     * @param prefix The prefix whose stack of URIs is to be pushed
+     * @param uri The URI to be pushed onto the stack
+     */
+    public void pushPrefixMapping(String prefix, String uri) {
+        LinkedList stack = (LinkedList) xmlPrefixMapper.get(prefix);
+        if (stack == null) {
+            stack = new LinkedList();
+            xmlPrefixMapper.put(prefix, stack);
+        }
+        stack.addFirst(uri);
+    }
+
+    /*
+     * Removes the URI at the top of the stack of URIs to which the given
+     * prefix is mapped.
+     *
+     * @param prefix The prefix whose stack of URIs is to be popped
+     */
+    public void popPrefixMapping(String prefix) {
+        LinkedList stack = (LinkedList) xmlPrefixMapper.get(prefix);
+        if (stack == null || stack.size() == 0) {
+            // XXX throw new Exception("XXX");
+        }
+        stack.removeFirst();
+    }
+
+    /*
+     * Returns the URI to which the given prefix maps.
+     *
+     * @param prefix The prefix whose URI is sought
+     *
+     * @return The URI to which the given prefix maps
+     */
+    public String getURI(String prefix) {
+
+        String uri = null;
+
+        LinkedList stack = (LinkedList) xmlPrefixMapper.get(prefix);
+        if (stack == null || stack.size() == 0) {
+            uri = (String) jspPrefixMapper.get(prefix);
+        } else {
+            uri = (String) stack.getFirst();
+        }
+
+        return uri;
+    }
+
+
+    /* Page/Tag directive attributes */
+
+    /*
+     * language
+     */
+    public void setLanguage(String value, Node n, ErrorDispatcher err,
+                boolean pagedir)
+        throws JasperException {
+
+        if (!"java".equalsIgnoreCase(value)) {
+            if (pagedir)
+                err.jspError(n, "jsp.error.page.language.nonjava");
+            else
+                err.jspError(n, "jsp.error.tag.language.nonjava");
+        }
+
+        language = value;
+    }
+
+    public String getLanguage(boolean useDefault) {
+        return (language == null && useDefault ? defaultLanguage : language);
+    }
+
+    public String getLanguage() {
+        return getLanguage(true);
+    }
+
+
+    /*
+     * extends
+     */
+    public void setExtends(String value, Node.PageDirective n) {
+
+        xtends = value;
+
+        /*
+         * If page superclass is top level class (i.e. not in a package)
+         * explicitly import it. If this is not done, the compiler will assume
+         * the extended class is in the same pkg as the generated servlet.
+         */
+        if (value.indexOf('.') < 0)
+            n.addImport(value);
+    }
+
+    /**
+     * Gets the value of the 'extends' page directive attribute.
+     *
+     * @param useDefault TRUE if the default
+     * (org.apache.jasper.runtime.HttpJspBase) should be returned if this
+     * attribute has not been set, FALSE otherwise
+     *
+     * @return The value of the 'extends' page directive attribute, or the
+     * default (org.apache.jasper.runtime.HttpJspBase) if this attribute has
+     * not been set and useDefault is TRUE
+     */
+    public String getExtends(boolean useDefault) {
+        return (xtends == null && useDefault ? defaultExtends : xtends);
+    }
+
+    /**
+     * Gets the value of the 'extends' page directive attribute.
+     *
+     * @return The value of the 'extends' page directive attribute, or the
+     * default (org.apache.jasper.runtime.HttpJspBase) if this attribute has
+     * not been set
+     */
+    public String getExtends() {
+        return getExtends(true);
+    }
+
+
+    /*
+     * contentType
+     */
+    public void setContentType(String value) {
+        contentType = value;
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+
+    /*
+     * buffer
+     */
+    public void setBufferValue(String value, Node n, ErrorDispatcher err)
+        throws JasperException {
+
+        if ("none".equalsIgnoreCase(value))
+            buffer = 0;
+        else {
+            if (value == null || !value.endsWith("kb"))
+                err.jspError(n, "jsp.error.page.invalid.buffer");
+            try {
+                Integer k = new Integer(value.substring(0, value.length()-2));
+                buffer = k.intValue() * 1024;
+            } catch (NumberFormatException e) {
+                err.jspError(n, "jsp.error.page.invalid.buffer");
+            }
+        }
+
+        bufferValue = value;
+    }
+
+    public String getBufferValue() {
+        return bufferValue;
+    }
+
+    public int getBuffer() {
+        return buffer;
+    }
+
+
+    /*
+     * session
+     */
+    public void setSession(String value, Node n, ErrorDispatcher err)
+        throws JasperException {
+
+        if ("true".equalsIgnoreCase(value))
+            isSession = true;
+        else if ("false".equalsIgnoreCase(value))
+            isSession = false;
+        else
+            err.jspError(n, "jsp.error.page.invalid.session");
+
+        session = value;
+    }
+
+    public String getSession() {
+        return session;
+    }
+
+    public boolean isSession() {
+        return isSession;
+    }
+
+
+    /*
+     * autoFlush
+     */
+    public void setAutoFlush(String value, Node n, ErrorDispatcher err)
+        throws JasperException {
+
+        if ("true".equalsIgnoreCase(value))
+            isAutoFlush = true;
+        else if ("false".equalsIgnoreCase(value))
+            isAutoFlush = false;
+        else
+            err.jspError(n, "jsp.error.autoFlush.invalid");
+
+        autoFlush = value;
+    }
+
+    public String getAutoFlush() {
+        return autoFlush;
+    }
+
+    public boolean isAutoFlush() {
+        return isAutoFlush;
+    }
+
+
+    /*
+     * isThreadSafe
+     */
+    public void setIsThreadSafe(String value, Node n, ErrorDispatcher err)
+        throws JasperException {
+
+        if ("true".equalsIgnoreCase(value))
+            isThreadSafe = true;
+        else if ("false".equalsIgnoreCase(value))
+            isThreadSafe = false;
+        else
+            err.jspError(n, "jsp.error.page.invalid.isthreadsafe");
+
+        isThreadSafeValue = value;
+    }
+
+    public String getIsThreadSafe() {
+        return isThreadSafeValue;
+    }
+
+    public boolean isThreadSafe() {
+        return isThreadSafe;
+    }
+
+
+    /*
+     * info
+     */
+    public void setInfo(String value) {
+        info = value;
+    }
+
+    public String getInfo() {
+        return info;
+    }
+
+
+    /*
+     * errorPage
+     */
+    public void setErrorPage(String value) {
+        errorPage = value;
+    }
+
+    public String getErrorPage() {
+        return errorPage;
+    }
+
+
+    /*
+     * isErrorPage
+     */
+    public void setIsErrorPage(String value, Node n, ErrorDispatcher err)
+        throws JasperException {
+
+        if ("true".equalsIgnoreCase(value))
+            isErrorPage = true;
+        else if ("false".equalsIgnoreCase(value))
+            isErrorPage = false;
+        else
+            err.jspError(n, "jsp.error.page.invalid.iserrorpage");
+
+        isErrorPageValue = value;
+    }
+
+    public String getIsErrorPage() {
+        return isErrorPageValue;
+    }
+
+    public boolean isErrorPage() {
+        return isErrorPage;
+    }
+
+
+    /*
+     * isELIgnored
+     */
+    public void setIsELIgnored(String value, Node n, ErrorDispatcher err,
+                   boolean pagedir)
+        throws JasperException {
+
+        if ("true".equalsIgnoreCase(value))
+            isELIgnored = true;
+        else if ("false".equalsIgnoreCase(value))
+            isELIgnored = false;
+        else {
+            if (pagedir)
+                err.jspError(n, "jsp.error.page.invalid.iselignored");
+            else
+                err.jspError(n, "jsp.error.tag.invalid.iselignored");
+        }
+
+        isELIgnoredValue = value;
+    }
+
+    public void setELIgnored(boolean s) {
+        isELIgnored = s;
+    }
+
+    public String getIsELIgnored() {
+        return isELIgnoredValue;
+    }
+
+    public boolean isELIgnored() {
+        return isELIgnored;
+    }
+
+    public void putNonCustomTagPrefix(String prefix, Mark where) {
+        nonCustomTagPrefixMap.put(prefix, where);
+    }
+
+    public Mark getNonCustomTagPrefix(String prefix) {
+        return (Mark) nonCustomTagPrefixMap.get(prefix);
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/Parser.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/Parser.java
new file mode 100644
index 0000000..f2f18d7
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/Parser.java
@@ -0,0 +1,1923 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.compiler;
+
+import java.io.CharArrayWriter;
+import java.io.FileNotFoundException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagFileInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * This class implements a parser for a JSP page (non-xml view).
+ * JSP page grammar is included here for reference.  The token '#'
+ * that appears in the production indicates the current input token
+ * location in the production.
+ * 
+ * @author Kin-man Chung
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+
+class Parser implements TagConstants {
+
+    private ParserController parserController;
+    private JspCompilationContext ctxt;
+    private JspReader reader;
+    private String currentFile;
+    private Mark start;
+    private ErrorDispatcher err;
+    private int scriptlessCount;
+    private boolean isTagFile;
+    private boolean directivesOnly;
+    private URL jarFileUrl;
+    private PageInfo pageInfo;
+
+    // Virtual body content types, to make parsing a little easier.
+    // These are not accessible from outside the parser.
+    private static final String JAVAX_BODY_CONTENT_PARAM = 
+        "JAVAX_BODY_CONTENT_PARAM";
+    private static final String JAVAX_BODY_CONTENT_PLUGIN = 
+        "JAVAX_BODY_CONTENT_PLUGIN";
+    private static final String JAVAX_BODY_CONTENT_TEMPLATE_TEXT = 
+        "JAVAX_BODY_CONTENT_TEMPLATE_TEXT";
+
+    /**
+     * The constructor
+     */
+    private Parser(ParserController pc, JspReader reader, boolean isTagFile,
+		   boolean directivesOnly, URL jarFileUrl) {
+	this.parserController = pc;
+	this.ctxt = pc.getJspCompilationContext();
+	this.pageInfo = pc.getCompiler().getPageInfo();
+	this.err = pc.getCompiler().getErrorDispatcher();
+	this.reader = reader;
+	this.currentFile = reader.mark().getFile();
+        this.scriptlessCount = 0;
+	this.isTagFile = isTagFile;
+	this.directivesOnly = directivesOnly;
+	this.jarFileUrl = jarFileUrl;
+        start = reader.mark();
+    }
+
+    /**
+     * The main entry for Parser
+     * 
+     * @param pc The ParseController, use for getting other objects in compiler
+     *		 and for parsing included pages
+     * @param reader To read the page
+     * @param parent The parent node to this page, null for top level page
+     * @return list of nodes representing the parsed page
+     */
+    public static Node.Nodes parse(ParserController pc,
+				   JspReader reader,
+				   Node parent,
+				   boolean isTagFile,
+				   boolean directivesOnly,
+				   URL jarFileUrl,
+				   String pageEnc,
+				   String jspConfigPageEnc,
+				   boolean isDefaultPageEncoding)
+		throws JasperException {
+
+	Parser parser = new Parser(pc, reader, isTagFile, directivesOnly,
+				   jarFileUrl);
+
+	Node.Root root = new Node.Root(reader.mark(), parent, false);
+	root.setPageEncoding(pageEnc);
+	root.setJspConfigPageEncoding(jspConfigPageEnc);
+	root.setIsDefaultPageEncoding(isDefaultPageEncoding);
+
+	if (directivesOnly) {
+	    parser.parseTagFileDirectives(root);
+	    return new Node.Nodes(root);
+	}
+
+	// For the Top level page, add inlcude-prelude and include-coda
+	PageInfo pageInfo = pc.getCompiler().getPageInfo();
+	if (parent == null) {
+	    parser.addInclude(root, pageInfo.getIncludePrelude());
+	}
+	while (reader.hasMoreInput()) {
+	    parser.parseElements(root);
+	}
+	if (parent == null) {
+	    parser.addInclude(root, pageInfo.getIncludeCoda());
+	}
+
+	Node.Nodes page = new Node.Nodes(root);
+	return page;
+    }
+
+    /**
+     * Attributes ::= (S Attribute)* S?
+     */
+    Attributes parseAttributes() throws JasperException {
+	AttributesImpl attrs = new AttributesImpl();
+
+	reader.skipSpaces();
+	while (parseAttribute(attrs))
+	    reader.skipSpaces();
+
+	return attrs;
+    }
+
+    /**
+     * Parse Attributes for a reader, provided for external use
+     */
+    public static Attributes parseAttributes(ParserController pc,
+					     JspReader reader)
+		throws JasperException {
+	Parser tmpParser = new Parser(pc, reader, false, false, null);
+	return tmpParser.parseAttributes();
+    }
+
+    /**
+     * Attribute ::= Name S? Eq S?
+     *               (   '"<%=' RTAttributeValueDouble
+     *                 | '"' AttributeValueDouble
+     *                 | "'<%=" RTAttributeValueSingle
+     *                 | "'" AttributeValueSingle
+     *               }
+     * Note: JSP and XML spec does not allow while spaces around Eq.  It is
+     * added to be backward compatible with Tomcat, and with other xml parsers.
+     */
+    private boolean parseAttribute(AttributesImpl attrs)
+	        throws JasperException {
+
+	// Get the qualified name
+	String qName = parseName();
+	if (qName == null)
+	    return false;
+
+	// Determine prefix and local name components
+	String localName = qName;
+	String uri = "";
+	int index = qName.indexOf(':');
+	if (index != -1) {
+	    String prefix = qName.substring(0, index);
+	    uri = pageInfo.getURI(prefix);
+	    if (uri == null) {
+		err.jspError(reader.mark(),
+			     "jsp.error.attribute.invalidPrefix", prefix);
+	    }
+	    localName = qName.substring(index+1);
+	}
+
+ 	reader.skipSpaces();
+	if (!reader.matches("="))
+	    err.jspError(reader.mark(), "jsp.error.attribute.noequal");
+
+ 	reader.skipSpaces();
+	char quote = (char) reader.nextChar();
+	if (quote != '\'' && quote != '"')
+	    err.jspError(reader.mark(), "jsp.error.attribute.noquote");
+
+ 	String watchString = "";
+	if (reader.matches("<%="))
+	    watchString = "%>";
+	watchString = watchString + quote;
+	
+	String attrValue = parseAttributeValue(watchString);
+	attrs.addAttribute(uri, localName, qName, "CDATA", attrValue);
+	return true;
+    }
+
+    /**
+     * Name ::= (Letter | '_' | ':') (Letter | Digit | '.' | '_' | '-' | ':')*
+     */
+    private String parseName() throws JasperException {
+	char ch = (char)reader.peekChar();
+	if (Character.isLetter(ch) || ch == '_' || ch == ':') {
+	    StringBuffer buf = new StringBuffer();
+	    buf.append(ch);
+	    reader.nextChar();
+	    ch = (char)reader.peekChar();
+	    while (Character.isLetter(ch) || Character.isDigit(ch) ||
+			ch == '.' || ch == '_' || ch == '-' || ch == ':') {
+		buf.append(ch);
+		reader.nextChar();
+		ch = (char) reader.peekChar();
+	    }
+	    return buf.toString();
+	}
+	return null;
+    }
+
+    /**
+     * AttributeValueDouble ::= (QuotedChar - '"')*
+     *				('"' | <TRANSLATION_ERROR>)
+     * RTAttributeValueDouble ::= ((QuotedChar - '"')* - ((QuotedChar-'"')'%>"')
+     *				  ('%>"' | TRANSLATION_ERROR)
+     */
+    private String parseAttributeValue(String watch) throws JasperException {
+	Mark start = reader.mark();
+	Mark stop = reader.skipUntilIgnoreEsc(watch);
+	if (stop == null) {
+	    err.jspError(start, "jsp.error.attribute.unterminated", watch);
+	}
+
+	String ret = parseQuoted(reader.getText(start, stop));
+	if (watch.length() == 1)	// quote
+	    return ret;
+
+	// putback delimiter '<%=' and '%>', since they are needed if the
+	// attribute does not allow RTexpression.
+	return "<%=" + ret + "%>";
+    }
+
+    /**
+     * QuotedChar ::=   '&apos;'
+     *	              | '&quot;'
+     *                | '\\'
+     *                | '\"'
+     *                | "\'"
+     *                | '\>'
+     *                | '\$'
+     *                | Char
+     */
+    private String parseQuoted(String tx) {
+	StringBuffer buf = new StringBuffer();
+	int size = tx.length();
+	int i = 0;
+	while (i < size) {
+	    char ch = tx.charAt(i);
+	    if (ch == '&') {
+		if (i+5 < size && tx.charAt(i+1) == 'a'
+		        && tx.charAt(i+2) == 'p' && tx.charAt(i+3) == 'o'
+		        && tx.charAt(i+4) == 's' && tx.charAt(i+5) == ';') {
+		    buf.append('\'');
+		    i += 6;
+		} else if (i+5 < size && tx.charAt(i+1) == 'q'
+			   && tx.charAt(i+2) == 'u' && tx.charAt(i+3) == 'o'
+			   && tx.charAt(i+4) == 't' && tx.charAt(i+5) == ';') {
+		    buf.append('"');
+		    i += 6;
+		} else {
+		    buf.append(ch);
+		    ++i;
+		}
+	    } else if (ch == '\\' && i+1 < size) {
+		ch = tx.charAt(i+1);
+		if (ch == '\\' || ch == '\"' || ch == '\'' || ch == '>') {
+		    buf.append(ch);
+		    i += 2;
+		} else if (ch == '$') {
+		    // Replace "\$" with some special char.  XXX hack!
+		    buf.append(Constants.ESC);
+		    i += 2;
+		} else {
+		    buf.append('\\');
+		    ++i;
+		}
+	    } else {
+		buf.append(ch);
+		++i;
+	    }
+	}
+	return buf.toString();
+    }
+
+    private String parseScriptText(String tx) {
+	CharArrayWriter cw = new CharArrayWriter();
+	int size = tx.length();
+	int i = 0;
+	while (i < size) {
+	    char ch = tx.charAt(i);
+	    if (i+2 < size && ch == '%' && tx.charAt(i+1) == '\\'
+		    && tx.charAt(i+2) == '>') {
+		cw.write('%');
+		cw.write('>');
+		i += 3;
+	    } else {
+		cw.write(ch);
+		++i;
+	    }
+	}
+	cw.close();
+	return cw.toString();
+    }
+
+    /*
+     * Invokes parserController to parse the included page
+     */
+    private void processIncludeDirective(String file, Node parent) 
+		throws JasperException {
+	if (file == null) {
+	    return;
+	}
+
+	try {
+	    parserController.parse(file, parent, jarFileUrl);
+	} catch (FileNotFoundException ex) {
+	    err.jspError(start, "jsp.error.file.not.found", file);
+	} catch (Exception ex) {
+	    err.jspError(start, ex.getMessage());
+	}
+    }
+
+    /*
+     * Parses a page directive with the following syntax:
+     *   PageDirective ::= ( S Attribute)*
+     */
+    private void parsePageDirective(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	Node.PageDirective n = new Node.PageDirective(attrs, start, parent);
+
+	/*
+	 * A page directive may contain multiple 'import' attributes, each of
+	 * which consists of a comma-separated list of package names.
+	 * Store each list with the node, where it is parsed.
+	 */
+	for (int i = 0; i < attrs.getLength(); i++) {
+	    if ("import".equals(attrs.getQName(i))) {
+		n.addImport(attrs.getValue(i));
+	    }
+	}
+    }
+
+    /*
+     * Parses an include directive with the following syntax:
+     *   IncludeDirective ::= ( S Attribute)*
+     */
+    private void parseIncludeDirective(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+
+	// Included file expanded here
+	Node includeNode = new Node.IncludeDirective(attrs, start, parent);
+	processIncludeDirective(attrs.getValue("file"), includeNode);
+    }
+
+    /**
+     * Add a list of files.  This is used for implementing include-prelude
+     * and include-coda of jsp-config element in web.xml
+     */
+    private void addInclude(Node parent, List files) throws JasperException {
+        if( files != null ) {
+            Iterator iter = files.iterator();
+            while (iter.hasNext()) {
+                String file = (String) iter.next();
+                AttributesImpl attrs = new AttributesImpl();
+                attrs.addAttribute("", "file", "file", "CDATA", file);
+
+                // Create a dummy Include directive node
+                Node includeNode = new Node.IncludeDirective(attrs, 
+                    reader.mark(), parent);
+                processIncludeDirective(file, includeNode);
+            }
+        }
+    }
+
+    /*
+     * Parses a taglib directive with the following syntax:
+     *   Directive ::= ( S Attribute)*
+     */
+    private void parseTaglibDirective(Node parent) throws JasperException {
+
+	Attributes attrs = parseAttributes();
+	String uri = attrs.getValue("uri");
+	String prefix = attrs.getValue("prefix");
+	if (prefix != null) {
+            Mark prevMark = pageInfo.getNonCustomTagPrefix(prefix);
+            if (prevMark != null) {
+                err.jspError(reader.mark(), "jsp.error.prefix.use_before_dcl",
+                    prefix, prevMark.getFile(), "" + prevMark.getLineNumber());
+            }
+	    if (uri != null) {
+		String uriPrev = pageInfo.getURI(prefix);
+		if (uriPrev != null && !uriPrev.equals(uri)) {
+		    err.jspError(reader.mark(), "jsp.error.prefix.refined",
+			prefix, uri, uriPrev);
+		}
+		if (pageInfo.getTaglib(uri) == null) {
+		    String[] location = ctxt.getTldLocation(uri);
+		    pageInfo.addTaglib(uri,
+				       new TagLibraryInfoImpl(ctxt,
+							      parserController,
+							      prefix,
+							      uri,
+							      location,
+							      err));
+		}
+		pageInfo.addPrefixMapping(prefix, uri);
+	    } else {
+		String tagdir = attrs.getValue("tagdir");
+		if (tagdir != null) {
+		    String urnTagdir = URN_JSPTAGDIR + tagdir;
+		    if (pageInfo.getTaglib(urnTagdir) == null) {
+			pageInfo.addTaglib(urnTagdir,
+					   new ImplicitTagLibraryInfo(
+                                                   ctxt,
+						   parserController,
+						   prefix, 
+						   tagdir,
+						   err));
+		    }
+		    pageInfo.addPrefixMapping(prefix, urnTagdir);
+		}
+	    }
+	}
+
+	new Node.TaglibDirective(attrs, start, parent);
+    }
+
+    /*
+     * Parses a directive with the following syntax:
+     *   Directive ::= S? (   'page' PageDirective
+     *			    | 'include' IncludeDirective
+     *			    | 'taglib' TagLibDirective)
+     *		       S? '%>'
+     *
+     *   TagDirective ::= S? ('tag' PageDirective
+     *			    | 'include' IncludeDirective
+     *			    | 'taglib' TagLibDirective)
+     *                      | 'attribute AttributeDirective
+     *                      | 'variable VariableDirective
+     *		       S? '%>'
+     */
+    private void parseDirective(Node parent) throws JasperException {
+	reader.skipSpaces();
+
+	String directive = null;
+	if (reader.matches("page")) {
+	    directive = "&lt;%@ page";
+	    if (isTagFile) {
+		err.jspError(reader.mark(), "jsp.error.directive.istagfile",
+					    directive);
+	    }
+	    parsePageDirective(parent);
+	} else if (reader.matches("include")) {
+	    directive = "&lt;%@ include";
+	    parseIncludeDirective(parent);
+	} else if (reader.matches("taglib")) {
+	    if (directivesOnly) {
+	        // No need to get the tagLibInfo objects.  This alos suppresses
+	        // parsing of any tag files used in this tag file.
+	        return;
+	    }
+	    directive = "&lt;%@ taglib";
+	    parseTaglibDirective(parent);
+	} else if (reader.matches("tag")) {
+	    directive = "&lt;%@ tag";
+	    if (!isTagFile) {
+		err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+					    directive);
+	    }
+	    parseTagDirective(parent);
+	} else if (reader.matches("attribute")) {
+	    directive = "&lt;%@ attribute";
+	    if (!isTagFile) {
+		err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+					    directive);
+	    }
+	    parseAttributeDirective(parent);
+	} else if (reader.matches("variable")) {
+	    directive = "&lt;%@ variable";
+	    if (!isTagFile) {
+		err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+					    directive);
+	    }
+	    parseVariableDirective(parent);
+	} else {
+	    err.jspError(reader.mark(), "jsp.error.invalid.directive");
+	}
+
+	reader.skipSpaces();
+	if (!reader.matches("%>")) {
+	    err.jspError(start, "jsp.error.unterminated", directive);
+	}
+    }
+	
+    /*
+     * Parses a directive with the following syntax:
+     *
+     *   XMLJSPDirectiveBody ::= S? (   ( 'page' PageDirectiveAttrList
+     *                                    S? ( '/>' | ( '>' S? ETag ) )
+     *                               | ( 'include' IncludeDirectiveAttrList
+     *                                    S? ( '/>' | ( '>' S? ETag ) )
+     *                           | <TRANSLATION_ERROR>
+     *
+     *   XMLTagDefDirectiveBody ::= (   ( 'tag' TagDirectiveAttrList
+     *                                    S? ( '/>' | ( '>' S? ETag ) )
+     *                                | ( 'include' IncludeDirectiveAttrList
+     *                                    S? ( '/>' | ( '>' S? ETag ) )
+     *                                | ( 'attribute' AttributeDirectiveAttrList
+     *                                    S? ( '/>' | ( '>' S? ETag ) )
+     *                                | ( 'variable' VariableDirectiveAttrList
+     *                                    S? ( '/>' | ( '>' S? ETag ) )
+     *                              )
+     *                            | <TRANSLATION_ERROR>
+     */
+    private void parseXMLDirective(Node parent) throws JasperException {
+       reader.skipSpaces();
+
+        String eTag = null;
+       if (reader.matches("page")) {
+            eTag = "jsp:directive.page";
+           if (isTagFile) {
+               err.jspError(reader.mark(), "jsp.error.directive.istagfile",
+                                           "&lt;" + eTag);
+           }
+           parsePageDirective(parent);
+       } else if (reader.matches("include")) {
+            eTag = "jsp:directive.include";
+           parseIncludeDirective(parent);
+       } else if (reader.matches("tag")) {
+            eTag = "jsp:directive.tag";
+           if (!isTagFile) {
+               err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+                                           "&lt;" + eTag);
+           }
+           parseTagDirective(parent);
+       } else if (reader.matches("attribute")) {
+            eTag = "jsp:directive.attribute";
+           if (!isTagFile) {
+               err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+                                           "&lt;" + eTag);
+           }
+           parseAttributeDirective(parent);
+       } else if (reader.matches("variable")) {
+            eTag = "jsp:directive.variable";
+           if (!isTagFile) {
+               err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+                                           "&lt;" + eTag);
+           }
+           parseVariableDirective(parent);
+       } else {
+           err.jspError(reader.mark(), "jsp.error.invalid.directive");
+       }
+
+       reader.skipSpaces();
+        if( reader.matches( ">" ) ) {
+            reader.skipSpaces();
+            if( !reader.matchesETag( eTag ) ) {
+                err.jspError(start, "jsp.error.unterminated", "&lt;" + eTag );
+            }
+        }
+        else if( !reader.matches( "/>" ) ) {
+            err.jspError(start, "jsp.error.unterminated", "&lt;" + eTag );
+        }
+    }
+
+    /*
+     * Parses a tag directive with the following syntax:
+     *   PageDirective ::= ( S Attribute)*
+     */
+    private void parseTagDirective(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	Node.TagDirective n = new Node.TagDirective(attrs, start, parent);
+
+        /*
+         * A page directive may contain multiple 'import' attributes, each of
+         * which consists of a comma-separated list of package names.
+         * Store each list with the node, where it is parsed.
+         */
+        for (int i = 0; i < attrs.getLength(); i++) {
+            if ("import".equals(attrs.getQName(i))) {
+                n.addImport(attrs.getValue(i));
+            }
+        }
+    }
+
+    /*
+     * Parses a attribute directive with the following syntax:
+     *   AttributeDirective ::= ( S Attribute)*
+     */
+    private void parseAttributeDirective(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	Node.AttributeDirective n =
+		new Node.AttributeDirective(attrs, start, parent);
+    }
+
+    /*
+     * Parses a variable directive with the following syntax:
+     *   PageDirective ::= ( S Attribute)*
+     */
+    private void parseVariableDirective(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	Node.VariableDirective n =
+		new Node.VariableDirective(attrs, start, parent);
+    }
+
+    /*
+     * JSPCommentBody ::= (Char* - (Char* '--%>')) '--%>'
+     */
+    private void parseComment(Node parent) throws JasperException {	
+	start = reader.mark();
+	Mark stop = reader.skipUntil("--%>");
+	if (stop == null) {
+	    err.jspError(start, "jsp.error.unterminated", "&lt;%--");
+	}
+
+	new Node.Comment(reader.getText(start, stop), start, parent);
+    }
+
+    /*
+     * DeclarationBody ::= (Char* - (char* '%>')) '%>'
+     */
+    private void parseDeclaration(Node parent) throws JasperException {
+	start = reader.mark();
+	Mark stop = reader.skipUntil("%>");
+	if (stop == null) {
+	    err.jspError(start, "jsp.error.unterminated", "&lt;%!");
+	}
+
+	new Node.Declaration(parseScriptText(reader.getText(start, stop)),
+			     start, parent);
+    }
+
+    /*
+     * XMLDeclarationBody ::=   ( S? '/>' )
+     *                        | ( S? '>' (Char* - (char* '<')) CDSect?)* ETag
+     *                        | <TRANSLATION_ERROR>
+     * CDSect ::= CDStart CData CDEnd
+     * CDStart ::= '<![CDATA['
+     * CData ::= (Char* - (Char* ']]>' Char*))
+     * CDEnd ::= ']]>'
+     */
+    private void parseXMLDeclaration(Node parent) throws JasperException {
+        reader.skipSpaces();
+        if( !reader.matches( "/>" ) ) {
+            if( !reader.matches( ">" ) ) {
+                err.jspError(start, "jsp.error.unterminated",
+                        "&lt;jsp:declaration&gt;");
+            }
+	    Mark stop;
+            String text;
+            while (true) {
+                start = reader.mark();
+                stop = reader.skipUntil("<");
+                if (stop == null) {
+                    err.jspError(start, "jsp.error.unterminated",
+                        "&lt;jsp:declaration&gt;");
+                }
+		text = parseScriptText(reader.getText(start, stop));
+                new Node.Declaration(text, start, parent);
+                if (reader.matches("![CDATA[")) {
+                    start = reader.mark();
+                    stop = reader.skipUntil("]]>");
+                    if (stop == null) {
+                        err.jspError(start, "jsp.error.unterminated", "CDATA");
+                    }
+		    text = parseScriptText(reader.getText(start, stop));
+                    new Node.Declaration(text, start, parent);
+                }
+                else {
+                    break;
+                }
+	    }
+		
+            if (!reader.matchesETagWithoutLessThan( "jsp:declaration" ) ) {
+                err.jspError(start, "jsp.error.unterminated",
+                        "&lt;jsp:declaration&gt;");
+            }
+        }
+    }
+
+    /*
+     * ExpressionBody ::= (Char* - (char* '%>')) '%>'
+     */
+    private void parseExpression(Node parent) throws JasperException {
+	start = reader.mark();
+	Mark stop = reader.skipUntil("%>");
+	if (stop == null) {
+	    err.jspError(start, "jsp.error.unterminated", "&lt;%=");
+	}
+
+	new Node.Expression(parseScriptText(reader.getText(start, stop)),
+			    start, parent);
+    }
+
+    /*
+     * XMLExpressionBody ::=   ( S? '/>' )
+     *                       | ( S? '>' (Char* - (char* '<')) CDSect?)* ETag )
+     *                       | <TRANSLATION_ERROR>
+     */
+    private void parseXMLExpression(Node parent) throws JasperException {
+        reader.skipSpaces();
+        if( !reader.matches( "/>" ) ) {
+            if( !reader.matches( ">" ) ) {
+                err.jspError(start, "jsp.error.unterminated",
+                    "&lt;jsp:expression&gt;");
+            }
+            Mark stop;
+            String text;
+            while (true) {
+                start = reader.mark();
+                stop = reader.skipUntil("<");
+                if (stop == null) {
+                    err.jspError(start, "jsp.error.unterminated",
+                        "&lt;jsp:expression&gt;");
+                }
+                text = parseScriptText(reader.getText(start, stop));
+                new Node.Expression(text, start, parent);
+                if (reader.matches("![CDATA[")) {
+                    start = reader.mark();
+                    stop = reader.skipUntil("]]>");
+                    if (stop == null) {
+                        err.jspError(start, "jsp.error.unterminated", "CDATA");
+                    }
+                    text = parseScriptText(reader.getText(start, stop));
+                    new Node.Expression(text, start, parent);
+                }
+                else {
+                    break;
+                }
+            }
+            if (!reader.matchesETagWithoutLessThan( "jsp:expression" )) {
+                err.jspError(start, "jsp.error.unterminated",
+                    "&lt;jsp:expression&gt;");
+            }
+        }
+    }
+
+    /*
+     * ELExpressionBody
+     * (following "${" to first unquoted "}")
+     * // XXX add formal production and confirm implementation against it,
+     * //     once it's decided
+     */
+    private void parseELExpression(Node parent) throws JasperException {
+        start = reader.mark();
+        Mark last = null;
+        boolean singleQuoted = false, doubleQuoted = false;
+        int currentChar;
+        do {
+            // XXX could move this logic to JspReader
+            last = reader.mark();               // XXX somewhat wasteful
+            currentChar = reader.nextChar();
+            if (currentChar == '\\' && (singleQuoted || doubleQuoted)) {
+                // skip character following '\' within quotes
+                reader.nextChar();
+                currentChar = reader.nextChar();
+            }
+            if (currentChar == -1)
+                err.jspError(start, "jsp.error.unterminated", "${");
+            if (currentChar == '"')
+                doubleQuoted = !doubleQuoted;
+            if (currentChar == '\'')
+                singleQuoted = !singleQuoted;
+        } while (currentChar != '}' || (singleQuoted || doubleQuoted));
+
+        new Node.ELExpression(reader.getText(start, last), start, parent);
+    }
+
+    /*
+     * ScriptletBody ::= (Char* - (char* '%>')) '%>'
+     */
+    private void parseScriptlet(Node parent) throws JasperException {
+	start = reader.mark();
+	Mark stop = reader.skipUntil("%>");
+	if (stop == null) {
+	    err.jspError(start, "jsp.error.unterminated", "&lt;%");
+	}
+
+	new Node.Scriptlet(parseScriptText(reader.getText(start, stop)),
+			   start, parent);
+    }
+
+    /*
+     * XMLScriptletBody ::=   ( S? '/>' )
+     *                      | ( S? '>' (Char* - (char* '<')) CDSect?)* ETag )
+     *                      | <TRANSLATION_ERROR>
+     */
+    private void parseXMLScriptlet(Node parent) throws JasperException {
+        reader.skipSpaces();
+        if( !reader.matches( "/>" ) ) {
+            if( !reader.matches( ">" ) ) {
+                err.jspError(start, "jsp.error.unterminated",
+                    "&lt;jsp:scriptlet&gt;");
+            }
+            Mark stop;
+            String text;
+            while (true) {
+                start = reader.mark();
+                stop = reader.skipUntil("<");
+                if (stop == null) {
+                    err.jspError(start, "jsp.error.unterminated",
+                        "&lt;jsp:scriptlet&gt;");
+                }
+                text = parseScriptText(reader.getText(start, stop));
+                new Node.Scriptlet(text, start, parent);
+                if (reader.matches("![CDATA[")) {
+                    start = reader.mark();
+                    stop = reader.skipUntil("]]>");
+                    if (stop == null) {
+                        err.jspError(start, "jsp.error.unterminated", "CDATA");
+                    }
+                    text = parseScriptText(reader.getText(start, stop));
+                    new Node.Scriptlet(text, start, parent);
+                }
+                else {
+                    break;
+                }
+            }
+
+            if (!reader.matchesETagWithoutLessThan( "jsp:scriptlet" )) {
+                err.jspError(start, "jsp.error.unterminated",
+                    "&lt;jsp:scriptlet&gt;");
+            }
+        }
+    }
+	
+    /**
+     * Param ::= '<jsp:param' S Attributes S? EmptyBody S?
+     */
+    private void parseParam(Node parent) throws JasperException {
+	if (!reader.matches("<jsp:param")) {
+	    err.jspError(reader.mark(), "jsp.error.paramexpected");
+	}
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+        
+        Node paramActionNode = new Node.ParamAction( attrs, start, parent );
+        
+        parseEmptyBody( paramActionNode, "jsp:param" );
+        
+        reader.skipSpaces();
+    }
+
+    /*
+     * For Include:
+     * StdActionContent ::= Attributes ParamBody
+     *
+     * ParamBody ::=   EmptyBody
+     *               | ( '>' S? ( '<jsp:attribute' NamedAttributes )?
+     *                   '<jsp:body'
+     *                   (JspBodyParam | <TRANSLATION_ERROR> )
+     *                   S? ETag
+     *                 )
+     *               | ( '>' S? Param* ETag )
+     *
+     * EmptyBody ::=   '/>'
+     *               | ( '>' ETag )
+     *               | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
+     *
+     * JspBodyParam ::= S? '>' Param* '</jsp:body>'
+     */
+    private void parseInclude(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+
+        Node includeNode = new Node.IncludeAction( attrs, start, parent );
+        
+        parseOptionalBody(includeNode, "jsp:include", 
+			  JAVAX_BODY_CONTENT_PARAM);
+    }
+
+    /*
+     * For Forward:
+     * StdActionContent ::= Attributes ParamBody
+     */
+    private void parseForward(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+
+        Node forwardNode = new Node.ForwardAction( attrs, start, parent );
+        
+        parseOptionalBody(forwardNode, "jsp:forward",
+			  JAVAX_BODY_CONTENT_PARAM);
+    }
+
+    private void parseInvoke(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+
+        Node invokeNode = new Node.InvokeAction(attrs, start, parent);
+        
+        parseEmptyBody(invokeNode, "jsp:invoke");
+    }
+
+    private void parseDoBody(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+
+        Node doBodyNode = new Node.DoBodyAction(attrs, start, parent);
+        
+        parseEmptyBody(doBodyNode, "jsp:doBody");
+    }
+
+    private void parseElement(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+
+        Node elementNode = new Node.JspElement(attrs, start, parent);
+        
+        parseOptionalBody( elementNode, "jsp:element", 
+            TagInfo.BODY_CONTENT_JSP );
+    }
+
+    /*
+     * For GetProperty:
+     * StdActionContent ::= Attributes EmptyBody
+     */
+    private void parseGetProperty(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+
+        Node getPropertyNode = new Node.GetProperty( attrs, start, parent );
+        
+        parseOptionalBody(getPropertyNode, "jsp:getProperty",
+			  TagInfo.BODY_CONTENT_EMPTY);
+    }
+
+    /*
+     * For SetProperty:
+     * StdActionContent ::= Attributes EmptyBody
+     */
+    private void parseSetProperty(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+
+        Node setPropertyNode = new Node.SetProperty( attrs, start, parent );
+        
+        parseOptionalBody(setPropertyNode, "jsp:setProperty",
+			  TagInfo.BODY_CONTENT_EMPTY);
+    }
+
+    /*
+     * EmptyBody ::=   '/>'
+     *               | ( '>' ETag )
+     *               | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
+     */
+    private void parseEmptyBody( Node parent, String tag ) 
+        throws JasperException
+    {
+	if( reader.matches("/>") ) {
+            // Done
+        }
+        else if( reader.matches( ">" ) ) {
+            if( reader.matchesETag( tag ) ) {
+                // Done
+            }
+            else if( reader.matchesOptionalSpacesFollowedBy(
+                "<jsp:attribute" ) )
+            {
+                // Parse the one or more named attribute nodes
+                parseNamedAttributes( parent );
+                if( !reader.matchesETag( tag ) ) {
+                    // Body not allowed
+                    err.jspError(reader.mark(),
+                        "jsp.error.jspbody.emptybody.only",
+                        "&lt;" + tag );
+                }
+            }
+            else {
+                err.jspError(reader.mark(), "jsp.error.jspbody.emptybody.only",
+                    "&lt;" + tag );
+            }
+        }
+        else {
+	    err.jspError(reader.mark(), "jsp.error.unterminated",
+                "&lt;" + tag );
+        }
+    }
+
+    /*
+     * For UseBean:
+     * StdActionContent ::= Attributes OptionalBody
+     */
+    private void parseUseBean(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+        
+        Node useBeanNode = new Node.UseBean( attrs, start, parent );
+        
+        parseOptionalBody( useBeanNode, "jsp:useBean", 
+            TagInfo.BODY_CONTENT_JSP );
+    }
+
+    /*
+     * Parses OptionalBody, but also reused to parse bodies for plugin
+     * and param since the syntax is identical (the only thing that
+     * differs substantially is how to process the body, and thus
+     * we accept the body type as a parameter).
+     *
+     * OptionalBody ::= EmptyBody | ActionBody
+     *
+     * ScriptlessOptionalBody ::= EmptyBody | ScriptlessActionBody
+     *
+     * TagDependentOptionalBody ::= EmptyBody | TagDependentActionBody
+     *
+     * EmptyBody ::=   '/>'
+     *               | ( '>' ETag )
+     *               | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
+     *
+     * ActionBody ::=   JspAttributeAndBody
+     *                | ( '>' Body ETag )
+     *
+     * ScriptlessActionBody ::=   JspAttributeAndBody 
+     *                          | ( '>' ScriptlessBody ETag )
+     * 
+     * TagDependentActionBody ::=   JspAttributeAndBody
+     *                            | ( '>' TagDependentBody ETag )
+     *
+     */
+    private void parseOptionalBody( Node parent, String tag, String bodyType ) 
+        throws JasperException 
+    {
+	if (reader.matches("/>")) {
+	    // EmptyBody
+	    return;
+	}
+
+	if (!reader.matches(">")) {
+	    err.jspError(reader.mark(), "jsp.error.unterminated",
+			 "&lt;" + tag );
+	}
+        
+        if( reader.matchesETag( tag ) ) {
+            // EmptyBody
+            return;
+        }
+        
+        if( !parseJspAttributeAndBody( parent, tag, bodyType ) ) {
+            // Must be ( '>' # Body ETag )
+            parseBody(parent, tag, bodyType );
+        }
+    }
+    
+    /**
+     * Attempts to parse 'JspAttributeAndBody' production.  Returns true if
+     * it matched, or false if not.  Assumes EmptyBody is okay as well.
+     *
+     * JspAttributeAndBody ::=
+     *                  ( '>' # S? ( '<jsp:attribute' NamedAttributes )?
+     *                    '<jsp:body'
+     *                    ( JspBodyBody | <TRANSLATION_ERROR> )
+     *                    S? ETag
+     *                  )
+     */
+    private boolean parseJspAttributeAndBody( Node parent, String tag, 
+        String bodyType ) 
+        throws JasperException
+    {
+        boolean result = false;
+        
+        if( reader.matchesOptionalSpacesFollowedBy( "<jsp:attribute" ) ) {
+            // May be an EmptyBody, depending on whether
+            // There's a "<jsp:body" before the ETag
+            
+            // First, parse <jsp:attribute> elements:
+            parseNamedAttributes( parent );
+            
+            result = true;
+        }
+        
+        if( reader.matchesOptionalSpacesFollowedBy( "<jsp:body" ) ) {
+            // ActionBody
+            parseJspBody( parent, bodyType );
+            reader.skipSpaces();
+            if( !reader.matchesETag( tag ) ) {
+                err.jspError(reader.mark(), "jsp.error.unterminated", 
+                    "&lt;" + tag );
+            }
+            
+            result = true;
+        }
+        else if( result && !reader.matchesETag( tag ) ) {
+            // If we have <jsp:attribute> but something other than
+            // <jsp:body> or the end tag, translation error.
+            err.jspError(reader.mark(), "jsp.error.jspbody.required", 
+                "&lt;" + tag );
+        }
+        
+        return result;
+    }
+
+    /*
+     * Params ::=  `>' S?
+     *              (   ( `<jsp:body>'
+     *                    ( ( S? Param+ S? `</jsp:body>' )
+     *                      | <TRANSLATION_ERROR>
+     *                    )
+     *                  )
+     *                | Param+
+     *              )
+     *              '</jsp:params>'
+     */
+    private void parseJspParams(Node parent) throws JasperException {
+	Node jspParamsNode = new Node.ParamsAction(start, parent);
+	parseOptionalBody(jspParamsNode, "jsp:params",
+			  JAVAX_BODY_CONTENT_PARAM );
+    }
+
+    /*
+     * Fallback ::=   '/>'
+     *               | ( `>' S? `<jsp:body>'
+     *                   (   ( S?
+     *                         ( Char* - ( Char* `</jsp:body>' ) )
+     *                         `</jsp:body>' S?
+     *                       )
+     *                     | <TRANSLATION_ERROR>
+     *                   )
+     *                   `</jsp:fallback>'
+     *                 )
+     *               | ( '>'
+     *                   ( Char* - ( Char* '</jsp:fallback>' ) )
+     *                   '</jsp:fallback>'
+     *                 )
+     */
+    private void parseFallBack(Node parent) throws JasperException {
+	Node fallBackNode = new Node.FallBackAction(start, parent);
+	parseOptionalBody(fallBackNode, "jsp:fallback", 
+			  JAVAX_BODY_CONTENT_TEMPLATE_TEXT);
+    }
+
+    /*
+     * For Plugin:
+     * StdActionContent ::= Attributes PluginBody
+     *
+     * PluginBody ::=   EmptyBody 
+     *                | ( '>' S? ( '<jsp:attribute' NamedAttributes )?
+     *                    '<jsp:body'
+     *                    ( JspBodyPluginTags | <TRANSLATION_ERROR> )
+     *                    S? ETag
+     *                  )
+     *                | ( '>' S? PluginTags ETag )
+     *
+     * EmptyBody ::=   '/>'
+     *               | ( '>' ETag )
+     *               | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
+     *
+     */
+    private void parsePlugin(Node parent) throws JasperException {
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+        
+	Node pluginNode = new Node.PlugIn(attrs, start, parent);
+        
+        parseOptionalBody( pluginNode, "jsp:plugin", 
+            JAVAX_BODY_CONTENT_PLUGIN );
+    }
+
+    /*
+     * PluginTags ::= ( '<jsp:params' Params S? )?
+     *                ( '<jsp:fallback' Fallback? S? )?
+     */
+    private void parsePluginTags( Node parent ) throws JasperException {
+        reader.skipSpaces();
+        
+        if( reader.matches( "<jsp:params" ) ) {
+            parseJspParams( parent );
+            reader.skipSpaces();
+        }
+        
+        if( reader.matches( "<jsp:fallback" ) ) {
+            parseFallBack( parent );
+            reader.skipSpaces();
+        }
+    }
+        
+    /*
+     * StandardAction ::=   'include'       StdActionContent
+     *                    | 'forward'       StdActionContent
+     *                    | 'invoke'        StdActionContent
+     *                    | 'doBody'        StdActionContent
+     *                    | 'getProperty'   StdActionContent
+     *                    | 'setProperty'   StdActionContent
+     *                    | 'useBean'       StdActionContent
+     *                    | 'plugin'        StdActionContent
+     *                    | 'element'       StdActionContent
+     */
+    private void parseStandardAction(Node parent) throws JasperException {
+	Mark start = reader.mark();
+
+	if (reader.matches(INCLUDE_ACTION)) {
+	    parseInclude(parent);
+	} else if (reader.matches(FORWARD_ACTION)) {
+	    parseForward(parent);
+	} else if (reader.matches(INVOKE_ACTION)) {
+	    if (!isTagFile) {
+		err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
+			     "&lt;jsp:invoke");
+	    }
+	    parseInvoke(parent);
+	} else if (reader.matches(DOBODY_ACTION)) {
+	    if (!isTagFile) {
+		err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
+			     "&lt;jsp:doBody");
+	    }
+	    parseDoBody(parent);
+	} else if (reader.matches(GET_PROPERTY_ACTION)) {
+	    parseGetProperty(parent);
+	} else if (reader.matches(SET_PROPERTY_ACTION)) {
+	    parseSetProperty(parent);
+	} else if (reader.matches(USE_BEAN_ACTION)) {
+	    parseUseBean(parent);
+	} else if (reader.matches(PLUGIN_ACTION)) {
+	    parsePlugin(parent);
+	} else if (reader.matches(ELEMENT_ACTION)) {
+	    parseElement(parent);
+	} else if (reader.matches(ATTRIBUTE_ACTION)) {
+	    err.jspError(start, "jsp.error.namedAttribute.invalidUse");
+	} else if (reader.matches(BODY_ACTION)) {
+	    err.jspError(start, "jsp.error.jspbody.invalidUse");
+	} else if (reader.matches(FALLBACK_ACTION)) {
+	    err.jspError(start, "jsp.error.fallback.invalidUse");
+	} else if (reader.matches(PARAMS_ACTION)) {
+	    err.jspError(start, "jsp.error.params.invalidUse");
+	} else if (reader.matches(PARAM_ACTION)) {
+	    err.jspError(start, "jsp.error.param.invalidUse");
+	} else if (reader.matches(OUTPUT_ACTION)) {
+	    err.jspError(start, "jsp.error.jspoutput.invalidUse");
+	} else {
+	    err.jspError(start, "jsp.error.badStandardAction");
+	}
+    }
+
+    /*
+     * # '<' CustomAction CustomActionBody
+     *
+     * CustomAction ::= TagPrefix ':' CustomActionName
+     *
+     * TagPrefix ::= Name
+     *
+     * CustomActionName ::= Name
+     *
+     * CustomActionBody ::=   ( Attributes CustomActionEnd )
+     *                      | <TRANSLATION_ERROR>
+     *
+     * Attributes ::= ( S Attribute )* S?
+     *
+     * CustomActionEnd ::=   CustomActionTagDependent
+     *                     | CustomActionJSPContent
+     *                     | CustomActionScriptlessContent
+     *
+     * CustomActionTagDependent ::= TagDependentOptionalBody
+     *
+     * CustomActionJSPContent ::= OptionalBody
+     *
+     * CustomActionScriptlessContent ::= ScriptlessOptionalBody
+     */
+    private boolean parseCustomTag(Node parent) throws JasperException {
+
+	if (reader.peekChar() != '<') {
+	    return false;
+	}
+
+        // Parse 'CustomAction' production (tag prefix and custom action name)
+	reader.nextChar();	// skip '<'
+	String tagName = reader.parseToken(false);
+	int i = tagName.indexOf(':');
+	if (i == -1) {
+	    reader.reset(start);
+	    return false;
+	}
+
+	String prefix = tagName.substring(0, i);
+	String shortTagName = tagName.substring(i+1);
+
+	// Check if this is a user-defined tag.
+	String uri = pageInfo.getURI(prefix);
+        if (uri == null) {
+	    reader.reset(start);
+            // Remember the prefix for later error checking
+            pageInfo.putNonCustomTagPrefix(prefix, reader.mark());
+	    return false;
+	}
+
+        TagLibraryInfo tagLibInfo = pageInfo.getTaglib(uri);
+	TagInfo tagInfo = tagLibInfo.getTag(shortTagName);
+	TagFileInfo tagFileInfo = tagLibInfo.getTagFile(shortTagName);
+	if (tagInfo == null && tagFileInfo == null) {
+	    err.jspError(start, "jsp.error.bad_tag", shortTagName, prefix);
+	}
+	Class tagHandlerClass = null;
+	if (tagInfo != null) {
+	    // Must be a classic tag, load it here.
+	    // tag files will be loaded later, in TagFileProcessor
+	    String handlerClassName = tagInfo.getTagClassName();
+	    try {
+	        tagHandlerClass = ctxt.getClassLoader().loadClass(handlerClassName);
+	    } catch (Exception e) {
+	        err.jspError(start, "jsp.error.loadclass.taghandler",
+			     handlerClassName, tagName);
+	    }
+	}
+
+        // Parse 'CustomActionBody' production:
+        // At this point we are committed - if anything fails, we produce
+        // a translation error.
+
+        // Parse 'Attributes' production:
+	Attributes attrs = parseAttributes();
+	reader.skipSpaces();
+	
+        // Parse 'CustomActionEnd' production:
+	if (reader.matches("/>")) {
+	    if (tagInfo != null) {
+		new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
+				   start, parent, tagInfo, tagHandlerClass);
+	    } else {
+		new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
+				   start, parent, tagFileInfo);
+	    }
+	    return true;
+	}
+	
+        // Now we parse one of 'CustomActionTagDependent', 
+        // 'CustomActionJSPContent', or 'CustomActionScriptlessContent'.
+        // depending on body-content in TLD.
+
+	// Looking for a body, it still can be empty; but if there is a
+	// a tag body, its syntax would be dependent on the type of
+	// body content declared in the TLD.
+	String bc;
+	if (tagInfo != null) {
+	    bc = tagInfo.getBodyContent();
+	} else {
+	    bc = tagFileInfo.getTagInfo().getBodyContent();
+	}
+
+	Node tagNode = null;
+	if (tagInfo != null) {
+	    tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
+					 attrs, start, parent, tagInfo,
+					 tagHandlerClass);
+	} else {
+	    tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
+					 attrs, start, parent, tagFileInfo);
+	}
+
+	parseOptionalBody( tagNode, tagName, bc );
+
+	return true;
+    }
+
+    /*
+     * Parse for a template text string until '<' or "${" is encountered, 
+     * recognizing escape sequences "\%" and "\$".
+     */
+    private void parseTemplateText(Node parent) throws JasperException {
+
+	if (!reader.hasMoreInput())
+	    return;
+
+	CharArrayWriter ttext = new CharArrayWriter();
+	// Output the first character
+	int ch = reader.nextChar();
+        if (ch == '\\') {
+            reader.pushChar();
+        } else {
+            ttext.write(ch);
+        }
+
+	while (reader.hasMoreInput()) {
+	    ch = reader.nextChar();
+	    if (ch == '<') {
+                reader.pushChar();
+                break;
+            }
+	    else if( ch == '$' ) {
+		if (!reader.hasMoreInput()) {
+		    ttext.write('$');
+		    break;
+                }
+		ch = reader.nextChar();
+		if (ch == '{') {
+		    reader.pushChar();
+		    reader.pushChar();
+		    break;
+		}
+		ttext.write('$');
+		reader.pushChar();
+		continue;
+	    }
+	    else if (ch == '\\') {
+		if (!reader.hasMoreInput()) {
+		    ttext.write('\\');
+		    break;
+		}
+                char next = (char)reader.peekChar();
+                // Looking for \% or \$
+                // TODO: only recognize \$ if isELIgnored is false, but since
+                // it can be set in a page directive, it cannot be determined
+                // here.  Argh!
+                if (next == '%' || next == '$') {
+                    ch = reader.nextChar();
+                }
+	    }
+	    ttext.write(ch);
+	}
+	new Node.TemplateText(ttext.toString(), start, parent);
+    }
+    
+    /*
+     * XMLTemplateText ::=   ( S? '/>' )
+     *                     | ( S? '>'
+     *                         ( ( Char* - ( Char* ( '<' | '${' ) ) )
+     *                           ( '${' ELExpressionBody )?
+     *                           CDSect?
+     *                         )* ETag
+     *                       )
+     *                     | <TRANSLATION_ERROR>
+     */
+    private void parseXMLTemplateText(Node parent) throws JasperException {
+        reader.skipSpaces();
+        if( !reader.matches( "/>" ) ) {
+            if( !reader.matches( ">" ) ) {
+                err.jspError(start, "jsp.error.unterminated",
+                    "&lt;jsp:text&gt;" );
+            }
+            CharArrayWriter ttext = new CharArrayWriter();
+            while (reader.hasMoreInput()) {
+        	int ch = reader.nextChar();
+                if( ch == '<' ) {
+                    // Check for <![CDATA[
+                    if (!reader.matches("![CDATA[")) {
+                        break;
+                    }
+                    start = reader.mark();
+                    Mark stop = reader.skipUntil("]]>");
+                    if (stop == null) {
+                        err.jspError(start, "jsp.error.unterminated", "CDATA");
+                    }
+                    String text = reader.getText(start, stop);
+                    ttext.write(text, 0, text.length());
+                }
+                else if( ch == '\\') {
+                    if (!reader.hasMoreInput()) {
+                        ttext.write('\\');
+                        break;
+		    }
+                    ch = reader.nextChar();
+                    if (ch != '$' ) {
+                        ttext.write('\\');
+                    }
+                    ttext.write(ch);
+                }
+                else if( ch == '$' ) {
+                    if (!reader.hasMoreInput()) {
+                        ttext.write('$');
+                        break;
+                    }
+                    ch = reader.nextChar();
+                    if (ch != '{') {
+                        ttext.write('$');
+                        reader.pushChar();
+                        continue;
+                    }
+                    // Create a template text node
+                    new Node.TemplateText( ttext.toString(), start, parent);
+
+                    // Mark and parse the EL expression and create its node:
+                    start = reader.mark();
+                    parseELExpression(parent);
+
+                    start = reader.mark();
+                    ttext = new CharArrayWriter();
+                }
+                else {
+                    ttext.write( ch );
+                }
+            }
+
+            new Node.TemplateText( ttext.toString(), start, parent );
+
+	    if (! reader.hasMoreInput()) {
+                err.jspError( start, "jsp.error.unterminated",
+                    "&lt;jsp:text&gt;" );
+	    } else if( !reader.matchesETagWithoutLessThan( "jsp:text" ) ) {
+                err.jspError( start, "jsp.error.jsptext.badcontent");
+            }
+        }
+    }
+
+    /*
+     * AllBody ::=       ( '<%--'              JSPCommentBody     )
+     *                 | ( '<%@'               DirectiveBody      )
+     *                 | ( '<jsp:directive.'   XMLDirectiveBody   )
+     *                 | ( '<%!'               DeclarationBody    )
+     *                 | ( '<jsp:declaration'  XMLDeclarationBody )
+     *                 | ( '<%='               ExpressionBody     )
+     *                 | ( '<jsp:expression'   XMLExpressionBody  )
+     *                 | ( '${'                ELExpressionBody   )
+     *                 | ( '<%'                ScriptletBody      )
+     *                 | ( '<jsp:scriptlet'    XMLScriptletBody   )
+     *                 | ( '<jsp:text'         XMLTemplateText    )
+     *                 | ( '<jsp:'             StandardAction     )
+     *                 | ( '<'                 CustomAction
+     *                                         CustomActionBody   )
+     *	               | TemplateText
+     */
+    private void parseElements(Node parent) 
+        throws JasperException 
+    {
+        if( scriptlessCount > 0 ) {
+            // vc: ScriptlessBody
+            // We must follow the ScriptlessBody production if one of
+            // our parents is ScriptlessBody.
+            parseElementsScriptless( parent );
+            return;
+        }
+        
+	start = reader.mark();
+	if (reader.matches("<%--")) {
+	    parseComment(parent);
+	} else if (reader.matches("<%@")) {
+	    parseDirective(parent);
+        } else if (reader.matches("<jsp:directive.")) {
+            parseXMLDirective(parent);
+	} else if (reader.matches("<%!")) {
+	    parseDeclaration(parent);
+        } else if (reader.matches("<jsp:declaration")) {
+            parseXMLDeclaration(parent);
+        } else if (reader.matches("<%=")) {
+            parseExpression(parent);
+        } else if (reader.matches("<jsp:expression")) {
+            parseXMLExpression(parent);
+	} else if (reader.matches("<%")) {
+	    parseScriptlet(parent);
+        } else if (reader.matches("<jsp:scriptlet")) {
+            parseXMLScriptlet(parent);
+        } else if (reader.matches("<jsp:text")) {
+            parseXMLTemplateText(parent);
+        } else if (reader.matches("${")) {
+            parseELExpression(parent);
+	} else if (reader.matches("<jsp:")) {
+	    parseStandardAction(parent);
+	} else if (!parseCustomTag(parent)) {
+            checkUnbalancedEndTag();
+            parseTemplateText(parent);
+	}
+    }
+
+    /*
+     * ScriptlessBody ::=  ( '<%--'              JSPCommentBody      )
+     *                   | ( '<%@'               DirectiveBody       )
+     *                   | ( '<jsp:directive.'   XMLDirectiveBody    )
+     *                   | ( '<%!'               <TRANSLATION_ERROR> )
+     *                   | ( '<jsp:declaration'  <TRANSLATION_ERROR> )
+     *                   | ( '<%='               <TRANSLATION_ERROR> )
+     *                   | ( '<jsp:expression'   <TRANSLATION_ERROR> )
+     *                   | ( '<%'                <TRANSLATION_ERROR> )
+     *                   | ( '<jsp:scriptlet'    <TRANSLATION_ERROR> )
+     *                   | ( '<jsp:text'         XMLTemplateText     )
+     *                   | ( '${'                ELExpressionBody    )
+     *                   | ( '<jsp:'             StandardAction      )
+     *                   | ( '<'                 CustomAction
+     *                                           CustomActionBody    )
+     *                   | TemplateText
+     */
+    private void parseElementsScriptless(Node parent) 
+        throws JasperException 
+    {
+        // Keep track of how many scriptless nodes we've encountered
+        // so we know whether our child nodes are forced scriptless
+        scriptlessCount++;
+        
+	start = reader.mark();
+	if (reader.matches("<%--")) {
+	    parseComment(parent);
+	} else if (reader.matches("<%@")) {
+	    parseDirective(parent);
+        } else if (reader.matches("<jsp:directive.")) {
+            parseXMLDirective(parent);
+	} else if (reader.matches("<%!")) {
+	    err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
+        } else if (reader.matches("<jsp:declaration")) {
+            err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
+	} else if (reader.matches("<%=")) {
+	    err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
+        } else if (reader.matches("<jsp:expression")) {
+            err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
+	} else if (reader.matches("<%")) {
+	    err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
+        } else if (reader.matches("<jsp:scriptlet")) {
+            err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
+        } else if (reader.matches("<jsp:text")) {
+            parseXMLTemplateText(parent);
+	} else if (reader.matches("${")) {
+	    parseELExpression(parent);
+	} else if (reader.matches("<jsp:")) {
+	    parseStandardAction(parent);
+	} else if (!parseCustomTag(parent)) {
+            checkUnbalancedEndTag();
+            parseTemplateText(parent);
+	}
+        
+        scriptlessCount--;
+    }
+    
+    /*
+     * TemplateTextBody ::=   ( '<%--'              JSPCommentBody      )
+     *                      | ( '<%@'               DirectiveBody       )
+     *                      | ( '<jsp:directive.'   XMLDirectiveBody    )
+     *                      | ( '<%!'               <TRANSLATION_ERROR> )
+     *                      | ( '<jsp:declaration'  <TRANSLATION_ERROR> )
+     *                      | ( '<%='               <TRANSLATION_ERROR> )
+     *                      | ( '<jsp:expression'   <TRANSLATION_ERROR> )
+     *                      | ( '<%'                <TRANSLATION_ERROR> )
+     *                      | ( '<jsp:scriptlet'    <TRANSLATION_ERROR> )
+     *                      | ( '<jsp:text'         <TRANSLATION_ERROR> )
+     *                      | ( '${'                <TRANSLATION_ERROR> )
+     *                      | ( '<jsp:'             <TRANSLATION_ERROR> )
+     *                      | TemplateText
+     */
+    private void parseElementsTemplateText(Node parent)
+        throws JasperException
+    {
+        start = reader.mark();
+        if (reader.matches("<%--")) {
+            parseComment(parent);
+        } else if (reader.matches("<%@")) {
+            parseDirective(parent);
+        } else if (reader.matches("<jsp:directive.")) {
+            parseXMLDirective(parent);
+        } else if (reader.matches("<%!")) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"Declarations" );
+        } else if (reader.matches("<jsp:declaration")) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"Declarations" );
+        } else if (reader.matches("<%=")) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"Expressions" );
+        } else if (reader.matches("<jsp:expression")) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"Expressions" );
+        } else if (reader.matches("<%")) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"Scriptlets" );
+        } else if (reader.matches("<jsp:scriptlet")) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"Scriptlets" );
+        } else if (reader.matches("<jsp:text")) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"&lt;jsp:text" );
+        } else if (reader.matches("${")) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"Expression language" );
+        } else if (reader.matches("<jsp:")) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"Standard actions" );
+	} else if (parseCustomTag(parent)) {
+            err.jspError( reader.mark(), "jsp.error.not.in.template",
+		"Custom actions" );
+	} else {
+            checkUnbalancedEndTag();
+            parseTemplateText(parent);
+	}
+    }
+
+    /*
+     * Flag as error if an unbalanced end tag appears by itself.
+     */
+    private void checkUnbalancedEndTag() throws JasperException {
+
+        if (!reader.matches("</")) {
+            return;
+        }
+
+        // Check for unbalanced standard actions
+        if (reader.matches("jsp:")) {
+            err.jspError(start, "jsp.error.unbalanced.endtag", "jsp:");
+        }
+
+        // Check for unbalanced custom actions
+        String tagName = reader.parseToken(false);
+        int i = tagName.indexOf(':');
+        if (i == -1 || pageInfo.getURI(tagName.substring(0, i)) == null) {
+            reader.reset(start);
+            return;
+        }
+
+        err.jspError(start, "jsp.error.unbalanced.endtag", tagName);
+    }
+
+    /**
+     * TagDependentBody := 
+     */
+    private void parseTagDependentBody(Node parent, String tag)
+		throws JasperException{
+	Mark bodyStart = reader.mark();
+	Mark bodyEnd = reader.skipUntilETag(tag);
+	if (bodyEnd == null) {
+	    err.jspError(start, "jsp.error.unterminated", "&lt;"+tag );
+	}
+	new Node.TemplateText(reader.getText(bodyStart, bodyEnd), bodyStart,
+			      parent);
+    }
+
+    /*
+     * Parses jsp:body action.
+     */
+    private void parseJspBody(Node parent, String bodyType) 
+        throws JasperException 
+    {
+        Mark start = reader.mark();
+	Node bodyNode = new Node.JspBody(start, parent);
+
+	reader.skipSpaces();
+	if (!reader.matches("/>")) {
+	    if (!reader.matches(">")) {
+		err.jspError(start, "jsp.error.unterminated",
+			     "&lt;jsp:body");
+	    }
+	    parseBody( bodyNode, "jsp:body", bodyType );
+	}
+    }
+
+    /*
+     * Parse the body as JSP content.
+     * @param tag The name of the tag whose end tag would terminate the body
+     * @param bodyType One of the TagInfo body types
+     */
+    private void parseBody(Node parent, String tag, String bodyType) 
+        throws JasperException 
+    {
+        if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_TAG_DEPENDENT ) ) {
+            parseTagDependentBody( parent, tag );
+        }
+        else if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_EMPTY ) ) {
+            if( !reader.matchesETag( tag ) ) {
+		err.jspError(start, "jasper.error.emptybodycontent.nonempty",
+			     tag);
+            }
+        }
+        else if( bodyType == JAVAX_BODY_CONTENT_PLUGIN ) {
+            // (note the == since we won't recognize JAVAX_* 
+            // from outside this module).
+            parsePluginTags(parent);
+            if( !reader.matchesETag( tag ) ) {
+                err.jspError( reader.mark(), "jsp.error.unterminated",
+                    "&lt;" + tag  );
+            }
+        }
+        else if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_JSP ) ||
+            bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_SCRIPTLESS ) ||
+            (bodyType == JAVAX_BODY_CONTENT_PARAM) ||
+            (bodyType == JAVAX_BODY_CONTENT_TEMPLATE_TEXT) )
+        {
+            while (reader.hasMoreInput()) {
+                if (reader.matchesETag(tag)) {
+                    return;
+                }
+                
+                // Check for nested jsp:body or jsp:attribute
+                if (tag.equals("jsp:body") || tag.equals("jsp:attribute")) {
+                    if (reader.matches("<jsp:attribute")) {
+                        err.jspError(reader.mark(), "jsp.error.nested.jspattribute");
+                    }
+                    else if (reader.matches("<jsp:body")) {
+                        err.jspError(reader.mark(), "jsp.error.nested.jspbody");
+                    }
+                }
+
+                if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_JSP ) ) {
+                    parseElements( parent );
+                }
+                else if( bodyType.equalsIgnoreCase( 
+                    TagInfo.BODY_CONTENT_SCRIPTLESS ) ) 
+                {
+                    parseElementsScriptless( parent );
+                }
+                else if( bodyType == JAVAX_BODY_CONTENT_PARAM ) {
+                    // (note the == since we won't recognize JAVAX_* 
+                    // from outside this module).
+                    reader.skipSpaces();
+                    parseParam( parent );
+                }
+		else if (bodyType == JAVAX_BODY_CONTENT_TEMPLATE_TEXT) {
+		    parseElementsTemplateText(parent);
+		}
+            }
+            err.jspError(start, "jsp.error.unterminated", "&lt;"+tag );
+        }
+        else {
+	    err.jspError(start, "jasper.error.bad.bodycontent.type");
+        }
+    }
+
+    /*
+     * Parses named attributes.
+     */
+    private void parseNamedAttributes(Node parent) throws JasperException {
+        do {
+            Mark start = reader.mark();
+            Attributes attrs = parseAttributes();
+            Node.NamedAttribute namedAttributeNode =
+                new Node.NamedAttribute( attrs, start, parent );
+
+            reader.skipSpaces();
+	    if (!reader.matches("/>")) {
+		if (!reader.matches(">")) {
+		    err.jspError(start, "jsp.error.unterminated",
+				 "&lt;jsp:attribute");
+		}
+                if (namedAttributeNode.isTrim()) {
+                    reader.skipSpaces();
+                }
+                parseBody(namedAttributeNode, "jsp:attribute", 
+			  getAttributeBodyType(parent,
+					       attrs.getValue("name")));
+                if (namedAttributeNode.isTrim()) {
+                    Node.Nodes subElems = namedAttributeNode.getBody();
+		    if (subElems != null) {
+			Node lastNode = subElems.getNode(subElems.size() - 1);
+			if (lastNode instanceof Node.TemplateText) {
+			    ((Node.TemplateText)lastNode).rtrim();
+			}
+		    }
+                }
+            }
+            reader.skipSpaces();
+        } while( reader.matches( "<jsp:attribute" ) );
+    }
+
+    /**
+     * Determine the body type of <jsp:attribute> from the enclosing node
+     */
+    private String getAttributeBodyType(Node n, String name) {
+
+	if (n instanceof Node.CustomTag) {
+	    TagInfo tagInfo = ((Node.CustomTag)n).getTagInfo();
+	    TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
+	    for (int i=0; i<tldAttrs.length; i++) {
+		if (name.equals(tldAttrs[i].getName())) {
+		    if (tldAttrs[i].isFragment()) {
+		        return TagInfo.BODY_CONTENT_SCRIPTLESS;
+		    }
+		    if (tldAttrs[i].canBeRequestTime()) {
+		        return TagInfo.BODY_CONTENT_JSP;
+		    }
+		}
+	    }
+	    if (tagInfo.hasDynamicAttributes()) {
+		return TagInfo.BODY_CONTENT_JSP;
+	    }
+	} else if (n instanceof Node.IncludeAction) {
+	    if ("page".equals(name)) {
+		return TagInfo.BODY_CONTENT_JSP;
+	    }
+	} else if (n instanceof Node.ForwardAction) {
+	    if ("page".equals(name)) {
+		return TagInfo.BODY_CONTENT_JSP;
+	    }
+	} else if (n instanceof Node.SetProperty) {
+	    if ("value".equals(name)) {
+		return TagInfo.BODY_CONTENT_JSP;
+	    }
+	} else if (n instanceof Node.UseBean) {
+	    if ("beanName".equals(name)) {
+		return TagInfo.BODY_CONTENT_JSP;
+	    }
+	} else if (n instanceof Node.PlugIn) {
+	    if ("width".equals(name) || "height".equals(name)) {
+		return TagInfo.BODY_CONTENT_JSP;
+	    }
+	} else if (n instanceof Node.ParamAction) {
+	    if ("value".equals(name)) {
+		return TagInfo.BODY_CONTENT_JSP;
+	    }
+	} else if (n instanceof Node.JspElement) {
+	    return TagInfo.BODY_CONTENT_JSP;
+	}
+
+	return JAVAX_BODY_CONTENT_TEMPLATE_TEXT;
+    }
+
+    private void parseTagFileDirectives(Node parent)
+        throws JasperException
+    {
+	reader.setSingleFile(true);
+	reader.skipUntil("<");
+        while (reader.hasMoreInput()) {
+            start = reader.mark();
+            if (reader.matches("%--")) {
+                parseComment(parent);
+            } else if (reader.matches("%@")) {
+                parseDirective(parent);
+            } else if (reader.matches("jsp:directive.")) {
+                parseXMLDirective(parent);
+            }
+	    reader.skipUntil("<");
+	}
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/ParserController.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/ParserController.java
new file mode 100644
index 0000000..d84e8cc
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/ParserController.java
@@ -0,0 +1,582 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.util.Stack;
+import java.util.jar.JarFile;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.xmlparser.XMLEncodingDetector;
+import org.xml.sax.Attributes;
+
+/**
+ * Controller for the parsing of a JSP page.
+ * <p>
+ * The same ParserController instance is used for a JSP page and any JSP
+ * segments included by it (via an include directive), where each segment may
+ * be provided in standard or XML syntax. This class selects and invokes the
+ * appropriate parser for the JSP page and its included segments.
+ *
+ * @author Pierre Delisle
+ * @author Jan Luehe
+ */
+class ParserController implements TagConstants {
+
+    private static final String CHARSET = "charset=";
+
+    private JspCompilationContext ctxt;
+    private Compiler compiler;
+    private ErrorDispatcher err;
+
+    /*
+     * Indicates the syntax (XML or standard) of the file being processed
+     */
+    private boolean isXml;
+
+    /*
+     * A stack to keep track of the 'current base directory'
+     * for include directives that refer to relative paths.
+     */
+    private Stack baseDirStack = new Stack();
+    
+    private boolean isEncodingSpecifiedInProlog;
+
+    private String sourceEnc;
+
+    private boolean isDefaultPageEncoding;
+    private boolean isTagFile;
+    private boolean directiveOnly;
+
+    /*
+     * Constructor
+     */
+    public ParserController(JspCompilationContext ctxt, Compiler compiler) {
+        this.ctxt = ctxt; 
+	this.compiler = compiler;
+	this.err = compiler.getErrorDispatcher();
+    }
+
+    public JspCompilationContext getJspCompilationContext () {
+	return ctxt;
+    }
+
+    public Compiler getCompiler () {
+	return compiler;
+    }
+
+    /**
+     * Parses a JSP page or tag file. This is invoked by the compiler.
+     *
+     * @param inFileName The path to the JSP page or tag file to be parsed.
+     */
+    public Node.Nodes parse(String inFileName)
+	        throws FileNotFoundException, JasperException, IOException {
+	// If we're parsing a packaged tag file or a resource included by it
+	// (using an include directive), ctxt.getTagFileJar() returns the 
+	// JAR file from which to read the tag file or included resource,
+	// respectively.
+        isTagFile = ctxt.isTagFile();
+        directiveOnly = false;
+        return doParse(inFileName, null, ctxt.getTagFileJarUrl());
+    }
+
+    /**
+     * Processes an include directive with the given path.
+     *
+     * @param inFileName The path to the resource to be included.
+     * @param parent The parent node of the include directive.
+     * @param jarFile The JAR file from which to read the included resource,
+     * or null of the included resource is to be read from the filesystem
+     */
+    public Node.Nodes parse(String inFileName, Node parent,
+			    URL jarFileUrl)
+	        throws FileNotFoundException, JasperException, IOException {
+        // For files that are statically included, isTagfile and directiveOnly
+        // remain unchanged.
+        return doParse(inFileName, parent, jarFileUrl);
+    }
+
+    /**
+     * Extracts tag file directive information from the tag file with the
+     * given name.
+     *
+     * This is invoked by the compiler 
+     *
+     * @param inFileName The name of the tag file to be parsed.
+     */
+    public Node.Nodes parseTagFileDirectives(String inFileName)
+	        throws FileNotFoundException, JasperException, IOException {
+        boolean isTagFileSave = isTagFile;
+        boolean directiveOnlySave = directiveOnly;
+        isTagFile = true;
+        directiveOnly = true;
+        Node.Nodes page = doParse(inFileName, null,
+                             (URL) ctxt.getTagFileJarUrls().get(inFileName));
+        directiveOnly = directiveOnlySave;
+        isTagFile = isTagFileSave;
+        return page;
+    }
+
+    /**
+     * Parses the JSP page or tag file with the given path name.
+     *
+     * @param inFileName The name of the JSP page or tag file to be parsed.
+     * @param parent The parent node (non-null when processing an include
+     * directive)
+     * @param isTagFile true if file to be parsed is tag file, and false if it
+     * is a regular JSP page
+     * @param directivesOnly true if the file to be parsed is a tag file and
+     * we are only interested in the directives needed for constructing a
+     * TagFileInfo.
+     * @param jarFile The JAR file from which to read the JSP page or tag file,
+     * or null if the JSP page or tag file is to be read from the filesystem
+     */
+    private Node.Nodes doParse(String inFileName,
+                               Node parent,
+                               URL jarFileUrl)
+	        throws FileNotFoundException, JasperException, IOException {
+
+	Node.Nodes parsedPage = null;
+	isEncodingSpecifiedInProlog = false;
+	isDefaultPageEncoding = false;
+
+	JarFile jarFile = getJarFile(jarFileUrl);
+	String absFileName = resolveFileName(inFileName);
+	String jspConfigPageEnc = getJspConfigPageEncoding(absFileName);
+
+	// Figure out what type of JSP document and encoding type we are
+	// dealing with
+	determineSyntaxAndEncoding(absFileName, jarFile, jspConfigPageEnc);
+
+	if (parent != null) {
+	    // Included resource, add to dependent list
+	    compiler.getPageInfo().addDependant(absFileName);
+	}
+
+	if (isXml && isEncodingSpecifiedInProlog) {
+	    /*
+	     * Make sure the encoding explicitly specified in the XML
+	     * prolog (if any) matches that in the JSP config element
+	     * (if any), treating "UTF-16", "UTF-16BE", and "UTF-16LE" as
+	     * identical.
+	     */
+	    if (jspConfigPageEnc != null && !jspConfigPageEnc.equals(sourceEnc)
+		        && (!jspConfigPageEnc.startsWith("UTF-16")
+			    || !sourceEnc.startsWith("UTF-16"))) {
+		err.jspError("jsp.error.prolog_config_encoding_mismatch",
+			     sourceEnc, jspConfigPageEnc);
+	    }
+	}
+
+	// Dispatch to the appropriate parser
+	if (isXml) {
+	    // JSP document (XML syntax)
+            // InputStream for jspx page is created and properly closed in
+            // JspDocumentParser.
+            parsedPage = JspDocumentParser.parse(this, absFileName,
+                                                 jarFile, parent,
+                                                 isTagFile, directiveOnly,
+                                                 sourceEnc,
+                                                 jspConfigPageEnc,
+                                                 isEncodingSpecifiedInProlog);
+	} else {
+	    // Standard syntax
+	    InputStreamReader inStreamReader = null;
+	    try {
+		inStreamReader = JspUtil.getReader(absFileName, sourceEnc,
+						   jarFile, ctxt, err);
+		JspReader jspReader = new JspReader(ctxt, absFileName,
+						    sourceEnc, inStreamReader,
+						    err);
+                parsedPage = Parser.parse(this, jspReader, parent, isTagFile,
+					  directiveOnly, jarFileUrl,
+					  sourceEnc, jspConfigPageEnc,
+					  isDefaultPageEncoding);
+            } finally {
+		if (inStreamReader != null) {
+		    try {
+			inStreamReader.close();
+		    } catch (Exception any) {
+		    }
+		}
+	    }
+	}
+
+	if (jarFile != null) {
+	    try {
+		jarFile.close();
+	    } catch (Throwable t) {}
+	}
+
+	baseDirStack.pop();
+
+	return parsedPage;
+    }
+
+    /*
+     * Checks to see if the given URI is matched by a URL pattern specified in
+     * a jsp-property-group in web.xml, and if so, returns the value of the
+     * <page-encoding> element.
+     *
+     * @param absFileName The URI to match
+     *
+     * @return The value of the <page-encoding> attribute of the 
+     * jsp-property-group with matching URL pattern
+     */
+    private String getJspConfigPageEncoding(String absFileName)
+            throws JasperException {
+
+	JspConfig jspConfig = ctxt.getOptions().getJspConfig();
+	JspConfig.JspProperty jspProperty
+	    = jspConfig.findJspProperty(absFileName);
+	return jspProperty.getPageEncoding();
+    }
+
+    /**
+     * Determines the syntax (standard or XML) and page encoding properties
+     * for the given file, and stores them in the 'isXml' and 'sourceEnc'
+     * instance variables, respectively.
+     */
+    private void determineSyntaxAndEncoding(String absFileName,
+					    JarFile jarFile,
+					    String jspConfigPageEnc)
+	        throws JasperException, IOException {
+
+	isXml = false;
+
+	/*
+	 * 'true' if the syntax (XML or standard) of the file is given
+	 * from external information: either via a JSP configuration element,
+	 * the ".jspx" suffix, or the enclosing file (for included resources)
+	 */
+	boolean isExternal = false;
+
+	/*
+	 * Indicates whether we need to revert from temporary usage of
+	 * "ISO-8859-1" back to "UTF-8"
+	 */
+	boolean revert = false;
+
+        JspConfig jspConfig = ctxt.getOptions().getJspConfig();
+        JspConfig.JspProperty jspProperty = jspConfig.findJspProperty(
+                                                                absFileName);
+        if (jspProperty.isXml() != null) {
+            // If <is-xml> is specified in a <jsp-property-group>, it is used.
+            isXml = JspUtil.booleanValue(jspProperty.isXml());
+	    isExternal = true;
+	} else if (absFileName.endsWith(".jspx")
+		   || absFileName.endsWith(".tagx")) {
+	    isXml = true;
+	    isExternal = true;
+	}
+	
+	if (isExternal && !isXml) {
+	    // JSP (standard) syntax. Use encoding specified in jsp-config
+	    // if provided.
+	    sourceEnc = jspConfigPageEnc;
+	    if (sourceEnc != null) {
+		return;
+	    }
+	    // We don't know the encoding
+	    sourceEnc = "ISO-8859-1";
+	} else {
+	    // XML syntax or unknown, (auto)detect encoding ...
+	    Object[] ret = XMLEncodingDetector.getEncoding(absFileName,
+							   jarFile, ctxt, err);
+	    sourceEnc = (String) ret[0];
+	    if (((Boolean) ret[1]).booleanValue()) {
+		isEncodingSpecifiedInProlog = true;
+	    }
+
+	    if (!isXml && sourceEnc.equals("UTF-8")) {
+		/*
+		 * We don't know if we're dealing with XML or standard syntax.
+		 * Therefore, we need to check to see if the page contains
+		 * a <jsp:root> element.
+		 *
+		 * We need to be careful, because the page may be encoded in
+		 * ISO-8859-1 (or something entirely different), and may
+		 * contain byte sequences that will cause a UTF-8 converter to
+		 * throw exceptions. 
+		 *
+		 * It is safe to use a source encoding of ISO-8859-1 in this
+		 * case, as there are no invalid byte sequences in ISO-8859-1,
+		 * and the byte/character sequences we're looking for (i.e.,
+		 * <jsp:root>) are identical in either encoding (both UTF-8
+		 * and ISO-8859-1 are extensions of ASCII).
+		 */
+		sourceEnc = "ISO-8859-1";
+		revert = true;
+	    }
+	}
+
+	if (isXml) {
+	    // (This implies 'isExternal' is TRUE.)
+	    // We know we're dealing with a JSP document (via JSP config or
+	    // ".jspx" suffix), so we're done.
+	    return;
+	}
+
+	/*
+	 * At this point, 'isExternal' or 'isXml' is FALSE.
+	 * Search for jsp:root action, in order to determine if we're dealing 
+	 * with XML or standard syntax (unless we already know what we're 
+	 * dealing with, i.e., when 'isExternal' is TRUE and 'isXml' is FALSE).
+	 * No check for XML prolog, since nothing prevents a page from
+	 * outputting XML and still using JSP syntax (in this case, the 
+	 * XML prolog is treated as template text).
+	 */
+	JspReader jspReader = null;
+	try {
+	    jspReader = new JspReader(ctxt, absFileName, sourceEnc, jarFile,
+				      err);
+	} catch (FileNotFoundException ex) {
+	    throw new JasperException(ex);
+	}
+        jspReader.setSingleFile(true);
+        Mark startMark = jspReader.mark();
+	if (!isExternal) {
+	    jspReader.reset(startMark);
+	    if (hasJspRoot(jspReader)) {
+	        isXml = true;
+		if (revert) sourceEnc = "UTF-8";
+		return;
+	    } else {
+	        isXml = false;
+	    }
+	}
+
+	/*
+	 * At this point, we know we're dealing with JSP syntax.
+	 * If an XML prolog is provided, it's treated as template text.
+	 * Determine the page encoding from the page directive, unless it's
+	 * specified via JSP config.
+	 */
+	sourceEnc = jspConfigPageEnc;
+	if (sourceEnc == null) {
+	    sourceEnc = getPageEncodingForJspSyntax(jspReader, startMark);
+	    if (sourceEnc == null) {
+		// Default to "ISO-8859-1" per JSP spec
+		sourceEnc = "ISO-8859-1";
+		isDefaultPageEncoding = true;
+	    }
+	}
+    }
+    
+    /*
+     * Determines page source encoding for page or tag file in JSP syntax,
+     * by reading (in this order) the value of the 'pageEncoding' page
+     * directive attribute, or the charset value of the 'contentType' page
+     * directive attribute.
+     *
+     * @return The page encoding, or null if not found
+     */
+    private String getPageEncodingForJspSyntax(JspReader jspReader,
+					       Mark startMark)
+	        throws JasperException {
+
+	String encoding = null;
+        String saveEncoding = null;
+
+        jspReader.reset(startMark);
+
+	/*
+	 * Determine page encoding from directive of the form <%@ page %>,
+	 * <%@ tag %>, <jsp:directive.page > or <jsp:directive.tag >.
+	 */
+        while (true) {
+            if (jspReader.skipUntil("<") == null) {
+                break;
+            }
+            // If this is a comment, skip until its end
+            if (jspReader.matches("%--")) {
+                if (jspReader.skipUntil("--%>") == null) {
+                    // error will be caught in Parser
+                    break;
+                }
+                continue;
+            }
+            boolean isDirective = jspReader.matches("%@");
+            if (isDirective) {
+	        jspReader.skipSpaces();
+            }
+            else {
+                isDirective = jspReader.matches("jsp:directive.");
+            }
+            if (!isDirective) {
+                continue;
+            }
+
+	    // compare for "tag ", so we don't match "taglib"
+	    if (jspReader.matches("tag ") || jspReader.matches("page")) {
+
+		jspReader.skipSpaces();
+                Attributes attrs = Parser.parseAttributes(this, jspReader);
+		encoding = getPageEncodingFromDirective(attrs, "pageEncoding");
+                if (encoding != null) {
+                    break;
+                }
+		encoding = getPageEncodingFromDirective(attrs, "contentType");
+                if (encoding != null) {
+                    saveEncoding = encoding;
+                }
+	    }
+	}
+
+        if (encoding == null) {
+            encoding = saveEncoding;
+        }
+
+	return encoding;
+    }
+
+    /*
+     * Scans the given attributes for the attribute with the given name,
+     * which is either 'pageEncoding' or 'contentType', and returns the
+     * specified page encoding.
+     *
+     * In the case of 'contentType', the page encoding is taken from the
+     * content type's 'charset' component.
+     *
+     * @param attrs The page directive attributes
+     * @param attrName The name of the attribute to search for (either
+     * 'pageEncoding' or 'contentType')
+     *
+     * @return The page encoding, or null
+     */
+    private String getPageEncodingFromDirective(Attributes attrs,
+                                                String attrName) {
+	String value = attrs.getValue(attrName);
+        if (attrName.equals("pageEncoding")) {
+            return value;
+        }
+
+        // attrName = contentType
+        String contentType = value;
+        String encoding = null;
+        if (contentType != null) {
+	    int loc = contentType.indexOf(CHARSET);
+	    if (loc != -1) {
+		encoding = contentType.substring(loc + CHARSET.length());
+	    }
+	}
+
+	return encoding;
+    }
+
+    /*
+     * Resolve the name of the file and update baseDirStack() to keep track of
+     * the current base directory for each included file.
+     * The 'root' file is always an 'absolute' path, so no need to put an
+     * initial value in the baseDirStack.
+     */
+    private String resolveFileName(String inFileName) {
+        String fileName = inFileName.replace('\\', '/');
+        boolean isAbsolute = fileName.startsWith("/");
+	fileName = isAbsolute ? fileName 
+            : (String) baseDirStack.peek() + fileName;
+	String baseDir = 
+	    fileName.substring(0, fileName.lastIndexOf("/") + 1);
+	baseDirStack.push(baseDir);
+	return fileName;
+    }
+
+    /*
+     * Checks to see if the given page contains, as its first element, a <root>
+     * element whose prefix is bound to the JSP namespace, as in:
+     *
+     * <wombat:root xmlns:wombat="http://java.sun.com/JSP/Page" version="1.2">
+     *   ...
+     * </wombat:root>
+     *
+     * @param reader The reader for this page
+     *
+     * @return true if this page contains a root element whose prefix is bound
+     * to the JSP namespace, and false otherwise
+     */
+    private boolean hasJspRoot(JspReader reader) throws JasperException {
+
+	// <prefix>:root must be the first element
+	Mark start = null;
+	while ((start = reader.skipUntil("<")) != null) {
+	    int c = reader.nextChar();
+	    if (c != '!' && c != '?') break;
+	}
+	if (start == null) {
+	    return false;
+	}
+	Mark stop = reader.skipUntil(":root");
+	if (stop == null) {
+	    return false;
+	}
+	// call substring to get rid of leading '<'
+	String prefix = reader.getText(start, stop).substring(1);
+
+	start = stop;
+	stop = reader.skipUntil(">");
+	if (stop == null) {
+	    return false;
+	}
+
+	// Determine namespace associated with <root> element's prefix
+	String root = reader.getText(start, stop);
+	String xmlnsDecl = "xmlns:" + prefix;
+	int index = root.indexOf(xmlnsDecl);
+	if (index == -1) {
+	    return false;
+	}
+	index += xmlnsDecl.length();
+	while (index < root.length()
+	           && Character.isWhitespace(root.charAt(index))) {
+	    index++;
+	}
+	if (index < root.length() && root.charAt(index) == '=') {
+	    index++;
+	    while (index < root.length()
+		       && Character.isWhitespace(root.charAt(index))) {
+		index++;
+	    }
+	    if (index < root.length() && root.charAt(index++) == '"'
+		    && root.regionMatches(index, JSP_URI, 0,
+					  JSP_URI.length())) {
+		return true;
+	    }
+	}
+
+	return false;
+    }
+
+    private JarFile getJarFile(URL jarFileUrl) throws IOException {
+	JarFile jarFile = null;
+
+	if (jarFileUrl != null) {
+	    JarURLConnection conn = (JarURLConnection) jarFileUrl.openConnection();
+	    conn.setUseCaches(false);
+	    conn.connect();
+	    jarFile = conn.getJarFile();
+	}
+
+	return jarFile;
+    }
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/ScriptingVariabler.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/ScriptingVariabler.java
new file mode 100644
index 0000000..5c91dbf
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/ScriptingVariabler.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.util.*;
+import javax.servlet.jsp.tagext.*;
+import org.apache.jasper.JasperException;
+
+/**
+ * Class responsible for determining the scripting variables that every
+ * custom action needs to declare.
+ *
+ * @author Jan Luehe
+ */
+class ScriptingVariabler {
+
+    private static final Integer MAX_SCOPE = new Integer(Integer.MAX_VALUE);
+
+    /*
+     * Assigns an identifier (of type integer) to every custom tag, in order
+     * to help identify, for every custom tag, the scripting variables that it
+     * needs to declare.
+     */
+    static class CustomTagCounter extends Node.Visitor {
+
+	private int count;
+	private Node.CustomTag parent;
+
+	public void visit(Node.CustomTag n) throws JasperException {
+	    n.setCustomTagParent(parent);
+	    Node.CustomTag tmpParent = parent;
+	    parent = n;
+	    visitBody(n);
+	    parent = tmpParent;
+	    n.setNumCount(new Integer(count++));
+	}
+    }
+
+    /*
+     * For every custom tag, determines the scripting variables it needs to
+     * declare. 
+     */
+    static class ScriptingVariableVisitor extends Node.Visitor {
+
+	private ErrorDispatcher err;
+	private Hashtable scriptVars;
+	
+	public ScriptingVariableVisitor(ErrorDispatcher err) {
+	    this.err = err;
+	    scriptVars = new Hashtable();
+	}
+
+	public void visit(Node.CustomTag n) throws JasperException {
+	    setScriptingVars(n, VariableInfo.AT_BEGIN);
+	    setScriptingVars(n, VariableInfo.NESTED);
+	    visitBody(n);
+	    setScriptingVars(n, VariableInfo.AT_END);
+	}
+
+	private void setScriptingVars(Node.CustomTag n, int scope)
+	        throws JasperException {
+
+	    TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+	    VariableInfo[] varInfos = n.getVariableInfos();
+	    if (tagVarInfos.length == 0 && varInfos.length == 0) {
+		return;
+	    }
+
+	    Vector vec = new Vector();
+
+	    Integer ownRange = null;
+	    if (scope == VariableInfo.AT_BEGIN
+		    || scope == VariableInfo.AT_END) {
+		Node.CustomTag parent = n.getCustomTagParent();
+		if (parent == null)
+		    ownRange = MAX_SCOPE;
+		else
+		    ownRange = parent.getNumCount();
+	    } else {
+		// NESTED
+		ownRange = n.getNumCount();
+	    }
+
+	    if (varInfos.length > 0) {
+		for (int i=0; i<varInfos.length; i++) {
+		    if (varInfos[i].getScope() != scope
+			    || !varInfos[i].getDeclare()) {
+			continue;
+		    }
+		    String varName = varInfos[i].getVarName();
+		    
+		    Integer currentRange = (Integer) scriptVars.get(varName);
+		    if (currentRange == null
+			    || ownRange.compareTo(currentRange) > 0) {
+			scriptVars.put(varName, ownRange);
+			vec.add(varInfos[i]);
+		    }
+		}
+	    } else {
+		for (int i=0; i<tagVarInfos.length; i++) {
+		    if (tagVarInfos[i].getScope() != scope
+			    || !tagVarInfos[i].getDeclare()) {
+			continue;
+		    }
+		    String varName = tagVarInfos[i].getNameGiven();
+		    if (varName == null) {
+			varName = n.getTagData().getAttributeString(
+		                        tagVarInfos[i].getNameFromAttribute());
+			if (varName == null) {
+			    err.jspError(n, "jsp.error.scripting.variable.missing_name",
+					 tagVarInfos[i].getNameFromAttribute());
+			}
+		    }
+
+		    Integer currentRange = (Integer) scriptVars.get(varName);
+		    if (currentRange == null
+			    || ownRange.compareTo(currentRange) > 0) {
+			scriptVars.put(varName, ownRange);
+			vec.add(tagVarInfos[i]);
+		    }
+		}
+	    }
+
+	    n.setScriptingVars(vec, scope);
+	}
+    }
+
+    public static void set(Node.Nodes page, ErrorDispatcher err)
+	    throws JasperException {
+	page.visit(new CustomTagCounter());
+	page.visit(new ScriptingVariableVisitor(err));
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/ServletWriter.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/ServletWriter.java
new file mode 100644
index 0000000..7e37dba
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/ServletWriter.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.compiler;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * This is what is used to generate servlets. 
+ *
+ * @author Anil K. Vijendran
+ * @author Kin-man Chung
+ */
+public class ServletWriter {
+    public static int TAB_WIDTH = 2;
+    public static String SPACES = "                              ";
+
+    // Current indent level:
+    private int indent = 0;
+    private int virtual_indent = 0;
+
+    // The sink writer:
+    PrintWriter writer;
+    
+    // servlet line numbers start from 1
+    private int javaLine = 1;
+
+
+    public ServletWriter(PrintWriter writer) {
+	this.writer = writer;
+    }
+
+    public void close() throws IOException {
+	writer.close();
+    }
+
+    
+    // -------------------- Access informations --------------------
+
+    public int getJavaLine() {
+        return javaLine;
+    }
+
+
+    // -------------------- Formatting --------------------
+
+    public void pushIndent() {
+	virtual_indent += TAB_WIDTH;
+	if (virtual_indent >= 0 && virtual_indent <= SPACES.length())
+	    indent = virtual_indent;
+    }
+
+    public void popIndent() {
+	virtual_indent -= TAB_WIDTH;
+	if (virtual_indent >= 0 && virtual_indent <= SPACES.length())
+	    indent = virtual_indent;
+    }
+
+    /**
+     * Print a standard comment for echo outputed chunk.
+     * @param start The starting position of the JSP chunk being processed. 
+     * @param stop  The ending position of the JSP chunk being processed. 
+     */
+    public void printComment(Mark start, Mark stop, char[] chars) {
+        if (start != null && stop != null) {
+            println("// from="+start);
+            println("//   to="+stop);
+        }
+        
+        if (chars != null)
+            for(int i = 0; i < chars.length;) {
+                printin();
+                print("// ");
+                while (chars[i] != '\n' && i < chars.length)
+                    writer.print(chars[i++]);
+            }
+    }
+
+    /**
+     * Prints the given string followed by '\n'
+     */
+    public void println(String s) {
+        javaLine++;
+	writer.println(s);
+    }
+
+    /**
+     * Prints a '\n'
+     */
+    public void println() {
+        javaLine++;
+	writer.println("");
+    }
+
+    /**
+     * Prints the current indention
+     */
+    public void printin() {
+	writer.print(SPACES.substring(0, indent));
+    }
+
+    /**
+     * Prints the current indention, followed by the given string
+     */
+    public void printin(String s) {
+	writer.print(SPACES.substring(0, indent));
+	writer.print(s);
+    }
+
+    /**
+     * Prints the current indention, and then the string, and a '\n'.
+     */
+    public void printil(String s) {
+        javaLine++;
+	writer.print(SPACES.substring(0, indent));
+	writer.println(s);
+    }
+
+    /**
+     * Prints the given char.
+     *
+     * Use println() to print a '\n'.
+     */
+    public void print(char c) {
+	writer.print(c);
+    }
+
+    /**
+     * Prints the given int.
+     */
+    public void print(int i) {
+	writer.print(i);
+    }
+
+    /**
+     * Prints the given string.
+     *
+     * The string must not contain any '\n', otherwise the line count will be
+     * off.
+     */
+    public void print(String s) {
+	writer.print(s);
+    }
+
+    /**
+     * Prints the given string.
+     *
+     * If the string spans multiple lines, the line count will be adjusted
+     * accordingly.
+     */
+    public void printMultiLn(String s) {
+        int index = 0;
+
+        // look for hidden newlines inside strings
+        while ((index=s.indexOf('\n',index)) > -1 ) {
+            javaLine++;
+            index++;
+        }
+
+	writer.print(s);
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/SmapGenerator.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/SmapGenerator.java
new file mode 100644
index 0000000..217b67e
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/SmapGenerator.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Represents a source map (SMAP), which serves to associate lines
+ * of the input JSP file(s) to lines in the generated servlet in the
+ * final .class file, according to the JSR-045 spec.
+ * 
+ * @author Shawn Bayern
+ */
+public class SmapGenerator {
+
+    //*********************************************************************
+    // Overview
+
+    /*
+     * The SMAP syntax is reasonably straightforward.  The purpose of this
+     * class is currently twofold:
+     *  - to provide a simple but low-level Java interface to build
+     *    a logical SMAP
+     *  - to serialize this logical SMAP for eventual inclusion directly
+     *    into a .class file.
+     */
+
+
+    //*********************************************************************
+    // Private state
+
+    private String outputFileName;
+    private String defaultStratum = "Java";
+    private List strata = new ArrayList();
+    private List embedded = new ArrayList();
+    private boolean doEmbedded = true;
+
+    //*********************************************************************
+    // Methods for adding mapping data
+
+    /**
+     * Sets the filename (without path information) for the generated
+     * source file.  E.g., "foo$jsp.java".
+     */
+    public synchronized void setOutputFileName(String x) {
+	outputFileName = x;
+    }
+
+    /**
+     * Adds the given SmapStratum object, representing a Stratum with
+     * logically associated FileSection and LineSection blocks, to
+     * the current SmapGenerator.  If <tt>default</tt> is true, this
+     * stratum is made the default stratum, overriding any previously
+     * set default.
+     *
+     * @param stratum the SmapStratum object to add
+     * @param defaultStratum if <tt>true</tt>, this SmapStratum is considered
+     *                to represent the default SMAP stratum unless
+     *                overwritten
+     */
+    public synchronized void addStratum(SmapStratum stratum,
+					boolean defaultStratum) {
+	strata.add(stratum);
+	if (defaultStratum)
+	    this.defaultStratum = stratum.getStratumName();
+    }
+
+    /**
+     * Adds the given string as an embedded SMAP with the given stratum name.
+     *
+     * @param smap the SMAP to embed
+     * @param stratumName the name of the stratum output by the compilation
+     *                    that produced the <tt>smap</tt> to be embedded
+     */
+    public synchronized void addSmap(String smap, String stratumName) {
+	embedded.add("*O " + stratumName + "\n"
+		   + smap
+		   + "*C " + stratumName + "\n");
+    }
+
+    /**
+     * Instructs the SmapGenerator whether to actually print any embedded
+     * SMAPs or not.  Intended for situations without an SMAP resolver.
+     *
+     * @param status If <tt>false</tt>, ignore any embedded SMAPs.
+     */
+    public void setDoEmbedded(boolean status) {
+	doEmbedded = status;
+    }
+
+    //*********************************************************************
+    // Methods for serializing the logical SMAP
+
+    public synchronized String getString() {
+	// check state and initialize buffer
+	if (outputFileName == null)
+	    throw new IllegalStateException();
+        StringBuffer out = new StringBuffer();
+
+	// start the SMAP
+	out.append("SMAP\n");
+	out.append(outputFileName + '\n');
+	out.append(defaultStratum + '\n');
+
+	// include embedded SMAPs
+	if (doEmbedded) {
+	    int nEmbedded = embedded.size();
+	    for (int i = 0; i < nEmbedded; i++) {
+	        out.append(embedded.get(i));
+	    }
+	}
+
+	// print our StratumSections, FileSections, and LineSections
+	int nStrata = strata.size();
+	for (int i = 0; i < nStrata; i++) {
+	    SmapStratum s = (SmapStratum) strata.get(i);
+	    out.append(s.getString());
+	}
+
+	// end the SMAP
+	out.append("*E\n");
+
+	return out.toString();
+    }
+
+    public String toString() { return getString(); }
+
+    //*********************************************************************
+    // For testing (and as an example of use)...
+
+    public static void main(String args[]) {
+	SmapGenerator g = new SmapGenerator();
+	g.setOutputFileName("foo.java");
+	SmapStratum s = new SmapStratum("JSP");
+	s.addFile("foo.jsp");
+	s.addFile("bar.jsp", "/foo/foo/bar.jsp");
+	s.addLineData(1, "foo.jsp", 1, 1, 1);
+	s.addLineData(2, "foo.jsp", 1, 6, 1);
+	s.addLineData(3, "foo.jsp", 2, 10, 5);
+	s.addLineData(20, "bar.jsp", 1, 30, 1);
+	g.addStratum(s, true);
+	System.out.print(g);
+
+	System.out.println("---");
+
+	SmapGenerator embedded = new SmapGenerator();
+	embedded.setOutputFileName("blargh.tier2");
+	s = new SmapStratum("Tier2");
+	s.addFile("1.tier2");
+	s.addLineData(1, "1.tier2", 1, 1, 1);
+	embedded.addStratum(s, true);
+	g.addSmap(embedded.toString(), "JSP");
+	System.out.println(g);
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/SmapStratum.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/SmapStratum.java
new file mode 100644
index 0000000..91f9d33
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/SmapStratum.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Represents the line and file mappings associated with a JSR-045
+ * "stratum".
+ *
+ * @author Jayson Falkner
+ * @author Shawn Bayern
+ */
+public class SmapStratum {
+
+    //*********************************************************************
+    // Class for storing LineInfo data
+
+    /**
+     * Represents a single LineSection in an SMAP, associated with
+     * a particular stratum.
+     */
+    public static class LineInfo {
+        private int inputStartLine = -1;
+        private int outputStartLine = -1;
+        private int lineFileID = 0;
+        private int inputLineCount = 1;
+        private int outputLineIncrement = 1;
+        private boolean lineFileIDSet = false;
+
+        /** Sets InputStartLine. */
+        public void setInputStartLine(int inputStartLine) {
+            if (inputStartLine < 0)
+                throw new IllegalArgumentException("" + inputStartLine);
+            this.inputStartLine = inputStartLine;
+        }
+
+        /** Sets OutputStartLine. */
+        public void setOutputStartLine(int outputStartLine) {
+            if (outputStartLine < 0)
+                throw new IllegalArgumentException("" + outputStartLine);
+            this.outputStartLine = outputStartLine;
+        }
+
+        /**
+             * Sets lineFileID.  Should be called only when different from
+             * that of prior LineInfo object (in any given context) or 0
+             * if the current LineInfo has no (logical) predecessor.
+             * <tt>LineInfo</tt> will print this file number no matter what.
+             */
+        public void setLineFileID(int lineFileID) {
+            if (lineFileID < 0)
+                throw new IllegalArgumentException("" + lineFileID);
+            this.lineFileID = lineFileID;
+            this.lineFileIDSet = true;
+        }
+
+        /** Sets InputLineCount. */
+        public void setInputLineCount(int inputLineCount) {
+            if (inputLineCount < 0)
+                throw new IllegalArgumentException("" + inputLineCount);
+            this.inputLineCount = inputLineCount;
+        }
+
+        /** Sets OutputLineIncrement. */
+        public void setOutputLineIncrement(int outputLineIncrement) {
+            if (outputLineIncrement < 0)
+                throw new IllegalArgumentException("" + outputLineIncrement);
+            this.outputLineIncrement = outputLineIncrement;
+        }
+
+        /**
+         * Retrieves the current LineInfo as a String, print all values
+         * only when appropriate (but LineInfoID if and only if it's been
+         * specified, as its necessity is sensitive to context).
+         */
+        public String getString() {
+            if (inputStartLine == -1 || outputStartLine == -1)
+                throw new IllegalStateException();
+            StringBuffer out = new StringBuffer();
+            out.append(inputStartLine);
+            if (lineFileIDSet)
+                out.append("#" + lineFileID);
+            if (inputLineCount != 1)
+                out.append("," + inputLineCount);
+            out.append(":" + outputStartLine);
+            if (outputLineIncrement != 1)
+                out.append("," + outputLineIncrement);
+            out.append('\n');
+            return out.toString();
+        }
+
+        public String toString() {
+            return getString();
+        }
+    }
+
+    //*********************************************************************
+    // Private state
+
+    private String stratumName;
+    private List fileNameList;
+    private List filePathList;
+    private List lineData;
+    private int lastFileID;
+
+    //*********************************************************************
+    // Constructor
+
+    /**
+     * Constructs a new SmapStratum object for the given stratum name
+     * (e.g., JSP).
+     *
+     * @param stratumName the name of the stratum (e.g., JSP)
+     */
+    public SmapStratum(String stratumName) {
+        this.stratumName = stratumName;
+        fileNameList = new ArrayList();
+        filePathList = new ArrayList();
+        lineData = new ArrayList();
+        lastFileID = 0;
+    }
+
+    //*********************************************************************
+    // Methods to add mapping information
+
+    /**
+     * Adds record of a new file, by filename.
+     *
+     * @param filename the filename to add, unqualified by path.
+     */
+    public void addFile(String filename) {
+        addFile(filename, filename);
+    }
+
+    /**
+     * Adds record of a new file, by filename and path.  The path
+     * may be relative to a source compilation path.
+     *
+     * @param filename the filename to add, unqualified by path
+     * @param filePath the path for the filename, potentially relative
+     *                 to a source compilation path
+     */
+    public void addFile(String filename, String filePath) {
+        int pathIndex = filePathList.indexOf(filePath);
+        if (pathIndex == -1) {
+            fileNameList.add(filename);
+            filePathList.add(filePath);
+        }
+    }
+
+    /**
+     * Combines consecutive LineInfos wherever possible
+     */
+    public void optimizeLineSection() {
+
+/* Some debugging code
+        for (int i = 0; i < lineData.size(); i++) {
+            LineInfo li = (LineInfo)lineData.get(i);
+            System.out.print(li.toString());
+        }
+*/
+        //Incorporate each LineInfo into the previous LineInfo's 
+        //outputLineIncrement, if possible
+        int i = 0;
+        while (i < lineData.size() - 1) {
+            LineInfo li = (LineInfo)lineData.get(i);
+            LineInfo liNext = (LineInfo)lineData.get(i + 1);
+            if (!liNext.lineFileIDSet
+                && liNext.inputStartLine == li.inputStartLine
+                && liNext.inputLineCount == 1
+                && li.inputLineCount == 1
+                && liNext.outputStartLine
+                    == li.outputStartLine
+                        + li.inputLineCount * li.outputLineIncrement) {
+                li.setOutputLineIncrement(
+                    liNext.outputStartLine
+                        - li.outputStartLine
+                        + liNext.outputLineIncrement);
+                lineData.remove(i + 1);
+            } else {
+                i++;
+            }
+        }
+
+        //Incorporate each LineInfo into the previous LineInfo's
+        //inputLineCount, if possible
+        i = 0;
+        while (i < lineData.size() - 1) {
+            LineInfo li = (LineInfo)lineData.get(i);
+            LineInfo liNext = (LineInfo)lineData.get(i + 1);
+            if (!liNext.lineFileIDSet
+                && liNext.inputStartLine == li.inputStartLine + li.inputLineCount
+                && liNext.outputLineIncrement == li.outputLineIncrement
+                && liNext.outputStartLine
+                    == li.outputStartLine
+                        + li.inputLineCount * li.outputLineIncrement) {
+                li.setInputLineCount(li.inputLineCount + liNext.inputLineCount);
+                lineData.remove(i + 1);
+            } else {
+                i++;
+            }
+        }
+    }
+
+    /**
+     * Adds complete information about a simple line mapping.  Specify
+     * all the fields in this method; the back-end machinery takes care
+     * of printing only those that are necessary in the final SMAP.
+     * (My view is that fields are optional primarily for spatial efficiency,
+     * not for programmer convenience.  Could always add utility methods
+     * later.)
+     *
+     * @param inputStartLine starting line in the source file
+     *        (SMAP <tt>InputStartLine</tt>)
+     * @param inputFileName the filepath (or name) from which the input comes
+     *        (yields SMAP <tt>LineFileID</tt>)  Use unqualified names
+     *        carefully, and only when they uniquely identify a file.
+     * @param inputLineCount the number of lines in the input to map
+     *        (SMAP <tt>LineFileCount</tt>)
+     * @param outputStartLine starting line in the output file 
+     *        (SMAP <tt>OutputStartLine</tt>)
+     * @param outputLineIncrement number of output lines to map to each
+     *        input line (SMAP <tt>OutputLineIncrement</tt>).  <i>Given the
+     *        fact that the name starts with "output", I continuously have
+     *        the subconscious urge to call this field
+     *        <tt>OutputLineExcrement</tt>.</i>
+     */
+    public void addLineData(
+        int inputStartLine,
+        String inputFileName,
+        int inputLineCount,
+        int outputStartLine,
+        int outputLineIncrement) {
+        // check the input - what are you doing here??
+        int fileIndex = filePathList.indexOf(inputFileName);
+        if (fileIndex == -1) // still
+            throw new IllegalArgumentException(
+                "inputFileName: " + inputFileName);
+
+        //Jasper incorrectly SMAPs certain Nodes, giving them an 
+        //outputStartLine of 0.  This can cause a fatal error in
+        //optimizeLineSection, making it impossible for Jasper to
+        //compile the JSP.  Until we can fix the underlying
+        //SMAPping problem, we simply ignore the flawed SMAP entries.
+        if (outputStartLine == 0)
+            return;
+
+        // build the LineInfo
+        LineInfo li = new LineInfo();
+        li.setInputStartLine(inputStartLine);
+        li.setInputLineCount(inputLineCount);
+        li.setOutputStartLine(outputStartLine);
+        li.setOutputLineIncrement(outputLineIncrement);
+        if (fileIndex != lastFileID)
+            li.setLineFileID(fileIndex);
+        lastFileID = fileIndex;
+
+        // save it
+        lineData.add(li);
+    }
+
+    //*********************************************************************
+    // Methods to retrieve information
+
+    /**
+     * Returns the name of the stratum.
+     */
+    public String getStratumName() {
+        return stratumName;
+    }
+
+    /**
+     * Returns the given stratum as a String:  a StratumSection,
+     * followed by at least one FileSection and at least one LineSection.
+     */
+    public String getString() {
+        // check state and initialize buffer
+        if (fileNameList.size() == 0 || lineData.size() == 0)
+            return null;
+
+        StringBuffer out = new StringBuffer();
+
+        // print StratumSection
+        out.append("*S " + stratumName + "\n");
+
+        // print FileSection
+        out.append("*F\n");
+        int bound = fileNameList.size();
+        for (int i = 0; i < bound; i++) {
+            if (filePathList.get(i) != null) {
+                out.append("+ " + i + " " + fileNameList.get(i) + "\n");
+                // Source paths must be relative, not absolute, so we
+                // remove the leading "/", if one exists.
+                String filePath = (String)filePathList.get(i);
+                if (filePath.startsWith("/")) {
+                    filePath = filePath.substring(1);
+                }
+                out.append(filePath + "\n");
+            } else {
+                out.append(i + " " + fileNameList.get(i) + "\n");
+            }
+        }
+
+        // print LineSection
+        out.append("*L\n");
+        bound = lineData.size();
+        for (int i = 0; i < bound; i++) {
+            LineInfo li = (LineInfo)lineData.get(i);
+            out.append(li.getString());
+        }
+
+        return out.toString();
+    }
+
+    public String toString() {
+        return getString();
+    }
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/SmapUtil.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/SmapUtil.java
new file mode 100644
index 0000000..8ff4a38
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/SmapUtil.java
@@ -0,0 +1,728 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+
+/**
+ * Contains static utilities for generating SMAP data based on the
+ * current version of Jasper.
+ * 
+ * @author Jayson Falkner
+ * @author Shawn Bayern
+ * @author Robert Field (inner SDEInstaller class)
+ * @author Mark Roth
+ * @author Kin-man Chung
+ */
+public class SmapUtil {
+
+    private org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( SmapUtil.class );
+
+    //*********************************************************************
+    // Constants
+
+    public static final String SMAP_ENCODING = "UTF-8";
+
+    //*********************************************************************
+    // Public entry points
+
+    /**
+     * Generates an appropriate SMAP representing the current compilation
+     * context.  (JSR-045.)
+     *
+     * @param ctxt Current compilation context
+     * @param pageNodes The current JSP page
+     * @return a SMAP for the page
+     */
+    public static String[] generateSmap(
+        JspCompilationContext ctxt,
+        Node.Nodes pageNodes)
+        throws IOException {
+
+        // Scan the nodes for presence of Jasper generated inner classes
+        PreScanVisitor psVisitor = new PreScanVisitor();
+        try {
+            pageNodes.visit(psVisitor);
+        } catch (JasperException ex) {
+        }
+        HashMap map = psVisitor.getMap();
+
+        // set up our SMAP generator
+        SmapGenerator g = new SmapGenerator();
+        
+        /** Disable reading of input SMAP because:
+            1. There is a bug here: getRealPath() is null if .jsp is in a jar
+        	Bugzilla 14660.
+            2. Mappings from other sources into .jsp files are not supported.
+            TODO: fix 1. if 2. is not true.
+        // determine if we have an input SMAP
+        String smapPath = inputSmapPath(ctxt.getRealPath(ctxt.getJspFile()));
+            File inputSmap = new File(smapPath);
+            if (inputSmap.exists()) {
+                byte[] embeddedSmap = null;
+            byte[] subSmap = SDEInstaller.readWhole(inputSmap);
+            String subSmapString = new String(subSmap, SMAP_ENCODING);
+            g.addSmap(subSmapString, "JSP");
+        }
+        **/
+
+        // now, assemble info about our own stratum (JSP) using JspLineMap
+        SmapStratum s = new SmapStratum("JSP");
+
+        g.setOutputFileName(unqualify(ctxt.getServletJavaFileName()));
+
+        // Map out Node.Nodes
+        evaluateNodes(pageNodes, s, map, ctxt.getOptions().getMappedFile());
+        s.optimizeLineSection();
+        g.addStratum(s, true);
+
+        if (ctxt.getOptions().isSmapDumped()) {
+            File outSmap = new File(ctxt.getClassFileName() + ".smap");
+            PrintWriter so =
+                new PrintWriter(
+                    new OutputStreamWriter(
+                        new FileOutputStream(outSmap),
+                        SMAP_ENCODING));
+            so.print(g.getString());
+            so.close();
+        }
+
+        String classFileName = ctxt.getClassFileName();
+        int innerClassCount = map.size();
+        String [] smapInfo = new String[2 + innerClassCount*2];
+        smapInfo[0] = classFileName;
+        smapInfo[1] = g.getString();
+
+        int count = 2;
+        Iterator iter = map.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            String innerClass = (String) entry.getKey();
+            s = (SmapStratum) entry.getValue();
+            s.optimizeLineSection();
+            g = new SmapGenerator();
+            g.setOutputFileName(unqualify(ctxt.getServletJavaFileName()));
+            g.addStratum(s, true);
+
+            String innerClassFileName =
+                classFileName.substring(0, classFileName.indexOf(".class")) +
+                '$' + innerClass + ".class";
+            if (ctxt.getOptions().isSmapDumped()) {
+                File outSmap = new File(innerClassFileName + ".smap");
+                PrintWriter so =
+                    new PrintWriter(
+                        new OutputStreamWriter(
+                            new FileOutputStream(outSmap),
+                            SMAP_ENCODING));
+                so.print(g.getString());
+                so.close();
+            }
+            smapInfo[count] = innerClassFileName;
+            smapInfo[count+1] = g.getString();
+            count += 2;
+        }
+
+        return smapInfo;
+    }
+
+    public static void installSmap(String[] smap)
+        throws IOException {
+        if (smap == null) {
+            return;
+        }
+
+        for (int i = 0; i < smap.length; i += 2) {
+            File outServlet = new File(smap[i]);
+            SDEInstaller.install(outServlet, smap[i+1].getBytes());
+        }
+    }
+
+    //*********************************************************************
+    // Private utilities
+
+    /**
+     * Returns an unqualified version of the given file path.
+     */
+    private static String unqualify(String path) {
+        path = path.replace('\\', '/');
+        return path.substring(path.lastIndexOf('/') + 1);
+    }
+
+    /**
+     * Returns a file path corresponding to a potential SMAP input
+     * for the given compilation input (JSP file).
+     */
+    private static String inputSmapPath(String path) {
+        return path.substring(0, path.lastIndexOf('.') + 1) + "smap";
+    }
+
+    //*********************************************************************
+    // Installation logic (from Robert Field, JSR-045 spec lead)
+    private static class SDEInstaller {
+
+        private org.apache.commons.logging.Log log=
+            org.apache.commons.logging.LogFactory.getLog( SDEInstaller.class );
+
+        static final String nameSDE = "SourceDebugExtension";
+
+        byte[] orig;
+        byte[] sdeAttr;
+        byte[] gen;
+
+        int origPos = 0;
+        int genPos = 0;
+
+        int sdeIndex;
+
+        public static void main(String[] args) throws IOException {
+            if (args.length == 2) {
+                install(new File(args[0]), new File(args[1]));
+            } else if (args.length == 3) {
+                install(
+                    new File(args[0]),
+                    new File(args[1]),
+                    new File(args[2]));
+            } else {
+                System.err.println(
+                    "Usage: <command> <input class file> "
+                        + "<attribute file> <output class file name>\n"
+                        + "<command> <input/output class file> <attribute file>");
+            }
+        }
+
+        static void install(File inClassFile, File attrFile, File outClassFile)
+            throws IOException {
+            new SDEInstaller(inClassFile, attrFile, outClassFile);
+        }
+
+        static void install(File inOutClassFile, File attrFile)
+            throws IOException {
+            File tmpFile = new File(inOutClassFile.getPath() + "tmp");
+            new SDEInstaller(inOutClassFile, attrFile, tmpFile);
+            if (!inOutClassFile.delete()) {
+                throw new IOException("inOutClassFile.delete() failed");
+            }
+            if (!tmpFile.renameTo(inOutClassFile)) {
+                throw new IOException("tmpFile.renameTo(inOutClassFile) failed");
+            }
+        }
+
+        static void install(File classFile, byte[] smap) throws IOException {
+            File tmpFile = new File(classFile.getPath() + "tmp");
+            new SDEInstaller(classFile, smap, tmpFile);
+            if (!classFile.delete()) {
+                throw new IOException("classFile.delete() failed");
+            }
+            if (!tmpFile.renameTo(classFile)) {
+                throw new IOException("tmpFile.renameTo(classFile) failed");
+            }
+        }
+
+        SDEInstaller(File inClassFile, byte[] sdeAttr, File outClassFile)
+            throws IOException {
+            if (!inClassFile.exists()) {
+                throw new FileNotFoundException("no such file: " + inClassFile);
+            }
+
+            this.sdeAttr = sdeAttr;
+            // get the bytes
+            orig = readWhole(inClassFile);
+            gen = new byte[orig.length + sdeAttr.length + 100];
+
+            // do it
+            addSDE();
+
+            // write result
+            FileOutputStream outStream = new FileOutputStream(outClassFile);
+            outStream.write(gen, 0, genPos);
+            outStream.close();
+        }
+
+        SDEInstaller(File inClassFile, File attrFile, File outClassFile)
+            throws IOException {
+            this(inClassFile, readWhole(attrFile), outClassFile);
+        }
+
+        static byte[] readWhole(File input) throws IOException {
+            FileInputStream inStream = new FileInputStream(input);
+            int len = (int)input.length();
+            byte[] bytes = new byte[len];
+            if (inStream.read(bytes, 0, len) != len) {
+                throw new IOException("expected size: " + len);
+            }
+            inStream.close();
+            return bytes;
+        }
+
+        void addSDE() throws UnsupportedEncodingException, IOException {
+            int i;
+            copy(4 + 2 + 2); // magic min/maj version
+            int constantPoolCountPos = genPos;
+            int constantPoolCount = readU2();
+            if (log.isDebugEnabled())
+                log.debug("constant pool count: " + constantPoolCount);
+            writeU2(constantPoolCount);
+
+            // copy old constant pool return index of SDE symbol, if found
+            sdeIndex = copyConstantPool(constantPoolCount);
+            if (sdeIndex < 0) {
+                // if "SourceDebugExtension" symbol not there add it
+                writeUtf8ForSDE();
+
+                // increment the countantPoolCount
+                sdeIndex = constantPoolCount;
+                ++constantPoolCount;
+                randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
+
+                if (log.isDebugEnabled())
+                    log.debug("SourceDebugExtension not found, installed at: " + sdeIndex);
+            } else {
+                if (log.isDebugEnabled())
+                    log.debug("SourceDebugExtension found at: " + sdeIndex);
+            }
+            copy(2 + 2 + 2); // access, this, super
+            int interfaceCount = readU2();
+            writeU2(interfaceCount);
+            if (log.isDebugEnabled())
+                log.debug("interfaceCount: " + interfaceCount);
+            copy(interfaceCount * 2);
+            copyMembers(); // fields
+            copyMembers(); // methods
+            int attrCountPos = genPos;
+            int attrCount = readU2();
+            writeU2(attrCount);
+            if (log.isDebugEnabled())
+                log.debug("class attrCount: " + attrCount);
+            // copy the class attributes, return true if SDE attr found (not copied)
+            if (!copyAttrs(attrCount)) {
+                // we will be adding SDE and it isn't already counted
+                ++attrCount;
+                randomAccessWriteU2(attrCountPos, attrCount);
+                if (log.isDebugEnabled())
+                    log.debug("class attrCount incremented");
+            }
+            writeAttrForSDE(sdeIndex);
+        }
+
+        void copyMembers() {
+            int count = readU2();
+            writeU2(count);
+            if (log.isDebugEnabled())
+                log.debug("members count: " + count);
+            for (int i = 0; i < count; ++i) {
+                copy(6); // access, name, descriptor
+                int attrCount = readU2();
+                writeU2(attrCount);
+                if (log.isDebugEnabled())
+                    log.debug("member attr count: " + attrCount);
+                copyAttrs(attrCount);
+            }
+        }
+
+        boolean copyAttrs(int attrCount) {
+            boolean sdeFound = false;
+            for (int i = 0; i < attrCount; ++i) {
+                int nameIndex = readU2();
+                // don't write old SDE
+                if (nameIndex == sdeIndex) {
+                    sdeFound = true;
+                    if (log.isDebugEnabled())
+                        log.debug("SDE attr found");
+                } else {
+                    writeU2(nameIndex); // name
+                    int len = readU4();
+                    writeU4(len);
+                    copy(len);
+                    if (log.isDebugEnabled())
+                        log.debug("attr len: " + len);
+                }
+            }
+            return sdeFound;
+        }
+
+        void writeAttrForSDE(int index) {
+            writeU2(index);
+            writeU4(sdeAttr.length);
+            for (int i = 0; i < sdeAttr.length; ++i) {
+                writeU1(sdeAttr[i]);
+            }
+        }
+
+        void randomAccessWriteU2(int pos, int val) {
+            int savePos = genPos;
+            genPos = pos;
+            writeU2(val);
+            genPos = savePos;
+        }
+
+        int readU1() {
+            return ((int)orig[origPos++]) & 0xFF;
+        }
+
+        int readU2() {
+            int res = readU1();
+            return (res << 8) + readU1();
+        }
+
+        int readU4() {
+            int res = readU2();
+            return (res << 16) + readU2();
+        }
+
+        void writeU1(int val) {
+            gen[genPos++] = (byte)val;
+        }
+
+        void writeU2(int val) {
+            writeU1(val >> 8);
+            writeU1(val & 0xFF);
+        }
+
+        void writeU4(int val) {
+            writeU2(val >> 16);
+            writeU2(val & 0xFFFF);
+        }
+
+        void copy(int count) {
+            for (int i = 0; i < count; ++i) {
+                gen[genPos++] = orig[origPos++];
+            }
+        }
+
+        byte[] readBytes(int count) {
+            byte[] bytes = new byte[count];
+            for (int i = 0; i < count; ++i) {
+                bytes[i] = orig[origPos++];
+            }
+            return bytes;
+        }
+
+        void writeBytes(byte[] bytes) {
+            for (int i = 0; i < bytes.length; ++i) {
+                gen[genPos++] = bytes[i];
+            }
+        }
+
+        int copyConstantPool(int constantPoolCount)
+            throws UnsupportedEncodingException, IOException {
+            int sdeIndex = -1;
+            // copy const pool index zero not in class file
+            for (int i = 1; i < constantPoolCount; ++i) {
+                int tag = readU1();
+                writeU1(tag);
+                switch (tag) {
+                    case 7 : // Class
+                    case 8 : // String
+                        if (log.isDebugEnabled())
+                            log.debug(i + " copying 2 bytes");
+                        copy(2);
+                        break;
+                    case 9 : // Field
+                    case 10 : // Method
+                    case 11 : // InterfaceMethod
+                    case 3 : // Integer
+                    case 4 : // Float
+                    case 12 : // NameAndType
+                        if (log.isDebugEnabled())
+                            log.debug(i + " copying 4 bytes");
+                        copy(4);
+                        break;
+                    case 5 : // Long
+                    case 6 : // Double
+                        if (log.isDebugEnabled())
+                            log.debug(i + " copying 8 bytes");
+                        copy(8);
+                        i++;
+                        break;
+                    case 1 : // Utf8
+                        int len = readU2();
+                        writeU2(len);
+                        byte[] utf8 = readBytes(len);
+                        String str = new String(utf8, "UTF-8");
+                        if (log.isDebugEnabled())
+                            log.debug(i + " read class attr -- '" + str + "'");
+                        if (str.equals(nameSDE)) {
+                            sdeIndex = i;
+                        }
+                        writeBytes(utf8);
+                        break;
+                    default :
+                        throw new IOException("unexpected tag: " + tag);
+                }
+            }
+            return sdeIndex;
+        }
+
+        void writeUtf8ForSDE() {
+            int len = nameSDE.length();
+            writeU1(1); // Utf8 tag
+            writeU2(len);
+            for (int i = 0; i < len; ++i) {
+                writeU1(nameSDE.charAt(i));
+            }
+        }
+    }
+
+    public static void evaluateNodes(
+        Node.Nodes nodes,
+        SmapStratum s,
+        HashMap innerClassMap,
+        boolean breakAtLF) {
+        try {
+            nodes.visit(new SmapGenVisitor(s, breakAtLF, innerClassMap));
+        } catch (JasperException ex) {
+        }
+    }
+
+    static class SmapGenVisitor extends Node.Visitor {
+
+        private SmapStratum smap;
+        private boolean breakAtLF;
+        private HashMap innerClassMap;
+
+        SmapGenVisitor(SmapStratum s, boolean breakAtLF, HashMap map) {
+            this.smap = s;
+            this.breakAtLF = breakAtLF;
+            this.innerClassMap = map;
+        }
+
+        public void visitBody(Node n) throws JasperException {
+            SmapStratum smapSave = smap;
+            String innerClass = n.getInnerClassName();
+            if (innerClass != null) {
+                this.smap = (SmapStratum) innerClassMap.get(innerClass);
+            }
+            super.visitBody(n);
+            smap = smapSave;
+        }
+
+        public void visit(Node.Declaration n) throws JasperException {
+            doSmapText(n);
+        }
+
+        public void visit(Node.Expression n) throws JasperException {
+            doSmapText(n);
+        }
+
+        public void visit(Node.Scriptlet n) throws JasperException {
+            doSmapText(n);
+        }
+
+        public void visit(Node.IncludeAction n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.ForwardAction n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.GetProperty n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.SetProperty n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.UseBean n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.PlugIn n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.CustomTag n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.UninterpretedTag n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.JspElement n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.JspText n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.NamedAttribute n) throws JasperException {
+            visitBody(n);
+        }
+
+        public void visit(Node.JspBody n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.InvokeAction n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.DoBodyAction n) throws JasperException {
+            doSmap(n);
+            visitBody(n);
+        }
+
+        public void visit(Node.ELExpression n) throws JasperException {
+            doSmap(n);
+        }
+
+        public void visit(Node.TemplateText n) throws JasperException {
+            Mark mark = n.getStart();
+            if (mark == null) {
+                return;
+            }
+
+            //Add the file information
+            String fileName = mark.getFile();
+            smap.addFile(unqualify(fileName), fileName);
+
+            //Add a LineInfo that corresponds to the beginning of this node
+            int iInputStartLine = mark.getLineNumber();
+            int iOutputStartLine = n.getBeginJavaLine();
+            int iOutputLineIncrement = breakAtLF? 1: 0;
+            smap.addLineData(iInputStartLine, fileName, 1, iOutputStartLine, 
+                             iOutputLineIncrement);
+
+            // Output additional mappings in the text
+            java.util.ArrayList extraSmap = n.getExtraSmap();
+
+            if (extraSmap != null) {
+                for (int i = 0; i < extraSmap.size(); i++) {
+                    iOutputStartLine += iOutputLineIncrement;
+                    smap.addLineData(
+                        iInputStartLine+((Integer)extraSmap.get(i)).intValue(),
+                        fileName,
+                        1,
+                        iOutputStartLine,
+                        iOutputLineIncrement);
+                }
+            }
+        }
+
+        private void doSmap(
+            Node n,
+            int inLineCount,
+            int outIncrement,
+            int skippedLines) {
+            Mark mark = n.getStart();
+            if (mark == null) {
+                return;
+            }
+
+            String unqualifiedName = unqualify(mark.getFile());
+            smap.addFile(unqualifiedName, mark.getFile());
+            smap.addLineData(
+                mark.getLineNumber() + skippedLines,
+                mark.getFile(),
+                inLineCount - skippedLines,
+                n.getBeginJavaLine() + skippedLines,
+                outIncrement);
+        }
+
+        private void doSmap(Node n) {
+            doSmap(n, 1, n.getEndJavaLine() - n.getBeginJavaLine(), 0);
+        }
+
+        private void doSmapText(Node n) {
+            String text = n.getText();
+            int index = 0;
+            int next = 0;
+            int lineCount = 1;
+            int skippedLines = 0;
+            boolean slashStarSeen = false;
+            boolean beginning = true;
+
+            // Count lines inside text, but skipping comment lines at the
+            // beginning of the text.
+            while ((next = text.indexOf('\n', index)) > -1) {
+                if (beginning) {
+                    String line = text.substring(index, next).trim();
+                    if (!slashStarSeen && line.startsWith("/*")) {
+                        slashStarSeen = true;
+                    }
+                    if (slashStarSeen) {
+                        skippedLines++;
+                        int endIndex = line.indexOf("*/");
+                        if (endIndex >= 0) {
+                            // End of /* */ comment
+                            slashStarSeen = false;
+                            if (endIndex < line.length() - 2) {
+                                // Some executable code after comment
+                                skippedLines--;
+                                beginning = false;
+                            }
+                        }
+                    } else if (line.length() == 0 || line.startsWith("//")) {
+                        skippedLines++;
+                    } else {
+                        beginning = false;
+                    }
+                }
+                lineCount++;
+                index = next + 1;
+            }
+
+            doSmap(n, lineCount, 1, skippedLines);
+        }
+    }
+
+    private static class PreScanVisitor extends Node.Visitor {
+
+        HashMap map = new HashMap();
+
+        public void doVisit(Node n) {
+            String inner = n.getInnerClassName();
+            if (inner != null && !map.containsKey(inner)) {
+                map.put(inner, new SmapStratum("JSP"));
+            }
+        }
+
+        HashMap getMap() {
+            return map;
+        }
+    }
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/TagConstants.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/TagConstants.java
new file mode 100644
index 0000000..ce118af
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/TagConstants.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+public interface TagConstants {
+
+    public static final String JSP_URI = "http://java.sun.com/JSP/Page";
+
+    public static final String DIRECTIVE_ACTION = "directive.";
+
+    public static final String ROOT_ACTION = "root";
+    public static final String JSP_ROOT_ACTION = "jsp:root";
+
+    public static final String PAGE_DIRECTIVE_ACTION = "directive.page";
+    public static final String JSP_PAGE_DIRECTIVE_ACTION = "jsp:directive.page";
+
+    public static final String INCLUDE_DIRECTIVE_ACTION = "directive.include";
+    public static final String JSP_INCLUDE_DIRECTIVE_ACTION = "jsp:directive.include";
+
+    public static final String DECLARATION_ACTION = "declaration";
+    public static final String JSP_DECLARATION_ACTION = "jsp:declaration";
+
+    public static final String SCRIPTLET_ACTION = "scriptlet";
+    public static final String JSP_SCRIPTLET_ACTION = "jsp:scriptlet";
+
+    public static final String EXPRESSION_ACTION = "expression";
+    public static final String JSP_EXPRESSION_ACTION = "jsp:expression";
+
+    public static final String USE_BEAN_ACTION = "useBean";
+    public static final String JSP_USE_BEAN_ACTION = "jsp:useBean";
+
+    public static final String SET_PROPERTY_ACTION = "setProperty";
+    public static final String JSP_SET_PROPERTY_ACTION = "jsp:setProperty";
+
+    public static final String GET_PROPERTY_ACTION = "getProperty";
+    public static final String JSP_GET_PROPERTY_ACTION = "jsp:getProperty";
+
+    public static final String INCLUDE_ACTION = "include";
+    public static final String JSP_INCLUDE_ACTION = "jsp:include";
+
+    public static final String FORWARD_ACTION = "forward";
+    public static final String JSP_FORWARD_ACTION = "jsp:forward";
+
+    public static final String PARAM_ACTION = "param";
+    public static final String JSP_PARAM_ACTION = "jsp:param";
+
+    public static final String PARAMS_ACTION = "params";
+    public static final String JSP_PARAMS_ACTION = "jsp:params";
+
+    public static final String PLUGIN_ACTION = "plugin";
+    public static final String JSP_PLUGIN_ACTION = "jsp:plugin";
+
+    public static final String FALLBACK_ACTION = "fallback";
+    public static final String JSP_FALLBACK_ACTION = "jsp:fallback";
+
+    public static final String TEXT_ACTION = "text";
+    public static final String JSP_TEXT_ACTION = "jsp:text";
+    public static final String JSP_TEXT_ACTION_END = "</jsp:text>";
+
+    public static final String ATTRIBUTE_ACTION = "attribute";
+    public static final String JSP_ATTRIBUTE_ACTION = "jsp:attribute";
+
+    public static final String BODY_ACTION = "body";
+    public static final String JSP_BODY_ACTION = "jsp:body";
+
+    public static final String ELEMENT_ACTION = "element";
+    public static final String JSP_ELEMENT_ACTION = "jsp:element";
+
+    public static final String OUTPUT_ACTION = "output";
+    public static final String JSP_OUTPUT_ACTION = "jsp:output";
+
+    public static final String TAGLIB_DIRECTIVE_ACTION = "taglib";
+    public static final String JSP_TAGLIB_DIRECTIVE_ACTION = "jsp:taglib";
+
+    /*
+     * Tag Files
+     */
+    public static final String INVOKE_ACTION = "invoke";
+    public static final String JSP_INVOKE_ACTION = "jsp:invoke";
+
+    public static final String DOBODY_ACTION = "doBody";
+    public static final String JSP_DOBODY_ACTION = "jsp:doBody";
+
+    /*
+     * Tag File Directives
+     */
+    public static final String TAG_DIRECTIVE_ACTION = "directive.tag";
+    public static final String JSP_TAG_DIRECTIVE_ACTION = "jsp:directive.tag";
+
+    public static final String ATTRIBUTE_DIRECTIVE_ACTION = "directive.attribute";
+    public static final String JSP_ATTRIBUTE_DIRECTIVE_ACTION = "jsp:directive.attribute";
+
+    public static final String VARIABLE_DIRECTIVE_ACTION = "directive.variable";
+    public static final String JSP_VARIABLE_DIRECTIVE_ACTION = "jsp:directive.variable";
+
+    /*
+     * Directive attributes
+     */
+    public static final String URN_JSPTAGDIR = "urn:jsptagdir:";
+    public static final String URN_JSPTLD = "urn:jsptld:";
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/TagFileProcessor.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/TagFileProcessor.java
new file mode 100644
index 0000000..9e2b6eb
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/TagFileProcessor.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+import java.util.HashMap;
+
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagExtraInfo;
+import javax.servlet.jsp.tagext.TagFileInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.servlet.jsp.tagext.TagVariableInfo;
+import javax.servlet.jsp.tagext.VariableInfo;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.servlet.JspServletWrapper;
+import org.apache.jasper.runtime.JspSourceDependent;
+
+/**
+ * 1. Processes and extracts the directive info in a tag file.
+ * 2. Compiles and loads tag files used in a JSP file.
+ *
+ * @author Kin-man Chung
+ */
+
+class TagFileProcessor {
+
+    private Vector tempVector;
+
+    /**
+     * A visitor the tag file
+     */
+    private static class TagFileDirectiveVisitor extends Node.Visitor {
+
+        private static final JspUtil.ValidAttribute[] tagDirectiveAttrs = {
+            new JspUtil.ValidAttribute("display-name"),
+            new JspUtil.ValidAttribute("body-content"),
+            new JspUtil.ValidAttribute("dynamic-attributes"),
+            new JspUtil.ValidAttribute("small-icon"),
+            new JspUtil.ValidAttribute("large-icon"),
+            new JspUtil.ValidAttribute("description"),
+            new JspUtil.ValidAttribute("example"),
+            new JspUtil.ValidAttribute("pageEncoding"),
+            new JspUtil.ValidAttribute("language"),
+            new JspUtil.ValidAttribute("import"),
+            new JspUtil.ValidAttribute("isELIgnored") };
+
+        private static final JspUtil.ValidAttribute[] attributeDirectiveAttrs = {
+            new JspUtil.ValidAttribute("name", true),
+            new JspUtil.ValidAttribute("required"),
+            new JspUtil.ValidAttribute("fragment"),
+            new JspUtil.ValidAttribute("rtexprvalue"),
+            new JspUtil.ValidAttribute("type"),
+            new JspUtil.ValidAttribute("description")
+        };
+
+        private static final JspUtil.ValidAttribute[] variableDirectiveAttrs = {
+            new JspUtil.ValidAttribute("name-given"),
+            new JspUtil.ValidAttribute("name-from-attribute"),
+            new JspUtil.ValidAttribute("alias"),
+            new JspUtil.ValidAttribute("variable-class"),
+            new JspUtil.ValidAttribute("scope"),
+            new JspUtil.ValidAttribute("declare"),
+            new JspUtil.ValidAttribute("description")
+        };
+
+        private ErrorDispatcher err;
+        private TagLibraryInfo tagLibInfo;
+
+        private String name = null;
+        private String path = null;
+        private TagExtraInfo tei = null;
+        private String bodycontent = null;
+        private String description = null;
+        private String displayName = null;
+        private String smallIcon = null;
+        private String largeIcon = null;
+        private String dynamicAttrsMapName;
+        private String example = null;
+        
+        private Vector attributeVector;
+        private Vector variableVector;
+
+        private static final String ATTR_NAME =
+            "the name attribute of the attribute directive";
+        private static final String VAR_NAME_GIVEN =
+            "the name-given attribute of the variable directive";
+        private static final String VAR_NAME_FROM =
+            "the name-from-attribute attribute of the variable directive";
+        private static final String VAR_ALIAS =
+            "the alias attribute of the variable directive";
+        private static final String TAG_DYNAMIC =
+            "the dynamic-attributes attribute of the tag directive";
+        private HashMap nameTable = new HashMap();
+        private HashMap nameFromTable = new HashMap();
+
+        public TagFileDirectiveVisitor(Compiler compiler,
+                                       TagLibraryInfo tagLibInfo,
+                                       String name,
+                                       String path) {
+            err = compiler.getErrorDispatcher();
+            this.tagLibInfo = tagLibInfo;
+            this.name = name;
+            this.path = path;
+            attributeVector = new Vector();
+            variableVector = new Vector();
+        }
+
+        public void visit(Node.TagDirective n) throws JasperException {
+
+            JspUtil.checkAttributes("Tag directive", n, tagDirectiveAttrs,
+                                    err);
+
+            bodycontent = checkConflict(n, bodycontent, "body-content");
+            if (bodycontent != null &&
+                    !bodycontent.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY) &&
+                    !bodycontent.equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT) &&
+                    !bodycontent.equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)) {
+                err.jspError(n, "jsp.error.tagdirective.badbodycontent",
+                             bodycontent);
+            }
+            dynamicAttrsMapName = checkConflict(n, dynamicAttrsMapName,
+                                                "dynamic-attributes");
+            if (dynamicAttrsMapName != null) {
+                checkUniqueName(dynamicAttrsMapName, TAG_DYNAMIC, n);
+            }
+            smallIcon = checkConflict(n, smallIcon, "small-icon");
+            largeIcon = checkConflict(n, largeIcon, "large-icon");
+            description = checkConflict(n, description, "description");
+            displayName = checkConflict(n, displayName, "display-name");
+            example = checkConflict(n, example, "example");
+        }
+
+        private String checkConflict(Node n, String oldAttrValue, String attr)
+                throws JasperException {
+
+            String result = oldAttrValue;
+            String attrValue = n.getAttributeValue(attr);
+            if (attrValue != null) {
+                if (oldAttrValue != null && !oldAttrValue.equals(attrValue)) {
+                    err.jspError(n, "jsp.error.tag.conflict.attr", attr,
+                                 oldAttrValue, attrValue);
+                }
+                result = attrValue;
+            }
+            return result;
+        }
+            
+
+        public void visit(Node.AttributeDirective n) throws JasperException {
+
+            JspUtil.checkAttributes("Attribute directive", n,
+                                    attributeDirectiveAttrs, err);
+
+            String attrName = n.getAttributeValue("name");
+            boolean required = JspUtil.booleanValue(
+                                        n.getAttributeValue("required"));
+            boolean rtexprvalue = true;
+            String rtexprvalueString = n.getAttributeValue("rtexprvalue");
+            if (rtexprvalueString != null) {
+                rtexprvalue = JspUtil.booleanValue( rtexprvalueString );
+            }
+            boolean fragment = JspUtil.booleanValue(
+                                        n.getAttributeValue("fragment"));
+            String type = n.getAttributeValue("type");
+            if (fragment) {
+                // type is fixed to "JspFragment" and a translation error
+                // must occur if specified.
+                if (type != null) {
+                    err.jspError(n, "jsp.error.fragmentwithtype");
+                }
+                // rtexprvalue is fixed to "true" and a translation error
+                // must occur if specified.
+                rtexprvalue = true;
+                if( rtexprvalueString != null ) {
+                    err.jspError(n, "jsp.error.frgmentwithrtexprvalue" );
+                }
+            } else {
+                if (type == null)
+                    type = "java.lang.String";
+            }
+
+            TagAttributeInfo tagAttributeInfo =
+                    new TagAttributeInfo(attrName, required, type, rtexprvalue,
+                                         fragment);
+            attributeVector.addElement(tagAttributeInfo);
+            checkUniqueName(attrName, ATTR_NAME, n, tagAttributeInfo);
+        }
+
+        public void visit(Node.VariableDirective n) throws JasperException {
+
+            JspUtil.checkAttributes("Variable directive", n,
+                                    variableDirectiveAttrs, err);
+
+            String nameGiven = n.getAttributeValue("name-given");
+            String nameFromAttribute = n.getAttributeValue("name-from-attribute");
+            if (nameGiven == null && nameFromAttribute == null) {
+                err.jspError("jsp.error.variable.either.name");
+            }
+
+            if (nameGiven != null && nameFromAttribute != null) {
+                err.jspError("jsp.error.variable.both.name");
+            }
+
+            String alias = n.getAttributeValue("alias");
+            if (nameFromAttribute != null && alias == null ||
+                nameFromAttribute == null && alias != null) {
+                err.jspError("jsp.error.variable.alias");
+            }
+
+            String className = n.getAttributeValue("variable-class");
+            if (className == null)
+                className = "java.lang.String";
+
+            String declareStr = n.getAttributeValue("declare");
+            boolean declare = true;
+            if (declareStr != null)
+                declare = JspUtil.booleanValue(declareStr);
+
+            int scope = VariableInfo.NESTED;
+            String scopeStr = n.getAttributeValue("scope");
+            if (scopeStr != null) {
+                if ("NESTED".equals(scopeStr)) {
+                    // Already the default
+                } else if ("AT_BEGIN".equals(scopeStr)) {
+                    scope = VariableInfo.AT_BEGIN;
+                } else if ("AT_END".equals(scopeStr)) {
+                    scope = VariableInfo.AT_END;
+                }
+            }
+
+            if (nameFromAttribute != null) {
+                /*
+		 * An alias has been specified. We use 'nameGiven' to hold the
+		 * value of the alias, and 'nameFromAttribute' to hold the 
+		 * name of the attribute whose value (at invocation-time)
+		 * denotes the name of the variable that is being aliased
+		 */
+                nameGiven = alias;
+                checkUniqueName(nameFromAttribute, VAR_NAME_FROM, n);
+                checkUniqueName(alias, VAR_ALIAS, n);
+            }
+            else {
+                // name-given specified
+                checkUniqueName(nameGiven, VAR_NAME_GIVEN, n);
+            }
+                
+            variableVector.addElement(new TagVariableInfo(
+                                                nameGiven,
+                                                nameFromAttribute,
+                                                className,
+                                                declare,
+                                                scope));
+        }
+
+        /*
+         * Returns the vector of attributes corresponding to attribute
+         * directives.
+         */
+        public Vector getAttributesVector() {
+            return attributeVector;
+        }
+
+        /*
+         * Returns the vector of variables corresponding to variable
+         * directives.
+         */        
+        public Vector getVariablesVector() {
+            return variableVector;
+        }
+
+	/*
+	 * Returns the value of the dynamic-attributes tag directive
+	 * attribute.
+	 */
+	public String getDynamicAttributesMapName() {
+	    return dynamicAttrsMapName;
+	}
+
+        public TagInfo getTagInfo() throws JasperException {
+
+            if (name == null) {
+                // XXX Get it from tag file name
+            }
+
+            if (bodycontent == null) {
+                bodycontent = TagInfo.BODY_CONTENT_SCRIPTLESS;
+            }
+
+            String tagClassName = JspUtil.getTagHandlerClassName(path, err);
+
+            TagVariableInfo[] tagVariableInfos
+                = new TagVariableInfo[variableVector.size()];
+            variableVector.copyInto(tagVariableInfos);
+
+            TagAttributeInfo[] tagAttributeInfo
+                = new TagAttributeInfo[attributeVector.size()];
+            attributeVector.copyInto(tagAttributeInfo);
+
+            return new JasperTagInfo(name,
+			       tagClassName,
+			       bodycontent,
+			       description,
+			       tagLibInfo,
+			       tei,
+			       tagAttributeInfo,
+			       displayName,
+			       smallIcon,
+			       largeIcon,
+			       tagVariableInfos,
+			       dynamicAttrsMapName);
+        }
+
+        static class NameEntry {
+            private String type;
+            private Node node;
+            private TagAttributeInfo attr;
+
+            NameEntry(String type, Node node, TagAttributeInfo attr) {
+                this.type = type;
+                this.node = node;
+                this.attr = attr;
+            }
+
+            String getType() { return type;}
+            Node getNode() { return node; }
+            TagAttributeInfo getTagAttributeInfo() { return attr; }
+        }
+
+        /**
+         * Reports a translation error if names specified in attributes of
+         * directives are not unique in this translation unit.
+         *
+         * The value of the following attributes must be unique.
+         *   1. 'name' attribute of an attribute directive
+         *   2. 'name-given' attribute of a variable directive
+         *   3. 'alias' attribute of variable directive
+         *   4. 'dynamic-attributes' of a tag directive
+         * except that 'dynamic-attributes' can (and must) have the same
+         * value when it appears in multiple tag directives.
+         *
+         * Also, 'name-from' attribute of a variable directive cannot have
+         * the same value as that from another variable directive.
+         */
+        private void checkUniqueName(String name, String type, Node n)
+                throws JasperException {
+            checkUniqueName(name, type, n, null);
+        }
+
+        private void checkUniqueName(String name, String type, Node n,
+                                     TagAttributeInfo attr)
+                throws JasperException {
+
+            HashMap table = (type == VAR_NAME_FROM)? nameFromTable: nameTable;
+            NameEntry nameEntry = (NameEntry) table.get(name);
+            if (nameEntry != null) {
+                if (type != TAG_DYNAMIC || nameEntry.getType() != TAG_DYNAMIC) {
+                    int line = nameEntry.getNode().getStart().getLineNumber();
+                    err.jspError(n, "jsp.error.tagfile.nameNotUnique",
+                         type, nameEntry.getType(), Integer.toString(line));
+                }
+            } else {
+                table.put(name, new NameEntry(type, n, attr));
+            }
+        }
+
+        /**
+         * Perform miscellean checks after the nodes are visited.
+         */
+        void postCheck() throws JasperException {
+            // Check that var.name-from-attributes has valid values.
+	    Iterator iter = nameFromTable.keySet().iterator();
+            while (iter.hasNext()) {
+                String nameFrom = (String) iter.next();
+                NameEntry nameEntry = (NameEntry) nameTable.get(nameFrom);
+                NameEntry nameFromEntry =
+                    (NameEntry) nameFromTable.get(nameFrom);
+                Node nameFromNode = nameFromEntry.getNode();
+                if (nameEntry == null) {
+                    err.jspError(nameFromNode,
+                                 "jsp.error.tagfile.nameFrom.noAttribute",
+                                 nameFrom);
+                } else {
+                    Node node = nameEntry.getNode();
+                    TagAttributeInfo tagAttr = nameEntry.getTagAttributeInfo();
+                    if (! "java.lang.String".equals(tagAttr.getTypeName())
+                            || ! tagAttr.isRequired()
+                            || tagAttr.canBeRequestTime()){
+                        err.jspError(nameFromNode,
+                            "jsp.error.tagfile.nameFrom.badAttribute",
+                            nameFrom,
+                            Integer.toString(node.getStart().getLineNumber()));
+                     }
+                }
+            }
+        }
+    }
+
+    /**
+     * Parses the tag file, and collects information on the directives included
+     * in it.  The method is used to obtain the info on the tag file, when the 
+     * handler that it represents is referenced.  The tag file is not compiled
+     * here.
+     *
+     * @param pc the current ParserController used in this compilation
+     * @param name the tag name as specified in the TLD
+     * @param tagfile the path for the tagfile
+     * @param tagLibInfo the TagLibraryInfo object associated with this TagInfo
+     * @return a TagInfo object assembled from the directives in the tag file.
+     */
+    public static TagInfo parseTagFileDirectives(ParserController pc,
+						 String name,
+						 String path,
+						 TagLibraryInfo tagLibInfo)
+                        throws JasperException {
+
+        ErrorDispatcher err = pc.getCompiler().getErrorDispatcher();
+
+        Node.Nodes page = null;
+        try {
+            page = pc.parseTagFileDirectives(path);
+        } catch (FileNotFoundException e) {
+            err.jspError("jsp.error.file.not.found", path);
+        } catch (IOException e) {
+            err.jspError("jsp.error.file.not.found", path);
+        }
+
+        TagFileDirectiveVisitor tagFileVisitor
+            = new TagFileDirectiveVisitor(pc.getCompiler(), tagLibInfo, name,
+                                          path);
+        page.visit(tagFileVisitor);
+        tagFileVisitor.postCheck();
+
+        return tagFileVisitor.getTagInfo();
+    }
+
+    /**
+     * Compiles and loads a tagfile.
+     */
+    private Class loadTagFile(Compiler compiler,
+                              String tagFilePath, TagInfo tagInfo,
+                              PageInfo parentPageInfo)
+        throws JasperException {
+
+        JspCompilationContext ctxt = compiler.getCompilationContext();
+        JspRuntimeContext rctxt = ctxt.getRuntimeContext();
+        JspServletWrapper wrapper =
+                (JspServletWrapper) rctxt.getWrapper(tagFilePath);
+
+        synchronized(rctxt) {
+            if (wrapper == null) {
+                wrapper = new JspServletWrapper(ctxt.getServletContext(),
+                                                ctxt.getOptions(),
+                                                tagFilePath,
+                                                tagInfo,
+                                                ctxt.getRuntimeContext(),
+                                                (URL) ctxt.getTagFileJarUrls().get(tagFilePath));
+                    rctxt.addWrapper(tagFilePath,wrapper);
+
+		// Use same classloader and classpath for compiling tag files
+		wrapper.getJspEngineContext().setClassLoader(
+				(URLClassLoader) ctxt.getClassLoader());
+		wrapper.getJspEngineContext().setClassPath(ctxt.getClassPath());
+            }
+            else {
+                // Make sure that JspCompilationContext gets the latest TagInfo
+                // for the tag file.  TagInfo instance was created the last
+                // time the tag file was scanned for directives, and the tag
+                // file may have been modified since then.
+                wrapper.getJspEngineContext().setTagInfo(tagInfo);
+            }
+
+            Class tagClazz;
+            int tripCount = wrapper.incTripCount();
+            try {
+                if (tripCount > 0) {
+                    // When tripCount is greater than zero, a circular
+                    // dependency exists.  The circularily dependant tag
+                    // file is compiled in prototype mode, to avoid infinite
+                    // recursion.
+
+                    JspServletWrapper tempWrapper
+                        = new JspServletWrapper(ctxt.getServletContext(),
+                                                ctxt.getOptions(),
+                                                tagFilePath,
+                                                tagInfo,
+                                                ctxt.getRuntimeContext(),
+                                                (URL) ctxt.getTagFileJarUrls().get(tagFilePath));
+                    tagClazz = tempWrapper.loadTagFilePrototype();
+                    tempVector.add(
+                               tempWrapper.getJspEngineContext().getCompiler());
+                } else {
+                    tagClazz = wrapper.loadTagFile();
+                }
+            } finally {
+                wrapper.decTripCount();
+            }
+        
+            // Add the dependants for this tag file to its parent's
+            // dependant list.  The only reliable dependency information
+            // can only be obtained from the tag instance.
+            try {
+                Object tagIns = tagClazz.newInstance();
+                if (tagIns instanceof JspSourceDependent) {
+                    Iterator iter = 
+                        ((List) ((JspSourceDependent) tagIns).getDependants()).iterator();
+                    while (iter.hasNext()) {
+                        parentPageInfo.addDependant((String)iter.next());
+                    }
+                }
+            } catch (Exception e) {
+                // ignore errors
+            }
+        
+            return tagClazz;
+        }
+    }
+
+
+    /*
+     * Visitor which scans the page and looks for tag handlers that are tag
+     * files, compiling (if necessary) and loading them.
+     */ 
+    private class TagFileLoaderVisitor extends Node.Visitor {
+
+        private Compiler compiler;
+        private PageInfo pageInfo;
+
+        TagFileLoaderVisitor(Compiler compiler) {
+            
+            this.compiler = compiler;
+            this.pageInfo = compiler.getPageInfo();
+        }
+
+        public void visit(Node.CustomTag n) throws JasperException {
+            TagFileInfo tagFileInfo = n.getTagFileInfo();
+            if (tagFileInfo != null) {
+                String tagFilePath = tagFileInfo.getPath();
+		JspCompilationContext ctxt = compiler.getCompilationContext();
+		if (ctxt.getTagFileJarUrls().get(tagFilePath) == null) {
+		    // Omit tag file dependency info on jar files for now.
+                    pageInfo.addDependant(tagFilePath);
+		}
+                Class c = loadTagFile(compiler, tagFilePath, n.getTagInfo(),
+                                      pageInfo);
+                n.setTagHandlerClass(c);
+            }
+            visitBody(n);
+        }
+    }
+
+    /**
+     * Implements a phase of the translation that compiles (if necessary)
+     * the tag files used in a JSP files.  The directives in the tag files
+     * are assumed to have been proccessed and encapsulated as TagFileInfo
+     * in the CustomTag nodes.
+     */
+    public void loadTagFiles(Compiler compiler, Node.Nodes page)
+                throws JasperException {
+
+        tempVector = new Vector();
+        page.visit(new TagFileLoaderVisitor(compiler));
+    }
+
+    /**
+     * Removed the java and class files for the tag prototype 
+     * generated from the current compilation.
+     * @param classFileName If non-null, remove only the class file with
+     *        with this name.
+     */
+    public void removeProtoTypeFiles(String classFileName) {
+        Iterator iter = tempVector.iterator();
+        while (iter.hasNext()) {
+            Compiler c = (Compiler) iter.next();
+            if (classFileName == null) {
+                c.removeGeneratedClassFiles();
+            } else if (classFileName.equals(
+                        c.getCompilationContext().getClassFileName())) {
+                c.removeGeneratedClassFiles();
+                tempVector.remove(c);
+                return;
+            }
+        }
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/TagLibraryInfoImpl.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/TagLibraryInfoImpl.java
new file mode 100644
index 0000000..f2e1d28
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/TagLibraryInfoImpl.java
@@ -0,0 +1,754 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import javax.servlet.jsp.tagext.FunctionInfo;
+import javax.servlet.jsp.tagext.PageData;
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagExtraInfo;
+import javax.servlet.jsp.tagext.TagFileInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.servlet.jsp.tagext.TagLibraryValidator;
+import javax.servlet.jsp.tagext.TagVariableInfo;
+import javax.servlet.jsp.tagext.ValidationMessage;
+import javax.servlet.jsp.tagext.VariableInfo;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.apache.jasper.xmlparser.TreeNode;
+
+/**
+ * Implementation of the TagLibraryInfo class from the JSP spec. 
+ *
+ * @author Anil K. Vijendran
+ * @author Mandar Raje
+ * @author Pierre Delisle
+ * @author Kin-man Chung
+ * @author Jan Luehe
+ */
+class TagLibraryInfoImpl extends TagLibraryInfo implements TagConstants {
+
+    // Logger
+    private Log log = LogFactory.getLog(TagLibraryInfoImpl.class);
+
+    private Hashtable jarEntries;
+    private JspCompilationContext ctxt;
+    private ErrorDispatcher err;
+    private ParserController parserController;
+
+    private final void print(String name, String value, PrintWriter w) {
+        if (value != null) {
+            w.print(name+" = {\n\t");
+            w.print(value);
+            w.print("\n}\n");
+        }
+    }
+
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        PrintWriter out = new PrintWriter(sw);
+        print("tlibversion", tlibversion, out);
+        print("jspversion", jspversion, out);
+        print("shortname", shortname, out);
+        print("urn", urn, out);
+        print("info", info, out);
+        print("uri", uri, out);
+        print("tagLibraryValidator", tagLibraryValidator.toString(), out);
+
+        for(int i = 0; i < tags.length; i++)
+            out.println(tags[i].toString());
+        
+        for(int i = 0; i < tagFiles.length; i++)
+            out.println(tagFiles[i].toString());
+        
+        for(int i = 0; i < functions.length; i++)
+            out.println(functions[i].toString());
+        
+        return sw.toString();
+    }
+    
+    // XXX FIXME
+    // resolveRelativeUri and/or getResourceAsStream don't seem to properly
+    // handle relative paths when dealing when home and getDocBase are set
+    // the following is a workaround until these problems are resolved.
+    private InputStream getResourceAsStream(String uri) 
+        throws FileNotFoundException 
+    {
+        try {
+            // see if file exists on the filesystem first
+            String real = ctxt.getRealPath(uri);
+            if (real == null) {
+                return ctxt.getResourceAsStream(uri);
+            } else {
+                return new FileInputStream(real);
+            }
+        }
+        catch (FileNotFoundException ex) {
+            // if file not found on filesystem, get the resource through
+            // the context
+            return ctxt.getResourceAsStream(uri);
+        }
+       
+    }
+
+    /**
+     * Constructor.
+     */
+    public TagLibraryInfoImpl(JspCompilationContext ctxt,
+                              ParserController pc,
+                              String prefix, 
+                              String uriIn,
+                              String[] location,
+                              ErrorDispatcher err) throws JasperException {
+        super(prefix, uriIn);
+
+        this.ctxt = ctxt;
+        this.parserController = pc;
+        this.err = err;
+        InputStream in = null;
+        JarFile jarFile = null;
+
+        if (location == null) {
+            // The URI points to the TLD itself or to a JAR file in which the
+            // TLD is stored
+            location = generateTLDLocation(uri, ctxt);
+        }
+
+        try {
+            if (!location[0].endsWith("jar")) {
+                // Location points to TLD file
+                try {
+                    in = getResourceAsStream(location[0]);
+                    if (in == null) {
+                        throw new FileNotFoundException(location[0]);
+                    }
+                } catch (FileNotFoundException ex) {
+                    err.jspError("jsp.error.file.not.found", location[0]);
+                }
+
+                parseTLD(ctxt, location[0], in, null);
+                // Add TLD to dependency list
+                PageInfo pageInfo = ctxt.createCompiler().getPageInfo();
+                if (pageInfo != null) {
+                    pageInfo.addDependant(location[0]);
+                }
+            } else {
+                // Tag library is packaged in JAR file
+                try {
+                    URL jarFileUrl = new URL("jar:" + location[0] + "!/");
+                    JarURLConnection conn =
+                        (JarURLConnection) jarFileUrl.openConnection();
+                    conn.setUseCaches(false);
+                    conn.connect();
+                    jarFile = conn.getJarFile();
+                    ZipEntry jarEntry = jarFile.getEntry(location[1]);
+                    in = jarFile.getInputStream(jarEntry);
+                    parseTLD(ctxt, location[0], in, jarFileUrl);
+                } catch (Exception ex) {
+                    err.jspError("jsp.error.tld.unable_to_read", location[0],
+                                 location[1], ex.toString());
+                }
+            }
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Throwable t) {}
+            }
+            if (jarFile != null) {
+                try {
+                    jarFile.close();
+                } catch (Throwable t) {}
+            }
+        }
+
+    }
+    
+    /*
+     * @param ctxt The JSP compilation context
+     * @param uri The TLD's uri
+     * @param in The TLD's input stream
+     * @param jarFileUrl The JAR file containing the TLD, or null if the tag
+     * library is not packaged in a JAR
+     */
+    private void parseTLD(JspCompilationContext ctxt,
+                          String uri, InputStream in, URL jarFileUrl) 
+        throws JasperException
+    {
+        Vector tagVector = new Vector();
+        Vector tagFileVector = new Vector();
+        Hashtable functionTable = new Hashtable();
+
+        // Create an iterator over the child elements of our <taglib> element
+        ParserUtils pu = new ParserUtils();
+        TreeNode tld = pu.parseXMLDocument(uri, in);
+
+        // Check to see if the <taglib> root element contains a 'version'
+        // attribute, which was added in JSP 2.0 to replace the <jsp-version>
+        // subelement
+        this.jspversion = tld.findAttribute("version");
+
+        // Process each child element of our <taglib> element
+        Iterator list = tld.findChildren();
+
+        while (list.hasNext()) {
+            TreeNode element = (TreeNode) list.next();
+            String tname = element.getName();
+
+            if ("tlibversion".equals(tname)                    // JSP 1.1
+                        || "tlib-version".equals(tname)) {     // JSP 1.2
+                this.tlibversion = element.getBody();
+            } else if ("jspversion".equals(tname)
+                        || "jsp-version".equals(tname)) {
+                this.jspversion = element.getBody();
+            } else if ("shortname".equals(tname) ||
+                     "short-name".equals(tname))
+                this.shortname = element.getBody();
+            else if ("uri".equals(tname))
+                this.urn = element.getBody();
+            else if ("info".equals(tname) ||
+                     "description".equals(tname))
+                this.info = element.getBody();
+            else if ("validator".equals(tname))
+                this.tagLibraryValidator = createValidator(element);
+            else if ("tag".equals(tname))
+                tagVector.addElement(createTagInfo(element, jspversion));
+            else if ("tag-file".equals(tname)) {
+                TagFileInfo tagFileInfo = createTagFileInfo(element, uri,
+                                                            jarFileUrl);
+                tagFileVector.addElement(tagFileInfo);
+            } else if ("function".equals(tname)) {         // JSP2.0
+                FunctionInfo funcInfo = createFunctionInfo(element);
+                String funcName = funcInfo.getName();
+                if (functionTable.containsKey(funcName)) {
+                    err.jspError("jsp.error.tld.fn.duplicate.name",
+                                 funcName, uri);
+
+                }
+                functionTable.put(funcName, funcInfo);
+            } else if ("display-name".equals(tname) ||    // Ignored elements
+                     "small-icon".equals(tname) ||
+                     "large-icon".equals(tname) ||
+                     "listener".equals(tname)) {
+                ;
+            } else if ("taglib-extension".equals(tname)) {
+                // Recognized but ignored
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage(
+                            "jsp.warning.unknown.element.in.taglib", tname));
+                }
+            }
+
+        }
+
+        if (tlibversion == null) {
+            err.jspError("jsp.error.tld.mandatory.element.missing", 
+                         "tlib-version");
+        }
+        if (jspversion == null) {
+            err.jspError("jsp.error.tld.mandatory.element.missing",
+                         "jsp-version");
+        }
+
+        this.tags = new TagInfo[tagVector.size()];
+        tagVector.copyInto (this.tags);
+
+        this.tagFiles = new TagFileInfo[tagFileVector.size()];
+        tagFileVector.copyInto (this.tagFiles);
+
+        this.functions = new FunctionInfo[functionTable.size()];
+        int i=0;
+        Enumeration enumeration = functionTable.elements();
+        while (enumeration.hasMoreElements()) {
+            this.functions[i++] = (FunctionInfo) enumeration.nextElement();
+        }
+    }
+    
+    /*
+     * @param uri The uri of the TLD
+     * @param ctxt The compilation context
+     *
+     * @return String array whose first element denotes the path to the TLD.
+     * If the path to the TLD points to a jar file, then the second element
+     * denotes the name of the TLD entry in the jar file, which is hardcoded
+     * to META-INF/taglib.tld.
+     */
+    private String[] generateTLDLocation(String uri,
+                                         JspCompilationContext ctxt)
+                throws JasperException {
+
+        int uriType = TldLocationsCache.uriType(uri);
+        if (uriType == TldLocationsCache.ABS_URI) {
+            err.jspError("jsp.error.taglibDirective.absUriCannotBeResolved",
+                         uri);
+        } else if (uriType == TldLocationsCache.NOROOT_REL_URI) {
+            uri = ctxt.resolveRelativeUri(uri);
+        }
+
+        String[] location = new String[2];
+        location[0] = uri;
+        if (location[0].endsWith("jar")) {
+            URL url = null;
+            try {
+                url = ctxt.getResource(location[0]);
+            } catch (Exception ex) {
+                err.jspError("jsp.error.tld.unable_to_get_jar", location[0],
+                             ex.toString());
+            }
+            if (url == null) {
+                err.jspError("jsp.error.tld.missing_jar", location[0]);
+            }
+            location[0] = url.toString();
+            location[1] = "META-INF/taglib.tld";
+        }
+
+        return location;
+    }
+
+    private TagInfo createTagInfo(TreeNode elem, String jspVersion)
+            throws JasperException {
+
+        String tagName = null;
+        String tagClassName = null;
+        String teiClassName = null;
+
+        /*
+         * Default body content for JSP 1.2 tag handlers (<body-content> has
+         * become mandatory in JSP 2.0, because the default would be invalid
+         * for simple tag handlers)
+         */
+        String bodycontent = "JSP";
+
+        String info = null;
+        String displayName = null;
+        String smallIcon = null;
+        String largeIcon = null;
+        boolean dynamicAttributes = false;
+        
+        Vector attributeVector = new Vector();
+        Vector variableVector = new Vector();
+        Iterator list = elem.findChildren();
+        while (list.hasNext()) {
+            TreeNode element = (TreeNode) list.next();
+            String tname = element.getName();
+
+            if ("name".equals(tname)) {
+                tagName = element.getBody();
+            } else if ("tagclass".equals(tname) ||
+                     "tag-class".equals(tname)) {
+                tagClassName = element.getBody();
+            } else if ("teiclass".equals(tname) ||
+                     "tei-class".equals(tname)) {
+                teiClassName = element.getBody();
+            } else if ("bodycontent".equals(tname) ||
+                     "body-content".equals(tname)) {
+                bodycontent = element.getBody();
+            } else if ("display-name".equals(tname)) {
+                displayName = element.getBody();
+            } else if ("small-icon".equals(tname)) {
+                smallIcon = element.getBody();
+            } else if ("large-icon".equals(tname)) {
+                largeIcon = element.getBody();
+            } else if ("icon".equals(tname)) {
+                TreeNode icon = element.findChild("small-icon");
+                if (icon != null) {
+                    smallIcon = icon.getBody();
+                }
+                icon = element.findChild("large-icon");
+                if (icon != null) {
+                    largeIcon = icon.getBody();
+                }
+            } else if ("info".equals(tname) ||
+                     "description".equals(tname)) {
+                info = element.getBody();
+            } else if ("variable".equals(tname)) {
+                variableVector.addElement(createVariable(element));
+            } else if ("attribute".equals(tname)) {
+                attributeVector.addElement(createAttribute(element, jspVersion));
+            } else if ("dynamic-attributes".equals(tname)) {
+                dynamicAttributes = JspUtil.booleanValue(element.getBody());
+            } else if ("example".equals(tname)) {
+                // Ignored elements
+            } else if ("tag-extension".equals(tname)) {
+                // Ignored
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage(
+                            "jsp.warning.unknown.element.in.tag", tname));
+                }
+            }
+        }
+
+        TagExtraInfo tei = null;
+        if (teiClassName != null && !teiClassName.equals("")) {
+            try {
+                Class teiClass = ctxt.getClassLoader().loadClass(teiClassName);
+                tei = (TagExtraInfo) teiClass.newInstance();
+            } catch (Exception e) {
+                err.jspError("jsp.error.teiclass.instantiation", teiClassName,
+                             e);
+            }
+        }
+
+        TagAttributeInfo[] tagAttributeInfo
+            = new TagAttributeInfo[attributeVector.size()];
+        attributeVector.copyInto(tagAttributeInfo);
+
+        TagVariableInfo[] tagVariableInfos
+            = new TagVariableInfo[variableVector.size()];
+        variableVector.copyInto(tagVariableInfos);
+
+        TagInfo taginfo = new TagInfo(tagName,
+                                      tagClassName,
+                                      bodycontent,
+                                      info,
+                                      this, 
+                                      tei,
+                                      tagAttributeInfo,
+                                      displayName,
+                                      smallIcon,
+                                      largeIcon,
+                                      tagVariableInfos,
+                                      dynamicAttributes);
+        return taginfo;
+    }
+
+    /*
+     * Parses the tag file directives of the given TagFile and turns them into
+     * a TagInfo.
+     *
+     * @param elem The <tag-file> element in the TLD
+     * @param uri The location of the TLD, in case the tag file is specified
+     * relative to it
+     * @param jarFile The JAR file, in case the tag file is packaged in a JAR
+     *
+     * @return TagInfo correspoding to tag file directives
+     */
+    private TagFileInfo createTagFileInfo(TreeNode elem, String uri,
+                                          URL jarFileUrl)
+                throws JasperException {
+
+        String name = null;
+        String path = null;
+
+        Iterator list = elem.findChildren();
+        while (list.hasNext()) {
+            TreeNode child = (TreeNode) list.next();
+            String tname = child.getName();
+            if ("name".equals(tname)) {
+                name = child.getBody();
+            } else if ("path".equals(tname)) {
+                path = child.getBody();
+            } else if ("example".equals(tname)) {
+                // Ignore <example> element: Bugzilla 33538
+            } else if ("tag-extension".equals(tname)) {
+                // Ignore <tag-extension> element: Bugzilla 33538
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage(
+                            "jsp.warning.unknown.element.in.tagfile", tname));
+                }
+            }
+        }
+
+        if (path.startsWith("/META-INF/tags")) {
+            // Tag file packaged in JAR
+            ctxt.getTagFileJarUrls().put(path, jarFileUrl);
+        } else if (!path.startsWith("/WEB-INF/tags")) {
+            err.jspError("jsp.error.tagfile.illegalPath", path);
+        }
+
+        TagInfo tagInfo
+            = TagFileProcessor.parseTagFileDirectives(parserController, name,
+                                                      path, this);
+        return new TagFileInfo(name, path, tagInfo);
+    }
+
+    TagAttributeInfo createAttribute(TreeNode elem, String jspVersion) {
+        String name = null;
+        String type = null;
+        boolean required = false, rtexprvalue = false, reqTime = false,
+            isFragment = false;
+        
+        Iterator list = elem.findChildren();
+        while (list.hasNext()) {
+            TreeNode element = (TreeNode) list.next();
+            String tname = element.getName();
+
+            if ("name".equals(tname)) {
+                name = element.getBody();
+            } else if ("required".equals(tname)) {
+                String s = element.getBody();
+                if (s != null)
+                    required = JspUtil.booleanValue(s);
+            } else if ("rtexprvalue".equals(tname)) {
+                String s = element.getBody();
+                if (s != null)
+                    rtexprvalue = JspUtil.booleanValue(s);
+            } else if ("type".equals(tname)) {
+                type = element.getBody();
+                if ("1.2".equals(jspVersion)
+                        && (type.equals("Boolean")
+                            || type.equals("Byte")
+                            || type.equals("Character")
+                            || type.equals("Double")
+                            || type.equals("Float")
+                            || type.equals("Integer")
+                            || type.equals("Long")
+                            || type.equals("Object")
+                            || type.equals("Short")
+                            || type.equals("String"))) {
+                    type = "java.lang." + type;
+                }
+            } else if ("fragment".equals(tname)) {
+                String s = element.getBody();
+                if (s != null) {
+                    isFragment = JspUtil.booleanValue(s);
+                }
+            } else if ("description".equals(tname) ||    // Ignored elements
+                       false) {
+                ;
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage(
+                            "jsp.warning.unknown.element.in.attribute", tname));
+                }
+            }
+        }
+
+        if (isFragment) {
+            /*
+             * According to JSP.C-3 ("TLD Schema Element Structure - tag"), 
+             * 'type' and 'rtexprvalue' must not be specified if 'fragment'
+             * has been specified (this will be enforced by validating parser).
+             * Also, if 'fragment' is TRUE, 'type' is fixed at
+             * javax.servlet.jsp.tagext.JspFragment, and 'rtexprvalue' is
+             * fixed at true. See also JSP.8.5.2.
+             */
+            type = "javax.servlet.jsp.tagext.JspFragment";
+            rtexprvalue = true;            
+        }
+        
+        if (!rtexprvalue) {
+            // According to JSP spec, for static values (those determined at
+            // translation time) the type is fixed at java.lang.String.
+            type = "java.lang.String";
+        }
+
+        return new TagAttributeInfo(name, required, type, rtexprvalue,
+                                    isFragment);
+    }
+
+    TagVariableInfo createVariable(TreeNode elem) {
+        String nameGiven = null;
+        String nameFromAttribute = null;
+        String className = "java.lang.String";
+        boolean declare = true;
+        int scope = VariableInfo.NESTED;
+
+        Iterator list = elem.findChildren();
+        while (list.hasNext()) {
+            TreeNode element = (TreeNode) list.next();
+            String tname = element.getName();
+            if ("name-given".equals(tname))
+                nameGiven = element.getBody();
+            else if ("name-from-attribute".equals(tname))
+                nameFromAttribute = element.getBody();
+            else if ("variable-class".equals(tname))
+                className = element.getBody();
+            else if ("declare".equals(tname)) {
+                String s = element.getBody();
+                if (s != null)
+                    declare = JspUtil.booleanValue(s);
+            } else if ("scope".equals(tname)) {
+                String s = element.getBody();
+                if (s != null) {
+                    if ("NESTED".equals(s)) {
+                        scope = VariableInfo.NESTED;
+                    } else if ("AT_BEGIN".equals(s)) {
+                        scope = VariableInfo.AT_BEGIN;
+                    } else if ("AT_END".equals(s)) {
+                        scope = VariableInfo.AT_END;
+                    }
+                }
+            } else if ("description".equals(tname) ||    // Ignored elements
+                     false ) {
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage(
+                            "jsp.warning.unknown.element.in.variable", tname));
+                }
+            }
+        }
+        return new TagVariableInfo(nameGiven, nameFromAttribute,
+                                   className, declare, scope);
+    }
+
+    private TagLibraryValidator createValidator(TreeNode elem)
+            throws JasperException {
+
+        String validatorClass = null;
+        Map initParams = new Hashtable();
+
+        Iterator list = elem.findChildren();
+        while (list.hasNext()) {
+            TreeNode element = (TreeNode) list.next();
+            String tname = element.getName();
+            if ("validator-class".equals(tname))
+                validatorClass = element.getBody();
+            else if ("init-param".equals(tname)) {
+                String[] initParam = createInitParam(element);
+                initParams.put(initParam[0], initParam[1]);
+            } else if ("description".equals(tname) ||    // Ignored elements
+                     false ) {
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage(
+                            "jsp.warning.unknown.element.in.validator", tname));
+                }
+            }
+        }
+
+        TagLibraryValidator tlv = null;
+        if (validatorClass != null && !validatorClass.equals("")) {
+            try {
+                Class tlvClass = 
+                    ctxt.getClassLoader().loadClass(validatorClass);
+                tlv = (TagLibraryValidator)tlvClass.newInstance();
+            } catch (Exception e) {
+                err.jspError("jsp.error.tlvclass.instantiation",
+                             validatorClass, e);
+            }
+        }
+        if (tlv != null) {
+            tlv.setInitParameters(initParams);
+        }
+        return tlv;
+    }
+
+    String[] createInitParam(TreeNode elem) {
+        String[] initParam = new String[2];
+        
+        Iterator list = elem.findChildren();
+        while (list.hasNext()) {
+            TreeNode element = (TreeNode) list.next();
+            String tname = element.getName();
+            if ("param-name".equals(tname)) {
+                initParam[0] = element.getBody();
+            } else if ("param-value".equals(tname)) {
+                initParam[1] = element.getBody();
+            } else if ("description".equals(tname)) {
+                ; // Do nothing
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage(
+                            "jsp.warning.unknown.element.in.initParam", tname));
+                }
+            }
+        }
+        return initParam;
+    }
+
+    FunctionInfo createFunctionInfo(TreeNode elem) {
+
+        String name = null;
+        String klass = null;
+        String signature = null;
+
+        Iterator list = elem.findChildren();
+        while (list.hasNext()) {
+            TreeNode element = (TreeNode) list.next();
+            String tname = element.getName();
+
+            if ("name".equals(tname)) {
+                name = element.getBody();
+            } else if ("function-class".equals(tname)) {
+                klass = element.getBody();
+            } else if ("function-signature".equals(tname)) {
+                signature = element.getBody();
+            } else if ("display-name".equals(tname) ||    // Ignored elements
+                     "small-icon".equals(tname) ||
+                     "large-icon".equals(tname) ||
+                     "description".equals(tname) || 
+                     "example".equals(tname)) {
+            } else {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage(
+                        "jsp.warning.unknown.element.in.function", tname));
+                }
+            }
+        }
+
+        return new FunctionInfo(name, klass, signature);
+    }
+
+
+    //*********************************************************************
+    // Until javax.servlet.jsp.tagext.TagLibraryInfo is fixed
+
+    /**
+     * The instance (if any) for the TagLibraryValidator class.
+     * 
+     * @return The TagLibraryValidator instance, if any.
+     */
+    public TagLibraryValidator getTagLibraryValidator() {
+        return tagLibraryValidator;
+    }
+
+    /**
+     * Translation-time validation of the XML document
+     * associated with the JSP page.
+     * This is a convenience method on the associated 
+     * TagLibraryValidator class.
+     *
+     * @param thePage The JSP page object
+     * @return A string indicating whether the page is valid or not.
+     */
+    public ValidationMessage[] validate(PageData thePage) {
+        TagLibraryValidator tlv = getTagLibraryValidator();
+        if (tlv == null) return null;
+
+        String uri = getURI();
+        if (uri.startsWith("/")) {
+            uri = URN_JSPTLD + uri;
+        }
+
+        return tlv.validate(getPrefixString(), uri, thePage);
+    }
+
+    protected TagLibraryValidator tagLibraryValidator; 
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/TagPluginManager.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/TagPluginManager.java
new file mode 100644
index 0000000..a918f00
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/TagPluginManager.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.util.*;
+import java.io.*;
+import javax.servlet.ServletContext;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.apache.jasper.xmlparser.TreeNode;
+import org.apache.jasper.compiler.tagplugin.TagPlugin;
+import org.apache.jasper.compiler.tagplugin.TagPluginContext;
+
+/**
+ * Manages tag plugin optimizations.
+ * @author Kin-man Chung
+ */
+
+public class TagPluginManager {
+
+    private static final String TAG_PLUGINS_XML = "/WEB-INF/tagPlugins.xml";
+    private static final String TAG_PLUGINS_ROOT_ELEM = "tag-plugins";
+
+    private boolean initialized = false;
+    private HashMap tagPlugins = null;
+    private ServletContext ctxt;
+    private PageInfo pageInfo;
+
+    public TagPluginManager(ServletContext ctxt) {
+	this.ctxt = ctxt;
+    }
+
+    public void apply(Node.Nodes page, ErrorDispatcher err, PageInfo pageInfo)
+	    throws JasperException {
+
+	init(err);
+	if (tagPlugins == null || tagPlugins.size() == 0) {
+	    return;
+	}
+
+	this.pageInfo = pageInfo;
+
+        page.visit(new Node.Visitor() {
+            public void visit(Node.CustomTag n)
+                    throws JasperException {
+                invokePlugin(n);
+                visitBody(n);
+            }
+        });
+
+    }
+ 
+    private void init(ErrorDispatcher err) throws JasperException {
+	if (initialized)
+	    return;
+
+	InputStream is = ctxt.getResourceAsStream(TAG_PLUGINS_XML);
+	if (is == null)
+	    return;
+
+	TreeNode root = (new ParserUtils()).parseXMLDocument(TAG_PLUGINS_XML,
+							     is);
+	if (root == null) {
+	    return;
+	}
+
+	if (!TAG_PLUGINS_ROOT_ELEM.equals(root.getName())) {
+	    err.jspError("jsp.error.plugin.wrongRootElement", TAG_PLUGINS_XML,
+			 TAG_PLUGINS_ROOT_ELEM);
+	}
+
+	tagPlugins = new HashMap();
+	Iterator pluginList = root.findChildren("tag-plugin");
+	while (pluginList.hasNext()) {
+	    TreeNode pluginNode = (TreeNode) pluginList.next();
+            TreeNode tagClassNode = pluginNode.findChild("tag-class");
+	    if (tagClassNode == null) {
+		// Error
+		return;
+	    }
+	    String tagClass = tagClassNode.getBody().trim();
+	    TreeNode pluginClassNode = pluginNode.findChild("plugin-class");
+	    if (pluginClassNode == null) {
+		// Error
+		return;
+	    }
+
+	    String pluginClassStr = pluginClassNode.getBody();
+	    TagPlugin tagPlugin = null;
+	    try {
+		Class pluginClass = Class.forName(pluginClassStr);
+		tagPlugin = (TagPlugin) pluginClass.newInstance();
+	    } catch (Exception e) {
+		throw new JasperException(e);
+	    }
+	    if (tagPlugin == null) {
+		return;
+	    }
+	    tagPlugins.put(tagClass, tagPlugin);
+	}
+	initialized = true;
+    }
+
+    /**
+     * Invoke tag plugin for the given custom tag, if a plugin exists for 
+     * the custom tag's tag handler.
+     *
+     * The given custom tag node will be manipulated by the plugin.
+     */
+    private void invokePlugin(Node.CustomTag n) {
+	TagPlugin tagPlugin = (TagPlugin)
+		tagPlugins.get(n.getTagHandlerClass().getName());
+	if (tagPlugin == null) {
+	    return;
+	}
+
+	TagPluginContext tagPluginContext = new TagPluginContextImpl(n, pageInfo);
+	n.setTagPluginContext(tagPluginContext);
+	tagPlugin.doTag(tagPluginContext);
+    }
+
+    static class TagPluginContextImpl implements TagPluginContext {
+	private Node.CustomTag node;
+	private Node.Nodes curNodes;
+	private PageInfo pageInfo;
+	private HashMap pluginAttributes;
+
+	TagPluginContextImpl(Node.CustomTag n, PageInfo pageInfo) {
+	    this.node = n;
+	    this.pageInfo = pageInfo;
+	    curNodes = new Node.Nodes();
+	    n.setAtETag(curNodes);
+	    curNodes = new Node.Nodes();
+	    n.setAtSTag(curNodes);
+	    n.setUseTagPlugin(true);
+	    pluginAttributes = new HashMap();
+	}
+
+	public TagPluginContext getParentContext() {
+	    Node parent = node.getParent();
+	    if (! (parent instanceof Node.CustomTag)) {
+		return null;
+	    }
+	    return ((Node.CustomTag) parent).getTagPluginContext();
+	}
+
+	public void setPluginAttribute(String key, Object value) {
+	    pluginAttributes.put(key, value);
+	}
+
+	public Object getPluginAttribute(String key) {
+	    return pluginAttributes.get(key);
+	}
+
+	public boolean isScriptless() {
+	    return node.getChildInfo().isScriptless();
+	}
+
+	public boolean isConstantAttribute(String attribute) {
+	    Node.JspAttribute attr = getNodeAttribute(attribute);
+	    if (attr == null)
+		return false;
+	    return attr.isLiteral();
+	}
+
+	public String getConstantAttribute(String attribute) {
+	    Node.JspAttribute attr = getNodeAttribute(attribute);
+            if (attr == null)
+		return null;
+	    return attr.getValue();
+	}
+
+	public boolean isAttributeSpecified(String attribute) {
+	    return getNodeAttribute(attribute) != null;
+	}
+
+	public String getTemporaryVariableName() {
+	    return JspUtil.nextTemporaryVariableName();
+	}
+
+	public void generateImport(String imp) {
+	    pageInfo.addImport(imp);
+	}
+
+	public void generateDeclaration(String id, String text) {
+	    if (pageInfo.isPluginDeclared(id)) {
+		return;
+	    }
+	    curNodes.add(new Node.Declaration(text, node.getStart(), null));
+	}
+
+	public void generateJavaSource(String sourceCode) {
+	    curNodes.add(new Node.Scriptlet(sourceCode, node.getStart(),
+					    null));
+	}
+
+	public void generateAttribute(String attributeName) {
+	    curNodes.add(new Node.AttributeGenerator(node.getStart(),
+						     attributeName,
+						     node));
+	}
+
+	public void dontUseTagPlugin() {
+	    node.setUseTagPlugin(false);
+	}
+
+	public void generateBody() {
+	    // Since we'll generate the body anyway, this is really a nop, 
+	    // except for the fact that it lets us put the Java sources the
+	    // plugins produce in the correct order (w.r.t the body).
+	    curNodes = node.getAtETag();
+	}
+
+	private Node.JspAttribute getNodeAttribute(String attribute) {
+	    Node.JspAttribute[] attrs = node.getJspAttributes();
+	    for (int i=0; attrs != null && i < attrs.length; i++) {
+		if (attrs[i].getName().equals(attribute)) {
+		    return attrs[i];
+		}
+	    }
+	    return null;
+	}
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/TextOptimizer.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/TextOptimizer.java
new file mode 100644
index 0000000..e4f209c
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/TextOptimizer.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.compiler;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.Options;
+
+/**
+ */
+public class TextOptimizer {
+
+    /**
+     * A visitor to concatenate contiguous template texts.
+     */
+    static class TextCatVisitor extends Node.Visitor {
+
+        private Options options;
+        private int textNodeCount = 0;
+        private Node.TemplateText firstTextNode = null;
+        private StringBuffer textBuffer;
+        private final String emptyText = new String("");
+
+        public TextCatVisitor(Compiler compiler) {
+            options = compiler.getCompilationContext().getOptions();
+        }
+
+        public void doVisit(Node n) throws JasperException {
+            collectText();
+        }
+
+	/*
+         * The following directis are ignored in text concatenation
+         */
+
+        public void visit(Node.PageDirective n) throws JasperException {
+        }
+
+        public void visit(Node.TagDirective n) throws JasperException {
+        }
+
+        public void visit(Node.TaglibDirective n) throws JasperException {
+        }
+
+        public void visit(Node.AttributeDirective n) throws JasperException {
+        }
+
+        public void visit(Node.VariableDirective n) throws JasperException {
+        }
+
+        /*
+         * Don't concatenate text across body boundaries
+         */
+        public void visitBody(Node n) throws JasperException {
+            super.visitBody(n);
+            collectText();
+        }
+
+        public void visit(Node.TemplateText n) throws JasperException {
+
+            if (options.getTrimSpaces() && n.isAllSpace()) {
+                n.setText(emptyText);
+                return;
+            }
+
+            if (textNodeCount++ == 0) {
+                firstTextNode = n;
+                textBuffer = new StringBuffer(n.getText());
+            } else {
+                // Append text to text buffer
+                textBuffer.append(n.getText());
+                n.setText(emptyText);
+            }
+        }
+
+        /**
+         * This method breaks concatenation mode.  As a side effect it copies
+         * the concatenated string to the first text node 
+         */
+        private void collectText() {
+
+            if (textNodeCount > 1) {
+                // Copy the text in buffer into the first template text node.
+                firstTextNode.setText(textBuffer.toString());
+            }
+            textNodeCount = 0;
+        }
+
+    }
+
+    public static void concatenate(Compiler compiler, Node.Nodes page)
+            throws JasperException {
+
+        TextCatVisitor v = new TextCatVisitor(compiler);
+        page.visit(v);
+
+	// Cleanup, in case the page ends with a template text
+        v.collectText();
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/TldLocationsCache.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/TldLocationsCache.java
new file mode 100644
index 0000000..815d25d
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/TldLocationsCache.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLConnection;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jasper.Constants;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.apache.jasper.xmlparser.TreeNode;
+
+/**
+ * A container for all tag libraries that are defined "globally"
+ * for the web application.
+ * 
+ * Tag Libraries can be defined globally in one of two ways:
+ *   1. Via <taglib> elements in web.xml:
+ *      the uri and location of the tag-library are specified in
+ *      the <taglib> element.
+ *   2. Via packaged jar files that contain .tld files
+ *      within the META-INF directory, or some subdirectory
+ *      of it. The taglib is 'global' if it has the <uri>
+ *      element defined.
+ *
+ * A mapping between the taglib URI and its associated TaglibraryInfoImpl
+ * is maintained in this container.
+ * Actually, that's what we'd like to do. However, because of the
+ * way the classes TagLibraryInfo and TagInfo have been defined,
+ * it is not currently possible to share an instance of TagLibraryInfo
+ * across page invocations. A bug has been submitted to the spec lead.
+ * In the mean time, all we do is save the 'location' where the
+ * TLD associated with a taglib URI can be found.
+ *
+ * When a JSP page has a taglib directive, the mappings in this container
+ * are first searched (see method getLocation()).
+ * If a mapping is found, then the location of the TLD is returned.
+ * If no mapping is found, then the uri specified
+ * in the taglib directive is to be interpreted as the location for
+ * the TLD of this tag library.
+ *
+ * @author Pierre Delisle
+ * @author Jan Luehe
+ */
+
+public class TldLocationsCache {
+
+    // Logger
+    private Log log = LogFactory.getLog(TldLocationsCache.class);
+
+    /**
+     * The types of URI one may specify for a tag library
+     */
+    public static final int ABS_URI = 0;
+    public static final int ROOT_REL_URI = 1;
+    public static final int NOROOT_REL_URI = 2;
+
+    private static final String WEB_XML = "/WEB-INF/web.xml";
+    private static final String FILE_PROTOCOL = "file:";
+    private static final String JAR_FILE_SUFFIX = ".jar";
+
+    // Names of JARs that are known not to contain any TLDs
+    private static HashSet noTldJars;
+
+    /**
+     * The mapping of the 'global' tag library URI to the location (resource
+     * path) of the TLD associated with that tag library. The location is
+     * returned as a String array:
+     *    [0] The location
+     *    [1] If the location is a jar file, this is the location of the tld.
+     */
+    private Hashtable mappings;
+
+    private boolean initialized;
+    private ServletContext ctxt;
+    private boolean redeployMode;
+
+    //*********************************************************************
+    // Constructor and Initilizations
+
+    /*
+     * Initializes the set of JARs that are known not to contain any TLDs
+     */
+    static {
+        noTldJars = new HashSet();
+        noTldJars.add("ant.jar");
+        noTldJars.add("catalina.jar");
+        noTldJars.add("catalina-ant.jar");
+        noTldJars.add("catalina-cluster.jar");
+        noTldJars.add("catalina-optional.jar");
+        noTldJars.add("catalina-i18n-fr.jar");
+        noTldJars.add("catalina-i18n-ja.jar");
+        noTldJars.add("catalina-i18n-es.jar");
+        noTldJars.add("commons-dbcp.jar");
+        noTldJars.add("commons-modeler.jar");
+        noTldJars.add("commons-logging-api.jar");
+        noTldJars.add("commons-beanutils.jar");
+        noTldJars.add("commons-fileupload-1.0.jar");
+        noTldJars.add("commons-pool.jar");
+        noTldJars.add("commons-digester.jar");
+        noTldJars.add("commons-logging.jar");
+        noTldJars.add("commons-collections.jar");
+        noTldJars.add("commons-el.jar");
+        noTldJars.add("jakarta-regexp-1.2.jar");
+        noTldJars.add("jasper-compiler.jar");
+        noTldJars.add("jasper-runtime.jar");
+        noTldJars.add("jmx.jar");
+        noTldJars.add("jmx-tools.jar");
+        noTldJars.add("jsp-api.jar");
+        noTldJars.add("jkshm.jar");
+        noTldJars.add("jkconfig.jar");
+        noTldJars.add("naming-common.jar");
+        noTldJars.add("naming-resources.jar");
+        noTldJars.add("naming-factory.jar");
+        noTldJars.add("naming-java.jar");
+        noTldJars.add("servlet-api.jar");
+        noTldJars.add("servlets-default.jar");
+        noTldJars.add("servlets-invoker.jar");
+        noTldJars.add("servlets-common.jar");
+        noTldJars.add("servlets-webdav.jar");
+        noTldJars.add("tomcat-util.jar");
+        noTldJars.add("tomcat-http11.jar");
+        noTldJars.add("tomcat-jni.jar");
+        noTldJars.add("tomcat-jk.jar");
+        noTldJars.add("tomcat-jk2.jar");
+        noTldJars.add("tomcat-coyote.jar");
+        noTldJars.add("xercesImpl.jar");
+        noTldJars.add("xmlParserAPIs.jar");
+        noTldJars.add("xml-apis.jar");
+        // JARs from J2SE runtime
+        noTldJars.add("sunjce_provider.jar");
+        noTldJars.add("ldapsec.jar");
+        noTldJars.add("localedata.jar");
+        noTldJars.add("dnsns.jar");
+    }
+    
+    public TldLocationsCache(ServletContext ctxt) {
+        this(ctxt, true);
+    }
+
+    /** Constructor. 
+     *
+     * @param ctxt the servlet context of the web application in which Jasper 
+     * is running
+     * @param redeployMode if true, then the compiler will allow redeploying 
+     * a tag library from the same jar, at the expense of slowing down the
+     * server a bit. Note that this may only work on JDK 1.3.1_01a and later,
+     * because of JDK bug 4211817 fixed in this release.
+     * If redeployMode is false, a faster but less capable mode will be used.
+     */
+    public TldLocationsCache(ServletContext ctxt, boolean redeployMode) {
+        this.ctxt = ctxt;
+        this.redeployMode = redeployMode;
+        mappings = new Hashtable();
+        initialized = false;
+    }
+
+    /**
+     * Sets the list of JARs that are known not to contain any TLDs.
+     *
+     * @param jarNames List of comma-separated names of JAR files that are 
+     * known not to contain any TLDs 
+     */
+    public static void setNoTldJars(String jarNames) {
+        if (jarNames != null) {
+            noTldJars.clear();
+            StringTokenizer tokenizer = new StringTokenizer(jarNames, ",");
+            while (tokenizer.hasMoreElements()) {
+                noTldJars.add(tokenizer.nextToken());
+            }
+        }
+    }
+
+    /**
+     * Gets the 'location' of the TLD associated with the given taglib 'uri'.
+     *
+     * Returns null if the uri is not associated with any tag library 'exposed'
+     * in the web application. A tag library is 'exposed' either explicitly in
+     * web.xml or implicitly via the uri tag in the TLD of a taglib deployed
+     * in a jar file (WEB-INF/lib).
+     * 
+     * @param uri The taglib uri
+     *
+     * @return An array of two Strings: The first element denotes the real
+     * path to the TLD. If the path to the TLD points to a jar file, then the
+     * second element denotes the name of the TLD entry in the jar file.
+     * Returns null if the uri is not associated with any tag library 'exposed'
+     * in the web application.
+     */
+    public String[] getLocation(String uri) throws JasperException {
+        if (!initialized) {
+            init();
+        }
+        return (String[]) mappings.get(uri);
+    }
+
+    /** 
+     * Returns the type of a URI:
+     *     ABS_URI
+     *     ROOT_REL_URI
+     *     NOROOT_REL_URI
+     */
+    public static int uriType(String uri) {
+        if (uri.indexOf(':') != -1) {
+            return ABS_URI;
+        } else if (uri.startsWith("/")) {
+            return ROOT_REL_URI;
+        } else {
+            return NOROOT_REL_URI;
+        }
+    }
+
+    private void init() throws JasperException {
+        if (initialized) return;
+        try {
+            processWebDotXml();
+            scanJars();
+            processTldsInFileSystem("/WEB-INF/");
+            initialized = true;
+        } catch (Exception ex) {
+            throw new JasperException(Localizer.getMessage(
+                    "jsp.error.internal.tldinit", ex.getMessage()));
+        }
+    }
+
+    /*
+     * Populates taglib map described in web.xml.
+     */    
+    private void processWebDotXml() throws Exception {
+
+        InputStream is = null;
+
+        try {
+            // Acquire input stream to web application deployment descriptor
+            String altDDName = (String)ctxt.getAttribute(
+                                                    Constants.ALT_DD_ATTR);
+            if (altDDName != null) {
+                try {
+                    is = new FileInputStream(altDDName);
+                } catch (FileNotFoundException e) {
+                    if (log.isWarnEnabled()) {
+                        log.warn(Localizer.getMessage(
+                                            "jsp.error.internal.filenotfound",
+                                            altDDName));
+                    }
+                }
+            } else {
+                is = ctxt.getResourceAsStream(WEB_XML);
+                if (is == null && log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage(
+                                            "jsp.error.internal.filenotfound",
+                                            WEB_XML));
+                }
+            }
+
+            if (is == null) {
+                return;
+            }
+
+            // Parse the web application deployment descriptor
+            TreeNode webtld = null;
+            // altDDName is the absolute path of the DD
+            if (altDDName != null) {
+                webtld = new ParserUtils().parseXMLDocument(altDDName, is);
+            } else {
+                webtld = new ParserUtils().parseXMLDocument(WEB_XML, is);
+            }
+
+            // Allow taglib to be an element of the root or jsp-config (JSP2.0)
+            TreeNode jspConfig = webtld.findChild("jsp-config");
+            if (jspConfig != null) {
+                webtld = jspConfig;
+            }
+            Iterator taglibs = webtld.findChildren("taglib");
+            while (taglibs.hasNext()) {
+
+                // Parse the next <taglib> element
+                TreeNode taglib = (TreeNode) taglibs.next();
+                String tagUri = null;
+                String tagLoc = null;
+                TreeNode child = taglib.findChild("taglib-uri");
+                if (child != null)
+                    tagUri = child.getBody();
+                child = taglib.findChild("taglib-location");
+                if (child != null)
+                    tagLoc = child.getBody();
+
+                // Save this location if appropriate
+                if (tagLoc == null)
+                    continue;
+                if (uriType(tagLoc) == NOROOT_REL_URI)
+                    tagLoc = "/WEB-INF/" + tagLoc;
+                String tagLoc2 = null;
+                if (tagLoc.endsWith(JAR_FILE_SUFFIX)) {
+                    tagLoc = ctxt.getResource(tagLoc).toString();
+                    tagLoc2 = "META-INF/taglib.tld";
+                }
+                mappings.put(tagUri, new String[] { tagLoc, tagLoc2 });
+            }
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (Throwable t) {}
+            }
+        }
+    }
+
+    /**
+     * Scans the given JarURLConnection for TLD files located in META-INF
+     * (or a subdirectory of it), adding an implicit map entry to the taglib
+     * map for any TLD that has a <uri> element.
+     *
+     * @param conn The JarURLConnection to the JAR file to scan
+     * @param ignore true if any exceptions raised when processing the given
+     * JAR should be ignored, false otherwise
+     */
+    private void scanJar(JarURLConnection conn, boolean ignore)
+                throws JasperException {
+
+        JarFile jarFile = null;
+        String resourcePath = conn.getJarFileURL().toString();
+        try {
+            if (redeployMode) {
+                conn.setUseCaches(false);
+            }
+            jarFile = conn.getJarFile();
+            Enumeration entries = jarFile.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry entry = (JarEntry) entries.nextElement();
+                String name = entry.getName();
+                if (!name.startsWith("META-INF/")) continue;
+                if (!name.endsWith(".tld")) continue;
+                InputStream stream = jarFile.getInputStream(entry);
+                try {
+                    String uri = getUriFromTld(resourcePath, stream);
+                    // Add implicit map entry only if its uri is not already
+                    // present in the map
+                    if (uri != null && mappings.get(uri) == null) {
+                        mappings.put(uri, new String[]{ resourcePath, name });
+                    }
+                } finally {
+                    if (stream != null) {
+                        try {
+                            stream.close();
+                        } catch (Throwable t) {
+                            // do nothing
+                        }
+                    }
+                }
+            }
+        } catch (Exception ex) {
+            if (!redeployMode) {
+                // if not in redeploy mode, close the jar in case of an error
+                if (jarFile != null) {
+                    try {
+                        jarFile.close();
+                    } catch (Throwable t) {
+                        // ignore
+                    }
+                }
+            }
+            if (!ignore) {
+                throw new JasperException(ex);
+            }
+        } finally {
+            if (redeployMode) {
+                // if in redeploy mode, always close the jar
+                if (jarFile != null) {
+                    try {
+                        jarFile.close();
+                    } catch (Throwable t) {
+                        // ignore
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * Searches the filesystem under /WEB-INF for any TLD files, and adds
+     * an implicit map entry to the taglib map for any TLD that has a <uri>
+     * element.
+     */
+    private void processTldsInFileSystem(String startPath)
+            throws Exception {
+
+        Set dirList = ctxt.getResourcePaths(startPath);
+        if (dirList != null) {
+            Iterator it = dirList.iterator();
+            while (it.hasNext()) {
+                String path = (String) it.next();
+                if (path.endsWith("/")) {
+                    processTldsInFileSystem(path);
+                }
+                if (!path.endsWith(".tld")) {
+                    continue;
+                }
+                InputStream stream = ctxt.getResourceAsStream(path);
+                String uri = null;
+                try {
+                    uri = getUriFromTld(path, stream);
+                } finally {
+                    if (stream != null) {
+                        try {
+                            stream.close();
+                        } catch (Throwable t) {
+                            // do nothing
+                        }
+                    }
+                }
+                // Add implicit map entry only if its uri is not already
+                // present in the map
+                if (uri != null && mappings.get(uri) == null) {
+                    mappings.put(uri, new String[] { path, null });
+                }
+            }
+        }
+    }
+
+    /*
+     * Returns the value of the uri element of the given TLD, or null if the
+     * given TLD does not contain any such element.
+     */
+    private String getUriFromTld(String resourcePath, InputStream in) 
+        throws JasperException
+    {
+        // Parse the tag library descriptor at the specified resource path
+        TreeNode tld = new ParserUtils().parseXMLDocument(resourcePath, in);
+        TreeNode uri = tld.findChild("uri");
+        if (uri != null) {
+            String body = uri.getBody();
+            if (body != null)
+                return body;
+        }
+
+        return null;
+    }
+
+    /*
+     * Scans all JARs accessible to the webapp's classloader and its
+     * parent classloaders for TLDs.
+     * 
+     * The list of JARs always includes the JARs under WEB-INF/lib, as well as
+     * all shared JARs in the classloader delegation chain of the webapp's
+     * classloader.
+     *
+     * Considering JARs in the classloader delegation chain constitutes a
+     * Tomcat-specific extension to the TLD search
+     * order defined in the JSP spec. It allows tag libraries packaged as JAR
+     * files to be shared by web applications by simply dropping them in a 
+     * location that all web applications have access to (e.g.,
+     * <CATALINA_HOME>/common/lib).
+     *
+     * The set of shared JARs to be scanned for TLDs is narrowed down by
+     * the <tt>noTldJars</tt> class variable, which contains the names of JARs
+     * that are known not to contain any TLDs.
+     */
+    private void scanJars() throws Exception {
+
+        ClassLoader webappLoader
+            = Thread.currentThread().getContextClassLoader();
+        ClassLoader loader = webappLoader;
+
+        while (loader != null) {
+            if (loader instanceof URLClassLoader) {
+                URL[] urls = ((URLClassLoader) loader).getURLs();
+                for (int i=0; i<urls.length; i++) {
+                    URLConnection conn = urls[i].openConnection();
+                    if (conn instanceof JarURLConnection) {
+                        if (needScanJar(loader, webappLoader,
+                                        ((JarURLConnection) conn).getJarFile().getName())) {
+                            scanJar((JarURLConnection) conn, true);
+                        }
+                    } else {
+                        String urlStr = urls[i].toString();
+                        if (urlStr.startsWith(FILE_PROTOCOL)
+                                && urlStr.endsWith(JAR_FILE_SUFFIX)
+                                && needScanJar(loader, webappLoader, urlStr)) {
+                            URL jarURL = new URL("jar:" + urlStr + "!/");
+                            scanJar((JarURLConnection) jarURL.openConnection(),
+                                    true);
+                        }
+                    }
+                }
+            }
+
+            loader = loader.getParent();
+        }
+    }
+
+    /*
+     * Determines if the JAR file with the given <tt>jarPath</tt> needs to be
+     * scanned for TLDs.
+     *
+     * @param loader The current classloader in the parent chain
+     * @param webappLoader The webapp classloader
+     * @param jarPath The JAR file path
+     *
+     * @return TRUE if the JAR file identified by <tt>jarPath</tt> needs to be
+     * scanned for TLDs, FALSE otherwise
+     */
+    private boolean needScanJar(ClassLoader loader, ClassLoader webappLoader,
+                                String jarPath) {
+        if (loader == webappLoader) {
+            // JARs under WEB-INF/lib must be scanned unconditionally according
+            // to the spec.
+            return true;
+        } else {
+            String jarName = jarPath;
+            int slash = jarPath.lastIndexOf('/');
+            if (slash >= 0) {
+                jarName = jarPath.substring(slash + 1);
+            }
+            return (!noTldJars.contains(jarName));
+        }
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/Validator.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/Validator.java
new file mode 100644
index 0000000..2e8e447
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/Validator.java
@@ -0,0 +1,1553 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import javax.servlet.jsp.el.FunctionMapper;
+import javax.servlet.jsp.tagext.FunctionInfo;
+import javax.servlet.jsp.tagext.JspFragment;
+import javax.servlet.jsp.tagext.PageData;
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagData;
+import javax.servlet.jsp.tagext.TagExtraInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.servlet.jsp.tagext.ValidationMessage;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.xml.sax.Attributes;
+
+/**
+ * Performs validation on the page elements.  Attributes are checked for
+ * mandatory presence, entry value validity, and consistency.  As a
+ * side effect, some page global value (such as those from page direcitves)
+ * are stored, for later use.
+ *
+ * @author Kin-man Chung
+ * @author Jan Luehe
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+class Validator {
+
+    /**
+     * A visitor to validate and extract page directive info
+     */
+    static class DirectiveVisitor extends Node.Visitor {
+
+	private PageInfo pageInfo;
+	private ErrorDispatcher err;
+
+	private static final JspUtil.ValidAttribute[] pageDirectiveAttrs = {
+	    new JspUtil.ValidAttribute("language"),
+	    new JspUtil.ValidAttribute("extends"),
+	    new JspUtil.ValidAttribute("import"),
+	    new JspUtil.ValidAttribute("session"),
+	    new JspUtil.ValidAttribute("buffer"),
+	    new JspUtil.ValidAttribute("autoFlush"),
+	    new JspUtil.ValidAttribute("isThreadSafe"),
+	    new JspUtil.ValidAttribute("info"),
+	    new JspUtil.ValidAttribute("errorPage"),
+	    new JspUtil.ValidAttribute("isErrorPage"),
+	    new JspUtil.ValidAttribute("contentType"),
+	    new JspUtil.ValidAttribute("pageEncoding"),
+	    new JspUtil.ValidAttribute("isELIgnored")
+	};
+
+	private boolean pageEncodingSeen = false;
+
+	/*
+	 * Constructor
+	 */
+	DirectiveVisitor(Compiler compiler) throws JasperException {
+	    this.pageInfo = compiler.getPageInfo();
+	    this.err = compiler.getErrorDispatcher();
+	    JspCompilationContext ctxt = compiler.getCompilationContext();
+	}
+
+	public void visit(Node.IncludeDirective n) throws JasperException {
+            // Since pageDirectiveSeen flag only applies to the Current page
+            // save it here and restore it after the file is included.
+            boolean pageEncodingSeenSave = pageEncodingSeen;
+            pageEncodingSeen = false;
+            visitBody(n);
+            pageEncodingSeen = pageEncodingSeenSave;
+        }
+
+	public void visit(Node.PageDirective n) throws JasperException {    
+
+            JspUtil.checkAttributes("Page directive", n,
+                                    pageDirectiveAttrs, err);
+
+	    // JSP.2.10.1
+	    Attributes attrs = n.getAttributes();
+	    for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
+		String attr = attrs.getQName(i);
+		String value = attrs.getValue(i);
+
+		if ("language".equals(attr)) {
+		    if (pageInfo.getLanguage(false) == null) {
+			pageInfo.setLanguage(value, n, err, true);
+		    } else if (!pageInfo.getLanguage(false).equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.language",
+				     pageInfo.getLanguage(false), value);
+		    }
+		} else if ("extends".equals(attr)) {
+		    if (pageInfo.getExtends(false) == null) {
+			pageInfo.setExtends(value, n);
+		    } else if (!pageInfo.getExtends(false).equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.extends",
+				     pageInfo.getExtends(false), value);
+		    }
+		} else if ("contentType".equals(attr)) {
+		    if (pageInfo.getContentType() == null) {
+			pageInfo.setContentType(value);
+		    } else if (!pageInfo.getContentType().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.contenttype",
+				     pageInfo.getContentType(), value);
+		    }
+		} else if ("session".equals(attr)) {
+		    if (pageInfo.getSession() == null) {
+			pageInfo.setSession(value, n, err);
+		    } else if (!pageInfo.getSession().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.session",
+				     pageInfo.getSession(), value);
+		    }
+		} else if ("buffer".equals(attr)) {
+		    if (pageInfo.getBufferValue() == null) {
+			pageInfo.setBufferValue(value, n, err);
+		    } else if (!pageInfo.getBufferValue().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.buffer",
+				     pageInfo.getBufferValue(), value);
+		    }
+		} else if ("autoFlush".equals(attr)) {
+		    if (pageInfo.getAutoFlush() == null) {
+			pageInfo.setAutoFlush(value, n, err);
+		    } else if (!pageInfo.getAutoFlush().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.autoflush",
+				     pageInfo.getAutoFlush(), value);
+		    }
+		} else if ("isThreadSafe".equals(attr)) {
+		    if (pageInfo.getIsThreadSafe() == null) {
+			pageInfo.setIsThreadSafe(value, n, err);
+		    } else if (!pageInfo.getIsThreadSafe().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.isthreadsafe",
+				     pageInfo.getIsThreadSafe(), value);
+		    }
+		} else if ("isELIgnored".equals(attr)) {
+		    if (pageInfo.getIsELIgnored() == null) {
+                        pageInfo.setIsELIgnored(value, n, err, true);
+		    } else if (!pageInfo.getIsELIgnored().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.iselignored",
+				     pageInfo.getIsELIgnored(), value);
+		    }
+		} else if ("isErrorPage".equals(attr)) {
+		    if (pageInfo.getIsErrorPage() == null) {
+			pageInfo.setIsErrorPage(value, n, err);
+		    } else if (!pageInfo.getIsErrorPage().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.iserrorpage",
+				     pageInfo.getIsErrorPage(), value);
+		    }
+		} else if ("errorPage".equals(attr)) {
+		    if (pageInfo.getErrorPage() == null) {
+			pageInfo.setErrorPage(value);
+		    } else if (!pageInfo.getErrorPage().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.errorpage",
+				     pageInfo.getErrorPage(), value);
+		    }
+		} else if ("info".equals(attr)) {
+		    if (pageInfo.getInfo() == null) {
+			pageInfo.setInfo(value);
+		    } else if (!pageInfo.getInfo().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.info",
+				     pageInfo.getInfo(), value);
+		    }
+		} else if ("pageEncoding".equals(attr)) {
+		    if (pageEncodingSeen) 
+			err.jspError(n, "jsp.error.page.multi.pageencoding");
+		    // 'pageEncoding' can occur at most once per file
+		    pageEncodingSeen = true;
+		    comparePageEncodings(value, n);
+		}
+	    }
+
+	    // Check for bad combinations
+	    if (pageInfo.getBuffer() == 0 && !pageInfo.isAutoFlush())
+		err.jspError(n, "jsp.error.page.badCombo");
+
+	    // Attributes for imports for this node have been processed by
+	    // the parsers, just add them to pageInfo.
+	    pageInfo.addImports(n.getImports());
+	}
+
+	public void visit(Node.TagDirective n) throws JasperException {
+            // Note: Most of the validation is done in TagFileProcessor
+            // when it created a TagInfo object from the
+            // tag file in which the directive appeared.
+        
+            // This method does additional processing to collect page info
+            
+	    Attributes attrs = n.getAttributes();
+	    for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
+		String attr = attrs.getQName(i);
+		String value = attrs.getValue(i);
+
+		if ("language".equals(attr)) {
+		    if (pageInfo.getLanguage(false) == null) {
+			pageInfo.setLanguage(value, n, err, false);
+		    } else if (!pageInfo.getLanguage(false).equals(value)) {
+			err.jspError(n, "jsp.error.tag.conflict.language",
+				     pageInfo.getLanguage(false), value);
+		    }
+		} else if ("isELIgnored".equals(attr)) {
+		    if (pageInfo.getIsELIgnored() == null) {
+                        pageInfo.setIsELIgnored(value, n, err, false);
+		    } else if (!pageInfo.getIsELIgnored().equals(value)) {
+			err.jspError(n, "jsp.error.tag.conflict.iselignored",
+				     pageInfo.getIsELIgnored(), value);
+		    }
+		} else if ("pageEncoding".equals(attr)) {
+		    if (pageEncodingSeen) 
+			err.jspError(n, "jsp.error.tag.multi.pageencoding");
+		    pageEncodingSeen = true;
+		    n.getRoot().setPageEncoding(value);
+		}
+	    }
+
+	    // Attributes for imports for this node have been processed by
+	    // the parsers, just add them to pageInfo.
+	    pageInfo.addImports(n.getImports());
+	}
+
+	public void visit(Node.AttributeDirective n) throws JasperException {
+	    // Do nothing, since this attribute directive has already been
+	    // validated by TagFileProcessor when it created a TagInfo object
+	    // from the tag file in which the directive appeared
+	}
+
+	public void visit(Node.VariableDirective n) throws JasperException {
+	    // Do nothing, since this variable directive has already been
+	    // validated by TagFileProcessor when it created a TagInfo object
+	    // from the tag file in which the directive appeared
+	}
+
+        /*
+         * Compares page encodings specified in various places, and throws
+         * exception in case of page encoding mismatch.
+         *
+         * @param pageDirEnc The value of the pageEncoding attribute of the
+         * page directive
+         * @param pageDir The page directive node
+         *
+         * @throws JasperException in case of page encoding mismatch
+         */
+        private void comparePageEncodings(String pageDirEnc,
+                                          Node.PageDirective pageDir)
+                throws JasperException {
+
+            Node.Root root = pageDir.getRoot();
+            String configEnc = root.getJspConfigPageEncoding();
+
+            /*
+             * Compare the 'pageEncoding' attribute of the page directive with
+             * the encoding specified in the JSP config element whose URL
+             * pattern matches this page.
+             * Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as identical.
+             */
+            if (configEnc != null && !pageDirEnc.equals(configEnc) 
+                    && (!pageDirEnc.startsWith("UTF-16")
+                        || !configEnc.startsWith("UTF-16"))) {
+                err.jspError(pageDir,
+                             "jsp.error.config_pagedir_encoding_mismatch",
+                             configEnc, pageDirEnc);
+            }
+
+            /*
+             * Compare the 'pageEncoding' attribute of the page directive with
+             * the encoding specified in the XML prolog (only for XML syntax,
+             * and only if JSP document contains XML prolog with encoding
+             * declaration).
+             * Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as identical.
+             */
+	    if (root.isXmlSyntax() && root.isEncodingSpecifiedInProlog()) {
+		String pageEnc = root.getPageEncoding();
+                if (!pageDirEnc.equals(pageEnc) 
+                        && (!pageDirEnc.startsWith("UTF-16")
+                            || !pageEnc.startsWith("UTF-16"))) {
+                    err.jspError(pageDir,
+                                 "jsp.error.prolog_pagedir_encoding_mismatch",
+                                 pageEnc, pageDirEnc);
+                }
+            }
+        }
+    }
+
+    /**
+     * A visitor for validating nodes other than page directives
+     */
+    static class ValidateVisitor extends Node.Visitor {
+
+	private PageInfo pageInfo;
+	private ErrorDispatcher err;
+	private TagInfo tagInfo;
+        private ClassLoader loader;
+
+	private static final JspUtil.ValidAttribute[] jspRootAttrs = {
+            new JspUtil.ValidAttribute("xsi:schemaLocation"),
+	    new JspUtil.ValidAttribute("version", true) };
+
+	private static final JspUtil.ValidAttribute[] includeDirectiveAttrs = {
+	    new JspUtil.ValidAttribute("file", true) };
+
+	private static final JspUtil.ValidAttribute[] taglibDirectiveAttrs = {
+	    new JspUtil.ValidAttribute("uri"),
+	    new JspUtil.ValidAttribute("tagdir"),
+	    new JspUtil.ValidAttribute("prefix", true) };
+
+	private static final JspUtil.ValidAttribute[] includeActionAttrs = {
+	    new JspUtil.ValidAttribute("page", true, true),
+	    new JspUtil.ValidAttribute("flush") };
+
+	private static final JspUtil.ValidAttribute[] paramActionAttrs = {
+	    new JspUtil.ValidAttribute("name", true),
+	    new JspUtil.ValidAttribute("value", true, true) };
+
+	private static final JspUtil.ValidAttribute[] forwardActionAttrs = {
+	    new JspUtil.ValidAttribute("page", true, true) };
+
+	private static final JspUtil.ValidAttribute[] getPropertyAttrs = {
+	    new JspUtil.ValidAttribute("name", true),
+	    new JspUtil.ValidAttribute("property", true) };
+
+	private static final JspUtil.ValidAttribute[] setPropertyAttrs = {
+	    new JspUtil.ValidAttribute("name", true),
+	    new JspUtil.ValidAttribute("property", true),
+	    new JspUtil.ValidAttribute("value", false, true),
+	    new JspUtil.ValidAttribute("param") };
+
+	private static final JspUtil.ValidAttribute[] useBeanAttrs = {
+	    new JspUtil.ValidAttribute("id", true),
+	    new JspUtil.ValidAttribute("scope"),
+	    new JspUtil.ValidAttribute("class"),
+	    new JspUtil.ValidAttribute("type"),
+	    new JspUtil.ValidAttribute("beanName", false, true) };
+
+	private static final JspUtil.ValidAttribute[] plugInAttrs = {
+	    new JspUtil.ValidAttribute("type",true),
+	    new JspUtil.ValidAttribute("code", true),
+	    new JspUtil.ValidAttribute("codebase"),
+	    new JspUtil.ValidAttribute("align"),
+	    new JspUtil.ValidAttribute("archive"),
+	    new JspUtil.ValidAttribute("height", false, true),
+	    new JspUtil.ValidAttribute("hspace"),
+	    new JspUtil.ValidAttribute("jreversion"),
+	    new JspUtil.ValidAttribute("name"),
+	    new JspUtil.ValidAttribute("vspace"),
+	    new JspUtil.ValidAttribute("width", false, true),
+	    new JspUtil.ValidAttribute("nspluginurl"),
+	    new JspUtil.ValidAttribute("iepluginurl") };
+            
+        private static final JspUtil.ValidAttribute[] attributeAttrs = {
+            new JspUtil.ValidAttribute("name", true),
+            new JspUtil.ValidAttribute("trim") };
+            
+        private static final JspUtil.ValidAttribute[] invokeAttrs = {
+            new JspUtil.ValidAttribute("fragment", true),
+	    new JspUtil.ValidAttribute("var"),
+	    new JspUtil.ValidAttribute("varReader"),
+	    new JspUtil.ValidAttribute("scope") };
+
+        private static final JspUtil.ValidAttribute[] doBodyAttrs = {
+            new JspUtil.ValidAttribute("var"),
+	    new JspUtil.ValidAttribute("varReader"),
+	    new JspUtil.ValidAttribute("scope") };
+
+	private static final JspUtil.ValidAttribute[] jspOutputAttrs = {
+	    new JspUtil.ValidAttribute("omit-xml-declaration"),
+	    new JspUtil.ValidAttribute("doctype-root-element"),
+	    new JspUtil.ValidAttribute("doctype-public"),
+	    new JspUtil.ValidAttribute("doctype-system") };
+
+	/*
+	 * Constructor
+	 */
+	ValidateVisitor(Compiler compiler) {
+	    this.pageInfo = compiler.getPageInfo();
+	    this.err = compiler.getErrorDispatcher();
+	    this.tagInfo = compiler.getCompilationContext().getTagInfo();
+	    this.loader = compiler.getCompilationContext().getClassLoader();
+	}
+
+	public void visit(Node.JspRoot n) throws JasperException {
+	    JspUtil.checkAttributes("Jsp:root", n,
+				    jspRootAttrs, err);
+	    String version = n.getTextAttribute("version");
+	    if (!version.equals("1.2") && !version.equals("2.0")) {
+		err.jspError(n, "jsp.error.jsproot.version.invalid", version);
+	    }
+	    visitBody(n);
+	}
+
+	public void visit(Node.IncludeDirective n) throws JasperException {
+            JspUtil.checkAttributes("Include directive", n,
+                                    includeDirectiveAttrs, err);
+	    visitBody(n);
+	}
+
+	public void visit(Node.TaglibDirective n) throws JasperException {
+            JspUtil.checkAttributes("Taglib directive", n,
+                                    taglibDirectiveAttrs, err);
+	    // Either 'uri' or 'tagdir' attribute must be specified
+	    String uri = n.getAttributeValue("uri");
+	    String tagdir = n.getAttributeValue("tagdir");
+	    if (uri == null && tagdir == null) {
+		err.jspError(n, "jsp.error.taglibDirective.missing.location");
+	    }
+	    if (uri != null && tagdir != null) {
+		err.jspError(n, "jsp.error.taglibDirective.both_uri_and_tagdir");
+	    }
+	}
+
+	public void visit(Node.ParamAction n) throws JasperException {
+            JspUtil.checkAttributes("Param action", n,
+                                    paramActionAttrs, err);
+	    // make sure the value of the 'name' attribute is not a
+	    // request-time expression
+	    throwErrorIfExpression(n, "name", "jsp:param");
+	    n.setValue(getJspAttribute("value", null, null,
+				       n.getAttributeValue("value"),
+                                       java.lang.String.class,
+				       n, false));
+            visitBody(n);
+	}
+
+	public void visit(Node.ParamsAction n) throws JasperException {
+	    // Make sure we've got at least one nested jsp:param
+            Node.Nodes subElems = n.getBody();
+            if (subElems == null) {
+		err.jspError(n, "jsp.error.params.emptyBody");
+	    }
+            visitBody(n);
+	}
+
+	public void visit(Node.IncludeAction n) throws JasperException {
+            JspUtil.checkAttributes("Include action", n,
+                                    includeActionAttrs, err);
+	    n.setPage(getJspAttribute("page", null, null,
+				      n.getAttributeValue("page"), 
+                                      java.lang.String.class, n, false));
+	    visitBody(n);
+        };
+
+	public void visit(Node.ForwardAction n) throws JasperException {
+            JspUtil.checkAttributes("Forward", n,
+                                    forwardActionAttrs, err);
+	    n.setPage(getJspAttribute("page", null, null,
+				      n.getAttributeValue("page"), 
+                                      java.lang.String.class, n, false));
+	    visitBody(n);
+	}
+
+	public void visit(Node.GetProperty n) throws JasperException {
+            JspUtil.checkAttributes("GetProperty", n,
+                                    getPropertyAttrs, err);
+	}
+
+	public void visit(Node.SetProperty n) throws JasperException {
+            JspUtil.checkAttributes("SetProperty", n,
+                                    setPropertyAttrs, err);
+	    String name = n.getTextAttribute("name");
+	    String property = n.getTextAttribute("property");
+	    String param = n.getTextAttribute("param");
+	    String value = n.getAttributeValue("value");
+
+            n.setValue(getJspAttribute("value", null, null, value, 
+                java.lang.Object.class, n, false));
+
+            boolean valueSpecified = n.getValue() != null;
+
+	    if ("*".equals(property)) { 
+                if (param != null || valueSpecified)
+		    err.jspError(n, "jsp.error.setProperty.invalid");
+		
+            } else if (param != null && valueSpecified) {
+		err.jspError(n, "jsp.error.setProperty.invalid");
+	    }
+            
+            visitBody(n);
+	}
+
+	public void visit(Node.UseBean n) throws JasperException {
+            JspUtil.checkAttributes("UseBean", n,
+                                    useBeanAttrs, err);
+
+	    String name = n.getTextAttribute ("id");
+	    String scope = n.getTextAttribute ("scope");
+	    JspUtil.checkScope(scope, n, err);
+	    String className = n.getTextAttribute ("class");
+	    String type = n.getTextAttribute ("type");
+	    BeanRepository beanInfo = pageInfo.getBeanRepository();
+
+	    if (className == null && type == null)
+		err.jspError(n, "jsp.error.usebean.missingType");
+
+	    if (beanInfo.checkVariable(name))
+		err.jspError(n, "jsp.error.usebean.duplicate");
+
+	    if ("session".equals(scope) && !pageInfo.isSession())
+		err.jspError(n, "jsp.error.usebean.noSession");
+
+	    Node.JspAttribute jattr
+		= getJspAttribute("beanName", null, null,
+				  n.getAttributeValue("beanName"),
+				  java.lang.String.class, n, false);
+	    n.setBeanName(jattr);
+	    if (className != null && jattr != null)
+		err.jspError(n, "jsp.error.usebean.notBoth");
+
+	    if (className == null)
+		className = type;
+
+	    beanInfo.addBean(n, name, className, scope);
+
+	    visitBody(n);
+	}
+
+	public void visit(Node.PlugIn n) throws JasperException {
+            JspUtil.checkAttributes("Plugin", n, plugInAttrs, err);
+
+	    throwErrorIfExpression(n, "type", "jsp:plugin");
+	    throwErrorIfExpression(n, "code", "jsp:plugin");
+	    throwErrorIfExpression(n, "codebase", "jsp:plugin");
+	    throwErrorIfExpression(n, "align", "jsp:plugin");
+	    throwErrorIfExpression(n, "archive", "jsp:plugin");
+	    throwErrorIfExpression(n, "hspace", "jsp:plugin");
+	    throwErrorIfExpression(n, "jreversion", "jsp:plugin");
+	    throwErrorIfExpression(n, "name", "jsp:plugin");
+	    throwErrorIfExpression(n, "vspace", "jsp:plugin");
+	    throwErrorIfExpression(n, "nspluginurl", "jsp:plugin");
+	    throwErrorIfExpression(n, "iepluginurl", "jsp:plugin");
+
+	    String type = n.getTextAttribute("type");
+	    if (type == null)
+		err.jspError(n, "jsp.error.plugin.notype");
+	    if (!type.equals("bean") && !type.equals("applet"))
+		err.jspError(n, "jsp.error.plugin.badtype");
+	    if (n.getTextAttribute("code") == null)
+		err.jspError(n, "jsp.error.plugin.nocode");
+            
+	    Node.JspAttribute width
+		= getJspAttribute("width", null, null,
+				  n.getAttributeValue("width"), 
+                                  java.lang.String.class, n, false);
+	    n.setWidth( width );
+            
+	    Node.JspAttribute height
+		= getJspAttribute("height", null, null,
+				  n.getAttributeValue("height"), 
+                                  java.lang.String.class, n, false);
+	    n.setHeight( height );
+
+	    visitBody(n);
+	}
+
+	public void visit(Node.NamedAttribute n) throws JasperException {
+	    JspUtil.checkAttributes("Attribute", n,
+				    attributeAttrs, err);
+            visitBody(n);
+	}
+        
+	public void visit(Node.JspBody n) throws JasperException {
+            visitBody(n);
+	}
+        
+	public void visit(Node.Declaration n) throws JasperException {
+	    if (pageInfo.isScriptingInvalid()) {
+		err.jspError(n.getStart(), "jsp.error.no.scriptlets");
+	    }
+	}
+
+        public void visit(Node.Expression n) throws JasperException {
+	    if (pageInfo.isScriptingInvalid()) {
+		err.jspError(n.getStart(), "jsp.error.no.scriptlets");
+	    }
+	}
+
+        public void visit(Node.Scriptlet n) throws JasperException {
+	    if (pageInfo.isScriptingInvalid()) {
+		err.jspError(n.getStart(), "jsp.error.no.scriptlets");
+	    }
+	}
+
+	public void visit(Node.ELExpression n) throws JasperException {
+            if ( !pageInfo.isELIgnored() ) {
+		String expressions = "${" + new String(n.getText()) + "}";
+		ELNode.Nodes el = ELParser.parse(expressions);
+		validateFunctions(el, n);
+                JspUtil.validateExpressions(
+                    n.getStart(),
+		    expressions,
+                    java.lang.String.class, // XXX - Should template text 
+                                            // always evaluate to String?
+                    getFunctionMapper(el),
+                    err);
+		n.setEL(el);
+            }
+        }
+
+	public void visit(Node.UninterpretedTag n) throws JasperException {
+            if (n.getNamedAttributeNodes().size() != 0) {
+		err.jspError(n, "jsp.error.namedAttribute.invalidUse");
+            }
+
+	    Attributes attrs = n.getAttributes();
+	    if (attrs != null) {
+		int attrSize = attrs.getLength();
+		Node.JspAttribute[] jspAttrs = new Node.JspAttribute[attrSize];
+		for (int i=0; i < attrSize; i++) {
+		    jspAttrs[i] = getJspAttribute(attrs.getQName(i),
+						  attrs.getURI(i),
+						  attrs.getLocalName(i),
+						  attrs.getValue(i),
+						  java.lang.Object.class,
+						  n,
+						  false);
+		}
+		n.setJspAttributes(jspAttrs);
+	    }
+
+	    visitBody(n);
+        }
+
+	public void visit(Node.CustomTag n) throws JasperException {
+
+	    TagInfo tagInfo = n.getTagInfo();
+	    if (tagInfo == null) {
+		err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
+	    }
+
+	    /*
+	     * The bodyconet of a SimpleTag cannot be JSP.
+	     */
+	    if (n.implementsSimpleTag() &&
+                tagInfo.getBodyContent().equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) {
+                err.jspError(n, "jsp.error.simpletag.badbodycontent",
+                        tagInfo.getTagClassName());
+	    }
+
+	    /*
+	     * If the tag handler declares in the TLD that it supports dynamic
+	     * attributes, it also must implement the DynamicAttributes
+	     * interface.
+	     */
+	    if (tagInfo.hasDynamicAttributes()
+		    && !n.implementsDynamicAttributes()) {
+		err.jspError(n, "jsp.error.dynamic.attributes.not.implemented",
+			     n.getQName());
+	    }
+
+	    /*
+	     * Make sure all required attributes are present, either as
+             * attributes or named attributes (<jsp:attribute>).
+ 	     * Also make sure that the same attribute is not specified in
+	     * both attributes or named attributes.
+	     */
+	    TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
+	    String customActionUri = n.getURI();
+	    Attributes attrs = n.getAttributes();
+	    int attrsSize = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<tldAttrs.length; i++) {
+		String attr = null;
+		if (attrs != null) {
+		    attr = attrs.getValue(tldAttrs[i].getName());
+		    if (attr == null) {
+			attr = attrs.getValue(customActionUri,
+					      tldAttrs[i].getName());
+		    }
+		}
+		Node.NamedAttribute na =
+			n.getNamedAttributeNode(tldAttrs[i].getName());
+		
+		if (tldAttrs[i].isRequired() && attr == null && na == null) {
+		    err.jspError(n, "jsp.error.missing_attribute",
+				 tldAttrs[i].getName(), n.getLocalName());
+		}
+		if (attr != null && na != null) {
+		    err.jspError(n, "jsp.error.duplicate.name.jspattribute",
+			tldAttrs[i].getName());
+		}
+	    }
+
+            Node.Nodes naNodes = n.getNamedAttributeNodes();
+	    int jspAttrsSize = naNodes.size() + attrsSize;
+	    Node.JspAttribute[] jspAttrs = null;
+	    if (jspAttrsSize > 0) {
+		jspAttrs = new Node.JspAttribute[jspAttrsSize];
+	    }
+	    Hashtable tagDataAttrs = new Hashtable(attrsSize);
+
+	    checkXmlAttributes(n, jspAttrs, tagDataAttrs);
+            checkNamedAttributes(n, jspAttrs, attrsSize, tagDataAttrs);
+
+	    TagData tagData = new TagData(tagDataAttrs);
+
+	    // JSP.C1: It is a (translation time) error for an action that
+	    // has one or more variable subelements to have a TagExtraInfo
+	    // class that returns a non-null object.
+	    TagExtraInfo tei = tagInfo.getTagExtraInfo();
+	    if (tei != null
+		    && tei.getVariableInfo(tagData) != null
+		    && tei.getVariableInfo(tagData).length > 0
+		    && tagInfo.getTagVariableInfos().length > 0) {
+		err.jspError("jsp.error.non_null_tei_and_var_subelems",
+			     n.getQName());
+	    }
+
+	    n.setTagData(tagData);
+	    n.setJspAttributes(jspAttrs);
+
+	    visitBody(n);
+	}
+
+	public void visit(Node.JspElement n) throws JasperException {
+
+	    Attributes attrs = n.getAttributes();
+	    if (attrs == null) {
+		err.jspError(n, "jsp.error.jspelement.missing.name");
+	    }
+	    int xmlAttrLen = attrs.getLength();
+
+            Node.Nodes namedAttrs = n.getNamedAttributeNodes();
+
+	    // XML-style 'name' attribute, which is mandatory, must not be
+	    // included in JspAttribute array
+	    int jspAttrSize = xmlAttrLen-1 + namedAttrs.size();
+
+	    Node.JspAttribute[] jspAttrs = new Node.JspAttribute[jspAttrSize];
+	    int jspAttrIndex = 0;
+
+	    // Process XML-style attributes
+	    for (int i=0; i<xmlAttrLen; i++) {
+		if ("name".equals(attrs.getLocalName(i))) {
+		    n.setNameAttribute(getJspAttribute(attrs.getQName(i),
+						       attrs.getURI(i),
+						       attrs.getLocalName(i),
+						       attrs.getValue(i),
+						       java.lang.String.class,
+						       n,
+						       false));
+		} else {
+		    if (jspAttrIndex<jspAttrSize) {
+			jspAttrs[jspAttrIndex++]
+			    = getJspAttribute(attrs.getQName(i),
+					      attrs.getURI(i),
+					      attrs.getLocalName(i),
+					      attrs.getValue(i),
+					      java.lang.Object.class,
+					      n,
+					      false);
+		    }
+		}
+	    }
+	    if (n.getNameAttribute() == null) {
+		err.jspError(n, "jsp.error.jspelement.missing.name");
+	    }
+
+	    // Process named attributes
+	    for (int i=0; i<namedAttrs.size(); i++) {
+                Node.NamedAttribute na = (Node.NamedAttribute) namedAttrs.getNode(i);
+		jspAttrs[jspAttrIndex++] = new Node.JspAttribute(na, false);
+	    }
+
+	    n.setJspAttributes(jspAttrs);
+
+	    visitBody(n);
+	}
+
+	public void visit(Node.JspOutput n) throws JasperException {
+            JspUtil.checkAttributes("jsp:output", n, jspOutputAttrs, err);
+
+	    if (n.getBody() != null) {
+                err.jspError(n, "jsp.error.jspoutput.nonemptybody");
+	    }
+
+	    String omitXmlDecl = n.getAttributeValue("omit-xml-declaration");
+	    String doctypeName = n.getAttributeValue("doctype-root-element");
+	    String doctypePublic = n.getAttributeValue("doctype-public");
+	    String doctypeSystem = n.getAttributeValue("doctype-system");
+
+	    String omitXmlDeclOld = pageInfo.getOmitXmlDecl();
+	    String doctypeNameOld = pageInfo.getDoctypeName();
+	    String doctypePublicOld = pageInfo.getDoctypePublic();
+	    String doctypeSystemOld = pageInfo.getDoctypeSystem();
+
+	    if (omitXmlDecl != null && omitXmlDeclOld != null &&
+			!omitXmlDecl.equals(omitXmlDeclOld) ) {
+                err.jspError(n, "jsp.error.jspoutput.conflict",
+			"omit-xml-declaration", omitXmlDeclOld, omitXmlDecl);
+	    }
+
+	    if (doctypeName != null && doctypeNameOld != null &&
+			!doctypeName.equals(doctypeNameOld) ) {
+                err.jspError(n, "jsp.error.jspoutput.conflict",
+			"doctype-root-element", doctypeNameOld, doctypeName);
+	    }
+
+	    if (doctypePublic != null && doctypePublicOld != null &&
+			!doctypePublic.equals(doctypePublicOld) ) {
+                err.jspError(n, "jsp.error.jspoutput.conflict",
+			"doctype-public", doctypePublicOld, doctypePublic);
+	    }
+
+	    if (doctypeSystem != null && doctypeSystemOld != null &&
+			!doctypeSystem.equals(doctypeSystemOld) ) {
+                err.jspError(n, "jsp.error.jspoutput.conflict",
+			"doctype-system", doctypeSystemOld, doctypeSystem);
+	    }
+
+	    if (doctypeName == null && doctypeSystem != null ||
+		doctypeName != null && doctypeSystem == null) {
+		err.jspError(n, "jsp.error.jspoutput.doctypenamesystem");
+	    }
+
+	    if (doctypePublic != null && doctypeSystem == null) {
+		err.jspError(n, "jsp.error.jspoutput.doctypepulicsystem");
+	    }
+
+	    if (omitXmlDecl != null) {
+		pageInfo.setOmitXmlDecl(omitXmlDecl);
+	    }
+	    if (doctypeName != null) {
+		pageInfo.setDoctypeName(doctypeName);
+	    }
+	    if (doctypeSystem != null) {
+		pageInfo.setDoctypeSystem(doctypeSystem);
+	    }
+	    if (doctypePublic != null) {
+		pageInfo.setDoctypePublic(doctypePublic);
+	    }
+	}
+
+	public void visit(Node.InvokeAction n) throws JasperException {
+
+            JspUtil.checkAttributes("Invoke", n, invokeAttrs, err);
+
+	    String scope = n.getTextAttribute ("scope");
+	    JspUtil.checkScope(scope, n, err);
+
+	    String var = n.getTextAttribute("var");
+	    String varReader = n.getTextAttribute("varReader");
+	    if (scope != null && var == null && varReader == null) {
+		err.jspError(n, "jsp.error.missing_var_or_varReader");
+	    }
+	    if (var != null && varReader != null) {
+		err.jspError(n, "jsp.error.var_and_varReader");
+	    }
+	}
+
+	public void visit(Node.DoBodyAction n) throws JasperException {
+
+            JspUtil.checkAttributes("DoBody", n, doBodyAttrs, err);
+
+	    String scope = n.getTextAttribute ("scope");
+	    JspUtil.checkScope(scope, n, err);
+
+	    String var = n.getTextAttribute("var");
+	    String varReader = n.getTextAttribute("varReader");
+	    if (scope != null && var == null && varReader == null) {
+		err.jspError(n, "jsp.error.missing_var_or_varReader");
+	    }
+	    if (var != null && varReader != null) {
+		err.jspError(n, "jsp.error.var_and_varReader");
+	    }
+	}
+
+	/*
+	 * Make sure the given custom action does not have any invalid
+	 * attributes.
+	 *
+	 * A custom action and its declared attributes always belong to the
+	 * same namespace, which is identified by the prefix name of the
+	 * custom tag invocation. For example, in this invocation:
+	 *
+	 *     <my:test a="1" b="2" c="3"/>, the action
+	 *
+	 * "test" and its attributes "a", "b", and "c" all belong to the
+	 * namespace identified by the prefix "my". The above invocation would
+	 * be equivalent to:
+	 *
+	 *     <my:test my:a="1" my:b="2" my:c="3"/>
+	 *
+	 * An action attribute may have a prefix different from that of the
+	 * action invocation only if the underlying tag handler supports
+	 * dynamic attributes, in which case the attribute with the different
+	 * prefix is considered a dynamic attribute.
+	 */
+	private void checkXmlAttributes(Node.CustomTag n,
+					Node.JspAttribute[] jspAttrs,
+					Hashtable tagDataAttrs)
+	        throws JasperException {
+
+	    TagInfo tagInfo = n.getTagInfo();
+	    if (tagInfo == null) {
+		err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
+	    }
+	    TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
+	    Attributes attrs = n.getAttributes();
+
+	    for (int i=0; attrs != null && i<attrs.getLength(); i++) {
+		boolean found = false;
+		for (int j=0; tldAttrs != null && j<tldAttrs.length; j++) {
+		    if (attrs.getLocalName(i).equals(tldAttrs[j].getName())
+			    && (attrs.getURI(i) == null
+				|| attrs.getURI(i).length() == 0
+				|| attrs.getURI(i).equals(n.getURI()))) {
+			if (tldAttrs[j].canBeRequestTime()) {
+                            Class expectedType = String.class;
+                            try {
+                                String typeStr = tldAttrs[j].getTypeName();
+                                if( tldAttrs[j].isFragment() ) {
+                                    expectedType = JspFragment.class;
+                                }
+                                else if( typeStr != null ) {
+                                    expectedType = JspUtil.toClass(typeStr,
+								   loader);
+                                }
+                                jspAttrs[i]
+                                    = getJspAttribute(attrs.getQName(i),
+                                                      attrs.getURI(i),
+                                                      attrs.getLocalName(i),
+                                                      attrs.getValue(i),
+                                                      expectedType,
+                                                      n,
+                                                      false);
+                            } catch (ClassNotFoundException e) {
+                                err.jspError(n, 
+                                    "jsp.error.unknown_attribute_type",
+                                    tldAttrs[j].getName(), 
+                                    tldAttrs[j].getTypeName() );
+                            }
+			} else {
+			    // Attribute does not accept any expressions.
+			    // Make sure its value does not contain any.
+			    if (isExpression(n, attrs.getValue(i))) {
+                                err.jspError(n,
+				        "jsp.error.attribute.custom.non_rt_with_expr",
+					     tldAttrs[j].getName());
+			    }
+			    jspAttrs[i]
+				= new Node.JspAttribute(attrs.getQName(i),
+							attrs.getURI(i),
+							attrs.getLocalName(i),
+							attrs.getValue(i),
+							false,
+							null,
+							false);
+			}
+			if (jspAttrs[i].isExpression()) {
+			    tagDataAttrs.put(attrs.getQName(i),
+					     TagData.REQUEST_TIME_VALUE);
+			} else {
+			    tagDataAttrs.put(attrs.getQName(i),
+					     attrs.getValue(i));
+			}
+			found = true;
+			break;
+		    }
+		}
+		if (!found) {
+		    if (tagInfo.hasDynamicAttributes()) {
+			jspAttrs[i] = getJspAttribute(attrs.getQName(i),
+						      attrs.getURI(i),
+						      attrs.getLocalName(i),
+						      attrs.getValue(i),
+						      java.lang.Object.class,
+                                                      n,
+						      true);
+		    } else {
+			err.jspError(n, "jsp.error.bad_attribute",
+				     attrs.getQName(i), n.getLocalName());
+		    }
+		}
+	    }
+	}
+
+	/*
+	 * Make sure the given custom action does not have any invalid named
+	 * attributes
+	 */
+	private void checkNamedAttributes(Node.CustomTag n,
+					  Node.JspAttribute[] jspAttrs,
+					  int start,
+					  Hashtable tagDataAttrs)
+	        throws JasperException {
+
+	    TagInfo tagInfo = n.getTagInfo();
+	    if (tagInfo == null) {
+		err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
+	    }
+	    TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
+            Node.Nodes naNodes = n.getNamedAttributeNodes();
+
+	    for (int i=0; i<naNodes.size(); i++) {
+                Node.NamedAttribute na = (Node.NamedAttribute)
+		    naNodes.getNode(i);
+		boolean found = false;
+		for (int j=0; j<tldAttrs.length; j++) {
+		    /*
+		     * See above comment about namespace matches. For named
+		     * attributes, we use the prefix instead of URI as the
+		     * match criterion, because in the case of a JSP document,
+		     * we'd have to keep track of which namespaces are in scope
+		     * when parsing a named attribute, in order to determine
+		     * the URI that the prefix of the named attribute's name
+		     * matches to.
+		     */
+		    String attrPrefix = na.getPrefix();
+		    if (na.getLocalName().equals(tldAttrs[j].getName())
+			    && (attrPrefix == null || attrPrefix.length() == 0
+				|| attrPrefix.equals(n.getPrefix()))) {
+			jspAttrs[start + i] = new Node.JspAttribute(na, false);
+			NamedAttributeVisitor nav = null;
+			if (na.getBody() != null) {
+			    nav = new NamedAttributeVisitor();
+			    na.getBody().visit(nav);
+			}
+			if (nav != null && nav.hasDynamicContent()) {
+			    tagDataAttrs.put(na.getName(),
+					     TagData.REQUEST_TIME_VALUE);
+			} else {
+			    tagDataAttrs.put(na.getName(), na.getText());    
+			}
+			found = true;
+			break;
+		    }
+		}
+		if (!found) {
+		    if (tagInfo.hasDynamicAttributes()) {
+			jspAttrs[start + i] = new Node.JspAttribute(na, true);
+		    } else {
+			err.jspError(n, "jsp.error.bad_attribute",
+				     na.getName(), n.getLocalName());
+		    }
+		}
+	    }
+	}
+
+	/**
+	 * Preprocess attributes that can be expressions.  Expression
+	 * delimiters are stripped.
+         * <p>
+         * If value is null, checks if there are any
+         * NamedAttribute subelements in the tree node, and if so,
+         * constructs a JspAttribute out of a child NamedAttribute node.
+	 */
+	private Node.JspAttribute getJspAttribute(String qName,
+						  String uri,
+						  String localName,
+						  String value,
+                                                  Class expectedType,
+                                                  Node n,
+						  boolean dynamic)
+                throws JasperException {
+
+            Node.JspAttribute result = null;
+
+	    // XXX Is it an error to see "%=foo%" in non-Xml page?
+	    // (We won't see "<%=foo%> in xml page because '<' is not a
+	    // valid attribute value in xml).
+
+            if (value != null) {
+                if (n.getRoot().isXmlSyntax() && value.startsWith("%=")) {
+                    result = new Node.JspAttribute(
+                                        qName,
+					uri,
+					localName,
+					value.substring(2, value.length()-1),
+					true,
+					null,
+					dynamic);
+                }
+                else if(!n.getRoot().isXmlSyntax() && value.startsWith("<%=")) {
+                    result = new Node.JspAttribute(
+                                        qName,
+					uri,
+					localName,
+					value.substring(3, value.length()-2),
+					true,
+					null,
+					dynamic);
+                }
+                else {
+                    // The attribute can contain expressions but is not a
+                    // scriptlet expression; thus, we want to run it through 
+                    // the expression interpreter
+
+                    // validate expression syntax if string contains
+                    // expression(s)
+                    ELNode.Nodes el = ELParser.parse(value);
+                    if (el.containsEL() && !pageInfo.isELIgnored()) {
+	                validateFunctions(el, n);
+                        JspUtil.validateExpressions(
+                            n.getStart(),
+                            value, 
+                            expectedType, 
+                            getFunctionMapper(el),
+                            this.err);
+
+                        
+                        result = new Node.JspAttribute(qName, uri, localName,
+						       value, false, el,
+						       dynamic);
+                    } else {
+			value = value.replace(Constants.ESC, '$');
+                        result = new Node.JspAttribute(qName, uri, localName,
+						       value, false, null,
+						       dynamic);
+                    }
+                }
+            }
+            else {
+                // Value is null.  Check for any NamedAttribute subnodes
+                // that might contain the value for this attribute.
+                // Otherwise, the attribute wasn't found so we return null.
+
+                Node.NamedAttribute namedAttributeNode =
+                    n.getNamedAttributeNode( qName );
+                if( namedAttributeNode != null ) {
+                    result = new Node.JspAttribute(namedAttributeNode,
+						   dynamic);
+                }
+            }
+
+            return result;
+        }
+
+	/*
+	 * Checks to see if the given attribute value represents a runtime or
+	 * EL expression.
+	 */
+	private boolean isExpression(Node n, String value) {
+	    if ((n.getRoot().isXmlSyntax() && value.startsWith("%="))
+		    || (!n.getRoot().isXmlSyntax() && value.startsWith("<%="))
+   		    || (value.indexOf("${") != -1 && !pageInfo.isELIgnored()))
+		return true;
+	    else
+		return false;
+	}
+
+	/*
+	 * Throws exception if the value of the attribute with the given
+	 * name in the given node is given as an RT or EL expression, but the
+	 * spec requires a static value.
+	 */
+	private void throwErrorIfExpression(Node n, String attrName,
+					    String actionName)
+	            throws JasperException {
+	    if (n.getAttributes() != null
+		    && n.getAttributes().getValue(attrName) != null
+		    && isExpression(n, n.getAttributes().getValue(attrName))) {
+		err.jspError(n,
+			     "jsp.error.attribute.standard.non_rt_with_expr",
+			     attrName, actionName);
+	    }
+	}
+
+	private static class NamedAttributeVisitor extends Node.Visitor {
+	    private boolean hasDynamicContent;
+
+	    public void doVisit(Node n) throws JasperException {
+		if (!(n instanceof Node.JspText)
+		        && !(n instanceof Node.TemplateText)) {
+		    hasDynamicContent = true;
+		}
+		visitBody(n);
+	    }
+	    
+	    public boolean hasDynamicContent() {
+		return hasDynamicContent;
+	    }
+	}
+
+	private String findUri(String prefix, Node n) {
+
+	    for (Node p = n; p != null; p = p.getParent()) {
+		Attributes attrs = p.getTaglibAttributes();
+		if (attrs == null) {
+		    continue;
+		}
+		for (int i = 0; i < attrs.getLength(); i++) {
+		    String name = attrs.getQName(i);
+		    int k = name.indexOf(':');
+		    if (prefix == null && k < 0) {
+			// prefix not specified and a default ns found
+			return attrs.getValue(i);
+		    }   
+		    if (prefix != null && k >= 0 &&
+				prefix.equals(name.substring(k+1))) {
+			return attrs.getValue(i);
+		    }
+		}
+	    }
+	    return null;
+	}
+
+	/**
+	 * Validate functions in EL expressions
+	 */
+	private void validateFunctions(ELNode.Nodes el, Node n) 
+		throws JasperException {
+
+	    class FVVisitor extends ELNode.Visitor {
+
+		Node n;
+
+		FVVisitor(Node n) {
+		    this.n = n;
+		}
+
+		public void visit(ELNode.Function func) throws JasperException {
+		    String prefix = func.getPrefix();
+		    String function = func.getName();
+		    String uri = null;
+
+		    if (n.getRoot().isXmlSyntax()) {
+		        uri = findUri(prefix, n);
+		    } else if (prefix != null) {
+			uri = pageInfo.getURI(prefix);
+		    }
+
+		    if (uri == null) {
+			if (prefix == null) {
+			    err.jspError(n, "jsp.error.noFunctionPrefix",
+				function);
+			}
+			else {
+			    err.jspError(n,
+				"jsp.error.attribute.invalidPrefix", prefix);
+			}
+		    }
+		    TagLibraryInfo taglib = pageInfo.getTaglib(uri);
+		    FunctionInfo funcInfo = null;
+		    if (taglib != null) {
+			funcInfo = taglib.getFunction(function);
+		    }
+		    if (funcInfo == null) {
+			err.jspError(n, "jsp.error.noFunction", function);
+		    }
+		    // Skip TLD function uniqueness check.  Done by Schema ?
+		    func.setUri(uri);
+		    func.setFunctionInfo(funcInfo);
+		    processSignature(func);
+		}
+	    }
+
+	    el.visit(new FVVisitor(n));
+	}
+
+	private void processSignature(ELNode.Function func)
+		throws JasperException {
+	    func.setMethodName(getMethod(func));
+	    func.setParameters(getParameters(func));
+	}
+
+	/**
+	 * Get the method name from the signature.
+	 */
+	private String getMethod(ELNode.Function func)
+		throws JasperException {
+	    FunctionInfo funcInfo = func.getFunctionInfo();
+	    String signature = funcInfo.getFunctionSignature();
+	    
+	    int start = signature.indexOf(' ');
+	    if (start < 0) {
+		err.jspError("jsp.error.tld.fn.invalid.signature",
+			     func.getPrefix(), func.getName());
+	    }
+	    int end = signature.indexOf('(');
+	    if (end < 0) {
+		err.jspError("jsp.error.tld.fn.invalid.signature.parenexpected",
+			     func.getPrefix(), func.getName());
+	    }
+	    return signature.substring(start+1, end).trim();
+	}
+
+	/**
+	 * Get the parameters types from the function signature.
+	 * @return An array of parameter class names
+	 */
+	private String[] getParameters(ELNode.Function func) 
+		throws JasperException {
+	    FunctionInfo funcInfo = func.getFunctionInfo();
+	    String signature = funcInfo.getFunctionSignature();
+	    ArrayList params = new ArrayList();
+	    // Signature is of the form
+	    // <return-type> S <method-name S? '('
+	    // < <arg-type> ( ',' <arg-type> )* )? ')'
+	    int start = signature.indexOf('(') + 1;
+	    boolean lastArg = false;
+	    while (true) {
+		int p = signature.indexOf(',', start);
+		if (p < 0) {
+		    p = signature.indexOf(')', start);
+		    if (p < 0) {
+			err.jspError("jsp.error.tld.fn.invalid.signature",
+				     func.getPrefix(), func.getName());
+		    }
+		    lastArg = true;
+		}
+                String arg = signature.substring(start, p).trim();
+                if (!"".equals(arg)) {
+                    params.add(arg);
+                }
+		if (lastArg) {
+		    break;
+		}
+		start = p+1;
+	    }
+	    return (String[]) params.toArray(new String[params.size()]);
+	}
+
+	private FunctionMapper getFunctionMapper(ELNode.Nodes el)
+		throws JasperException {
+
+	    class ValidateFunctionMapper implements FunctionMapper {
+
+		private HashMap fnmap = new java.util.HashMap();
+		public void mapFunction(String fnQName, Method method) {
+		    fnmap.put(fnQName, method);
+		}
+
+		public Method resolveFunction(String prefix,
+					      String localName) {
+		    return (Method) this.fnmap.get(prefix + ":" + localName);
+		}
+	    }
+
+	    class MapperELVisitor extends ELNode.Visitor {
+		ValidateFunctionMapper fmapper;
+
+		MapperELVisitor(ValidateFunctionMapper fmapper) {
+		    this.fmapper = fmapper;
+		}
+
+		public void visit(ELNode.Function n) throws JasperException {
+
+		    Class c = null;
+		    Method method = null;
+		    try {
+			c = loader.loadClass(
+				n.getFunctionInfo().getFunctionClass());
+		    } catch (ClassNotFoundException e) {
+			err.jspError("jsp.error.function.classnotfound",
+				     n.getFunctionInfo().getFunctionClass(),
+				     n.getPrefix() + ':' + n.getName(),
+				     e.getMessage());
+		    }
+		    String paramTypes[] = n.getParameters();
+		    int size = paramTypes.length;
+		    Class params[] = new Class[size];
+		    int i = 0;
+		    try {
+			for (i = 0; i < size; i++) {
+			    params[i] = JspUtil.toClass(paramTypes[i], loader);
+			}
+			method = c.getDeclaredMethod(n.getMethodName(),
+						     params);
+		    } catch (ClassNotFoundException e) {
+			err.jspError("jsp.error.signature.classnotfound",
+				     paramTypes[i],
+				     n.getPrefix() + ':' + n.getName(),
+				     e.getMessage());
+		    } catch (NoSuchMethodException e ) {
+			err.jspError("jsp.error.noFunctionMethod",
+				     n.getMethodName(), n.getName(),
+				     c.getName());
+		    }
+		    fmapper.mapFunction(n.getPrefix() + ':' + n.getName(),
+					method);
+		}
+	    }
+
+	    ValidateFunctionMapper fmapper = new ValidateFunctionMapper();
+	    el.visit(new MapperELVisitor(fmapper));
+	    return fmapper;
+	}
+    } // End of ValidateVisitor
+
+    /**
+     * A visitor for validating TagExtraInfo classes of all tags
+     */
+    static class TagExtraInfoVisitor extends Node.Visitor {
+
+	private PageInfo pageInfo;
+	private ErrorDispatcher err;
+
+	/*
+	 * Constructor
+	 */
+	TagExtraInfoVisitor(Compiler compiler) {
+	    this.pageInfo = compiler.getPageInfo();
+	    this.err = compiler.getErrorDispatcher();
+	}
+
+	public void visit(Node.CustomTag n) throws JasperException {
+	    TagInfo tagInfo = n.getTagInfo();
+	    if (tagInfo == null) {
+		err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
+	    }
+
+	    ValidationMessage[] errors = tagInfo.validate(n.getTagData());
+            if (errors != null && errors.length != 0) {
+		StringBuffer errMsg = new StringBuffer();
+                errMsg.append("<h3>");
+                errMsg.append(Localizer.getMessage("jsp.error.tei.invalid.attributes",
+						   n.getQName()));
+                errMsg.append("</h3>");
+                for (int i=0; i<errors.length; i++) {
+                    errMsg.append("<p>");
+		    if (errors[i].getId() != null) {
+			errMsg.append(errors[i].getId());
+			errMsg.append(": ");
+		    }
+                    errMsg.append(errors[i].getMessage());
+                    errMsg.append("</p>");
+                }
+
+		err.jspError(n, errMsg.toString());
+            }
+
+	    visitBody(n);
+	}
+    }
+
+    public static void validate(Compiler compiler,
+				Node.Nodes page) throws JasperException {
+
+	/*
+	 * Visit the page/tag directives first, as they are global to the page
+	 * and are position independent.
+	 */
+	page.visit(new DirectiveVisitor(compiler));
+
+	// Determine the default output content type
+	PageInfo pageInfo = compiler.getPageInfo();
+	String contentType = pageInfo.getContentType();
+
+	if (contentType == null || contentType.indexOf("charset=") < 0) {
+	    boolean isXml = page.getRoot().isXmlSyntax();
+	    String defaultType;
+	    if (contentType == null) {
+		defaultType = isXml? "text/xml": "text/html";
+	    } else {
+		defaultType = contentType;
+	    }
+
+	    String charset = null;
+	    if (isXml) {
+		charset = "UTF-8";
+	    } else {
+		if (!page.getRoot().isDefaultPageEncoding()) {
+		    charset = page.getRoot().getPageEncoding();
+		}
+	    }
+
+	    if (charset != null) {
+		pageInfo.setContentType(defaultType + ";charset=" + charset);
+	    } else {
+		pageInfo.setContentType(defaultType);
+	    }
+	}
+
+	/*
+	 * Validate all other nodes.
+	 * This validation step includes checking a custom tag's mandatory and
+	 * optional attributes against information in the TLD (first validation
+	 * step for custom tags according to JSP.10.5).
+	 */
+	page.visit(new ValidateVisitor(compiler));
+
+	/*
+	 * Invoke TagLibraryValidator classes of all imported tags
+	 * (second validation step for custom tags according to JSP.10.5).
+	 */
+	validateXmlView(new PageDataImpl(page, compiler), compiler);
+
+	/*
+	 * Invoke TagExtraInfo method isValid() for all imported tags 
+	 * (third validation step for custom tags according to JSP.10.5).
+	 */
+	page.visit(new TagExtraInfoVisitor(compiler));
+
+    }
+
+
+    //*********************************************************************
+    // Private (utility) methods
+
+    /**
+     * Validate XML view against the TagLibraryValidator classes of all
+     * imported tag libraries.
+     */
+    private static void validateXmlView(PageData xmlView, Compiler compiler)
+	        throws JasperException {
+
+	StringBuffer errMsg = null;
+	ErrorDispatcher errDisp = compiler.getErrorDispatcher();
+
+	for (Iterator iter=compiler.getPageInfo().getTaglibs().iterator();
+	         iter.hasNext(); ) {
+
+	    Object o = iter.next();
+	    if (!(o instanceof TagLibraryInfoImpl))
+		continue;
+	    TagLibraryInfoImpl tli = (TagLibraryInfoImpl) o;
+
+	    ValidationMessage[] errors = tli.validate(xmlView);
+            if ((errors != null) && (errors.length != 0)) {
+                if (errMsg == null) {
+		    errMsg = new StringBuffer();
+		}
+                errMsg.append("<h3>");
+                errMsg.append(Localizer.getMessage("jsp.error.tlv.invalid.page",
+						   tli.getShortName()));
+                errMsg.append("</h3>");
+                for (int i=0; i<errors.length; i++) {
+		    if (errors[i] != null) {
+			errMsg.append("<p>");
+			errMsg.append(errors[i].getId());
+			errMsg.append(": ");
+			errMsg.append(errors[i].getMessage());
+			errMsg.append("</p>");
+		    }
+                }
+            }
+        }
+
+	if (errMsg != null) {
+            errDisp.jspError(errMsg.toString());
+	}
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/tagplugin/TagPlugin.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/tagplugin/TagPlugin.java
new file mode 100644
index 0000000..34af677
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/tagplugin/TagPlugin.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler.tagplugin;
+
+/**
+ * This interface is to be implemented by the plugin author, to supply
+ * an alternate implementation of the tag handlers.  It can be used to
+ * specify the Java codes to be generated when a tag is invoked.
+ *
+ * An implementation of this interface must be registered in a file
+ * named "tagPlugins.xml" under WEB-INF.
+ */
+
+public interface TagPlugin {
+
+    /**
+     * Generate codes for a custom tag.
+     * @param ctxt a TagPluginContext for accessing Jasper functions
+     */
+    void doTag(TagPluginContext ctxt);
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/compiler/tagplugin/TagPluginContext.java b/jasper/jasper2/src/share/org/apache/jasper/compiler/tagplugin/TagPluginContext.java
new file mode 100644
index 0000000..40490de
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/compiler/tagplugin/TagPluginContext.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.compiler.tagplugin;
+
+
+/**
+ * This interface allows the plugin author to make inqueries about the
+ * properties of the current tag, and to use Jasper resources to generate
+ * direct Java codes in place of tag handler invocations.
+ */
+
+public interface TagPluginContext {
+    /**
+     * @return true if the body of the tag is scriptless.
+     */
+    boolean isScriptless();
+
+    /**
+     * @param attribute Name of the attribute
+     * @return true if the attribute is specified in the tag
+     */
+    boolean isAttributeSpecified(String attribute);
+
+    /**
+     * @return An unique temporary variable name that the plugin can use.
+     */
+    String getTemporaryVariableName();
+
+    /**
+     * Generate an import statement
+     * @param s Name of the import class, '*' allowed.
+     */
+    void generateImport(String s);
+
+    /**
+     * Generate a declaration in the of the generated class.  This can be
+     * used to declare an innter class, a method, or a class variable.
+     * @param id An unique ID identifying the declaration.  It is not
+     *           part of the declaration, and is used to ensure that the
+     *           declaration will only appear once.  If this method is
+     *           invoked with the same id more than once in the translation
+     *           unit, only the first declaration will be taken.
+     * @param text The text of the declaration.
+     **/
+    void generateDeclaration(String id, String text);
+
+    /**
+     * Generate Java source codes
+     */
+    void generateJavaSource(String s);
+
+    /**
+     * @return true if the attribute is specified and its value is a
+     *         translation-time constant.
+     */
+    boolean isConstantAttribute(String attribute);
+
+    /**
+     * @return A string that is the value of a constant attribute.  Undefined
+     *         if the attribute is not a (translation-time) constant.
+     *         null if the attribute is not specified.
+     */
+    String getConstantAttribute(String attribute);
+
+    /**
+     * Generate codesto evaluate value of a attribute in the custom tag
+     * The codes is a Java expression.
+     * NOTE: Currently cannot handle attributes that are fragments.
+     * @param attribute The specified attribute
+     */
+    void generateAttribute(String attribute);
+
+    /**
+     * Generate codes for the body of the custom tag
+     */
+    void generateBody();
+
+    /**
+     * Abandon optimization for this tag handler, and instruct
+     * Jasper to generate the tag handler calls, as usual.
+     * Should be invoked if errors are detected, or when the tag body
+     * is deemed too compilicated for optimization.
+     */
+    void dontUseTagPlugin();
+
+    /**
+     * Get the PluginContext for the parent of this custom tag.  NOTE:
+     * The operations available for PluginContext so obtained is limited
+     * to getPluginAttribute and setPluginAttribute, and queries (e.g.
+     * isScriptless().  There should be no calls to generate*().
+     * @return The pluginContext for the parent node.
+     *         null if the parent is not a custom tag, or if the pluginConxt
+     *         if not available (because useTagPlugin is false, e.g).
+     */
+    TagPluginContext getParentContext();
+
+    /**
+     * Associate the attribute with a value in the current tagplugin context.
+     * The plugin attributes can be used for communication among tags that
+     * must work together as a group.  See <c:when> for an example.
+     */
+    void setPluginAttribute(String attr, Object value);
+
+    /**
+     * Get the value of an attribute in the current tagplugin context.
+     */
+    Object getPluginAttribute(String attr);
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings.properties b/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings.properties
new file mode 100644
index 0000000..3e19b7c
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings.properties
@@ -0,0 +1,409 @@
+# $Id$
+#
+# Default localized string information
+# Localized this the Default Locale as is en_US
+
+jsp.error.bad.servlet.engine=Incorrect servlet engine version!
+jsp.error.no.scratch.dir=The JSP engine is not configured with a scratch dir.\
+\n Please add \"jsp.initparams=scratchdir=<dir-name>\" \
+\n in the servlets.properties file for this context.
+jsp.error.bad.scratch.dir=The scratchDir you specified: {0} is unusable.
+jsp.message.scratch.dir.is=Scratch dir for the JSP engine is: {0}
+jsp.message.parent_class_loader_is=Parent class loader is: {0}
+jsp.message.dont.modify.servlets=IMPORTANT: Do not modify the generated servlets
+jsp.error.not.impl.comments=Internal error: Comments not implemented
+jsp.error.not.impl.directives=Internal error: Directives not implemented
+jsp.error.not.impl.declarations=Internal error: Declarations not implemented
+jsp.error.not.impl.expressions=Internal error: Expressions not implemented
+jsp.error.not.impl.scriptlets=Internal error: Scriptlets not implemented
+jsp.error.not.impl.usebean=Internal error: useBean not implemented
+jsp.error.not.impl.getp=Internal error: getProperty not implemented
+jsp.error.not.impl.setp=Internal error: setProperty not implemented
+jsp.error.not.impl.plugin=Internal error: plugin not implemented
+jsp.error.not.impl.forward=Internal error: forward not implemented
+jsp.error.not.impl.include=Internal error: include not implemented
+jsp.error.unavailable=JSP has been marked unavailable
+jsp.error.usebean.missing.attribute=useBean: id attribute missing or misspelled
+jsp.error.usebean.missing.type=useBean ({0}): Either class or type attribute must be \
+specified: 
+jsp.error.usebean.duplicate=useBean: Duplicate bean name: {0}
+jsp.error.usebean.prohibited.as.session=Can't use as session bean {0} since it is prohibited \
+by jsp directive defined earlier: 
+jsp.error.usebean.not.both=useBean: Can't specify both class and beanName attribute: 
+jsp.error.usebean.bad.type.cast=useBean ({0}): Type ({1}) is not assignable from class ({2}) 
+jsp.error.invalid.scope=Illegal value of \'scope\' attribute: {0} (must be one of \"page\", \"request\", \"session\", or \"application\")
+jsp.error.classname=Can't determine classname from .class file
+jsp.warning.bad.type=Warning: bad type in .class file
+jsp.error.data.file.write=Error while writing data file
+jsp.error.page.invalid.buffer=Page directive: invalid buffer size
+jsp.error.page.conflict.contenttype=Page directive: illegal to have multiple occurrences of 'contentType' with different values (old: {0}, new: {1})
+jsp.error.page.invalid.contenttype=Page directive: invalid value for contentType
+jsp.error.page.conflict.session=Page directive: illegal to have multiple occurrences of 'session' with different values (old: {0}, new: {1})
+jsp.error.page.invalid.session=Page directive: invalid value for session
+jsp.error.page.conflict.buffer=Page directive: illegal to have multiple occurrences of 'buffer' with different values (old: {0}, new: {1})
+jsp.error.page.invalid.buffer=Page directive: invalid value for buffer
+jsp.error.page.conflict.autoflush=Page directive: illegal to have multiple occurrences of 'autoFlush' with different values (old: {0}, new: {1})
+jsp.error.page.invalid.autoflush=Page directive: invalid value for autoFlush
+jsp.error.page.conflict.isthreadsafe=Page directive: illegal to have multiple occurrences of 'isThreadSafe' with different values (old: {0}, new: {1})
+jsp.error.page.invalid.isthreadsafe=Page directive: invalid value for isThreadSafe
+jsp.error.page.conflict.info=Page directive: illegal to have multiple occurrences of 'info' with different values (old: {0}, new: {1})
+jsp.error.page.invalid.info=Page directive: invalid value for info
+jsp.error.page.conflict.iserrorpage=Page directive: illegal to have multiple occurrences of 'isErrorPage' with different values (old: {0}, new: {1})
+jsp.error.page.invalid.iserrorpage=Page directive: invalid value for isErrorPage
+jsp.error.page.conflict.errorpage=Page directive: illegal to have multiple occurrences of 'errorPage' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.language=Page directive: illegal to have multiple occurrences of 'language' with different values (old: {0}, new: {1})
+jsp.error.tag.conflict.language=Tag directive: illegal to have multiple occurrences of 'language' with different values (old: {0}, new: {1})
+jsp.error.page.language.nonjava=Page directive: invalid language attribute
+jsp.error.tag.language.nonjava=Tag directive: invalid language attribute
+jsp.error.page.defafteruse.language=Page directive: can't define language after a scriptlet 
+jsp.error.page.nomapping.language=Page directive: No mapping for language: 
+jsp.error.page.conflict.extends=Page directive: illegal to have multiple occurrences of 'extends' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.iselignored=Page directive: illegal to have multiple occurrences of 'isELIgnored' with different values (old: {0}, new: {1})
+jsp.error.tag.conflict.iselignored=Tag directive: illegal to have multiple occurrences of 'isELIgnored' with different values (old: {0}, new: {1})
+jsp.error.page.invalid.iselignored=Page directive: invalid value for isELIgnored
+jsp.error.tag.invalid.iselignored=Tag directive: invalid value for isELIgnored
+jsp.error.page.multi.pageencoding=Page directive must not have multiple occurrences of pageencoding
+jsp.error.tag.conflict.attr=Tag directive: illegal to have multiple occurrences of the attribute \"{0}\" with different values (old: {1}, new: {2})
+jsp.error.tag.multi.pageencoding=Tag directive must not have multiple occurrences of pageencoding
+jsp.error.page.bad_b_and_a_combo=Page directive: Illegal combination of buffer=\"none\" && autoFlush=\"false\"
+jsp.error.not.impl.taglib=Internal error: Tag extensions not implemented
+jsp.error.include.missing.file=Missing file argument to include
+jsp.error.include.bad.file=Bad file argument to include 
+jsp.error.include.exception=Unable to include {0}
+jsp.error.stream.closed=Stream closed
+jsp.error.invalid.forward=Invalid forward tag
+jsp.error.unknownException=Unhandled error! You might want to consider having an error page \
+to report such errors more gracefully
+jsp.error.invalid.directive=Invalid directive
+jsp.error.directive.istagfile={0} directive cannot be used in a tag file
+jsp.error.directive.isnottagfile={0} directive can only be used in a tag file
+jsp.error.tagfile.tld.name=The \"name\" attribute of the tag directive has a value {0} while the \"name\" tag of the \"tag-file\" element in the TLD is {1}
+jsp.error.action.istagfile={0} action cannot be used in a tag file
+jsp.error.action.isnottagfile={0} action can be used in tag files only
+jsp.error.unterminated=Unterminated {0} tag
+jsp.error.usebean.notinsamefile=useBean tag must begin and end in the same physical file
+jsp.error.loadclass.taghandler=Unable to load tag handler class \"{0}\" for tag \"{1}\"
+jsp.error.unable.compile=Unable to compile class for JSP
+jsp.error.unable.load=Unable to load class for JSP
+jsp.error.unable.rename=Unable to rename class file {0} to {1}
+jsp.error.mandatory.attribute={0}: Mandatory attribute {1} missing
+jsp.engine.info=Jasper JSP 2.0 Engine
+jsp.error.invalid.expression="{0}" contains invalid expression(s): {1}
+jsp.error.invalid.attribute={0} has invalid attribute: {1}
+jsp.error.usebean.class.notfound=Class: {0} not found
+jsp.error.file.cannot.read=Cannot read file: {0}
+jsp.error.file.already.registered=Recursive include of file {0}
+jsp.error.file.not.registered=file {0} not seen in include
+jsp.error.quotes.unterminated=Unterminated quotes
+jsp.error.attr.quoted=Attribute value should be quoted
+jsp.error.attr.novalue=Attribute {0} has no value
+jsp.error.tag.attr.unterminated=Unterminated tag attribute list
+jsp.error.param.noname=No name in PARAM tag
+jsp.error.param.novalue=No value in PARAM tag
+jsp.error.beans.nullbean=Attempted a bean operation on a null object.
+jsp.error.beans.nobeaninfo=No BeanInfo for the bean of type ''{0}'' could be found, the class likely does not exist.
+jsp.error.beans.introspection=An exception occurred while introspecting the read method of property ''{0}'' in a bean of type ''{1}'':\n{2}
+jsp.error.beans.nomethod=Cannot find a method to read property ''{0}'' in a bean of type ''{1}''
+jsp.error.beans.nomethod.setproperty=Can''t find a method to write property ''{0}'' of type ''{1}'' in a bean of type ''{2}''
+jsp.error.beans.noproperty=Cannot find any information on property ''{0}'' in a bean of type ''{1}''
+jsp.error.beans.setproperty.noindexset=Cannot set indexed property
+jsp.error.include.tag=Invalid jsp:include tag
+jsp.error.include.noflush=jsp:include needs to have \"flush=true\"
+jsp.error.include.badflush=jsp:include page=\"...\" flush=\"true\" is the only valid combination in JSP 1.0
+jsp.error.attempt_to_clear_flushed_buffer=Error: Attempt to clear a buffer that's already been flushed
+jsp.error.overflow=Error: JSP Buffer overflow
+jsp.error.paramexpected=Expecting \"jsp:param\" standard action with \"name\" and \"value\" attributes
+jsp.error.param.invalidUse=The jsp:param action must not be used outside the jsp:include, jsp:forward, or jsp:params elements
+jsp.error.params.invalidUse=jsp:params must be a direct child of jsp:plugin
+jsp.error.fallback.invalidUse=jsp:fallback must be a direct child of jsp:plugin
+jsp.error.namedAttribute.invalidUse=jsp:attribute must be the subelement of a standard or custom action
+jsp.error.jspbody.invalidUse=jsp:body must be the subelement of a standard or custom action
+jsp.error.closeindividualparam=param tag needs to be closed with \"/>\"
+jsp.error.closeparams=param tag needs to be closed with /params
+jsp.error.params.emptyBody=jsp:params must contain at least one nested jsp:param
+jsp.error.params.illegalChild=jsp:params must not have any nested elements other than jsp:param
+jsp.error.plugin.notype=type not declared in jsp:plugin
+jsp.error.plugin.badtype=Illegal value for 'type' attribute in jsp:plugin: must be 'bean' or 'applet'
+jsp.error.plugin.nocode=code not declared in jsp:plugin
+jsp.error.ise_on_clear=Illegal to clear() when buffer size == 0
+jsp.error.setproperty.beanNotFound=setProperty: Bean {0} not found
+jsp.error.getproperty.beanNotFound=getProperty: Bean {0} not found
+jsp.error.setproperty.ClassNotFound=setProperty: Class {0} not found
+# typo ?
+#jsp.error.setproperty.invalidSayntax=setProperty: can't have non-null value when property=*
+jsp.error.setproperty.invalidSyntax=setProperty: can't have non-null value when property=*
+jsp.error.setproperty.beanInfoNotFound=setproperty: beanInfo for bean {0} not found
+jsp.error.setproperty.paramOrValue=setProperty: either param or value can be present
+jsp.error.setproperty.arrayVal=setProperty: can't set array property {0} through a string constant value
+jsp.warning.keepgen=Warning: Invalid value for the initParam keepgenerated. Will use the default value of \"false\"
+jsp.warning.xpoweredBy=Warning: Invalid value for the initParam xpoweredBy. Will use the default value of \"false\"
+jsp.warning.enablePooling=Warning: Invalid value for the initParam enablePooling. Will use the default value of \"true\"
+jsp.warning.invalidTagPoolSize=Warning: Invalid value for the init parameter named tagPoolSize. Will use default size of {0}
+jsp.warning.mappedFile=Warning: Invalid value for the initParam mappedFile. Will use the default value of \"false\"
+jsp.warning.sendErrToClient=Warning: Invalid value for the initParam sendErrToClient. Will use the default value of \"false\"
+jsp.warning.classDebugInfo=Warning: Invalid value for the initParam classdebuginfo. Will use the default value of \"false\"
+jsp.warning.checkInterval=Warning: Invalid value for the initParam checkInterval. Will use the default value of \"300\" seconds
+jsp.warning.modificationTestInterval=Warning: Invalid value for the initParam modificationTestInterval. Will use the default value of \"4\" seconds
+jsp.warning.development=Warning: Invalid value for the initParam development. Will use the default value of \"true\"
+jsp.warning.fork=Warning: Invalid value for the initParam fork. Will use the default value of \"true\"
+jsp.warning.reloading=Warning: Invalid value for the initParam reloading. Will use the default value of \"true\"
+jsp.warning.dumpSmap=Warning: Invalid value for the initParam dumpSmap. Will use the default value of \"false\"
+jsp.warning.genchararray=Warning: Invalid value for the initParam genStrAsCharArray. Will use the default value of \"false\"
+jsp.warning.suppressSmap=Warning: Invalid value for the initParam suppressSmap. Will use the default value of \"false\"
+jsp.error.badtaglib=Unable to open taglibrary {0} : {1}
+jsp.error.badGetReader=Cannot create a reader when the stream is not buffered
+jsp.warning.unknown.element.in.taglib=Unknown element ({0}) in taglib
+jsp.warning.unknown.element.in.tag=Unknown element ({0}) in tag
+jsp.warning.unknown.element.in.tagfile=Unknown element ({0}) in tag-file
+jsp.warning.unknown.element.in.attribute=Unknown element ({0}) in attribute
+jsp.warning.unknown.element.in.variable=Unknown element ({0}) in variable
+jsp.warning.unknown.element.in.validator=Unknown element ({0}) in validator
+jsp.warning.unknown.element.in.initParam=Unknown element ({0}) in validator's init-param
+jsp.warning.unknown.element.in.function=Unknown element ({0}) in function
+jsp.error.more.than.one.taglib=More than one taglib in the TLD: {0}
+jsp.error.teiclass.instantiation=Failed to load or instantiate TagExtraInfo class: {0}
+jsp.error.non_null_tei_and_var_subelems=Tag {0} has one or more variable subelements and a TagExtraInfo class that returns one or more VariableInfo
+jsp.error.parse.error.in.TLD=Parse Error in the tag library descriptor: {0}
+jsp.error.unable.to.open.TLD=Unable to open the tag library descriptor: {0}
+jsp.buffer.size.zero=Buffer size <= 0
+jsp.error.file.not.found=File \"{0}\" not found
+jsp.message.copyinguri=Copying {0} into {1}
+jsp.message.htmlcomment=\nStripping Comment: \t{0}
+jsp.message.handling_directive=\nHandling Directive: {0}\t{1}
+jsp.message.handling_plugin=\nPlugin: {0}
+jsp.message.package_name_is=Package name is: {0}
+jsp.message.class_name_is=Class name is: {0}
+jsp.message.java_file_name_is=Java file name is: {0}
+jsp.message.class_file_name_is=Class file name is: {0}
+jsp.message.accepted=Accepted {0} at {1}
+jsp.message.adding_jar=Adding jar {0} to my classpath
+jsp.message.compiling_with=Compiling with: {0}
+jsp.message.template_text=template text
+jsp.error.missing_attribute=According to the TLD or the tag file, attribute {0} is mandatory for tag {1}
+jsp.error.bad_attribute=Attribute {0} invalid for tag {1} according to TLD
+jsp.error.tld.unable_to_read=Unable to read TLD \"{1}\" from JAR file \"{0}\": {2}
+jsp.error.tld.unable_to_get_jar=Unable to get JAR resource \"{0}\" containing TLD: {1}
+jsp.error.tld.missing_jar=Missing JAR resource \"{0}\" containing TLD
+jsp.error.webxml_not_found=Could not locate web.xml
+jsp.cmd_line.usage=Usage: jsptoservlet [-dd <path/to/outputDirectory>] [-keepgenerated] \
+<.jsp files>
+jsp.message.cp_is=Classpath {0} is: {1}
+jsp.error.unable.to_load_taghandler_class=Unable to load tag handler class {0} because of {1}
+jsp.error.unable.to_find_method=Unable to find setter method for attribute: {0}
+jsp.error.unable.to_convert_string=Unable to convert a String to {0} for attribute {1}
+jsp.error.unable.to_introspect=Unable to introspect on tag handler class: {0} because of {1}
+jsp.error.bad_tag=No tag \"{0}\" defined in tag library imported with prefix \"{1}\"
+jsp.error.xml.bad_tag=No tag \"{0}\" defined in tag library associated with uri \"{1}\"
+jsp.error.bad_string_Character=Cannot extract a Character from a zero length array
+jsp.error.bad_string_char=Cannot extract a char from a zero length array
+jsp.warning.compiler.class.cantcreate=Can't create an instance of specified compiler plugin class {0} due to {1}. Will default to Sun Java Compiler.
+jsp.warning.compiler.class.notfound=Specified compiler plugin class {0} not found. Will default to Sun Java Compiler.
+jsp.warning.compiler.path.notfound=Specified compiler path {0} not found. Will default to system PATH.
+jsp.error.jspc.uriroot_not_dir=The -uriroot option must specify a pre-existing directory
+jsp.error.jspc.missingTarget=Missing target: Must specify -webapp or -uriroot, or one or more JSP pages
+jsp.error.jspc.no_uriroot=The uriroot is not specified and cannot be located with the specified JSP file(s)
+jspc.implicit.uriRoot=uriRoot implicitly set to "{0}"
+jspc.usage=Usage: jspc <options> [--] <jsp files>\n\
+where jsp files is\n\
+\    -webapp <dir>      A directory containing a web-app, whose JSP pages\n\
+\                       will be processed recursively\n\
+or any number of\n\
+\    <file>             A file to be parsed as a JSP page\n\
+where options include:\n\
+\    -help              Print this help message\n\
+\    -v                 Verbose mode\n\
+\    -d <dir>           Output Directory (default -Djava.io.tmpdir)\n\
+\    -l                 Outputs the name of the JSP page upon failure\n\
+\    -s                 Outputs the name of the JSP page upon success\n\
+\    -p <name>          Name of target package (default org.apache.jsp)\n\
+\    -c <name>          Name of target class name (only applies to first JSP page)\n\
+\    -mapped            Generates separate write() calls for each HTML line in the JSP\n\
+\    -die[#]            Generates an error return code (#) on fatal errors (default 1)\n\
+\    -uribase <dir>     The uri directory compilations should be relative to\n\
+\                       (default "/")\n\
+\    -uriroot <dir>     Same as -webapp\n\
+\    -compile           Compiles generated servlets\n\
+\    -webinc <file>     Creates a partial servlet mappings in the file\n\
+\    -webxml <file>     Creates a complete web.xml in the file\n\
+\    -ieplugin <clsid>  Java Plugin classid for Internet Explorer\n\
+\    -classpath <path>  Overrides java.class.path system property\n\
+\    -xpoweredBy        Add X-Powered-By response header\n\
+\    -trimSpaces        Trim spaces in template text between actions, directives\n\
+\    -javaEncoding <enc> Set the encoding charset for Java classes (default UTF-8)\n\
+\    -source <version>   Set the -source argument to the compiler (default 1.4)\n\
+\    -target <version>   Set the -target argument to the compiler (default 1.4)\n\
+
+jspc.webxml.header=<?xml version="1.0" encoding="ISO-8859-1"?>\n\
+\n\
+<!DOCTYPE web-app\n\
+\    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"\n\
+\    "http://java.sun.com/dtd/web-app_2_3.dtd">\n\
+<!--\n\
+Automatically created by Apache Jakarta Tomcat JspC.\n\
+-->\n\
+<web-app>\n\
+\n
+jspc.webxml.footer=\n\
+</web-app>\n\
+\n
+jspc.webinc.header=\n\
+<!--\n\
+Automatically created by Apache Jakarta Tomcat JspC.\n\
+Place this fragment in the web.xml before all icon, display-name,\n\
+description, distributable, and context-param elements.\n\
+-->\n
+jspc.webinc.footer=\n\
+<!--\n\
+All session-config, mime-mapping, welcome-file-list, error-page, taglib,\n\
+resource-ref, security-constraint, login-config, security-role,\n\
+env-entry, and ejb-ref elements should follow this fragment.\n\
+-->\n
+jspc.webinc.insertEnd=<!-- JSPC servlet mappings end -->
+jspc.webinc.insertStart=<!-- JSPC servlet mappings start -->
+jspc.error.jasperException=error-the file ''{0}'' generated the following parse exception: {1}
+jspc.error.generalException=ERROR-the file ''{0}'' generated the following general exception:
+jspc.error.fileDoesNotExist=The file argument ''{0}'' does not exist
+jspc.error.emptyWebApp=-webapp requires a trailing file argument
+jsp.error.library.invalid=JSP page is invalid according to library {0}: {1}
+jsp.error.tlvclass.instantiation=Failed to load or instantiate TagLibraryValidator class: {0}
+jsp.error.tlv.invalid.page=Validation error messages from TagLibraryValidator for {0}
+jsp.error.tei.invalid.attributes=Validation error messages from TagExtraInfo for {0}
+jsp.parser.sax.propertynotsupported=SAX property not supported: {0}
+jsp.parser.sax.propertynotrecognized=SAX property not recognized: {0}
+jsp.parser.sax.featurenotsupported=SAX feature not supported: {0}
+jsp.parser.sax.featurenotrecognized=SAX feature not recognized: {0}
+jsp.error.no.more.content=End of content reached while more parsing required: tag nesting error?
+jsp.error.parse.xml=XML parsing error on file {0}
+jsp.error.parse.xml.line=XML parsing error on file {0}: (line {1}, col {2})
+jsp.error.parse.xml.scripting.invalid.body=Body of {0} element must not contain any XML elements
+jsp.error.internal.tldinit=Unable to initialize TldLocationsCache: {0}
+jsp.error.internal.filenotfound=Internal Error: File {0} not found
+jsp.error.internal.evaluator_not_found=Internal error: unable to load expression evaluator
+jsp.error.parse.xml.invalidPublicId=Invalid PUBLIC ID: {0}
+jsp.error.include.flush.invalid.value=Invalid value for the flush attribute: {0}
+jsp.error.unsupported.encoding=Unsupported encoding: {0}
+tld.error.variableNotAllowed=It is an error for a tag that has one or more variable subelements to have a TagExtraInfo class that returns a non-null object.
+jsp.error.tldInWebDotXmlNotFound=Could not locate TLD {1} for URI {0} specified in web.xml
+jsp.error.taglibDirective.absUriCannotBeResolved=The absolute uri: {0} cannot be resolved in either web.xml or the jar files deployed with this application
+jsp.error.taglibDirective.missing.location=Neither \'uri\' nor \'tagdir\' attribute specified
+jsp.error.taglibDirective.both_uri_and_tagdir=Both \'uri\' and \'tagdir\' attributes specified
+jsp.error.invalid.tagdir=Tag file directory {0} does not start with \"/WEB-INF/tags\"
+jsp.error.unterminated.user.tag=Unterminated user-defined tag: ending tag {0} not found or incorrectly nested
+#jspx.error.templateDataNotInJspCdata=Validation Error: Element &lt;{0}&gt; cannot have template data. Template data must be encapsulated within a &lt;jsp:cdata&gt; element. [JSP1.2 PFD section 5.1.9]\nTemplate data in error: {1}
+jspx.error.templateDataNotInJspCdata=Validation Error: Element &lt;{0}&gt; cannot have template data. Template data must be encapsulated within a &lt;jsp:text&gt; element. [JSP1.2 PFD section 5.1.9]\nTemplate data in error: {1}
+#Error while processing taglib jar file {0}: {1}
+jsp.error.taglib.reserved.prefix=The taglib prefix {0} is reserved
+jsp.error.invalid.javaEncoding=Invalid java encodings. Tried {0} and then {1}. Both failed.
+jsp.error.needAlternateJavaEncoding=Default java encoding {0} is invalid on your java platform. An alternate can be specified via the 'javaEncoding' parameter of JspServlet.
+#Error when compiling, used for jsp line number error messages
+jsp.error.single.line.number=An error occurred at line: {0} in the jsp file: {1}
+jsp.error.multiple.line.number=\n\nAn error occurred between lines: {0} and {1} in the jsp file: {2}\n\n
+jsp.error.corresponding.servlet=Generated servlet error:\n
+jsp.error.empty.body.not.allowed=Empty body not allowed for {0}
+jsp.error.jspbody.required=Must use jsp:body to specify tag body for {0} if jsp:attribute is used.
+jsp.error.jspbody.emptybody.only=The {0} tag can only have jsp:attribute in its body.
+jsp.error.no.scriptlets=Scripting elements ( &lt;%!, &lt;jsp:declaration, &lt;%=, &lt;jsp:expression, &lt;%, &lt;jsp:scriptlet ) are disallowed here.
+jsp.error.internal.unexpected_node_type=Internal Error: Unexpected node type encountered
+jsp.error.tld.fn.invalid.signature=Invalid syntax for function signature in TLD.  Tag Library: {0}, Function: {1}
+jsp.error.tld.fn.duplicate.name=Duplicate function name {0} in tag library {1}
+jsp.error.tld.fn.invalid.signature.commaexpected=Invalid syntax for function signature in TLD.  Comma ',' expected.  Tag Library: {0}, Function: {1}.
+jsp.error.tld.fn.invalid.signature.parenexpected=Invalid syntax for function signature in TLD.  Parenthesis '(' expected.  Tag Library: {0}, Function: {1}.
+jsp.error.tld.mandatory.element.missing=Mandatory TLD element missing or empty: {0}
+jsp.error.dynamic.attributes.not.implemented=The {0} tag declares that it accepts dynamic attributes but does not implement the required interface
+jsp.error.nomatching.fragment=Cannot find an attribute directive (with name={0} and fragment=true) prior to the fragment directive.
+jsp.error.attribute.noequal=equal symbol expected
+jsp.error.attribute.noquote=quote symbol expected
+jsp.error.attribute.unterminated=attribute for {0} is not properly terminated
+jsp.error.missing.tagInfo=TagInfo object for {0} is missing from TLD
+jsp.error.fragmentwithtype=Cannot specify both 'fragment' and 'type' attributes.  If 'fragment' is present, 'type' is fixed as 'javax.servlet.jsp.tagext.JspFragment'
+jsp.error.fragmentwithrtexprvalue=Cannot specify both 'fragment' and 'rtexprvalue' attributes.  If 'fragment' is present, 'rtexprvalue' is fixed as 'true'
+jsp.error.fragmentWithDeclareOrScope=Both 'fragment' and 'declare' or 'scope' attributes specified in variable directive
+jsp.error.var_and_varReader=Only one of \'var\' or \'varReader\' may be specified
+jsp.error.missing_var_or_varReader=Missing \'var\' or \'varReader\' attribute
+jsp.warning.bad.urlpattern.propertygroup=Bad value {0} in the url-pattern subelement in web.xml
+jsp.error.unknown_attribute_type=Unknown attribute type ({1}) for attribute {0}.
+jsp.error.jspelement.missing.name=Mandatory XML-style \'name\' attribute missing
+jsp.error.xmlns.redefinition.notimplemented=Internal error: Attempt to redefine xmlns:{0}.  Redefinition of namespaces is not implemented.
+jsp.error.could.not.add.taglibraries=Could not add one or more tag libraries.
+jsp.error.duplicate.name.jspattribute=The attribute {0} specified in the standard or custom action also appears as the value of the name attribute in the enclosed jsp:attribute
+jsp.error.not.in.template={0} not allowed in a template text body.
+jsp.error.badStandardAction=Invalid standard action
+jsp.error.xml.badStandardAction=Invalid standard action: {0}
+jsp.error.tagdirective.badbodycontent=Invalid body-content ({0}) in tag directive
+jsp.error.simpletag.badbodycontent=The TLD for the class {0} specifies an invalid body-content (JSP) for a SimpleTag.
+jsp.error.config_pagedir_encoding_mismatch=Page-encoding specified in jsp-property-group ({0}) is different from that specified in page directive ({1})
+jsp.error.prolog_pagedir_encoding_mismatch=Page-encoding specified in XML prolog ({0}) is different from that specified in page directive ({1})
+jsp.error.prolog_config_encoding_mismatch=Page-encoding specified in XML prolog ({0}) is different from that specified in jsp-property-group ({1})
+jsp.error.attribute.custom.non_rt_with_expr=According to TLD or attribute directive in tag file, attribute {0} does not accept any expressions
+jsp.error.attribute.standard.non_rt_with_expr=The {0} attribute of the {1} standard action does not accept any expressions
+jsp.error.scripting.variable.missing_name=Unable to determine scripting variable name from attribute {0}
+jasper.error.emptybodycontent.nonempty=According to TLD, tag {0} must be empty, but is not
+jsp.error.tagfile.nameNotUnique=The value of {0} and the value of {1} in line {2} are the same.
+jsp.error.tagfile.nameFrom.noAttribute=Cannot find an attribute directive with a name attribute with a value \"{0}\", the value of this name-from-attribute attribute.
+jsp.error.tagfile.nameFrom.badAttribute=The attribute directive (declared in line {1} and whose name attribute is \"{0}\", the value of this name-from-attribute attribute) must be of type java.lang.String, is \"required\" and not a \"rtexprvalue\".
+jsp.error.page.noSession=Cannot access session scope in page that does not participate in any session
+jsp.error.usebean.noSession=Illegal for useBean to use session scope when JSP page declares (via page directive) that it does not participate in sessions
+jsp.error.xml.encodingByteOrderUnsupported = Given byte order for encoding \"{0}\" is not supported.
+jsp.error.xml.encodingDeclInvalid = Invalid encoding name \"{0}\".
+jsp.error.xml.encodingDeclRequired = The encoding declaration is required in the text declaration.
+jsp.error.xml.morePseudoAttributes = more pseudo attributes is expected.
+jsp.error.xml.noMorePseudoAttributes = no more pseudo attributes is allowed.
+jsp.error.xml.versionInfoRequired = The version is required in the XML declaration.
+jsp.error.xml.xmlDeclUnterminated = The XML declaration must end with \"?>\".
+jsp.error.xml.reservedPITarget = The processing instruction target matching \"[xX][mM][lL]\" is not allowed.
+jsp.error.xml.spaceRequiredInPI = White space is required between the processing instruction target and data.
+jsp.error.xml.invalidCharInContent = An invalid XML character (Unicode: 0x{0}) was found in the element content of the document.
+jsp.error.xml.spaceRequiredBeforeStandalone = White space is required before the encoding pseudo attribute in the XML declaration.
+jsp.error.xml.sdDeclInvalid = The standalone document declaration value must be \"yes\" or \"no\", not \"{0}\".
+jsp.error.xml.invalidCharInPI = An invalid XML character (Unicode: 0x{0}) was found in the processing instruction.
+jsp.error.xml.versionNotSupported = XML version \"{0}\" is not supported, only XML 1.0 is supported.
+jsp.error.xml.pseudoAttrNameExpected = a pseudo attribute name is expected.
+jsp.error.xml.expectedByte = Expected byte {0} of {1}-byte UTF-8 sequence.
+jsp.error.xml.invalidByte = Invalid byte {0} of {1}-byte UTF-8 sequence.
+jsp.error.xml.operationNotSupported = Operation \"{0}\" not supported by {1} reader.
+jsp.error.xml.invalidHighSurrogate = High surrogate bits in UTF-8 sequence must not exceed 0x10 but found 0x{0}.
+jsp.error.xml.invalidASCII = Byte \"{0}\" not 7-bit ASCII.
+jsp.error.xml.spaceRequiredBeforeEncodingInXMLDecl = White space is required before the encoding pseudo attribute in the XML declaration.
+jsp.error.xml.spaceRequiredBeforeEncodingInTextDecl = White space is required before the encoding pseudo attribute in the text declaration.
+jsp.error.xml.spaceRequiredBeforeVersionInTextDecl = White space is required before the version pseudo attribute in the text declaration.
+jsp.error.xml.spaceRequiredBeforeVersionInXMLDecl = White space is required before the version pseudo attribute in the XML declaration.
+jsp.error.xml.eqRequiredInXMLDecl = The '' = '' character must follow \"{0}\" in the XML declaration.
+jsp.error.xml.eqRequiredInTextDecl = The '' = '' character must follow \"{0}\" in the text declaration.
+jsp.error.xml.quoteRequiredInTextDecl = The value following \"{0}\" in the text declaration must be a quoted string.
+jsp.error.xml.quoteRequiredInXMLDecl = The value following \"{0}\" in the XML declaration must be a quoted string.
+jsp.error.xml.invalidCharInTextDecl = An invalid XML character (Unicode: 0x{0}) was found in the text declaration.
+jsp.error.xml.invalidCharInXMLDecl = An invalid XML character (Unicode: 0x{0}) was found in the XML declaration.
+jsp.error.xml.closeQuoteMissingInTextDecl = closing quote in the value following \"{0}\" in the text declaration is missing.
+jsp.error.xml.closeQuoteMissingInXMLDecl = closing quote in the value following \"{0}\" in the XML declaration is missing.
+jsp.error.xml.invalidHighSurrogate = High surrogate bits in UTF-8 sequence must not exceed 0x10 but found 0x{0}.
+jsp.error.multiple.jsp = Cannot have multiple specifications of 
+jsp.error.jspoutput.conflict=&lt;jsp:output&gt;: illegal to have multiple occurrences of \"{0}\" with different values (old: {1}, new: {2})
+jsp.error.jspoutput.doctypenamesystem=&lt;jsp:output&gt;: 'doctype-root-element' and 'doctype-system' attributes must appear together
+jsp.error.jspoutput.doctypepulicsystem=&lt;jsp:output&gt;: 'doctype-system' attribute must appear if 'doctype-public' attribute appears
+jsp.error.jspoutput.nonemptybody=&lt;jsp:output&gt; must not have a body
+jsp.error.jspoutput.invalidUse=&lt;jsp:output&gt; must not be used in standard syntax
+jsp.error.attributes.not.allowed = {0} must not have any attributes
+jsp.error.tagfile.badSuffix=Missing \".tag\" suffix in tag file path {0}
+jsp.error.tagfile.illegalPath=Illegal tag file path: {0}, must start with \"/WEB-INF/tags\" or \"/META-INF/tags\"
+jsp.error.plugin.wrongRootElement=Name of root element in {0} different from {1}
+jsp.error.attribute.invalidPrefix=The attribute prefix {0} does not correspond to any imported tag library
+jsp.error.nested.jspattribute=A jsp:attribute standard action cannot be nested within another jsp:attribute standard action
+jsp.error.nested.jspbody=A jsp:body standard action cannot be nested within another jsp:body or jsp:attribute standard action
+jsp.error.variable.either.name=Either name-given or name-from-attribute attribute must be specified in a variable directive
+jsp.error.variable.both.name=Cannot specify both name-given or name-from-attribute attributes in a variable directive
+jsp.error.variable.alias=Both or none of the name-from-attribute and alias attributes must be specified in a variable directive
+jsp.error.attribute.null_name=Null attribute name
+jsp.error.jsptext.badcontent=\'&lt;\', when appears in the body of &lt;jsp:text&gt;, must be encapsulated within a CDATA
+jsp.error.jsproot.version.invalid=Invalid version number: \"{0}\", must be \"1.2\" or \"2.0\"
+jsp.error.noFunctionPrefix=The function {0} must be used with a prefix when a default namespace is not specified
+jsp.error.noFunction=The function {0} cannot be located with the specified prefix
+jsp.error.noFunctionMethod=Method \"{0}\" for function \"{1}\" not found in class \"{2}\"
+jsp.error.function.classnotfound=The class {0} specified in TLD for the function {1} cannot be found: {2}
+jsp.error.signature.classnotfound=The class {0} specified in the method signature in TLD for the function {1} cannot be found. {2}
+jsp.error.text.has_subelement=&lt;jsp:text&gt; must not have any subelements
+jsp.error.data.file.read=Error reading file \"{0}\"
+jsp.error.prefix.refined=Attempt to redefine the prefix {0} to {1}, when it was already defined as {2} in the current scope.
+jsp.error.nested_jsproot=Nested &lt;jsp:root&gt;
+jsp.error.unbalanced.endtag=The end tag \"&lt;/{0}\" is unbalanced
+jsp.error.invalid.bean=The value for the useBean class attribute {0} is invalid.
+jsp.error.prefix.use_before_dcl=The prefix {0} specified in this tag directive has been previously used by an action in file {1} line {2}.
diff --git a/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings_es.properties b/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings_es.properties
new file mode 100644
index 0000000..546b460
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings_es.properties
@@ -0,0 +1,408 @@
+# $Id$
+#
+# Default localized string information
+# Localized para Locale es_ES
+
+jsp.error.bad.servlet.engine=¡Versión incorrecta del motor servlet!
+jsp.error.no.scratch.dir=El motor JSP no tiene configurado un directorio de trabajo.\
+\n Añada \"jsp.initparams=scratchdir=<dir-name>\" \
+\n en el fichero servlets.properties para este contexto.
+jsp.error.bad.scratch.dir=El directorio de trabajo especificado: {0} no es utilizable.
+jsp.message.scratch.dir.is=El directorio de trabajo para el motor JSP es: {0}
+jsp.message.parent_class_loader_is=El cargador de clases es: {0}
+jsp.message.dont.modify.servlets=IMPORTANTE: No modifique los servlets generados
+jsp.error.not.impl.comments=Error Interno: Comments no implementado
+jsp.error.not.impl.directives=Error Interno: Directives no implementado
+jsp.error.not.impl.declarations=Error Interno: Declarations no implementado
+jsp.error.not.impl.expressions=Error Interno: Expressions no implementado
+jsp.error.not.impl.scriptlets=Error Interno: Scriptlets no implementado
+jsp.error.not.impl.useBean=Error Interno: useBean no implementado
+jsp.error.not.impl.getp=Error Interno: getProperty no implementado
+jsp.error.not.impl.setp=Error Interno: setProperty no implementado
+jsp.error.not.impl.plugin=Error Interno: plugin no implementado
+jsp.error.not.impl.forward=Error Interno: forward no implementado
+jsp.error.not.impl.include=Error Interno: include no implementado
+jsp.error.unavailable=JSP ha sido marcado como no disponible
+jsp.error.usebean.missing.attribute=useBean: falta atributo id o está mal digitado
+jsp.error.usebean.missing.type=useBean ({0}): Se debe de especificar atributo class o type:
+jsp.error.usebean.duplicate=useBean: Nombre de bean duplicado: {0}
+jsp.error.usebean.prohibited.as.session=No puedo usar como bean de sesión {0} ya que está prohibido \
+por directiva jsp definida previamente: 
+jsp.error.usebean.not.both=useBean: No puede especificar ambos atributos class y beanName: 
+jsp.error.usebean.bad.type.cast=useBean ({0}): Tipo ({1}) no es asignable desde clase ({2}) 
+jsp.error.invalid.scope=Valor ilegal de atributo \'scope\': {0} (debe de ser uno de \"page\", \"request\", \"session\", o \"application\")
+jsp.error.classname=No pude determinar el nombre de clase desde el fichero .class
+jsp.warning.bad.type=Aviso: tipo incorrecto en archivo .class
+jsp.error.data.file.write=Error mientras escribía el archivo de datos
+jsp.error.page.invalid.buffer=Directiva Page: medida de buffer inválida
+jsp.error.page.conflict.contenttype=Directiva Page: es ilegal tener múltiples ocurrencias de 'contentType' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.invalid.contenttype=Directiva Page: valor incorrecto para contentType
+jsp.error.page.conflict.session=Directiva Page: es ilegal tener múltiples ocurrencias de 'session' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.invalid.session=Directiva Page: valor incorrecto para session
+jsp.error.page.conflict.buffer=Directiva Page: es ilegal tener múltiples ocurrencias de 'buffer'con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.invalid.buffer=Directiva Page: valor incorrecto para buffer
+jsp.error.page.conflict.autoflush=Directiva Page: es ilegal tener múltiples ocurrencias de 'autoFlush' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.invalid.autoflush==Directiva Page: valor incorrecto para autoFlush
+jsp.error.page.conflict.isthreadsafe=Directiva Page: es ilegal tener múltiples ocurrencias de 'isThreadSafe' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.invalid.isthreadsafe==Directiva Page: valor incorrecto para isThreadSafe
+jsp.error.page.conflict.info=Directiva Page: es ilegal tener múltiples ocurrencias de 'info' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.invalid.info==Directiva Page: valor incorrecto para info
+jsp.error.page.conflict.iserrorpage=Directiva Page: es ilegal tener múltiples ocurrencias de 'isErrorPage' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.invalid.iserrorpage==Directiva Page: valor incorrecto para isErrorPage
+jsp.error.page.conflict.errorpage=Directiva Page: es ilegal tener múltiples ocurrencias de 'errorPage' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.conflict.language=Directiva Page: es ilegal tener múltiples ocurrencias de 'language' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.tag.conflict.language=Directiva Tag: es ilegal tener múltiples ocurrencias de 'language' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.language.nonjava=Directiva Page: atributo language incorrecto
+jsp.error.tag.language.nonjava=Directiva Tag: atributo language incorrecto
+jsp.error.page.defafterusar.language=Directiva Page: No puede definir language tras un scriptlet 
+jsp.error.page.nomapping.language=Directiva Page: No hay mapeado para language: 
+jsp.error.page.conflict.extends=Directiva Page: es ilegal tener múltiples ocurrencias de 'extends' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.conflict.iselignored=Directiva Page: es ilegal tener múltiples ocurrencias de 'isELIgnored' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.tag.conflict.iselignored=Directiva Tag: es ilegal tener múltiples ocurrencias de 'isELIgnored' con valores distintos (viejo: {0}, nuevo: {1})
+jsp.error.page.invalid.iselignored=Directiva Page: valor inválido para isELIgnored
+jsp.error.tag.invalid.iselignored=Directiva Tag: valor incorrecto para isELIgnored
+jsp.error.page.multi.pageencoding=La directiva Page no debe de tener múltiples ocurrencias de pageencoding
+jsp.error.tag.conflict.attr=Directiva Tag: es ilegal tener múltiples ocurrencias del atributo \"{0}\" con valores distintos (viejo: {1}, nuevo: {2})
+jsp.error.tag.multi.pageencoding=La directiva Tag no debe de tener múltiples ocurrencias de pageencoding
+jsp.error.page.bad_b_and_a_combo=Directiva Page: Combinación ilegal de buffer=\"none\" y autoFlush=\"false\"
+jsp.error.not.impl.taglib=Error Interno: Tag extensions no implementado
+jsp.error.include.missing.file=No tiene argumento de nombre de fichero 
+jsp.error.include.bad.file=Argumento de nombre de fichero no válido
+jsp.error.include.exception=No se puede incluir {0}
+jsp.error.stream.closed=Stream cerrado
+jsp.error.invalid.forward=Tag forward no válido
+jsp.error.unknownException=¡Error no caturado!. Deberías de considerar el poner una página de error para avisar de los errores más elegantemente
+jsp.error.invalid.directive=Directiva no válida
+jsp.error.directive.istagfile=La Directiva {0} no puede usarse en archivo de tag
+jsp.error.directive.isnottagfile=La Directiva {0} sólo se puede usar en un archivo de tag
+jsp.error.tagfile.tld.name=El atributo \"name\" de la directiva tag tiene un valor {0} mientras que el tag \"name\" del elemento \"tag-file\" en el TLD es {1}
+jsp.error.action.istagfile=La acción {0} no se puede usar en un archivo tag
+jsp.error.action.isnottagfile=La acción {0} sólo se puede usar en archivos tag
+jsp.error.unterminated=Tag {0} no terminado
+jsp.error.usebean.notinsamefile=El Tag useBean debe de empezar y terminar en el mismo archivo físico
+jsp.error.loadclass.taghandler=No se puede cargar la clase {0}
+jsp.error.unable.compile=No se puede compilar la clase para JSP
+jsp.error.unable.load=No se puede cargar la clase para JSP
+jsp.error.unable.rename=No se puede renombrar el archivo de clase {0} a {1}
+jsp.error.mandatory.atributo={0}: atributo obligatorio {1} perdido
+jsp.engine.info=Motor Jasper JSP 2.0
+jsp.error.invalid.expression="{0}" contiene expresiones incorrectas: {1}
+jsp.error.invalid.attribute={0}: Atributo incorrecto, {1}
+jsp.error.usebean.class.notfound=Clase: {0} no hallada
+jsp.error.file.cannot.read=No se puede leer el archivo: {0}
+jsp.error.file.already.registered=El archivo {0} ya se ha visto, ¿podría ser un include recursivo?
+jsp.error.file.not.registered=Archivo {0} not visto en include
+jsp.error.quotes.unterminated=Comillas no terminadas
+jsp.error.attr.quoted=El valor del atributo debería ir entre comillas
+jsp.error.attr.novalue=Atributo {0} no tiene valor
+jsp.error.tag.attr.unterminated=Lista de atributos del tag no terminada
+jsp.error.param.noname=No hay nombre en el tag PARAM
+jsp.error.param.novalue=No hay valor en el tag PARAM
+jsp.error.beans.nullbean=Se ha intentado una operación de bean en un objeto nulo
+jsp.error.beans.nobeaninfo=No se puede encontrar BeanInfo para el bean  ''{0}'' seguramente la clase no existe
+jsp.error.beans.introspection=Una excepción ha tenido lugar mientras se leía el método de lectura de la propiedad ''{0}'' en un bean del tipo ''{1}'':\n{2}
+jsp.error.beans.nomethod=No puedo encontrar un método para leer la propiedad ''{0}'' en un bean del tipo ''{1}''
+jsp.error.beans.nomethod.setproperty=No puedo encontrar un método para escribir la propiedad ''{0}'' en un bean del tipo ''{2}''
+jsp.error.beans.noproperty=No puedo encontrar información de la propiedad ''{0}'' en un bean del tipo ''{1}''
+jsp.error.beans.setproperty.noindexset=No puedo poner la propiedad indexada
+jsp.error.include.tag=Tag jsp:include no válido
+jsp.error.include.noflush=jsp:include necesita tener \"flush=true\"
+jsp.error.include.badflush=jsp:include page=\"...\" flush=\"true\" es la única combinación válida en JSP 1.0
+jsp.error.attempt_to_clear_flushed_buffer=Error: Se ha intentado limpiar un buffer que ya había sido escrito
+jsp.error.overflow=Error:Buffer de JSP desbordado
+jsp.error.paramexpected=El tag \"param\" era esperado con los atributos \"name\" y \"value\" después del tag \"params\".
+jsp.error.param.invalidUse=La acción jsp:param no debe de ser usada fuera de los elementos jsp:include, jsp:forward o jsp:params
+jsp.error.params.invalidUse=jsp:params debe de ser un hijo directo de jsp:plugin
+jsp.error.fallback.invalidUse=jsp:fallback debe de ser un hijo directo de jsp:plugin
+jsp.error.namedAttribute.invalidUse=jsp:attribute debe de ser el subelemento de una acción estándar o de cliente
+jsp.error.jspbody.invalidUse=jsp:body debe de ser el subelemento de una acción estándar o de cliente
+jsp.error.closeindividualparam=El tag param necesita ser cerrado con \"/>\"
+jsp.error.closeparams=El tag param necesita ser cerrado con /params
+jsp.error.params.emptyBody=jsp:params debe de contener al menos un jsp:param anidado
+jsp.error.params.illegalChild=jsp:params no debe de contener elementos anidados que no sean jsp:param
+jsp.error.plugin.notype=Tipo no declarado en jsp:plugin
+jsp.error.plugin.badtype=Valor ilegal para atributo 'type' en jsp:plugin: debe de ser 'bean' o 'applet'
+jsp.error.plugin.nocode=Código no declarado en jsp:plugin
+jsp.error.ise_on_clear=Es ilegal usar clear() cuando el tamaño del buffer es cero
+jsp.error.setproperty.beanNotFound=setProperty: Bean {0} no encontrado
+jsp.error.getproperty.beanNotFound=getProperty: Bean {0} no encontrado
+jsp.error.setproperty.ClassNotFound=setProperty: clase {0} no encontrada
+# typo ?
+#jsp.error.setproperty.invalidSayntax=setProperty: can't have non-null value when property=*
+jsp.error.setproperty.invalidSyantax=setProperty: No puede haber un valor no nulo cuando se ha especificado property=*
+jsp.error.setproperty.beanInfoNotFound=setproperty: beanInfo para bean {0} no encontrado
+jsp.error.setproperty.paramOrValue=setProperty: O param o value pueden estar presentes
+jsp.error.setproperty.arrayVal=setProperty: No puede escribir en la propiedad de array {0} a través de una valor de cadena literal
+jsp.warning.keepgen=Aviso: valor incorrecto para el initParam keepgen. Se usará el valor por defecto de \"false\"
+jsp.warning.xpoweredBy=Aviso: valor incorrecto para el initParam xpoweredBy. Se usará el valor por defecto de \"false\"
+jsp.warning.enablePooling=Aviso: valor incorrecto para el initParam enablePooling. Se usará el valor por defecto de \"true\"
+jsp.warning.invalidTagPoolSize=Aviso: valor incorrecto para el parámetro init llamado tagPoolSize. Se usará la medida por defecto de {0}
+jsp.warning.mappedFile=Aviso: valor incorrecto para el initParam mappedFile. Se usará el valor por defecto de \"false\"
+jsp.warning.sendErrToClient=Aviso: valor incorrecto para el initParam sendErrToClient. Se usará el valor por defecto de \"false\"
+jsp.warning.classDebugInfo=Aviso: valor incorrecto para el initParam classdebuginfo. Se usará el valor por defecto de \"false\"
+jsp.warning.checkInterval=Aviso: valor incorrecto para el initParam checkInterval. Se usará el valor por defecto de \"300\" segundos
+jsp.warning.development=Aviso: valor incorrecto para el initParam development. Se usará el valor por defecto de \"true\"
+jsp.warning.fork=Aviso: valor incorrecto para el initParam fork. Se usará el valor por defecto de \"true\"
+jsp.warning.reloading=Aviso: valor incorrecto para el initParam reloading. Se usará el valor por defecto de \"true\"
+jsp.warning.dumpSmap=Aviso: valor incorrecto para el initParam dumpSmap. Se usará el valor por defecto de \"false\"
+jsp.warning.genchararray=Aviso: valor incorrecto para el initParam genStrAsCharArray. Se usará el valor por defecto de \"false\"
+jsp.warning.suppressSmap=Aviso: valor incorrecto para el initParam suppressSmap. Se usará el valor por defecto de \"false\"
+jsp.error.badtaglib=No se puede abrir la biblioteca de tags {0}: {1}
+jsp.error.badGetReader=No se puede crear un reader cuando el stream no tiene buffer
+jsp.warning.unknown.element.in.taglib=Elemento desconocido ({0}) en taglib
+jsp.warning.unknown.element.in.tag=Elemento desconocido ({0}) en tag
+jsp.warning.unknown.element.in.tagfile=Elemento desconocido ({0}) en tag-file
+jsp.warning.unknown.element.in.attribute=Elemento desconocido ({0}) en attribute
+jsp.warning.unknown.element.in.variable=Elemento desconocido ({0}) en variable
+jsp.warning.unknown.element.in.validator=Elemento desconocido ({0}) en validator
+jsp.warning.unknown.element.in.initParam=Elemento desconocido ({0}) en init-param de validator
+jsp.warning.unknown.element.in.function=Elemento desconocido ({0}) en function
+jsp.error.more.than.one.taglib=Más de una biblioteca de tags en el TLD: {0}
+jsp.error.teiclass.instantiation=No se puede cargar la clase TagExtraInfo llamada: {0}
+jsp.error.non_null_tei_and_var_subelems=Tag {0} tiene uno o más subelementos variable y una clase TagExtraInfo que devuelve una o más VariableInfo
+jsp.error.parse.error.in.TLD=Error de análisis en el descriptor de biblioteca de tags: {0}
+jsp.error.unable.to.open.TLD=No se puede abrir el descriptor de biblioteca de tags: {0}
+jsp.buffer.size.zero=Tamaño de buffer <= 0
+jsp.error.file.not.found=Archivo JSP \"{0}\" no encontrado
+jsp.message.copyinguri=Copiando {0} en {1}
+jsp.message.htmlcomment=\nQuitando comentario: \t{0}
+jsp.message.handling_directive=\nResolviendo directiva: {0}\t{1}
+jsp.message.handling_plugin=\nPlugin: {0}
+jsp.message.package_name_is=El Nombre del Package es: {0}
+jsp.message.class_name_is=El Nombre de la clase es: {0}
+jsp.message.java_file_name_is=El Nombre del Archivo Java es: {0}
+jsp.message.class_file_name_is=El Nombre del Archivo de clase es: {0}
+jsp.message.accepted=Aceptó {0} en {1}
+jsp.message.adding_jar=Añadiendo jar {0} a mi classpath
+jsp.message.compiling_with=Compilado con: {0}
+jsp.message.template_text=texto plantilla
+jsp.error.missing_attribute=De acuerdo con el TLD el atributo {0} es obligatorio para el tag {1}
+jsp.error.bad_attribute=El atributo {0} no es válido según el TLD especificado
+jsp.error.tld.unable_to_read=Imposible de leer TLD \"{1}\" desde archivo JAR \"{0}\": {2}
+jsp.error.tld.unable_to_get_jar=Imposible obtener recurso JAR \"{0}\" conteniendo TLD: {1}
+jsp.error.tld.missing_jar=Falta recurso JAR \"{0}\" conteniendo TLD
+jsp.error.webxml_not_found=No puedo localizar web.xml
+jsp.cmd_line.usage=Uso: jsptoservlet [-dd <ruta/a/DirectorioSalida>] [-keepgenerated] <Archivos .jsp>
+jsp.message.cp_is=Classpath {0} es: {1}
+jsp.error.unable.to_load_taghandler_class=No se puede cargar clase manejadora {0} del tag a causa de {1}
+jsp.error.unable.to_find_method=No se puede encontrar el método de escritura para el atributo: {0}
+jsp.error.unable.to_convert_string=No pude convertir un String a {0} para atributo {1}
+jsp.error.unable.to_introspect=No se puede hacer introspección en manejador de tag clase: {0} a causa de {1}
+jsp.error.bad_tag=No existe el tag {0} en la biblioteca importada con prefijo {1}
+jsp.error.xml.bad_tag=No se ha definido el tag \"{0}\" en la biblioteca tag asociada con uri \"{1}\"
+jsp.error.bad_string_Character=No puede extraer un Character desde un array de tamaño cero
+jsp.error.bad_string_char=No puede extraer un char desde un array de tamaño cero
+jsp.warning.compiler.class.cantcreate=No puedo crear una instancia de la clase especificada {0} de plugin del compilador debido a {1}. Se usará el compilador Java de Sun.
+jsp.warning.compiler.class.notfound=No puedo encontrar una instancia de la clase {0} de plugin de compilador. Se usará el compilador del Java de Sun.
+jsp.warning.compiler.path.notfound=Trayectoria del compilador especificado {0} no encontrada. Se usará el PATH del sistema.
+jsp.error.jspc.uriroot_not_dir=La opción -uriroot debe de especificar un directorio ya existente
+jsp.error.jspc.missingTarget=Falta target: Debe de especificar -webapp o -uriroot o una o más páginas JSP
+jsp.error.jspc.no_uriroot=No se ha especificado uriroot y no puede ser localizado en los archivos JSP especificados
+jspc.implicit.uriRoot=uriRoot implicitamente puesto a "{0}"
+jspc.usage=Uso: jspc <opciones> [--] <Archivos JSP>\n\
+donde <Archivos JSP> son:\n\
+\    -webapp <dir>      Un directorio conteniendo una web-app. Todas las\n\
+\                       páginas jsp serán compiladas recursivamente\n\
+o cualquier número de\n\
+\    <Archivo>          Un Archivo para ser interpretado como una página jsp\n\
+y donde <opciones> incluyen:\n\
+\    -help              Muestra este mensaje de ayuda\n\
+\    -v                 Modo detallado\n\
+\    -d <dir>           Directorio de salida\n\
+\    -l                 Muestra el nombre de la página JSP al ocurrir un fallo\n\
+\    -s                 Muestra el nombre de la página JSP al tener éxito\n\
+\    -p <name>          Nombre del package objetivo\n\
+\                       (por defecto org.apache.jsp)\n\
+\    -c <name>          Nombre de la clase objetivo\n\
+\                       (sólo se aplica a la primera página JSP)\n\
+\    -mapped            Genera llamadas separadas a write() para cada línea de\n\
+\                       HTML en el JSP\n\
+\    -die[#]            Genera un código de retorno de error (#) en errores\n\
+\                       fatales. (por defecto 1).\n\
+\    -uribase <dir>     El directorio uri de donde deben de partir las\n\
+\                       compilaciones. (por defecto "/")\n\
+\    -uriroot <dir>     Igual que -webapp\n\
+\    -compile           Compila los servlets generados\n\
+\    -webinc <file>     Crea unos mapeos parciales de servlet en el archivo\n\
+\    -webxml <file>     Crea un web.xml completo en el archivo.\n\
+\    -ieplugin <clsid>  Java Plugin classid para Internet Explorer\n\
+\    -classpath <path>  Pasa por alto la propiedad de sistema java.class.path\n\
+\    -xpoweredBy        Añade cabecera de respuesta  X-Powered-By\n\
+\    -trimSpaces        Trim spaces in template text between actions, directives\n\
+\    -javaEncoding <enc> Set the encoding charset for Java classes (default UTF-8)\n\
+\    -source <version>   Set the -source argument to the compiler (default 1.4)\n\
+\    -target <version>   Set the -target argument to the compiler (default 1.4)\n\
+
+jspc.webxml.header=<?xml version="1.0" encoding="ISO-8859-1"?>\n\
+\n\
+<!DOCTYPE web-app\n\
+\    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"\n\
+\    "http://java.sun.com/dtd/web-app_2_3.dtd">\n\
+<!--\n\
+Creado automaticamente mediante Apache Jakarta Tomcat JspC.\n\
+-->\n\
+<web-app>\n\
+\n
+jspc.webxml.footer=\n\
+</web-app>\n\
+\n
+jspc.webinc.header=\n\
+<!--\n\
+Creado automaticamente mediante Apache Jakarta Tomcat JspC.\n\
+Coloque este fragmento en el fichero web.xml antes de \n\
+todos los elementos: icon, display-name, description, \n\
+distributable y context-param .\n\
+-->\n
+jspc.webinc.footer=\n\
+<!--\n\
+Los Elementos: session-config, mime-mapping, welcome-file-list, error-page, taglib,\n\
+resource-ref, security-constraint, login-config, security-role,\n\
+env-entry y ejb-ref deberán de ir después de este fragmento .\n\
+-->\n
+jspc.webinc.insertEnd=<!-- Fin de mapeos de servlet JSPC -->
+jspc.webinc.insertStart=<!-- Inicio de mapeos de servlet JSPC -->
+jspc.error.jasperException=error-el archivo ''{0}'' ha generado la excepción de sintáxis siguiente: {1}
+jspc.error.generalException=ERROR-el archivo ''{0}'' ha generado la excepción general siguiente:
+jspc.error.fileDoesNotExist=El archivo ''{0}'' utilizado como argumento no existe.
+jspc.error.emptyWebApp=-webapp necesita un argumento de archivo
+jsp.error.library.invalid=La página JSP es incorrecta de acuerdo a la biblioteca {0}: {1}
+jsp.error.tlvclass.instantiation=No pude cargar o instanciar clase TagLibraryValidator: {0}
+jsp.error.tlv.invalid.page=Mensajes de error de validación desde TagLibraryValidator para {0}
+jsp.error.tei.invalid.attributes=Mensajes de error de validación desde TagExtraInfo para {0}
+jsp.parser.sax.propertynotsupported=Propiedad SAX no soportada: {0}
+jsp.parser.sax.propertynotrecognized=Propiedad SAX no reconocida: {0}
+jsp.parser.sax.featurenotsupported=Característica SAX no soportada: {0}
+jsp.parser.sax.featurenotrecognized=Característica SAX no reconocida: {0}
+jsp.error.no.more.content=Alcanzado fin de contenido mietras se requería más análisis: ¿error de anidamiento de tag?
+jsp.error.parse.xml=Error de análisis XML en archivo {0}
+jsp.error.parse.xml.line=Error de análisis XML en archivo {0}: (línea {1}, col {2})
+jsp.error.parse.xml.scripting.invalid.body=El cuerpo de elemento {0} no debe de contener elementos XML
+jsp.error.internal.tldinit=No pude inicializar TldLocationsCache: {0}
+jsp.error.internal.filenotfound=Error Interno: Archivo {0} no hallado
+jsp.error.internal.evaluator_not_found=Error interno: no pude cargar evaluador de expresiones
+jsp.error.parse.xml.invalidPublicId=PUBLIC ID incorrecta: {0}
+jsp.error.include.flush.invalid.value=Valor incorrecto para atributo flush: {0}
+jsp.error.unsupported.encoding=Codificación no soportada: {0}
+tld.error.variableNotAllowed=Es un error para un tag, que tiene uno o más subelementos variables, el tener una clase TagExtraInfo que devuelve un objeto no nulo.
+jsp.error.tldInWebDotXmlNotFound=No pude localizar TLD {1} para URI {0} especificado en web.xml
+jsp.error.taglibDirective.absUriCannotBeResolved=La uri absoluta: {0} no puede resolverse o en web.xml o el los archivos jar desplegados con esta aplicación
+jsp.error.taglibDirective.missing.location=No se ha especificado ni el atributo \'uri\' ni el \'tagdir\'
+jsp.error.taglibDirective.both_uri_and_tagdir=Se han especificado ambos atributos \'uri\' y \'tagdir\'
+jsp.error.invalid.tagdir=El directorio de archivo Tag {0} no comienza con \"/WEB-INF/tags\"
+jsp.error.unterminated.user.tag=Tag definido por usuario no terminado: tag final {0} no hallado o anidado incorrectamente
+#jspx.error.templateDataNotInJspCdata=Validation Error: Element &lt;{0}&gt; cannot have template data. Template data must be encapsulated within a &lt;jsp:cdata&gt; element. [JSP1.2 PFD section 5.1.9]\nTemplate data in error: {1}
+jspx.error.templateDataNotInJspCdata=Error de Validación: El Elemento &lt;{0}&gt; no puede tener datos plantilla. Los datos plantilla deben de estar encapsulados dentro de un elemento &lt;jsp:text&gt;. [JSP1.2 PFD sección 5.1.9]\nDatos de Plantilla en error: {1}
+#Error while processing taglib jar file {0}: {1}
+jsp.error.taglib.reserved.prefix=El prefijo taglib {0} está reservado
+jsp.error.invalid.javaEncoding=Codificaciones java incorrectas. Intenté {0} y luego {1}. Ambas fallaron.
+jsp.error.needAlternateJavaEncoding=La codificación java por defecto {0} es incorrecta en tu plataforma java. Se puede especificar una alternativa vía parámetro 'javaEncoding' de JspServlet.
+#Error when compiling, used for jsp line number error messages
+jsp.error.single.line.number=Ha tenido lugar un error en la línea: {0} en el archivo jsp: {1}
+jsp.error.multiple.line.number=\n\nHa tenido lugar un error entre las líneas: {0} y {1} en el archivo jsp: {2}\n\n
+jsp.error.corresponding.servlet=Error de servlet generado:\n
+jsp.error.empty.body.not.allowed=Cuerpo vacío no permitido para {0}
+jsp.error.jspbody.required=Se debe de usar jsp:body para especificar cuerpo tag para {0} si se usa jsp:attribute.
+jsp.error.jspbody.emptybody.only=El tag {0} sólo puede tener jsp:attribute en su cuerpo.
+jsp.error.no.scriptlets=Los elementos de Scripting (&lt;%!, &lt;jsp:declaration, &lt;%=, &lt;jsp:expression, &lt;%, &lt;jsp:scriptlet ) no están permitidos aquí.
+jsp.error.internal.unexpected_node_type=Error Interno: Encontrado tipo de nodo inesperado
+jsp.error.tld.fn.invalid.signature=Sintáxis incorrecta para firma de función en TLD. Biblioteca de Tag: {0}, Función: {1}
+jsp.error.tld.fn.duplicate.name=Nombre duplicado de función {0} en biblioteca de tag {1}
+jsp.error.tld.fn.invalid.signature.commaexpected=Sintáxis incorrecta para firma de función en TLD. Se esperaba Coma ','. Biblioteca de Tag: {0}, Función: {1}.
+jsp.error.tld.fn.invalid.signature.parenexpected=Sintáxis incorrecta para firma de función en TLD. Se esperaba Paréntesis '('. Biblioteca de Tag: {0}, Función: {1}.
+jsp.error.tld.mandatory.element.missing=Falta o está vacío elemento TLD obligatorio: {0}
+jsp.error.dynamic.attributes.not.implemented=El tag {0} declara que acepta atributos dinámicos pero no implementa la interfaz requerida
+jsp.error.nomatching.fragment=No puedo hallar una directiva de atributo (con name={0} y fragment=true) antes de la directiva de fragment.
+jsp.error.attribute.noequal=se esperaba símbolo igual
+jsp.error.attribute.noquote=se esperaba símbolo comillas
+jsp.error.attribute.unterminated=el atributo para {0} no está terminado correctamente
+jsp.error.missing.tagInfo=El objeto TagInfo para {0} falta del TLD
+jsp.error.fragmentwithtype=No puede especificar ambos atributos 'fragment' y 'type'. Si está presente 'fragment', 'type' se pone como 'javax.servlet.jsp.tagext.JspFragment'
+jsp.error.fragmentwithrtexprvalue=No puede especificar ambos atributos 'fragment' y 'rtexprvalue'.  Si está presente 'fragment', 'rtexprvalue' se pone como 'true'
+jsp.error.fragmentWithDeclareOrScope=Ambos atributos 'fragment' y 'declare' o 'scope' se han especificado en la directiva variable
+jsp.error.var_and_varReader=Sólo se puede especificar uno de \'var\' o \'varReader\'
+jsp.error.missing_var_or_varReader=Falta atributo \'var\' o \'varReader\'
+jsp.warning.bad.urlpattern.propertygroup=Valor malo {0} en el subelemento url-pattern en web.xml
+jsp.error.unknown_attribute_type=Tipo de atributo desconocido ({1}) para atributo {0}.
+jsp.error.jspelement.missing.name=Falta atributo obligatorio XML-style \'name\'
+jsp.error.xmlns.redefinition.notimplemented=Error interno: Intento de redefinir xmlns:{0}. La redefinición de espacios de nombre no está implementada.
+jsp.error.could.not.add.taglibraries=No pude añadir una o más bibliotecas.
+jsp.error.duplicate.name.jspattribute=El atributo {0} especificado en la acción standard o custom también aparece como el valor del atributo name en jsp:attribute
+jsp.error.not.in.template={0} no permitido en una plantilla cuerpo de texto.
+jsp.error.badStandardAction=Acción estándar incorrecta
+jsp.error.xml.badStandardAction=Acción estándar incorrecta: {0}
+jsp.error.tagdirective.badbodycontent=body-content incorrecto ({0}) en directiva tag
+jsp.error.simpletag.badbodycontent=El TLD para la clase {0} especifica un body-content es incorrecto (JSP) para un SimpleTag.
+jsp.error.config_pagedir_encoding_mismatch=El Page-encoding especificado en jsp-property-group ({0}) es diferente del especificado en la diectiva page ({1})
+jsp.error.prolog_pagedir_encoding_mismatch=El Page-encoding especificado en XML prolog ({0}) difiere del especificado en la directiva page ({1})
+jsp.error.prolog_config_encoding_mismatch=El Page-encoding especificado en XML prolog ({0}) difiere del especificado en jsp-property-group ({1})
+jsp.error.attribute.custom.non_rt_with_expr=Según el TLD o la directiva attribute del archivo tag, el atributo {0} no acepta expresiones
+jsp.error.attribute.standard.non_rt_with_expr=El atributo {0} de la acción estándar {1} no acepta expresiones
+jsp.error.scripting.variable.missing_name=Imposible determinar nombre de variable de scripting desde atributo {0}
+jasper.error.emptybodycontent.nonempty=Según el TLD, el tag {0} debe de estar vacío, pero no lo está
+jsp.error.tagfile.nameNotUnique=El valor de {0} y el valor de {1} en la línea {2} son el mismo.
+jsp.error.tagfile.nameFrom.noAttribute=No puedo hallar una directiva attribute con un atributo name con un valor \"{0}\", el valor de este atributo name-from-attribute.
+jsp.error.tagfile.nameFrom.badAttribute=La directiva attribute (declarada en la línea {1} y cuyo atributo name es \"{0}\", el valor de este atributo name-from-attribute attribute) debe de ser del tipo java.lang.String, es \"required\" y no un \"rtexprvalue\".
+jsp.error.page.noSession=No puedo acceder al ámbito de sesión en una página que no participa en una sesión
+jsp.error.useBean.noSession=Es ilegal para useBean usar un ámbito de sesión cuando la página JSP declara (vía directiva page) que no participa en sesiones
+jsp.error.xml.encodingByteOrderUnsupported=El orden de byte dado para encoding \"{0}\" no está soportado
+jsp.error.xml.encodingDeclInvalid=Nombre de codificación \"{0}\" incorrecto.
+jsp.error.xml.encodingDeclRequired=Se necesita la declaración encoding en la declaración de texto
+jsp.error.xml.morePseudoAttributes=se esperan más pseudo-atributos
+jsp.error.xml.noMorePseudoAttributes=no se permiten más pseudo-atributos.
+jsp.error.xml.versionInfoRequired=Se requiere la versión en la declaración XML.
+jsp.error.xml.xmlDeclUnterminated=La declaración XML debe de terminar con \"?>\".
+jsp.error.xml.reservedPITarget=La instrucción de procesamiento que coincide con \"[xX][mM][lL]\" no está permitida.
+jsp.error.xml.spaceRequiredInPI=Se necesita un espacio en blanco entre la instrucción de procesamiento y los datos.
+jsp.error.xml.invalidCharInContent=Un carácter XML incorrecto (Unicode: 0x{0}) se halló en el contenido del elemento del documento.
+jsp.error.xml.spaceRequiredBeforeStandalone=Se necesita un espacio en blanco antes del pseudo-atributo encoding en la declaración XML.
+jsp.error.xml.sdDeclInvalid=El valor de declaración de documento standalone debe de ser \"yes\" o \"no\", no \"{0}\".
+jsp.error.xml.invalidCharInPI=Se halló un carácter XML incorrecto (Unicode: 0x{0}) en la instrucción de procesamiento
+jsp.error.xml.versionNotSupported=No se soporta la versión XML \"{0}\", sólo se soporta XML 1.0
+jsp.error.xml.pseudoAttrNameExpected=se esperaba un pseudo-atributo name.
+jsp.error.xml.expectedByte=Se esperaba byte {0} de {1}-byte de secuencia UTF-8.
+jsp.error.xml.invalidByte=Incorrecto byte {0} de {1}-byte de secuencia UTF-8.
+jsp.error.xml.operationNotSupported=La operación \"{0}\" no está soportada por lector {1}.
+jsp.error.xml.invalidHighSurrogate=Surrogación Alta de bits en secuencia UTF-8 no debe de exceder 0x10, pero se halló 0x{0}.
+jsp.error.xml.invalidASCII=El Byte \"{0}\" no es ASCII de 7-bit.
+jsp.error.xml.spaceRequiredBeforeEncodingInXMLDecl=Se necesita espacio en blanco antes del pseudo-atributo encoding en la declaración XML.
+jsp.error.xml.spaceRequiredBeforeEncodingInTextDecl=Se necesita espacio en blanco antes del pseudo-atributo encoding en la declaración text.
+jsp.error.xml.spaceRequiredBeforeVersionInTextDecl=Se necesita espacio en blanco antes del pseudo-atributo version en la declaración text.
+jsp.error.xml.spaceRequiredBeforeVersionInXMLDecl=Se necesita espacio en blanco antes del pseudo-atributo version en la declaración XML.
+jsp.error.xml.eqRequiredInXMLDecl=El carácter '' = '' debe de serguir a \"{0}\" en la declaración XML.
+jsp.error.xml.eqRequiredInTextDecl=El carácter '' = '' debe de serguir a \"{0}\" en la declaración text.
+jsp.error.xml.quoteRequiredInTextDecl=El valor que sigue a \"{0}\" en la declaración text debe de ser una cadena entre comillas.
+jsp.error.xml.quoteRequiredInXMLDecl=El valor que sigue a \"{0}\" en la declaración XML debe de ser un cadena entre comillas.
+jsp.error.xml.invalidCharInTextDecl=Un carácter XML incorrecto (Unicode: 0x{0}) se halló en la declaración text
+jsp.error.xml.invalidCharInXMLDecl=Un carácter XML incorrecto (Unicode: 0x{0}) se halló en la declaración XML
+jsp.error.xml.closeQuoteMissingInTextDecl=Faltan las comillas de cierre en el valor que sigue a \"{0}\" en la declaración text.
+jsp.error.xml.closeQuoteMissingInXMLDecl=Faltan las comillas de cierre en el valor que sigue a  \"{0}\" en la declaración XML.
+jsp.error.multiple.jsp=No puedo tener múltiples especificaciones de
+jsp.error.jspoutput.conflict=&lt;jsp:output&gt;: ilegal tener ocurrencias múltiples de \"{0}\" con diferentes valores (viejo: {1}, nuevo: {2})
+jsp.error.jspoutput.doctypenamesystem=&lt;jsp:output&gt;: atributos 'doctype-root-element' y 'doctype-system' deben de aparecer juntos
+jsp.error.jspoutput.doctypepulicsystem=&lt;jsp:output&gt;: atributo 'doctype-system' debe de aparecer si aparece atributo 'doctype-public'
+jsp.error.jspoutput.nonemptybody=&lt;jsp:output&gt; no debe de tener un cuerpo
+jsp.error.jspoutput.invalidUse=&lt;jsp:output&gt; no se debe de usar en sintáxis estándar
+jsp.error.attributes.not.allowed = {0} no debe de tener atributos
+jsp.error.tagfile.badSuffix=Falta sufijo \".tag\" en trayectoria de archivo de tag {0}
+jsp.error.tagfile.illegalPath=Trayectoria de archivo de tag: {0}, debe de comenzar con \"/WEB-INF/tags\" o \"/META-INF/tags\"
+jsp.error.plugin.wrongRootElement=El nombre del elemento raíz en {0} difiere de {1}
+jsp.error.attribute.invalidPrefix=El prefijo de atributo {0} no se correponde con ninguna biblioteca importada
+jsp.error.nested.jspattribute=Una acción estándar jsp:attribute no puede estar anidada dentro de otra acción estándar jsp:attribute
+jsp.error.nested.jspbody=Una acción estándar jsp:body no puede estar anidada dentro de otra acción estándar jsp:body o jsp:attribute
+jsp.error.variable.either.name=O el atributo name-given o name-from-attribute deben de ser especificados en una directiva variable
+jsp.error.variable.both.name=No se puede especificar ambos atributos name-given o name-from-attribute en una directiva variable
+jsp.error.variable.alias=Ambos atributos o ninguno de name-from-attribute y alias pueden ser especificados en una directiva variable
+jsp.error.attribute.null_name=Nombre de atributo nulo
+jsp.error.jsptext.badcontent=\'&lt;\', cuando aparece en el cuerpo de &lt;jsp:text&gt;, debe de estar encapsulado dentro de un CDATA
+jsp.error.jsproot.version.invalid=Número incorrecto de versión: \"{0}\", debe de ser \"1.2\" o \"2.0\"
+jsp.error.noFunctionPrefix=La función {0} debe de usarse con un prefijo cuando no se especifica un espacio de nombres por defecto
+jsp.error.noFunction=La función {0} no puede ser localizada mediante el prefijo especificado
+jsp.error.noFunctionMethod=El método \"{0}\" para la función \"{1}\" no se pudo hallar en la clase \"{2}\"
+jsp.error.function.classnotfound=La clase {0} especificada en el TLD para la función {1} no se puede hallar: {2}
+jsp.error.signature.classnotfound=La clase {0} especificada en la firma del método en el TLD para la función {1} no se puede hallar. {2}
+jsp.error.text.has_subelement=&lt;jsp:text&gt; no debe de tener subelementos
+jsp.error.data.file.read=Error leyendo archivo \"{0}\"
+jsp.error.prefix.refined=Intento de redefinir el prefijo {0} por {1}, cuando ya estaba definido como {2} en el ámbito en curso.
+jsp.error.nested_jsproot=&lt;jsp:root&gt; anidado
+jsp.error.unbalanced.endtag=El tgag final \"&lt;/{0}\" está desequilibrado
+jsp.error.invalid.bean=
diff --git a/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings_fr.properties b/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings_fr.properties
new file mode 100644
index 0000000..f6607d5
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings_fr.properties
@@ -0,0 +1,305 @@
+# $Id$
+#
+# Default localized string information
+# Localized this the Default Locale as is fr_FR
+
+jsp.error.bad.servlet.engine=Version de moteur de servlet incorrecte!
+jsp.error.no.scratch.dir=Le moteur de JSP engine n''est pas configuré avec un répertoire de travail.\
+\n Merci d''ajouter \"jsp.initparams=scratchdir=<dir-name>\" \
+\n dans le fichier "servlets.properties" de ce contexte.
+jsp.error.bad.scratch.dir=Le paramêtre "scratchDir" que vous avez spécifié: {0} est inutilisable.
+jsp.message.scratch.dir.is=Le répertoire de travail (scratch dir) pour le moteur de JSP est: {0}
+jsp.message.parent_class_loader_is=Le chargeur de classe parent (class loader) est: {0}
+jsp.message.dont.modify.servlets=IMPORTANT: Ne pas modifier les servlets générées
+jsp.error.not.impl.comments=Erreur interne: Commentaires non implémentés
+jsp.error.not.impl.directives=Erreur interne: Directives non implémentées
+jsp.error.not.impl.declarations=Erreur interne: Declarations non implémentées
+jsp.error.not.impl.expressions=Erreur interne: Expressions non implémentées
+jsp.error.not.impl.scriptlets=Erreur interne: Scriptlets non implémentés
+jsp.error.not.impl.usebean=Erreur interne: useBean non implémenté
+jsp.error.not.impl.getp=Erreur interne: getProperty non implémenté
+jsp.error.not.impl.setp=Erreur interne: setProperty non implémenté
+jsp.error.not.impl.plugin=Erreur interne: plugin non implémenté
+jsp.error.not.impl.forward=Erreur interne: forward non implémenté
+jsp.error.not.impl.include=Erreur interne: include non implémenté
+jsp.error.unavailable=La JSP a été marquée comme non disponible
+jsp.error.usebean.missing.attribute=useBean: l''identificateur d''attribut (id attribute) est manquant ou mal orthographié
+jsp.error.usebean.missing.type=useBean ({0}): La classe ou le type d''attribut doit être\
+spécifié: 
+jsp.error.usebean.duplicate=useBean: Nom de bean dupliqué: {0}
+jsp.error.usebean.prohibited.as.session=Impossible d''utiliser comme bean de session {0} car c''est interdit\
+par la directive jsp définie précédemment: 
+jsp.error.usebean.not.both=useBean: Impossible de spécifier à la fois la classe et l''attribut beanName: 
+jsp.error.usebean.bad.type.cast=useBean ({0}): Le type ({1}) n''est pas assignable depuis la classe ({2}) 
+jsp.error.classname=Impossible de déterminer le nom de classe d''après le fichier .class
+jsp.warning.bad.type=Attention: mauvais type dans le fichier .class
+jsp.error.data.file.write=Erreur lors de l''écriture du fichier de données
+#Directive de Page: valeur incorrecte pour pageEncoding
+jsp.error.page.invalid.contenttype=Directive de Page: valeur incorrecte pour contentType
+jsp.error.page.invalid.session=Directive de Page: valeur incorrecte pour session
+jsp.error.page.invalid.buffer=Directive de Page: valeur incorrecte pour "buffer"
+jsp.error.page.invalid.autoflush=Directive de Page: valeur incorrecte pour autoFlush
+jsp.error.page.invalid.isthreadsafe=Directive de Page: valeur incorrecte pour isThreadSafe
+jsp.error.page.invalid.info=Directive de Page: valeur incorrecte pour info
+jsp.error.page.invalid.iserrorpage=Directive de Page: valeur incorrecte pour isErrorPage
+jsp.error.page.defafteruse.language=Directive de Page: on ne peut définir language après un scriptlet 
+jsp.error.page.nomapping.language=Directive de Page: Pas de correspondance pour language: 
+jsp.error.page.bad_b_and_a_combo=Directive de Page: combinaison illégale de buffer=\"none\" && autoFlush=\"false\"
+jsp.error.not.impl.taglib=Internal error: Tag extensions non implémentés
+jsp.error.include.missing.file=l''argument fichier (file) pour l''inclusion (include) est absent
+jsp.error.include.bad.file=Mauvais argument fichier (file) pour l''inclusion (include)
+jsp.error.include.exception=Impossible d''inclure (include) {0}
+jsp.error.stream.closed=Flux fermé
+jsp.error.invalid.forward=Tag forward incorrect
+jsp.error.unknownException=Erreur non traitée! Vous devriez penser à utiliser une page d''erreur \
+pour rapporter ce type d''erreur plus élégamment
+jsp.error.invalid.directive=Directive incorrecte
+jsp.error.directive.istagfile=La directive {0} ne peut être utilisée dans un fichier tag
+jsp.error.directive.isnottagfile=La directive {0} ne peut être utilisée que dans un fichier tag
+jsp.error.tagfile.tld.name=L''attribut \"name\" de la directive tag contient la valeur {0} alors que le tag \"name\" de l''élément \"tag-file\" dans le TLD est {1}
+jsp.error.action.istagfile=L''action {0} ne peut être utilisée dans un fichier tag
+jsp.error.action.isnottagfile=L''action {0} ne peut être utilisée que dans un fichier tag
+jsp.error.unterminated=Tag {0} non terminé
+jsp.error.usebean.notinsamefile=le tag useBean doit commencé et finir dans le même fichier physique
+jsp.error.loadclass.taghandler=Impossible de charger la classe {0}
+jsp.error.unable.compile=Impossible de compiler la classe pour la JSP
+jsp.error.unable.load=Impossible de charger la classe pour la JSP
+jsp.error.unable.rename=Impossible de renommer le fichier classe de {0} vers {1}
+jsp.error.mandatory.attribute={0}: L''attribut obligatoire {1} est manquant
+jsp.engine.info=Moteur Jasper JSP 2.0
+jsp.error.invalid.expression="{0}" contient d''incorrecte(s) expression(s): {1}
+jsp.error.invalid.attribute={0}: Attribut incorrect: {1}
+jsp.error.usebean.class.notfound=Classe: {0} non trouvée
+jsp.error.file.cannot.read=Impossible de lire le fichier: {0}
+jsp.error.file.already.registered=Inclusion récursive du fichier {0}
+jsp.error.file.not.registered=Le fichier {0} n''apparaît pas dans l''inclusion (include)
+jsp.error.quotes.unterminated=guillemets non terminés
+jsp.error.attr.quoted=La valeur de l''attribute doit être entre guillemets 
+jsp.error.attr.novalue=L''attribute {0} n''a pas de valeur
+jsp.error.tag.attr.unterminated=Liste de tag d''attribut non terminée
+jsp.error.param.noname=Pas de nom dans le tag PARAM
+jsp.error.param.novalue=Pas de valeur dans le tag PARAM
+jsp.error.beans.nullbean=Tentative d''opération bean sur un objet nul.
+jsp.error.beans.nobeaninfo=Pas d''information bean (BeanInfo) pour le bean de type ''{0}'' n''a pu être trouvée, la classe n''existe probablement pas.
+jsp.error.beans.introspection=Une exception s''est produite lors de l''introspection de la méthode read de la propriété ''{0}'' dans le bean de type ''{1}'':\n{2}
+jsp.error.beans.nomethod=Impossible de trouver une méthode pour lire la propriété ''{0}'' dans le bean de type ''{1}''
+jsp.error.beans.nomethod.setproperty=Impossible de trouver une méthode pour mettre à jour la propriété ''{0}'' de type ''{1}''dans le bean de type ''{2}''
+jsp.error.beans.noproperty==Impossible de trouver de l''information sur la propriété ''{0}'' dans le bean de type ''{1}''
+jsp.error.beans.setproperty.noindexset=Impossible de renseigner la propriété indéxée
+jsp.error.include.tag=Tag jsp:include incorrect
+jsp.error.include.noflush=jsp:include doit avoir \"flush=true\"
+jsp.error.include.badflush=jsp:include page=\"...\" flush=\"true\" est la seule combinaison valide dans JSP 1.0
+jsp.error.attempt_to_clear_flushed_buffer=Erreur: Tentative d''effacement d''un tampon qui a déjà été vidangé (flush)
+jsp.error.overflow=Erreur: Dépassement de capacité du tampon JSP
+jsp.error.paramexpected=Le tag \"param\" est attendu avec les attributs \"name\" et \"value\" après le tag \"params\".
+jsp.error.closeindividualparam=Le tag param doit être fermé avec \"/>\"
+jsp.error.closeparams=Le tag param tag doit être fermé avec  /params
+jsp.error.plugin.notype=type non déclaré dans jsp:plugin
+jsp.error.plugin.nocode=code non déclaré dans jsp:plugin
+jsp.error.ise_on_clear=Il est interdit d''utiliser clear() quand la taille de tampon== 0
+jsp.error.setproperty.beanNotFound=setProperty: le Bean {0} est introuvable
+jsp.error.getproperty.beanNotFound=getProperty: le Bean {0} est introuvable
+jsp.error.setproperty.ClassNotFound=setProperty: la Classe {0} est introuvable
+jsp.error.setproperty.invalidSyntax=setProperty: On ne peut avoir de valeur non nulle quand property=*
+jsp.error.setproperty.beanInfoNotFound=setproperty: beanInfo pour le bean {0} est introuvable
+jsp.error.setproperty.paramOrValue=setProperty: param ou value doit être présent
+jsp.error.setproperty.arrayVal=setProperty: on ne peut renseigner les array property {0} au travers d''une valeur chaîne constante (string constant value)
+jsp.warning.keepgen=Attention: Valeur incorrecte pour le initParam keepgenerated. Utilisation de la valeur par défaut \"false\"
+jsp.warning.enablePooling=Attention: Valeur incorrecte pour le initParam enablePooling. Utilisation de la valeur par défaut \"true\"
+jsp.warning.mappedFile=Attention: Valeur incorrecte pour le initParam mappedFile. Utilisation de la valeur par défaut \"false\"
+jsp.warning.sendErrToClient=Attention: Valeur incorrecte pour le  initParam sendErrToClient. Utilisation de la valeur par défaut \"false\"
+jsp.warning.classDebugInfo=Attention: Valeur incorrecte pour le initParam classdebuginfo. Utilisation de la valeur par défaut \"false\"
+jsp.warning.checkInterval=Attention: Valeur incorrecte pour le initParam checkInterval. Utilisation de la valeur par défaut \"300\" secondes
+jsp.warning.development=Attention: Valeur incorrecte pour le initParam development. Utilisation de la valeur par défaut \"true\"
+jsp.warning.reloading=Attention: Valeur incorrecte pour le initParam reloading. Utilisation de la valeur par défaut \"true\"
+jsp.warning.reloading=
+jsp.error.badtaglib=Impossible d''ouvrir le taglibrary {0} : {1}
+jsp.error.badGetReader=Impossible de créer un lecteur (reader) quand le flux n''utilse pas des tampons (not buffered)
+jsp.warning.unknown.element.in.TLD=Attention: Elément inconnu {0} dans le TLD
+jsp.warning.unknown.element.in.tag=Attention: Elément inconnu {0} dans le tag
+jsp.warning.unknown.element.in.tagfile=Attention: El?ment inconnu {0} dans le tag-file
+jsp.warning.unknown.element.in.attribute=Attention: Elément inconnu {0} dans l''attribute
+jsp.error.more.than.one.taglib=plus d''un taglib dans le TLD: {0}
+jsp.error.teiclass.instantiation=Impossible de charger ou d''instancier la classe TagExtraInfo: {0}
+jsp.error.non_null_tei_and_var_subelems=Le tag {0} possède une ou plusieurs variables subelements et une classe TagExtraInfo qui retourne une ou plusieurs VariableInfo
+jsp.error.parse.error.in.TLD=Erreur d''évaluation (parse) dans le descripteur de librairie de tag (TLD): {0}
+jsp.error.unable.to.open.TLD=Impossible d''ouvrir le descripteur de librairie de tag (TLD): {0}
+jsp.buffer.size.zero=Taille du tampon <= 0
+jsp.error.file.not.found=Le fichier \"{0}\" n''a pas été trouvé
+jsp.message.copyinguri=Copie de {0} dans {1}
+jsp.message.htmlcomment=\nEffacement des commentaires: \t{0}
+jsp.message.handling_directive=\nDirective de gestion (handling): {0}\t{1}
+jsp.message.handling_plugin=\nPlugin: {0}
+jsp.message.package_name_is=Le nom de package est: {0}
+jsp.message.class_name_is=Le nom de classe est: {0}
+jsp.message.java_file_name_is=Le nom de fichier Java est: {0}
+jsp.message.class_file_name_is=Le nom de fichier Class est: {0}
+jsp.message.accepted=Accepté {0} à {1}
+jsp.message.adding_jar=Ajout du jar {0} à mon classpath
+jsp.message.compiling_with=Compilation avec: {0}
+jsp.message.template_text=texte template
+jsp.error.missing_attribute=D''après le TLD l''attribut {0} est obligatoire pour le tag {1}
+jsp.error.bad_attribute=L''attribut {0} est incorrect pour le tag {1} d''après la TLD indiquée
+jsp.error.webxml_not_found=Impossible de localiser le fichier web.xml
+jsp.cmd_line.usage=Usage: jsptoservlet [-dd <path/to/outputDirectory>] [-keepgenerated] \
+<.jsp files>
+jsp.message.cp_is=Le Classpath {0} est: {1}
+jsp.error.unable.to_load_taghandler_class=Impossible de charger la classe gestionnaire de tag {0} car {1}
+jsp.error.unable.to_find_method=Impossible de trouver une méthode de mise à jour pour l''attribut: {0}
+jsp.error.unable.to_convert_string=Impossible de convertir une chaîne vers {0} pour l''attribut {1}
+jsp.error.unable.to_introspect=Impossible d''introspecter la classe gestionnaire de tag : {0} car {1}
+jsp.error.bad_tag=Aucun tag {0} dans la librairie de tag importée avec le préfixe {1}
+jsp.error.bad_string_Character=Impossible d''extraire un caractère depuis un tableau vide
+jsp.error.bad_string_char=Impossible d''extraire un caractère depuis un tableau vide
+jsp.warning.compiler.class.cantcreate=Impossible de créer une instance de classe plugin pour le compilateur indiqué {0} due to {1}. Utilisation par défaut du Compilateur Java Sun.
+jsp.warning.compiler.class.notfound=La classe plugin de compilateur {0} est introuvable. Utilisation par défaut du Compilateur Java Sun.
+jsp.warning.compiler.path.notfound=le chemin de compilateur indiqué {0} est introuvable. Utilisation par défaut du chemin système (system PATH).
+jsp.error.jspc.uriroot_not_dir=L''option -uriroot doit indiqué un répertoire déjà existant
+jspc.implicit.uriRoot=uriRoot réglé implicitement à "{0}"
+jspc.usage=Usage: jspc <options> [--] <fichiers jsp>\n\
+où les fichiers jsp sont n''importe quel nombre de:\n\
+\    <file>         Un fichier à évaluer (parser) comme page jsp\n\
+\    -webapp <dir>  Un répertoire contenant une application web, toutes les pages jsp\n\
+\                   seront récursivement évaluées\n\
+où les options comprennet:\n\
+\    -q          Mode silencieux (identique à -v0)\n\
+\    -v[#]       Mode bavard (Le nombre optionnel indique le niveau, 2 par défaut)\n\
+\    -d <dir>    Dossier de sortie\n\
+\    -dd <dir>   Dossier de sortie literal.  (Les dossiers de paquets ne seront pas construits)\n\
+\    -l          Sortie du nom la page JSP en cas d''échec\n\
+\    -s          Sortie du nom la page JSP en cas de succès\n\
+\    -p <name>   Nom du paquet cible\n\
+\    -c <name>   Nom d'un nom de classe cible\n\
+\                (s''applique seulement à la première page JSP)\n\
+\    -mapped     Génère des appels à write() séparés pour chaque ligne HTML dans la JSP\n\
+\    -die[#]     Génère un code d''erreur de retour (#) en cas d''erreurs fatales.\n\
+\                Si le nombre est absent ou non numérique, le défaut est 1.\n\
+\    -uribase <dir>  Le répertoire uri de compilations relatif\n\
+\                    (Par défaut "/")\n\
+\    -uriroot <dir>  The répertoire racine contre lequel les fichiers seront résolus\n\
+\                    , (Par défaut le répertoire depuis lequel jspc est appelé)\n\
+\    -webinc <file>  Création d''association partielle de servlet pour l''option -webapp.\n\
+\    -webxml <file>  Création d''un fichier web.xml complet pour l''option -webapp.\n\
+\    -ieplugin <clsid>  Le classid du Plugin Java Plugin pour Internet Explorer\n\
+\    -sax2 <driverclassname>  Le nom de classe du Driver SAX 2.0 à utiliser\n\
+\    -trimSpaces        Trim spaces in template text between actions, directives\n\
+\    -javaEncoding <enc> Set the encoding charset for Java classes (default UTF-8)\n\
+\    -source <version>   Set the -source argument to the compiler (default 1.4)\n\
+\    -target <version>   Set the -target argument to the compiler (default 1.4)\n\
+
+jspc.webxml.header=<?xml version="1.0" encoding="ISO-8859-1"?>\n\
+\n\
+<!DOCTYPE web-app\n\
+\    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"\n\
+\    "http://java.sun.com/dtd/web-app_2_3.dtd">\n\
+<!--\n\
+Créer automatiquement par le JspC Apache Jakarta Tomcat.\n\
+-->\n\
+<web-app>\n\
+\n
+jspc.webxml.footer=\n\
+</web-app>\n\
+\n
+jspc.webinc.header=\n\
+<!--\n\
+Créer automatiquement par le JspC Apache Jakarta Tomcat.\n\
+Placez ce fragment dans le fichier web.xml avant all icon, display-name,\n\
+description, distributable, and context-param elements.\n\
+-->\n
+jspc.webinc.footer=\n\
+<!--\n\
+All session-config, mime-mapping, welcome-file-list, error-page, taglib,\n\
+resource-ref, security-constraint, login-config, security-role,\n\
+env-entry, and ejb-ref elements should follow this fragment.\n\
+-->\n
+jspc.error.jasperException=erreur-le fichier ''{0}'' a généré l''exception d''évaluation suivante: {1}
+jspc.error.generalException=ERREUR-le fichier ''{0}'' a généré l''exception générale suivante:
+jspc.error.fileDoesNotExist=L''argument fichier ''{0}'' n''existe pas
+jspc.error.emptyWebApp=-webapp nécessite à sa suite un argument fichier
+jsp.error.library.invalid=La page JSP page est incorrecte d''après la librairie {0}: {1}
+jsp.error.tlvclass.instantiation=Impossible de charger ou d''instancier la classe TagLibraryValidator: {0}
+jsp.error.tlv.invalid.page=Message d''erreurs de validation provenant du TagLibraryValidator pour {0}
+jsp.error.tei.invalid.attributes=Message d''erreurs de validation provenant du TagExtraInfo pour {0}
+jsp.parser.sax.propertynotsupported=Propriété SAX non supportée: {0}
+jsp.parser.sax.propertynotrecognized=Propriété SAX non reconnue: {0}
+jsp.parser.sax.featurenotsupported=Fonctionnalité SAX non supportée: {0}
+jsp.parser.sax.featurenotrecognized=Fonctionnalité SAX non reconnue: {0}
+jsp.error.no.more.content=Fin de contenu alors que l''évalution n''était pas terminée: erreur de tags imbriqués?
+jsp.error.parse.xml=Erreur d''évaluation XML sur le fichier {0}
+jsp.error.parse.xml.line=Erreur d''évaluation XML sur le fichier  {0}: (ligne {1}, col {2})
+jsp.error.parse.xml.scripting.invalid.body=Le corps de l''élément {0} ne doit contenir aucun éléments XML
+jsp.error.internal.tldinit=Exception lors de l'initialisation de TldLocationsCache: {0}
+jsp.error.internal.filenotfound=Erreur interne: Fichier {0} introuvable
+jsp.error.internal.evaluator_not_found=Erreur interne: Impossible de charger l''évaluateur d''expression
+jsp.error.parse.xml.invalidPublicId=PUBLIC ID invalide: {0}
+jsp.error.include.flush.invalid.value=Valeur incorrecte pour l''attribut flush: {0}
+jsp.error.unsupported.encoding=Encodage non supporté: {0}
+jsp.warning.unknown.element.in.variable=Attention: Element inconnu {0} dans la variable
+tld.error.variableNotAllowed=Ceci est une erreur pour le tag qui possède une ou plusieurs variables subelements pour avoir une classe TagExtraInfo qui retourne un objet non-nul.
+jsp.error.tldInWebDotXmlNotFound=Ne peut trouver le TLD {1} pour l''URI {0} indiquée dans le fichier web.xml
+jsp.error.taglibDirective.absUriCannotBeResolved=L''uri absolue: {0} ne peut être résolu dans le fichier web.xml ou dans les fichiers jar déployés avec cette application
+jsp.error.taglibDirective.missing.location=Ni l''uri' ni l''attribut 'tagdir' n''ont été indiqués dans la directive taglib
+jsp.error.invalid.tagdir=Le répertoire du fichier Tag {0} ne commence pas par \"/WEB-INF/tags\"
+jsp.error.unterminated.user.tag=Tag user-defined non terminé: Le tag de fermeture {0} est introuvable found ou incorrectement imbriqué
+#jspx.error.templateDataNotInJspCdata=Erreur de validation: l''élément &lt;{0}&gt; ne peut avoir de données template. Les données Template doivent être encapsulées à l''intérieur d''un élément &lt;jsp:cdata&gt;. [JSP1.2 PFD section 5.1.9]\nDonnée Template en erreur: {1}
+jspx.error.templateDataNotInJspCdata=Erreur de validation: l''élément &lt;{0}&gt; ne peut avoir de données template. Les données Template doivent être encapsulées à l''intérieur d''un élément &lt;jsp:text&gt;. [JSP1.2 PFD section 5.1.9]\nDonnée Template en erreur: {1}
+#Erreur lors du traitement du fichier jar de la taglib {0}: {1}
+jsp.error.taglib.reserved.prefix=Le préfixe taglib {0} est réservé
+jsp.error.invalid.javaEncoding=Encodage java incorrect. Essai de {0} puis de {1}. Les deux ont échoué.
+jsp.error.needAlternateJavaEncoding=L''encodage java par défaut {0} est incorrect sur votre environnement java. Une alternative peut être indiquée via le paramêtre 'javaEncoding' de la JspServlet.
+#Erreur lors de la compilation, utilisé pour la ligne jsp des messages d''erreur
+jsp.error.single.line.number=Une erreur s''est produite à la ligne: {0} dans le fichier jsp: {1}
+jsp.error.multiple.line.number=\n\nUne erreur s''est produite entre les lignes: {0} et {1} dans le fichier jsp: {2}\n\n
+jsp.error.corresponding.servlet=Erreur de servlet générée:\n
+jsp.error.empty.body.not.allowed=Un corps vide n'est pas autorisé pour {0}
+jsp.error.jspbody.required=Doit utiliser jsp:body pour indiqué le corps de tag body de {0} si jsp:attribute est utilisé.
+jsp.error.jspbody.emptybody.only=Le tag {0} ne peut avoir que jsp:attribute dans son corps.
+jsp.error.no.scriptlets=Les éléments de Scripting ( <%!, <jsp:declaration, <%=, <jsp:expression, <%, <jsp:scriptlet ) ne sont pas autorisés ici.
+jsp.error.internal.unexpected_node_type=Erreur Interne: Type de node inattendu rencontré
+jsp.error.tld.fn.invalid.signature=Synthaxe invalide pour la signature de fonction dans la TLD.  Librairie de Tag : {0}, Fonction: {1}
+jsp.error.tld.fn.invalid.signature.classnotfound=Synthaxe invalide pour la signature de fonction dans la TLD.  Classe introuvable: ${0}.  Librairie de Tag: {1}, Fonction: {2}.
+jsp.error.tld.fn.invalid.signature.commaexpected=Synthaxe invalide pour la signature de fonction dans la TLD.  Virgule ',' attendue.  Librairie de Tag: {0}, Fonction: {1}.
+jsp.error.tld.fn.invalid.signature.parenexpected=Synthaxe invalide pour la signature de fonction dans la TLD.  Parenthèse '(' attendue.  Librairie de Tag: {0}, Fonction: {1}.
+jsp.error.dynamic.attributes.not.implemented=Le tag {0} indique qu''il accepte des attributs dynamics mais n''implémente pas l''interface requise
+jsp.error.nomatching.fragment=Ne peut trouver une directive attribut (avec pour nom={0} et fragment=true) avant la directive fragment.
+jsp.error.attribute.noequal=Symbole égal (equal) attendu
+jsp.error.attribute.noquote=Symbole guillemet (quote) attendu
+jsp.error.attribute.unterminated=L''attribut pour {0} n''est pas correctement terminé
+jsp.error.missing.tagInfo=L''objet TagInfo de {0} est absent de la TLD
+jsp.error.fragmentwithtype=On ne peut indiquer à la fois les attributs 'fragment' et 'type'.  Si 'fragment' est présent, 'type' est fixé comme 'javax.servlet.jsp.tagext.JspFragment'
+jsp.error.fragmentwithrtexprvalue=On ne peut indiquer à la fois les attributs 'fragment' et 'rtexprvalue'.  Si 'fragment' est présent, 'rtexprvalue' est fixé à 'true'
+jsp.error.fragmentWithDeclareOrScope=Les attributs 'fragment' et 'declare' ou 'scope' sont indiqués dans la directive variable
+jsp.error.var_and_varReader=A la fois 'var' et 'varReader' sont indiqués
+jsp.warning.bad.urlpattern.propertygroup=Mauvaise valeur {0} dans le sous-élément (subelement) url-pattern du fichier web.xml
+jsp.error.unknown_attribute_type=Type d''attribut inconnu ({1}) pour l''attribut {0}.
+jsp.error.jspelement.missing.name=L''attribut obligatoire 'name' est absent de jsp:element
+jsp.error.xmlns.redefinition.notimplemented=Erreur Interne: Tentative de redéfinition de xmlns:{0}.  La redéfinition des domaines de noms (namespaces) n''est pas implémentée.
+jsp.error.could.not.add.taglibraries=Impossible d''ajouter une ou plusieurs librairies de tag.
+jsp.error.duplicate.name.jspattribute=L''attribut {0} indiqué dans l''action standard ou spécifique (custom) apparait aussi comme valeur de l''attribut de nom dans le jsp:attribute inclus
+jsp.error.not.in.template={0} n''est pas autorisé dans le corps de texte de template.
+jsp.error.badStandardAction=L''action n''est pas reconnue comme une action standard.
+jsp.error.tagdirective.badbodycontent=Contenu de corps (body-content) ({0}) invalide dans la directive tag
+jsp.error.config_pagedir_encoding_mismatch=L''encode de page (Page-encoding) indiqué dans le jsp-property-group ({0}) est différent de celui indiqué dans la directive de page ({1})
+jsp.error.prolog_pagedir_encoding_mismatch=
+jsp.error.prolog_config_encoding_mismatch=
+jsp.error.attribute.custom.non_rt_with_expr=D''après la TLD, l''attribut {0} n''accepte aucune expression
+jsp.error.scripting.variable.missing_name=Incapable de déterminer le nom de variable scripting d''après l''attribut {0}
+jasper.error.emptybodycontent.nonempty=D''après la TLD, le tag {0} doit être vide, mais ne l''est pas
+jsp.error.tagfile.nameNotUnique=
+jsp.error.tagfile.nameFrom.noAttribute=
+jsp.error.tagfile.nameFrom.badAttribute=
+jsp.error.useBean.noSession=Il est illégal pour useBean d''utiliser une portée de session (session scope) quand la page JSP indique (via la directive de page) qu''elle ne participe pas aux sessions
+jsp.error.attributes.not.allowed = {0} ne doit avoir aucun attribut
+jsp.error.nested.jspattribute=
+jsp.error.nested.jspbody=
+jsp.error.variable.either.name=
+jsp.error.variable.both.name=
+jsp.error.variable.alias=
+jsp.error.jsptext.badcontent=
+jsp.error.prefix.refined=
+jsp.error.jspoutput.conflict=
+jsp.error.jspoutput.doctypenamesystem=
+jsp.error.jspoutput.doctypepulicsystem=
+jsp.error.jspoutput.nonemptybody=
+jsp.error.jspoutput.invalidUse=
+jsp.error.invalid.bean=
diff --git a/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings_ja.properties b/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings_ja.properties
new file mode 100644
index 0000000..5b92c42
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/resources/LocalStrings_ja.properties
@@ -0,0 +1,407 @@
+# $Id$
+#
+# Default localized string information
+# Localized this the Default Locale as is ja_JP
+
+jsp.error.bad.servlet.engine=\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30a8\u30f3\u30b8\u30f3\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093
+jsp.error.no.scratch.dir=JSP\u30a8\u30f3\u30b8\u30f3\u306b\u30c7\u30d5\u30a9\u30eb\u30c8\u306escratchDir\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\
+\n \u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306eservlets.properties\u30d5\u30a1\u30a4\u30eb\u306b\u3001\
+\n \"jsp.initparams=scratchdir=<dir-name>\" \u3092\u8ffd\u52a0\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+jsp.error.bad.scratch.dir=\u3042\u306a\u305f\u304c\u6307\u5b9a\u3057\u305fscratchDir\u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
+jsp.message.scratch.dir.is=JSP\u30a8\u30f3\u30b8\u30f3\u306eScratchdir: {0}
+jsp.message.parent_class_loader_is=\u89aa\u30af\u30e9\u30b9\u30ed\u30fc\u30c0: {0}
+jsp.message.dont.modify.servlets=\u91cd\u8981: \u751f\u6210\u3055\u308c\u305f\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u3092\u5909\u66f4\u3057\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+jsp.error.not.impl.comments=\u5185\u90e8\u30a8\u30e9\u30fc: Comments\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.directives=\u5185\u90e8\u30a8\u30e9\u30fc: Directives\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.declarations=\u5185\u90e8\u30a8\u30e9\u30fc: Declarations\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.expressions=\u5185\u90e8\u30a8\u30e9\u30fc: Expressions\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.scriptlets=\u5185\u90e8\u30a8\u30e9\u30fc: Scriptlets\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.usebean=\u5185\u90e8\u30a8\u30e9\u30fc: useBean\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.getp=\u5185\u90e8\u30a8\u30e9\u30fc: getProperty\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.setp=\u5185\u90e8\u30a8\u30e9\u30fc: setProperty\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.plugin=\u5185\u90e8\u30a8\u30e9\u30fc: plugin\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.forward=\u5185\u90e8\u30a8\u30e9\u30fc: forward\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.not.impl.include=\u5185\u90e8\u30a8\u30e9\u30fc: include\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.unavailable=JSP\u306f\u5229\u7528\u4e0d\u53ef\u3068\u30de\u30fc\u30af\u3055\u308c\u3066\u3044\u307e\u3059
+jsp.error.usebean.missing.attribute=useBean: id\u5c5e\u6027\u304c\u5b58\u5728\u3057\u306a\u3044\u304b\u3001\u30b9\u30da\u30eb\u30df\u30b9\u3067\u3059
+jsp.error.usebean.missing.type=useBean ({0}): class\u5c5e\u6027\u304btype\u5c5e\u6027\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044:
+jsp.error.usebean.duplicate=useBean: beanName\u5c5e\u6027\u304c\u91cd\u8907\u3057\u3066\u3044\u307e\u3059: {0}
+jsp.error.usebean.prohibited.as.session=\u4ee5\u524d\u306b\u5b9a\u7fa9\u3057\u305fJSP\u6307\u793a\u5b50\u306b\u3088\u3063\u3066\u7981\u6b62\u3055\u308c\u3066\u3044\u308b\u305f\u3081\u306b\u3001session bean {0} \u3068\u3057\u3066\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093:
+jsp.error.usebean.not.both=useBean: class\u5c5e\u6027\u3068beanName\u5c5e\u6027\u306e\u4e21\u65b9\u3092\u540c\u6642\u306b\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093:
+jsp.error.usebean.bad.type.cast=useBean ({0}): type ({1}) \u306fclass ({2}) \u304b\u3089\u5272\u308a\u5f53\u3066\u3089\u308c\u307e\u305b\u3093
+jsp.error.invalid.scope='scope'\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059: {0} (\"page\"\u3001\"request\"\u3001\"session\"\u53c8\u306f\"application\"\u306e\u3069\u308c\u304b\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093)
+jsp.error.classname=.class\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30af\u30e9\u30b9\u540d\u3092\u6c7a\u5b9a\u3067\u304d\u307e\u305b\u3093
+jsp.warning.bad.type=\u8b66\u544a: .class\u30d5\u30a1\u30a4\u30eb\u4e2d\u306e\u578b\u304c\u9055\u3044\u307e\u3059
+jsp.error.data.file.write=\u30c7\u30fc\u30bf\u30d5\u30a1\u30a4\u30eb\u3092\u66f8\u304d\u8fbc\u307f\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+jsp.error.page.invalid.buffer=page\u6307\u793a\u5b50: \u7121\u52b9\u306a\u30d0\u30c3\u30d5\u30a1\u30b5\u30a4\u30ba\u3067\u3059
+jsp.error.page.conflict.contenttype=page\u6307\u793a\u5b50: 'contentType'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.invalid.contenttype=page\u6307\u793a\u5b50: contentType\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
+jsp.error.page.conflict.session=page\u6307\u793a\u5b50: 'session'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.invalid.session=page\u6307\u793a\u5b50: session\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
+jsp.error.page.conflict.buffer=page\u6307\u793a\u5b50: 'buffer'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.invalid.buffer=page\u6307\u793a\u5b50: buffer\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
+jsp.error.page.conflict.autoflush=page\u6307\u793a\u5b50: 'autoFlush'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.invalid.autoflush=page\u6307\u793a\u5b50: autoFlush\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
+jsp.error.page.conflict.isthreadsafe=page\u6307\u793a\u5b50: 'isThreadSafe'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.invalid.isthreadsafe=page\u6307\u793a\u5b50: isThreadSafe\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
+jsp.error.page.conflict.info=page\u6307\u793a\u5b50: 'info'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.invalid.info=page\u6307\u793a\u5b50: info\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
+jsp.error.page.conflict.iserrorpage=page\u6307\u793a\u5b50: 'isErrorPage'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.invalid.iserrorpage=page\u6307\u793a\u5b50: isErrorPage\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
+jsp.error.page.conflict.errorpage=page\u6307\u793a\u5b50: 'errorPage'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.language=page\u6307\u793a\u5b50: 'language'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.tag.conflict.language=tag\u6307\u793a\u5b50: 'language'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.language.nonjava=page\u6307\u793a\u5b50: \u7121\u52b9\u306alanguage\u5c5e\u6027\u3067\u3059
+jsp.error.tag.language.nonjava=tag\u6307\u793a\u5b50: \u7121\u52b9\u306alanguage\u5c5e\u6027\u3067\u3059
+jsp.error.page.defafteruse.language=page\u6307\u793a\u5b50: scriptlet\u306e\u5f8c\u3067language\u5c5e\u6027\u3092\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093
+jsp.error.page.nomapping.language=page\u6307\u793a\u5b50 language\u5c5e\u6027\u306e\u30de\u30c3\u30d4\u30f3\u30b0\u304c\u5b58\u5728\u3057\u307e\u305b\u3093:
+jsp.error.page.conflict.extends=page\u6307\u793a\u5b50: 'extends'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.iselignored=page\u6307\u793a\u5b50: 'isELIgnored'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.tag.conflict.iselignored=tag\u6307\u793a\u5b50: 'isELIgnored'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.invalid.iselignored=page\u6307\u793a\u5b50: isELIgnored\u306b\u7121\u52b9\u306a\u5024\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059
+jsp.error.tag.invalid.iselignored=tag\u6307\u793a\u5b50: isELIgnored\u306b\u7121\u52b9\u306a\u5024\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059
+jsp.error.page.multi.pageencoding=page\u6307\u793a\u5b50\u306f\u8907\u6570\u306epageencoding\u3092\u6301\u3064\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+jsp.error.tag.conflict.attr=Tag\u6307\u793a\u5b50: \u5c5e\u6027\"{0}\"\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u4e0d\u6b63\u3067\u3059 (\u65e7: {1}, \u65b0: {2})
+jsp.error.tag.multi.pageencoding=tag\u6307\u793a\u5b50\u306f\u8907\u6570\u306epageencoding\u3092\u6301\u3064\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+jsp.error.page.bad_b_and_a_combo=page\u6307\u793a\u5b50: buffer=\"none\"\u3068autoFlush=\"false\"\u3092\u540c\u6642\u306b\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
+jsp.error.not.impl.taglib=\u5185\u90e8\u30a8\u30e9\u30fc: \u30bf\u30b0\u62e1\u5f35\u5b50\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.include.missing.file=\u53d6\u308a\u8fbc\u3080\u30d5\u30a1\u30a4\u30eb\u5f15\u6570\u304c\u3042\u308a\u307e\u305b\u3093
+jsp.error.include.bad.file=include\u5c5e\u6027\u306e\u30d5\u30a1\u30a4\u30eb\u5f15\u6570\u304c\u9593\u9055\u3063\u3066\u3044\u307e\u3059
+jsp.error.include.exception={0} \u3092\u53d6\u308a\u8fbc\u3081\u307e\u305b\u3093
+jsp.error.stream.closed=\u30b9\u30c8\u30ea\u30fc\u30e0\u304c\u30af\u30ed\u30fc\u30ba\u3055\u308c\u3066\u3044\u307e\u3059
+jsp.error.invalid.forward=\u7121\u52b9\u306aforward\u30bf\u30b0\u3067\u3059
+jsp.error.unknownException=\u51e6\u7406\u4e0d\u53ef\u80fd\u306a\u30a8\u30e9\u30fc\u3067\u3059! \u3053\u306e\u3088\u3046\u306a\u30a8\u30e9\u30fc\u3092\u3088\u308a\u8a73\u7d30\u306b\u5831\u544a\u3059\u308b\u30a8\u30e9\u30fc\u30da\u30fc\u30b8\u3092\u6301\u3063\u305f\u65b9\u304c\u3088\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093
+jsp.error.invalid.directive=\u7121\u52b9\u306a\u6307\u793a\u5b50\u3067\u3059
+jsp.error.directive.istagfile={0} \u6307\u793a\u5b50\u306f\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u4e2d\u3067\u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
+jsp.error.directive.isnottagfile={0} \u6307\u793a\u5b50\u306f\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u4e2d\u3067\u3057\u304b\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
+jsp.error.tagfile.tld.name=TLD\u4e2d\u306e\u30bf\u30b0\u6307\u793a\u5b50\u306e\"tag-file\"\u8981\u7d20\u306e\"name\"\u30bf\u30b0\u306f {1} \u3067\u3059\u304c\uff0c\"name\"\u5c5e\u6027\u306f\u5024 {0} \u3092\u6301\u3063\u3066\u3044\u307e\u3059
+jsp.error.action.istagfile={0} \u30a2\u30af\u30b7\u30e7\u30f3\u306f\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u4e2d\u3067\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
+jsp.error.action.isnottagfile={0} \u30a2\u30af\u30b7\u30e7\u30f3\u306f\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u4e2d\u3067\u306e\u307f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
+jsp.error.unterminated={0} \u30bf\u30b0\u304c\u7d42\u4e86\u3057\u3066\u3044\u307e\u305b\u3093
+jsp.error.usebean.notinsamefile=useBean\u30bf\u30b0\u306f\u3001\u540c\u4e00\u30d5\u30a1\u30a4\u30eb\u4e2d\u3067\u958b\u59cb\u3057\u3001\u7d42\u4e86\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.loadclass.taghandler=\u30bf\u30b0 \"{1}\" \u306b\u30bf\u30b0\u30cf\u30f3\u30c9\u30e9\u30af\u30e9\u30b9 \"{0}\" \u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093
+jsp.error.unable.compile=JSP\u306e\u30af\u30e9\u30b9\u3092\u30b3\u30f3\u30d1\u30a4\u30eb\u3067\u304d\u307e\u305b\u3093
+jsp.error.unable.load=JSP\u306e\u30af\u30e9\u30b9\u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093
+jsp.error.unable.rename=\u30af\u30e9\u30b9\u30d5\u30a1\u30a4\u30eb {0} \u3092 {1} \u306b\u30d5\u30a1\u30a4\u30eb\u540d\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093
+jsp.error.mandatory.attribute={0}: \u5fc5\u9808\u5c5e\u6027 {1} \u304c\u3042\u308a\u307e\u305b\u3093
+jsp.engine.info=Jasper JSP 2.0\u30a8\u30f3\u30b8\u30f3
+jsp.error.invalid.expression="{0}" \u306f\u7121\u52b9\u306a\u5f0f\u3092\u542b\u3093\u3067\u3044\u307e\u3059: {1}
+jsp.error.invalid.attribute={0}\u306f\u7121\u52b9\u306a\u5c5e\u6027\u3092\u6301\u3063\u3066\u3044\u307e\u3059: {1}
+jsp.error.usebean.class.notfound=\u30af\u30e9\u30b9: {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.error.file.cannot.read=\u30d5\u30a1\u30a4\u30eb\u304c\u8aad\u3081\u307e\u305b\u3093: {0}
+jsp.error.file.already.registered=\u30d5\u30a1\u30a4\u30eb {0} \u306e\u518d\u5e30\u7684\u306a\u53d6\u308a\u8fbc\u307f\u3067\u3059
+jsp.error.file.not.registered=include\u5c5e\u6027\u4e2d\u306e\u30d5\u30a1\u30a4\u30eb {0} \u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+jsp.error.quotes.unterminated=\u5f15\u7528\u7b26\u304c\u7d42\u4e86\u3057\u3066\u3044\u307e\u305b\u3093
+jsp.error.attr.quoted=\u5c5e\u6027\u5024\u306f\u5f15\u7528\u7b26\u3067\u56f2\u308f\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.attr.novalue=\u5c5e\u6027 {0} \u306b\u306f\u5024\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.tag.attr.unterminated=\u30bf\u30b0\u306e\u5c5e\u6027\u30ea\u30b9\u30c8\u304c\u7d42\u4e86\u3057\u3066\u3044\u307e\u305b\u3093
+jsp.error.param.noname=PARAM\u30bf\u30b0\u306bname\u5c5e\u6027\u304c\u3042\u308a\u307e\u305b\u3093
+jsp.error.param.novalue=PARAM\u30bf\u30b0\u306bvalue\u5c5e\u6027\u304c\u3042\u308a\u307e\u305b\u3093
+jsp.error.beans.nullbean=null\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306bBean\u64cd\u4f5c\u3092\u304a\u3053\u306a\u304a\u3046\u3068\u3057\u307e\u3057\u305f
+jsp.error.beans.nobeaninfo=\u30bf\u30a4\u30d7 ''{0}'' \u306eBean\u306bBeanInfo\u304c\u306a\u3044\u306e\u3092\u691c\u51fa\u3057\u307e\u3057\u305f, \u30af\u30e9\u30b9\u304c\u5b58\u5728\u3057\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093
+jsp.error.beans.introspection=\u30bf\u30a4\u30d7 ''{1}'' \u306eBean\u4e2d\u306e\u5c5e\u6027 ''{0}'' \u306eread\u30e1\u30bd\u30c3\u30c9\u3092\u5185\u7701\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f:\n{2}
+jsp.error.beans.nomethod=\u30bf\u30a4\u30d7 ''{1}'' \u306eBean\u4e2d\u306e\u5c5e\u6027 ''{0}'' \u3092\u8aad\u307f\u8fbc\u3080\u30e1\u30bd\u30c3\u30c9\u3092\u767a\u898b\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
+jsp.error.beans.nomethod.setproperty=\u30bf\u30a4\u30d7''{2}''\u306eBean\u306e\u30bf\u30a4\u30d7 ''{1}'' \u306e\u5c5e\u6027 ''{0}'' \u3092\u66f8\u304d\u8fbc\u3080\u30e1\u30bd\u30c3\u30c9\u3092\u767a\u898b\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
+jsp.error.beans.noproperty=\u30bf\u30a4\u30d7 ''{1}'' \u306ebean\u4e2d\u306e\u5c5e\u6027 ''{0}'' \u306e\u60c5\u5831\u3092\u767a\u898b\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
+jsp.error.beans.setproperty.noindexset=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u4ed8\u304d\u306e\u5c5e\u6027\u3092\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093
+jsp.error.include.tag=\u7121\u52b9\u306ajsp:include\u30bf\u30b0\u3067\u3059
+jsp.error.include.noflush=jsp:include\u30bf\u30b0\u306b \"flush=true\" \u3092\u5b9a\u7fa9\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.include.badflush=jsp:include page=\"...\" flush=\"true\" \u306f\u3001JSP 1.0\u3067\u306e\u307f\u6709\u52b9\u306a\u7d44\u307f\u5408\u308f\u305b\u3067\u3059
+jsp.error.attempt_to_clear_flushed_buffer=\u30a8\u30e9\u30fc: \u65e2\u306b\u30d5\u30e9\u30c3\u30b7\u30e5\u3055\u308c\u3066\u3044\u308b\u30d0\u30c3\u30d5\u30a1\u3092\u30af\u30ea\u30a2\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f
+jsp.error.overflow=\u30a8\u30e9\u30fc: JSP\u30d0\u30c3\u30d5\u30a1\u304c\u30aa\u30fc\u30d0\u30fc\u30d5\u30ed\u30fc\u3057\u307e\u3057\u305f
+jsp.error.paramexpected=\"name\"\u5c5e\u6027 \u3068 \"value\" \u5c5e\u6027\u3092\u6301\u3064 \"jsp:param\" \u6a19\u6e96\u30a2\u30af\u30b7\u30e7\u30f3\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.param.invalidUse=jsp:include\u3001jsp:forward\u3001\u53c8\u306fjsp:params\u8981\u7d20\u306e\u5916\u3067jsp:param\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u4f7f\u7528\u3057\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+jsp.error.params.invalidUse=jsp:params\u306fjsp:plugin\u306e\u76f4\u63a5\u306e\u5b50\u4f9b\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.fallback.invalidUse=jsp:fallback\u306fjsp:plugin\u306e\u76f4\u63a5\u306e\u5b50\u4f9b\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.namedAttribute.invalidUse=jsp:attribute\u306f\u6a19\u6e96\u53c8\u306f\u30ab\u30b9\u30bf\u30e0\u30a2\u30af\u30b7\u30e7\u30f3\u306e\u526f\u8981\u7d20\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.jspbody.invalidUse=jsp:body\u306f\u6a19\u6e96\u53c8\u306f\u30ab\u30b9\u30bf\u30e0\u30a2\u30af\u30b7\u30e7\u30f3\u306e\u526f\u8981\u7d20\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.closeindividualparam=param\u30bf\u30b0\u306f \"/>\" \u3067\u9589\u3058\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.closeparams=param\u30bf\u30b0\u306f/params\u3067\u9589\u3058\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.params.emptyBody=jsp:params\u306f\u5c11\u306a\u304f\u3068\u3082\u4e00\u3064\u306e\u30cd\u30b9\u30c8\u3057\u305fjsp:param\u3092\u542b\u307e\u306d\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.params.illegalChild=jsp:params\u306fjsp:param\u4ee5\u5916\u306e\u30cd\u30b9\u30c8\u3057\u305f\u8981\u7d20\u3092\u542b\u3093\u3067\u306f\u3044\u3051\u307e\u305b\u3093
+jsp.error.plugin.notype=jsp:plugin\u3067type\u5c5e\u6027\u304c\u5ba3\u8a00\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.plugin.badtype=jsp:plugin\u306e 'type'\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059: 'bean'\u53c8\u306f'applet'\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.plugin.nocode=jsp:plugin\u3067code\u5c5e\u6027\u304c\u5ba3\u8a00\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.ise_on_clear=\u30d0\u30c3\u30d5\u30a1\u30b5\u30a4\u30ba\u304c0\u306e\u6642\u306bclear()\u3092\u5b9f\u884c\u3057\u3066\u3082\u7121\u52b9\u3067\u3059
+jsp.error.setproperty.beanNotFound=setProperty: Bean {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.error.getproperty.beanNotFound=getProperty: Bean {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.error.setproperty.ClassNotFound=setProperty: \u30af\u30e9\u30b9 {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+# typo ?
+#jsp.error.setproperty.invalidSayntax=setProperty: property=*\u306e\u5834\u5408\u306fnull\u3067\u306a\u3044\u5024\u3092\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093
+jsp.error.setproperty.invalidSyntax=setProperty: property=*\u306e\u5834\u5408\u306fnull\u3067\u306a\u3044\u5024\u3092\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093
+jsp.error.setproperty.beanInfoNotFound=setproperty: Bean {0} \u306ebeanInfo\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.error.setproperty.paramOrValue=setProperty: param\u5c5e\u6027\u304bvalue\u5c5e\u6027\u306e\u3069\u3061\u3089\u304b\u4e00\u3064\u3060\u3051\u3092\u6307\u5b9a\u3067\u304d\u307e\u3059
+jsp.error.setproperty.arrayVal=setProperty: \u5c5e\u6027\u914d\u5217 {0} \u3092\u6587\u5b57\u5217\u5b9a\u6570\u5024\u3067\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093
+jsp.warning.keepgen=\u8b66\u544a: initParam keepgenerated\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002 \u30c7\u30d5\u30a9\u30eb\u30c8\u5024 \"false\" \u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.xpoweredBy=\u8b66\u544a: Invalid value for the initParam xpoweredBy\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u5024 \"false\" \u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.enablePooling=\u8b66\u544a: initParam enablePooling\u304c\u7121\u52b9\u306a\u5024\u3067\u3059\u3002\"true\"\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.invalidTagPoolSize=\u8b66\u544a: tagPoolSize\u306e\u521d\u671f\u30d1\u30e9\u30e1\u30fc\u30bf\u304c\u7121\u52b9\u306a\u5024\u3067\u3059\u3002{0}\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30b5\u30a4\u30ba\u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.mappedFile=\u8b66\u544a: initParam mappedFile\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u5024 \"false\" \u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.sendErrToClient=\u8b66\u544a: initParam sendErrToClient\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u5024 \"false\" \u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.classDebugInfo=\u8b66\u544a: initParam classDebugInfo\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u5024 \"false\"\u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.checkInterval=\u8b66\u544a: initParam checkInterval\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\"300\"\u79d2\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.development=\u8b66\u544a: initParam development\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\"true\"\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.fork=\u8b66\u544a: initParam fork\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\"true\"\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.reloading=\u8b66\u544a: initParam reloading\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\"true\"\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.dumpSmap=\u8b66\u544a: initParam dumpSmap\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\"false\"\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.genchararray=\u8b66\u544a: initParam genStrAsCharArray\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\"false\"\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.warning.suppressSmap=\u8b66\u544a: initParam suppressSmap\u306e\u5024\u304c\u7121\u52b9\u3067\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u5024 \"false\" \u3092\u4f7f\u7528\u3057\u307e\u3059
+jsp.error.badtaglib=\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea {0} \u3092\u30aa\u30fc\u30d7\u30f3\u3067\u304d\u307e\u305b\u3093: {1}
+jsp.error.badGetReader=\u30b9\u30c8\u30ea\u30fc\u30e0\u304c\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306b\u306f\u3001Reader\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093
+jsp.warning.unknown.element.in.taglib=taglib\u4e2d\u306b\u672a\u77e5\u306e\u8981\u7d20 ({0}) \u304c\u3042\u308a\u307e\u3059
+jsp.warning.unknown.element.in.tag=tag\u4e2d\u306b\u672a\u77e5\u306e\u8981\u7d20 ({0}) \u304c\u3042\u308a\u307e\u3059
+jsp.warning.unknown.element.in.tagfile=tag-file\u4e2d\u306b\u672a\u77e5\u306e\u8981\u7d20 ({0}) \u304c\u3042\u308a\u307e\u3059
+jsp.warning.unknown.element.in.attribute=attribute\u4e2d\u306b\u672a\u77e5\u306e\u8981\u7d20 ({0}) \u304c\u3042\u308a\u307e\u3059
+jsp.warning.unknown.element.in.variable=variable\u4e2d\u306b\u672a\u77e5\u306e\u8981\u7d20 ({0}) \u304c\u3042\u308a\u307e\u3059
+jsp.warning.unknown.element.in.validator=validator\u4e2d\u306b\u672a\u77e5\u306e\u8981\u7d20 ({0}) \u304c\u3042\u308a\u307e\u3059
+jsp.warning.unknown.element.in.initParam=validator\u306einit-param\u4e2d\u306b\u672a\u77e5\u306e\u8981\u7d20 ({0}) \u304c\u3042\u308a\u307e\u3059
+jsp.warning.unknown.element.in.function=function\u4e2d\u306b\u672a\u77e5\u306e\u8981\u7d20 ({0}) \u304c\u3042\u308a\u307e\u3059
+jsp.error.more.than.one.taglib=TLD\u306e\u4e2d\u306b\u8907\u6570\u306etaglib\u304c\u5b58\u5728\u3057\u307e\u3059: {0}
+jsp.error.teiclass.instantiation=TagExtraInfo class\u306e\u30ed\u30fc\u30c9\u53c8\u306f\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5316\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+jsp.error.non_null_tei_and_var_subelems=\u30bf\u30b0 {0} \u306f\u4e00\u3064\u4ee5\u4e0a\u306evariable\u526f\u8981\u7d20\u3068\u4e00\u3064\u4ee5\u4e0a\u306eVariableInfo\u3092\u8fd4\u3059TagExtraInfo\u30af\u30e9\u30b9\u3092\u6301\u3063\u3066\u3044\u307e\u3059
+jsp.error.parse.error.in.TLD=\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea\u8a18\u8ff0\u5b50 {0} \u4e2d\u306e\u89e3\u6790\u30a8\u30e9\u30fc\u3067\u3059
+jsp.error.unable.to.open.TLD=\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea\u8a18\u8ff0\u5b50 {0} \u3092\u30aa\u30fc\u30d7\u30f3\u3067\u304d\u307e\u305b\u3093
+jsp.buffer.size.zero=\u30d0\u30c3\u30d5\u30a1\u30b5\u30a4\u30ba\u304c0\u4ee5\u4e0b\u3067\u3059
+jsp.error.file.not.found=JSP \u30d5\u30a1\u30a4\u30eb \"{0}\" \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.message.copyinguri={0} \u3092 {1} \u306b\u30b3\u30d4\u30fc\u3057\u307e\u3059
+jsp.message.htmlcomment=\n\u524a\u9664\u3059\u308b\u30b3\u30e1\u30f3\u30c8: \t{0}
+jsp.message.handling_directive=\n\u51e6\u7406\u3059\u308b\u6307\u793a\u5b50: {0}\t{1}
+jsp.message.handling_plugin=\nPlugin: {0}
+jsp.message.package_name_is=\u30d1\u30c3\u30b1\u30fc\u30b8\u540d: {0}
+jsp.message.class_name_is=\u30af\u30e9\u30b9\u540d: {0}
+jsp.message.java_file_name_is=Java\u30d5\u30a1\u30a4\u30eb\u540d: {0}
+jsp.message.class_file_name_is=\u30af\u30e9\u30b9\u30d5\u30a1\u30a4\u30eb\u540d: {0}
+jsp.message.accepted={1} \u3067 {0} \u3092\u53d7\u3051\u5165\u308c\u307e\u3059
+jsp.message.adding_jar=jar {0} \u3092\u30af\u30e9\u30b9\u30d1\u30b9\u306b\u8ffd\u52a0\u3057\u307e\u3059
+jsp.message.compiling_with={0} \u3092\u30b3\u30f3\u30d1\u30a4\u30eb\u4e2d\u3067\u3059
+jsp.message.template_text=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30c6\u30ad\u30b9\u30c8
+jsp.error.missing_attribute=TLD\u53c8\u306f\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u306b\u3088\u308b\u3068\u3001\u5c5e\u6027 {0} \u306f\u30bf\u30b0 {1} \u306b\u306f\u5fc5\u9808\u3067\u3059
+jsp.error.bad_attribute=TLD\u306b\u3088\u308b\u3068\u3001\u30bf\u30b0 {1} \u306e\u5c5e\u6027 {0} \u306f\u7121\u52b9\u3067\u3059
+jsp.error.tld.unable_to_read=JAR\u30d5\u30a1\u30a4\u30eb \"{0}\" \u304b\u3089TLD \"{1}\" \u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093: {2}
+jsp.error.tld.unable_to_get_jar=TLD\u3092\u542b\u3080JAR\u30ea\u30bd\u30fc\u30b9 \"{0}\" \u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093 : {1}
+jsp.error.tld.missing_jar=TLD\u3092\u542b\u3080JAR\u30ea\u30bd\u30fc\u30b9 \"{0}\" \u304c\u3042\u308a\u307e\u305b\u3093
+jsp.error.webxml_not_found=web.xml\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.cmd_line.usage=\u4f7f\u7528\u6cd5:  [-dd <\u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306e\u30d1\u30b9>] [-keepgenerated] \
+<.jsp\u30d5\u30a1\u30a4\u30eb\u7fa4>
+jsp.message.cp_is=\u30af\u30e9\u30b9\u30d1\u30b9 {0} \u306f {1} \u3067\u3059
+jsp.error.unable.to_load_taghandler_class=\u30bf\u30b0\u30cf\u30f3\u30c9\u30e9\u30af\u30e9\u30b9 {0} \u3092 {1} \u306e\u305f\u3081\u306b\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093
+jsp.error.unable.to_find_method=\u5c5e\u6027 {0} \u306esetter\u30e1\u30bd\u30c3\u30c9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.error.unable.to_convert_string=\u5c5e\u6027 {1}\u306e\u6587\u5b57\u5217\u3092 {0}\u306b\u5909\u63db\u3067\u304d\u307e\u305b\u3093
+jsp.error.unable.to_introspect=\u30bf\u30b0\u30cf\u30f3\u30c9\u30e9\u30af\u30e9\u30b9 {0} \u3092 {1} \u306e\u305f\u3081\u306b\u5185\u7701\u3067\u304d\u307e\u305b\u3093
+jsp.error.bad_tag=\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9 {1}\u3067\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u305f\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea\u306b\u306f\u3001\u30bf\u30b0 {0} \u306f\u5b58\u5728\u3057\u307e\u305b\u3093
+jsp.error.xml.bad_tag=URI \"{1}\" \u306b\u95a2\u9023\u3065\u3051\u3089\u308c\u305f\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea\u306e\u4e2d\u306b\u306f\u30bf\u30b0 \"{0}\" \u306f\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.bad_string_Character=\u9577\u30550\u306e\u914d\u5217\u304b\u3089\u306f\u6587\u5b57\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093
+jsp.error.bad_string_char=\u9577\u30550\u306e\u914d\u5217\u304b\u3089\u306f\u6587\u5b57\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093
+jsp.warning.compiler.class.cantcreate=\u6307\u5b9a\u3055\u308c\u305f\u30b3\u30f3\u30d1\u30a4\u30e9\u30d7\u30e9\u30b0\u30a4\u30f3\u30af\u30e9\u30b9 {0} \u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092 {1} \u306e\u305f\u3081\u306b\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u3092Sun\u306eJava\u30b3\u30f3\u30d1\u30a4\u30e9\u306b\u3057\u307e\u3059\u3002
+jsp.warning.compiler.class.notfound=\u6307\u5b9a\u3055\u308c\u305f\u30b3\u30f3\u30d1\u30a4\u30e9\u30d7\u30e9\u30b0\u30a4\u30f3\u30af\u30e9\u30b9 {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002not found. \u30c7\u30d5\u30a9\u30eb\u30c8\u3092Sun\u306eJava\u30b3\u30f3\u30d1\u30a4\u30e9\u306b\u3057\u307e\u3059\u3002
+jsp.warning.compiler.path.notfound=\u6307\u5b9a\u3055\u308c\u305f\u30b3\u30f3\u30d1\u30a4\u30e9\u30d1\u30b9 {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u30b7\u30b9\u30c6\u30e0\u306ePATH\u3092\u30c7\u30d5\u30a9\u30eb\u30c8\u306b\u3057\u307e\u3059\u3002
+jsp.error.jspc.uriroot_not_dir=-uriroot \u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u3001\u65e2\u306b\u5b58\u5728\u3059\u308b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u6307\u5b9a\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.jspc.missingTarget=\u30bf\u30fc\u30b2\u30c3\u30c8\u304c\u3042\u308a\u307e\u305b\u3093: -webapp\u53c8\u306f-uriroot\uff0c\u53c8\u306f\u4e00\u3064\u4ee5\u4e0a\u306eJSP\u30da\u30fc\u30b8\u3092\u6307\u5b9a\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.jspc.no_uriroot=uriroot\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u306e\u3067\u3001\u6307\u5b9a\u3055\u308c\u305fJSP\u30d5\u30a1\u30a4\u30eb(\u7fa4)\u3092\u914d\u7f6e\u3067\u304d\u307e\u305b\u3093
+jspc.implicit.uriRoot=uriRoot\u306f\u30c7\u30d5\u30a9\u30eb\u30c8"{0}"\u306b\u8a2d\u5b9a\u3055\u308c\u307e\u3059
+jspc.usage=\u4f7f\u7528\u6cd5: jspc <options> [--] <jsp files>\n\
+JSP\u30d5\u30a1\u30a4\u30eb\u306e\u5834\u6240\u306f\u6b21\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3067\u6307\u5b9a\u3059\u308b\u304b\u3001\n\
+\    -webapp <dir>   web-app\u3092\u542b\u3080\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3002\u3059\u3079\u3066\u306eJSP\u30d5\u30a1\u30a4\u30eb\u306f\n\
+\                    \u518d\u5e30\u7684\u306b\u89e3\u6790\u3055\u308c\u308b\n\
+\u53c8\u306f\u6b21\u306e\u4efb\u610f\u306e\u6570\u306e\u30d5\u30a1\u30a4\u30eb\u3067\u6307\u5b9a\u3057\u307e\u3059\u3002\n\
+\    <file>          JSP\u3068\u3057\u3066\u89e3\u6790\u3055\u308c\u308b\u30d5\u30a1\u30a4\u30eb\n\
+\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u4ee5\u4e0b\u306e\u901a\u308a\u3067\u3059\n\
+\    -help           \u3053\u306e\u30d8\u30eb\u30d7\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u8868\u793a\n\
+\    -v              Verbose\u30e2\u30fc\u30c9\n\
+\    -d <dir>        \u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\n\
+\    -l              \u5931\u6557\u3057\u305fJSP\u30da\u30fc\u30b8\u306e\u540d\u524d\u306e\u51fa\u529b\n\
+\    -s              \u6210\u529f\u3057\u305fJSP\u30da\u30fc\u30b8\u306e\u540d\u524d\u306e\u51fa\u529b\n\
+\    -p <name>       \u30bf\u30fc\u30b2\u30c3\u30c8\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u540d\u524d  (\u30c7\u30d5\u30a9\u30eb\u30c8\u306forg.apache.jsp)\n\
+\    -c <name>       \u30bf\u30fc\u30b2\u30c3\u30c8\u30af\u30e9\u30b9\u306e\u540d\u524d (\u6700\u521d\u306eJSP\u30da\u30fc\u30b8\u3060\u3051\u306b\u9069\u7528\u3055\u308c\u308b)\n\
+\    -mapped         JSP\u306e\u5404HTML\u884c\u3054\u3068\u306bwrite()\u30b3\u30fc\u30eb\u3092\u751f\u6210\n\
+\    -die[#]         \u81f4\u547d\u7684\u30a8\u30e9\u30fc\u306b\u30a8\u30e9\u30fc\u30ea\u30bf\u30fc\u30f3\u30b3\u30fc\u30c9(#)\u3092\u751f\u6210 (\u30c7\u30d5\u30a9\u30eb\u30c8\u306f1)\n\
+\    -uribase <dir>  \u30b3\u30f3\u30d1\u30a4\u30eb\u304c\u76f8\u5bfe\u7684\u306b\u304a\u3053\u306a\u308f\u308c\u308buri\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\n\
+\                    (\u30c7\u30d5\u30a9\u30eb\u30c8\u306f"/")\n\
+\    -uriroot <dir>  -webapp\u3068\u540c\u3058\n\
+\    -compile        \u751f\u6210\u3057\u305f\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u306e\u30b3\u30f3\u30d1\u30a4\u30eb\n\
+\    -webinc <file>  \u30d5\u30a1\u30a4\u30eb\u306b\u90e8\u5206\u7684\u306a\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u30de\u30c3\u30d4\u30f3\u30b0\u3092\u4f5c\u6210\n\
+\    -webxml <file>  \u30d5\u30a1\u30a4\u30eb\u306b\u5b8c\u5168\u306aweb.xml\u3092\u4f5c\u6210\n\
+\    -ieplugin <clsid>  Internet Explorer\u306eJava Plugin\u306eclassid\n\
+\    -classpath <path>  java.class.path\u30b7\u30b9\u30c6\u30e0\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u4e0a\u66f8\u304d\n\
+\    -xpoweredBy        X-Powered-By\u30ec\u30b9\u30dd\u30f3\u30b9\u30d8\u30c3\u30c0\u306e\u8ffd\u52a0\n\
+\    -trimSpaces        \u30a2\u30af\u30b7\u30e7\u30f3\u3084\u6307\u793a\u5b50\u306e\u9593\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30c6\u30ad\u30b9\u30c8\u4e2d\u306e\u30b9\u30da\u30fc\u30b9\u3092\u524a\u9664\n\
+\    -trimSpaces        Trim spaces in template text between actions, directives\n\
+\    -javaEncoding <enc> Set the encoding charset for Java classes (default UTF-8)\n\
+\    -source <version>   Set the -source argument to the compiler (default 1.4)\n\
+\    -target <version>   Set the -target argument to the compiler (default 1.4)\n\
+
+jspc.webxml.header=<?xml version="1.0" encoding="ISO-8859-1"?>\n\
+\n\
+<!DOCTYPE web-app\n\
+\    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"\n\
+\    "http://java.sun.com/dtd/web-app_2_3.dtd">\n\
+<!--\n\
+Automatically created by Apache Jakarta Tomcat JspC.\n\
+-->\n\
+<web-app>\n\
+\n
+jspc.webxml.footer=\n\
+</web-app>\n\
+\n
+jspc.webinc.header=\n\
+<!--\n\
+Automatically created by Apache Jakarta Tomcat JspC.\n\
+Place this fragment in the web.xml before all icon, display-name,\n\
+description, distributable, and context-param elements.\n\
+-->\n
+jspc.webinc.footer=\n\
+<!--\n\
+All session-config, mime-mapping, welcome-file-list, error-page, taglib,\n\
+resource-ref, security-constraint, login-config, security-role,\n\
+env-entry, and ejb-ref elements should follow this fragment.\n\
+-->\n
+jspc.webinc.insertEnd=<!-- JSPC servlet mappings end -->
+jspc.webinc.insertStart=<!-- JSPC servlet mappings start -->
+jspc.error.jasperException=\u30a8\u30e9\u30fc: \u30d5\u30a1\u30a4\u30eb ''{0}'' \u306f\u6b21\u306e\u4f8b\u5916\u3092\u767a\u751f\u3057\u307e\u3057\u305f: {1}
+jspc.error.generalException=\u30a8\u30e9\u30fc: \u30d5\u30a1\u30a4\u30eb ''{0}'' \u306f\u6b21\u306e\u4f8b\u5916\u3092\u767a\u751f\u3057\u307e\u3057\u305f:
+jspc.error.fileDoesNotExist=\u30d5\u30a1\u30a4\u30eb\u5f15\u6570 ''{0}'' \u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002
+jspc.error.emptyWebApp=-webapp\u30aa\u30d7\u30b7\u30e7\u30f3\u306b\u306f\u3001\u30d5\u30a1\u30a4\u30eb\u5f15\u6570\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.library.invalid=\u30e9\u30a4\u30d6\u30e9\u30ea{0}\u306b\u5f93\u3046\u3068JSP\u30da\u30fc\u30b8\u306f\u7121\u52b9\u3067\u3059: {1}
+jsp.error.tlvclass.instantiation=TagLibraryValidator\u30af\u30e9\u30b9\u306e\u30ed\u30fc\u30c9\u53c8\u306f\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5316\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+jsp.error.tlv.invalid.page={0} \u306b\u5bfe\u3059\u308bTagLibraryValidator\u306e\u691c\u8a3c\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8\u3067\u3059
+jsp.error.tei.invalid.attributes={0} \u306b\u5bfe\u3059\u308bTagExtraInfo\u304b\u3089\u306e\u691c\u8a3c\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8\u3067\u3059
+jsp.parser.sax.propertynotsupported=SAX\u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u307e\u305b\u3093: {0}
+jsp.parser.sax.propertynotrecognized=SAX\u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u8a8d\u8b58\u3055\u308c\u307e\u305b\u3093: {0}
+jsp.parser.sax.featurenotsupported=SAX\u30d5\u30a3\u30fc\u30c1\u30e3\u304c\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u307e\u305b\u3093: {0}
+jsp.parser.sax.featurenotrecognized=SAX\u30d5\u30a3\u30fc\u30c1\u30e3\u304c\u8a8d\u8b58\u3055\u308c\u307e\u305b\u3093: {0}
+jsp.error.no.more.content=\u5fc5\u8981\u306a\u89e3\u6790\u4e2d\u306b\u5185\u5bb9\u306e\u6700\u5f8c\u307e\u3067\u9054\u3057\u307e\u3057\u305f: \u30bf\u30b0\u306e\u30cd\u30b9\u30c8\u306e\u30a8\u30e9\u30fc\u304b\u3082\u3057\u308c\u307e\u305b\u3093
+jsp.error.parse.xml=\u30d5\u30a1\u30a4\u30eb{0}\u306eXML\u89e3\u6790\u30a8\u30e9\u30fc
+jsp.error.parse.xml.line=\u30d5\u30a1\u30a4\u30eb{0}\u306eXML\u89e3\u6790\u30a8\u30e9\u30fc: (\u884c {1}, \u5217 {2})
+jsp.error.parse.xml.scripting.invalid.body={0} \u8981\u7d20\u306e\u30dc\u30c7\u30a3\u306fXML\u8981\u7d20\u3092\u542b\u3093\u3067\u306f\u3044\u3051\u307e\u305b\u3093
+jsp.error.internal.tldinit=TldLocationsCache\u3092\u521d\u671f\u5316\u4e2d\u306e\u4f8b\u5916\u3067\u3059: {0}
+jsp.error.internal.filenotfound=\u5185\u90e8\u30a8\u30e9\u30fc: \u30d5\u30a1\u30a4\u30eb {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.error.internal.evaluator_not_found=\u5185\u90e8\u30a8\u30e9\u30fc: \u5f0f\u691c\u8a3c\u5668\u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093
+jsp.error.parse.xml.invalidPublicId=\u7121\u52b9\u306aPUBLIC ID: {0}
+jsp.error.include.flush.invalid.value=flush\u5c5e\u6027\u306b\u7121\u52b9\u306a\u5024\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059: {0}
+jsp.error.unsupported.encoding=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u3067\u3059: {0}
+tld.error.variableNotAllowed=null\u3067\u306a\u3044\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u8fd4\u3059TagExtraInfo\u3092\u6301\u3064\u4e00\u3064\u4ee5\u4e0a\u306e\u5909\u6570\u526f\u8981\u7d20\u3092\u6301\u3064\u30bf\u30b0\u306f\u30a8\u30e9\u30fc\u3067\u3059\u3002
+jsp.error.tldInWebDotXmlNotFound=web.xml\u3067\u6307\u5b9a\u3055\u308c\u305fURI {0} \u306bTLD\u3092\u914d\u7f6e\u3067\u304d\u307e\u305b\u3093
+jsp.error.taglibDirective.absUriCannotBeResolved=\u7d76\u5bfeURI:  {0} \u306fweb.xml\u3068\u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u3057\u305fJAR\u30d5\u30a1\u30a4\u30eb\u306e\u3069\u3061\u3089\u304b\u3067\u3082\u89e3\u6c7a\u3067\u304d\u307e\u305b\u3093
+jsp.error.taglibDirective.missing.location=taglib\u6307\u793a\u5b50\u306e\u4e2d\u306b'uri'\u5c5e\u6027\u3068'tagdir'\u5c5e\u6027\u306e\u3069\u3061\u3089\u3082\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.taglibDirective.both_uri_and_tagdir=\'uri\'\u5c5e\u6027 \u3068 \'tagdir\'\u5c5e\u6027\u306e\u4e21\u65b9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059
+jsp.error.invalid.tagdir=\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u304c\"/WEB-INF/tags\"\u3067\u59cb\u307e\u308a\u307e\u305b\u3093
+jsp.error.unterminated.user.tag=\u672a\u7d42\u4e86\u306e\u30e6\u30fc\u30b6\u5b9a\u7fa9\u30bf\u30b0: \u7d42\u4e86\u30bf\u30b0 {0} \u304c\u898b\u3064\u304b\u3089\u306a\u3044\u304b\u3001\u30cd\u30b9\u30c8\u304c\u9593\u9055\u3063\u3066\u3044\u307e\u3059
+#jspx.error.templateDataNotInJspCdata=Validation Error: Element &lt;{0}&gt; cannot have template data. Template data must be encapsulated within a &lt;jsp:cdata&gt; element. [JSP1.2 PFD section 5.1.9]\nTemplate data in error: {1}
+jspx.error.templateDataNotInJspCdata=\u8a3c\u660e\u30a8\u30e9\u30fc: \u8981\u7d20&lt;{0}&gt;\u306f\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30c7\u30fc\u30bf\u3092\u6301\u3064\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30c7\u30fc\u30bf\u306f\u3001&lt;jsp:text&gt;\u8981\u7d20\u306e\u4e2d\u3067\u96a0\u853d\u3055\u308c\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002[JSP1.2 PFD 5.1.9]\n\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30c7\u30fc\u30bf\u306e\u30a8\u30e9\u30fc\u3067\u3059: {1}
+#Error while processing taglib jar file {0}: {1}
+jsp.error.taglib.reserved.prefix=taglib\u30d7\u30ea\u30d5\u30a3\u30af\u30b9 {0} \u306f\u4e88\u7d04\u3055\u308c\u3066\u3044\u307e\u3059
+jsp.error.invalid.javaEncoding=\u7121\u52b9\u306aJava\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u3067\u3059\u3002{0}\u3092\u8a66\u3057\u3066\u3001\u305d\u308c\u304b\u3089{1}\u3092\u8a66\u3057\u307e\u3057\u305f\u304c\u3001\u4e21\u65b9\u304c\u5931\u6557\u3057\u307e\u3057\u305f
+jsp.error.needAlternateJavaEncoding=\u30c7\u30d5\u30a9\u30eb\u30c8\u306eJava\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0 {0} \u306f\u3042\u306a\u305f\u306e\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u3067\u306f\u7121\u52b9\u3067\u3059\u3002JspServlet\u306e 'javaEncoding' \u30d1\u30e9\u30e1\u30bf\u3067\u3001\u5225\u306e\u5024\u3092\u6307\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002
+#Error when compiling, used for jsp line number error messages
+jsp.error.single.line.number=JSP\u30d5\u30a1\u30a4\u30eb: {1} \u306e\u4e2d\u306e{0}\u884c\u76ee\u3067\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+jsp.error.multiple.line.number=\n\nJPS\u30d5\u30a1\u30a4\u30eb: {2}\u306e\u4e2d\u306e{0}\u884c\u76ee\u3068{1}\u884c\u76ee\u306e\u9593\u3067\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\n\n
+jsp.error.corresponding.servlet=\u751f\u6210\u3055\u308c\u305f\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u306e\u30a8\u30e9\u30fc\u3067\u3059:\n
+jsp.error.empty.body.not.allowed={0} \u306b\u5bfe\u3057\u3066\u7a7a\u306e\u30dc\u30c7\u30a3\u306f\u8a31\u3055\u308c\u307e\u305b\u3093
+jsp.error.jspbody.required=jsp:attribute\u304c\u4f7f\u7528\u3055\u308c\u305f\u5834\u5408\u306b\u306f\u3001{0}\u306b\u30bf\u30b0\u30dc\u30c7\u30a3\u3092\u6307\u5b9a\u3059\u308b\u305f\u3081\u306bjsp:body\u3092\u4f7f\u7528\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.jspbody.emptybody.only={0} \u30bf\u30b0\u306f\u3001\u305d\u306e\u30dc\u30c7\u30a3\u4e2d\u306bjsp:attribute\u3060\u3051\u3092\u6301\u3064\u3053\u3068\u304c\u3067\u304d\u307e\u3059
+jsp.error.no.scriptlets=\u30b9\u30af\u30ea\u30d7\u30c6\u30a3\u30f3\u30b0\u8981\u7d20 ( &lt;%!\u3001&lt;jsp:declaration\u3001&lt;%=\u3001&lt;jsp:expression\u3001&lt;%\u3001&lt;jsp:scriptlet ) \u306f\u3053\u3053\u3067\u306f\u8a31\u3055\u308c\u307e\u305b\u3093
+jsp.error.internal.unexpected_node_type=\u5185\u90e8\u30a8\u30e9\u30fc: \u672a\u77e5\u306e\u30ce\u30fc\u30c9\u30bf\u30a4\u30d7\u304c\u8868\u308c\u307e\u3057\u305f
+jsp.error.tld.fn.invalid.signature=TLD\u306e\u4e2d\u306e\u95a2\u6570\u30b7\u30b0\u30cd\u30c1\u30e3\u306b\u5bfe\u3059\u308b\u7121\u52b9\u306a\u69cb\u6587\u3067\u3059\u3002\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea: {0}\u3001\u95a2\u6570: {1}
+jsp.error.tld.fn.duplicate.name=\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea {1} \u306e\u4e2d\u306e\u95a2\u6570\u540d {0} \u304c\u91cd\u8907\u3057\u3066\u3044\u307e\u3059
+jsp.error.tld.fn.invalid.signature.commaexpected=TLD\u306e\u4e2d\u306e\u95a2\u6570\u30b7\u30b0\u30cd\u30c1\u30e3\u306b\u5bfe\u3059\u308b\u7121\u52b9\u306a\u69cb\u6587\u3067\u3059\u3002\u30b3\u30f3\u30de ',' \u304c\u3042\u308a\u307e\u305b\u3093\u3002\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea: {0}\u3001\u95a2\u6570: {1}\u3002
+jsp.error.tld.fn.invalid.signature.parenexpected=TLD\u306e\u4e2d\u306e\u95a2\u6570\u30b7\u30b0\u30cd\u30c1\u30e3\u306b\u5bfe\u3059\u308b\u7121\u52b9\u306a\u69cb\u6587\u3067\u3059\u3002\u62ec\u5f27 '(' \u304c\u3042\u308a\u307e\u305b\u3093\u3002\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea: {0}\u3001\u95a2\u6570: {1}\u3002
+jsp.error.tld.mandatory.element.missing=\u5fc5\u9808TLD\u8981\u7d20\u304c\u306a\u3044\u3001\u53c8\u306f\u7a7a\u3067\u3059: {0}
+jsp.error.dynamic.attributes.not.implemented={0} \u30bf\u30b0\u306f\u305d\u308c\u304cdynamic\u5c5e\u6027\u3092\u53d7\u3051\u4ed8\u3051\u308b\u3068\u5ba3\u8a00\u3057\u3066\u3044\u307e\u3059\u304c\u3001\u305d\u308c\u306b\u5fc5\u8981\u306a\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u5b9f\u88c5\u3057\u3066\u3044\u307e\u305b\u3093
+jsp.error.nomatching.fragment=attribute\u6307\u793a\u5b50 (name={0}\u304a\u3088\u3073fragment=true\u3092\u6301\u3064) \u304cfragment\u6307\u793a\u5b50\u3088\u308a\u524d\u306b\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.error.attribute.noequal=\u7b49\u53f7\u8a18\u53f7\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.attribute.noquote=\u5f15\u7528\u7b26\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.attribute.unterminated={0} \u306e\u5c5e\u6027\u304c\u6b63\u3057\u304f\u7d42\u4e86\u3057\u3066\u3044\u307e\u305b\u3093
+jsp.error.missing.tagInfo={0} \u306b\u5bfe\u3059\u308bTagInfo\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u304cTLD\u304b\u3089\u5931\u308f\u308c\u307e\u3057\u305f
+jsp.error.fragmentwithtype='fragment'\u5c5e\u6027\u3068'type'\u5c5e\u6027\u3092\u4e21\u65b9\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093\u3002'fragment'\u304c\u5b58\u5728\u3059\u308b\u5834\u5408\u306b\u306f'type'\u306f'javax.servlet.jsp.tagext.JspFragment'\u306b\u56fa\u5b9a\u3055\u308c\u307e\u3059
+jsp.error.fragmentwithrtexprvalue='fragment'\u5c5e\u6027\u3068'rtexprvalue'\u5c5e\u6027\u3092\u4e21\u65b9\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093\u3002'fragment'\u304c\u5b58\u5728\u3059\u308b\u5834\u5408\u306b\u306f'rtexprvalue'\u306f'true'\u306b\u56fa\u5b9a\u3055\u308c\u307e\u3059
+jsp.error.fragmentWithDeclareOrScope='fragment'\u5c5e\u6027\u3068'declare'\u5c5e\u6027\u306e\u4e21\u65b9\u53c8\u306f'scope'\u5c5e\u6027\u304cvariable\u6307\u793a\u5b50\u4e2d\u3067\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059
+jsp.error.var_and_varReader=\'var\'\u53c8\u306f\'varReader\'\u306e\u3069\u3061\u3089\u304b\u4e00\u3064\u3092\u6307\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059
+jsp.error.missing_var_or_varReader=\'var\'\u53c8\u306f\'varReader\'\u5c5e\u6027\u304c\u3042\u308a\u307e\u305b\u3093
+jsp.warning.bad.urlpattern.propertygroup=web.xml\u4e2d\u306eurl-pattern\u526f\u8981\u7d20\u4e2d\u306b\u8aa4\u3063\u305f\u5024 {0} \u304c\u3042\u308a\u307e\u3059
+jsp.error.unknown_attribute_type=\u5c5e\u6027 {0} \u306b\u5bfe\u3059\u308b\u672a\u77e5\u306e\u5c5e\u6027\u30bf\u30a4\u30d7\u3067\u3059
+jsp.error.jspelement.missing.name=\u5fc5\u9808\u306eXML\u30b9\u30bf\u30a4\u30eb\u306e'name'\u5c5e\u6027\u304cjsp:element\u4e2d\u306b\u3042\u308a\u307e\u305b\u3093
+jsp.error.xmlns.redefinition.notimplemented=\u5185\u90e8\u30a8\u30e9\u30fc: xmlns:{0}\u3092\u518d\u5b9a\u7fa9\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f\u3002\u540d\u524d\u7a7a\u9593\u306e\u518d\u5b9a\u7fa9\u306f\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002
+jsp.error.could.not.add.taglibraries=1\u3064\u4ee5\u4e0a\u306e\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093
+jsp.error.duplicate.name.jspattribute=\u6a19\u6e96\u53c8\u306f\u30ab\u30b9\u30bf\u30e0\u30a2\u30af\u30b7\u30e7\u30f3\u4e2d\u3067\u6307\u5b9a\u3055\u308c\u3066\u3044\u308b\u5c5e\u6027 {0} \u306f\u305d\u308c\u306b\u56f2\u307e\u308c\u305fjsp:attribute\u4e2d\u306ename\u5c5e\u6027\u306e\u5024\u3068\u3057\u3066\u3082\u8868\u308c\u307e\u3059
+jsp.error.not.in.template=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30c6\u30ad\u30b9\u30c8\u30dc\u30c7\u30a3\u4e2d\u3067\u306f {0} \u306f\u8a31\u3055\u308c\u307e\u305b\u3093
+jsp.error.badStandardAction=\u7121\u52b9\u306a\u6a19\u6e96\u30a2\u30af\u30b7\u30e7\u30f3\u3067\u3059
+jsp.error.xml.badStandardAction=\u7121\u52b9\u306a\u6a19\u6e96\u30a2\u30af\u30b7\u30e7\u30f3\u3067\u3059: {0}
+jsp.error.tagdirective.badbodycontent=tag\u6307\u793a\u5b50\u4e2d\u306e\u7121\u52b9\u306abody-content ({0})\u3067\u3059
+jsp.error.simpletag.badbodycontent=\u30af\u30e9\u30b9 {0} \u306eTLD\u306fSimpleTag\u306b\u7121\u52b9\u306abody-content (JSP)\u3092\u6307\u5b9a\u3057\u3066\u3044\u307e\u3059
+jsp.error.config_pagedir_encoding_mismatch=jsp-property-group\u4e2d\u306b\u6307\u5b9a\u3055\u308c\u3066\u3044\u308bPage-encoding ({0}) \u304cpage\u6307\u793a\u5b50\u4e2d\u306e\u6307\u5b9a ({1}) \u3068\u9055\u3044\u307e\u3059
+jsp.error.prolog_pagedir_encoding_mismatch=XML\u5c0e\u5165\u90e8\u3067\u6307\u5b9a\u3055\u308c\u305fpage-encoding ({0}) \u304cpage\u6307\u793a\u5b50\u4e2d\u306e\u6307\u5b9a ({1}) \u3068\u9055\u3044\u307e\u3059
+jsp.error.prolog_config_encoding_mismatch=XML\u5c0e\u5165\u90e8\u3067\u6307\u5b9a\u3055\u308c\u305fpage-encoding ({0}) \u304cjsp-property-group\u4e2d\u306e\u6307\u5b9a\u3068\u9055\u3044\u307e\u3059 ({1})
+jsp.error.attribute.custom.non_rt_with_expr=TLD\u53c8\u306f\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u4e2d\u306eattribute\u6307\u793a\u5b50\u306b\u5f93\u3063\u3066\u5c5e\u6027{0}\u306f\u3069\u3093\u306a\u5f0f\u3082\u53d7\u3051\u4ed8\u3051\u307e\u305b\u3093
+jsp.error.attribute.standard.non_rt_with_expr={1} \u6a19\u6e96\u30a2\u30af\u30b7\u30e7\u30f3\u306e {0} \u5c5e\u6027\u306f\u3069\u3093\u306a\u5f0f\u3082\u53d7\u3051\u4ed8\u3051\u307e\u305b\u3093
+jsp.error.scripting.variable.missing_name=\u5c5e\u6027 {0} \u304b\u3089\u30b9\u30af\u30ea\u30d7\u30c8\u5909\u6570\u540d\u3092\u6c7a\u5b9a\u3067\u304d\u307e\u305b\u3093
+jasper.error.emptybodycontent.nonempty=TLD\u306b\u5f93\u3063\u3066\u30bf\u30b0 {0} \u306f\u7a7a\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u304c\u3001\u305d\u3046\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+jsp.error.tagfile.nameNotUnique={2}\u884c\u76ee\u306e {0} \u306e\u5024\u3068 {1} \u306e\u5024\u306f\u540c\u3058\u3067\u3059
+jsp.error.tagfile.nameFrom.noAttribute=\u3053\u306ename-from-attribute\u5c5e\u6027\u306e\u5024\u3067\u3042\u308b\u5024 \"{0}\" \u306ename\u5c5e\u6027\u3092\u6301\u3064attribute\u6307\u793a\u5b50\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.error.tagfile.nameFrom.badAttribute=attribute\u6307\u793a\u5b50 ({1}\u884c\u76ee\u3067\u5ba3\u8a00\u3055\u308c\u3001\u305d\u306ename\u5c5e\u6027\u304c\"{0}\"\u3001\u3053\u306ename-from-attribute\u5c5e\u6027\u306e\u5024) \u306fjava.lang.String\u578b\u306e\"required\" \u3067 \"rtexprvalue\".\u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+jsp.error.page.noSession=\u30bb\u30c3\u30b7\u30e7\u30f3\u306b\u52a0\u308f\u3063\u3066\u3044\u306a\u3044\u30da\u30fc\u30b8\u306e\u4e2d\u3067\u306f\u30bb\u30c3\u30b7\u30e7\u30f3\u30b9\u30b3\u30fc\u30d7\u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u305b\u3093
+jsp.error.useBean.noSession=JSP\u30da\u30fc\u30b8\u304c(page\u6307\u793a\u5b50\u306b\u3088\u308a)\u30bb\u30c3\u30b7\u30e7\u30f3\u4e2d\u3067\u5354\u8abf\u3057\u306a\u3044\u3053\u3068\u3092\u5ba3\u8a00\u3057\u3066\u3044\u308b\u6642\u3001\u30bb\u30c3\u30b7\u30e7\u30f3\u30b9\u30b3\u30fc\u30d7\u3092\u4f7f\u7528\u3059\u308b\u305f\u3081\u306euseBean\u304c\u4e0d\u6b63\u3067\u3059
+jsp.error.xml.encodingByteOrderUnsupported = \u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0 \"{0}\" \u306b\u6307\u5b9a\u3055\u308c\u305f\u30d0\u30a4\u30c8\u30aa\u30fc\u30c0\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.xml.encodingDeclInvalid = \u7121\u52b9\u306a\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u540d \"{0}\" \u3067\u3059
+jsp.error.xml.encodingDeclRequired = \u30c6\u30ad\u30b9\u30c8\u5ba3\u8a00\u4e2d\u306b\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u5ba3\u8a00\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.morePseudoAttributes = \u3088\u308a\u591a\u304f\u306e\u7591\u4f3c\u5c5e\u6027\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.noMorePseudoAttributes = \u3053\u308c\u4ee5\u4e0a\u306e\u7591\u4f3c\u5c5e\u6027\u306f\u8a31\u3055\u308c\u307e\u305b\u3093
+jsp.error.xml.versionInfoRequired = XML\u5ba3\u8a00\u306e\u4e2d\u306b\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.xmlDeclUnterminated = XML\u5ba3\u8a00\u306f\"?>\"\u3067\u7d42\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.xml.reservedPITarget = \"[xX][mM][lL]\"\u306b\u4e00\u81f4\u3059\u308b\u51e6\u7406\u547d\u4ee4\u30bf\u30fc\u30b2\u30c3\u30c8\u306f\u8a31\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.xml.spaceRequiredInPI = \u7a7a\u767d\u304c\u51e6\u7406\u547d\u4ee4\u30bf\u30fc\u30b2\u30c3\u30c8\u3068\u30c7\u30fc\u30bf\u306e\u9593\u306b\u5fc5\u8981\u3067\u3059
+jsp.error.xml.invalidCharInContent = \u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306e\u7121\u52b9\u306aXML\u6587\u5b57 (Unicode: 0x{0}) \u304c\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306e\u8981\u7d20\u5185\u5bb9\u306e\u4e2d\u306b\u898b\u3064\u304b\u308a\u307e\u3057\u305f
+jsp.error.xml.spaceRequiredBeforeStandalone = XML\u5ba3\u8a00\u306eencoding\u7591\u4f3c\u5c5e\u6027\u306e\u524d\u306b\u7a7a\u767d\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.sdDeclInvalid = \u30b9\u30bf\u30f3\u30c9\u30a2\u30ed\u30f3\u6587\u66f8\u5ba3\u8a00\u5024\u306f\"yes\"\u53c8\u306f\"no\"\u306e\u3069\u3061\u3089\u304b\u3067\u3042\u308a\u3001\"{0}\"\u3067\u306f\u3044\u3051\u307e\u305b\u3093
+jsp.error.xml.invalidCharInPI = \u7121\u52b9\u306aXML\u6587\u5b57 (Unicode: 0x{0}) \u304c\u547d\u4ee4\u51e6\u7406\u4e2d\u306b\u898b\u3064\u304b\u308a\u307e\u3057\u305f
+jsp.error.xml.versionNotSupported = XML\u30d0\u30fc\u30b8\u30e7\u30f3 \"{0}\" \u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3001XML 1.0\u3060\u3051\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u3059
+jsp.error.xml.pseudoAttrNameExpected = \u7591\u4f3c\u5c5e\u6027\u540d\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.expectedByte ={1}\u30d0\u30a4\u30c8UTF-8\u30b7\u30fc\u30b1\u30f3\u30b9\u306e\u30d0\u30a4\u30c8 {0} \u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.invalidByte = {1}\u30d0\u30a4\u30c8UTF-8\u30b7\u30fc\u30b1\u30f3\u30b9\u306e\u7121\u52b9\u306a\u30d0\u30a4\u30c8 {0} \u3067\u3059
+jsp.error.xml.operationNotSupported = {1} reader\u306f\u64cd\u4f5c \"{0}\" \u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093
+jsp.error.xml.invalidHighSurrogate = UTF-8\u30b7\u30fc\u30b1\u30f3\u30b9\u306e\u30cf\u30a4\u30b5\u30ed\u30b2\u30fc\u30c8\u30d3\u30c3\u30c8\u306f0x10\u3092\u8d8a\u3048\u3066\u306f\u3044\u3051\u307e\u305b\u3093\u304c\u30010x{0}\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f
+jsp.error.xml.invalidASCII = \u30d0\u30a4\u30c8 \"{0}\" \u306f7\u30d3\u30c3\u30c8ASCII\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+jsp.error.xml.spaceRequiredBeforeEncodingInXMLDecl = XML\u5ba3\u8a00\u306eencoding\u7591\u4f3c\u5c5e\u6027\u306e\u524d\u306b\u7a7a\u767d\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.spaceRequiredBeforeEncodingInTextDecl = \u30c6\u30ad\u30b9\u30c8\u5ba3\u8a00\u306eencoding\u7591\u4f3c\u5c5e\u6027\u306e\u524d\u306b\u7a7a\u767d\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.spaceRequiredBeforeVersionInTextDecl = \u30c6\u30ad\u30b9\u30c8\u5ba3\u8a00\u306eversion\u7591\u4f3c\u5c5e\u6027\u306e\u524d\u306b\u7a7a\u767d\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.spaceRequiredBeforeVersionInXMLDecl = XML\u5ba3\u8a00\u306eversion\u7591\u4f3c\u5c5e\u6027\u306e\u524d\u306b\u7a7a\u767d\u304c\u5fc5\u8981\u3067\u3059
+jsp.error.xml.eqRequiredInXMLDecl = XML\u5ba3\u8a00\u4e2d\u3067\"{0}\"\u306e\u6b21\u306b'' = '' \u6587\u5b57\u304c\u7d9a\u304b\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.xml.eqRequiredInTextDecl = \u30c6\u30ad\u30b9\u30c8\u5ba3\u8a00\u4e2d\u3067\"{0}\"\u306e\u6b21\u306b'' = ''\u6587\u5b57\u304c\u7d9a\u304b\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.xml.quoteRequiredInTextDecl = \u30c6\u30ad\u30b9\u30c8\u5ba3\u8a00\u4e2d\u306e\"{0}\"\u306b\u7d9a\u304f\u5024\u306f\u30af\u30aa\u30fc\u30c8\u3067\u56f2\u307e\u308c\u305f\u6587\u5b57\u5217\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.xml.quoteRequiredInXMLDecl = XML\u5ba3\u8a00\u4e2d\u306e\"{0}\"\u306b\u7d9a\u304f\u5024\u306f\u30af\u30aa\u30fc\u30c8\u3067\u56f2\u307e\u308c\u305f\u6587\u5b57\u5217\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.xml.invalidCharInTextDecl = \u30c6\u30ad\u30b9\u30c8\u5ba3\u8a00\u306e\u4e2d\u306b\u7121\u52b9\u306aXML\u6587\u5b57 (Unicode: 0x{0}) \u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f
+jsp.error.xml.invalidCharInXMLDecl = XML\u5ba3\u8a00\u306e\u4e2d\u306b\u7121\u52b9\u306aXML\u6587\u5b57 (Unicode: 0x{0}) \u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f
+jsp.error.xml.closeQuoteMissingInTextDecl = \u30c6\u30ad\u30b9\u30c8\u5ba3\u8a00\u4e2d\u306e\"{0}\"\u306b\u7d9a\u304f\u5024\u306e\u4e2d\u306e\u6700\u5f8c\u306e\u30af\u30aa\u30fc\u30c8\u304c\u3042\u308a\u307e\u305b\u3093
+jsp.error.xml.closeQuoteMissingInXMLDecl = XML\u5ba3\u8a00\u4e2d\u306e\"{0}\"\u306b\u7d9a\u304f\u5024\u306e\u4e2d\u306e\u6700\u5f8c\u306e\u30af\u30aa\u30fc\u30c8\u304c\u3042\u308a\u307e\u305b\u3093
+jsp.error.xml.invalidHighSurrogate = UTF-8\u30b7\u30fc\u30b1\u30f3\u30b9\u306e\u30cf\u30a4\u30b5\u30ed\u30b2\u30fc\u30c8\u30d3\u30c3\u30c8\u306f0x10\u3092\u8d8a\u3048\u3066\u306f\u3044\u3051\u307e\u305b\u3093\u304c\u30010x{0}\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f
+jsp.error.multiple.jsp = \u8907\u6570\u306e\u4ed5\u69d8\u3092\u6e80\u305f\u3059\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+jsp.error.jspoutput.conflict=&lt;jsp:output&gt;: \"{0}\"\u306b\u7570\u306a\u308b\u5024\u3092\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {1}, \u65b0: {2})
+jsp.error.jspoutput.doctypenamesystem=&lt;jsp:output&gt;: 'doctype-root-element' \u53ca\u3073 'doctype-system' \u5c5e\u6027\u306f\u540c\u6642\u306b\u6307\u5b9a\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.jspoutput.doctypepulicsystem=&lt;jsp:output&gt;: 'doctype-public'\u5c5e\u6027\u3092\u6307\u5b9a\u3059\u308b\u5834\u5408\u306f\u3001'doctype-system' \u5c5e\u6027\u3082\u6307\u5b9a\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.jspoutput.nonemptybody=&lt;jsp:output&gt; \u30dc\u30c7\u30a3\u3092\u6301\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+jsp.error.jspoutput.invalidUse=&lt;jsp:output&gt; \u6a19\u6e96\u69cb\u6587\u306e\u4e2d\u3067\u4f7f\u7528\u3057\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+jsp.error.attributes.not.allowed = {0} \u306f\u5c5e\u6027\u3092\u6301\u3064\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+jsp.error.tagfile.badSuffix=\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 {0} \u306e\u4e2d\u306b\".tag\" \u62e1\u5f35\u5b50\u304c\u3042\u308a\u307e\u305b\u3093
+jsp.error.tagfile.illegalPath=\u4e0d\u6b63\u306a\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3067\u3059: {0}\u3001\u3053\u308c\u306f\"/WEB-INF/tags\"\u53c8\u306f\"/META-INF/tags\"\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.plugin.wrongRootElement={0} \u306e\u4e2d\u306e\u30eb\u30fc\u30c8\u8981\u7d20\u306e\u540d\u524d\u306f {1} \u3067\u306f\u3042\u308a\u307e\u305b\u3093
+jsp.error.attribute.invalidPrefix=\u5c5e\u6027\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9 {0} \u306f\u3069\u306e\u53d6\u308a\u8fbc\u307e\u308c\u305f\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea\u306b\u3082\u5bfe\u5fdc\u3057\u307e\u305b\u3093
+jsp.error.nested.jspattribute=jsp:attribute\u6a19\u6e96\u30a2\u30af\u30b7\u30e7\u30f3\u306f\u5225\u306ejsp:attribute\u6a19\u6e96\u30a2\u30af\u30b7\u30e7\u30f3\u306e\u7bc4\u56f2\u5185\u3067\u30cd\u30b9\u30c8\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+jsp.error.nested.jspbody=jsp:body\u6a19\u6e96\u30a2\u30af\u30b7\u30e7\u30f3\u306f\u5225\u306ejsp:body\u53c8\u306fjsp:attribute\u6a19\u6e96\u30a2\u30af\u30b7\u30e7\u30f3\u306e\u7bc4\u56f2\u5185\u3067\u30cd\u30b9\u30c8\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+jsp.error.variable.either.name=name-given\u53c8\u306fname-from-attribute\u5c5e\u6027\u306e\u3069\u3061\u3089\u304b\u3092variable\u6307\u793a\u5b50\u306e\u4e2d\u3067\u6307\u5b9a\u3055\u308c\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.variable.both.name=variable\u6307\u793a\u5b50\u4e2d\u3067name-given\u3068name-from-attribute\u5c5e\u6027\u306e\u4e21\u65b9\u3092\u6307\u5b9a\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+jsp.error.variable.alias=name-from-attribute\u304a\u3088\u3073alias\u5c5e\u6027\u306e\u4e21\u65b9\u3092variable\u6307\u793a\u5b50\u4e2d\u306b\u6307\u5b9a\u3059\u308b\u3001\u53c8\u306f\u3069\u3061\u3089\u3082\u6307\u5b9a\u3057\u306a\u3044\u3053\u3068\u304c\u3067\u304d\u307e\u3059
+jsp.error.attribute.null_name=\u7a7a\u306e\u5c5e\u6027\u540d\u3067\u3059
+jsp.error.jsptext.badcontent=\'&lt;\'\u304c&lt;jsp:text&gt;\u306e\u30dc\u30c7\u30a3\u306e\u4e2d\u306b\u73fe\u308c\u308b\u6642\u306f\u3001CDATA\u306e\u4e2d\u306b\u96a0\u853d\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.jsproot.version.invalid=\u7121\u52b9\u306a\u30d0\u30fc\u30b8\u30e7\u30f3\u756a\u53f7\u3067\u3059: \"{0}\"\u3001\"1.2\" \u53c8\u306f \"2.0\"\u3000\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.noFunctionPrefix=\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u540d\u524d\u7a7a\u9593\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u6642\u306b\u306f\u3001\u95a2\u6570 {0} \u306f\u30d7\u30ea\u30d5\u30a3\u30af\u30b9\u4ed8\u304d\u3067\u4f7f\u7528\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+jsp.error.noFunction=\u95a2\u6570 {0} \u3092\u6307\u5b9a\u3055\u308c\u305f\u30d7\u30ea\u30d5\u30a3\u30af\u30b9\u3067\u914d\u7f6e\u3067\u304d\u307e\u305b\u3093
+jsp.error.noFunctionMethod=\u95a2\u6570 \"{1}\" \u306e\u30e1\u30bd\u30c3\u30c9 \"{0}\" \u304c \"{2}\" \u4e2d\u3067\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+jsp.error.function.classnotfound=TLD\u306e\u4e2d\u3067\u95a2\u6570 {1} \u306b\u6307\u5b9a\u3055\u308c\u3066\u3044\u308b\u30af\u30e9\u30b9 {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093: {2}
+jsp.error.signature.classnotfound=TLD\u306e\u4e2d\u306e\u30e1\u30bd\u30c3\u30c9\u30b7\u30b0\u30cd\u30c1\u30e3\u3067\u95a2\u6570 {1} \u306b\u6307\u5b9a\u3055\u308c\u3066\u3044\u308b\u30af\u30e9\u30b9 {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002 {2}
+jsp.error.text.has_subelement=&lt;jsp:text&gt; \u306f\u526f\u8981\u7d20\u3092\u6301\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+jsp.error.data.file.read=\u30d5\u30a1\u30a4\u30eb \"{0}\" \u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+jsp.error.prefix.refined=\u30d7\u30ea\u30d5\u30a3\u30c3\u30af\u30b9 {0} \u304c\u73fe\u5728\u306e\u30b9\u30b3\u30fc\u30d7\u4e2d\u3067\u65e2\u306b {2} \u3068\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u308b\u306e\u3067 {1} \u306b\u518d\u5b9a\u7fa9\u3057\u307e\u3057\u305f
+jsp.error.nested_jsproot=\u5165\u308c\u5b50\u306b\u306a\u3063\u305f &lt;jsp:root&gt; \u3067\u3059
+jsp.error.unbalanced.endtag=\u7d42\u4e86\u30bf\u30b0 \"&lt;/{0}\" \u306e\u5bfe\u5fdc\u304c\u53d6\u308c\u3066\u3044\u307e\u305b\u3093
+jsp.error.invalid.bean=useBean\u306e\u30af\u30e9\u30b9\u5c5e\u6027 {0} \u306e\u5024\u304c\u7121\u52b9\u3067\u3059
+jsp.error.prefix.use_before_dcl=\u3053\u306e\u30bf\u30b0\u6307\u793a\u5b50\u3067\u6307\u5b9a\u3055\u308c\u3066\u3044\u308b\u30d7\u30ea\u30d5\u30a3\u30c3\u30af\u30b9 {0} \u306f\u3001\u3059\u3067\u306b\u30d5\u30a1\u30a4\u30eb {1} \u306e {2} \u884c\u76ee\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u3067\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/BodyContentImpl.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/BodyContentImpl.java
new file mode 100644
index 0000000..09c5b64
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/BodyContentImpl.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.BodyContent;
+
+import org.apache.jasper.Constants;
+
+/**
+ * Write text to a character-output stream, buffering characters so as
+ * to provide for the efficient writing of single characters, arrays,
+ * and strings. 
+ *
+ * Provide support for discarding for the output that has been buffered. 
+ *
+ * @author Rajiv Mordani
+ * @author Jan Luehe
+ */
+public class BodyContentImpl extends BodyContent {
+    
+    private static final String LINE_SEPARATOR = 
+        System.getProperty("line.separator");
+    
+    private char[] cb;
+    private int nextChar;
+    private boolean closed;
+    
+    // Enclosed writer to which any output is written
+    private Writer writer;
+    
+    // See comment in setWriter()
+    private int bufferSizeSave;
+    
+    /**
+     * Constructor.
+     */
+    public BodyContentImpl(JspWriter enclosingWriter) {
+        super(enclosingWriter);
+        bufferSize = Constants.DEFAULT_TAG_BUFFER_SIZE;
+        cb = new char[bufferSize];
+        nextChar = 0;
+        closed = false;
+    }
+    
+    /**
+     * Write a single character.
+     */
+    public void write(int c) throws IOException {
+        if (writer != null) {
+            writer.write(c);
+        } else {
+            ensureOpen();
+            if (nextChar >= bufferSize) {
+                reAllocBuff (1);
+            }
+            cb[nextChar++] = (char) c;
+        }
+    }
+    
+    /**
+     * Write a portion of an array of characters.
+     *
+     * <p> Ordinarily this method stores characters from the given array into
+     * this stream's buffer, flushing the buffer to the underlying stream as
+     * needed.  If the requested length is at least as large as the buffer,
+     * however, then this method will flush the buffer and write the characters
+     * directly to the underlying stream.  Thus redundant
+     * <code>DiscardableBufferedWriter</code>s will not copy data
+     * unnecessarily.
+     *
+     * @param cbuf A character array
+     * @param off Offset from which to start reading characters
+     * @param len Number of characters to write
+     */
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        if (writer != null) {
+            writer.write(cbuf, off, len);
+        } else {
+            ensureOpen();
+            
+            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
+                    ((off + len) > cbuf.length) || ((off + len) < 0)) {
+                throw new IndexOutOfBoundsException();
+            } else if (len == 0) {
+                return;
+            } 
+            
+            if (len >= bufferSize - nextChar)
+                reAllocBuff (len);
+            
+            System.arraycopy(cbuf, off, cb, nextChar, len);
+            nextChar+=len;
+        }
+    }
+    
+    /**
+     * Write an array of characters.  This method cannot be inherited from the
+     * Writer class because it must suppress I/O exceptions.
+     */
+    public void write(char[] buf) throws IOException {
+        if (writer != null) {
+            writer.write(buf);
+        } else {
+            write(buf, 0, buf.length);
+        }
+    }
+    
+    /**
+     * Write a portion of a String.
+     *
+     * @param s String to be written
+     * @param off Offset from which to start reading characters
+     * @param len Number of characters to be written
+     */
+    public void write(String s, int off, int len) throws IOException {
+        if (writer != null) {
+            writer.write(s, off, len);
+        } else {
+            ensureOpen();
+            if (len >= bufferSize - nextChar)
+                reAllocBuff(len);
+            
+            s.getChars(off, off + len, cb, nextChar);
+            nextChar += len;
+        }
+    }
+    
+    /**
+     * Write a string.  This method cannot be inherited from the Writer class
+     * because it must suppress I/O exceptions.
+     */
+    public void write(String s) throws IOException {
+        if (writer != null) {
+            writer.write(s);
+        } else {
+            write(s, 0, s.length());
+        }
+    }
+    
+    /**
+     * Write a line separator.  The line separator string is defined by the
+     * system property <tt>line.separator</tt>, and is not necessarily a single
+     * newline ('\n') character.
+     *
+     * @throws IOException If an I/O error occurs
+     */
+    public void newLine() throws IOException {
+        if (writer != null) {
+            writer.write(LINE_SEPARATOR);
+        } else {
+            write(LINE_SEPARATOR);
+        }
+    }
+    
+    /**
+     * Print a boolean value.  The string produced by <code>{@link
+     * java.lang.String#valueOf(boolean)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param b The <code>boolean</code> to be printed
+     * @throws IOException
+     */
+    public void print(boolean b) throws IOException {
+        if (writer != null) {
+            writer.write(b ? "true" : "false");
+        } else {
+            write(b ? "true" : "false");
+        }
+    }
+    
+    /**
+     * Print a character.  The character is translated into one or more bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param c The <code>char</code> to be printed
+     * @throws IOException
+     */
+    public void print(char c) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(c));
+        } else {
+            write(String.valueOf(c));
+        }
+    }
+    
+    /**
+     * Print an integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(int)}</code> is translated into bytes according
+     * to the platform's default character encoding, and these bytes are
+     * written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param i The <code>int</code> to be printed
+     * @throws IOException
+     */
+    public void print(int i) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(i));
+        } else {
+            write(String.valueOf(i));
+        }
+    }
+    
+    /**
+     * Print a long integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(long)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param l The <code>long</code> to be printed
+     * @throws IOException
+     */
+    public void print(long l) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(l));
+        } else {
+            write(String.valueOf(l));
+        }
+    }
+    
+    /**
+     * Print a floating-point number.  The string produced by <code>{@link
+     * java.lang.String#valueOf(float)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param f The <code>float</code> to be printed
+     * @throws IOException
+     */
+    public void print(float f) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(f));
+        } else {
+            write(String.valueOf(f));
+        }
+    }
+    
+    /**
+     * Print a double-precision floating-point number.  The string produced by
+     * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
+     * bytes according to the platform's default character encoding, and these
+     * bytes are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param d The <code>double</code> to be printed
+     * @throws IOException
+     */
+    public void print(double d) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(d));
+        } else {
+            write(String.valueOf(d));
+        }
+    }
+    
+    /**
+     * Print an array of characters.  The characters are converted into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param s The array of chars to be printed
+     *
+     * @throws NullPointerException If <code>s</code> is <code>null</code>
+     * @throws IOException
+     */
+    public void print(char[] s) throws IOException {
+        if (writer != null) {
+            writer.write(s);
+        } else {
+            write(s);
+        }
+    }
+    
+    /**
+     * Print a string.  If the argument is <code>null</code> then the string
+     * <code>"null"</code> is printed.  Otherwise, the string's characters are
+     * converted into bytes according to the platform's default character
+     * encoding, and these bytes are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param s The <code>String</code> to be printed
+     * @throws IOException
+     */
+    public void print(String s) throws IOException {
+        if (s == null) s = "null";
+        if (writer != null) {
+            writer.write(s);
+        } else {
+            write(s);
+        }
+    }
+    
+    /**
+     * Print an object.  The string produced by the <code>{@link
+     * java.lang.String#valueOf(Object)}</code> method is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param obj The <code>Object</code> to be printed
+     * @throws IOException
+     */
+    public void print(Object obj) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(obj));
+        } else {
+            write(String.valueOf(obj));
+        }
+    }
+    
+    /**
+     * Terminate the current line by writing the line separator string.  The
+     * line separator string is defined by the system property
+     * <code>line.separator</code>, and is not necessarily a single newline
+     * character (<code>'\n'</code>).
+     *
+     * @throws IOException
+     */
+    public void println() throws IOException {
+        newLine();
+    }
+    
+    /**
+     * Print a boolean value and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(boolean)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(boolean x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a character and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(char)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(char x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an integer and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(int)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(int x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a long integer and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(long)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(long x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a floating-point number and then terminate the line.  This method
+     * behaves as though it invokes <code>{@link #print(float)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(float x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a double-precision floating-point number and then terminate the
+     * line.  This method behaves as though it invokes <code>{@link
+     * #print(double)}</code> and then <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(double x) throws IOException{
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an array of characters and then terminate the line.  This method
+     * behaves as though it invokes <code>{@link #print(char[])}</code> and
+     * then <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(char x[]) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a String and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(String)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(String x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an Object and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(Object)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(Object x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Clear the contents of the buffer. If the buffer has been already
+     * been flushed then the clear operation shall throw an IOException
+     * to signal the fact that some data has already been irrevocably 
+     * written to the client response stream.
+     *
+     * @throws IOException If an I/O error occurs
+     */
+    public void clear() throws IOException {
+        if (writer != null) {
+            throw new IOException();
+        } else {
+            nextChar = 0;
+        }
+    }
+    
+    /**
+     * Clears the current contents of the buffer. Unlike clear(), this
+     * mehtod will not throw an IOException if the buffer has already been
+     * flushed. It merely clears the current content of the buffer and
+     * returns.
+     *
+     * @throws IOException If an I/O error occurs
+     */
+    public void clearBuffer() throws IOException {
+        if (writer == null) {
+            this.clear();
+        }
+    }
+    
+    /**
+     * Close the stream, flushing it first.  Once a stream has been closed,
+     * further write() or flush() invocations will cause an IOException to be
+     * thrown.  Closing a previously-closed stream, however, has no effect.
+     *
+     * @throws IOException If an I/O error occurs
+     */
+    public void close() throws IOException {
+        if (writer != null) {
+            writer.close();
+        } else {
+            closed = true;
+        }
+    }
+    
+    /**
+     * @return the number of bytes unused in the buffer
+     */
+    public int getRemaining() {
+        return (writer == null) ? bufferSize-nextChar : 0;
+    }
+    
+    /**
+     * Return the value of this BodyJspWriter as a Reader.
+     * Note: this is after evaluation!!  There are no scriptlets,
+     * etc in this stream.
+     *
+     * @return the value of this BodyJspWriter as a Reader
+     */
+    public Reader getReader() {
+        return (writer == null) ? new CharArrayReader (cb, 0, nextChar) : null;
+    }
+    
+    /**
+     * Return the value of the BodyJspWriter as a String.
+     * Note: this is after evaluation!!  There are no scriptlets,
+     * etc in this stream.
+     *
+     * @return the value of the BodyJspWriter as a String
+     */
+    public String getString() {
+        return (writer == null) ? new String(cb, 0, nextChar) : null;
+    }
+    
+    /**
+     * Write the contents of this BodyJspWriter into a Writer.
+     * Subclasses are likely to do interesting things with the
+     * implementation so some things are extra efficient.
+     *
+     * @param out The writer into which to place the contents of this body
+     * evaluation
+     */
+    public void writeOut(Writer out) throws IOException {
+        if (writer == null) {
+            out.write(cb, 0, nextChar);
+            // Flush not called as the writer passed could be a BodyContent and
+            // it doesn't allow to flush.
+        }
+    }
+    
+    /**
+     * Sets the writer to which all output is written.
+     */
+    void setWriter(Writer writer) {
+        this.writer = writer;
+        closed = false;
+        if (writer != null) {
+            // According to the spec, the JspWriter returned by 
+            // JspContext.pushBody(java.io.Writer writer) must behave as
+            // though it were unbuffered. This means that its getBufferSize()
+            // must always return 0. The implementation of
+            // JspWriter.getBufferSize() returns the value of JspWriter's
+            // 'bufferSize' field, which is inherited by this class. 
+            // Therefore, we simply save the current 'bufferSize' (so we can 
+            // later restore it should this BodyContentImpl ever be reused by
+            // a call to PageContext.pushBody()) before setting it to 0.
+            if (bufferSize != 0) {
+                bufferSizeSave = bufferSize;
+                bufferSize = 0;
+            }
+        } else {
+            bufferSize = bufferSizeSave;
+            clearBody();
+        }
+    }
+    
+    private void ensureOpen() throws IOException {
+        if (closed) throw new IOException("Stream closed");
+    }
+    
+    /**
+     * Reallocates buffer since the spec requires it to be unbounded.
+     */
+    private void reAllocBuff(int len) {
+        
+        if (bufferSize + len <= cb.length) {
+            bufferSize = cb.length;
+            return;
+        }
+        
+        if (len < cb.length) {
+            len = cb.length;
+        }
+        
+        bufferSize = cb.length + len;
+        char[] tmp = new char[bufferSize];
+        
+        System.arraycopy(cb, 0, tmp, 0, cb.length);
+        cb = tmp;
+        tmp = null;
+        
+    }
+    
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/HttpJspBase.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/HttpJspBase.java
new file mode 100644
index 0000000..71efd1a
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/HttpJspBase.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.HttpJspPage;
+import javax.servlet.jsp.JspFactory;
+
+import org.apache.jasper.compiler.Localizer;
+
+/**
+ * This is the super class of all JSP-generated servlets.
+ *
+ * @author Anil K. Vijendran
+ */
+public abstract class HttpJspBase 
+    extends HttpServlet 
+    implements HttpJspPage 
+        
+    
+{
+    
+    static {
+        if( JspFactory.getDefaultFactory() == null ) {
+            JspFactoryImpl factory = new JspFactoryImpl();
+            if( System.getSecurityManager() != null ) {
+                String basePackage = "org.apache.jasper.";
+                try {
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.JspFactoryImpl$PrivilegedGetPageContext");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.JspFactoryImpl$PrivilegedReleasePageContext");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.JspRuntimeLibrary");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.JspRuntimeLibrary$PrivilegedIntrospectHelper");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.ServletResponseWrapperInclude");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "servlet.JspServletWrapper");
+                } catch (ClassNotFoundException ex) {
+                    org.apache.commons.logging.LogFactory.getLog( HttpJspBase.class )
+                        .error("Jasper JspRuntimeContext preload of class failed: " +
+                                       ex.getMessage(), ex);
+                }
+            }
+            JspFactory.setDefaultFactory(factory);
+        }
+    }
+
+    protected HttpJspBase() {
+    }
+
+    public final void init(ServletConfig config) 
+	throws ServletException 
+    {
+        super.init(config);
+	jspInit();
+        _jspInit();
+    }
+    
+    public String getServletInfo() {
+	return Localizer.getMessage("jsp.engine.info");
+    }
+
+    public final void destroy() {
+	jspDestroy();
+	_jspDestroy();
+    }
+
+    /**
+     * Entry point into service.
+     */
+    public final void service(HttpServletRequest request, HttpServletResponse response) 
+	throws ServletException, IOException 
+    {
+        _jspService(request, response);
+    }
+    
+    public void jspInit() {
+    }
+
+    public void _jspInit() {
+    }
+
+    public void jspDestroy() {
+    }
+
+    protected void _jspDestroy() {
+    }
+
+    public abstract void _jspService(HttpServletRequest request, 
+				     HttpServletResponse response) 
+	throws ServletException, IOException;
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/JspContextWrapper.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspContextWrapper.java
new file mode 100644
index 0000000..3ab533e
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspContextWrapper.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.el.ELException;
+import javax.servlet.jsp.el.ExpressionEvaluator;
+import javax.servlet.jsp.el.VariableResolver;
+import javax.servlet.jsp.tagext.BodyContent;
+import javax.servlet.jsp.tagext.VariableInfo;
+
+import org.apache.commons.el.VariableResolverImpl;
+import org.apache.jasper.compiler.Localizer;
+
+/**
+ * Implementation of a JSP Context Wrapper.
+ *
+ * The JSP Context Wrapper is a JspContext created and maintained by a tag
+ * handler implementation. It wraps the Invoking JSP Context, that is, the
+ * JspContext instance passed to the tag handler by the invoking page via
+ * setJspContext().
+ *
+ * @author Kin-man Chung
+ * @author Jan Luehe
+ */
+public class JspContextWrapper
+            extends PageContext implements VariableResolver {
+
+    // Invoking JSP context
+    private PageContext invokingJspCtxt;
+
+    private transient Hashtable	pageAttributes;
+
+    // ArrayList of NESTED scripting variables
+    private ArrayList nestedVars;
+
+    // ArrayList of AT_BEGIN scripting variables
+    private ArrayList atBeginVars;
+
+    // ArrayList of AT_END scripting variables
+    private ArrayList atEndVars;
+
+    private Map aliases;
+
+    private Hashtable originalNestedVars;
+
+    /**
+     * The variable resolver, for evaluating EL expressions.
+     */
+    private VariableResolverImpl variableResolver
+        = new VariableResolverImpl(this);
+
+    public JspContextWrapper(JspContext jspContext, ArrayList nestedVars,
+			     ArrayList atBeginVars, ArrayList atEndVars,
+			     Map aliases) {
+        this.invokingJspCtxt = (PageContext) jspContext;
+	this.nestedVars = nestedVars;
+	this.atBeginVars = atBeginVars;
+	this.atEndVars = atEndVars;
+	this.pageAttributes = new Hashtable(16);
+	this.aliases = aliases;
+
+	if (nestedVars != null) {
+	    this.originalNestedVars = new Hashtable(nestedVars.size());
+	}
+	syncBeginTagFile();
+    }
+
+    public void initialize(Servlet servlet, ServletRequest request,
+                           ServletResponse response, String errorPageURL,
+                           boolean needsSession, int bufferSize,
+                           boolean autoFlush)
+        throws IOException, IllegalStateException, IllegalArgumentException
+    {
+    }
+    
+    public Object getAttribute(String name) {
+
+	if (name == null) {
+	    throw new NullPointerException(
+	            Localizer.getMessage("jsp.error.attribute.null_name"));
+	}
+
+	return pageAttributes.get(name);
+    }
+
+    public Object getAttribute(String name, int scope) {
+
+	if (name == null) {
+	    throw new NullPointerException(
+	            Localizer.getMessage("jsp.error.attribute.null_name"));
+	}
+
+	if (scope == PAGE_SCOPE) {
+	    return pageAttributes.get(name);
+	}
+
+	return invokingJspCtxt.getAttribute(name, scope);
+    }
+
+    public void setAttribute(String name, Object value) {
+
+	if (name == null) {
+	    throw new NullPointerException(
+	            Localizer.getMessage("jsp.error.attribute.null_name"));
+	}
+
+	if (value != null) {
+	    pageAttributes.put(name, value);
+	} else {
+	    removeAttribute(name, PAGE_SCOPE);
+	}
+    }
+
+    public void setAttribute(String name, Object value, int scope) {
+
+	if (name == null) {
+	    throw new NullPointerException(
+	            Localizer.getMessage("jsp.error.attribute.null_name"));
+	}
+
+	if (scope == PAGE_SCOPE) {
+	    if (value != null) {
+		pageAttributes.put(name, value);
+	    } else {
+		removeAttribute(name, PAGE_SCOPE);
+	    }
+	} else {
+	    invokingJspCtxt.setAttribute(name, value, scope);
+	}
+    }
+
+    public Object findAttribute(String name) {
+
+	if (name == null) {
+	    throw new NullPointerException(
+	            Localizer.getMessage("jsp.error.attribute.null_name"));
+	}
+
+        Object o = pageAttributes.get(name);
+        if (o == null) {
+	    o = invokingJspCtxt.getAttribute(name, REQUEST_SCOPE);
+	    if (o == null) {
+		if (getSession() != null) {
+		    o = invokingJspCtxt.getAttribute(name, SESSION_SCOPE);
+		}
+		if (o == null) {
+		    o = invokingJspCtxt.getAttribute(name, APPLICATION_SCOPE);
+		} 
+	    }
+	}
+
+	return o;
+    }
+
+    public void removeAttribute(String name) {
+
+	if (name == null) {
+	    throw new NullPointerException(
+	            Localizer.getMessage("jsp.error.attribute.null_name"));
+	}
+
+	pageAttributes.remove(name);
+	invokingJspCtxt.removeAttribute(name, REQUEST_SCOPE);
+	if (getSession() != null) {
+	    invokingJspCtxt.removeAttribute(name, SESSION_SCOPE);
+	}
+	invokingJspCtxt.removeAttribute(name, APPLICATION_SCOPE);
+    }
+
+    public void removeAttribute(String name, int scope) {
+
+	if (name == null) {
+	    throw new NullPointerException(
+	            Localizer.getMessage("jsp.error.attribute.null_name"));
+	}
+
+	if (scope == PAGE_SCOPE){
+	    pageAttributes.remove(name);
+	} else {
+	    invokingJspCtxt.removeAttribute(name, scope);
+	}
+    }
+
+    public int getAttributesScope(String name) {
+
+	if (name == null) {
+	    throw new NullPointerException(
+	            Localizer.getMessage("jsp.error.attribute.null_name"));
+	}
+
+	if (pageAttributes.get(name) != null) {
+	    return PAGE_SCOPE;
+	} else {
+	    return invokingJspCtxt.getAttributesScope(name);
+	}
+    }
+
+    public Enumeration getAttributeNamesInScope(int scope) {
+        if (scope == PAGE_SCOPE) {
+            return pageAttributes.keys();
+	}
+
+	return invokingJspCtxt.getAttributeNamesInScope(scope);
+    }
+
+    public void release() {
+	invokingJspCtxt.release();
+    }
+
+    public JspWriter getOut() {
+	return invokingJspCtxt.getOut();
+    }
+
+    public HttpSession getSession() {
+	return invokingJspCtxt.getSession();
+    }
+
+    public Object getPage() {
+	return invokingJspCtxt.getPage();
+    }
+
+    public ServletRequest getRequest() {
+	return invokingJspCtxt.getRequest();
+    }
+
+    public ServletResponse getResponse() {
+	return invokingJspCtxt.getResponse();
+    }
+
+    public Exception getException() {
+	return invokingJspCtxt.getException();
+    }
+
+    public ServletConfig getServletConfig() {
+	return invokingJspCtxt.getServletConfig();
+    }
+
+    public ServletContext getServletContext() {
+	return invokingJspCtxt.getServletContext();
+    }
+
+    public void forward(String relativeUrlPath)
+        throws ServletException, IOException
+    {
+	invokingJspCtxt.forward(relativeUrlPath);
+    }
+
+    public void include(String relativeUrlPath)
+	throws ServletException, IOException
+    {
+	invokingJspCtxt.include(relativeUrlPath);
+    }
+
+    public void include(String relativeUrlPath, boolean flush) 
+	    throws ServletException, IOException {
+	include(relativeUrlPath, false); // XXX
+    }
+
+    public VariableResolver getVariableResolver() {
+	return this;
+    }
+
+    public BodyContent pushBody() {
+	return invokingJspCtxt.pushBody();
+    }
+
+    public JspWriter pushBody(Writer writer) {
+	return invokingJspCtxt.pushBody(writer);
+    }
+
+    public JspWriter popBody() {
+        return invokingJspCtxt.popBody();
+    }
+
+    public ExpressionEvaluator getExpressionEvaluator() {
+	return invokingJspCtxt.getExpressionEvaluator();
+    }
+
+    public void handlePageException(Exception ex)
+        throws IOException, ServletException 
+    {
+	// Should never be called since handleException() called with a
+	// Throwable in the generated servlet.
+	handlePageException((Throwable) ex);
+    }
+
+    public void handlePageException(Throwable t)
+        throws IOException, ServletException 
+    {
+	invokingJspCtxt.handlePageException(t);
+    }
+
+    /**
+     * VariableResolver interface
+     */
+    public Object resolveVariable( String pName ) throws ELException
+    {
+        return variableResolver.resolveVariable(pName);
+    }
+
+    /**
+     * Synchronize variables at begin of tag file
+     */
+    public void syncBeginTagFile() {
+	saveNestedVariables();
+    }
+
+    /**
+     * Synchronize variables before fragment invokation
+     */
+    public void syncBeforeInvoke() {
+	copyTagToPageScope(VariableInfo.NESTED);
+	copyTagToPageScope(VariableInfo.AT_BEGIN);
+    }
+
+    /**
+     * Synchronize variables at end of tag file
+     */
+    public void syncEndTagFile() {
+	copyTagToPageScope(VariableInfo.AT_BEGIN);
+	copyTagToPageScope(VariableInfo.AT_END);
+	restoreNestedVariables();
+    }
+
+    /**
+     * Copies the variables of the given scope from the virtual page scope of
+     * this JSP context wrapper to the page scope of the invoking JSP context.
+     *
+     * @param scope variable scope (one of NESTED, AT_BEGIN, or AT_END)
+     */
+    private void copyTagToPageScope(int scope) {
+	Iterator iter = null;
+
+	switch (scope) {
+	case VariableInfo.NESTED:
+	    if (nestedVars != null) {
+		iter = nestedVars.iterator();
+	    }
+	    break;
+	case VariableInfo.AT_BEGIN:
+	    if (atBeginVars != null) {
+		iter = atBeginVars.iterator();
+	    }
+	    break;
+	case VariableInfo.AT_END:
+	    if (atEndVars != null) {
+		iter = atEndVars.iterator();
+	    }
+	    break;
+	}
+
+	while ((iter != null) && iter.hasNext()) {
+	    String varName = (String) iter.next();
+	    Object obj = getAttribute(varName);
+	    varName = findAlias(varName);
+	    if (obj != null) {
+		invokingJspCtxt.setAttribute(varName, obj);
+	    } else {
+		invokingJspCtxt.removeAttribute(varName, PAGE_SCOPE);
+	    }
+	}
+    }
+
+    /**
+     * Saves the values of any NESTED variables that are present in
+     * the invoking JSP context, so they can later be restored.
+     */
+    private void saveNestedVariables() {
+	if (nestedVars != null) {
+	    Iterator iter = nestedVars.iterator();
+	    while (iter.hasNext()) {
+		String varName = (String) iter.next();
+		varName = findAlias(varName);
+		Object obj = invokingJspCtxt.getAttribute(varName);
+		if (obj != null) {
+		    originalNestedVars.put(varName, obj);
+		}
+	    }
+	}
+    }
+
+    /**
+     * Restores the values of any NESTED variables in the invoking JSP
+     * context.
+     */
+    private void restoreNestedVariables() {
+	if (nestedVars != null) {
+	    Iterator iter = nestedVars.iterator();
+	    while (iter.hasNext()) {
+		String varName = (String) iter.next();
+		varName = findAlias(varName);
+		Object obj = originalNestedVars.get(varName);
+		if (obj != null) {
+		    invokingJspCtxt.setAttribute(varName, obj);
+		} else {
+		    invokingJspCtxt.removeAttribute(varName, PAGE_SCOPE);
+		}
+	    }
+	}
+    }
+
+    /**
+     * Checks to see if the given variable name is used as an alias, and if so,
+     * returns the variable name for which it is used as an alias.
+     *
+     * @param varName The variable name to check
+     * @return The variable name for which varName is used as an alias, or
+     * varName if it is not being used as an alias
+     */
+    private String findAlias(String varName) {
+
+	if (aliases == null)
+	    return varName;
+
+	String alias = (String) aliases.get(varName);
+	if (alias == null) {
+	    return varName;
+	}
+	return alias;
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/JspFactoryImpl.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspFactoryImpl.java
new file mode 100644
index 0000000..154cfdb
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspFactoryImpl.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.runtime;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.JspEngineInfo;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.jasper.util.SimplePool;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implementation of JspFactory.
+ *
+ * @author Anil K. Vijendran
+ */
+public class JspFactoryImpl extends JspFactory {
+
+    // Logger
+    private Log log = LogFactory.getLog(JspFactoryImpl.class);
+
+    private static final String SPEC_VERSION = "2.0";
+    private static final boolean USE_POOL = true;
+
+    private SimplePool pool = new SimplePool( 100 );
+    
+    public PageContext getPageContext(Servlet servlet,
+				      ServletRequest request,
+                                      ServletResponse response,
+                                      String errorPageURL,                    
+                                      boolean needsSession,
+				      int bufferSize,
+                                      boolean autoflush) {
+
+	if( System.getSecurityManager() != null ) {
+	    PrivilegedGetPageContext dp = new PrivilegedGetPageContext(
+		(JspFactoryImpl)this, servlet, request, response, errorPageURL,
+                needsSession, bufferSize, autoflush);
+	    return (PageContext)AccessController.doPrivileged(dp);
+	} else {
+	    return internalGetPageContext(servlet, request, response,
+					  errorPageURL, needsSession,
+					  bufferSize, autoflush);
+	}
+    }
+
+    public void releasePageContext(PageContext pc) {
+	if( pc == null )
+	    return;
+        if( System.getSecurityManager() != null ) {
+            PrivilegedReleasePageContext dp = new PrivilegedReleasePageContext(
+                (JspFactoryImpl)this,pc);
+            AccessController.doPrivileged(dp);
+        } else {
+            internalReleasePageContext(pc);
+	}
+    }
+
+    public JspEngineInfo getEngineInfo() {
+        return new JspEngineInfo() {
+		public String getSpecificationVersion() {
+		    return SPEC_VERSION;
+		}
+	    };
+    }
+
+    private PageContext internalGetPageContext(Servlet servlet,
+					       ServletRequest request,
+					       ServletResponse response, 
+					       String errorPageURL, 
+					       boolean needsSession,
+					       int bufferSize, 
+					       boolean autoflush) {
+        try {
+	    PageContext pc;
+	    if( USE_POOL ) {
+                pc = (PageContext) pool.get();
+		if( pc == null ) {
+		    pc= new PageContextImpl(this);
+		}
+	    } else {
+		pc = new PageContextImpl(this);
+	    }
+	    pc.initialize(servlet, request, response, errorPageURL, 
+                          needsSession, bufferSize, autoflush);
+            return pc;
+        } catch (Throwable ex) {
+            /* FIXME: need to do something reasonable here!! */
+            log.fatal("Exception initializing page context", ex);
+            return null;
+        }
+    }
+
+    private void internalReleasePageContext(PageContext pc) {
+        pc.release();
+	if (USE_POOL && (pc instanceof PageContextImpl)) {
+	    pool.put( pc );
+	}
+    }
+
+    private class PrivilegedGetPageContext implements PrivilegedAction {
+
+	private JspFactoryImpl factory;
+	private Servlet servlet;
+	private ServletRequest request;
+	private ServletResponse response;
+	private String errorPageURL;
+	private boolean needsSession;
+	private int bufferSize;
+	private boolean autoflush;
+
+	PrivilegedGetPageContext(JspFactoryImpl factory,
+				 Servlet servlet,
+				 ServletRequest request,
+				 ServletResponse response,
+				 String errorPageURL,
+				 boolean needsSession,
+				 int bufferSize,
+				 boolean autoflush) {
+	    this.factory = factory;
+	    this.servlet = servlet;
+	    this.request = request;
+	    this.response = response;
+	    this.errorPageURL = errorPageURL;
+	    this.needsSession = needsSession;
+	    this.bufferSize = bufferSize;
+	    this.autoflush = autoflush;
+	}
+ 
+	public Object run() {
+	    return factory.internalGetPageContext(servlet,
+						  request,
+						  response,
+						  errorPageURL,
+						  needsSession,
+						  bufferSize,
+						  autoflush);
+	}
+    }
+
+    private class PrivilegedReleasePageContext implements PrivilegedAction {
+
+        private JspFactoryImpl factory;
+	private PageContext pageContext;
+
+        PrivilegedReleasePageContext(JspFactoryImpl factory,
+				     PageContext pageContext) {
+            this.factory = factory;
+            this.pageContext = pageContext;
+        }
+
+        public Object run() {
+            factory.internalReleasePageContext(pageContext);
+	    return null;
+        }
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java
new file mode 100644
index 0000000..3e73f52
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.JspFragment;
+import javax.servlet.jsp.tagext.JspTag;
+
+/**
+ * Helper class from which all Jsp Fragment helper classes extend.
+ * This class allows for the emulation of numerous fragments within
+ * a single class, which in turn reduces the load on the class loader
+ * since there are potentially many JspFragments in a single page.
+ * <p>
+ * The class also provides various utility methods for JspFragment
+ * implementations.
+ *
+ * @author Mark Roth
+ */
+public abstract class JspFragmentHelper 
+    extends JspFragment 
+{
+    
+    protected int discriminator;
+    protected JspContext jspContext;
+    protected PageContext _jspx_page_context;
+    protected JspTag parentTag;
+
+    public JspFragmentHelper( int discriminator, JspContext jspContext, 
+        JspTag parentTag ) 
+    {
+        this.discriminator = discriminator;
+        this.jspContext = jspContext;
+        this._jspx_page_context = null;
+        if( jspContext instanceof PageContext ) {
+            _jspx_page_context = (PageContext)jspContext;
+        }
+        this.parentTag = parentTag;
+    }
+    
+    public JspContext getJspContext() {
+        return this.jspContext;
+    }
+    
+    public JspTag getParentTag() {
+        return this.parentTag;
+    }
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java
new file mode 100644
index 0000000..930d142
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java
@@ -0,0 +1,1045 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorManager;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Enumeration;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.BodyContent;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.compiler.Localizer;
+
+/**
+ * Bunch of util methods that are used by code generated for useBean,
+ * getProperty and setProperty.  
+ *
+ * The __begin, __end stuff is there so that the JSP engine can
+ * actually parse this file and inline them if people don't want
+ * runtime dependencies on this class. However, I'm not sure if that
+ * works so well right now. It got forgotten at some point. -akv
+ *
+ * @author Mandar Raje
+ * @author Shawn Bayern
+ */
+public class JspRuntimeLibrary {
+    
+    private static final String SERVLET_EXCEPTION
+	= "javax.servlet.error.exception";
+    private static final String JSP_EXCEPTION
+	= "javax.servlet.jsp.jspException";
+
+    protected static class PrivilegedIntrospectHelper
+	implements PrivilegedExceptionAction {
+
+	private Object bean;
+	private String prop;
+	private String value;
+	private ServletRequest request;
+	private String param;
+	private boolean ignoreMethodNF;
+
+        PrivilegedIntrospectHelper(Object bean, String prop,
+                                   String value, ServletRequest request,
+                                   String param, boolean ignoreMethodNF)
+        {
+	    this.bean = bean;
+	    this.prop = prop;
+	    this.value = value;
+            this.request = request;
+	    this.param = param;
+	    this.ignoreMethodNF = ignoreMethodNF;
+        }
+         
+        public Object run() throws JasperException {
+	    internalIntrospecthelper(
+                bean,prop,value,request,param,ignoreMethodNF);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value of the javax.servlet.error.exception request
+     * attribute value, if present, otherwise the value of the
+     * javax.servlet.jsp.jspException request attribute value.
+     *
+     * This method is called at the beginning of the generated servlet code
+     * for a JSP error page, when the "exception" implicit scripting language
+     * variable is initialized.
+     */
+    public static Throwable getThrowable(ServletRequest request) {
+	Throwable error = (Throwable) request.getAttribute(SERVLET_EXCEPTION);
+	if (error == null) {
+	    error = (Throwable) request.getAttribute(JSP_EXCEPTION);
+	    if (error != null) {
+		/*
+		 * The only place that sets JSP_EXCEPTION is
+		 * PageContextImpl.handlePageException(). It really should set
+		 * SERVLET_EXCEPTION, but that would interfere with the 
+		 * ErrorReportValve. Therefore, if JSP_EXCEPTION is set, we
+		 * need to set SERVLET_EXCEPTION.
+		 */
+		request.setAttribute(SERVLET_EXCEPTION, error);
+	    }
+	}
+
+	return error;
+    }
+
+    public static boolean coerceToBoolean(String s) {
+	if (s == null || s.length() == 0)
+	    return false;
+	else
+	    return Boolean.valueOf(s).booleanValue();
+    }
+
+    public static byte coerceToByte(String s) {
+	if (s == null || s.length() == 0)
+	    return (byte) 0;
+	else
+	    return Byte.valueOf(s).byteValue();
+    }
+
+    public static char coerceToChar(String s) {
+	if (s == null || s.length() == 0) {
+	    return (char) 0;
+	} else {
+	    // this trick avoids escaping issues
+	    return (char)(int) s.charAt(0);
+	}
+    }
+
+    public static double coerceToDouble(String s) {
+	if (s == null || s.length() == 0)
+	    return (double) 0;
+	else
+	    return Double.valueOf(s).doubleValue();
+    }
+
+    public static float coerceToFloat(String s) {
+	if (s == null || s.length() == 0)
+	    return (float) 0;
+	else
+	    return Float.valueOf(s).floatValue();
+    }
+
+    public static int coerceToInt(String s) {
+	if (s == null || s.length() == 0)
+	    return 0;
+	else
+	    return Integer.valueOf(s).intValue();
+    }
+
+    public static short coerceToShort(String s) {
+	if (s == null || s.length() == 0)
+	    return (short) 0;
+	else
+	    return Short.valueOf(s).shortValue();
+    }
+
+    public static long coerceToLong(String s) {
+	if (s == null || s.length() == 0)
+	    return (long) 0;
+	else
+	    return Long.valueOf(s).longValue();
+    }
+
+    public static Object coerce(String s, Class target) {
+
+	boolean isNullOrEmpty = (s == null || s.length() == 0);
+
+	if (target == Boolean.class) {
+	    if (isNullOrEmpty) {
+		s = "false";
+	    }
+	    return new Boolean(s);
+	} else if (target == Byte.class) {
+	    if (isNullOrEmpty)
+		return new Byte((byte) 0);
+	    else
+		return new Byte(s);
+	} else if (target == Character.class) {
+	    if (isNullOrEmpty)
+		return new Character((char) 0);
+	    else 
+		return new Character(s.charAt(0));
+	} else if (target == Double.class) {
+	    if (isNullOrEmpty)
+		return new Double(0);
+	    else
+		return new Double(s);
+	} else if (target == Float.class) {
+	    if (isNullOrEmpty)
+		return new Float(0);
+	    else
+		return new Float(s);
+	} else if (target == Integer.class) {
+	    if (isNullOrEmpty)
+		return new Integer(0);
+	    else
+		return new Integer(s);
+	} else if (target == Short.class) {
+	    if (isNullOrEmpty)
+		return new Short((short) 0);
+	    else
+		return new Short(s);
+	} else if (target == Long.class) {
+	    if (isNullOrEmpty)
+		return new Long(0);
+	    else
+		return new Long(s);
+	} else {
+	    return null;
+	}
+    }
+
+   // __begin convertMethod
+    public static Object convert(String propertyName, String s, Class t,
+				 Class propertyEditorClass) 
+       throws JasperException 
+    {
+        try {
+            if (s == null) {
+                if (t.equals(Boolean.class) || t.equals(Boolean.TYPE))
+                    s = "false";
+                else
+                    return null;
+            }
+	    if (propertyEditorClass != null) {
+		return getValueFromBeanInfoPropertyEditor(
+				    t, propertyName, s, propertyEditorClass);
+	    } else if ( t.equals(Boolean.class) || t.equals(Boolean.TYPE) ) {
+                if (s.equalsIgnoreCase("on") || s.equalsIgnoreCase("true"))
+                    s = "true";
+                else
+                    s = "false";
+                return new Boolean(s);
+            } else if ( t.equals(Byte.class) || t.equals(Byte.TYPE) ) {
+                return new Byte(s);
+            } else if (t.equals(Character.class) || t.equals(Character.TYPE)) {
+                return s.length() > 0 ? new Character(s.charAt(0)) : null;
+            } else if ( t.equals(Short.class) || t.equals(Short.TYPE) ) {
+                return new Short(s);
+            } else if ( t.equals(Integer.class) || t.equals(Integer.TYPE) ) {
+                return new Integer(s);
+            } else if ( t.equals(Float.class) || t.equals(Float.TYPE) ) {
+                return new Float(s);
+            } else if ( t.equals(Long.class) || t.equals(Long.TYPE) ) {
+                return new Long(s);
+            } else if ( t.equals(Double.class) || t.equals(Double.TYPE) ) {
+                return new Double(s);
+            } else if ( t.equals(String.class) ) {
+                return s;
+            } else if ( t.equals(java.io.File.class) ) {
+                return new java.io.File(s);
+            } else if (t.getName().equals("java.lang.Object")) {
+                return new Object[] {s};
+	    } else {
+		return getValueFromPropertyEditorManager(
+                                            t, propertyName, s);
+            }
+        } catch (Exception ex) {
+            throw new JasperException(ex);
+        }
+    }
+    // __end convertMethod
+
+    // __begin introspectMethod
+    public static void introspect(Object bean, ServletRequest request)
+                                  throws JasperException
+    {
+	Enumeration e = request.getParameterNames();
+	while ( e.hasMoreElements() ) {
+	    String name  = (String) e.nextElement();
+	    String value = request.getParameter(name);
+	    introspecthelper(bean, name, value, request, name, true);
+	}
+    }
+    // __end introspectMethod
+    
+    // __begin introspecthelperMethod
+    public static void introspecthelper(Object bean, String prop,
+                                        String value, ServletRequest request,
+                                        String param, boolean ignoreMethodNF)
+                                        throws JasperException
+    {
+        if( System.getSecurityManager() != null ) {
+            try {
+                PrivilegedIntrospectHelper dp =
+		    new PrivilegedIntrospectHelper(
+			bean,prop,value,request,param,ignoreMethodNF);
+                AccessController.doPrivileged(dp);
+            } catch( PrivilegedActionException pe) {
+                Exception e = pe.getException();
+                throw (JasperException)e;
+            }
+        } else {
+            internalIntrospecthelper(
+		bean,prop,value,request,param,ignoreMethodNF);
+        }
+    }
+
+    private static void internalIntrospecthelper(Object bean, String prop,
+					String value, ServletRequest request,
+					String param, boolean ignoreMethodNF) 
+					throws JasperException
+    {
+        Method method = null;
+        Class type = null;
+        Class propertyEditorClass = null;
+	try {
+	    java.beans.BeanInfo info
+		= java.beans.Introspector.getBeanInfo(bean.getClass());
+	    if ( info != null ) {
+		java.beans.PropertyDescriptor pd[]
+		    = info.getPropertyDescriptors();
+		for (int i = 0 ; i < pd.length ; i++) {
+		    if ( pd[i].getName().equals(prop) ) {
+			method = pd[i].getWriteMethod();
+			type   = pd[i].getPropertyType();
+			propertyEditorClass = pd[i].getPropertyEditorClass();
+			break;
+		    }
+		}
+	    }
+	    if ( method != null ) {
+		if (type.isArray()) {
+                    if (request == null) {
+			throw new JasperException(
+		            Localizer.getMessage("jsp.error.beans.setproperty.noindexset"));
+                    }
+		    Class t = type.getComponentType();
+		    String[] values = request.getParameterValues(param);
+		    //XXX Please check.
+		    if(values == null) return;
+		    if(t.equals(String.class)) {
+			method.invoke(bean, new Object[] { values });
+		    } else {
+			Object tmpval = null;
+			createTypedArray (prop, bean, method, values, t,
+					  propertyEditorClass); 
+		    }
+		} else {
+		    if(value == null || (param != null && value.equals(""))) return;
+		    Object oval = convert(prop, value, type, propertyEditorClass);
+		    if ( oval != null )
+			method.invoke(bean, new Object[] { oval });
+		}
+	    }
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}
+        if (!ignoreMethodNF && (method == null)) {
+            if (type == null) {
+		throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.noproperty",
+					 prop,
+					 bean.getClass().getName()));
+            } else {
+		throw new JasperException(
+	            Localizer.getMessage("jsp.error.beans.nomethod.setproperty",
+					 prop,
+					 type.getName(),
+					 bean.getClass().getName()));
+            }
+        }
+    }
+    // __end introspecthelperMethod
+    
+    //-------------------------------------------------------------------
+    // functions to convert builtin Java data types to string.
+    //-------------------------------------------------------------------
+    // __begin toStringMethod
+    public static String toString(Object o) {
+        return String.valueOf(o);
+    }
+
+    public static String toString(byte b) {
+        return new Byte(b).toString();
+    }
+
+    public static String toString(boolean b) {
+        return new Boolean(b).toString();
+    }
+
+    public static String toString(short s) {
+        return new Short(s).toString();
+    }
+
+    public static String toString(int i) {
+        return new Integer(i).toString();
+    }
+
+    public static String toString(float f) {
+        return new Float(f).toString();
+    }
+
+    public static String toString(long l) {
+        return new Long(l).toString();
+    }
+
+    public static String toString(double d) {
+        return new Double(d).toString();
+    }
+
+    public static String toString(char c) {
+        return new Character(c).toString();
+    }
+    // __end toStringMethod
+
+
+    /**
+     * Create a typed array.
+     * This is a special case where params are passed through
+     * the request and the property is indexed.
+     */
+    public static void createTypedArray(String propertyName,
+					Object bean,
+					Method method,
+					String[] values,
+					Class t,
+					Class propertyEditorClass)
+	        throws JasperException {
+
+	try {
+	    if (propertyEditorClass != null) {
+		Object[] tmpval = new Integer[values.length];
+		for (int i=0; i<values.length; i++) {
+		    tmpval[i] = getValueFromBeanInfoPropertyEditor(
+                            t, propertyName, values[i], propertyEditorClass);
+		}
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Integer.class)) {
+		Integer []tmpval = new Integer[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] =  new Integer (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Byte.class)) {
+		Byte[] tmpval = new Byte[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Byte (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Boolean.class)) {
+		Boolean[] tmpval = new Boolean[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Boolean (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Short.class)) {
+		Short[] tmpval = new Short[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Short (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Long.class)) {
+		Long[] tmpval = new Long[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Long (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Double.class)) {
+		Double[] tmpval = new Double[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Double (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Float.class)) {
+		Float[] tmpval = new Float[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Float (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Character.class)) {
+		Character[] tmpval = new Character[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Character(values[i].charAt(0));
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(int.class)) {
+		int []tmpval = new int[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Integer.parseInt (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(byte.class)) {
+		byte[] tmpval = new byte[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Byte.parseByte (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(boolean.class)) {
+		boolean[] tmpval = new boolean[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = (Boolean.valueOf(values[i])).booleanValue();
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(short.class)) {
+		short[] tmpval = new short[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Short.parseShort (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(long.class)) {
+		long[] tmpval = new long[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Long.parseLong (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(double.class)) {
+		double[] tmpval = new double[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Double.valueOf(values[i]).doubleValue();
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(float.class)) {
+		float[] tmpval = new float[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Float.valueOf(values[i]).floatValue();
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(char.class)) {
+		char[] tmpval = new char[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = values[i].charAt(0);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else {
+		Object[] tmpval = new Integer[values.length];
+		for (int i=0; i<values.length; i++) {
+		    tmpval[i] =  
+			getValueFromPropertyEditorManager(
+                                            t, propertyName, values[i]);
+		}
+		method.invoke (bean, new Object[] {tmpval});
+	    }
+	} catch (Exception ex) {
+            throw new JasperException ("error in invoking method", ex);
+	}
+    }
+
+    /**
+     * Escape special shell characters.
+     * @param unescString The string to shell-escape
+     * @return The escaped shell string.
+     */
+
+    public static String escapeQueryString(String unescString) {
+    if ( unescString == null )
+        return null;
+   
+    String escString    = "";
+    String shellSpChars = "&;`'\"|*?~<>^()[]{}$\\\n";
+   
+    for(int index=0; index<unescString.length(); index++) {
+        char nextChar = unescString.charAt(index);
+
+        if( shellSpChars.indexOf(nextChar) != -1 )
+        escString += "\\";
+
+        escString += nextChar;
+    }
+    return escString;
+    }
+
+    /**
+     * Decode an URL formatted string.
+     * @param encoded The string to decode.
+     * @return The decoded string.
+     */
+
+    public static String decode(String encoded) {
+        // speedily leave if we're not needed
+    if (encoded == null) return null;
+        if (encoded.indexOf('%') == -1 && encoded.indexOf('+') == -1)
+        return encoded;
+
+    //allocate the buffer - use byte[] to avoid calls to new.
+        byte holdbuffer[] = new byte[encoded.length()];
+
+        char holdchar;
+        int bufcount = 0;
+
+        for (int count = 0; count < encoded.length(); count++) {
+        char cur = encoded.charAt(count);
+            if (cur == '%') {
+            holdbuffer[bufcount++] =
+          (byte)Integer.parseInt(encoded.substring(count+1,count+3),16);
+                if (count + 2 >= encoded.length())
+                    count = encoded.length();
+                else
+                    count += 2;
+            } else if (cur == '+') {
+        holdbuffer[bufcount++] = (byte) ' ';
+        } else {
+            holdbuffer[bufcount++] = (byte) cur;
+            }
+        }
+	// REVISIT -- remedy for Deprecated warning.
+    //return new String(holdbuffer,0,0,bufcount);
+    return new String(holdbuffer,0,bufcount);
+    }
+
+    // __begin lookupReadMethodMethod
+    public static Object handleGetProperty(Object o, String prop)
+    throws JasperException {
+        if (o == null) {
+	    throw new JasperException(
+	            Localizer.getMessage("jsp.error.beans.nullbean"));
+        }
+	Object value = null;
+        try {
+            Method method = getReadMethod(o.getClass(), prop);
+	    value = method.invoke(o, null);
+        } catch (Exception ex) {
+	    throw new JasperException (ex);
+        }
+        return value;
+    }
+    // __end lookupReadMethodMethod
+
+    // handles <jsp:setProperty> with EL expression for 'value' attribute
+/** Use proprietaryEvaluate
+    public static void handleSetPropertyExpression(Object bean,
+        String prop, String expression, PageContext pageContext,
+        VariableResolver variableResolver, FunctionMapper functionMapper )
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { 
+		pageContext.getExpressionEvaluator().evaluate(
+		    expression,
+		    method.getParameterTypes()[0],
+                    variableResolver,
+                    functionMapper,
+                    null )
+	    });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}
+    }
+**/
+    public static void handleSetPropertyExpression(Object bean,
+        String prop, String expression, PageContext pageContext,
+	ProtectedFunctionMapper functionMapper )
+        throws JasperException
+    {
+        try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+            method.invoke(bean, new Object[] {
+                PageContextImpl.proprietaryEvaluate(
+                    expression,
+                    method.getParameterTypes()[0],
+		    pageContext,
+                    functionMapper,
+                    false )
+            });
+        } catch (Exception ex) {
+            throw new JasperException(ex);
+        }
+    }
+
+    public static void handleSetProperty(Object bean, String prop,
+					 Object value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { value });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 int value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Integer(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 short value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Short(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 long value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Long(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    } 
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 double value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Double(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 float value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Float(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 char value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Character(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+
+    public static void handleSetProperty(Object bean, String prop,
+					 byte value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Byte(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 boolean value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Boolean(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static Method getWriteMethod(Class beanClass, String prop)
+    throws JasperException {
+	Method method = null;	
+        Class type = null;
+	try {
+	    java.beans.BeanInfo info
+                = java.beans.Introspector.getBeanInfo(beanClass);
+	    if ( info != null ) {
+		java.beans.PropertyDescriptor pd[]
+		    = info.getPropertyDescriptors();
+		for (int i = 0 ; i < pd.length ; i++) {
+		    if ( pd[i].getName().equals(prop) ) {
+			method = pd[i].getWriteMethod();
+			type   = pd[i].getPropertyType();
+			break;
+		    }
+		}
+            } else {        
+                // just in case introspection silently fails.
+                throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.nobeaninfo",
+					 beanClass.getName()));
+            }
+        } catch (Exception ex) {
+            throw new JasperException (ex);
+        }
+        if (method == null) {
+            if (type == null) {
+		throw new JasperException(
+                        Localizer.getMessage("jsp.error.beans.noproperty",
+					     prop,
+					     beanClass.getName()));
+            } else {
+		throw new JasperException(
+		    Localizer.getMessage("jsp.error.beans.nomethod.setproperty",
+					 prop,
+					 type.getName(),
+					 beanClass.getName()));
+            }
+        }
+        return method;
+    }
+
+    public static Method getReadMethod(Class beanClass, String prop)
+	    throws JasperException {
+
+        Method method = null;        
+        Class type = null;
+        try {
+            java.beans.BeanInfo info
+                = java.beans.Introspector.getBeanInfo(beanClass);
+            if ( info != null ) {
+                java.beans.PropertyDescriptor pd[]
+                    = info.getPropertyDescriptors();
+                for (int i = 0 ; i < pd.length ; i++) {
+                    if ( pd[i].getName().equals(prop) ) {
+                        method = pd[i].getReadMethod();
+                        type   = pd[i].getPropertyType();
+                        break;
+                    }
+                }
+            } else {        
+                // just in case introspection silently fails.
+		throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.nobeaninfo",
+					 beanClass.getName()));
+	    }
+	} catch (Exception ex) {
+	    throw new JasperException (ex);
+	}
+        if (method == null) {
+            if (type == null) {
+		throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.noproperty", prop,
+					 beanClass.getName()));
+            } else {
+		throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.nomethod", prop,
+					 beanClass.getName()));
+            }
+        }
+
+	return method;
+    }
+
+    //*********************************************************************
+    // PropertyEditor Support
+
+    public static Object getValueFromBeanInfoPropertyEditor(
+		           Class attrClass, String attrName, String attrValue,
+			   Class propertyEditorClass) 
+	throws JasperException 
+    {
+	try {
+	    PropertyEditor pe = (PropertyEditor)propertyEditorClass.newInstance();
+	    pe.setAsText(attrValue);
+	    return pe.getValue();
+	} catch (Exception ex) {
+	    throw new JasperException(
+                Localizer.getMessage("jsp.error.beans.property.conversion",
+				     attrValue, attrClass.getName(), attrName,
+				     ex.getMessage()));
+	}
+    }
+
+    public static Object getValueFromPropertyEditorManager(
+	             Class attrClass, String attrName, String attrValue) 
+	throws JasperException 
+    {
+	try {
+	    PropertyEditor propEditor = 
+		PropertyEditorManager.findEditor(attrClass);
+	    if (propEditor != null) {
+		propEditor.setAsText(attrValue);
+		return propEditor.getValue();
+	    } else {
+		throw new IllegalArgumentException(
+                    Localizer.getMessage("jsp.error.beans.propertyeditor.notregistered"));
+	    }
+	} catch (IllegalArgumentException ex) {
+	    throw new JasperException(
+                Localizer.getMessage("jsp.error.beans.property.conversion",
+				     attrValue, attrClass.getName(), attrName,
+				     ex.getMessage()));
+	}
+    }
+
+
+    // ************************************************************************
+    // General Purpose Runtime Methods
+    // ************************************************************************
+
+
+    /**
+     * Convert a possibly relative resource path into a context-relative
+     * resource path that starts with a '/'.
+     *
+     * @param request The servlet request we are processing
+     * @param relativePath The possibly relative resource path
+     */
+    public static String getContextRelativePath(ServletRequest request,
+                                                String relativePath) {
+
+        if (relativePath.startsWith("/"))
+            return (relativePath);
+        if (!(request instanceof HttpServletRequest))
+            return (relativePath);
+        HttpServletRequest hrequest = (HttpServletRequest) request;
+        String uri = (String)
+            request.getAttribute("javax.servlet.include.servlet_path");
+        if (uri != null) {
+            String pathInfo = (String)
+                request.getAttribute("javax.servlet.include.path_info");
+            if (pathInfo == null) {
+                if (uri.lastIndexOf('/') >= 0) 
+                    uri = uri.substring(0, uri.lastIndexOf('/'));
+            }
+        }
+        else {
+            uri = hrequest.getServletPath();
+            if (uri.lastIndexOf('/') >= 0) 
+                uri = uri.substring(0, uri.lastIndexOf('/'));
+        }
+        return uri + '/' + relativePath;
+
+    }
+
+
+    /**
+     * Perform a RequestDispatcher.include() operation, with optional flushing
+     * of the response beforehand.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     * @param relativePath The relative path of the resource to be included
+     * @param out The Writer to whom we are currently writing
+     * @param flush Should we flush before the include is processed?
+     *
+     * @exception IOException if thrown by the included servlet
+     * @exception ServletException if thrown by the included servlet
+     */
+    public static void include(ServletRequest request,
+                               ServletResponse response,
+                               String relativePath,
+                               JspWriter out,
+                               boolean flush)
+        throws IOException, ServletException {
+
+        if (flush && !(out instanceof BodyContent))
+            out.flush();
+
+        // FIXME - It is tempting to use request.getRequestDispatcher() to
+        // resolve a relative path directly, but Catalina currently does not
+        // take into account whether the caller is inside a RequestDispatcher
+        // include or not.  Whether Catalina *should* take that into account
+        // is a spec issue currently under review.  In the mean time,
+        // replicate Jasper's previous behavior
+
+        String resourcePath = getContextRelativePath(request, relativePath);
+        RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
+
+        rd.include(request,
+                   new ServletResponseWrapperInclude(response, out));
+
+    }
+
+    /**
+     * URL encodes a string, based on the supplied character encoding.
+     * This performs the same function as java.next.URLEncode.encode
+     * in J2SDK1.4, and should be removed if the only platform supported
+     * is 1.4 or higher.
+     * @param s The String to be URL encoded.
+     * @param enc The character encoding 
+     * @return The URL encoded String
+     */
+    public static String URLEncode(String s, String enc) {
+
+	if (s == null) {
+	    return "null";
+	}
+
+	if (enc == null) {
+	    enc = "ISO-8859-1";	// The default request encoding 
+	}
+
+	StringBuffer out = new StringBuffer(s.length());
+	ByteArrayOutputStream buf = new ByteArrayOutputStream();
+	OutputStreamWriter writer = null;
+	try {
+	    writer = new OutputStreamWriter(buf, enc);
+	} catch (java.io.UnsupportedEncodingException ex) {
+	    // Use the default encoding?
+	    writer = new OutputStreamWriter(buf);
+	}
+	
+	for (int i = 0; i < s.length(); i++) {
+	    int c = s.charAt(i);
+	    if (c == ' ') {
+		out.append('+');
+	    } else if (isSafeChar(c)) {
+		out.append((char)c);
+	    } else {
+		// convert to external encoding before hex conversion
+		try {
+		    writer.write(c);
+		    writer.flush();
+		} catch(IOException e) {
+		    buf.reset();
+		    continue;
+		}
+		byte[] ba = buf.toByteArray();
+		for (int j = 0; j < ba.length; j++) {
+		    out.append('%');
+		    // Converting each byte in the buffer
+		    out.append(Character.forDigit((ba[j]>>4) & 0xf, 16));
+		    out.append(Character.forDigit(ba[j] & 0xf, 16));
+		}
+		buf.reset();
+	    }
+	}
+	return out.toString();
+    }
+
+    private static boolean isSafeChar(int c) {
+	if (c >= 'a' && c <= 'z') {
+	    return true;
+	}
+	if (c >= 'A' && c <= 'Z') {
+	    return true;
+	}
+	if (c >= '0' && c <= '9') {
+	    return true;
+	}
+	if (c == '-' || c == '_' || c == '.' || c == '!' ||
+	    c == '~' || c == '*' || c == '\'' || c == '(' || c == ')') {
+	    return true;
+	}
+	return false;
+    }
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/JspSourceDependent.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspSourceDependent.java
new file mode 100644
index 0000000..880cba1
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspSourceDependent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+/**
+ * Interface for tracking the source files dependencies, for the purpose
+ * of compiling out of date pages.  This is used for
+ * 1) files that are included by page directives
+ * 2) files that are included by include-prelude and include-coda in jsp:config
+ * 3) files that are tag files and referenced
+ * 4) TLDs referenced
+ */
+
+public interface JspSourceDependent {
+
+   /**
+    * Returns a list of files names that the current page has a source
+    * dependency on.
+    */
+    // FIXME: Type used is Object due to very weird behavior 
+    // with Eclipse JDT 3.1 in Java 5 mode
+    public Object getDependants();
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/JspWriterImpl.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspWriterImpl.java
new file mode 100644
index 0000000..617b46b
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/JspWriterImpl.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.servlet.ServletResponse;
+import javax.servlet.jsp.JspWriter;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.security.SecurityUtil;
+
+/**
+ * Write text to a character-output stream, buffering characters so as
+ * to provide for the efficient writing of single characters, arrays,
+ * and strings. 
+ *
+ * Provide support for discarding for the output that has been 
+ * buffered. 
+ * 
+ * This needs revisiting when the buffering problems in the JSP spec
+ * are fixed -akv 
+ *
+ * @author Anil K. Vijendran
+ */
+public class JspWriterImpl extends JspWriter {
+    
+    private Writer out;
+    private ServletResponse response;    
+    private char cb[];
+    private int nextChar;
+    private boolean flushed = false;
+    private boolean closed = false;
+    
+    public JspWriterImpl() {
+        super( Constants.DEFAULT_BUFFER_SIZE, true );
+    }
+    
+    /**
+     * Create a buffered character-output stream that uses a default-sized
+     * output buffer.
+     *
+     * @param  response  A Servlet Response
+     */
+    public JspWriterImpl(ServletResponse response) {
+        this(response, Constants.DEFAULT_BUFFER_SIZE, true);
+    }
+    
+    /**
+     * Create a new buffered character-output stream that uses an output
+     * buffer of the given size.
+     *
+     * @param  response A Servlet Response
+     * @param  sz   	Output-buffer size, a positive integer
+     *
+     * @exception  IllegalArgumentException  If sz is <= 0
+     */
+    public JspWriterImpl(ServletResponse response, int sz, 
+            boolean autoFlush) {
+        super(sz, autoFlush);
+        if (sz < 0)
+            throw new IllegalArgumentException("Buffer size <= 0");
+        this.response = response;
+        cb = sz == 0 ? null : new char[sz];
+        nextChar = 0;
+    }
+    
+    void init( ServletResponse response, int sz, boolean autoFlush ) {
+        this.response= response;
+        if( sz > 0 && ( cb == null || sz > cb.length ) )
+            cb=new char[sz];
+        nextChar = 0;
+        this.autoFlush=autoFlush;
+        this.bufferSize=sz;
+    }
+    
+    /** Package-level access
+     */
+    void recycle() {
+        flushed = false;
+        closed = false;
+        out = null;
+        nextChar = 0;
+        response = null;
+    }
+    
+    /**
+     * Flush the output buffer to the underlying character stream, without
+     * flushing the stream itself.  This method is non-private only so that it
+     * may be invoked by PrintStream.
+     */
+    protected final void flushBuffer() throws IOException {
+        if (bufferSize == 0)
+            return;
+        flushed = true;
+        ensureOpen();
+        if (nextChar == 0)
+            return;
+        initOut();
+        out.write(cb, 0, nextChar);
+        nextChar = 0;
+    }
+    
+    private void initOut() throws IOException {
+        if (out == null) {
+            out = response.getWriter();
+        }
+    }
+    
+    private String getLocalizeMessage(final String message){
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return (String)AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    return Localizer.getMessage(message); 
+                }
+            });
+        } else {
+            return Localizer.getMessage(message);
+        }
+    }
+    
+    /**
+     * Discard the output buffer.
+     */
+    public final void clear() throws IOException {
+        if ((bufferSize == 0) && (out != null))
+            // clear() is illegal after any unbuffered output (JSP.5.5)
+            throw new IllegalStateException(
+                    getLocalizeMessage("jsp.error.ise_on_clear"));
+        if (flushed)
+            throw new IOException(
+                    getLocalizeMessage("jsp.error.attempt_to_clear_flushed_buffer"));
+        ensureOpen();
+        nextChar = 0;
+    }
+    
+    public void clearBuffer() throws IOException {
+        if (bufferSize == 0)
+            throw new IllegalStateException(
+                    getLocalizeMessage("jsp.error.ise_on_clear"));
+        ensureOpen();
+        nextChar = 0;
+    }
+    
+    private final void bufferOverflow() throws IOException {
+        throw new IOException(getLocalizeMessage("jsp.error.overflow"));
+    }
+    
+    /**
+     * Flush the stream.
+     *
+     */
+    public void flush()  throws IOException {
+        flushBuffer();
+        if (out != null) {
+            out.flush();
+        }
+    }
+    
+    /**
+     * Close the stream.
+     *
+     */
+    public void close() throws IOException {
+        if (response == null || closed)
+            // multiple calls to close is OK
+            return;
+        flush();
+        if (out != null)
+            out.close();
+        out = null;
+        closed = true;
+    }
+    
+    /**
+     * @return the number of bytes unused in the buffer
+     */
+    public int getRemaining() {
+        return bufferSize - nextChar;
+    }
+    
+    /** check to make sure that the stream has not been closed */
+    private void ensureOpen() throws IOException {
+        if (response == null || closed)
+            throw new IOException("Stream closed");
+    }
+    
+    
+    /**
+     * Write a single character.
+     */
+    public void write(int c) throws IOException {
+        ensureOpen();
+        if (bufferSize == 0) {
+            initOut();
+            out.write(c);
+        }
+        else {
+            if (nextChar >= bufferSize)
+                if (autoFlush)
+                    flushBuffer();
+                else
+                    bufferOverflow();
+            cb[nextChar++] = (char) c;
+        }
+    }
+    
+    /**
+     * Our own little min method, to avoid loading java.lang.Math if we've run
+     * out of file descriptors and we're trying to print a stack trace.
+     */
+    private int min(int a, int b) {
+        if (a < b) return a;
+        return b;
+    }
+    
+    /**
+     * Write a portion of an array of characters.
+     *
+     * <p> Ordinarily this method stores characters from the given array into
+     * this stream's buffer, flushing the buffer to the underlying stream as
+     * needed.  If the requested length is at least as large as the buffer,
+     * however, then this method will flush the buffer and write the characters
+     * directly to the underlying stream.  Thus redundant
+     * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
+     *
+     * @param  cbuf  A character array
+     * @param  off   Offset from which to start reading characters
+     * @param  len   Number of characters to write
+     */
+    public void write(char cbuf[], int off, int len) 
+    throws IOException 
+    {
+        ensureOpen();
+        
+        if (bufferSize == 0) {
+            initOut();
+            out.write(cbuf, off, len);
+            return;
+        }
+        
+        if ((off < 0) || (off > cbuf.length) || (len < 0) ||
+                ((off + len) > cbuf.length) || ((off + len) < 0)) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return;
+        } 
+        
+        if (len >= bufferSize) {
+            /* If the request length exceeds the size of the output buffer,
+             flush the buffer and then write the data directly.  In this
+             way buffered streams will cascade harmlessly. */
+            if (autoFlush)
+                flushBuffer();
+            else
+                bufferOverflow();
+            initOut();
+            out.write(cbuf, off, len);
+            return;
+        }
+        
+        int b = off, t = off + len;
+        while (b < t) {
+            int d = min(bufferSize - nextChar, t - b);
+            System.arraycopy(cbuf, b, cb, nextChar, d);
+            b += d;
+            nextChar += d;
+            if (nextChar >= bufferSize) 
+                if (autoFlush)
+                    flushBuffer();
+                else
+                    bufferOverflow();
+        }
+        
+    }
+    
+    /**
+     * Write an array of characters.  This method cannot be inherited from the
+     * Writer class because it must suppress I/O exceptions.
+     */
+    public void write(char buf[]) throws IOException {
+        write(buf, 0, buf.length);
+    }
+    
+    /**
+     * Write a portion of a String.
+     *
+     * @param  s     String to be written
+     * @param  off   Offset from which to start reading characters
+     * @param  len   Number of characters to be written
+     */
+    public void write(String s, int off, int len) throws IOException {
+        ensureOpen();
+        if (bufferSize == 0) {
+            initOut();
+            out.write(s, off, len);
+            return;
+        }
+        int b = off, t = off + len;
+        while (b < t) {
+            int d = min(bufferSize - nextChar, t - b);
+            s.getChars(b, b + d, cb, nextChar);
+            b += d;
+            nextChar += d;
+            if (nextChar >= bufferSize) 
+                if (autoFlush)
+                    flushBuffer();
+                else
+                    bufferOverflow();
+        }
+    }
+    
+    /**
+     * Write a string.  This method cannot be inherited from the Writer class
+     * because it must suppress I/O exceptions.
+     */
+    public void write(String s) throws IOException {
+        // Simple fix for Bugzilla 35410
+        // Calling the other write function so as to init the buffer anyways
+        if(s == null) {
+            write(s, 0, 0);
+        } else {
+            write(s, 0, s.length());
+        }
+    }
+    
+    
+    static String lineSeparator = System.getProperty("line.separator");
+    
+    /**
+     * Write a line separator.  The line separator string is defined by the
+     * system property <tt>line.separator</tt>, and is not necessarily a single
+     * newline ('\n') character.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    
+    public void newLine() throws IOException {
+        write(lineSeparator);
+    }
+    
+    
+    /* Methods that do not terminate lines */
+    
+    /**
+     * Print a boolean value.  The string produced by <code>{@link
+     * java.lang.String#valueOf(boolean)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param      b   The <code>boolean</code> to be printed
+     */
+    public void print(boolean b) throws IOException {
+        write(b ? "true" : "false");
+    }
+    
+    /**
+     * Print a character.  The character is translated into one or more bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param      c   The <code>char</code> to be printed
+     */
+    public void print(char c) throws IOException {
+        write(String.valueOf(c));
+    }
+    
+    /**
+     * Print an integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(int)}</code> is translated into bytes according
+     * to the platform's default character encoding, and these bytes are
+     * written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      i   The <code>int</code> to be printed
+     */
+    public void print(int i) throws IOException {
+        write(String.valueOf(i));
+    }
+    
+    /**
+     * Print a long integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(long)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      l   The <code>long</code> to be printed
+     */
+    public void print(long l) throws IOException {
+        write(String.valueOf(l));
+    }
+    
+    /**
+     * Print a floating-point number.  The string produced by <code>{@link
+     * java.lang.String#valueOf(float)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      f   The <code>float</code> to be printed
+     */
+    public void print(float f) throws IOException {
+        write(String.valueOf(f));
+    }
+    
+    /**
+     * Print a double-precision floating-point number.  The string produced by
+     * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
+     * bytes according to the platform's default character encoding, and these
+     * bytes are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param      d   The <code>double</code> to be printed
+     */
+    public void print(double d) throws IOException {
+        write(String.valueOf(d));
+    }
+    
+    /**
+     * Print an array of characters.  The characters are converted into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      s   The array of chars to be printed
+     *
+     * @throws  NullPointerException  If <code>s</code> is <code>null</code>
+     */
+    public void print(char s[]) throws IOException {
+        write(s);
+    }
+    
+    /**
+     * Print a string.  If the argument is <code>null</code> then the string
+     * <code>"null"</code> is printed.  Otherwise, the string's characters are
+     * converted into bytes according to the platform's default character
+     * encoding, and these bytes are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param      s   The <code>String</code> to be printed
+     */
+    public void print(String s) throws IOException {
+        if (s == null) {
+            s = "null";
+        }
+        write(s);
+    }
+    
+    /**
+     * Print an object.  The string produced by the <code>{@link
+     * java.lang.String#valueOf(Object)}</code> method is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      obj   The <code>Object</code> to be printed
+     */
+    public void print(Object obj) throws IOException {
+        write(String.valueOf(obj));
+    }
+    
+    /* Methods that do terminate lines */
+    
+    /**
+     * Terminate the current line by writing the line separator string.  The
+     * line separator string is defined by the system property
+     * <code>line.separator</code>, and is not necessarily a single newline
+     * character (<code>'\n'</code>).
+     *
+     * Need to change this from PrintWriter because the default
+     * println() writes  to the sink directly instead of through the
+     * write method...  
+     */
+    public void println() throws IOException {
+        newLine();
+    }
+    
+    /**
+     * Print a boolean value and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(boolean)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(boolean x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a character and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
+     * #println()}</code>.
+     */
+    public void println(char x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an integer and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
+     * #println()}</code>.
+     */
+    public void println(int x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a long integer and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(long)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(long x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a floating-point number and then terminate the line.  This method
+     * behaves as though it invokes <code>{@link #print(float)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(float x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a double-precision floating-point number and then terminate the
+     * line.  This method behaves as though it invokes <code>{@link
+     * #print(double)}</code> and then <code>{@link #println()}</code>.
+     */
+    public void println(double x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an array of characters and then terminate the line.  This method
+     * behaves as though it invokes <code>{@link #print(char[])}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(char x[]) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a String and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(String)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(String x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an Object and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(Object)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(Object x) throws IOException {
+        print(x);
+        println();
+    }
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java
new file mode 100644
index 0000000..f8c5cdd
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java
@@ -0,0 +1,934 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.el.ELException;
+import javax.servlet.jsp.el.ExpressionEvaluator;
+import javax.servlet.jsp.el.VariableResolver;
+import javax.servlet.jsp.tagext.BodyContent;
+
+import org.apache.commons.el.ExpressionEvaluatorImpl;
+import org.apache.commons.el.VariableResolverImpl;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jasper.Constants;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.security.SecurityUtil;
+
+/**
+ * Implementation of the PageContext class from the JSP spec.
+ * Also doubles as a VariableResolver for the EL.
+ *
+ * @author Anil K. Vijendran
+ * @author Larry Cable
+ * @author Hans Bergsten
+ * @author Pierre Delisle
+ * @author Mark Roth
+ * @author Jan Luehe
+ */
+public class PageContextImpl extends PageContext implements VariableResolver {
+
+    // Logger
+    private static Log log = LogFactory.getLog(PageContextImpl.class);
+
+    // The expression evaluator, for evaluating EL expressions.
+    private static ExpressionEvaluatorImpl elExprEval
+	= new ExpressionEvaluatorImpl(false);
+
+    // The variable resolver, for evaluating EL expressions.
+    private VariableResolverImpl variableResolver;
+
+    private BodyContentImpl[] outs;
+    private int depth;
+
+    // per-servlet state
+    private Servlet servlet;
+    private ServletConfig config;
+    private ServletContext context;
+    private JspFactory factory;
+    private boolean needsSession;
+    private String errorPageURL;
+    private boolean autoFlush;
+    private int	bufferSize;
+
+    // page-scope attributes
+    private transient Hashtable	attributes;
+
+    // per-request state
+    private transient ServletRequest request;
+    private transient ServletResponse response;
+    private transient Object page;
+    private transient HttpSession session;
+    private boolean isIncluded;
+
+    // initial output stream
+    private transient JspWriter out;
+    private transient JspWriterImpl baseOut;
+
+    /*
+     * Constructor.
+     */
+    PageContextImpl(JspFactory factory) {
+        this.factory = factory;
+	this.variableResolver = new VariableResolverImpl(this);
+	this.outs = new BodyContentImpl[0];
+	this.attributes = new Hashtable(16);
+	this.depth = -1;
+    }
+
+    public void initialize(Servlet servlet,
+			   ServletRequest request,
+                           ServletResponse response,
+			   String errorPageURL,
+                           boolean needsSession,
+			   int bufferSize,
+                           boolean autoFlush) throws IOException {
+
+	_initialize(servlet, request, response, errorPageURL, needsSession,
+		    bufferSize, autoFlush);
+    }
+
+    private void _initialize(Servlet servlet,
+			     ServletRequest request,
+			     ServletResponse response,
+			     String errorPageURL,
+			     boolean needsSession,
+			     int bufferSize,
+			     boolean autoFlush) throws IOException {
+
+	// initialize state
+	this.servlet = servlet;
+	this.config = servlet.getServletConfig();
+	this.context = config.getServletContext();
+	this.needsSession = needsSession;
+	this.errorPageURL = errorPageURL;
+	this.bufferSize = bufferSize;
+	this.autoFlush = autoFlush;
+	this.request = request;
+ 	this.response = response;
+
+	// Setup session (if required)
+	if (request instanceof HttpServletRequest && needsSession)
+	    this.session = ((HttpServletRequest)request).getSession();
+	if (needsSession && session == null)
+	    throw new IllegalStateException
+                ("Page needs a session and none is available");
+
+        // initialize the initial out ...
+        depth = -1;
+        if (this.baseOut == null) {
+            this.baseOut = new JspWriterImpl(response, bufferSize, autoFlush);
+        } else {
+            this.baseOut.init(response, bufferSize, autoFlush);
+        }
+        this.out = baseOut;
+
+	// register names/values as per spec
+	setAttribute(OUT, this.out);
+	setAttribute(REQUEST, request);
+	setAttribute(RESPONSE, response);
+
+	if (session != null)
+	    setAttribute(SESSION, session);
+
+	setAttribute(PAGE, servlet);
+	setAttribute(CONFIG, config);
+	setAttribute(PAGECONTEXT, this);
+	setAttribute(APPLICATION, context);
+
+	isIncluded = request.getAttribute(
+	    "javax.servlet.include.servlet_path") != null;
+    }
+
+    public void release() {
+        out = baseOut;
+	try {
+	    if (isIncluded) {
+		((JspWriterImpl)out).flushBuffer();
+                // push it into the including jspWriter
+	    } else {
+                // Old code:
+                //out.flush();
+                // Do not flush the buffer even if we're not included (i.e.
+                // we are the main page. The servlet will flush it and close
+                // the stream.
+                ((JspWriterImpl)out).flushBuffer();
+            }
+	} catch (IOException ex) {
+	    log.warn("Internal error flushing the buffer in release()");
+	}
+
+	servlet = null;
+	config = null;
+	context = null;
+	needsSession = false;
+	errorPageURL = null;
+	bufferSize = JspWriter.DEFAULT_BUFFER;
+	autoFlush = true;
+	request = null;
+	response = null;
+        depth = -1;
+	baseOut.recycle();
+	session = null;
+
+	attributes.clear();
+    }
+
+    public Object getAttribute(final String name) {
+
+        if (name == null) {
+            throw new NullPointerException(
+                    Localizer.getMessage("jsp.error.attribute.null_name"));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    return doGetAttribute(name);
+                }
+            });
+        } else {
+            return doGetAttribute(name);
+        }
+
+    }
+
+    private Object doGetAttribute(String name){
+        return attributes.get(name);
+    }
+
+    public Object getAttribute(final String name, final int scope) {
+
+        if (name == null) {
+            throw new NullPointerException(
+                    Localizer.getMessage("jsp.error.attribute.null_name"));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    return doGetAttribute(name, scope);
+                }
+            });
+        } else {
+            return doGetAttribute(name, scope);
+        }
+
+    }
+
+    private Object doGetAttribute(String name, int scope){
+        switch (scope) {
+            case PAGE_SCOPE:
+            return attributes.get(name);
+
+            case REQUEST_SCOPE:
+            return request.getAttribute(name);
+
+            case SESSION_SCOPE:
+            if (session == null) {
+                throw new IllegalStateException(
+                        Localizer.getMessage("jsp.error.page.noSession"));
+            }
+            return session.getAttribute(name);
+
+            case APPLICATION_SCOPE:
+            return context.getAttribute(name);
+
+            default:
+            throw new IllegalArgumentException("Invalid scope");
+        }
+    }
+
+    public void setAttribute(final String name, final Object attribute) {
+
+        if (name == null) {
+            throw new NullPointerException(
+                    Localizer.getMessage("jsp.error.attribute.null_name"));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    doSetAttribute(name, attribute);
+                    return null;
+                }
+            });
+        } else {
+            doSetAttribute(name, attribute);
+        }
+    }
+
+    private void doSetAttribute(String name, Object attribute){
+        if (attribute != null) {
+            attributes.put(name, attribute);
+        } else {
+            removeAttribute(name, PAGE_SCOPE);
+        }
+    }
+
+    public void setAttribute(final String name, final Object o, final int scope) {
+
+        if (name == null) {
+            throw new NullPointerException(
+                    Localizer.getMessage("jsp.error.attribute.null_name"));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    doSetAttribute(name, o, scope);
+                    return null;
+                }
+            });
+        } else {
+            doSetAttribute(name, o, scope);
+        }
+
+    }
+
+    private void doSetAttribute(String name, Object o, int scope ){
+        if (o != null) {
+            switch (scope) {
+            case PAGE_SCOPE:
+            attributes.put(name, o);
+            break;
+
+            case REQUEST_SCOPE:
+            request.setAttribute(name, o);
+            break;
+
+            case SESSION_SCOPE:
+            if (session == null) {
+                throw new IllegalStateException(
+                        Localizer.getMessage("jsp.error.page.noSession"));
+            }
+            session.setAttribute(name, o);
+            break;
+
+            case APPLICATION_SCOPE:
+            context.setAttribute(name, o);
+            break;
+
+            default:
+            throw new IllegalArgumentException("Invalid scope");
+            }
+        } else {
+            removeAttribute(name, scope);
+        }
+    }
+
+    public void removeAttribute(final String name, final int scope) {
+
+        if (name == null) {
+            throw new NullPointerException(
+                    Localizer.getMessage("jsp.error.attribute.null_name"));
+        }
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    doRemoveAttribute(name, scope);
+                    return null;
+                }
+            });
+        } else {
+            doRemoveAttribute(name, scope);
+        }
+    }
+
+    private void doRemoveAttribute(String name, int scope){
+        switch (scope) {
+        case PAGE_SCOPE:
+            attributes.remove(name);
+            break;
+
+        case REQUEST_SCOPE:
+            request.removeAttribute(name);
+            break;
+
+        case SESSION_SCOPE:
+            if (session == null) {
+            throw new IllegalStateException(
+                        Localizer.getMessage("jsp.error.page.noSession"));
+            }
+            session.removeAttribute(name);
+            break;
+
+        case APPLICATION_SCOPE:
+            context.removeAttribute(name);
+            break;
+            
+        default:
+            throw new IllegalArgumentException("Invalid scope");
+        }
+    }
+
+    public int getAttributesScope(final String name) {
+
+        if (name == null) {
+            throw new NullPointerException(
+                    Localizer.getMessage("jsp.error.attribute.null_name"));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return ((Integer)AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    return new Integer(doGetAttributeScope(name));
+                }
+            })).intValue();
+        } else {
+            return doGetAttributeScope(name);
+        }
+    }
+
+    private int doGetAttributeScope(String name){
+        if (attributes.get(name) != null)
+            return PAGE_SCOPE;
+
+        if (request.getAttribute(name) != null)
+            return REQUEST_SCOPE;
+
+        if (session != null) {
+            if (session.getAttribute(name) != null)
+                return SESSION_SCOPE;
+        }
+
+        if (context.getAttribute(name) != null)
+            return APPLICATION_SCOPE;
+
+        return 0;
+    }
+
+    public Object findAttribute(final String name) {
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    if (name == null) {
+                        throw new NullPointerException(
+                                Localizer.getMessage("jsp.error.attribute.null_name"));
+                    }
+
+                    return doFindAttribute(name);
+                }
+            });
+        } else {
+            if (name == null) {
+                throw new NullPointerException(
+                        Localizer.getMessage("jsp.error.attribute.null_name"));
+            }
+
+            return doFindAttribute(name);
+        }
+    }
+
+    private Object doFindAttribute(String name){
+
+        Object o = attributes.get(name);
+        if (o != null)
+            return o;
+
+        o = request.getAttribute(name);
+        if (o != null)
+            return o;
+
+        if (session != null) {
+            o = session.getAttribute(name);
+            if (o != null)
+                return o;
+        }
+
+        return context.getAttribute(name);
+    }
+
+
+    public Enumeration getAttributeNamesInScope(final int scope) {
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return (Enumeration)
+                    AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    return doGetAttributeNamesInScope(scope);
+                }
+            });
+        } else {
+            return doGetAttributeNamesInScope(scope);
+        }
+    }
+
+    private Enumeration doGetAttributeNamesInScope(int scope){
+        switch (scope) {
+        case PAGE_SCOPE:
+            return attributes.keys();
+            
+        case REQUEST_SCOPE:
+            return request.getAttributeNames();
+
+        case SESSION_SCOPE:
+            if (session == null) {
+            throw new IllegalStateException(
+                        Localizer.getMessage("jsp.error.page.noSession"));
+            }
+            return session.getAttributeNames();
+
+        case APPLICATION_SCOPE:
+            return context.getAttributeNames();
+
+        default:
+            throw new IllegalArgumentException("Invalid scope");
+        }
+    }
+
+    public void removeAttribute(final String name) {
+
+        if (name == null) {
+            throw new NullPointerException(
+                    Localizer.getMessage("jsp.error.attribute.null_name"));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    doRemoveAttribute(name);
+                    return null;
+                }
+            });
+        } else {
+            doRemoveAttribute(name);
+        }
+	}
+
+
+    private void doRemoveAttribute(String name){
+        try {
+            removeAttribute(name, PAGE_SCOPE);
+            removeAttribute(name, REQUEST_SCOPE);
+            if( session != null ) {
+                    removeAttribute(name, SESSION_SCOPE);
+                }
+                removeAttribute(name, APPLICATION_SCOPE);
+            } catch (Exception ex) {
+            // we remove as much as we can, and
+            // simply ignore possible exceptions
+        }
+    }
+
+    public JspWriter getOut() {
+	return out;
+    }
+
+    public HttpSession getSession() { return session; }
+    public Servlet getServlet() { return servlet; }
+    public ServletConfig getServletConfig() { return config; }
+    public ServletContext getServletContext() {
+	return config.getServletContext();
+    }
+    public ServletRequest getRequest() { return request; }
+    public ServletResponse getResponse() { return response; }
+
+
+    /**
+     * Returns the exception associated with this page
+     * context, if any.
+     * <p/>
+     * Added wrapping for Throwables to avoid ClassCastException:
+     * see Bugzilla 31171 for details.
+     *
+     * @return The Exception associated with this page context, if any.
+     */
+    public Exception getException() {
+        Throwable t = JspRuntimeLibrary.getThrowable(request);
+
+        // Only wrap if needed
+        if((t != null) && (! (t instanceof Exception))) {
+            t = new JspException(t);
+        }
+
+        return (Exception) t;
+    }
+
+
+    public Object getPage() { return servlet; }
+
+
+    private final String getAbsolutePathRelativeToContext(String relativeUrlPath) {
+        String path = relativeUrlPath;
+
+        if (!path.startsWith("/")) {
+	    String uri = (String)
+		request.getAttribute("javax.servlet.include.servlet_path");
+	    if (uri == null)
+		uri = ((HttpServletRequest) request).getServletPath();
+            String baseURI = uri.substring(0, uri.lastIndexOf('/'));
+            path = baseURI+'/'+path;
+        }
+
+        return path;
+    }
+
+    public void include(String relativeUrlPath)
+	        throws ServletException, IOException {
+        JspRuntimeLibrary.include(request, response, relativeUrlPath, out,
+				  true);
+    }
+
+    public void include(final String relativeUrlPath, final boolean flush)
+	        throws ServletException, IOException {
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                AccessController.doPrivileged(new PrivilegedExceptionAction(){
+                    public Object run() throws Exception{
+                        doInclude(relativeUrlPath, flush);
+                        return null;
+                    }
+                });
+            } catch (PrivilegedActionException e){
+                Exception ex =  e.getException();
+                if (ex instanceof IOException){
+                    throw (IOException)ex;
+                } else {
+                    throw (ServletException)ex;
+                }
+            }
+        } else {
+            doInclude(relativeUrlPath, flush);
+        }
+    }
+
+    private void doInclude(String relativeUrlPath, boolean flush)
+	        throws ServletException, IOException {
+        JspRuntimeLibrary.include(request, response, relativeUrlPath, out,
+				  flush);
+    }
+
+    public VariableResolver getVariableResolver() {
+	return this;
+    }
+
+    public void forward(final String relativeUrlPath)
+        throws ServletException, IOException {
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                AccessController.doPrivileged(new PrivilegedExceptionAction(){
+                    public Object run() throws Exception{
+                        doForward(relativeUrlPath);
+                        return null;
+                    }
+                });
+            } catch (PrivilegedActionException e){
+                Exception ex =  e.getException();
+                if (ex instanceof IOException){
+                    throw (IOException)ex;
+                } else {
+                    throw (ServletException)ex;
+                }
+            }
+        } else {
+            doForward(relativeUrlPath);
+        }
+    }
+
+    private void doForward(String relativeUrlPath)
+        throws ServletException, IOException{
+
+        // JSP.4.5 If the buffer was flushed, throw IllegalStateException
+        try {
+            out.clear();
+        } catch (IOException ex) {
+            IllegalStateException ise =
+                new IllegalStateException(Localizer.getMessage(
+                            "jsp.error.attempt_to_clear_flushed_buffer"));
+            ise.initCause(ex);
+            throw ise;
+        }
+
+        // Make sure that the response object is not the wrapper for include
+        while (response instanceof ServletResponseWrapperInclude) {
+            response = ((ServletResponseWrapperInclude)response).getResponse();
+        }
+
+        final String path = getAbsolutePathRelativeToContext(relativeUrlPath);
+        String includeUri
+            = (String) request.getAttribute(Constants.INC_SERVLET_PATH);
+            
+        final ServletResponse fresponse = response;
+        final ServletRequest frequest = request;
+            
+        if (includeUri != null)
+            request.removeAttribute(Constants.INC_SERVLET_PATH);
+        try {
+            context.getRequestDispatcher(path).forward(request, response);
+        } finally {
+            if (includeUri != null)
+                request.setAttribute(Constants.INC_SERVLET_PATH, includeUri);
+            request.setAttribute(Constants.FORWARD_SEEN, "true");
+        }
+    }
+
+    public BodyContent pushBody() {
+	return (BodyContent) pushBody(null);
+    }
+
+    public JspWriter pushBody(Writer writer) {
+        depth++;
+        if (depth >= outs.length) {
+            BodyContentImpl[] newOuts = new BodyContentImpl[depth + 1];
+            for (int i=0; i<outs.length; i++) {
+                newOuts[i] = outs[i];
+            }
+            newOuts[depth] = new BodyContentImpl(out);
+            outs = newOuts;
+        }
+
+	outs[depth].setWriter(writer);
+        out = outs[depth];
+
+	// Update the value of the "out" attribute in the page scope
+	// attribute namespace of this PageContext
+	setAttribute(OUT, out);
+
+        return outs[depth];
+    }
+
+    public JspWriter popBody() {
+        depth--;
+        if (depth >= 0) {
+            out = outs[depth];
+        } else {
+            out = baseOut;
+        }
+
+	// Update the value of the "out" attribute in the page scope
+	// attribute namespace of this PageContext
+	setAttribute(OUT, out);
+
+        return out;
+    }
+
+    /**
+     * Provides programmatic access to the ExpressionEvaluator.
+     * The JSP Container must return a valid instance of an
+     * ExpressionEvaluator that can parse EL expressions.
+     */
+    public ExpressionEvaluator getExpressionEvaluator() {
+        return elExprEval;
+    }
+
+    public void handlePageException(Exception ex)
+        throws IOException, ServletException
+    {
+        // Should never be called since handleException() called with a
+        // Throwable in the generated servlet.
+        handlePageException((Throwable) ex);
+    }
+
+    public void handlePageException(final Throwable t)
+        throws IOException, ServletException
+    {
+        if (t == null)
+            throw new NullPointerException("null Throwable");
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                AccessController.doPrivileged(new PrivilegedExceptionAction(){
+                    public Object run() throws Exception{
+                        doHandlePageException(t);
+                        return null;
+                    }
+                });
+            } catch (PrivilegedActionException e){
+                Exception ex =  e.getException();
+                if (ex instanceof IOException){
+                    throw (IOException)ex;
+                } else {
+                    throw (ServletException)ex;
+                }
+            }
+        } else {
+            doHandlePageException(t);
+        }
+
+    }
+
+    private void doHandlePageException(Throwable t)
+        throws IOException, ServletException {
+
+        if (errorPageURL != null && !errorPageURL.equals("")) {
+
+            /*
+             * Set request attributes.
+             * Do not set the javax.servlet.error.exception attribute here
+             * (instead, set in the generated servlet code for the error page)
+             * in order to prevent the ErrorReportValve, which is invoked as
+             * part of forwarding the request to the error page, from
+             * throwing it if the response has not been committed (the response
+             * will have been committed if the error page is a JSP page).
+             */
+            request.setAttribute("javax.servlet.jsp.jspException", t);
+            request.setAttribute("javax.servlet.error.status_code",
+                new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
+            request.setAttribute("javax.servlet.error.request_uri",
+            ((HttpServletRequest) request).getRequestURI());
+            request.setAttribute("javax.servlet.error.servlet_name",
+                     config.getServletName());
+            try {
+                forward(errorPageURL);
+            } catch (IllegalStateException ise) {
+                include(errorPageURL);
+            }
+
+            // The error page could be inside an include.
+
+            Object newException = request.getAttribute("javax.servlet.error.exception");
+
+            // t==null means the attribute was not set.
+            if( (newException!= null) && (newException==t) ) {
+                request.removeAttribute("javax.servlet.error.exception");
+            }
+
+            // now clear the error code - to prevent double handling.
+            request.removeAttribute("javax.servlet.error.status_code");
+            request.removeAttribute("javax.servlet.error.request_uri");
+            request.removeAttribute("javax.servlet.error.status_code");
+            request.removeAttribute("javax.servlet.jsp.jspException");
+
+        } else {
+            // Otherwise throw the exception wrapped inside a ServletException.
+            // Set the exception as the root cause in the ServletException
+            // to get a stack trace for the real problem
+            if (t instanceof IOException) throw (IOException)t;
+            if (t instanceof ServletException) throw (ServletException)t;
+            if (t instanceof RuntimeException) throw (RuntimeException)t;
+
+            Throwable rootCause = null;
+            if (t instanceof JspException) {
+                rootCause = ((JspException) t).getRootCause();
+            } else if (t instanceof ELException) {
+                rootCause = ((ELException) t).getRootCause();
+            }
+
+            if (rootCause != null) {
+                throw new ServletException(t.getClass().getName() + ": " + 
+                                           t.getMessage(), rootCause);
+            }
+                 
+            throw new ServletException(t);
+        }
+    }
+
+    /**
+     * VariableResolver interface
+     */
+    public Object resolveVariable(String pName) throws ELException {
+	return variableResolver.resolveVariable(pName);
+    }
+
+    private static String XmlEscape(String s) {
+        if (s == null) return null;
+        StringBuffer sb = new StringBuffer();
+        for(int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '<') {
+                sb.append("&lt;");
+            } else if (c == '>') {
+                sb.append("&gt;");
+            } else if (c == '\'') {
+                sb.append("&#039;");	// &apos;
+            } else if (c == '&') {
+                sb.append("&amp;");
+            } else if (c == '"') {
+                sb.append("&#034;");	// &quot;
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Proprietary method to evaluate EL expressions.
+     * XXX - This method should go away once the EL interpreter moves
+     * out of JSTL and into its own project.  For now, this is necessary
+     * because the standard machinery is too slow.
+     *
+     * @param expression The expression to be evaluated
+     * @param expectedType The expected resulting type
+     * @param pageContext The page context
+     * @param functionMap Maps prefix and name to Method
+     * @return The result of the evaluation
+     */
+    public static Object proprietaryEvaluate(final String expression, 
+					     final Class expectedType,
+					     final PageContext pageContext,
+					     final ProtectedFunctionMapper functionMap,
+					     final boolean escape)
+	throws ELException
+    {
+	Object retValue;
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try {
+                retValue = AccessController.doPrivileged(
+			new PrivilegedExceptionAction(){
+
+                    public Object run() throws Exception{
+                        return elExprEval.evaluate(expression,
+						   expectedType,
+						   pageContext.getVariableResolver(),
+						   functionMap);
+                    }
+                });
+            } catch (PrivilegedActionException ex) {
+                Exception realEx = ex.getException();
+		if (realEx instanceof ELException) {
+		    throw (ELException) realEx;
+		} else {
+		    throw new ELException(realEx);
+		}
+            }
+        } else {
+	    retValue = elExprEval.evaluate(expression,
+					   expectedType,
+					   pageContext.getVariableResolver(),
+					   functionMap);
+        }
+	if (escape) {
+	    retValue = XmlEscape(retValue.toString());
+	}
+
+	return retValue;
+    }
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/PerThreadTagHandlerPool.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/PerThreadTagHandlerPool.java
new file mode 100644
index 0000000..94b7a5c
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/PerThreadTagHandlerPool.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.Tag;
+
+import org.apache.jasper.Constants;
+
+/**
+ * Thread-local based pool of tag handlers that can be reused.
+ *
+ * @author Jan Luehe
+ * @author Costin Manolache
+ */
+public class PerThreadTagHandlerPool extends TagHandlerPool {
+
+    private int maxSize;
+
+    // For cleanup
+    private Vector perThreadDataVector;
+
+    private ThreadLocal perThread;
+
+    private static class PerThreadData {
+        Tag handlers[];
+        int current;
+    }
+
+    /**
+     * Constructs a tag handler pool with the default capacity.
+     */
+    public PerThreadTagHandlerPool() {
+        super();
+        perThreadDataVector = new Vector();
+    }
+
+    protected void init(ServletConfig config) {
+        maxSize = Constants.MAX_POOL_SIZE;
+        String maxSizeS = getOption(config, OPTION_MAXSIZE, null);
+        if (maxSizeS != null) {
+            maxSize = Integer.parseInt(maxSizeS);
+            if (maxSize < 0) {
+                maxSize = Constants.MAX_POOL_SIZE;
+            }
+        }
+
+        perThread = new ThreadLocal() {
+            protected Object initialValue() {
+                PerThreadData ptd = new PerThreadData();
+                ptd.handlers = new Tag[maxSize];
+                ptd.current = -1;
+                perThreadDataVector.addElement(ptd);
+                return ptd;
+            }
+        };
+    }
+
+    /**
+     * Gets the next available tag handler from this tag handler pool,
+     * instantiating one if this tag handler pool is empty.
+     *
+     * @param handlerClass Tag handler class
+     *
+     * @return Reused or newly instantiated tag handler
+     *
+     * @throws JspException if a tag handler cannot be instantiated
+     */
+    public Tag get(Class handlerClass) throws JspException {
+        PerThreadData ptd = (PerThreadData)perThread.get();
+        if(ptd.current >=0 ) {
+            return ptd.handlers[ptd.current--];
+        } else {
+	    try {
+		return (Tag) handlerClass.newInstance();
+	    } catch (Exception e) {
+		throw new JspException(e.getMessage(), e);
+	    }
+	}
+    }
+
+    /**
+     * Adds the given tag handler to this tag handler pool, unless this tag
+     * handler pool has already reached its capacity, in which case the tag
+     * handler's release() method is called.
+     *
+     * @param handler Tag handler to add to this tag handler pool
+     */
+    public void reuse(Tag handler) {
+        PerThreadData ptd=(PerThreadData)perThread.get();
+	if (ptd.current < (ptd.handlers.length - 1)) {
+	    ptd.handlers[++ptd.current] = handler;
+        } else {
+            handler.release();
+        }
+    }
+
+    /**
+     * Calls the release() method of all tag handlers in this tag handler pool.
+     */
+    public void release() {        
+        Enumeration enumeration = perThreadDataVector.elements();
+        while (enumeration.hasMoreElements()) {
+	    PerThreadData ptd = (PerThreadData)enumeration.nextElement();
+            if (ptd.handlers != null) {
+                for (int i=ptd.current; i>=0; i--) {
+                    if (ptd.handlers[i] != null) {
+                        ptd.handlers[i].release();
+		    }
+                }
+            }
+        }
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/ProtectedFunctionMapper.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/ProtectedFunctionMapper.java
new file mode 100644
index 0000000..2f0682f
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/ProtectedFunctionMapper.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.util.HashMap;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
+import java.lang.reflect.Method;
+import javax.servlet.jsp.el.FunctionMapper;
+
+import org.apache.jasper.security.SecurityUtil;
+
+/**
+ * Maps EL functions to their Java method counterparts.  Keeps the
+ * actual Method objects protected so that JSP pages can't indirectly
+ * do reflection.
+ *
+ * @author Mark Roth
+ * @author Kin-man Chung
+ */
+public final class ProtectedFunctionMapper implements FunctionMapper {
+
+    /** 
+     * Maps "prefix:name" to java.lang.Method objects.
+     */
+    private HashMap fnmap = null;
+
+    /**
+     * If there is only one function in the map, this is the Method for it.
+     */
+    private Method theMethod = null;
+
+    /**
+     * Constructor has protected access.
+     */
+    private ProtectedFunctionMapper() {
+    }
+
+    /**
+     * Generated Servlet and Tag Handler implementations call this
+     * method to retrieve an instance of the ProtectedFunctionMapper.
+     * This is necessary since generated code does not have access to
+     * create instances of classes in this package.
+     *
+     * @return A new protected function mapper.
+     */
+    public static ProtectedFunctionMapper getInstance() {
+        ProtectedFunctionMapper funcMapper;
+	if (SecurityUtil.isPackageProtectionEnabled()) {
+	    funcMapper = (ProtectedFunctionMapper)AccessController.doPrivileged(
+		new PrivilegedAction() {
+		public Object run() {
+		    return new ProtectedFunctionMapper();
+		}
+	    } );
+	} else {
+	    funcMapper = new ProtectedFunctionMapper();
+	}
+	funcMapper.fnmap = new java.util.HashMap();
+	return funcMapper;
+    }
+
+    /**
+     * Stores a mapping from the given EL function prefix and name to 
+     * the given Java method.
+     *
+     * @param fnQName The EL function qualified name (including prefix)
+     * @param c The class containing the Java method
+     * @param methodName The name of the Java method
+     * @param args The arguments of the Java method
+     * @throws RuntimeException if no method with the given signature
+     *     could be found.
+     */
+    public void mapFunction(String fnQName, final Class c,
+			    final String methodName, final Class[] args ) 
+    {
+	java.lang.reflect.Method method;
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                method = (java.lang.reflect.Method)AccessController.doPrivileged(new PrivilegedExceptionAction(){
+
+                    public Object run() throws Exception{
+                        return c.getDeclaredMethod(methodName, args);
+                    }                
+                });      
+            } catch (PrivilegedActionException ex){
+                throw new RuntimeException(
+                    "Invalid function mapping - no such method: "
+		    + ex.getException().getMessage());               
+            }
+        } else {
+             try {
+                method = c.getDeclaredMethod(methodName, args);
+            } catch( NoSuchMethodException e ) {
+                throw new RuntimeException(
+                    "Invalid function mapping - no such method: "
+		    + e.getMessage());
+            }
+        }
+
+	this.fnmap.put(fnQName, method );
+    }
+
+    /**
+     * Creates an instance for this class, and stores the Method for
+     * the given EL function prefix and name. This method is used for
+     * the case when there is only one function in the EL expression.
+     *
+     * @param fnQName The EL function qualified name (including prefix)
+     * @param c The class containing the Java method
+     * @param methodName The name of the Java method
+     * @param args The arguments of the Java method
+     * @throws RuntimeException if no method with the given signature
+     *     could be found.
+     */
+    public static ProtectedFunctionMapper getMapForFunction(
+		String fnQName, final Class c,
+                final String methodName, final Class[] args )
+    {
+        java.lang.reflect.Method method;
+        ProtectedFunctionMapper funcMapper;
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            funcMapper = (ProtectedFunctionMapper)AccessController.doPrivileged(
+                new PrivilegedAction(){
+                public Object run() {
+                    return new ProtectedFunctionMapper();
+                }
+            });
+
+            try{
+                method = (java.lang.reflect.Method)AccessController.doPrivileged
+(new PrivilegedExceptionAction(){
+
+                    public Object run() throws Exception{
+                        return c.getDeclaredMethod(methodName, args);
+                    }
+                });
+            } catch (PrivilegedActionException ex){
+                throw new RuntimeException(
+                    "Invalid function mapping - no such method: "
+                    + ex.getException().getMessage());
+            }
+        } else {
+	    funcMapper = new ProtectedFunctionMapper();
+             try {
+                method = c.getDeclaredMethod(methodName, args);
+            } catch( NoSuchMethodException e ) {
+                throw new RuntimeException(
+                    "Invalid function mapping - no such method: "
+                    + e.getMessage());
+            }
+        }
+        funcMapper.theMethod = method;
+	return funcMapper;
+    }
+
+    /**
+     * Resolves the specified local name and prefix into a Java.lang.Method.
+     * Returns null if the prefix and local name are not found.
+     * 
+     * @param prefix the prefix of the function
+     * @param localName the short name of the function
+     * @return the result of the method mapping.  Null means no entry found.
+     **/
+    public Method resolveFunction(String prefix, String localName) {
+        if (this.fnmap != null) {
+            return (Method) this.fnmap.get(prefix + ":" + localName);
+        }
+	return theMethod;
+    }
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/ServletResponseWrapperInclude.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/ServletResponseWrapperInclude.java
new file mode 100644
index 0000000..25ad3ce
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/ServletResponseWrapperInclude.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import javax.servlet.jsp.JspWriter;
+
+/**
+ * ServletResponseWrapper used by the JSP 'include' action.
+ *
+ * This wrapper response object is passed to RequestDispatcher.include(), so
+ * that the output of the included resource is appended to that of the
+ * including page.
+ *
+ * @author Pierre Delisle
+ */
+
+public class ServletResponseWrapperInclude extends HttpServletResponseWrapper {
+
+    /**
+     * PrintWriter which appends to the JspWriter of the including page.
+     */
+    private PrintWriter printWriter;
+
+    private JspWriter jspWriter;
+
+    public ServletResponseWrapperInclude(ServletResponse response, 
+					 JspWriter jspWriter) {
+	super((HttpServletResponse)response);
+	this.printWriter = new PrintWriter(jspWriter);
+	this.jspWriter = jspWriter;
+    }
+
+    /**
+     * Returns a wrapper around the JspWriter of the including page.
+     */
+    public PrintWriter getWriter() throws IOException {
+	return printWriter;
+    }
+
+    public ServletOutputStream getOutputStream() throws IOException {
+	throw new IllegalStateException();
+    }
+
+    /**
+     * Clears the output buffer of the JspWriter associated with the including
+     * page.
+     */
+    public void resetBuffer() {
+	try {
+	    jspWriter.clearBuffer();
+	} catch (IOException ioe) {
+	}
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/runtime/TagHandlerPool.java b/jasper/jasper2/src/share/org/apache/jasper/runtime/TagHandlerPool.java
new file mode 100644
index 0000000..bba9e30
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/runtime/TagHandlerPool.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.Tag;
+import javax.servlet.ServletConfig;
+import org.apache.jasper.Constants;
+
+/**
+ * Pool of tag handlers that can be reused.
+ *
+ * @author Jan Luehe
+ */
+public class TagHandlerPool {
+
+    private Tag[] handlers;
+
+    public static String OPTION_TAGPOOL="tagpoolClassName";
+    public static String OPTION_MAXSIZE="tagpoolMaxSize";
+
+    // index of next available tag handler
+    private int current;
+
+    public static TagHandlerPool getTagHandlerPool( ServletConfig config) {
+        TagHandlerPool result=null;
+
+        String tpClassName=getOption( config, OPTION_TAGPOOL, null);
+        if( tpClassName != null ) {
+            try {
+                Class c=Class.forName( tpClassName );
+                result=(TagHandlerPool)c.newInstance();
+            } catch (Exception e) {
+                e.printStackTrace();
+                result=null;
+            }
+        }
+        if( result==null ) result=new TagHandlerPool();
+        result.init(config);
+
+        return result;
+    }
+
+    protected void init( ServletConfig config ) {
+        int maxSize=-1;
+        String maxSizeS=getOption(config, OPTION_MAXSIZE, null);
+        if( maxSizeS != null ) {
+            try {
+                maxSize=Integer.parseInt(maxSizeS);
+            } catch( Exception ex) {
+                maxSize=-1;
+            }
+        }
+        if( maxSize <0  ) {
+            maxSize=Constants.MAX_POOL_SIZE;
+        }
+        this.handlers = new Tag[maxSize];
+        this.current = -1;
+    }
+
+    /**
+     * Constructs a tag handler pool with the default capacity.
+     */
+    public TagHandlerPool() {
+	// Nothing - jasper generated servlets call the other constructor,
+        // this should be used in future + init .
+    }
+
+    /**
+     * Constructs a tag handler pool with the given capacity.
+     *
+     * @param capacity Tag handler pool capacity
+     * @deprecated Use static getTagHandlerPool
+     */
+    public TagHandlerPool(int capacity) {
+	this.handlers = new Tag[capacity];
+	this.current = -1;
+    }
+
+    /**
+     * Gets the next available tag handler from this tag handler pool,
+     * instantiating one if this tag handler pool is empty.
+     *
+     * @param handlerClass Tag handler class
+     *
+     * @return Reused or newly instantiated tag handler
+     *
+     * @throws JspException if a tag handler cannot be instantiated
+     */
+    public Tag get(Class handlerClass) throws JspException {
+	Tag handler = null;
+        synchronized( this ) {
+            if (current >= 0) {
+                handler = handlers[current--];
+                return handler;
+            }
+        }
+
+        // Out of sync block - there is no need for other threads to
+        // wait for us to construct a tag for this thread.
+        try {
+            return (Tag) handlerClass.newInstance();
+        } catch (Exception e) {
+            throw new JspException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Adds the given tag handler to this tag handler pool, unless this tag
+     * handler pool has already reached its capacity, in which case the tag
+     * handler's release() method is called.
+     *
+     * @param handler Tag handler to add to this tag handler pool
+     */
+    public void reuse(Tag handler) {
+        synchronized( this ) {
+            if (current < (handlers.length - 1)) {
+                handlers[++current] = handler;
+                return;
+            }
+        }
+        // There is no need for other threads to wait for us to release
+        handler.release();
+    }
+
+    /**
+     * Calls the release() method of all available tag handlers in this tag
+     * handler pool.
+     */
+    public synchronized void release() {
+	for (int i=current; i>=0; i--) {
+	    handlers[i].release();
+	}
+    }
+
+    protected static String getOption( ServletConfig config, String name, String defaultV) {
+        if( config == null ) return defaultV;
+
+        String value=config.getInitParameter(name);
+        if( value != null ) return value;
+        if( config.getServletContext() ==null )
+            return defaultV;
+        value=config.getServletContext().getInitParameter(name);
+        if( value!=null ) return value;
+        return defaultV;
+    }
+
+}
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/security/SecurityClassLoad.java b/jasper/jasper2/src/share/org/apache/jasper/security/SecurityClassLoad.java
new file mode 100644
index 0000000..fb48df5
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/security/SecurityClassLoad.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.jasper.security;
+
+/**
+ * Static class used to preload java classes when using the
+ * Java SecurityManager so that the defineClassInPackage
+ * RuntimePermission does not trigger an AccessControlException.
+ *
+ * @author Jean-Francois Arcand
+ */
+
+public final class SecurityClassLoad {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( SecurityClassLoad.class );
+
+    public static void securityClassLoad(ClassLoader loader){
+
+        if( System.getSecurityManager() == null ){
+            return;
+        }
+
+        String basePackage = "org.apache.jasper.";
+        try {
+            loader.loadClass( basePackage +
+                "runtime.JspFactoryImpl$PrivilegedGetPageContext");
+            loader.loadClass( basePackage +
+                "runtime.JspFactoryImpl$PrivilegedReleasePageContext");
+
+            loader.loadClass( basePackage +
+                "runtime.JspRuntimeLibrary");
+            loader.loadClass( basePackage +
+                "runtime.JspRuntimeLibrary$PrivilegedIntrospectHelper");
+            
+            loader.loadClass( basePackage +
+                "runtime.ServletResponseWrapperInclude");
+            loader.loadClass( basePackage +
+                "runtime.TagHandlerPool");
+            loader.loadClass( basePackage +
+                "runtime.JspFragmentHelper");
+
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper");
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper$1");
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper$2"); 
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper$3");
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper$4"); 
+
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$1");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$2");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$3");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$4");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$5");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$6");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$7");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$8");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$9");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$10");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$11");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$12");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$13");      
+
+            loader.loadClass( basePackage +
+                "runtime.JspContextWrapper");   
+
+            loader.loadClass( basePackage +
+                "servlet.JspServletWrapper");
+
+            loader.loadClass( basePackage +
+                "runtime.JspWriterImpl$1");
+        } catch (ClassNotFoundException ex) {
+            log.error("SecurityClassLoad", ex);
+        }
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/security/SecurityUtil.java b/jasper/jasper2/src/share/org/apache/jasper/security/SecurityUtil.java
new file mode 100644
index 0000000..32ca26b
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/security/SecurityUtil.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.security;
+
+/**
+ * Util class for Security related operations.
+ *
+ * @author Jean-Francois Arcand
+ */
+
+public final class SecurityUtil{
+    
+    private static boolean packageDefinitionEnabled =  
+         System.getProperty("package.definition") == null ? false : true;
+    
+    /**
+     * Return the <code>SecurityManager</code> only if Security is enabled AND
+     * package protection mechanism is enabled.
+     */
+    public static boolean isPackageProtectionEnabled(){
+        if (packageDefinitionEnabled && System.getSecurityManager() !=  null){
+            return true;
+        }
+        return false;
+    }
+    
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/servlet/JasperLoader.java b/jasper/jasper2/src/share/org/apache/jasper/servlet/JasperLoader.java
new file mode 100644
index 0000000..1acfd10
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/servlet/JasperLoader.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.servlet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSource;
+import java.security.PermissionCollection;
+
+import org.apache.jasper.Constants;
+
+/**
+ * Class loader for loading servlet class files (corresponding to JSP files) 
+ * and tag handler class files (corresponding to tag files).
+ *
+ * @author Anil K. Vijendran
+ * @author Harish Prabandham
+ * @author Jean-Francois Arcand
+ */
+public class JasperLoader extends URLClassLoader {
+
+    private PermissionCollection permissionCollection;
+    private CodeSource codeSource;
+    private String className;
+    private ClassLoader parent;
+    private SecurityManager securityManager;
+
+    public JasperLoader(URL[] urls, ClassLoader parent,
+			PermissionCollection permissionCollection,
+			CodeSource codeSource) {
+	super(urls, parent);
+	this.permissionCollection = permissionCollection;
+	this.codeSource = codeSource;
+	this.parent = parent;
+	this.securityManager = System.getSecurityManager();
+    }
+
+    /**
+     * Load the class with the specified name.  This method searches for
+     * classes in the same manner as <code>loadClass(String, boolean)</code>
+     * with <code>false</code> as the second argument.
+     *
+     * @param name Name of the class to be loaded
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class loadClass(String name) throws ClassNotFoundException {
+
+        return (loadClass(name, false));
+    }
+
+    /**
+     * Load the class with the specified name, searching using the following
+     * algorithm until it finds and returns the class.  If the class cannot
+     * be found, returns <code>ClassNotFoundException</code>.
+     * <ul>
+     * <li>Call <code>findLoadedClass(String)</code> to check if the
+     *     class has already been loaded.  If it has, the same
+     *     <code>Class</code> object is returned.</li>
+     * <li>If the <code>delegate</code> property is set to <code>true</code>,
+     *     call the <code>loadClass()</code> method of the parent class
+     *     loader, if any.</li>            
+     * <li>Call <code>findClass()</code> to find this class in our locally
+     *     defined repositories.</li>      
+     * <li>Call the <code>loadClass()</code> method of our parent
+     *     class loader, if any.</li>      
+     * </ul>
+     * If the class was found using the above steps, and the
+     * <code>resolve</code> flag is <code>true</code>, this method will then
+     * call <code>resolveClass(Class)</code> on the resulting Class object.
+     *                                     
+     * @param name Name of the class to be loaded
+     * @param resolve If <code>true</code> then resolve the class
+     *                                     
+     * @exception ClassNotFoundException if the class was not found
+     */                                    
+    public Class loadClass(final String name, boolean resolve)
+        throws ClassNotFoundException {
+
+        Class clazz = null;                
+                                           
+        // (0) Check our previously loaded class cache
+        clazz = findLoadedClass(name);     
+        if (clazz != null) {               
+            if (resolve)                   
+                resolveClass(clazz);       
+            return (clazz);        
+        }                          
+                          
+        // (.5) Permission to access this class when using a SecurityManager
+        if (securityManager != null) {     
+            int dot = name.lastIndexOf('.');
+            if (dot >= 0) {                
+                try {        
+                    // Do not call the security manager since by default, we grant that package.
+                    if (!"org.apache.jasper.runtime".equalsIgnoreCase(name.substring(0,dot))){
+                        securityManager.checkPackageAccess(name.substring(0,dot));
+                    }
+                } catch (SecurityException se) {
+                    String error = "Security Violation, attempt to use " +
+                        "Restricted Class: " + name;
+                    se.printStackTrace();
+                    throw new ClassNotFoundException(error);
+                }                          
+            }                              
+        }
+
+	if( !name.startsWith(Constants.JSP_PACKAGE_NAME) ) {
+            // Class is not in org.apache.jsp, therefore, have our
+            // parent load it
+            clazz = parent.loadClass(name);            
+	    if( resolve )
+		resolveClass(clazz);
+	    return clazz;
+	}
+
+	return findClass(name);
+    }
+
+    
+    /**
+     * Delegate to parent
+     * 
+     * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
+     */
+    public InputStream getResourceAsStream(String name) {
+        InputStream is = parent.getResourceAsStream(name);
+        if (is == null) {
+            URL url = findResource(name);
+            if (url != null) {
+                try {
+                    is = url.openStream();
+                } catch (IOException e) {
+                    is = null;
+                }
+            }
+        }
+        return is;
+    }
+    
+    
+    /**
+     * Get the Permissions for a CodeSource.
+     *
+     * Since this ClassLoader is only used for a JSP page in
+     * a web application context, we just return our preset
+     * PermissionCollection for the web app context.
+     *
+     * @param codeSource Code source where the code was loaded from
+     * @return PermissionCollection for CodeSource
+     */
+    public final PermissionCollection getPermissions(CodeSource codeSource) {
+        return permissionCollection;
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/servlet/JspCServletContext.java b/jasper/jasper2/src/share/org/apache/jasper/servlet/JspCServletContext.java
new file mode 100644
index 0000000..6755e36
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/servlet/JspCServletContext.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.servlet;
+
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+
+/**
+ * Simple <code>ServletContext</code> implementation without
+ * HTTP-specific methods.
+ *
+ * @author Peter Rossbach (pr@webapp.de)
+ */
+
+public class JspCServletContext implements ServletContext {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Servlet context attributes.
+     */
+    protected Hashtable myAttributes;
+
+
+    /**
+     * The log writer we will write log messages to.
+     */
+    protected PrintWriter myLogWriter;
+
+
+    /**
+     * The base URL (document root) for this context.
+     */
+    protected URL myResourceBaseURL;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new instance of this ServletContext implementation.
+     *
+     * @param aLogWriter PrintWriter which is used for <code>log()</code> calls
+     * @param aResourceBaseURL Resource base URL
+     */
+    public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL) {
+
+        myAttributes = new Hashtable();
+        myLogWriter = aLogWriter;
+        myResourceBaseURL = aResourceBaseURL;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the specified context attribute, if any.
+     *
+     * @param name Name of the requested attribute
+     */
+    public Object getAttribute(String name) {
+
+        return (myAttributes.get(name));
+
+    }
+
+
+    /**
+     * Return an enumeration of context attribute names.
+     */
+    public Enumeration getAttributeNames() {
+
+        return (myAttributes.keys());
+
+    }
+
+
+    /**
+     * Return the servlet context for the specified path.
+     *
+     * @param uripath Server-relative path starting with '/'
+     */
+    public ServletContext getContext(String uripath) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the specified context initialization parameter.
+     *
+     * @param name Name of the requested parameter
+     */
+    public String getInitParameter(String name) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return an enumeration of the names of context initialization
+     * parameters.
+     */
+    public Enumeration getInitParameterNames() {
+
+        return (new Vector().elements());
+
+    }
+
+
+    /**
+     * Return the Servlet API major version number.
+     */
+    public int getMajorVersion() {
+
+        return (2);
+
+    }
+
+
+    /**
+     * Return the MIME type for the specified filename.
+     *
+     * @param file Filename whose MIME type is requested
+     */
+    public String getMimeType(String file) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the Servlet API minor version number.
+     */
+    public int getMinorVersion() {
+
+        return (3);
+
+    }
+
+
+    /**
+     * Return a request dispatcher for the specified servlet name.
+     *
+     * @param name Name of the requested servlet
+     */
+    public RequestDispatcher getNamedDispatcher(String name) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the real path for the specified context-relative
+     * virtual path.
+     *
+     * @param path The context-relative virtual path to resolve
+     */
+    public String getRealPath(String path) {
+
+        if (!myResourceBaseURL.getProtocol().equals("file"))
+            return (null);
+        if (!path.startsWith("/"))
+            return (null);
+        try {
+            return
+                (getResource(path).getFile().replace('/', File.separatorChar));
+        } catch (Throwable t) {
+            return (null);
+        }
+
+    }
+            
+            
+    /**
+     * Return a request dispatcher for the specified context-relative path.
+     *
+     * @param path Context-relative path for which to acquire a dispatcher
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return a URL object of a resource that is mapped to the
+     * specified context-relative path.
+     *
+     * @param path Context-relative path of the desired resource
+     *
+     * @exception MalformedURLException if the resource path is
+     *  not properly formed
+     */
+    public URL getResource(String path) throws MalformedURLException {
+
+        if (!path.startsWith("/"))
+            throw new MalformedURLException("Path '" + path +
+                                            "' does not start with '/'");
+        URL url = new URL(myResourceBaseURL, path.substring(1));
+        InputStream is = null;
+        try {
+            is = url.openStream();
+        } catch (Throwable t) {
+            url = null;
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (Throwable t2) {
+                    // Ignore
+                }
+            }
+        }
+        return url;
+
+    }
+
+
+    /**
+     * Return an InputStream allowing access to the resource at the
+     * specified context-relative path.
+     *
+     * @param path Context-relative path of the desired resource
+     */
+    public InputStream getResourceAsStream(String path) {
+
+        try {
+            return (getResource(path).openStream());
+        } catch (Throwable t) {
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Return the set of resource paths for the "directory" at the
+     * specified context path.
+     *
+     * @param path Context-relative base path
+     */
+    public Set getResourcePaths(String path) {
+
+        Set thePaths = new HashSet();
+        if (!path.endsWith("/"))
+            path += "/";
+        String basePath = getRealPath(path);
+        if (basePath == null)
+            return (thePaths);
+        File theBaseDir = new File(basePath);
+        if (!theBaseDir.exists() || !theBaseDir.isDirectory())
+            return (thePaths);
+        String theFiles[] = theBaseDir.list();
+        for (int i = 0; i < theFiles.length; i++) {
+            File testFile = new File(basePath + File.separator + theFiles[i]);
+            if (testFile.isFile())
+                thePaths.add(path + theFiles[i]);
+            else if (testFile.isDirectory())
+                thePaths.add(path + theFiles[i] + "/");
+        }
+        return (thePaths);
+
+    }
+
+
+    /**
+     * Return descriptive information about this server.
+     */
+    public String getServerInfo() {
+
+        return ("JspCServletContext/1.0");
+
+    }
+
+
+    /**
+     * Return a null reference for the specified servlet name.
+     *
+     * @param name Name of the requested servlet
+     *
+     * @deprecated This method has been deprecated with no replacement
+     */
+    public Servlet getServlet(String name) throws ServletException {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the name of this servlet context.
+     */
+    public String getServletContextName() {
+
+        return (getServerInfo());
+
+    }
+
+
+    /**
+     * Return an empty enumeration of servlet names.
+     *
+     * @deprecated This method has been deprecated with no replacement
+     */
+    public Enumeration getServletNames() {
+
+        return (new Vector().elements());
+
+    }
+
+
+    /**
+     * Return an empty enumeration of servlets.
+     *
+     * @deprecated This method has been deprecated with no replacement
+     */
+    public Enumeration getServlets() {
+
+        return (new Vector().elements());
+
+    }
+
+
+    /**
+     * Log the specified message.
+     *
+     * @param message The message to be logged
+     */
+    public void log(String message) {
+
+        myLogWriter.println(message);
+
+    }
+
+
+    /**
+     * Log the specified message and exception.
+     *
+     * @param exception The exception to be logged
+     * @param message The message to be logged
+     *
+     * @deprecated Use log(String,Throwable) instead
+     */
+    public void log(Exception exception, String message) {
+
+        log(message, exception);
+
+    }
+
+
+    /**
+     * Log the specified message and exception.
+     *
+     * @param message The message to be logged
+     * @param exception The exception to be logged
+     */
+    public void log(String message, Throwable exception) {
+
+        myLogWriter.println(message);
+        exception.printStackTrace(myLogWriter);
+
+    }
+
+
+    /**
+     * Remove the specified context attribute.
+     *
+     * @param name Name of the attribute to remove
+     */
+    public void removeAttribute(String name) {
+
+        myAttributes.remove(name);
+
+    }
+
+
+    /**
+     * Set or replace the specified context attribute.
+     *
+     * @param name Name of the context attribute to set
+     * @param value Corresponding attribute value
+     */
+    public void setAttribute(String name, Object value) {
+
+        myAttributes.put(name, value);
+
+    }
+
+
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServlet.java b/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServlet.java
new file mode 100644
index 0000000..82e5f5e
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServlet.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.servlet;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.Enumeration;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.EmbeddedServletOptions;
+import org.apache.jasper.Options;
+import org.apache.jasper.compiler.JspRuntimeContext;
+import org.apache.jasper.compiler.Localizer;
+
+/**
+ * The JSP engine (a.k.a Jasper).
+ *
+ * The servlet container is responsible for providing a
+ * URLClassLoader for the web application context Jasper
+ * is being used in. Jasper will try get the Tomcat
+ * ServletContext attribute for its ServletContext class
+ * loader, if that fails, it uses the parent class loader.
+ * In either case, it must be a URLClassLoader.
+ *
+ * @author Anil K. Vijendran
+ * @author Harish Prabandham
+ * @author Remy Maucherat
+ * @author Kin-man Chung
+ * @author Glenn Nielsen
+ */
+public class JspServlet extends HttpServlet {
+
+    // Logger
+    private Log log = LogFactory.getLog(JspServlet.class);
+
+    private ServletContext context;
+    private ServletConfig config;
+    private Options options;
+    private JspRuntimeContext rctxt;
+
+
+    /*
+     * Initializes this JspServlet.
+     */
+    public void init(ServletConfig config) throws ServletException {
+        
+        super.init(config);
+        this.config = config;
+        this.context = config.getServletContext();
+        
+        // Initialize the JSP Runtime Context
+        // Check for a custom Options implementation
+        String engineOptionsName = 
+            config.getInitParameter("engineOptionsClass");
+        if (engineOptionsName != null) {
+            // Instantiate the indicated Options implementation
+            try {
+                ClassLoader loader = Thread.currentThread()
+                        .getContextClassLoader();
+                Class engineOptionsClass = loader.loadClass(engineOptionsName);
+                Class[] ctorSig = { ServletConfig.class, ServletContext.class };
+                Constructor ctor = engineOptionsClass.getConstructor(ctorSig);
+                Object[] args = { config, context };
+                options = (Options) ctor.newInstance(args);
+            } catch (Throwable e) {
+                // Need to localize this.
+                log.warn("Failed to load engineOptionsClass", e);
+                // Use the default Options implementation
+                options = new EmbeddedServletOptions(config, context);
+            }
+        } else {
+            // Use the default Options implementation
+            options = new EmbeddedServletOptions(config, context);
+        }
+        rctxt = new JspRuntimeContext(context, options);
+        
+        if (log.isDebugEnabled()) {
+            log.debug(Localizer.getMessage("jsp.message.scratch.dir.is",
+                    options.getScratchDir().toString()));
+            log.debug(Localizer.getMessage("jsp.message.dont.modify.servlets"));
+        }
+    }
+
+
+    /**
+     * Returns the number of JSPs for which JspServletWrappers exist, i.e.,
+     * the number of JSPs that have been loaded into the webapp with which
+     * this JspServlet is associated.
+     *
+     * <p>This info may be used for monitoring purposes.
+     *
+     * @return The number of JSPs that have been loaded into the webapp with
+     * which this JspServlet is associated
+     */
+    public int getJspCount() {
+        return this.rctxt.getJspCount();
+    }
+
+
+    /**
+     * Resets the JSP reload counter.
+     *
+     * @param count Value to which to reset the JSP reload counter
+     */
+    public void setJspReloadCount(int count) {
+        this.rctxt.setJspReloadCount(count);
+    }
+
+
+    /**
+     * Gets the number of JSPs that have been reloaded.
+     *
+     * <p>This info may be used for monitoring purposes.
+     *
+     * @return The number of JSPs (in the webapp with which this JspServlet is
+     * associated) that have been reloaded
+     */
+    public int getJspReloadCount() {
+        return this.rctxt.getJspReloadCount();
+    }
+
+
+    /**
+     * <p>Look for a <em>precompilation request</em> as described in
+     * Section 8.4.2 of the JSP 1.2 Specification.  <strong>WARNING</strong> -
+     * we cannot use <code>request.getParameter()</code> for this, because
+     * that will trigger parsing all of the request parameters, and not give
+     * a servlet the opportunity to call
+     * <code>request.setCharacterEncoding()</code> first.</p>
+     *
+     * @param request The servlet requset we are processing
+     *
+     * @exception ServletException if an invalid parameter value for the
+     *  <code>jsp_precompile</code> parameter name is specified
+     */
+    boolean preCompile(HttpServletRequest request) throws ServletException {
+
+        String queryString = request.getQueryString();
+        if (queryString == null) {
+            return (false);
+        }
+        int start = queryString.indexOf(Constants.PRECOMPILE);
+        if (start < 0) {
+            return (false);
+        }
+        queryString =
+            queryString.substring(start + Constants.PRECOMPILE.length());
+        if (queryString.length() == 0) {
+            return (true);             // ?jsp_precompile
+        }
+        if (queryString.startsWith("&")) {
+            return (true);             // ?jsp_precompile&foo=bar...
+        }
+        if (!queryString.startsWith("=")) {
+            return (false);            // part of some other name or value
+        }
+        int limit = queryString.length();
+        int ampersand = queryString.indexOf("&");
+        if (ampersand > 0) {
+            limit = ampersand;
+        }
+        String value = queryString.substring(1, limit);
+        if (value.equals("true")) {
+            return (true);             // ?jsp_precompile=true
+        } else if (value.equals("false")) {
+	    // Spec says if jsp_precompile=false, the request should not
+	    // be delivered to the JSP page; the easiest way to implement
+	    // this is to set the flag to true, and precompile the page anyway.
+	    // This still conforms to the spec, since it says the
+	    // precompilation request can be ignored.
+            return (true);             // ?jsp_precompile=false
+        } else {
+            throw new ServletException("Cannot have request parameter " +
+                                       Constants.PRECOMPILE + " set to " +
+                                       value);
+        }
+
+    }
+    
+
+    public void service (HttpServletRequest request, 
+    			 HttpServletResponse response)
+                throws ServletException, IOException {
+
+        String jspUri = null;
+
+        String jspFile = (String) request.getAttribute(Constants.JSP_FILE);
+        if (jspFile != null) {
+            // JSP is specified via <jsp-file> in <servlet> declaration
+            jspUri = jspFile;
+        } else {
+            /*
+             * Check to see if the requested JSP has been the target of a
+             * RequestDispatcher.include()
+             */
+            jspUri = (String) request.getAttribute(Constants.INC_SERVLET_PATH);
+            if (jspUri != null) {
+                /*
+		 * Requested JSP has been target of
+                 * RequestDispatcher.include(). Its path is assembled from the
+                 * relevant javax.servlet.include.* request attributes
+                 */
+                String pathInfo = (String) request.getAttribute(
+                                    "javax.servlet.include.path_info");
+                if (pathInfo != null) {
+                    jspUri += pathInfo;
+                }
+            } else {
+                /*
+                 * Requested JSP has not been the target of a 
+                 * RequestDispatcher.include(). Reconstruct its path from the
+                 * request's getServletPath() and getPathInfo()
+                 */
+                jspUri = request.getServletPath();
+                String pathInfo = request.getPathInfo();
+                if (pathInfo != null) {
+                    jspUri += pathInfo;
+                }
+            }
+        }
+
+        if (log.isDebugEnabled()) {	    
+            log.debug("JspEngine --> " + jspUri);
+            log.debug("\t     ServletPath: " + request.getServletPath());
+            log.debug("\t        PathInfo: " + request.getPathInfo());
+            log.debug("\t        RealPath: " + context.getRealPath(jspUri));
+            log.debug("\t      RequestURI: " + request.getRequestURI());
+            log.debug("\t     QueryString: " + request.getQueryString());
+            log.debug("\t  Request Params: ");
+            Enumeration e = request.getParameterNames();
+            while (e.hasMoreElements()) {
+                String name = (String) e.nextElement();
+                log.debug("\t\t " + name + " = " 
+                          + request.getParameter(name));
+            }
+        }
+
+        try {
+            boolean precompile = preCompile(request);
+            serviceJspFile(request, response, jspUri, null, precompile);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (ServletException e) {
+            throw e;
+        } catch (IOException e) {
+            throw e;
+        } catch (Throwable e) {
+            throw new ServletException(e);
+        }
+
+    }
+
+    public void destroy() {
+        if (log.isDebugEnabled()) {
+            log.debug("JspServlet.destroy()");
+        }
+
+        rctxt.destroy();
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+    private void serviceJspFile(HttpServletRequest request,
+                                HttpServletResponse response, String jspUri,
+                                Throwable exception, boolean precompile)
+        throws ServletException, IOException {
+
+        JspServletWrapper wrapper =
+            (JspServletWrapper) rctxt.getWrapper(jspUri);
+        if (wrapper == null) {
+            synchronized(this) {
+                wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);
+                if (wrapper == null) {
+                    // Check if the requested JSP page exists, to avoid
+                    // creating unnecessary directories and files.
+                    if (null == context.getResource(jspUri)) {
+                        response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                           jspUri);
+                        return;
+                    }
+                    boolean isErrorPage = exception != null;
+                    wrapper = new JspServletWrapper(config, options, jspUri,
+                                                    isErrorPage, rctxt);
+                    rctxt.addWrapper(jspUri,wrapper);
+                }
+            }
+        }
+
+        wrapper.service(request, response, precompile);
+
+    }
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServletWrapper.java b/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServletWrapper.java
new file mode 100644
index 0000000..0319dae
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServletWrapper.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.servlet;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.SingleThreadModel;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.tagext.TagInfo;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.Options;
+import org.apache.jasper.compiler.JspRuntimeContext;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.runtime.JspSourceDependent;
+
+/**
+ * The JSP engine (a.k.a Jasper).
+ *
+ * The servlet container is responsible for providing a
+ * URLClassLoader for the web application context Jasper
+ * is being used in. Jasper will try get the Tomcat
+ * ServletContext attribute for its ServletContext class
+ * loader, if that fails, it uses the parent class loader.
+ * In either case, it must be a URLClassLoader.
+ *
+ * @author Anil K. Vijendran
+ * @author Harish Prabandham
+ * @author Remy Maucherat
+ * @author Kin-man Chung
+ * @author Glenn Nielsen
+ */
+
+public class JspServletWrapper {
+
+    // Logger
+    private Log log = LogFactory.getLog(JspServletWrapper.class);
+
+    private Servlet theServlet;
+    private String jspUri;
+    private Class servletClass;
+    private Class tagHandlerClass;
+    private JspCompilationContext ctxt;
+    private long available = 0L;
+    private ServletConfig config;
+    private Options options;
+    private boolean firstTime = true;
+    private boolean reload = true;
+    private boolean isTagFile;
+    private int tripCount;
+    private JasperException compileException;
+    private long servletClassLastModifiedTime;
+    private long lastModificationTest = 0L;
+
+    /*
+     * JspServletWrapper for JSP pages.
+     */
+    JspServletWrapper(ServletConfig config, Options options, String jspUri,
+                      boolean isErrorPage, JspRuntimeContext rctxt)
+            throws JasperException {
+
+	this.isTagFile = false;
+        this.config = config;
+        this.options = options;
+        this.jspUri = jspUri;
+        ctxt = new JspCompilationContext(jspUri, isErrorPage, options,
+					 config.getServletContext(),
+					 this, rctxt);
+    }
+
+    /*
+     * JspServletWrapper for tag files.
+     */
+    public JspServletWrapper(ServletContext servletContext,
+			     Options options,
+			     String tagFilePath,
+			     TagInfo tagInfo,
+			     JspRuntimeContext rctxt,
+			     URL tagFileJarUrl)
+	    throws JasperException {
+
+	this.isTagFile = true;
+        this.config = null;	// not used
+        this.options = options;
+	this.jspUri = tagFilePath;
+	this.tripCount = 0;
+        ctxt = new JspCompilationContext(jspUri, tagInfo, options,
+					 servletContext, this, rctxt,
+					 tagFileJarUrl);
+    }
+
+    public JspCompilationContext getJspEngineContext() {
+        return ctxt;
+    }
+
+    public void setReload(boolean reload) {
+        this.reload = reload;
+    }
+
+    public Servlet getServlet()
+        throws ServletException, IOException, FileNotFoundException
+    {
+        if (reload) {
+            synchronized (this) {
+                // Synchronizing on jsw enables simultaneous loading
+                // of different pages, but not the same page.
+                if (reload) {
+                    // This is to maintain the original protocol.
+                    destroy();
+                    
+                    try {
+                        servletClass = ctxt.load();
+                        theServlet = (Servlet) servletClass.newInstance();
+                    } catch( IllegalAccessException ex1 ) {
+                        throw new JasperException( ex1 );
+                    } catch( InstantiationException ex ) {
+                        throw new JasperException( ex );
+                    }
+                    
+                    theServlet.init(config);
+
+                    if (!firstTime) {
+                        ctxt.getRuntimeContext().incrementJspReloadCount();
+                    }
+
+                    reload = false;
+                }
+            }    
+        }
+        return theServlet;
+    }
+
+    public ServletContext getServletContext() {
+        return config.getServletContext();
+    }
+
+    /**
+     * Sets the compilation exception for this JspServletWrapper.
+     *
+     * @param je The compilation exception
+     */
+    public void setCompilationException(JasperException je) {
+        this.compileException = je;
+    }
+
+    /**
+     * Sets the last-modified time of the servlet class file associated with
+     * this JspServletWrapper.
+     *
+     * @param lastModified Last-modified time of servlet class
+     */
+    public void setServletClassLastModifiedTime(long lastModified) {
+        if (this.servletClassLastModifiedTime < lastModified) {
+            synchronized (this) {
+                if (this.servletClassLastModifiedTime < lastModified) {
+                    this.servletClassLastModifiedTime = lastModified;
+                    reload = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * Compile (if needed) and load a tag file
+     */
+    public Class loadTagFile() throws JasperException {
+
+        try {
+            if (ctxt.isRemoved()) {
+                throw new FileNotFoundException(jspUri);
+            }
+            if (options.getDevelopment() || firstTime ) {
+                synchronized (this) {
+                    firstTime = false;
+                    ctxt.compile();
+                }
+            } else {
+                if (compileException != null) {
+                    throw compileException;
+                }
+            }
+
+            if (reload) {
+                tagHandlerClass = ctxt.load();
+            }
+        } catch (FileNotFoundException ex) {
+            throw new JasperException(ex);
+	}
+
+	return tagHandlerClass;
+    }
+
+    /**
+     * Compile and load a prototype for the Tag file.  This is needed
+     * when compiling tag files with circular dependencies.  A prototpe
+     * (skeleton) with no dependencies on other other tag files is
+     * generated and compiled.
+     */
+    public Class loadTagFilePrototype() throws JasperException {
+
+	ctxt.setPrototypeMode(true);
+	try {
+	    return loadTagFile();
+	} finally {
+	    ctxt.setPrototypeMode(false);
+	}
+    }
+
+    /**
+     * Get a list of files that the current page has source dependency on.
+     */
+    public java.util.List getDependants() {
+	try {
+	    Object target;
+	    if (isTagFile) {
+                if (reload) {
+                    tagHandlerClass = ctxt.load();
+                }
+		target = tagHandlerClass.newInstance();
+	    } else {
+		target = getServlet();
+	    }
+	    if (target != null && target instanceof JspSourceDependent) {
+            return ((java.util.List) ((JspSourceDependent) target).getDependants());
+	    }
+	} catch (Throwable ex) {
+	}
+	return null;
+    }
+
+    public boolean isTagFile() {
+	return this.isTagFile;
+    }
+
+    public int incTripCount() {
+	return tripCount++;
+    }
+
+    public int decTripCount() {
+	return tripCount--;
+    }
+
+    public void service(HttpServletRequest request, 
+                        HttpServletResponse response,
+                        boolean precompile)
+	    throws ServletException, IOException, FileNotFoundException {
+        try {
+
+            if (ctxt.isRemoved()) {
+                throw new FileNotFoundException(jspUri);
+            }
+
+            if ((available > 0L) && (available < Long.MAX_VALUE)) {
+                response.setDateHeader("Retry-After", available);
+                response.sendError
+                    (HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+                     Localizer.getMessage("jsp.error.unavailable"));
+            }
+
+            /*
+             * (1) Compile
+             */
+            if (options.getDevelopment() || firstTime ) {
+                synchronized (this) {
+                    firstTime = false;
+
+                    // The following sets reload to true, if necessary
+                    ctxt.compile();
+                }
+            } else {
+                if (compileException != null) {
+                    // Throw cached compilation exception
+                    throw compileException;
+                }
+            }
+
+            /*
+             * (2) (Re)load servlet class file
+             */
+            getServlet();
+
+            // If a page is to be precompiled only, return.
+            if (precompile) {
+                return;
+            }
+
+            /*
+             * (3) Service request
+             */
+            if (theServlet instanceof SingleThreadModel) {
+               // sync on the wrapper so that the freshness
+               // of the page is determined right before servicing
+               synchronized (this) {
+                   theServlet.service(request, response);
+                }
+            } else {
+                theServlet.service(request, response);
+            }
+
+        } catch (UnavailableException ex) {
+            String includeRequestUri = (String)
+                request.getAttribute("javax.servlet.include.request_uri");
+            if (includeRequestUri != null) {
+                // This file was included. Throw an exception as
+                // a response.sendError() will be ignored by the
+                // servlet engine.
+                throw ex;
+            } else {
+                int unavailableSeconds = ex.getUnavailableSeconds();
+                if (unavailableSeconds <= 0) {
+                    unavailableSeconds = 60;        // Arbitrary default
+                }
+                available = System.currentTimeMillis() +
+                    (unavailableSeconds * 1000L);
+                response.sendError
+                    (HttpServletResponse.SC_SERVICE_UNAVAILABLE, 
+                     ex.getMessage());
+            }
+        } catch (FileNotFoundException ex) {
+            ctxt.incrementRemoved();
+            String includeRequestUri = (String)
+                request.getAttribute("javax.servlet.include.request_uri");
+            if (includeRequestUri != null) {
+                // This file was included. Throw an exception as
+                // a response.sendError() will be ignored by the
+                // servlet engine.
+                throw new ServletException(ex);
+            } else {
+                try {
+                    response.sendError(HttpServletResponse.SC_NOT_FOUND, 
+                                      ex.getMessage());
+                } catch (IllegalStateException ise) {
+                    log.error(Localizer.getMessage("jsp.error.file.not.found",
+						   ex.getMessage()),
+			      ex);
+                }
+            }
+        } catch (ServletException ex) {
+	    throw ex;
+        } catch (IOException ex) {
+            throw ex;
+        } catch (IllegalStateException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new JasperException(ex);
+        }
+    }
+
+    public void destroy() {
+        if (theServlet != null) {
+            theServlet.destroy();
+        }
+    }
+
+    /**
+     * @return Returns the lastModificationTest.
+     */
+    public long getLastModificationTest() {
+        return lastModificationTest;
+    }
+    /**
+     * @param lastModificationTest The lastModificationTest to set.
+     */
+    public void setLastModificationTest(long lastModificationTest) {
+        this.lastModificationTest = lastModificationTest;
+    }
+    
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/servlet/mbeans-descriptors.xml b/jasper/jasper2/src/share/org/apache/jasper/servlet/mbeans-descriptors.xml
new file mode 100644
index 0000000..d90a832
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/servlet/mbeans-descriptors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+  <mbean         name="JspMonitor"
+          description="JSP Monitoring"
+               domain="Catalina"
+                group="Monitoring"
+                 type="org.apache.jasper.servlet.JspServlet">
+
+    <attribute   name="jspCount"
+          description="The number of JSPs that have been loaded into a webapp"
+                 type="int"/>
+
+    <attribute   name="jspReloadCount"
+          description="The number of JSPs that have been reloaded"
+                 type="int"/>
+
+  </mbean>
+
+</mbeans-descriptors>
\ No newline at end of file
diff --git a/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/Choose.java b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/Choose.java
new file mode 100644
index 0000000..00f3532
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/Choose.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.tagplugins.jstl;
+
+import org.apache.jasper.compiler.tagplugin.*;
+
+public final class Choose implements TagPlugin {
+
+    public void doTag(TagPluginContext ctxt) {
+
+	// Not much to do here, much of the work will be done in the
+	// containing tags, <c:when> and <c:otherwise>.
+
+	ctxt.generateBody();
+	// See comments in When.java for the reason "}" is generated here.
+	ctxt.generateJavaSource("}");
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/ForEach.java b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/ForEach.java
new file mode 100644
index 0000000..024ccce
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/ForEach.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.tagplugins.jstl;
+
+import org.apache.jasper.compiler.tagplugin.*;
+
+public final class ForEach implements TagPlugin {
+
+    private boolean hasVar, hasBegin, hasEnd, hasStep;
+
+    public void doTag(TagPluginContext ctxt) {
+
+	String index = null;
+
+	boolean hasVarStatus = ctxt.isAttributeSpecified("varStatus");
+	if (hasVarStatus) {
+	    ctxt.dontUseTagPlugin();
+	    return;
+	}
+
+	hasVar = ctxt.isAttributeSpecified("var");
+	hasBegin = ctxt.isAttributeSpecified("begin");
+	hasEnd = ctxt.isAttributeSpecified("end");
+	hasStep = ctxt.isAttributeSpecified("step");
+
+	boolean hasItems = ctxt.isAttributeSpecified("items");
+	if (hasItems) {
+	    doCollection(ctxt);
+	    return;
+	}
+
+	// We must have a begin and end attributes
+	index = ctxt.getTemporaryVariableName();
+	ctxt.generateJavaSource("for (int " + index + " = ");
+	ctxt.generateAttribute("begin");
+	ctxt.generateJavaSource("; " + index + " <= ");
+	ctxt.generateAttribute("end");
+	if (hasStep) {
+	    ctxt.generateJavaSource("; " + index + "+=");
+	    ctxt.generateAttribute("step");
+	    ctxt.generateJavaSource(") {");
+	}
+	else {
+	    ctxt.generateJavaSource("; " + index + "++) {");
+	}
+
+	// If var is specified and the body contains an EL, then sycn
+	// the var attribute
+	if (hasVar /* && ctxt.hasEL() */) {
+	    ctxt.generateJavaSource("_jspx_page_context.setAttribute(");
+	    ctxt.generateAttribute("var");
+	    ctxt.generateJavaSource(", String.valueOf(" + index + "));");
+	}
+	ctxt.generateBody();
+	ctxt.generateJavaSource("}");
+    }
+
+    /**
+     * Generate codes for Collections
+     * The pseudo code is:
+     */
+    private void doCollection(TagPluginContext ctxt) {
+
+	ctxt.generateImport("java.util.*");
+	generateIterators(ctxt);
+
+        String itemsV = ctxt.getTemporaryVariableName();
+        ctxt.generateJavaSource("Object " + itemsV + "= ");
+        ctxt.generateAttribute("items");
+        ctxt.generateJavaSource(";");
+	
+	String indexV=null, beginV=null, endV=null, stepV=null;
+	if (hasBegin) {
+	    beginV = ctxt.getTemporaryVariableName();
+	    ctxt.generateJavaSource("int " + beginV + " = ");
+	    ctxt.generateAttribute("begin");
+	    ctxt.generateJavaSource(";");
+	}
+	if (hasEnd) {
+	    indexV = ctxt.getTemporaryVariableName();
+	    ctxt.generateJavaSource("int " + indexV + " = 0;");
+	    endV = ctxt.getTemporaryVariableName();
+	    ctxt.generateJavaSource("int " + endV + " = ");
+	    ctxt.generateAttribute("end");
+	    ctxt.generateJavaSource(";");
+	}
+	if (hasStep) {
+	    stepV = ctxt.getTemporaryVariableName();
+	    ctxt.generateJavaSource("int " + stepV + " = ");
+	    ctxt.generateAttribute("step");
+	    ctxt.generateJavaSource(";");
+	}
+
+        String iterV = ctxt.getTemporaryVariableName();
+        ctxt.generateJavaSource("Iterator " + iterV + " = null;");
+	// Object[]
+	ctxt.generateJavaSource("if (" + itemsV + " instanceof Object[])");
+	ctxt.generateJavaSource(iterV + "=toIterator((Object[])" + itemsV + ");");
+	// boolean[]
+	ctxt.generateJavaSource("else if (" + itemsV + " instanceof boolean[])");
+	ctxt.generateJavaSource(iterV + "=toIterator((boolean[])" + itemsV + ");");
+	// byte[]
+	ctxt.generateJavaSource("else if (" + itemsV + " instanceof byte[])");
+	ctxt.generateJavaSource(iterV + "=toIterator((byte[])" + itemsV + ");");
+	// char[]
+	ctxt.generateJavaSource("else if (" + itemsV + " instanceof char[])");
+	ctxt.generateJavaSource(iterV + "=toIterator((char[])" + itemsV + ");");
+	// short[]
+	ctxt.generateJavaSource("else if (" + itemsV + " instanceof short[])");
+	ctxt.generateJavaSource(iterV + "=toIterator((short[])" + itemsV + ");");
+	// int[]
+	ctxt.generateJavaSource("else if (" + itemsV + " instanceof int[])");
+	ctxt.generateJavaSource(iterV + "=toIterator((int[])" + itemsV + ");");
+	// long[]
+	ctxt.generateJavaSource("else if (" + itemsV + " instanceof long[])");
+	ctxt.generateJavaSource(iterV + "=toIterator((long[])" + itemsV + ");");
+	// float[]
+	ctxt.generateJavaSource("else if (" + itemsV + " instanceof float[])");
+	ctxt.generateJavaSource(iterV + "=toIterator((float[])" + itemsV + ");");
+	// double[]
+	ctxt.generateJavaSource("else if (" + itemsV + " instanceof double[])");
+	ctxt.generateJavaSource(iterV + "=toIterator((double[])" + itemsV + ");");
+
+        // Collection
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof Collection)");
+        ctxt.generateJavaSource(iterV + "=((Collection)" + itemsV + ").iterator();");
+
+        // Iterator
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof Iterator)");
+        ctxt.generateJavaSource(iterV + "=(Iterator)" + itemsV + ";");
+
+	// Enumeration
+	ctxt.generateJavaSource("else if (" + itemsV + " instanceof Enumeration)");
+	ctxt.generateJavaSource(iterV + "=toIterator((Enumeration)" + itemsV + ");");
+
+        // Map
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof Map)");
+        ctxt.generateJavaSource(iterV + "=((Map)" + itemsV + ").entrySet().iterator();");
+
+	if (hasBegin) {
+            String tV = ctxt.getTemporaryVariableName();
+	    ctxt.generateJavaSource("for (int " + tV + "=" + beginV + ";" +
+			tV + ">0 && " + iterV + ".hasNext(); " +
+			tV + "--)");
+	    ctxt.generateJavaSource(iterV + ".next();");
+	}
+
+	ctxt.generateJavaSource("while (" + iterV + ".hasNext()){");
+	if (hasVar) {
+	    ctxt.generateJavaSource("_jspx_page_context.setAttribute(");
+	    ctxt.generateAttribute("var");
+	    ctxt.generateJavaSource(", " + iterV + ".next());");
+	}
+
+	ctxt.generateBody();
+
+	if (hasStep) {
+	    String tV = ctxt.getTemporaryVariableName();
+	    ctxt.generateJavaSource("for (int " + tV + "=" + stepV + "-1;" +
+			tV + ">0 && " + iterV + ".hasNext(); " +
+			tV + "--)");
+	    ctxt.generateJavaSource(iterV + ".next();");
+	}
+	if (hasEnd) {
+	    if (hasStep) {
+		ctxt.generateJavaSource(indexV + "+=" + stepV + ";");
+	    }
+	    else {
+		ctxt.generateJavaSource(indexV + "++;");
+	    }
+	    if (hasBegin) {
+		ctxt.generateJavaSource("if(" + beginV + "+" + indexV +
+			">"+ endV + ")");
+	    }
+	    else {
+		ctxt.generateJavaSource("if(" + indexV + ">" + endV + ")");
+	    }
+	    ctxt.generateJavaSource("break;");
+	}
+	ctxt.generateJavaSource("}");	// while
+    }
+
+    /**
+     * Generate iterators for data types supported in items
+     */
+    private void generateIterators(TagPluginContext ctxt) {
+
+	// Object[]
+	ctxt.generateDeclaration("ObjectArrayIterator", 
+	    "private Iterator toIterator(final Object[] a){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    int index=0;\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return index < a.length;}\n" +
+	    "    public Object next() {\n" +
+	    "      return a[index++];}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+	// boolean[]
+	ctxt.generateDeclaration("booleanArrayIterator", 
+	    "private Iterator toIterator(final boolean[] a){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    int index=0;\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return index < a.length;}\n" +
+	    "    public Object next() {\n" +
+	    "      return new Boolean(a[index++]);}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+	// byte[]
+	ctxt.generateDeclaration("byteArrayIterator", 
+	    "private Iterator toIterator(final byte[] a){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    int index=0;\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return index < a.length;}\n" +
+	    "    public Object next() {\n" +
+	    "      return new Byte(a[index++]);}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+	// char[]
+	ctxt.generateDeclaration("charArrayIterator", 
+	    "private Iterator toIterator(final char[] a){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    int index=0;\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return index < a.length;}\n" +
+	    "    public Object next() {\n" +
+	    "      return new Character(a[index++]);}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+	// short[]
+	ctxt.generateDeclaration("shortArrayIterator", 
+	    "private Iterator toIterator(final short[] a){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    int index=0;\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return index < a.length;}\n" +
+	    "    public Object next() {\n" +
+	    "      return new Short(a[index++]);}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+	// int[]
+	ctxt.generateDeclaration("intArrayIterator", 
+	    "private Iterator toIterator(final int[] a){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    int index=0;\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return index < a.length;}\n" +
+	    "    public Object next() {\n" +
+	    "      return new Integer(a[index++]);}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+	// long[]
+	ctxt.generateDeclaration("longArrayIterator", 
+	    "private Iterator toIterator(final long[] a){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    int index=0;\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return index < a.length;}\n" +
+	    "    public Object next() {\n" +
+	    "      return new Long(a[index++]);}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+	// float[]
+	ctxt.generateDeclaration("floatArrayIterator",
+	    "private Iterator toIterator(final float[] a){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    int index=0;\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return index < a.length;}\n" +
+	    "    public Object next() {\n" +
+	    "      return new Float(a[index++]);}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+	// double[]
+	ctxt.generateDeclaration("doubleArrayIterator",
+	    "private Iterator toIterator(final double[] a){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    int index=0;\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return index < a.length;}\n" +
+	    "    public Object next() {\n" +
+	    "      return new Double(a[index++]);}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+	// Enumeration
+	ctxt.generateDeclaration("enumIterator",
+	    "private Iterator toIterator(final Enumeration e){\n" +
+	    "  return (new Iterator() {\n" +
+	    "    public boolean hasNext() {\n" +
+	    "      return e.hasMoreElements();}\n" +
+	    "    public Object next() {\n" +
+	    "      return e.nextElement();}\n" +
+	    "    public void remove() {}\n" +
+	    "  });\n" +
+	    "}"
+	);
+
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/If.java b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/If.java
new file mode 100644
index 0000000..f48278c
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/If.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.tagplugins.jstl;
+
+import org.apache.jasper.compiler.tagplugin.*;
+
+public final class If implements TagPlugin {
+
+    public void doTag(TagPluginContext ctxt) {
+	String condV = ctxt.getTemporaryVariableName();
+	ctxt.generateJavaSource("boolean " + condV + "=");
+	ctxt.generateAttribute("test");
+	ctxt.generateJavaSource(";");
+	if (ctxt.isAttributeSpecified("var")) {
+	    String scope = "PageContext.PAGE_SCOPE";
+	    if (ctxt.isAttributeSpecified("scope")) {
+		String scopeStr = ctxt.getConstantAttribute("scope");
+		if ("request".equals(scopeStr)) {
+		    scope = "PageContext.REQUEST_SCOPE";
+		} else if ("session".equals(scopeStr)) {
+		    scope = "PageContext.SESSION_SCOPE";
+		} else if ("application".equals(scopeStr)) {
+		    scope = "PageContext.APPLICATION_SCOPE";
+		}
+	    }
+	    ctxt.generateJavaSource("_jspx_page_context.setAttribute(");
+	    ctxt.generateAttribute("var");
+	    ctxt.generateJavaSource(", new Boolean(" + condV + ")," + scope + ");");
+	}
+	ctxt.generateJavaSource("if (" + condV + "){");
+	ctxt.generateBody();
+	ctxt.generateJavaSource("}");
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/Otherwise.java b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/Otherwise.java
new file mode 100644
index 0000000..cb071d3
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/Otherwise.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.tagplugins.jstl;
+
+import org.apache.jasper.compiler.tagplugin.*;
+
+public final class Otherwise implements TagPlugin {
+
+    public void doTag(TagPluginContext ctxt) {
+
+	// See When.java for the reason whey "}" is need at the beginng and
+	// not at the end.
+	ctxt.generateJavaSource("} else {");
+	ctxt.generateBody();
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/When.java b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/When.java
new file mode 100644
index 0000000..62e3d94
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/When.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.tagplugins.jstl;
+
+import org.apache.jasper.compiler.tagplugin.*;
+
+public final class When implements TagPlugin {
+
+    public void doTag(TagPluginContext ctxt) {
+	// Get the parent context to determine if this is the first <c:when>
+	TagPluginContext parentContext = ctxt.getParentContext();
+	if (parentContext == null) {
+	    ctxt.dontUseTagPlugin();
+	    return;
+	}
+
+	if ("true".equals(parentContext.getPluginAttribute("hasBeenHere"))) {
+	    ctxt.generateJavaSource("} else if(");
+	    // See comment below for the reason we generate the extra "}" here.
+	}
+	else {
+	    ctxt.generateJavaSource("if(");
+	    parentContext.setPluginAttribute("hasBeenHere", "true");
+	}
+	ctxt.generateAttribute("test");
+	ctxt.generateJavaSource("){");
+	ctxt.generateBody();
+
+	// We don't generate the closing "}" for the "if" here because there
+	// may be whitespaces in between <c:when>'s.  Instead we delay
+	// generating it until the next <c:when> or <c:otherwise> or
+	// <c:choose>
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/tagPlugins.xml b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/tagPlugins.xml
new file mode 100644
index 0000000..3576c67
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/tagplugins/jstl/tagPlugins.xml
@@ -0,0 +1,22 @@
+<tag-plugins>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.IfTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.If</plugin-class>
+  </tag-plugin>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.ChooseTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.Choose</plugin-class>
+  </tag-plugin>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.WhenTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.When</plugin-class>
+  </tag-plugin>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.OtherwiseTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.Otherwise</plugin-class>
+  </tag-plugin>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ForEachTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.ForEach</plugin-class>
+  </tag-plugin>
+</tag-plugins>
diff --git a/jasper/jasper2/src/share/org/apache/jasper/util/FastDateFormat.java b/jasper/jasper2/src/share/org/apache/jasper/util/FastDateFormat.java
new file mode 100644
index 0000000..95d1f2f
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/util/FastDateFormat.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.util;
+
+import java.util.Date;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+
+/**
+ * Fast date formatter that caches recently formatted date information
+ * and uses it to avoid too-frequent calls to the underlying
+ * formatter.  Note: breaks fieldPosition param of format(Date,
+ * StringBuffer, FieldPosition).  If you care about the field
+ * position, call the underlying DateFormat directly.
+ *
+ * @author Stan Bailes
+ * @author Alex Chaffee
+ */
+public class FastDateFormat extends DateFormat {
+
+    private DateFormat df;
+    private long lastSec = -1;
+    private StringBuffer sb = new StringBuffer();
+    private FieldPosition fp = new FieldPosition(DateFormat.MILLISECOND_FIELD);
+    
+    public FastDateFormat(DateFormat df) {
+        this.df = df;
+    }
+
+    public Date parse(String text, ParsePosition pos) {
+	return df.parse(text, pos);
+    }
+
+    /**
+     * Note: breaks functionality of fieldPosition param. Also:
+     * there's a bug in SimpleDateFormat with "S" and "SS", use "SSS"
+     * instead if you want a msec field.
+     */
+    public StringBuffer format(Date date, StringBuffer toAppendTo,
+			       FieldPosition fieldPosition) {
+        long dt = date.getTime();
+        long ds = dt / 1000;
+        if (ds != lastSec) {
+            sb.setLength(0);
+            df.format(date, sb, fp);
+            lastSec = ds;
+        } else {
+	    // munge current msec into existing string
+            int ms = (int)(dt % 1000);
+            int pos = fp.getEndIndex();
+	    int begin = fp.getBeginIndex();
+	    if (pos > 0) {
+		if (pos > begin)
+		    sb.setCharAt(--pos, Character.forDigit(ms % 10, 10));
+		ms /= 10;
+		if (pos > begin)
+		    sb.setCharAt(--pos, Character.forDigit(ms % 10, 10));
+		ms /= 10;
+		if (pos > begin)
+		    sb.setCharAt(--pos, Character.forDigit(ms % 10, 10));
+	    }
+        }
+	toAppendTo.append(sb.toString());
+	return toAppendTo;
+    }
+
+    public static void main(String[] args) {
+	String format = "yyyy-MM-dd HH:mm:ss.SSS";
+	if (args.length > 0)
+	    format = args[0];
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        FastDateFormat fdf = new FastDateFormat(sdf);
+        Date d = new Date();
+
+	d.setTime(1);
+	System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+	d.setTime(20);
+	System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+	d.setTime(500);
+	System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+	d.setTime(543);
+	System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+	d.setTime(999);
+	System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+	d.setTime(1050);
+	System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+	d.setTime(2543);
+	System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+	d.setTime(12345);
+	System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+	d.setTime(12340);
+	System.out.println(fdf.format(d) + "\t" + sdf.format(d));
+	
+        final int reps = 100000;
+        {
+            long start = System.currentTimeMillis();
+            for (int i = 0; i < reps; i++) {
+                d.setTime(System.currentTimeMillis());
+                fdf.format(d);
+            }
+            long elap = System.currentTimeMillis() - start;
+            System.out.println("fast: " + elap + " elapsed");
+	    System.out.println(fdf.format(d));
+        }
+        {
+            long start = System.currentTimeMillis();
+            for (int i = 0; i < reps; i++) {
+                d.setTime(System.currentTimeMillis());
+                sdf.format(d);
+            }
+            long elap = System.currentTimeMillis() - start;	    
+            System.out.println("slow: " + elap + " elapsed");
+	    System.out.println(sdf.format(d));
+        }
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/util/Queue.java b/jasper/jasper2/src/share/org/apache/jasper/util/Queue.java
new file mode 100644
index 0000000..c17bc81
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/util/Queue.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.util;
+
+import java.util.Vector;
+
+/**
+ * A simple FIFO queue class which causes the calling thread to wait
+ * if the queue is empty and notifies threads that are waiting when it
+ * is not empty.
+ *
+ * @author Anil V (akv@eng.sun.com)
+ */
+public class Queue {
+    private Vector vector = new Vector();
+
+    /** 
+     * Put the object into the queue.
+     * 
+     * @param	object		the object to be appended to the
+     * 				queue. 
+     */
+    public synchronized void put(Object object) {
+	vector.addElement(object);
+	notify();
+    }
+    
+    /**
+     * Pull the first object out of the queue. Wait if the queue is
+     * empty.
+     */
+    public synchronized Object pull() {
+	while (isEmpty())
+	    try {
+		wait();
+	    } catch (InterruptedException ex) {
+	    }
+	return get();
+    }
+
+    /**
+     * Get the first object out of the queue. Return null if the queue
+     * is empty. 
+     */
+    public synchronized Object get() {
+	Object object = peek();
+	if (object != null)
+	    vector.removeElementAt(0);
+	return object;
+    }
+
+    /**
+     * Peek to see if something is available.
+     */
+    public Object peek() {
+	if (isEmpty())
+	    return null;
+	return vector.elementAt(0);
+    }
+    
+    /**
+     * Is the queue empty?
+     */
+    public boolean isEmpty() {
+	return vector.isEmpty();
+    }
+
+    /**
+     * How many elements are there in this queue?
+     */
+    public int size() {
+	return vector.size();
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/util/SimplePool.java b/jasper/jasper2/src/share/org/apache/jasper/util/SimplePool.java
new file mode 100644
index 0000000..22cd14f
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/util/SimplePool.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.util;
+
+/**
+ * Simple object pool. Based on ThreadPool and few other classes
+ *
+ * The pool will ignore overflow and return null if empty.
+ *
+ * @author Gal Shachor
+ * @author Costin
+ */
+public final class SimplePool  {
+
+    private static final int DEFAULT_SIZE=16;
+
+    /*
+     * Where the threads are held.
+     */
+    private Object pool[];
+
+    private int max;
+    private int current=-1;
+
+    private Object lock;
+    
+    public SimplePool() {
+	this.max=DEFAULT_SIZE;
+	this.pool=new Object[max];
+	this.lock=new Object();
+    }
+    
+    public SimplePool(int max) {
+	this.max=max;
+	this.pool=new Object[max];
+	this.lock=new Object();
+    }
+
+    /**
+     * Adds the given object to the pool, and does nothing if the pool is full
+     */
+    public void put(Object o) {
+	synchronized( lock ) {
+	    if( current < (max-1) ) {
+		current += 1;
+		pool[current] = o;
+            }
+	}
+    }
+
+    /**
+     * Get an object from the pool, null if the pool is empty.
+     */
+    public Object get() {
+	Object item = null;
+	synchronized( lock ) {
+	    if( current >= 0 ) {
+		item = pool[current];
+		current -= 1;
+	    }
+	}
+	return item;
+    }
+
+    /**
+     * Return the size of the pool
+     */
+    public int getMax() {
+	return max;
+    }
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/util/SystemLogHandler.java b/jasper/jasper2/src/share/org/apache/jasper/util/SystemLogHandler.java
new file mode 100644
index 0000000..19847ea
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/util/SystemLogHandler.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+
+/**
+ * This helper class may be used to do sophisticated redirection of 
+ * System.out and System.err.
+ * 
+ * @author Remy Maucherat
+ */
+public class SystemLogHandler extends PrintStream {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct the handler to capture the output of the given steam.
+     */
+    public SystemLogHandler(PrintStream wrapped) {
+        super(wrapped);
+        this.wrapped = wrapped;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Wrapped PrintStream.
+     */
+    protected PrintStream wrapped = null;
+
+
+    /**
+     * Thread <-> PrintStream associations.
+     */
+    protected static ThreadLocal streams = new ThreadLocal();
+
+
+    /**
+     * Thread <-> ByteArrayOutputStream associations.
+     */
+    protected static ThreadLocal data = new ThreadLocal();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    public PrintStream getWrapped() {
+      return wrapped;
+    }
+
+    /**
+     * Start capturing thread's output.
+     */
+    public static void setThread() {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        data.set(baos);
+        streams.set(new PrintStream(baos));
+    }
+
+
+    /**
+     * Stop capturing thread's output and return captured data as a String.
+     */
+    public static String unsetThread() {
+        ByteArrayOutputStream baos = 
+            (ByteArrayOutputStream) data.get();
+        if (baos == null) {
+            return null;
+        }
+        streams.set(null);
+        data.set(null);
+        return baos.toString();
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Find PrintStream to which the output must be written to.
+     */
+    protected PrintStream findStream() {
+        PrintStream ps = (PrintStream) streams.get();
+        if (ps == null) {
+            ps = wrapped;
+        }
+        return ps;
+    }
+
+
+    // ---------------------------------------------------- PrintStream Methods
+
+
+    public void flush() {
+        findStream().flush();
+    }
+
+    public void close() {
+        findStream().close();
+    }
+
+    public boolean checkError() {
+        return findStream().checkError();
+    }
+
+    protected void setError() {
+        //findStream().setError();
+    }
+
+    public void write(int b) {
+        findStream().write(b);
+    }
+
+    public void write(byte[] b)
+        throws IOException {
+        findStream().write(b);
+    }
+
+    public void write(byte[] buf, int off, int len) {
+        findStream().write(buf, off, len);
+    }
+
+    public void print(boolean b) {
+        findStream().print(b);
+    }
+
+    public void print(char c) {
+        findStream().print(c);
+    }
+
+    public void print(int i) {
+        findStream().print(i);
+    }
+
+    public void print(long l) {
+        findStream().print(l);
+    }
+
+    public void print(float f) {
+        findStream().print(f);
+    }
+
+    public void print(double d) {
+        findStream().print(d);
+    }
+
+    public void print(char[] s) {
+        findStream().print(s);
+    }
+
+    public void print(String s) {
+        findStream().print(s);
+    }
+
+    public void print(Object obj) {
+        findStream().print(obj);
+    }
+
+    public void println() {
+        findStream().println();
+    }
+
+    public void println(boolean x) {
+        findStream().println(x);
+    }
+
+    public void println(char x) {
+        findStream().println(x);
+    }
+
+    public void println(int x) {
+        findStream().println(x);
+    }
+
+    public void println(long x) {
+        findStream().println(x);
+    }
+
+    public void println(float x) {
+        findStream().println(x);
+    }
+
+    public void println(double x) {
+        findStream().println(x);
+    }
+
+    public void println(char[] x) {
+        findStream().println(x);
+    }
+
+    public void println(String x) {
+        findStream().println(x);
+    }
+
+    public void println(Object x) {
+        findStream().println(x);
+    }
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/ASCIIReader.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/ASCIIReader.java
new file mode 100644
index 0000000..6f7805a
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/ASCIIReader.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.xmlparser;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.Reader;
+import org.apache.jasper.compiler.Localizer;
+
+/**
+ * A simple ASCII byte reader. This is an optimized reader for reading
+ * byte streams that only contain 7-bit ASCII characters.
+ *
+ * @author Andy Clark, IBM
+ *
+ * @version $Id$
+ */
+public class ASCIIReader
+    extends Reader {
+
+    //
+    // Constants
+    //
+
+    /** Default byte buffer size (2048). */
+    public static final int DEFAULT_BUFFER_SIZE = 2048;
+
+    //
+    // Data
+    //
+
+    /** Input stream. */
+    protected InputStream fInputStream;
+
+    /** Byte buffer. */
+    protected byte[] fBuffer;
+
+    //
+    // Constructors
+    //
+
+    /** 
+     * Constructs an ASCII reader from the specified input stream 
+     * and buffer size.
+     *
+     * @param inputStream The input stream.
+     * @param size        The initial buffer size.
+     */
+    public ASCIIReader(InputStream inputStream, int size) {
+        fInputStream = inputStream;
+        fBuffer = new byte[size];
+    }
+
+    //
+    // Reader methods
+    //
+
+    /**
+     * Read a single character.  This method will block until a character is
+     * available, an I/O error occurs, or the end of the stream is reached.
+     *
+     * <p> Subclasses that intend to support efficient single-character input
+     * should override this method.
+     *
+     * @return     The character read, as an integer in the range 0 to 127
+     *             (<tt>0x00-0x7f</tt>), or -1 if the end of the stream has
+     *             been reached
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public int read() throws IOException {
+        int b0 = fInputStream.read();
+        if (b0 > 0x80) {
+            throw new IOException(Localizer.getMessage("jsp.error.xml.invalidASCII",
+						       Integer.toString(b0)));
+        }
+        return b0;
+    } // read():int
+
+    /**
+     * Read characters into a portion of an array.  This method will block
+     * until some input is available, an I/O error occurs, or the end of the
+     * stream is reached.
+     *
+     * @param      ch     Destination buffer
+     * @param      offset Offset at which to start storing characters
+     * @param      length Maximum number of characters to read
+     *
+     * @return     The number of characters read, or -1 if the end of the
+     *             stream has been reached
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public int read(char ch[], int offset, int length) throws IOException {
+        if (length > fBuffer.length) {
+            length = fBuffer.length;
+        }
+        int count = fInputStream.read(fBuffer, 0, length);
+        for (int i = 0; i < count; i++) {
+            int b0 = fBuffer[i];
+            if (b0 > 0x80) {
+                throw new IOException(Localizer.getMessage("jsp.error.xml.invalidASCII",
+							   Integer.toString(b0)));
+            }
+            ch[offset + i] = (char)b0;
+        }
+        return count;
+    } // read(char[],int,int)
+
+    /**
+     * Skip characters.  This method will block until some characters are
+     * available, an I/O error occurs, or the end of the stream is reached.
+     *
+     * @param  n  The number of characters to skip
+     *
+     * @return    The number of characters actually skipped
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public long skip(long n) throws IOException {
+        return fInputStream.skip(n);
+    } // skip(long):long
+
+    /**
+     * Tell whether this stream is ready to be read.
+     *
+     * @return True if the next read() is guaranteed not to block for input,
+     * false otherwise.  Note that returning false does not guarantee that the
+     * next read will block.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public boolean ready() throws IOException {
+	return false;
+    } // ready()
+
+    /**
+     * Tell whether this stream supports the mark() operation.
+     */
+    public boolean markSupported() {
+	return fInputStream.markSupported();
+    } // markSupported()
+
+    /**
+     * Mark the present position in the stream.  Subsequent calls to reset()
+     * will attempt to reposition the stream to this point.  Not all
+     * character-input streams support the mark() operation.
+     *
+     * @param  readAheadLimit  Limit on the number of characters that may be
+     *                         read while still preserving the mark.  After
+     *                         reading this many characters, attempting to
+     *                         reset the stream may fail.
+     *
+     * @exception  IOException  If the stream does not support mark(),
+     *                          or if some other I/O error occurs
+     */
+    public void mark(int readAheadLimit) throws IOException {
+	fInputStream.mark(readAheadLimit);
+    } // mark(int)
+
+    /**
+     * Reset the stream.  If the stream has been marked, then attempt to
+     * reposition it at the mark.  If the stream has not been marked, then
+     * attempt to reset it in some way appropriate to the particular stream,
+     * for example by repositioning it to its starting point.  Not all
+     * character-input streams support the reset() operation, and some support
+     * reset() without supporting mark().
+     *
+     * @exception  IOException  If the stream has not been marked,
+     *                          or if the mark has been invalidated,
+     *                          or if the stream does not support reset(),
+     *                          or if some other I/O error occurs
+     */
+    public void reset() throws IOException {
+        fInputStream.reset();
+    } // reset()
+
+    /**
+     * Close the stream.  Once a stream has been closed, further read(),
+     * ready(), mark(), or reset() invocations will throw an IOException.
+     * Closing a previously-closed stream, however, has no effect.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+     public void close() throws IOException {
+         fInputStream.close();
+     } // close()
+
+} // class ASCIIReader
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/EncodingMap.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/EncodingMap.java
new file mode 100644
index 0000000..5dcf5ae
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/EncodingMap.java
@@ -0,0 +1,1019 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.apache.org.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.jasper.xmlparser;
+
+import java.util.Hashtable;
+
+/**
+ * EncodingMap is a convenience class which handles conversions between 
+ * IANA encoding names and Java encoding names, and vice versa. The
+ * encoding names used in XML instance documents <strong>must</strong>
+ * be the IANA encoding names specified or one of the aliases for those names
+ * which IANA defines.
+ * <p>
+ * <TABLE BORDER="0" WIDTH="100%">
+ *  <TR>
+ *      <TD WIDTH="33%">
+ *          <P ALIGN="CENTER"><B>Common Name</B>
+ *      </TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER"><B>Use this name in XML files</B>
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER"><B>Name Type</B>
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER"><B>Xerces converts to this Java Encoder Name</B>
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">8 bit Unicode</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">UTF-8
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">UTF8
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 1</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-1
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-1
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 2</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-2
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-2
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 3</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-3
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-3
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 4</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-4
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-4
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Cyrillic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-5
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-5
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Arabic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-6
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-6
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Greek</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-7
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-7
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Hebrew</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-8
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-8
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 5</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-9
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-9
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: US</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-us
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Canada</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ca
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Netherlands</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-nl
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Denmark</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-dk
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp277
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Norway</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-no
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp277
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Finland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-fi
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp278
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Sweden</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-se
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp278
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Italy</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-it
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp280
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Spain, Latin America</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-es
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp284
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Great Britain</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-gb
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp285
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: France</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-fr
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp297
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Arabic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ar1
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp420
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Hebrew</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-he
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp424
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Switzerland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ch
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp500
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Roece</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-roece
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp870
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Yugoslavia</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-yu
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp870
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Iceland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-is
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp871
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Urdu</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ar2
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp918
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Chinese for PRC, mixed 1/2 byte</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">gb2312
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">GB2312
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Extended Unix Code, packed for Japanese</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">euc-jp
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">eucjis
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Japanese: iso-2022-jp</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">iso-2020-jp
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">JIS
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Japanese: Shift JIS</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">Shift_JIS
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">SJIS
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Chinese: Big5</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">Big5
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">Big5
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Extended Unix Code, packed for Korean</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">euc-kr
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">iso2022kr
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Cyrillic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">koi8-r
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">koi8-r
+ *      </TD>
+ *  </TR>
+ * </TABLE>
+ * 
+ * @author TAMURA Kent, IBM
+ * @author Andy Clark, IBM
+ *
+ * @version $Id$
+ */
+public class EncodingMap {
+
+    //
+    // Data
+    //
+
+    /** fIANA2JavaMap */
+    protected final static Hashtable fIANA2JavaMap = new Hashtable();
+
+    /** fJava2IANAMap */
+    protected final static Hashtable fJava2IANAMap = new Hashtable();
+
+    //
+    // Static initialization
+    //
+
+    static {
+
+        // add IANA to Java encoding mappings.
+        fIANA2JavaMap.put("BIG5",            "Big5");
+        fIANA2JavaMap.put("CSBIG5",            "Big5");
+        fIANA2JavaMap.put("CP037",    "CP037");
+        fIANA2JavaMap.put("IBM037",    "CP037");
+        fIANA2JavaMap.put("CSIBM037",    "CP037");
+        fIANA2JavaMap.put("EBCDIC-CP-US",    "CP037");
+        fIANA2JavaMap.put("EBCDIC-CP-CA",    "CP037");
+        fIANA2JavaMap.put("EBCDIC-CP-NL",    "CP037");
+        fIANA2JavaMap.put("EBCDIC-CP-WT",    "CP037");
+        fIANA2JavaMap.put("IBM273",    "CP273");
+        fIANA2JavaMap.put("CP273",    "CP273");
+        fIANA2JavaMap.put("CSIBM273",    "CP273");
+        fIANA2JavaMap.put("IBM277",    "CP277");
+        fIANA2JavaMap.put("CP277",    "CP277");
+        fIANA2JavaMap.put("CSIBM277",    "CP277");
+        fIANA2JavaMap.put("EBCDIC-CP-DK",    "CP277");
+        fIANA2JavaMap.put("EBCDIC-CP-NO",    "CP277");
+        fIANA2JavaMap.put("IBM278",    "CP278");
+        fIANA2JavaMap.put("CP278",    "CP278");
+        fIANA2JavaMap.put("CSIBM278",    "CP278");
+        fIANA2JavaMap.put("EBCDIC-CP-FI",    "CP278");
+        fIANA2JavaMap.put("EBCDIC-CP-SE",    "CP278");
+        fIANA2JavaMap.put("IBM280",    "CP280");
+        fIANA2JavaMap.put("CP280",    "CP280");
+        fIANA2JavaMap.put("CSIBM280",    "CP280");
+        fIANA2JavaMap.put("EBCDIC-CP-IT",    "CP280");
+        fIANA2JavaMap.put("IBM284",    "CP284");
+        fIANA2JavaMap.put("CP284",    "CP284");
+        fIANA2JavaMap.put("CSIBM284",    "CP284");
+        fIANA2JavaMap.put("EBCDIC-CP-ES",    "CP284");
+        fIANA2JavaMap.put("EBCDIC-CP-GB",    "CP285");
+        fIANA2JavaMap.put("IBM285",    "CP285");
+        fIANA2JavaMap.put("CP285",    "CP285");
+        fIANA2JavaMap.put("CSIBM285",    "CP285");
+        fIANA2JavaMap.put("EBCDIC-JP-KANA",    "CP290");
+        fIANA2JavaMap.put("IBM290",    "CP290");
+        fIANA2JavaMap.put("CP290",    "CP290");
+        fIANA2JavaMap.put("CSIBM290",    "CP290");
+        fIANA2JavaMap.put("EBCDIC-CP-FR",    "CP297");
+        fIANA2JavaMap.put("IBM297",    "CP297");
+        fIANA2JavaMap.put("CP297",    "CP297");
+        fIANA2JavaMap.put("CSIBM297",    "CP297");
+        fIANA2JavaMap.put("EBCDIC-CP-AR1",   "CP420");
+        fIANA2JavaMap.put("IBM420",    "CP420");
+        fIANA2JavaMap.put("CP420",    "CP420");
+        fIANA2JavaMap.put("CSIBM420",    "CP420");
+        fIANA2JavaMap.put("EBCDIC-CP-HE",    "CP424");
+        fIANA2JavaMap.put("IBM424",    "CP424");
+        fIANA2JavaMap.put("CP424",    "CP424");
+        fIANA2JavaMap.put("CSIBM424",    "CP424");
+        fIANA2JavaMap.put("IBM437",    "CP437");
+        fIANA2JavaMap.put("437",    "CP437");
+        fIANA2JavaMap.put("CP437",    "CP437");
+        fIANA2JavaMap.put("CSPC8CODEPAGE437",    "CP437");
+        fIANA2JavaMap.put("EBCDIC-CP-CH",    "CP500");
+        fIANA2JavaMap.put("IBM500",    "CP500");
+        fIANA2JavaMap.put("CP500",    "CP500");
+        fIANA2JavaMap.put("CSIBM500",    "CP500");
+        fIANA2JavaMap.put("EBCDIC-CP-CH",    "CP500");
+        fIANA2JavaMap.put("EBCDIC-CP-BE",    "CP500"); 
+        fIANA2JavaMap.put("IBM775",    "CP775");
+        fIANA2JavaMap.put("CP775",    "CP775");
+        fIANA2JavaMap.put("CSPC775BALTIC",    "CP775");
+        fIANA2JavaMap.put("IBM850",    "CP850");
+        fIANA2JavaMap.put("850",    "CP850");
+        fIANA2JavaMap.put("CP850",    "CP850");
+        fIANA2JavaMap.put("CSPC850MULTILINGUAL",    "CP850");
+        fIANA2JavaMap.put("IBM852",    "CP852");
+        fIANA2JavaMap.put("852",    "CP852");
+        fIANA2JavaMap.put("CP852",    "CP852");
+        fIANA2JavaMap.put("CSPCP852",    "CP852");
+        fIANA2JavaMap.put("IBM855",    "CP855");
+        fIANA2JavaMap.put("855",    "CP855");
+        fIANA2JavaMap.put("CP855",    "CP855");
+        fIANA2JavaMap.put("CSIBM855",    "CP855");
+        fIANA2JavaMap.put("IBM857",    "CP857");
+        fIANA2JavaMap.put("857",    "CP857");
+        fIANA2JavaMap.put("CP857",    "CP857");
+        fIANA2JavaMap.put("CSIBM857",    "CP857");
+        fIANA2JavaMap.put("IBM00858",    "CP858");
+        fIANA2JavaMap.put("CP00858",    "CP858");
+        fIANA2JavaMap.put("CCSID00858",    "CP858");
+        fIANA2JavaMap.put("IBM860",    "CP860");
+        fIANA2JavaMap.put("860",    "CP860");
+        fIANA2JavaMap.put("CP860",    "CP860");
+        fIANA2JavaMap.put("CSIBM860",    "CP860");
+        fIANA2JavaMap.put("IBM861",    "CP861");
+        fIANA2JavaMap.put("861",    "CP861");
+        fIANA2JavaMap.put("CP861",    "CP861");
+        fIANA2JavaMap.put("CP-IS",    "CP861");
+        fIANA2JavaMap.put("CSIBM861",    "CP861");
+        fIANA2JavaMap.put("IBM862",    "CP862");
+        fIANA2JavaMap.put("862",    "CP862");
+        fIANA2JavaMap.put("CP862",    "CP862");
+        fIANA2JavaMap.put("CSPC862LATINHEBREW",    "CP862");
+        fIANA2JavaMap.put("IBM863",    "CP863");
+        fIANA2JavaMap.put("863",    "CP863");
+        fIANA2JavaMap.put("CP863",    "CP863");
+        fIANA2JavaMap.put("CSIBM863",    "CP863");
+        fIANA2JavaMap.put("IBM864",    "CP864");
+        fIANA2JavaMap.put("CP864",    "CP864");
+        fIANA2JavaMap.put("CSIBM864",    "CP864");
+        fIANA2JavaMap.put("IBM865",    "CP865");
+        fIANA2JavaMap.put("865",    "CP865");
+        fIANA2JavaMap.put("CP865",    "CP865");
+        fIANA2JavaMap.put("CSIBM865",    "CP865");
+        fIANA2JavaMap.put("IBM866",    "CP866");
+        fIANA2JavaMap.put("866",    "CP866");
+        fIANA2JavaMap.put("CP866",    "CP866");
+        fIANA2JavaMap.put("CSIBM866",    "CP866");
+        fIANA2JavaMap.put("IBM868",    "CP868");
+        fIANA2JavaMap.put("CP868",    "CP868");
+        fIANA2JavaMap.put("CSIBM868",    "CP868");
+        fIANA2JavaMap.put("CP-AR",        "CP868");
+        fIANA2JavaMap.put("IBM869",    "CP869");
+        fIANA2JavaMap.put("CP869",    "CP869");
+        fIANA2JavaMap.put("CSIBM869",    "CP869");
+        fIANA2JavaMap.put("CP-GR",        "CP869");
+        fIANA2JavaMap.put("IBM870",    "CP870");
+        fIANA2JavaMap.put("CP870",    "CP870");
+        fIANA2JavaMap.put("CSIBM870",    "CP870");
+        fIANA2JavaMap.put("EBCDIC-CP-ROECE", "CP870");
+        fIANA2JavaMap.put("EBCDIC-CP-YU",    "CP870");
+        fIANA2JavaMap.put("IBM871",    "CP871");
+        fIANA2JavaMap.put("CP871",    "CP871");
+        fIANA2JavaMap.put("CSIBM871",    "CP871");
+        fIANA2JavaMap.put("EBCDIC-CP-IS",    "CP871");
+        fIANA2JavaMap.put("IBM918",    "CP918");
+        fIANA2JavaMap.put("CP918",    "CP918");
+        fIANA2JavaMap.put("CSIBM918",    "CP918");
+        fIANA2JavaMap.put("EBCDIC-CP-AR2",   "CP918");
+        fIANA2JavaMap.put("IBM00924",    "CP924");
+        fIANA2JavaMap.put("CP00924",    "CP924");
+        fIANA2JavaMap.put("CCSID00924",    "CP924");
+        // is this an error???
+        fIANA2JavaMap.put("EBCDIC-LATIN9--EURO",    "CP924");
+        fIANA2JavaMap.put("IBM1026",    "CP1026");
+        fIANA2JavaMap.put("CP1026",    "CP1026");
+        fIANA2JavaMap.put("CSIBM1026",    "CP1026");
+        fIANA2JavaMap.put("IBM01140",    "Cp1140");
+        fIANA2JavaMap.put("CP01140",    "Cp1140");
+        fIANA2JavaMap.put("CCSID01140",    "Cp1140");
+        fIANA2JavaMap.put("IBM01141",    "Cp1141");
+        fIANA2JavaMap.put("CP01141",    "Cp1141");
+        fIANA2JavaMap.put("CCSID01141",    "Cp1141");
+        fIANA2JavaMap.put("IBM01142",    "Cp1142");
+        fIANA2JavaMap.put("CP01142",    "Cp1142");
+        fIANA2JavaMap.put("CCSID01142",    "Cp1142");
+        fIANA2JavaMap.put("IBM01143",    "Cp1143");
+        fIANA2JavaMap.put("CP01143",    "Cp1143");
+        fIANA2JavaMap.put("CCSID01143",    "Cp1143");
+        fIANA2JavaMap.put("IBM01144",    "Cp1144");
+        fIANA2JavaMap.put("CP01144",    "Cp1144");
+        fIANA2JavaMap.put("CCSID01144",    "Cp1144");
+        fIANA2JavaMap.put("IBM01145",    "Cp1145");
+        fIANA2JavaMap.put("CP01145",    "Cp1145");
+        fIANA2JavaMap.put("CCSID01145",    "Cp1145");
+        fIANA2JavaMap.put("IBM01146",    "Cp1146");
+        fIANA2JavaMap.put("CP01146",    "Cp1146");
+        fIANA2JavaMap.put("CCSID01146",    "Cp1146");
+        fIANA2JavaMap.put("IBM01147",    "Cp1147");
+        fIANA2JavaMap.put("CP01147",    "Cp1147");
+        fIANA2JavaMap.put("CCSID01147",    "Cp1147");
+        fIANA2JavaMap.put("IBM01148",    "Cp1148");
+        fIANA2JavaMap.put("CP01148",    "Cp1148");
+        fIANA2JavaMap.put("CCSID01148",    "Cp1148");
+        fIANA2JavaMap.put("IBM01149",    "Cp1149");
+        fIANA2JavaMap.put("CP01149",    "Cp1149");
+        fIANA2JavaMap.put("CCSID01149",    "Cp1149");
+        fIANA2JavaMap.put("EUC-JP",          "EUCJIS");
+        fIANA2JavaMap.put("CSEUCPKDFMTJAPANESE",          "EUCJIS");
+        fIANA2JavaMap.put("EXTENDED_UNIX_CODE_PACKED_FORMAT_FOR_JAPANESE",          "EUCJIS");
+        fIANA2JavaMap.put("EUC-KR",          "KSC5601");
+        fIANA2JavaMap.put("CSEUCKR",          "KSC5601");
+        fIANA2JavaMap.put("KS_C_5601-1987",          "KS_C_5601-1987");
+        fIANA2JavaMap.put("ISO-IR-149",          "KS_C_5601-1987");
+        fIANA2JavaMap.put("KS_C_5601-1989",          "KS_C_5601-1987");
+        fIANA2JavaMap.put("KSC_5601",          "KS_C_5601-1987");
+        fIANA2JavaMap.put("KOREAN",          "KS_C_5601-1987");
+        fIANA2JavaMap.put("CSKSC56011987",          "KS_C_5601-1987");
+        fIANA2JavaMap.put("GB2312",          "GB2312");
+        fIANA2JavaMap.put("CSGB2312",          "GB2312");
+        fIANA2JavaMap.put("ISO-2022-JP",     "JIS");
+        fIANA2JavaMap.put("CSISO2022JP",     "JIS");
+        fIANA2JavaMap.put("ISO-2022-KR",     "ISO2022KR");
+        fIANA2JavaMap.put("CSISO2022KR",     "ISO2022KR");
+        fIANA2JavaMap.put("ISO-2022-CN",     "ISO2022CN");
+
+        fIANA2JavaMap.put("X0201",  "JIS0201");
+        fIANA2JavaMap.put("CSISO13JISC6220JP", "JIS0201");
+        fIANA2JavaMap.put("X0208",  "JIS0208");
+        fIANA2JavaMap.put("ISO-IR-87",  "JIS0208");
+        fIANA2JavaMap.put("X0208dbiJIS_X0208-1983",  "JIS0208");
+        fIANA2JavaMap.put("CSISO87JISX0208",  "JIS0208");
+        fIANA2JavaMap.put("X0212",  "JIS0212");
+        fIANA2JavaMap.put("ISO-IR-159",  "JIS0212");
+        fIANA2JavaMap.put("CSISO159JISX02121990",  "JIS0212");
+        fIANA2JavaMap.put("GB18030",       "GB18030");
+        fIANA2JavaMap.put("GBK",       "GBK");
+        fIANA2JavaMap.put("CP936",       "GBK");
+        fIANA2JavaMap.put("MS936",       "GBK");
+        fIANA2JavaMap.put("WINDOWS-936",       "GBK");
+        fIANA2JavaMap.put("SHIFT_JIS",       "SJIS");
+        fIANA2JavaMap.put("CSSHIFTJIS",       "SJIS");
+        fIANA2JavaMap.put("MS_KANJI",       "SJIS");
+        fIANA2JavaMap.put("WINDOWS-31J",       "MS932");
+        fIANA2JavaMap.put("CSWINDOWS31J",       "MS932");
+
+	    // Add support for Cp1252 and its friends
+        fIANA2JavaMap.put("WINDOWS-1250",   "Cp1250");
+        fIANA2JavaMap.put("WINDOWS-1251",   "Cp1251");
+        fIANA2JavaMap.put("WINDOWS-1252",   "Cp1252");
+        fIANA2JavaMap.put("WINDOWS-1253",   "Cp1253");
+        fIANA2JavaMap.put("WINDOWS-1254",   "Cp1254");
+        fIANA2JavaMap.put("WINDOWS-1255",   "Cp1255");
+        fIANA2JavaMap.put("WINDOWS-1256",   "Cp1256");
+        fIANA2JavaMap.put("WINDOWS-1257",   "Cp1257");
+        fIANA2JavaMap.put("WINDOWS-1258",   "Cp1258");
+        fIANA2JavaMap.put("TIS-620",   "TIS620");
+
+        fIANA2JavaMap.put("ISO-8859-1",      "ISO8859_1"); 
+        fIANA2JavaMap.put("ISO-IR-100",      "ISO8859_1");
+        fIANA2JavaMap.put("ISO_8859-1",      "ISO8859_1");
+        fIANA2JavaMap.put("LATIN1",      "ISO8859_1");
+        fIANA2JavaMap.put("CSISOLATIN1",      "ISO8859_1");
+        fIANA2JavaMap.put("L1",      "ISO8859_1");
+        fIANA2JavaMap.put("IBM819",      "ISO8859_1");
+        fIANA2JavaMap.put("CP819",      "ISO8859_1");
+
+        fIANA2JavaMap.put("ISO-8859-2",      "ISO8859_2"); 
+        fIANA2JavaMap.put("ISO-IR-101",      "ISO8859_2");
+        fIANA2JavaMap.put("ISO_8859-2",      "ISO8859_2");
+        fIANA2JavaMap.put("LATIN2",      "ISO8859_2");
+        fIANA2JavaMap.put("CSISOLATIN2",      "ISO8859_2");
+        fIANA2JavaMap.put("L2",      "ISO8859_2");
+
+        fIANA2JavaMap.put("ISO-8859-3",      "ISO8859_3"); 
+        fIANA2JavaMap.put("ISO-IR-109",      "ISO8859_3");
+        fIANA2JavaMap.put("ISO_8859-3",      "ISO8859_3");
+        fIANA2JavaMap.put("LATIN3",      "ISO8859_3");
+        fIANA2JavaMap.put("CSISOLATIN3",      "ISO8859_3");
+        fIANA2JavaMap.put("L3",      "ISO8859_3");
+
+        fIANA2JavaMap.put("ISO-8859-4",      "ISO8859_4"); 
+        fIANA2JavaMap.put("ISO-IR-110",      "ISO8859_4");
+        fIANA2JavaMap.put("ISO_8859-4",      "ISO8859_4");
+        fIANA2JavaMap.put("LATIN4",      "ISO8859_4");
+        fIANA2JavaMap.put("CSISOLATIN4",      "ISO8859_4");
+        fIANA2JavaMap.put("L4",      "ISO8859_4");
+
+        fIANA2JavaMap.put("ISO-8859-5",      "ISO8859_5"); 
+        fIANA2JavaMap.put("ISO-IR-144",      "ISO8859_5");
+        fIANA2JavaMap.put("ISO_8859-5",      "ISO8859_5");
+        fIANA2JavaMap.put("CYRILLIC",      "ISO8859_5");
+        fIANA2JavaMap.put("CSISOLATINCYRILLIC",      "ISO8859_5");
+
+        fIANA2JavaMap.put("ISO-8859-6",      "ISO8859_6"); 
+        fIANA2JavaMap.put("ISO-IR-127",      "ISO8859_6");
+        fIANA2JavaMap.put("ISO_8859-6",      "ISO8859_6");
+        fIANA2JavaMap.put("ECMA-114",      "ISO8859_6");
+        fIANA2JavaMap.put("ASMO-708",      "ISO8859_6");
+        fIANA2JavaMap.put("ARABIC",      "ISO8859_6");
+        fIANA2JavaMap.put("CSISOLATINARABIC",      "ISO8859_6");
+
+        fIANA2JavaMap.put("ISO-8859-7",      "ISO8859_7"); 
+        fIANA2JavaMap.put("ISO-IR-126",      "ISO8859_7");
+        fIANA2JavaMap.put("ISO_8859-7",      "ISO8859_7");
+        fIANA2JavaMap.put("ELOT_928",      "ISO8859_7");
+        fIANA2JavaMap.put("ECMA-118",      "ISO8859_7");
+        fIANA2JavaMap.put("GREEK",      "ISO8859_7");
+        fIANA2JavaMap.put("CSISOLATINGREEK",      "ISO8859_7");
+        fIANA2JavaMap.put("GREEK8",      "ISO8859_7");
+
+        fIANA2JavaMap.put("ISO-8859-8",      "ISO8859_8"); 
+        fIANA2JavaMap.put("ISO-8859-8-I",      "ISO8859_8"); // added since this encoding only differs w.r.t. presentation 
+        fIANA2JavaMap.put("ISO-IR-138",      "ISO8859_8");
+        fIANA2JavaMap.put("ISO_8859-8",      "ISO8859_8");
+        fIANA2JavaMap.put("HEBREW",      "ISO8859_8");
+        fIANA2JavaMap.put("CSISOLATINHEBREW",      "ISO8859_8");
+
+        fIANA2JavaMap.put("ISO-8859-9",      "ISO8859_9"); 
+        fIANA2JavaMap.put("ISO-IR-148",      "ISO8859_9");
+        fIANA2JavaMap.put("ISO_8859-9",      "ISO8859_9");
+        fIANA2JavaMap.put("LATIN5",      "ISO8859_9");
+        fIANA2JavaMap.put("CSISOLATIN5",      "ISO8859_9");
+        fIANA2JavaMap.put("L5",      "ISO8859_9");
+
+        fIANA2JavaMap.put("ISO-8859-13",      "ISO8859_13"); 
+        
+        fIANA2JavaMap.put("ISO-8859-15",      "ISO8859_15_FDIS"); 
+        fIANA2JavaMap.put("ISO_8859-15",      "ISO8859_15_FDIS");
+        fIANA2JavaMap.put("LATIN-9",          "ISO8859_15_FDIS"); 
+
+        fIANA2JavaMap.put("KOI8-R",          "KOI8_R");
+        fIANA2JavaMap.put("CSKOI8R",          "KOI8_R");
+        fIANA2JavaMap.put("US-ASCII",        "ASCII"); 
+        fIANA2JavaMap.put("ISO-IR-6",        "ASCII");
+        fIANA2JavaMap.put("ANSI_X3.4-1968",        "ASCII");
+        fIANA2JavaMap.put("ANSI_X3.4-1986",        "ASCII");
+        fIANA2JavaMap.put("ISO_646.IRV:1991",        "ASCII");
+        fIANA2JavaMap.put("ASCII",        "ASCII");
+        fIANA2JavaMap.put("CSASCII",        "ASCII");
+        fIANA2JavaMap.put("ISO646-US",        "ASCII");
+        fIANA2JavaMap.put("US",        "ASCII");
+        fIANA2JavaMap.put("IBM367",        "ASCII");
+        fIANA2JavaMap.put("CP367",        "ASCII");
+        fIANA2JavaMap.put("UTF-8",           "UTF8");
+        fIANA2JavaMap.put("UTF-16",           "UTF-16");
+        fIANA2JavaMap.put("UTF-16BE",           "UnicodeBig");
+        fIANA2JavaMap.put("UTF-16LE",           "UnicodeLittle");
+
+        // support for 1047, as proposed to be added to the 
+        // IANA registry in 
+        // http://lists.w3.org/Archives/Public/ietf-charset/2002JulSep/0049.html
+        fIANA2JavaMap.put("IBM-1047",    "Cp1047");
+        fIANA2JavaMap.put("IBM1047",    "Cp1047");
+        fIANA2JavaMap.put("CP1047",    "Cp1047");
+
+        // Adding new aliases as proposed in
+        // http://lists.w3.org/Archives/Public/ietf-charset/2002JulSep/0058.html
+        fIANA2JavaMap.put("IBM-37",    "CP037");
+        fIANA2JavaMap.put("IBM-273",    "CP273");
+        fIANA2JavaMap.put("IBM-277",    "CP277");
+        fIANA2JavaMap.put("IBM-278",    "CP278");
+        fIANA2JavaMap.put("IBM-280",    "CP280");
+        fIANA2JavaMap.put("IBM-284",    "CP284");
+        fIANA2JavaMap.put("IBM-285",    "CP285");
+        fIANA2JavaMap.put("IBM-290",    "CP290");
+        fIANA2JavaMap.put("IBM-297",    "CP297");
+        fIANA2JavaMap.put("IBM-420",    "CP420");
+        fIANA2JavaMap.put("IBM-424",    "CP424");
+        fIANA2JavaMap.put("IBM-437",    "CP437");
+        fIANA2JavaMap.put("IBM-500",    "CP500");
+        fIANA2JavaMap.put("IBM-775",    "CP775");
+        fIANA2JavaMap.put("IBM-850",    "CP850");
+        fIANA2JavaMap.put("IBM-852",    "CP852");
+        fIANA2JavaMap.put("IBM-855",    "CP855");
+        fIANA2JavaMap.put("IBM-857",    "CP857");
+        fIANA2JavaMap.put("IBM-858",    "CP858");
+        fIANA2JavaMap.put("IBM-860",    "CP860");
+        fIANA2JavaMap.put("IBM-861",    "CP861");
+        fIANA2JavaMap.put("IBM-862",    "CP862");
+        fIANA2JavaMap.put("IBM-863",    "CP863");
+        fIANA2JavaMap.put("IBM-864",    "CP864");
+        fIANA2JavaMap.put("IBM-865",    "CP865");
+        fIANA2JavaMap.put("IBM-866",    "CP866");
+        fIANA2JavaMap.put("IBM-868",    "CP868");
+        fIANA2JavaMap.put("IBM-869",    "CP869");
+        fIANA2JavaMap.put("IBM-870",    "CP870");
+        fIANA2JavaMap.put("IBM-871",    "CP871");
+        fIANA2JavaMap.put("IBM-918",    "CP918");
+        fIANA2JavaMap.put("IBM-924",    "CP924");
+        fIANA2JavaMap.put("IBM-1026",    "CP1026");
+        fIANA2JavaMap.put("IBM-1140",    "Cp1140");
+        fIANA2JavaMap.put("IBM-1141",    "Cp1141");
+        fIANA2JavaMap.put("IBM-1142",    "Cp1142");
+        fIANA2JavaMap.put("IBM-1143",    "Cp1143");
+        fIANA2JavaMap.put("IBM-1144",    "Cp1144");
+        fIANA2JavaMap.put("IBM-1145",    "Cp1145");
+        fIANA2JavaMap.put("IBM-1146",    "Cp1146");
+        fIANA2JavaMap.put("IBM-1147",    "Cp1147");
+        fIANA2JavaMap.put("IBM-1148",    "Cp1148");
+        fIANA2JavaMap.put("IBM-1149",    "Cp1149");
+        fIANA2JavaMap.put("IBM-819",      "ISO8859_1");
+        fIANA2JavaMap.put("IBM-367",        "ASCII");
+
+        // REVISIT:
+        //   j:CNS11643 -> EUC-TW?
+        //   ISO-2022-CN? ISO-2022-CN-EXT?
+                                                
+        // add Java to IANA encoding mappings
+        //fJava2IANAMap.put("8859_1",    "US-ASCII"); // ?
+        fJava2IANAMap.put("ISO8859_1",    "ISO-8859-1");
+        fJava2IANAMap.put("ISO8859_2",    "ISO-8859-2");
+        fJava2IANAMap.put("ISO8859_3",    "ISO-8859-3");
+        fJava2IANAMap.put("ISO8859_4",    "ISO-8859-4");
+        fJava2IANAMap.put("ISO8859_5",    "ISO-8859-5");
+        fJava2IANAMap.put("ISO8859_6",    "ISO-8859-6");
+        fJava2IANAMap.put("ISO8859_7",    "ISO-8859-7");
+        fJava2IANAMap.put("ISO8859_8",    "ISO-8859-8");
+        fJava2IANAMap.put("ISO8859_9",    "ISO-8859-9");
+        fJava2IANAMap.put("ISO8859_13",    "ISO-8859-13");
+        fJava2IANAMap.put("ISO8859_15",    "ISO-8859-15");
+        fJava2IANAMap.put("ISO8859_15_FDIS",    "ISO-8859-15");
+        fJava2IANAMap.put("Big5",      "BIG5");
+        fJava2IANAMap.put("CP037",     "EBCDIC-CP-US");
+        fJava2IANAMap.put("CP273",     "IBM273");
+        fJava2IANAMap.put("CP277",     "EBCDIC-CP-DK");
+        fJava2IANAMap.put("CP278",     "EBCDIC-CP-FI");
+        fJava2IANAMap.put("CP280",     "EBCDIC-CP-IT");
+        fJava2IANAMap.put("CP284",     "EBCDIC-CP-ES");
+        fJava2IANAMap.put("CP285",     "EBCDIC-CP-GB");
+        fJava2IANAMap.put("CP290",     "EBCDIC-JP-KANA");
+        fJava2IANAMap.put("CP297",     "EBCDIC-CP-FR");
+        fJava2IANAMap.put("CP420",     "EBCDIC-CP-AR1");
+        fJava2IANAMap.put("CP424",     "EBCDIC-CP-HE");
+        fJava2IANAMap.put("CP437",     "IBM437");
+        fJava2IANAMap.put("CP500",     "EBCDIC-CP-CH");
+        fJava2IANAMap.put("CP775",     "IBM775");
+        fJava2IANAMap.put("CP850",     "IBM850");
+        fJava2IANAMap.put("CP852",     "IBM852");
+        fJava2IANAMap.put("CP855",     "IBM855");
+        fJava2IANAMap.put("CP857",     "IBM857");
+        fJava2IANAMap.put("CP858",     "IBM00858");
+        fJava2IANAMap.put("CP860",     "IBM860");
+        fJava2IANAMap.put("CP861",     "IBM861");
+        fJava2IANAMap.put("CP862",     "IBM862");
+        fJava2IANAMap.put("CP863",     "IBM863");
+        fJava2IANAMap.put("CP864",     "IBM864");
+        fJava2IANAMap.put("CP865",     "IBM865");
+        fJava2IANAMap.put("CP866",     "IBM866");
+        fJava2IANAMap.put("CP868",     "IBM868");
+        fJava2IANAMap.put("CP869",     "IBM869");
+        fJava2IANAMap.put("CP870",     "EBCDIC-CP-ROECE");
+        fJava2IANAMap.put("CP871",     "EBCDIC-CP-IS");
+        fJava2IANAMap.put("CP918",     "EBCDIC-CP-AR2");
+        fJava2IANAMap.put("CP924",     "IBM00924");
+        fJava2IANAMap.put("CP1026",     "IBM1026");
+        fJava2IANAMap.put("Cp01140",     "IBM01140");
+        fJava2IANAMap.put("Cp01141",     "IBM01141");
+        fJava2IANAMap.put("Cp01142",     "IBM01142");
+        fJava2IANAMap.put("Cp01143",     "IBM01143");
+        fJava2IANAMap.put("Cp01144",     "IBM01144");
+        fJava2IANAMap.put("Cp01145",     "IBM01145");
+        fJava2IANAMap.put("Cp01146",     "IBM01146");
+        fJava2IANAMap.put("Cp01147",     "IBM01147");
+        fJava2IANAMap.put("Cp01148",     "IBM01148");
+        fJava2IANAMap.put("Cp01149",     "IBM01149");
+        fJava2IANAMap.put("EUCJIS",    "EUC-JP");
+        fJava2IANAMap.put("KS_C_5601-1987",          "KS_C_5601-1987");
+        fJava2IANAMap.put("GB2312",    "GB2312");
+        fJava2IANAMap.put("ISO2022KR", "ISO-2022-KR");
+        fJava2IANAMap.put("ISO2022CN", "ISO-2022-CN");
+        fJava2IANAMap.put("JIS",       "ISO-2022-JP");
+        fJava2IANAMap.put("KOI8_R",    "KOI8-R");
+        fJava2IANAMap.put("KSC5601",   "EUC-KR");
+        fJava2IANAMap.put("GB18030",      "GB18030");
+        fJava2IANAMap.put("GBK",       "GBK");
+        fJava2IANAMap.put("SJIS",      "SHIFT_JIS");
+        fJava2IANAMap.put("MS932",      "WINDOWS-31J");
+        fJava2IANAMap.put("UTF8",      "UTF-8");
+        fJava2IANAMap.put("Unicode",   "UTF-16");
+        fJava2IANAMap.put("UnicodeBig",   "UTF-16BE");
+        fJava2IANAMap.put("UnicodeLittle",   "UTF-16LE");
+        fJava2IANAMap.put("JIS0201",  "X0201");
+        fJava2IANAMap.put("JIS0208",  "X0208");
+        fJava2IANAMap.put("JIS0212",  "ISO-IR-159");
+
+        // proposed addition (see above for details):
+        fJava2IANAMap.put("CP1047",    "IBM1047");
+
+    } // <clinit>()
+
+    //
+    // Constructors
+    //
+
+    /** Default constructor. */
+    public EncodingMap() {}
+
+    //
+    // Public static methods
+    //
+
+    /**
+     * Adds an IANA to Java encoding name mapping.
+     * 
+     * @param ianaEncoding The IANA encoding name.
+     * @param javaEncoding The Java encoding name.
+     */
+    public static void putIANA2JavaMapping(String ianaEncoding, 
+                                           String javaEncoding) {
+        fIANA2JavaMap.put(ianaEncoding, javaEncoding);
+    } // putIANA2JavaMapping(String,String)
+
+    /**
+     * Returns the Java encoding name for the specified IANA encoding name.
+     * 
+     * @param ianaEncoding The IANA encoding name.
+     */
+    public static String getIANA2JavaMapping(String ianaEncoding) {
+        return (String)fIANA2JavaMap.get(ianaEncoding);
+    } // getIANA2JavaMapping(String):String
+
+    /**
+     * Removes an IANA to Java encoding name mapping.
+     * 
+     * @param ianaEncoding The IANA encoding name.
+     */
+    public static String removeIANA2JavaMapping(String ianaEncoding) {
+        return (String)fIANA2JavaMap.remove(ianaEncoding);
+    } // removeIANA2JavaMapping(String):String
+
+    /**
+     * Adds a Java to IANA encoding name mapping.
+     * 
+     * @param javaEncoding The Java encoding name.
+     * @param ianaEncoding The IANA encoding name.
+     */
+    public static void putJava2IANAMapping(String javaEncoding, 
+                                           String ianaEncoding) {
+        fJava2IANAMap.put(javaEncoding, ianaEncoding);
+    } // putJava2IANAMapping(String,String)
+
+    /**
+     * Returns the IANA encoding name for the specified Java encoding name.
+     * 
+     * @param javaEncoding The Java encoding name.
+     */
+    public static String getJava2IANAMapping(String javaEncoding) {
+        return (String)fJava2IANAMap.get(javaEncoding);
+    } // getJava2IANAMapping(String):String
+
+    /**
+     * Removes a Java to IANA encoding name mapping.
+     * 
+     * @param javaEncoding The Java encoding name.
+     */
+    public static String removeJava2IANAMapping(String javaEncoding) {
+        return (String)fJava2IANAMap.remove(javaEncoding);
+    } // removeJava2IANAMapping
+
+} // class EncodingMap
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/ParserUtils.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/ParserUtils.java
new file mode 100755
index 0000000..83bd45c
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/ParserUtils.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.xmlparser;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jasper.Constants;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.compiler.Localizer;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+
+/**
+ * XML parsing utilities for processing web application deployment
+ * descriptor and tag library descriptor files.  FIXME - make these
+ * use a separate class loader for the parser to be used.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class ParserUtils {
+
+    /**
+     * An error handler for use when parsing XML documents.
+     */
+    static ErrorHandler errorHandler = new MyErrorHandler();
+
+    /**
+     * An entity resolver for use when parsing XML documents.
+     */
+    static EntityResolver entityResolver = new MyEntityResolver();
+
+    // Turn off for JSP 2.0 until switch over to using xschema.
+    public static boolean validating = false;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Parse the specified XML document, and return a <code>TreeNode</code>
+     * that corresponds to the root node of the document tree.
+     *
+     * @param uri URI of the XML document being parsed
+     * @param is Input source containing the deployment descriptor
+     *
+     * @exception JasperException if an input/output error occurs
+     * @exception JasperException if a parsing error occurs
+     */
+    public TreeNode parseXMLDocument(String uri, InputSource is)
+        throws JasperException {
+
+        Document document = null;
+
+        // Perform an XML parse of this document, via JAXP
+        try {
+            DocumentBuilderFactory factory =
+                DocumentBuilderFactory.newInstance();
+            factory.setNamespaceAware(true);
+            factory.setValidating(validating);
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            builder.setEntityResolver(entityResolver);
+            builder.setErrorHandler(errorHandler);
+            document = builder.parse(is);
+	} catch (ParserConfigurationException ex) {
+            throw new JasperException
+                (Localizer.getMessage("jsp.error.parse.xml", uri), ex);
+	} catch (SAXParseException ex) {
+            throw new JasperException
+                (Localizer.getMessage("jsp.error.parse.xml.line",
+				      uri,
+				      Integer.toString(ex.getLineNumber()),
+				      Integer.toString(ex.getColumnNumber())),
+		 ex);
+	} catch (SAXException sx) {
+            throw new JasperException
+                (Localizer.getMessage("jsp.error.parse.xml", uri), sx);
+        } catch (IOException io) {
+            throw new JasperException
+                (Localizer.getMessage("jsp.error.parse.xml", uri), io);
+	}
+
+        // Convert the resulting document to a graph of TreeNodes
+        return (convert(null, document.getDocumentElement()));
+    }
+
+
+    /**
+     * Parse the specified XML document, and return a <code>TreeNode</code>
+     * that corresponds to the root node of the document tree.
+     *
+     * @param uri URI of the XML document being parsed
+     * @param is Input stream containing the deployment descriptor
+     *
+     * @exception JasperException if an input/output error occurs
+     * @exception JasperException if a parsing error occurs
+     */
+    public TreeNode parseXMLDocument(String uri, InputStream is)
+            throws JasperException {
+
+        return (parseXMLDocument(uri, new InputSource(is)));
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Create and return a TreeNode that corresponds to the specified Node,
+     * including processing all of the attributes and children nodes.
+     *
+     * @param parent The parent TreeNode (if any) for the new TreeNode
+     * @param node The XML document Node to be converted
+     */
+    protected TreeNode convert(TreeNode parent, Node node) {
+
+        // Construct a new TreeNode for this node
+        TreeNode treeNode = new TreeNode(node.getNodeName(), parent);
+
+        // Convert all attributes of this node
+        NamedNodeMap attributes = node.getAttributes();
+        if (attributes != null) {
+            int n = attributes.getLength();
+            for (int i = 0; i < n; i++) {
+                Node attribute = attributes.item(i);
+                treeNode.addAttribute(attribute.getNodeName(),
+                                      attribute.getNodeValue());
+            }
+        }
+
+        // Create and attach all children of this node
+        NodeList children = node.getChildNodes();
+        if (children != null) {
+            int n = children.getLength();
+            for (int i = 0; i < n; i++) {
+                Node child = children.item(i);
+                if (child instanceof Comment)
+                    continue;
+                if (child instanceof Text) {
+                    String body = ((Text) child).getData();
+                    if (body != null) {
+                        body = body.trim();
+                        if (body.length() > 0)
+                            treeNode.setBody(body);
+                    }
+                } else {
+                    TreeNode treeChild = convert(treeNode, child);
+                }
+            }
+        }
+        
+        // Return the completed TreeNode graph
+        return (treeNode);
+    }
+}
+
+
+// ------------------------------------------------------------ Private Classes
+
+class MyEntityResolver implements EntityResolver {
+
+    // Logger
+    private Log log = LogFactory.getLog(MyEntityResolver.class);
+
+    public InputSource resolveEntity(String publicId, String systemId)
+            throws SAXException {
+        for (int i = 0; i < Constants.CACHED_DTD_PUBLIC_IDS.length; i++) {
+            String cachedDtdPublicId = Constants.CACHED_DTD_PUBLIC_IDS[i];
+            if (cachedDtdPublicId.equals(publicId)) {
+                String resourcePath = Constants.CACHED_DTD_RESOURCE_PATHS[i];
+                InputStream input = this.getClass().getResourceAsStream(
+                        resourcePath);
+                if (input == null) {
+                    throw new SAXException(Localizer.getMessage(
+                            "jsp.error.internal.filenotfound", resourcePath));
+                }
+                InputSource isrc = new InputSource(input);
+                return isrc;
+            }
+        }
+        if (log.isDebugEnabled())
+            log.debug("Resolve entity failed" + publicId + " " + systemId);
+        log.error(Localizer.getMessage("jsp.error.parse.xml.invalidPublicId",
+                publicId));
+        return null;
+    }
+}
+
+class MyErrorHandler implements ErrorHandler {
+
+    // Logger
+    private Log log = LogFactory.getLog(MyErrorHandler.class);
+
+    public void warning(SAXParseException ex) throws SAXException {
+        if (log.isDebugEnabled())
+            log.debug("ParserUtils: warning ", ex);
+        // We ignore warnings
+    }
+
+    public void error(SAXParseException ex) throws SAXException {
+        throw ex;
+    }
+
+    public void fatalError(SAXParseException ex) throws SAXException {
+        throw ex;
+    }
+}
\ No newline at end of file
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/SymbolTable.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/SymbolTable.java
new file mode 100644
index 0000000..e283883
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/SymbolTable.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.apache.org.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.jasper.xmlparser;
+
+/**
+ * This class is a symbol table implementation that guarantees that
+ * strings used as identifiers are unique references. Multiple calls
+ * to <code>addSymbol</code> will always return the same string
+ * reference.
+ * <p>
+ * The symbol table performs the same task as <code>String.intern()</code>
+ * with the following differences:
+ * <ul>
+ *  <li>
+ *   A new string object does not need to be created in order to
+ *   retrieve a unique reference. Symbols can be added by using
+ *   a series of characters in a character array.
+ *  </li>
+ *  <li>
+ *   Users of the symbol table can provide their own symbol hashing
+ *   implementation. For example, a simple string hashing algorithm
+ *   may fail to produce a balanced set of hashcodes for symbols
+ *   that are <em>mostly</em> unique. Strings with similar leading
+ *   characters are especially prone to this poor hashing behavior.
+ *  </li>
+ * </ul>
+ *
+ * @author Andy Clark
+ * @version $Id$
+ */
+public class SymbolTable {
+
+    //
+    // Constants
+    //
+
+    /** Default table size. */
+    protected static final int TABLE_SIZE = 101;
+
+    //
+    // Data
+    //
+
+    /** Buckets. */
+    protected Entry[] fBuckets = null;
+
+    // actual table size
+    protected int fTableSize;
+
+    //
+    // Constructors
+    //
+
+    /** Constructs a symbol table with a default number of buckets. */
+    public SymbolTable() {
+        this(TABLE_SIZE);
+    }
+
+    /** Constructs a symbol table with a specified number of buckets. */
+    public SymbolTable(int tableSize) {
+        fTableSize = tableSize;
+        fBuckets = new Entry[fTableSize];
+    }
+
+    //
+    // Public methods
+    //
+
+    /**
+     * Adds the specified symbol to the symbol table and returns a
+     * reference to the unique symbol. If the symbol already exists,
+     * the previous symbol reference is returned instead, in order
+     * guarantee that symbol references remain unique.
+     *
+     * @param symbol The new symbol.
+     */
+    public String addSymbol(String symbol) {
+
+        // search for identical symbol
+        int bucket = hash(symbol) % fTableSize;
+        int length = symbol.length();
+        OUTER: for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) {
+            if (length == entry.characters.length) {
+                for (int i = 0; i < length; i++) {
+                    if (symbol.charAt(i) != entry.characters[i]) {
+                        continue OUTER;
+                    }
+                }
+                return entry.symbol;
+            }
+        }
+
+        // create new entry
+        Entry entry = new Entry(symbol, fBuckets[bucket]);
+        fBuckets[bucket] = entry;
+        return entry.symbol;
+
+    } // addSymbol(String):String
+
+    /**
+     * Adds the specified symbol to the symbol table and returns a
+     * reference to the unique symbol. If the symbol already exists,
+     * the previous symbol reference is returned instead, in order
+     * guarantee that symbol references remain unique.
+     *
+     * @param buffer The buffer containing the new symbol.
+     * @param offset The offset into the buffer of the new symbol.
+     * @param length The length of the new symbol in the buffer.
+     */
+    public String addSymbol(char[] buffer, int offset, int length) {
+
+        // search for identical symbol
+        int bucket = hash(buffer, offset, length) % fTableSize;
+        OUTER: for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) {
+            if (length == entry.characters.length) {
+                for (int i = 0; i < length; i++) {
+                    if (buffer[offset + i] != entry.characters[i]) {
+                        continue OUTER;
+                    }
+                }
+                return entry.symbol;
+            }
+        }
+
+        // add new entry
+        Entry entry = new Entry(buffer, offset, length, fBuckets[bucket]);
+        fBuckets[bucket] = entry;
+        return entry.symbol;
+
+    } // addSymbol(char[],int,int):String
+
+    /**
+     * Returns a hashcode value for the specified symbol. The value
+     * returned by this method must be identical to the value returned
+     * by the <code>hash(char[],int,int)</code> method when called
+     * with the character array that comprises the symbol string.
+     *
+     * @param symbol The symbol to hash.
+     */
+    public int hash(String symbol) {
+
+        int code = 0;
+        int length = symbol.length();
+        for (int i = 0; i < length; i++) {
+            code = code * 37 + symbol.charAt(i);
+        }
+        return code & 0x7FFFFFF;
+
+    } // hash(String):int
+
+    /**
+     * Returns a hashcode value for the specified symbol information.
+     * The value returned by this method must be identical to the value
+     * returned by the <code>hash(String)</code> method when called
+     * with the string object created from the symbol information.
+     *
+     * @param buffer The character buffer containing the symbol.
+     * @param offset The offset into the character buffer of the start
+     *               of the symbol.
+     * @param length The length of the symbol.
+     */
+    public int hash(char[] buffer, int offset, int length) {
+
+        int code = 0;
+        for (int i = 0; i < length; i++) {
+            code = code * 37 + buffer[offset + i];
+        }
+        return code & 0x7FFFFFF;
+
+    } // hash(char[],int,int):int
+
+    /**
+     * Returns true if the symbol table already contains the specified
+     * symbol.
+     *
+     * @param symbol The symbol to look for.
+     */
+    public boolean containsSymbol(String symbol) {
+
+        // search for identical symbol
+        int bucket = hash(symbol) % fTableSize;
+        int length = symbol.length();
+        OUTER: for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) {
+            if (length == entry.characters.length) {
+                for (int i = 0; i < length; i++) {
+                    if (symbol.charAt(i) != entry.characters[i]) {
+                        continue OUTER;
+                    }
+                }
+                return true;
+            }
+        }
+
+        return false;
+
+    } // containsSymbol(String):boolean
+
+    /**
+     * Returns true if the symbol table already contains the specified
+     * symbol.
+     *
+     * @param buffer The buffer containing the symbol to look for.
+     * @param offset The offset into the buffer.
+     * @param length The length of the symbol in the buffer.
+     */
+    public boolean containsSymbol(char[] buffer, int offset, int length) {
+
+        // search for identical symbol
+        int bucket = hash(buffer, offset, length) % fTableSize;
+        OUTER: for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) {
+            if (length == entry.characters.length) {
+                for (int i = 0; i < length; i++) {
+                    if (buffer[offset + i] != entry.characters[i]) {
+                        continue OUTER;
+                    }
+                }
+                return true;
+            }
+        }
+
+        return false;
+
+    } // containsSymbol(char[],int,int):boolean
+
+    //
+    // Classes
+    //
+
+    /**
+     * This class is a symbol table entry. Each entry acts as a node
+     * in a linked list.
+     */
+    protected static final class Entry {
+
+        //
+        // Data
+        //
+
+        /** Symbol. */
+        public String symbol;
+
+        /**
+         * Symbol characters. This information is duplicated here for
+         * comparison performance.
+         */
+        public char[] characters;
+
+        /** The next entry. */
+        public Entry next;
+
+        //
+        // Constructors
+        //
+
+        /**
+         * Constructs a new entry from the specified symbol and next entry
+         * reference.
+         */
+        public Entry(String symbol, Entry next) {
+            this.symbol = symbol.intern();
+            characters = new char[symbol.length()];
+            symbol.getChars(0, characters.length, characters, 0);
+            this.next = next;
+        }
+
+        /**
+         * Constructs a new entry from the specified symbol information and
+         * next entry reference.
+         */
+        public Entry(char[] ch, int offset, int length, Entry next) {
+            characters = new char[length];
+            System.arraycopy(ch, offset, characters, 0, length);
+            symbol = new String(characters).intern();
+            this.next = next;
+        }
+
+    } // class Entry
+
+} // class SymbolTable
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/TreeNode.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/TreeNode.java
new file mode 100644
index 0000000..e85e762
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/TreeNode.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.xmlparser;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+
+
+/**
+ * Simplified implementation of a Node from a Document Object Model (DOM)
+ * parse of an XML document.  This class is used to represent a DOM tree
+ * so that the XML parser's implementation of <code>org.w3c.dom</code> need
+ * not be visible to the remainder of Jasper.
+ * <p>
+ * <strong>WARNING</strong> - Construction of a new tree, or modifications
+ * to an existing one, are not thread-safe and such accesses must be
+ * synchronized.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class TreeNode {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new node with no parent.
+     *
+     * @param name The name of this node
+     */
+    public TreeNode(String name) {
+
+        this(name, null);
+
+    }
+
+
+    /**
+     * Construct a new node with the specified parent.
+     *
+     * @param name The name of this node
+     * @param parent The node that is the parent of this node
+     */
+    public TreeNode(String name, TreeNode parent) {
+
+        super();
+        this.name = name;
+        this.parent = parent;
+        if (this.parent != null)
+            this.parent.addChild(this);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The attributes of this node, keyed by attribute name,
+     * Instantiated only if required.
+     */
+    protected HashMap attributes = null;
+
+
+    /**
+     * The body text associated with this node (if any).
+     */
+    protected String body = null;
+
+
+    /**
+     * The children of this node, instantiated only if required.
+     */
+    protected ArrayList children = null;
+
+
+    /**
+     * The name of this node.
+     */
+    protected String name = null;
+
+
+    /**
+     * The parent node of this node.
+     */
+    protected TreeNode parent = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an attribute to this node, replacing any existing attribute
+     * with the same name.
+     *
+     * @param name The attribute name to add
+     * @param value The new attribute value
+     */
+    public void addAttribute(String name, String value) {
+
+        if (attributes == null)
+            attributes = new HashMap();
+        attributes.put(name, value);
+
+    }
+
+
+    /**
+     * Add a new child node to this node.
+     *
+     * @param node The new child node
+     */
+    public void addChild(TreeNode node) {
+
+        if (children == null)
+            children = new ArrayList();
+        children.add(node);
+
+    }
+
+
+    /**
+     * Return the value of the specified node attribute if it exists, or
+     * <code>null</code> otherwise.
+     *
+     * @param name Name of the requested attribute
+     */
+    public String findAttribute(String name) {
+
+        if (attributes == null)
+            return (null);
+        else
+            return ((String) attributes.get(name));
+
+    }
+
+
+    /**
+     * Return an Iterator of the attribute names of this node.  If there are
+     * no attributes, an empty Iterator is returned.
+     */
+    public Iterator findAttributes() {
+
+        if (attributes == null)
+            return (Collections.EMPTY_LIST.iterator());
+        else
+            return (attributes.keySet().iterator());
+
+    }
+
+
+    /**
+     * Return the first child node of this node with the specified name,
+     * if there is one; otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired child element
+     */
+    public TreeNode findChild(String name) {
+
+        if (children == null)
+            return (null);
+        Iterator items = children.iterator();
+        while (items.hasNext()) {
+            TreeNode item = (TreeNode) items.next();
+            if (name.equals(item.getName()))
+                return (item);
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Return an Iterator of all children of this node.  If there are no
+     * children, an empty Iterator is returned.
+     */
+    public Iterator findChildren() {
+
+        if (children == null)
+            return (Collections.EMPTY_LIST.iterator());
+        else
+            return (children.iterator());
+
+    }
+
+
+    /**
+     * Return an Iterator over all children of this node that have the
+     * specified name.  If there are no such children, an empty Iterator
+     * is returned.
+     *
+     * @param name Name used to select children
+     */
+    public Iterator findChildren(String name) {
+
+        if (children == null)
+            return (Collections.EMPTY_LIST.iterator());
+
+        ArrayList results = new ArrayList();
+        Iterator items = children.iterator();
+        while (items.hasNext()) {
+            TreeNode item = (TreeNode) items.next();
+            if (name.equals(item.getName()))
+                results.add(item);
+        }
+        return (results.iterator());
+
+    }
+
+
+    /**
+     * Return the body text associated with this node (if any).
+     */
+    public String getBody() {
+
+        return (this.body);
+
+    }
+
+
+    /**
+     * Return the name of this node.
+     */
+    public String getName() {
+
+        return (this.name);
+
+    }
+
+
+    /**
+     * Remove any existing value for the specified attribute name.
+     *
+     * @param name The attribute name to remove
+     */
+    public void removeAttribute(String name) {
+
+        if (attributes != null)
+            attributes.remove(name);
+
+    }
+
+
+    /**
+     * Remove a child node from this node, if it is one.
+     *
+     * @param node The child node to remove
+     */
+    public void removeNode(TreeNode node) {
+
+        if (children != null)
+            children.remove(node);
+
+    }
+
+
+    /**
+     * Set the body text associated with this node (if any).
+     *
+     * @param body The body text (if any)
+     */
+    public void setBody(String body) {
+
+        this.body = body;
+
+    }
+
+
+    /**
+     * Return a String representation of this TreeNode.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer();
+        toString(sb, 0, this);
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Append to the specified StringBuffer a character representation of
+     * this node, with the specified amount of indentation.
+     *
+     * @param sb The StringBuffer to append to
+     * @param indent Number of characters of indentation
+     * @param node The TreeNode to be printed
+     */
+    protected void toString(StringBuffer sb, int indent,
+                            TreeNode node) {
+
+        int indent2 = indent + 2;
+
+        // Reconstruct an opening node
+        for (int i = 0; i < indent; i++)
+            sb.append(' ');
+        sb.append('<');
+        sb.append(node.getName());
+        Iterator names = node.findAttributes();
+        while (names.hasNext()) {
+            sb.append(' ');
+            String name = (String) names.next();
+            sb.append(name);
+            sb.append("=\"");
+            String value = node.findAttribute(name);
+            sb.append(value);
+            sb.append("\"");
+        }
+        sb.append(">\n");
+
+        // Reconstruct the body text of this node (if any)
+        String body = node.getBody();
+        if ((body != null) && (body.length() > 0)) {
+            for (int i = 0; i < indent2; i++)
+                sb.append(' ');
+            sb.append(body);
+            sb.append("\n");
+        }
+
+        // Reconstruct child nodes with extra indentation
+        Iterator children = node.findChildren();
+        while (children.hasNext()) {
+            TreeNode child = (TreeNode) children.next();
+            toString(sb, indent2, child);
+        }
+
+        // Reconstruct a closing node marker
+        for (int i = 0; i < indent; i++)
+            sb.append(' ');
+        sb.append("</");
+        sb.append(node.getName());
+        sb.append(">\n");
+
+    }
+
+
+}
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/UCSReader.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/UCSReader.java
new file mode 100644
index 0000000..5d0a990
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/UCSReader.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.xmlparser;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.Reader;
+
+/** 
+ * Reader for UCS-2 and UCS-4 encodings.
+ * (i.e., encodings from ISO-10646-UCS-(2|4)).
+ *
+ * @author Neil Graham, IBM
+ *
+ * @version $Id$
+ */
+public class UCSReader extends Reader {
+
+    private org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( UCSReader.class );
+    
+    //
+    // Constants
+    //
+
+    /** Default byte buffer size (8192, larger than that of ASCIIReader
+     * since it's reasonable to surmise that the average UCS-4-encoded
+     * file should be 4 times as large as the average ASCII-encoded file). 
+     */
+    public static final int DEFAULT_BUFFER_SIZE = 8192;
+
+    public static final short UCS2LE = 1;
+    public static final short UCS2BE = 2;
+    public static final short UCS4LE = 4;
+    public static final short UCS4BE = 8;
+
+    //
+    // Data
+    //
+
+    /** Input stream. */
+    protected InputStream fInputStream;
+
+    /** Byte buffer. */
+    protected byte[] fBuffer;
+
+    // what kind of data we're dealing with
+    protected short fEncoding;
+
+    //
+    // Constructors
+    //
+
+    /** 
+     * Constructs an ASCII reader from the specified input stream 
+     * using the default buffer size.  The Endian-ness and whether this is
+     * UCS-2 or UCS-4 needs also to be known in advance.
+     *
+     * @param inputStream The input stream.
+     * @param encoding One of UCS2LE, UCS2BE, UCS4LE or UCS4BE.
+     */
+    public UCSReader(InputStream inputStream, short encoding) {
+        this(inputStream, DEFAULT_BUFFER_SIZE, encoding);
+    } // <init>(InputStream, short)
+
+    /** 
+     * Constructs an ASCII reader from the specified input stream 
+     * and buffer size.  The Endian-ness and whether this is
+     * UCS-2 or UCS-4 needs also to be known in advance.
+     *
+     * @param inputStream The input stream.
+     * @param size        The initial buffer size.
+     * @param encoding One of UCS2LE, UCS2BE, UCS4LE or UCS4BE.
+     */
+    public UCSReader(InputStream inputStream, int size, short encoding) {
+        fInputStream = inputStream;
+        fBuffer = new byte[size];
+        fEncoding = encoding;
+    } // <init>(InputStream,int,short)
+
+    //
+    // Reader methods
+    //
+
+    /**
+     * Read a single character.  This method will block until a character is
+     * available, an I/O error occurs, or the end of the stream is reached.
+     *
+     * <p> Subclasses that intend to support efficient single-character input
+     * should override this method.
+     *
+     * @return     The character read, as an integer in the range 0 to 127
+     *             (<tt>0x00-0x7f</tt>), or -1 if the end of the stream has
+     *             been reached
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public int read() throws IOException { 
+        int b0 = fInputStream.read() & 0xff;
+        if (b0 == 0xff)
+            return -1;
+        int b1 = fInputStream.read() & 0xff;
+        if (b1 == 0xff)
+            return -1;
+        if(fEncoding >=4) {
+            int b2 = fInputStream.read() & 0xff;
+            if (b2 == 0xff)
+                return -1;
+            int b3 = fInputStream.read() & 0xff;
+            if (b3 == 0xff)
+                return -1;
+            if (log.isDebugEnabled())
+                log.debug("b0 is " + (b0 & 0xff) + " b1 " + (b1 & 0xff) + " b2 " + (b2 & 0xff) + " b3 " + (b3 & 0xff));
+            if (fEncoding == UCS4BE)
+                return (b0<<24)+(b1<<16)+(b2<<8)+b3;
+            else
+                return (b3<<24)+(b2<<16)+(b1<<8)+b0;
+        } else { // UCS-2
+            if (fEncoding == UCS2BE)
+                return (b0<<8)+b1;
+            else
+                return (b1<<8)+b0;
+        }
+    } // read():int
+
+    /**
+     * Read characters into a portion of an array.  This method will block
+     * until some input is available, an I/O error occurs, or the end of the
+     * stream is reached.
+     *
+     * @param      ch     Destination buffer
+     * @param      offset Offset at which to start storing characters
+     * @param      length Maximum number of characters to read
+     *
+     * @return     The number of characters read, or -1 if the end of the
+     *             stream has been reached
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public int read(char ch[], int offset, int length) throws IOException {
+        int byteLength = length << ((fEncoding >= 4)?2:1);
+        if (byteLength > fBuffer.length) {
+            byteLength = fBuffer.length;
+        }
+        int count = fInputStream.read(fBuffer, 0, byteLength);
+        if(count == -1) return -1;
+        // try and make count be a multiple of the number of bytes we're looking for
+        if(fEncoding >= 4) { // BigEndian
+            // this looks ugly, but it avoids an if at any rate...
+            int numToRead = (4 - (count & 3) & 3);
+            for(int i=0; i<numToRead; i++) {
+                int charRead = fInputStream.read();
+                if(charRead == -1) { // end of input; something likely went wrong!A  Pad buffer with nulls.
+                    for (int j = i;j<numToRead; j++)
+                        fBuffer[count+j] = 0;
+                    break;
+                } else {
+                    fBuffer[count+i] = (byte)charRead; 
+                }
+            }
+            count += numToRead;
+        } else {
+            int numToRead = count & 1;
+            if(numToRead != 0) {
+                count++;
+                int charRead = fInputStream.read();
+                if(charRead == -1) { // end of input; something likely went wrong!A  Pad buffer with nulls.
+                    fBuffer[count] = 0;
+                } else {
+                    fBuffer[count] = (byte)charRead;
+                }
+            }
+        }
+
+        // now count is a multiple of the right number of bytes
+        int numChars = count >> ((fEncoding >= 4)?2:1);
+        int curPos = 0;
+        for (int i = 0; i < numChars; i++) {
+            int b0 = fBuffer[curPos++] & 0xff;
+            int b1 = fBuffer[curPos++] & 0xff;
+            if(fEncoding >=4) {
+                int b2 = fBuffer[curPos++] & 0xff;
+                int b3 = fBuffer[curPos++] & 0xff;
+                if (fEncoding == UCS4BE)
+                    ch[offset+i] = (char)((b0<<24)+(b1<<16)+(b2<<8)+b3);
+                else
+                    ch[offset+i] = (char)((b3<<24)+(b2<<16)+(b1<<8)+b0);
+            } else { // UCS-2
+                if (fEncoding == UCS2BE)
+                    ch[offset+i] = (char)((b0<<8)+b1);
+                else
+                    ch[offset+i] = (char)((b1<<8)+b0);
+            }
+        }
+        return numChars;
+    } // read(char[],int,int)
+
+    /**
+     * Skip characters.  This method will block until some characters are
+     * available, an I/O error occurs, or the end of the stream is reached.
+     *
+     * @param  n  The number of characters to skip
+     *
+     * @return    The number of characters actually skipped
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public long skip(long n) throws IOException {
+        // charWidth will represent the number of bits to move
+        // n leftward to get num of bytes to skip, and then move the result rightward
+        // to get num of chars effectively skipped.
+        // The trick with &'ing, as with elsewhere in this dcode, is
+        // intended to avoid an expensive use of / that might not be optimized
+        // away.
+        int charWidth = (fEncoding >=4)?2:1;
+        long bytesSkipped = fInputStream.skip(n<<charWidth);
+        if((bytesSkipped & (charWidth | 1)) == 0) return bytesSkipped >> charWidth;
+        return (bytesSkipped >> charWidth) + 1;
+    } // skip(long):long
+
+    /**
+     * Tell whether this stream is ready to be read.
+     *
+     * @return True if the next read() is guaranteed not to block for input,
+     * false otherwise.  Note that returning false does not guarantee that the
+     * next read will block.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public boolean ready() throws IOException {
+	return false;
+    } // ready()
+
+    /**
+     * Tell whether this stream supports the mark() operation.
+     */
+    public boolean markSupported() {
+	return fInputStream.markSupported();
+    } // markSupported()
+
+    /**
+     * Mark the present position in the stream.  Subsequent calls to reset()
+     * will attempt to reposition the stream to this point.  Not all
+     * character-input streams support the mark() operation.
+     *
+     * @param  readAheadLimit  Limit on the number of characters that may be
+     *                         read while still preserving the mark.  After
+     *                         reading this many characters, attempting to
+     *                         reset the stream may fail.
+     *
+     * @exception  IOException  If the stream does not support mark(),
+     *                          or if some other I/O error occurs
+     */
+    public void mark(int readAheadLimit) throws IOException {
+	fInputStream.mark(readAheadLimit);
+    } // mark(int)
+
+    /**
+     * Reset the stream.  If the stream has been marked, then attempt to
+     * reposition it at the mark.  If the stream has not been marked, then
+     * attempt to reset it in some way appropriate to the particular stream,
+     * for example by repositioning it to its starting point.  Not all
+     * character-input streams support the reset() operation, and some support
+     * reset() without supporting mark().
+     *
+     * @exception  IOException  If the stream has not been marked,
+     *                          or if the mark has been invalidated,
+     *                          or if the stream does not support reset(),
+     *                          or if some other I/O error occurs
+     */
+    public void reset() throws IOException {
+        fInputStream.reset();
+    } // reset()
+
+    /**
+     * Close the stream.  Once a stream has been closed, further read(),
+     * ready(), mark(), or reset() invocations will throw an IOException.
+     * Closing a previously-closed stream, however, has no effect.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+     public void close() throws IOException {
+         fInputStream.close();
+     } // close()
+
+} // class UCSReader
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/UTF8Reader.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/UTF8Reader.java
new file mode 100644
index 0000000..f0b3784
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/UTF8Reader.java
@@ -0,0 +1,634 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.xmlparser;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UTFDataFormatException;
+import org.apache.jasper.compiler.Localizer;
+
+/**
+ * @author Andy Clark, IBM
+ *
+ * @version $Id$
+ */
+public class UTF8Reader
+    extends Reader {
+
+    private org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( UTF8Reader.class );
+    
+    //
+    // Constants
+    //
+
+    /** Default byte buffer size (2048). */
+    public static final int DEFAULT_BUFFER_SIZE = 2048;
+
+    // debugging
+
+    /** Debug read. */
+    private static final boolean DEBUG_READ = false;
+
+    //
+    // Data
+    //
+
+    /** Input stream. */
+    protected InputStream fInputStream;
+
+    /** Byte buffer. */
+    protected byte[] fBuffer;
+
+    /** Offset into buffer. */
+    protected int fOffset;
+
+    /** Surrogate character. */
+    private int fSurrogate = -1;
+
+    //
+    // Constructors
+    //
+
+    /** 
+     * Constructs a UTF-8 reader from the specified input stream, 
+     * buffer size and MessageFormatter.
+     *
+     * @param inputStream The input stream.
+     * @param size        The initial buffer size.
+     */
+    public UTF8Reader(InputStream inputStream, int size) {
+        fInputStream = inputStream;
+        fBuffer = new byte[size];
+    }
+
+    //
+    // Reader methods
+    //
+
+    /**
+     * Read a single character.  This method will block until a character is
+     * available, an I/O error occurs, or the end of the stream is reached.
+     *
+     * <p> Subclasses that intend to support efficient single-character input
+     * should override this method.
+     *
+     * @return     The character read, as an integer in the range 0 to 16383
+     *             (<tt>0x00-0xffff</tt>), or -1 if the end of the stream has
+     *             been reached
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public int read() throws IOException {
+
+        // decode character
+        int c = fSurrogate;
+        if (fSurrogate == -1) {
+            // NOTE: We use the index into the buffer if there are remaining
+            //       bytes from the last block read. -Ac
+            int index = 0;
+
+            // get first byte
+            int b0 = index == fOffset 
+                   ? fInputStream.read() : fBuffer[index++] & 0x00FF;
+            if (b0 == -1) {
+                return -1;
+            }
+
+            // UTF-8:   [0xxx xxxx]
+            // Unicode: [0000 0000] [0xxx xxxx]
+            if (b0 < 0x80) {
+                c = (char)b0;
+            }
+
+            // UTF-8:   [110y yyyy] [10xx xxxx]
+            // Unicode: [0000 0yyy] [yyxx xxxx]
+            else if ((b0 & 0xE0) == 0xC0) {
+                int b1 = index == fOffset 
+                       ? fInputStream.read() : fBuffer[index++] & 0x00FF;
+                if (b1 == -1) {
+                    expectedByte(2, 2);
+                }
+                if ((b1 & 0xC0) != 0x80) {
+                    invalidByte(2, 2, b1);
+                }
+                c = ((b0 << 6) & 0x07C0) | (b1 & 0x003F);
+            }
+
+            // UTF-8:   [1110 zzzz] [10yy yyyy] [10xx xxxx]
+            // Unicode: [zzzz yyyy] [yyxx xxxx]
+            else if ((b0 & 0xF0) == 0xE0) {
+                int b1 = index == fOffset
+                       ? fInputStream.read() : fBuffer[index++] & 0x00FF;
+                if (b1 == -1) {
+                    expectedByte(2, 3);
+                }
+                if ((b1 & 0xC0) != 0x80) {
+                    invalidByte(2, 3, b1);
+                }
+                int b2 = index == fOffset 
+                       ? fInputStream.read() : fBuffer[index++] & 0x00FF;
+                if (b2 == -1) {
+                    expectedByte(3, 3);
+                }
+                if ((b2 & 0xC0) != 0x80) {
+                    invalidByte(3, 3, b2);
+                }
+                c = ((b0 << 12) & 0xF000) | ((b1 << 6) & 0x0FC0) |
+                    (b2 & 0x003F);
+            }
+
+            // UTF-8:   [1111 0uuu] [10uu zzzz] [10yy yyyy] [10xx xxxx]*
+            // Unicode: [1101 10ww] [wwzz zzyy] (high surrogate)
+            //          [1101 11yy] [yyxx xxxx] (low surrogate)
+            //          * uuuuu = wwww + 1
+            else if ((b0 & 0xF8) == 0xF0) {
+                int b1 = index == fOffset 
+                       ? fInputStream.read() : fBuffer[index++] & 0x00FF;
+                if (b1 == -1) {
+                    expectedByte(2, 4);
+                }
+                if ((b1 & 0xC0) != 0x80) {
+                    invalidByte(2, 3, b1);
+                }
+                int b2 = index == fOffset 
+                       ? fInputStream.read() : fBuffer[index++] & 0x00FF;
+                if (b2 == -1) {
+                    expectedByte(3, 4);
+                }
+                if ((b2 & 0xC0) != 0x80) {
+                    invalidByte(3, 3, b2);
+                }
+                int b3 = index == fOffset 
+                       ? fInputStream.read() : fBuffer[index++] & 0x00FF;
+                if (b3 == -1) {
+                    expectedByte(4, 4);
+                }
+                if ((b3 & 0xC0) != 0x80) {
+                    invalidByte(4, 4, b3);
+                }
+                int uuuuu = ((b0 << 2) & 0x001C) | ((b1 >> 4) & 0x0003);
+                if (uuuuu > 0x10) {
+                    invalidSurrogate(uuuuu);
+                }
+                int wwww = uuuuu - 1;
+                int hs = 0xD800 | 
+                         ((wwww << 6) & 0x03C0) | ((b1 << 2) & 0x003C) | 
+                         ((b2 >> 4) & 0x0003);
+                int ls = 0xDC00 | ((b2 << 6) & 0x03C0) | (b3 & 0x003F);
+                c = hs;
+                fSurrogate = ls;
+            }
+
+            // error
+            else {
+                invalidByte(1, 1, b0);
+            }
+        }
+
+        // use surrogate
+        else {
+            fSurrogate = -1;
+        }
+
+        // return character
+        if (DEBUG_READ) {
+            if (log.isDebugEnabled())
+                log.debug("read(): 0x"+Integer.toHexString(c));
+        }
+        return c;
+
+    } // read():int
+
+    /**
+     * Read characters into a portion of an array.  This method will block
+     * until some input is available, an I/O error occurs, or the end of the
+     * stream is reached.
+     *
+     * @param      ch     Destination buffer
+     * @param      offset Offset at which to start storing characters
+     * @param      length Maximum number of characters to read
+     *
+     * @return     The number of characters read, or -1 if the end of the
+     *             stream has been reached
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public int read(char ch[], int offset, int length) throws IOException {
+
+        // handle surrogate
+        int out = offset;
+        if (fSurrogate != -1) {
+            ch[offset + 1] = (char)fSurrogate;
+            fSurrogate = -1;
+            length--;
+            out++;
+        }
+
+        // read bytes
+        int count = 0;
+        if (fOffset == 0) {
+            // adjust length to read
+            if (length > fBuffer.length) {
+                length = fBuffer.length;
+            }
+
+            // perform read operation
+            count = fInputStream.read(fBuffer, 0, length);
+            if (count == -1) {
+                return -1;
+            }
+            count += out - offset;
+        }
+
+        // skip read; last character was in error
+        // NOTE: Having an offset value other than zero means that there was
+        //       an error in the last character read. In this case, we have
+        //       skipped the read so we don't consume any bytes past the 
+        //       error. By signalling the error on the next block read we
+        //       allow the method to return the most valid characters that
+        //       it can on the previous block read. -Ac
+        else {
+            count = fOffset;
+            fOffset = 0;
+        }
+
+        // convert bytes to characters
+        final int total = count;
+        for (int in = 0; in < total; in++) {
+            int b0 = fBuffer[in] & 0x00FF;
+
+            // UTF-8:   [0xxx xxxx]
+            // Unicode: [0000 0000] [0xxx xxxx]
+            if (b0 < 0x80) {
+                ch[out++] = (char)b0;
+                continue;
+            }
+
+            // UTF-8:   [110y yyyy] [10xx xxxx]
+            // Unicode: [0000 0yyy] [yyxx xxxx]
+            if ((b0 & 0xE0) == 0xC0) {
+                int b1 = -1;
+                if (++in < total) { 
+                    b1 = fBuffer[in] & 0x00FF; 
+                }
+                else {
+                    b1 = fInputStream.read();
+                    if (b1 == -1) {
+                        if (out > offset) {
+                            fBuffer[0] = (byte)b0;
+                            fOffset = 1;
+                            return out - offset;
+                        }
+                        expectedByte(2, 2);
+                    }
+                    count++;
+                }
+                if ((b1 & 0xC0) != 0x80) {
+                    if (out > offset) {
+                        fBuffer[0] = (byte)b0;
+                        fBuffer[1] = (byte)b1;
+                        fOffset = 2;
+                        return out - offset;
+                    }
+                    invalidByte(2, 2, b1);
+                }
+                int c = ((b0 << 6) & 0x07C0) | (b1 & 0x003F);
+                ch[out++] = (char)c;
+                count -= 1;
+                continue;
+            }
+
+            // UTF-8:   [1110 zzzz] [10yy yyyy] [10xx xxxx]
+            // Unicode: [zzzz yyyy] [yyxx xxxx]
+            if ((b0 & 0xF0) == 0xE0) {
+                int b1 = -1;
+                if (++in < total) { 
+                    b1 = fBuffer[in] & 0x00FF; 
+                }
+                else {
+                    b1 = fInputStream.read();
+                    if (b1 == -1) {
+                        if (out > offset) {
+                            fBuffer[0] = (byte)b0;
+                            fOffset = 1;
+                            return out - offset;
+                        }
+                        expectedByte(2, 3);
+                    }
+                    count++;
+                }
+                if ((b1 & 0xC0) != 0x80) {
+                    if (out > offset) {
+                        fBuffer[0] = (byte)b0;
+                        fBuffer[1] = (byte)b1;
+                        fOffset = 2;
+                        return out - offset;
+                    }
+                    invalidByte(2, 3, b1);
+                }
+                int b2 = -1;
+                if (++in < total) { 
+                    b2 = fBuffer[in] & 0x00FF; 
+                }
+                else {
+                    b2 = fInputStream.read();
+                    if (b2 == -1) {
+                        if (out > offset) {
+                            fBuffer[0] = (byte)b0;
+                            fBuffer[1] = (byte)b1;
+                            fOffset = 2;
+                            return out - offset;
+                        }
+                        expectedByte(3, 3);
+                    }
+                    count++;
+                }
+                if ((b2 & 0xC0) != 0x80) {
+                    if (out > offset) {
+                        fBuffer[0] = (byte)b0;
+                        fBuffer[1] = (byte)b1;
+                        fBuffer[2] = (byte)b2;
+                        fOffset = 3;
+                        return out - offset;
+                    }
+                    invalidByte(3, 3, b2);
+                }
+                int c = ((b0 << 12) & 0xF000) | ((b1 << 6) & 0x0FC0) |
+                        (b2 & 0x003F);
+                ch[out++] = (char)c;
+                count -= 2;
+                continue;
+            }
+
+            // UTF-8:   [1111 0uuu] [10uu zzzz] [10yy yyyy] [10xx xxxx]*
+            // Unicode: [1101 10ww] [wwzz zzyy] (high surrogate)
+            //          [1101 11yy] [yyxx xxxx] (low surrogate)
+            //          * uuuuu = wwww + 1
+            if ((b0 & 0xF8) == 0xF0) {
+                int b1 = -1;
+                if (++in < total) { 
+                    b1 = fBuffer[in] & 0x00FF; 
+                }
+                else {
+                    b1 = fInputStream.read();
+                    if (b1 == -1) {
+                        if (out > offset) {
+                            fBuffer[0] = (byte)b0;
+                            fOffset = 1;
+                            return out - offset;
+                        }
+                        expectedByte(2, 4);
+                    }
+                    count++;
+                }
+                if ((b1 & 0xC0) != 0x80) {
+                    if (out > offset) {
+                        fBuffer[0] = (byte)b0;
+                        fBuffer[1] = (byte)b1;
+                        fOffset = 2;
+                        return out - offset;
+                    }
+                    invalidByte(2, 4, b1);
+                }
+                int b2 = -1;
+                if (++in < total) { 
+                    b2 = fBuffer[in] & 0x00FF; 
+                }
+                else {
+                    b2 = fInputStream.read();
+                    if (b2 == -1) {
+                        if (out > offset) {
+                            fBuffer[0] = (byte)b0;
+                            fBuffer[1] = (byte)b1;
+                            fOffset = 2;
+                            return out - offset;
+                        }
+                        expectedByte(3, 4);
+                    }
+                    count++;
+                }
+                if ((b2 & 0xC0) != 0x80) {
+                    if (out > offset) {
+                        fBuffer[0] = (byte)b0;
+                        fBuffer[1] = (byte)b1;
+                        fBuffer[2] = (byte)b2;
+                        fOffset = 3;
+                        return out - offset;
+                    }
+                    invalidByte(3, 4, b2);
+                }
+                int b3 = -1;
+                if (++in < total) { 
+                    b3 = fBuffer[in] & 0x00FF; 
+                }
+                else {
+                    b3 = fInputStream.read();
+                    if (b3 == -1) {
+                        if (out > offset) {
+                            fBuffer[0] = (byte)b0;
+                            fBuffer[1] = (byte)b1;
+                            fBuffer[2] = (byte)b2;
+                            fOffset = 3;
+                            return out - offset;
+                        }
+                        expectedByte(4, 4);
+                    }
+                    count++;
+                }
+                if ((b3 & 0xC0) != 0x80) {
+                    if (out > offset) {
+                        fBuffer[0] = (byte)b0;
+                        fBuffer[1] = (byte)b1;
+                        fBuffer[2] = (byte)b2;
+                        fBuffer[3] = (byte)b3;
+                        fOffset = 4;
+                        return out - offset;
+                    }
+                    invalidByte(4, 4, b2);
+                }
+
+                // decode bytes into surrogate characters
+                int uuuuu = ((b0 << 2) & 0x001C) | ((b1 >> 4) & 0x0003);
+                if (uuuuu > 0x10) {
+                    invalidSurrogate(uuuuu);
+                }
+                int wwww = uuuuu - 1;
+                int zzzz = b1 & 0x000F;
+                int yyyyyy = b2 & 0x003F;
+                int xxxxxx = b3 & 0x003F;
+                int hs = 0xD800 | ((wwww << 6) & 0x03C0) | (zzzz << 2) | (yyyyyy >> 4);
+                int ls = 0xDC00 | ((yyyyyy << 6) & 0x03C0) | xxxxxx;
+
+                // set characters
+                ch[out++] = (char)hs;
+                ch[out++] = (char)ls;
+                count -= 2;
+                continue;
+            }
+
+            // error
+            if (out > offset) {
+                fBuffer[0] = (byte)b0;
+                fOffset = 1;
+                return out - offset;
+            }
+            invalidByte(1, 1, b0);
+        }
+
+        // return number of characters converted
+        if (DEBUG_READ) {
+            if (log.isDebugEnabled())
+                log.debug("read(char[],"+offset+','+length+"): count="+count);
+        }
+        return count;
+
+    } // read(char[],int,int)
+
+    /**
+     * Skip characters.  This method will block until some characters are
+     * available, an I/O error occurs, or the end of the stream is reached.
+     *
+     * @param  n  The number of characters to skip
+     *
+     * @return    The number of characters actually skipped
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public long skip(long n) throws IOException {
+
+        long remaining = n;
+        final char[] ch = new char[fBuffer.length];
+        do {
+            int length = ch.length < remaining ? ch.length : (int)remaining;
+            int count = read(ch, 0, length);
+            if (count > 0) {
+                remaining -= count;
+            }
+            else {
+                break;
+            }
+        } while (remaining > 0);
+
+        long skipped = n - remaining;
+        return skipped;
+
+    } // skip(long):long
+
+    /**
+     * Tell whether this stream is ready to be read.
+     *
+     * @return True if the next read() is guaranteed not to block for input,
+     * false otherwise.  Note that returning false does not guarantee that the
+     * next read will block.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public boolean ready() throws IOException {
+	    return false;
+    } // ready()
+
+    /**
+     * Tell whether this stream supports the mark() operation.
+     */
+    public boolean markSupported() {
+	    return false;
+    } // markSupported()
+
+    /**
+     * Mark the present position in the stream.  Subsequent calls to reset()
+     * will attempt to reposition the stream to this point.  Not all
+     * character-input streams support the mark() operation.
+     *
+     * @param  readAheadLimit  Limit on the number of characters that may be
+     *                         read while still preserving the mark.  After
+     *                         reading this many characters, attempting to
+     *                         reset the stream may fail.
+     *
+     * @exception  IOException  If the stream does not support mark(),
+     *                          or if some other I/O error occurs
+     */
+    public void mark(int readAheadLimit) throws IOException {
+	throw new IOException(
+                Localizer.getMessage("jsp.error.xml.operationNotSupported",
+				     "mark()", "UTF-8"));
+    }
+
+    /**
+     * Reset the stream.  If the stream has been marked, then attempt to
+     * reposition it at the mark.  If the stream has not been marked, then
+     * attempt to reset it in some way appropriate to the particular stream,
+     * for example by repositioning it to its starting point.  Not all
+     * character-input streams support the reset() operation, and some support
+     * reset() without supporting mark().
+     *
+     * @exception  IOException  If the stream has not been marked,
+     *                          or if the mark has been invalidated,
+     *                          or if the stream does not support reset(),
+     *                          or if some other I/O error occurs
+     */
+    public void reset() throws IOException {
+        fOffset = 0;
+        fSurrogate = -1;
+    } // reset()
+
+    /**
+     * Close the stream.  Once a stream has been closed, further read(),
+     * ready(), mark(), or reset() invocations will throw an IOException.
+     * Closing a previously-closed stream, however, has no effect.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public void close() throws IOException {
+        fInputStream.close();
+    } // close()
+
+    //
+    // Private methods
+    //
+
+    /** Throws an exception for expected byte. */
+    private void expectedByte(int position, int count)
+        throws UTFDataFormatException {
+
+        throw new UTFDataFormatException(
+                Localizer.getMessage("jsp.error.xml.expectedByte",
+				     Integer.toString(position),
+				     Integer.toString(count)));
+
+    } // expectedByte(int,int,int)
+
+    /** Throws an exception for invalid byte. */
+    private void invalidByte(int position, int count, int c) 
+        throws UTFDataFormatException {
+
+        throw new UTFDataFormatException(
+                Localizer.getMessage("jsp.error.xml.invalidByte",
+				     Integer.toString(position),
+				     Integer.toString(count)));
+    } // invalidByte(int,int,int,int)
+
+    /** Throws an exception for invalid surrogate bits. */
+    private void invalidSurrogate(int uuuuu) throws UTFDataFormatException {
+        
+        throw new UTFDataFormatException(
+                Localizer.getMessage("jsp.error.xml.invalidHighSurrogate",
+				     Integer.toHexString(uuuuu)));
+    } // invalidSurrogate(int)
+
+} // class UTF8Reader
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLChar.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLChar.java
new file mode 100644
index 0000000..fb49435
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLChar.java
@@ -0,0 +1,1030 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.apache.org.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.jasper.xmlparser;
+
+import java.util.Arrays;
+
+/**
+ * This class defines the basic XML character properties. The data
+ * in this class can be used to verify that a character is a valid
+ * XML character or if the character is a space, name start, or name
+ * character.
+ * <p>
+ * A series of convenience methods are supplied to ease the burden
+ * of the developer. Because inlining the checks can improve per
+ * character performance, the tables of character properties are
+ * public. Using the character as an index into the <code>CHARS</code>
+ * array and applying the appropriate mask flag (e.g.
+ * <code>MASK_VALID</code>), yields the same results as calling the
+ * convenience methods. There is one exception: check the comments
+ * for the <code>isValid</code> method for details.
+ *
+ * @author Glenn Marcy, IBM
+ * @author Andy Clark, IBM
+ * @author Eric Ye, IBM
+ * @author Arnaud  Le Hors, IBM
+ * @author Michael Glavassevich, IBM
+ * @author Rahul Srivastava, Sun Microsystems Inc.
+ *
+ * @version $Id$
+ */
+public class XMLChar {
+
+    //
+    // Constants
+    //
+
+    /** Character flags. */
+    private static final byte[] CHARS = new byte[1 << 16];
+
+    /** Valid character mask. */
+    public static final int MASK_VALID = 0x01;
+
+    /** Space character mask. */
+    public static final int MASK_SPACE = 0x02;
+
+    /** Name start character mask. */
+    public static final int MASK_NAME_START = 0x04;
+
+    /** Name character mask. */
+    public static final int MASK_NAME = 0x08;
+
+    /** Pubid character mask. */
+    public static final int MASK_PUBID = 0x10;
+    
+    /** 
+     * Content character mask. Special characters are those that can
+     * be considered the start of markup, such as '&lt;' and '&amp;'. 
+     * The various newline characters are considered special as well.
+     * All other valid XML characters can be considered content.
+     * <p>
+     * This is an optimization for the inner loop of character scanning.
+     */
+    public static final int MASK_CONTENT = 0x20;
+
+    /** NCName start character mask. */
+    public static final int MASK_NCNAME_START = 0x40;
+
+    /** NCName character mask. */
+    public static final int MASK_NCNAME = 0x80;
+
+    //
+    // Static initialization
+    //
+
+    static {
+        
+        // Initializing the Character Flag Array
+        // Code generated by: XMLCharGenerator.
+        
+        CHARS[9] = 35;
+        CHARS[10] = 19;
+        CHARS[13] = 19;
+        CHARS[32] = 51;
+        CHARS[33] = 49;
+        CHARS[34] = 33;
+        Arrays.fill(CHARS, 35, 38, (byte) 49 ); // Fill 3 of value (byte) 49
+        CHARS[38] = 1;
+        Arrays.fill(CHARS, 39, 45, (byte) 49 ); // Fill 6 of value (byte) 49
+        Arrays.fill(CHARS, 45, 47, (byte) -71 ); // Fill 2 of value (byte) -71
+        CHARS[47] = 49;
+        Arrays.fill(CHARS, 48, 58, (byte) -71 ); // Fill 10 of value (byte) -71
+        CHARS[58] = 61;
+        CHARS[59] = 49;
+        CHARS[60] = 1;
+        CHARS[61] = 49;
+        CHARS[62] = 33;
+        Arrays.fill(CHARS, 63, 65, (byte) 49 ); // Fill 2 of value (byte) 49
+        Arrays.fill(CHARS, 65, 91, (byte) -3 ); // Fill 26 of value (byte) -3
+        Arrays.fill(CHARS, 91, 93, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[93] = 1;
+        CHARS[94] = 33;
+        CHARS[95] = -3;
+        CHARS[96] = 33;
+        Arrays.fill(CHARS, 97, 123, (byte) -3 ); // Fill 26 of value (byte) -3
+        Arrays.fill(CHARS, 123, 183, (byte) 33 ); // Fill 60 of value (byte) 33
+        CHARS[183] = -87;
+        Arrays.fill(CHARS, 184, 192, (byte) 33 ); // Fill 8 of value (byte) 33
+        Arrays.fill(CHARS, 192, 215, (byte) -19 ); // Fill 23 of value (byte) -19
+        CHARS[215] = 33;
+        Arrays.fill(CHARS, 216, 247, (byte) -19 ); // Fill 31 of value (byte) -19
+        CHARS[247] = 33;
+        Arrays.fill(CHARS, 248, 306, (byte) -19 ); // Fill 58 of value (byte) -19
+        Arrays.fill(CHARS, 306, 308, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 308, 319, (byte) -19 ); // Fill 11 of value (byte) -19
+        Arrays.fill(CHARS, 319, 321, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 321, 329, (byte) -19 ); // Fill 8 of value (byte) -19
+        CHARS[329] = 33;
+        Arrays.fill(CHARS, 330, 383, (byte) -19 ); // Fill 53 of value (byte) -19
+        CHARS[383] = 33;
+        Arrays.fill(CHARS, 384, 452, (byte) -19 ); // Fill 68 of value (byte) -19
+        Arrays.fill(CHARS, 452, 461, (byte) 33 ); // Fill 9 of value (byte) 33
+        Arrays.fill(CHARS, 461, 497, (byte) -19 ); // Fill 36 of value (byte) -19
+        Arrays.fill(CHARS, 497, 500, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 500, 502, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 502, 506, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 506, 536, (byte) -19 ); // Fill 30 of value (byte) -19
+        Arrays.fill(CHARS, 536, 592, (byte) 33 ); // Fill 56 of value (byte) 33
+        Arrays.fill(CHARS, 592, 681, (byte) -19 ); // Fill 89 of value (byte) -19
+        Arrays.fill(CHARS, 681, 699, (byte) 33 ); // Fill 18 of value (byte) 33
+        Arrays.fill(CHARS, 699, 706, (byte) -19 ); // Fill 7 of value (byte) -19
+        Arrays.fill(CHARS, 706, 720, (byte) 33 ); // Fill 14 of value (byte) 33
+        Arrays.fill(CHARS, 720, 722, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 722, 768, (byte) 33 ); // Fill 46 of value (byte) 33
+        Arrays.fill(CHARS, 768, 838, (byte) -87 ); // Fill 70 of value (byte) -87
+        Arrays.fill(CHARS, 838, 864, (byte) 33 ); // Fill 26 of value (byte) 33
+        Arrays.fill(CHARS, 864, 866, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 866, 902, (byte) 33 ); // Fill 36 of value (byte) 33
+        CHARS[902] = -19;
+        CHARS[903] = -87;
+        Arrays.fill(CHARS, 904, 907, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[907] = 33;
+        CHARS[908] = -19;
+        CHARS[909] = 33;
+        Arrays.fill(CHARS, 910, 930, (byte) -19 ); // Fill 20 of value (byte) -19
+        CHARS[930] = 33;
+        Arrays.fill(CHARS, 931, 975, (byte) -19 ); // Fill 44 of value (byte) -19
+        CHARS[975] = 33;
+        Arrays.fill(CHARS, 976, 983, (byte) -19 ); // Fill 7 of value (byte) -19
+        Arrays.fill(CHARS, 983, 986, (byte) 33 ); // Fill 3 of value (byte) 33
+        CHARS[986] = -19;
+        CHARS[987] = 33;
+        CHARS[988] = -19;
+        CHARS[989] = 33;
+        CHARS[990] = -19;
+        CHARS[991] = 33;
+        CHARS[992] = -19;
+        CHARS[993] = 33;
+        Arrays.fill(CHARS, 994, 1012, (byte) -19 ); // Fill 18 of value (byte) -19
+        Arrays.fill(CHARS, 1012, 1025, (byte) 33 ); // Fill 13 of value (byte) 33
+        Arrays.fill(CHARS, 1025, 1037, (byte) -19 ); // Fill 12 of value (byte) -19
+        CHARS[1037] = 33;
+        Arrays.fill(CHARS, 1038, 1104, (byte) -19 ); // Fill 66 of value (byte) -19
+        CHARS[1104] = 33;
+        Arrays.fill(CHARS, 1105, 1117, (byte) -19 ); // Fill 12 of value (byte) -19
+        CHARS[1117] = 33;
+        Arrays.fill(CHARS, 1118, 1154, (byte) -19 ); // Fill 36 of value (byte) -19
+        CHARS[1154] = 33;
+        Arrays.fill(CHARS, 1155, 1159, (byte) -87 ); // Fill 4 of value (byte) -87
+        Arrays.fill(CHARS, 1159, 1168, (byte) 33 ); // Fill 9 of value (byte) 33
+        Arrays.fill(CHARS, 1168, 1221, (byte) -19 ); // Fill 53 of value (byte) -19
+        Arrays.fill(CHARS, 1221, 1223, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 1223, 1225, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 1225, 1227, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 1227, 1229, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 1229, 1232, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 1232, 1260, (byte) -19 ); // Fill 28 of value (byte) -19
+        Arrays.fill(CHARS, 1260, 1262, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 1262, 1270, (byte) -19 ); // Fill 8 of value (byte) -19
+        Arrays.fill(CHARS, 1270, 1272, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 1272, 1274, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 1274, 1329, (byte) 33 ); // Fill 55 of value (byte) 33
+        Arrays.fill(CHARS, 1329, 1367, (byte) -19 ); // Fill 38 of value (byte) -19
+        Arrays.fill(CHARS, 1367, 1369, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[1369] = -19;
+        Arrays.fill(CHARS, 1370, 1377, (byte) 33 ); // Fill 7 of value (byte) 33
+        Arrays.fill(CHARS, 1377, 1415, (byte) -19 ); // Fill 38 of value (byte) -19
+        Arrays.fill(CHARS, 1415, 1425, (byte) 33 ); // Fill 10 of value (byte) 33
+        Arrays.fill(CHARS, 1425, 1442, (byte) -87 ); // Fill 17 of value (byte) -87
+        CHARS[1442] = 33;
+        Arrays.fill(CHARS, 1443, 1466, (byte) -87 ); // Fill 23 of value (byte) -87
+        CHARS[1466] = 33;
+        Arrays.fill(CHARS, 1467, 1470, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[1470] = 33;
+        CHARS[1471] = -87;
+        CHARS[1472] = 33;
+        Arrays.fill(CHARS, 1473, 1475, (byte) -87 ); // Fill 2 of value (byte) -87
+        CHARS[1475] = 33;
+        CHARS[1476] = -87;
+        Arrays.fill(CHARS, 1477, 1488, (byte) 33 ); // Fill 11 of value (byte) 33
+        Arrays.fill(CHARS, 1488, 1515, (byte) -19 ); // Fill 27 of value (byte) -19
+        Arrays.fill(CHARS, 1515, 1520, (byte) 33 ); // Fill 5 of value (byte) 33
+        Arrays.fill(CHARS, 1520, 1523, (byte) -19 ); // Fill 3 of value (byte) -19
+        Arrays.fill(CHARS, 1523, 1569, (byte) 33 ); // Fill 46 of value (byte) 33
+        Arrays.fill(CHARS, 1569, 1595, (byte) -19 ); // Fill 26 of value (byte) -19
+        Arrays.fill(CHARS, 1595, 1600, (byte) 33 ); // Fill 5 of value (byte) 33
+        CHARS[1600] = -87;
+        Arrays.fill(CHARS, 1601, 1611, (byte) -19 ); // Fill 10 of value (byte) -19
+        Arrays.fill(CHARS, 1611, 1619, (byte) -87 ); // Fill 8 of value (byte) -87
+        Arrays.fill(CHARS, 1619, 1632, (byte) 33 ); // Fill 13 of value (byte) 33
+        Arrays.fill(CHARS, 1632, 1642, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 1642, 1648, (byte) 33 ); // Fill 6 of value (byte) 33
+        CHARS[1648] = -87;
+        Arrays.fill(CHARS, 1649, 1720, (byte) -19 ); // Fill 71 of value (byte) -19
+        Arrays.fill(CHARS, 1720, 1722, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 1722, 1727, (byte) -19 ); // Fill 5 of value (byte) -19
+        CHARS[1727] = 33;
+        Arrays.fill(CHARS, 1728, 1743, (byte) -19 ); // Fill 15 of value (byte) -19
+        CHARS[1743] = 33;
+        Arrays.fill(CHARS, 1744, 1748, (byte) -19 ); // Fill 4 of value (byte) -19
+        CHARS[1748] = 33;
+        CHARS[1749] = -19;
+        Arrays.fill(CHARS, 1750, 1765, (byte) -87 ); // Fill 15 of value (byte) -87
+        Arrays.fill(CHARS, 1765, 1767, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 1767, 1769, (byte) -87 ); // Fill 2 of value (byte) -87
+        CHARS[1769] = 33;
+        Arrays.fill(CHARS, 1770, 1774, (byte) -87 ); // Fill 4 of value (byte) -87
+        Arrays.fill(CHARS, 1774, 1776, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 1776, 1786, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 1786, 2305, (byte) 33 ); // Fill 519 of value (byte) 33
+        Arrays.fill(CHARS, 2305, 2308, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[2308] = 33;
+        Arrays.fill(CHARS, 2309, 2362, (byte) -19 ); // Fill 53 of value (byte) -19
+        Arrays.fill(CHARS, 2362, 2364, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[2364] = -87;
+        CHARS[2365] = -19;
+        Arrays.fill(CHARS, 2366, 2382, (byte) -87 ); // Fill 16 of value (byte) -87
+        Arrays.fill(CHARS, 2382, 2385, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 2385, 2389, (byte) -87 ); // Fill 4 of value (byte) -87
+        Arrays.fill(CHARS, 2389, 2392, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 2392, 2402, (byte) -19 ); // Fill 10 of value (byte) -19
+        Arrays.fill(CHARS, 2402, 2404, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 2404, 2406, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2406, 2416, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 2416, 2433, (byte) 33 ); // Fill 17 of value (byte) 33
+        Arrays.fill(CHARS, 2433, 2436, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[2436] = 33;
+        Arrays.fill(CHARS, 2437, 2445, (byte) -19 ); // Fill 8 of value (byte) -19
+        Arrays.fill(CHARS, 2445, 2447, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2447, 2449, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 2449, 2451, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2451, 2473, (byte) -19 ); // Fill 22 of value (byte) -19
+        CHARS[2473] = 33;
+        Arrays.fill(CHARS, 2474, 2481, (byte) -19 ); // Fill 7 of value (byte) -19
+        CHARS[2481] = 33;
+        CHARS[2482] = -19;
+        Arrays.fill(CHARS, 2483, 2486, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 2486, 2490, (byte) -19 ); // Fill 4 of value (byte) -19
+        Arrays.fill(CHARS, 2490, 2492, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[2492] = -87;
+        CHARS[2493] = 33;
+        Arrays.fill(CHARS, 2494, 2501, (byte) -87 ); // Fill 7 of value (byte) -87
+        Arrays.fill(CHARS, 2501, 2503, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2503, 2505, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 2505, 2507, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2507, 2510, (byte) -87 ); // Fill 3 of value (byte) -87
+        Arrays.fill(CHARS, 2510, 2519, (byte) 33 ); // Fill 9 of value (byte) 33
+        CHARS[2519] = -87;
+        Arrays.fill(CHARS, 2520, 2524, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 2524, 2526, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[2526] = 33;
+        Arrays.fill(CHARS, 2527, 2530, (byte) -19 ); // Fill 3 of value (byte) -19
+        Arrays.fill(CHARS, 2530, 2532, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 2532, 2534, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2534, 2544, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 2544, 2546, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 2546, 2562, (byte) 33 ); // Fill 16 of value (byte) 33
+        CHARS[2562] = -87;
+        Arrays.fill(CHARS, 2563, 2565, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2565, 2571, (byte) -19 ); // Fill 6 of value (byte) -19
+        Arrays.fill(CHARS, 2571, 2575, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 2575, 2577, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 2577, 2579, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2579, 2601, (byte) -19 ); // Fill 22 of value (byte) -19
+        CHARS[2601] = 33;
+        Arrays.fill(CHARS, 2602, 2609, (byte) -19 ); // Fill 7 of value (byte) -19
+        CHARS[2609] = 33;
+        Arrays.fill(CHARS, 2610, 2612, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[2612] = 33;
+        Arrays.fill(CHARS, 2613, 2615, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[2615] = 33;
+        Arrays.fill(CHARS, 2616, 2618, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 2618, 2620, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[2620] = -87;
+        CHARS[2621] = 33;
+        Arrays.fill(CHARS, 2622, 2627, (byte) -87 ); // Fill 5 of value (byte) -87
+        Arrays.fill(CHARS, 2627, 2631, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 2631, 2633, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 2633, 2635, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2635, 2638, (byte) -87 ); // Fill 3 of value (byte) -87
+        Arrays.fill(CHARS, 2638, 2649, (byte) 33 ); // Fill 11 of value (byte) 33
+        Arrays.fill(CHARS, 2649, 2653, (byte) -19 ); // Fill 4 of value (byte) -19
+        CHARS[2653] = 33;
+        CHARS[2654] = -19;
+        Arrays.fill(CHARS, 2655, 2662, (byte) 33 ); // Fill 7 of value (byte) 33
+        Arrays.fill(CHARS, 2662, 2674, (byte) -87 ); // Fill 12 of value (byte) -87
+        Arrays.fill(CHARS, 2674, 2677, (byte) -19 ); // Fill 3 of value (byte) -19
+        Arrays.fill(CHARS, 2677, 2689, (byte) 33 ); // Fill 12 of value (byte) 33
+        Arrays.fill(CHARS, 2689, 2692, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[2692] = 33;
+        Arrays.fill(CHARS, 2693, 2700, (byte) -19 ); // Fill 7 of value (byte) -19
+        CHARS[2700] = 33;
+        CHARS[2701] = -19;
+        CHARS[2702] = 33;
+        Arrays.fill(CHARS, 2703, 2706, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[2706] = 33;
+        Arrays.fill(CHARS, 2707, 2729, (byte) -19 ); // Fill 22 of value (byte) -19
+        CHARS[2729] = 33;
+        Arrays.fill(CHARS, 2730, 2737, (byte) -19 ); // Fill 7 of value (byte) -19
+        CHARS[2737] = 33;
+        Arrays.fill(CHARS, 2738, 2740, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[2740] = 33;
+        Arrays.fill(CHARS, 2741, 2746, (byte) -19 ); // Fill 5 of value (byte) -19
+        Arrays.fill(CHARS, 2746, 2748, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[2748] = -87;
+        CHARS[2749] = -19;
+        Arrays.fill(CHARS, 2750, 2758, (byte) -87 ); // Fill 8 of value (byte) -87
+        CHARS[2758] = 33;
+        Arrays.fill(CHARS, 2759, 2762, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[2762] = 33;
+        Arrays.fill(CHARS, 2763, 2766, (byte) -87 ); // Fill 3 of value (byte) -87
+        Arrays.fill(CHARS, 2766, 2784, (byte) 33 ); // Fill 18 of value (byte) 33
+        CHARS[2784] = -19;
+        Arrays.fill(CHARS, 2785, 2790, (byte) 33 ); // Fill 5 of value (byte) 33
+        Arrays.fill(CHARS, 2790, 2800, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 2800, 2817, (byte) 33 ); // Fill 17 of value (byte) 33
+        Arrays.fill(CHARS, 2817, 2820, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[2820] = 33;
+        Arrays.fill(CHARS, 2821, 2829, (byte) -19 ); // Fill 8 of value (byte) -19
+        Arrays.fill(CHARS, 2829, 2831, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2831, 2833, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 2833, 2835, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2835, 2857, (byte) -19 ); // Fill 22 of value (byte) -19
+        CHARS[2857] = 33;
+        Arrays.fill(CHARS, 2858, 2865, (byte) -19 ); // Fill 7 of value (byte) -19
+        CHARS[2865] = 33;
+        Arrays.fill(CHARS, 2866, 2868, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 2868, 2870, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2870, 2874, (byte) -19 ); // Fill 4 of value (byte) -19
+        Arrays.fill(CHARS, 2874, 2876, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[2876] = -87;
+        CHARS[2877] = -19;
+        Arrays.fill(CHARS, 2878, 2884, (byte) -87 ); // Fill 6 of value (byte) -87
+        Arrays.fill(CHARS, 2884, 2887, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 2887, 2889, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 2889, 2891, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 2891, 2894, (byte) -87 ); // Fill 3 of value (byte) -87
+        Arrays.fill(CHARS, 2894, 2902, (byte) 33 ); // Fill 8 of value (byte) 33
+        Arrays.fill(CHARS, 2902, 2904, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 2904, 2908, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 2908, 2910, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[2910] = 33;
+        Arrays.fill(CHARS, 2911, 2914, (byte) -19 ); // Fill 3 of value (byte) -19
+        Arrays.fill(CHARS, 2914, 2918, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 2918, 2928, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 2928, 2946, (byte) 33 ); // Fill 18 of value (byte) 33
+        Arrays.fill(CHARS, 2946, 2948, (byte) -87 ); // Fill 2 of value (byte) -87
+        CHARS[2948] = 33;
+        Arrays.fill(CHARS, 2949, 2955, (byte) -19 ); // Fill 6 of value (byte) -19
+        Arrays.fill(CHARS, 2955, 2958, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 2958, 2961, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[2961] = 33;
+        Arrays.fill(CHARS, 2962, 2966, (byte) -19 ); // Fill 4 of value (byte) -19
+        Arrays.fill(CHARS, 2966, 2969, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 2969, 2971, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[2971] = 33;
+        CHARS[2972] = -19;
+        CHARS[2973] = 33;
+        Arrays.fill(CHARS, 2974, 2976, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 2976, 2979, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 2979, 2981, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 2981, 2984, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 2984, 2987, (byte) -19 ); // Fill 3 of value (byte) -19
+        Arrays.fill(CHARS, 2987, 2990, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 2990, 2998, (byte) -19 ); // Fill 8 of value (byte) -19
+        CHARS[2998] = 33;
+        Arrays.fill(CHARS, 2999, 3002, (byte) -19 ); // Fill 3 of value (byte) -19
+        Arrays.fill(CHARS, 3002, 3006, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 3006, 3011, (byte) -87 ); // Fill 5 of value (byte) -87
+        Arrays.fill(CHARS, 3011, 3014, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 3014, 3017, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[3017] = 33;
+        Arrays.fill(CHARS, 3018, 3022, (byte) -87 ); // Fill 4 of value (byte) -87
+        Arrays.fill(CHARS, 3022, 3031, (byte) 33 ); // Fill 9 of value (byte) 33
+        CHARS[3031] = -87;
+        Arrays.fill(CHARS, 3032, 3047, (byte) 33 ); // Fill 15 of value (byte) 33
+        Arrays.fill(CHARS, 3047, 3056, (byte) -87 ); // Fill 9 of value (byte) -87
+        Arrays.fill(CHARS, 3056, 3073, (byte) 33 ); // Fill 17 of value (byte) 33
+        Arrays.fill(CHARS, 3073, 3076, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[3076] = 33;
+        Arrays.fill(CHARS, 3077, 3085, (byte) -19 ); // Fill 8 of value (byte) -19
+        CHARS[3085] = 33;
+        Arrays.fill(CHARS, 3086, 3089, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[3089] = 33;
+        Arrays.fill(CHARS, 3090, 3113, (byte) -19 ); // Fill 23 of value (byte) -19
+        CHARS[3113] = 33;
+        Arrays.fill(CHARS, 3114, 3124, (byte) -19 ); // Fill 10 of value (byte) -19
+        CHARS[3124] = 33;
+        Arrays.fill(CHARS, 3125, 3130, (byte) -19 ); // Fill 5 of value (byte) -19
+        Arrays.fill(CHARS, 3130, 3134, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 3134, 3141, (byte) -87 ); // Fill 7 of value (byte) -87
+        CHARS[3141] = 33;
+        Arrays.fill(CHARS, 3142, 3145, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[3145] = 33;
+        Arrays.fill(CHARS, 3146, 3150, (byte) -87 ); // Fill 4 of value (byte) -87
+        Arrays.fill(CHARS, 3150, 3157, (byte) 33 ); // Fill 7 of value (byte) 33
+        Arrays.fill(CHARS, 3157, 3159, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 3159, 3168, (byte) 33 ); // Fill 9 of value (byte) 33
+        Arrays.fill(CHARS, 3168, 3170, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 3170, 3174, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 3174, 3184, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 3184, 3202, (byte) 33 ); // Fill 18 of value (byte) 33
+        Arrays.fill(CHARS, 3202, 3204, (byte) -87 ); // Fill 2 of value (byte) -87
+        CHARS[3204] = 33;
+        Arrays.fill(CHARS, 3205, 3213, (byte) -19 ); // Fill 8 of value (byte) -19
+        CHARS[3213] = 33;
+        Arrays.fill(CHARS, 3214, 3217, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[3217] = 33;
+        Arrays.fill(CHARS, 3218, 3241, (byte) -19 ); // Fill 23 of value (byte) -19
+        CHARS[3241] = 33;
+        Arrays.fill(CHARS, 3242, 3252, (byte) -19 ); // Fill 10 of value (byte) -19
+        CHARS[3252] = 33;
+        Arrays.fill(CHARS, 3253, 3258, (byte) -19 ); // Fill 5 of value (byte) -19
+        Arrays.fill(CHARS, 3258, 3262, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 3262, 3269, (byte) -87 ); // Fill 7 of value (byte) -87
+        CHARS[3269] = 33;
+        Arrays.fill(CHARS, 3270, 3273, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[3273] = 33;
+        Arrays.fill(CHARS, 3274, 3278, (byte) -87 ); // Fill 4 of value (byte) -87
+        Arrays.fill(CHARS, 3278, 3285, (byte) 33 ); // Fill 7 of value (byte) 33
+        Arrays.fill(CHARS, 3285, 3287, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 3287, 3294, (byte) 33 ); // Fill 7 of value (byte) 33
+        CHARS[3294] = -19;
+        CHARS[3295] = 33;
+        Arrays.fill(CHARS, 3296, 3298, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 3298, 3302, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 3302, 3312, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 3312, 3330, (byte) 33 ); // Fill 18 of value (byte) 33
+        Arrays.fill(CHARS, 3330, 3332, (byte) -87 ); // Fill 2 of value (byte) -87
+        CHARS[3332] = 33;
+        Arrays.fill(CHARS, 3333, 3341, (byte) -19 ); // Fill 8 of value (byte) -19
+        CHARS[3341] = 33;
+        Arrays.fill(CHARS, 3342, 3345, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[3345] = 33;
+        Arrays.fill(CHARS, 3346, 3369, (byte) -19 ); // Fill 23 of value (byte) -19
+        CHARS[3369] = 33;
+        Arrays.fill(CHARS, 3370, 3386, (byte) -19 ); // Fill 16 of value (byte) -19
+        Arrays.fill(CHARS, 3386, 3390, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 3390, 3396, (byte) -87 ); // Fill 6 of value (byte) -87
+        Arrays.fill(CHARS, 3396, 3398, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 3398, 3401, (byte) -87 ); // Fill 3 of value (byte) -87
+        CHARS[3401] = 33;
+        Arrays.fill(CHARS, 3402, 3406, (byte) -87 ); // Fill 4 of value (byte) -87
+        Arrays.fill(CHARS, 3406, 3415, (byte) 33 ); // Fill 9 of value (byte) 33
+        CHARS[3415] = -87;
+        Arrays.fill(CHARS, 3416, 3424, (byte) 33 ); // Fill 8 of value (byte) 33
+        Arrays.fill(CHARS, 3424, 3426, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 3426, 3430, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 3430, 3440, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 3440, 3585, (byte) 33 ); // Fill 145 of value (byte) 33
+        Arrays.fill(CHARS, 3585, 3631, (byte) -19 ); // Fill 46 of value (byte) -19
+        CHARS[3631] = 33;
+        CHARS[3632] = -19;
+        CHARS[3633] = -87;
+        Arrays.fill(CHARS, 3634, 3636, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 3636, 3643, (byte) -87 ); // Fill 7 of value (byte) -87
+        Arrays.fill(CHARS, 3643, 3648, (byte) 33 ); // Fill 5 of value (byte) 33
+        Arrays.fill(CHARS, 3648, 3654, (byte) -19 ); // Fill 6 of value (byte) -19
+        Arrays.fill(CHARS, 3654, 3663, (byte) -87 ); // Fill 9 of value (byte) -87
+        CHARS[3663] = 33;
+        Arrays.fill(CHARS, 3664, 3674, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 3674, 3713, (byte) 33 ); // Fill 39 of value (byte) 33
+        Arrays.fill(CHARS, 3713, 3715, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[3715] = 33;
+        CHARS[3716] = -19;
+        Arrays.fill(CHARS, 3717, 3719, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 3719, 3721, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[3721] = 33;
+        CHARS[3722] = -19;
+        Arrays.fill(CHARS, 3723, 3725, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[3725] = -19;
+        Arrays.fill(CHARS, 3726, 3732, (byte) 33 ); // Fill 6 of value (byte) 33
+        Arrays.fill(CHARS, 3732, 3736, (byte) -19 ); // Fill 4 of value (byte) -19
+        CHARS[3736] = 33;
+        Arrays.fill(CHARS, 3737, 3744, (byte) -19 ); // Fill 7 of value (byte) -19
+        CHARS[3744] = 33;
+        Arrays.fill(CHARS, 3745, 3748, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[3748] = 33;
+        CHARS[3749] = -19;
+        CHARS[3750] = 33;
+        CHARS[3751] = -19;
+        Arrays.fill(CHARS, 3752, 3754, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 3754, 3756, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[3756] = 33;
+        Arrays.fill(CHARS, 3757, 3759, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[3759] = 33;
+        CHARS[3760] = -19;
+        CHARS[3761] = -87;
+        Arrays.fill(CHARS, 3762, 3764, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 3764, 3770, (byte) -87 ); // Fill 6 of value (byte) -87
+        CHARS[3770] = 33;
+        Arrays.fill(CHARS, 3771, 3773, (byte) -87 ); // Fill 2 of value (byte) -87
+        CHARS[3773] = -19;
+        Arrays.fill(CHARS, 3774, 3776, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 3776, 3781, (byte) -19 ); // Fill 5 of value (byte) -19
+        CHARS[3781] = 33;
+        CHARS[3782] = -87;
+        CHARS[3783] = 33;
+        Arrays.fill(CHARS, 3784, 3790, (byte) -87 ); // Fill 6 of value (byte) -87
+        Arrays.fill(CHARS, 3790, 3792, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 3792, 3802, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 3802, 3864, (byte) 33 ); // Fill 62 of value (byte) 33
+        Arrays.fill(CHARS, 3864, 3866, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 3866, 3872, (byte) 33 ); // Fill 6 of value (byte) 33
+        Arrays.fill(CHARS, 3872, 3882, (byte) -87 ); // Fill 10 of value (byte) -87
+        Arrays.fill(CHARS, 3882, 3893, (byte) 33 ); // Fill 11 of value (byte) 33
+        CHARS[3893] = -87;
+        CHARS[3894] = 33;
+        CHARS[3895] = -87;
+        CHARS[3896] = 33;
+        CHARS[3897] = -87;
+        Arrays.fill(CHARS, 3898, 3902, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 3902, 3904, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 3904, 3912, (byte) -19 ); // Fill 8 of value (byte) -19
+        CHARS[3912] = 33;
+        Arrays.fill(CHARS, 3913, 3946, (byte) -19 ); // Fill 33 of value (byte) -19
+        Arrays.fill(CHARS, 3946, 3953, (byte) 33 ); // Fill 7 of value (byte) 33
+        Arrays.fill(CHARS, 3953, 3973, (byte) -87 ); // Fill 20 of value (byte) -87
+        CHARS[3973] = 33;
+        Arrays.fill(CHARS, 3974, 3980, (byte) -87 ); // Fill 6 of value (byte) -87
+        Arrays.fill(CHARS, 3980, 3984, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 3984, 3990, (byte) -87 ); // Fill 6 of value (byte) -87
+        CHARS[3990] = 33;
+        CHARS[3991] = -87;
+        CHARS[3992] = 33;
+        Arrays.fill(CHARS, 3993, 4014, (byte) -87 ); // Fill 21 of value (byte) -87
+        Arrays.fill(CHARS, 4014, 4017, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 4017, 4024, (byte) -87 ); // Fill 7 of value (byte) -87
+        CHARS[4024] = 33;
+        CHARS[4025] = -87;
+        Arrays.fill(CHARS, 4026, 4256, (byte) 33 ); // Fill 230 of value (byte) 33
+        Arrays.fill(CHARS, 4256, 4294, (byte) -19 ); // Fill 38 of value (byte) -19
+        Arrays.fill(CHARS, 4294, 4304, (byte) 33 ); // Fill 10 of value (byte) 33
+        Arrays.fill(CHARS, 4304, 4343, (byte) -19 ); // Fill 39 of value (byte) -19
+        Arrays.fill(CHARS, 4343, 4352, (byte) 33 ); // Fill 9 of value (byte) 33
+        CHARS[4352] = -19;
+        CHARS[4353] = 33;
+        Arrays.fill(CHARS, 4354, 4356, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[4356] = 33;
+        Arrays.fill(CHARS, 4357, 4360, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[4360] = 33;
+        CHARS[4361] = -19;
+        CHARS[4362] = 33;
+        Arrays.fill(CHARS, 4363, 4365, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[4365] = 33;
+        Arrays.fill(CHARS, 4366, 4371, (byte) -19 ); // Fill 5 of value (byte) -19
+        Arrays.fill(CHARS, 4371, 4412, (byte) 33 ); // Fill 41 of value (byte) 33
+        CHARS[4412] = -19;
+        CHARS[4413] = 33;
+        CHARS[4414] = -19;
+        CHARS[4415] = 33;
+        CHARS[4416] = -19;
+        Arrays.fill(CHARS, 4417, 4428, (byte) 33 ); // Fill 11 of value (byte) 33
+        CHARS[4428] = -19;
+        CHARS[4429] = 33;
+        CHARS[4430] = -19;
+        CHARS[4431] = 33;
+        CHARS[4432] = -19;
+        Arrays.fill(CHARS, 4433, 4436, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 4436, 4438, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 4438, 4441, (byte) 33 ); // Fill 3 of value (byte) 33
+        CHARS[4441] = -19;
+        Arrays.fill(CHARS, 4442, 4447, (byte) 33 ); // Fill 5 of value (byte) 33
+        Arrays.fill(CHARS, 4447, 4450, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[4450] = 33;
+        CHARS[4451] = -19;
+        CHARS[4452] = 33;
+        CHARS[4453] = -19;
+        CHARS[4454] = 33;
+        CHARS[4455] = -19;
+        CHARS[4456] = 33;
+        CHARS[4457] = -19;
+        Arrays.fill(CHARS, 4458, 4461, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 4461, 4463, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 4463, 4466, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 4466, 4468, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[4468] = 33;
+        CHARS[4469] = -19;
+        Arrays.fill(CHARS, 4470, 4510, (byte) 33 ); // Fill 40 of value (byte) 33
+        CHARS[4510] = -19;
+        Arrays.fill(CHARS, 4511, 4520, (byte) 33 ); // Fill 9 of value (byte) 33
+        CHARS[4520] = -19;
+        Arrays.fill(CHARS, 4521, 4523, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[4523] = -19;
+        Arrays.fill(CHARS, 4524, 4526, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 4526, 4528, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 4528, 4535, (byte) 33 ); // Fill 7 of value (byte) 33
+        Arrays.fill(CHARS, 4535, 4537, (byte) -19 ); // Fill 2 of value (byte) -19
+        CHARS[4537] = 33;
+        CHARS[4538] = -19;
+        CHARS[4539] = 33;
+        Arrays.fill(CHARS, 4540, 4547, (byte) -19 ); // Fill 7 of value (byte) -19
+        Arrays.fill(CHARS, 4547, 4587, (byte) 33 ); // Fill 40 of value (byte) 33
+        CHARS[4587] = -19;
+        Arrays.fill(CHARS, 4588, 4592, (byte) 33 ); // Fill 4 of value (byte) 33
+        CHARS[4592] = -19;
+        Arrays.fill(CHARS, 4593, 4601, (byte) 33 ); // Fill 8 of value (byte) 33
+        CHARS[4601] = -19;
+        Arrays.fill(CHARS, 4602, 7680, (byte) 33 ); // Fill 3078 of value (byte) 33
+        Arrays.fill(CHARS, 7680, 7836, (byte) -19 ); // Fill 156 of value (byte) -19
+        Arrays.fill(CHARS, 7836, 7840, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 7840, 7930, (byte) -19 ); // Fill 90 of value (byte) -19
+        Arrays.fill(CHARS, 7930, 7936, (byte) 33 ); // Fill 6 of value (byte) 33
+        Arrays.fill(CHARS, 7936, 7958, (byte) -19 ); // Fill 22 of value (byte) -19
+        Arrays.fill(CHARS, 7958, 7960, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 7960, 7966, (byte) -19 ); // Fill 6 of value (byte) -19
+        Arrays.fill(CHARS, 7966, 7968, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 7968, 8006, (byte) -19 ); // Fill 38 of value (byte) -19
+        Arrays.fill(CHARS, 8006, 8008, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 8008, 8014, (byte) -19 ); // Fill 6 of value (byte) -19
+        Arrays.fill(CHARS, 8014, 8016, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 8016, 8024, (byte) -19 ); // Fill 8 of value (byte) -19
+        CHARS[8024] = 33;
+        CHARS[8025] = -19;
+        CHARS[8026] = 33;
+        CHARS[8027] = -19;
+        CHARS[8028] = 33;
+        CHARS[8029] = -19;
+        CHARS[8030] = 33;
+        Arrays.fill(CHARS, 8031, 8062, (byte) -19 ); // Fill 31 of value (byte) -19
+        Arrays.fill(CHARS, 8062, 8064, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 8064, 8117, (byte) -19 ); // Fill 53 of value (byte) -19
+        CHARS[8117] = 33;
+        Arrays.fill(CHARS, 8118, 8125, (byte) -19 ); // Fill 7 of value (byte) -19
+        CHARS[8125] = 33;
+        CHARS[8126] = -19;
+        Arrays.fill(CHARS, 8127, 8130, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 8130, 8133, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[8133] = 33;
+        Arrays.fill(CHARS, 8134, 8141, (byte) -19 ); // Fill 7 of value (byte) -19
+        Arrays.fill(CHARS, 8141, 8144, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 8144, 8148, (byte) -19 ); // Fill 4 of value (byte) -19
+        Arrays.fill(CHARS, 8148, 8150, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 8150, 8156, (byte) -19 ); // Fill 6 of value (byte) -19
+        Arrays.fill(CHARS, 8156, 8160, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 8160, 8173, (byte) -19 ); // Fill 13 of value (byte) -19
+        Arrays.fill(CHARS, 8173, 8178, (byte) 33 ); // Fill 5 of value (byte) 33
+        Arrays.fill(CHARS, 8178, 8181, (byte) -19 ); // Fill 3 of value (byte) -19
+        CHARS[8181] = 33;
+        Arrays.fill(CHARS, 8182, 8189, (byte) -19 ); // Fill 7 of value (byte) -19
+        Arrays.fill(CHARS, 8189, 8400, (byte) 33 ); // Fill 211 of value (byte) 33
+        Arrays.fill(CHARS, 8400, 8413, (byte) -87 ); // Fill 13 of value (byte) -87
+        Arrays.fill(CHARS, 8413, 8417, (byte) 33 ); // Fill 4 of value (byte) 33
+        CHARS[8417] = -87;
+        Arrays.fill(CHARS, 8418, 8486, (byte) 33 ); // Fill 68 of value (byte) 33
+        CHARS[8486] = -19;
+        Arrays.fill(CHARS, 8487, 8490, (byte) 33 ); // Fill 3 of value (byte) 33
+        Arrays.fill(CHARS, 8490, 8492, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(CHARS, 8492, 8494, (byte) 33 ); // Fill 2 of value (byte) 33
+        CHARS[8494] = -19;
+        Arrays.fill(CHARS, 8495, 8576, (byte) 33 ); // Fill 81 of value (byte) 33
+        Arrays.fill(CHARS, 8576, 8579, (byte) -19 ); // Fill 3 of value (byte) -19
+        Arrays.fill(CHARS, 8579, 12293, (byte) 33 ); // Fill 3714 of value (byte) 33
+        CHARS[12293] = -87;
+        CHARS[12294] = 33;
+        CHARS[12295] = -19;
+        Arrays.fill(CHARS, 12296, 12321, (byte) 33 ); // Fill 25 of value (byte) 33
+        Arrays.fill(CHARS, 12321, 12330, (byte) -19 ); // Fill 9 of value (byte) -19
+        Arrays.fill(CHARS, 12330, 12336, (byte) -87 ); // Fill 6 of value (byte) -87
+        CHARS[12336] = 33;
+        Arrays.fill(CHARS, 12337, 12342, (byte) -87 ); // Fill 5 of value (byte) -87
+        Arrays.fill(CHARS, 12342, 12353, (byte) 33 ); // Fill 11 of value (byte) 33
+        Arrays.fill(CHARS, 12353, 12437, (byte) -19 ); // Fill 84 of value (byte) -19
+        Arrays.fill(CHARS, 12437, 12441, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(CHARS, 12441, 12443, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 12443, 12445, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 12445, 12447, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(CHARS, 12447, 12449, (byte) 33 ); // Fill 2 of value (byte) 33
+        Arrays.fill(CHARS, 12449, 12539, (byte) -19 ); // Fill 90 of value (byte) -19
+        CHARS[12539] = 33;
+        Arrays.fill(CHARS, 12540, 12543, (byte) -87 ); // Fill 3 of value (byte) -87
+        Arrays.fill(CHARS, 12543, 12549, (byte) 33 ); // Fill 6 of value (byte) 33
+        Arrays.fill(CHARS, 12549, 12589, (byte) -19 ); // Fill 40 of value (byte) -19
+        Arrays.fill(CHARS, 12589, 19968, (byte) 33 ); // Fill 7379 of value (byte) 33
+        Arrays.fill(CHARS, 19968, 40870, (byte) -19 ); // Fill 20902 of value (byte) -19
+        Arrays.fill(CHARS, 40870, 44032, (byte) 33 ); // Fill 3162 of value (byte) 33
+        Arrays.fill(CHARS, 44032, 55204, (byte) -19 ); // Fill 11172 of value (byte) -19
+        Arrays.fill(CHARS, 55204, 55296, (byte) 33 ); // Fill 92 of value (byte) 33
+        Arrays.fill(CHARS, 57344, 65534, (byte) 33 ); // Fill 8190 of value (byte) 33
+
+    } // <clinit>()
+
+    //
+    // Public static methods
+    //
+
+    /**
+     * Returns true if the specified character is a supplemental character.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isSupplemental(int c) {
+        return (c >= 0x10000 && c <= 0x10FFFF);
+    }
+
+    /**
+     * Returns true the supplemental character corresponding to the given
+     * surrogates.
+     *
+     * @param h The high surrogate.
+     * @param l The low surrogate.
+     */
+    public static int supplemental(char h, char l) {
+        return (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000;
+    }
+
+    /**
+     * Returns the high surrogate of a supplemental character
+     *
+     * @param c The supplemental character to "split".
+     */
+    public static char highSurrogate(int c) {
+        return (char) (((c - 0x00010000) >> 10) + 0xD800);
+    }
+
+    /**
+     * Returns the low surrogate of a supplemental character
+     *
+     * @param c The supplemental character to "split".
+     */
+    public static char lowSurrogate(int c) {
+        return (char) (((c - 0x00010000) & 0x3FF) + 0xDC00);
+    }
+
+    /**
+     * Returns whether the given character is a high surrogate
+     *
+     * @param c The character to check.
+     */
+    public static boolean isHighSurrogate(int c) {
+        return (0xD800 <= c && c <= 0xDBFF);
+    }
+
+    /**
+     * Returns whether the given character is a low surrogate
+     *
+     * @param c The character to check.
+     */
+    public static boolean isLowSurrogate(int c) {
+        return (0xDC00 <= c && c <= 0xDFFF);
+    }
+
+
+    /**
+     * Returns true if the specified character is valid. This method
+     * also checks the surrogate character range from 0x10000 to 0x10FFFF.
+     * <p>
+     * If the program chooses to apply the mask directly to the
+     * <code>CHARS</code> array, then they are responsible for checking
+     * the surrogate character range.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isValid(int c) {
+        return (c < 0x10000 && (CHARS[c] & MASK_VALID) != 0) ||
+               (0x10000 <= c && c <= 0x10FFFF);
+    } // isValid(int):boolean
+
+    /**
+     * Returns true if the specified character is invalid.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isInvalid(int c) {
+        return !isValid(c);
+    } // isInvalid(int):boolean
+
+    /**
+     * Returns true if the specified character can be considered content.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isContent(int c) {
+        return (c < 0x10000 && (CHARS[c] & MASK_CONTENT) != 0) ||
+               (0x10000 <= c && c <= 0x10FFFF);
+    } // isContent(int):boolean
+
+    /**
+     * Returns true if the specified character can be considered markup.
+     * Markup characters include '&lt;', '&amp;', and '%'.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isMarkup(int c) {
+        return c == '<' || c == '&' || c == '%';
+    } // isMarkup(int):boolean
+
+    /**
+     * Returns true if the specified character is a space character
+     * as defined by production [3] in the XML 1.0 specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isSpace(int c) {
+        return c <= 0x20 && (CHARS[c] & MASK_SPACE) != 0;
+    } // isSpace(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid name start
+     * character as defined by production [5] in the XML 1.0
+     * specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isNameStart(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_NAME_START) != 0;
+    } // isNameStart(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid name
+     * character as defined by production [4] in the XML 1.0
+     * specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isName(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_NAME) != 0;
+    } // isName(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid NCName start
+     * character as defined by production [4] in Namespaces in XML
+     * recommendation.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isNCNameStart(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_NCNAME_START) != 0;
+    } // isNCNameStart(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid NCName
+     * character as defined by production [5] in Namespaces in XML
+     * recommendation.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isNCName(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_NCNAME) != 0;
+    } // isNCName(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid Pubid
+     * character as defined by production [13] in the XML 1.0
+     * specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isPubid(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_PUBID) != 0;
+    } // isPubid(int):boolean
+
+    /*
+     * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+     */
+    /**
+     * Check to see if a string is a valid Name according to [5]
+     * in the XML 1.0 Recommendation
+     *
+     * @param name string to check
+     * @return true if name is a valid Name
+     */
+    public static boolean isValidName(String name) {
+        if (name.length() == 0)
+            return false;
+        char ch = name.charAt(0);
+        if( isNameStart(ch) == false)
+           return false;
+        for (int i = 1; i < name.length(); i++ ) {
+           ch = name.charAt(i);
+           if( isName( ch ) == false ){
+              return false;
+           }
+        }
+        return true;
+    } // isValidName(String):boolean
+    
+
+    /*
+     * from the namespace rec
+     * [4] NCName ::= (Letter | '_') (NCNameChar)*
+     */
+    /**
+     * Check to see if a string is a valid NCName according to [4]
+     * from the XML Namespaces 1.0 Recommendation
+     *
+     * @param ncName string to check
+     * @return true if name is a valid NCName
+     */
+    public static boolean isValidNCName(String ncName) {
+        if (ncName.length() == 0)
+            return false;
+        char ch = ncName.charAt(0);
+        if( isNCNameStart(ch) == false)
+           return false;
+        for (int i = 1; i < ncName.length(); i++ ) {
+           ch = ncName.charAt(i);
+           if( isNCName( ch ) == false ){
+              return false;
+           }
+        }
+        return true;
+    } // isValidNCName(String):boolean
+
+    /*
+     * [7] Nmtoken ::= (NameChar)+
+     */
+    /**
+     * Check to see if a string is a valid Nmtoken according to [7]
+     * in the XML 1.0 Recommendation
+     *
+     * @param nmtoken string to check
+     * @return true if nmtoken is a valid Nmtoken 
+     */
+    public static boolean isValidNmtoken(String nmtoken) {
+        if (nmtoken.length() == 0)
+            return false;
+        for (int i = 0; i < nmtoken.length(); i++ ) {
+           char ch = nmtoken.charAt(i);
+           if(  ! isName( ch ) ){
+              return false;
+           }
+        }
+        return true;
+    } // isValidName(String):boolean
+
+
+
+
+
+    // encodings
+
+    /**
+     * Returns true if the encoding name is a valid IANA encoding.
+     * This method does not verify that there is a decoder available
+     * for this encoding, only that the characters are valid for an
+     * IANA encoding name.
+     *
+     * @param ianaEncoding The IANA encoding name.
+     */
+    public static boolean isValidIANAEncoding(String ianaEncoding) {
+        if (ianaEncoding != null) {
+            int length = ianaEncoding.length();
+            if (length > 0) {
+                char c = ianaEncoding.charAt(0);
+                if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
+                    for (int i = 1; i < length; i++) {
+                        c = ianaEncoding.charAt(i);
+                        if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') &&
+                            (c < '0' || c > '9') && c != '.' && c != '_' &&
+                            c != '-') {
+                            return false;
+                        }
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
+    } // isValidIANAEncoding(String):boolean
+
+    /**
+     * Returns true if the encoding name is a valid Java encoding.
+     * This method does not verify that there is a decoder available
+     * for this encoding, only that the characters are valid for an
+     * Java encoding name.
+     *
+     * @param javaEncoding The Java encoding name.
+     */
+    public static boolean isValidJavaEncoding(String javaEncoding) {
+        if (javaEncoding != null) {
+            int length = javaEncoding.length();
+            if (length > 0) {
+                for (int i = 1; i < length; i++) {
+                    char c = javaEncoding.charAt(i);
+                    if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') &&
+                        (c < '0' || c > '9') && c != '.' && c != '_' &&
+                        c != '-') {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    } // isValidIANAEncoding(String):boolean
+
+
+} // class XMLChar
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLEncodingDetector.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLEncodingDetector.java
new file mode 100644
index 0000000..a0f050c
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLEncodingDetector.java
@@ -0,0 +1,1624 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.apache.org.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.jasper.xmlparser;
+
+import java.io.EOFException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Locale;
+import java.util.jar.JarFile;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.compiler.ErrorDispatcher;
+import org.apache.jasper.compiler.JspUtil;
+
+public class XMLEncodingDetector {
+    
+    private InputStream stream;
+    private String encoding;
+    private boolean isEncodingSetInProlog;
+    private Boolean isBigEndian;
+    private Reader reader;
+    
+    // org.apache.xerces.impl.XMLEntityManager fields
+    public static final int DEFAULT_BUFFER_SIZE = 2048;
+    public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
+    private boolean fAllowJavaEncodings;
+    private SymbolTable fSymbolTable;
+    private XMLEncodingDetector fCurrentEntity;
+    private int fBufferSize = DEFAULT_BUFFER_SIZE;
+    
+    // org.apache.xerces.impl.XMLEntityManager.ScannedEntity fields
+    private int lineNumber = 1;
+    private int columnNumber = 1;
+    private boolean literal;
+    private char[] ch = new char[DEFAULT_BUFFER_SIZE];
+    private int position;
+    private int count;
+    private boolean mayReadChunks = false;
+    
+    // org.apache.xerces.impl.XMLScanner fields
+    private XMLString fString = new XMLString();    
+    private XMLStringBuffer fStringBuffer = new XMLStringBuffer();
+    private XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
+    private final static String fVersionSymbol = "version";
+    private final static String fEncodingSymbol = "encoding";
+    private final static String fStandaloneSymbol = "standalone";
+    
+    // org.apache.xerces.impl.XMLDocumentFragmentScannerImpl fields
+    private int fMarkupDepth = 0;
+    private String[] fStrings = new String[3];
+
+    private ErrorDispatcher err;
+
+    /**
+     * Constructor
+     */
+    public XMLEncodingDetector() {
+        fSymbolTable = new SymbolTable();
+        fCurrentEntity = this;
+    }
+
+    /**
+     * Autodetects the encoding of the XML document supplied by the given
+     * input stream.
+     *
+     * Encoding autodetection is done according to the XML 1.0 specification,
+     * Appendix F.1: Detection Without External Encoding Information.
+     *
+     * @return Two-element array, where the first element (of type
+     * java.lang.String) contains the name of the (auto)detected encoding, and
+     * the second element (of type java.lang.Boolean) specifies whether the 
+     * encoding was specified using the 'encoding' attribute of an XML prolog
+     * (TRUE) or autodetected (FALSE).
+     */
+    public static Object[] getEncoding(String fname, JarFile jarFile,
+                                       JspCompilationContext ctxt,
+                                       ErrorDispatcher err)
+        throws IOException, JasperException
+    {
+        InputStream inStream = JspUtil.getInputStream(fname, jarFile, ctxt,
+                                                      err);
+        XMLEncodingDetector detector = new XMLEncodingDetector();
+        Object[] ret = detector.getEncoding(inStream, err);
+        inStream.close();
+
+        return ret;
+    }
+
+    private Object[] getEncoding(InputStream in, ErrorDispatcher err)
+        throws IOException, JasperException
+    {
+        this.stream = in;
+        this.err=err;
+        createInitialReader();
+        scanXMLDecl();
+	
+        return new Object[] { this.encoding,
+                              new Boolean(this.isEncodingSetInProlog) };
+    }
+    
+    // stub method
+    void endEntity() {
+    }
+    
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.startEntity()
+    private void createInitialReader() throws IOException, JasperException {
+
+	// wrap this stream in RewindableInputStream
+	stream = new RewindableInputStream(stream);
+
+	// perform auto-detect of encoding if necessary
+	if (encoding == null) {
+	    // read first four bytes and determine encoding
+	    final byte[] b4 = new byte[4];
+	    int count = 0;
+	    for (; count<4; count++ ) {
+		b4[count] = (byte)stream.read();
+	    }
+	    if (count == 4) {
+		Object [] encodingDesc = getEncodingName(b4, count);
+		encoding = (String)(encodingDesc[0]);
+		isBigEndian = (Boolean)(encodingDesc[1]);
+
+		stream.reset();
+		// Special case UTF-8 files with BOM created by Microsoft
+		// tools. It's more efficient to consume the BOM than make
+		// the reader perform extra checks. -Ac
+		if (count > 2 && encoding.equals("UTF-8")) {
+		    int b0 = b4[0] & 0xFF;
+		    int b1 = b4[1] & 0xFF;
+		    int b2 = b4[2] & 0xFF;
+		    if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
+			// ignore first three bytes...
+			stream.skip(3);
+		    }
+		}
+		reader = createReader(stream, encoding, isBigEndian);
+	    } else {
+		reader = createReader(stream, encoding, isBigEndian);
+	    }
+	}
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.createReader
+    /**
+     * Creates a reader capable of reading the given input stream in
+     * the specified encoding.
+     *
+     * @param inputStream  The input stream.
+     * @param encoding     The encoding name that the input stream is
+     *                     encoded using. If the user has specified that
+     *                     Java encoding names are allowed, then the
+     *                     encoding name may be a Java encoding name;
+     *                     otherwise, it is an ianaEncoding name.
+     * @param isBigEndian   For encodings (like uCS-4), whose names cannot
+     *                      specify a byte order, this tells whether the order
+     *                      is bigEndian. null means unknown or not relevant.
+     *
+     * @return Returns a reader.
+     */
+    private Reader createReader(InputStream inputStream, String encoding,
+				Boolean isBigEndian)
+                throws IOException, JasperException {
+
+        // normalize encoding name
+        if (encoding == null) {
+            encoding = "UTF-8";
+        }
+
+        // try to use an optimized reader
+        String ENCODING = encoding.toUpperCase(Locale.ENGLISH);
+        if (ENCODING.equals("UTF-8")) {
+            return new UTF8Reader(inputStream, fBufferSize);
+        }
+        if (ENCODING.equals("US-ASCII")) {
+            return new ASCIIReader(inputStream, fBufferSize);
+        }
+        if (ENCODING.equals("ISO-10646-UCS-4")) {
+            if (isBigEndian != null) {
+                boolean isBE = isBigEndian.booleanValue();
+                if (isBE) {
+                    return new UCSReader(inputStream, UCSReader.UCS4BE);
+                } else {
+                    return new UCSReader(inputStream, UCSReader.UCS4LE);
+                }
+            } else {
+                err.jspError("jsp.error.xml.encodingByteOrderUnsupported",
+			     encoding);
+            }
+        }
+        if (ENCODING.equals("ISO-10646-UCS-2")) {
+            if (isBigEndian != null) { // sould never happen with this encoding...
+                boolean isBE = isBigEndian.booleanValue();
+                if (isBE) {
+                    return new UCSReader(inputStream, UCSReader.UCS2BE);
+                } else {
+                    return new UCSReader(inputStream, UCSReader.UCS2LE);
+                }
+            } else {
+                err.jspError("jsp.error.xml.encodingByteOrderUnsupported",
+			     encoding);
+            }
+        }
+
+        // check for valid name
+        boolean validIANA = XMLChar.isValidIANAEncoding(encoding);
+        boolean validJava = XMLChar.isValidJavaEncoding(encoding);
+        if (!validIANA || (fAllowJavaEncodings && !validJava)) {
+            err.jspError("jsp.error.xml.encodingDeclInvalid", encoding);
+            // NOTE: AndyH suggested that, on failure, we use ISO Latin 1
+            //       because every byte is a valid ISO Latin 1 character.
+            //       It may not translate correctly but if we failed on
+            //       the encoding anyway, then we're expecting the content
+            //       of the document to be bad. This will just prevent an
+            //       invalid UTF-8 sequence to be detected. This is only
+            //       important when continue-after-fatal-error is turned
+            //       on. -Ac
+            encoding = "ISO-8859-1";
+        }
+
+        // try to use a Java reader
+        String javaEncoding = EncodingMap.getIANA2JavaMapping(ENCODING);
+        if (javaEncoding == null) {
+            if (fAllowJavaEncodings) {
+		javaEncoding = encoding;
+            } else {
+                err.jspError("jsp.error.xml.encodingDeclInvalid", encoding);
+                // see comment above.
+                javaEncoding = "ISO8859_1";
+            }
+        }
+        return new InputStreamReader(inputStream, javaEncoding);
+
+    } // createReader(InputStream,String, Boolean): Reader
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.getEncodingName
+    /**
+     * Returns the IANA encoding name that is auto-detected from
+     * the bytes specified, with the endian-ness of that encoding where
+     * appropriate.
+     *
+     * @param b4    The first four bytes of the input.
+     * @param count The number of bytes actually read.
+     * @return a 2-element array:  the first element, an IANA-encoding string,
+     *  the second element a Boolean which is true iff the document is big
+     *  endian, false if it's little-endian, and null if the distinction isn't
+     *  relevant.
+     */
+    private Object[] getEncodingName(byte[] b4, int count) {
+
+        if (count < 2) {
+            return new Object[]{"UTF-8", null};
+        }
+
+        // UTF-16, with BOM
+        int b0 = b4[0] & 0xFF;
+        int b1 = b4[1] & 0xFF;
+        if (b0 == 0xFE && b1 == 0xFF) {
+            // UTF-16, big-endian
+            return new Object [] {"UTF-16BE", new Boolean(true)};
+        }
+        if (b0 == 0xFF && b1 == 0xFE) {
+            // UTF-16, little-endian
+            return new Object [] {"UTF-16LE", new Boolean(false)};
+        }
+
+        // default to UTF-8 if we don't have enough bytes to make a
+        // good determination of the encoding
+        if (count < 3) {
+            return new Object [] {"UTF-8", null};
+        }
+
+        // UTF-8 with a BOM
+        int b2 = b4[2] & 0xFF;
+        if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
+            return new Object [] {"UTF-8", null};
+        }
+
+        // default to UTF-8 if we don't have enough bytes to make a
+        // good determination of the encoding
+        if (count < 4) {
+            return new Object [] {"UTF-8", null};
+        }
+
+        // other encodings
+        int b3 = b4[3] & 0xFF;
+        if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) {
+            // UCS-4, big endian (1234)
+            return new Object [] {"ISO-10646-UCS-4", new Boolean(true)};
+        }
+        if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) {
+            // UCS-4, little endian (4321)
+            return new Object [] {"ISO-10646-UCS-4", new Boolean(false)};
+        }
+        if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) {
+            // UCS-4, unusual octet order (2143)
+            // REVISIT: What should this be?
+            return new Object [] {"ISO-10646-UCS-4", null};
+        }
+        if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) {
+            // UCS-4, unusual octect order (3412)
+            // REVISIT: What should this be?
+            return new Object [] {"ISO-10646-UCS-4", null};
+        }
+        if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
+            // UTF-16, big-endian, no BOM
+            // (or could turn out to be UCS-2...
+            // REVISIT: What should this be?
+            return new Object [] {"UTF-16BE", new Boolean(true)};
+        }
+        if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
+            // UTF-16, little-endian, no BOM
+            // (or could turn out to be UCS-2...
+            return new Object [] {"UTF-16LE", new Boolean(false)};
+        }
+        if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) {
+            // EBCDIC
+            // a la xerces1, return CP037 instead of EBCDIC here
+            return new Object [] {"CP037", null};
+        }
+
+        // default encoding
+        return new Object [] {"UTF-8", null};
+
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.EntityScanner.isExternal
+    /** Returns true if the current entity being scanned is external. */
+    public boolean isExternal() {
+	return true;
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.EntityScanner.peekChar
+    /**
+     * Returns the next character on the input.
+     * <p>
+     * <strong>Note:</strong> The character is <em>not</em> consumed.
+     *
+     * @throws IOException  Thrown if i/o error occurs.
+     * @throws EOFException Thrown on end of file.
+     */
+    public int peekChar() throws IOException {
+	
+	// load more characters, if needed
+	if (fCurrentEntity.position == fCurrentEntity.count) {
+	    load(0, true);
+	}
+	
+	// peek at character
+	int c = fCurrentEntity.ch[fCurrentEntity.position];
+
+	// return peeked character
+	if (fCurrentEntity.isExternal()) {
+	    return c != '\r' ? c : '\n';
+	}
+	else {
+	    return c;
+	}
+	
+    } // peekChar():int
+    
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.EntityScanner.scanChar
+    /**
+     * Returns the next character on the input.
+     * <p>
+     * <strong>Note:</strong> The character is consumed.
+     *
+     * @throws IOException  Thrown if i/o error occurs.
+     * @throws EOFException Thrown on end of file.
+     */
+    public int scanChar() throws IOException {
+
+	// load more characters, if needed
+	if (fCurrentEntity.position == fCurrentEntity.count) {
+	    load(0, true);
+	}
+
+	// scan character
+	int c = fCurrentEntity.ch[fCurrentEntity.position++];
+	boolean external = false;
+	if (c == '\n' ||
+	    (c == '\r' && (external = fCurrentEntity.isExternal()))) {
+	    fCurrentEntity.lineNumber++;
+	    fCurrentEntity.columnNumber = 1;
+	    if (fCurrentEntity.position == fCurrentEntity.count) {
+		fCurrentEntity.ch[0] = (char)c;
+		load(1, false);
+	    }
+	    if (c == '\r' && external) {
+		if (fCurrentEntity.ch[fCurrentEntity.position++] != '\n') {
+		    fCurrentEntity.position--;
+		}
+		c = '\n';
+	    }
+	}
+
+	// return character that was scanned
+	fCurrentEntity.columnNumber++;
+	return c;
+	
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.EntityScanner.scanName
+    /**
+     * Returns a string matching the Name production appearing immediately
+     * on the input as a symbol, or null if no Name string is present.
+     * <p>
+     * <strong>Note:</strong> The Name characters are consumed.
+     * <p>
+     * <strong>Note:</strong> The string returned must be a symbol. The
+     * SymbolTable can be used for this purpose.
+     *
+     * @throws IOException  Thrown if i/o error occurs.
+     * @throws EOFException Thrown on end of file.
+     *
+     * @see SymbolTable
+     * @see XMLChar#isName
+     * @see XMLChar#isNameStart
+     */
+    public String scanName() throws IOException {
+	
+	// load more characters, if needed
+	if (fCurrentEntity.position == fCurrentEntity.count) {
+	    load(0, true);
+	}
+	
+	// scan name
+	int offset = fCurrentEntity.position;
+	if (XMLChar.isNameStart(fCurrentEntity.ch[offset])) {
+	    if (++fCurrentEntity.position == fCurrentEntity.count) {
+		fCurrentEntity.ch[0] = fCurrentEntity.ch[offset];
+		offset = 0;
+		if (load(1, false)) {
+		    fCurrentEntity.columnNumber++;
+		    String symbol = fSymbolTable.addSymbol(fCurrentEntity.ch,
+							   0, 1);
+		    return symbol;
+		}
+	    }
+	    while (XMLChar.isName(fCurrentEntity.ch[fCurrentEntity.position])) {
+		if (++fCurrentEntity.position == fCurrentEntity.count) {
+		    int length = fCurrentEntity.position - offset;
+		    if (length == fBufferSize) {
+			// bad luck we have to resize our buffer
+			char[] tmp = new char[fBufferSize * 2];
+			System.arraycopy(fCurrentEntity.ch, offset,
+					 tmp, 0, length);
+			fCurrentEntity.ch = tmp;
+			fBufferSize *= 2;
+		    } else {
+			System.arraycopy(fCurrentEntity.ch, offset,
+					 fCurrentEntity.ch, 0, length);
+		    }
+		    offset = 0;
+		    if (load(length, false)) {
+			break;
+		    }
+		}
+	    }
+	}
+	int length = fCurrentEntity.position - offset;
+	fCurrentEntity.columnNumber += length;
+
+	// return name
+	String symbol = null;
+	if (length > 0) {
+	    symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length);
+	}
+	return symbol;
+	
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.EntityScanner.scanLiteral
+    /**
+     * Scans a range of attribute value data, setting the fields of the
+     * XMLString structure, appropriately.
+     * <p>
+     * <strong>Note:</strong> The characters are consumed.
+     * <p>
+     * <strong>Note:</strong> This method does not guarantee to return
+     * the longest run of attribute value data. This method may return
+     * before the quote character due to reaching the end of the input
+     * buffer or any other reason.
+     * <p>
+     * <strong>Note:</strong> The fields contained in the XMLString
+     * structure are not guaranteed to remain valid upon subsequent calls
+     * to the entity scanner. Therefore, the caller is responsible for
+     * immediately using the returned character data or making a copy of
+     * the character data.
+     *
+     * @param quote   The quote character that signifies the end of the
+     *                attribute value data.
+     * @param content The content structure to fill.
+     *
+     * @return Returns the next character on the input, if known. This
+     *         value may be -1 but this does <em>note</em> designate
+     *         end of file.
+     *
+     * @throws IOException  Thrown if i/o error occurs.
+     * @throws EOFException Thrown on end of file.
+     */
+    public int scanLiteral(int quote, XMLString content)
+	throws IOException {
+
+	// load more characters, if needed
+	if (fCurrentEntity.position == fCurrentEntity.count) {
+	    load(0, true);
+	} else if (fCurrentEntity.position == fCurrentEntity.count - 1) {
+	    fCurrentEntity.ch[0] = fCurrentEntity.ch[fCurrentEntity.count - 1];
+	    load(1, false);
+	    fCurrentEntity.position = 0;
+	}
+
+	// normalize newlines
+	int offset = fCurrentEntity.position;
+	int c = fCurrentEntity.ch[offset];
+	int newlines = 0;
+	boolean external = fCurrentEntity.isExternal();
+	if (c == '\n' || (c == '\r' && external)) {
+	    do {
+		c = fCurrentEntity.ch[fCurrentEntity.position++];
+		if (c == '\r' && external) {
+		    newlines++;
+		    fCurrentEntity.lineNumber++;
+		    fCurrentEntity.columnNumber = 1;
+		    if (fCurrentEntity.position == fCurrentEntity.count) {
+			offset = 0;
+			fCurrentEntity.position = newlines;
+			if (load(newlines, false)) {
+			    break;
+			}
+		    }
+		    if (fCurrentEntity.ch[fCurrentEntity.position] == '\n') {
+			fCurrentEntity.position++;
+			offset++;
+		    }
+		    /*** NEWLINE NORMALIZATION ***/
+		    else {
+			newlines++;
+		    }
+		    /***/
+		}
+		else if (c == '\n') {
+		    newlines++;
+		    fCurrentEntity.lineNumber++;
+		    fCurrentEntity.columnNumber = 1;
+		    if (fCurrentEntity.position == fCurrentEntity.count) {
+			offset = 0;
+			fCurrentEntity.position = newlines;
+			if (load(newlines, false)) {
+			    break;
+			}
+		    }
+		    /*** NEWLINE NORMALIZATION ***
+			 if (fCurrentEntity.ch[fCurrentEntity.position] == '\r'
+			 && external) {
+			 fCurrentEntity.position++;
+			 offset++;
+			 }
+			 /***/
+		}
+		else {
+		    fCurrentEntity.position--;
+		    break;
+		}
+	    } while (fCurrentEntity.position < fCurrentEntity.count - 1);
+	    for (int i = offset; i < fCurrentEntity.position; i++) {
+		fCurrentEntity.ch[i] = '\n';
+	    }
+	    int length = fCurrentEntity.position - offset;
+	    if (fCurrentEntity.position == fCurrentEntity.count - 1) {
+		content.setValues(fCurrentEntity.ch, offset, length);
+		return -1;
+	    }
+	}
+
+	// scan literal value
+	while (fCurrentEntity.position < fCurrentEntity.count) {
+	    c = fCurrentEntity.ch[fCurrentEntity.position++];
+	    if ((c == quote &&
+		 (!fCurrentEntity.literal || external))
+		|| c == '%' || !XMLChar.isContent(c)) {
+		fCurrentEntity.position--;
+		break;
+	    }
+	}
+	int length = fCurrentEntity.position - offset;
+	fCurrentEntity.columnNumber += length - newlines;
+	content.setValues(fCurrentEntity.ch, offset, length);
+
+	// return next character
+	if (fCurrentEntity.position != fCurrentEntity.count) {
+	    c = fCurrentEntity.ch[fCurrentEntity.position];
+	    // NOTE: We don't want to accidentally signal the
+	    //       end of the literal if we're expanding an
+	    //       entity appearing in the literal. -Ac
+	    if (c == quote && fCurrentEntity.literal) {
+		c = -1;
+	    }
+	}
+	else {
+	    c = -1;
+	}
+	return c;
+
+    }
+
+    /**
+     * Scans a range of character data up to the specified delimiter,
+     * setting the fields of the XMLString structure, appropriately.
+     * <p>
+     * <strong>Note:</strong> The characters are consumed.
+     * <p>
+     * <strong>Note:</strong> This assumes that the internal buffer is
+     * at least the same size, or bigger, than the length of the delimiter
+     * and that the delimiter contains at least one character.
+     * <p>
+     * <strong>Note:</strong> This method does not guarantee to return
+     * the longest run of character data. This method may return before
+     * the delimiter due to reaching the end of the input buffer or any
+     * other reason.
+     * <p>
+     * <strong>Note:</strong> The fields contained in the XMLString
+     * structure are not guaranteed to remain valid upon subsequent calls
+     * to the entity scanner. Therefore, the caller is responsible for
+     * immediately using the returned character data or making a copy of
+     * the character data.
+     *
+     * @param delimiter The string that signifies the end of the character
+     *                  data to be scanned.
+     * @param buffer    The data structure to fill.
+     *
+     * @return Returns true if there is more data to scan, false otherwise.
+     *
+     * @throws IOException  Thrown if i/o error occurs.
+     * @throws EOFException Thrown on end of file.
+     */
+    public boolean scanData(String delimiter, XMLStringBuffer buffer)
+	throws IOException {
+
+	boolean done = false;
+	int delimLen = delimiter.length();
+	char charAt0 = delimiter.charAt(0);
+	boolean external = fCurrentEntity.isExternal();
+	do {
+    
+	    // load more characters, if needed
+    
+	    if (fCurrentEntity.position == fCurrentEntity.count) {
+		load(0, true);
+	    }
+	    else if (fCurrentEntity.position >= fCurrentEntity.count - delimLen) {
+		System.arraycopy(fCurrentEntity.ch, fCurrentEntity.position,
+				 fCurrentEntity.ch, 0, fCurrentEntity.count - fCurrentEntity.position);
+		load(fCurrentEntity.count - fCurrentEntity.position, false);
+		fCurrentEntity.position = 0;
+	    } 
+	    if (fCurrentEntity.position >= fCurrentEntity.count - delimLen) {
+		// something must be wrong with the input: e.g., file ends an
+		// unterminated comment
+		int length = fCurrentEntity.count - fCurrentEntity.position;
+		buffer.append (fCurrentEntity.ch, fCurrentEntity.position,
+			       length); 
+		fCurrentEntity.columnNumber += fCurrentEntity.count;
+		fCurrentEntity.position = fCurrentEntity.count;
+		load(0,true);
+		return false;
+	    }
+    
+	    // normalize newlines
+	    int offset = fCurrentEntity.position;
+	    int c = fCurrentEntity.ch[offset];
+	    int newlines = 0;
+	    if (c == '\n' || (c == '\r' && external)) {
+		do {
+		    c = fCurrentEntity.ch[fCurrentEntity.position++];
+		    if (c == '\r' && external) {
+			newlines++;
+			fCurrentEntity.lineNumber++;
+			fCurrentEntity.columnNumber = 1;
+			if (fCurrentEntity.position == fCurrentEntity.count) {
+			    offset = 0;
+			    fCurrentEntity.position = newlines;
+			    if (load(newlines, false)) {
+				break;
+			    }
+			}
+			if (fCurrentEntity.ch[fCurrentEntity.position] == '\n') {
+			    fCurrentEntity.position++;
+			    offset++;
+			}
+			/*** NEWLINE NORMALIZATION ***/
+			else {
+			    newlines++;
+			}
+		    }
+		    else if (c == '\n') {
+			newlines++;
+			fCurrentEntity.lineNumber++;
+			fCurrentEntity.columnNumber = 1;
+			if (fCurrentEntity.position == fCurrentEntity.count) {
+			    offset = 0;
+			    fCurrentEntity.position = newlines;
+			    fCurrentEntity.count = newlines;
+			    if (load(newlines, false)) {
+				break;
+			    }
+			}
+		    }
+		    else {
+			fCurrentEntity.position--;
+			break;
+		    }
+		} while (fCurrentEntity.position < fCurrentEntity.count - 1);
+		for (int i = offset; i < fCurrentEntity.position; i++) {
+		    fCurrentEntity.ch[i] = '\n';
+		}
+		int length = fCurrentEntity.position - offset;
+		if (fCurrentEntity.position == fCurrentEntity.count - 1) {
+		    buffer.append(fCurrentEntity.ch, offset, length);
+		    return true;
+		}
+	    }
+    
+	    // iterate over buffer looking for delimiter
+	OUTER: while (fCurrentEntity.position < fCurrentEntity.count) {
+	    c = fCurrentEntity.ch[fCurrentEntity.position++];
+	    if (c == charAt0) {
+		// looks like we just hit the delimiter
+		int delimOffset = fCurrentEntity.position - 1;
+		for (int i = 1; i < delimLen; i++) {
+		    if (fCurrentEntity.position == fCurrentEntity.count) {
+			fCurrentEntity.position -= i;
+			break OUTER;
+		    }
+		    c = fCurrentEntity.ch[fCurrentEntity.position++];
+		    if (delimiter.charAt(i) != c) {
+			fCurrentEntity.position--;
+			break;
+		    }
+		}
+		if (fCurrentEntity.position == delimOffset + delimLen) {
+		    done = true;
+		    break;
+		}
+	    }
+	    else if (c == '\n' || (external && c == '\r')) {
+		fCurrentEntity.position--;
+		break;
+	    }
+	    else if (XMLChar.isInvalid(c)) {
+		fCurrentEntity.position--;
+		int length = fCurrentEntity.position - offset;
+		fCurrentEntity.columnNumber += length - newlines;
+		buffer.append(fCurrentEntity.ch, offset, length); 
+		return true;
+	    }
+	}
+	    int length = fCurrentEntity.position - offset;
+	    fCurrentEntity.columnNumber += length - newlines;
+	    if (done) {
+		length -= delimLen;
+	    }
+	    buffer.append (fCurrentEntity.ch, offset, length);
+    
+	    // return true if string was skipped
+	} while (!done);
+	return !done;
+
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.EntityScanner.skipChar
+    /**
+     * Skips a character appearing immediately on the input.
+     * <p>
+     * <strong>Note:</strong> The character is consumed only if it matches
+     * the specified character.
+     *
+     * @param c The character to skip.
+     *
+     * @return Returns true if the character was skipped.
+     *
+     * @throws IOException  Thrown if i/o error occurs.
+     * @throws EOFException Thrown on end of file.
+     */
+    public boolean skipChar(int c) throws IOException {
+
+	// load more characters, if needed
+	if (fCurrentEntity.position == fCurrentEntity.count) {
+	    load(0, true);
+	}
+
+	// skip character
+	int cc = fCurrentEntity.ch[fCurrentEntity.position];
+	if (cc == c) {
+	    fCurrentEntity.position++;
+	    if (c == '\n') {
+		fCurrentEntity.lineNumber++;
+		fCurrentEntity.columnNumber = 1;
+	    }
+	    else {
+		fCurrentEntity.columnNumber++;
+	    }
+	    return true;
+	} else if (c == '\n' && cc == '\r' && fCurrentEntity.isExternal()) {
+	    // handle newlines
+	    if (fCurrentEntity.position == fCurrentEntity.count) {
+		fCurrentEntity.ch[0] = (char)cc;
+		load(1, false);
+	    }
+	    fCurrentEntity.position++;
+	    if (fCurrentEntity.ch[fCurrentEntity.position] == '\n') {
+		fCurrentEntity.position++;
+	    }
+	    fCurrentEntity.lineNumber++;
+	    fCurrentEntity.columnNumber = 1;
+	    return true;
+	}
+
+	// character was not skipped
+	return false;
+
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.EntityScanner.skipSpaces
+    /**
+     * Skips space characters appearing immediately on the input.
+     * <p>
+     * <strong>Note:</strong> The characters are consumed only if they are
+     * space characters.
+     *
+     * @return Returns true if at least one space character was skipped.
+     *
+     * @throws IOException  Thrown if i/o error occurs.
+     * @throws EOFException Thrown on end of file.
+     *
+     * @see XMLChar#isSpace
+     */
+    public boolean skipSpaces() throws IOException {
+
+	// load more characters, if needed
+	if (fCurrentEntity.position == fCurrentEntity.count) {
+	    load(0, true);
+	}
+
+	// skip spaces
+	int c = fCurrentEntity.ch[fCurrentEntity.position];
+	if (XMLChar.isSpace(c)) {
+	    boolean external = fCurrentEntity.isExternal();
+	    do {
+		boolean entityChanged = false;
+		// handle newlines
+		if (c == '\n' || (external && c == '\r')) {
+		    fCurrentEntity.lineNumber++;
+		    fCurrentEntity.columnNumber = 1;
+		    if (fCurrentEntity.position == fCurrentEntity.count - 1) {
+			fCurrentEntity.ch[0] = (char)c;
+			entityChanged = load(1, true);
+			if (!entityChanged)
+                                // the load change the position to be 1,
+                                // need to restore it when entity not changed
+			    fCurrentEntity.position = 0;
+		    }
+		    if (c == '\r' && external) {
+			// REVISIT: Does this need to be updated to fix the
+			//          #x0D ^#x0A newline normalization problem? -Ac
+			if (fCurrentEntity.ch[++fCurrentEntity.position] != '\n') {
+			    fCurrentEntity.position--;
+			}
+		    }
+		    /*** NEWLINE NORMALIZATION ***
+			 else {
+			 if (fCurrentEntity.ch[fCurrentEntity.position + 1] == '\r'
+			 && external) {
+			 fCurrentEntity.position++;
+			 }
+			 }
+			 /***/
+		}
+		else {
+		    fCurrentEntity.columnNumber++;
+		}
+		// load more characters, if needed
+		if (!entityChanged)
+		    fCurrentEntity.position++;
+		if (fCurrentEntity.position == fCurrentEntity.count) {
+		    load(0, true);
+		}
+	    } while (XMLChar.isSpace(c = fCurrentEntity.ch[fCurrentEntity.position]));
+	    return true;
+	}
+
+	// no spaces were found
+	return false;
+
+    }
+
+    /**
+     * Skips the specified string appearing immediately on the input.
+     * <p>
+     * <strong>Note:</strong> The characters are consumed only if they are
+     * space characters.
+     *
+     * @param s The string to skip.
+     *
+     * @return Returns true if the string was skipped.
+     *
+     * @throws IOException  Thrown if i/o error occurs.
+     * @throws EOFException Thrown on end of file.
+     */
+    public boolean skipString(String s) throws IOException {
+
+	// load more characters, if needed
+	if (fCurrentEntity.position == fCurrentEntity.count) {
+	    load(0, true);
+	}
+
+	// skip string
+	final int length = s.length();
+	for (int i = 0; i < length; i++) {
+	    char c = fCurrentEntity.ch[fCurrentEntity.position++];
+	    if (c != s.charAt(i)) {
+		fCurrentEntity.position -= i + 1;
+		return false;
+	    }
+	    if (i < length - 1 && fCurrentEntity.position == fCurrentEntity.count) {
+		System.arraycopy(fCurrentEntity.ch, fCurrentEntity.count - i - 1, fCurrentEntity.ch, 0, i + 1);
+		// REVISIT: Can a string to be skipped cross an
+		//          entity boundary? -Ac
+		if (load(i + 1, false)) {
+		    fCurrentEntity.position -= i + 1;
+		    return false;
+		}
+	    }
+	}
+	fCurrentEntity.columnNumber += length;
+	return true;
+
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.EntityScanner.load
+    /**
+     * Loads a chunk of text.
+     *
+     * @param offset       The offset into the character buffer to
+     *                     read the next batch of characters.
+     * @param changeEntity True if the load should change entities
+     *                     at the end of the entity, otherwise leave
+     *                     the current entity in place and the entity
+     *                     boundary will be signaled by the return
+     *                     value.
+     *
+     * @returns Returns true if the entity changed as a result of this
+     *          load operation.
+     */
+    final boolean load(int offset, boolean changeEntity)
+	throws IOException {
+
+	// read characters
+	int length = fCurrentEntity.mayReadChunks?
+	    (fCurrentEntity.ch.length - offset):
+	    (DEFAULT_XMLDECL_BUFFER_SIZE);
+	int count = fCurrentEntity.reader.read(fCurrentEntity.ch, offset,
+					       length);
+
+	// reset count and position
+	boolean entityChanged = false;
+	if (count != -1) {
+	    if (count != 0) {
+		fCurrentEntity.count = count + offset;
+		fCurrentEntity.position = offset;
+	    }
+	}
+
+	// end of this entity
+	else {
+	    fCurrentEntity.count = offset;
+	    fCurrentEntity.position = offset;
+	    entityChanged = true;
+	    if (changeEntity) {
+		endEntity();
+		if (fCurrentEntity == null) {
+		    throw new EOFException();
+		}
+		// handle the trailing edges
+		if (fCurrentEntity.position == fCurrentEntity.count) {
+		    load(0, false);
+		}
+	    }
+	}
+
+	return entityChanged;
+
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLEntityManager.RewindableInputStream
+    /**
+     * This class wraps the byte inputstreams we're presented with.
+     * We need it because java.io.InputStreams don't provide
+     * functionality to reread processed bytes, and they have a habit
+     * of reading more than one character when you call their read()
+     * methods.  This means that, once we discover the true (declared)
+     * encoding of a document, we can neither backtrack to read the
+     * whole doc again nor start reading where we are with a new
+     * reader.
+     *
+     * This class allows rewinding an inputStream by allowing a mark
+     * to be set, and the stream reset to that position.  <strong>The
+     * class assumes that it needs to read one character per
+     * invocation when it's read() method is inovked, but uses the
+     * underlying InputStream's read(char[], offset length) method--it
+     * won't buffer data read this way!</strong>
+     *
+     * @author Neil Graham, IBM
+     * @author Glenn Marcy, IBM
+     */
+    private final class RewindableInputStream extends InputStream {
+
+        private InputStream fInputStream;
+        private byte[] fData;
+        private int fStartOffset;
+        private int fEndOffset;
+        private int fOffset;
+        private int fLength;
+        private int fMark;
+
+        public RewindableInputStream(InputStream is) {
+            fData = new byte[DEFAULT_XMLDECL_BUFFER_SIZE];
+            fInputStream = is;
+            fStartOffset = 0;
+            fEndOffset = -1;
+            fOffset = 0;
+            fLength = 0;
+            fMark = 0;
+        }
+
+        public void setStartOffset(int offset) {
+            fStartOffset = offset;
+        }
+
+        public void rewind() {
+            fOffset = fStartOffset;
+        }
+
+        public int read() throws IOException {
+            int b = 0;
+            if (fOffset < fLength) {
+                return fData[fOffset++] & 0xff;
+            }
+            if (fOffset == fEndOffset) {
+                return -1;
+            }
+            if (fOffset == fData.length) {
+                byte[] newData = new byte[fOffset << 1];
+                System.arraycopy(fData, 0, newData, 0, fOffset);
+                fData = newData;
+            }
+            b = fInputStream.read();
+            if (b == -1) {
+                fEndOffset = fOffset;
+                return -1;
+            }
+            fData[fLength++] = (byte)b;
+            fOffset++;
+            return b & 0xff;
+        }
+
+        public int read(byte[] b, int off, int len) throws IOException {
+            int bytesLeft = fLength - fOffset;
+            if (bytesLeft == 0) {
+                if (fOffset == fEndOffset) {
+                    return -1;
+                }
+                // better get some more for the voracious reader...
+                if (fCurrentEntity.mayReadChunks) {
+                    return fInputStream.read(b, off, len);
+                }
+                int returnedVal = read();
+                if (returnedVal == -1) {
+                    fEndOffset = fOffset;
+                    return -1;
+                }
+                b[off] = (byte)returnedVal;
+                return 1;
+            }
+            if (len < bytesLeft) {
+                if (len <= 0) {
+                    return 0;
+                }
+            }
+            else {
+                len = bytesLeft;
+            }
+            if (b != null) {
+                System.arraycopy(fData, fOffset, b, off, len);
+            }
+            fOffset += len;
+            return len;
+        }
+
+        public long skip(long n)
+            throws IOException
+        {
+            int bytesLeft;
+            if (n <= 0) {
+                return 0;
+            }
+            bytesLeft = fLength - fOffset;
+            if (bytesLeft == 0) {
+                if (fOffset == fEndOffset) {
+                    return 0;
+                }
+                return fInputStream.skip(n);
+            }
+            if (n <= bytesLeft) {
+                fOffset += n;
+                return n;
+            }
+            fOffset += bytesLeft;
+            if (fOffset == fEndOffset) {
+                return bytesLeft;
+            }
+            n -= bytesLeft;
+	    /*
+	     * In a manner of speaking, when this class isn't permitting more
+	     * than one byte at a time to be read, it is "blocking".  The
+	     * available() method should indicate how much can be read without
+	     * blocking, so while we're in this mode, it should only indicate
+	     * that bytes in its buffer are available; otherwise, the result of
+	     * available() on the underlying InputStream is appropriate.
+	     */
+            return fInputStream.skip(n) + bytesLeft;
+        }
+
+        public int available() throws IOException {
+            int bytesLeft = fLength - fOffset;
+            if (bytesLeft == 0) {
+                if (fOffset == fEndOffset) {
+                    return -1;
+                }
+                return fCurrentEntity.mayReadChunks ? fInputStream.available()
+		    : 0;
+            }
+            return bytesLeft;
+        }
+
+        public void mark(int howMuch) {
+            fMark = fOffset;
+        }
+
+        public void reset() {
+            fOffset = fMark;
+        }
+
+        public boolean markSupported() {
+            return true;
+        }
+
+        public void close() throws IOException {
+            if (fInputStream != null) {
+                fInputStream.close();
+                fInputStream = null;
+            }
+        }
+    } // end of RewindableInputStream class
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLDocumentScannerImpl.dispatch
+    private void scanXMLDecl() throws IOException, JasperException {
+
+	if (skipString("<?xml")) {
+	    fMarkupDepth++;
+	    // NOTE: special case where document starts with a PI
+	    //       whose name starts with "xml" (e.g. "xmlfoo")
+	    if (XMLChar.isName(peekChar())) {
+		fStringBuffer.clear();
+		fStringBuffer.append("xml");
+		while (XMLChar.isName(peekChar())) {
+		    fStringBuffer.append((char)scanChar());
+		}
+		String target = fSymbolTable.addSymbol(fStringBuffer.ch,
+						       fStringBuffer.offset,
+						       fStringBuffer.length);
+		scanPIData(target, fString);
+	    }
+
+	    // standard XML declaration
+	    else {
+		scanXMLDeclOrTextDecl(false);
+	    }
+	}
+    }
+    
+    // Adapted from:
+    // org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanXMLDeclOrTextDecl
+    /**
+     * Scans an XML or text declaration.
+     * <p>
+     * <pre>
+     * [23] XMLDecl ::= '&lt;?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
+     * [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
+     * [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' |  "'" EncName "'" )
+     * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
+     * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'")
+     *                 | ('"' ('yes' | 'no') '"'))
+     *
+     * [77] TextDecl ::= '&lt;?xml' VersionInfo? EncodingDecl S? '?>'
+     * </pre>
+     *
+     * @param scanningTextDecl True if a text declaration is to
+     *                         be scanned instead of an XML
+     *                         declaration.
+     */
+    private void scanXMLDeclOrTextDecl(boolean scanningTextDecl) 
+        throws IOException, JasperException {
+
+        // scan decl
+        scanXMLDeclOrTextDecl(scanningTextDecl, fStrings);
+        fMarkupDepth--;
+
+        // pseudo-attribute values
+        String encodingPseudoAttr = fStrings[1];
+
+        // set encoding on reader
+        if (encodingPseudoAttr != null) {
+            isEncodingSetInProlog = true;
+	    encoding = encodingPseudoAttr;
+        }
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLScanner.scanXMLDeclOrTextDecl
+    /**
+     * Scans an XML or text declaration.
+     * <p>
+     * <pre>
+     * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
+     * [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
+     * [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' |  "'" EncName "'" )
+     * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
+     * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'")
+     *                 | ('"' ('yes' | 'no') '"'))
+     *
+     * [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
+     * </pre>
+     *
+     * @param scanningTextDecl True if a text declaration is to
+     *                         be scanned instead of an XML
+     *                         declaration.
+     * @param pseudoAttributeValues An array of size 3 to return the version,
+     *                         encoding and standalone pseudo attribute values
+     *                         (in that order).
+     *
+     * <strong>Note:</strong> This method uses fString, anything in it
+     * at the time of calling is lost.
+     */
+    private void scanXMLDeclOrTextDecl(boolean scanningTextDecl,
+				       String[] pseudoAttributeValues) 
+                throws IOException, JasperException {
+
+        // pseudo-attribute values
+        String version = null;
+        String encoding = null;
+        String standalone = null;
+
+        // scan pseudo-attributes
+        final int STATE_VERSION = 0;
+        final int STATE_ENCODING = 1;
+        final int STATE_STANDALONE = 2;
+        final int STATE_DONE = 3;
+        int state = STATE_VERSION;
+
+        boolean dataFoundForTarget = false;
+        boolean sawSpace = skipSpaces();
+        while (peekChar() != '?') {
+            dataFoundForTarget = true;
+            String name = scanPseudoAttribute(scanningTextDecl, fString);
+            switch (state) {
+                case STATE_VERSION: {
+                    if (name == fVersionSymbol) {
+                        if (!sawSpace) {
+                            reportFatalError(scanningTextDecl
+                                       ? "jsp.error.xml.spaceRequiredBeforeVersionInTextDecl"
+                                       : "jsp.error.xml.spaceRequiredBeforeVersionInXMLDecl",
+                                             null);
+                        }
+                        version = fString.toString();
+                        state = STATE_ENCODING;
+                        if (!version.equals("1.0")) {
+                            // REVISIT: XML REC says we should throw an error
+			    // in such cases.
+                            // some may object the throwing of fatalError.
+                            err.jspError("jsp.error.xml.versionNotSupported",
+					 version);
+                        }
+                    } else if (name == fEncodingSymbol) {
+                        if (!scanningTextDecl) {
+                            err.jspError("jsp.error.xml.versionInfoRequired");
+                        }
+                        if (!sawSpace) {
+                            reportFatalError(scanningTextDecl
+                                      ? "jsp.error.xml.spaceRequiredBeforeEncodingInTextDecl"
+                                      : "jsp.error.xml.spaceRequiredBeforeEncodingInXMLDecl",
+                                             null);
+                        }
+                        encoding = fString.toString();
+                        state = scanningTextDecl ? STATE_DONE : STATE_STANDALONE;
+                    } else {
+                        if (scanningTextDecl) {
+                            err.jspError("jsp.error.xml.encodingDeclRequired");
+                        }
+                        else {
+                            err.jspError("jsp.error.xml.versionInfoRequired");
+                        }
+                    }
+                    break;
+                }
+                case STATE_ENCODING: {
+                    if (name == fEncodingSymbol) {
+                        if (!sawSpace) {
+                            reportFatalError(scanningTextDecl
+                                      ? "jsp.error.xml.spaceRequiredBeforeEncodingInTextDecl"
+                                      : "jsp.error.xml.spaceRequiredBeforeEncodingInXMLDecl",
+                                             null);
+                        }
+                        encoding = fString.toString();
+                        state = scanningTextDecl ? STATE_DONE : STATE_STANDALONE;
+                        // TODO: check encoding name; set encoding on
+                        //       entity scanner
+                    } else if (!scanningTextDecl && name == fStandaloneSymbol) {
+                        if (!sawSpace) {
+                            err.jspError("jsp.error.xml.spaceRequiredBeforeStandalone");
+                        }
+                        standalone = fString.toString();
+                        state = STATE_DONE;
+                        if (!standalone.equals("yes") && !standalone.equals("no")) {
+                            err.jspError("jsp.error.xml.sdDeclInvalid");
+                        }
+                    } else {
+                        err.jspError("jsp.error.xml.encodingDeclRequired");
+                    }
+                    break;
+                }
+                case STATE_STANDALONE: {
+                    if (name == fStandaloneSymbol) {
+                        if (!sawSpace) {
+                            err.jspError("jsp.error.xml.spaceRequiredBeforeStandalone");
+                        }
+                        standalone = fString.toString();
+                        state = STATE_DONE;
+                        if (!standalone.equals("yes") && !standalone.equals("no")) {
+                            err.jspError("jsp.error.xml.sdDeclInvalid");
+                        }
+                    } else {
+			err.jspError("jsp.error.xml.encodingDeclRequired");
+                    }
+                    break;
+                }
+                default: {
+                    err.jspError("jsp.error.xml.noMorePseudoAttributes");
+                }
+            }
+            sawSpace = skipSpaces();
+        }
+        // REVISIT: should we remove this error reporting?
+        if (scanningTextDecl && state != STATE_DONE) {
+            err.jspError("jsp.error.xml.morePseudoAttributes");
+        }
+        
+        // If there is no data in the xml or text decl then we fail to report
+	// error for version or encoding info above.
+        if (scanningTextDecl) {
+            if (!dataFoundForTarget && encoding == null) {
+                err.jspError("jsp.error.xml.encodingDeclRequired");
+            }
+        } else {
+            if (!dataFoundForTarget && version == null) {
+                err.jspError("jsp.error.xml.versionInfoRequired");
+            }
+        }
+
+        // end
+        if (!skipChar('?')) {
+            err.jspError("jsp.error.xml.xmlDeclUnterminated");
+        }
+        if (!skipChar('>')) {
+            err.jspError("jsp.error.xml.xmlDeclUnterminated");
+
+        }
+        
+        // fill in return array
+        pseudoAttributeValues[0] = version;
+        pseudoAttributeValues[1] = encoding;
+        pseudoAttributeValues[2] = standalone;
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLScanner.scanPseudoAttribute
+    /**
+     * Scans a pseudo attribute.
+     *
+     * @param scanningTextDecl True if scanning this pseudo-attribute for a
+     *                         TextDecl; false if scanning XMLDecl. This 
+     *                         flag is needed to report the correct type of
+     *                         error.
+     * @param value            The string to fill in with the attribute 
+     *                         value.
+     *
+     * @return The name of the attribute
+     *
+     * <strong>Note:</strong> This method uses fStringBuffer2, anything in it
+     * at the time of calling is lost.
+     */
+    public String scanPseudoAttribute(boolean scanningTextDecl, 
+                                      XMLString value) 
+                throws IOException, JasperException {
+
+        String name = scanName();
+        if (name == null) {
+            err.jspError("jsp.error.xml.pseudoAttrNameExpected");
+        }
+        skipSpaces();
+        if (!skipChar('=')) {
+            reportFatalError(scanningTextDecl ?
+			     "jsp.error.xml.eqRequiredInTextDecl"
+                             : "jsp.error.xml.eqRequiredInXMLDecl",
+			     name);
+        }
+        skipSpaces();
+        int quote = peekChar();
+        if (quote != '\'' && quote != '"') {
+            reportFatalError(scanningTextDecl ?
+			     "jsp.error.xml.quoteRequiredInTextDecl"
+                             : "jsp.error.xml.quoteRequiredInXMLDecl" ,
+			     name);
+        }
+        scanChar();
+        int c = scanLiteral(quote, value);
+        if (c != quote) {
+            fStringBuffer2.clear();
+            do {
+                fStringBuffer2.append(value);
+                if (c != -1) {
+                    if (c == '&' || c == '%' || c == '<' || c == ']') {
+                        fStringBuffer2.append((char)scanChar());
+                    }
+                    else if (XMLChar.isHighSurrogate(c)) {
+                        scanSurrogates(fStringBuffer2);
+                    }
+                    else if (XMLChar.isInvalid(c)) {
+                        String key = scanningTextDecl
+                            ? "jsp.error.xml.invalidCharInTextDecl"
+			    : "jsp.error.xml.invalidCharInXMLDecl";
+                        reportFatalError(key, Integer.toString(c, 16));
+                        scanChar();
+                    }
+                }
+                c = scanLiteral(quote, value);
+            } while (c != quote);
+            fStringBuffer2.append(value);
+            value.setValues(fStringBuffer2);
+        }
+        if (!skipChar(quote)) {
+            reportFatalError(scanningTextDecl ?
+			     "jsp.error.xml.closeQuoteMissingInTextDecl"
+                             : "jsp.error.xml.closeQuoteMissingInXMLDecl",
+			     name);
+        }
+
+        // return
+        return name;
+
+    }
+    
+    // Adapted from:
+    // org.apache.xerces.impl.XMLScanner.scanPIData
+    /**
+     * Scans a processing data. This is needed to handle the situation
+     * where a document starts with a processing instruction whose 
+     * target name <em>starts with</em> "xml". (e.g. xmlfoo)
+     *
+     * <strong>Note:</strong> This method uses fStringBuffer, anything in it
+     * at the time of calling is lost.
+     *
+     * @param target The PI target
+     * @param data The string to fill in with the data
+     */
+    private void scanPIData(String target, XMLString data) 
+        throws IOException, JasperException {
+
+        // check target
+        if (target.length() == 3) {
+            char c0 = Character.toLowerCase(target.charAt(0));
+            char c1 = Character.toLowerCase(target.charAt(1));
+            char c2 = Character.toLowerCase(target.charAt(2));
+            if (c0 == 'x' && c1 == 'm' && c2 == 'l') {
+                err.jspError("jsp.error.xml.reservedPITarget");
+            }
+        }
+
+        // spaces
+        if (!skipSpaces()) {
+            if (skipString("?>")) {
+                // we found the end, there is no data
+                data.clear();
+                return;
+            }
+            else {
+                // if there is data there should be some space
+                err.jspError("jsp.error.xml.spaceRequiredInPI");
+            }
+        }
+
+        fStringBuffer.clear();
+        // data
+        if (scanData("?>", fStringBuffer)) {
+            do {
+                int c = peekChar();
+                if (c != -1) {
+                    if (XMLChar.isHighSurrogate(c)) {
+                        scanSurrogates(fStringBuffer);
+                    } else if (XMLChar.isInvalid(c)) {
+                        err.jspError("jsp.error.xml.invalidCharInPI",
+				     Integer.toHexString(c));
+                        scanChar();
+                    }
+                }
+            } while (scanData("?>", fStringBuffer));
+        }
+        data.setValues(fStringBuffer);
+
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLScanner.scanSurrogates
+    /**
+     * Scans surrogates and append them to the specified buffer.
+     * <p>
+     * <strong>Note:</strong> This assumes the current char has already been
+     * identified as a high surrogate.
+     *
+     * @param buf The StringBuffer to append the read surrogates to.
+     * @returns True if it succeeded.
+     */
+    private boolean scanSurrogates(XMLStringBuffer buf)
+        throws IOException, JasperException {
+
+        int high = scanChar();
+        int low = peekChar();
+        if (!XMLChar.isLowSurrogate(low)) {
+            err.jspError("jsp.error.xml.invalidCharInContent",
+			 Integer.toString(high, 16));
+            return false;
+        }
+        scanChar();
+
+        // convert surrogates to supplemental character
+        int c = XMLChar.supplemental((char)high, (char)low);
+
+        // supplemental character must be a valid XML character
+        if (!XMLChar.isValid(c)) {
+            err.jspError("jsp.error.xml.invalidCharInContent",
+			 Integer.toString(c, 16)); 
+            return false;
+        }
+
+        // fill in the buffer
+        buf.append((char)high);
+        buf.append((char)low);
+
+        return true;
+
+    }
+
+    // Adapted from:
+    // org.apache.xerces.impl.XMLScanner.reportFatalError
+    /**
+     * Convenience function used in all XML scanners.
+     */
+    private void reportFatalError(String msgId, String arg)
+                throws JasperException {
+        err.jspError(msgId, arg);
+    }
+
+}
+
+
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLString.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLString.java
new file mode 100644
index 0000000..2da7cbb
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLString.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.apache.org.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.jasper.xmlparser;
+
+/**
+ * This class is used as a structure to pass text contained in the underlying
+ * character buffer of the scanner. The offset and length fields allow the
+ * buffer to be re-used without creating new character arrays.
+ * <p>
+ * <strong>Note:</strong> Methods that are passed an XMLString structure
+ * should consider the contents read-only and not make any modifications
+ * to the contents of the buffer. The method receiving this structure
+ * should also not modify the offset and length if this structure (or
+ * the values of this structure) are passed to another method.
+ * <p>
+ * <strong>Note:</strong> Methods that are passed an XMLString structure
+ * are required to copy the information out of the buffer if it is to be
+ * saved for use beyond the scope of the method. The contents of the 
+ * structure are volatile and the contents of the character buffer cannot
+ * be assured once the method that is passed this structure returns.
+ * Therefore, methods passed this structure should not save any reference
+ * to the structure or the character array contained in the structure.
+ *
+ * @author Eric Ye, IBM
+ * @author Andy Clark, IBM
+ *
+ * @version $Id$
+ */
+public class XMLString {
+
+    //
+    // Data
+    //
+
+    /** The character array. */
+    public char[] ch;
+
+    /** The offset into the character array. */
+    public int offset;
+
+    /** The length of characters from the offset. */
+    public int length;
+
+    //
+    // Constructors
+    //
+
+    /** Default constructor. */
+    public XMLString() {
+    } // <init>()
+
+    /**
+     * Constructs an XMLString structure preset with the specified
+     * values.
+     * 
+     * @param ch     The character array.
+     * @param offset The offset into the character array.
+     * @param length The length of characters from the offset.
+     */
+    public XMLString(char[] ch, int offset, int length) {
+        setValues(ch, offset, length);
+    } // <init>(char[],int,int)
+
+    /**
+     * Constructs an XMLString structure with copies of the values in
+     * the given structure.
+     * <p>
+     * <strong>Note:</strong> This does not copy the character array;
+     * only the reference to the array is copied.
+     *
+     * @param string The XMLString to copy.
+     */
+    public XMLString(XMLString string) {
+        setValues(string);
+    } // <init>(XMLString)
+
+    //
+    // Public methods
+    //
+
+    /**
+     * Initializes the contents of the XMLString structure with the
+     * specified values.
+     * 
+     * @param ch     The character array.
+     * @param offset The offset into the character array.
+     * @param length The length of characters from the offset.
+     */
+    public void setValues(char[] ch, int offset, int length) {
+        this.ch = ch;
+        this.offset = offset;
+        this.length = length;
+    } // setValues(char[],int,int)
+
+    /**
+     * Initializes the contents of the XMLString structure with copies
+     * of the given string structure.
+     * <p>
+     * <strong>Note:</strong> This does not copy the character array;
+     * only the reference to the array is copied.
+     * 
+     * @param s
+     */
+    public void setValues(XMLString s) {
+        setValues(s.ch, s.offset, s.length);
+    } // setValues(XMLString)
+
+    /** Resets all of the values to their defaults. */
+    public void clear() {
+        this.ch = null;
+        this.offset = 0;
+        this.length = -1;
+    } // clear()
+
+    /**
+     * Returns true if the contents of this XMLString structure and
+     * the specified array are equal.
+     * 
+     * @param ch     The character array.
+     * @param offset The offset into the character array.
+     * @param length The length of characters from the offset.
+     */
+    public boolean equals(char[] ch, int offset, int length) {
+        if (ch == null) {
+            return false;
+        }
+        if (this.length != length) {
+            return false;
+        }
+
+        for (int i=0; i<length; i++) {
+            if (this.ch[this.offset+i] != ch[offset+i] ) {
+                return false;
+            }
+        }
+        return true;
+    } // equals(char[],int,int):boolean
+
+    /**
+     * Returns true if the contents of this XMLString structure and
+     * the specified string are equal.
+     * 
+     * @param s The string to compare.
+     */
+    public boolean equals(String s) {
+        if (s == null) {
+            return false;
+        }
+        if ( length != s.length() ) {
+            return false;
+        }
+
+        // is this faster than call s.toCharArray first and compare the 
+        // two arrays directly, which will possibly involve creating a
+        // new char array object.
+        for (int i=0; i<length; i++) {
+            if (ch[offset+i] != s.charAt(i)) {
+                return false;
+            }
+        }
+
+        return true;
+    } // equals(String):boolean
+
+    //
+    // Object methods
+    //
+
+    /** Returns a string representation of this object. */
+    public String toString() {
+        return length > 0 ? new String(ch, offset, length) : "";
+    } // toString():String
+
+} // class XMLString
diff --git a/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLStringBuffer.java b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLStringBuffer.java
new file mode 100644
index 0000000..0154263
--- /dev/null
+++ b/jasper/jasper2/src/share/org/apache/jasper/xmlparser/XMLStringBuffer.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.apache.org.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.jasper.xmlparser;
+
+/**
+ * XMLString is a structure used to pass character arrays. However,
+ * XMLStringBuffer is a buffer in which characters can be appended
+ * and extends XMLString so that it can be passed to methods
+ * expecting an XMLString object. This is a safe operation because
+ * it is assumed that any callee will <strong>not</strong> modify
+ * the contents of the XMLString structure.
+ * <p> 
+ * The contents of the string are managed by the string buffer. As
+ * characters are appended, the string buffer will grow as needed.
+ * <p>
+ * <strong>Note:</strong> Never set the <code>ch</code>, 
+ * <code>offset</code>, and <code>length</code> fields directly.
+ * These fields are managed by the string buffer. In order to reset
+ * the buffer, call <code>clear()</code>.
+ * 
+ * @author Andy Clark, IBM
+ * @author Eric Ye, IBM
+ *
+ * @version $Id$
+ */
+public class XMLStringBuffer
+    extends XMLString {
+
+    //
+    // Constants
+    //
+
+    /** Default buffer size (32). */
+    public static final int DEFAULT_SIZE = 32;
+
+    //
+    // Constructors
+    //
+
+    /**
+     * 
+     */
+    public XMLStringBuffer() {
+        this(DEFAULT_SIZE);
+    } // <init>()
+
+    /**
+     * 
+     * 
+     * @param size 
+     */
+    public XMLStringBuffer(int size) {
+        ch = new char[size];
+    } // <init>(int)
+
+    /** Constructs a string buffer from a char. */
+    public XMLStringBuffer(char c) {
+        this(1);
+        append(c);
+    } // <init>(char)
+
+    /** Constructs a string buffer from a String. */
+    public XMLStringBuffer(String s) {
+        this(s.length());
+        append(s);
+    } // <init>(String)
+
+    /** Constructs a string buffer from the specified character array. */
+    public XMLStringBuffer(char[] ch, int offset, int length) {
+        this(length);
+        append(ch, offset, length);
+    } // <init>(char[],int,int)
+
+    /** Constructs a string buffer from the specified XMLString. */
+    public XMLStringBuffer(XMLString s) {
+        this(s.length);
+        append(s);
+    } // <init>(XMLString)
+
+    //
+    // Public methods
+    //
+
+    /** Clears the string buffer. */
+    public void clear() {
+        offset = 0;
+        length = 0;
+    }
+
+    /**
+     * append
+     * 
+     * @param c 
+     */
+    public void append(char c) {
+        if (this.length + 1 > this.ch.length) {
+                    int newLength = this.ch.length*2;
+                    if (newLength < this.ch.length + DEFAULT_SIZE)
+                        newLength = this.ch.length + DEFAULT_SIZE;
+                    char[] newch = new char[newLength];
+                    System.arraycopy(this.ch, 0, newch, 0, this.length);
+                    this.ch = newch;
+        }
+        this.ch[this.length] = c;
+        this.length++;
+    } // append(char)
+
+    /**
+     * append
+     * 
+     * @param s 
+     */
+    public void append(String s) {
+        int length = s.length();
+        if (this.length + length > this.ch.length) {
+            int newLength = this.ch.length*2;
+            if (newLength < this.length + length + DEFAULT_SIZE)
+                newLength = this.ch.length + length + DEFAULT_SIZE;
+            char[] newch = new char[newLength];            
+            System.arraycopy(this.ch, 0, newch, 0, this.length);
+            this.ch = newch;
+        }
+        s.getChars(0, length, this.ch, this.length);
+        this.length += length;
+    } // append(String)
+
+    /**
+     * append
+     * 
+     * @param ch 
+     * @param offset 
+     * @param length 
+     */
+    public void append(char[] ch, int offset, int length) {
+        if (this.length + length > this.ch.length) {
+            char[] newch = new char[this.ch.length + length + DEFAULT_SIZE];
+            System.arraycopy(this.ch, 0, newch, 0, this.length);
+            this.ch = newch;
+        }
+        System.arraycopy(ch, offset, this.ch, this.length, length);
+        this.length += length;
+    } // append(char[],int,int)
+
+    /**
+     * append
+     * 
+     * @param s 
+     */
+    public void append(XMLString s) {
+        append(s.ch, s.offset, s.length);
+    } // append(XMLString)
+
+} // class XMLStringBuffer
diff --git a/servletapi/jsr152/.cvsignore b/servletapi/jsr152/.cvsignore
new file mode 100644
index 0000000..dbc1574
--- /dev/null
+++ b/servletapi/jsr152/.cvsignore
@@ -0,0 +1,4 @@
+build
+build.properties.sample
+dist
+lib
diff --git a/servletapi/jsr152/BUILDING.txt b/servletapi/jsr152/BUILDING.txt
new file mode 100644
index 0000000..fd46c59
--- /dev/null
+++ b/servletapi/jsr152/BUILDING.txt
@@ -0,0 +1,134 @@
+$Id$
+
+
+               Building The Java Servlet and JSP API Classes
+               =============================================
+
+This subproject contains the source code for the implementation classes of the
+Java Servlet and JSP APIs (packages javax.servlet, javax.servlet.http,
+javax.servlet.jsp, and javax.servlet.jsp.tagext).  In order to build these
+sources successfully, you must do the following:
+
+
+(1) Download and Install the Ant Binary Distribution
+
+NOTE:  These instructions assume that you are using the Ant 1.3 release.
+Procedures for Ant 1.4 and later versions should be similar, but have not
+been tested.
+
+* Download a binary distribution of Ant 1.3 from:
+
+    http://jakarta.apache.org/builds/jakarta-ant/release/v1.3/bin/
+
+  On a Windows platform, you will need:
+    jakarta-ant-1.3-bin.zip
+    jakarta-ant-1.3-optional.jar
+
+  On a Unix platform, you will need:
+    jakarta-ant-1.3-bin.tar.gz
+    jakarta-ant-1.3-optional.jar
+
+* Unpack the binary distribution into a convenient location so that the
+  Ant release resides in its own directory (conventionally named
+  "jakarta-ant-1.3").  For the purposes of the remainder of this document,
+  the symbolic name "${ant.home}" is used to refer to the full pathname of
+  the release directory.
+
+* Copy the file "jakarta-ant-1.3-optional.jar", downloaded above, into
+  the directory "${ant.home}/lib".  This makes available several Ant
+  extension commands that are commonly required when building Jakarta
+  based projects.
+
+* Modify the PATH environment variable to include directory
+  "${ant.home}/bin" in its list.  This makes the "ant" command line script
+  available, which will be used to actually perform the build.
+
+
+(2) Download and Install the JAXP/1.1 Reference Implementation (OPTIONAL)
+
+NOTE:  Although this step is not required to build this particular subproject,
+it is commonly required to build other Jakarta projects.  Hence, the steps
+required are documented here.
+
+* Download a binary distribution of JAXP 1.1 (Final Version) from:
+
+    http://java.sun.com/xml/download.html
+
+* Unpack the binary distribution into a convenient location so that the
+  JAXP/1.1 release resides in its own directory (conventionally named
+  "jaxp-1.1".  For the purposes of the remainder of this document, the
+  symbolic name "${jaxp.home}" is used to refer to the full pathname of
+  the release directory.
+
+* Make the JAR files of this distribution ("crimson.jar", "jaxp.jar", and
+  "xalan.jar") available for use by performing ONE of the following options:
+
+  - Remove the existing "jaxp.jar" and "parser.jar" files found in the
+    "${ant.home}/lib" directory, and copy these JAR files into the
+    "${ant.home}/lib" directory (prefered option).
+
+  - Add these files to your CLASSPATH environment variable.
+
+
+(3) Download and Install Subproject Source Code
+
+* Use Anonymous CVS (as described on the Jakarta web site at
+  <http://jakarta.apache.org/site/cvsindex.html>, or
+  download a source distribution from:
+
+    http://jakarta.apache.org/builds/jakarta-servletapi-4/nightly/src/
+
+  On a Windows platform, you will need:
+    jakarta-servletapi-4-src-YYYYMMDD.zip
+
+  On a Unix platform, you will need:
+    jakarta-servletapi-4-src-YYYYMMDD.zip
+
+  (Alternatively, you can download the Servlet API source distribution
+  from the same directory as you find a released version of Tomcat 4.
+  Such distributions will contain exactly the Servlet API classes used
+  to build the "servlet.jar" file inside that Tomcat distribution.)
+
+* Unpack the source distribution into a convenient location so that the
+  distribution resides in its own directory (conventionally named
+  "jakarta-servletapi-4").  For the purposes of the remainder of this document,
+  the symbolic name "${servletapi.source}" is used to refer to the full
+  pathname of the release directory.
+
+
+(4) Customize Build Properties For This Subproject
+
+Most Jakarta subprojects allow you to customize Ant properties (with default
+values defined in the "build.xml" file.  This is done by creating a text file
+named "build.properties" in the source distribution directory (for property
+definitions local to this subproject) and/or your user home directory (for
+property definitions shared across subprojects).
+
+The "jakarta-servletapi-4" subproject does not define any customizable
+build properties.
+
+
+(5) Build A Binary Distribution
+
+Open a command line shell, and issue the following commands:
+
+  cd ${servletapi.source}
+  ant -projecthelp
+
+If everything is installed correctly, you should see a list of the Ant
+"targets" that represent different commands you might wish to build.  By
+convention, the "dist" target creates a complete binary distribution.  To
+execute it, type the following commands:
+
+  cd ${servletapi.source}
+  ant dist
+
+This will create a complete binary distribution of the subproject (equivalent
+in structure to the corresponding binary distribution downloadable from the
+Jakarta web site), in the "${servletapi.source}/dist" directory.  It will have
+the contents described in the corresponding "README.txt" file.
+
+The file most commonly required by other projects will be the "servlet.jar"
+file, found in "${servletapi.source}/lib/servlet.jar".  Make a note of the
+full pathname to this file, because you will need it when customizing build
+properties for other Jakarta subprojects that depend on these classes.
diff --git a/servletapi/jsr152/LICENSE b/servletapi/jsr152/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/servletapi/jsr152/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/servletapi/jsr152/README.txt b/servletapi/jsr152/README.txt
new file mode 100644
index 0000000..e4b8328
--- /dev/null
+++ b/servletapi/jsr152/README.txt
@@ -0,0 +1,28 @@
+$Id$
+
+                      Java Servlet and JSP API Classes
+                      ================================
+
+This subproject contains the compiled code for the implementation classes of
+the Java Servlet and JSP APIs (packages javax.servlet, javax.servlet.http,
+javax.servlet.jsp, and javax.servlet.jsp.tagext).  It includes the following
+contents:
+
+
+  BUILDING.txt                Instructions for building from sources
+  LICENSE                     Apache Software License for this release
+  README.txt                  This document
+  docs/                       Documentation for this release
+      api/                    Javadocs for Servlet and JSP API classes
+  lib/                        Binary JAR files for this release
+      servlet.jar             Binary Servlet and JSP API classes
+  src/                        Sources for Servlet and JSP API classes
+
+In general, you will need to add the "servlet.jar" file (found in the "lib"
+subdirectory of this release) into the compilation class path for your
+projects that depend on these APIs.
+
+The compiled "servlet.jar" file included in this subproject is automatically
+included in binary distributions of Tomcat 4.0, so you need not download this
+subproject separately unless you wish to utilize the Javadocs, or peruse the
+source code to see how the API classes are implemented.
diff --git a/servletapi/jsr152/build.xml b/servletapi/jsr152/build.xml
new file mode 100644
index 0000000..98040b1
--- /dev/null
+++ b/servletapi/jsr152/build.xml
@@ -0,0 +1,277 @@
+<project name="JSP API Classes" default="compile" basedir=".">
+
+
+  <!-- =================== Environmental Properties ======================= -->
+
+  <!-- Load user property definition overrides -->
+  <property file="build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <!-- Establish property definition defaults -->
+  <property name="compile.debug"       value="true"/>
+  <property name="compile.deprecation" value="false"/>
+  <property name="compile.optimize"    value="true"/>
+  <property name="implementation.revision" value="public_draft"/>
+  <property name="jsp-api.build"    value="build"/>
+  <property name="jsp-api.dist"     value="dist"/>
+  <property name="servlet-api.jar"  value="../jsr154/dist/lib/servlet-api.jar"/>
+  <property name="jsp-api.jar" value="${jsp-api.dist}/lib/jsp-api.jar"/>
+
+  <path id="examples.classpath">
+    <pathelement location="${jsp-api.build}/classes"/>
+    <pathelement location="${servlet-api.jar}"/>
+    <pathelement location="${mail.jar}"/>
+  </path>
+
+
+  <!-- ===================== Prepare Directories ========================= -->
+  <target name="prepare">
+
+    <!-- "Build" Hierarchy -->
+    <mkdir dir="${jsp-api.build}"/>
+    <mkdir dir="${jsp-api.build}/classes"/>
+    <mkdir dir="${jsp-api.build}/docs"/>
+    <mkdir dir="${jsp-api.build}/docs/api"/>
+    <mkdir dir="${jsp-api.build}/examples"/>
+    <mkdir dir="${jsp-api.build}/ant"/>
+
+    <!-- "Dist" Hierarchy -->
+    <mkdir dir="${jsp-api.dist}"/>
+    <mkdir dir="${jsp-api.dist}/docs"/>
+    <mkdir dir="${jsp-api.dist}/docs/api"/>
+    <mkdir dir="${jsp-api.dist}/lib"/>
+    <mkdir dir="${jsp-api.dist}/src"/>
+    <mkdir dir="${jsp-api.dist}/examples"/>
+
+    <uptodate property="docs.uptodate" targetfile="${jsp-api.build}/docs/api/index.html">
+      <srcfiles dir="src/share" includes="**/*.java" />
+    </uptodate>
+  </target>
+
+
+  <!-- ======================= Static Files ============================== -->
+  <target name="static" depends="prepare">
+
+    <!-- "Dist" Hierarchy -->
+    <copy todir="${jsp-api.dist}">
+      <fileset dir="." includes="BUILDING.txt"/>
+      <fileset dir="." includes="LICENSE"/>
+      <fileset dir="." includes="README.txt"/>
+    </copy>
+
+  </target>
+
+
+  <!-- ======================== Compile Classes ========================== -->
+  <target name="compile" depends="static"
+   description="Compile API classes (Default)">
+
+    <!-- Java classes -->
+    <javac srcdir="src/share" destdir="${jsp-api.build}/classes"
+           debug="${compile.debug}" deprecation="${compile.deprecation}"
+        optimize="${compile.optimize}"
+       classpath="${servlet-api.jar}" />
+
+    <!-- Associated property files -->
+    <copy todir="${jsp-api.build}/classes">
+        <fileset dir="src/share">
+          <include name="**/*.properties"/>
+        </fileset>
+    </copy>
+
+    <!-- JSP resources -->
+    <copy todir="${jsp-api.build}/classes/javax/servlet/jsp/resources">
+        <fileset dir="src/share/dtd">
+          <include name="jsp*.dtd"/>
+          <include name="jsp*.xsd"/>
+          <include name="web-jsp*.dtd"/>
+          <include name="web-jsp*.xsd"/>
+        </fileset>
+    </copy>
+
+  </target>
+
+
+  <!-- ======================== Build JavaDoc =========================== -->
+  <target name="javadoc" depends="prepare" unless="docs.uptodate">
+
+    <javadoc packagenames="javax.servlet.jsp.*"
+             sourcepath="${basedir}/src/share"
+             classpath="${servlet-api.jar}"
+             destdir="${jsp-api.build}/docs/api"
+             use="true"
+             windowtitle="JavaServer Pages API Documentation"
+             doctitle="JavaServer Pages API Documentation"
+             bottom="Copyright &amp;copy; 1999-2002 The Apache Software Foundation.  All Rights Reserved."/>
+
+  </target>
+
+
+  <!-- ======================== Build Examples =========================== -->
+  <target name="ant" depends="prepare">
+    <javac  srcdir="src/ant" destdir="${jsp-api.build}/ant"
+            debug="${compile.debug}" deprecation="${compile.deprecation}"
+            optimize="${compile.optimize}" 
+            classpath="${ant.home}/lib/ant.jar" />
+    <taskdef    name="txt2html" classname="task.Txt2Html"
+                classpath="${jsp-api.build}/ant" />
+  </target>
+  
+  <target name="examples" depends="prepare,ant">
+
+    <copy todir="${jsp-api.build}/examples">
+      <fileset dir="examples">
+        <exclude name="build.*"/>
+      </fileset>
+    </copy>
+
+    <txt2html todir="${jsp-api.build}/examples/jsp2/simpletag">
+      <fileset dir="examples/WEB-INF/classes/jsp2/examples">
+        <include name="BookBean.java"/>
+      </fileset>
+      <fileset dir="examples/WEB-INF/classes/jsp2/examples/simpletag">
+        <include name="FindBookSimpleTag.java"/>
+        <include name="RepeatSimpleTag.java"/>
+        <include name="HelloWorldSimpleTag.java"/>
+      </fileset>
+      <fileset dir="examples/WEB-INF/classes/jsp2/examples/el">
+        <include name="Functions.java"/>
+      </fileset>
+    </txt2html>
+
+    <txt2html todir="${jsp-api.build}/examples/jsp2/jspattribute">
+      <fileset dir="examples/WEB-INF/classes/jsp2/examples">
+        <include name="FooBean.java"/>
+      </fileset>
+      <fileset dir="examples/WEB-INF/classes/jsp2/examples/simpletag">
+        <include name="ShuffleSimpleTag.java"/>
+        <include name="TileSimpleTag.java"/>
+        <include name="HelloWorldSimpleTag.java"/>
+      </fileset>
+    </txt2html>
+
+    <txt2html todir="${jsp-api.build}/examples/cal">
+      <fileset dir="examples/WEB-INF/classes/cal">
+        <include name="Entries.java"/>
+        <include name="Entry.java"/>
+        <include name="JspCalendar.java"/>
+        <include name="TableBean.java"/>
+      </fileset>
+    </txt2html>
+
+    <txt2html todir="${jsp-api.build}/examples/jsptoserv">
+      <fileset dir="examples/WEB-INF/classes">
+        <include name="servletToJsp.java"/>
+      </fileset>
+    </txt2html>
+
+    <txt2html todir="${jsp-api.build}/examples/jsp2/el">
+      <fileset dir="examples/WEB-INF/classes/jsp2/examples/el">
+        <include name="Functions.java"/>
+      </fileset>
+    </txt2html>
+
+    <txt2html todir="${jsp-api.build}/examples/jsp2/misc">
+      <fileset dir="examples/WEB-INF/classes/jsp2/examples/simpletag">
+        <include name="EchoAttributesTag.java"/>
+      </fileset>
+    </txt2html>
+
+    <txt2html todir="${jsp-api.build}/examples/jsp2/tagfiles">
+      <fileset dir="examples/WEB-INF/tags">
+        <include name="**/*.tag" />
+      </fileset>
+    </txt2html>
+
+    <txt2html todir="${jsp-api.build}/examples">
+      <fileset dir="examples">
+        <include name="**/*.jsp" />
+        <include name="**/*.jspx" />
+        <include name="**/*.jspf" />
+        <exclude name="error/errorpge.jsp"/>
+        <exclude name="forward/one.jsp"/>
+        <exclude name="include/foo.jsp"/>
+        <exclude name="jsptoserv/hello.jsp"/>
+        <exclude name="security/protected/error.jsp"/>
+        <exclude name="security/protected/index.jsp"/>
+        <exclude name="security/protected/login.jsp"/>
+        <exclude name="source.jsp"/>
+      </fileset>
+    </txt2html>
+
+    <javac   srcdir="examples/WEB-INF/classes" 
+             destdir="${jsp-api.build}/examples/WEB-INF/classes"
+             debug="${compile.debug}" deprecation="${compile.deprecation}"
+             optimize="${compile.optimize}"
+             excludes="**/CVS/**">
+      <classpath refid="examples.classpath" />
+    </javac>
+
+    <javac   srcdir="examples/plugin/applet" 
+             destdir="${jsp-api.build}/examples/plugin/applet"
+             debug="${compile.debug}" deprecation="${compile.deprecation}"
+             optimize="${compile.optimize}"
+             excludes="**/CVS/**">
+      <classpath refid="examples.classpath" />
+    </javac>
+
+    <jar   jarfile="${jsp-api.dist}/examples/examples.war"
+           basedir="${jsp-api.build}/examples" includes="**"/>
+
+  </target>
+
+
+  <!-- ===================== Distribution Files ========================= -->
+  <target name="jar" depends="compile"
+          description="Create jar">
+
+    <!-- Prepare Manifest -->
+    <copy tofile="${jsp-api.build}/manifest"
+            file="src/etc/manifest" overwrite="yes">
+      <filterset>
+        <filter token="implementation.revision"
+                value="${implementation.revision}"/>
+      </filterset>
+    </copy>
+
+    <!-- Create JAR file -->
+    <jar jarfile="${jsp-api.jar}"
+         basedir="${jsp-api.build}/classes"
+         manifest="${jsp-api.build}/manifest">
+      <include name="javax/servlet/jsp/**" />
+    </jar>
+
+  </target>
+
+  <target name="dist" depends="compile,examples,javadoc,jar"
+   description="Create binary distribution">
+
+    <!-- Copy Javadocs -->
+    <copy todir="${jsp-api.dist}/docs/api">
+        <fileset dir="${jsp-api.build}/docs/api"/>
+    </copy>
+
+    <!-- Copy API source files -->
+    <copy todir="${jsp-api.dist}/src">
+        <fileset dir="src/share"/>
+    </copy>
+
+  </target>
+
+
+  <!-- ====================== Clean Generated Files ===================== -->
+  <target name="clean"
+   description="Clean previous build results">
+
+    <delete dir="${jsp-api.build}"/>
+    <delete dir="${jsp-api.dist}"/>
+
+  </target>
+
+
+  <!-- ========================= All In One Build ======================= -->
+  <target name="all" depends="clean,dist"
+   description="Clean, compile, and dist"/>
+
+
+</project>
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/cal/Entries.java b/servletapi/jsr152/examples/WEB-INF/classes/cal/Entries.java
new file mode 100644
index 0000000..f762590
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/cal/Entries.java
@@ -0,0 +1,72 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package cal;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import javax.servlet.http.*;
+
+public class Entries {
+
+  private Hashtable entries;
+  private static final String[] time = {"8am", "9am", "10am", "11am", "12pm", 
+					"1pm", "2pm", "3pm", "4pm", "5pm", "6pm",
+					"7pm", "8pm" };
+  public static final int rows = 12;
+
+  public Entries () {   
+   entries = new Hashtable (rows);
+   for (int i=0; i < rows; i++) {
+     entries.put (time[i], new Entry(time[i]));
+   }
+  }
+
+  public int getRows () {
+    return rows;
+  }
+
+  public Entry getEntry (int index) {
+    return (Entry)this.entries.get(time[index]);
+  }
+
+  public int getIndex (String tm) {
+    for (int i=0; i<rows; i++)
+      if(tm.equals(time[i])) return i;
+    return -1;
+  }
+
+  public void processRequest (HttpServletRequest request, String tm) {
+    int index = getIndex (tm);
+    if (index >= 0) {
+      String descr = request.getParameter ("description");
+      ((Entry)entries.get(time[index])).setDescription (descr);
+    }
+  }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/cal/Entry.java b/servletapi/jsr152/examples/WEB-INF/classes/cal/Entry.java
new file mode 100644
index 0000000..035eda0
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/cal/Entry.java
@@ -0,0 +1,54 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package cal;
+
+public class Entry {
+
+  String hour;
+  String description;
+  String color;
+
+  public Entry (String hour) {
+    this.hour = hour;
+    this.description = "";
+
+  }
+
+  public String getHour () {
+    return this.hour;
+  }
+
+  public String getColor () {
+    if (description.equals("")) return "lightblue";
+    else return "red";
+  }
+
+  public String getDescription () {
+    if (description.equals("")) return "None";
+    else return this.description;
+  }
+
+  public void setDescription (String descr) {
+    description = descr;
+  }
+ 
+}
+
+
+
+
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/cal/JspCalendar.java b/servletapi/jsr152/examples/WEB-INF/classes/cal/JspCalendar.java
new file mode 100644
index 0000000..14063fd
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/cal/JspCalendar.java
@@ -0,0 +1,154 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package cal;
+
+import java.text.DateFormat;
+import java.util.*;
+
+public class JspCalendar {
+    Calendar  calendar = null;
+    Date currentDate;
+
+    public JspCalendar() {
+	calendar = Calendar.getInstance();
+	Date trialTime = new Date();
+	calendar.setTime(trialTime);
+    }
+
+
+    public int getYear() {
+	return calendar.get(Calendar.YEAR);
+    }
+    
+    public String getMonth() {
+	int m = getMonthInt();
+	String[] months = new String [] { "January", "February", "March",
+					"April", "May", "June",
+					"July", "August", "September",
+					"October", "November", "December" };
+	if (m > 12)
+	    return "Unknown to Man";
+	
+	return months[m - 1];
+
+    }
+
+    public String getDay() {
+	int x = getDayOfWeek();
+	String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday", 
+				      "Thursday", "Friday", "Saturday"};
+
+	if (x > 7)
+	    return "Unknown to Man";
+
+	return days[x - 1];
+
+    }
+    
+    public int getMonthInt() {
+	return 1 + calendar.get(Calendar.MONTH);
+    }
+
+    public String getDate() {
+	return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();	
+    }
+
+    public String getCurrentDate() {
+        Date dt = new Date ();
+	calendar.setTime (dt);
+	return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();
+
+    }
+
+    public String getNextDate() {
+        calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() + 1);
+	return getDate ();
+    }
+
+    public String getPrevDate() {
+        calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() - 1);
+	return getDate ();
+    }
+
+    public String getTime() {
+	return getHour() + ":" + getMinute() + ":" + getSecond();
+    }
+
+    public int getDayOfMonth() {
+	return calendar.get(Calendar.DAY_OF_MONTH);
+    }
+
+    public int getDayOfYear() {
+	return calendar.get(Calendar.DAY_OF_YEAR);
+    }
+
+    public int getWeekOfYear() {
+	return calendar.get(Calendar.WEEK_OF_YEAR);
+    }
+
+    public int getWeekOfMonth() {
+	return calendar.get(Calendar.WEEK_OF_MONTH);
+    }
+
+    public int getDayOfWeek() {
+	return calendar.get(Calendar.DAY_OF_WEEK);
+    }
+     
+    public int getHour() {
+	return calendar.get(Calendar.HOUR_OF_DAY);
+    }
+    
+    public int getMinute() {
+	return calendar.get(Calendar.MINUTE);
+    }
+
+
+    public int getSecond() {
+	return calendar.get(Calendar.SECOND);
+    }
+
+  
+    public int getEra() {
+	return calendar.get(Calendar.ERA);
+    }
+
+    public String getUSTimeZone() {
+	String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",
+				       "Mountain", "Central", "Eastern"};
+	
+	return zones[10 + getZoneOffset()];
+    }
+
+    public int getZoneOffset() {
+	return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);
+    }
+
+
+    public int getDSTOffset() {
+	return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);
+    }
+
+    
+    public int getAMPM() {
+	return calendar.get(Calendar.AM_PM);
+    }
+}
+
+
+
+
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/cal/TableBean.java b/servletapi/jsr152/examples/WEB-INF/classes/cal/TableBean.java
new file mode 100644
index 0000000..75cd599
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/cal/TableBean.java
@@ -0,0 +1,101 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package cal;
+
+import java.beans.*;
+import javax.servlet.http.*;
+import javax.servlet.*;
+import java.util.Hashtable;
+
+public class TableBean {
+
+  Hashtable table;
+  JspCalendar JspCal;
+  Entries entries;
+  String date;
+  String name = null;
+  String email = null;
+  boolean processError = false;
+
+  public TableBean () {
+    this.table = new Hashtable (10);
+    this.JspCal = new JspCalendar ();
+    this.date = JspCal.getCurrentDate ();
+  }
+
+  public void setName (String nm) {
+    this.name = nm;
+  }
+
+  public String getName () {
+    return this.name;
+  }
+  
+  public void setEmail (String mail) {
+    this.email = mail;
+  }
+
+  public String getEmail () {
+    return this.email;
+  }
+
+  public String getDate () {
+    return this.date;
+  }
+
+  public Entries getEntries () {
+    return this.entries;
+  }
+
+  public void processRequest (HttpServletRequest request) {
+
+    // Get the name and e-mail.
+    this.processError = false;
+    if (name == null || name.equals("")) setName(request.getParameter ("name"));  
+    if (email == null || email.equals("")) setEmail(request.getParameter ("email"));
+    if (name == null || email == null ||
+		name.equals("") || email.equals("")) {
+      this.processError = true;
+      return;
+    }
+
+    // Get the date.
+    String dateR = request.getParameter ("date");
+    if (dateR == null) date = JspCal.getCurrentDate ();
+    else if (dateR.equalsIgnoreCase("next")) date = JspCal.getNextDate ();
+    else if (dateR.equalsIgnoreCase("prev")) date = JspCal.getPrevDate ();
+
+    entries = (Entries) table.get (date);
+    if (entries == null) {
+      entries = new Entries ();
+      table.put (date, entries);
+    }
+
+    // If time is provided add the event.
+	String time = request.getParameter("time");
+    if (time != null) entries.processRequest (request, time);
+  }
+
+  public boolean getProcessError () {
+    return this.processError;
+  }
+}
+
+
+
+
+
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/checkbox/CheckTest.java b/servletapi/jsr152/examples/WEB-INF/classes/checkbox/CheckTest.java
new file mode 100644
index 0000000..ad6d2ed
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/checkbox/CheckTest.java
@@ -0,0 +1,30 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package checkbox;
+
+public class CheckTest {
+
+    String b[] = new String[] { "1", "2", "3", "4" };
+
+    public String[] getFruit() {
+	return b;
+    }
+
+    public void setFruit(String [] b) {
+	this.b = b;
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/colors/ColorGameBean.java b/servletapi/jsr152/examples/WEB-INF/classes/colors/ColorGameBean.java
new file mode 100644
index 0000000..1235b98
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/colors/ColorGameBean.java
@@ -0,0 +1,114 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package colors;
+
+import javax.servlet.http.*;
+
+public class ColorGameBean {
+
+    private String background = "yellow";
+    private String foreground = "red";
+    private String color1 = foreground;
+    private String color2 = background;
+    private String hint = "no";
+    private int attempts = 0;
+	private int intval = 0;
+    private boolean tookHints = false;
+
+    public void processRequest(HttpServletRequest request) {
+
+	// background = "yellow";
+	// foreground = "red";
+
+	if (! color1.equals(foreground)) {
+	    if (color1.equalsIgnoreCase("black") ||
+			color1.equalsIgnoreCase("cyan")) {
+			background = color1;
+		}
+	}
+
+	if (! color2.equals(background)) {
+	    if (color2.equalsIgnoreCase("black") ||
+			color2.equalsIgnoreCase("cyan")) {
+			foreground = color2;
+	    }
+	}
+
+	attempts++;
+    }
+
+    public void setColor2(String x) {
+	color2 = x;
+    }
+
+    public void setColor1(String x) {
+	color1 = x;
+    }
+
+    public void setAction(String x) {
+	if (!tookHints)
+	    tookHints = x.equalsIgnoreCase("Hint");
+	hint = x;
+    }
+
+    public String getColor2() {
+	 return background;
+    }
+
+    public String getColor1() {
+	 return foreground;
+    }
+
+    public int getAttempts() {
+	return attempts;
+    }
+
+    public boolean getHint() {
+	return hint.equalsIgnoreCase("Hint");
+    }
+
+    public boolean getSuccess() {
+	if (background.equalsIgnoreCase("black") ||
+	    background.equalsIgnoreCase("cyan")) {
+	
+	    if (foreground.equalsIgnoreCase("black") ||
+		foreground.equalsIgnoreCase("cyan"))
+		return true;
+	    else
+		return false;
+	}
+
+	return false;
+    }
+
+    public boolean getHintTaken() {
+	return tookHints;
+    }
+
+    public void reset() {
+	foreground = "red";
+	background = "yellow";
+    }
+
+    public void setIntval(int value) {
+	intval = value;
+	}
+
+    public int getIntval() {
+	return intval;
+	}
+}
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java b/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
new file mode 100644
index 0000000..ef7e1ff
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
@@ -0,0 +1,219 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package compressionFilters;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Implementation of <code>javax.servlet.Filter</code> used to compress
+ * the ServletResponse if it is bigger than a threshold.
+ *
+ * @author Amy Roh
+ * @author Dmitri Valdin
+ * @version $Revision$, $Date$
+ */
+
+public class CompressionFilter implements Filter{
+
+    /**
+     * The filter configuration object we are associated with.  If this value
+     * is null, this filter instance is not currently configured.
+     */
+    private FilterConfig config = null;
+
+    /**
+     * Minimal reasonable threshold
+     */
+    private int minThreshold = 128;
+
+
+    /**
+     * The threshold number to compress
+     */
+    protected int compressionThreshold;
+
+    /**
+     * Debug level for this filter
+     */
+    private int debug = 0;
+
+    /**
+     * Place this filter into service.
+     *
+     * @param filterConfig The filter configuration object
+     */
+
+    public void init(FilterConfig filterConfig) {
+
+        config = filterConfig;
+        if (filterConfig != null) {
+            String value = filterConfig.getInitParameter("debug");
+            if (value!=null) {
+                debug = Integer.parseInt(value);
+            } else {
+                debug = 0;
+            }
+            String str = filterConfig.getInitParameter("compressionThreshold");
+            if (str!=null) {
+                compressionThreshold = Integer.parseInt(str);
+                if (compressionThreshold != 0 && compressionThreshold < minThreshold) {
+                    if (debug > 0) {
+                        System.out.println("compressionThreshold should be either 0 - no compression or >= " + minThreshold);
+                        System.out.println("compressionThreshold set to " + minThreshold);
+                    }
+                    compressionThreshold = minThreshold;
+                }
+            } else {
+                compressionThreshold = 0;
+            }
+
+        } else {
+            compressionThreshold = 0;
+        }
+
+    }
+
+    /**
+    * Take this filter out of service.
+    */
+    public void destroy() {
+
+        this.config = null;
+
+    }
+
+    /**
+     * The <code>doFilter</code> method of the Filter is called by the container
+     * each time a request/response pair is passed through the chain due
+     * to a client request for a resource at the end of the chain.
+     * The FilterChain passed into this method allows the Filter to pass on the
+     * request and response to the next entity in the chain.<p>
+     * This method first examines the request to check whether the client support
+     * compression. <br>
+     * It simply just pass the request and response if there is no support for
+     * compression.<br>
+     * If the compression support is available, it creates a
+     * CompressionServletResponseWrapper object which compresses the content and
+     * modifies the header if the content length is big enough.
+     * It then invokes the next entity in the chain using the FilterChain object
+     * (<code>chain.doFilter()</code>), <br>
+     **/
+
+    public void doFilter ( ServletRequest request, ServletResponse response,
+                        FilterChain chain ) throws IOException, ServletException {
+
+        if (debug > 0) {
+            System.out.println("@doFilter");
+        }
+
+        if (compressionThreshold == 0) {
+            if (debug > 0) {
+                System.out.println("doFilter gets called, but compressionTreshold is set to 0 - no compression");
+            }
+            chain.doFilter(request, response);
+            return;
+        }
+
+        boolean supportCompression = false;
+        if (request instanceof HttpServletRequest) {
+            if (debug > 1) {
+                System.out.println("requestURI = " + ((HttpServletRequest)request).getRequestURI());
+            }
+
+            // Are we allowed to compress ?
+            String s = (String) ((HttpServletRequest)request).getParameter("gzip");
+            if ("false".equals(s)) {
+                if (debug > 0) {
+                    System.out.println("got parameter gzip=false --> don't compress, just chain filter");
+                }
+                chain.doFilter(request, response);
+                return;
+            }
+
+            Enumeration e =
+                ((HttpServletRequest)request).getHeaders("Accept-Encoding");
+            while (e.hasMoreElements()) {
+                String name = (String)e.nextElement();
+                if (name.indexOf("gzip") != -1) {
+                    if (debug > 0) {
+                        System.out.println("supports compression");
+                    }
+                    supportCompression = true;
+                } else {
+                    if (debug > 0) {
+                        System.out.println("no support for compresion");
+                    }
+                }
+            }
+        }
+
+        if (!supportCompression) {
+            if (debug > 0) {
+                System.out.println("doFilter gets called wo compression");
+            }
+            chain.doFilter(request, response);
+            return;
+        } else {
+            if (response instanceof HttpServletResponse) {
+                CompressionServletResponseWrapper wrappedResponse =
+                    new CompressionServletResponseWrapper((HttpServletResponse)response);
+                wrappedResponse.setDebugLevel(debug);
+                wrappedResponse.setCompressionThreshold(compressionThreshold);
+                if (debug > 0) {
+                    System.out.println("doFilter gets called with compression");
+                }
+                try {
+                    chain.doFilter(request, wrappedResponse);
+                } finally {
+                    wrappedResponse.finishResponse();
+                }
+                return;
+            }
+        }
+    }
+
+    /**
+     * Set filter config
+     * This function is equivalent to init. Required by Weblogic 6.1
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void setFilterConfig(FilterConfig filterConfig) {
+        init(filterConfig);
+    }
+
+    /**
+     * Return filter config
+     * Required by Weblogic 6.1
+     */
+    public FilterConfig getFilterConfig() {
+        return config;
+    }
+
+}
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java b/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java
new file mode 100644
index 0000000..dce2b67
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java
@@ -0,0 +1,57 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package compressionFilters;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Very Simple test servlet to test compression filter
+ * @author Amy Roh
+ * @version $Revision$, $Date$
+ */
+
+public class CompressionFilterTestServlet extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws ServletException, IOException {
+
+        ServletOutputStream out = response.getOutputStream();
+        response.setContentType("text/plain");
+
+        Enumeration e = ((HttpServletRequest)request).getHeaders("Accept-Encoding");
+        while (e.hasMoreElements()) {
+            String name = (String)e.nextElement();
+            out.println(name);
+            if (name.indexOf("gzip") != -1) {
+                out.println("gzip supported -- able to compress");
+            }
+            else {
+                out.println("gzip not supported");
+            }
+        }
+
+
+        out.println("Compression Filter Test Servlet");
+        out.close();
+    }
+
+}
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java b/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
new file mode 100644
index 0000000..dc7fb18
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
@@ -0,0 +1,318 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package compressionFilters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.GZIPOutputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Implementation of <b>ServletOutputStream</b> that works with
+ * the CompressionServletResponseWrapper implementation.
+ *
+ * @author Amy Roh
+ * @author Dmitri Valdin
+ * @version $Revision$, $Date$
+ */
+
+public class CompressionResponseStream
+    extends ServletOutputStream {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a servlet output stream associated with the specified Response.
+     *
+     * @param response The associated response
+     */
+    public CompressionResponseStream(HttpServletResponse response) throws IOException{
+
+        super();
+        closed = false;
+        this.response = response;
+        this.output = response.getOutputStream();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The threshold number which decides to compress or not.
+     * Users can configure in web.xml to set it to fit their needs.
+     */
+    protected int compressionThreshold = 0;
+
+    /**
+     * Debug level
+     */
+    private int debug = 0;
+
+    /**
+     * The buffer through which all of our output bytes are passed.
+     */
+    protected byte[] buffer = null;
+
+    /**
+     * The number of data bytes currently in the buffer.
+     */
+    protected int bufferCount = 0;
+
+    /**
+     * The underlying gzip output stream to which we should write data.
+     */
+    protected GZIPOutputStream gzipstream = null;
+
+    /**
+     * Has this stream been closed?
+     */
+    protected boolean closed = false;
+
+    /**
+     * The content length past which we will not write, or -1 if there is
+     * no defined content length.
+     */
+    protected int length = -1;
+
+    /**
+     * The response with which this servlet output stream is associated.
+     */
+    protected HttpServletResponse response = null;
+
+    /**
+     * The underlying servket output stream to which we should write data.
+     */
+    protected ServletOutputStream output = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Set debug level
+     */
+    public void setDebugLevel(int debug) {
+        this.debug = debug;
+    }
+
+
+    /**
+     * Set the compressionThreshold number and create buffer for this size
+     */
+    protected void setBuffer(int threshold) {
+        compressionThreshold = threshold;
+        buffer = new byte[compressionThreshold];
+        if (debug > 1) {
+            System.out.println("buffer is set to "+compressionThreshold);
+        }
+    }
+
+    /**
+     * Close this output stream, causing any buffered data to be flushed and
+     * any further output data to throw an IOException.
+     */
+    public void close() throws IOException {
+
+        if (debug > 1) {
+            System.out.println("close() @ CompressionResponseStream");
+        }
+        if (closed)
+            throw new IOException("This output stream has already been closed");
+
+        if (gzipstream != null) {
+            flushToGZip();
+            gzipstream.close();
+            gzipstream = null;
+        } else {
+            if (bufferCount > 0) {
+                if (debug > 2) {
+                    System.out.print("output.write(");
+                    System.out.write(buffer, 0, bufferCount);
+                    System.out.println(")");
+                }
+                output.write(buffer, 0, bufferCount);
+                bufferCount = 0;
+            }
+        }
+
+        output.close();
+        closed = true;
+
+    }
+
+
+    /**
+     * Flush any buffered data for this output stream, which also causes the
+     * response to be committed.
+     */
+    public void flush() throws IOException {
+
+        if (debug > 1) {
+            System.out.println("flush() @ CompressionResponseStream");
+        }
+        if (closed) {
+            throw new IOException("Cannot flush a closed output stream");
+        }
+
+        if (gzipstream != null) {
+            gzipstream.flush();
+        }
+
+    }
+
+    public void flushToGZip() throws IOException {
+
+        if (debug > 1) {
+            System.out.println("flushToGZip() @ CompressionResponseStream");
+        }
+        if (bufferCount > 0) {
+            if (debug > 1) {
+                System.out.println("flushing out to GZipStream, bufferCount = " + bufferCount);
+            }
+            writeToGZip(buffer, 0, bufferCount);
+            bufferCount = 0;
+        }
+
+    }
+
+    /**
+     * Write the specified byte to our output stream.
+     *
+     * @param b The byte to be written
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void write(int b) throws IOException {
+
+        if (debug > 1) {
+            System.out.println("write "+b+" in CompressionResponseStream ");
+        }
+        if (closed)
+            throw new IOException("Cannot write to a closed output stream");
+
+        if (bufferCount >= buffer.length) {
+            flushToGZip();
+        }
+
+        buffer[bufferCount++] = (byte) b;
+
+    }
+
+
+    /**
+     * Write <code>b.length</code> bytes from the specified byte array
+     * to our output stream.
+     *
+     * @param b The byte array to be written
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void write(byte b[]) throws IOException {
+
+        write(b, 0, b.length);
+
+    }
+
+
+    /**
+     * Write <code>len</code> bytes from the specified byte array, starting
+     * at the specified offset, to our output stream.
+     *
+     * @param b The byte array containing the bytes to be written
+     * @param off Zero-relative starting offset of the bytes to be written
+     * @param len The number of bytes to be written
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void write(byte b[], int off, int len) throws IOException {
+
+        if (debug > 1) {
+            System.out.println("write, bufferCount = " + bufferCount + " len = " + len + " off = " + off);
+        }
+        if (debug > 2) {
+            System.out.print("write(");
+            System.out.write(b, off, len);
+            System.out.println(")");
+        }
+
+        if (closed)
+            throw new IOException("Cannot write to a closed output stream");
+
+        if (len == 0)
+            return;
+
+        // Can we write into buffer ?
+        if (len <= (buffer.length - bufferCount)) {
+            System.arraycopy(b, off, buffer, bufferCount, len);
+            bufferCount += len;
+            return;
+        }
+
+        // There is not enough space in buffer. Flush it ...
+        flushToGZip();
+
+        // ... and try again. Note, that bufferCount = 0 here !
+        if (len <= (buffer.length - bufferCount)) {
+            System.arraycopy(b, off, buffer, bufferCount, len);
+            bufferCount += len;
+            return;
+        }
+
+        // write direct to gzip
+        writeToGZip(b, off, len);
+    }
+
+    public void writeToGZip(byte b[], int off, int len) throws IOException {
+
+        if (debug > 1) {
+            System.out.println("writeToGZip, len = " + len);
+        }
+        if (debug > 2) {
+            System.out.print("writeToGZip(");
+            System.out.write(b, off, len);
+            System.out.println(")");
+        }
+        if (gzipstream == null) {
+            if (debug > 1) {
+                System.out.println("new GZIPOutputStream");
+            }
+            response.addHeader("Content-Encoding", "gzip");
+            gzipstream = new GZIPOutputStream(output);
+        }
+        gzipstream.write(b, off, len);
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Has this response stream been closed?
+     */
+    public boolean closed() {
+
+        return (this.closed);
+
+    }
+
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java b/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
new file mode 100644
index 0000000..edaca65
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
@@ -0,0 +1,277 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package compressionFilters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Locale;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * Implementation of <b>HttpServletResponseWrapper</b> that works with
+ * the CompressionServletResponseStream implementation..
+ *
+ * @author Amy Roh
+ * @author Dmitri Valdin
+ * @version $Revision$, $Date$
+ */
+
+public class CompressionServletResponseWrapper extends HttpServletResponseWrapper {
+
+    // ----------------------------------------------------- Constructor
+
+    /**
+     * Calls the parent constructor which creates a ServletResponse adaptor
+     * wrapping the given response object.
+     */
+
+    public CompressionServletResponseWrapper(HttpServletResponse response) {
+        super(response);
+        origResponse = response;
+        if (debug > 1) {
+            System.out.println("CompressionServletResponseWrapper constructor gets called");
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Original response
+     */
+
+    protected HttpServletResponse origResponse = null;
+
+    /**
+     * Descriptive information about this Response implementation.
+     */
+
+    protected static final String info = "CompressionServletResponseWrapper";
+
+    /**
+     * The ServletOutputStream that has been returned by
+     * <code>getOutputStream()</code>, if any.
+     */
+
+    protected ServletOutputStream stream = null;
+
+
+    /**
+     * The PrintWriter that has been returned by
+     * <code>getWriter()</code>, if any.
+     */
+
+    protected PrintWriter writer = null;
+
+    /**
+     * The threshold number to compress
+     */
+    protected int threshold = 0;
+
+    /**
+     * Debug level
+     */
+    private int debug = 0;
+
+    /**
+     * Content type
+     */
+    protected String contentType = null;
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Set content type
+     */
+    public void setContentType(String contentType) {
+        if (debug > 1) {
+            System.out.println("setContentType to "+contentType);
+        }
+        this.contentType = contentType;
+        origResponse.setContentType(contentType);
+    }
+
+
+    /**
+     * Set threshold number
+     */
+    public void setCompressionThreshold(int threshold) {
+        if (debug > 1) {
+            System.out.println("setCompressionThreshold to " + threshold);
+        }
+        this.threshold = threshold;
+    }
+
+
+    /**
+     * Set debug level
+     */
+    public void setDebugLevel(int debug) {
+        this.debug = debug;
+    }
+
+
+    /**
+     * Create and return a ServletOutputStream to write the content
+     * associated with this Response.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletOutputStream createOutputStream() throws IOException {
+        if (debug > 1) {
+            System.out.println("createOutputStream gets called");
+        }
+
+        CompressionResponseStream stream = new CompressionResponseStream(origResponse);
+        stream.setDebugLevel(debug);
+        stream.setBuffer(threshold);
+
+        return stream;
+
+    }
+
+
+    /**
+     * Finish a response.
+     */
+    public void finishResponse() {
+        try {
+            if (writer != null) {
+                writer.close();
+            } else {
+                if (stream != null)
+                    stream.close();
+            }
+        } catch (IOException e) {
+        }
+    }
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    /**
+     * Flush the buffer and commit this response.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void flushBuffer() throws IOException {
+        if (debug > 1) {
+            System.out.println("flush buffer @ CompressionServletResponseWrapper");
+        }
+        ((CompressionResponseStream)stream).flush();
+
+    }
+
+    /**
+     * Return the servlet output stream associated with this Response.
+     *
+     * @exception IllegalStateException if <code>getWriter</code> has
+     *  already been called for this response
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletOutputStream getOutputStream() throws IOException {
+
+        if (writer != null)
+            throw new IllegalStateException("getWriter() has already been called for this response");
+
+        if (stream == null)
+            stream = createOutputStream();
+        if (debug > 1) {
+            System.out.println("stream is set to "+stream+" in getOutputStream");
+        }
+
+        return (stream);
+
+    }
+
+    /**
+     * Return the writer associated with this Response.
+     *
+     * @exception IllegalStateException if <code>getOutputStream</code> has
+     *  already been called for this response
+     * @exception IOException if an input/output error occurs
+     */
+    public PrintWriter getWriter() throws IOException {
+
+        if (writer != null)
+            return (writer);
+
+        if (stream != null)
+            throw new IllegalStateException("getOutputStream() has already been called for this response");
+
+        stream = createOutputStream();
+        if (debug > 1) {
+            System.out.println("stream is set to "+stream+" in getWriter");
+        }
+        //String charset = getCharsetFromContentType(contentType);
+        String charEnc = origResponse.getCharacterEncoding();
+        if (debug > 1) {
+            System.out.println("character encoding is " + charEnc);
+        }
+        // HttpServletResponse.getCharacterEncoding() shouldn't return null
+        // according the spec, so feel free to remove that "if"
+        if (charEnc != null) {
+            writer = new PrintWriter(new OutputStreamWriter(stream, charEnc));
+        } else {
+            writer = new PrintWriter(stream);
+        }
+        
+        return (writer);
+
+    }
+
+
+    public void setContentLength(int length) {
+    }
+
+
+    /**
+     * Returns character from content type. This method was taken from tomcat.
+     * @author rajo
+     */
+    private static String getCharsetFromContentType(String type) {
+
+        if (type == null) {
+            return null;
+        }
+        int semi = type.indexOf(";");
+        if (semi == -1) {
+            return null;
+        }
+        String afterSemi = type.substring(semi + 1);
+        int charsetLocation = afterSemi.indexOf("charset=");
+        if(charsetLocation == -1) {
+            return null;
+        } else {
+            String afterCharset = afterSemi.substring(charsetLocation + 8);
+            String encoding = afterCharset.trim();
+            return encoding;
+        }
+    }
+
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/dates/JspCalendar.java b/servletapi/jsr152/examples/WEB-INF/classes/dates/JspCalendar.java
new file mode 100644
index 0000000..b019475
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/dates/JspCalendar.java
@@ -0,0 +1,152 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package dates;
+
+import java.text.DateFormat;
+import java.util.*;
+
+public class JspCalendar {
+    Calendar  calendar = null;
+
+    public JspCalendar() {
+	calendar = Calendar.getInstance();
+	Date trialTime = new Date();
+	calendar.setTime(trialTime);
+    }
+
+    public int getYear() {
+	return calendar.get(Calendar.YEAR);
+    }
+    
+    public String getMonth() {
+	int m = getMonthInt();
+	String[] months = new String [] { "January", "February", "March",
+					"April", "May", "June",
+					"July", "August", "September",
+					"October", "November", "December" };
+	if (m > 12)
+	    return "Unknown to Man";
+	
+	return months[m - 1];
+
+    }
+
+    public String getDay() {
+	int x = getDayOfWeek();
+	String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday", 
+				      "Thursday", "Friday", "Saturday"};
+
+	if (x > 7)
+	    return "Unknown to Man";
+
+	return days[x - 1];
+
+    }
+    
+    public int getMonthInt() {
+	return 1 + calendar.get(Calendar.MONTH);
+    }
+
+    public String getDate() {
+	return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();
+
+    }
+
+    public String getTime() {
+	return getHour() + ":" + getMinute() + ":" + getSecond();
+    }
+
+    public int getDayOfMonth() {
+	return calendar.get(Calendar.DAY_OF_MONTH);
+    }
+
+    public int getDayOfYear() {
+	return calendar.get(Calendar.DAY_OF_YEAR);
+    }
+
+    public int getWeekOfYear() {
+	return calendar.get(Calendar.WEEK_OF_YEAR);
+    }
+
+    public int getWeekOfMonth() {
+	return calendar.get(Calendar.WEEK_OF_MONTH);
+    }
+
+    public int getDayOfWeek() {
+	return calendar.get(Calendar.DAY_OF_WEEK);
+    }
+     
+    public int getHour() {
+	return calendar.get(Calendar.HOUR_OF_DAY);
+    }
+    
+    public int getMinute() {
+	return calendar.get(Calendar.MINUTE);
+    }
+
+
+    public int getSecond() {
+	return calendar.get(Calendar.SECOND);
+    }
+
+    public static void main(String args[]) {
+	JspCalendar db = new JspCalendar();
+	p("date: " + db.getDayOfMonth());
+	p("year: " + db.getYear());
+	p("month: " + db.getMonth());
+	p("time: " + db.getTime());
+	p("date: " + db.getDate());
+	p("Day: " + db.getDay());
+	p("DayOfYear: " + db.getDayOfYear());
+	p("WeekOfYear: " + db.getWeekOfYear());
+	p("era: " + db.getEra());
+	p("ampm: " + db.getAMPM());
+	p("DST: " + db.getDSTOffset());
+	p("ZONE Offset: " + db.getZoneOffset());
+	p("TIMEZONE: " + db.getUSTimeZone());
+    }
+
+    private static void p(String x) {
+	System.out.println(x);
+    }
+
+
+    public int getEra() {
+	return calendar.get(Calendar.ERA);
+    }
+
+    public String getUSTimeZone() {
+	String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",
+				       "Mountain", "Central", "Eastern"};
+	
+	return zones[10 + getZoneOffset()];
+    }
+
+    public int getZoneOffset() {
+	return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);
+    }
+
+
+    public int getDSTOffset() {
+	return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);
+    }
+
+    
+    public int getAMPM() {
+	return calendar.get(Calendar.AM_PM);
+    }
+}
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/error/Smart.java b/servletapi/jsr152/examples/WEB-INF/classes/error/Smart.java
new file mode 100644
index 0000000..e5158e7
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/error/Smart.java
@@ -0,0 +1,34 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package error;
+
+import java.io.*;
+import java.lang.*;
+
+public class Smart {
+
+  String name = "JSP";
+
+  public String getName () {
+	return name;
+  }	
+
+  public void setName (String name) {
+	this.name = name;
+  }	
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/examples/ExampleTagBase.java b/servletapi/jsr152/examples/WEB-INF/classes/examples/ExampleTagBase.java
new file mode 100644
index 0000000..4b64e95
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/examples/ExampleTagBase.java
@@ -0,0 +1,66 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package examples;
+
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+public abstract class ExampleTagBase extends BodyTagSupport {
+
+    public void setParent(Tag parent) {
+        this.parent = parent;
+    }
+
+    public void setBodyContent(BodyContent bodyOut) {
+        this.bodyOut = bodyOut;
+    }
+
+    public void setPageContext(PageContext pageContext) {
+        this.pageContext = pageContext;
+    }
+
+    public Tag getParent() {
+        return this.parent;
+    }
+    
+    public int doStartTag() throws JspException {
+        return SKIP_BODY;
+    }
+
+    public int doEndTag() throws JspException {
+        return EVAL_PAGE;
+    }
+    
+
+    // Default implementations for BodyTag methods as well
+    // just in case a tag decides to implement BodyTag.
+    public void doInitBody() throws JspException {
+    }
+
+    public int doAfterBody() throws JspException {
+        return SKIP_BODY;
+    }
+
+    public void release() {
+        bodyOut = null;
+        pageContext = null;
+        parent = null;
+    }
+    
+    protected BodyContent bodyOut;
+    protected PageContext pageContext;
+    protected Tag parent;
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/examples/FooTag.java b/servletapi/jsr152/examples/WEB-INF/classes/examples/FooTag.java
new file mode 100644
index 0000000..e03dc19
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/examples/FooTag.java
@@ -0,0 +1,82 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package examples;
+
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+import java.util.Hashtable;
+import java.io.Writer;
+import java.io.IOException;
+
+/**
+ * Example1: the simplest tag
+ * Collect attributes and call into some actions
+ *
+ * <foo att1="..." att2="...." att3="...." />
+ */
+
+public class FooTag 
+    extends ExampleTagBase 
+{
+    private String atts[] = new String[3];
+    int i = 0;
+    
+    private final void setAtt(int index, String value) {
+        atts[index] = value;
+    }
+    
+    public void setAtt1(String value) {
+        setAtt(0, value);
+    }
+    
+    public void setAtt2(String value) {
+        setAtt(1, value);
+    }
+
+    public void setAtt3(String value) {
+        setAtt(2, value);
+    }
+    
+    /**
+     * Process start tag
+     *
+     * @return EVAL_BODY_INCLUDE
+     */
+    public int doStartTag() throws JspException {
+        i = 0;
+	return EVAL_BODY_TAG;
+    }
+
+    public void doInitBody() throws JspException {
+        pageContext.setAttribute("member", atts[i]);
+        i++;
+    }
+    
+    public int doAfterBody() throws JspException {
+        try {
+            if (i == 3) {
+                bodyOut.writeOut(bodyOut.getEnclosingWriter());
+                return SKIP_BODY;
+            } else
+                pageContext.setAttribute("member", atts[i]);
+            i++;
+            return EVAL_BODY_TAG;
+        } catch (IOException ex) {
+            throw new JspTagException(ex.toString());
+        }
+    }
+}
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/examples/FooTagExtraInfo.java b/servletapi/jsr152/examples/WEB-INF/classes/examples/FooTagExtraInfo.java
new file mode 100644
index 0000000..c32c28d
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/examples/FooTagExtraInfo.java
@@ -0,0 +1,32 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package examples;
+
+import javax.servlet.jsp.tagext.*;
+
+public class FooTagExtraInfo extends TagExtraInfo {
+    public VariableInfo[] getVariableInfo(TagData data) {
+        return new VariableInfo[] 
+            {
+                new VariableInfo("member",
+                                 "String",
+                                 true,
+                                 VariableInfo.NESTED)
+            };
+    }
+}
+
+        
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/examples/LogTag.java b/servletapi/jsr152/examples/WEB-INF/classes/examples/LogTag.java
new file mode 100644
index 0000000..392a3d5
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/examples/LogTag.java
@@ -0,0 +1,60 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package examples;
+
+
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+import java.io.IOException;
+
+/**
+ * Log the contents of the body. Could be used to handle errors etc. 
+ */
+public class LogTag 
+    extends ExampleTagBase
+{
+    boolean toBrowser = false;
+    
+    public void setToBrowser(String value) {
+        if (value == null)
+            toBrowser = false;
+        else if (value.equalsIgnoreCase("true"))
+            toBrowser = true;
+        else
+            toBrowser = false;
+    }
+
+    public int doStartTag() throws JspException {
+        return EVAL_BODY_TAG;
+    }
+    
+    public int doAfterBody() throws JspException {
+        try {
+            String s = bodyOut.getString();
+            System.err.println(s);
+            if (toBrowser)
+                bodyOut.writeOut(bodyOut.getEnclosingWriter());
+            return SKIP_BODY;
+        } catch (IOException ex) {
+            throw new JspTagException(ex.toString());
+        }
+    }
+}
+
+    
+        
+    
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/examples/ShowSource.java b/servletapi/jsr152/examples/WEB-INF/classes/examples/ShowSource.java
new file mode 100644
index 0000000..6daf0cf
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/examples/ShowSource.java
@@ -0,0 +1,72 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package examples;
+
+
+import javax.servlet.*;
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+import java.io.*;
+
+/**
+ * Display the sources of the JSP file.
+ */
+public class ShowSource
+    extends TagSupport
+{
+    String jspFile;
+    
+    public void setJspFile(String jspFile) {
+        this.jspFile = jspFile;
+    }
+
+    public int doEndTag() throws JspException {
+	if ((jspFile.indexOf( ".." ) >= 0) ||
+            (jspFile.toUpperCase().indexOf("/WEB-INF/") != 0) ||
+            (jspFile.toUpperCase().indexOf("/META-INF/") != 0))
+	    throw new JspTagException("Invalid JSP file " + jspFile);
+
+        InputStream in
+            = pageContext.getServletContext().getResourceAsStream(jspFile);
+
+        if (in == null)
+            throw new JspTagException("Unable to find JSP file: "+jspFile);
+
+        InputStreamReader reader = new InputStreamReader(in);
+	JspWriter out = pageContext.getOut();
+
+
+        try {
+            out.println("<body>");
+            out.println("<pre>");
+            for(int ch = in.read(); ch != -1; ch = in.read())
+                if (ch == '<')
+                    out.print("&lt;");
+                else
+                    out.print((char) ch);
+            out.println("</pre>");
+            out.println("</body>");
+        } catch (IOException ex) {
+            throw new JspTagException("IOException: "+ex.toString());
+        }
+        return super.doEndTag();
+    }
+}
+
+    
+        
+    
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/filters/ExampleFilter.java b/servletapi/jsr152/examples/WEB-INF/classes/filters/ExampleFilter.java
new file mode 100644
index 0000000..a3784a8
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/filters/ExampleFilter.java
@@ -0,0 +1,140 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package filters;
+
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Example filter that can be attached to either an individual servlet
+ * or to a URL pattern.  This filter performs the following functions:
+ * <ul>
+ * <li>Attaches itself as a request attribute, under the attribute name
+ *     defined by the value of the <code>attribute</code> initialization
+ *     parameter.</li>
+ * <li>Calculates the number of milliseconds required to perform the
+ *     servlet processing required by this request, including any
+ *     subsequently defined filters, and logs the result to the servlet
+ *     context log for this application.
+ * </ul>
+ *
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class ExampleFilter implements Filter {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The request attribute name under which we store a reference to ourself.
+     */
+    private String attribute = null;
+
+
+    /**
+     * The filter configuration object we are associated with.  If this value
+     * is null, this filter instance is not currently configured.
+     */
+    private FilterConfig filterConfig = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Take this filter out of service.
+     */
+    public void destroy() {
+
+        this.attribute = null;
+        this.filterConfig = null;
+
+    }
+
+
+    /**
+     * Time the processing that is performed by all subsequent filters in the
+     * current filter stack, including the ultimately invoked servlet.
+     *
+     * @param request The servlet request we are processing
+     * @param result The servlet response we are creating
+     * @param chain The filter chain we are processing
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain)
+	throws IOException, ServletException {
+
+	// Store ourselves as a request attribute (if requested)
+	if (attribute != null)
+	    request.setAttribute(attribute, this);
+
+	// Time and log the subsequent processing
+	long startTime = System.currentTimeMillis();
+        chain.doFilter(request, response);
+	long stopTime = System.currentTimeMillis();
+	filterConfig.getServletContext().log
+	    (this.toString() + ": " + (stopTime - startTime) +
+	     " milliseconds");
+
+    }
+
+
+    /**
+     * Place this filter into service.
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+	this.filterConfig = filterConfig;
+        this.attribute = filterConfig.getInitParameter("attribute");
+
+    }
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+	if (filterConfig == null)
+	    return ("InvokerFilter()");
+	StringBuffer sb = new StringBuffer("InvokerFilter(");
+	sb.append(filterConfig);
+	sb.append(")");
+	return (sb.toString());
+
+    }
+
+
+}
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/filters/RequestDumperFilter.java b/servletapi/jsr152/examples/WEB-INF/classes/filters/RequestDumperFilter.java
new file mode 100644
index 0000000..a8642f3
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/filters/RequestDumperFilter.java
@@ -0,0 +1,201 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+
+package filters;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.sql.Timestamp;
+import java.util.Enumeration;
+import java.util.Locale;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * Example filter that dumps interesting state information about a request
+ * to the associated servlet context log file, before allowing the servlet
+ * to process the request in the usual way.  This can be installed as needed
+ * to assist in debugging problems.
+ *
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class RequestDumperFilter implements Filter {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The filter configuration object we are associated with.  If this value
+     * is null, this filter instance is not currently configured.
+     */
+    private FilterConfig filterConfig = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Take this filter out of service.
+     */
+    public void destroy() {
+
+        this.filterConfig = null;
+
+    }
+
+
+    /**
+     * Time the processing that is performed by all subsequent filters in the
+     * current filter stack, including the ultimately invoked servlet.
+     *
+     * @param request The servlet request we are processing
+     * @param result The servlet response we are creating
+     * @param chain The filter chain we are processing
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain)
+	throws IOException, ServletException {
+
+        if (filterConfig == null)
+	    return;
+
+	// Render the generic servlet request properties
+	StringWriter sw = new StringWriter();
+	PrintWriter writer = new PrintWriter(sw);
+	writer.println("Request Received at " +
+		       (new Timestamp(System.currentTimeMillis())));
+	writer.println(" characterEncoding=" + request.getCharacterEncoding());
+	writer.println("     contentLength=" + request.getContentLength());
+	writer.println("       contentType=" + request.getContentType());
+	writer.println("            locale=" + request.getLocale());
+	writer.print("           locales=");
+	Enumeration locales = request.getLocales();
+	boolean first = true;
+	while (locales.hasMoreElements()) {
+	    Locale locale = (Locale) locales.nextElement();
+	    if (first)
+	        first = false;
+	    else
+	        writer.print(", ");
+	    writer.print(locale.toString());
+	}
+	writer.println();
+	Enumeration names = request.getParameterNames();
+	while (names.hasMoreElements()) {
+	    String name = (String) names.nextElement();
+	    writer.print("         parameter=" + name + "=");
+	    String values[] = request.getParameterValues(name);
+	    for (int i = 0; i < values.length; i++) {
+	        if (i > 0)
+		    writer.print(", ");
+		writer.print(values[i]);
+	    }
+	    writer.println();
+	}
+	writer.println("          protocol=" + request.getProtocol());
+	writer.println("        remoteAddr=" + request.getRemoteAddr());
+	writer.println("        remoteHost=" + request.getRemoteHost());
+	writer.println("            scheme=" + request.getScheme());
+	writer.println("        serverName=" + request.getServerName());
+	writer.println("        serverPort=" + request.getServerPort());
+	writer.println("          isSecure=" + request.isSecure());
+
+	// Render the HTTP servlet request properties
+	if (request instanceof HttpServletRequest) {
+	    writer.println("---------------------------------------------");
+	    HttpServletRequest hrequest = (HttpServletRequest) request;
+	    writer.println("       contextPath=" + hrequest.getContextPath());
+	    Cookie cookies[] = hrequest.getCookies();
+            if (cookies == null)
+                cookies = new Cookie[0];
+	    for (int i = 0; i < cookies.length; i++) {
+	        writer.println("            cookie=" + cookies[i].getName() +
+			       "=" + cookies[i].getValue());
+	    }
+	    names = hrequest.getHeaderNames();
+	    while (names.hasMoreElements()) {
+	        String name = (String) names.nextElement();
+		String value = hrequest.getHeader(name);
+	        writer.println("            header=" + name + "=" + value);
+	    }
+	    writer.println("            method=" + hrequest.getMethod());
+	    writer.println("          pathInfo=" + hrequest.getPathInfo());
+	    writer.println("       queryString=" + hrequest.getQueryString());
+	    writer.println("        remoteUser=" + hrequest.getRemoteUser());
+	    writer.println("requestedSessionId=" +
+			   hrequest.getRequestedSessionId());
+	    writer.println("        requestURI=" + hrequest.getRequestURI());
+	    writer.println("       servletPath=" + hrequest.getServletPath());
+	}
+	writer.println("=============================================");
+
+	// Log the resulting string
+	writer.flush();
+	filterConfig.getServletContext().log(sw.getBuffer().toString());
+
+	// Pass control on to the next filter
+        chain.doFilter(request, response);
+
+    }
+
+
+    /**
+     * Place this filter into service.
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+	this.filterConfig = filterConfig;
+
+    }
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+	if (filterConfig == null)
+	    return ("RequestDumperFilter()");
+	StringBuffer sb = new StringBuffer("RequestDumperFilter(");
+	sb.append(filterConfig);
+	sb.append(")");
+	return (sb.toString());
+
+    }
+
+
+}
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java b/servletapi/jsr152/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java
new file mode 100644
index 0000000..a5765b5
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java
@@ -0,0 +1,172 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package filters;
+
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.UnavailableException;
+
+
+/**
+ * <p>Example filter that sets the character encoding to be used in parsing the
+ * incoming request, either unconditionally or only if the client did not
+ * specify a character encoding.  Configuration of this filter is based on
+ * the following initialization parameters:</p>
+ * <ul>
+ * <li><strong>encoding</strong> - The character encoding to be configured
+ *     for this request, either conditionally or unconditionally based on
+ *     the <code>ignore</code> initialization parameter.  This parameter
+ *     is required, so there is no default.</li>
+ * <li><strong>ignore</strong> - If set to "true", any character encoding
+ *     specified by the client is ignored, and the value returned by the
+ *     <code>selectEncoding()</code> method is set.  If set to "false,
+ *     <code>selectEncoding()</code> is called <strong>only</strong> if the
+ *     client has not already specified an encoding.  By default, this
+ *     parameter is set to "true".</li>
+ * </ul>
+ *
+ * <p>Although this filter can be used unchanged, it is also easy to
+ * subclass it and make the <code>selectEncoding()</code> method more
+ * intelligent about what encoding to choose, based on characteristics of
+ * the incoming request (such as the values of the <code>Accept-Language</code>
+ * and <code>User-Agent</code> headers, or a value stashed in the current
+ * user's session.</p>
+ *
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SetCharacterEncodingFilter implements Filter {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The default character encoding to set for requests that pass through
+     * this filter.
+     */
+    protected String encoding = null;
+
+
+    /**
+     * The filter configuration object we are associated with.  If this value
+     * is null, this filter instance is not currently configured.
+     */
+    protected FilterConfig filterConfig = null;
+
+
+    /**
+     * Should a character encoding specified by the client be ignored?
+     */
+    protected boolean ignore = true;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Take this filter out of service.
+     */
+    public void destroy() {
+
+        this.encoding = null;
+        this.filterConfig = null;
+
+    }
+
+
+    /**
+     * Select and set (if specified) the character encoding to be used to
+     * interpret request parameters for this request.
+     *
+     * @param request The servlet request we are processing
+     * @param result The servlet response we are creating
+     * @param chain The filter chain we are processing
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain)
+	throws IOException, ServletException {
+
+        // Conditionally select and set the character encoding to be used
+        if (ignore || (request.getCharacterEncoding() == null)) {
+            String encoding = selectEncoding(request);
+            if (encoding != null)
+                request.setCharacterEncoding(encoding);
+        }
+
+	// Pass control on to the next filter
+        chain.doFilter(request, response);
+
+    }
+
+
+    /**
+     * Place this filter into service.
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+	this.filterConfig = filterConfig;
+        this.encoding = filterConfig.getInitParameter("encoding");
+        String value = filterConfig.getInitParameter("ignore");
+        if (value == null)
+            this.ignore = true;
+        else if (value.equalsIgnoreCase("true"))
+            this.ignore = true;
+        else if (value.equalsIgnoreCase("yes"))
+            this.ignore = true;
+        else
+            this.ignore = false;
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Select an appropriate character encoding to be used, based on the
+     * characteristics of the current request and/or filter initialization
+     * parameters.  If no character encoding should be set, return
+     * <code>null</code>.
+     * <p>
+     * The default implementation unconditionally returns the value configured
+     * by the <strong>encoding</strong> initialization parameter for this
+     * filter.
+     *
+     * @param request The servlet request we are processing
+     */
+    protected String selectEncoding(ServletRequest request) {
+
+        return (this.encoding);
+
+    }
+
+
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/BookBean.java b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/BookBean.java
new file mode 100644
index 0000000..4e2f18d
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/BookBean.java
@@ -0,0 +1,43 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package jsp2.examples;
+
+public class BookBean {
+    private String title;
+    private String author;
+    private String isbn;
+    
+    public BookBean( String title, String author, String isbn ) {
+        this.title = title;
+        this.author = author;
+        this.isbn = isbn;
+    }
+
+    public String getTitle() {
+        return this.title;
+    }
+    
+    public String getAuthor() {
+        return this.author;
+    }
+    
+    public String getIsbn() {
+        return this.isbn;
+    }
+    
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/FooBean.java b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/FooBean.java
new file mode 100644
index 0000000..c0bd107
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/FooBean.java
@@ -0,0 +1,35 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package jsp2.examples;
+
+public class FooBean {
+    private String bar;
+    
+    public FooBean() {
+        bar = "Initial value";
+    }
+    
+    public String getBar() {
+        return this.bar;
+    }
+    
+    public void setBar(String bar) {
+        this.bar = bar;
+    }
+    
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/el/Functions.java b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/el/Functions.java
new file mode 100644
index 0000000..34732e2
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/el/Functions.java
@@ -0,0 +1,44 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package jsp2.examples.el;
+
+import java.util.*;
+
+/**
+ * Defines the functions for the jsp2 example tag library.
+ * 
+ * <p>Each function is defined as a static method.</p>
+ */
+public class Functions {
+    public static String reverse( String text ) {
+        return new StringBuffer( text ).reverse().toString();
+    }
+
+    public static int numVowels( String text ) {
+        String vowels = "aeiouAEIOU";
+	int result = 0;
+        for( int i = 0; i < text.length(); i++ ) {
+	    if( vowels.indexOf( text.charAt( i ) ) != -1 ) {
+	        result++;
+	    }
+	}
+	return result;
+    }
+
+    public static String caps( String text ) {
+        return text.toUpperCase();
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.java b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.java
new file mode 100644
index 0000000..8c55db9
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.java
@@ -0,0 +1,54 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package jsp2.examples.simpletag;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+import javax.servlet.jsp.tagext.DynamicAttributes;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.io.IOException;
+
+/**
+ * SimpleTag handler that echoes all its attributes 
+ */
+public class EchoAttributesTag 
+    extends SimpleTagSupport
+    implements DynamicAttributes
+{
+    private ArrayList keys = new ArrayList();
+    private ArrayList values = new ArrayList();
+
+    public void doTag() throws JspException, IOException {
+	JspWriter out = getJspContext().getOut();
+	for( int i = 0; i < keys.size(); i++ ) {
+	    String key = (String)keys.get( i );
+	    Object value = values.get( i );
+	    out.println( "<li>" + key + " = " + value + "</li>" );
+        }
+    }
+
+    public void setDynamicAttribute( String uri, String localName, 
+	Object value ) 
+	throws JspException
+    {
+	keys.add( localName );
+	values.add( value );
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.java b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.java
new file mode 100644
index 0000000..52a5e15
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.java
@@ -0,0 +1,44 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package jsp2.examples.simpletag;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+import java.util.HashMap;
+import jsp2.examples.BookBean;
+
+/**
+ * SimpleTag handler that pretends to search for a book, and stores
+ * the result in a scoped variable.
+ */
+public class FindBookSimpleTag extends SimpleTagSupport {
+    private String var;
+    
+    private static final String BOOK_TITLE = "The Lord of the Rings";
+    private static final String BOOK_AUTHOR = "J. R. R. Tolkein";
+    private static final String BOOK_ISBN = "0618002251";
+
+    public void doTag() throws JspException {
+        BookBean book = new BookBean( BOOK_TITLE, BOOK_AUTHOR, BOOK_ISBN );
+        getJspContext().setAttribute( this.var, book );
+    }
+
+    public void setVar( String var ) {
+	this.var = var;
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.java b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.java
new file mode 100644
index 0000000..7a4196c
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.java
@@ -0,0 +1,31 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package jsp2.examples.simpletag;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+import java.io.IOException;
+
+/**
+ * SimpleTag handler that prints "Hello, world!"
+ */
+public class HelloWorldSimpleTag extends SimpleTagSupport {
+    public void doTag() throws JspException, IOException {
+	getJspContext().getOut().write( "Hello, world!" );
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.java b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.java
new file mode 100644
index 0000000..0f4b1e5
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.java
@@ -0,0 +1,42 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package jsp2.examples.simpletag;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+import java.util.HashMap;
+import java.io.IOException;
+
+/**
+ * SimpleTag handler that accepts a num attribute and 
+ * invokes its body 'num' times.
+ */
+public class RepeatSimpleTag extends SimpleTagSupport {
+    private int num;
+
+    public void doTag() throws JspException, IOException {
+        for (int i=0; i<num; i++) {
+            getJspContext().setAttribute("count", String.valueOf( i + 1 ) );
+	    getJspBody().invoke(null);
+        }
+    }
+
+    public void setNum(int num) {
+	this.num = num;
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.java b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.java
new file mode 100644
index 0000000..0c12099
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.java
@@ -0,0 +1,81 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package jsp2.examples.simpletag;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.JspFragment;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+import java.util.HashMap;
+import java.io.IOException;
+
+/**
+ * SimpleTag handler that accepts takes three attributes of type
+ * JspFragment and invokes then in a random order.
+ */
+public class ShuffleSimpleTag extends SimpleTagSupport {
+    private JspFragment fragment1;
+    private JspFragment fragment2;
+    private JspFragment fragment3;
+
+    public void doTag() throws JspException, IOException {
+        switch( (int)(Math.random() * 6) ) {
+            case 0:
+                fragment1.invoke( null );
+                fragment2.invoke( null );
+                fragment3.invoke( null );
+                break;
+            case 1:
+                fragment1.invoke( null );
+                fragment3.invoke( null );
+                fragment2.invoke( null );
+                break;
+            case 2:
+                fragment2.invoke( null );
+                fragment1.invoke( null );
+                fragment3.invoke( null );
+                break;
+            case 3:
+                fragment2.invoke( null );
+                fragment3.invoke( null );
+                fragment1.invoke( null );
+                break;
+            case 4:
+                fragment3.invoke( null );
+                fragment1.invoke( null );
+                fragment2.invoke( null );
+                break;
+            case 5:
+                fragment3.invoke( null );
+                fragment2.invoke( null );
+                fragment1.invoke( null );
+                break;
+        }
+    }
+
+    public void setFragment1( JspFragment fragment1 ) {
+        this.fragment1 = fragment1;
+    }
+    
+    public void setFragment2( JspFragment fragment2 ) {
+        this.fragment2 = fragment2;
+    }
+    
+    public void setFragment3( JspFragment fragment3 ) {
+        this.fragment3 = fragment3;
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.java b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.java
new file mode 100644
index 0000000..7f33480
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.java
@@ -0,0 +1,46 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package jsp2.examples.simpletag;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * Displays a tile as a single cell in a table.
+ */
+public class TileSimpleTag extends SimpleTagSupport {
+    private String color;
+    private String label;
+
+    public void doTag() throws JspException, IOException {
+	getJspContext().getOut().write( 
+	    "<td width=\"32\" height=\"32\" bgcolor=\"" + this.color + 
+	    "\"><font color=\"#ffffff\"><center>" + this.label + 
+                "</center></font></td>" );
+    }
+
+    public void setColor( String color ) {
+        this.color = color;
+    }
+    
+    public void setLabel( String label ) {
+        this.label = label;
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/listeners/ContextListener.java b/servletapi/jsr152/examples/WEB-INF/classes/listeners/ContextListener.java
new file mode 100644
index 0000000..d999f5b
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/listeners/ContextListener.java
@@ -0,0 +1,156 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package listeners;
+
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+
+/**
+ * Example listener for context-related application events, which were
+ * introduced in the 2.3 version of the Servlet API.  This listener
+ * merely documents the occurrence of such events in the application log
+ * associated with our servlet context.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class ContextListener
+    implements ServletContextAttributeListener, ServletContextListener {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The servlet context with which we are associated.
+     */
+    private ServletContext context = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Record the fact that a servlet context attribute was added.
+     *
+     * @param event The servlet context attribute event
+     */
+    public void attributeAdded(ServletContextAttributeEvent event) {
+
+	log("attributeAdded('" + event.getName() + "', '" +
+	    event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a servlet context attribute was removed.
+     *
+     * @param event The servlet context attribute event
+     */
+    public void attributeRemoved(ServletContextAttributeEvent event) {
+
+	log("attributeRemoved('" + event.getName() + "', '" +
+	    event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a servlet context attribute was replaced.
+     *
+     * @param event The servlet context attribute event
+     */
+    public void attributeReplaced(ServletContextAttributeEvent event) {
+
+	log("attributeReplaced('" + event.getName() + "', '" +
+	    event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that this web application has been destroyed.
+     *
+     * @param event The servlet context event
+     */
+    public void contextDestroyed(ServletContextEvent event) {
+
+	log("contextDestroyed()");
+	this.context = null;
+
+    }
+
+
+    /**
+     * Record the fact that this web application has been initialized.
+     *
+     * @param event The servlet context event
+     */
+    public void contextInitialized(ServletContextEvent event) {
+
+	this.context = event.getServletContext();
+	log("contextInitialized()");
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Log a message to the servlet context application log.
+     *
+     * @param message Message to be logged
+     */
+    private void log(String message) {
+
+	if (context != null)
+	    context.log("ContextListener: " + message);
+	else
+	    System.out.println("ContextListener: " + message);
+
+    }
+
+
+    /**
+     * Log a message and associated exception to the servlet context
+     * application log.
+     *
+     * @param message Message to be logged
+     * @param throwable Exception to be logged
+     */
+    private void log(String message, Throwable throwable) {
+
+	if (context != null)
+	    context.log("ContextListener: " + message, throwable);
+	else {
+	    System.out.println("ContextListener: " + message);
+	    throwable.printStackTrace(System.out);
+	}
+
+    }
+
+
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/listeners/SessionListener.java b/servletapi/jsr152/examples/WEB-INF/classes/listeners/SessionListener.java
new file mode 100644
index 0000000..25a1057
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/listeners/SessionListener.java
@@ -0,0 +1,183 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package listeners;
+
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+
+/**
+ * Example listener for context-related application events, which were
+ * introduced in the 2.3 version of the Servlet API.  This listener
+ * merely documents the occurrence of such events in the application log
+ * associated with our servlet context.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class SessionListener
+    implements ServletContextListener,
+	       HttpSessionAttributeListener, HttpSessionListener {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The servlet context with which we are associated.
+     */
+    private ServletContext context = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Record the fact that a servlet context attribute was added.
+     *
+     * @param event The session attribute event
+     */
+    public void attributeAdded(HttpSessionBindingEvent event) {
+
+	log("attributeAdded('" + event.getSession().getId() + "', '" +
+	    event.getName() + "', '" + event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a servlet context attribute was removed.
+     *
+     * @param event The session attribute event
+     */
+    public void attributeRemoved(HttpSessionBindingEvent event) {
+
+	log("attributeRemoved('" + event.getSession().getId() + "', '" +
+	    event.getName() + "', '" + event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a servlet context attribute was replaced.
+     *
+     * @param event The session attribute event
+     */
+    public void attributeReplaced(HttpSessionBindingEvent event) {
+
+	log("attributeReplaced('" + event.getSession().getId() + "', '" +
+	    event.getName() + "', '" + event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that this web application has been destroyed.
+     *
+     * @param event The servlet context event
+     */
+    public void contextDestroyed(ServletContextEvent event) {
+
+	log("contextDestroyed()");
+	this.context = null;
+
+    }
+
+
+    /**
+     * Record the fact that this web application has been initialized.
+     *
+     * @param event The servlet context event
+     */
+    public void contextInitialized(ServletContextEvent event) {
+
+	this.context = event.getServletContext();
+	log("contextInitialized()");
+
+    }
+
+
+    /**
+     * Record the fact that a session has been created.
+     *
+     * @param event The session event
+     */
+    public void sessionCreated(HttpSessionEvent event) {
+
+	log("sessionCreated('" + event.getSession().getId() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a session has been destroyed.
+     *
+     * @param event The session event
+     */
+    public void sessionDestroyed(HttpSessionEvent event) {
+
+	log("sessionDestroyed('" + event.getSession().getId() + "')");
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Log a message to the servlet context application log.
+     *
+     * @param message Message to be logged
+     */
+    private void log(String message) {
+
+	if (context != null)
+	    context.log("SessionListener: " + message);
+	else
+	    System.out.println("SessionListener: " + message);
+
+    }
+
+
+    /**
+     * Log a message and associated exception to the servlet context
+     * application log.
+     *
+     * @param message Message to be logged
+     * @param throwable Exception to be logged
+     */
+    private void log(String message, Throwable throwable) {
+
+	if (context != null)
+	    context.log("SessionListener: " + message, throwable);
+	else {
+	    System.out.println("SessionListener: " + message);
+	    throwable.printStackTrace(System.out);
+	}
+
+    }
+
+
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/num/NumberGuessBean.java b/servletapi/jsr152/examples/WEB-INF/classes/num/NumberGuessBean.java
new file mode 100644
index 0000000..b5afeab
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/num/NumberGuessBean.java
@@ -0,0 +1,78 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+/*
+ * Originally written by Jason Hunter, http://www.servlets.com.
+ */
+
+package num;
+
+import java.util.*;
+
+public class NumberGuessBean {
+
+  int answer;
+  boolean success;
+  String hint;
+  int numGuesses;
+
+  public NumberGuessBean() {
+    reset();
+  }
+
+  public void setGuess(String guess) {
+    numGuesses++;
+
+    int g;
+    try {
+      g = Integer.parseInt(guess);
+    }
+    catch (NumberFormatException e) {
+      g = -1;
+    }
+
+    if (g == answer) {
+      success = true;
+    }
+    else if (g == -1) {
+      hint = "a number next time";
+    }
+    else if (g < answer) {
+      hint = "higher";
+    }
+    else if (g > answer) {
+      hint = "lower";
+    }
+  }
+
+  public boolean getSuccess() {
+    return success;
+  }
+
+  public String getHint() {
+    return "" + hint;
+  }
+
+  public int getNumGuesses() {
+    return numGuesses;
+  }
+
+  public void reset() {
+    answer = Math.abs(new Random().nextInt() % 100) + 1;
+    success = false;
+    numGuesses = 0;
+  }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/servletToJsp.java b/servletapi/jsr152/examples/WEB-INF/classes/servletToJsp.java
new file mode 100644
index 0000000..38a2c72
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/servletToJsp.java
@@ -0,0 +1,32 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+public class servletToJsp extends HttpServlet {
+
+    public void doGet (HttpServletRequest request,
+		       HttpServletResponse response) {
+
+	try {
+	    // Set the attribute and Forward to hello.jsp
+	    request.setAttribute ("servletName", "servletToJsp");
+	    getServletConfig().getServletContext().getRequestDispatcher("/jsptoserv/hello.jsp").forward(request, response);
+	} catch (Exception ex) {
+	    ex.printStackTrace ();
+	}
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/sessions/DummyCart.java b/servletapi/jsr152/examples/WEB-INF/classes/sessions/DummyCart.java
new file mode 100644
index 0000000..17ccd80
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/sessions/DummyCart.java
@@ -0,0 +1,69 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package sessions;
+
+import javax.servlet.http.*;
+import java.util.Vector;
+import java.util.Enumeration;
+
+public class DummyCart {
+    Vector v = new Vector();
+    String submit = null;
+    String item = null;
+    
+    private void addItem(String name) {
+	v.addElement(name);
+    }
+
+    private void removeItem(String name) {
+	v.removeElement(name);
+    }
+
+    public void setItem(String name) {
+	item = name;
+    }
+    
+    public void setSubmit(String s) {
+	submit = s;
+    }
+
+    public String[] getItems() {
+	String[] s = new String[v.size()];
+	v.copyInto(s);
+	return s;
+    }
+    
+    public void processRequest(HttpServletRequest request) {
+	// null value for submit - user hit enter instead of clicking on 
+	// "add" or "remove"
+	if (submit == null) 
+	    addItem(item);
+
+	if (submit.equals("add"))
+	    addItem(item);
+	else if (submit.equals("remove")) 
+	    removeItem(item);
+	
+	// reset at the end of the request
+	reset();
+    }
+
+    // reset
+    private void reset() {
+	submit = null;
+	item = null;
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/util/HTMLFilter.java b/servletapi/jsr152/examples/WEB-INF/classes/util/HTMLFilter.java
new file mode 100644
index 0000000..2556a5f
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/util/HTMLFilter.java
@@ -0,0 +1,69 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package util;
+
+/**
+ * HTML filter utility.
+ *
+ * @author Craig R. McClanahan
+ * @author Tim Tye
+ * @version $Revision$ $Date$
+ */
+
+public final class HTMLFilter {
+
+
+    /**
+     * Filter the specified message string for characters that are sensitive
+     * in HTML.  This avoids potential attacks caused by including JavaScript
+     * codes in the request URL that is often reported in error messages.
+     *
+     * @param message The message string to be filtered
+     */
+    public static String filter(String message) {
+
+        if (message == null)
+            return (null);
+
+        char content[] = new char[message.length()];
+        message.getChars(0, message.length(), content, 0);
+        StringBuffer result = new StringBuffer(content.length + 50);
+        for (int i = 0; i < content.length; i++) {
+            switch (content[i]) {
+            case '<':
+                result.append("&lt;");
+                break;
+            case '>':
+                result.append("&gt;");
+                break;
+            case '&':
+                result.append("&amp;");
+                break;
+            case '"':
+                result.append("&quot;");
+                break;
+            default:
+                result.append(content[i]);
+            }
+        }
+        return (result.toString());
+
+    }
+
+
+}
+
diff --git a/servletapi/jsr152/examples/WEB-INF/classes/validators/DebugValidator.java b/servletapi/jsr152/examples/WEB-INF/classes/validators/DebugValidator.java
new file mode 100644
index 0000000..de305e8
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/classes/validators/DebugValidator.java
@@ -0,0 +1,83 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package validators;
+
+
+import java.io.InputStream;
+import java.io.IOException;
+import javax.servlet.jsp.tagext.PageData;
+import javax.servlet.jsp.tagext.TagLibraryValidator;
+import javax.servlet.jsp.tagext.ValidationMessage;
+
+
+/**
+ * Example tag library validator that simply dumps the XML version of each
+ * page to standard output (which will typically be sent to the file
+ * <code>$CATALINA_HOME/logs/catalina.out</code>).  To utilize it, simply
+ * include a <code>taglib</code> directive for this tag library at the top
+ * of your JSP page.
+ *
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class DebugValidator extends TagLibraryValidator {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Validate a JSP page.  This will get invoked once per directive in the
+     * JSP page.  This method will return <code>null</code> if the page is
+     * valid; otherwise the method should return an array of
+     * <code>ValidationMessage</code> objects.  An array of length zero is
+     * also interpreted as no errors.
+     *
+     * @param prefix The value of the prefix argument in this directive
+     * @param uri The value of the URI argument in this directive
+     * @param page The page data for this page
+     */
+    public ValidationMessage[] validate(String prefix, String uri,
+                                        PageData page) {
+
+        System.out.println("---------- Prefix=" + prefix + " URI=" + uri +
+                           "----------");
+
+        InputStream is = page.getInputStream();
+        while (true) {
+            try {
+                int ch = is.read();
+                if (ch < 0)
+                    break;
+                System.out.print((char) ch);
+            } catch (IOException e) {
+                break;
+            }
+        }
+        System.out.println();
+        System.out.println("-----------------------------------------------");
+        return (null);
+
+    }
+
+
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/jsp/applet/Clock2.java b/servletapi/jsr152/examples/WEB-INF/jsp/applet/Clock2.java
new file mode 100644
index 0000000..edbf292
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/jsp/applet/Clock2.java
@@ -0,0 +1,211 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import java.util.*;
+import java.awt.*;
+import java.applet.*;
+import java.text.*;
+
+/**
+ * Time!
+ *
+ * @author Rachel Gollub
+ */
+
+public class Clock2 extends Applet implements Runnable {
+    Thread timer;                // The thread that displays clock
+    int lastxs, lastys, lastxm,
+        lastym, lastxh, lastyh;  // Dimensions used to draw hands 
+    SimpleDateFormat formatter;  // Formats the date displayed
+    String lastdate;             // String to hold date displayed
+    Font clockFaceFont;          // Font for number display on clock
+    Date currentDate;            // Used to get date to display
+    Color handColor;             // Color of main hands and dial
+    Color numberColor;           // Color of second hand and numbers
+
+    public void init() {
+        int x,y;
+        lastxs = lastys = lastxm = lastym = lastxh = lastyh = 0;
+        formatter = new SimpleDateFormat ("EEE MMM dd hh:mm:ss yyyy", Locale.getDefault());
+        currentDate = new Date();
+        lastdate = formatter.format(currentDate);
+        clockFaceFont = new Font("Serif", Font.PLAIN, 14);
+        handColor = Color.blue;
+        numberColor = Color.darkGray;
+
+        try {
+            setBackground(new Color(Integer.parseInt(getParameter("bgcolor"),16)));
+        } catch (Exception E) { }
+        try {
+            handColor = new Color(Integer.parseInt(getParameter("fgcolor1"),16));
+        } catch (Exception E) { }
+        try {
+            numberColor = new Color(Integer.parseInt(getParameter("fgcolor2"),16));
+        } catch (Exception E) { }
+        resize(300,300);              // Set clock window size
+    }
+
+    // Plotpoints allows calculation to only cover 45 degrees of the circle,
+    // and then mirror
+    public void plotpoints(int x0, int y0, int x, int y, Graphics g) {
+        g.drawLine(x0+x,y0+y,x0+x,y0+y);
+        g.drawLine(x0+y,y0+x,x0+y,y0+x);
+        g.drawLine(x0+y,y0-x,x0+y,y0-x);
+        g.drawLine(x0+x,y0-y,x0+x,y0-y);
+        g.drawLine(x0-x,y0-y,x0-x,y0-y);
+        g.drawLine(x0-y,y0-x,x0-y,y0-x);
+        g.drawLine(x0-y,y0+x,x0-y,y0+x);
+        g.drawLine(x0-x,y0+y,x0-x,y0+y);
+    }
+
+    // Circle is just Bresenham's algorithm for a scan converted circle
+    public void circle(int x0, int y0, int r, Graphics g) {
+        int x,y;
+        float d;
+        x=0;
+        y=r;
+        d=5/4-r;
+        plotpoints(x0,y0,x,y,g);
+
+        while (y>x){
+            if (d<0) {
+                d=d+2*x+3;
+                x++;
+            }
+            else {
+                d=d+2*(x-y)+5;
+                x++;
+                y--;
+            }
+            plotpoints(x0,y0,x,y,g);
+        }
+    }
+
+    // Paint is the main part of the program
+    public void paint(Graphics g) {
+        int xh, yh, xm, ym, xs, ys, s = 0, m = 10, h = 10, xcenter, ycenter;
+        String today;
+
+        currentDate = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat("s",Locale.getDefault());
+        try {
+            s = Integer.parseInt(formatter.format(currentDate));
+        } catch (NumberFormatException n) {
+            s = 0;
+        }
+        formatter.applyPattern("m");
+        try {
+            m = Integer.parseInt(formatter.format(currentDate));
+        } catch (NumberFormatException n) {
+            m = 10;
+        }    
+        formatter.applyPattern("h");
+        try {
+            h = Integer.parseInt(formatter.format(currentDate));
+        } catch (NumberFormatException n) {
+            h = 10;
+        }
+        formatter.applyPattern("EEE MMM dd HH:mm:ss yyyy");
+        today = formatter.format(currentDate);
+        xcenter=80;
+        ycenter=55;
+    
+    // a= s* pi/2 - pi/2 (to switch 0,0 from 3:00 to 12:00)
+    // x = r(cos a) + xcenter, y = r(sin a) + ycenter
+    
+        xs = (int)(Math.cos(s * 3.14f/30 - 3.14f/2) * 45 + xcenter);
+        ys = (int)(Math.sin(s * 3.14f/30 - 3.14f/2) * 45 + ycenter);
+        xm = (int)(Math.cos(m * 3.14f/30 - 3.14f/2) * 40 + xcenter);
+        ym = (int)(Math.sin(m * 3.14f/30 - 3.14f/2) * 40 + ycenter);
+        xh = (int)(Math.cos((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 30 + xcenter);
+        yh = (int)(Math.sin((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 30 + ycenter);
+    
+    // Draw the circle and numbers
+    
+        g.setFont(clockFaceFont);
+        g.setColor(handColor);
+        circle(xcenter,ycenter,50,g);
+        g.setColor(numberColor);
+        g.drawString("9",xcenter-45,ycenter+3); 
+        g.drawString("3",xcenter+40,ycenter+3);
+        g.drawString("12",xcenter-5,ycenter-37);
+        g.drawString("6",xcenter-3,ycenter+45);
+
+    // Erase if necessary, and redraw
+    
+        g.setColor(getBackground());
+        if (xs != lastxs || ys != lastys) {
+            g.drawLine(xcenter, ycenter, lastxs, lastys);
+            g.drawString(lastdate, 5, 125);
+        }
+        if (xm != lastxm || ym != lastym) {
+            g.drawLine(xcenter, ycenter-1, lastxm, lastym);
+            g.drawLine(xcenter-1, ycenter, lastxm, lastym); }
+        if (xh != lastxh || yh != lastyh) {
+            g.drawLine(xcenter, ycenter-1, lastxh, lastyh);
+            g.drawLine(xcenter-1, ycenter, lastxh, lastyh); }
+        g.setColor(numberColor);
+        g.drawString("", 5, 125);
+        g.drawString(today, 5, 125);    
+        g.drawLine(xcenter, ycenter, xs, ys);
+        g.setColor(handColor);
+        g.drawLine(xcenter, ycenter-1, xm, ym);
+        g.drawLine(xcenter-1, ycenter, xm, ym);
+        g.drawLine(xcenter, ycenter-1, xh, yh);
+        g.drawLine(xcenter-1, ycenter, xh, yh);
+        lastxs=xs; lastys=ys;
+        lastxm=xm; lastym=ym;
+        lastxh=xh; lastyh=yh;
+        lastdate = today;
+        currentDate=null;
+    }
+
+    public void start() {
+        timer = new Thread(this);
+        timer.start();
+    }
+
+    public void stop() {
+        timer = null;
+    }
+
+    public void run() {
+        Thread me = Thread.currentThread();
+        while (timer == me) {
+            try {
+                Thread.currentThread().sleep(100);
+            } catch (InterruptedException e) {
+            }
+            repaint();
+        }
+    }
+
+    public void update(Graphics g) {
+        paint(g);
+    }
+
+    public String getAppletInfo() {
+        return "Title: A Clock \nAuthor: Rachel Gollub, 1995 \nAn analog clock.";
+    }
+  
+    public String[][] getParameterInfo() {
+        String[][] info = {
+            {"bgcolor", "hexadecimal RGB number", "The background color. Default is the color of your browser."},
+            {"fgcolor1", "hexadecimal RGB number", "The color of the hands and dial. Default is blue."},
+            {"fgcolor2", "hexadecimal RGB number", "The color of the seconds hand and numbers. Default is dark gray."}
+        };
+        return info;
+    }
+}
diff --git a/servletapi/jsr152/examples/WEB-INF/jsp/debug-taglib.tld b/servletapi/jsr152/examples/WEB-INF/jsp/debug-taglib.tld
new file mode 100644
index 0000000..dbf39b0
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/jsp/debug-taglib.tld
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE taglib
+        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
+	"http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
+
+<!-- a tag library descriptor -->
+
+<taglib>
+  <tlib-version>1.0</tlib-version>
+  <jsp-version>1.2</jsp-version>
+  <short-name>debug</short-name>
+  <uri>http://jakarta.apache.org/tomcat/debug-taglib</uri>
+  <description>
+    This tag library defines no tags.  Instead, its purpose is encapsulated
+    in the TagLibraryValidator implementation that simply outputs the XML
+    version of a JSP page to standard output, whenever this tag library is
+    referenced in a "taglib" directive in a JSP page.
+  </description>
+  <validator>
+    <validator-class>validators.DebugValidator</validator-class>
+  </validator>
+
+  <!-- This is a dummy tag solely to satisfy DTD requirements -->  
+  <tag>
+    <name>log</name>
+    <tag-class>examples.LogTag</tag-class>
+    <body-content>TAGDEPENDENT</body-content>
+    <description>
+	Perform a server side action; Log the message.
+    </description>
+    <attribute>
+	<name>toBrowser</name>
+	<required>false</required>
+    </attribute>
+  </tag>
+  
+
+</taglib>
diff --git a/servletapi/jsr152/examples/WEB-INF/jsp/example-taglib.tld b/servletapi/jsr152/examples/WEB-INF/jsp/example-taglib.tld
new file mode 100644
index 0000000..5c0e30b
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/jsp/example-taglib.tld
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE taglib
+        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
+	"http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
+
+<taglib>
+
+  <tlib-version>1.0</tlib-version>
+  <jsp-version>1.2</jsp-version>
+  <short-name>simple</short-name>
+  <uri>http://jakarta.apache.org/tomcat/example-taglib</uri>
+  <description>
+	A simple tab library for the examples
+  </description>
+
+  <tag>
+    <name>ShowSource</name>
+    <tag-class>examples.ShowSource</tag-class>
+    <description> Display JSP sources </description>
+    <attribute>
+       <name>jspFile</name>
+       <required>true</required>
+       <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>       
+
+  <!-- A simple Tag -->
+  <!-- foo tag -->
+  <tag>
+    <name>foo</name>
+    <tag-class>examples.FooTag</tag-class>
+    <tei-class>examples.FooTagExtraInfo</tei-class>
+    <body-content>JSP</body-content>
+    <description>
+	Perform a server side action; uses 3 mandatory attributes
+    </description>
+
+    <attribute>
+      <name>att1</name>
+      <required>true</required>
+    </attribute>
+    <attribute>
+      <name>att2</name>
+      <required>true</required>
+    </attribute>
+    <attribute>
+      <name>att3</name>
+      <required>true</required>
+    </attribute>
+  </tag>
+
+  <!-- Another simple tag -->
+  <!-- log tag -->
+  <tag>
+    <name>log</name>
+    <tag-class>examples.LogTag</tag-class>
+    <body-content>TAGDEPENDENT</body-content>
+    <description>
+	Perform a server side action; Log the message.
+    </description>
+    <attribute>
+	<name>toBrowser</name>
+	<required>false</required>
+    </attribute>
+  </tag>
+  
+</taglib>
diff --git a/servletapi/jsr152/examples/WEB-INF/jsp2/jsp2-example-taglib.tld b/servletapi/jsr152/examples/WEB-INF/jsp2/jsp2-example-taglib.tld
new file mode 100644
index 0000000..c22cd9e
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/jsp2/jsp2-example-taglib.tld
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+    version="2.0">
+    <description>A tag library exercising SimpleTag handlers.</description>
+    <tlib-version>1.0</tlib-version>
+    <short-name>SimpleTagLibrary</short-name>
+    <uri>/SimpleTagLibrary</uri>
+    <tag>
+	<description>Outputs Hello, World</description>
+        <name>helloWorld</name>
+	<tag-class>jsp2.examples.simpletag.HelloWorldSimpleTag</tag-class>
+	<body-content>empty</body-content>
+    </tag>
+    <tag>
+        <description>Repeats the body of the tag 'num' times</description>
+        <name>repeat</name>
+        <tag-class>jsp2.examples.simpletag.RepeatSimpleTag</tag-class>
+        <body-content>scriptless</body-content>
+        <variable>
+            <description>Current invocation count (1 to num)</description>
+            <name-given>count</name-given>
+        </variable>
+        <attribute>
+            <name>num</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+    </tag>
+    <tag>
+	<description>Populates the page context with a BookBean</description>
+        <name>findBook</name>
+	<tag-class>jsp2.examples.simpletag.FindBookSimpleTag</tag-class>
+	<body-content>empty</body-content>
+	<attribute>
+	    <name>var</name>
+	    <required>true</required>
+	    <rtexprvalue>true</rtexprvalue>
+	</attribute>
+    </tag>
+    <tag>
+        <description>
+            Takes 3 fragments and invokes them in a random order
+        </description>
+        <name>shuffle</name>
+        <tag-class>jsp2.examples.simpletag.ShuffleSimpleTag</tag-class>
+        <body-content>empty</body-content>
+        <attribute>
+            <name>fragment1</name>
+            <required>true</required>
+	    <fragment>true</fragment>
+        </attribute>
+        <attribute>
+            <name>fragment2</name>
+            <required>true</required>
+	    <fragment>true</fragment>
+        </attribute>
+        <attribute>
+            <name>fragment3</name>
+            <required>true</required>
+	    <fragment>true</fragment>
+        </attribute>
+    </tag>
+    <tag>
+        <description>Outputs a colored tile</description>
+        <name>tile</name>
+        <tag-class>jsp2.examples.simpletag.TileSimpleTag</tag-class>
+        <body-content>empty</body-content>
+        <attribute>
+            <name>color</name>
+            <required>true</required>
+        </attribute>
+        <attribute>
+            <name>label</name>
+            <required>true</required>
+        </attribute>
+    </tag>
+    <tag>
+	<description>
+	  Tag that echoes all its attributes and body content
+	</description>
+	<name>echoAttributes</name>
+	<tag-class>jsp2.examples.simpletag.EchoAttributesTag</tag-class>
+	<body-content>empty</body-content>
+	<dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <function>
+        <description>Reverses the characters in the given String</description>
+        <name>reverse</name>
+	<function-class>jsp2.examples.el.Functions</function-class>
+	<function-signature>java.lang.String reverse( java.lang.String )</function-signature>
+    </function>
+    <function>
+        <description>Counts the number of vowels (a,e,i,o,u) in the given String</description>
+        <name>countVowels</name>
+	<function-class>jsp2.examples.el.Functions</function-class>
+	<function-signature>java.lang.String numVowels( java.lang.String )</function-signature>
+    </function>
+    <function>
+	<description>Converts the string to all caps</description>
+        <name>caps</name>
+	<function-class>jsp2.examples.el.Functions</function-class>
+	<function-signature>java.lang.String caps( java.lang.String )</function-signature>
+    </function>
+</taglib>
+
diff --git a/servletapi/jsr152/examples/WEB-INF/lib/jstl.jar b/servletapi/jsr152/examples/WEB-INF/lib/jstl.jar
new file mode 100644
index 0000000..6b41358
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/lib/jstl.jar
Binary files differ
diff --git a/servletapi/jsr152/examples/WEB-INF/lib/standard.jar b/servletapi/jsr152/examples/WEB-INF/lib/standard.jar
new file mode 100644
index 0000000..258daae
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/lib/standard.jar
Binary files differ
diff --git a/servletapi/jsr152/examples/WEB-INF/tagPlugins.xml b/servletapi/jsr152/examples/WEB-INF/tagPlugins.xml
new file mode 100644
index 0000000..3576c67
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/tagPlugins.xml
@@ -0,0 +1,22 @@
+<tag-plugins>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.IfTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.If</plugin-class>
+  </tag-plugin>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.ChooseTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.Choose</plugin-class>
+  </tag-plugin>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.WhenTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.When</plugin-class>
+  </tag-plugin>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.OtherwiseTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.Otherwise</plugin-class>
+  </tag-plugin>
+  <tag-plugin>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ForEachTag</tag-class>
+    <plugin-class>org.apache.jasper.tagplugins.jstl.ForEach</plugin-class>
+  </tag-plugin>
+</tag-plugins>
diff --git a/servletapi/jsr152/examples/WEB-INF/tags/displayProducts.tag b/servletapi/jsr152/examples/WEB-INF/tags/displayProducts.tag
new file mode 100644
index 0000000..df16c8a
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/tags/displayProducts.tag
@@ -0,0 +1,54 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ attribute name="normalPrice" fragment="true" %>
+<%@ attribute name="onSale" fragment="true" %>
+<%@ variable name-given="name" %>
+<%@ variable name-given="price" %>
+<%@ variable name-given="origPrice" %>
+<%@ variable name-given="salePrice" %>
+
+<table border="1">
+  <tr>
+    <td> 
+      <c:set var="name" value="Hand-held Color PDA"/>
+      <c:set var="price" value="$298.86"/>
+      <jsp:invoke fragment="normalPrice"/>
+    </td>
+    <td> 
+      <c:set var="name" value="4-Pack 150 Watt Light Bulbs"/>
+      <c:set var="origPrice" value="$2.98"/>
+      <c:set var="salePrice" value="$2.32"/>
+      <jsp:invoke fragment="onSale"/>
+    </td>
+    <td> 
+      <c:set var="name" value="Digital Cellular Phone"/>
+      <c:set var="price" value="$68.74"/>
+      <jsp:invoke fragment="normalPrice"/>
+    </td>
+    <td> 
+      <c:set var="name" value="Baby Grand Piano"/>
+      <c:set var="price" value="$10,800.00"/>
+      <jsp:invoke fragment="normalPrice"/>
+    </td>
+    <td> 
+      <c:set var="name" value="Luxury Car w/ Leather Seats"/>
+      <c:set var="origPrice" value="$23,980.00"/>
+      <c:set var="salePrice" value="$21,070.00"/>
+      <jsp:invoke fragment="onSale"/>
+    </td>
+  </tr>
+</table>
diff --git a/servletapi/jsr152/examples/WEB-INF/tags/helloWorld.tag b/servletapi/jsr152/examples/WEB-INF/tags/helloWorld.tag
new file mode 100644
index 0000000..6c20214
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/tags/helloWorld.tag
@@ -0,0 +1,16 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+Hello, world!
diff --git a/servletapi/jsr152/examples/WEB-INF/tags/panel.tag b/servletapi/jsr152/examples/WEB-INF/tags/panel.tag
new file mode 100644
index 0000000..7c62795
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/tags/panel.tag
@@ -0,0 +1,28 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ attribute name="color" %>
+<%@ attribute name="bgcolor" %>
+<%@ attribute name="title" %>
+<table border="1" bgcolor="${color}">
+  <tr>
+    <td><b>${title}</b></td>
+  </tr>
+  <tr>
+    <td bgcolor="${bgcolor}">
+      <jsp:doBody/>
+    </td>
+  </tr>
+</table>
diff --git a/servletapi/jsr152/examples/WEB-INF/tags/xhtmlbasic.tag b/servletapi/jsr152/examples/WEB-INF/tags/xhtmlbasic.tag
new file mode 100644
index 0000000..138d10c
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/tags/xhtmlbasic.tag
@@ -0,0 +1,20 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN"
+"http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<jsp:doBody/>
+</html>
diff --git a/servletapi/jsr152/examples/WEB-INF/web.xml b/servletapi/jsr152/examples/WEB-INF/web.xml
new file mode 100644
index 0000000..aa4379a
--- /dev/null
+++ b/servletapi/jsr152/examples/WEB-INF/web.xml
@@ -0,0 +1,258 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+    <description>
+      JSP 2.0 Examples.
+    </description>
+    <display-name>JSP 2.0 Examples</display-name>
+
+
+    <!-- Define servlet-mapped and path-mapped example filters -->
+    <filter>
+        <filter-name>Servlet Mapped Filter</filter-name>
+        <filter-class>filters.ExampleFilter</filter-class>
+	<init-param>
+	    <param-name>attribute</param-name>
+	    <param-value>filters.ExampleFilter.SERVLET_MAPPED</param-value>
+	</init-param>
+    </filter>
+    <filter>
+        <filter-name>Path Mapped Filter</filter-name>
+        <filter-class>filters.ExampleFilter</filter-class>
+	<init-param>
+	    <param-name>attribute</param-name>
+	    <param-value>filters.ExampleFilter.PATH_MAPPED</param-value>
+	</init-param>
+    </filter>
+    <filter>
+        <filter-name>Request Dumper Filter</filter-name>
+        <filter-class>filters.RequestDumperFilter</filter-class>
+    </filter>
+
+    <!-- Example filter to set character encoding on each request -->
+    <filter>
+        <filter-name>Set Character Encoding</filter-name>
+        <filter-class>filters.SetCharacterEncodingFilter</filter-class>
+        <init-param>
+            <param-name>encoding</param-name>
+            <param-value>EUC_JP</param-value>
+        </init-param>
+    </filter>
+
+    <filter>
+        <filter-name>Compression Filter</filter-name>
+        <filter-class>compressionFilters.CompressionFilter</filter-class>
+
+        <init-param>
+          <param-name>compressionThreshold</param-name>
+          <param-value>10</param-value>
+        </init-param>
+        <init-param>
+          <param-name>debug</param-name>
+          <param-value>0</param-value>
+        </init-param>
+    </filter>
+
+<!-- Example filter mapping to apply the "Set Character Encoding" filter
+     to *all* requests processed by this web application -->
+<!--
+    <filter-mapping>
+        <filter-name>Set Character Encoding</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+-->
+
+<!--
+    <filter-mapping>
+      <filter-name>Compression Filter</filter-name>
+      <url-pattern>/CompressionTest</url-pattern>
+    </filter-mapping>
+-->
+
+<!--
+    <filter-mapping>
+        <filter-name>Request Dumper Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+-->
+
+    <!-- Define example application events listeners -->
+    <listener>
+        <listener-class>listeners.ContextListener</listener-class>
+    </listener>
+    <listener>
+        <listener-class>listeners.SessionListener</listener-class>
+    </listener>
+
+    <!-- Define servlets that are included in the example application -->
+
+    <servlet>
+      <servlet-name>
+          servletToJsp
+      </servlet-name>
+      <servlet-class>
+          servletToJsp
+      </servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>
+            CompressionFilterTestServlet
+        </servlet-name>
+        <servlet-class>
+            compressionFilters.CompressionFilterTestServlet
+        </servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>
+            CompressionFilterTestServlet
+        </servlet-name>
+        <url-pattern>
+            /CompressionTest
+        </url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>
+            servletToJsp
+        </servlet-name>
+        <url-pattern>
+            /servletToJsp
+        </url-pattern>
+    </servlet-mapping>
+
+    <jsp-config>
+        <taglib>
+	    <taglib-uri>
+	       http://jakarta.apache.org/tomcat/debug-taglib
+	    </taglib-uri>
+	    <taglib-location>
+	       /WEB-INF/jsp/debug-taglib.tld
+	    </taglib-location>
+	</taglib>
+
+	<taglib>
+	    <taglib-uri>
+	       http://jakarta.apache.org/tomcat/examples-taglib
+	    </taglib-uri>
+	    <taglib-location>
+	       /WEB-INF/jsp/example-taglib.tld
+	    </taglib-location>
+	</taglib>
+
+	<taglib>
+	    <taglib-uri>
+	       http://jakarta.apache.org/tomcat/jsp2-example-taglib
+	    </taglib-uri>
+	    <taglib-location>
+	       /WEB-INF/jsp2/jsp2-example-taglib.tld
+	    </taglib-location>
+	</taglib>
+
+	<jsp-property-group>
+	    <description>
+		Special property group for JSP Configuration JSP example.
+	    </description>
+	    <display-name>JSPConfiguration</display-name>
+	    <url-pattern>/jsp2/misc/config.jsp</url-pattern>
+	    <el-ignored>true</el-ignored>
+	    <page-encoding>ISO-8859-1</page-encoding>
+	    <scripting-invalid>true</scripting-invalid>
+	    <include-prelude>/jsp2/misc/prelude.jspf</include-prelude>
+	    <include-coda>/jsp2/misc/coda.jspf</include-coda>
+	</jsp-property-group>
+    </jsp-config>
+    
+   <security-constraint>
+      <display-name>Example Security Constraint</display-name>
+      <web-resource-collection>
+         <web-resource-name>Protected Area</web-resource-name>
+	 <!-- Define the context-relative URL(s) to be protected -->
+         <url-pattern>/security/protected/*</url-pattern>
+	 <!-- If you list http methods, only those methods are protected -->
+	 <http-method>DELETE</http-method>
+         <http-method>GET</http-method>
+         <http-method>POST</http-method>
+	 <http-method>PUT</http-method>
+      </web-resource-collection>
+      <auth-constraint>
+         <!-- Anyone with one of the listed roles may access this area -->
+         <role-name>tomcat</role-name>
+	 <role-name>role1</role-name>
+      </auth-constraint>
+    </security-constraint>
+
+    <!-- Default login configuration uses form-based authentication -->
+    <login-config>
+      <auth-method>FORM</auth-method>
+      <realm-name>Example Form-Based Authentication Area</realm-name>
+      <form-login-config>
+        <form-login-page>/security/protected/login.jsp</form-login-page>
+        <form-error-page>/security/protected/error.jsp</form-error-page>
+      </form-login-config>
+    </login-config>
+        
+    <!-- Security roles referenced by this web application -->
+    <security-role>
+      <role-name>role1</role-name>
+    </security-role>
+    <security-role>
+      <role-name>tomcat</role-name>
+    </security-role>    
+
+    <!-- Environment entry examples -->
+    <!--env-entry>
+      <env-entry-description>
+         The maximum number of tax exemptions allowed to be set.
+      </env-entry-description>
+      <env-entry-name>maxExemptions</env-entry-name>
+      <env-entry-type>java.lang.Integer</env-entry-type>
+      <env-entry-value>15</env-entry-value>
+    </env-entry-->
+    <env-entry>
+      <env-entry-name>minExemptions</env-entry-name>
+      <env-entry-type>java.lang.Integer</env-entry-type>
+      <env-entry-value>1</env-entry-value>
+    </env-entry>
+    <env-entry>
+      <env-entry-name>foo/name1</env-entry-name>
+      <env-entry-type>java.lang.String</env-entry-type>
+      <env-entry-value>value1</env-entry-value>
+    </env-entry>
+    <env-entry>
+      <env-entry-name>foo/bar/name2</env-entry-name>
+      <env-entry-type>java.lang.Boolean</env-entry-type>
+      <env-entry-value>true</env-entry-value>
+    </env-entry>
+    <env-entry>
+      <env-entry-name>name3</env-entry-name>
+      <env-entry-type>java.lang.Integer</env-entry-type>
+      <env-entry-value>1</env-entry-value>
+    </env-entry>
+    <env-entry>
+      <env-entry-name>foo/name4</env-entry-name>
+      <env-entry-type>java.lang.Integer</env-entry-type>
+      <env-entry-value>10</env-entry-value>
+    </env-entry>
+
+</web-app>
diff --git a/servletapi/jsr152/examples/cal/cal1.jsp b/servletapi/jsr152/examples/cal/cal1.jsp
new file mode 100644
index 0000000..b3a766d
--- /dev/null
+++ b/servletapi/jsr152/examples/cal/cal1.jsp
@@ -0,0 +1,94 @@
+<HTML>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<HEAD><TITLE> 
+	Calendar: A JSP APPLICATION
+</TITLE></HEAD>
+
+
+<BODY BGCOLOR="white">
+
+<%@ page language="java" import="cal.*" %>
+<jsp:useBean id="table" scope="session" class="cal.TableBean" />
+
+<%
+	table.processRequest(request);
+	if (table.getProcessError() == false) {
+%>
+
+<!-- html table goes here -->
+<CENTER>
+<TABLE WIDTH=60% BGCOLOR=yellow CELLPADDING=15>
+<TR>
+<TD ALIGN=CENTER> <A HREF=cal1.jsp?date=prev> prev </A>
+<TD ALIGN=CENTER> Calendar:<%= table.getDate() %></TD>
+<TD ALIGN=CENTER> <A HREF=cal1.jsp?date=next> next </A>
+</TR>
+</TABLE>
+
+<!-- the main table -->
+<TABLE WIDTH=60% BGCOLOR=lightblue BORDER=1 CELLPADDING=10>
+<TR>
+<TH> Time </TH>
+<TH> Appointment </TH>
+</TR>
+<FORM METHOD=POST ACTION=cal1.jsp>
+<%
+	for(int i=0; i<table.getEntries().getRows(); i++) {
+	   cal.Entry entr = table.getEntries().getEntry(i);	
+%>
+	<TR>
+	<TD> 
+	<A HREF=cal2.jsp?time=<%= entr.getHour() %>>
+		<%= entr.getHour() %> </A>
+	</TD>
+	<TD BGCOLOR=<%= entr.getColor() %>>
+	<% out.print(util.HTMLFilter.filter(entr.getDescription())); %>
+	</TD> 
+	</TR>
+<%
+	}
+%>
+</FORM>
+</TABLE>
+<BR>
+
+<!-- footer -->
+<TABLE WIDTH=60% BGCOLOR=yellow CELLPADDING=15>
+<TR>
+<TD ALIGN=CENTER>  <% out.print(util.HTMLFilter.filter(table.getName())); %> : 
+		     <% out.print(util.HTMLFilter.filter(table.getEmail())); %> </TD>
+</TR>
+</TABLE>
+</CENTER>
+
+<%
+	} else {
+%>
+<font size=5>
+	You must enter your name and email address correctly.
+</font>
+<%
+	}
+%>
+
+
+</BODY>
+</HTML>
+
+
+
+
diff --git a/servletapi/jsr152/examples/cal/cal2.jsp b/servletapi/jsr152/examples/cal/cal2.jsp
new file mode 100644
index 0000000..cfebd8a
--- /dev/null
+++ b/servletapi/jsr152/examples/cal/cal2.jsp
@@ -0,0 +1,44 @@
+<HTML>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<HEAD><TITLE> 
+	Calendar: A JSP APPLICATION
+</TITLE></HEAD>
+
+
+<BODY BGCOLOR="white">
+<jsp:useBean id="table" scope="session" class="cal.TableBean" />
+
+<% 
+	String time = request.getParameter ("time");
+%>
+
+<FONT SIZE=5> Please add the following event:
+<BR> <h3> Date <%= table.getDate() %>
+<BR> Time <%= time %> </h3>
+</FONT>
+<FORM METHOD=POST ACTION=cal1.jsp>
+<BR> 
+<BR> <INPUT NAME="date" TYPE=HIDDEN VALUE="current">
+<BR> <INPUT NAME="time" TYPE=HIDDEN VALUE=<%= time %>
+<BR> <h2> Description of the event <INPUT NAME="description" TYPE=TEXT SIZE=20> </h2>
+<BR> <INPUT TYPE=SUBMIT VALUE="submit">
+</FORM>
+
+</BODY>
+</HTML>
+
diff --git a/servletapi/jsr152/examples/cal/calendar.html b/servletapi/jsr152/examples/cal/calendar.html
new file mode 100644
index 0000000..8b1a74c
--- /dev/null
+++ b/servletapi/jsr152/examples/cal/calendar.html
@@ -0,0 +1,42 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="login.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h2> Source Code for Calendar Example. <br>
+<h3><a href="cal1.jsp.html">cal1.jsp<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="cal2.jsp.html">cal2.jsp<font color="#0000FF"></a>
+  </font> </h3>
+
+<br>
+<h2> Beans.
+<h3><a href="TableBean.java.html">TableBean<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="Entries.java.html">Entries<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="Entry.java.html">Entry<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/cal/login.html b/servletapi/jsr152/examples/cal/login.html
new file mode 100644
index 0000000..cb8ba42
--- /dev/null
+++ b/servletapi/jsr152/examples/cal/login.html
@@ -0,0 +1,46 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+	<title> Login page for the calendar. </title>
+</head>
+
+<body bgcolor="white">
+<center>
+
+	<font size=7 color="red"> Please Enter the following information: </font>
+
+<br>
+	<form method=GET action=cal1.jsp>
+
+		<font size=5> Name <input type=text name="name" size=20>
+		</font>
+		<br>
+		<font size=5> Email <input type=text name="email" size=20>
+		</font>
+		<br>
+		<input type=submit name=action value="Submit">
+
+	</form>
+<hr>
+<font size=3 color="red"> Note: This application does not implement the complete 
+functionality of a typical calendar application. It demostartes a way JSP can be 
+used with html tables and forms.</font>
+
+</center>
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/checkbox/CheckTest.html b/servletapi/jsr152/examples/checkbox/CheckTest.html
new file mode 100644
index 0000000..b26c367
--- /dev/null
+++ b/servletapi/jsr152/examples/checkbox/CheckTest.html
@@ -0,0 +1,55 @@
+<HTML>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<HEAD>
+<title>
+checkbox.CheckTest Bean Properties
+</title>
+<BODY BGCOLOR="white">
+<H2>
+checkbox.CheckTest Bean Properties
+</H2>
+<HR>
+<DL>
+<DT>public class <B>CheckTest</B><DT>extends Object</DL>
+
+<P>
+<HR>
+
+<P>
+
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0">
+<TR BGCOLOR="#EEEEFF">
+<TD COLSPAN=3><FONT SIZE="+2">
+<B>Properties Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white">
+<td align="right" valign="top" width="1%">
+<FONT SIZE="-1">
+String
+</FONT></TD>
+<TD><B>CheckTest:fruit</B>
+<BR>
+       </TD>
+<td width="1%">
+<FONT SIZE="-1">
+Multi
+</FONT></TD>
+</TABLE>
+<HR>
+</BODY>
+</HTML>
diff --git a/servletapi/jsr152/examples/checkbox/check.html b/servletapi/jsr152/examples/checkbox/check.html
new file mode 100644
index 0000000..dabc610
--- /dev/null
+++ b/servletapi/jsr152/examples/checkbox/check.html
@@ -0,0 +1,37 @@
+<HTML>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<BODY bgcolor="white">
+
+
+<FORM TYPE=POST ACTION=checkresult.jsp>
+<BR>
+<font size=5 color="red">
+Check all Favorite fruits: <br>
+
+<input TYPE=checkbox name=fruit VALUE=apples> Apples <BR>
+<input TYPE=checkbox name=fruit VALUE=grapes> Grapes <BR>
+<input TYPE=checkbox name=fruit VALUE=oranges> Oranges <BR>
+<input TYPE=checkbox name=fruit VALUE=melons> Melons <BR>
+
+
+<br> <INPUT TYPE=submit name=submit Value="Submit">
+
+</font>
+</FORM>
+</BODY>
+</HTML>
diff --git a/servletapi/jsr152/examples/checkbox/checkresult.jsp b/servletapi/jsr152/examples/checkbox/checkresult.jsp
new file mode 100644
index 0000000..fb188bb
--- /dev/null
+++ b/servletapi/jsr152/examples/checkbox/checkresult.jsp
@@ -0,0 +1,63 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<body bgcolor="white">
+<font size=5 color="red">
+<%! String[] fruits; %>
+<jsp:useBean id="foo" scope="page" class="checkbox.CheckTest" />
+
+<jsp:setProperty name="foo" property="fruit" param="fruit" />
+<hr>
+The checked fruits (got using request) are: <br>
+<% 
+	fruits = request.getParameterValues("fruit");
+%>
+<ul>
+<%
+    if (fruits != null) {
+	  for (int i = 0; i < fruits.length; i++) {
+%>
+<li>
+<%
+	      out.println (util.HTMLFilter.filter(fruits[i]));
+	  }
+	} else out.println ("none selected");
+%>
+</ul>
+<br>
+<hr>
+
+The checked fruits (got using beans) are <br>
+
+<% 
+		fruits = foo.getFruit();
+%>
+<ul>
+<%
+    if (!fruits[0].equals("1")) {
+	  for (int i = 0; i < fruits.length; i++) {
+%>
+<li>
+<%
+		  out.println (util.HTMLFilter.filter(fruits[i]));
+	  }
+	} else out.println ("none selected");
+%>
+</ul>
+</font>
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/checkbox/cresult.html b/servletapi/jsr152/examples/checkbox/cresult.html
new file mode 100644
index 0000000..1668e5b
--- /dev/null
+++ b/servletapi/jsr152/examples/checkbox/cresult.html
@@ -0,0 +1,33 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="check.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="checkresult.jsp.html">Source Code for Checkbox Example<font color="#0000FF"></a>
+  </font> </h3>
+
+<h3><a href="CheckTest.html">Property Sheet for CheckTest
+<font color="#0000FF"></a> </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/colors/ColorGameBean.html b/servletapi/jsr152/examples/colors/ColorGameBean.html
new file mode 100644
index 0000000..11a33f1
--- /dev/null
+++ b/servletapi/jsr152/examples/colors/ColorGameBean.html
@@ -0,0 +1,115 @@
+<HTML>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<HEAD>
+<title>
+colors.ColorGameBean Bean Properties
+</title>
+<BODY BGCOLOR="white">
+<H2>
+colors.ColorGameBean Bean Properties
+</H2>
+<HR>
+<DL>
+<DT>public class <B>ColorGameBean</B><DT>extends Object</DL>
+
+<P>
+<HR>
+
+<P>
+
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0">
+<TR BGCOLOR="#EEEEFF">
+<TD COLSPAN=3><FONT SIZE="+2">
+<B>Properties Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white">
+<td align="right" valign="top" width="1%">
+<FONT SIZE="-1">
+String
+</FONT></TD>
+<TD><B>ColorGameBean:color2</B>
+<BR>
+       </TD>
+<td width="1%">
+<FONT SIZE="-1">
+Single
+</FONT></TD>
+<TR BGCOLOR="white">
+<td align="right" valign="top" width="1%">
+<FONT SIZE="-1">
+String
+</FONT></TD>
+<TD><B>ColorGameBean:color1</B>
+<BR>
+       </TD>
+<td width="1%">
+<FONT SIZE="-1">
+Single
+</FONT></TD>
+<TR BGCOLOR="white">
+<td align="right" valign="top" width="1%">
+<FONT SIZE="-1">
+int
+</FONT></TD>
+<TD><B>ColorGameBean:attempts</B>
+<BR>
+       </TD>
+<td width="1%">
+<FONT SIZE="-1">
+Single
+</FONT></TD>
+<TR BGCOLOR="white">
+<td align="right" valign="top" width="1%">
+<FONT SIZE="-1">
+boolean
+</FONT></TD>
+<TD><B>ColorGameBean:hint</B>
+<BR>
+       </TD>
+<td width="1%">
+<FONT SIZE="-1">
+Single
+</FONT></TD>
+<TR BGCOLOR="white">
+<td align="right" valign="top" width="1%">
+<FONT SIZE="-1">
+boolean
+</FONT></TD>
+<TD><B>ColorGameBean:success</B>
+<BR>
+       </TD>
+<td width="1%">
+<FONT SIZE="-1">
+Single
+</FONT></TD>
+<TR BGCOLOR="white">
+<td align="right" valign="top" width="1%">
+<FONT SIZE="-1">
+boolean
+</FONT></TD>
+<TD><B>ColorGameBean:hintTaken</B>
+<BR>
+       </TD>
+<td width="1%">
+<FONT SIZE="-1">
+Single
+</FONT></TD>
+</TABLE>
+<HR>
+</BODY>
+</HTML>
diff --git a/servletapi/jsr152/examples/colors/clr.html b/servletapi/jsr152/examples/colors/clr.html
new file mode 100644
index 0000000..69713e1
--- /dev/null
+++ b/servletapi/jsr152/examples/colors/clr.html
@@ -0,0 +1,33 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="colors.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="colrs.jsp.html">Source Code for Color Example<font color="#0000FF"></a>
+  </font> </h3>
+
+<h3><a href="ColorGameBean.html">Property Sheet for ColorGameBean
+<font color="#0000FF"></a> </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/colors/colors.html b/servletapi/jsr152/examples/colors/colors.html
new file mode 100644
index 0000000..bf061fe
--- /dev/null
+++ b/servletapi/jsr152/examples/colors/colors.html
@@ -0,0 +1,46 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<body bgcolor= white>
+<font size=6 color=red>
+
+<hr>
+This web page is an example using JSP and BEANs. 
+<p>
+Guess my favorite two colors 
+
+<p> If you fail to guess both of them - you get yellow on red.
+
+<p> If you guess one of them right, either your foreground or 
+    your background will change to the color that was guessed right.
+
+<p> Guess them both right and your browser foreground/background 
+    will change to my two favorite colors to display this page.
+
+<hr>
+<form method=GET action=colrs.jsp>
+Color #1: <input type=text name= color1 size=16>
+<br>
+Color #2: <input type=text name= color2 size=16>
+<p>
+<input type=submit name=action value="Submit">
+<input type=submit name=action value="Hint">
+</form>
+
+</font>
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/colors/colrs.jsp b/servletapi/jsr152/examples/colors/colrs.jsp
new file mode 100644
index 0000000..8bb7c60
--- /dev/null
+++ b/servletapi/jsr152/examples/colors/colrs.jsp
@@ -0,0 +1,69 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<jsp:useBean id="cb" scope="session" class="colors.ColorGameBean" />
+<jsp:setProperty name="cb" property="*" />
+
+<%
+	cb.processRequest(request);
+%>
+
+<body bgcolor=<%= cb.getColor1() %>>
+<font size=6 color=<%= cb.getColor2() %>>
+<p>
+
+<% if (cb.getHint()==true) { %>
+	
+	<p> Hint #1: Vampires prey at night!
+	<p>  <p> Hint #2: Nancy without the n.
+
+<% } %>
+
+<% if  (cb.getSuccess()==true) { %>
+
+    <p> CONGRATULATIONS!!
+	<% if  (cb.getHintTaken()==true) { %>
+    
+        <p> ( although I know you cheated and peeked into the hints)
+
+	<% } %>
+
+<% } %>
+
+<p> Total attempts so far: <%= cb.getAttempts() %>
+<p>
+
+<p>
+
+<form method=POST action=colrs.jsp>
+
+Color #1: <input type=text name= color1 size=16>
+
+<br>
+
+Color #2: <input type=text name= color2 size=16>
+
+<p>
+
+<input type=submit name=action value="Submit">
+<input type=submit name=action value="Hint">
+
+</form>
+
+</font>
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/dates/date.html b/servletapi/jsr152/examples/dates/date.html
new file mode 100644
index 0000000..e8081ca
--- /dev/null
+++ b/servletapi/jsr152/examples/dates/date.html
@@ -0,0 +1,30 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="date.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="date.jsp.html">Source Code for Date Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/dates/date.jsp b/servletapi/jsr152/examples/dates/date.jsp
new file mode 100644
index 0000000..7f3c3b4
--- /dev/null
+++ b/servletapi/jsr152/examples/dates/date.jsp
@@ -0,0 +1,40 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<%@ page session="false"%>
+
+<body bgcolor="white">
+<jsp:useBean id='clock' scope='page' class='dates.JspCalendar' type="dates.JspCalendar" />
+
+<font size=4>
+<ul>
+<li>	Day of month: is  <jsp:getProperty name="clock" property="dayOfMonth"/>
+<li>	Year: is  <jsp:getProperty name="clock" property="year"/>
+<li>	Month: is  <jsp:getProperty name="clock" property="month"/>
+<li>	Time: is  <jsp:getProperty name="clock" property="time"/>
+<li>	Date: is  <jsp:getProperty name="clock" property="date"/>
+<li>	Day: is  <jsp:getProperty name="clock" property="day"/>
+<li>	Day Of Year: is  <jsp:getProperty name="clock" property="dayOfYear"/>
+<li>	Week Of Year: is  <jsp:getProperty name="clock" property="weekOfYear"/>
+<li>	era: is  <jsp:getProperty name="clock" property="era"/>
+<li>	DST Offset: is  <jsp:getProperty name="clock" property="DSTOffset"/>
+<li>	Zone Offset: is  <jsp:getProperty name="clock" property="zoneOffset"/>
+</ul>
+</font>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/error/er.html b/servletapi/jsr152/examples/error/er.html
new file mode 100644
index 0000000..1bea6ee
--- /dev/null
+++ b/servletapi/jsr152/examples/error/er.html
@@ -0,0 +1,30 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="error.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="err.jsp.html">Source Code for Error Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/error/err.jsp b/servletapi/jsr152/examples/error/err.jsp
new file mode 100644
index 0000000..3076380
--- /dev/null
+++ b/servletapi/jsr152/examples/error/err.jsp
@@ -0,0 +1,43 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<body bgcolor="lightblue">
+
+	<%@ page errorPage="errorpge.jsp" %>
+	<jsp:useBean id="foo" scope="request" class="error.Smart" />
+	<% 
+		String name = null;
+
+		if (request.getParameter("name") == null) {
+	%>
+	<%@ include file="/error/error.html" %>
+	<%
+		} else {
+		  foo.setName(request.getParameter("name"));
+		  if (foo.getName().equalsIgnoreCase("integra"))
+		  	name = "acura";
+		  if (name.equalsIgnoreCase("acura")) {
+	%>
+
+	<H1> Yes!!! <a href="http://www.acura.com">Acura</a> is my favorite car.
+
+	<% 
+		  }
+		}	
+	%>	
+</body>
+</html>
+
diff --git a/servletapi/jsr152/examples/error/error.html b/servletapi/jsr152/examples/error/error.html
new file mode 100644
index 0000000..7487188
--- /dev/null
+++ b/servletapi/jsr152/examples/error/error.html
@@ -0,0 +1,36 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<body bgcolor="white">
+
+<h1> This example uses <b>errorpage</b> directive </h1>
+<br>
+<h3> Select my favourite car.</h3>
+<form method=get action=err.jsp>
+<!-- <br> Make a guess: -->
+<SELECT NAME="name" SIZE=5>
+<OPTION VALUE="integra"> Acura Integra <BR>
+<OPTION VALUE="bmw328i"> BMW 328I <BR>
+<OPTION VALUE="z3"> BMW Z3 <BR>
+<OPTION VALUE="infiniti"> InfinitiQ3 <BR>
+<OPTION VALUE="audi"> Audi A8 <BR>
+</SELECT>
+<br> <INPUT TYPE=submit name=submit Value="Submit">
+</form>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/error/errorpge.jsp b/servletapi/jsr152/examples/error/errorpge.jsp
new file mode 100644
index 0000000..c91ce61
--- /dev/null
+++ b/servletapi/jsr152/examples/error/errorpge.jsp
@@ -0,0 +1,24 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<body bgcolor="red">
+
+	<%@ page isErrorPage="true" %>
+	<h1> The exception <%= exception.getMessage() %> tells me you
+	     made a wrong choice. 
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/forward/forward.jsp b/servletapi/jsr152/examples/forward/forward.jsp
new file mode 100644
index 0000000..4a37fd0
--- /dev/null
+++ b/servletapi/jsr152/examples/forward/forward.jsp
@@ -0,0 +1,22 @@
+<html>
+<!--
+  Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  reserved.
+-->
+
+<% 
+   double freeMem = Runtime.getRuntime().freeMemory();
+   double totlMem = Runtime.getRuntime().totalMemory();
+   double percent = freeMem/totlMem;
+   if (percent < 0.5) { 
+%>
+
+<jsp:forward page="/forward/one.jsp"/>
+
+<% } else { %>
+
+<jsp:forward page="two.html"/>
+
+<% } %>
+
+</html>
diff --git a/servletapi/jsr152/examples/forward/fwd.html b/servletapi/jsr152/examples/forward/fwd.html
new file mode 100644
index 0000000..980a53f
--- /dev/null
+++ b/servletapi/jsr152/examples/forward/fwd.html
@@ -0,0 +1,18 @@
+<html>
+<!--
+  Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  reserved.
+-->
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="forward.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="forward.jsp.html">Source Code for Forward Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/forward/one.jsp b/servletapi/jsr152/examples/forward/one.jsp
new file mode 100644
index 0000000..4afe6f2
--- /dev/null
+++ b/servletapi/jsr152/examples/forward/one.jsp
@@ -0,0 +1,11 @@
+<html>
+<!--
+  Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  reserved.
+-->
+
+<body bgcolor="white">
+<font color="red">
+
+VM Memory usage < 50%.
+</html>
\ No newline at end of file
diff --git a/servletapi/jsr152/examples/forward/two.html b/servletapi/jsr152/examples/forward/two.html
new file mode 100644
index 0000000..3bc2917
--- /dev/null
+++ b/servletapi/jsr152/examples/forward/two.html
@@ -0,0 +1,11 @@
+<html>
+<!--
+  Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  reserved.
+-->
+
+<body bgcolor="white">
+<font color="red">
+
+VM Memory usage > 50%.
+</html>
\ No newline at end of file
diff --git a/servletapi/jsr152/examples/images/code.gif b/servletapi/jsr152/examples/images/code.gif
new file mode 100644
index 0000000..93af2cd
--- /dev/null
+++ b/servletapi/jsr152/examples/images/code.gif
Binary files differ
diff --git a/servletapi/jsr152/examples/images/execute.gif b/servletapi/jsr152/examples/images/execute.gif
new file mode 100644
index 0000000..f64d70f
--- /dev/null
+++ b/servletapi/jsr152/examples/images/execute.gif
Binary files differ
diff --git a/servletapi/jsr152/examples/images/read.gif b/servletapi/jsr152/examples/images/read.gif
new file mode 100644
index 0000000..66cb4e9
--- /dev/null
+++ b/servletapi/jsr152/examples/images/read.gif
Binary files differ
diff --git a/servletapi/jsr152/examples/images/return.gif b/servletapi/jsr152/examples/images/return.gif
new file mode 100644
index 0000000..af4f68f
--- /dev/null
+++ b/servletapi/jsr152/examples/images/return.gif
Binary files differ
diff --git a/servletapi/jsr152/examples/include/foo.html b/servletapi/jsr152/examples/include/foo.html
new file mode 100644
index 0000000..47d1464
--- /dev/null
+++ b/servletapi/jsr152/examples/include/foo.html
@@ -0,0 +1,16 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+To get the current time in ms
diff --git a/servletapi/jsr152/examples/include/foo.jsp b/servletapi/jsr152/examples/include/foo.jsp
new file mode 100644
index 0000000..0130b07
--- /dev/null
+++ b/servletapi/jsr152/examples/include/foo.jsp
@@ -0,0 +1,20 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<body bgcolor="white">
+<font color="red">
+
+<%= System.currentTimeMillis() %>
diff --git a/servletapi/jsr152/examples/include/inc.html b/servletapi/jsr152/examples/include/inc.html
new file mode 100644
index 0000000..1bb3fb3
--- /dev/null
+++ b/servletapi/jsr152/examples/include/inc.html
@@ -0,0 +1,29 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="include.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="include.jsp.html">Source Code for Include Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/include/include.jsp b/servletapi/jsr152/examples/include/include.jsp
new file mode 100644
index 0000000..fef9736
--- /dev/null
+++ b/servletapi/jsr152/examples/include/include.jsp
@@ -0,0 +1,34 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<body bgcolor="white">
+
+<font color="red">
+
+<%@ page buffer="5kb" autoFlush="false" %>
+
+<p>In place evaluation of another JSP which gives you the current time:
+
+<%@ include file="foo.jsp" %>
+
+<p> <jsp:include page="/include/foo.html" flush="true"/> by including the output of another JSP:
+
+<jsp:include page="foo.jsp" flush="true"/>
+
+:-) 
+
+</html>
diff --git a/servletapi/jsr152/examples/index.html b/servletapi/jsr152/examples/index.html
new file mode 100644
index 0000000..546c2b6
--- /dev/null
+++ b/servletapi/jsr152/examples/index.html
@@ -0,0 +1,373 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+\<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="GENERATOR" content="Mozilla/4.61 [en] (WinNT; I) [Netscape]">
+   <meta name="Author" content="Anil K. Vijendran">
+   <title>JSP Examples</title>
+<!--
+  Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  reserved.
+-->
+</head>
+<body bgcolor="#FFFFFF">
+<b><font face="Arial, Helvetica, sans-serif"><font size=+2>JSP
+Samples</font></font></b>
+<p>This is a collection of samples demonstrating the usage of different
+parts of the Java Server Pages (JSP) specification.  Both JSP 2.0 and
+JSP 1.2 examples are presented below.
+<p>These examples will only work when these pages are being served by a
+servlet engine; of course, we recommend
+<a href="http://jakarta.apache.org/tomcat/">Tomcat</a>.
+They will not work if you are viewing these pages via a
+"file://..." URL.
+<p>To navigate your way through the examples, the following icons will
+help:
+<br>&nbsp;
+<table BORDER=0 CELLSPACING=5 WIDTH="85%" >
+<tr VALIGN=TOP>
+<td WIDTH="30"><img SRC="images/execute.gif" ></td>
+
+<td>Execute the example</td>
+</tr>
+
+<tr VALIGN=TOP>
+<td WIDTH="30"><img SRC="images/code.gif" height=24 width=24></td>
+
+<td>Look at the source code for the example</td>
+</tr>
+
+<!--<tr VALIGN=TOP>
+<td WIDTH="30"><img SRC="images/read.gif" height=24 width=24></td>
+
+<td>Read more about this feature</td>
+-->
+
+</tr>
+<tr VALIGN=TOP>
+<td WIDTH="30"><img SRC="images/return.gif" height=24 width=24></td>
+
+<td>Return to this screen</td>
+</tr>
+</table>
+
+<p>Tip: For session scoped beans to work, the cookies must be enabled.
+This can be done using browser options.
+<br>&nbsp;
+<br>
+<b><u><font size="+1">JSP 2.0 Examples</font></u></b><br>
+
+<table BORDER=0 CELLSPACING=5 WIDTH="85%" >
+<tr valign=TOP>
+<td><b>Expression Language</b></td>
+</tr>
+
+<tr valign=TOP>
+<td>Basic Arithmetic</td>
+<td valign=TOP width="30%"><a href="jsp2/el/basic-arithmetic.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/el/basic-arithmetic.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/el/basic-arithmetic.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/el/basic-arithmetic.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>Basic Comparisons</td>
+<td valign=TOP width="30%"><a href="jsp2/el/basic-comparisons.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/el/basic-comparisons.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/el/basic-comparisons.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/el/basic-comparisons.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>Implicit Objects</td>
+<td valign=TOP width="30%"><a href="jsp2/el/implicit-objects.jsp?foo=bar"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/el/implicit-objects.jsp?foo=bar">Execute</a></td>
+
+<td width="30%"><a href="jsp2/el/implicit-objects.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/el/implicit-objects.html">Source</a></td>
+</tr>
+<tr valign=TOP>
+
+<td>Functions</td>
+<td valign=TOP width="30%"><a href="jsp2/el/functions.jsp?foo=JSP+2.0"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/el/functions.jsp?foo=JSP+2.0">Execute</a></td>
+
+<td width="30%"><a href="jsp2/el/functions.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/el/functions.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td><br><b>SimpleTag Handlers and JSP Fragments</b></td>
+</tr>
+
+<tr valign=TOP>
+<td>Hello World Tag</td>
+<td valign=TOP width="30%"><a href="jsp2/simpletag/hello.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/simpletag/hello.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/simpletag/hello.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/simpletag/hello.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>Repeat Tag</td>
+<td valign=TOP width="30%"><a href="jsp2/simpletag/repeat.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/simpletag/repeat.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/simpletag/repeat.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/simpletag/repeat.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>Book Example</td>
+<td valign=TOP width="30%"><a href="jsp2/simpletag/book.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/simpletag/book.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/simpletag/book.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/simpletag/book.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td><br><b>Tag Files</b></td>
+</tr>
+
+<tr valign=TOP>
+<td>Hello World Tag File</td>
+<td valign=TOP width="30%"><a href="jsp2/tagfiles/hello.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/tagfiles/hello.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/tagfiles/hello.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/tagfiles/hello.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>Panel Tag File</td>
+<td valign=TOP width="30%"><a href="jsp2/tagfiles/panel.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/tagfiles/panel.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/tagfiles/panel.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/tagfiles/panel.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>Display Products Example</td>
+<td valign=TOP width="30%"><a href="jsp2/tagfiles/products.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/tagfiles/products.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/tagfiles/products.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/tagfiles/products.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td><br><b>New JSP XML Syntax (.jspx)</b></td>
+</tr>
+
+<tr valign=TOP>
+<td>XHTML Basic Example</td>
+<td valign=TOP width="30%"><a href="jsp2/jspx/basic.jspx"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/jspx/basic.jspx">Execute</a></td>
+
+<td width="30%"><a href="jsp2/jspx/basic.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/jspx/basic.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>SVG (Scalable Vector Graphics)</td>
+<td valign=TOP width="30%"><a href="jsp2/jspx/svgexample.html"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/jspx/svgexample.html">Execute</a></td>
+
+<td width="30%"><a href="jsp2/jspx/textRotate.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/jspx/textRotate.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td><br><b>Other JSP 2.0 Features</b></td>
+</tr>
+
+<tr valign=TOP>
+<td>&lt;jsp:attribute&gt; and &lt;jsp:body&gt;</td>
+<td valign=TOP width="30%"><a href="jsp2/jspattribute/jspattribute.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/jspattribute/jspattribute.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/jspattribute/jspattribute.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/jspattribute/jspattribute.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>Shuffle Example</td>
+<td valign=TOP width="30%"><a href="jsp2/jspattribute/shuffle.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/jspattribute/shuffle.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/jspattribute/shuffle.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/jspattribute/shuffle.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>Attributes With Dynamic Names</td>
+<td valign=TOP width="30%"><a href="jsp2/misc/dynamicattrs.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/misc/dynamicattrs.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/misc/dynamicattrs.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/misc/dynamicattrs.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>JSP Configuration</td>
+<td valign=TOP width="30%"><a href="jsp2/misc/config.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="jsp2/misc/config.jsp">Execute</a></td>
+
+<td width="30%"><a href="jsp2/misc/config.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsp2/misc/config.html">Source</a></td>
+</tr>
+
+</table>
+
+<br>
+<b><u><font size="+1">JSP 1.2 Examples</font></u></b><br>
+<table BORDER=0 CELLSPACING=5 WIDTH="85%" >
+<tr VALIGN=TOP>
+<td>Numberguess&nbsp;</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="num/numguess.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="num/numguess.jsp">Execute</a></td>
+
+<td WIDTH="30%"><a href="num/numguess.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="num/numguess.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Date&nbsp;</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="dates/date.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="dates/date.jsp">Execute</a></td>
+
+<td WIDTH="30%"><a href="dates/date.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="dates/date.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Snoop</td>
+
+<td WIDTH="30%"><a href="snp/snoop.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="snp/snoop.jsp">Execute</a></td>
+
+<td WIDTH="30%"><a href="snp/snoop.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="snp/snoop.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>ErrorPage&nbsp;</td>
+
+<td WIDTH="30%"><a href="error/error.html"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="error/error.html">Execute</a></td>
+
+<td WIDTH="30%"><a href="error/er.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="error/er.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Carts&nbsp;</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="sessions/carts.html"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="sessions/carts.html">Execute</a></td>
+
+<td WIDTH="30%"><a href="sessions/crt.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="sessions/crt.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Checkbox&nbsp;</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="checkbox/check.html"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="checkbox/check.html">Execute</a></td>
+
+<td WIDTH="30%"><a href="checkbox/cresult.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="checkbox/cresult.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Color&nbsp;</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="colors/colors.html"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="colors/colors.html">Execute</a></td>
+
+<td WIDTH="30%"><a href="colors/clr.html.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="colors/clr.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Calendar&nbsp;</td>
+
+<td WIDTH="30%"><a href="cal/login.html"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="cal/login.html">Execute</a></td>
+
+<td WIDTH="30%"><a href="cal/calendar.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="cal/calendar.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Include&nbsp;</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="/include/include.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="include/include.jsp">Execute</a></td>
+
+<td WIDTH="30%"><a href="include/inc.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="include/inc.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Forward&nbsp;</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="forward/forward.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="forward/forward.jsp">Execute</a></td>
+
+<td WIDTH="30%"><a href="forward/fwd.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="forward/fwd.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Plugin&nbsp;</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="plugin/plugin.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="plugin/plugin.jsp">Execute</a></td>
+
+<td WIDTH="30%"><a href="plugin/plugin.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="plugin/plugin.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>JSP-Servlet-JSP&nbsp;</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="jsptoserv/jsptoservlet.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="jsptoserv/jsptoservlet.jsp">Execute</a></td>
+
+<td WIDTH="30%"><a href="jsptoserv/jts.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="jsptoserv/jts.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Custom tag example</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="simpletag/foo.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="simpletag/foo.jsp">Execute</a></td>
+
+<td WIDTH="30%"><a href="simpletag/foo.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="simpletag/foo.html">Source</a></td>
+</tr>
+
+<tr valign=TOP>
+<td>XML syntax example</td>
+<td valign=TOP width="30%"><a href="xml/xml.jsp"><img src="images/execute.gif" hspace=4 border=0  align=top></a><a href="xml/xml.jsp">Execute</a></td>
+
+<td width="30%"><a href="xml/xml.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="xml/xml.html">Source</a></td>
+</tr>
+
+</table>
+
+<br/>
+<b><u><font size="+1">Tag Plugins</font></u></b><br>
+<table BORDER=0 CELLSPACING=5 WIDTH="85%" >
+
+<tr VALIGN=TOP>
+  <td>If&nbsp;</td>
+  <td VALIGN=TOP WIDTH="30%">
+    <a href="tagplugin/if.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  
+align=TOP></a>
+    <a href="tagplugin/if.jsp">Execute</a>
+  </td>
+  <td WIDTH="30%">
+    <a href="tagplugin/if.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 hei
+ght=24 width=24 align=TOP></a>
+    <a href="tagplugin/if.html">Source</a>
+  </td>
+</tr>
+
+<tr VALIGN=TOP>
+  <td>ForEach&nbsp;</td>
+  <td VALIGN=TOP WIDTH="30%">
+    <a href="tagplugin/foreach.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  
+align=TOP></a>
+    <a href="tagplugin/foreach.jsp">Execute</a>
+  </td>
+  <td WIDTH="30%">
+    <a href="tagplugin/foreach.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 hei
+ght=24 width=24 align=TOP></a>
+    <a href="tagplugin/foreach.html">Source</a>
+  </td>
+</tr>
+
+<tr VALIGN=TOP>
+  <td>Choose&nbsp;</td>
+  <td VALIGN=TOP WIDTH="30%">
+    <a href="tagplugin/choose.jsp"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a>
+    <a href="tagplugin/choose.jsp">Execute</a>
+  </td>
+  <td WIDTH="30%">
+    <a href="tagplugin/choose.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a>
+    <a href="tagplugin/choose.html">Source</a>
+  </td>
+</tr>
+
+</table>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/el/basic-arithmetic.html b/servletapi/jsr152/examples/jsp2/el/basic-arithmetic.html
new file mode 100644
index 0000000..fe9c719
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/el/basic-arithmetic.html
@@ -0,0 +1,29 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="basic-arithmetic.jsp"><img src="../../images/execute.gif" align="right" border="0"></a><a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="basic-arithmetic.jsp.html">Source Code for Basic Arithmetic Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/el/basic-arithmetic.jsp b/servletapi/jsr152/examples/jsp2/el/basic-arithmetic.jsp
new file mode 100644
index 0000000..86ffb15
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/el/basic-arithmetic.jsp
@@ -0,0 +1,87 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+  <head>
+    <title>JSP 2.0 Expression Language - Basic Arithmetic</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Expression Language - Basic Arithmetic</h1>
+    <hr>
+    This example illustrates basic Expression Language arithmetic.
+    Addition (+), subtraction (-), multiplication (*), division (/ or div), 
+    and modulus (% or mod) are all supported.  Error conditions, like
+    division by zero, are handled gracefully.
+    <br>
+    <blockquote>
+      <code>
+        <table border="1">
+          <thead>
+	    <td><b>EL Expression</b></td>
+	    <td><b>Result</b></td>
+	  </thead>
+	  <tr>
+	    <td>\${1}</td>
+	    <td>${1}</td>
+	  </tr>
+	  <tr>
+	    <td>\${1 + 2}</td>
+	    <td>${1 + 2}</td>
+	  </tr>
+	  <tr>
+	    <td>\${1.2 + 2.3}</td>
+	    <td>${1.2 + 2.3}</td>
+	  </tr>
+	  <tr>
+	    <td>\${1.2E4 + 1.4}</td>
+	    <td>${1.2E4 + 1.4}</td>
+	  </tr>
+	  <tr>
+	    <td>\${-4 - 2}</td>
+	    <td>${-4 - 2}</td>
+	  </tr>
+	  <tr>
+	    <td>\${21 * 2}</td>
+	    <td>${21 * 2}</td>
+	  </tr>
+	  <tr>
+	    <td>\${3/4}</td>
+	    <td>${3/4}</td>
+	  </tr>
+	  <tr>
+	    <td>\${3 div 4}</td>
+	    <td>${3 div 4}</td>
+	  </tr>
+	  <tr>
+	    <td>\${3/0}</td>
+	    <td>${3/0}</td>
+	  </tr>
+	  <tr>
+	    <td>\${10%4}</td>
+	    <td>${10%4}</td>
+	  </tr>
+	  <tr>
+	    <td>\${10 mod 4}</td>
+	    <td>${10 mod 4}</td>
+	  </tr>
+    <tr>
+      <td>\${(1==2) ? 3 : 4}</td>
+      <td>${(1==2) ? 3 : 4}</td>
+    </tr>
+	</table>
+      </code>
+    </blockquote>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/el/basic-comparisons.html b/servletapi/jsr152/examples/jsp2/el/basic-comparisons.html
new file mode 100644
index 0000000..22cd174
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/el/basic-comparisons.html
@@ -0,0 +1,29 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="basic-comparisons.jsp"><img src="../../images/execute.gif" align="right" border="0"></a><a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="basic-comparisons.jsp.html">Source Code for Basic Comparisons Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/el/basic-comparisons.jsp b/servletapi/jsr152/examples/jsp2/el/basic-comparisons.jsp
new file mode 100644
index 0000000..1e85dba
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/el/basic-comparisons.jsp
@@ -0,0 +1,115 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+  <head>
+    <title>JSP 2.0 Expression Language - Basic Comparisons</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Expression Language - Basic Comparisons</h1>
+    <hr>
+    This example illustrates basic Expression Language comparisons.
+    The following comparison operators are supported:
+    <ul>
+      <li>Less-than (&lt; or lt)</li>
+      <li>Greater-than (&gt; or gt)</li>
+      <li>Less-than-or-equal (&lt;= or le)</li>
+      <li>Greater-than-or-equal (&gt;= or ge)</li>
+      <li>Equal (== or eq)</li>
+      <li>Not Equal (!= or ne)</li>
+    </ul>
+    <blockquote>
+      <u><b>Numeric</b></u>
+      <code>
+        <table border="1">
+          <thead>
+	    <td><b>EL Expression</b></td>
+	    <td><b>Result</b></td>
+	  </thead>
+	  <tr>
+	    <td>\${1 &lt; 2}</td>
+	    <td>${1 < 2}</td>
+	  </tr>
+	  <tr>
+	    <td>\${1 lt 2}</td>
+	    <td>${1 lt 2}</td>
+	  </tr>
+	  <tr>
+	    <td>\${1 &gt; (4/2)}</td>
+	    <td>${1 > (4/2)}</td>
+	  </tr>
+	  <tr>
+	    <td>\${1 &gt; (4/2)}</td>
+	    <td>${1 > (4/2)}</td>
+	  </tr>
+	  <tr>
+	    <td>\${4.0 &gt;= 3}</td>
+	    <td>${4.0 >= 3}</td>
+	  </tr>
+	  <tr>
+	    <td>\${4.0 ge 3}</td>
+	    <td>${4.0 ge 3}</td>
+	  </tr>
+	  <tr>
+	    <td>\${4 &lt;= 3}</td>
+	    <td>${4 <= 3}</td>
+	  </tr>
+	  <tr>
+	    <td>\${4 le 3}</td>
+	    <td>${4 le 3}</td>
+	  </tr>
+	  <tr>
+	    <td>\${100.0 == 100}</td>
+	    <td>${100.0 == 100}</td>
+	  </tr>
+	  <tr>
+	    <td>\${100.0 eq 100}</td>
+	    <td>${100.0 eq 100}</td>
+	  </tr>
+	  <tr>
+	    <td>\${(10*10) != 100}</td>
+	    <td>${(10*10) != 100}</td>
+	  </tr>
+	  <tr>
+	    <td>\${(10*10) ne 100}</td>
+	    <td>${(10*10) ne 100}</td>
+	  </tr>
+	</table>
+      </code>
+      <br>
+      <u><b>Alphabetic</b></u>
+      <code>
+        <table border="1">
+          <thead>
+	    <td><b>EL Expression</b></td>
+	    <td><b>Result</b></td>
+	  </thead>
+	  <tr>
+	    <td>\${'a' &lt; 'b'}</td>
+	    <td>${'a' < 'b'}</td>
+	  </tr>
+	  <tr>
+	    <td>\${'hip' &gt; 'hit'}</td>
+	    <td>${'hip' > 'hit'}</td>
+	  </tr>
+	  <tr>
+	    <td>\${'4' &gt; 3}</td>
+	    <td>${'4' > 3}</td>
+	  </tr>
+	</table>
+      </code>
+    </blockquote>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/el/functions.html b/servletapi/jsr152/examples/jsp2/el/functions.html
new file mode 100644
index 0000000..59b0350
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/el/functions.html
@@ -0,0 +1,31 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="functions.jsp?foo=JSP+2.0"><img src="../../images/execute.gif" align="right" border="0"></a><a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="functions.jsp.html">Source Code for functions.jsp<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="Functions.java.html">Source Code for Functions.java<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/el/functions.jsp b/servletapi/jsr152/examples/jsp2/el/functions.jsp
new file mode 100644
index 0000000..b6c53df
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/el/functions.jsp
@@ -0,0 +1,65 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
+<%@ taglib prefix="my" uri="http://jakarta.apache.org/tomcat/jsp2-example-taglib"%>
+
+<html>
+  <head>
+    <title>JSP 2.0 Expression Language - Functions</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Expression Language - Functions</h1>
+    <hr>
+    An upgrade from the JSTL expression language, the JSP 2.0 EL also
+    allows for simple function invocation.  Functions are defined
+    by tag libraries and are implemented by a Java programmer as 
+    static methods.
+
+    <blockquote>
+      <u><b>Change Parameter</b></u>
+      <form action="functions.jsp" method="GET">
+	  foo = <input type="text" name="foo" value="${fn:escapeXml(param["foo"])}">
+          <input type="submit">
+      </form>
+      <br>
+      <code>
+        <table border="1">
+          <thead>
+	    <td><b>EL Expression</b></td>
+	    <td><b>Result</b></td>
+	  </thead>
+	  <tr>
+	    <td>\${param["foo"]}</td>
+	    <td>${fn:escapeXml(param["foo"])}&nbsp;</td>
+	  </tr>
+	  <tr>
+	    <td>\${my:reverse(param["foo"])}</td>
+	    <td>${my:reverse(fn:escapeXml(param["foo"]))}&nbsp;</td>
+	  </tr>
+	  <tr>
+	    <td>\${my:reverse(my:reverse(param["foo"]))}</td>
+	    <td>${my:reverse(my:reverse(fn:escapeXml(param["foo"])))}&nbsp;</td>
+	  </tr>
+	  <tr>
+	    <td>\${my:countVowels(param["foo"])}</td>
+	    <td>${my:countVowels(fn:escapeXml(param["foo"]))}&nbsp;</td>
+	  </tr>
+	</table>
+      </code>
+    </blockquote>
+  </body>
+</html>
+
diff --git a/servletapi/jsr152/examples/jsp2/el/implicit-objects.html b/servletapi/jsr152/examples/jsp2/el/implicit-objects.html
new file mode 100644
index 0000000..391ecdb
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/el/implicit-objects.html
@@ -0,0 +1,19 @@
+<html>
+<!--
+  Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  reserved.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="implicit-objects.jsp?foo=bar"><img src="../../images/execute.gif" align="right" border="0"></a><a href="../../index.html">
+<img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="implicit-objects.jsp.html">Source Code for Implicit Objects Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/el/implicit-objects.jsp b/servletapi/jsr152/examples/jsp2/el/implicit-objects.jsp
new file mode 100644
index 0000000..992aa1d
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/el/implicit-objects.jsp
@@ -0,0 +1,88 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
+
+<html>
+  <head>
+    <title>JSP 2.0 Expression Language - Implicit Objects</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Expression Language - Implicit Objects</h1>
+    <hr>
+    This example illustrates some of the implicit objects available 
+    in the Expression Lanaguage.  The following implicit objects are 
+    available (not all illustrated here):
+    <ul>
+      <li>pageContext - the PageContext object</li>
+      <li>pageScope - a Map that maps page-scoped attribute names to 
+          their values</li>
+      <li>requestScope - a Map that maps request-scoped attribute names 
+          to their values</li>
+      <li>sessionScope - a Map that maps session-scoped attribute names 
+          to their values</li>
+      <li>applicationScope - a Map that maps application-scoped attribute 
+          names to their values</li>
+      <li>param - a Map that maps parameter names to a single String 
+          parameter value</li>
+      <li>paramValues - a Map that maps parameter names to a String[] of 
+          all values for that parameter</li>
+      <li>header - a Map that maps header names to a single String 
+          header value</li>
+      <li>headerValues - a Map that maps header names to a String[] of 
+          all values for that header</li>
+      <li>initParam - a Map that maps context initialization parameter 
+          names to their String parameter value</li>
+      <li>cookie - a Map that maps cookie names to a single Cookie object.</li>
+    </ul>
+
+    <blockquote>
+      <u><b>Change Parameter</b></u>
+      <form action="implicit-objects.jsp" method="GET">
+	  foo = <input type="text" name="foo" value="${fn:escapeXml(param["foo"])}">
+          <input type="submit">
+      </form>
+      <br>
+      <code>
+        <table border="1">
+          <thead>
+	    <td><b>EL Expression</b></td>
+	    <td><b>Result</b></td>
+	  </thead>
+	  <tr>
+	    <td>\${param.foo}</td>
+	    <td>${fn:escapeXml(param["foo"])}&nbsp;</td>
+	  </tr>
+	  <tr>
+	    <td>\${param["foo"]}</td>
+	    <td>${fn:escapeXml(param["foo"])}&nbsp;</td>
+	  </tr>
+	  <tr>
+	    <td>\${header["host"]}</td>
+	    <td>${header["host"]}</td>
+	  </tr>
+	  <tr>
+	    <td>\${header["accept"]}</td>
+	    <td>${header["accept"]}</td>
+	  </tr>
+	  <tr>
+	    <td>\${header["user-agent"]}</td>
+	    <td>${header["user-agent"]}</td>
+	  </tr>
+	</table>
+      </code>
+    </blockquote>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/jspattribute/jspattribute.html b/servletapi/jsr152/examples/jsp2/jspattribute/jspattribute.html
new file mode 100644
index 0000000..5faea39
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspattribute/jspattribute.html
@@ -0,0 +1,36 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="jspattribute.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="jspattribute.jsp.html">Source Code for jspattribute.jsp<font color="#0000FF"></a>
+  </font> </h3>
+
+<h3><a href="HelloWorldSimpleTag.java.html">Source Code for HelloWorldSimpleTag.java<font color="#0000FF"></a>
+  </font> </h3>
+
+<h3><a href="FooBean.java.html">Source Code for FooBean.java<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/jspattribute/jspattribute.jsp b/servletapi/jsr152/examples/jsp2/jspattribute/jspattribute.jsp
new file mode 100644
index 0000000..22b705e
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspattribute/jspattribute.jsp
@@ -0,0 +1,45 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="my" uri="http://jakarta.apache.org/tomcat/jsp2-example-taglib"%>
+
+<html>
+  <head>
+    <title>JSP 2.0 Examples - jsp:attribute and jsp:body</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Examples - jsp:attribute and jsp:body</h1>
+    <hr>
+    <p>The new &lt;jsp:attribute&gt; and &lt;jsp:body&gt; 
+    standard actions can be used to specify the value of any standard
+    action or custom action attribute.</p>
+    <p>This example uses the &lt;jsp:attribute&gt;
+    standard action to use the output of a custom action invocation
+    (one that simply outputs "Hello, World!") to set the value of a
+    bean property.  This would normally require an intermediary
+    step, such as using JSTL's &lt;c:set&gt; action.</p>
+    <br>
+    <jsp:useBean id="foo" class="jsp2.examples.FooBean">
+      Bean created!  Setting foo.bar...<br>
+      <jsp:setProperty name="foo" property="bar">
+        <jsp:attribute name="value">
+	  <my:helloWorld/>
+        </jsp:attribute>
+      </jsp:setProperty>
+    </jsp:useBean>
+    <br>
+    Result: ${foo.bar}
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/jspattribute/shuffle.html b/servletapi/jsr152/examples/jsp2/jspattribute/shuffle.html
new file mode 100644
index 0000000..907c5aa
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspattribute/shuffle.html
@@ -0,0 +1,36 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="shuffle.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="shuffle.jsp.html">Source Code for shuffle.jsp<font color="#0000FF"></a>
+  </font> </h3>
+
+<h3><a href="ShuffleSimpleTag.java.html">Source Code for ShuffleSimpleTag.java<font color="#0000FF"></a>
+  </font> </h3>
+
+<h3><a href="TileSimpleTag.java.html">Source Code for TileSimpleTag.java<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/jspattribute/shuffle.jsp b/servletapi/jsr152/examples/jsp2/jspattribute/shuffle.jsp
new file mode 100644
index 0000000..4f139fd
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspattribute/shuffle.jsp
@@ -0,0 +1,89 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="my" uri="http://jakarta.apache.org/tomcat/jsp2-example-taglib"%>
+
+<html>
+  <head>
+    <title>JSP 2.0 Examples - Shuffle Example</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Examples - Shuffle Example</h1>
+    <hr>
+    <p>Try reloading the page a few times.  Both the rows and the columns
+    are shuffled and appear different each time.</p>
+    <p>Here's how the code works.  The SimpleTag handler called 
+    &lt;my:shuffle&gt; accepts three attributes.  Each attribute is a 
+    JSP Fragment, meaning it is a fragment of JSP code that can be
+    dynamically executed by the shuffle tag handler on demand.  The 
+    shuffle tag handler executes the three fragments in a random order.
+    To shuffle both the rows and the columns, the shuffle tag is used
+    with itself as a parameter.</p>
+    <hr>
+    <blockquote>
+     <font color="#ffffff">
+      <table>
+        <my:shuffle>
+          <jsp:attribute name="fragment1">
+            <tr>
+              <my:shuffle>
+                <jsp:attribute name="fragment1">
+                  <my:tile color="#ff0000" label="A"/>
+                </jsp:attribute>
+                <jsp:attribute name="fragment2">
+                  <my:tile color="#00ff00" label="B"/>
+                </jsp:attribute>
+                <jsp:attribute name="fragment3">
+                  <my:tile color="#0000ff" label="C"/>
+                </jsp:attribute>
+              </my:shuffle>
+            </tr>
+          </jsp:attribute>
+          <jsp:attribute name="fragment2">
+            <tr>
+              <my:shuffle>
+                <jsp:attribute name="fragment1">
+                  <my:tile color="#ff0000" label="1"/>
+                </jsp:attribute>
+                <jsp:attribute name="fragment2">
+                  <my:tile color="#00ff00" label="2"/>
+                </jsp:attribute>
+                <jsp:attribute name="fragment3">
+                  <my:tile color="#0000ff" label="3"/>
+                </jsp:attribute>
+              </my:shuffle>
+            </tr>
+          </jsp:attribute>
+          <jsp:attribute name="fragment3">
+            <tr>
+              <my:shuffle>
+                <jsp:attribute name="fragment1">
+                  <my:tile color="#ff0000" label="!"/>
+                </jsp:attribute>
+                <jsp:attribute name="fragment2">
+                  <my:tile color="#00ff00" label="@"/>
+                </jsp:attribute>
+                <jsp:attribute name="fragment3">
+                  <my:tile color="#0000ff" label="#"/>
+                </jsp:attribute>
+              </my:shuffle>
+            </tr>
+          </jsp:attribute>
+        </my:shuffle>
+      </table>
+     </font>
+    </blockquote>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/jspx/basic.html b/servletapi/jsr152/examples/jsp2/jspx/basic.html
new file mode 100644
index 0000000..db2427b
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspx/basic.html
@@ -0,0 +1,30 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="basic.jspx"><img src="../../images/execute.gif" align="right" border="0"></a><a
+href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="basic.jspx.html">Source Code for XHTML Basic Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/jspx/basic.jspx b/servletapi/jsr152/examples/jsp2/jspx/basic.jspx
new file mode 100644
index 0000000..8a2f35b
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspx/basic.jspx
@@ -0,0 +1,30 @@
+<tags:xhtmlbasic xmlns:tags="urn:jsptagdir:/WEB-INF/tags"
+                 xmlns:jsp="http://java.sun.com/JSP/Page"
+                 xmlns:fmt="http://java.sun.com/jsp/jstl/fmt"
+		 xmlns="http://www.w3.org/1999/xhtml">
+  <jsp:directive.page contentType="text/html" />
+  <head>
+    <title>JSPX - XHTML Basic Example</title>
+  </head>
+  <body>
+    <h1>JSPX - XHTML Basic Example</h1>
+    <hr/>
+    This example illustrates how to use JSPX to produce an XHTML basic
+    document suitable for use with mobile phones, televisions, 
+    PDAs, vending machines, pagers, car navigation systems,
+    mobile game machines, digital book readers, smart watches, etc.
+    <p/>
+    JSPX lets you create dynamic documents in a pure XML syntax compatible
+    with existing XML tools.  The XML syntax in JSP 1.2 was awkward and
+    required &amp;lt;jsp:root&amp;gt; to be the root element of the document.
+    This is no longer the case in JSP 2.0.
+    <p/>
+    This particular example uses a tag file to produce the DOCTYPE and
+    namespace declarations to make the output of this page a valid XHTML
+    Basic document.
+    <p/>
+    Just to prove this is live, here's some dynamic content:
+    <jsp:useBean id="now" class="java.util.Date" />
+    <fmt:formatDate value="${now}" pattern="MMMM d, yyyy, H:mm:ss"/>
+  </body>
+</tags:xhtmlbasic>
diff --git a/servletapi/jsr152/examples/jsp2/jspx/svgexample.html b/servletapi/jsr152/examples/jsp2/jspx/svgexample.html
new file mode 100644
index 0000000..7f95459
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspx/svgexample.html
@@ -0,0 +1,51 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+  <head>
+    <title>JSP 2.0 SVG Example</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 SVG Example</h1>
+    <hr>
+    This example uses JSP 2.0's new, simplified JSPX syntax to render a
+    Scalable Vector Graphics (SVG) document.  When you view the source,
+    notice the lack of a &lt;jsp:root&gt; element!  The text to be rendered 
+    can be modified by changing the value of the name parameter.
+    <p>
+    SVG has many potential uses, such as searchable images, or images
+    customized with the name of your site's visitor (e.g. a "Susan's Store"
+    tab image).  JSPX is a natural fit for generating dynamic XML content
+    such as SVG.
+    <p>
+    To execute this example, follow these steps:
+    <ol>
+      <li>Download <a href="http://xml.apache.org/batik/index.html">Batik</a>,
+          or any other SVG viewer.</li>
+      <li>Copy the following URL:
+      <a href="http://localhost:8080/jsp-examples/jsp2/jspx/textRotate.jspx?name=JSPX">
+      http://localhost:8080/jsp-examples/jsp2/jspx/textRotate.jspx?name=JSPX</a>
+      </li>
+      <li>Paste the URL into Batik's Location field and press Enter</li>
+      <li>Customize by changing the name=JSPX parameter</li>
+    </ol>
+    <br>
+    The following is a screenshot of the resulting image, for those that
+    don't have an SVG viewer:
+    <blockquote>
+      <img src="textRotate.jpg" border="1">
+    </blockquote>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/jspx/textRotate.html b/servletapi/jsr152/examples/jsp2/jspx/textRotate.html
new file mode 100644
index 0000000..1874ecb
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspx/textRotate.html
@@ -0,0 +1,31 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="textRotate.jspx"><img src="../../images/execute.gif" align="right" border="0"></a><a
+href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="textRotate.jspx.html">Source Code for SVG (Scalable Vector Graphics)
+Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/jspx/textRotate.jpg b/servletapi/jsr152/examples/jsp2/jspx/textRotate.jpg
new file mode 100644
index 0000000..9e98736
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspx/textRotate.jpg
Binary files differ
diff --git a/servletapi/jsr152/examples/jsp2/jspx/textRotate.jspx b/servletapi/jsr152/examples/jsp2/jspx/textRotate.jspx
new file mode 100644
index 0000000..08f5890
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/jspx/textRotate.jspx
@@ -0,0 +1,36 @@
+<!-- 
+  - This example is based off the textRotate.svg example that comes
+  - with Batik.  The original example was written by Bill Haneman.
+  - This version by Mark Roth.
+  -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     width="450" height="500" viewBox="0 0 450 500"
+     xmlns:c="http://java.sun.com/jsp/jstl/core"
+     xmlns:fn="http://java.sun.com/jsp/jstl/functions"
+     xmlns:jsp="http://java.sun.com/JSP/Page">
+  <jsp:directive.page contentType="image/svg+xml" />
+  <title>JSP 2.0 JSPX</title>
+  <!-- select name parameter, or default to JSPX -->
+  <c:set var="name" value='${empty fn:escapeXml(param["name"]) ? "JSPX" : fn:escapeXml(param["name"])}'/>
+  <g id="testContent">
+    <text class="title" x="50%" y="10%" font-size="15" text-anchor="middle" >
+            JSP 2.0 XML Syntax (.jspx) Demo</text>
+    <text class="title" x="50%" y="15%" font-size="15" text-anchor="middle" >
+            Try changing the name parameter!</text>
+    <g opacity="1.0" transform="translate(225, 250)" id="rotatedText">
+      <c:forEach var="i" begin="1" end="24">
+        <jsp:text>
+          <![CDATA[<g opacity="0.95" transform="scale(1.05) rotate(15)">]]>
+        </jsp:text>
+        <text x="0" y="0" transform="scale(1.6, 1.6)" fill="DarkSlateBlue" 
+              text-anchor="middle" font-size="40" font-family="Serif" 
+              id="words">${name}</text>
+      </c:forEach>
+      <c:forEach var="i" begin="1" end="24">
+        <jsp:text><![CDATA[</g>]]></jsp:text>
+      </c:forEach>
+      <text style="font-size:75;font-family:Serif;fill:white" 
+            text-anchor="middle">${name}</text>
+    </g>
+  </g>
+</svg>
diff --git a/servletapi/jsr152/examples/jsp2/misc/coda.jspf b/servletapi/jsr152/examples/jsp2/misc/coda.jspf
new file mode 100644
index 0000000..31faec3
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/misc/coda.jspf
@@ -0,0 +1,5 @@
+<hr>
+<center>
+This banner included with &lt;include-coda&gt;
+</center>
+<hr>
diff --git a/servletapi/jsr152/examples/jsp2/misc/config.html b/servletapi/jsr152/examples/jsp2/misc/config.html
new file mode 100644
index 0000000..160d778
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/misc/config.html
@@ -0,0 +1,34 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="config.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="config.jsp.html">Source Code for config.jsp<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="prelude.jspf.html">Source Code for prelude.jspf<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="coda.jspf.html">Source Code for coda.jspf<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/misc/config.jsp b/servletapi/jsr152/examples/jsp2/misc/config.jsp
new file mode 100644
index 0000000..791a087
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/misc/config.jsp
@@ -0,0 +1,31 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="my" uri="http://jakarta.apache.org/tomcat/jsp2-example-taglib"%>
+    <h1>JSP 2.0 Examples - JSP Configuration</h1>
+    <hr>
+    <p>Using a &lt;jsp-property-group&gt; element in the web.xml 
+    deployment descriptor, this JSP page has been configured in the
+    following ways:</p>
+    <ul>
+      <li>Uses &lt;include-prelude&gt; to include the top banner.</li>
+      <li>Uses &lt;include-coda&gt; to include the bottom banner.</li>
+      <li>Uses &lt;scripting-invalid&gt; true to disable 
+	  &lt;% scripting %&gt; elements</li>
+      <li>Uses &lt;el-ignored&gt; true to disable ${EL} elements</li>
+      <li>Uses &lt;page-encoding&gt; ISO-8859-1 to set the page encoding (though this is the default anyway)</li>
+    </ul>
+    There are various other configuration options that can be used.
+
diff --git a/servletapi/jsr152/examples/jsp2/misc/dynamicattrs.html b/servletapi/jsr152/examples/jsp2/misc/dynamicattrs.html
new file mode 100644
index 0000000..307072a
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/misc/dynamicattrs.html
@@ -0,0 +1,32 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="dynamicattrs.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="dynamicattrs.jsp.html">Source Code for dynamicattrs.jsp<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="EchoAttributesTag.java.html">Source Code for EchoAttributesTag.java<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/misc/dynamicattrs.jsp b/servletapi/jsr152/examples/jsp2/misc/dynamicattrs.jsp
new file mode 100644
index 0000000..8721108
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/misc/dynamicattrs.jsp
@@ -0,0 +1,43 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="my" uri="http://jakarta.apache.org/tomcat/jsp2-example-taglib"%>
+<html>
+  <head>
+    <title>JSP 2.0 Examples - Dynamic Attributes</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Examples - Dynamic Attributes</h1>
+    <hr>
+    <p>This JSP page invokes a custom tag that accepts a dynamic set 
+    of attributes.  The tag echoes the name and value of all attributes
+    passed to it.</p>
+    <hr>
+    <h2>Invocation 1 (six attributes)</h2>
+    <ul>
+      <my:echoAttributes x="1" y="2" z="3" r="red" g="green" b="blue"/>
+    </ul>
+    <h2>Invocation 2 (zero attributes)</h2>
+    <ul>
+      <my:echoAttributes/>
+    </ul>
+    <h2>Invocation 3 (three attributes)</h2>
+    <ul>
+      <my:echoAttributes dogName="Scruffy" 
+	   		 catName="Fluffy" 
+			 blowfishName="Puffy"/>
+    </ul>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/misc/prelude.jspf b/servletapi/jsr152/examples/jsp2/misc/prelude.jspf
new file mode 100644
index 0000000..e30a8d8
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/misc/prelude.jspf
@@ -0,0 +1,5 @@
+<hr>
+<center>
+This banner included with &lt;include-prelude&gt;
+</center>
+<hr>
diff --git a/servletapi/jsr152/examples/jsp2/simpletag/book.html b/servletapi/jsr152/examples/jsp2/simpletag/book.html
new file mode 100644
index 0000000..ec8a4c9
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/simpletag/book.html
@@ -0,0 +1,36 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="book.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="book.jsp.html">Source Code for the Book Example JSP<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="FindBookSimpleTag.java.html">Source Code for the FindBook SimpleTag Handler<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="BookBean.java.html">Source Code for BookBean<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="Functions.java.html">Source Code for the EL Functions<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/simpletag/book.jsp b/servletapi/jsr152/examples/jsp2/simpletag/book.jsp
new file mode 100644
index 0000000..1f2c4c7
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/simpletag/book.jsp
@@ -0,0 +1,54 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="my" uri="/WEB-INF/jsp2/jsp2-example-taglib.tld" %>
+<html>
+  <head>
+    <title>JSP 2.0 Examples - Book SimpleTag Handler</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Examples - Book SimpleTag Handler</h1>
+    <hr>
+    <p>Illustrates a semi-realistic use of SimpleTag and the Expression 
+    Language.  First, a &lt;my:findBook&gt; tag is invoked to populate 
+    the page context with a BookBean.  Then, the books fields are printed 
+    in all caps.</p>
+    <br>
+    <b><u>Result:</u></b><br>
+    <my:findBook var="book"/>
+    <table border="1">
+        <thead>
+	    <td><b>Field</b></td>
+	    <td><b>Value</b></td>
+	    <td><b>Capitalized</b></td>
+	</thead>
+	<tr>
+	    <td>Title</td>
+	    <td>${book.title}</td>
+	    <td>${my:caps(book.title)}</td>
+	</tr>
+	<tr>
+	    <td>Author</td>
+	    <td>${book.author}</td>
+	    <td>${my:caps(book.author)}</td>
+	</tr>
+	<tr>
+	    <td>ISBN</td>
+	    <td>${book.isbn}</td>
+	    <td>${my:caps(book.isbn)}</td>
+	</tr>
+    </table>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/simpletag/hello.html b/servletapi/jsr152/examples/jsp2/simpletag/hello.html
new file mode 100644
index 0000000..943b0aa
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/simpletag/hello.html
@@ -0,0 +1,32 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="hello.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="hello.jsp.html">Source Code for the Hello World Tag Example JSP<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="HelloWorldSimpleTag.java.html">Source Code for the Hello World SimpleTag Handler<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/simpletag/hello.jsp b/servletapi/jsr152/examples/jsp2/simpletag/hello.jsp
new file mode 100644
index 0000000..e86a8c9
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/simpletag/hello.jsp
@@ -0,0 +1,30 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="mytag" uri="/WEB-INF/jsp2/jsp2-example-taglib.tld" %>
+<html>
+  <head>
+    <title>JSP 2.0 Examples - Hello World SimpleTag Handler</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Examples - Hello World SimpleTag Handler</h1>
+    <hr>
+    <p>This tag handler simply echos "Hello, World!"  It's an example of
+    a very basic SimpleTag handler with no body.</p>
+    <br>
+    <b><u>Result:</u></b>
+    <mytag:helloWorld/>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/simpletag/repeat.html b/servletapi/jsr152/examples/jsp2/simpletag/repeat.html
new file mode 100644
index 0000000..6ddc39d
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/simpletag/repeat.html
@@ -0,0 +1,32 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="repeat.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="repeat.jsp.html">Source Code for the Repeat Tag Example JSP<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="RepeatSimpleTag.java.html">Source Code for the Repeat SimpleTag Handler<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/simpletag/repeat.jsp b/servletapi/jsr152/examples/jsp2/simpletag/repeat.jsp
new file mode 100644
index 0000000..04d4630
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/simpletag/repeat.jsp
@@ -0,0 +1,38 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="mytag" uri="/WEB-INF/jsp2/jsp2-example-taglib.tld" %>
+<html>
+  <head>
+    <title>JSP 2.0 Examples - Repeat SimpleTag Handler</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Examples - Repeat SimpleTag Handler</h1>
+    <hr>
+    <p>This tag handler accepts a "num" parameter and repeats the body of the
+    tag "num" times.  It's a simple example, but the implementation of 
+    such a tag in JSP 2.0 is substantially simpler than the equivalent 
+    JSP 1.2-style classic tag handler.</p>
+    <p>The body of the tag is encapsulated in a "JSP Fragment" and passed
+    to the tag handler, which then executes it five times, inside a 
+    for loop.  The tag handler passes in the current invocation in a
+    scoped variable called count, which can be accessed using the EL.</p>
+    <br>
+    <b><u>Result:</u></b><br>
+    <mytag:repeat num="5">
+      Invocation ${count} of 5<br>
+    </mytag:repeat>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/tagfiles/hello.html b/servletapi/jsr152/examples/jsp2/tagfiles/hello.html
new file mode 100644
index 0000000..f5dc2c2
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/tagfiles/hello.html
@@ -0,0 +1,32 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="hello.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="hello.jsp.html">Source Code for hello.jsp<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="helloWorld.tag.html">Source Code for helloWorld.tag<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/tagfiles/hello.jsp b/servletapi/jsr152/examples/jsp2/tagfiles/hello.jsp
new file mode 100644
index 0000000..4fd06b2
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/tagfiles/hello.jsp
@@ -0,0 +1,34 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
+<html>
+  <head>
+    <title>JSP 2.0 Examples - Hello World Using a Tag File</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Examples - Hello World Using a Tag File</h1>
+    <hr>
+    <p>This JSP page invokes a custom tag that simply echos "Hello, World!"  
+    The custom tag is generated from a tag file in the /WEB-INF/tags
+    directory.</p>
+    <p>Notice that we did not need to write a TLD for this tag.  We just
+    created /WEB-INF/tags/helloWorld.tag, imported it using the taglib
+    directive, and used it!</p>
+    <br>
+    <b><u>Result:</u></b>
+    <tags:helloWorld/>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/tagfiles/panel.html b/servletapi/jsr152/examples/jsp2/tagfiles/panel.html
new file mode 100644
index 0000000..c3db5c7
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/tagfiles/panel.html
@@ -0,0 +1,32 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="panel.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="panel.jsp.html">Source Code for panel.jsp<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="panel.tag.html">Source Code for panel.tag<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/tagfiles/panel.jsp b/servletapi/jsr152/examples/jsp2/tagfiles/panel.jsp
new file mode 100644
index 0000000..af04e43
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/tagfiles/panel.jsp
@@ -0,0 +1,57 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
+<html>
+  <head>
+    <title>JSP 2.0 Examples - Panels using Tag Files</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Examples - Panels using Tag Files</h1>
+    <hr>
+    <p>This JSP page invokes a custom tag that draws a 
+    panel around the contents of the tag body.  Normally, such a tag 
+    implementation would require a Java class with many println() statements,
+    outputting HTML.  Instead, we can use a .tag file as a template,
+    and we don't need to write a single line of Java or even a TLD!</p>
+    <hr>
+    <table border="0">
+      <tr valign="top">
+        <td>
+          <tags:panel color="#ff8080" bgcolor="#ffc0c0" title="Panel 1">
+	    First panel.<br/>
+	  </tags:panel>
+        </td>
+        <td>
+          <tags:panel color="#80ff80" bgcolor="#c0ffc0" title="Panel 2">
+	    Second panel.<br/>
+	    Second panel.<br/>
+	    Second panel.<br/>
+	    Second panel.<br/>
+	  </tags:panel>
+        </td>
+        <td>
+          <tags:panel color="#8080ff" bgcolor="#c0c0ff" title="Panel 3">
+	    Third panel.<br/>
+            <tags:panel color="#ff80ff" bgcolor="#ffc0ff" title="Inner">
+	      A panel in a panel.
+	    </tags:panel>
+	    Third panel.<br/>
+	  </tags:panel>
+        </td>
+      </tr>
+    </table>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/tagfiles/products.html b/servletapi/jsr152/examples/jsp2/tagfiles/products.html
new file mode 100644
index 0000000..b03e84d
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/tagfiles/products.html
@@ -0,0 +1,21 @@
+<html>
+<!--
+  Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  reserved.
+-->
+<head>
+<title>View Source Code</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="products.jsp"><img src="../../images/execute.gif" align="right" border="0"></a>
+<a href="../../index.html"><img src="../../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="products.jsp.html">Source Code for products.jsp<font color="#0000FF"></a>
+  </font> </h3>
+<h3><a href="displayProducts.tag.html">Source Code for displayProducts.tag<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/jsp2/tagfiles/products.jsp b/servletapi/jsr152/examples/jsp2/tagfiles/products.jsp
new file mode 100644
index 0000000..9fa7355
--- /dev/null
+++ b/servletapi/jsr152/examples/jsp2/tagfiles/products.jsp
@@ -0,0 +1,53 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
+<html>
+  <head>
+    <title>JSP 2.0 Examples - Display Products Tag File</title>
+  </head>
+  <body>
+    <h1>JSP 2.0 Examples - Display Products Tag File</h1>
+    <hr>
+    <p>This JSP page invokes a tag file that displays a listing of 
+    products.  The custom tag accepts two fragments that enable
+    customization of appearance.  One for when the product is on sale
+    and one for normal price.</p>
+    <p>The tag is invoked twice, using different styles</p>
+    <hr>
+    <h2>Products</h2>
+    <tags:displayProducts>
+      <jsp:attribute name="normalPrice">
+	Item: ${name}<br/>
+	Price: ${price}
+      </jsp:attribute>
+      <jsp:attribute name="onSale">
+	Item: ${name}<br/>
+	<font color="red"><strike>Was: ${origPrice}</strike></font><br/>
+	<b>Now: ${salePrice}</b>
+      </jsp:attribute>
+    </tags:displayProducts>
+    <hr>
+    <h2>Products (Same tag, alternate style)</h2>
+    <tags:displayProducts>
+      <jsp:attribute name="normalPrice">
+	<b>${name}</b> @ ${price} ea.
+      </jsp:attribute>
+      <jsp:attribute name="onSale">
+	<b>${name}</b> @ ${salePrice} ea. (was: ${origPrice})
+      </jsp:attribute>
+    </tags:displayProducts>
+  </body>
+</html>
diff --git a/servletapi/jsr152/examples/jsptoserv/hello.jsp b/servletapi/jsr152/examples/jsptoserv/hello.jsp
new file mode 100644
index 0000000..f1807f3
--- /dev/null
+++ b/servletapi/jsr152/examples/jsptoserv/hello.jsp
@@ -0,0 +1,25 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<body bgcolor="white">
+
+<h1>
+I have been invoked by
+<% out.print (request.getAttribute("servletName").toString()); %>
+Servlet.
+</h1>
+
+</html>
\ No newline at end of file
diff --git a/servletapi/jsr152/examples/jsptoserv/jsptoservlet.jsp b/servletapi/jsr152/examples/jsptoserv/jsptoservlet.jsp
new file mode 100644
index 0000000..d05f919
--- /dev/null
+++ b/servletapi/jsr152/examples/jsptoserv/jsptoservlet.jsp
@@ -0,0 +1,22 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<body bgcolor="white">
+
+<!-- Forward to a servlet -->
+<jsp:forward page="/servletToJsp" />
+
+</html>
\ No newline at end of file
diff --git a/servletapi/jsr152/examples/jsptoserv/jts.html b/servletapi/jsr152/examples/jsptoserv/jts.html
new file mode 100644
index 0000000..7f83a45
--- /dev/null
+++ b/servletapi/jsr152/examples/jsptoserv/jts.html
@@ -0,0 +1,33 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="jsptoservlet.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="jsptoservlet.jsp.html">Source Code for JSP calling servlet <font color="#0000FF"></a>
+  </font> </h3>
+
+<h3><a href="servletToJsp.java.html">Source Code for Servlet calling JSP 
+<font color="#0000FF"></a> </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/num/numguess.html b/servletapi/jsr152/examples/num/numguess.html
new file mode 100644
index 0000000..348e773
--- /dev/null
+++ b/servletapi/jsr152/examples/num/numguess.html
@@ -0,0 +1,33 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+  Number Guess Game
+  Written by Jason Hunter, CTO, K&A Software
+  http://www.servlets.com
+-->
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="numguess.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="numguess.jsp.html">Source Code for Numguess Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/num/numguess.jsp b/servletapi/jsr152/examples/num/numguess.jsp
new file mode 100644
index 0000000..f1b8e02
--- /dev/null
+++ b/servletapi/jsr152/examples/num/numguess.jsp
@@ -0,0 +1,68 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+  Number Guess Game
+  Written by Jason Hunter, CTO, K&A Software
+  http://www.servlets.com
+-->
+
+<%@ page import = "num.NumberGuessBean" %>
+
+<jsp:useBean id="numguess" class="num.NumberGuessBean" scope="session"/>
+<jsp:setProperty name="numguess" property="*"/>
+
+<html>
+<head><title>Number Guess</title></head>
+<body bgcolor="white">
+<font size=4>
+
+<% if (numguess.getSuccess()) { %>
+
+  Congratulations!  You got it.
+  And after just <%= numguess.getNumGuesses() %> tries.<p>
+
+  <% numguess.reset(); %>
+
+  Care to <a href="numguess.jsp">try again</a>?
+
+<% } else if (numguess.getNumGuesses() == 0) { %>
+
+  Welcome to the Number Guess game.<p>
+
+  I'm thinking of a number between 1 and 100.<p>
+
+  <form method=get>
+  What's your guess? <input type=text name=guess>
+  <input type=submit value="Submit">
+  </form>
+
+<% } else { %>
+
+  Good guess, but nope.  Try <b><%= numguess.getHint() %></b>.
+
+  You have made <%= numguess.getNumGuesses() %> guesses.<p>
+
+  I'm thinking of a number between 1 and 100.<p>
+
+  <form method=get>
+  What's your guess? <input type=text name=guess>
+  <input type=submit value="Submit">
+  </form>
+
+<% } %>
+
+</font>
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/plugin/applet/Clock2.java b/servletapi/jsr152/examples/plugin/applet/Clock2.java
new file mode 100644
index 0000000..32edb89
--- /dev/null
+++ b/servletapi/jsr152/examples/plugin/applet/Clock2.java
@@ -0,0 +1,212 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import java.util.*;
+import java.awt.*;
+import java.applet.*;
+import java.text.*;
+
+/**
+ * Time!
+ *
+ * @author Rachel Gollub
+ */
+
+public class Clock2 extends Applet implements Runnable {
+    Thread timer;                // The thread that displays clock
+    int lastxs, lastys, lastxm,
+        lastym, lastxh, lastyh;  // Dimensions used to draw hands 
+    SimpleDateFormat formatter;  // Formats the date displayed
+    String lastdate;             // String to hold date displayed
+    Font clockFaceFont;          // Font for number display on clock
+    Date currentDate;            // Used to get date to display
+    Color handColor;             // Color of main hands and dial
+    Color numberColor;           // Color of second hand and numbers
+
+    public void init() {
+        int x,y;
+        lastxs = lastys = lastxm = lastym = lastxh = lastyh = 0;
+        formatter = new SimpleDateFormat ("EEE MMM dd hh:mm:ss yyyy", Locale.getDefault());
+        currentDate = new Date();
+        lastdate = formatter.format(currentDate);
+        clockFaceFont = new Font("Serif", Font.PLAIN, 14);
+        handColor = Color.blue;
+        numberColor = Color.darkGray;
+
+        try {
+            setBackground(new Color(Integer.parseInt(getParameter("bgcolor"),16)));
+        } catch (Exception E) { }
+        try {
+            handColor = new Color(Integer.parseInt(getParameter("fgcolor1"),16));
+        } catch (Exception E) { }
+        try {
+            numberColor = new Color(Integer.parseInt(getParameter("fgcolor2"),16));
+        } catch (Exception E) { }
+        resize(300,300);              // Set clock window size
+    }
+
+    // Plotpoints allows calculation to only cover 45 degrees of the circle,
+    // and then mirror
+    public void plotpoints(int x0, int y0, int x, int y, Graphics g) {
+        g.drawLine(x0+x,y0+y,x0+x,y0+y);
+        g.drawLine(x0+y,y0+x,x0+y,y0+x);
+        g.drawLine(x0+y,y0-x,x0+y,y0-x);
+        g.drawLine(x0+x,y0-y,x0+x,y0-y);
+        g.drawLine(x0-x,y0-y,x0-x,y0-y);
+        g.drawLine(x0-y,y0-x,x0-y,y0-x);
+        g.drawLine(x0-y,y0+x,x0-y,y0+x);
+        g.drawLine(x0-x,y0+y,x0-x,y0+y);
+    }
+
+    // Circle is just Bresenham's algorithm for a scan converted circle
+    public void circle(int x0, int y0, int r, Graphics g) {
+        int x,y;
+        float d;
+        x=0;
+        y=r;
+        d=5/4-r;
+        plotpoints(x0,y0,x,y,g);
+
+        while (y>x){
+            if (d<0) {
+                d=d+2*x+3;
+                x++;
+            }
+            else {
+                d=d+2*(x-y)+5;
+                x++;
+                y--;
+            }
+            plotpoints(x0,y0,x,y,g);
+        }
+    }
+
+    // Paint is the main part of the program
+    public void paint(Graphics g) {
+        int xh, yh, xm, ym, xs, ys, s = 0, m = 10, h = 10, xcenter, ycenter;
+        String today;
+
+        currentDate = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat("s",Locale.getDefault());
+        try {
+            s = Integer.parseInt(formatter.format(currentDate));
+        } catch (NumberFormatException n) {
+            s = 0;
+        }
+        formatter.applyPattern("m");
+        try {
+            m = Integer.parseInt(formatter.format(currentDate));
+        } catch (NumberFormatException n) {
+            m = 10;
+        }    
+        formatter.applyPattern("h");
+        try {
+            h = Integer.parseInt(formatter.format(currentDate));
+        } catch (NumberFormatException n) {
+            h = 10;
+        }
+        formatter.applyPattern("EEE MMM dd HH:mm:ss yyyy");
+        today = formatter.format(currentDate);
+        xcenter=80;
+        ycenter=55;
+    
+    // a= s* pi/2 - pi/2 (to switch 0,0 from 3:00 to 12:00)
+    // x = r(cos a) + xcenter, y = r(sin a) + ycenter
+    
+        xs = (int)(Math.cos(s * 3.14f/30 - 3.14f/2) * 45 + xcenter);
+        ys = (int)(Math.sin(s * 3.14f/30 - 3.14f/2) * 45 + ycenter);
+        xm = (int)(Math.cos(m * 3.14f/30 - 3.14f/2) * 40 + xcenter);
+        ym = (int)(Math.sin(m * 3.14f/30 - 3.14f/2) * 40 + ycenter);
+        xh = (int)(Math.cos((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 30 + xcenter);
+        yh = (int)(Math.sin((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 30 + ycenter);
+    
+    // Draw the circle and numbers
+    
+        g.setFont(clockFaceFont);
+        g.setColor(handColor);
+        circle(xcenter,ycenter,50,g);
+        g.setColor(numberColor);
+        g.drawString("9",xcenter-45,ycenter+3); 
+        g.drawString("3",xcenter+40,ycenter+3);
+        g.drawString("12",xcenter-5,ycenter-37);
+        g.drawString("6",xcenter-3,ycenter+45);
+
+    // Erase if necessary, and redraw
+    
+        g.setColor(getBackground());
+        if (xs != lastxs || ys != lastys) {
+            g.drawLine(xcenter, ycenter, lastxs, lastys);
+            g.drawString(lastdate, 5, 125);
+        }
+        if (xm != lastxm || ym != lastym) {
+            g.drawLine(xcenter, ycenter-1, lastxm, lastym);
+            g.drawLine(xcenter-1, ycenter, lastxm, lastym); }
+        if (xh != lastxh || yh != lastyh) {
+            g.drawLine(xcenter, ycenter-1, lastxh, lastyh);
+            g.drawLine(xcenter-1, ycenter, lastxh, lastyh); }
+        g.setColor(numberColor);
+        g.drawString("", 5, 125);
+        g.drawString(today, 5, 125);    
+        g.drawLine(xcenter, ycenter, xs, ys);
+        g.setColor(handColor);
+        g.drawLine(xcenter, ycenter-1, xm, ym);
+        g.drawLine(xcenter-1, ycenter, xm, ym);
+        g.drawLine(xcenter, ycenter-1, xh, yh);
+        g.drawLine(xcenter-1, ycenter, xh, yh);
+        lastxs=xs; lastys=ys;
+        lastxm=xm; lastym=ym;
+        lastxh=xh; lastyh=yh;
+        lastdate = today;
+        currentDate=null;
+    }
+
+    public void start() {
+        timer = new Thread(this);
+        timer.start();
+    }
+
+    public void stop() {
+        timer = null;
+    }
+
+    public void run() {
+        Thread me = Thread.currentThread();
+        while (timer == me) {
+            try {
+                Thread.currentThread().sleep(100);
+            } catch (InterruptedException e) {
+            }
+            repaint();
+        }
+    }
+
+    public void update(Graphics g) {
+        paint(g);
+    }
+
+    public String getAppletInfo() {
+        return "Title: A Clock \nAuthor: Rachel Gollub, 1995 \nAn analog clock.";
+    }
+  
+    public String[][] getParameterInfo() {
+        String[][] info = {
+            {"bgcolor", "hexadecimal RGB number", "The background color. Default is the color of your browser."},
+            {"fgcolor1", "hexadecimal RGB number", "The color of the hands and dial. Default is blue."},
+            {"fgcolor2", "hexadecimal RGB number", "The color of the seconds hand and numbers. Default is dark gray."}
+        };
+        return info;
+    }
+}
diff --git a/servletapi/jsr152/examples/plugin/plugin.html b/servletapi/jsr152/examples/plugin/plugin.html
new file mode 100644
index 0000000..eee55c6
--- /dev/null
+++ b/servletapi/jsr152/examples/plugin/plugin.html
@@ -0,0 +1,29 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="plugin.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="plugin.jsp.html">Source Code for Plugin Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/plugin/plugin.jsp b/servletapi/jsr152/examples/plugin/plugin.jsp
new file mode 100644
index 0000000..fed5d4e
--- /dev/null
+++ b/servletapi/jsr152/examples/plugin/plugin.jsp
@@ -0,0 +1,33 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<title> Plugin example </title>
+<body bgcolor="white">
+<h3> Current time is : </h3>
+<jsp:plugin type="applet" code="Clock2.class" codebase="applet" jreversion="1.2" width="160" height="150" >
+    <jsp:fallback>
+        Plugin tag OBJECT or EMBED not supported by browser.
+    </jsp:fallback>
+</jsp:plugin>
+<p>
+<h4>
+<font color=red> 
+The above applet is loaded using the Java Plugin from a jsp page using the
+plugin tag.
+</font>
+</h4>
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/security/protected/error.jsp b/servletapi/jsr152/examples/security/protected/error.jsp
new file mode 100644
index 0000000..ff24ca3
--- /dev/null
+++ b/servletapi/jsr152/examples/security/protected/error.jsp
@@ -0,0 +1,24 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>Error Page For Examples</title>
+</head>
+<body bgcolor="white">
+Invalid username and/or password, please try
+<a href='<%= response.encodeURL("login.jsp") %>'>again</a>.
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/security/protected/index.jsp b/servletapi/jsr152/examples/security/protected/index.jsp
new file mode 100644
index 0000000..71d9778
--- /dev/null
+++ b/servletapi/jsr152/examples/security/protected/index.jsp
@@ -0,0 +1,76 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%
+  if (request.getParameter("logoff") != null) {
+    session.invalidate();
+    response.sendRedirect("index.jsp");
+    return;
+  }
+%>
+<html>
+<head>
+<title>Protected Page for Examples</title>
+</head>
+<body bgcolor="white">
+
+You are logged in as remote user <b><%= request.getRemoteUser() %></b>
+in session <b><%= session.getId() %></b><br><br>
+
+<%
+  if (request.getUserPrincipal() != null) {
+%>
+    Your user principal name is
+    <b><%= request.getUserPrincipal().getName() %></b><br><br>
+<%
+  } else {
+%>
+    No user principal could be identified.<br><br>
+<%
+  }
+%>
+
+<%
+  String role = request.getParameter("role");
+  if (role == null)
+    role = "";
+  if (role.length() > 0) {
+    if (request.isUserInRole(role)) {
+%>
+      You have been granted role <b><%= role %></b><br><br>
+<%
+    } else {
+%>
+      You have <i>not</i> been granted role <b><%= role %></b><br><br>
+<%
+    }
+  }
+%>
+
+To check whether your username has been granted a particular role,
+enter it here:
+<form method="GET" action='<%= response.encodeURL("index.jsp") %>'>
+<input type="text" name="role" value="<%= role %>">
+</form>
+<br><br>
+
+If you have configured this app for form-based authentication, you can log
+off by clicking
+<a href='<%= response.encodeURL("index.jsp?logoff=true") %>'>here</a>.
+This should cause you to be returned to the logon page after the redirect
+that is performed.
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/security/protected/login.jsp b/servletapi/jsr152/examples/security/protected/login.jsp
new file mode 100644
index 0000000..af1e6c1
--- /dev/null
+++ b/servletapi/jsr152/examples/security/protected/login.jsp
@@ -0,0 +1,37 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>Login Page for Examples</title>
+<body bgcolor="white">
+<form method="POST" action='<%= response.encodeURL("j_security_check") %>' >
+  <table border="0" cellspacing="5">
+    <tr>
+      <th align="right">Username:</th>
+      <td align="left"><input type="text" name="j_username"></td>
+    </tr>
+    <tr>
+      <th align="right">Password:</th>
+      <td align="left"><input type="password" name="j_password"></td>
+    </tr>
+    <tr>
+      <td align="right"><input type="submit" value="Log In"></td>
+      <td align="left"><input type="reset"></td>
+    </tr>
+  </table>
+</form>
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/sessions/DummyCart.html b/servletapi/jsr152/examples/sessions/DummyCart.html
new file mode 100644
index 0000000..b6d64f9
--- /dev/null
+++ b/servletapi/jsr152/examples/sessions/DummyCart.html
@@ -0,0 +1,55 @@
+<HTML>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<HEAD>
+<title>
+sessions.DummyCart Bean Properties
+</title>
+<BODY BGCOLOR="white">
+<H2>
+sessions.DummyCart Bean Properties
+</H2>
+<HR>
+<DL>
+<DT>public class <B>DummyCart</B><DT>extends Object</DL>
+
+<P>
+<HR>
+
+<P>
+
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0">
+<TR BGCOLOR="#EEEEFF">
+<TD COLSPAN=3><FONT SIZE="+2">
+<B>Properties Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white">
+<td align="right" valign="top" width="1%">
+<FONT SIZE="-1">
+String
+</FONT></TD>
+<TD><B>DummyCart:items</B>
+<BR>
+       </TD>
+<td width="1%">
+<FONT SIZE="-1">
+Multi
+</FONT></TD>
+</TABLE>
+<HR>
+</BODY>
+</HTML>
diff --git a/servletapi/jsr152/examples/sessions/carts.html b/servletapi/jsr152/examples/sessions/carts.html
new file mode 100644
index 0000000..ad5c03d
--- /dev/null
+++ b/servletapi/jsr152/examples/sessions/carts.html
@@ -0,0 +1,52 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+    <title>carts</title>
+</head>
+
+ <body bgcolor="white">
+<font size = 5 color="#CC0000">
+
+<form type=POST action=carts.jsp>
+<BR>
+Please enter item to add or remove:
+<br>
+Add Item:
+
+<SELECT NAME="item">
+<OPTION>Beavis & Butt-head Video collection
+<OPTION>X-files movie
+<OPTION>Twin peaks tapes
+<OPTION>NIN CD
+<OPTION>JSP Book
+<OPTION>Concert tickets
+<OPTION>Love life
+<OPTION>Switch blade
+<OPTION>Rex, Rugs & Rock n' Roll
+</SELECT>
+
+
+<br> <br>
+<INPUT TYPE=submit name="submit" value="add">
+<INPUT TYPE=submit name="submit" value="remove">
+
+</form>
+       
+</FONT>
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/sessions/carts.jsp b/servletapi/jsr152/examples/sessions/carts.jsp
new file mode 100644
index 0000000..d46ceed
--- /dev/null
+++ b/servletapi/jsr152/examples/sessions/carts.jsp
@@ -0,0 +1,43 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<jsp:useBean id="cart" scope="session" class="sessions.DummyCart" />
+
+<jsp:setProperty name="cart" property="*" />
+<%
+	cart.processRequest(request);
+%>
+
+
+<FONT size = 5 COLOR="#CC0000">
+<br> You have the following items in your cart:
+<ol>
+<% 
+	String[] items = cart.getItems();
+	for (int i=0; i<items.length; i++) {
+%>
+<li> <% out.print(util.HTMLFilter.filter(items[i])); %> 
+<%
+	}
+%>
+</ol>
+
+</FONT>
+
+<hr>
+<%@ include file ="/sessions/carts.html" %>
+</html>
diff --git a/servletapi/jsr152/examples/sessions/crt.html b/servletapi/jsr152/examples/sessions/crt.html
new file mode 100644
index 0000000..4be80f4
--- /dev/null
+++ b/servletapi/jsr152/examples/sessions/crt.html
@@ -0,0 +1,33 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="carts.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="carts.jsp.html">Source Code for Cart Example<font color="#0000FF"></a>
+  </font> </h3>
+
+<h3><a href="DummyCart.html">Property Sheet for DummyCart
+<font color="#0000FF"></a> </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/simpletag/foo.html b/servletapi/jsr152/examples/simpletag/foo.html
new file mode 100644
index 0000000..25b322b
--- /dev/null
+++ b/servletapi/jsr152/examples/simpletag/foo.html
@@ -0,0 +1,29 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="foo.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="foo.jsp.html">Source Code for the Simple Tag Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/simpletag/foo.jsp b/servletapi/jsr152/examples/simpletag/foo.jsp
new file mode 100644
index 0000000..9e3008b
--- /dev/null
+++ b/servletapi/jsr152/examples/simpletag/foo.jsp
@@ -0,0 +1,37 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<body>
+<%@ taglib uri="http://jakarta.apache.org/tomcat/examples-taglib" prefix="eg"%>
+
+Radio stations that rock:
+
+<ul>
+<eg:foo att1="98.5" att2="92.3" att3="107.7">
+<li><%= member %></li>
+</eg:foo>
+</ul>
+
+<eg:log>
+Did you see me on the stderr window?
+</eg:log>
+
+<eg:log toBrowser="true">
+Did you see me on the browser window as well?
+</eg:log>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/snp/snoop.html b/servletapi/jsr152/examples/snp/snoop.html
new file mode 100644
index 0000000..fffeb09
--- /dev/null
+++ b/servletapi/jsr152/examples/snp/snoop.html
@@ -0,0 +1,30 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="snoop.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="snoop.jsp.html">Source Code for Request Parameters Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/snp/snoop.jsp b/servletapi/jsr152/examples/snp/snoop.jsp
new file mode 100644
index 0000000..bec02d2
--- /dev/null
+++ b/servletapi/jsr152/examples/snp/snoop.jsp
@@ -0,0 +1,55 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<body bgcolor="white">
+<h1> Request Information </h1>
+<font size="4">
+JSP Request Method: <% out.print(util.HTMLFilter.filter(request.getMethod())); %>
+<br>
+Request URI: <%= request.getRequestURI() %>
+<br>
+Request Protocol: <%= request.getProtocol() %>
+<br>
+Servlet path: <%= request.getServletPath() %>
+<br>
+Path info: <% out.print(util.HTMLFilter.filter(request.getPathInfo())); %>
+<br>
+Query string: <% out.print(util.HTMLFilter.filter(request.getQueryString())); %>
+<br>
+Content length: <%= request.getContentLength() %>
+<br>
+Content type: <% out.print(util.HTMLFilter.filter(request.getContentType())); %>
+<br>
+Server name: <%= request.getServerName() %>
+<br>
+Server port: <%= request.getServerPort() %>
+<br>
+Remote user: <%= request.getRemoteUser() %>
+<br>
+Remote address: <%= request.getRemoteAddr() %>
+<br>
+Remote host: <%= request.getRemoteHost() %>
+<br>
+Authorization scheme: <%= request.getAuthType() %> 
+<br>
+Locale: <%= request.getLocale() %>
+<hr>
+The browser you are using is <% out.print(util.HTMLFilter.filter(request.getHeader("User-Agent"))); %>
+<hr>
+</font>
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/source.jsp b/servletapi/jsr152/examples/source.jsp
new file mode 100644
index 0000000..3d9e958
--- /dev/null
+++ b/servletapi/jsr152/examples/source.jsp
@@ -0,0 +1,19 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ taglib uri="http://jakarta.apache.org/tomcat/examples-taglib"
+        prefix="eg" %>
+
+<eg:ShowSource jspFile="<%= request.getQueryString() %>"/>
diff --git a/servletapi/jsr152/examples/tagplugin/choose.html b/servletapi/jsr152/examples/tagplugin/choose.html
new file mode 100644
index 0000000..9b15c3f
--- /dev/null
+++ b/servletapi/jsr152/examples/tagplugin/choose.html
@@ -0,0 +1,35 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>View Source Code</title>
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF">
+  <a href="choose.jsp">
+    <img src="../images/execute.gif" align="right" border="0"></a>
+  <a href="../index.html">
+    <img src="../images/return.gif" width="24" height="24" align="right" border="0">
+  </a></font>
+</p>
+
+<h3>
+  <a href="choose.jsp.html">Source Code for choose.jsp<font color="#0000FF"/></a>
+</h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/tagplugin/choose.jsp b/servletapi/jsr152/examples/tagplugin/choose.jsp
new file mode 100644
index 0000000..8d35451
--- /dev/null
+++ b/servletapi/jsr152/examples/tagplugin/choose.jsp
@@ -0,0 +1,57 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+  <head>
+    <title>Tag Examples - choose</title>
+  </head>
+  <body>
+    <h1>Tag Plugin Examples - &lt;c:choose></h1>
+
+    <hr>
+    </br>
+    <a href="notes.html">Plugin Introductory Notes<font <font color="#0000FF"></
+a>
+    <br/>
+    <a href="howto.html">Brief Instructions for Writing Plugins<font color="#000
+0
+FF"></a>
+    <br/> <br/>
+    <hr>
+
+    <font color="#000000"/>
+    </br>
+
+    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
+    <c:forEach var="index" begin="0" end="4">
+      # ${index}: 
+      <c:choose>
+	<c:when test="${index == 1}">
+          One!</br>
+	</c:when>
+	<c:when test="${index == 4}">
+          Four!</br>
+	</c:when>
+	<c:when test="${index == 3}">
+          Three!</br>
+	</c:when>
+	<c:otherwise>
+          Huh?</br>
+	</c:otherwise>
+      </c:choose>
+    </c:forEach>
+  </body>
+</html> 
diff --git a/servletapi/jsr152/examples/tagplugin/foreach.html b/servletapi/jsr152/examples/tagplugin/foreach.html
new file mode 100644
index 0000000..bc56a66
--- /dev/null
+++ b/servletapi/jsr152/examples/tagplugin/foreach.html
@@ -0,0 +1,35 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>View Source Code</title>
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF">
+  <a href="foreach.jsp">
+    <img src="../images/execute.gif" align="right" border="0"></a>
+  <a href="../index.html">
+    <img src="../images/return.gif" width="24" height="24" align="right" border="0">
+  </a></font>
+</p>
+
+<h3>
+  <a href="foreach.jsp.html">Source Code for foreach.jsp<font color="#0000FF"/></a>
+</h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/tagplugin/foreach.jsp b/servletapi/jsr152/examples/tagplugin/foreach.jsp
new file mode 100644
index 0000000..d38e18e
--- /dev/null
+++ b/servletapi/jsr152/examples/tagplugin/foreach.jsp
@@ -0,0 +1,56 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+  <head>
+    <title>Tag Plugin Examples: forEach</title>
+  </head>
+  <body>
+    <h1>Tag Plugin Examples - &lt;c:forEach></h1>
+
+    <hr>
+    </br>
+    <a href="notes.html">Plugin Introductory Notes<font <font color="#0000FF"></
+a>
+    <br/>
+    <a href="howto.html">Brief Instructions for Writing Plugins<font color="#0000
+FF"></a>
+    <br/> <br/>
+    <hr>
+
+    <font color="#000000"/>
+    </br>
+
+    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+    <%@ page import="java.util.Vector" %>
+
+    <h3>Iterating over a range</h3>
+    <c:forEach var="item" begin="1" end="10">
+        ${item}
+    </c:forEach>
+
+    <% Vector v = new Vector();
+	v.add("One"); v.add("Two"); v.add("Three"); v.add("Four");
+
+	pageContext.setAttribute("vector", v);
+    %>
+
+    <h3>Iterating over a Vector</h3>
+
+    <c:forEach items="${vector}" var="item" >
+	${item}
+    </c:forEach>
+  </body>
+</html> 
diff --git a/servletapi/jsr152/examples/tagplugin/howto.html b/servletapi/jsr152/examples/tagplugin/howto.html
new file mode 100644
index 0000000..e875465
--- /dev/null
+++ b/servletapi/jsr152/examples/tagplugin/howto.html
@@ -0,0 +1,42 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+  <head>
+    <title>Tag Plugin Implementation</title>
+    <h2>How to write tag plugins</h2>
+    <p>
+      To write a plugin, you'll need to download the source for Tomcat 5.
+      There are two steps:
+    <ol>
+      <li>
+        Implement the plugin class.<p/>
+        This class, which implements 
+        <tt>org.apache.jasper.compiler.tagplugin.TagPlugin</tt>
+        instructs Jasper what Java codes to generate in place of the tag
+        handler calls.
+        See Javadoc for <tt>org.apache.jasper.compiler.tagplugin.TagPlugin</tt>
+        for details.
+      </li>
+
+      <li>
+        Create the plugin descriptor file <tt> WEB-INF/tagPlugins.xml</tt><p/>
+        This file
+        specifies the plugin classes and their corresponding tag handler
+        classes.
+      </li>
+    </ol>
+  </head>
+</html>
diff --git a/servletapi/jsr152/examples/tagplugin/if.html b/servletapi/jsr152/examples/tagplugin/if.html
new file mode 100644
index 0000000..385958a
--- /dev/null
+++ b/servletapi/jsr152/examples/tagplugin/if.html
@@ -0,0 +1,35 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>View Source Code</title>
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF">
+  <a href="if.jsp">
+    <img src="../images/execute.gif" align="right" border="0"></a>
+  <a href="../index.html">
+    <img src="../images/return.gif" width="24" height="24" align="right" border="0">
+  </a></font>
+</p>
+
+<h3>
+  <a href="if.jsp.html">Source Code for if.jsp<font color="#0000FF"/></a>
+</h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/tagplugin/if.jsp b/servletapi/jsr152/examples/tagplugin/if.jsp
new file mode 100644
index 0000000..56c2647
--- /dev/null
+++ b/servletapi/jsr152/examples/tagplugin/if.jsp
@@ -0,0 +1,44 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+  <head>
+    <title>Tag Plugin Examples: if</title>
+  </head>
+  <body>
+    <h1>Tag Plugin Examples - &lt;c:if></h1>
+
+    <hr>
+    </br>
+    <a href="notes.html">Plugin Introductory Notes<font <font color="#0000FF"></a>
+    <br/>
+    <a href="howto.html">Brief Instructions for Wrieting Plugins<font color="#0000FF"></a>
+    <br/> <br/>
+    <hr>
+
+    <font color="#000000"/>
+    </br>
+    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
+    <h3>Set the test result to a variable</h3>
+    <c:if test="${1==1}" var="theTruth" scope="session"/>
+    The result of testing for (1==1) is: ${theTruth}
+
+    <h3>Conditionally execute the body</h3>
+    <c:if test="${2>0}">
+	It's true that (2>0)!
+    </c:if>
+  </body>
+</html> 
diff --git a/servletapi/jsr152/examples/tagplugin/notes.html b/servletapi/jsr152/examples/tagplugin/notes.html
new file mode 100644
index 0000000..6107450
--- /dev/null
+++ b/servletapi/jsr152/examples/tagplugin/notes.html
@@ -0,0 +1,38 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+  <head>
+    <title>Tag Plugin Introduction</title>
+    <h2>Tag Plugins: Introductory Notes</h2>
+    <p>
+      Tomcat 5 provides a framework for implementing tag plugins.  The
+      plugins instruct Jasper, at translation time, to replace tag handler
+      calls with Java scriptlets.
+      The framework allows tag library authors to implement plugins for
+      their tags.
+    </p>
+    <p>
+      Tomcat 5 is released with plugins for several JSTL tags.  Note
+      that these plugins work with JSTL 1.1 as well as JSTL 1.0, though
+      the examples uses JSTL 1.1 and JSP 2.0.  
+      These plugins are not complete (for instance, some item types not
+      handled in &lt;c:if>).
+      They do serve as examples to show plugins in action (just
+      examine the generated Java files), and how they can be implemented.
+    </p>
+  </head>
+</html>
+
diff --git a/servletapi/jsr152/examples/xml/xml.html b/servletapi/jsr152/examples/xml/xml.html
new file mode 100644
index 0000000..1c84128
--- /dev/null
+++ b/servletapi/jsr152/examples/xml/xml.html
@@ -0,0 +1,30 @@
+<html>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="xml.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+
+<h3><a href="xml.jsp.html">Source Code for XML syntax Example<font color="#0000FF"></a>
+  </font> </h3>
+
+</body>
+</html>
diff --git a/servletapi/jsr152/examples/xml/xml.jsp b/servletapi/jsr152/examples/xml/xml.jsp
new file mode 100644
index 0000000..d014ff8
--- /dev/null
+++ b/servletapi/jsr152/examples/xml/xml.jsp
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
+  version="1.2">
+<jsp:directive.page contentType="text/html"/>
+<jsp:directive.page import="java.util.Date, java.util.Locale"/>
+<jsp:directive.page import="java.text.*"/>
+
+<jsp:declaration>
+  String getDateTimeStr(Locale l) {
+    DateFormat df = SimpleDateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, l);
+    return df.format(new Date());
+  }
+</jsp:declaration>
+
+<html>
+<head>
+  <title>Example JSP in XML format</title>
+</head>
+
+<body>
+This is the output of a simple JSP using XML format. 
+<br />
+
+<div>Use a jsp:scriptlet to loop from 1 to 10: </div>
+<jsp:scriptlet>
+// Note we need to declare CDATA because we don't escape the less than symbol
+<![CDATA[
+  for (int i = 1; i<=10; i++) {
+    out.println(i);
+    if (i < 10) {
+      out.println(", ");
+    }
+  }
+]]>
+</jsp:scriptlet>
+
+<!-- Because I omit br's end tag, declare it as CDATA -->
+<![CDATA[
+  <br><br>
+]]>
+
+<div align="left">
+  Use a jsp:expression to write the date and time in the browser's locale: 
+  <jsp:expression>getDateTimeStr(request.getLocale())</jsp:expression>
+</div>
+
+
+<jsp:text>
+  &lt;p&gt;This sentence is enclosed in a jsp:text element.&lt;/p&gt;
+</jsp:text>
+
+</body>
+</html>
+</jsp:root>
diff --git a/servletapi/jsr152/src/ant/task/Txt2Html.java b/servletapi/jsr152/src/ant/task/Txt2Html.java
new file mode 100644
index 0000000..1bbf85a
--- /dev/null
+++ b/servletapi/jsr152/src/ant/task/Txt2Html.java
@@ -0,0 +1,163 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package task;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Ant task to convert a given set of files from Text to HTML.
+ * Inserts an HTML header including pre tags and replaces special characters
+ * with their HTML escaped equivalents.
+ *
+ * <p>This task is currently used by the ant script to build our examples</p>
+ *
+ * @author Mark Roth
+ */
+public class Txt2Html 
+    extends Task 
+{
+    
+    /** The directory to contain the resulting files */
+    private File todir;
+    
+    /** The file to be converted into HTML */
+    private List filesets = new LinkedList();
+    
+    /**
+     * Sets the directory to contain the resulting files
+     *
+     * @param todir The directory
+     */
+    public void setTodir( File todir ) {
+        this.todir = todir;
+    }
+    
+    /**
+     * Sets the files to be converted into HTML
+     *
+     * @param fileset The fileset to be converted.
+     */
+    public void addFileset( FileSet fs ) {
+        filesets.add( fs );
+    }
+    
+    /**
+     * Perform the conversion
+     *
+     * @param BuildException Thrown if an error occurs during execution of
+     *    this task.
+     */
+    public void execute() 
+        throws BuildException 
+    {
+        int count = 0;
+        
+        // Step through each file and convert.
+        Iterator iter = filesets.iterator();
+        while( iter.hasNext() ) {
+            FileSet fs = (FileSet)iter.next();
+            DirectoryScanner ds = fs.getDirectoryScanner( project );
+            File basedir = ds.getBasedir();
+            String[] files = ds.getIncludedFiles();
+            for( int i = 0; i < files.length; i++ ) {
+                File from = new File( basedir, files[i] );
+                File to = new File( todir, files[i] + ".html" );
+                if( !to.exists() || 
+                    (from.lastModified() > to.lastModified()) ) 
+                {
+                    log( "Converting file '" + from.getAbsolutePath() + 
+                        "' to '" + to.getAbsolutePath(), Project.MSG_VERBOSE );
+                    try {
+                        convert( from, to );
+                    }
+                    catch( IOException e ) {
+                        throw new BuildException( "Could not convert '" + 
+                            from.getAbsolutePath() + "' to '" + 
+                            to.getAbsolutePath() + "'", e );
+                    }
+                    count++;
+                }
+            }
+            if( count > 0 ) {
+                log( "Converted " + count + " file" + (count > 1 ? "s" : "") + 
+                    " to " + todir.getAbsolutePath() );
+            }
+        }
+    }
+    
+    /**
+     * Perform the actual copy and conversion
+     *
+     * @param from The input file
+     * @param to The output file
+     * @throws IOException Thrown if an error occurs during the conversion
+     */
+    private void convert( File from, File to )
+        throws IOException
+    {
+        // Open files:
+        BufferedReader in = new BufferedReader( new FileReader( from ) );
+        PrintWriter out = new PrintWriter( new FileWriter( to ) );
+        
+        // Output header:
+        out.println( "<html><body><pre>" );
+        
+        // Convert, line-by-line:
+        String line;
+        while( (line = in.readLine()) != null ) {
+            StringBuffer result = new StringBuffer();
+            int len = line.length();
+            for( int i = 0; i < len; i++ ) {
+                char c = line.charAt( i );
+                switch( c ) {
+                    case '&':
+                        result.append( "&amp;" );
+                        break;
+                    case '<':
+                        result.append( "&lt;" );
+                        break;
+                    default:
+                        result.append( c );
+                }
+            }
+            out.println( result.toString() );
+        }
+        
+        // Output footer:
+        out.println( "</pre></body></html>" );
+        
+        // Close streams:
+        out.close();
+        in.close();
+    }
+    
+}
+
+
diff --git a/servletapi/jsr152/src/etc/manifest b/servletapi/jsr152/src/etc/manifest
new file mode 100644
index 0000000..394843e
--- /dev/null
+++ b/servletapi/jsr152/src/etc/manifest
@@ -0,0 +1,10 @@
+Manifest-version: 1.0
+
+Name: javax/servlet/jsp/
+Specification-Title: Java API for JavaServer Pages
+Specification-Version: 2.0
+Specification-Vendor: Sun Microsystems, Inc.
+Implementation-Title: javax.servlet.jsp
+Implementation-Version: 2.0.@implementation.revision@
+Implementation-Vendor: Apache Software Foundation
+
diff --git a/servletapi/jsr152/src/share/dtd/jsp_2_0.xsd b/servletapi/jsr152/src/share/dtd/jsp_2_0.xsd
new file mode 100644
index 0000000..799bbaa
--- /dev/null
+++ b/servletapi/jsr152/src/share/dtd/jsp_2_0.xsd
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
+	    targetNamespace="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	    elementFormDefault="qualified"
+	    attributeFormDefault="unqualified"
+	    version="2.0">
+  <xsd:annotation>
+    <xsd:documentation>
+      @(#)jsp_2_0.xsds	1.17 03/18/03
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      Copyright 2002 Sun Microsystems, Inc., 901 San Antonio
+      Road, Palo Alto, California 94303, U.S.A. All rights
+      reserved.
+
+      Sun Microsystems, Inc. has intellectual property rights
+      relating to technology described in this document. In
+      particular, and without limitation, these intellectual
+      property rights may include one or more of the U.S. patents
+      listed at http://www.sun.com/patents and one or more
+      additional patents or pending patent applications in the
+      U.S. and other countries.
+
+      This document and the technology which it describes are
+      distributed under licenses restricting their use, copying,
+      distribution, and decompilation. No part of this document
+      may be reproduced in any form by any means without prior
+      written authorization of Sun and its licensors, if any.
+
+      Third-party software, including font technology, is
+      copyrighted and licensed from Sun suppliers.
+
+      Sun, Sun Microsystems, the Sun logo, Solaris, Java, J2EE,
+      JavaServer Pages, Enterprise JavaBeans and the Java Coffee
+      Cup logo are trademarks or registered trademarks of Sun
+      Microsystems, Inc. in the U.S. and other countries.
+
+      Federal Acquisitions: Commercial Software - Government Users
+      Subject to Standard License Terms and Conditions.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      This is the XML Schema for the JSP 2.0 deployment descriptor
+      types.  The JSP 2.0 schema contains all the special
+      structures and datatypes that are necessary to use JSP files
+      from a web application.
+
+      The contents of this schema is used by the web-app_2_4.xsd
+      file to define JSP specific content.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      The following conventions apply to all J2EE
+      deployment descriptor elements unless indicated otherwise.
+
+      - In elements that specify a pathname to a file within the
+	same JAR file, relative filenames (i.e., those not
+	starting with "/") are considered relative to the root of
+	the JAR file's namespace.  Absolute filenames (i.e., those
+	starting with "/") also specify names in the root of the
+	JAR file's namespace.  In general, relative names are
+	preferred.  The exception is .war files where absolute
+	names are preferred for consistency with the Servlet API.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:include schemaLocation="j2ee_1_4.xsd"/>
+
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="jsp-configType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The jsp-configType is used to provide global configuration
+	information for the JSP files in a web application. It has
+	two subelements, taglib and jsp-property-group.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="taglib"
+		   type="j2ee:taglibType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="jsp-property-group"
+		   type="j2ee:jsp-property-groupType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="jsp-fileType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The jsp-file element contains the full path to a JSP file
+	within the web application beginning with a `/'.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:pathType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="jsp-property-groupType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The jsp-property-groupType is used to group a number of
+	files so they can be given global property information.
+	All files so described are deemed to be JSP files.  The
+	following additional properties can be described:
+
+	    - Control whether EL is ignored
+	    - Control whether scripting elements are invalid
+	    - Indicate pageEncoding information.
+	    - Indicate that a resource is a JSP document (XML)
+	    - Prelude and Coda automatic includes.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="url-pattern"
+		   type="j2ee:url-patternType"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="el-ignored"
+		   type="j2ee:true-falseType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Can be used to easily set the isELIgnored
+	    property of a group of JSP pages.  By default, the
+	    EL evaluation is enabled for Web Applications using
+	    a Servlet 2.4 or greater web.xml, and disabled
+	    otherwise.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="page-encoding"
+		   type="j2ee:string"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The valid values of page-encoding are those of the
+	    pageEncoding page directive.  It is a
+	    translation-time error to name different encodings
+	    in the pageEncoding attribute of the page directive
+	    of a JSP page and in a JSP configuration element
+	    matching the page.  It is also a translation-time
+	    error to name different encodings in the prolog
+	    or text declaration of a document in XML syntax and
+	    in a JSP configuration element matching the document.
+	    It is legal to name the same encoding through
+	    mulitple mechanisms.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="scripting-invalid"
+		   type="j2ee:true-falseType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Can be used to easily disable scripting in a
+	    group of JSP pages.  By default, scripting is
+	    enabled.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="is-xml"
+		   type="j2ee:true-falseType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    If true, denotes that the group of resources
+	    that match the URL pattern are JSP documents,
+	    and thus must be interpreted as XML documents.
+	    If false, the resources are assumed to not
+	    be JSP documents, unless there is another
+	    property group that indicates otherwise.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="include-prelude"
+		   type="j2ee:pathType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The include-prelude element is a context-relative
+	    path that must correspond to an element in the
+	    Web Application.  When the element is present,
+	    the given path will be automatically included (as
+	    in an include directive) at the beginning of each
+	    JSP page in this jsp-property-group.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="include-coda"
+		   type="j2ee:pathType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The include-coda element is a context-relative
+	    path that must correspond to an element in the
+	    Web Application.  When the element is present,
+	    the given path will be automatically included (as
+	    in an include directive) at the end of each
+	    JSP page in this jsp-property-group.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="taglibType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The taglibType defines the syntax for declaring in
+	the deployment descriptor that a tag library is
+	available to the application.  This can be done
+	to override implicit map entries from TLD files and
+	from the container.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="taglib-uri"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    A taglib-uri element describes a URI identifying a
+	    tag library used in the web application.  The body
+	    of the taglib-uri element may be either an
+	    absolute URI specification, or a relative URI.
+	    There should be no entries in web.xml with the
+	    same taglib-uri value.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="taglib-location"
+		   type="j2ee:pathType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    the taglib-location element contains the location
+	    (as a resource relative to the root of the web
+	    application) where to find the Tag Library
+	    Description file for the tag library.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+</xsd:schema>
+
diff --git a/servletapi/jsr152/src/share/dtd/jspxml.dtd b/servletapi/jsr152/src/share/dtd/jspxml.dtd
new file mode 100644
index 0000000..dffe046
--- /dev/null
+++ b/servletapi/jsr152/src/share/dtd/jspxml.dtd
@@ -0,0 +1,189 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- DTD for JSP 2.0
+     thanks to Bob Foster, WebGain
+-->
+
+<!-- 
+     This DTD is not conditional on any parameter entities in the internal
+     subset and does not export any general entities.
+-->
+
+<!--================== Constrained Names ====================================-->
+
+<!ENTITY % URI "CDATA">
+    <!-- a Uniform Resource Identifier, see [RFC2396] -->
+
+<!ENTITY % UriList "CDATA">
+    <!-- a space separated list of Uniform Resource Identifiers -->
+
+<!ENTITY % URL "CDATA">
+    <!-- a relative urlSpec is as in Section 2.10.2. -->
+
+<!ENTITY % BeanID "IDREF">
+    <!-- a previously declared bean ID in the current scope. -->
+
+<!ENTITY % Prefix "CDATA">
+    <!-- a Name that contains no : characters. -->
+
+<!ENTITY % ClassName "CDATA">
+    <!-- a fully qualified class name. -->
+
+<!ENTITY % TypeName "CDATA">
+    <!-- a fully qualified class or interface name. -->
+
+<!ENTITY % BeanName "CDATA">
+    <!-- a bean name as expected by java.beans.Beans instantiate(). -->
+
+<!ENTITY % Content "CDATA">
+    <!-- a MIME type followed by an IANA charset, as " type [; S? ['charset='] charset] " -->
+
+<!ENTITY % Length "CDATA">
+    <!-- nn for pixels or nn% for percentage length -->
+
+<!ENTITY % Pixels "CDATA">
+    <!-- integer representing length in pixels -->
+
+<!ENTITY % Bool "(true|false|yes|no)">
+    <!-- boolean -->
+
+<!-- used for object, applet, img, input and iframe -->
+<!ENTITY % ImgAlign "(top|middle|bottom|left|right)">
+
+<!--=================== Element Groups ====================================-->
+
+<!ENTITY % Directives "jsp:directive.page|jsp:directive.include">
+
+<!ENTITY % Scripts "jsp:scriptlet|jsp:declaration|jsp:expression">
+
+<!ENTITY % Actions
+    "jsp:useBean
+    |jsp:setProperty
+    |jsp:getProperty
+    |jsp:include
+    |jsp:forward
+    |jsp:plugin"
+>
+
+<!ENTITY % Body "(jsp:text|%Directives;|%Scripts;|%Actions;)*">
+
+
+<!-- ============================ Elements ============================ -->
+
+<!--    Root element of a JSP page.
+-->
+<!ELEMENT jsp:root %Body;>
+<!ATTLIST jsp:root
+    xmlns:jsp       CDATA           "http://java.sun.com/JSP/Page"
+    version         CDATA           #REQUIRED
+>
+
+<!ELEMENT jsp:directive.page EMPTY>
+<!ATTLIST jsp:directive.page
+    language        CDATA           "java"
+    extends         %ClassName;     #IMPLIED
+    contentType     %Content;       "text/html; ISO-8859-1"
+    import          CDATA           #IMPLIED
+    session         %Bool;          "true"
+    buffer          CDATA           "8kb"
+    autoFlush       %Bool;          "true"
+    isThreadSafe    %Bool;          "true"
+    info            CDATA           #IMPLIED
+    errorPage       %URL;           #IMPLIED
+    isErrorPage     %Bool;          "false"
+    pageEncoding    CDATA           #IMPLIED
+    isELIgnored     %Bool;          #IMPLIED
+>
+
+<!-- the jsp:directive.include only appears in JSP documents and does
+     not appear in the XML views of JSP pages.
+-->
+
+<!ELEMENT jsp:directive.include EMPTY>
+<!ATTLIST jsp:directive.include
+    file            %URI;           #REQUIRED
+>
+
+<!ELEMENT jsp:scriptlet (#PCDATA)>
+
+<!ELEMENT jsp:declaration (#PCDATA)>
+
+<!ELEMENT jsp:expression (#PCDATA)>
+
+<!ELEMENT jsp:useBean %Body;>
+<!ATTLIST jsp:useBean
+    id              ID              #REQUIRED
+    class           %ClassName;     #IMPLIED
+    type            %TypeName;      #IMPLIED
+    beanName        %BeanName;      #IMPLIED
+    scope           (page
+                    |session
+                    |request
+                    |application)   "page"
+>
+
+<!ELEMENT jsp:setProperty EMPTY>
+<!ATTLIST jsp:setProperty
+    name            %BeanID;        #REQUIRED
+    property        CDATA           #REQUIRED
+    value           CDATA           #IMPLIED
+    param           CDATA           #IMPLIED
+>
+
+<!ELEMENT jsp:getProperty EMPTY>
+<!ATTLIST jsp:getProperty
+    name            %BeanID;        #REQUIRED
+    property        CDATA           #REQUIRED
+>
+
+<!ELEMENT jsp:include (jsp:param*)>
+<!ATTLIST jsp:include
+    flush           %Bool;          "false"
+    page            %URL;           #REQUIRED
+>
+
+<!ELEMENT jsp:forward (jsp:param*)>
+<!ATTLIST jsp:forward
+    page            %URL;           #REQUIRED
+>
+
+<!ELEMENT jsp:plugin (jsp:params?, jsp:fallback?)>
+<!ATTLIST jsp:plugin
+    type            (bean|applet)   #REQUIRED
+    code            %URI;           #IMPLIED
+    codebase        %URI;           #IMPLIED
+    align           %ImgAlign;      #IMPLIED
+    archive         %UriList;       #IMPLIED
+    height          %Length;        #IMPLIED
+    hspace          %Pixels;        #IMPLIED
+    jreversion      CDATA           "1.2"
+    name            NMTOKEN         #IMPLIED
+    vspace          %Pixels;        #IMPLIED
+    width           %Length;        #IMPLIED
+    nspluginurl     %URI;           #IMPLIED
+    iepluginurl     %URI;           #IMPLIED
+>
+
+<!ELEMENT jsp:params (jsp:param+)>
+
+<!ELEMENT jsp:param EMPTY>
+<!ATTLIST jsp:param
+    name            CDATA           #REQUIRED
+    value           CDATA           #REQUIRED
+>
+
+<!ELEMENT jsp:text #PCDATA>
+
diff --git a/servletapi/jsr152/src/share/dtd/jspxml.xsd b/servletapi/jsr152/src/share/dtd/jspxml.xsd
new file mode 100644
index 0000000..886ee65
--- /dev/null
+++ b/servletapi/jsr152/src/share/dtd/jspxml.xsd
@@ -0,0 +1,513 @@
+<?xml version ="1.0"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE schema [
+<!-- Patterns -->
+<!ENTITY Identifier   "(\p{L}|_|$)(\p{N}|\p{L}|_|$)*">
+<!ENTITY TypeName     "&Identifier;(\.&Identifier;)*">
+<!ENTITY WS       "\s*">
+<!ENTITY Import     "&TypeName;(\.\*)?">
+<!ENTITY ImportList   "&Import;(&WS;,&WS;&Import;)*">
+<!ENTITY SetProp    "(&Identifier;|\*)">
+<!ENTITY RelativeURL  "[^:#/\?]*(:{0,0}|[#/\?].*)">
+<!ENTITY Length     "[0-9]*&#x25;?">
+<!ENTITY AsciiName    "[A-Za-z0-9_-]*">
+<!ENTITY ValidContentType  "&AsciiName;/&AsciiName;(;&WS;(charset=)?&AsciiName;)?">
+<!ENTITY ValidPageEncoding  "&AsciiName;/&AsciiName;">
+<!ENTITY Buffer     "[0-9]+kb">
+<!ENTITY RTexpr     "&#x25;=.*&#x25;">
+]>
+
+
+<!--Conforms to w3c http://www.w3.org/2001/XMLSchema -->
+
+<xsd:schema
+    xmlns = "http://java.sun.com/JSP/Page"
+    xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
+    xmlns:jsp = "http://java.sun.com/JSP/Page"
+    targetNamespace = "http://java.sun.com/JSP/Page"
+    elementFormDefault = "qualified"
+    attributeFormDefault = "unqualified">
+
+  <xsd:annotation>
+    <xsd:documentation>
+      XML Schema for JSP 2.0.
+
+      This schema is based upon the recent (May 5th, 2001)
+      W3C recommendation for XML Schema.
+
+      A JSP translator should reject an XML-format file that is
+      not strictly valid according to this schema or does not observe
+      the constraints documented here. A translator is not required
+      to use this schema for validation or to use a validating parser.
+    </xsd:documentation>
+  </xsd:annotation>
+
+
+  <!-- Complex Types -->
+
+  <xsd:complexType name = "Body">
+    <xsd:annotation>
+      <xsd:documentation>
+        Body defines the "top-level" elements in root and beanInfo.
+        There are probably other elements that should use it.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:group ref = "Bodygroup" minOccurs = "0" maxOccurs = "unbounded"/>
+  </xsd:complexType>
+
+  <!-- groups -->
+
+  <xsd:group name = "Bodygroup">
+    <xsd:choice>
+      <xsd:element ref = "directive.page"/>
+      <xsd:element ref = "directive.include"/>
+      <xsd:element ref = "scriptlet"/>
+      <xsd:element ref = "declaration"/>
+      <xsd:element ref = "expression"/>
+      <xsd:element ref = "useBean"/>
+      <xsd:element ref = "setProperty"/>
+      <xsd:element ref = "getProperty"/>
+      <xsd:element ref = "include"/>
+      <xsd:element ref = "forward"/>
+      <xsd:element ref = "plugin"/>
+      <xsd:element ref = "text"/>
+      <xsd:any namespace="##other" processContents = "lax"/>
+    </xsd:choice>
+  </xsd:group>
+
+
+  <!-- Simple types are next -->
+
+  <xsd:simpleType name = "RTE">
+    <xsd:annotation>
+      <xsd:documentation>
+        A request-time expression value
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&RTexpr;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "Bool">
+    <xsd:annotation>
+      <xsd:documentation>
+        Bool would be boolean except it does not accept 1 and 0.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:NMTOKEN" >
+      <xsd:enumeration value = "true"/>
+      <xsd:enumeration value = "false"/>
+      <xsd:enumeration value = "yes"/>
+      <xsd:enumeration value = "no"/>
+    </xsd:restriction>     
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "Identifier">
+    <xsd:annotation>
+      <xsd:documentation>
+        Identifier is an unqualified Java identifier.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&Identifier;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "TypeName">
+    <xsd:annotation>
+      <xsd:documentation>
+        TypeName is one or more Java identifiers separated by dots
+        with no whitespace.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&TypeName;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "ImportList">
+    <xsd:annotation>
+      <xsd:documentation>
+        ImportList is one or more typeNames separated by commas.
+        Whitespace is allowed before and after the comma.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&ImportList;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "SetProp">
+    <xsd:annotation>
+      <xsd:documentation>
+        SetProp is an Identifier or *.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&SetProp;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "RelativeURL">
+    <xsd:annotation>
+      <xsd:documentation>
+        RelativeURL is a uriReference with no colon character
+        before the first /, ? or #, if any (RFC2396).
+      </xsd:documentation>
+    </xsd:annotation>
+	<xsd:restriction base = "xsd:anyURI">
+      <xsd:pattern value = "&RelativeURL;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "RTERelativeURL">
+    <xsd:union memberTypes = "RelativeURL RTE"/>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "Length">
+    <xsd:annotation>
+      <xsd:documentation>
+        Length is nn or nn%.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&Length;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+
+  <xsd:simpleType name = "ExplicitBufferSize">
+    <xsd:annotation>
+      <xsd:documentation>
+         Buffer Size with an explicit value
+      </xsd:documentation>
+    </xsd:annotation> 
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&Buffer;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "NoneBufferSize">
+    <xsd:annotation>
+      <xsd:documentation>
+         Buffer Size with a "none" value
+      </xsd:documentation>
+    </xsd:annotation> 
+       <xsd:restriction base = "xsd:string">
+         <xsd:enumeration value = "none"/>
+       </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "BufferSize">
+    <xsd:annotation>
+      <xsd:documentation>
+        Buffer size is xkb or none.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:union memberTypes = "ExplicitBufferSize NoneBufferSize"/>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "ContentType">
+    <xsd:annotation>
+      <xsd:documentation>
+        Content type and character encoding for this page.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&ValidContentType;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "PageEncoding">
+    <xsd:annotation>
+      <xsd:documentation>
+        Page Encoding for this page.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&ValidPageEncoding;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "Scope">
+    <xsd:annotation>
+      <xsd:documentation>
+        valid scope values
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:NMTOKEN">
+      <xsd:enumeration value = "page"/>
+      <xsd:enumeration value = "session"/>
+      <xsd:enumeration value = "request"/>
+      <xsd:enumeration value = "application"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "PlugInType">
+    <xsd:annotation>
+      <xsd:documentation>
+        valid values for a plugin type
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:NMTOKEN">
+      <xsd:enumeration value = "bean"/>
+      <xsd:enumeration value = "applet"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "AlignType">
+    <xsd:annotation>
+      <xsd:documentation>
+        Buffer size is xkb.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:NMTOKEN">
+      <xsd:enumeration value = "top"/>
+      <xsd:enumeration value = "middle"/>
+      <xsd:enumeration value = "bottom"/>
+      <xsd:enumeration value = "left"/>
+      <xsd:enumeration value = "right"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <!-- Elements follow -->
+
+  <xsd:element name = "root">
+    <xsd:annotation>
+      <xsd:documentation>
+        The root element of all JSP documents is named root.
+        
+        Authors may, if they wish, include schema location information.
+        If specified, the information may appear as attributes of
+        the root element as follows:
+
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/JSP/Page xsd-file-location"
+
+        Documents should not specify the system identifier of a DTD
+        in a DOCTYPE declaration.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:complexContent>
+        <xsd:extension base = "Body">
+          <xsd:attribute name = "version" fixed = "2.0" type = "xsd:string"/>
+        </xsd:extension>
+      </xsd:complexContent>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "directive.page">
+    <xsd:annotation>
+      <xsd:documentation>
+        directive.page is the "page directive".
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:attribute name = "language" default = "java" type = "xsd:string"/>
+      <xsd:attribute name = "extends" type = "TypeName"/>
+      <xsd:attribute name = "contentType" default = "text/html; ISO-8859-1" type = "ContentType"/>
+      <xsd:attribute name = "pageEncoding" use = "optional" type = "PageEncoding"/>
+      <xsd:attribute name = "import" type = "ImportList"/>
+      <xsd:attribute name = "session" default = "true" type = "Bool"/>
+      <xsd:attribute name = "buffer" default = "8kb" type = "BufferSize"/>
+      <xsd:attribute name = "autoFlush" default = "true" type = "Bool"/>
+      <xsd:attribute name = "isThreadSafe" default = "true" type = "Bool"/>
+      <xsd:attribute name = "info" type = "xsd:string"/>
+      <xsd:attribute name = "errorPage" type = "RelativeURL"/>
+      <xsd:attribute name = "isErrorPage" default = "false" type = "Bool"/>
+      <xsd:attribute name = "isELIgnored" type = "Bool"/>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "directive.include">
+    <xsd:annotation>
+      <xsd:documentation>
+        directive.include is the "include directive".
+	This element does not appear on XML views of JSP pages.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:attribute name = "file" use = "required" type = "RelativeURL"/>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name = "scriptlet" type = "xsd:string">
+    <xsd:annotation>
+      <xsd:documentation>
+        The representation of a scriplet.
+      </xsd:documentation>
+    </xsd:annotation>
+  </xsd:element>
+  
+  <xsd:element name = "declaration" type = "xsd:string">
+    <xsd:annotation>
+      <xsd:documentation>
+        The reprsentation of a declaration.
+      </xsd:documentation>
+    </xsd:annotation>
+  </xsd:element>
+  
+  <xsd:element name = "expression" type = "xsd:string">
+    <xsd:annotation>
+      <xsd:documentation>
+        The representation of an expression.
+      </xsd:documentation>
+    </xsd:annotation>
+  </xsd:element>
+ 
+  <xsd:element name = "text" type = "xsd:string">
+    <xsd:annotation>
+      <xsd:documentation>
+        Verbatim template text.
+      </xsd:documentation>
+    </xsd:annotation>
+  </xsd:element>
+
+  <xsd:element name = "useBean">
+    <xsd:annotation>
+      <xsd:documentation>
+        useBean instantiates or accesses a bean in the specified scope.
+        
+        Constraint: The allowed combinations of attributes are:
+        
+          class [type] | type [( class | beanName)]
+        
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:complexContent>
+        <xsd:extension base="Body">
+          <xsd:attribute name = "id" use = "required" type = "Identifier"/>
+          <xsd:attribute name = "class" type = "TypeName"/>
+          <xsd:attribute name = "type" type = "TypeName"/>
+          <xsd:attribute name = "beanName" type = "TypeName"/>
+          <xsd:attribute name = "scope" default = "page" type = "Scope"/>
+        </xsd:extension>
+      </xsd:complexContent>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "setProperty">
+    <xsd:annotation>
+      <xsd:documentation>
+        setProperty changes the value of an object property.
+        
+        Constraint: The object named by the name must have been
+        "introduced" to the JSP processor using either the
+        jsp:useBean action or a custom action with an associated
+        VariableInfo entry for this name.
+
+        Exact valid combinations are not expressable in XML Schema.
+        They are:
+
+        name="Identifier" property="*"
+        name="Identifier" property="Identfiier" param="string"
+        name="Identifier" property="Identifier" value="string"
+                
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:attribute name = "name" use = "required" type = "Identifier"/>
+      <xsd:attribute name = "property" use = "required" type = "SetProp"/>
+      <xsd:attribute name = "param" type = "xsd:string"/>
+      <xsd:attribute name = "value" type = "xsd:string"/>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name = "getProperty">
+    <xsd:annotation>
+      <xsd:documentation>
+        getProperty obtains the value of an object property.
+        
+        Constraint: The object named by the name must have been
+        "introduced" to the JSP processor using either the
+        jsp:useBean action or a custom action with an associated
+        VariableInfo entry for this name.
+        
+        ???The spec is interpreted as restricting the values of
+        property to Identifier.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:attribute name = "name" use = "required" type = "Identifier"/>
+      <xsd:attribute name = "property" use = "required" type = "Identifier"/>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "include">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element ref = "param" minOccurs = "0" maxOccurs = "unbounded"/>
+      </xsd:sequence>
+      <xsd:attribute name = "flush" default = "false" type = "Bool"/>
+      <xsd:attribute name = "page" use = "required" type = "RTERelativeURL"/>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name = "forward">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element ref = "param" minOccurs = "0" maxOccurs = "unbounded"/>
+      </xsd:sequence>
+      <xsd:attribute name = "page" use = "required" type = "RTERelativeURL"/>
+     </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "plugin">
+    <xsd:complexType> <!-- content only! -->
+      <xsd:sequence>
+        <xsd:element ref = "params" minOccurs = "0" maxOccurs = "1"/>
+        <xsd:element name = "fallback" minOccurs = "0" maxOccurs = "1" type = "Body"/>
+      </xsd:sequence>
+      <xsd:attribute name = "type" use = "required" type = "PlugInType"/>
+      <xsd:attribute name = "code" type = "xsd:anyURI"/>
+      <xsd:attribute name = "codebase" type = "xsd:anyURI"/>
+      <xsd:attribute name = "align" type = "AlignType"/>
+      <xsd:attribute name = "archive">
+        <xsd:simpleType>
+           <xsd:list itemType="xsd:anyURI"/>
+        </xsd:simpleType>
+      </xsd:attribute>
+      <xsd:attribute name = "height" type = "Length"/>
+      <xsd:attribute name = "hspace" type = "xsd:int"/>
+      <xsd:attribute name = "jreversion" default = "1.2" type = "xsd:string"/>
+      <xsd:attribute name = "name" type = "xsd:NMTOKEN"/>
+      <xsd:attribute name = "vspace" type = "xsd:int"/>
+      <xsd:attribute name = "width" type = "Length"/>
+      <xsd:attribute name = "nspluginurl" type = "xsd:anyURI"/>
+      <xsd:attribute name = "iepluginurl" type = "xsd:anyURI"/>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "params">
+    <xsd:complexType>
+       <xsd:sequence>
+         <xsd:element ref = "param" minOccurs = "1" maxOccurs = "unbounded"/>
+       </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name = "param">
+    <xsd:complexType>
+      <xsd:attribute name = "name" use = "required" type = "xsd:NMTOKEN"/>
+      <xsd:attribute name = "value" use = "required" type = "xsd:string"/>
+    </xsd:complexType>
+  </xsd:element>
+  
+</xsd:schema>
diff --git a/servletapi/jsr152/src/share/dtd/web-jsptaglibrary_1_1.dtd b/servletapi/jsr152/src/share/dtd/web-jsptaglibrary_1_1.dtd
new file mode 100644
index 0000000..4a5fc7a
--- /dev/null
+++ b/servletapi/jsr152/src/share/dtd/web-jsptaglibrary_1_1.dtd
@@ -0,0 +1,206 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!--
+
+   This is the DTD defining the JavaServer Pages 1.1 Tag Library
+   descriptor (.tld) (XML) file format/syntax.
+
+   A Tag Library is a JAR file containing a valid instance of a Tag Library
+   Descriptor (taglib.tld) file in the META-INF subdirectory, along with the
+   appropriate implementing classes, and other resources required to
+   implement the tags defined therein.
+
+   Use is subject to license terms.
+  -->
+
+<!--
+The taglib tag is the document root, it defines:
+
+tlibversion	the version of the tag library implementation
+jspversion	the version of JSP the tag library depends upon
+
+shortname	a simple default short name that could be used by
+		a JSP authoring tool to create names with a mnemonic
+		value; for example, the it may be used as the prefered
+		prefix value in taglib directives
+uri		a uri uniquely identifying this taglib
+info		a simple string describing the "use" of this taglib,
+		should be user discernable
+-->
+
+<!ELEMENT taglib (tlibversion, jspversion?, shortname, uri?, info?, tag+) >
+<!ATTLIST taglib id ID #IMPLIED
+	  xmlns CDATA #FIXED
+		"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"
+>
+
+<!--
+Describes this version (number) of the taglibrary (dewey decimal)
+
+#PCDATA ::= [0-9]*{ "."[0-9] }0..3
+-->
+
+<!ELEMENT tlibversion (#PCDATA) >
+
+<!--
+Describes the JSP version (number) this taglibrary requires in
+order to function (dewey decimal)
+
+The default is 1.1
+
+#PCDATA ::= [0-9]*{ "."[0-9] }0..3
+-->
+
+<!ELEMENT jspversion  (#PCDATA) >
+
+<!--
+Defines a short (default) shortname to be used for tags and
+variable names used/created by this tag library.  Do not use
+white space, and do not start with digits or underscore.
+
+#PCDATA ::= NMTOKEN
+-->
+
+<!ELEMENT shortname      (#PCDATA) >
+
+<!--
+Defines a public URI that uniquely identifies this version of
+the taglibrary Leave it empty if it does not apply.
+-->
+
+<!ELEMENT uri	 (#PCDATA) >
+
+<!--
+Defines an arbitrary text string descirbing the tag library
+-->
+
+<!ELEMENT info	(#PCDATA) >
+
+<!--
+The tag defines a unique tag in this tag library, defining:
+
+- the unique tag/element name
+- the subclass of javax.servlet.jsp.tagext.Tag implementation class
+- an optional subclass of javax.servlet.jsp.tagext.TagExtraInfo
+- the body content type (hint)
+- optional tag-specific information
+- any attributes
+-->
+
+<!ELEMENT tag (name, tagclass, teiclass?, bodycontent?, info?, attribute*) >
+
+<!--
+Defines the subclass of javax.serlvet.jsp.tagext.Tag that implements
+the request time semantics for this tag. (required)
+
+#PCDATA ::= fully qualified Java class name
+-->
+
+<!ELEMENT tagclass (#PCDATA) >
+
+<!--
+Defines the subclass of javax.servlet.jsp.tagext.TagExtraInfo for
+this tag. (optional)
+
+If this is not given, the class is not consulted at translation time.
+
+#PCDATA ::= fully qualified Java class name
+-->
+
+<!ELEMENT teiclass (#PCDATA) >
+
+<!--
+Provides a hint as to the content of the body of this tag. Primarily
+intended for use by page composition tools.
+
+There are currently three values specified:
+
+tagdependent	The body of the tag is interpreted by the tag
+		implementation itself, and is most likely in a
+		different "langage", e.g embedded SQL statements.
+
+JSP		The body of the tag contains nested JSP syntax
+
+empty		The body must be empty
+
+The default (if not defined) is JSP
+
+#PCDATA ::=  tagdependent | JSP | empty
+
+-->
+
+<!ELEMENT bodycontent (#PCDATA) >
+
+<!--
+The attribute tag defines an attribute for the nesting tag
+
+An attribute definition is composed of:
+
+- the attributes name (required)
+- if the attribute is required or optional (optional)
+- if the attributes value may be dynamically calculated at runtime
+  by a scriptlet expression (optional)
+
+-->
+
+<!ELEMENT attribute (name, required? , rtexprvalue?) >
+
+<!--
+Defines the canonical name of a tag or attribute being defined
+
+#PCDATA ::= NMTOKEN
+-->
+
+<!ELEMENT name	(#PCDATA) >
+
+<!--
+Defines if the nesting attribute is required or optional.
+
+#PCDATA ::= true | false | yes | no
+
+If not present then the default is "false", i.e the attribute
+is optional.
+-->
+
+<!ELEMENT required    (#PCDATA) >
+
+<!--
+Defines if the nesting attribute can have scriptlet expressions as
+a value, i.e the value of the attribute may be dynamically calculated
+at request time, as opposed to a static value determined at translation
+time.
+
+#PCDATA ::= true | false | yes | no
+
+If not present then the default is "false", i.e the attribute
+has a static value
+-->
+
+<!ELEMENT rtexprvalue (#PCDATA) >
+
+<!ATTLIST tlibversion id ID #IMPLIED>
+<!ATTLIST jspversion id ID #IMPLIED>
+<!ATTLIST shortname id ID #IMPLIED>
+<!ATTLIST uri id ID #IMPLIED>
+<!ATTLIST info id ID #IMPLIED>
+<!ATTLIST tag id ID #IMPLIED>
+<!ATTLIST tagclass id ID #IMPLIED>
+<!ATTLIST teiclass id ID #IMPLIED>
+<!ATTLIST bodycontent id ID #IMPLIED>
+<!ATTLIST attribute id ID #IMPLIED>
+<!ATTLIST name id ID #IMPLIED>
+<!ATTLIST required id ID #IMPLIED>
+<!ATTLIST rtexprvalue id ID #IMPLIED>
diff --git a/servletapi/jsr152/src/share/dtd/web-jsptaglibrary_1_2.dtd b/servletapi/jsr152/src/share/dtd/web-jsptaglibrary_1_2.dtd
new file mode 100644
index 0000000..41165b2
--- /dev/null
+++ b/servletapi/jsr152/src/share/dtd/web-jsptaglibrary_1_2.dtd
@@ -0,0 +1,477 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!--
+
+   This is the DTD defining the JavaServer Pages 1.2 Tag Library
+   descriptor (.tld) (XML) file format/syntax.
+
+   A Tag Library is a JAR file containing a valid instance of a Tag Library
+   Descriptor (taglib.tld) file in the META-INF subdirectory, along with the
+   appropriate implementing classes, and other resources required to
+   implement the tags defined therein.
+
+   Use is subject to license terms.
+  -->
+
+<!NOTATION WEB-JSPTAGLIB.1_2 PUBLIC
+          "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN">
+
+<!--
+This is the XML DTD for the JSP 1.2 Tag Library Descriptor.
+All JSP 1.2 tag library descriptors must include a DOCTYPE
+of the following form:
+
+  <!DOCTYPE taglib
+        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
+	"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
+
+-->
+
+<!--
+The taglib tag is the document root, it defines:
+
+tlib-version	the version of the tag library implementation
+
+jsp-version	the version of JSP the tag library depends upon
+
+short-name	a simple default short name that could be used by
+		a JSP authoring tool to create names with a mnemonic
+		value; for example, the it may be used as the prefered
+		prefix value in taglib directives
+
+uri		a uri uniquely identifying this taglib
+
+display-name    the display-name element contains a short name that
+                is intended to be displayed by tools
+small-icon      optional small-icon that can be used by tools
+
+large-icon      optional large-icon that can be used by tools
+
+description	a simple string describing the "use" of this taglib,
+		should be user discernable
+
+validator       optional TagLibraryValidator information
+
+listener        optional event listener specification
+
+
+-->
+
+<!ELEMENT taglib (tlib-version, jsp-version, short-name, uri?,
+                  display-name?, small-icon?, large-icon?, description?,
+                  validator?, listener*, tag+) >
+
+<!ATTLIST taglib id ID #IMPLIED
+	  xmlns CDATA #FIXED
+		"http://java.sun.com/JSP/TagLibraryDescriptor"
+>
+
+<!--
+Describes this version (number) of the taglibrary (dewey decimal)
+
+#PCDATA ::= [0-9]*{ "."[0-9] }0..3
+-->
+
+<!ELEMENT tlib-version (#PCDATA) >
+
+<!--
+Describes the JSP version (number) this taglibrary requires in
+order to function (dewey decimal)
+
+The default is 1.2
+
+#PCDATA ::= [0-9]*{ "."[0-9] }0..3
+-->
+
+<!ELEMENT jsp-version  (#PCDATA) >
+
+<!--
+Defines a short (default) short-name to be used for tags and
+variable names used/created by this tag library.  Do not use
+white space, and do not start with digits or underscore.
+
+#PCDATA ::= NMTOKEN
+-->
+
+<!ELEMENT short-name      (#PCDATA) >
+
+<!--
+Defines a public URI that uniquely identifies this version of
+the taglibrary.  Leave it empty if it does not apply.
+-->
+
+<!ELEMENT uri	 (#PCDATA) >
+
+<!--
+Defines an arbitrary text string descirbing the tag library
+-->
+
+<!ELEMENT description	(#PCDATA) >
+
+<!--
+
+Defines an optional validator that can be used to
+validate the conformance of a JSP page to using this tag library.
+-->
+
+<!ELEMENT validator (validator-class, init-param*, description?) >
+
+
+<!--
+
+Defines the TagLibraryValidator class that can be used to
+validate the conformance of a JSP page to using this tag library.
+-->
+
+<!ELEMENT validator-class (#PCDATA) >
+
+
+<!--
+
+The init-param element contains a name/value pair as an
+initialization param
+-->
+
+<!ELEMENT init-param (param-name, param-value, description?)>
+
+<!--
+
+The param-name element contains the name of a parameter.
+-->
+
+<!ELEMENT param-name (#PCDATA)>
+
+<!--
+
+The param-value element contains the value of a parameter.
+-->
+
+<!ELEMENT param-value (#PCDATA)>
+
+
+<!--
+
+Defines an optional event listener object to be instantiated and
+registered automatically.
+-->
+
+<!ELEMENT listener (listener-class) >
+
+<!--
+
+The listener-class element declares a class in the application that
+must be registered as a web application listener bean.  See the
+Servlet 2.3 specification for details.
+-->
+
+<!ELEMENT listener-class (#PCDATA) >
+
+
+<!--
+The tag defines a unique tag in this tag library.  It has one
+attribute, id.
+
+The tag element may have several subelements defining:
+
+name              The unique action name
+
+tag-class         The tag handler class implementing
+                  javax.servlet.jsp.tagext.Tag
+
+tei-class         An optional subclass of
+                  javax.servlet.jsp.tagext.TagExtraInfo
+
+body-content      The body content type
+
+display-name      A short name that is intended to be displayed
+                  by tools
+
+small-icon        Optional small-icon that can be used by tools
+
+large-icon        Optional large-icon that can be used by tools
+
+description       Optional tag-specific information
+
+variable          Optional scripting variable information
+
+attribute         All attributes of this action
+
+example           Optional informal description of an example of a
+                  use of this tag
+
+-->
+
+<!ELEMENT tag (name, tag-class, tei-class?, body-content?, display-name?,
+               small-icon?, large-icon?, description?, variable*, attribute*,
+               example?) >
+
+<!--
+Defines the subclass of javax.serlvet.jsp.tagext.Tag that implements
+the request time semantics for this tag. (required)
+
+#PCDATA ::= fully qualified Java class name
+-->
+
+<!ELEMENT tag-class (#PCDATA) >
+
+<!--
+Defines the subclass of javax.servlet.jsp.tagext.TagExtraInfo for
+this tag. (optional)
+
+If this is not given, the class is not consulted at translation time.
+
+#PCDATA ::= fully qualified Java class name
+-->
+
+<!ELEMENT tei-class (#PCDATA) >
+
+<!--
+Provides a hint as to the content of the body of this tag. Primarily
+intended for use by page composition tools.
+
+There are currently three values specified:
+
+tagdependent	The body of the tag is interpreted by the tag
+		implementation itself, and is most likely in a
+		different "langage", e.g embedded SQL statements.
+
+JSP		The body of the tag contains nested JSP syntax
+
+empty		The body must be empty
+
+The default (if not defined) is JSP
+
+#PCDATA ::=  tagdependent | JSP | empty
+
+-->
+
+<!ELEMENT body-content (#PCDATA) >
+
+<!--
+
+The display-name element contains a short name that is intended
+to be displayed by tools.
+-->
+
+<!ELEMENT display-name (#PCDATA) >
+
+
+<!--
+
+The large-icon element contains the name of a file containing a large
+(32 x 32) icon image.  The file name is a relative path within the
+tag library.  The image must be either in the JPEG or GIF format, and
+the file name must end with the suffix ".jpg" or ".gif" respectively.
+The icon can be used by tools.
+-->
+
+<!ELEMENT large-icon (#PCDATA) >
+
+<!--
+
+The small-icon element contains the name of a file containing a large
+(32 x 32) icon image.  The file name is a relative path within the
+tag library.  The image must be either in the JPEG or GIF format, and
+the file name must end with the suffix ".jpg" or ".gif" respectively.
+The icon can be used by tools.
+-->
+
+<!ELEMENT small-icon (#PCDATA) >
+
+<!--
+
+The example element contains an informal description of an example
+of the use of a tag.
+-->
+
+<!ELEMENT example (#PCDATA) >
+
+<!--
+
+The variable tag provides information on the scripting variables
+defined by this tag.  It is a (translation time) error for a tag
+that has one or more variable subelements to have a TagExtraInfo
+class that returns a non-null object.
+
+The subelements of variable are of the form:
+
+name-given               The variable name as a constant
+
+name-from-attribute      The name of an attribute whose (translation
+                         time) value will give the name of the
+                         variable.  One of name-given or
+                         name-from-attribute is required.
+
+variable-class           Name of the class of the variable.
+                         java.lang.String is default.
+
+declare                  Whether the variable is declared or not.
+                         True is the default.
+
+scope                    The scope of the scripting varaible
+                         defined.  NESTED is default.
+
+description              Optional description of this variable
+
+-->
+
+<!ELEMENT variable ( (name-given | name-from-attribute), variable-class?,
+                    declare?, scope?, description?) >
+
+<!--
+
+The name for the scripting variable.  One of name-given or
+name-from-attribute is required.
+-->
+
+<!ELEMENT name-given (#PCDATA) >
+
+<!--
+
+The name of an attribute whose (translation-time) value will give
+the name of the variable.  One of name-given or name-from-attribute
+is required.
+-->
+
+<!ELEMENT name-from-attribute (#PCDATA) >
+
+<!--
+
+The optional name of the class for the scripting variable.  The
+default is java.lang.String.
+-->
+
+<!ELEMENT variable-class (#PCDATA) >
+
+<!--
+
+Whether the scripting variable is to be defined or not.  See
+TagExtraInfo for details.  This element is optional and "true"
+is the default.
+-->
+
+<!ELEMENT declare (#PCDATA) >
+
+<!--
+
+The scope of the scripting variable.  See TagExtraInfo for details.
+The element is optional and "NESTED" is the default.  Other legal
+values are "AT_BEGIN" and "AT_END".
+-->
+
+<!ELEMENT scope (#PCDATA) >
+
+<!--
+
+The attribute tag defines an attribute for the nesting tag
+
+An attribute definition is composed of:
+	
+- the attributes name (required)
+- if the attribute is required or optional (optional)
+- if the attributes value may be dynamically calculated at runtime
+  by a scriptlet expression (optional)
+- the type of the attributes value (optional)
+- an informal description of the meaning of the attribute (optional)
+
+-->
+
+
+<!--
+The attribute tag defines an attribute for the nesting tag
+
+An attribute definition is composed of:
+
+- the attributes name (required)
+
+- if the attribute is required or optional (optional)
+
+- if the attributes value may be dynamically calculated at runtime
+  by a scriptlet expression (optional)
+
+- the type of the attributes value (optional)
+
+- an informal description of the meaning of the attribute (optional)
+-->
+
+<!ELEMENT attribute (name, required? , rtexprvalue?, type?, description?) >
+
+<!--
+Defines the canonical name of a tag or attribute being defined
+
+#PCDATA ::= NMTOKEN
+-->
+
+<!ELEMENT name	(#PCDATA) >
+
+<!--
+Defines if the nesting attribute is required or optional.
+
+#PCDATA ::= true | false | yes | no
+
+If not present then the default is "false", i.e the attribute
+is optional.
+-->
+
+<!ELEMENT required    (#PCDATA) >
+
+<!--
+Defines if the nesting attribute can have scriptlet expressions as
+a value, i.e the value of the attribute may be dynamically calculated
+at request time, as opposed to a static value determined at translation
+time.
+
+#PCDATA ::= true | false | yes | no
+
+If not present then the default is "false", i.e the attribute
+has a static value
+-->
+
+<!ELEMENT rtexprvalue (#PCDATA) >
+
+
+<!--
+
+Defines the Java type of the attributes value.  For static values
+(those determined at translation time) the type is always
+java.lang.String.
+-->
+
+<!ELEMENT type (#PCDATA) >
+
+
+<!-- ID attributes -->
+
+<!ATTLIST tlib-version id ID #IMPLIED>
+<!ATTLIST jsp-version id ID #IMPLIED>
+<!ATTLIST short-name id ID #IMPLIED>
+<!ATTLIST uri id ID #IMPLIED>
+<!ATTLIST description id ID #IMPLIED>
+<!ATTLIST example id ID #IMPLIED>
+<!ATTLIST tag id ID #IMPLIED>
+<!ATTLIST tag-class id ID #IMPLIED>
+<!ATTLIST tei-class id ID #IMPLIED>
+<!ATTLIST body-content id ID #IMPLIED>
+<!ATTLIST attribute id ID #IMPLIED>
+<!ATTLIST name id ID #IMPLIED>
+<!ATTLIST required id ID #IMPLIED>
+<!ATTLIST rtexprvalue id ID #IMPLIED>
+
+
+<!ATTLIST param-name id ID #IMPLIED>
+<!ATTLIST param-value id ID #IMPLIED>
+<!ATTLIST listener id ID #IMPLIED>
+<!ATTLIST listener-class id ID #IMPLIED>
diff --git a/servletapi/jsr152/src/share/dtd/web-jsptaglibrary_2_0.xsd b/servletapi/jsr152/src/share/dtd/web-jsptaglibrary_2_0.xsd
new file mode 100644
index 0000000..8113b09
--- /dev/null
+++ b/servletapi/jsr152/src/share/dtd/web-jsptaglibrary_2_0.xsd
@@ -0,0 +1,1025 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<xsd:schema
+     targetNamespace="http://java.sun.com/xml/ns/j2ee"
+     xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
+     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+     elementFormDefault="qualified"
+     attributeFormDefault="unqualified"
+     version="2.0">
+
+  <xsd:annotation>
+    <xsd:documentation>
+      %W% %G%
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      Copyright 2003 Sun Microsystems, Inc., 901 San Antonio
+      Road, Palo Alto, California 94303, U.S.A. All rights
+      reserved.
+
+      Sun Microsystems, Inc. has intellectual property rights
+      relating to technology described in this document. In
+      particular, and without limitation, these intellectual
+      property rights may include one or more of the U.S. patents
+      listed at http://www.sun.com/patents and one or more
+      additional patents or pending patent applications in the
+      U.S. and other countries.
+
+      This document and the technology which it describes are
+      distributed under licenses restricting their use, copying,
+      distribution, and decompilation. No part of this document
+      may be reproduced in any form by any means without prior
+      written authorization of Sun and its licensors, if any.
+
+      Third-party software, including font technology, is
+      copyrighted and licensed from Sun suppliers.
+
+      Sun, Sun Microsystems, the Sun logo, Solaris, Java, J2EE,
+      JavaServer Pages, Enterprise JavaBeans and the Java Coffee
+      Cup logo are trademarks or registered trademarks of Sun
+      Microsystems, Inc. in the U.S. and other countries.
+
+      Federal Acquisitions: Commercial Software - Government Users
+      Subject to Standard License Terms and Conditions.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+      <![CDATA[
+
+	This is the XML Schema for the JSP Taglibrary
+	descriptor.  All Taglibrary descriptors must
+	indicate the tag library schema by using the Taglibrary
+	namespace:
+
+	http://java.sun.com/xml/ns/j2ee
+
+	and by indicating the version of the schema by
+	using the version element as shown below:
+
+	    <taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+	      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	      xsi:schemaLocation="..."
+	      version="2.0">
+	      ...
+	    </taglib>
+
+	The instance documents may indicate the published
+	version of the schema using xsi:schemaLocation attribute
+	for J2EE namespace with the following location:
+
+	http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd
+
+	]]>
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:include schemaLocation="j2ee_1_4.xsd"/>
+
+
+<!-- **************************************************** -->
+
+
+  <xsd:element name="taglib" type="j2ee:tldTaglibType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The taglib tag is the document root.
+	The definition of taglib is provided
+	by the tldTaglibType.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:unique name="tag-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The taglib element contains, among other things, tag and
+	  tag-file elements.
+	  The name subelements of these elements must each be unique.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:tag|j2ee:tag-file"/>
+      <xsd:field    xpath="j2ee:name"/>
+    </xsd:unique>
+
+    <xsd:unique name="function-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The taglib element contains function elements.
+	  The name subelements of these elements must each be unique.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:function"/>
+      <xsd:field    xpath="j2ee:name"/>
+    </xsd:unique>
+
+  </xsd:element>
+
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="body-contentType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+        Specifies the type of body that is valid for a tag.
+	This value is used by the JSP container to validate
+	that a tag invocation has the correct body syntax and
+	by page composition tools to assist the page author
+	in providing a valid tag body.
+
+	There are currently four values specified:
+
+	tagdependent    The body of the tag is interpreted by the tag
+			implementation itself, and is most likely
+			in a different "language", e.g embedded SQL
+			statements.
+
+	JSP             The body of the tag contains nested JSP
+			syntax.
+
+	empty           The body must be empty
+
+	scriptless      The body accepts only template text, EL
+			Expressions, and JSP action elements.  No
+			scripting elements are allowed.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="tagdependent"/>
+	<xsd:enumeration value="JSP"/>
+	<xsd:enumeration value="empty"/>
+	<xsd:enumeration value="scriptless"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="extensibleType" abstract="true">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The extensibleType is an abstract base type that is used to
+	define the type of extension-elements. Instance documents
+	must substitute a known type to define the extension by
+	using xsi:type attribute to define the actual type of
+	extension-elements.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="functionType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The function element is used to provide information on each
+	function in the tag library that is to be exposed to the EL.
+
+	The function element may have several subelements defining:
+
+	description         Optional tag-specific information
+
+	display-name        A short name that is intended to be
+			    displayed by tools
+
+	icon                Optional icon element that can be used
+			    by tools
+
+	name                A unique name for this function
+
+	function-class      Provides the name of the Java class that
+			    implements the function
+
+	function-signature  Provides the signature, as in the Java
+			    Language Specification, of the Java
+			    method that is to be used to implement
+			    the function.
+
+	example             Optional informal description of an
+			    example of a use of this function
+
+	function-extension  Zero or more extensions that provide extra
+			    information about this function, for tool
+			    consumption
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="name"
+		   type="j2ee:tld-canonical-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    A unique name for this function.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="function-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Provides the fully-qualified class name of the Java
+	    class containing the static method that implements
+	    the function.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="function-signature"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Provides the signature, of the static Java method that is
+	    to be used to implement the function.  The syntax of the
+	    function-signature element is as follows:
+
+		FunctionSignature ::= ReturnType S MethodName S?
+				      '(' S? Parameters? S? ')'
+
+                ReturnType        ::= Type
+
+		MethodName        ::= Identifier
+
+		Parameters        ::=   Parameter
+				      | ( Parameter S? ',' S? Parameters )
+
+                Parameter         ::= Type
+
+		Where:
+
+ 		    * Type is a basic type or a fully qualified
+		      Java class name (including package name),
+		      as per the 'Type' production in the Java
+		      Language Specification, Second Edition,
+		      Chapter 18.
+
+                    * Identifier is a Java identifier, as per
+		      the 'Identifier' production in the Java
+		      Language Specification, Second
+		      Edition, Chapter 18.
+
+	    Example:
+
+	    java.lang.String nickName( java.lang.String, int )
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="example"
+		   type="j2ee:xsdStringType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The example element contains an informal description
+	    of an example of the use of this function.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="function-extension"
+		   type="j2ee:tld-extensionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Function extensions are for tool use only and must not affect
+	    the behavior of a container.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tagFileType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	Defines an action in this tag library that is implemented
+	as a .tag file.
+
+	The tag-file element has two required subelements:
+
+	description       Optional tag-specific information
+
+	display-name      A short name that is intended to be
+			  displayed by tools
+
+	icon              Optional icon element that can be used
+			  by tools
+
+	name              The unique action name
+
+	path              Where to find the .tag file implementing this
+			  action, relative to the root of the web
+			  application or the root of the JAR file for a
+			  tag library packaged in a JAR.  This must
+			  begin with /WEB-INF/tags if the .tag file
+			  resides in the WAR, or /META-INF/tags if the
+			  .tag file resides in a JAR.
+
+	example           Optional informal description of an
+			  example of a use of this tag
+
+	tag-extension     Zero or more extensions that provide extra
+			  information about this tag, for tool
+			  consumption
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="name"
+		   type="j2ee:tld-canonical-nameType"/>
+      <xsd:element name="path"
+		   type="j2ee:pathType"/>
+      <xsd:element name="example"
+		   type="j2ee:xsdStringType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The example element contains an informal description
+	    of an example of the use of a tag.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="tag-extension"
+		   type="j2ee:tld-extensionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Tag extensions are for tool use only and must not affect
+	    the behavior of a container.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tagType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The tag defines a unique tag in this tag library.  It has one
+	attribute, id.
+
+	The tag element may have several subelements defining:
+
+	description       Optional tag-specific information
+
+	display-name      A short name that is intended to be
+			  displayed by tools
+
+	icon              Optional icon element that can be used
+			  by tools
+
+	name              The unique action name
+
+	tag-class         The tag handler class implementing
+			  javax.servlet.jsp.tagext.JspTag
+
+	tei-class         An optional subclass of
+			  javax.servlet.jsp.tagext.TagExtraInfo
+
+	body-content      The body content type
+
+	variable          Optional scripting variable information
+
+	attribute         All attributes of this action that are
+			  evaluated prior to invocation.
+
+	dynamic-attributes Whether this tag supports additional
+			   attributes with dynamic names.  If
+			   true, the tag-class must implement the
+			   javax.servlet.jsp.tagext.DynamicAttributes
+			   interface.  Defaults to false.
+
+	example           Optional informal description of an
+			  example of a use of this tag
+
+	tag-extension     Zero or more extensions that provide extra
+			  information about this tag, for tool
+			  consumption
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="name"
+		   type="j2ee:tld-canonical-nameType"/>
+      <xsd:element name="tag-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the subclass of javax.serlvet.jsp.tagext.JspTag
+	    that implements the request time semantics for
+	    this tag. (required)
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="tei-class"
+		   type="j2ee:fully-qualified-classType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the subclass of javax.servlet.jsp.tagext.TagExtraInfo
+	    for this tag. (optional)
+
+	    If this is not given, the class is not consulted at
+	    translation time.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="body-content"
+		   type="j2ee:body-contentType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Specifies the format for the body of this tag.
+	    The default in JSP 1.2 was "JSP" but because this
+	    is an invalid setting for simple tag handlers, there
+	    is no longer a default in JSP 2.0.  A reasonable
+	    default for simple tag handlers is "scriptless" if
+	    the tag can have a body.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="variable"
+		   type="j2ee:variableType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="attribute"
+		   type="j2ee:tld-attributeType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="dynamic-attributes"
+		   type="j2ee:generic-booleanType"
+		   minOccurs="0"/>
+      <xsd:element name="example"
+		   type="j2ee:xsdStringType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The example element contains an informal description
+	    of an example of the use of a tag.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="tag-extension"
+		   type="j2ee:tld-extensionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Tag extensions are for tool use only and must not affect
+	    the behavior of a container.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tld-attributeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The attribute element defines an attribute for the nesting
+	tag.  The attributre element may have several subelements
+	defining:
+
+	description     a description of the attribute
+
+	name            the name of the attribute
+
+	required        whether the attribute is required or
+			optional
+
+	rtexprvalue     whether the attribute is a runtime attribute
+
+	type            the type of the attributes
+
+	fragment        whether this attribute is a fragment
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="name"
+		   type="j2ee:java-identifierType"/>
+      <xsd:element name="required"
+		   type="j2ee:generic-booleanType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines if the nesting attribute is required or
+	    optional.
+
+	    If not present then the default is "false", i.e
+	    the attribute is optional.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:choice>
+	<xsd:sequence>
+	  <xsd:element name="rtexprvalue"
+		       type="j2ee:generic-booleanType"
+		       minOccurs="0">
+	    <xsd:annotation>
+	      <xsd:documentation>
+
+		Defines if the nesting attribute can have scriptlet
+		expressions as a value, i.e the value of the
+		attribute may be dynamically calculated at request
+		time, as opposed to a static value determined at
+		translation time.
+
+		If not present then the default is "false", i.e the
+		attribute has a static value
+
+	      </xsd:documentation>
+	    </xsd:annotation>
+
+	  </xsd:element>
+	  <xsd:element name="type"
+		       type="j2ee:fully-qualified-classType"
+		       minOccurs="0">
+	    <xsd:annotation>
+	      <xsd:documentation>
+
+		Defines the Java type of the attributes value.  For
+		static values (those determined at translation time)
+		the type is always java.lang.String.
+
+	      </xsd:documentation>
+	    </xsd:annotation>
+	  </xsd:element>
+	</xsd:sequence>
+	<xsd:element name="fragment"
+		     type="j2ee:generic-booleanType"
+		     minOccurs="0">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      "true" if this attribute is of type
+	      javax.jsp.tagext.JspFragment, representing dynamic
+	      content that can be re-evaluated as many times
+	      as needed by the tag handler.  If omitted or "false",
+	      the default is still type="java.lang.String"
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	</xsd:element>
+      </xsd:choice>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tld-canonical-nameType">
+
+    <xsd:annotation>
+      <xsd:documentation>
+
+	Defines the canonical name of a tag or attribute being
+	defined.
+
+	The name must conform to the lexical rules for an NMTOKEN.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:xsdNMTOKENType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tld-extensionType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The tld-extensionType is used to indicate
+	extensions to a specific TLD element.
+
+	It is used by elements to designate an extension block
+	that is targeted to a specific extension designated by
+	a set of extension elements that are declared by a
+	namespace. The namespace identifies the extension to
+	the tool that processes the extension.
+
+	The type of the extension-element is abstract. Therefore,
+	a concrete type must be specified by the TLD using
+	xsi:type attribute for each extension-element.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="extension-element"
+		   type="j2ee:extensibleType"
+		   maxOccurs="unbounded"/>
+    </xsd:sequence>
+
+    <xsd:attribute name="namespace"
+		   use="required"
+		   type="xsd:anyURI"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tldTaglibType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The taglib tag is the document root, it defines:
+
+	description     a simple string describing the "use" of this
+			taglib, should be user discernable
+
+	display-name    the display-name element contains a
+			short name that is intended to be displayed
+			by tools
+
+	icon            optional icon that can be used by tools
+
+	tlib-version    the version of the tag library implementation
+
+	short-name      a simple default short name that could be
+			used by a JSP authoring tool to create
+			names with a mnemonic value; for example,
+			the it may be used as the prefered prefix
+			value in taglib directives
+
+	uri             a uri uniquely identifying this taglib
+
+	validator       optional TagLibraryValidator information
+
+	listener        optional event listener specification
+
+	tag             tags in this tag library
+
+	tag-file        tag files in this tag library
+
+	function        zero or more EL functions defined in this
+			tag library
+
+	taglib-extension zero or more extensions that provide extra
+			information about this taglib, for tool
+			consumption
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="tlib-version"
+		   type="j2ee:dewey-versionType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Describes this version (number) of the taglibrary.
+	    It is described as a dewey decimal.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+
+      <xsd:element name="short-name"
+		   type="j2ee:tld-canonical-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines a simple default name that could be used by
+	    a JSP authoring tool to create names with a
+	    mnemonicvalue; for example, it may be used as the
+	    preferred prefix value in taglib directives.  Do
+	    not use white space, and do not start with digits
+	    or underscore.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="uri"
+		   type="j2ee:xsdAnyURIType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines a public URI that uniquely identifies this
+	    version of the taglibrary.  Leave it empty if it
+	    does not apply.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="validator"
+		   type="j2ee:validatorType"
+		   minOccurs="0">
+      </xsd:element>
+      <xsd:element name="listener"
+		   type="j2ee:listenerType"
+		   minOccurs="0" maxOccurs="unbounded">
+      </xsd:element>
+      <xsd:element name="tag"
+		   type="j2ee:tagType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="tag-file"
+		   type="j2ee:tagFileType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="function"
+		   type="j2ee:functionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="taglib-extension"
+		   type="j2ee:tld-extensionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Taglib extensions are for tool use only and must not affect
+	    the behavior of a container.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="version"
+		   type="j2ee:dewey-versionType"
+		   fixed="2.0"
+		   use="required">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  Describes the JSP version (number) this taglibrary
+	  requires in order to function (dewey decimal)
+
+	</xsd:documentation>
+      </xsd:annotation>
+
+    </xsd:attribute>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="validatorType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	A validator that can be used to validate
+	the conformance of a JSP page to using this tag library is
+	defined by a validatorType.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="validator-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the TagLibraryValidator class that can be used
+	    to validate the conformance of a JSP page to using this
+	    tag library.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="init-param"
+		   type="j2ee:param-valueType"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The init-param element contains a name/value pair as an
+	    initialization param.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="variable-scopeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type defines scope of the scripting variable.  See
+	TagExtraInfo for details.  The allowed values are,
+	"NESTED", "AT_BEGIN" and "AT_END".
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="NESTED"/>
+	<xsd:enumeration value="AT_BEGIN"/>
+	<xsd:enumeration value="AT_END"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="variableType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The variableType provides information on the scripting
+	variables defined by using this tag.  It is a (translation
+	time) error for a tag that has one or more variable
+	subelements to have a TagExtraInfo class that returns a
+	non-null value from a call to getVariableInfo().
+
+	The subelements of variableType are of the form:
+
+	description              Optional description of this
+				 variable
+
+	name-given               The variable name as a constant
+
+	name-from-attribute      The name of an attribute whose
+				 (translation time) value will
+				 give the name of the
+				 variable.  One of name-given or
+				 name-from-attribute is required.
+
+	variable-class           Name of the class of the variable.
+				 java.lang.String is default.
+
+	declare                  Whether the variable is declared
+				 or not.  True is the default.
+
+	scope                    The scope of the scripting varaible
+				 defined.  NESTED is default.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:choice>
+	<xsd:element name="name-given"
+		     type="j2ee:java-identifierType">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      The name for the scripting variable.
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	</xsd:element>
+
+	<xsd:element name="name-from-attribute"
+		     type="j2ee:java-identifierType">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      The name of an attribute whose
+	      (translation-time) value will give the name of
+	      the variable.
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	</xsd:element>
+      </xsd:choice>
+      <xsd:element name="variable-class"
+		   type="j2ee:fully-qualified-classType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The optional name of the class for the scripting
+	    variable.  The default is java.lang.String.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+
+      <xsd:element name="declare"
+		   type="j2ee:generic-booleanType"
+		   minOccurs="0">
+
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Whether the scripting variable is to be defined
+	    or not.  See TagExtraInfo for details.  This
+	    element is optional and "true" is the default.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="scope"
+		   type="j2ee:variable-scopeType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The element is optional and "NESTED" is the default.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+</xsd:schema>
+
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/ErrorData.java b/servletapi/jsr152/src/share/javax/servlet/jsp/ErrorData.java
new file mode 100644
index 0000000..e36c073
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/ErrorData.java
@@ -0,0 +1,88 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/ 
+package javax.servlet.jsp;
+
+/**
+ * Contains information about an error, for error pages.
+ * The information contained in this instance is meaningless if not used
+ * in the context of an error page.  To indicate a JSP is an error page,
+ * the page author must set the isErrorPage attribute of the page directive
+ * to "true".
+ *
+ * @see PageContext#getErrorData
+ * @since 2.0
+ */
+
+public final class ErrorData {
+
+    private Throwable throwable;
+    private int statusCode;
+    private String uri;
+    private String servletName;
+
+    /**
+     * Creates a new ErrorData object.
+     *
+     * @param throwable The Throwable that is the cause of the error
+     * @param statusCode The status code of the error
+     * @param uri The request URI
+     * @param servletName The name of the servlet invoked
+     */
+    public ErrorData( Throwable throwable, int statusCode, String uri, 
+	String servletName )
+    {
+	this.throwable = throwable;
+	this.statusCode = statusCode;
+	this.uri = uri;
+	this.servletName = servletName;
+    }
+
+    /**
+     * Returns the Throwable that caused the error.
+     *
+     * @return The Throwable that caused the error
+     */
+    public Throwable getThrowable() {
+	return this.throwable;
+    }
+
+    /**
+     * Returns the status code of the error.
+     *
+     * @return The status code of the error
+     */
+    public int getStatusCode() {
+	return this.statusCode;
+    }
+
+    /**
+     * Returns the request URI.
+     *
+     * @return The request URI
+     */
+    public String getRequestURI() {
+	return this.uri;
+    }
+
+    /**
+     * Returns the name of the servlet invoked.
+     *
+     * @return The name of the servlet invoked
+     */
+    public String getServletName() {
+	return this.servletName;
+    }
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/HttpJspPage.java b/servletapi/jsr152/src/share/javax/servlet/jsp/HttpJspPage.java
new file mode 100644
index 0000000..50cd147
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/HttpJspPage.java
@@ -0,0 +1,57 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+package javax.servlet.jsp;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+import java.io.IOException;
+
+/**
+ * The HttpJspPage interface describes the interaction that a JSP Page
+ * Implementation Class must satisfy when using the HTTP protocol.
+ *
+ * <p>
+ * The behaviour is identical to that of the JspPage, except for the signature
+ * of the _jspService method, which is now expressible in the Java type
+ * system and included explicitly in the interface.
+ * 
+ * @see JspPage
+ */
+
+public interface HttpJspPage extends JspPage {
+
+    /** The _jspService()method corresponds to the body of the JSP page. This
+     * method is defined automatically by the JSP container and should never
+     * be defined by the JSP page author.
+     * <p>
+     * If a superclass is specified using the extends attribute, that
+     * superclass may choose to perform some actions in its service() method
+     * before or after calling the _jspService() method.  See using the extends
+     * attribute in the JSP_Engine chapter of the JSP specification.
+     *
+     * @param request Provides client request information to the JSP.
+     * @param response Assists the JSP in sending a response to the client.
+     * @throws ServletException Thrown if an error occurred during the 
+     *     processing of the JSP and that the container should take 
+     *     appropriate action to clean up the request.
+     * @throws IOException Thrown if an error occurred while writing the
+     *     response for this page.
+     */
+    public void _jspService(HttpServletRequest request,
+                            HttpServletResponse response)
+       throws ServletException, IOException;
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/JspContext.java b/servletapi/jsr152/src/share/javax/servlet/jsp/JspContext.java
new file mode 100644
index 0000000..302ed31
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/JspContext.java
@@ -0,0 +1,270 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/ 
+package javax.servlet.jsp;
+
+import java.util.Enumeration;
+
+import javax.servlet.jsp.el.ExpressionEvaluator;
+import javax.servlet.jsp.el.VariableResolver;
+
+/**
+ * <p>
+ * <code>JspContext</code> serves as the base class for the 
+ * PageContext class and abstracts all information that is not specific
+ * to servlets.  This allows for Simple Tag Extensions to be used
+ * outside of the context of a request/response Servlet.
+ * <p>
+ * The JspContext provides a number of facilities to the 
+ * page/component author and page implementor, including:
+ * <ul>
+ * <li>a single API to manage the various scoped namespaces
+ * <li>a mechanism to obtain the JspWriter for output
+ * <li>a mechanism to expose page directive attributes to the 
+ *     scripting environment
+ * </ul>
+ *
+ * <p><B>Methods Intended for Container Generated Code</B>
+ * <p>
+ * The following methods enable the <B>management of nested</B> JspWriter 
+ * streams to implement Tag Extensions: <code>pushBody()</code> and
+ * <code>popBody()</code>
+ *
+ * <p><B>Methods Intended for JSP authors</B>
+ * <p>
+ * Some methods provide <B>uniform access</B> to the diverse objects
+ * representing scopes.
+ * The implementation must use the underlying machinery
+ * corresponding to that scope, so information can be passed back and
+ * forth between the underlying environment (e.g. Servlets) and JSP pages.
+ * The methods are:
+ * <code>setAttribute()</code>,  <code>getAttribute()</code>,
+ * <code>findAttribute()</code>,  <code>removeAttribute()</code>,
+ * <code>getAttributesScope()</code> and 
+ * <code>getAttributeNamesInScope()</code>.
+ * 
+ * <p>
+ * The following methods provide <B>convenient access</B> to implicit objects:
+ * <code>getOut()</code>
+ *
+ * <p>
+ * The following methods provide <B>programmatic access</b> to the 
+ * Expression Language evaluator:
+ * <code>getExpressionEvaluator()</code>, <code>getVariableResolver()</code>
+ *
+ * @since 2.0
+ */
+
+public abstract class JspContext {
+
+    /**
+     * Sole constructor. (For invocation by subclass constructors, 
+     * typically implicit.)
+     */
+    public JspContext() {
+    }
+    
+    /** 
+     * Register the name and value specified with page scope semantics.
+     * If the value passed in is <code>null</code>, this has the same 
+     * effect as calling 
+     * <code>removeAttribute( name, PageContext.PAGE_SCOPE )</code>.
+     *
+     * @param name the name of the attribute to set
+     * @param value the value to associate with the name, or null if the
+     *     attribute is to be removed from the page scope.
+     * @throws NullPointerException if the name is null
+     */
+
+    abstract public void setAttribute(String name, Object value);
+
+    /**
+     * Register the name and value specified with appropriate 
+     * scope semantics.  If the value passed in is <code>null</code>, 
+     * this has the same effect as calling
+     * <code>removeAttribute( name, scope )</code>.
+     * 
+     * @param name the name of the attribute to set
+     * @param value the object to associate with the name, or null if
+     *     the attribute is to be removed from the specified scope.
+     * @param scope the scope with which to associate the name/object
+     * 
+     * @throws NullPointerException if the name is null
+     * @throws IllegalArgumentException if the scope is invalid
+     * @throws IllegalStateException if the scope is 
+     *     PageContext.SESSION_SCOPE but the page that was requested
+     *     does not participate in a session or the session has been
+     *     invalidated.
+     */
+
+    abstract public void setAttribute(String name, Object value, int scope);
+
+    /**
+     * Returns the object associated with the name in the page scope or null
+     * if not found.
+     *
+     * @param name the name of the attribute to get
+     * @return the object associated with the name in the page scope 
+     *     or null if not found.
+     * 
+     * @throws NullPointerException if the name is null
+     */
+
+    abstract public Object getAttribute(String name);
+
+    /**
+     * Return the object associated with the name in the specified
+     * scope or null if not found.
+     *
+     * @param name the name of the attribute to set
+     * @param scope the scope with which to associate the name/object
+     * @return the object associated with the name in the specified
+     *     scope or null if not found.
+     * 
+     * @throws NullPointerException if the name is null
+     * @throws IllegalArgumentException if the scope is invalid 
+     * @throws IllegalStateException if the scope is 
+     *     PageContext.SESSION_SCOPE but the page that was requested
+     *     does not participate in a session or the session has been
+     *     invalidated.
+     */
+
+    abstract public Object getAttribute(String name, int scope);
+
+    /**
+     * Searches for the named attribute in page, request, session (if valid),
+     * and application scope(s) in order and returns the value associated or
+     * null.
+     *
+     * @param name the name of the attribute to search for
+     * @return the value associated or null
+     * @throws NullPointerException if the name is null
+     */
+
+    abstract public Object findAttribute(String name);
+
+    /**
+     * Remove the object reference associated with the given name
+     * from all scopes.  Does nothing if there is no such object.
+     *
+     * @param name The name of the object to remove.
+     * @throws NullPointerException if the name is null
+     */
+
+    abstract public void removeAttribute(String name);
+
+    /**
+     * Remove the object reference associated with the specified name
+     * in the given scope.  Does nothing if there is no such object.
+     *
+     * @param name The name of the object to remove.
+     * @param scope The scope where to look.
+     * @throws IllegalArgumentException if the scope is invalid
+     * @throws IllegalStateException if the scope is 
+     *     PageContext.SESSION_SCOPE but the page that was requested
+     *     does not participate in a session or the session has been
+     *     invalidated.
+     * @throws NullPointerException if the name is null
+     */
+
+    abstract public void removeAttribute(String name, int scope);
+
+    /**
+     * Get the scope where a given attribute is defined.
+     *
+     * @param name the name of the attribute to return the scope for
+     * @return the scope of the object associated with the name specified or 0
+     * @throws NullPointerException if the name is null
+     */
+
+    abstract public int getAttributesScope(String name);
+
+    /**
+     * Enumerate all the attributes in a given scope.
+     *
+     * @param scope the scope to enumerate all the attributes for
+     * @return an enumeration of names (java.lang.String) of all the 
+     *     attributes the specified scope
+     * @throws IllegalArgumentException if the scope is invalid
+     * @throws IllegalStateException if the scope is 
+     *     PageContext.SESSION_SCOPE but the page that was requested
+     *     does not participate in a session or the session has been
+     *     invalidated.
+     */
+
+    abstract public Enumeration getAttributeNamesInScope(int scope);
+
+    /**
+     * The current value of the out object (a JspWriter).
+     *
+     * @return the current JspWriter stream being used for client response
+     */
+    abstract public JspWriter getOut();
+    
+    /**
+     * Provides programmatic access to the ExpressionEvaluator.
+     * The JSP Container must return a valid instance of an 
+     * ExpressionEvaluator that can parse EL expressions.
+     *
+     * @return A valid instance of an ExpressionEvaluator.
+     * @since 2.0
+     */
+    public abstract ExpressionEvaluator getExpressionEvaluator();
+    
+    /**
+     * Returns an instance of a VariableResolver that provides access to the
+     * implicit objects specified in the JSP specification using this JspContext
+     * as the context object.
+     *
+     * @return A valid instance of a VariableResolver.
+     * @since 2.0
+     */
+    public abstract VariableResolver getVariableResolver();
+    
+    /**
+     * Return a new JspWriter object that sends output to the
+     * provided Writer.  Saves the current "out" JspWriter,
+     * and updates the value of the "out" attribute in the
+     * page scope attribute namespace of the JspContext.
+     * <p>The returned JspWriter must implement all methods and
+     * behave as though it were unbuffered.  More specifically:
+     * <ul>
+     *   <li>clear() must throw an IOException</li>
+     *   <li>clearBuffer() does nothing</li>
+     *   <li>getBufferSize() always returns 0</li>
+     *   <li>getRemaining() always returns 0</li>
+     * </ul>
+     * </p>
+     *
+     * @param writer The Writer for the returned JspWriter to send
+     *     output to.
+     * @return a new JspWriter that writes to the given Writer.
+     * @since 2.0
+     */
+    public JspWriter pushBody( java.io.Writer writer ) {
+        return null; // XXX to implement
+    }
+    
+    /**
+     * Return the previous JspWriter "out" saved by the matching
+     * pushBody(), and update the value of the "out" attribute in
+     * the page scope attribute namespace of the JspContext.
+     *
+     * @return the saved JspWriter.
+     */
+    public JspWriter popBody() {
+        return null; // XXX to implement
+    }
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/JspEngineInfo.java b/servletapi/jsr152/src/share/javax/servlet/jsp/JspEngineInfo.java
new file mode 100644
index 0000000..736f00e
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/JspEngineInfo.java
@@ -0,0 +1,47 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/ 
+package javax.servlet.jsp;
+
+/**
+ * The JspEngineInfo is an abstract class that provides information on the
+ * current JSP engine.
+ */
+
+public abstract class JspEngineInfo {
+
+    /**
+     * Sole constructor. (For invocation by subclass constructors, 
+     * typically implicit.)
+     */
+    public JspEngineInfo() {
+    }
+    
+    /**
+     * Return the version number of the JSP specification that is supported by
+     * this JSP engine.
+     * <p>
+     * Specification version numbers that consists of positive decimal integers
+     * separated by periods ".", for example, "2.0" or "1.2.3.4.5.6.7".
+     * This allows an extensible number to be used to
+     * represent major, minor, micro, etc versions.
+     * The version number must begin with a number.
+     * </p>
+     *
+     * @return the specification version, null is returned if it is not known
+     */
+
+    public abstract String getSpecificationVersion();
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/JspException.java b/servletapi/jsr152/src/share/javax/servlet/jsp/JspException.java
new file mode 100644
index 0000000..e2eb944
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/JspException.java
@@ -0,0 +1,112 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp;
+
+/**
+ * A generic exception known to the JSP engine; uncaught
+ * JspExceptions will result in an invocation of the errorpage
+ * machinery.
+ */
+
+public class JspException extends Exception {
+
+    private Throwable rootCause;
+
+
+    /**
+     * Construct a JspException.
+     */
+    public JspException() {
+    }
+
+
+    /**
+     * Constructs a new JSP exception with the
+     * specified message. The message can be written 
+     * to the server log and/or displayed for the user. 
+     *
+     * @param msg 		a <code>String</code> 
+     *				specifying the text of 
+     *				the exception message
+     *
+     */
+    public JspException(String msg) {
+	super(msg);
+    }
+
+
+    /**
+     * Constructs a new JSP exception when the JSP 
+     * needs to throw an exception and include a message 
+     * about the "root cause" exception that interfered with its 
+     * normal operation, including a description message.
+     *
+     *
+     * @param message 		a <code>String</code> containing 
+     *				the text of the exception message
+     *
+     * @param rootCause		the <code>Throwable</code> exception 
+     *				that interfered with the servlet's
+     *				normal operation, making this servlet
+     *				exception necessary
+     *
+     */
+    
+    public JspException(String message, Throwable rootCause) {
+	super(message);
+	this.rootCause = rootCause;
+    }
+
+
+    /**
+     * Constructs a new JSP exception when the JSP 
+     * needs to throw an exception and include a message
+     * about the "root cause" exception that interfered with its
+     * normal operation.  The exception's message is based on the localized
+     * message of the underlying exception.
+     *
+     * <p>This method calls the <code>getLocalizedMessage</code> method
+     * on the <code>Throwable</code> exception to get a localized exception
+     * message. When subclassing <code>JspException</code>, 
+     * this method can be overridden to create an exception message 
+     * designed for a specific locale.
+     *
+     * @param rootCause 	the <code>Throwable</code> exception
+     * 				that interfered with the JSP's
+     *				normal operation, making the JSP exception
+     *				necessary
+     *
+     */
+
+    public JspException(Throwable rootCause) {
+	super(rootCause.getLocalizedMessage());
+	this.rootCause = rootCause;
+    }
+
+    
+    /**
+     * Returns the exception that caused this JSP exception.
+     *
+     *
+     * @return			the <code>Throwable</code> 
+     *				that caused this JSP exception
+     *
+     */
+    
+    public Throwable getRootCause() {
+	return rootCause;
+    }
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/JspFactory.java b/servletapi/jsr152/src/share/javax/servlet/jsp/JspFactory.java
new file mode 100644
index 0000000..ac53c41
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/JspFactory.java
@@ -0,0 +1,142 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.jsp.PageContext;
+
+/**
+ * <p>
+ * The JspFactory is an abstract class that defines a number of factory
+ * methods available to a JSP page at runtime for the purposes of creating
+ * instances of various interfaces and classes used to support the JSP 
+ * implementation.
+ * <p>
+ * A conformant JSP Engine implementation will, during it's initialization
+ * instantiate an implementation dependent subclass of this class, and make 
+ * it globally available for use by JSP implementation classes by registering
+ * the instance created with this class via the
+ * static <code> setDefaultFactory() </code> method.
+ * <p>
+ * The PageContext and the JspEngineInfo classes are the only implementation-dependent
+ * classes that can be created from the factory.
+ * <p>
+ * JspFactory objects should not be used by JSP page authors.
+ */
+
+public abstract class JspFactory {
+
+    private static JspFactory deflt = null;
+    
+    /**
+     * Sole constructor. (For invocation by subclass constructors, 
+     * typically implicit.)
+     */
+    public JspFactory() {
+    }
+
+    /**
+     * <p>
+     * set the default factory for this implementation. It is illegal for
+     * any principal other than the JSP Engine runtime to call this method.
+     * </p>
+     *
+     * @param deflt	The default factory implementation
+     */
+
+    public static synchronized void setDefaultFactory(JspFactory deflt) {
+	JspFactory.deflt = deflt;
+    }
+
+    /**
+     * Returns the default factory for this implementation.
+     *
+     * @return the default factory for this implementation
+     */
+
+    public static synchronized JspFactory getDefaultFactory() {
+	return deflt;
+    }
+
+    /**
+     * <p>
+     * obtains an instance of an implementation dependent 
+     * javax.servlet.jsp.PageContext abstract class for the calling Servlet
+     * and currently pending request and response.
+     * </p>
+     *
+     * <p>
+     * This method is typically called early in the processing of the 
+     * _jspService() method of a JSP implementation class in order to 
+     * obtain a PageContext object for the request being processed.
+     * </p>
+     * <p>
+     * Invoking this method shall result in the PageContext.initialize()
+     * method being invoked. The PageContext returned is properly initialized.
+     * </p>
+     * <p>
+     * All PageContext objects obtained via this method shall be released
+     * by invoking releasePageContext().
+     * </p>
+     *
+     * @param servlet   the requesting servlet
+     * @param request	the current request pending on the servlet
+     * @param response	the current response pending on the servlet
+     * @param errorPageURL the URL of the error page for the requesting JSP, or null
+     * @param needsSession true if the JSP participates in a session
+     * @param buffer	size of buffer in bytes, PageContext.NO_BUFFER if no buffer,
+     *			PageContext.DEFAULT_BUFFER if implementation default.
+     * @param autoflush	should the buffer autoflush to the output stream on buffer
+     *			overflow, or throw an IOException?
+     *
+     * @return the page context
+     *
+     * @see javax.servlet.jsp.PageContext
+     */
+
+    public abstract PageContext getPageContext(Servlet	       servlet,
+				    	       ServletRequest  request,
+				    	       ServletResponse response,
+				    	       String	       errorPageURL,
+				    	       boolean         needsSession,
+				    	       int             buffer,
+				    	       boolean         autoflush);
+
+    /**
+     * <p>
+     * called to release a previously allocated PageContext object.
+     * Results in PageContext.release() being invoked.
+     * This method should be invoked prior to returning from the _jspService() method of a JSP implementation
+     * class.
+     * </p>
+     *
+     * @param pc A PageContext previously obtained by getPageContext()
+     */
+
+    public abstract void releasePageContext(PageContext pc);
+
+    /**
+     * <p>
+     * called to get implementation-specific information on the current JSP engine.
+     * </p>
+     *
+     * @return a JspEngineInfo object describing the current JSP engine
+     */
+    
+    public abstract JspEngineInfo getEngineInfo();
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/JspPage.java b/servletapi/jsr152/src/share/javax/servlet/jsp/JspPage.java
new file mode 100644
index 0000000..e93074d
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/JspPage.java
@@ -0,0 +1,89 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/ 
+package javax.servlet.jsp;
+
+import javax.servlet.*;
+
+/**
+ * The JspPage interface describes the generic interaction that a JSP Page
+ * Implementation class must satisfy; pages that use the HTTP protocol
+ * are described by the HttpJspPage interface.
+ *
+ * <p><B>Two plus One Methods</B>
+ * <p>
+ * The interface defines a protocol with 3 methods; only two of
+ * them: jspInit() and jspDestroy() are part of this interface as
+ * the signature of the third method: _jspService() depends on
+ * the specific protocol used and cannot be expressed in a generic
+ * way in Java.
+ * <p>
+ * A class implementing this interface is responsible for invoking
+ * the above methods at the appropriate time based on the
+ * corresponding Servlet-based method invocations.
+ * <p>
+ * The jspInit() and jspDestroy() methods can be defined by a JSP
+ * author, but the _jspService() method is defined automatically
+ * by the JSP processor based on the contents of the JSP page.
+ *
+ * <p><B>_jspService()</B>
+ * <p>
+ * The _jspService()method corresponds to the body of the JSP page. This
+ * method is defined automatically by the JSP container and should never
+ * be defined by the JSP page author.
+ * <p>
+ * If a superclass is specified using the extends attribute, that
+ * superclass may choose to perform some actions in its service() method
+ * before or after calling the _jspService() method.  See using the extends
+ * attribute in the JSP_Engine chapter of the JSP specification.
+ * <p>
+ * The specific signature depends on the protocol supported by the JSP page.
+ *
+ * <pre>
+ * public void _jspService(<em>ServletRequestSubtype</em> request,
+ *                             <em>ServletResponseSubtype</em> response)
+ *        throws ServletException, IOException;
+ * </pre>
+ */
+
+
+public interface JspPage extends Servlet {
+
+    /**
+     * The jspInit() method is invoked when the JSP page is initialized. It
+     * is the responsibility of the JSP implementation (and of the class
+     * mentioned by the extends attribute, if present) that at this point
+     * invocations to the getServletConfig() method will return the desired
+     * value.
+     *
+     * A JSP page can override this method by including a definition for it
+     * in a declaration element.
+     *
+     * A JSP page should redefine the init() method from Servlet.
+     */
+    public void jspInit();
+
+    /**
+     * The jspDestroy() method is invoked when the JSP page is about to be
+     * destroyed.
+     * 
+     * A JSP page can override this method by including a definition for it
+     * in a declaration element.
+     *
+     * A JSP page should redefine the destroy() method from Servlet.
+     */
+    public void jspDestroy();
+
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/JspTagException.java b/servletapi/jsr152/src/share/javax/servlet/jsp/JspTagException.java
new file mode 100644
index 0000000..35d8769
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/JspTagException.java
@@ -0,0 +1,92 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp;
+
+/**
+ * Exception to be used by a Tag Handler to indicate some unrecoverable
+ * error.
+ * This error is to be caught by the top level of the JSP page and will result
+ * in an error page.
+ */
+
+public class JspTagException extends JspException {
+    /**
+     * Constructs a new JspTagException with the specified message.
+     * The message can be written to the server log and/or displayed
+     * for the user.
+     * 
+     * @param msg a <code>String</code> specifying the text of 
+     *     the exception message
+     */
+    public JspTagException(String msg) {
+	super( msg );
+    }
+
+    /**
+     * Constructs a new JspTagException with no message.
+     */
+    public JspTagException() {
+	super();
+    }
+
+    /**
+     * Constructs a new JspTagException when the JSP Tag
+     * needs to throw an exception and include a message 
+     * about the "root cause" exception that interfered with its 
+     * normal operation, including a description message.
+     *
+     *
+     * @param message 		a <code>String</code> containing 
+     *				the text of the exception message
+     *
+     * @param rootCause		the <code>Throwable</code> exception 
+     *				that interfered with the JSP Tag's
+     *				normal operation, making this JSP Tag
+     *				exception necessary
+     *
+     * @since 2.0
+     */
+    public JspTagException(String message, Throwable rootCause) {
+	super( message, rootCause );
+    }
+
+
+    /**
+     * Constructs a new JSP Tag exception when the JSP Tag
+     * needs to throw an exception and include a message
+     * about the "root cause" exception that interfered with its
+     * normal operation.  The exception's message is based on the localized
+     * message of the underlying exception.
+     *
+     * <p>This method calls the <code>getLocalizedMessage</code> method
+     * on the <code>Throwable</code> exception to get a localized exception
+     * message. When subclassing <code>JspTagException</code>, 
+     * this method can be overridden to create an exception message 
+     * designed for a specific locale.
+     *
+     * @param rootCause 	the <code>Throwable</code> exception
+     * 				that interfered with the JSP Tag's
+     *				normal operation, making the JSP Tag 
+     *                          exception necessary
+     *
+     * @since 2.0
+     */
+
+    public JspTagException(Throwable rootCause) {
+	super( rootCause );
+    }
+
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/JspWriter.java b/servletapi/jsr152/src/share/javax/servlet/jsp/JspWriter.java
new file mode 100644
index 0000000..b11050d
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/JspWriter.java
@@ -0,0 +1,442 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+package javax.servlet.jsp;
+
+import java.io.IOException;
+
+/**
+ * <p>
+ * The actions and template data in a JSP page is written using the
+ * JspWriter object that is referenced by the implicit variable out which
+ * is initialized automatically using methods in the PageContext object.
+ *<p>
+ * This abstract class emulates some of the functionality found in the
+ * java.io.BufferedWriter and java.io.PrintWriter classes,
+ * however it differs in that it throws java.io.IOException from the print
+ * methods while PrintWriter does not.
+ * <p><B>Buffering</B>
+ * <p>
+ * The initial JspWriter object is associated with the PrintWriter object
+ * of the ServletResponse in a way that depends on whether the page is or
+ * is not buffered. If the page is not buffered, output written to this
+ * JspWriter object will be written through to the PrintWriter directly,
+ * which will be created if necessary by invoking the getWriter() method
+ * on the response object. But if the page is buffered, the PrintWriter
+ * object will not be created until the buffer is flushed and
+ * operations like setContentType() are legal. Since this flexibility
+ * simplifies programming substantially, buffering is the default for JSP
+ * pages.
+ * <p>
+ * Buffering raises the issue of what to do when the buffer is
+ * exceeded. Two approaches can be taken:
+ * <ul>
+ * <li>
+ * Exceeding the buffer is not a fatal error; when the buffer is
+ * exceeded, just flush the output.
+ * <li>
+ * Exceeding the buffer is a fatal error; when the buffer is exceeded,
+ * raise an exception.
+ * </ul>
+ * <p>
+ * Both approaches are valid, and thus both are supported in the JSP
+ * technology. The behavior of a page is controlled by the autoFlush
+ * attribute, which defaults to true. In general, JSP pages that need to
+ * be sure that correct and complete data has been sent to their client
+ * may want to set autoFlush to false, with a typical case being that
+ * where the client is an application itself. On the other hand, JSP
+ * pages that send data that is meaningful even when partially
+ * constructed may want to set autoFlush to true; such as when the
+ * data is sent for immediate display through a browser. Each application
+ * will need to consider their specific needs.
+ * <p>
+ * An alternative considered was to make the buffer size unbounded; but,
+ * this had the disadvantage that runaway computations would consume an
+ * unbounded amount of resources.
+ * <p>
+ * The "out" implicit variable of a JSP implementation class is of this type.
+ * If the page directive selects autoflush="true" then all the I/O operations
+ * on this class shall automatically flush the contents of the buffer if an
+ * overflow condition would result if the current operation were performed
+ * without a flush. If autoflush="false" then all the I/O operations on this
+ * class shall throw an IOException if performing the current operation would
+ * result in a buffer overflow condition.
+ *
+ * @see java.io.Writer
+ * @see java.io.BufferedWriter
+ * @see java.io.PrintWriter
+ */
+
+abstract public class JspWriter extends java.io.Writer {
+
+    /**
+     * Constant indicating that the Writer is not buffering output.
+     */
+
+    public static final int	NO_BUFFER = 0;
+
+    /**
+     * Constant indicating that the Writer is buffered and is using the
+     * implementation default buffer size.
+     */
+
+    public static final int	DEFAULT_BUFFER = -1;
+
+    /**
+     * Constant indicating that the Writer is buffered and is unbounded; this
+     * is used in BodyContent.
+     */
+
+    public static final int	UNBOUNDED_BUFFER = -2;
+
+    /**
+     * Protected constructor.
+     *
+     * @param bufferSize the size of the buffer to be used by the JspWriter
+     * @param autoFlush whether the JspWriter should be autoflushing
+     */
+
+    protected JspWriter(int bufferSize, boolean autoFlush) {
+	this.bufferSize = bufferSize;
+	this.autoFlush  = autoFlush;
+    }
+
+    /**
+     * Write a line separator.  The line separator string is defined by the
+     * system property <tt>line.separator</tt>, and is not necessarily a single
+     * newline ('\n') character.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+
+    abstract public void newLine() throws IOException;
+
+    /**
+     * Print a boolean value.  The string produced by <code>{@link
+     * java.lang.String#valueOf(boolean)}</code> is written to the
+     * JspWriter's buffer or, if no buffer is used, directly to the 
+     * underlying writer.
+     *
+     * @param      b   The <code>boolean</code> to be printed
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void print(boolean b) throws IOException;
+
+    /**
+     * Print a character.  The character is written to the
+     * JspWriter's buffer or, if no buffer is used, directly to the
+     * underlying writer.
+     *
+     * @param      c   The <code>char</code> to be printed
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void print(char c) throws IOException;
+
+    /**
+     * Print an integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(int)}</code> is written to the
+     * JspWriter's buffer or, if no buffer is used, directly to the
+     * underlying writer.
+     *
+     * @param      i   The <code>int</code> to be printed
+     * @see        java.lang.Integer#toString(int)
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void print(int i) throws IOException;
+
+    /**
+     * Print a long integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(long)}</code> is written to the
+     * JspWriter's buffer or, if no buffer is used, directly to the
+     * underlying writer.
+     *
+     * @param      l   The <code>long</code> to be printed
+     * @see        java.lang.Long#toString(long)
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void print(long l) throws IOException;
+
+    /**
+     * Print a floating-point number.  The string produced by <code>{@link
+     * java.lang.String#valueOf(float)}</code> is written to the
+     * JspWriter's buffer or, if no buffer is used, directly to the
+     * underlying writer.
+     *
+     * @param      f   The <code>float</code> to be printed
+     * @see        java.lang.Float#toString(float)
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void print(float f) throws IOException;
+
+    /**
+     * Print a double-precision floating-point number.  The string produced by
+     * <code>{@link java.lang.String#valueOf(double)}</code> is written to
+     * the JspWriter's buffer or, if no buffer is used, directly to the
+     * underlying writer.
+     *
+     * @param      d   The <code>double</code> to be printed
+     * @see        java.lang.Double#toString(double)
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void print(double d) throws IOException;
+
+    /**
+     * Print an array of characters.  The characters are written to the
+     * JspWriter's buffer or, if no buffer is used, directly to the
+     * underlying writer.
+     *
+     * @param      s   The array of chars to be printed
+     *
+     * @throws  NullPointerException  If <code>s</code> is <code>null</code>
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void print(char s[]) throws IOException;
+
+    /**
+     * Print a string.  If the argument is <code>null</code> then the string
+     * <code>"null"</code> is printed.  Otherwise, the string's characters are
+     * written to the JspWriter's buffer or, if no buffer is used, directly
+     * to the underlying writer.
+     *
+     * @param      s   The <code>String</code> to be printed
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void print(String s) throws IOException;
+
+    /**
+     * Print an object.  The string produced by the <code>{@link
+     * java.lang.String#valueOf(Object)}</code> method is written to the
+     * JspWriter's buffer or, if no buffer is used, directly to the
+     * underlying writer.
+     *
+     * @param      obj   The <code>Object</code> to be printed
+     * @see        java.lang.Object#toString()
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void print(Object obj) throws IOException;
+
+    /**
+     * Terminate the current line by writing the line separator string.  The
+     * line separator string is defined by the system property
+     * <code>line.separator</code>, and is not necessarily a single newline
+     * character (<code>'\n'</code>).
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println() throws IOException;
+
+    /**
+     * Print a boolean value and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(boolean)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @param      x the boolean to write
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println(boolean x) throws IOException;
+
+    /**
+     * Print a character and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
+     * #println()}</code>.
+     *
+     * @param      x the char to write
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println(char x) throws IOException;
+
+    /**
+     * Print an integer and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
+     * #println()}</code>.
+     *
+     * @param      x the int to write
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println(int x) throws IOException;
+
+    /**
+     * Print a long integer and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(long)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @param      x the long to write
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println(long x) throws IOException;
+
+    /**
+     * Print a floating-point number and then terminate the line.  This method
+     * behaves as though it invokes <code>{@link #print(float)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @param      x the float to write
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println(float x) throws IOException;
+
+    /**
+     * Print a double-precision floating-point number and then terminate the
+     * line.  This method behaves as though it invokes <code>{@link
+     * #print(double)}</code> and then <code>{@link #println()}</code>.
+     *
+     * @param      x the double to write
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println(double x) throws IOException;
+
+    /**
+     * Print an array of characters and then terminate the line.  This method
+     * behaves as though it invokes <code>print(char[])</code> and then
+     * <code>println()</code>.
+     *
+     * @param      x the char[] to write
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println(char x[]) throws IOException;
+
+    /**
+     * Print a String and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(String)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @param      x the String to write
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println(String x) throws IOException;
+
+    /**
+     * Print an Object and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(Object)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @param      x the Object to write
+     * @throws	   java.io.IOException If an error occured while writing
+     */
+
+    abstract public void println(Object x) throws IOException;
+
+
+    /**
+     * Clear the contents of the buffer. If the buffer has been already
+     * been flushed then the clear operation shall throw an IOException
+     * to signal the fact that some data has already been irrevocably 
+     * written to the client response stream.
+     *
+     * @throws IOException		If an I/O error occurs
+     */
+
+    abstract public void clear() throws IOException;
+
+    /**
+     * Clears the current contents of the buffer. Unlike clear(), this
+     * method will not throw an IOException if the buffer has already been
+     * flushed. It merely clears the current content of the buffer and
+     * returns.
+     *
+     * @throws IOException		If an I/O error occurs
+     */
+
+    abstract public void clearBuffer() throws IOException;
+
+    /**
+     * Flush the stream.  If the stream has saved any characters from the
+     * various write() methods in a buffer, write them immediately to their
+     * intended destination.  Then, if that destination is another character or
+     * byte stream, flush it.  Thus one flush() invocation will flush all the
+     * buffers in a chain of Writers and OutputStreams.
+     * <p>
+     * The method may be invoked indirectly if the buffer size is exceeded.
+     * <p>
+     * Once a stream has been closed,
+     * further write() or flush() invocations will cause an IOException to be
+     * thrown.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+
+    abstract public void flush() throws IOException;
+
+    /**
+     * Close the stream, flushing it first.
+     * <p>
+     * This method needs not be invoked explicitly for the initial JspWriter
+     * as the code generated by the JSP container will automatically
+     * include a call to close().
+     * <p>
+     * Closing a previously-closed stream, unlike flush(), has no effect.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+
+    abstract public void close() throws IOException;
+
+    /**
+     * This method returns the size of the buffer used by the JspWriter.
+     *
+     * @return the size of the buffer in bytes, or 0 is unbuffered.
+     */
+
+    public int getBufferSize() { return bufferSize; }
+
+    /**
+     * This method returns the number of unused bytes in the buffer.
+     *
+     * @return the number of bytes unused in the buffer
+     */
+
+    abstract public int getRemaining();
+
+    /**
+     * This method indicates whether the JspWriter is autoFlushing.
+     *
+     * @return if this JspWriter is auto flushing or throwing IOExceptions 
+     *     on buffer overflow conditions
+     */
+
+    public boolean isAutoFlush() { return autoFlush; }
+
+    /*
+     * fields
+     */
+
+    /**
+     * The size of the buffer used by the JspWriter.
+     */
+    protected int     bufferSize;
+    
+    /**
+     * Whether the JspWriter is autoflushing.
+     */
+    protected boolean autoFlush;
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/PageContext.java b/servletapi/jsr152/src/share/javax/servlet/jsp/PageContext.java
new file mode 100644
index 0000000..b190619
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/PageContext.java
@@ -0,0 +1,522 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+package javax.servlet.jsp;
+
+import java.io.IOException;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import javax.servlet.http.HttpSession;
+
+import javax.servlet.jsp.tagext.BodyContent;
+
+/**
+ * <p>
+ * PageContext extends JspContext to provide useful context information for
+ * when JSP technology is used in a Servlet environment.
+ * <p>
+ * A PageContext instance provides access to all the namespaces associated
+ * with a JSP page, provides access to several page attributes, as well as
+ * a layer above the implementation details.  Implicit objects are added
+ * to the pageContext automatically.
+ *
+ * <p> The <code> PageContext </code> class is an abstract class, designed to be
+ * extended to provide implementation dependent implementations thereof, by
+ * conformant JSP engine runtime environments. A PageContext instance is 
+ * obtained by a JSP implementation class by calling the
+ * JspFactory.getPageContext() method, and is released by calling
+ * JspFactory.releasePageContext().
+ *
+ * <p> An example of how PageContext, JspFactory, and other classes can be
+ * used  within a JSP Page Implementation object is given elsewhere.
+ *
+ * <p>
+ * The PageContext provides a number of facilities to the page/component 
+ * author and page implementor, including:
+ * <ul>
+ * <li>a single API to manage the various scoped namespaces
+ * <li>a number of convenience API's to access various public objects
+ * <li>a mechanism to obtain the JspWriter for output
+ * <li>a mechanism to manage session usage by the page
+ * <li>a mechanism to expose page directive attributes to the scripting 
+ *     environment
+ * <li>mechanisms to forward or include the current request to other active 
+ *     components in the application
+ * <li>a mechanism to handle errorpage exception processing
+ * </ul>
+ *
+ * <p><B>Methods Intended for Container Generated Code</B>
+ * <p>Some methods are intended to be used by the code generated by the
+ * container, not by code written by JSP page authors, or JSP tag library 
+ * authors.
+ * <p>The methods supporting <B>lifecycle</B> are <code>initialize()</code>
+ * and <code>release()</code>
+ *
+ * <p>
+ * The following methods enable the <B>management of nested</B> JspWriter 
+ * streams to implement Tag Extensions: <code>pushBody()</code>
+ *
+ * <p><B>Methods Intended for JSP authors</B>
+ * <p>
+ * The following methods provide <B>convenient access</B> to implicit objects:
+ * <code>getException()</code>,  <code>getPage()</code>
+ * <code>getRequest()</code>,  <code>getResponse()</code>,
+ * <code>getSession()</code>,  <code>getServletConfig()</code>
+ * and <code>getServletContext()</code>.
+ *
+ * <p>
+ * The following methods provide support for <B>forwarding, inclusion
+ * and error handling</B>:
+ * <code>forward()</code>,  <code>include()</code>,
+ * and  <code>handlePageException()</code>.
+ */
+
+abstract public class PageContext 
+    extends JspContext
+{
+    
+    /**
+     * Sole constructor. (For invocation by subclass constructors, 
+     * typically implicit.)
+     */
+    public PageContext() {
+    }
+    
+    /**
+     * Page scope: (this is the default) the named reference remains available
+     * in this PageContext until the return from the current Servlet.service()
+     * invocation.
+     */
+
+    public static final int PAGE_SCOPE		= 1;
+
+    /**
+     * Request scope: the named reference remains available from the 
+     * ServletRequest associated with the Servlet until the current request 
+     * is completed.
+     */
+
+    public static final int REQUEST_SCOPE	= 2;
+
+    /**
+     * Session scope (only valid if this page participates in a session):
+     * the named reference remains available from the HttpSession (if any)
+     * associated with the Servlet until the HttpSession is invalidated.
+     */
+
+    public static final int SESSION_SCOPE	= 3;
+
+    /**
+     * Application scope: named reference remains available in the 
+     * ServletContext until it is reclaimed.
+     */
+
+    public static final int APPLICATION_SCOPE	= 4;
+
+    /**
+     * Name used to store the Servlet in this PageContext's nametables.
+     */
+
+    public static final String PAGE = "javax.servlet.jsp.jspPage";
+
+    /**
+     * Name used to store this PageContext in it's own name table.
+     */
+
+    public static final String PAGECONTEXT = "javax.servlet.jsp.jspPageContext";
+
+    /**
+     * Name used to store ServletRequest in PageContext name table.
+     */
+
+    public static final String REQUEST = "javax.servlet.jsp.jspRequest";
+
+    /**
+     * Name used to store ServletResponse in PageContext name table.
+     */
+
+    public static final String RESPONSE = "javax.servlet.jsp.jspResponse";
+
+    /**
+     * Name used to store ServletConfig in PageContext name table.
+     */
+
+    public static final String CONFIG = "javax.servlet.jsp.jspConfig";
+
+    /**
+     * Name used to store HttpSession in PageContext name table.
+     */
+
+    public static final String SESSION = "javax.servlet.jsp.jspSession";
+    /**
+     * Name used to store current JspWriter in PageContext name table.
+     */
+
+    public static final String OUT = "javax.servlet.jsp.jspOut";
+
+    /**
+     * Name used to store ServletContext in PageContext name table.
+     */
+
+    public static final String APPLICATION = "javax.servlet.jsp.jspApplication";
+
+    /**
+     * Name used to store uncaught exception in ServletRequest attribute 
+     * list and PageContext name table.
+     */
+
+    public static final String EXCEPTION = "javax.servlet.jsp.jspException";
+
+    /**
+     * <p>
+     * The initialize method is called to initialize an uninitialized PageContext
+     * so that it may be used by a JSP Implementation class to service an
+     * incoming request and response within it's _jspService() method.
+     *
+     * <p>
+     * This method is typically called from JspFactory.getPageContext() in
+     * order to initialize state.
+     *
+     * <p>
+     * This method is required to create an initial JspWriter, and associate
+     * the "out" name in page scope with this newly created object.
+     *
+     * <p>
+     * This method should not be used by page  or tag library authors.
+     *
+     * @param servlet The Servlet that is associated with this PageContext
+     * @param request The currently pending request for this Servlet
+     * @param response The currently pending response for this Servlet
+     * @param errorPageURL The value of the errorpage attribute from the page 
+     *     directive or null
+     * @param needsSession The value of the session attribute from the 
+     *     page directive
+     * @param bufferSize The value of the buffer attribute from the page 
+     *     directive
+     * @param autoFlush The value of the autoflush attribute from the page 
+     *     directive
+     *
+     * @throws IOException during creation of JspWriter
+     * @throws IllegalStateException if out not correctly initialized
+     * @throws IllegalArgumentException If one of the given parameters
+     *     is invalid
+     */
+ 
+    abstract public void initialize(Servlet servlet, ServletRequest request, 
+        ServletResponse response, String errorPageURL, boolean needsSession, 
+        int bufferSize, boolean autoFlush)  
+        throws IOException, IllegalStateException, IllegalArgumentException;
+
+    /**
+     * <p>
+     * This method shall "reset" the internal state of a PageContext, releasing
+     * all internal references, and preparing the PageContext for potential
+     * reuse by a later invocation of initialize(). This method is typically
+     * called from JspFactory.releasePageContext().
+     *
+     * <p>
+     * Subclasses shall envelope this method.
+     *
+     * <p>
+     * This method should not be used by page  or tag library authors.
+     *
+     */
+
+    abstract public void release();
+
+    /**
+     * The current value of the session object (an HttpSession).
+     *
+     * @return the HttpSession for this PageContext or null
+     */
+
+    abstract public HttpSession getSession();
+
+    /**
+     * The current value of the page object (In a Servlet environment, 
+     * this is an instance of javax.servlet.Servlet).
+     *
+     * @return the Page implementation class instance associated 
+     *     with this PageContext
+     */
+
+    abstract public Object getPage();
+
+
+    /**
+     * The current value of the request object (a ServletRequest).
+     *
+     * @return The ServletRequest for this PageContext
+     */
+
+    abstract public ServletRequest getRequest();
+
+    /**
+     * The current value of the response object (a ServletResponse).
+     *
+     * @return the ServletResponse for this PageContext
+     */
+
+    abstract public ServletResponse getResponse();
+
+    /**
+     * The current value of the exception object (an Exception).
+     *
+     * @return any exception passed to this as an errorpage
+     */
+
+    abstract public Exception getException();
+
+    /**
+     * The ServletConfig instance.
+     *
+     * @return the ServletConfig for this PageContext
+     */
+
+    abstract public ServletConfig getServletConfig();
+
+    /**
+     * The ServletContext instance.
+     * 
+     * @return the ServletContext for this PageContext
+     */
+
+    abstract public ServletContext getServletContext();
+
+    /**
+     * <p>
+     * This method is used to re-direct, or "forward" the current 
+     * ServletRequest and ServletResponse to another active component in 
+     * the application.
+     * </p>
+     * <p>
+     * If the <I> relativeUrlPath </I> begins with a "/" then the URL specified
+     * is calculated relative to the DOCROOT of the <code> ServletContext </code>
+     * for this JSP. If the path does not begin with a "/" then the URL 
+     * specified is calculated relative to the URL of the request that was
+     * mapped to the calling JSP.
+     * </p>
+     * <p>
+     * It is only valid to call this method from a <code> Thread </code>
+     * executing within a <code> _jspService(...) </code> method of a JSP.
+     * </p>
+     * <p>
+     * Once this method has been called successfully, it is illegal for the
+     * calling <code> Thread </code> to attempt to modify the <code>
+     * ServletResponse </code> object.  Any such attempt to do so, shall result
+     * in undefined behavior. Typically, callers immediately return from 
+     * <code> _jspService(...) </code> after calling this method.
+     * </p>
+     *
+     * @param relativeUrlPath specifies the relative URL path to the target 
+     *     resource as described above
+     *
+     * @throws IllegalStateException if <code> ServletResponse </code> is not 
+     *     in a state where a forward can be performed
+     * @throws ServletException if the page that was forwarded to throws
+     *     a ServletException
+     * @throws IOException if an I/O error occurred while forwarding
+     */
+
+    abstract public void forward(String relativeUrlPath) 
+        throws ServletException, IOException;
+
+    /**
+     * <p>
+     * Causes the resource specified to be processed as part of the current
+     * ServletRequest and ServletResponse being processed by the calling Thread.
+     * The output of the target resources processing of the request is written
+     * directly to the ServletResponse output stream.
+     * </p>
+     * <p>
+     * The current JspWriter "out" for this JSP is flushed as a side-effect
+     * of this call, prior to processing the include.
+     * </p>
+     * <p>
+     * If the <I> relativeUrlPath </I> begins with a "/" then the URL specified
+     * is calculated relative to the DOCROOT of the <code>ServletContext</code>
+     * for this JSP. If the path does not begin with a "/" then the URL 
+     * specified is calculated relative to the URL of the request that was
+     * mapped to the calling JSP.
+     * </p>
+     * <p>
+     * It is only valid to call this method from a <code> Thread </code>
+     * executing within a <code> _jspService(...) </code> method of a JSP.
+     * </p>
+     *
+     * @param relativeUrlPath specifies the relative URL path to the target 
+     *     resource to be included
+     *
+     * @throws ServletException if the page that was forwarded to throws
+     *     a ServletException
+     * @throws IOException if an I/O error occurred while forwarding
+     */
+    abstract public void include(String relativeUrlPath) 
+        throws ServletException, IOException;
+
+    /**
+     * <p>
+     * Causes the resource specified to be processed as part of the current
+     * ServletRequest and ServletResponse being processed by the calling Thread.
+     * The output of the target resources processing of the request is written
+     * directly to the current JspWriter returned by a call to getOut().
+     * </p>
+     * <p>
+     * If flush is true, The current JspWriter "out" for this JSP 
+     * is flushed as a side-effect of this call, prior to processing 
+     * the include.  Otherwise, the JspWriter "out" is not flushed.
+     * </p>
+     * <p>
+     * If the <i>relativeUrlPath</i> begins with a "/" then the URL specified
+     * is calculated relative to the DOCROOT of the <code>ServletContext</code>
+     * for this JSP. If the path does not begin with a "/" then the URL 
+     * specified is calculated relative to the URL of the request that was
+     * mapped to the calling JSP.
+     * </p>
+     * <p>
+     * It is only valid to call this method from a <code> Thread </code>
+     * executing within a <code> _jspService(...) </code> method of a JSP.
+     * </p>
+     *
+     * @param relativeUrlPath specifies the relative URL path to the 
+     *     target resource to be included
+     * @param flush True if the JspWriter is to be flushed before the include,
+     *     or false if not.
+     *
+     * @throws ServletException if the page that was forwarded to throws
+     *     a ServletException
+     * @throws IOException if an I/O error occurred while forwarding
+     * @since 2.0
+     */
+    abstract public void include(String relativeUrlPath, boolean flush) 
+	throws ServletException, IOException;
+
+    /**
+     * <p>
+     * This method is intended to process an unhandled 'page' level
+     * exception by forwarding the exception to the specified
+     * error page for this JSP.  If forwarding is not possible (for
+     * example because the response has already been committed), an
+     * implementation dependent mechanism should be used to invoke
+     * the error page (e.g. "including" the error page instead).
+     *
+     * <p>
+     * If no error page is defined in the page, the exception should
+     * be rethrown so that the standard servlet error handling
+     * takes over.
+     *
+     * <p>
+     * A JSP implementation class shall typically clean up any local state
+     * prior to invoking this and will return immediately thereafter. It is
+     * illegal to generate any output to the client, or to modify any 
+     * ServletResponse state after invoking this call.
+     *
+     * <p>
+     * This method is kept for backwards compatiblity reasons.  Newly
+     * generated code should use PageContext.handlePageException(Throwable).
+     *
+     * @param e the exception to be handled
+     *
+     * @throws ServletException if an error occurs while invoking the error page
+     * @throws IOException if an I/O error occurred while invoking the error
+     *     page
+     * @throws NullPointerException if the exception is null
+     *
+     * @see #handlePageException(Throwable)
+     */
+
+    abstract public void handlePageException(Exception e) 
+        throws ServletException, IOException;
+
+    /**
+     * <p>
+     * This method is intended to process an unhandled 'page' level
+     * exception by forwarding the exception to the specified
+     * error page for this JSP.  If forwarding is not possible (for
+     * example because the response has already been committed), an
+     * implementation dependent mechanism should be used to invoke
+     * the error page (e.g. "including" the error page instead).
+     *
+     * <p>
+     * If no error page is defined in the page, the exception should
+     * be rethrown so that the standard servlet error handling
+     * takes over.
+     *
+     * <p>
+     * This method is intended to process an unhandled "page" level exception
+     * by redirecting the exception to either the specified error page for this
+     * JSP, or if none was specified, to perform some implementation dependent
+     * action.
+     *
+     * <p>
+     * A JSP implementation class shall typically clean up any local state
+     * prior to invoking this and will return immediately thereafter. It is
+     * illegal to generate any output to the client, or to modify any 
+     * ServletResponse state after invoking this call.
+     *
+     * @param t the throwable to be handled
+     *
+     * @throws ServletException if an error occurs while invoking the error page
+     * @throws IOException if an I/O error occurred while invoking the error
+     *     page
+     * @throws NullPointerException if the exception is null
+     *
+     * @see #handlePageException(Exception)
+     */
+
+    abstract public void handlePageException(Throwable t) 
+        throws ServletException, IOException;
+
+    /**
+     * Return a new BodyContent object, save the current "out" JspWriter,
+     * and update the value of the "out" attribute in the page scope
+     * attribute namespace of the PageContext.
+     *
+     * @return the new BodyContent
+     */
+
+    public BodyContent pushBody() {
+        return null; // XXX to implement
+    }
+         
+
+    /**
+     * Provides convenient access to error information.
+     *
+     * @return an ErrorData instance containing information about the 
+     * error, as obtained from the request attributes, as per the 
+     * Servlet specification.  If this is not an error page (that is,
+     * if the isErrorPage attribute of the page directive is not set
+     * to "true"), the information is meaningless.
+     *
+     * @since 2.0
+     */
+    public ErrorData getErrorData() {
+	return new ErrorData( 
+	    (Throwable)getRequest().getAttribute( "javax.servlet.error.exception" ),
+	    ((Integer)getRequest().getAttribute( 
+		"javax.servlet.error.status_code" )).intValue(),
+	    (String)getRequest().getAttribute( "javax.servlet.error.request_uri" ),
+	    (String)getRequest().getAttribute( "javax.servlet.error.servlet_name" ) );
+    }
+    
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/SkipPageException.java b/servletapi/jsr152/src/share/javax/servlet/jsp/SkipPageException.java
new file mode 100644
index 0000000..b0ea0cd
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/SkipPageException.java
@@ -0,0 +1,75 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp;
+
+/**
+ * Exception to indicate the calling page must cease evaluation.
+ * Thrown by a simple tag handler to indicate that the remainder of 
+ * the page must not be evaluated.  The result is propagated back to
+ * the pagein the case where one tag invokes another (as can be
+ * the case with tag files).  The effect is similar to that of a 
+ * Classic Tag Handler returning Tag.SKIP_PAGE from doEndTag().
+ * Jsp Fragments may also throw this exception.  This exception
+ * should not be thrown manually in a JSP page or tag file - the behavior is
+ * undefined.  The exception is intended to be thrown inside 
+ * SimpleTag handlers and in JSP fragments.
+ * 
+ * @see javax.servlet.jsp.tagext.SimpleTag#doTag
+ * @see javax.servlet.jsp.tagext.JspFragment#invoke
+ * @see javax.servlet.jsp.tagext.Tag#doEndTag
+ * @since 2.0
+ */
+public class SkipPageException
+    extends JspException
+{
+    /**
+     * Creates a SkipPageException with no message.
+     */
+    public SkipPageException() {
+        super();
+    }
+    
+    /**
+     * Creates a SkipPageException with the provided message.
+     *
+     * @param message the detail message
+     */
+    public SkipPageException( String message ) {
+        super( message );
+    }
+
+    /**
+     * Creates a SkipPageException with the provided message and root cause.
+     *
+     * @param message the detail message
+     * @param rootCause the originating cause of this exception
+     */
+    public SkipPageException( String message, Throwable rootCause ) {
+	super( message, rootCause );
+    }
+
+    /**
+     * Creates a SkipPageException with the provided root cause.
+     *
+     * @param rootCause the originating cause of this exception
+     */
+    public SkipPageException( Throwable rootCause ) {
+	super( rootCause );
+    }
+    
+}
+
+
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/el/ELException.java b/servletapi/jsr152/src/share/javax/servlet/jsp/el/ELException.java
new file mode 100644
index 0000000..a5c218c
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/el/ELException.java
@@ -0,0 +1,91 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.jsp.el;
+
+
+/**
+ * Represents any of the exception conditions that arise during the
+ * operation evaluation of the evaluator.
+ *
+ * @since 2.0
+ */
+public class ELException
+  extends Exception
+{
+  //-------------------------------------
+  // Member variables
+  //-------------------------------------
+
+  private Throwable mRootCause;
+
+  //-------------------------------------
+  /**
+   * Creates an ELException with no detail message.
+   **/
+  public ELException ()
+  {
+    super ();
+  }
+
+  //-------------------------------------
+  /**
+   * Creates an ELException with the provided detail message.
+   *
+   * @param pMessage the detail message
+   **/
+  public ELException (String pMessage)
+  {
+    super (pMessage);
+  }
+
+  //-------------------------------------
+  /**
+   * Creates an ELException with the given root cause.
+   *
+   * @param pRootCause the originating cause of this exception
+   **/
+  public ELException (Throwable pRootCause)
+  {
+    super( pRootCause.getLocalizedMessage() );
+    mRootCause = pRootCause;
+  }
+
+  //-------------------------------------
+  /**
+   * Creates an ELException with the given detail message and root cause.
+   *
+   * @param pMessage the detail message
+   * @param pRootCause the originating cause of this exception
+   **/
+  public ELException (String pMessage,
+		      Throwable pRootCause)
+  {
+    super (pMessage);
+    mRootCause = pRootCause;
+  }
+
+  //-------------------------------------
+  /**
+   * Returns the root cause.
+   *
+   * @return the root cause of this exception
+   */
+  public Throwable getRootCause ()
+  {
+    return mRootCause;
+  }
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/el/ELParseException.java b/servletapi/jsr152/src/share/javax/servlet/jsp/el/ELParseException.java
new file mode 100644
index 0000000..25c2602
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/el/ELParseException.java
@@ -0,0 +1,49 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.jsp.el;
+
+
+/**
+ * Represents a parsing error encountered while parsing an EL expression.
+ *
+ * @since 2.0
+ */
+
+public class ELParseException extends ELException {
+
+ //-------------------------------------
+  /**
+   * Creates an ELParseException with no detail message.
+   */
+  public ELParseException ()
+  {
+    super ();
+  }
+
+  //-------------------------------------
+  /**
+   * Creates an ELParseException with the provided detail message.
+   *
+   * @param pMessage the detail message
+   **/
+  public ELParseException (String pMessage)
+  {
+    super (pMessage);
+  }
+
+  //-------------------------------------
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/el/Expression.java b/servletapi/jsr152/src/share/javax/servlet/jsp/el/Expression.java
new file mode 100644
index 0000000..89ba645
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/el/Expression.java
@@ -0,0 +1,51 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package javax.servlet.jsp.el;
+
+
+/**
+ * <p>The abstract class for a prepared expression.</p>
+ *
+ * <p>An instance of an Expression can be obtained via from an 
+ * ExpressionEvaluator instance.</p>
+ *
+ * <p>An Expression may or not have done a syntactic parse of the expression.
+ * A client invoking the evaluate() method should be ready for the case 
+ * where ELParseException exceptions are raised. </p>
+ *
+ * @since 2.0
+ */
+public abstract class Expression {
+
+    /** 
+     * Evaluates an expression that was previously prepared.  In some 
+     * implementations preparing an expression involves full syntactic 
+     * validation, but others may not do so.  Evaluating the expression may 
+     * raise an ELParseException as well as other ELExceptions due to 
+     * run-time evaluation.
+     *
+     * @param vResolver A VariableResolver instance that can be used at 
+     *   runtime to resolve the name of implicit objects into Objects.
+     * @return The result of the expression evaluation.
+     *
+     * @exception ELException Thrown if the expression evaluation failed.
+     */ 
+    public abstract Object evaluate( VariableResolver vResolver )
+        throws ELException;
+}
+
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/el/ExpressionEvaluator.java b/servletapi/jsr152/src/share/javax/servlet/jsp/el/ExpressionEvaluator.java
new file mode 100644
index 0000000..c2070d9
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/el/ExpressionEvaluator.java
@@ -0,0 +1,106 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.jsp.el;
+
+
+/**
+ * <p>The abstract base class for an expression-language evaluator.
+ * Classes that implement an expression language expose their functionality
+ * via this abstract class.</p>
+ *
+ * <p>An instance of the ExpressionEvaluator can be obtained via the 
+ * JspContext / PageContext</p>
+ *
+ * <p>The parseExpression() and evaluate() methods must be thread-safe.  
+ * That is, multiple threads may call these methods on the same 
+ * ExpressionEvaluator object simultaneously.  Implementations should 
+ * synchronize access if they depend on transient state.  Implementations 
+ * should not, however, assume that only one object of each 
+ * ExpressionEvaluator type will be instantiated; global caching should 
+ * therefore be static.</p>
+ *
+ * <p>Only a single EL expression, starting with '${' and ending with
+ * '}', can be parsed or evaluated at a time.  EL expressions 
+ * cannot be mixed with static text.  For example, attempting to 
+ * parse or evaluate "<code>abc${1+1}def${1+1}ghi</code>" or even
+ * "<code>${1+1}${1+1}</code>" will cause an <code>ELException</code> to
+ * be thrown.</p>
+ *
+ * <p>The following are examples of syntactically legal EL expressions:
+ *
+ * <ul>
+ *   <li><code>${person.lastName}</code></li>
+ *   <li><code>${8 * 8}</code></li>
+ *   <li><code>${my:reverse('hello')}</code></li>
+ * </ul>
+ * </p>
+ *
+ * @since 2.0
+ */
+public abstract class ExpressionEvaluator {
+
+    /**
+     * Prepare an expression for later evaluation.  This method should perform
+     * syntactic validation of the expression; if in doing so it detects 
+     * errors, it should raise an ELParseException.
+     *
+     * @param expression The expression to be evaluated.
+     * @param expectedType The expected type of the result of the evaluation
+     * @param fMapper A FunctionMapper to resolve functions found in 
+     *     the expression.  It can be null, in which case no functions 
+     *     are supported for this invocation.  The ExpressionEvaluator 
+     *     must not hold on to the FunctionMapper reference after 
+     *     returning from <code>parseExpression()</code>.  The 
+     *     <code>Expression</code> object returned must invoke the same 
+     *     functions regardless of whether the mappings in the 
+     *     provided <code>FunctionMapper</code> instance change between 
+     *     calling <code>ExpressionEvaluator.parseExpression()</code>
+     *     and <code>Expression.evaluate()</code>.
+     * @return The Expression object encapsulating the arguments.
+     *
+     * @exception ELException Thrown if parsing errors were found.
+     */ 
+    public abstract Expression parseExpression( String expression, 
+				       Class expectedType, 
+				       FunctionMapper fMapper ) 
+      throws ELException; 
+
+
+    /** 
+     * Evaluates an expression.  This method may perform some syntactic 
+     * validation and, if so, it should raise an ELParseException error if 
+     * it encounters syntactic errors.  EL evaluation errors should cause 
+     * an ELException to be raised.
+     *
+     * @param expression The expression to be evaluated.
+     * @param expectedType The expected type of the result of the evaluation
+     * @param vResolver A VariableResolver instance that can be used at 
+     *     runtime to resolve the name of implicit objects into Objects.
+     * @param fMapper A FunctionMapper to resolve functions found in 
+     *     the expression.  It can be null, in which case no functions 
+     *     are supported for this invocation.  
+     * @return The result of the expression evaluation.
+     *
+     * @exception ELException Thrown if the expression evaluation failed.
+     */ 
+    public abstract Object evaluate( String expression, 
+			    Class expectedType, 
+			    VariableResolver vResolver,
+			    FunctionMapper fMapper ) 
+      throws ELException; 
+}
+
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/el/FunctionMapper.java b/servletapi/jsr152/src/share/javax/servlet/jsp/el/FunctionMapper.java
new file mode 100644
index 0000000..cad79a6
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/el/FunctionMapper.java
@@ -0,0 +1,38 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.jsp.el;
+
+/**
+ * <p>The interface to a map between EL function names and methods.</p>
+ *
+ * <p>Classes implementing this interface may, for instance, consult tag library
+ * information to resolve the map. </p>
+ *
+ * @since 2.0
+ */
+public interface FunctionMapper {
+  /**
+   * Resolves the specified local name and prefix into a Java.lang.Method.
+   * Returns null if the prefix and local name are not found.
+   * 
+   * @param prefix the prefix of the function, or "" if no prefix.
+   * @param localName the short name of the function
+   * @return the result of the method mapping.  Null means no entry found.
+   **/
+  public java.lang.reflect.Method resolveFunction(String prefix, 
+      String localName);
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/el/VariableResolver.java b/servletapi/jsr152/src/share/javax/servlet/jsp/el/VariableResolver.java
new file mode 100644
index 0000000..3072e8c
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/el/VariableResolver.java
@@ -0,0 +1,49 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.jsp.el;
+
+/**
+ * <p>This class is used to customize the way an ExpressionEvaluator resolves
+ * variable references at evaluation time.  For example, instances of this class can
+ * implement their own variable lookup mechanisms, or introduce the
+ * notion of "implicit variables" which override any other variables.
+ * An instance of this class should be passed when evaluating
+ * an expression.</p>
+ *
+ * <p>An instance of this class includes the context against which resolution
+ * will happen</p>
+ *
+ * @since 2.0
+ */
+public interface VariableResolver
+{
+  //-------------------------------------
+  /**
+   * Resolves the specified variable.
+   * Returns null if the variable is not found.
+   * 
+   * @param pName the name of the variable to resolve
+   * @return the result of the variable resolution
+   *
+   * @throws ELException if a failure occurred while trying to resolve
+   *     the given variable
+   **/
+  public Object resolveVariable (String pName)
+    throws ELException;
+					
+  //-------------------------------------
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/el/package.html b/servletapi/jsr152/src/share/javax/servlet/jsp/el/package.html
new file mode 100644
index 0000000..f0eafbb
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/el/package.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<!--
+  - The Apache Software License, Version 1.1
+  -
+  - Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  - reserved.
+  -
+  - Redistribution and use in source and binary forms, with or without
+  - modification, are permitted provided that the following conditions
+  - are met:
+  -
+  - 1. Redistributions of source code must retain the above copyright
+  -    notice, this list of conditions and the following disclaimer. 
+  -
+  - 2. Redistributions in binary form must reproduce the above copyright
+  -    notice, this list of conditions and the following disclaimer in
+  -    the documentation and/or other materials provided with the
+  -    distribution.
+  -
+  - 3. The end-user documentation included with the redistribution, if
+  -    any, must include the following acknowlegement:  
+  -       "This product includes software developed by the 
+  -        Apache Software Foundation (http://www.apache.org/)."
+  -    Alternately, this acknowlegement may appear in the software itself,
+  -    if and wherever such third-party acknowlegements normally appear.
+  -
+  - 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
+  -    Foundation" must not be used to endorse or promote products derived
+  -    from this software without prior written permission. For written 
+  -    permission, please contact apache@apache.org.
+  -
+  - 5. Products derived from this software may not be called "Apache"
+  -    nor may "Apache" appear in their names without prior written
+  -    permission of the Apache Group.
+  -
+  - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+  - WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+  - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  - DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+  - ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+  - USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+  - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+  - OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  - SUCH DAMAGE.
+  - ====================================================================
+  -
+  - This software consists of voluntary contributions made by many
+  - individuals on behalf of the Apache Software Foundation.  For more
+  - information on the Apache Software Foundation, please see
+  - <http://www.apache.org/>.
+  -
+  -->
+</head>
+<body bgcolor="white">
+
+Classes and interfaces for the JSP 2.0 Expression Language API.
+
+<p>
+The JavaServer Pages(tm) (JSP) 2.0 specification provides a portable
+API for evaluating "EL Expressions".  As of JSP 2.0, EL expressions can
+be placed directly in the template text of JSP pages and tag files.
+<p>
+This package contains a number of classes and interfaces that describe 
+and define programmatic access to the Expression Language evaluator. 
+This API can also be used by an implementation of JSP to evaluate the 
+expressions, but other implementations, like open-coding into Java 
+bytecodes, are allowed.  This package is intended to have no dependencies 
+on other portions of the JSP 2.0 specification. 
+</body>
+</html>
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/package.html b/servletapi/jsr152/src/share/javax/servlet/jsp/package.html
new file mode 100644
index 0000000..f0eafb9
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/package.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<!--
+  - The Apache Software License, Version 1.1
+  -
+  - Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  - reserved.
+  -
+  - Redistribution and use in source and binary forms, with or without
+  - modification, are permitted provided that the following conditions
+  - are met:
+  -
+  - 1. Redistributions of source code must retain the above copyright
+  -    notice, this list of conditions and the following disclaimer. 
+  -
+  - 2. Redistributions in binary form must reproduce the above copyright
+  -    notice, this list of conditions and the following disclaimer in
+  -    the documentation and/or other materials provided with the
+  -    distribution.
+  -
+  - 3. The end-user documentation included with the redistribution, if
+  -    any, must include the following acknowlegement:  
+  -       "This product includes software developed by the 
+  -        Apache Software Foundation (http://www.apache.org/)."
+  -    Alternately, this acknowlegement may appear in the software itself,
+  -    if and wherever such third-party acknowlegements normally appear.
+  -
+  - 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
+  -    Foundation" must not be used to endorse or promote products derived
+  -    from this software without prior written permission. For written 
+  -    permission, please contact apache@apache.org.
+  -
+  - 5. Products derived from this software may not be called "Apache"
+  -    nor may "Apache" appear in their names without prior written
+  -    permission of the Apache Group.
+  -
+  - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+  - WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+  - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  - DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+  - ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+  - USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+  - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+  - OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  - SUCH DAMAGE.
+  - ====================================================================
+  -
+  - This software consists of voluntary contributions made by many
+  - individuals on behalf of the Apache Software Foundation.  For more
+  - information on the Apache Software Foundation, please see
+  - <http://www.apache.org/>.
+  -
+  -->
+</head>
+<body bgcolor="white">
+Classes and interfaces for the Core JSP 2.0 API.
+<p>
+The javax.servlet.jsp package contains a number of classes and
+interfaces that describe and define the contracts between a JSP page
+implementation class and the runtime environment provided for an
+instance of such a class by a conforming JSP container.
+</body>
+</html>
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/BodyContent.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/BodyContent.java
new file mode 100644
index 0000000..ca09e2f
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/BodyContent.java
@@ -0,0 +1,138 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/ 
+package javax.servlet.jsp.tagext;
+
+import java.io.Reader;
+import java.io.Writer;
+import java.io.IOException;
+import javax.servlet.jsp.*;
+
+/**
+ * An encapsulation of the evaluation of the body of an action so it is
+ * available to a tag handler.  BodyContent is a subclass of JspWriter.
+ *
+ * <p>
+ * Note that the content of BodyContent is the result of evaluation, so
+ * it will not contain actions and the like, but the result of their
+ * invocation.
+ * 
+ * <p>
+ * BodyContent has methods to convert its contents into
+ * a String, to read its contents, and to clear the contents.
+ *
+ * <p>
+ * The buffer size of a BodyContent object is unbounded.  A
+ * BodyContent object cannot be in autoFlush mode.  It is not possible to
+ * invoke flush on a BodyContent object, as there is no backing stream.
+ *
+ * <p>
+ * Instances of BodyContent are created by invoking the pushBody and
+ * popBody methods of the PageContext class.  A BodyContent is enclosed
+ * within another JspWriter (maybe another BodyContent object) following
+ * the structure of their associated actions.
+ *
+ * <p>
+ * A BodyContent is made available to a BodyTag through a setBodyContent()
+ * call.  The tag handler can use the object until after the call to
+ * doEndTag().
+ */
+
+public abstract class BodyContent extends JspWriter {
+    
+    /**
+     * Protected constructor.
+     *
+     * Unbounded buffer, no autoflushing.
+     *
+     * @param e the enclosing JspWriter
+     */
+
+    protected BodyContent(JspWriter e) {
+	super(UNBOUNDED_BUFFER , false);
+	this.enclosingWriter = e;
+    }
+
+    /**
+     * Redefined flush() so it is not legal.
+     *
+     * <p>
+     * It is not valid to flush a BodyContent because there is no backing
+     * stream behind it.
+     *
+     * @throws IOException always thrown
+     */
+
+    public void flush() throws IOException {
+	throw new IOException("Illegal to flush within a custom tag");
+    }
+
+    /**
+     * Clear the body without throwing any exceptions.
+     */
+    
+    public void clearBody() {
+	try {
+	    this.clear();
+	} catch (IOException ex) {
+	    // TODO -- clean this one up.
+	    throw new Error("internal error!;");
+	}
+    }
+
+    /**
+     * Return the value of this BodyContent as a Reader.
+     *
+     * @return the value of this BodyContent as a Reader
+     */
+    public abstract Reader getReader();
+
+
+    /**
+     * Return the value of the BodyContent as a String.
+     *
+     * @return the value of the BodyContent as a String
+     */
+    public abstract String getString();
+	
+
+    /**
+     * Write the contents of this BodyContent into a Writer.
+     * Subclasses may optimize common invocation patterns.
+     *
+     * @param out The writer into which to place the contents of
+     *     this body evaluation
+     * @throws IOException if an I/O error occurred while writing the
+     *     contents of this BodyContent to the given Writer
+     */
+
+    public abstract void writeOut(Writer out) throws IOException;
+
+
+    /**
+     * Get the enclosing JspWriter.
+     *
+     * @return the enclosing JspWriter passed at construction time
+     */
+
+    public JspWriter getEnclosingWriter() {
+	return enclosingWriter;
+    }
+
+
+    // private fields
+
+    private JspWriter enclosingWriter;
+ }
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/BodyTag.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/BodyTag.java
new file mode 100644
index 0000000..fa2d4f4
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/BodyTag.java
@@ -0,0 +1,185 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp.tagext;
+
+import javax.servlet.jsp.*;
+
+/**
+ * The BodyTag interface extends IterationTag by defining additional
+ * methods that let a tag handler manipulate the content of evaluating its body.
+ *
+ * <p>
+ * It is the responsibility of the tag handler to manipulate the body
+ * content.  For example the tag handler may take the body content,
+ * convert it into a String using the bodyContent.getString
+ * method and then use it.  Or the tag handler may take the body
+ * content and write it out into its enclosing JspWriter using
+ * the bodyContent.writeOut method.
+ *
+ * <p> A tag handler that implements BodyTag is treated as one that
+ * implements IterationTag, except that the doStartTag method can
+ * return SKIP_BODY, EVAL_BODY_INCLUDE or EVAL_BODY_BUFFERED.
+ *
+ * <p>
+ * If EVAL_BODY_INCLUDE is returned, then evaluation happens
+ * as in IterationTag.
+ *
+ * <p>
+ * If EVAL_BODY_BUFFERED is returned, then a BodyContent object will be
+ * created (by code generated by the JSP compiler) to capture the body
+ * evaluation.
+ * The code generated by the JSP compiler obtains the BodyContent object by
+ * calling the pushBody method of the current pageContext, which
+ * additionally has the effect of saving the previous out value.
+ * The page compiler returns this object by calling the popBody
+ * method of the PageContext class;
+ * the call also restores the value of out.
+ *
+ * <p>
+ * The interface provides one new property with a setter method and one
+ * new action method.
+ *
+ * <p><B>Properties</B>
+ * <p> There is a new property: bodyContent, to contain the BodyContent
+ * object, where the JSP Page implementation object will place the
+ * evaluation (and reevaluation, if appropriate) of the body.  The setter
+ * method (setBodyContent) will only be invoked if doStartTag() returns
+ * EVAL_BODY_BUFFERED and the corresponding action element does not have
+ * an empty body.
+ *
+ * <p><B>Methods</B>
+ * <p> In addition to the setter method for the bodyContent property, there
+ * is a new action method: doInitBody(), which is invoked right after
+ * setBodyContent() and before the body evaluation.  This method is only
+ * invoked if doStartTag() returns EVAL_BODY_BUFFERED.
+ *
+ * <p><B>Lifecycle</B>
+ * <p> Lifecycle details are described by the transition diagram below.
+ * Exceptions that are thrown during the computation of doStartTag(),
+ * setBodyContent(), doInitBody(), BODY, doAfterBody() interrupt the
+ * execution sequence and are propagated up the stack, unless the
+ * tag handler implements the TryCatchFinally interface; see that
+ * interface for details.
+ * <p>
+ * <IMG src="doc-files/BodyTagProtocol.gif"
+ *      alt="Lifecycle Details Transition Diagram for BodyTag"/>
+ *
+ * <p><B>Empty and Non-Empty Action</B>
+ * <p> If the TagLibraryDescriptor file indicates that the action must
+ * always have an empty element body, by an &lt;body-content&gt; entry 
+ * of "empty", then the doStartTag() method must return SKIP_BODY.
+ * Otherwise, the doStartTag() method may return SKIP_BODY,
+ * EVAL_BODY_INCLUDE, or EVAL_BODY_BUFFERED.
+ *
+ * <p>Note that which methods are invoked after the doStartTag() depends on 
+ * both the return value and on if the custom action element is empty
+ * or not in the JSP page, not how it's declared in the TLD.
+ *
+ * <p>
+ * If SKIP_BODY is returned the body is not evaluated, and doEndTag() is
+ * invoked.
+ *
+ * <p>
+ * If EVAL_BODY_INCLUDE is returned, and the custom action element is not
+ * empty, setBodyContent() is not invoked,
+ * doInitBody() is not invoked, the body is evaluated and
+ * "passed through" to the current out, doAfterBody() is invoked
+ * and then, after zero or more iterations, doEndTag() is invoked.
+ * If the custom action element is empty, only doStart() and 
+ * doEndTag() are invoked.
+ *
+ * <p>
+ * If EVAL_BODY_BUFFERED is returned, and the custom action element is not
+ * empty, setBodyContent() is invoked,
+ * doInitBody() is invoked, the body is evaluated, doAfterBody() is
+ * invoked, and then, after zero or more iterations, doEndTag() is invoked.
+ * If the custom action element is empty, only doStart() and doEndTag() 
+ * are invoked.
+ */
+
+public interface BodyTag extends IterationTag {
+
+    /**
+     * Deprecated constant that has the same value as EVAL_BODY_BUFFERED
+     * and EVAL_BODY_AGAIN.  This name has been marked as deprecated
+     * to encourage the use of the two different terms, which are much
+     * more descriptive.
+     *
+     * @deprecated	As of Java JSP API 1.2, use BodyTag.EVAL_BODY_BUFFERED
+     * or IterationTag.EVAL_BODY_AGAIN.
+     */
+ 
+    public final static int EVAL_BODY_TAG = 2;
+
+    /**
+     * Request the creation of new buffer, a BodyContent on which to
+     * evaluate the body of this tag.
+     *
+     * Returned from doStartTag when it implements BodyTag.
+     * This is an illegal return value for doStartTag when the class
+     * does not implement BodyTag.
+     */
+
+    public final static int EVAL_BODY_BUFFERED = 2;
+
+
+    /**
+     * Set the bodyContent property.
+     * This method is invoked by the JSP page implementation object at
+     * most once per action invocation.
+     * This method will be invoked before doInitBody.
+     * This method will not be invoked for empty tags or for non-empty
+     * tags whose doStartTag() method returns SKIP_BODY or EVAL_BODY_INCLUDE.
+     *
+     * <p>
+     * When setBodyContent is invoked, the value of the implicit object out
+     * has already been changed in the pageContext object.  The BodyContent
+     * object passed will have not data on it but may have been reused
+     * (and cleared) from some previous invocation.
+     *
+     * <p>
+     * The BodyContent object is available and with the appropriate content
+     * until after the invocation of the doEndTag method, at which case it
+     * may be reused.
+     *
+     * @param b the BodyContent
+     * @see #doInitBody
+     * @see #doAfterBody
+     */
+
+    void setBodyContent(BodyContent b);
+
+
+    /**
+     * Prepare for evaluation of the body.
+     * This method is invoked by the JSP page implementation object
+     * after setBodyContent and before the first time
+     * the body is to be evaluated.
+     * This method will not be invoked for empty tags or for non-empty
+     * tags whose doStartTag() method returns SKIP_BODY or EVAL_BODY_INCLUDE.
+     *
+     * <p>
+     * The JSP container will resynchronize the values of any AT_BEGIN and
+     * NESTED variables (defined by the associated TagExtraInfo or TLD) after
+     * the invocation of doInitBody().
+     *
+     * @throws JspException if an error occurred while processing this tag
+     * @see #doAfterBody
+     */
+
+    void doInitBody() throws JspException;
+
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/BodyTagSupport.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/BodyTagSupport.java
new file mode 100644
index 0000000..d6bdc6b
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/BodyTagSupport.java
@@ -0,0 +1,159 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp.tagext;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+
+/**
+ * A base class for defining tag handlers implementing BodyTag.
+ *
+ * <p>
+ * The BodyTagSupport class implements the BodyTag interface and adds
+ * additional convenience methods including getter methods for the
+ * bodyContent property and methods to get at the previous out JspWriter.
+ *
+ * <p>
+ * Many tag handlers will extend BodyTagSupport and only redefine a
+ * few methods.
+ */
+
+public class BodyTagSupport extends TagSupport implements BodyTag {
+
+    /**
+     * Default constructor, all subclasses are required to only define
+     * a public constructor with the same signature, and to call the
+     * superclass constructor.
+     *
+     * This constructor is called by the code generated by the JSP
+     * translator.
+     */
+
+    public BodyTagSupport() {
+	super();
+    }
+
+    /**
+     * Default processing of the start tag returning EVAL_BODY_BUFFERED.
+     *
+     * @return EVAL_BODY_BUFFERED
+     * @throws JspException if an error occurred while processing this tag
+     * @see BodyTag#doStartTag
+     */
+ 
+    public int doStartTag() throws JspException {
+        return EVAL_BODY_BUFFERED;
+    }
+
+
+    /**
+     * Default processing of the end tag returning EVAL_PAGE.
+     *
+     * @return EVAL_PAGE
+     * @throws JspException if an error occurred while processing this tag
+     * @see Tag#doEndTag
+     */
+
+    public int doEndTag() throws JspException {
+	return super.doEndTag();
+    }
+
+
+    // Actions related to body evaluation
+
+    /**
+     * Prepare for evaluation of the body: stash the bodyContent away.
+     *
+     * @param b the BodyContent
+     * @see #doAfterBody
+     * @see #doInitBody()
+     * @see BodyTag#setBodyContent
+     */
+
+    public void setBodyContent(BodyContent b) {
+	this.bodyContent = b;
+    }
+
+
+    /**
+     * Prepare for evaluation of the body just before the first body evaluation:
+     * no action.
+     *
+     * @throws JspException if an error occurred while processing this tag
+     * @see #setBodyContent
+     * @see #doAfterBody
+     * @see BodyTag#doInitBody
+     */
+
+    public void doInitBody() throws JspException {
+    }
+
+
+    /**
+     * After the body evaluation: do not reevaluate and continue with the page.
+     * By default nothing is done with the bodyContent data (if any).
+     *
+     * @return SKIP_BODY
+     * @throws JspException if an error occurred while processing this tag
+     * @see #doInitBody
+     * @see BodyTag#doAfterBody
+     */
+
+    public int doAfterBody() throws JspException {
+ 	return SKIP_BODY;
+    }
+
+
+    /**
+     * Release state.
+     *
+     * @see Tag#release
+     */
+
+    public void release() {
+	bodyContent = null;
+
+	super.release();
+    }
+
+    /**
+     * Get current bodyContent.
+     *
+     * @return the body content.
+     */
+    
+    public BodyContent getBodyContent() {
+	return bodyContent;
+    }
+
+
+    /**
+     * Get surrounding out JspWriter.
+     *
+     * @return the enclosing JspWriter, from the bodyContent.
+     */
+
+    public JspWriter getPreviousOut() {
+	return bodyContent.getEnclosingWriter();
+    }
+
+    // protected fields
+
+    /**
+     * The current BodyContent for this BodyTag.
+     */
+    protected BodyContent   bodyContent;
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/DynamicAttributes.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/DynamicAttributes.java
new file mode 100644
index 0000000..6fbbc11
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/DynamicAttributes.java
@@ -0,0 +1,51 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp.tagext;
+
+import javax.servlet.jsp.JspException;
+
+/**
+ * For a tag to declare that it accepts dynamic attributes, it must implement
+ * this interface.  The entry for the tag in the Tag Library Descriptor must 
+ * also be configured to indicate dynamic attributes are accepted.
+ * <br>
+ * For any attribute that is not declared in the Tag Library Descriptor for
+ * this tag, instead of getting an error at translation time, the 
+ * <code>setDynamicAttribute()</code> method is called, with the name and
+ * value of the attribute.  It is the responsibility of the tag to 
+ * remember the names and values of the dynamic attributes.
+ *
+ * @since 2.0
+ */
+public interface DynamicAttributes {
+    
+    /**
+     * Called when a tag declared to accept dynamic attributes is passed
+     * an attribute that is not declared in the Tag Library Descriptor.
+     * 
+     * @param uri the namespace of the attribute, or null if in the default
+     *     namespace.
+     * @param localName the name of the attribute being set.
+     * @param value the value of the attribute
+     * @throws JspException if the tag handler wishes to
+     *     signal that it does not accept the given attribute.  The 
+     *     container must not call doStartTag() or doTag() for this tag.
+     */
+    public void setDynamicAttribute(
+        String uri, String localName, Object value ) 
+        throws JspException;
+    
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/FunctionInfo.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/FunctionInfo.java
new file mode 100644
index 0000000..1c984a3
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/FunctionInfo.java
@@ -0,0 +1,80 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+package javax.servlet.jsp.tagext;
+
+/**
+ * Information for a function in a Tag Library.
+ * This class is instantiated from the Tag Library Descriptor file (TLD)
+ * and is available only at translation time.
+ * 
+ * @since 2.0
+ */
+public class FunctionInfo {
+
+    /**
+     * Constructor for FunctionInfo.
+     *
+     * @param name The name of the function
+     * @param klass The class of the function
+     * @param signature The signature of the function
+     */
+
+    public FunctionInfo(String name, String klass, String signature) {
+
+	this.name = name;
+        this.functionClass = klass;
+        this.functionSignature = signature;
+    }
+
+    /**
+     * The name of the function.
+     *
+     * @return The name of the function
+     */
+
+    public String getName() {
+	return name;
+    }
+
+    /**
+     * The class of the function.
+     *
+     * @return The class of the function
+     */
+
+    public String getFunctionClass() {
+        return functionClass;
+    }
+
+    /**
+     * The signature of the function.
+     *
+     * @return The signature of the function
+     */
+
+    public String getFunctionSignature() {
+        return functionSignature;
+    }
+
+    /*
+     * fields
+     */
+
+    private String name;
+    private String functionClass;
+    private String functionSignature;
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/IterationTag.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/IterationTag.java
new file mode 100644
index 0000000..da643bc
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/IterationTag.java
@@ -0,0 +1,119 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.jsp.tagext;
+
+import javax.servlet.jsp.*;
+
+/**
+ * The IterationTag interface extends Tag by defining one additional
+ * method that controls the reevaluation of its body.
+ *
+ * <p> A tag handler that implements IterationTag is treated as one that
+ * implements Tag regarding  the doStartTag() and doEndTag() methods.
+ * IterationTag provides a new method: <code>doAfterBody()</code>.
+ *
+ * <p> The doAfterBody() method is invoked after every body evaluation
+ * to control whether the body will be reevaluated or not.  If doAfterBody()
+ * returns IterationTag.EVAL_BODY_AGAIN, then the body will be reevaluated.
+ * If doAfterBody() returns Tag.SKIP_BODY, then the body will be skipped
+ * and doEndTag() will be evaluated instead.
+ *
+ * <p><B>Properties</B>
+ * There are no new properties in addition to those in Tag.
+ *
+ * <p><B>Methods</B>
+ * There is one new methods: doAfterBody().
+ *
+ * <p><B>Lifecycle</B>
+ *
+ * <p> Lifecycle details are described by the transition diagram
+ * below.  Exceptions that are thrown during the computation of
+ * doStartTag(), BODY and doAfterBody() interrupt the execution
+ * sequence and are propagated up the stack, unless the tag handler
+ * implements the TryCatchFinally interface; see that interface for
+ * details.
+ *
+ * <p>
+ * <IMG src="doc-files/IterationTagProtocol.gif"
+ *      alt="Lifecycle Details Transition Diagram for IterationTag"/>
+ *
+ * <p><B>Empty and Non-Empty Action</B>
+ * <p> If the TagLibraryDescriptor file indicates that the action must
+ * always have an empty element body, by a &lt;body-content&gt; entry of 
+ * "empty", then the doStartTag() method must return SKIP_BODY.
+ *
+ * <p>Note that which methods are invoked after the doStartTag() depends on
+ * both the return value and on if the custom action element is empty
+ * or not in the JSP page, not on how it's declared in the TLD.
+ *
+ * <p>
+ * If SKIP_BODY is returned the body is not evaluated, and then doEndTag()
+ * is invoked.
+ *
+ * <p>
+ * If EVAL_BODY_INCLUDE is returned, and the custom action element is not
+ * empty, the body is evaluated and "passed through" to the current out, 
+ * then doAfterBody() is invoked and, after zero or more iterations, 
+ * doEndTag() is invoked.
+ */
+
+public interface IterationTag extends Tag {
+
+    /**
+     * Request the reevaluation of some body.
+     * Returned from doAfterBody.
+     *
+     * For compatibility with JSP 1.1, the value is carefully selected
+     * to be the same as the, now deprecated, BodyTag.EVAL_BODY_TAG,
+     * 
+     */
+ 
+    public final static int EVAL_BODY_AGAIN = 2;
+
+    /**
+     * Process body (re)evaluation.  This method is invoked by the
+     * JSP Page implementation object after every evaluation of
+     * the body into the BodyEvaluation object. The method is
+     * not invoked if there is no body evaluation.
+     *
+     * <p>
+     * If doAfterBody returns EVAL_BODY_AGAIN, a new evaluation of the
+     * body will happen (followed by another invocation of doAfterBody).
+     * If doAfterBody returns SKIP_BODY, no more body evaluations will occur,
+     * and the doEndTag method will be invoked.
+     *
+     * <p>
+     * If this tag handler implements BodyTag and doAfterBody returns
+     * SKIP_BODY, the value of out will be restored using the popBody 
+     * method in pageContext prior to invoking doEndTag.
+     *
+     * <p>
+     * The method re-invocations may be lead to different actions because
+     * there might have been some changes to shared state, or because
+     * of external computation.
+     *
+     * <p>
+     * The JSP container will resynchronize the values of any AT_BEGIN and
+     * NESTED variables (defined by the associated TagExtraInfo or TLD) after
+     * the invocation of doAfterBody().
+     *
+     * @return whether additional evaluations of the body are desired
+     * @throws JspException if an error occurred while processing this tag
+     */
+
+    int doAfterBody() throws JspException;
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/JspFragment.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/JspFragment.java
new file mode 100644
index 0000000..00f52bc
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/JspFragment.java
@@ -0,0 +1,82 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+package javax.servlet.jsp.tagext;
+
+import java.io.IOException;
+import java.io.Writer;
+import javax.servlet.jsp.*;
+
+/**
+ * Encapsulates a portion of JSP code in an object that 
+ * can be invoked as many times as needed.  JSP Fragments are defined 
+ * using JSP syntax as the body of a tag for an invocation to a SimpleTag 
+ * handler, or as the body of a &lt;jsp:attribute&gt; standard action
+ * specifying the value of an attribute that is declared as a fragment,
+ * or to be of type JspFragment in the TLD.
+ * <p>
+ * The definition of the JSP fragment must only contain template 
+ * text and JSP action elements.  In other words, it must not contain
+ * scriptlets or scriptlet expressions.  At translation time, the 
+ * container generates an implementation of the JspFragment abstract class
+ * capable of executing the defined fragment.
+ * <p>
+ * A tag handler can invoke the fragment zero or more times, or 
+ * pass it along to other tags, before returning.  To communicate values
+ * to/from a JSP fragment, tag handlers store/retrieve values in 
+ * the JspContext associated with the fragment.
+ * <p>
+ * Note that tag library developers and page authors should not generate
+ * JspFragment implementations manually.
+ * <p>
+ * <i>Implementation Note</i>: It is not necessary to generate a 
+ * separate class for each fragment.  One possible implementation is 
+ * to generate a single helper class for each page that implements 
+ * JspFragment. Upon construction, a discriminator can be passed to 
+ * select which fragment that instance will execute.
+ *
+ * @since 2.0
+ */
+public abstract class JspFragment {
+
+    /**
+     * Executes the fragment and directs all output to the given Writer,
+     * or the JspWriter returned by the getOut() method of the JspContext
+     * associated with the fragment if out is null.
+     *
+     * @param out The Writer to output the fragment to, or null if 
+     *     output should be sent to JspContext.getOut().
+     * @throws javax.servlet.jsp.JspException Thrown if an error occured
+     *     while invoking this fragment.
+     * @throws javax.servlet.jsp.SkipPageException Thrown if the page
+     *     that (either directly or indirectly) invoked the tag handler that
+     *     invoked this fragment is to cease evaluation.  The container
+     *     must throw this exception if a Classic Tag Handler returned
+     *     Tag.SKIP_PAGE or if a Simple Tag Handler threw SkipPageException.
+     * @throws java.io.IOException If there was an error writing to the 
+     *     stream.
+     */
+    public abstract void invoke( Writer out )
+        throws JspException, IOException;
+
+    /**
+     * Returns the JspContext that is bound to this JspFragment.
+     *
+     * @return The JspContext used by this fragment at invocation time.
+     */
+    public abstract JspContext getJspContext();
+
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/JspTag.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/JspTag.java
new file mode 100644
index 0000000..3e1d482
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/JspTag.java
@@ -0,0 +1,25 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp.tagext;
+
+/**
+ * Serves as a base class for Tag and SimpleTag.  
+ * This is mostly for organizational and type-safety purposes.
+ *
+ * @since 2.0
+ */
+public interface JspTag {
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/PageData.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/PageData.java
new file mode 100644
index 0000000..50e62d6
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/PageData.java
@@ -0,0 +1,48 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package javax.servlet.jsp.tagext;
+
+import java.io.InputStream;
+
+/**
+ * Translation-time information on a JSP page.  The information
+ * corresponds to the XML view of the JSP page.
+ *
+ * <p>
+ * Objects of this type are generated by the JSP translator, e.g.
+ * when being pased to a TagLibraryValidator instance.
+ */
+
+abstract public class PageData {
+
+    /**
+     * Sole constructor. (For invocation by subclass constructors, 
+     * typically implicit.)
+     */
+    public PageData() {
+    }
+    
+    /**
+     * Returns an input stream on the XML view of a JSP page.
+     * The stream is encoded in UTF-8.  Recall tht the XML view of a 
+     * JSP page has the include directives expanded.
+     * 
+     * @return An input stream on the document.
+     */
+   abstract public InputStream getInputStream();
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/SimpleTag.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/SimpleTag.java
new file mode 100644
index 0000000..a9a414f
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/SimpleTag.java
@@ -0,0 +1,139 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp.tagext;
+
+import javax.servlet.jsp.JspContext;
+
+/**
+ * Interface for defining Simple Tag Handlers.
+ * 
+ * <p>Simple Tag Handlers differ from Classic Tag Handlers in that instead 
+ * of supporting <code>doStartTag()</code> and <code>doEndTag()</code>, 
+ * the <code>SimpleTag</code> interface provides a simple 
+ * <code>doTag()</code> method, which is called once and only once for any 
+ * given tag invocation.  All tag logic, iteration, body evaluations, etc. 
+ * are to be performed in this single method.  Thus, simple tag handlers 
+ * have the equivalent power of <code>BodyTag</code>, but with a much 
+ * simpler lifecycle and interface.</p>
+ *
+ * <p>To support body content, the <code>setJspBody()</code> 
+ * method is provided.  The container invokes the <code>setJspBody()</code> 
+ * method with a <code>JspFragment</code> object encapsulating the body of 
+ * the tag.  The tag handler implementation can call 
+ * <code>invoke()</code> on that fragment to evaluate the body as
+ * many times as it needs.</p>
+ *
+ * <p>A SimpleTag handler must have a public no-args constructor.  Most
+ * SimpleTag handlers should extend SimpleTagSupport.</p>
+ * 
+ * <p><b>Lifecycle</b></p>
+ *
+ * <p>The following is a non-normative, brief overview of the 
+ * SimpleTag lifecycle.  Refer to the JSP Specification for details.</p>
+ *
+ * <ol>
+ *   <li>A new tag handler instance is created each time by the container 
+ *       by calling the provided zero-args constructor.  Unlike classic
+ *       tag handlers, simple tag handlers are never cached and reused by
+ *       the JSP container.</li>
+ *   <li>The <code>setJspContext()</code> and <code>setParent()</code> 
+ *       methods are called by the container.  The <code>setParent()</code>
+ *       method is only called if the element is nested within another tag 
+ *       invocation.</li>
+ *   <li>The setters for each attribute defined for this tag are called
+ *       by the container.</li>
+ *   <li>If a body exists, the <code>setJspBody()</code> method is called 
+ *       by the container to set the body of this tag, as a 
+ *       <code>JspFragment</code>.  If the action element is empty in
+ *       the page, this method is not called at all.</li>
+ *   <li>The <code>doTag()</code> method is called by the container.  All
+ *       tag logic, iteration, body evaluations, etc. occur in this 
+ *       method.</li>
+ *   <li>The <code>doTag()</code> method returns and all variables are
+ *       synchronized.</li>
+ * </ol>
+ * 
+ * @see SimpleTagSupport
+ * @since 2.0
+ */
+public interface SimpleTag extends JspTag {
+    
+    /** 
+     * Called by the container to invoke this tag.
+     * The implementation of this method is provided by the tag library
+     * developer, and handles all tag processing, body iteration, etc.
+     *
+     * <p>
+     * The JSP container will resynchronize any AT_BEGIN and AT_END
+     * variables (defined by the associated tag file, TagExtraInfo, or TLD)
+     * after the invocation of doTag().
+     * 
+     * @throws javax.servlet.jsp.JspException If an error occurred 
+     *     while processing this tag.
+     * @throws javax.servlet.jsp.SkipPageException If the page that
+     *     (either directly or indirectly) invoked this tag is to
+     *     cease evaluation.  A Simple Tag Handler generated from a 
+     *     tag file must throw this exception if an invoked Classic 
+     *     Tag Handler returned SKIP_PAGE or if an invoked Simple
+     *     Tag Handler threw SkipPageException or if an invoked Jsp Fragment
+     *     threw a SkipPageException.
+     * @throws java.io.IOException If there was an error writing to the
+     *     output stream.
+     */ 
+    public void doTag() 
+        throws javax.servlet.jsp.JspException, java.io.IOException;
+    
+    /**
+     * Sets the parent of this tag, for collaboration purposes.
+     * <p>
+     * The container invokes this method only if this tag invocation is 
+     * nested within another tag invocation.
+     *
+     * @param parent the tag that encloses this tag
+     */
+    public void setParent( JspTag parent );
+    
+    /**
+     * Returns the parent of this tag, for collaboration purposes.
+     *
+     * @return the parent of this tag
+     */ 
+    public JspTag getParent();
+    
+    /**
+     * Called by the container to provide this tag handler with
+     * the <code>JspContext</code> for this invocation.
+     * An implementation should save this value.
+     * 
+     * @param pc the page context for this invocation
+     * @see Tag#setPageContext
+     */
+    public void setJspContext( JspContext pc );
+                
+    /** 
+     * Provides the body of this tag as a JspFragment object, able to be 
+     * invoked zero or more times by the tag handler. 
+     * <p>
+     * This method is invoked by the JSP page implementation 
+     * object prior to <code>doTag()</code>.  If the action element is
+     * empty in the page, this method is not called at all.
+     * 
+     * @param jspBody The fragment encapsulating the body of this tag.
+     */ 
+    public void setJspBody( JspFragment jspBody );
+
+    
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/SimpleTagSupport.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/SimpleTagSupport.java
new file mode 100644
index 0000000..1824639
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/SimpleTagSupport.java
@@ -0,0 +1,212 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp.tagext;
+
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.JspException;
+import java.io.IOException;
+
+/**
+ * A base class for defining tag handlers implementing SimpleTag.
+ * <p>
+ * The SimpleTagSupport class is a utility class intended to be used
+ * as the base class for new simple tag handlers.  The SimpleTagSupport
+ * class implements the SimpleTag interface and adds additional
+ * convenience methods including getter methods for the properties in
+ * SimpleTag.
+ *
+ * @since 2.0
+ */
+public class SimpleTagSupport 
+    implements SimpleTag
+{
+    /** Reference to the enclosing tag. */
+    private JspTag parentTag;
+    
+    /** The JSP context for the upcoming tag invocation. */
+    private JspContext jspContext;
+    
+    /** The body of the tag. */
+    private JspFragment jspBody;
+    
+    /**
+     * Sole constructor. (For invocation by subclass constructors, 
+     * typically implicit.)
+     */
+    public SimpleTagSupport() {
+    }
+    
+    /** 
+     * Default processing of the tag does nothing.
+     *
+     * @throws JspException Subclasses can throw JspException to indicate
+     *     an error occurred while processing this tag.
+     * @throws javax.servlet.jsp.SkipPageException If the page that
+     *     (either directly or indirectly) invoked this tag is to
+     *     cease evaluation.  A Simple Tag Handler generated from a 
+     *     tag file must throw this exception if an invoked Classic 
+     *     Tag Handler returned SKIP_PAGE or if an invoked Simple
+     *     Tag Handler threw SkipPageException or if an invoked Jsp Fragment
+     *     threw a SkipPageException.
+     * @throws IOException Subclasses can throw IOException if there was
+     *     an error writing to the output stream
+     * @see SimpleTag#doTag()
+     */ 
+    public void doTag() 
+        throws JspException, IOException
+    {
+    }
+    
+    /**
+     * Sets the parent of this tag, for collaboration purposes.
+     * <p>
+     * The container invokes this method only if this tag invocation is
+     * nested within another tag invocation.
+     *
+     * @param parent the tag that encloses this tag
+     */
+    public void setParent( JspTag parent ) {
+        this.parentTag = parent;
+    }
+    
+    /**
+     * Returns the parent of this tag, for collaboration purposes.
+     *
+     * @return the parent of this tag
+     */ 
+    public JspTag getParent() {
+        return this.parentTag;
+    }
+    
+    /**
+     * Stores the provided JSP context in the private jspContext field.
+     * Subclasses can access the <code>JspContext</code> via 
+     * <code>getJspContext()</code>.
+     * 
+     * @param pc the page context for this invocation
+     * @see SimpleTag#setJspContext
+     */
+    public void setJspContext( JspContext pc ) {
+        this.jspContext = pc;
+    }
+    
+    /**
+     * Returns the page context passed in by the container via 
+     * setJspContext.
+     *
+     * @return the page context for this invocation
+     */
+    protected JspContext getJspContext() {
+        return this.jspContext;
+    }
+                
+    /** 
+     * Stores the provided JspFragment.
+     *
+     * @param jspBody The fragment encapsulating the body of this tag.
+     *     If the action element is empty in the page, this method is 
+     *     not called at all.
+     * @see SimpleTag#setJspBody
+     */ 
+    public void setJspBody( JspFragment jspBody ) {
+        this.jspBody = jspBody;
+    }
+    
+    /**
+     * Returns the body passed in by the container via setJspBody.
+     *
+     * @return the fragment encapsulating the body of this tag, or
+     *    null if the action element is empty in the page.
+     */
+    protected JspFragment getJspBody() {
+        return this.jspBody;
+    }
+
+    /**
+     * Find the instance of a given class type that is closest to a given
+     * instance.
+     * This method uses the getParent method from the Tag and/or SimpleTag
+     * interfaces.  This method is used for coordination among 
+     * cooperating tags.
+     *
+     * <p> For every instance of TagAdapter
+     * encountered while traversing the ancestors, the tag handler returned by
+     * <tt>TagAdapter.getAdaptee()</tt> - instead of the TagAdpater itself -
+     * is compared to <tt>klass</tt>. If the tag handler matches, it - and
+     * not its TagAdapter - is returned.
+     *
+     * <p>
+     * The current version of the specification only provides one formal
+     * way of indicating the observable type of a tag handler: its
+     * tag handler implementation class, described in the tag-class
+     * subelement of the tag element.  This is extended in an
+     * informal manner by allowing the tag library author to
+     * indicate in the description subelement an observable type.
+     * The type should be a subtype of the tag handler implementation
+     * class or void.
+     * This addititional constraint can be exploited by a
+     * specialized container that knows about that specific tag library,
+     * as in the case of the JSP standard tag library.
+     *
+     * <p>
+     * When a tag library author provides information on the
+     * observable type of a tag handler, client programmatic code
+     * should adhere to that constraint.  Specifically, the Class
+     * passed to findAncestorWithClass should be a subtype of the
+     * observable type.
+     * 
+     *
+     * @param from The instance from where to start looking.
+     * @param klass The subclass of JspTag or interface to be matched
+     * @return the nearest ancestor that implements the interface
+     * or is an instance of the class specified
+     */
+    public static final JspTag findAncestorWithClass(
+	JspTag from, Class klass) 
+    {
+	boolean isInterface = false;
+
+	if (from == null || klass == null
+	        || (!JspTag.class.isAssignableFrom(klass)
+		    && !(isInterface = klass.isInterface()))) {
+	    return null;
+	}
+
+	for (;;) {
+	    JspTag parent = null;
+	    if( from instanceof SimpleTag ) {
+		parent = ((SimpleTag)from).getParent();
+	    }
+	    else if( from instanceof Tag ) {
+		parent = ((Tag)from).getParent();
+	    }
+	    if (parent == null) {
+		return null;
+	    }
+
+	    if (parent instanceof TagAdapter) {
+		parent = ((TagAdapter) parent).getAdaptee();
+	    }
+
+	    if ((isInterface && klass.isInstance(parent))
+		    || klass.isAssignableFrom(parent.getClass())) {
+		return parent;
+	    }
+
+	    from = parent;
+	}
+    }    
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/Tag.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/Tag.java
new file mode 100644
index 0000000..28cbcf7
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/Tag.java
@@ -0,0 +1,262 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+package javax.servlet.jsp.tagext;
+
+import javax.servlet.jsp.*;
+
+
+/**
+ * The interface of a classic tag handler that does not want to manipulate 
+ * its body.  The Tag interface defines the basic protocol between a Tag 
+ * handler and JSP page implementation class.  It defines the life cycle 
+ * and the methods to be invoked at start and end tag.
+ *
+ * <p><B>Properties</B></p>
+ *
+ * <p>The Tag interface specifies the setter and getter methods for the core
+ * pageContext and parent properties.</p>
+ *
+ * <p>The JSP page implementation object invokes setPageContext and
+ * setParent, in that order, before invoking doStartTag() or doEndTag().</p>
+ *
+ * <p><B>Methods</B></p>
+ *
+ * <p>There are two main actions: doStartTag and doEndTag.  Once all
+ * appropriate properties have been initialized, the doStartTag and
+ * doEndTag methods can be invoked on the tag handler.  Between these
+ * invocations, the tag handler is assumed to hold a state that must
+ * be preserved.  After the doEndTag invocation, the tag handler is
+ * available for further invocations (and it is expected to have
+ * retained its properties).</p>
+ *
+ * <p><B>Lifecycle</B></p>
+ *
+ * <p>Lifecycle details are described by the transition diagram below,
+ * with the following comments:
+ * <ul>
+ * <li> [1] This transition is intended to be for releasing long-term data.
+ * no guarantees are assumed on whether any properties have been retained
+ * or not.
+ * <li> [2] This transition happens if and only if the tag ends normally
+ * without raising an exception
+ * <li> [3] Some setters may be called again before a tag handler is 
+ * reused.  For instance, <code>setParent()</code> is called if it's 
+ * reused within the same page but at a different level, 
+ * <code>setPageContext()</code> is called if it's used in another page, 
+ * and attribute setters are called if the values differ or are expressed 
+ * as request-time attribute values.
+ * <li> Check the TryCatchFinally interface for additional details related
+ * to exception handling and resource management.
+ * </ul></p>
+ *
+ * <IMG src="doc-files/TagProtocol.gif"
+ *      alt="Lifecycle Details Transition Diagram for Tag"/>
+ * 
+ * <p>Once all invocations on the tag handler
+ * are completed, the release method is invoked on it.  Once a release
+ * method is invoked <em>all</em> properties, including parent and
+ * pageContext, are assumed to have been reset to an unspecified value.
+ * The page compiler guarantees that release() will be invoked on the Tag
+ * handler before the handler is released to the GC.</p>
+ *
+ * <p><B>Empty and Non-Empty Action</B></p>
+ * <p>If the TagLibraryDescriptor file indicates that the action must
+ * always have an empty action, by an &lt;body-content&gt; entry of "empty",
+ * then the doStartTag() method must return SKIP_BODY.</p>
+ *
+ * <p>Otherwise, the doStartTag() method may return SKIP_BODY or
+ * EVAL_BODY_INCLUDE.</p>
+ *
+ * <p>If SKIP_BODY is returned the body, if present, is not evaluated.</p>
+ * 
+ * <p>If EVAL_BODY_INCLUDE is returned, the body is evaluated and
+ * "passed through" to the current out.</p>
+*/
+
+public interface Tag extends JspTag {
+
+    /**
+     * Skip body evaluation.
+     * Valid return value for doStartTag and doAfterBody.
+     */
+ 
+    public final static int SKIP_BODY = 0;
+ 
+    /**
+     * Evaluate body into existing out stream.
+     * Valid return value for doStartTag.
+     */
+ 
+    public final static int EVAL_BODY_INCLUDE = 1;
+
+    /**
+     * Skip the rest of the page.
+     * Valid return value for doEndTag.
+     */
+
+    public final static int SKIP_PAGE = 5;
+
+    /**
+     * Continue evaluating the page.
+     * Valid return value for doEndTag().
+     */
+
+    public final static int EVAL_PAGE = 6;
+
+    // Setters for Tag handler data
+
+
+    /**
+     * Set the current page context.
+     * This method is invoked by the JSP page implementation object
+     * prior to doStartTag().
+     * <p>
+     * This value is *not* reset by doEndTag() and must be explicitly reset
+     * by a page implementation if it changes between calls to doStartTag().
+     *
+     * @param pc The page context for this tag handler.
+     */
+
+    void setPageContext(PageContext pc);
+
+
+    /**
+     * Set the parent (closest enclosing tag handler) of this tag handler.
+     * Invoked by the JSP page implementation object prior to doStartTag().
+     * <p>
+     * This value is *not* reset by doEndTag() and must be explicitly reset
+     * by a page implementation.
+     *
+     * @param t The parent tag, or null.
+     */
+
+
+    void setParent(Tag t);
+
+
+    /**
+     * Get the parent (closest enclosing tag handler) for this tag handler.
+     *
+     * <p>
+     * The getParent() method can be used to navigate the nested tag
+     * handler structure at runtime for cooperation among custom actions;
+     * for example, the findAncestorWithClass() method in TagSupport
+     * provides a convenient way of doing this.
+     *
+     * <p>
+     * The current version of the specification only provides one formal
+     * way of indicating the observable type of a tag handler: its
+     * tag handler implementation class, described in the tag-class
+     * subelement of the tag element.  This is extended in an
+     * informal manner by allowing the tag library author to
+     * indicate in the description subelement an observable type.
+     * The type should be a subtype of the tag handler implementation
+     * class or void.
+     * This addititional constraint can be exploited by a
+     * specialized container that knows about that specific tag library,
+     * as in the case of the JSP standard tag library.
+     *
+     * @return the current parent, or null if none.
+     * @see TagSupport#findAncestorWithClass
+     */
+
+    Tag getParent();
+
+
+    // Actions for basic start/end processing.
+
+
+    /**
+     * Process the start tag for this instance.
+     * This method is invoked by the JSP page implementation object.
+     *
+     * <p>
+     * The doStartTag method assumes that the properties pageContext and
+     * parent have been set. It also assumes that any properties exposed as
+     * attributes have been set too.  When this method is invoked, the body
+     * has not yet been evaluated.
+     *
+     * <p>
+     * This method returns Tag.EVAL_BODY_INCLUDE or
+     * BodyTag.EVAL_BODY_BUFFERED to indicate
+     * that the body of the action should be evaluated or SKIP_BODY to
+     * indicate otherwise.
+     *
+     * <p>
+     * When a Tag returns EVAL_BODY_INCLUDE the result of evaluating
+     * the body (if any) is included into the current "out" JspWriter as it
+     * happens and then doEndTag() is invoked.
+     *
+     * <p>
+     * BodyTag.EVAL_BODY_BUFFERED is only valid  if the tag handler
+     * implements BodyTag.
+     *
+     * <p>
+     * The JSP container will resynchronize the values of any AT_BEGIN and
+     * NESTED variables (defined by the associated TagExtraInfo or TLD)
+     * after the invocation of doStartTag(), except for a tag handler
+     * implementing BodyTag whose doStartTag() method returns
+     * BodyTag.EVAL_BODY_BUFFERED.
+     *
+     * @return EVAL_BODY_INCLUDE if the tag wants to process body, SKIP_BODY 
+     *     if it does not want to process it.
+     * @throws JspException if an error occurred while processing this tag
+     * @see BodyTag
+     */
+ 
+    int doStartTag() throws JspException;
+ 
+
+    /**
+     * Process the end tag for this instance.
+     * This method is invoked by the JSP page implementation object
+     * on all Tag handlers.
+     *
+     * <p>
+     * This method will be called after returning from doStartTag. The
+     * body of the action may or may not have been evaluated, depending on
+     * the return value of doStartTag.
+     *
+     * <p>
+     * If this method returns EVAL_PAGE, the rest of the page continues
+     * to be evaluated.  If this method returns SKIP_PAGE, the rest of
+     * the page is not evaluated, the request is completed, and 
+     * the doEndTag() methods of enclosing tags are not invoked.  If this
+     * request was forwarded or included from another page (or Servlet),
+     * only the current page evaluation is stopped.
+     *
+     * <p>
+     * The JSP container will resynchronize the values of any AT_BEGIN and
+     * AT_END variables (defined by the associated TagExtraInfo or TLD)
+     * after the invocation of doEndTag().
+     *
+     * @return indication of whether to continue evaluating the JSP page.
+     * @throws JspException if an error occurred while processing this tag
+     */
+
+    int doEndTag() throws JspException;
+
+    /**
+     * Called on a Tag handler to release state.
+     * The page compiler guarantees that JSP page implementation
+     * objects will invoke this method on all tag handlers,
+     * but there may be multiple invocations on doStartTag and doEndTag in between.
+     */
+
+    void release();
+
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagAdapter.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagAdapter.java
new file mode 100644
index 0000000..d325f93
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagAdapter.java
@@ -0,0 +1,158 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+package javax.servlet.jsp.tagext;
+
+import javax.servlet.jsp.*;
+
+
+/**
+ * Wraps any SimpleTag and exposes it using a Tag interface.  This is used
+ * to allow collaboration between classic Tag handlers and SimpleTag
+ * handlers.
+ * <p>
+ * Because SimpleTag does not extend Tag, and because Tag.setParent()
+ * only accepts a Tag instance, a classic tag handler (one
+ * that implements Tag) cannot have a SimpleTag as its parent.  To remedy
+ * this, a TagAdapter is created to wrap the SimpleTag parent, and the
+ * adapter is passed to setParent() instead.  A classic Tag Handler can
+ * call getAdaptee() to retrieve the encapsulated SimpleTag instance.
+ *
+ * @since 2.0
+ */
+public class TagAdapter 
+    implements Tag
+{
+    /** The simple tag that's being adapted. */
+    private SimpleTag simpleTagAdaptee;
+
+    /** The parent, of this tag, converted (if necessary) to be of type Tag. */
+    private Tag parent;
+
+    // Flag indicating whether we have already determined the parent
+    private boolean parentDetermined;
+
+    /**
+     * Creates a new TagAdapter that wraps the given SimpleTag and 
+     * returns the parent tag when getParent() is called.
+     *
+     * @param adaptee The SimpleTag being adapted as a Tag.
+     */
+    public TagAdapter( SimpleTag adaptee ) {
+        if( adaptee == null ) {
+	    // Cannot wrap a null adaptee.
+	    throw new IllegalArgumentException();
+        }
+        this.simpleTagAdaptee = adaptee;
+    }
+    
+    /**
+     * Must not be called.
+     *
+     * @param pc ignored.
+     * @throws UnsupportedOperationException Must not be called
+     */
+    public void setPageContext(PageContext pc) {
+        throw new UnsupportedOperationException( 
+            "Illegal to invoke setPageContext() on TagAdapter wrapper" );
+    }
+
+
+    /**
+     * Must not be called.  The parent of this tag is always 
+     * getAdaptee().getParent().
+     *
+     * @param parentTag ignored.
+     * @throws UnsupportedOperationException Must not be called.
+     */
+    public void setParent( Tag parentTag ) {
+        throw new UnsupportedOperationException( 
+            "Illegal to invoke setParent() on TagAdapter wrapper" );
+    }
+
+
+    /**
+     * Returns the parent of this tag, which is always
+     * getAdaptee().getParent().  
+     *
+     * This will either be the enclosing Tag (if getAdaptee().getParent()
+     * implements Tag), or an adapter to the enclosing Tag (if 
+     * getAdaptee().getParent() does not implement Tag).
+     *
+     * @return The parent of the tag being adapted.
+     */
+    public Tag getParent() {
+	if (!parentDetermined) {
+	    JspTag adapteeParent = simpleTagAdaptee.getParent();
+	    if (adapteeParent != null) {
+		if (adapteeParent instanceof Tag) {
+		    this.parent = (Tag) adapteeParent;
+		} else {
+		    // Must be SimpleTag - no other types defined.
+		    this.parent = new TagAdapter((SimpleTag) adapteeParent);
+		}
+	    }
+	    parentDetermined = true;
+	}
+
+	return this.parent;
+    }
+    
+    /**
+     * Gets the tag that is being adapted to the Tag interface.
+     * This should be an instance of SimpleTag in JSP 2.0, but room
+     * is left for other kinds of tags in future spec versions.
+     *
+     * @return the tag that is being adapted
+     */
+    public JspTag getAdaptee() {
+        return this.simpleTagAdaptee;
+    }
+
+    /**
+     * Must not be called.
+     *
+     * @return always throws UnsupportedOperationException
+     * @throws UnsupportedOperationException Must not be called
+     * @throws JspException never thrown
+     */
+    public int doStartTag() throws JspException {
+        throw new UnsupportedOperationException( 
+            "Illegal to invoke doStartTag() on TagAdapter wrapper" );
+    }
+ 
+    /**
+     * Must not be called.
+     *
+     * @return always throws UnsupportedOperationException
+     * @throws UnsupportedOperationException Must not be called
+     * @throws JspException never thrown
+     */
+    public int doEndTag() throws JspException {
+        throw new UnsupportedOperationException( 
+            "Illegal to invoke doEndTag() on TagAdapter wrapper" );
+    }
+
+    /**
+     * Must not be called.
+     *
+     * @throws UnsupportedOperationException Must not be called
+     */
+    public void release() {
+        throw new UnsupportedOperationException( 
+            "Illegal to invoke release() on TagAdapter wrapper" );
+    }
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagAttributeInfo.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagAttributeInfo.java
new file mode 100644
index 0000000..314f7d3
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagAttributeInfo.java
@@ -0,0 +1,177 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+ 
+package javax.servlet.jsp.tagext;
+
+/**
+ * Information on the attributes of a Tag, available at translation time.
+ * This class is instantiated from the Tag Library Descriptor file (TLD).
+ *
+ * <p>
+ * Only the information needed to generate code is included here.  Other information
+ * like SCHEMA for validation belongs elsewhere.
+ */
+
+public class TagAttributeInfo {
+    /**
+     * "id" is wired in to be ID.  There is no real benefit in having it be something else
+     * IDREFs are not handled any differently.
+     */
+
+    public static final String ID = "id";
+
+    /**
+     * Constructor for TagAttributeInfo.
+     * This class is to be instantiated only from the
+     * TagLibrary code under request from some JSP code that is parsing a
+     * TLD (Tag Library Descriptor).
+     *
+     * @param name The name of the attribute.
+     * @param required If this attribute is required in tag instances.
+     * @param type The name of the type of the attribute.
+     * @param reqTime Whether this attribute holds a request-time Attribute.
+     */
+
+    public TagAttributeInfo(String name, boolean required,
+                            String type, boolean reqTime)
+    {
+	this.name = name;
+        this.required = required;
+        this.type = type;
+	this.reqTime = reqTime;
+    }
+
+    /**
+     * JSP 2.0 Constructor for TagAttributeInfo.
+     * This class is to be instantiated only from the
+     * TagLibrary code under request from some JSP code that is parsing a
+     * TLD (Tag Library Descriptor).
+     *
+     * @param name The name of the attribute.
+     * @param required If this attribute is required in tag instances.
+     * @param type The name of the type of the attribute.
+     * @param reqTime Whether this attribute holds a request-time Attribute.
+     * @param fragment Whether this attribute is of type JspFragment
+     *
+     * @since 2.0
+     */
+
+    public TagAttributeInfo(String name, boolean required,
+                            String type, boolean reqTime,
+			    boolean fragment)
+    {
+	this( name, required, type, reqTime );
+	this.fragment = fragment;
+    }
+
+    /**
+     * The name of this attribute.
+     *
+     * @return the name of the attribute
+     */
+
+    public String getName() {
+	return name;
+    }
+
+    /**
+     * The type (as a String) of this attribute.
+     *
+     * @return the type of the attribute
+     */
+
+    public String getTypeName() {
+	return type;
+    }
+
+    /**
+     * Whether this attribute can hold a request-time value.
+     *
+     * @return if the attribute can hold a request-time value.
+     */
+
+    public boolean canBeRequestTime() {
+	return reqTime;
+    }
+
+    /**
+     * Whether this attribute is required.
+     *
+     * @return if the attribute is required.
+     */
+    public boolean isRequired() {
+        return required;
+    }
+
+    /**
+     * Convenience static method that goes through an array of TagAttributeInfo
+     * objects and looks for "id".
+     *
+     * @param a An array of TagAttributeInfo
+     * @return The TagAttributeInfo reference with name "id"
+     */
+    public static TagAttributeInfo getIdAttribute(TagAttributeInfo a[]) {
+	for (int i=0; i<a.length; i++) {
+	    if (a[i].getName().equals(ID)) {
+		return a[i];
+	    }
+	}
+	return null;		// no such attribute
+    }
+
+    /**
+     * Whether this attribute is of type JspFragment.
+     *
+     * @return if the attribute is of type JspFragment
+     *
+     * @since 2.0
+     */
+    public boolean isFragment() {
+	return fragment;
+    }
+
+
+    
+    /**
+     * Returns a String representation of this TagAttributeInfo, suitable
+     * for debugging purposes.
+     *
+     * @return a String representation of this TagAttributeInfo
+     */
+    public String toString() {
+        StringBuffer b = new StringBuffer();
+        b.append("name = "+name+" ");
+        b.append("type = "+type+" ");
+	b.append("reqTime = "+reqTime+" ");
+        b.append("required = "+required+" ");
+        b.append("fragment = "+fragment+" ");
+        return b.toString();
+    }
+
+    /*
+     * private fields
+     */
+    private String name;
+    private String type;
+    private boolean reqTime;
+    private boolean required;
+
+    /*
+     * private fields for JSP 2.0
+     */
+    private boolean fragment;
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagData.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagData.java
new file mode 100644
index 0000000..58003ea
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagData.java
@@ -0,0 +1,153 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.jsp.tagext;
+
+import java.util.Hashtable;
+
+/**
+ * The (translation-time only) attribute/value information for a tag instance.
+ *
+ * <p>
+ * TagData is only used as an argument to the isValid, validate, and 
+ * getVariableInfo methods of TagExtraInfo, which are invoked at 
+ * translation time.
+ */
+
+public class TagData implements Cloneable {
+
+    /**
+     * Distinguished value for an attribute to indicate its value
+     * is a request-time expression (which is not yet available because
+     * TagData instances are used at translation-time).
+     */
+
+    public static final Object REQUEST_TIME_VALUE = new Object();
+
+
+    /**
+     * Constructor for TagData.
+     *
+     * <p>
+     * A typical constructor may be
+     * <pre>
+     * static final Object[][] att = {{"connection", "conn0"}, {"id", "query0"}};
+     * static final TagData td = new TagData(att);
+     * </pre>
+     *
+     * All values must be Strings except for those holding the
+     * distinguished object REQUEST_TIME_VALUE.
+
+     * @param atts the static attribute and values.  May be null.
+     */
+    public TagData(Object[] atts[]) {
+	if (atts == null) {
+	    attributes = new Hashtable();
+	} else {
+	    attributes = new Hashtable(atts.length);
+	}
+
+	if (atts != null) {
+	    for (int i = 0; i < atts.length; i++) {
+		attributes.put(atts[i][0], atts[i][1]);
+	    }
+	}
+    }
+
+    /**
+     * Constructor for a TagData.
+     *
+     * If you already have the attributes in a hashtable, use this
+     * constructor. 
+     *
+     * @param attrs A hashtable to get the values from.
+     */
+    public TagData(Hashtable attrs) {
+        this.attributes = attrs;
+    }
+
+    /**
+     * The value of the tag's id attribute.
+     *
+     * @return the value of the tag's id attribute, or null if no such
+     *     attribute was specified.
+     */
+
+    public String getId() {
+	return getAttributeString(TagAttributeInfo.ID);
+    }
+
+    /**
+     * The value of the attribute.
+     * If a static value is specified for an attribute that accepts a
+     * request-time attribute expression then that static value is returned,
+     * even if the value is provided in the body of a <jsp:attribute> action.
+     * The distinguished object REQUEST_TIME_VALUE is only returned if
+     * the value is specified as a request-time attribute expression
+     * or via the &lt;jsp:attribute&gt; action with a body that contains
+     * dynamic content (scriptlets, scripting expressions, EL expressions, 
+     * standard actions, or custom actions).  Returns null if the attribute 
+     * is not set. 
+     *
+     * @param attName the name of the attribute
+     * @return the attribute's value
+     */
+
+    public Object getAttribute(String attName) {
+	return attributes.get(attName);
+    }
+
+    /**
+     * Set the value of an attribute.
+     *
+     * @param attName the name of the attribute
+     * @param value the value.
+     */
+    public void setAttribute(String attName,
+			     Object value) {
+	attributes.put(attName, value);
+    }
+
+    /**
+     * Get the value for a given attribute.
+     *
+     * @param attName the name of the attribute
+     * @return the attribute value string
+     * @throws ClassCastException if attribute value is not a String
+     */
+
+    public String getAttributeString(String attName) {
+	Object o = attributes.get(attName);
+	if (o == null) {
+	    return null;
+	} else {
+	    return (String) o;
+	}	
+    }
+
+    /**
+     * Enumerates the attributes.
+     *
+     *@return An enumeration of the attributes in a TagData
+     */
+    public java.util.Enumeration getAttributes() {
+        return attributes.keys();
+    };
+
+    // private data
+
+    private Hashtable attributes;	// the tagname/value map
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagExtraInfo.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagExtraInfo.java
new file mode 100644
index 0000000..78858f1
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagExtraInfo.java
@@ -0,0 +1,143 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+ 
+package javax.servlet.jsp.tagext;
+
+/**
+ * Optional class provided by the tag library author to describe additional
+ * translation-time information not described in the TLD.
+ * The TagExtraInfo class is mentioned in the Tag Library Descriptor file (TLD).
+ *
+ * <p>
+ * This class can be used:
+ * <ul>
+ * <li> to indicate that the tag defines scripting variables
+ * <li> to perform translation-time validation of the tag attributes.
+ * </ul>
+ *
+ * <p>
+ * It is the responsibility of the JSP translator that the initial value
+ * to be returned by calls to getTagInfo() corresponds to a TagInfo
+ * object for the tag being translated. If an explicit call to
+ * setTagInfo() is done, then the object passed will be returned in
+ * subsequent calls to getTagInfo().
+ * 
+ * <p>
+ * The only way to affect the value returned by getTagInfo()
+ * is through a setTagInfo() call, and thus, TagExtraInfo.setTagInfo() is
+ * to be called by the JSP translator, with a TagInfo object that
+ * corresponds to the tag being translated. The call should happen before
+ * any invocation on validate() and before any invocation on
+ * getVariableInfo().
+ *
+ * <p>
+ * <tt>NOTE:</tt> It is a (translation time) error for a tag definition
+ * in a TLD with one or more variable subelements to have an associated
+ * TagExtraInfo implementation that returns a VariableInfo array with
+ * one or more elements from a call to getVariableInfo().
+ */
+
+public abstract class TagExtraInfo {
+
+    /**
+     * Sole constructor. (For invocation by subclass constructors, 
+     * typically implicit.)
+     */
+    public TagExtraInfo() {
+    }
+    
+    /**
+     * information on scripting variables defined by the tag associated with
+     * this TagExtraInfo instance.
+     * Request-time attributes are indicated as such in the TagData parameter.
+     *
+     * @param data The TagData instance.
+     * @return An array of VariableInfo data, or null or a zero length array
+     *         if no scripting variables are to be defined.
+     */
+    public VariableInfo[] getVariableInfo(TagData data) {
+	return ZERO_VARIABLE_INFO;
+    }
+
+    /**
+     * Translation-time validation of the attributes. 
+     * Request-time attributes are indicated as such in the TagData parameter.
+     * Note that the preferred way to do validation is with the validate()
+     * method, since it can return more detailed information.
+     *
+     * @param data The TagData instance.
+     * @return Whether this tag instance is valid.
+     * @see TagExtraInfo#validate
+     */
+
+    public boolean isValid(TagData data) {
+	return true;
+    }
+
+    /**
+     * Translation-time validation of the attributes.
+     * Request-time attributes are indicated as such in the TagData parameter.
+     * Because of the higher quality validation messages possible, 
+     * this is the preferred way to do validation (although isValid() 
+     * still works).  
+     * 
+     * <p>JSP 2.0 and higher containers call validate() instead of isValid().
+     * The default implementation of this method is to call isValid().  If 
+     * isValid() returns false, a generic ValidationMessage[] is returned
+     * indicating isValid() returned false.</p>
+     *
+     * @param data The TagData instance.
+     * @return A null object, or zero length array if no errors, an 
+     *     array of ValidationMessages otherwise.
+     * @since 2.0
+     */
+    public ValidationMessage[] validate( TagData data ) {
+	ValidationMessage[] result = null;
+
+	if( !isValid( data ) ) {
+	    result = new ValidationMessage[] {
+		new ValidationMessage( data.getId(), "isValid() == false" ) };
+	}
+
+	return result;
+    }
+
+    /**
+     * Set the TagInfo for this class.
+     *
+     * @param tagInfo The TagInfo this instance is extending
+     */
+    public final void setTagInfo(TagInfo tagInfo) {
+	this.tagInfo = tagInfo;
+    }
+
+    /**
+     * Get the TagInfo for this class.
+     *
+     * @return the taginfo instance this instance is extending
+     */
+    public final TagInfo getTagInfo() {
+	return tagInfo;
+    }
+    
+    // private data
+    private TagInfo tagInfo;
+
+    // zero length VariableInfo array
+    private static final VariableInfo[] ZERO_VARIABLE_INFO = { };
+}
+
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagFileInfo.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagFileInfo.java
new file mode 100644
index 0000000..4a93d02
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagFileInfo.java
@@ -0,0 +1,85 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+ 
+package javax.servlet.jsp.tagext;
+
+/**
+ * Tag information for a tag file in a Tag Library;
+ * This class is instantiated from the Tag Library Descriptor file (TLD)
+ * and is available only at translation time.
+ *
+ * @since 2.0
+ */
+public class TagFileInfo {
+
+    /**
+     * Constructor for TagFileInfo from data in the JSP 2.0 format for TLD.
+     * This class is to be instantiated only from the TagLibrary code
+     * under request from some JSP code that is parsing a
+     * TLD (Tag Library Descriptor).
+     *
+     * Note that, since TagLibibraryInfo reflects both TLD information
+     * and taglib directive information, a TagFileInfo instance is
+     * dependent on a taglib directive.  This is probably a
+     * design error, which may be fixed in the future.
+     *
+     * @param name The unique action name of this tag
+     * @param path Where to find the .tag file implementing this 
+     *     action, relative to the location of the TLD file.
+     * @param tagInfo The detailed information about this tag, as parsed
+     *     from the directives in the tag file.
+     */
+    public TagFileInfo( String name, String path, TagInfo tagInfo ) {
+        this.name = name;
+        this.path = path;
+        this.tagInfo = tagInfo;
+    }
+
+    /**
+     * The unique action name of this tag.
+     *
+     * @return The (short) name of the tag.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Where to find the .tag file implementing this action.
+     *
+     * @return The path of the tag file, relative to the TLD, or "." if 
+     *     the tag file was defined in an implicit tag file.
+     */
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * Returns information about this tag, parsed from the directives 
+     * in the tag file.
+     *
+     * @return a TagInfo object containing information about this tag
+     */
+    public TagInfo getTagInfo() {
+        return tagInfo;
+    }
+
+    // private fields for 2.0 info
+    private String name;
+    private String path;
+    private TagInfo tagInfo;
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagInfo.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagInfo.java
new file mode 100644
index 0000000..e4d341f
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagInfo.java
@@ -0,0 +1,446 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+ 
+package javax.servlet.jsp.tagext;
+
+/**
+ * Tag information for a tag in a Tag Library;
+ * This class is instantiated from the Tag Library Descriptor file (TLD)
+ * and is available only at translation time.
+ *
+ * 
+*/
+
+public class TagInfo {
+
+    /**
+     * Static constant for getBodyContent() when it is JSP.
+     */
+
+    public static final String BODY_CONTENT_JSP = "JSP";
+
+    /**
+     * Static constant for getBodyContent() when it is Tag dependent.
+     */
+
+    public static final String BODY_CONTENT_TAG_DEPENDENT = "TAGDEPENDENT";
+
+
+    /**
+     * Static constant for getBodyContent() when it is empty.
+     */
+
+    public static final String BODY_CONTENT_EMPTY = "EMPTY";
+    
+    /**
+     * Static constant for getBodyContent() when it is scriptless.
+     * 
+     * @since 2.0
+     */ 
+    public static final String BODY_CONTENT_SCRIPTLESS = "SCRIPTLESS";
+
+    /**
+     * Constructor for TagInfo from data in the JSP 1.1 format for TLD.
+     * This class is to be instantiated only from the TagLibrary code
+     * under request from some JSP code that is parsing a
+     * TLD (Tag Library Descriptor).
+     *
+     * Note that, since TagLibibraryInfo reflects both TLD information
+     * and taglib directive information, a TagInfo instance is
+     * dependent on a taglib directive.  This is probably a
+     * design error, which may be fixed in the future.
+     *
+     * @param tagName The name of this tag
+     * @param tagClassName The name of the tag handler class
+     * @param bodycontent Information on the body content of these tags
+     * @param infoString The (optional) string information for this tag
+     * @param taglib The instance of the tag library that contains us.
+     * @param tagExtraInfo The instance providing extra Tag info.  May be null
+     * @param attributeInfo An array of AttributeInfo data from descriptor.
+     * May be null;
+     *
+     */
+    public TagInfo(String tagName,
+	    String tagClassName,
+	    String bodycontent,
+	    String infoString,
+	    TagLibraryInfo taglib,
+	    TagExtraInfo tagExtraInfo,
+	    TagAttributeInfo[] attributeInfo) {
+	this.tagName       = tagName;
+	this.tagClassName  = tagClassName;
+	this.bodyContent   = bodycontent;
+	this.infoString    = infoString;
+	this.tagLibrary    = taglib;
+	this.tagExtraInfo  = tagExtraInfo;
+	this.attributeInfo = attributeInfo;
+
+	if (tagExtraInfo != null)
+            tagExtraInfo.setTagInfo(this);
+    }
+			 
+    /**
+     * Constructor for TagInfo from data in the JSP 1.2 format for TLD.
+     * This class is to be instantiated only from the TagLibrary code
+     * under request from some JSP code that is parsing a
+     * TLD (Tag Library Descriptor).
+     *
+     * Note that, since TagLibibraryInfo reflects both TLD information
+     * and taglib directive information, a TagInfo instance is
+     * dependent on a taglib directive.  This is probably a
+     * design error, which may be fixed in the future.
+     *
+     * @param tagName The name of this tag
+     * @param tagClassName The name of the tag handler class
+     * @param bodycontent Information on the body content of these tags
+     * @param infoString The (optional) string information for this tag
+     * @param taglib The instance of the tag library that contains us.
+     * @param tagExtraInfo The instance providing extra Tag info.  May be null
+     * @param attributeInfo An array of AttributeInfo data from descriptor.
+     * May be null;
+     * @param displayName A short name to be displayed by tools
+     * @param smallIcon Path to a small icon to be displayed by tools
+     * @param largeIcon Path to a large icon to be displayed by tools
+     * @param tvi An array of a TagVariableInfo (or null)
+     */
+    public TagInfo(String tagName,
+	    String tagClassName,
+	    String bodycontent,
+	    String infoString,
+	    TagLibraryInfo taglib,
+	    TagExtraInfo tagExtraInfo,
+	    TagAttributeInfo[] attributeInfo,
+	    String displayName,
+	    String smallIcon,
+	    String largeIcon,
+	    TagVariableInfo[] tvi) {
+	this.tagName       = tagName;
+	this.tagClassName  = tagClassName;
+	this.bodyContent   = bodycontent;
+	this.infoString    = infoString;
+	this.tagLibrary    = taglib;
+	this.tagExtraInfo  = tagExtraInfo;
+	this.attributeInfo = attributeInfo;
+	this.displayName = displayName;
+	this.smallIcon = smallIcon;
+	this.largeIcon = largeIcon;
+	this.tagVariableInfo = tvi;
+
+	if (tagExtraInfo != null)
+            tagExtraInfo.setTagInfo(this);
+    }
+			 
+    /**
+     * Constructor for TagInfo from data in the JSP 2.0 format for TLD.
+     * This class is to be instantiated only from the TagLibrary code
+     * under request from some JSP code that is parsing a
+     * TLD (Tag Library Descriptor).
+     *
+     * Note that, since TagLibibraryInfo reflects both TLD information
+     * and taglib directive information, a TagInfo instance is
+     * dependent on a taglib directive.  This is probably a
+     * design error, which may be fixed in the future.
+     *
+     * @param tagName The name of this tag
+     * @param tagClassName The name of the tag handler class
+     * @param bodycontent Information on the body content of these tags
+     * @param infoString The (optional) string information for this tag
+     * @param taglib The instance of the tag library that contains us.
+     * @param tagExtraInfo The instance providing extra Tag info.  May be null
+     * @param attributeInfo An array of AttributeInfo data from descriptor.
+     * May be null;
+     * @param displayName A short name to be displayed by tools
+     * @param smallIcon Path to a small icon to be displayed by tools
+     * @param largeIcon Path to a large icon to be displayed by tools
+     * @param tvi An array of a TagVariableInfo (or null)
+     * @param dynamicAttributes True if supports dynamic attributes
+     *
+     * @since 2.0
+     */
+    public TagInfo(String tagName,
+            String tagClassName,
+            String bodycontent,
+            String infoString,
+            TagLibraryInfo taglib,
+            TagExtraInfo tagExtraInfo,
+            TagAttributeInfo[] attributeInfo,
+            String displayName,
+            String smallIcon,
+            String largeIcon,
+            TagVariableInfo[] tvi,
+            boolean dynamicAttributes) {
+        this.tagName       = tagName;
+        this.tagClassName  = tagClassName;
+        this.bodyContent   = bodycontent;
+        this.infoString    = infoString;
+        this.tagLibrary    = taglib;
+        this.tagExtraInfo  = tagExtraInfo;
+        this.attributeInfo = attributeInfo;
+        this.displayName = displayName;
+        this.smallIcon = smallIcon;
+        this.largeIcon = largeIcon;
+        this.tagVariableInfo = tvi;
+        this.dynamicAttributes = dynamicAttributes;
+
+        if (tagExtraInfo != null)
+            tagExtraInfo.setTagInfo(this);
+    }
+
+    /**
+     * The name of the Tag.
+     *
+     * @return The (short) name of the tag.
+     */
+
+    public String getTagName() {
+	return tagName;
+    }
+
+    /**
+     * Attribute information (in the TLD) on this tag.
+     * The return is an array describing the attributes of this tag, as
+     * indicated in the TLD.
+     *
+     * @return The array of TagAttributeInfo for this tag, or a
+     *         zero-length array if the tag has no attributes.
+     */
+
+   public TagAttributeInfo[] getAttributes() {
+       return attributeInfo;
+   }
+
+    /**
+     * Information on the scripting objects created by this tag at runtime.
+     * This is a convenience method on the associated TagExtraInfo class.
+     *
+     * @param data TagData describing this action.
+     * @return if a TagExtraInfo object is associated with this TagInfo, the
+     *     result of getTagExtraInfo().getVariableInfo( data ), otherwise
+     *     null.
+     */
+   public VariableInfo[] getVariableInfo(TagData data) {
+       VariableInfo[] result = null;
+       TagExtraInfo tei = getTagExtraInfo();
+       if (tei != null) {
+	   result = tei.getVariableInfo( data );
+       }
+       return result;
+   }
+
+    /**
+     * Translation-time validation of the attributes. 
+     * This is a convenience method on the associated TagExtraInfo class.
+     *
+     * @param data The translation-time TagData instance.
+     * @return Whether the data is valid.
+     */
+    public boolean isValid(TagData data) {
+        TagExtraInfo tei = getTagExtraInfo();
+        if (tei == null) {
+	    return true;
+        }
+        return tei.isValid(data);
+    }
+
+    /**
+     * Translation-time validation of the attributes.
+     * This is a convenience method on the associated TagExtraInfo class.
+     *
+     * @param data The translation-time TagData instance.
+     * @return A null object, or zero length array if no errors, an
+     *     array of ValidationMessages otherwise.
+     * @since 2.0
+     */
+    public ValidationMessage[] validate( TagData data ) {
+	TagExtraInfo tei = getTagExtraInfo();
+	if( tei == null ) {
+	    return null;
+	}
+	return tei.validate( data );
+    }
+
+    /**
+     * Set the instance for extra tag information.
+     * 
+     * @param tei the TagExtraInfo instance
+     */
+    public void setTagExtraInfo(TagExtraInfo tei) {
+	tagExtraInfo = tei;
+    }
+
+
+    /**
+     * The instance (if any) for extra tag information.
+     * 
+     * @return The TagExtraInfo instance, if any.
+     */
+    public TagExtraInfo getTagExtraInfo() {
+	return tagExtraInfo;
+    }
+
+
+    /**
+     * Name of the class that provides the handler for this tag.
+     *
+     * @return The name of the tag handler class.
+     */
+    
+    public String getTagClassName() {
+	return tagClassName;
+    }
+
+
+    /**
+     * The bodycontent information for this tag.
+     * If the bodycontent is not defined for this
+     * tag, the default of JSP will be returned.
+     *
+     * @return the body content string.
+     */
+
+    public String getBodyContent() {
+	return bodyContent;
+    }
+
+
+    /**
+     * The information string for the tag.
+     *
+     * @return the info string, or null if 
+     *         not defined
+     */
+
+    public String getInfoString() {
+	return infoString;
+    }
+
+
+    /**
+     * Set the TagLibraryInfo property.
+     *
+     * Note that a TagLibraryInfo element is dependent
+     * not just on the TLD information but also on the
+     * specific taglib instance used.  This means that
+     * a fair amount of work needs to be done to construct
+     * and initialize TagLib objects.
+     *
+     * If used carefully, this setter can be used to avoid having to
+     * create new TagInfo elements for each taglib directive.
+     *
+     * @param tl the TagLibraryInfo to assign
+     */
+
+    public void setTagLibrary(TagLibraryInfo tl) {
+	tagLibrary = tl;
+    }
+
+    /**
+     * The instance of TabLibraryInfo we belong to.
+     *
+     * @return the tag library instance we belong to
+     */
+
+    public TagLibraryInfo getTagLibrary() {
+	return tagLibrary;
+    }
+
+
+    // ============== JSP 2.0 TLD Information ========
+
+
+    /**
+     * Get the displayName.
+     *
+     * @return A short name to be displayed by tools,
+     *         or null if not defined
+     */
+
+    public String getDisplayName() {
+	return displayName;
+    }
+
+    /**
+     * Get the path to the small icon.
+     *
+     * @return Path to a small icon to be displayed by tools,
+     *         or null if not defined
+     */
+
+    public String getSmallIcon() {
+	return smallIcon;
+    }
+
+    /**
+     * Get the path to the large icon.
+     *
+     * @return Path to a large icon to be displayed by tools,
+     *         or null if not defined
+     */
+
+    public String getLargeIcon() {
+	return largeIcon;
+    }
+
+    /**
+     * Get TagVariableInfo objects associated with this TagInfo.
+     *
+     * @return Array of TagVariableInfo objects corresponding to
+     *         variables declared by this tag, or a zero length
+     *         array if no variables have been declared
+     */
+
+    public TagVariableInfo[] getTagVariableInfos() {
+	return tagVariableInfo;
+    }
+
+
+    // ============== JSP 2.0 TLD Information ========
+
+    /**
+     * Get dynamicAttributes associated with this TagInfo.
+     *
+     * @return True if tag handler supports dynamic attributes
+     * @since 2.0
+     */
+    public boolean hasDynamicAttributes() {
+        return dynamicAttributes;
+    }
+
+    /*
+     * private fields for 1.1 info
+     */
+    private String             tagName; // the name of the tag
+    private String             tagClassName;
+    private String             bodyContent;
+    private String             infoString;
+    private TagLibraryInfo     tagLibrary;
+    private TagExtraInfo       tagExtraInfo; // instance of TagExtraInfo
+    private TagAttributeInfo[] attributeInfo;
+
+    /*
+     * private fields for 1.2 info
+     */
+    private String             displayName;
+    private String             smallIcon;
+    private String             largeIcon;
+    private TagVariableInfo[]  tagVariableInfo;
+
+    /*
+     * Additional private fields for 2.0 info
+     */
+    private boolean dynamicAttributes;
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagLibraryInfo.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagLibraryInfo.java
new file mode 100644
index 0000000..8a548bf
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagLibraryInfo.java
@@ -0,0 +1,286 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+package javax.servlet.jsp.tagext;
+
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagFileInfo;
+
+/**
+ * Translation-time information associated with a taglib directive, and its
+ * underlying TLD file.
+ *
+ * Most of the information is directly from the TLD, except for
+ * the prefix and the uri values used in the taglib directive
+ *
+ *
+ */
+
+abstract public class TagLibraryInfo {
+
+    /**
+     * Constructor.
+     *
+     * This will invoke the constructors for TagInfo, and TagAttributeInfo
+     * after parsing the TLD file.
+     *
+     * @param prefix the prefix actually used by the taglib directive
+     * @param uri the URI actually used by the taglib directive
+     */
+    protected TagLibraryInfo(String prefix, String uri) {
+	this.prefix = prefix;
+	this.uri    = uri;
+    }
+
+    // ==== methods accessing taglib information =======
+
+    /**
+     * The value of the uri attribute from the taglib directive for 
+     * this library.
+     *
+     * @return the value of the uri attribute
+     */
+   
+    public String getURI() {
+        return uri;
+    }
+
+    /**
+     * The prefix assigned to this taglib from the taglib directive
+     *
+     * @return the prefix assigned to this taglib from the taglib directive
+     */
+
+    public String getPrefixString() {
+	return prefix;
+    }
+
+    // ==== methods using the TLD data =======
+
+    /**
+     * The preferred short name (prefix) as indicated in the TLD.
+     * This may be used by authoring tools as the preferred prefix
+     * to use when creating an taglib directive for this library.
+     *
+     * @return the preferred short name for the library
+     */
+    public String getShortName() {
+        return shortname;
+    }
+
+    /**
+     * The "reliable" URN indicated in the TLD (the uri element).
+     * This may be used by authoring tools as a global identifier
+     * to use when creating a taglib directive for this library.
+     *
+     * @return a reliable URN to a TLD like this
+     */
+    public String getReliableURN() {
+        return urn;
+    }
+
+
+    /**
+     * Information (documentation) for this TLD.
+     *
+     * @return the info string for this tag lib
+     */
+   
+    public String getInfoString() {
+        return info;
+    }
+
+
+    /**
+     * A string describing the required version of the JSP container.
+     * 
+     * @return the (minimal) required version of the JSP container.
+     * @see javax.servlet.jsp.JspEngineInfo
+     */
+   
+    public String getRequiredVersion() {
+        return jspversion;
+    }
+
+
+    /**
+     * An array describing the tags that are defined in this tag library.
+     *
+     * @return the TagInfo objects corresponding to the tags defined by this
+     *         tag library, or a zero length array if this tag library
+     *         defines no tags
+     */
+    public TagInfo[] getTags() {
+        return tags;
+    }
+
+    /**
+     * An array describing the tag files that are defined in this tag library.
+     *
+     * @return the TagFileInfo objects corresponding to the tag files defined
+     *         by this tag library, or a zero length array if this
+     *         tag library defines no tags files
+     * @since 2.0
+     */
+    public TagFileInfo[] getTagFiles() {
+        return tagFiles;
+    }
+
+
+    /**
+     * Get the TagInfo for a given tag name, looking through all the
+     * tags in this tag library.
+     *
+     * @param shortname The short name (no prefix) of the tag
+     * @return the TagInfo for the tag with the specified short name, or
+     *         null if no such tag is found
+     */
+
+    public TagInfo getTag(String shortname) {
+        TagInfo tags[] = getTags();
+
+        if (tags == null || tags.length == 0) {
+            return null;
+        }
+
+        for (int i=0; i < tags.length; i++) {
+            if (tags[i].getTagName().equals(shortname)) {
+                return tags[i];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Get the TagFileInfo for a given tag name, looking through all the
+     * tag files in this tag library.
+     *
+     * @param shortname The short name (no prefix) of the tag
+     * @return the TagFileInfo for the specified Tag file, or null
+     *         if no Tag file is found
+     * @since 2.0
+     */
+    public TagFileInfo getTagFile(String shortname) {
+        TagFileInfo tagFiles[] = getTagFiles();
+
+        if (tagFiles == null || tagFiles.length == 0) {
+            return null;
+        }
+
+        for (int i=0; i < tagFiles.length; i++) {
+            if (tagFiles[i].getName().equals(shortname)) {
+                return tagFiles[i];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * An array describing the functions that are defined in this tag library.
+     *
+     * @return the functions defined in this tag library, or a zero
+     *         length array if the tag library defines no functions.
+     * @since 2.0
+     */
+    public FunctionInfo[] getFunctions() {
+        return functions;
+    }
+
+
+    /**
+     * Get the FunctionInfo for a given function name, looking through all the
+     * functions in this tag library.
+     *
+     * @param name The name (no prefix) of the function
+     * @return the FunctionInfo for the function with the given name, or null
+     *         if no such function exists
+     * @since 2.0
+     */
+    public FunctionInfo getFunction(String name) {
+
+        if (functions == null || functions.length == 0) {
+            System.err.println("No functions");
+            return null;
+        }
+
+        for (int i=0; i < functions.length; i++) {
+            if (functions[i].getName().equals(name)) {
+                return functions[i];
+            }
+        }
+        return null;
+    }
+
+
+    // Protected fields
+
+    /**
+     * The prefix assigned to this taglib from the taglib directive.
+     */
+    protected String        prefix;
+    
+    /**
+     * The value of the uri attribute from the taglib directive for 
+     * this library.
+     */
+    protected String        uri;
+    
+    /**
+     * An array describing the tags that are defined in this tag library.
+     */
+    protected TagInfo[]     tags;
+    
+    /**
+     * An array describing the tag files that are defined in this tag library.
+     *
+     * @since 2.0
+     */
+    protected TagFileInfo[] tagFiles;
+    
+    /**
+     * An array describing the functions that are defined in this tag library.
+     *
+     * @since 2.0
+     */
+    protected FunctionInfo[] functions;
+
+    // Tag Library Data
+    
+    /**
+     * The version of the tag library.
+     */
+    protected String tlibversion; // required
+    
+    /**
+     * The version of the JSP specification this tag library is written to.
+     */
+    protected String jspversion;  // required
+    
+    /**
+     * The preferred short name (prefix) as indicated in the TLD.
+     */
+    protected String shortname;   // required
+    
+    /**
+     * The "reliable" URN indicated in the TLD.
+     */
+    protected String urn;         // required
+    
+    /**
+     * Information (documentation) for this TLD.
+     */
+    protected String info;        // optional
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagLibraryValidator.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagLibraryValidator.java
new file mode 100644
index 0000000..41acb81
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagLibraryValidator.java
@@ -0,0 +1,143 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.jsp.tagext;
+
+import java.util.Map;
+
+/**
+ * Translation-time validator class for a JSP page. 
+ * A validator operates on the XML view associated with the JSP page.
+ *
+ * <p>
+ * The TLD file associates a TagLibraryValidator class and some init
+ * arguments with a tag library.
+ *
+ * <p>
+ * The JSP container is reponsible for locating an appropriate
+ * instance of the appropriate subclass by
+ *
+ * <ul>
+ * <li> new a fresh instance, or reuse an available one
+ * <li> invoke the setInitParams(Map) method on the instance
+ * </ul>
+ *
+ * once initialized, the validate(String, String, PageData) method will
+ * be invoked, where the first two arguments are the prefix
+ * and uri for this tag library in the XML View.  The prefix is intended
+ * to make it easier to produce an error message.  However, it is not
+ * always accurate.  In the case where a single URI is mapped to more 
+ * than one prefix in the XML view, the prefix of the first URI is provided.
+ * Therefore, to provide high quality error messages in cases where the 
+ * tag elements themselves are checked, the prefix parameter should be 
+ * ignored and the actual prefix of the element should be used instead.  
+ * TagLibraryValidators should always use the uri to identify elements 
+ * as beloning to the tag library, not the prefix.
+ *
+ * <p>
+ * A TagLibraryValidator instance
+ * may create auxiliary objects internally to perform
+ * the validation (e.g. an XSchema validator) and may reuse it for all
+ * the pages in a given translation run.
+ *
+ * <p>
+ * The JSP container is not guaranteed to serialize invocations of
+ * validate() method, and TagLibraryValidators should perform any
+ * synchronization they may require.
+ *
+ * <p>
+ * As of JSP 2.0, a JSP container must provide a jsp:id attribute to
+ * provide higher quality validation errors.
+ * The container will track the JSP pages
+ * as passed to the container, and will assign to each element
+ * a unique "id", which is passed as the value of the jsp:id
+ * attribute.  Each XML element in the XML view available will
+ * be extended with this attribute.  The TagLibraryValidator
+ * can then use the attribute in one or more ValidationMessage
+ * objects.  The container then, in turn, can use these
+ * values to provide more precise information on the location
+ * of an error.
+ *
+ * <p>
+ * The actual prefix of the <code>id</code> attribute may or may not be 
+ * <code>jsp</code> but it will always map to the namespace
+ * <code>http://java.sun.com/JSP/Page</code>.  A TagLibraryValidator
+ * implementation must rely on the uri, not the prefix, of the <code>id</code>
+ * attribute.
+ */
+
+abstract public class TagLibraryValidator {
+
+    /**
+     * Sole constructor. (For invocation by subclass constructors, 
+     * typically implicit.)
+     */
+    public TagLibraryValidator() {
+    }
+    
+    /**
+     * Set the init data in the TLD for this validator.
+     * Parameter names are keys, and parameter values are the values.
+     *
+     * @param map A Map describing the init parameters
+     */
+    public void setInitParameters(Map map) {
+	initParameters = map;
+    }
+
+
+    /**
+     * Get the init parameters data as an immutable Map.
+     * Parameter names are keys, and parameter values are the values.
+     *
+     * @return The init parameters as an immutable map.
+     */
+    public Map getInitParameters() {
+	return initParameters;
+    }
+
+    /**
+     * Validate a JSP page.
+     * This will get invoked once per unique tag library URI in the
+     * XML view.  This method will return null if the page is valid; otherwise
+     * the method should return an array of ValidationMessage objects.
+     * An array of length zero is also interpreted as no errors.
+     *
+     * @param prefix the first prefix with which the tag library is 
+     *     associated, in the XML view.  Note that some tags may use 
+     *     a different prefix if the namespace is redefined.
+     * @param uri the tag library's unique identifier
+     * @param page the JspData page object
+     * @return A null object, or zero length array if no errors, an array
+     * of ValidationMessages otherwise.
+     */
+    public ValidationMessage[] validate(String prefix, String uri, 
+        PageData page) 
+    {
+	return null;
+    }
+
+    /**
+     * Release any data kept by this instance for validation purposes.
+     */
+    public void release() {
+	initParameters = null;
+    }
+
+    // Private data
+    private Map initParameters;
+
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagSupport.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagSupport.java
new file mode 100644
index 0000000..1e6ca3c
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagSupport.java
@@ -0,0 +1,293 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.jsp.tagext;
+
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.PageContext;
+
+/**
+ * A base class for defining new tag handlers implementing Tag.
+ *
+ * <p> The TagSupport class is a utility class intended to be used as
+ * the base class for new tag handlers.  The TagSupport class
+ * implements the Tag and IterationTag interfaces and adds additional
+ * convenience methods including getter methods for the properties in
+ * Tag.  TagSupport has one static method that is included to
+ * facilitate coordination among cooperating tags.
+ *
+ * <p> Many tag handlers will extend TagSupport and only redefine a
+ * few methods. 
+ */
+
+public class TagSupport implements IterationTag, Serializable {
+
+    /**
+     * Find the instance of a given class type that is closest to a given
+     * instance.
+     * This method uses the getParent method from the Tag
+     * interface.
+     * This method is used for coordination among cooperating tags.
+     *
+     * <p>
+     * The current version of the specification only provides one formal
+     * way of indicating the observable type of a tag handler: its
+     * tag handler implementation class, described in the tag-class
+     * subelement of the tag element.  This is extended in an
+     * informal manner by allowing the tag library author to
+     * indicate in the description subelement an observable type.
+     * The type should be a subtype of the tag handler implementation
+     * class or void.
+     * This addititional constraint can be exploited by a
+     * specialized container that knows about that specific tag library,
+     * as in the case of the JSP standard tag library.
+     *
+     * <p>
+     * When a tag library author provides information on the
+     * observable type of a tag handler, client programmatic code
+     * should adhere to that constraint.  Specifically, the Class
+     * passed to findAncestorWithClass should be a subtype of the
+     * observable type.
+     * 
+     *
+     * @param from The instance from where to start looking.
+     * @param klass The subclass of Tag or interface to be matched
+     * @return the nearest ancestor that implements the interface
+     * or is an instance of the class specified
+     */
+
+    public static final Tag findAncestorWithClass(Tag from, Class klass) {
+	boolean isInterface = false;
+
+	if (from == null ||
+	    klass == null ||
+	    (!Tag.class.isAssignableFrom(klass) &&
+	     !(isInterface = klass.isInterface()))) {
+	    return null;
+	}
+
+	for (;;) {
+	    Tag tag = from.getParent();
+
+	    if (tag == null) {
+		return null;
+	    }
+
+	    if ((isInterface && klass.isInstance(tag)) ||
+	        klass.isAssignableFrom(tag.getClass()))
+		return tag;
+	    else
+		from = tag;
+	}
+    }
+
+    /**
+     * Default constructor, all subclasses are required to define only
+     * a public constructor with the same signature, and to call the
+     * superclass constructor.
+     *
+     * This constructor is called by the code generated by the JSP
+     * translator.
+     */
+
+    public TagSupport() { }
+
+    /**
+     * Default processing of the start tag, returning SKIP_BODY.
+     *
+     * @return SKIP_BODY
+     * @throws JspException if an error occurs while processing this tag
+     *
+     * @see Tag#doStartTag()
+     */
+ 
+    public int doStartTag() throws JspException {
+        return SKIP_BODY;
+    }
+
+    /**
+     * Default processing of the end tag returning EVAL_PAGE.
+     *
+     * @return EVAL_PAGE
+     * @throws JspException if an error occurs while processing this tag
+     *
+     * @see Tag#doEndTag()
+     */
+
+    public int doEndTag() throws JspException {
+	return EVAL_PAGE;
+    }
+
+
+    /**
+     * Default processing for a body.
+     *
+     * @return SKIP_BODY
+     * @throws JspException if an error occurs while processing this tag
+     *
+     * @see IterationTag#doAfterBody()
+     */
+    
+    public int doAfterBody() throws JspException {
+	return SKIP_BODY;
+    }
+
+    // Actions related to body evaluation
+
+
+    /**
+     * Release state.
+     *
+     * @see Tag#release()
+     */
+
+    public void release() {
+	parent = null;
+	id = null;
+	if( values != null ) {
+	    values.clear();
+	}
+	values = null;
+    }
+
+    /**
+     * Set the nesting tag of this tag.
+     *
+     * @param t The parent Tag.
+     * @see Tag#setParent(Tag)
+     */
+
+    public void setParent(Tag t) {
+	parent = t;
+    }
+
+    /**
+     * The Tag instance most closely enclosing this tag instance.
+     * @see Tag#getParent()
+     *
+     * @return the parent tag instance or null
+     */
+
+    public Tag getParent() {
+	return parent;
+    }
+
+    /**
+     * Set the id attribute for this tag.
+     *
+     * @param id The String for the id.
+     */
+
+    public void setId(String id) {
+	this.id = id;
+    }
+
+    /**
+     * The value of the id attribute of this tag; or null.
+     *
+     * @return the value of the id attribute, or null
+     */
+    
+    public String getId() {
+	return id;
+    }
+
+    /**
+     * Set the page context.
+     *
+     * @param pageContext The PageContext.
+     * @see Tag#setPageContext
+     */
+
+    public void setPageContext(PageContext pageContext) {
+	this.pageContext = pageContext;
+    }
+
+    /**
+     * Associate a value with a String key.
+     *
+     * @param k The key String.
+     * @param o The value to associate.
+     */
+
+    public void setValue(String k, Object o) {
+	if (values == null) {
+	    values = new Hashtable();
+	}
+	values.put(k, o);
+    }
+
+    /**
+     * Get a the value associated with a key.
+     *
+     * @param k The string key.
+     * @return The value associated with the key, or null.
+     */
+
+    public Object getValue(String k) {
+	if (values == null) {
+	    return null;
+	} else {
+	    return values.get(k);
+	}
+    }
+
+    /**
+     * Remove a value associated with a key.
+     *
+     * @param k The string key.
+     */
+
+    public void removeValue(String k) {
+	if (values != null) {
+	    values.remove(k);
+	}
+    }
+
+    /**
+     * Enumerate the keys for the values kept by this tag handler.
+     *
+     * @return An enumeration of all the keys for the values set,
+     *     or null or an empty Enumeration if no values have been set.
+     */
+
+    public Enumeration getValues() {
+	if (values == null) {
+	    return null;
+	}
+	return values.keys();
+    }
+
+    // private fields
+
+    private   Tag         parent;
+    private   Hashtable   values;
+    /**
+     * The value of the id attribute of this tag; or null.
+     */
+    protected String	  id;
+
+    // protected fields
+
+    /**
+     * The PageContext.
+     */
+    protected PageContext pageContext;
+}
+
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagVariableInfo.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagVariableInfo.java
new file mode 100644
index 0000000..ba1759f
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TagVariableInfo.java
@@ -0,0 +1,120 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+ 
+package javax.servlet.jsp.tagext;
+
+/**
+ * Variable information for a tag in a Tag Library;
+ * This class is instantiated from the Tag Library Descriptor file (TLD)
+ * and is available only at translation time.
+ *
+ * This object should be immutable.
+ *
+ * This information is only available in JSP 1.2 format TLDs or above.
+ */
+
+public class TagVariableInfo {
+
+    /**
+     * Constructor for TagVariableInfo.
+     *
+     * @param nameGiven value of &lt;name-given&gt;
+     * @param nameFromAttribute value of &lt;name-from-attribute&gt;
+     * @param className value of &lt;variable-class&gt;
+     * @param declare value of &lt;declare&gt;
+     * @param scope value of &lt;scope&gt;
+     */
+    public TagVariableInfo(
+	    String nameGiven,
+	    String nameFromAttribute,
+	    String className,
+	    boolean declare,
+	    int scope) {
+	this.nameGiven         = nameGiven;
+	this.nameFromAttribute = nameFromAttribute;
+	this.className         = className;
+	this.declare           = declare;
+	this.scope             = scope;
+    }
+
+    /**
+     * The body of the &lt;name-given&gt; element.
+     *
+     * @return The variable name as a constant
+     */
+
+    public String getNameGiven() {
+	return nameGiven;
+    }
+
+    /**
+     * The body of the &lt;name-from-attribute&gt; element.
+     * This is the name of an attribute whose (translation-time)
+     * value will give the name of the variable.  One of
+     * &lt;name-given&gt; or &lt;name-from-attribute&gt; is required.
+     *
+     * @return The attribute whose value defines the variable name
+     */
+
+    public String getNameFromAttribute() {
+	return nameFromAttribute;
+    }
+
+    /**
+     * The body of the &lt;variable-class&gt; element.  
+     *
+     * @return The name of the class of the variable or
+     *         'java.lang.String' if not defined in the TLD.
+     */
+
+    public String getClassName() {
+	return className;
+    }
+
+    /**
+     * The body of the &lt;declare&gt; element.
+     *
+     * @return Whether the variable is to be declared or not.
+     *         If not defined in the TLD, 'true' will be returned.
+     */
+
+    public boolean getDeclare() {
+	return declare;
+    }
+
+    /**
+     * The body of the &lt;scope&gt; element.
+     *
+     * @return The scope to give the variable.  NESTED
+     *         scope will be returned if not defined in 
+     *         the TLD.
+     */
+
+    public int getScope() {
+	return scope;
+    }
+
+
+    /*
+     * private fields
+     */
+    private String   nameGiven;         // <name-given>
+    private String   nameFromAttribute; // <name-from-attribute>
+    private String   className;         // <class>
+    private boolean  declare;           // <declare>
+    private int      scope;             // <scope>
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TryCatchFinally.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TryCatchFinally.java
new file mode 100644
index 0000000..cd849eb
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/TryCatchFinally.java
@@ -0,0 +1,98 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+ 
+package javax.servlet.jsp.tagext;
+
+
+
+/**
+ * The auxiliary interface of a Tag, IterationTag or BodyTag tag
+ * handler that wants additional hooks for managing resources.
+ *
+ * <p>This interface provides two new methods: doCatch(Throwable)
+ * and doFinally().  The prototypical invocation is as follows:
+ *
+ * <pre>
+ * h = get a Tag();  // get a tag handler, perhaps from pool
+ *
+ * h.setPageContext(pc);  // initialize as desired
+ * h.setParent(null);
+ * h.setFoo("foo");
+ * 
+ * // tag invocation protocol; see Tag.java
+ * try {
+ *   doStartTag()...
+ *   ....
+ *   doEndTag()...
+ * } catch (Throwable t) {
+ *   // react to exceptional condition
+ *   h.doCatch(t);
+ * } finally {
+ *   // restore data invariants and release per-invocation resources
+ *   h.doFinally();
+ * }
+ * 
+ * ... other invocations perhaps with some new setters
+ * ...
+ * h.release();  // release long-term resources
+ * </pre>
+ */
+
+public interface TryCatchFinally {
+
+    /**
+     * Invoked if a Throwable occurs while evaluating the BODY
+     * inside a tag or in any of the following methods:
+     * Tag.doStartTag(), Tag.doEndTag(),
+     * IterationTag.doAfterBody() and BodyTag.doInitBody().
+     *
+     * <p>This method is not invoked if the Throwable occurs during
+     * one of the setter methods.
+     *
+     * <p>This method may throw an exception (the same or a new one)
+     * that will be propagated further up the nest chain.  If an exception
+     * is thrown, doFinally() will be invoked.
+     *
+     * <p>This method is intended to be used to respond to an exceptional
+     * condition.
+     *
+     * @param t The throwable exception navigating through this tag.
+     * @throws Throwable if the exception is to be rethrown further up 
+     *     the nest chain.
+     */
+ 
+    void doCatch(Throwable t) throws Throwable;
+
+    /**
+     * Invoked in all cases after doEndTag() for any class implementing
+     * Tag, IterationTag or BodyTag.  This method is invoked even if
+     * an exception has occurred in the BODY of the tag,
+     * or in any of the following methods:
+     * Tag.doStartTag(), Tag.doEndTag(),
+     * IterationTag.doAfterBody() and BodyTag.doInitBody().
+     *
+     * <p>This method is not invoked if the Throwable occurs during
+     * one of the setter methods.
+     *
+     * <p>This method should not throw an Exception.
+     *
+     * <p>This method is intended to maintain per-invocation data
+     * integrity and resource management actions.
+     */
+
+    void doFinally();
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/ValidationMessage.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/ValidationMessage.java
new file mode 100644
index 0000000..c544553
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/ValidationMessage.java
@@ -0,0 +1,85 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package javax.servlet.jsp.tagext;
+
+
+/**
+ * A validation message from either TagLibraryValidator or TagExtraInfo.
+ * <p>
+ * As of JSP 2.0, a JSP container must support a jsp:id attribute
+ * to provide higher quality validation errors.
+ * The container will track the JSP pages
+ * as passed to the container, and will assign to each element
+ * a unique "id", which is passed as the value of the jsp:id
+ * attribute.  Each XML element in the XML view available will
+ * be extended with this attribute.  The TagLibraryValidator
+ * can then use the attribute in one or more ValidationMessage
+ * objects.  The container then, in turn, can use these
+ * values to provide more precise information on the location
+ * of an error.
+ *  
+ * <p>
+ * The actual prefix of the <code>id</code> attribute may or may not be 
+ * <code>jsp</code> but it will always map to the namespace
+ * <code>http://java.sun.com/JSP/Page</code>.  A TagLibraryValidator
+ * implementation must rely on the uri, not the prefix, of the <code>id</code>
+ * attribute.
+ */
+
+public class ValidationMessage {
+
+    /**
+     * Create a ValidationMessage.  The message String should be
+     * non-null.  The value of id may be null, if the message
+     * is not specific to any XML element, or if no jsp:id
+     * attributes were passed on.  If non-null, the value of
+     * id must be the value of a jsp:id attribute for the PageData
+     * passed into the validate() method.
+     *
+     * @param id Either null, or the value of a jsp:id attribute.
+     * @param message A localized validation message.
+     */
+    public ValidationMessage(String id, String message) {
+	this.id = id;
+	this.message = message;
+    }
+
+
+    /**
+     * Get the jsp:id.
+     * Null means that there is no information available.
+     *
+     * @return The jsp:id information.
+     */
+    public String getId() {
+	return id;
+    }
+
+    /**
+     * Get the localized validation message.
+     *
+     * @return A validation message
+     */
+    public String getMessage(){
+	return message;
+    }
+
+    // Private data
+    private String id;
+    private String message;
+}
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/VariableInfo.java b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/VariableInfo.java
new file mode 100644
index 0000000..1cf6d11
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/VariableInfo.java
@@ -0,0 +1,283 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+ 
+package javax.servlet.jsp.tagext;
+
+/**
+ * Information on the scripting variables that are created/modified by
+ * a tag (at run-time). This information is provided by TagExtraInfo
+ * classes and it is used by the translation phase of JSP.
+ *
+ * <p>
+ * Scripting variables generated by a custom action have an associated 
+ * scope of either AT_BEGIN, NESTED, or AT_END.
+ *
+ * <p>
+ * The class name (VariableInfo.getClassName) in the returned objects
+ * is used to determine the types of the scripting variables.
+ * Note that because scripting variables are assigned their values
+ * from scoped attributes which cannot be of primitive types,
+ * &quot;boxed&quot; types such as <code>java.lang.Integer</code> must 
+ * be used instead of primitives.
+ *
+ * <p>
+ * The class name may be a Fully Qualified Class Name, or a short
+ * class name.
+ *
+ * <p>
+ * If a Fully Qualified Class Name is provided, it should refer to a
+ * class that should be in the CLASSPATH for the Web Application (see
+ * Servlet 2.4 specification - essentially it is WEB-INF/lib and
+ * WEB-INF/classes). Failure to be so will lead to a translation-time
+ * error.
+ *
+ * <p>
+ * If a short class name is given in the VariableInfo objects, then
+ * the class name must be that of a public class in the context of the
+ * import directives of the page where the custom action appears. 
+ * The class must also be in the CLASSPATH for the Web Application 
+ * (see Servlet 2.4 specification - essentially it is WEB-INF/lib and
+ * WEB-INF/classes). Failure to be so will lead to a translation-time
+ * error.
+ *
+ * <p><B>Usage Comments</B>
+ * <p>
+ * Frequently a fully qualified class name will refer to a class that
+ * is known to the tag library and thus, delivered in the same JAR
+ * file as the tag handlers. In most other remaining cases it will
+ * refer to a class that is in the platform on which the JSP processor
+ * is built (like J2EE). Using fully qualified class names in this
+ * manner makes the usage relatively resistant to configuration
+ * errors.
+ *
+ * <p>
+ * A short name is usually generated by the tag library based on some
+ * attributes passed through from the custom action user (the author),
+ * and it is thus less robust: for instance a missing import directive
+ * in the referring JSP page will lead to an invalid short name class
+ * and a translation error.
+ *
+ * <p><B>Synchronization Protocol</B>
+ *
+ * <p>
+ * The result of the invocation on getVariableInfo is an array of
+ * VariableInfo objects.  Each such object describes a scripting
+ * variable by providing its name, its type, whether the variable is
+ * new or not, and what its scope is.  Scope is best described through
+ * a picture:
+ *
+ * <p>
+ * <IMG src="doc-files/VariableInfo-1.gif"
+ *      alt="NESTED, AT_BEGIN and AT_END Variable Scopes"/>
+ *
+ *<p>
+ * The JSP 2.0 specification defines the interpretation of 3 values:
+ * 
+ * <ul>
+ * <li> NESTED, if the scripting variable is available between
+ * the start tag and the end tag of the action that defines it.
+ * <li>
+ * AT_BEGIN, if the scripting variable is available from the start tag
+ * of the action that defines it until the end of the scope.
+ * <li> AT_END, if the scripting variable is available after the end tag
+ * of the action that defines it until the end of the scope.
+ * </ul>
+ *
+ * The scope value for a variable implies what methods may affect its
+ * value and thus where synchronization is needed as illustrated by
+ * the table below.  <b>Note:</b> the synchronization of the variable(s)
+ * will occur <em>after</em> the respective method has been called.
+ *
+ * <blockquote>
+ * <table cellpadding="2" cellspacing="2" border="0" width="55%"
+ *        bgcolor="#999999" summary="Variable Synchronization Points">
+ * <tbody>
+ *   <tr align="center">
+ *     <td valign="top" colspan="6" bgcolor="#999999"><u><b>Variable Synchronization
+ *     Points</b></u><br>
+ *     </td>
+ *   </tr>
+ *   <tr>
+ *     <th valign="top" bgcolor="#c0c0c0">&nbsp;</th>
+ *     <th valign="top" bgcolor="#c0c0c0" align="center">doStartTag()</th>
+ *     <th valign="top" bgcolor="#c0c0c0" align="center">doInitBody()</th>
+ *     <th valign="top" bgcolor="#c0c0c0" align="center">doAfterBody()</th>
+ *     <th valign="top" bgcolor="#c0c0c0" align="center">doEndTag()</th>
+ *     <th valign="top" bgcolor="#c0c0c0" align="center">doTag()</th>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top" bgcolor="#c0c0c0"><b>Tag<br>
+ *     </b></td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, NESTED<br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, AT_END<br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top" bgcolor="#c0c0c0"><b>IterationTag<br>
+ *     </b></td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, NESTED<br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, NESTED<br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, AT_END<br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top" bgcolor="#c0c0c0"><b>BodyTag<br>
+ *     </b></td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, NESTED<sup>1</sup><br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, NESTED<sup>1</sup><br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, NESTED<br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, AT_END<br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top" bgcolor="#c0c0c0"><b>SimpleTag<br>
+ *     </b></td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff"><br>
+ *     </td>
+ *     <td valign="top" align="center" bgcolor="#ffffff">AT_BEGIN, AT_END<br>
+ *     </td>
+ *   </tr>
+ * </tbody>
+ * </table>
+ * <sup>1</sup> Called after <code>doStartTag()</code> if 
+ * <code>EVAL_BODY_INCLUDE</code> is returned, or after 
+ * <code>doInitBody()</code> otherwise.
+ * </blockquote>
+ *
+ * <p><B>Variable Information in the TLD</B>
+ * <p>
+ * Scripting variable information can also be encoded directly for most cases
+ * into the Tag Library Descriptor using the &lt;variable&gt; subelement of the
+ * &lt;tag&gt; element.  See the JSP specification.
+ */
+
+public class VariableInfo {
+
+    /**
+     * Scope information that scripting variable is visible only within the
+     * start/end tags.
+     */
+    public static final int NESTED = 0;
+
+    /**
+     * Scope information that scripting variable is visible after start tag.
+     */
+    public static final int AT_BEGIN = 1;
+
+    /**
+     * Scope information that scripting variable is visible after end tag.
+     */
+    public static final int AT_END = 2;
+
+
+    /**
+     * Constructor
+     * These objects can be created (at translation time) by the TagExtraInfo
+     * instances.
+     *
+     * @param varName The name of the scripting variable
+     * @param className The type of this variable
+     * @param declare If true, it is a new variable (in some languages this will
+     *     require a declaration)
+     * @param scope Indication on the lexical scope of the variable
+     */
+
+    public VariableInfo(String varName,
+			String className,
+			boolean declare,
+			int scope) {
+	this.varName = varName;
+	this.className = className;
+	this.declare = declare;
+	this.scope = scope;
+    }
+
+    // Accessor methods
+    
+    /**
+     * Returns the name of the scripting variable.
+     *
+     * @return the name of the scripting variable
+     */
+    public String getVarName() { 
+        return varName; 
+    }
+    
+    /**
+     * Returns the type of this variable.
+     *
+     * @return the type of this variable
+     */
+    public String getClassName() { 
+        return className; 
+    }
+    
+    /**
+     * Returns whether this is a new variable.
+     * If so, in some languages this will require a declaration.
+     *
+     * @return whether this is a new variable.
+     */
+    public boolean getDeclare() { 
+        return declare; 
+    }
+    
+    /**
+     * Returns the lexical scope of the variable.
+     * 
+     * @return the lexical scope of the variable, either AT_BEGIN, AT_END,
+     *    or NESTED.
+     * @see #AT_BEGIN
+     * @see #AT_END
+     * @see #NESTED
+     */
+    public int getScope() { 
+        return scope; 
+    }
+
+
+    // == private data
+    private String varName;
+    private String className;
+    private boolean declare;
+    private int scope;
+}
+
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/BodyTagProtocol.gif b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/BodyTagProtocol.gif
new file mode 100644
index 0000000..a61e82b
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/BodyTagProtocol.gif
Binary files differ
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/IterationTagProtocol.gif b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/IterationTagProtocol.gif
new file mode 100644
index 0000000..c262341
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/IterationTagProtocol.gif
Binary files differ
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/TagProtocol.gif b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/TagProtocol.gif
new file mode 100644
index 0000000..9a1880e
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/TagProtocol.gif
Binary files differ
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/VariableInfo-1.gif b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/VariableInfo-1.gif
new file mode 100644
index 0000000..32eabeb
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/doc-files/VariableInfo-1.gif
Binary files differ
diff --git a/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/package.html b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/package.html
new file mode 100644
index 0000000..e74afc8
--- /dev/null
+++ b/servletapi/jsr152/src/share/javax/servlet/jsp/tagext/package.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<!--
+  - The Apache Software License, Version 1.1
+  -
+  - Copyright (c) 1999 The Apache Software Foundation.  All rights 
+  - reserved.
+  -
+  - Redistribution and use in source and binary forms, with or without
+  - modification, are permitted provided that the following conditions
+  - are met:
+  -
+  - 1. Redistributions of source code must retain the above copyright
+  -    notice, this list of conditions and the following disclaimer. 
+  -
+  - 2. Redistributions in binary form must reproduce the above copyright
+  -    notice, this list of conditions and the following disclaimer in
+  -    the documentation and/or other materials provided with the
+  -    distribution.
+  -
+  - 3. The end-user documentation included with the redistribution, if
+  -    any, must include the following acknowlegement:  
+  -       "This product includes software developed by the 
+  -        Apache Software Foundation (http://www.apache.org/)."
+  -    Alternately, this acknowlegement may appear in the software itself,
+  -    if and wherever such third-party acknowlegements normally appear.
+  -
+  - 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
+  -    Foundation" must not be used to endorse or promote products derived
+  -    from this software without prior written permission. For written 
+  -    permission, please contact apache@apache.org.
+  -
+  - 5. Products derived from this software may not be called "Apache"
+  -    nor may "Apache" appear in their names without prior written
+  -    permission of the Apache Group.
+  -
+  - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+  - WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+  - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  - DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+  - ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+  - USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+  - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+  - OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  - SUCH DAMAGE.
+  - ====================================================================
+  -
+  - This software consists of voluntary contributions made by many
+  - individuals on behalf of the Apache Software Foundation.  For more
+  - information on the Apache Software Foundation, please see
+  - <http://www.apache.org/>.
+  -
+  -->
+</head>
+<body bgcolor="white">
+
+Classes and interfaces for the definition of JavaServer Pages Tag Libraries.
+
+<p>
+The JavaServer Pages(tm) (JSP) 2.0 specification provides a portable
+mechanism for the description of tag libraries.
+<p>
+A JSP tag library contains
+<ul>
+<li>A Tag Library Descriptor</li>
+<li>A number of Tag Files or Tag handler classes defining 
+    request-time behavior</li>
+<li>Additional classes and resources used at runtime</li>
+<li>Possibly some additional classes to provide extra translation 
+    information</li>
+</ul>
+<p>
+The JSP 2.0 specification and the reference implementation both contain
+simple and moderately complex examples of actions defined using this
+mechanism.  These are available at JSP's web site, at
+<a href="http://java.sun.com/products/jsp">http://java.sun.com/products/jsp</a>.
+Some readers may want to consult those to get a quick feel for how
+the mechanisms work together.
+
+</body>
+</html>
diff --git a/servletapi/jsr154/.cvsignore b/servletapi/jsr154/.cvsignore
new file mode 100644
index 0000000..dbc1574
--- /dev/null
+++ b/servletapi/jsr154/.cvsignore
@@ -0,0 +1,4 @@
+build
+build.properties.sample
+dist
+lib
diff --git a/servletapi/jsr154/BUILDING.txt b/servletapi/jsr154/BUILDING.txt
new file mode 100644
index 0000000..fd46c59
--- /dev/null
+++ b/servletapi/jsr154/BUILDING.txt
@@ -0,0 +1,134 @@
+$Id$
+
+
+               Building The Java Servlet and JSP API Classes
+               =============================================
+
+This subproject contains the source code for the implementation classes of the
+Java Servlet and JSP APIs (packages javax.servlet, javax.servlet.http,
+javax.servlet.jsp, and javax.servlet.jsp.tagext).  In order to build these
+sources successfully, you must do the following:
+
+
+(1) Download and Install the Ant Binary Distribution
+
+NOTE:  These instructions assume that you are using the Ant 1.3 release.
+Procedures for Ant 1.4 and later versions should be similar, but have not
+been tested.
+
+* Download a binary distribution of Ant 1.3 from:
+
+    http://jakarta.apache.org/builds/jakarta-ant/release/v1.3/bin/
+
+  On a Windows platform, you will need:
+    jakarta-ant-1.3-bin.zip
+    jakarta-ant-1.3-optional.jar
+
+  On a Unix platform, you will need:
+    jakarta-ant-1.3-bin.tar.gz
+    jakarta-ant-1.3-optional.jar
+
+* Unpack the binary distribution into a convenient location so that the
+  Ant release resides in its own directory (conventionally named
+  "jakarta-ant-1.3").  For the purposes of the remainder of this document,
+  the symbolic name "${ant.home}" is used to refer to the full pathname of
+  the release directory.
+
+* Copy the file "jakarta-ant-1.3-optional.jar", downloaded above, into
+  the directory "${ant.home}/lib".  This makes available several Ant
+  extension commands that are commonly required when building Jakarta
+  based projects.
+
+* Modify the PATH environment variable to include directory
+  "${ant.home}/bin" in its list.  This makes the "ant" command line script
+  available, which will be used to actually perform the build.
+
+
+(2) Download and Install the JAXP/1.1 Reference Implementation (OPTIONAL)
+
+NOTE:  Although this step is not required to build this particular subproject,
+it is commonly required to build other Jakarta projects.  Hence, the steps
+required are documented here.
+
+* Download a binary distribution of JAXP 1.1 (Final Version) from:
+
+    http://java.sun.com/xml/download.html
+
+* Unpack the binary distribution into a convenient location so that the
+  JAXP/1.1 release resides in its own directory (conventionally named
+  "jaxp-1.1".  For the purposes of the remainder of this document, the
+  symbolic name "${jaxp.home}" is used to refer to the full pathname of
+  the release directory.
+
+* Make the JAR files of this distribution ("crimson.jar", "jaxp.jar", and
+  "xalan.jar") available for use by performing ONE of the following options:
+
+  - Remove the existing "jaxp.jar" and "parser.jar" files found in the
+    "${ant.home}/lib" directory, and copy these JAR files into the
+    "${ant.home}/lib" directory (prefered option).
+
+  - Add these files to your CLASSPATH environment variable.
+
+
+(3) Download and Install Subproject Source Code
+
+* Use Anonymous CVS (as described on the Jakarta web site at
+  <http://jakarta.apache.org/site/cvsindex.html>, or
+  download a source distribution from:
+
+    http://jakarta.apache.org/builds/jakarta-servletapi-4/nightly/src/
+
+  On a Windows platform, you will need:
+    jakarta-servletapi-4-src-YYYYMMDD.zip
+
+  On a Unix platform, you will need:
+    jakarta-servletapi-4-src-YYYYMMDD.zip
+
+  (Alternatively, you can download the Servlet API source distribution
+  from the same directory as you find a released version of Tomcat 4.
+  Such distributions will contain exactly the Servlet API classes used
+  to build the "servlet.jar" file inside that Tomcat distribution.)
+
+* Unpack the source distribution into a convenient location so that the
+  distribution resides in its own directory (conventionally named
+  "jakarta-servletapi-4").  For the purposes of the remainder of this document,
+  the symbolic name "${servletapi.source}" is used to refer to the full
+  pathname of the release directory.
+
+
+(4) Customize Build Properties For This Subproject
+
+Most Jakarta subprojects allow you to customize Ant properties (with default
+values defined in the "build.xml" file.  This is done by creating a text file
+named "build.properties" in the source distribution directory (for property
+definitions local to this subproject) and/or your user home directory (for
+property definitions shared across subprojects).
+
+The "jakarta-servletapi-4" subproject does not define any customizable
+build properties.
+
+
+(5) Build A Binary Distribution
+
+Open a command line shell, and issue the following commands:
+
+  cd ${servletapi.source}
+  ant -projecthelp
+
+If everything is installed correctly, you should see a list of the Ant
+"targets" that represent different commands you might wish to build.  By
+convention, the "dist" target creates a complete binary distribution.  To
+execute it, type the following commands:
+
+  cd ${servletapi.source}
+  ant dist
+
+This will create a complete binary distribution of the subproject (equivalent
+in structure to the corresponding binary distribution downloadable from the
+Jakarta web site), in the "${servletapi.source}/dist" directory.  It will have
+the contents described in the corresponding "README.txt" file.
+
+The file most commonly required by other projects will be the "servlet.jar"
+file, found in "${servletapi.source}/lib/servlet.jar".  Make a note of the
+full pathname to this file, because you will need it when customizing build
+properties for other Jakarta subprojects that depend on these classes.
diff --git a/servletapi/jsr154/LICENSE b/servletapi/jsr154/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/servletapi/jsr154/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/servletapi/jsr154/README.txt b/servletapi/jsr154/README.txt
new file mode 100644
index 0000000..e4b8328
--- /dev/null
+++ b/servletapi/jsr154/README.txt
@@ -0,0 +1,28 @@
+$Id$
+
+                      Java Servlet and JSP API Classes
+                      ================================
+
+This subproject contains the compiled code for the implementation classes of
+the Java Servlet and JSP APIs (packages javax.servlet, javax.servlet.http,
+javax.servlet.jsp, and javax.servlet.jsp.tagext).  It includes the following
+contents:
+
+
+  BUILDING.txt                Instructions for building from sources
+  LICENSE                     Apache Software License for this release
+  README.txt                  This document
+  docs/                       Documentation for this release
+      api/                    Javadocs for Servlet and JSP API classes
+  lib/                        Binary JAR files for this release
+      servlet.jar             Binary Servlet and JSP API classes
+  src/                        Sources for Servlet and JSP API classes
+
+In general, you will need to add the "servlet.jar" file (found in the "lib"
+subdirectory of this release) into the compilation class path for your
+projects that depend on these APIs.
+
+The compiled "servlet.jar" file included in this subproject is automatically
+included in binary distributions of Tomcat 4.0, so you need not download this
+subproject separately unless you wish to utilize the Javadocs, or peruse the
+source code to see how the API classes are implemented.
diff --git a/servletapi/jsr154/build.xml b/servletapi/jsr154/build.xml
new file mode 100644
index 0000000..23c177f
--- /dev/null
+++ b/servletapi/jsr154/build.xml
@@ -0,0 +1,178 @@
+<project name="Servlet API Classes" default="compile" basedir=".">
+
+
+  <!-- =================== Environmental Properties ======================= -->
+
+  <!-- Load user property definition overrides -->
+  <property file="build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <!-- Establish property definition defaults -->
+  <property name="compile.debug"       value="true"/>
+  <property name="compile.deprecation" value="false"/>
+  <property name="compile.optimize"    value="true"/>
+  <property name="implementation.revision" value="public_draft"/>
+  <property name="servlet-api.build"    value="build"/>
+  <property name="servlet-api.dist"     value="dist"/>
+  <property name="servlet-api.jar" value="${servlet-api.dist}/lib/servlet-api.jar"/>
+  <path id="examples.classpath">
+    <pathelement location="${servlet-api.build}/classes"/>
+  </path>
+
+
+  <!-- ===================== Prepare Directories ========================= -->
+  <target name="prepare">
+
+    <!-- "Build" Hierarchy -->
+    <mkdir dir="${servlet-api.build}"/>
+    <mkdir dir="${servlet-api.build}/classes"/>
+    <mkdir dir="${servlet-api.build}/docs"/>
+    <mkdir dir="${servlet-api.build}/docs/api"/>
+    <mkdir dir="${servlet-api.build}/examples"/>
+
+    <!-- "Dist" Hierarchy -->
+    <mkdir dir="${servlet-api.dist}"/>
+    <mkdir dir="${servlet-api.dist}/docs"/>
+    <mkdir dir="${servlet-api.dist}/docs/api"/>
+    <mkdir dir="${servlet-api.dist}/lib"/>
+    <mkdir dir="${servlet-api.dist}/src"/>
+    <mkdir dir="${servlet-api.dist}/examples"/>
+
+    <uptodate property="docs.uptodate" targetfile="${servlet-api.build}/docs/api/index.html">
+      <srcfiles dir="src/share" includes="**/*.java" />
+    </uptodate>
+  </target>
+
+
+  <!-- ======================= Static Files ============================== -->
+  <target name="static" depends="prepare">
+
+    <!-- "Dist" Hierarchy -->
+    <copy todir="${servlet-api.dist}">
+      <fileset dir="." includes="BUILDING.txt"/>
+      <fileset dir="." includes="LICENSE"/>
+      <fileset dir="." includes="README.txt"/>
+    </copy>
+
+  </target>
+
+
+  <!-- ======================== Compile Classes ========================== -->
+  <target name="compile" depends="static"
+   description="Compile API classes (Default)">
+
+    <!-- Java classes -->
+    <javac srcdir="src/share" destdir="${servlet-api.build}/classes"
+           debug="${compile.debug}" deprecation="${compile.deprecation}"
+        optimize="${compile.optimize}"/>
+
+    <!-- Associated property files -->
+    <copy todir="${servlet-api.build}/classes">
+        <fileset dir="src/share">
+          <include name="**/*.properties"/>
+        </fileset>
+    </copy>
+
+    <!-- Servlet resources -->
+    <copy todir="${servlet-api.build}/classes/javax/servlet/resources">
+        <fileset dir="src/share/dtd" includes="*.dtd,*.xsd">
+          <exclude name="jsp*.dtd"/>
+          <exclude name="web-jsp*.dtd"/>
+        </fileset>
+    </copy>
+
+  </target>
+
+
+  <!-- ======================== Build JavaDoc =========================== -->
+  <target name="javadoc" depends="prepare" unless="docs.uptodate">
+
+    <javadoc packagenames="javax.servlet.*"
+             sourcepath="${basedir}/src/share"
+             destdir="${servlet-api.build}/docs/api"
+             use="true"
+             windowtitle="Servlet API Documentation"
+             doctitle="Servlet API Documentation"
+             bottom="Copyright &amp;copy; 1999-2002 The Apache Software Foundation.  All Rights Reserved."/>
+
+  </target>
+
+
+  <!-- ======================== Build JavaDoc =========================== -->
+  <target name="examples" depends="prepare">
+
+    <copy todir="${servlet-api.build}/examples">
+      <fileset dir="examples">
+        <exclude name="build.*"/>
+      </fileset>
+    </copy>
+
+    <javac   srcdir="examples/WEB-INF/classes" 
+             destdir="${servlet-api.build}/examples/WEB-INF/classes"
+             debug="${compile.debug}" deprecation="${compile.deprecation}"
+             optimize="${compile.optimize}"
+             excludes="**/CVS/**">
+      <classpath refid="examples.classpath" />
+    </javac>
+
+    <jar   jarfile="${servlet-api.dist}/examples/examples.war"
+           basedir="${servlet-api.build}/examples" includes="**"/>
+
+  </target>
+
+
+  <!-- ===================== Distribution Files ========================= -->
+  <target name="jar" depends="compile"
+          description="Create the jar">
+
+    <!-- Prepare Manifest -->
+    <copy tofile="${servlet-api.build}/manifest"
+            file="src/etc/manifest" overwrite="yes">
+      <filterset>
+        <filter token="implementation.revision"
+                value="${implementation.revision}"/>
+      </filterset>
+    </copy>
+
+    <!-- Create JAR file -->
+    <jar jarfile="${servlet-api.jar}"
+         basedir="${servlet-api.build}/classes"
+         manifest="${servlet-api.build}/manifest">
+      <include name="javax/servlet/**"/>
+      <exclude name="javax/servlet/jsp/**"/>
+    </jar>
+
+  </target>
+
+  <target name="dist" depends="compile,examples,javadoc,jar"
+          description="Create binary distribution">
+
+    <!-- Copy Javadocs -->
+    <copy todir="${servlet-api.dist}/docs/api">
+        <fileset dir="${servlet-api.build}/docs/api"/>
+    </copy>
+
+    <!-- Copy API source files -->
+    <copy todir="${servlet-api.dist}/src">
+        <fileset dir="src/share"/>
+    </copy>
+
+  </target>
+
+
+  <!-- ====================== Clean Generated Files ===================== -->
+  <target name="clean"
+   description="Clean previous build results">
+
+    <delete dir="${servlet-api.build}"/>
+    <delete dir="${servlet-api.dist}"/>
+
+  </target>
+
+
+  <!-- ========================= All In One Build ======================= -->
+  <target name="all" depends="clean,dist"
+   description="Clean, compile, and dist"/>
+
+
+</project>
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/CookieExample.java b/servletapi/jsr154/examples/WEB-INF/classes/CookieExample.java
new file mode 100644
index 0000000..4070bef
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/CookieExample.java
@@ -0,0 +1,121 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/* $Id$
+ *
+ */
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import util.HTMLFilter;
+
+/**
+ * Example servlet showing request headers
+ *
+ * @author James Duncan Davidson <duncan@eng.sun.com>
+ */
+
+public class CookieExample extends HttpServlet {
+
+    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");
+    
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        response.setContentType("text/html");
+
+        PrintWriter out = response.getWriter();
+        out.println("<html>");
+        out.println("<body bgcolor=\"white\">");
+        out.println("<head>");
+
+        String title = rb.getString("cookies.title");
+        out.println("<title>" + title + "</title>");
+        out.println("</head>");
+        out.println("<body>");
+
+	// relative links
+
+        // XXX
+        // making these absolute till we work out the
+        // addition of a PathInfo issue 
+	
+        out.println("<a href=\"../cookies.html\">");
+        out.println("<img src=\"../images/code.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"view code\"></a>");
+        out.println("<a href=\"../index.html\">");
+        out.println("<img src=\"../images/return.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"return\"></a>");
+
+        out.println("<h3>" + title + "</h3>");
+
+        Cookie[] cookies = request.getCookies();
+        if ((cookies != null) && (cookies.length > 0)) {
+            out.println(rb.getString("cookies.cookies") + "<br>");
+            for (int i = 0; i < cookies.length; i++) {
+                Cookie cookie = cookies[i];
+                out.print("Cookie Name: " + HTMLFilter.filter(cookie.getName())
+                          + "<br>");
+                out.println("  Cookie Value: " 
+                            + HTMLFilter.filter(cookie.getValue())
+                            + "<br><br>");
+            }
+        } else {
+            out.println(rb.getString("cookies.no-cookies"));
+        }
+
+        String cookieName = request.getParameter("cookiename");
+        String cookieValue = request.getParameter("cookievalue");
+        if (cookieName != null && cookieValue != null) {
+            Cookie cookie = new Cookie(cookieName, cookieValue);
+            response.addCookie(cookie);
+            out.println("<P>");
+            out.println(rb.getString("cookies.set") + "<br>");
+            out.print(rb.getString("cookies.name") + "  " 
+                      + HTMLFilter.filter(cookieName) + "<br>");
+            out.print(rb.getString("cookies.value") + "  " 
+                      + HTMLFilter.filter(cookieValue));
+        }
+        
+        out.println("<P>");
+        out.println(rb.getString("cookies.make-cookie") + "<br>");
+        out.print("<form action=\"");
+        out.println("CookieExample\" method=POST>");
+        out.print(rb.getString("cookies.name") + "  ");
+        out.println("<input type=text length=20 name=cookiename><br>");
+        out.print(rb.getString("cookies.value") + "  ");
+        out.println("<input type=text length=20 name=cookievalue><br>");
+        out.println("<input type=submit></form>");
+            
+            
+        out.println("</body>");
+        out.println("</html>");
+    }
+
+    public void doPost(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        doGet(request, response);
+    }
+
+}
+
+
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/HelloWorldExample.java b/servletapi/jsr154/examples/WEB-INF/classes/HelloWorldExample.java
new file mode 100644
index 0000000..6713600
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/HelloWorldExample.java
@@ -0,0 +1,75 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/* $Id$
+ *
+ */
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * The simplest possible servlet.
+ *
+ * @author James Duncan Davidson
+ */
+
+public class HelloWorldExample extends HttpServlet {
+
+
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        ResourceBundle rb =
+            ResourceBundle.getBundle("LocalStrings",request.getLocale());
+        response.setContentType("text/html");
+        PrintWriter out = response.getWriter();
+
+        out.println("<html>");
+        out.println("<head>");
+
+	    String title = rb.getString("helloworld.title");
+
+	    out.println("<title>" + title + "</title>");
+        out.println("</head>");
+        out.println("<body bgcolor=\"white\">");
+
+	// note that all links are created to be relative. this
+	// ensures that we can move the web application that this
+	// servlet belongs to to a different place in the url
+	// tree and not have any harmful side effects.
+
+        // XXX
+        // making these absolute till we work out the
+        // addition of a PathInfo issue
+
+	    out.println("<a href=\"../helloworld.html\">");
+        out.println("<img src=\"../images/code.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"view code\"></a>");
+        out.println("<a href=\"../index.html\">");
+        out.println("<img src=\"../images/return.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"return\"></a>");
+        out.println("<h1>" + title + "</h1>");
+        out.println("</body>");
+        out.println("</html>");
+    }
+}
+
+
+
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings.properties b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings.properties
new file mode 100644
index 0000000..ba3cbaa
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings.properties
@@ -0,0 +1,52 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# $Id$
+
+# Default localized resources for example servlets
+# This locale is en_US
+
+helloworld.title=Hello World!
+
+requestinfo.title=Request Information Example
+requestinfo.label.method=Method:
+requestinfo.label.requesturi=Request URI:
+requestinfo.label.protocol=Protocol:
+requestinfo.label.pathinfo=Path Info:
+requestinfo.label.remoteaddr=Remote Address:
+
+requestheader.title=Request Header Example
+
+requestparams.title=Request Parameters Example
+requestparams.params-in-req=Parameters in this request:
+requestparams.no-params=No Parameters, Please enter some
+requestparams.firstname=First Name:
+requestparams.lastname=Last Name:
+
+cookies.title=Cookies Example
+cookies.cookies=Your browser is sending the following cookies:
+cookies.no-cookies=Your browser isn't sending any cookies
+cookies.make-cookie=Create a cookie to send to your browser
+cookies.name=Name:
+cookies.value=Value:
+cookies.set=You just sent the following cookie to your browser:
+
+sessions.title=Sessions Example
+sessions.id=Session ID:
+sessions.created=Created:
+sessions.lastaccessed=Last Accessed:
+sessions.data=The following data is in your session:
+sessions.adddata=Add data to your session
+sessions.dataname=Name of Session Attribute:
+sessions.datavalue=Value of Session Attribute:
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_en.properties b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_en.properties
new file mode 100644
index 0000000..ba3cbaa
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_en.properties
@@ -0,0 +1,52 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# $Id$
+
+# Default localized resources for example servlets
+# This locale is en_US
+
+helloworld.title=Hello World!
+
+requestinfo.title=Request Information Example
+requestinfo.label.method=Method:
+requestinfo.label.requesturi=Request URI:
+requestinfo.label.protocol=Protocol:
+requestinfo.label.pathinfo=Path Info:
+requestinfo.label.remoteaddr=Remote Address:
+
+requestheader.title=Request Header Example
+
+requestparams.title=Request Parameters Example
+requestparams.params-in-req=Parameters in this request:
+requestparams.no-params=No Parameters, Please enter some
+requestparams.firstname=First Name:
+requestparams.lastname=Last Name:
+
+cookies.title=Cookies Example
+cookies.cookies=Your browser is sending the following cookies:
+cookies.no-cookies=Your browser isn't sending any cookies
+cookies.make-cookie=Create a cookie to send to your browser
+cookies.name=Name:
+cookies.value=Value:
+cookies.set=You just sent the following cookie to your browser:
+
+sessions.title=Sessions Example
+sessions.id=Session ID:
+sessions.created=Created:
+sessions.lastaccessed=Last Accessed:
+sessions.data=The following data is in your session:
+sessions.adddata=Add data to your session
+sessions.dataname=Name of Session Attribute:
+sessions.datavalue=Value of Session Attribute:
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_es.properties b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_es.properties
new file mode 100644
index 0000000..21e6712
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_es.properties
@@ -0,0 +1,52 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# $Id$
+#
+# Default localized string information
+# Localized para Locale es_ES
+
+helloworld.title=Hola Mundo!
+
+requestinfo.title=Ejemplo de Informacion de Request
+requestinfo.label.method=Metodo:
+requestinfo.label.requesturi=Request URI:
+requestinfo.label.protocol=Protocolo:
+requestinfo.label.pathinfo=Path Info:
+requestinfo.label.remoteaddr=Direccion Remota:
+
+requestheader.title=Ejemplo de Cabecera de Request
+
+requestparams.title=Ejemplo de parametros de Request
+requestparams.params-in-req=Parametros en este Request:
+requestparams.no-params=No hay parametro. por favor usa alguno
+requestparams.firstname=Nombre:
+requestparams.lastname=Apellidos:
+
+cookies.title=Ejemplo de Cookies
+cookies.cookies=Tu navegador esta enviando los siguientes cookies:
+cookies.no-cookies=Tu navegador no esta enviando cookies
+cookies.make-cookie=Crea un cookie para enviarlo a tu navegador
+cookies.name=Nombre:
+cookies.value=Valor:
+cookies.set=Acabas de enviar a tu navegador estos cookies:
+
+sessions.title=ejemplo de Sesiones
+sessions.id=ID de Sesion:
+sessions.created=Creado:
+sessions.lastaccessed=Ultimo Acceso:
+sessions.data=Lo siguientes datos estan en tu sesion:
+sessions.adddata=Añade datos a tu sesion:
+sessions.dataname=Nombre del atributo de sesion:
+sessions.datavalue=Valor del atributo de sesion:
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_fr.properties b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_fr.properties
new file mode 100644
index 0000000..212cb05
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_fr.properties
@@ -0,0 +1,52 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# $Id$
+
+# Default localized resources for example servlets
+# This locale is fr_FR
+
+helloworld.title=Salut le Monde!
+
+requestinfo.title=Exemple d''information sur la requête
+requestinfo.label.method=Méthode:
+requestinfo.label.requesturi=URI de requête:
+requestinfo.label.protocol=Protocole:
+requestinfo.label.pathinfo=Info de chemin:
+requestinfo.label.remoteaddr=Adresse distante:
+
+requestheader.title=Exemple d''information sur les entête de requête
+
+requestparams.title=Exemple de requête avec paramêtres
+requestparams.params-in-req=Paramêtres dans la requête:
+requestparams.no-params=Pas de paramêtre, merci dans saisir quelqu'uns
+requestparams.firstname=Prénom:
+requestparams.lastname=Nom:
+
+cookies.title=Exemple d''utilisation de Cookies
+cookies.cookies=Votre navigateur retourne les cookies suivant:
+cookies.no-cookies=Votre navigateur ne retourne aucun cookie
+cookies.make-cookie=Création d''un cookie à retourner à votre navigateur
+cookies.name=Nom:
+cookies.value=Valeur:
+cookies.set=Vous venez d''envoyer le cookie suivant à votre navigateur:
+
+sessions.title=Exemple de Sessions
+sessions.id=ID de Session:
+sessions.created=Crée le:
+sessions.lastaccessed=Dernier accès:
+sessions.data=Les données existantes dans votre session:
+sessions.adddata=Ajouter des données à votre session
+sessions.dataname=Nom de l''Attribut de Session:
+sessions.datavalue=Valeur de l''Attribut de Session:
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_pt.properties b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_pt.properties
new file mode 100644
index 0000000..318a0b2
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/LocalStrings_pt.properties
@@ -0,0 +1,52 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# $Id$
+
+# Default localized resources for example servlets
+# This locale is pt_PT
+
+helloworld.title=Ola Mundo!
+
+requestinfo.title=Exemplo da Informacao do Pedido
+requestinfo.label.method=Metodo:
+requestinfo.label.requesturi=URI do Pedido:
+requestinfo.label.protocol=Protocolo:
+requestinfo.label.pathinfo=Informacao do Caminho:
+requestinfo.label.remoteaddr=Endereco Remoto:
+
+requestheader.title=Exemplo da Cebeceira do Pedido
+
+requestparams.title=Examplo de Parametros do Pedido
+requestparams.params-in-req=Parametros neste pedido:
+requestparams.no-params=Sem Parametros, Por favor entre alguns
+requestparams.firstname=Primeiro Nome:
+requestparams.lastname=Apelido:
+
+cookies.title=CExamplo de Cookies
+cookies.cookies=O se browser esta a enviar os seguintes cookies:
+cookies.no-cookies=O seu browser nao esta a enviar nenhuns cookies
+cookies.make-cookie=Crie um cookie para enviar para o seu browser
+cookies.name=Nome:
+cookies.value=Valor:
+cookies.set=Acabou de enviar o seguinte cookie para o seu browser:
+
+sessions.title=Examplo de sessoes
+sessions.id=Identificador da Sessao:
+sessions.created=Criada:
+sessions.lastaccessed=Ultima vez acedida:
+sessions.data=Os seguintes dados fazem parte da sua sessao:
+sessions.adddata=Adicione data a sua sessao
+sessions.dataname=Nome do atributo da sessao:
+sessions.datavalue=Valor do atributo da Sessao:
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/RequestHeaderExample.java b/servletapi/jsr154/examples/WEB-INF/classes/RequestHeaderExample.java
new file mode 100644
index 0000000..33788d3
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/RequestHeaderExample.java
@@ -0,0 +1,90 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/* $Id$
+ *
+ */
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import util.HTMLFilter;
+
+/**
+ * Example servlet showing request headers
+ *
+ * @author James Duncan Davidson <duncan@eng.sun.com>
+ */
+
+public class RequestHeaderExample extends HttpServlet {
+
+    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");
+    
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        response.setContentType("text/html");
+
+        PrintWriter out = response.getWriter();
+        out.println("<html>");
+        out.println("<body bgcolor=\"white\">");
+        out.println("<head>");
+
+        String title = rb.getString("requestheader.title");
+        out.println("<title>" + title + "</title>");
+        out.println("</head>");
+        out.println("<body>");
+
+	// all links relative
+
+        // XXX
+        // making these absolute till we work out the
+        // addition of a PathInfo issue 
+	
+        out.println("<a href=\"../reqheaders.html\">");
+        out.println("<img src=\"../images/code.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"view code\"></a>");
+        out.println("<a href=\"../index.html\">");
+        out.println("<img src=\"../images/return.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"return\"></a>");
+
+        out.println("<h3>" + title + "</h3>");
+        out.println("<table border=0>");
+        Enumeration e = request.getHeaderNames();
+        while (e.hasMoreElements()) {
+            String headerName = (String)e.nextElement();
+            String headerValue = request.getHeader(headerName);
+            out.println("<tr><td bgcolor=\"#CCCCCC\">");
+            out.println(HTMLFilter.filter(headerName));
+            out.println("</td><td>");
+            out.println(HTMLFilter.filter(headerValue));
+            out.println("</td></tr>");
+        }
+        out.println("</table>");
+    }
+
+    public void doPost(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        doGet(request, response);
+    }
+
+}
+
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/RequestInfoExample.java b/servletapi/jsr154/examples/WEB-INF/classes/RequestInfoExample.java
new file mode 100644
index 0000000..5a4df1a
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/RequestInfoExample.java
@@ -0,0 +1,114 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/* $Id$
+ *
+ */
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import util.HTMLFilter;
+
+/**
+ * Example servlet showing request information.
+ *
+ * @author James Duncan Davidson <duncan@eng.sun.com>
+ */
+
+public class RequestInfoExample extends HttpServlet {
+
+
+    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");
+
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        response.setContentType("text/html");
+
+        PrintWriter out = response.getWriter();
+        out.println("<html>");
+        out.println("<body>");
+        out.println("<head>");
+
+        String title = rb.getString("requestinfo.title");
+        out.println("<title>" + title + "</title>");
+        out.println("</head>");
+        out.println("<body bgcolor=\"white\">");
+
+        // img stuff not req'd for source code html showing
+	// all links relative!
+
+        // XXX
+        // making these absolute till we work out the
+        // addition of a PathInfo issue
+	
+        out.println("<a href=\"../reqinfo.html\">");
+        out.println("<img src=\"../images/code.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"view code\"></a>");
+        out.println("<a href=\"../index.html\">");
+        out.println("<img src=\"../images/return.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"return\"></a>");
+
+        out.println("<h3>" + title + "</h3>");
+        out.println("<table border=0><tr><td>");
+        out.println(rb.getString("requestinfo.label.method"));
+        out.println("</td><td>");
+        out.println(request.getMethod());
+        out.println("</td></tr><tr><td>");
+        out.println(rb.getString("requestinfo.label.requesturi"));
+        out.println("</td><td>");        
+        out.println(HTMLFilter.filter(request.getRequestURI()));
+        out.println("</td></tr><tr><td>");        
+        out.println(rb.getString("requestinfo.label.protocol"));
+        out.println("</td><td>");        
+        out.println(request.getProtocol());
+        out.println("</td></tr><tr><td>");
+        out.println(rb.getString("requestinfo.label.pathinfo"));
+        out.println("</td><td>");        
+        out.println(HTMLFilter.filter(request.getPathInfo()));
+        out.println("</td></tr><tr><td>");
+        out.println(rb.getString("requestinfo.label.remoteaddr"));
+
+ 	String cipherSuite=
+ 	    (String)request.getAttribute("javax.servlet.request.cipher_suite");
+        out.println("</td><td>");                
+        out.println(request.getRemoteAddr());
+        out.println("</table>");
+
+ 	if(cipherSuite!=null){
+ 	    out.println("</td></tr><tr><td>");	
+ 	    out.println("SSLCipherSuite:");
+ 	    out.println("</td>");
+ 	    out.println("<td>");	    
+ 	    out.println(request.getAttribute("javax.servlet.request.cipher_suite"));
+	    out.println("</td>");	    
+ 	}
+	
+    }
+
+    public void doPost(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        doGet(request, response);
+    }
+
+}
+
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/RequestParamExample.java b/servletapi/jsr154/examples/WEB-INF/classes/RequestParamExample.java
new file mode 100644
index 0000000..4f52eee
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/RequestParamExample.java
@@ -0,0 +1,106 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/* $Id$
+ *
+ */
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import util.HTMLFilter;
+
+/**
+ * Example servlet showing request headers
+ *
+ * @author James Duncan Davidson <duncan@eng.sun.com>
+ */
+
+public class RequestParamExample extends HttpServlet {
+
+
+    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");
+    
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        response.setContentType("text/html");
+
+        PrintWriter out = response.getWriter();
+        out.println("<html>");
+        out.println("<body>");
+        out.println("<head>");
+
+        String title = rb.getString("requestparams.title");
+        out.println("<title>" + title + "</title>");
+        out.println("</head>");
+        out.println("<body bgcolor=\"white\">");
+
+        // img stuff not req'd for source code html showing
+
+	// all links relative
+
+        // XXX
+        // making these absolute till we work out the
+        // addition of a PathInfo issue 
+	
+        out.println("<a href=\"../reqparams.html\">");
+        out.println("<img src=\"../images/code.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"view code\"></a>");
+        out.println("<a href=\"../index.html\">");
+        out.println("<img src=\"../images/return.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"return\"></a>");
+
+        out.println("<h3>" + title + "</h3>");
+        String firstName = request.getParameter("firstname");
+        String lastName = request.getParameter("lastname");
+        out.println(rb.getString("requestparams.params-in-req") + "<br>");
+        if (firstName != null || lastName != null) {
+            out.println(rb.getString("requestparams.firstname"));
+            out.println(" = " + HTMLFilter.filter(firstName) + "<br>");
+            out.println(rb.getString("requestparams.lastname"));
+            out.println(" = " + HTMLFilter.filter(lastName));
+        } else {
+            out.println(rb.getString("requestparams.no-params"));
+        }
+        out.println("<P>");
+        out.print("<form action=\"");
+        out.print("RequestParamExample\" ");
+        out.println("method=POST>");
+        out.println(rb.getString("requestparams.firstname"));
+        out.println("<input type=text size=20 name=firstname>");
+        out.println("<br>");
+        out.println(rb.getString("requestparams.lastname"));
+        out.println("<input type=text size=20 name=lastname>");
+        out.println("<br>");
+        out.println("<input type=submit>");
+        out.println("</form>");
+
+        out.println("</body>");
+        out.println("</html>");
+    }
+
+    public void doPost(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        doGet(request, response);
+    }
+
+}
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/SessionExample.java b/servletapi/jsr154/examples/WEB-INF/classes/SessionExample.java
new file mode 100644
index 0000000..e5e7882
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/SessionExample.java
@@ -0,0 +1,140 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/* $Id$
+ *
+ */
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import util.HTMLFilter;
+
+/**
+ * Example servlet showing request headers
+ *
+ * @author James Duncan Davidson <duncan@eng.sun.com>
+ */
+
+public class SessionExample extends HttpServlet {
+
+    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");
+    
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        response.setContentType("text/html");
+
+        PrintWriter out = response.getWriter();
+        out.println("<html>");
+        out.println("<body bgcolor=\"white\">");
+        out.println("<head>");
+
+        String title = rb.getString("sessions.title");
+        out.println("<title>" + title + "</title>");
+        out.println("</head>");
+        out.println("<body>");
+
+        // img stuff not req'd for source code html showing
+	// relative links everywhere!
+
+        // XXX
+        // making these absolute till we work out the
+        // addition of a PathInfo issue 
+	
+        out.println("<a href=\"../sessions.html\">");
+        out.println("<img src=\"../images/code.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"view code\"></a>");
+        out.println("<a href=\"../index.html\">");
+        out.println("<img src=\"../images/return.gif\" height=24 " +
+                    "width=24 align=right border=0 alt=\"return\"></a>");
+
+        out.println("<h3>" + title + "</h3>");
+
+        HttpSession session = request.getSession(true);
+        out.println(rb.getString("sessions.id") + " " + session.getId());
+        out.println("<br>");
+        out.println(rb.getString("sessions.created") + " ");
+        out.println(new Date(session.getCreationTime()) + "<br>");
+        out.println(rb.getString("sessions.lastaccessed") + " ");
+        out.println(new Date(session.getLastAccessedTime()));
+
+        String dataName = request.getParameter("dataname");
+        String dataValue = request.getParameter("datavalue");
+        if (dataName != null && dataValue != null) {
+            session.setAttribute(dataName, dataValue);
+        }
+
+        out.println("<P>");
+        out.println(rb.getString("sessions.data") + "<br>");
+        Enumeration names = session.getAttributeNames();
+        while (names.hasMoreElements()) {
+            String name = (String) names.nextElement(); 
+            String value = session.getAttribute(name).toString();
+            out.println(HTMLFilter.filter(name) + " = " 
+                        + HTMLFilter.filter(value) + "<br>");
+        }
+
+        out.println("<P>");
+        out.print("<form action=\"");
+	out.print(response.encodeURL("SessionExample"));
+        out.print("\" ");
+        out.println("method=POST>");
+        out.println(rb.getString("sessions.dataname"));
+        out.println("<input type=text size=20 name=dataname>");
+        out.println("<br>");
+        out.println(rb.getString("sessions.datavalue"));
+        out.println("<input type=text size=20 name=datavalue>");
+        out.println("<br>");
+        out.println("<input type=submit>");
+        out.println("</form>");
+
+        out.println("<P>GET based form:<br>");
+        out.print("<form action=\"");
+	out.print(response.encodeURL("SessionExample"));
+        out.print("\" ");
+        out.println("method=GET>");
+        out.println(rb.getString("sessions.dataname"));
+        out.println("<input type=text size=20 name=dataname>");
+        out.println("<br>");
+        out.println(rb.getString("sessions.datavalue"));
+        out.println("<input type=text size=20 name=datavalue>");
+        out.println("<br>");
+        out.println("<input type=submit>");
+        out.println("</form>");
+
+        out.print("<p><a href=\"");
+	out.print(response.encodeURL("SessionExample?dataname=foo&datavalue=bar"));
+	out.println("\" >URL encoded </a>");
+	
+        out.println("</body>");
+        out.println("</html>");
+        
+        out.println("</body>");
+        out.println("</html>");
+    }
+
+    public void doPost(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        doGet(request, response);
+    }
+
+}
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java b/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
new file mode 100644
index 0000000..a8f18e8
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
@@ -0,0 +1,218 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package compressionFilters;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Implementation of <code>javax.servlet.Filter</code> used to compress
+ * the ServletResponse if it is bigger than a threshold.
+ *
+ * @author Amy Roh
+ * @author Dmitri Valdin
+ * @version $Revision$, $Date$
+ */
+
+public class CompressionFilter implements Filter{
+
+    /**
+     * The filter configuration object we are associated with.  If this value
+     * is null, this filter instance is not currently configured.
+     */
+    private FilterConfig config = null;
+
+    /**
+     * Minimal reasonable threshold
+     */
+    private int minThreshold = 128;
+
+
+    /**
+     * The threshold number to compress
+     */
+    protected int compressionThreshold;
+
+    /**
+     * Debug level for this filter
+     */
+    private int debug = 0;
+
+    /**
+     * Place this filter into service.
+     *
+     * @param filterConfig The filter configuration object
+     */
+
+    public void init(FilterConfig filterConfig) {
+
+        config = filterConfig;
+        if (filterConfig != null) {
+            String value = filterConfig.getInitParameter("debug");
+            if (value!=null) {
+                debug = Integer.parseInt(value);
+            } else {
+                debug = 0;
+            }
+            String str = filterConfig.getInitParameter("compressionThreshold");
+            if (str!=null) {
+                compressionThreshold = Integer.parseInt(str);
+                if (compressionThreshold != 0 && compressionThreshold < minThreshold) {
+                    if (debug > 0) {
+                        System.out.println("compressionThreshold should be either 0 - no compression or >= " + minThreshold);
+                        System.out.println("compressionThreshold set to " + minThreshold);
+                    }
+                    compressionThreshold = minThreshold;
+                }
+            } else {
+                compressionThreshold = 0;
+            }
+
+        } else {
+            compressionThreshold = 0;
+        }
+
+    }
+
+    /**
+    * Take this filter out of service.
+    */
+    public void destroy() {
+
+        this.config = null;
+
+    }
+
+    /**
+     * The <code>doFilter</code> method of the Filter is called by the container
+     * each time a request/response pair is passed through the chain due
+     * to a client request for a resource at the end of the chain.
+     * The FilterChain passed into this method allows the Filter to pass on the
+     * request and response to the next entity in the chain.<p>
+     * This method first examines the request to check whether the client support
+     * compression. <br>
+     * It simply just pass the request and response if there is no support for
+     * compression.<br>
+     * If the compression support is available, it creates a
+     * CompressionServletResponseWrapper object which compresses the content and
+     * modifies the header if the content length is big enough.
+     * It then invokes the next entity in the chain using the FilterChain object
+     * (<code>chain.doFilter()</code>), <br>
+     **/
+
+    public void doFilter ( ServletRequest request, ServletResponse response,
+                        FilterChain chain ) throws IOException, ServletException {
+
+        if (debug > 0) {
+            System.out.println("@doFilter");
+        }
+
+        if (compressionThreshold == 0) {
+            if (debug > 0) {
+                System.out.println("doFilter gets called, but compressionTreshold is set to 0 - no compression");
+            }
+            chain.doFilter(request, response);
+            return;
+        }
+
+        boolean supportCompression = false;
+        if (request instanceof HttpServletRequest) {
+            if (debug > 1) {
+                System.out.println("requestURI = " + ((HttpServletRequest)request).getRequestURI());
+            }
+
+            // Are we allowed to compress ?
+            String s = (String) ((HttpServletRequest)request).getParameter("gzip");
+            if ("false".equals(s)) {
+                if (debug > 0) {
+                    System.out.println("got parameter gzip=false --> don't compress, just chain filter");
+                }
+                chain.doFilter(request, response);
+                return;
+            }
+
+            Enumeration e =
+                ((HttpServletRequest)request).getHeaders("Accept-Encoding");
+            while (e.hasMoreElements()) {
+                String name = (String)e.nextElement();
+                if (name.indexOf("gzip") != -1) {
+                    if (debug > 0) {
+                        System.out.println("supports compression");
+                    }
+                    supportCompression = true;
+                } else {
+                    if (debug > 0) {
+                        System.out.println("no support for compresion");
+                    }
+                }
+            }
+        }
+
+        if (!supportCompression) {
+            if (debug > 0) {
+                System.out.println("doFilter gets called wo compression");
+            }
+            chain.doFilter(request, response);
+            return;
+        } else {
+            if (response instanceof HttpServletResponse) {
+                CompressionServletResponseWrapper wrappedResponse =
+                    new CompressionServletResponseWrapper((HttpServletResponse)response);
+                wrappedResponse.setDebugLevel(debug);
+                wrappedResponse.setCompressionThreshold(compressionThreshold);
+                if (debug > 0) {
+                    System.out.println("doFilter gets called with compression");
+                }
+                try {
+                    chain.doFilter(request, wrappedResponse);
+                } finally {
+                    wrappedResponse.finishResponse();
+                }
+                return;
+            }
+        }
+    }
+
+    /**
+     * Set filter config
+     * This function is equivalent to init. Required by Weblogic 6.1
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void setFilterConfig(FilterConfig filterConfig) {
+        init(filterConfig);
+    }
+
+    /**
+     * Return filter config
+     * Required by Weblogic 6.1
+     */
+    public FilterConfig getFilterConfig() {
+        return config;
+    }
+
+}
+
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java b/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java
new file mode 100644
index 0000000..dce2b67
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java
@@ -0,0 +1,57 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package compressionFilters;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Very Simple test servlet to test compression filter
+ * @author Amy Roh
+ * @version $Revision$, $Date$
+ */
+
+public class CompressionFilterTestServlet extends HttpServlet {
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws ServletException, IOException {
+
+        ServletOutputStream out = response.getOutputStream();
+        response.setContentType("text/plain");
+
+        Enumeration e = ((HttpServletRequest)request).getHeaders("Accept-Encoding");
+        while (e.hasMoreElements()) {
+            String name = (String)e.nextElement();
+            out.println(name);
+            if (name.indexOf("gzip") != -1) {
+                out.println("gzip supported -- able to compress");
+            }
+            else {
+                out.println("gzip not supported");
+            }
+        }
+
+
+        out.println("Compression Filter Test Servlet");
+        out.close();
+    }
+
+}
+
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java b/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
new file mode 100644
index 0000000..23ef322
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
@@ -0,0 +1,317 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package compressionFilters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.GZIPOutputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Implementation of <b>ServletOutputStream</b> that works with
+ * the CompressionServletResponseWrapper implementation.
+ *
+ * @author Amy Roh
+ * @author Dmitri Valdin
+ * @version $Revision$, $Date$
+ */
+
+public class CompressionResponseStream
+    extends ServletOutputStream {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a servlet output stream associated with the specified Response.
+     *
+     * @param response The associated response
+     */
+    public CompressionResponseStream(HttpServletResponse response) throws IOException{
+
+        super();
+        closed = false;
+        this.response = response;
+        this.output = response.getOutputStream();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The threshold number which decides to compress or not.
+     * Users can configure in web.xml to set it to fit their needs.
+     */
+    protected int compressionThreshold = 0;
+
+    /**
+     * Debug level
+     */
+    private int debug = 0;
+
+    /**
+     * The buffer through which all of our output bytes are passed.
+     */
+    protected byte[] buffer = null;
+
+    /**
+     * The number of data bytes currently in the buffer.
+     */
+    protected int bufferCount = 0;
+
+    /**
+     * The underlying gzip output stream to which we should write data.
+     */
+    protected GZIPOutputStream gzipstream = null;
+
+    /**
+     * Has this stream been closed?
+     */
+    protected boolean closed = false;
+
+    /**
+     * The content length past which we will not write, or -1 if there is
+     * no defined content length.
+     */
+    protected int length = -1;
+
+    /**
+     * The response with which this servlet output stream is associated.
+     */
+    protected HttpServletResponse response = null;
+
+    /**
+     * The underlying servket output stream to which we should write data.
+     */
+    protected ServletOutputStream output = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Set debug level
+     */
+    public void setDebugLevel(int debug) {
+        this.debug = debug;
+    }
+
+
+    /**
+     * Set the compressionThreshold number and create buffer for this size
+     */
+    protected void setBuffer(int threshold) {
+        compressionThreshold = threshold;
+        buffer = new byte[compressionThreshold];
+        if (debug > 1) {
+            System.out.println("buffer is set to "+compressionThreshold);
+        }
+    }
+
+    /**
+     * Close this output stream, causing any buffered data to be flushed and
+     * any further output data to throw an IOException.
+     */
+    public void close() throws IOException {
+
+        if (debug > 1) {
+            System.out.println("close() @ CompressionResponseStream");
+        }
+        if (closed)
+            throw new IOException("This output stream has already been closed");
+
+        if (gzipstream != null) {
+            flushToGZip();
+            gzipstream.close();
+            gzipstream = null;
+        } else {
+            if (bufferCount > 0) {
+                if (debug > 2) {
+                    System.out.print("output.write(");
+                    System.out.write(buffer, 0, bufferCount);
+                    System.out.println(")");
+                }
+                output.write(buffer, 0, bufferCount);
+                bufferCount = 0;
+            }
+        }
+
+        output.close();
+        closed = true;
+
+    }
+
+
+    /**
+     * Flush any buffered data for this output stream, which also causes the
+     * response to be committed.
+     */
+    public void flush() throws IOException {
+
+        if (debug > 1) {
+            System.out.println("flush() @ CompressionResponseStream");
+        }
+        if (closed) {
+            throw new IOException("Cannot flush a closed output stream");
+        }
+
+        if (gzipstream != null) {
+            gzipstream.flush();
+        }
+
+    }
+
+    public void flushToGZip() throws IOException {
+
+        if (debug > 1) {
+            System.out.println("flushToGZip() @ CompressionResponseStream");
+        }
+        if (bufferCount > 0) {
+            if (debug > 1) {
+                System.out.println("flushing out to GZipStream, bufferCount = " + bufferCount);
+            }
+            writeToGZip(buffer, 0, bufferCount);
+            bufferCount = 0;
+        }
+
+    }
+
+    /**
+     * Write the specified byte to our output stream.
+     *
+     * @param b The byte to be written
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void write(int b) throws IOException {
+
+        if (debug > 1) {
+            System.out.println("write "+b+" in CompressionResponseStream ");
+        }
+        if (closed)
+            throw new IOException("Cannot write to a closed output stream");
+
+        if (bufferCount >= buffer.length) {
+            flushToGZip();
+        }
+
+        buffer[bufferCount++] = (byte) b;
+
+    }
+
+
+    /**
+     * Write <code>b.length</code> bytes from the specified byte array
+     * to our output stream.
+     *
+     * @param b The byte array to be written
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void write(byte b[]) throws IOException {
+
+        write(b, 0, b.length);
+
+    }
+
+
+    /**
+     * Write <code>len</code> bytes from the specified byte array, starting
+     * at the specified offset, to our output stream.
+     *
+     * @param b The byte array containing the bytes to be written
+     * @param off Zero-relative starting offset of the bytes to be written
+     * @param len The number of bytes to be written
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void write(byte b[], int off, int len) throws IOException {
+
+        if (debug > 1) {
+            System.out.println("write, bufferCount = " + bufferCount + " len = " + len + " off = " + off);
+        }
+        if (debug > 2) {
+            System.out.print("write(");
+            System.out.write(b, off, len);
+            System.out.println(")");
+        }
+
+        if (closed)
+            throw new IOException("Cannot write to a closed output stream");
+
+        if (len == 0)
+            return;
+
+        // Can we write into buffer ?
+        if (len <= (buffer.length - bufferCount)) {
+            System.arraycopy(b, off, buffer, bufferCount, len);
+            bufferCount += len;
+            return;
+        }
+
+        // There is not enough space in buffer. Flush it ...
+        flushToGZip();
+
+        // ... and try again. Note, that bufferCount = 0 here !
+        if (len <= (buffer.length - bufferCount)) {
+            System.arraycopy(b, off, buffer, bufferCount, len);
+            bufferCount += len;
+            return;
+        }
+
+        // write direct to gzip
+        writeToGZip(b, off, len);
+    }
+
+    public void writeToGZip(byte b[], int off, int len) throws IOException {
+
+        if (debug > 1) {
+            System.out.println("writeToGZip, len = " + len);
+        }
+        if (debug > 2) {
+            System.out.print("writeToGZip(");
+            System.out.write(b, off, len);
+            System.out.println(")");
+        }
+        if (gzipstream == null) {
+            if (debug > 1) {
+                System.out.println("new GZIPOutputStream");
+            }
+            response.addHeader("Content-Encoding", "gzip");
+            gzipstream = new GZIPOutputStream(output);
+        }
+        gzipstream.write(b, off, len);
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Has this response stream been closed?
+     */
+    public boolean closed() {
+
+        return (this.closed);
+
+    }
+
+}
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java b/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
new file mode 100644
index 0000000..5353fd6
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
@@ -0,0 +1,276 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package compressionFilters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Locale;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * Implementation of <b>HttpServletResponseWrapper</b> that works with
+ * the CompressionServletResponseStream implementation..
+ *
+ * @author Amy Roh
+ * @author Dmitri Valdin
+ * @version $Revision$, $Date$
+ */
+
+public class CompressionServletResponseWrapper extends HttpServletResponseWrapper {
+
+    // ----------------------------------------------------- Constructor
+
+    /**
+     * Calls the parent constructor which creates a ServletResponse adaptor
+     * wrapping the given response object.
+     */
+
+    public CompressionServletResponseWrapper(HttpServletResponse response) {
+        super(response);
+        origResponse = response;
+        if (debug > 1) {
+            System.out.println("CompressionServletResponseWrapper constructor gets called");
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Original response
+     */
+
+    protected HttpServletResponse origResponse = null;
+
+    /**
+     * Descriptive information about this Response implementation.
+     */
+
+    protected static final String info = "CompressionServletResponseWrapper";
+
+    /**
+     * The ServletOutputStream that has been returned by
+     * <code>getOutputStream()</code>, if any.
+     */
+
+    protected ServletOutputStream stream = null;
+
+
+    /**
+     * The PrintWriter that has been returned by
+     * <code>getWriter()</code>, if any.
+     */
+
+    protected PrintWriter writer = null;
+
+    /**
+     * The threshold number to compress
+     */
+    protected int threshold = 0;
+
+    /**
+     * Debug level
+     */
+    private int debug = 0;
+
+    /**
+     * Content type
+     */
+    protected String contentType = null;
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Set content type
+     */
+    public void setContentType(String contentType) {
+        if (debug > 1) {
+            System.out.println("setContentType to "+contentType);
+        }
+        this.contentType = contentType;
+        origResponse.setContentType(contentType);
+    }
+
+
+    /**
+     * Set threshold number
+     */
+    public void setCompressionThreshold(int threshold) {
+        if (debug > 1) {
+            System.out.println("setCompressionThreshold to " + threshold);
+        }
+        this.threshold = threshold;
+    }
+
+
+    /**
+     * Set debug level
+     */
+    public void setDebugLevel(int debug) {
+        this.debug = debug;
+    }
+
+
+    /**
+     * Create and return a ServletOutputStream to write the content
+     * associated with this Response.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletOutputStream createOutputStream() throws IOException {
+        if (debug > 1) {
+            System.out.println("createOutputStream gets called");
+        }
+
+        CompressionResponseStream stream = new CompressionResponseStream(origResponse);
+        stream.setDebugLevel(debug);
+        stream.setBuffer(threshold);
+
+        return stream;
+
+    }
+
+
+    /**
+     * Finish a response.
+     */
+    public void finishResponse() {
+        try {
+            if (writer != null) {
+                writer.close();
+            } else {
+                if (stream != null)
+                    stream.close();
+            }
+        } catch (IOException e) {
+        }
+    }
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    /**
+     * Flush the buffer and commit this response.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void flushBuffer() throws IOException {
+        if (debug > 1) {
+            System.out.println("flush buffer @ CompressionServletResponseWrapper");
+        }
+        ((CompressionResponseStream)stream).flush();
+
+    }
+
+    /**
+     * Return the servlet output stream associated with this Response.
+     *
+     * @exception IllegalStateException if <code>getWriter</code> has
+     *  already been called for this response
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletOutputStream getOutputStream() throws IOException {
+
+        if (writer != null)
+            throw new IllegalStateException("getWriter() has already been called for this response");
+
+        if (stream == null)
+            stream = createOutputStream();
+        if (debug > 1) {
+            System.out.println("stream is set to "+stream+" in getOutputStream");
+        }
+
+        return (stream);
+
+    }
+
+    /**
+     * Return the writer associated with this Response.
+     *
+     * @exception IllegalStateException if <code>getOutputStream</code> has
+     *  already been called for this response
+     * @exception IOException if an input/output error occurs
+     */
+    public PrintWriter getWriter() throws IOException {
+
+        if (writer != null)
+            return (writer);
+
+        if (stream != null)
+            throw new IllegalStateException("getOutputStream() has already been called for this response");
+
+        stream = createOutputStream();
+        if (debug > 1) {
+            System.out.println("stream is set to "+stream+" in getWriter");
+        }
+        //String charset = getCharsetFromContentType(contentType);
+        String charEnc = origResponse.getCharacterEncoding();
+        if (debug > 1) {
+            System.out.println("character encoding is " + charEnc);
+        }
+        // HttpServletResponse.getCharacterEncoding() shouldn't return null
+        // according the spec, so feel free to remove that "if"
+        if (charEnc != null) {
+            writer = new PrintWriter(new OutputStreamWriter(stream, charEnc));
+        } else {
+            writer = new PrintWriter(stream);
+        }
+        
+        return (writer);
+
+    }
+
+
+    public void setContentLength(int length) {
+    }
+
+
+    /**
+     * Returns character from content type. This method was taken from tomcat.
+     * @author rajo
+     */
+    private static String getCharsetFromContentType(String type) {
+
+        if (type == null) {
+            return null;
+        }
+        int semi = type.indexOf(";");
+        if (semi == -1) {
+            return null;
+        }
+        String afterSemi = type.substring(semi + 1);
+        int charsetLocation = afterSemi.indexOf("charset=");
+        if(charsetLocation == -1) {
+            return null;
+        } else {
+            String afterCharset = afterSemi.substring(charsetLocation + 8);
+            String encoding = afterCharset.trim();
+            return encoding;
+        }
+    }
+
+}
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/filters/ExampleFilter.java b/servletapi/jsr154/examples/WEB-INF/classes/filters/ExampleFilter.java
new file mode 100644
index 0000000..b078cf5
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/filters/ExampleFilter.java
@@ -0,0 +1,139 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package filters;
+
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Example filter that can be attached to either an individual servlet
+ * or to a URL pattern.  This filter performs the following functions:
+ * <ul>
+ * <li>Attaches itself as a request attribute, under the attribute name
+ *     defined by the value of the <code>attribute</code> initialization
+ *     parameter.</li>
+ * <li>Calculates the number of milliseconds required to perform the
+ *     servlet processing required by this request, including any
+ *     subsequently defined filters, and logs the result to the servlet
+ *     context log for this application.
+ * </ul>
+ *
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class ExampleFilter implements Filter {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The request attribute name under which we store a reference to ourself.
+     */
+    private String attribute = null;
+
+
+    /**
+     * The filter configuration object we are associated with.  If this value
+     * is null, this filter instance is not currently configured.
+     */
+    private FilterConfig filterConfig = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Take this filter out of service.
+     */
+    public void destroy() {
+
+        this.attribute = null;
+        this.filterConfig = null;
+
+    }
+
+
+    /**
+     * Time the processing that is performed by all subsequent filters in the
+     * current filter stack, including the ultimately invoked servlet.
+     *
+     * @param request The servlet request we are processing
+     * @param result The servlet response we are creating
+     * @param chain The filter chain we are processing
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain)
+	throws IOException, ServletException {
+
+	// Store ourselves as a request attribute (if requested)
+	if (attribute != null)
+	    request.setAttribute(attribute, this);
+
+	// Time and log the subsequent processing
+	long startTime = System.currentTimeMillis();
+        chain.doFilter(request, response);
+	long stopTime = System.currentTimeMillis();
+	filterConfig.getServletContext().log
+	    (this.toString() + ": " + (stopTime - startTime) +
+	     " milliseconds");
+
+    }
+
+
+    /**
+     * Place this filter into service.
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+	this.filterConfig = filterConfig;
+        this.attribute = filterConfig.getInitParameter("attribute");
+
+    }
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+	if (filterConfig == null)
+	    return ("InvokerFilter()");
+	StringBuffer sb = new StringBuffer("InvokerFilter(");
+	sb.append(filterConfig);
+	sb.append(")");
+	return (sb.toString());
+
+    }
+
+
+}
+
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/filters/RequestDumperFilter.java b/servletapi/jsr154/examples/WEB-INF/classes/filters/RequestDumperFilter.java
new file mode 100644
index 0000000..0073705
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/filters/RequestDumperFilter.java
@@ -0,0 +1,200 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package filters;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.sql.Timestamp;
+import java.util.Enumeration;
+import java.util.Locale;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * Example filter that dumps interesting state information about a request
+ * to the associated servlet context log file, before allowing the servlet
+ * to process the request in the usual way.  This can be installed as needed
+ * to assist in debugging problems.
+ *
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class RequestDumperFilter implements Filter {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The filter configuration object we are associated with.  If this value
+     * is null, this filter instance is not currently configured.
+     */
+    private FilterConfig filterConfig = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Take this filter out of service.
+     */
+    public void destroy() {
+
+        this.filterConfig = null;
+
+    }
+
+
+    /**
+     * Time the processing that is performed by all subsequent filters in the
+     * current filter stack, including the ultimately invoked servlet.
+     *
+     * @param request The servlet request we are processing
+     * @param result The servlet response we are creating
+     * @param chain The filter chain we are processing
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain)
+	throws IOException, ServletException {
+
+        if (filterConfig == null)
+	    return;
+
+	// Render the generic servlet request properties
+	StringWriter sw = new StringWriter();
+	PrintWriter writer = new PrintWriter(sw);
+	writer.println("Request Received at " +
+		       (new Timestamp(System.currentTimeMillis())));
+	writer.println(" characterEncoding=" + request.getCharacterEncoding());
+	writer.println("     contentLength=" + request.getContentLength());
+	writer.println("       contentType=" + request.getContentType());
+	writer.println("            locale=" + request.getLocale());
+	writer.print("           locales=");
+	Enumeration locales = request.getLocales();
+	boolean first = true;
+	while (locales.hasMoreElements()) {
+	    Locale locale = (Locale) locales.nextElement();
+	    if (first)
+	        first = false;
+	    else
+	        writer.print(", ");
+	    writer.print(locale.toString());
+	}
+	writer.println();
+	Enumeration names = request.getParameterNames();
+	while (names.hasMoreElements()) {
+	    String name = (String) names.nextElement();
+	    writer.print("         parameter=" + name + "=");
+	    String values[] = request.getParameterValues(name);
+	    for (int i = 0; i < values.length; i++) {
+	        if (i > 0)
+		    writer.print(", ");
+		writer.print(values[i]);
+	    }
+	    writer.println();
+	}
+	writer.println("          protocol=" + request.getProtocol());
+	writer.println("        remoteAddr=" + request.getRemoteAddr());
+	writer.println("        remoteHost=" + request.getRemoteHost());
+	writer.println("            scheme=" + request.getScheme());
+	writer.println("        serverName=" + request.getServerName());
+	writer.println("        serverPort=" + request.getServerPort());
+	writer.println("          isSecure=" + request.isSecure());
+
+	// Render the HTTP servlet request properties
+	if (request instanceof HttpServletRequest) {
+	    writer.println("---------------------------------------------");
+	    HttpServletRequest hrequest = (HttpServletRequest) request;
+	    writer.println("       contextPath=" + hrequest.getContextPath());
+	    Cookie cookies[] = hrequest.getCookies();
+            if (cookies == null)
+                cookies = new Cookie[0];
+	    for (int i = 0; i < cookies.length; i++) {
+	        writer.println("            cookie=" + cookies[i].getName() +
+			       "=" + cookies[i].getValue());
+	    }
+	    names = hrequest.getHeaderNames();
+	    while (names.hasMoreElements()) {
+	        String name = (String) names.nextElement();
+		String value = hrequest.getHeader(name);
+	        writer.println("            header=" + name + "=" + value);
+	    }
+	    writer.println("            method=" + hrequest.getMethod());
+	    writer.println("          pathInfo=" + hrequest.getPathInfo());
+	    writer.println("       queryString=" + hrequest.getQueryString());
+	    writer.println("        remoteUser=" + hrequest.getRemoteUser());
+	    writer.println("requestedSessionId=" +
+			   hrequest.getRequestedSessionId());
+	    writer.println("        requestURI=" + hrequest.getRequestURI());
+	    writer.println("       servletPath=" + hrequest.getServletPath());
+	}
+	writer.println("=============================================");
+
+	// Log the resulting string
+	writer.flush();
+	filterConfig.getServletContext().log(sw.getBuffer().toString());
+
+	// Pass control on to the next filter
+        chain.doFilter(request, response);
+
+    }
+
+
+    /**
+     * Place this filter into service.
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+	this.filterConfig = filterConfig;
+
+    }
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+	if (filterConfig == null)
+	    return ("RequestDumperFilter()");
+	StringBuffer sb = new StringBuffer("RequestDumperFilter(");
+	sb.append(filterConfig);
+	sb.append(")");
+	return (sb.toString());
+
+    }
+
+
+}
+
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java b/servletapi/jsr154/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java
new file mode 100644
index 0000000..bd57cac
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java
@@ -0,0 +1,171 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package filters;
+
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.UnavailableException;
+
+
+/**
+ * <p>Example filter that sets the character encoding to be used in parsing the
+ * incoming request, either unconditionally or only if the client did not
+ * specify a character encoding.  Configuration of this filter is based on
+ * the following initialization parameters:</p>
+ * <ul>
+ * <li><strong>encoding</strong> - The character encoding to be configured
+ *     for this request, either conditionally or unconditionally based on
+ *     the <code>ignore</code> initialization parameter.  This parameter
+ *     is required, so there is no default.</li>
+ * <li><strong>ignore</strong> - If set to "true", any character encoding
+ *     specified by the client is ignored, and the value returned by the
+ *     <code>selectEncoding()</code> method is set.  If set to "false,
+ *     <code>selectEncoding()</code> is called <strong>only</strong> if the
+ *     client has not already specified an encoding.  By default, this
+ *     parameter is set to "true".</li>
+ * </ul>
+ *
+ * <p>Although this filter can be used unchanged, it is also easy to
+ * subclass it and make the <code>selectEncoding()</code> method more
+ * intelligent about what encoding to choose, based on characteristics of
+ * the incoming request (such as the values of the <code>Accept-Language</code>
+ * and <code>User-Agent</code> headers, or a value stashed in the current
+ * user's session.</p>
+ *
+ * @author Craig McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class SetCharacterEncodingFilter implements Filter {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The default character encoding to set for requests that pass through
+     * this filter.
+     */
+    protected String encoding = null;
+
+
+    /**
+     * The filter configuration object we are associated with.  If this value
+     * is null, this filter instance is not currently configured.
+     */
+    protected FilterConfig filterConfig = null;
+
+
+    /**
+     * Should a character encoding specified by the client be ignored?
+     */
+    protected boolean ignore = true;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Take this filter out of service.
+     */
+    public void destroy() {
+
+        this.encoding = null;
+        this.filterConfig = null;
+
+    }
+
+
+    /**
+     * Select and set (if specified) the character encoding to be used to
+     * interpret request parameters for this request.
+     *
+     * @param request The servlet request we are processing
+     * @param result The servlet response we are creating
+     * @param chain The filter chain we are processing
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain)
+	throws IOException, ServletException {
+
+        // Conditionally select and set the character encoding to be used
+        if (ignore || (request.getCharacterEncoding() == null)) {
+            String encoding = selectEncoding(request);
+            if (encoding != null)
+                request.setCharacterEncoding(encoding);
+        }
+
+	// Pass control on to the next filter
+        chain.doFilter(request, response);
+
+    }
+
+
+    /**
+     * Place this filter into service.
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+	this.filterConfig = filterConfig;
+        this.encoding = filterConfig.getInitParameter("encoding");
+        String value = filterConfig.getInitParameter("ignore");
+        if (value == null)
+            this.ignore = true;
+        else if (value.equalsIgnoreCase("true"))
+            this.ignore = true;
+        else if (value.equalsIgnoreCase("yes"))
+            this.ignore = true;
+        else
+            this.ignore = false;
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Select an appropriate character encoding to be used, based on the
+     * characteristics of the current request and/or filter initialization
+     * parameters.  If no character encoding should be set, return
+     * <code>null</code>.
+     * <p>
+     * The default implementation unconditionally returns the value configured
+     * by the <strong>encoding</strong> initialization parameter for this
+     * filter.
+     *
+     * @param request The servlet request we are processing
+     */
+    protected String selectEncoding(ServletRequest request) {
+
+        return (this.encoding);
+
+    }
+
+
+}
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/listeners/ContextListener.java b/servletapi/jsr154/examples/WEB-INF/classes/listeners/ContextListener.java
new file mode 100644
index 0000000..bf1e054
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/listeners/ContextListener.java
@@ -0,0 +1,155 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package listeners;
+
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+
+/**
+ * Example listener for context-related application events, which were
+ * introduced in the 2.3 version of the Servlet API.  This listener
+ * merely documents the occurrence of such events in the application log
+ * associated with our servlet context.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class ContextListener
+    implements ServletContextAttributeListener, ServletContextListener {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The servlet context with which we are associated.
+     */
+    private ServletContext context = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Record the fact that a servlet context attribute was added.
+     *
+     * @param event The servlet context attribute event
+     */
+    public void attributeAdded(ServletContextAttributeEvent event) {
+
+	log("attributeAdded('" + event.getName() + "', '" +
+	    event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a servlet context attribute was removed.
+     *
+     * @param event The servlet context attribute event
+     */
+    public void attributeRemoved(ServletContextAttributeEvent event) {
+
+	log("attributeRemoved('" + event.getName() + "', '" +
+	    event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a servlet context attribute was replaced.
+     *
+     * @param event The servlet context attribute event
+     */
+    public void attributeReplaced(ServletContextAttributeEvent event) {
+
+	log("attributeReplaced('" + event.getName() + "', '" +
+	    event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that this web application has been destroyed.
+     *
+     * @param event The servlet context event
+     */
+    public void contextDestroyed(ServletContextEvent event) {
+
+	log("contextDestroyed()");
+	this.context = null;
+
+    }
+
+
+    /**
+     * Record the fact that this web application has been initialized.
+     *
+     * @param event The servlet context event
+     */
+    public void contextInitialized(ServletContextEvent event) {
+
+	this.context = event.getServletContext();
+	log("contextInitialized()");
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Log a message to the servlet context application log.
+     *
+     * @param message Message to be logged
+     */
+    private void log(String message) {
+
+	if (context != null)
+	    context.log("ContextListener: " + message);
+	else
+	    System.out.println("ContextListener: " + message);
+
+    }
+
+
+    /**
+     * Log a message and associated exception to the servlet context
+     * application log.
+     *
+     * @param message Message to be logged
+     * @param throwable Exception to be logged
+     */
+    private void log(String message, Throwable throwable) {
+
+	if (context != null)
+	    context.log("ContextListener: " + message, throwable);
+	else {
+	    System.out.println("ContextListener: " + message);
+	    throwable.printStackTrace(System.out);
+	}
+
+    }
+
+
+}
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/listeners/SessionListener.java b/servletapi/jsr154/examples/WEB-INF/classes/listeners/SessionListener.java
new file mode 100644
index 0000000..013e91f
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/listeners/SessionListener.java
@@ -0,0 +1,182 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package listeners;
+
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+
+/**
+ * Example listener for context-related application events, which were
+ * introduced in the 2.3 version of the Servlet API.  This listener
+ * merely documents the occurrence of such events in the application log
+ * associated with our servlet context.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class SessionListener
+    implements ServletContextListener,
+	       HttpSessionAttributeListener, HttpSessionListener {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The servlet context with which we are associated.
+     */
+    private ServletContext context = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Record the fact that a servlet context attribute was added.
+     *
+     * @param event The session attribute event
+     */
+    public void attributeAdded(HttpSessionBindingEvent event) {
+
+	log("attributeAdded('" + event.getSession().getId() + "', '" +
+	    event.getName() + "', '" + event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a servlet context attribute was removed.
+     *
+     * @param event The session attribute event
+     */
+    public void attributeRemoved(HttpSessionBindingEvent event) {
+
+	log("attributeRemoved('" + event.getSession().getId() + "', '" +
+	    event.getName() + "', '" + event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a servlet context attribute was replaced.
+     *
+     * @param event The session attribute event
+     */
+    public void attributeReplaced(HttpSessionBindingEvent event) {
+
+	log("attributeReplaced('" + event.getSession().getId() + "', '" +
+	    event.getName() + "', '" + event.getValue() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that this web application has been destroyed.
+     *
+     * @param event The servlet context event
+     */
+    public void contextDestroyed(ServletContextEvent event) {
+
+	log("contextDestroyed()");
+	this.context = null;
+
+    }
+
+
+    /**
+     * Record the fact that this web application has been initialized.
+     *
+     * @param event The servlet context event
+     */
+    public void contextInitialized(ServletContextEvent event) {
+
+	this.context = event.getServletContext();
+	log("contextInitialized()");
+
+    }
+
+
+    /**
+     * Record the fact that a session has been created.
+     *
+     * @param event The session event
+     */
+    public void sessionCreated(HttpSessionEvent event) {
+
+	log("sessionCreated('" + event.getSession().getId() + "')");
+
+    }
+
+
+    /**
+     * Record the fact that a session has been destroyed.
+     *
+     * @param event The session event
+     */
+    public void sessionDestroyed(HttpSessionEvent event) {
+
+	log("sessionDestroyed('" + event.getSession().getId() + "')");
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Log a message to the servlet context application log.
+     *
+     * @param message Message to be logged
+     */
+    private void log(String message) {
+
+	if (context != null)
+	    context.log("SessionListener: " + message);
+	else
+	    System.out.println("SessionListener: " + message);
+
+    }
+
+
+    /**
+     * Log a message and associated exception to the servlet context
+     * application log.
+     *
+     * @param message Message to be logged
+     * @param throwable Exception to be logged
+     */
+    private void log(String message, Throwable throwable) {
+
+	if (context != null)
+	    context.log("SessionListener: " + message, throwable);
+	else {
+	    System.out.println("SessionListener: " + message);
+	    throwable.printStackTrace(System.out);
+	}
+
+    }
+
+
+}
diff --git a/servletapi/jsr154/examples/WEB-INF/classes/util/HTMLFilter.java b/servletapi/jsr154/examples/WEB-INF/classes/util/HTMLFilter.java
new file mode 100644
index 0000000..b72d352
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/classes/util/HTMLFilter.java
@@ -0,0 +1,68 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package util;
+
+/**
+ * HTML filter utility.
+ *
+ * @author Craig R. McClanahan
+ * @author Tim Tye
+ * @version $Revision$ $Date$
+ */
+
+public final class HTMLFilter {
+
+
+    /**
+     * Filter the specified message string for characters that are sensitive
+     * in HTML.  This avoids potential attacks caused by including JavaScript
+     * codes in the request URL that is often reported in error messages.
+     *
+     * @param message The message string to be filtered
+     */
+    public static String filter(String message) {
+
+        if (message == null)
+            return (null);
+
+        char content[] = new char[message.length()];
+        message.getChars(0, message.length(), content, 0);
+        StringBuffer result = new StringBuffer(content.length + 50);
+        for (int i = 0; i < content.length; i++) {
+            switch (content[i]) {
+            case '<':
+                result.append("&lt;");
+                break;
+            case '>':
+                result.append("&gt;");
+                break;
+            case '&':
+                result.append("&amp;");
+                break;
+            case '"':
+                result.append("&quot;");
+                break;
+            default:
+                result.append(content[i]);
+            }
+        }
+        return (result.toString());
+
+    }
+
+
+}
+
diff --git a/servletapi/jsr154/examples/WEB-INF/web.xml b/servletapi/jsr154/examples/WEB-INF/web.xml
new file mode 100644
index 0000000..600570f
--- /dev/null
+++ b/servletapi/jsr154/examples/WEB-INF/web.xml
@@ -0,0 +1,249 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!DOCTYPE web-app
+    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+    "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+
+    <display-name>Servlet 2.4 Examples</display-name>
+    <description>
+      Servlet 2.4 Examples.
+    </description>
+
+    <!-- Define servlet-mapped and path-mapped example filters -->
+    <filter>
+        <filter-name>Servlet Mapped Filter</filter-name>
+        <filter-class>filters.ExampleFilter</filter-class>
+	<init-param>
+	    <param-name>attribute</param-name>
+	    <param-value>filters.ExampleFilter.SERVLET_MAPPED</param-value>
+	</init-param>
+    </filter>
+    <filter>
+        <filter-name>Path Mapped Filter</filter-name>
+        <filter-class>filters.ExampleFilter</filter-class>
+	<init-param>
+	    <param-name>attribute</param-name>
+	    <param-value>filters.ExampleFilter.PATH_MAPPED</param-value>
+	</init-param>
+    </filter>
+    <filter>
+        <filter-name>Request Dumper Filter</filter-name>
+        <filter-class>filters.RequestDumperFilter</filter-class>
+    </filter>
+
+    <!-- Example filter to set character encoding on each request -->
+    <filter>
+        <filter-name>Set Character Encoding</filter-name>
+        <filter-class>filters.SetCharacterEncodingFilter</filter-class>
+        <init-param>
+            <param-name>encoding</param-name>
+            <param-value>EUC_JP</param-value>
+        </init-param>
+    </filter>
+
+    <filter>
+        <filter-name>Compression Filter</filter-name>
+        <filter-class>compressionFilters.CompressionFilter</filter-class>
+
+        <init-param>
+          <param-name>compressionThreshold</param-name>
+          <param-value>10</param-value>
+        </init-param>
+        <init-param>
+          <param-name>debug</param-name>
+          <param-value>0</param-value>
+        </init-param>
+    </filter>
+
+    <!-- Define filter mappings for the defined filters -->
+    <filter-mapping>
+        <filter-name>Servlet Mapped Filter</filter-name>
+	<servlet-name>invoker</servlet-name>
+    </filter-mapping>
+    <filter-mapping>
+        <filter-name>Path Mapped Filter</filter-name>
+	<url-pattern>/servlet/*</url-pattern>
+    </filter-mapping>
+
+
+<!-- Example filter mapping to apply the "Set Character Encoding" filter
+     to *all* requests processed by this web application -->
+<!--
+    <filter-mapping>
+        <filter-name>Set Character Encoding</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+-->
+
+<!--
+    <filter-mapping>
+      <filter-name>Compression Filter</filter-name>
+      <url-pattern>/CompressionTest</url-pattern>
+    </filter-mapping>
+-->
+
+<!--
+    <filter-mapping>
+        <filter-name>Request Dumper Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+-->
+
+    <!-- Define example application events listeners -->
+    <listener>
+        <listener-class>listeners.ContextListener</listener-class>
+    </listener>
+    <listener>
+        <listener-class>listeners.SessionListener</listener-class>
+    </listener>
+
+    <!-- Define servlets that are included in the example application -->
+
+    <servlet>
+        <servlet-name>CompressionFilterTestServlet</servlet-name>
+        <servlet-class>compressionFilters.CompressionFilterTestServlet</servlet-class>
+    </servlet>
+    <servlet>
+        <servlet-name>HelloWorldExample</servlet-name>
+        <servlet-class>HelloWorldExample</servlet-class>
+    </servlet>
+    <servlet>
+        <servlet-name>RequestInfoExample</servlet-name>
+        <servlet-class>RequestInfoExample</servlet-class>
+    </servlet>
+    <servlet>
+        <servlet-name>RequestHeaderExample</servlet-name>
+        <servlet-class>RequestHeaderExample</servlet-class>
+    </servlet>
+    <servlet>
+        <servlet-name>RequestParamExample</servlet-name>
+        <servlet-class>RequestParamExample</servlet-class>
+    </servlet>
+    <servlet>
+        <servlet-name>CookieExample</servlet-name>
+        <servlet-class>CookieExample</servlet-class>
+    </servlet>
+    <servlet>
+        <servlet-name>SessionExample</servlet-name>
+        <servlet-class>SessionExample</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>CompressionFilterTestServlet</servlet-name>
+        <url-pattern>/CompressionTest</url-pattern>
+    </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>HelloWorldExample</servlet-name>
+        <url-pattern>/servlet/HelloWorldExample</url-pattern>
+    </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>RequestInfoExample</servlet-name>
+        <url-pattern>/servlet/RequestInfoExample/*</url-pattern>
+    </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>RequestHeaderExample</servlet-name>
+        <url-pattern>/servlet/RequestHeaderExample</url-pattern>
+    </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>RequestParamExample</servlet-name>
+        <url-pattern>/servlet/RequestParamExample</url-pattern>
+    </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>CookieExample</servlet-name>
+        <url-pattern>/servlet/CookieExample</url-pattern>
+    </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>SessionExample</servlet-name>
+        <url-pattern>/servlet/SessionExample</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+      <display-name>Example Security Constraint</display-name>
+      <web-resource-collection>
+         <web-resource-name>Protected Area</web-resource-name>
+	 <!-- Define the context-relative URL(s) to be protected -->
+         <url-pattern>/jsp/security/protected/*</url-pattern>
+	 <!-- If you list http methods, only those methods are protected -->
+	 <http-method>DELETE</http-method>
+         <http-method>GET</http-method>
+         <http-method>POST</http-method>
+	 <http-method>PUT</http-method>
+      </web-resource-collection>
+      <auth-constraint>
+         <!-- Anyone with one of the listed roles may access this area -->
+         <role-name>tomcat</role-name>
+	 <role-name>role1</role-name>
+      </auth-constraint>
+    </security-constraint>
+
+    <!-- Default login configuration uses form-based authentication -->
+    <login-config>
+      <auth-method>FORM</auth-method>
+      <realm-name>Example Form-Based Authentication Area</realm-name>
+      <form-login-config>
+        <form-login-page>/jsp/security/protected/login.jsp</form-login-page>
+        <form-error-page>/jsp/security/protected/error.jsp</form-error-page>
+      </form-login-config>
+    </login-config>
+
+    <!-- Security roles referenced by this web application -->
+    <security-role>
+      <role-name>role1</role-name>
+    </security-role>
+    <security-role>
+      <role-name>tomcat</role-name>
+    </security-role>
+
+    <!-- Environment entry examples -->
+    <!--env-entry>
+      <env-entry-description>
+         The maximum number of tax exemptions allowed to be set.
+      </env-entry-description>
+      <env-entry-name>maxExemptions</env-entry-name>
+      <env-entry-value>15</env-entry-value>
+      <env-entry-type>java.lang.Integer</env-entry-type>
+    </env-entry-->
+    <env-entry>
+      <env-entry-name>minExemptions</env-entry-name>
+      <env-entry-value>1</env-entry-value>
+      <env-entry-type>java.lang.Integer</env-entry-type>
+    </env-entry>
+    <env-entry>
+      <env-entry-name>foo/name1</env-entry-name>
+      <env-entry-value>value1</env-entry-value>
+      <env-entry-type>java.lang.String</env-entry-type>
+    </env-entry>
+    <env-entry>
+      <env-entry-name>foo/bar/name2</env-entry-name>
+      <env-entry-value>true</env-entry-value>
+      <env-entry-type>java.lang.Boolean</env-entry-type>
+    </env-entry>
+    <env-entry>
+      <env-entry-name>name3</env-entry-name>
+      <env-entry-value>1</env-entry-value>
+      <env-entry-type>java.lang.Integer</env-entry-type>
+    </env-entry>
+    <env-entry>
+      <env-entry-name>foo/name4</env-entry-name>
+      <env-entry-value>10</env-entry-value>
+      <env-entry-type>java.lang.Integer</env-entry-type>
+    </env-entry>
+
+</web-app>
diff --git a/servletapi/jsr154/examples/cookies.html b/servletapi/jsr154/examples/cookies.html
new file mode 100644
index 0000000..f59ccba
--- /dev/null
+++ b/servletapi/jsr154/examples/cookies.html
@@ -0,0 +1,60 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="servlet/CookieExample"><img src="images/execute.gif" align="right" border="0"></a><a href="index.html"><img src="images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+<h3>Source Code for Cookie Example<font color="#0000FF"><br>
+  </font> </h3>
+<font color="#0000FF"></font> 
+<pre><font color="#0000FF">import</font> java.io.*;
+<font color="#0000FF">import</font> javax.servlet.*;
+<font color="#0000FF">import</font> javax.servlet.http.*;
+
+<font color="#0000FF">public class</font> CookieExample <font color="#0000FF">extends</font> HttpServlet {
+
+    <font color="#0000FF">public void</font> doGet(HttpServletRequest request, HttpServletResponse response)
+    <font color="#0000FF">throws</font> IOException, ServletException
+    {
+        response.setContentType(&quot;<font color="#009900">text/html</font>&quot;);
+        PrintWriter out = response.getWriter();
+        
+        <font color="#CC0000">// print out cookies</font>
+
+        Cookie[] cookies = request.getCookies();
+        for (int i = 0; i &lt; cookies.length; i++) {
+            Cookie c = cookies[i];
+            String name = c.getName();
+            String value = c.getValue();
+            out.println(name + &quot;<font color="#009900"> = </font>&quot; + value);
+        }
+
+        <font color="#CC0000">// set a cookie</font>
+
+        String name = request.getParameter(&quot;<font color="#009900">cookieName</font>&quot;);
+        if (name != null &amp;&amp; name.length() &gt; 0) {
+            String value = request.getParameter(&quot;<font color="#009900">cookieValue</font>&quot;);
+            Cookie c = new Cookie(name, value);
+            response.addCookie(c);
+        }
+    }
+}</pre>
+</body>
+</html>
diff --git a/servletapi/jsr154/examples/helloworld.html b/servletapi/jsr154/examples/helloworld.html
new file mode 100644
index 0000000..25bdf00
--- /dev/null
+++ b/servletapi/jsr154/examples/helloworld.html
@@ -0,0 +1,49 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="servlet/HelloWorldExample"><img src="images/execute.gif" align="right" border="0"></a><a href="index.html"><img src="images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+<h3>Source Code for HelloWorld Example<font color="#0000FF"><br>
+  </font> </h3>
+<font color="#0000FF"></font>
+<pre><font color="#0000FF">import</font> java.io.*;
+<font color="#0000FF">import</font> javax.servlet.*;
+<font color="#0000FF">import</font> javax.servlet.http.*;
+
+<font color="#0000FF">public class</font> HelloWorld <font color="#0000FF">extends</font> HttpServlet {
+
+    <font color="#0000FF">public void</font> doGet(HttpServletRequest request, HttpServletResponse response)
+    <font color="#0000FF">throws</font> IOException, ServletException
+    {
+        response.setContentType(&quot;<font color="#009900">text/html</font>&quot;);
+        PrintWriter out = response.getWriter();
+        out.println(&quot;<font color="#009900">&lt;html&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;head&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;title&gt;Hello World!&lt;/title&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;/head&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;body&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;h1&gt;Hello World!&lt;/h1&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;/body&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;/html&gt;</font>&quot;);
+    }
+}</pre>
+</body>
+</html>
diff --git a/servletapi/jsr154/examples/images/code.gif b/servletapi/jsr154/examples/images/code.gif
new file mode 100644
index 0000000..93af2cd
--- /dev/null
+++ b/servletapi/jsr154/examples/images/code.gif
Binary files differ
diff --git a/servletapi/jsr154/examples/images/execute.gif b/servletapi/jsr154/examples/images/execute.gif
new file mode 100644
index 0000000..f64d70f
--- /dev/null
+++ b/servletapi/jsr154/examples/images/execute.gif
Binary files differ
diff --git a/servletapi/jsr154/examples/images/return.gif b/servletapi/jsr154/examples/images/return.gif
new file mode 100644
index 0000000..af4f68f
--- /dev/null
+++ b/servletapi/jsr154/examples/images/return.gif
Binary files differ
diff --git a/servletapi/jsr154/examples/index.html b/servletapi/jsr154/examples/index.html
new file mode 100644
index 0000000..05a8ab2
--- /dev/null
+++ b/servletapi/jsr154/examples/index.html
@@ -0,0 +1,120 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="GENERATOR" content="Mozilla/4.61 [en] (WinNT; I) [Netscape]">
+   <meta name="Author" content="Anil K. Vijendran">
+   <title>Servlet Examples</title>
+</head>
+<body bgcolor="#FFFFFF">
+<b><font face="Arial, Helvetica, sans-serif"><font size=+2>Servlet
+Examples with Code</font></font></b>
+<p>This is a collection of examples which demonstrate some of the more
+frequently used parts of the Servlet API. Familiarity with the Java(tm)
+Programming Language is assumed.
+<p>These examples will only work when viewed via an http URL. They will
+not work if you are viewing these pages via a "file://..." URL. Please
+refer to the <i>README</i> file provide with this Tomcat release regarding
+how to configure and start the provided web server.
+<p>Wherever you see a form, enter some data and see how the servlet reacts.
+When playing with the Cookie and Session Examples, jump back to the Headers
+Example to see exactly what your browser is sending the server.
+<p>To navigate your way through the examples, the following icons will
+help:
+<br>&nbsp;
+<table BORDER=0 CELLSPACING=5 WIDTH="85%" >
+<tr VALIGN=TOP>
+<td WIDTH="30"><img SRC="images/execute.gif" ></td>
+
+<td>Execute the example</td>
+</tr>
+
+<tr VALIGN=TOP>
+<td WIDTH="30"><img SRC="images/code.gif" height=24 width=24></td>
+
+<td>Look at the source code for the example</td>
+</tr>
+
+<tr VALIGN=TOP>
+<td WIDTH="30"><img SRC="images/return.gif" height=24 width=24></td>
+
+<td>Return to this screen</td>
+</tr>
+</table>
+
+<p>Tip: To see the cookie interactions with your browser, try turning on
+the "notify when setting a cookie" option in your browser preferences.
+This will let you see when a session is created and give some feedback
+when looking at the cookie demo.
+<br>&nbsp;
+<table BORDER=0 CELLSPACING=5 WIDTH="85%" >
+<tr VALIGN=TOP>
+<td>Hello World</td>
+
+<td VALIGN=TOP WIDTH="30%"><a href="servlet/HelloWorldExample"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="servlet/HelloWorldExample">Execute</a></td>
+
+<td WIDTH="30%"><a href="helloworld.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="helloworld.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Request Info</td>
+
+<td WIDTH="30%"><a href="servlet/RequestInfoExample"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="servlet/RequestInfoExample">Execute</a></td>
+
+<td WIDTH="30%"><a href="reqinfo.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="reqinfo.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Request Headers</td>
+
+<td WIDTH="30%"><a href="servlet/RequestHeaderExample"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="servlet/RequestHeaderExample">Execute</a></td>
+
+<td WIDTH="30%"><a href="reqheaders.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="reqheaders.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Request Parameters</td>
+
+<td WIDTH="30%"><a href="servlet/RequestParamExample"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="servlet/RequestParamExample">Execute</a></td>
+
+<td WIDTH="30%"><a href="reqparams.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="reqparams.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Cookies</td>
+
+<td WIDTH="30%"><a href="servlet/CookieExample"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="servlet/CookieExample">Execute</a></td>
+
+<td WIDTH="30%"><a href="cookies.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="cookies.html">Source</a></td>
+</tr>
+
+<tr VALIGN=TOP>
+<td>Sessions</td>
+
+<td WIDTH="30%"><a href="servlet/SessionExample"><img SRC="images/execute.gif" HSPACE=4 BORDER=0  align=TOP></a><a href="servlet/SessionExample">Execute</a></td>
+
+<td WIDTH="30%"><a href="sessions.html"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP></a><a href="sessions.html">Source</a></td>
+</tr>
+</table>
+
+<p>Note: The source code for these examples does not contain all of the
+source code that is actually in the example, only the important sections
+of code. Code not important to understand the example has been removed
+for clarity.
+</body>
+</html>
diff --git a/servletapi/jsr154/examples/reqheaders.html b/servletapi/jsr154/examples/reqheaders.html
new file mode 100644
index 0000000..c9c2391
--- /dev/null
+++ b/servletapi/jsr154/examples/reqheaders.html
@@ -0,0 +1,48 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="servlet/RequestHeaderExample"><img src="images/execute.gif" align="right" border="0"></a><a href="index.html"><img src="images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+<h3>Source Code for RequestHeader Example<font color="#0000FF"><br>
+  </font> </h3>
+<font color="#0000FF"></font>
+<pre><font color="#0000FF">import</font> java.io.*;
+<font color="#0000FF">import</font> java.util.*;
+<font color="#0000FF">import</font> javax.servlet.*;
+<font color="#0000FF">import</font> javax.servlet.http.*;
+
+<font color="#0000FF">public class</font> RequestHeaderExample <font color="#0000FF">extends</font> HttpServlet {
+
+    <font color="#0000FF">public void</font> doGet(HttpServletRequest request, HttpServletResponse response)
+    <font color="#0000FF">throws</font> IOException, ServletException
+    {
+        response.setContentType(&quot;<font color="#009900">text/html</font>&quot;);
+        PrintWriter out = response.getWriter();
+        Enumeration e = request.getHeaderNames();
+        while (e.hasMoreElements()) {
+            String name = (String)e.nextElement();
+            String value = request.getHeader(name);
+            out.println(name + &quot;<font color="#009900"> = </font>&quot; + value);
+        }
+    }
+}</pre>
+</body>
+</html>
diff --git a/servletapi/jsr154/examples/reqinfo.html b/servletapi/jsr154/examples/reqinfo.html
new file mode 100644
index 0000000..1d926b7
--- /dev/null
+++ b/servletapi/jsr154/examples/reqinfo.html
@@ -0,0 +1,67 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="servlet/RequestInfoExample"><img src="images/execute.gif" align="right" border="0"></a><a href="index.html"><img src="images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+<h3>Source Code for Request Info Example<font color="#0000FF"><br>
+  </font> </h3>
+<font color="#0000FF"></font> 
+<pre><font color="#0000FF">import</font> java.io.*;
+<font color="#0000FF">import</font> javax.servlet.*;
+<font color="#0000FF">import</font> javax.servlet.http.*;
+
+<font color="#0000FF">public class</font> RequestInfo <font color="#0000FF">extends</font> HttpServlet {
+
+    <font color="#0000FF">public void</font> doGet(HttpServletRequest request, HttpServletResponse response)
+    <font color="#0000FF">throws</font> IOException, ServletException
+    {
+        response.setContentType(&quot;<font color="#009900">text/html</font>&quot;);
+        PrintWriter out = response.getWriter();
+        out.println(&quot;<font color="#009900">&lt;html&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;body&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;head&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;title&gt;Request Information Example&lt;/title&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;/head&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;body&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;h3&gt;Request Information Example&lt;/h3&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">Method: </font>&quot; + request.getMethod());
+        out.println(&quot;<font color="#009900">Request URI: </font>&quot; + request.getRequestURI());
+        out.println(&quot;<font color="#009900">Protocol: </font>&quot; + request.getProtocol());
+        out.println(&quot;<font color="#009900">PathInfo: </font>&quot; + request.getPathInfo());
+        out.println(&quot;<font color="#009900">Remote Address: </font>&quot; + request.getRemoteAddr());
+        out.println(&quot;<font color="#009900">&lt;/body&gt;</font>&quot;);
+        out.println(&quot;<font color="#009900">&lt;/html&gt;</font>&quot;);
+    }
+
+<font color="#FF0000">    /**
+     * We are going to perform the same operations for POST requests
+     * as for GET methods, so this method just sends the request to
+     * the doGet method.
+     */</font>
+
+    <font color="#0000FF">public void</font> doPost(HttpServletRequest request, HttpServletResponse response)
+    <font color="#0000FF">throws</font> IOException, ServletException
+    {
+        doGet(request, response);
+    }
+}</pre>
+</body>
+</html>
diff --git a/servletapi/jsr154/examples/reqparams.html b/servletapi/jsr154/examples/reqparams.html
new file mode 100644
index 0000000..381d7c7
--- /dev/null
+++ b/servletapi/jsr154/examples/reqparams.html
@@ -0,0 +1,55 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="servlet/RequestParamExample"><img src="images/execute.gif" align="right" border="0"></a><a href="index.html"><img src="images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+<h3>Source Code for Request Parameter Example<font color="#0000FF"><br>
+  </font> </h3>
+<font color="#0000FF"></font> 
+<pre><font color="#0000FF">import</font> java.io.*;
+<font color="#0000FF">import</font> java.util.*;
+<font color="#0000FF">import</font> javax.servlet.*;
+<font color="#0000FF">import</font> javax.servlet.http.*;
+
+<font color="#0000FF">public class</font> RequestParamExample <font color="#0000FF">extends</font> HttpServlet {
+
+    <font color="#0000FF">public void</font> doGet(HttpServletRequest request, HttpServletResponse response)
+    <font color="#0000FF">throws</font> IOException, ServletException
+    {
+        response.setContentType(&quot;<font color="#009900">text/html</font>&quot;);
+        PrintWriter out = response.getWriter();
+        out.println(&quot;GET Request. No Form Data Posted&quot;);
+    }
+
+    <font color="#0000FF">public void</font> doPost(HttpServletRequest request, HttpServletResponse res)
+    <font color="#0000FF">throws</font> IOException, ServletException
+    {
+        Enumeration e = request.getParameterNames();
+	PrintWriter out = res.getWriter ();
+        while (e.hasMoreElements()) {
+            String name = (String)e.nextElement();
+            String value = request.getParameter(name);
+            out.println(name + &quot;<font color="#009900"> = </font>&quot; + value);
+        }
+    }
+}</pre>
+</body>
+</html>
diff --git a/servletapi/jsr154/examples/sessions.html b/servletapi/jsr154/examples/sessions.html
new file mode 100644
index 0000000..779d684
--- /dev/null
+++ b/servletapi/jsr154/examples/sessions.html
@@ -0,0 +1,69 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<p><font color="#0000FF"><a href="servlet/SessionExample"><img src="images/execute.gif" align="right" border="0"></a><a href="index.html"><img src="images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+<h3>Source Code for Session Example<font color="#0000FF"><br>
+  </font> </h3>
+<font color="#0000FF"></font> 
+<pre><font color="#0000FF">import</font> java.io.*;
+<font color="#0000FF">import</font> java.util.*;
+<font color="#0000FF">import</font> javax.servlet.*;
+<font color="#0000FF">import</font> javax.servlet.http.*;
+
+<font color="#0000FF">public class</font> SessionExample <font color="#0000FF">extends</font> HttpServlet {
+
+    <font color="#0000FF">public void</font> doGet(HttpServletRequest request, HttpServletResponse response)
+    <font color="#0000FF">throws</font> IOException, ServletException
+    {
+        response.setContentType(&quot;<font color="#009900">text/html</font>&quot;);
+        PrintWriter out = response.getWriter();
+        
+        HttpSession session = request.getSession(true);
+
+        <font color="#CC0000">// print session info</font>
+
+        Date created = new Date(session.getCreationTime());
+        Date accessed = new Date(session.getLastAccessedTime());
+        out.println(&quot;<font color="#009900">ID </font>&quot; + session.getId());
+        out.println(&quot;<font color="#009900">Created: </font>&quot; + created);
+        out.println(&quot;<font color="#009900">Last Accessed: </font>&quot; + accessed);
+
+        <font color="#CC0000">// set session info if needed</font>
+
+        String dataName = request.getParameter(&quot;<font color="#009900">dataName</font>&quot;);
+        if (dataName != null &amp;&amp; dataName.length() &gt; 0) {
+            String dataValue = request.getParameter(&quot;<font color="#009900">dataValue</font>&quot;);
+            session.setAttribute(dataName, dataValue);
+        }
+
+        // print session contents
+
+        Enumeration e = session.getAttributeNames();
+        while (e.hasMoreElements()) {
+            String name = (String)e.nextElement();
+            String value = session.getAttribute(name).toString();
+            out.println(name + &quot; <font color="#009900">= </font>&quot; + value);
+        }
+    }
+}</pre>
+</body>
+</html>
diff --git a/servletapi/jsr154/src/etc/manifest b/servletapi/jsr154/src/etc/manifest
new file mode 100644
index 0000000..0b57698
--- /dev/null
+++ b/servletapi/jsr154/src/etc/manifest
@@ -0,0 +1,10 @@
+Manifest-version: 1.0
+
+Name: javax/servlet/
+Specification-Title: Java API for Servlets
+Specification-Version: 2.4
+Specification-Vendor: Sun Microsystems, Inc.
+Implementation-Title: javax.servlet
+Implementation-Version: 2.4.@implementation.revision@
+Implementation-Vendor: Apache Software Foundation
+
diff --git a/servletapi/jsr154/src/share/dtd/XMLSchema.dtd b/servletapi/jsr154/src/share/dtd/XMLSchema.dtd
new file mode 100644
index 0000000..7b7ddbe
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/XMLSchema.dtd
@@ -0,0 +1,417 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- DTD for XML Schemas: Part 1: Structures
+     Public Identifier: "-//W3C//DTD XMLSCHEMA 200102//EN"
+     Official Location: http://www.w3.org/2001/XMLSchema.dtd -->
+<!-- $Id$ -->
+<!-- Note this DTD is NOT normative, or even definitive. -->           <!--d-->
+<!-- prose copy in the structures REC is the definitive version -->    <!--d-->
+<!-- (which shouldn't differ from this one except for this -->         <!--d-->
+<!-- comment and entity expansions, but just in case) -->              <!--d-->
+<!-- With the exception of cases with multiple namespace
+     prefixes for the XML Schema namespace, any XML document which is
+     not valid per this DTD given redefinitions in its internal subset of the
+     'p' and 's' parameter entities below appropriate to its namespace
+     declaration of the XML Schema namespace is almost certainly not
+     a valid schema. -->
+
+<!-- The simpleType element and its constituent parts
+     are defined in XML Schema: Part 2: Datatypes -->
+<!ENTITY % xs-datatypes PUBLIC 'datatypes' 'datatypes.dtd' >
+
+<!ENTITY % p 'xs:'> <!-- can be overriden in the internal subset of a
+                         schema document to establish a different
+                         namespace prefix -->
+<!ENTITY % s ':xs'> <!-- if %p is defined (e.g. as foo:) then you must
+                         also define %s as the suffix for the appropriate
+                         namespace declaration (e.g. :foo) -->
+<!ENTITY % nds 'xmlns%s;'>
+
+<!-- Define all the element names, with optional prefix -->
+<!ENTITY % schema "%p;schema">
+<!ENTITY % complexType "%p;complexType">
+<!ENTITY % complexContent "%p;complexContent">
+<!ENTITY % simpleContent "%p;simpleContent">
+<!ENTITY % extension "%p;extension">
+<!ENTITY % element "%p;element">
+<!ENTITY % unique "%p;unique">
+<!ENTITY % key "%p;key">
+<!ENTITY % keyref "%p;keyref">
+<!ENTITY % selector "%p;selector">
+<!ENTITY % field "%p;field">
+<!ENTITY % group "%p;group">
+<!ENTITY % all "%p;all">
+<!ENTITY % choice "%p;choice">
+<!ENTITY % sequence "%p;sequence">
+<!ENTITY % any "%p;any">
+<!ENTITY % anyAttribute "%p;anyAttribute">
+<!ENTITY % attribute "%p;attribute">
+<!ENTITY % attributeGroup "%p;attributeGroup">
+<!ENTITY % include "%p;include">
+<!ENTITY % import "%p;import">
+<!ENTITY % redefine "%p;redefine">
+<!ENTITY % notation "%p;notation">
+
+<!-- annotation elements -->
+<!ENTITY % annotation "%p;annotation">
+<!ENTITY % appinfo "%p;appinfo">
+<!ENTITY % documentation "%p;documentation">
+
+<!-- Customisation entities for the ATTLIST of each element type.
+     Define one of these if your schema takes advantage of the
+     anyAttribute='##other' in the schema for schemas -->
+
+<!ENTITY % schemaAttrs ''>
+<!ENTITY % complexTypeAttrs ''>
+<!ENTITY % complexContentAttrs ''>
+<!ENTITY % simpleContentAttrs ''>
+<!ENTITY % extensionAttrs ''>
+<!ENTITY % elementAttrs ''>
+<!ENTITY % groupAttrs ''>
+<!ENTITY % allAttrs ''>
+<!ENTITY % choiceAttrs ''>
+<!ENTITY % sequenceAttrs ''>
+<!ENTITY % anyAttrs ''>
+<!ENTITY % anyAttributeAttrs ''>
+<!ENTITY % attributeAttrs ''>
+<!ENTITY % attributeGroupAttrs ''>
+<!ENTITY % uniqueAttrs ''>
+<!ENTITY % keyAttrs ''>
+<!ENTITY % keyrefAttrs ''>
+<!ENTITY % selectorAttrs ''>
+<!ENTITY % fieldAttrs ''>
+<!ENTITY % includeAttrs ''>
+<!ENTITY % importAttrs ''>
+<!ENTITY % redefineAttrs ''>
+<!ENTITY % notationAttrs ''>
+<!ENTITY % annotationAttrs ''>
+<!ENTITY % appinfoAttrs ''>
+<!ENTITY % documentationAttrs ''>
+
+<!ENTITY % complexDerivationSet "CDATA">
+      <!-- #all or space-separated list drawn from derivationChoice -->
+<!ENTITY % blockSet "CDATA">
+      <!-- #all or space-separated list drawn from
+                      derivationChoice + 'substitution' -->
+
+<!ENTITY % mgs '%all; | %choice; | %sequence;'>
+<!ENTITY % cs '%choice; | %sequence;'>
+<!ENTITY % formValues '(qualified|unqualified)'>
+
+
+<!ENTITY % attrDecls    '((%attribute;| %attributeGroup;)*,(%anyAttribute;)?)'>
+
+<!ENTITY % particleAndAttrs '((%mgs; | %group;)?, %attrDecls;)'>
+
+<!-- This is used in part2 -->
+<!ENTITY % restriction1 '((%mgs; | %group;)?)'>
+
+%xs-datatypes;
+
+<!-- the duplication below is to produce an unambiguous content model
+     which allows annotation everywhere -->
+<!ELEMENT %schema; ((%include; | %import; | %redefine; | %annotation;)*,
+                    ((%simpleType; | %complexType;
+                      | %element; | %attribute;
+                      | %attributeGroup; | %group;
+                      | %notation; ),
+                     (%annotation;)*)* )>
+<!ATTLIST %schema;
+   targetNamespace      %URIref;               #IMPLIED
+   version              CDATA                  #IMPLIED
+   %nds;                %URIref;               #FIXED 'http://www.w3.org/2001/XMLSchema'
+   xmlns                CDATA                  #IMPLIED
+   finalDefault         %complexDerivationSet; ''
+   blockDefault         %blockSet;             ''
+   id                   ID                     #IMPLIED
+   elementFormDefault   %formValues;           'unqualified'
+   attributeFormDefault %formValues;           'unqualified'
+   xml:lang             CDATA                  #IMPLIED
+   %schemaAttrs;>
+<!-- Note the xmlns declaration is NOT in the Schema for Schemas,
+     because at the Infoset level where schemas operate,
+     xmlns(:prefix) is NOT an attribute! -->
+<!-- The declaration of xmlns is a convenience for schema authors -->
+ 
+<!-- The id attribute here and below is for use in external references
+     from non-schemas using simple fragment identifiers.
+     It is NOT used for schema-to-schema reference, internal or
+     external. -->
+
+<!-- a type is a named content type specification which allows attribute
+     declarations-->
+<!-- -->
+
+<!ELEMENT %complexType; ((%annotation;)?,
+                         (%simpleContent;|%complexContent;|
+                          %particleAndAttrs;))>
+
+<!ATTLIST %complexType;
+          name      %NCName;                        #IMPLIED
+          id        ID                              #IMPLIED
+          abstract  %boolean;                       #IMPLIED
+          final     %complexDerivationSet;          #IMPLIED
+          block     %complexDerivationSet;          #IMPLIED
+          mixed (true|false) 'false'
+          %complexTypeAttrs;>
+
+<!-- particleAndAttrs is shorthand for a root type -->
+<!-- mixed is disallowed if simpleContent, overriden if complexContent
+     has one too. -->
+
+<!-- If anyAttribute appears in one or more referenced attributeGroups
+     and/or explicitly, the intersection of the permissions is used -->
+
+<!ELEMENT %complexContent; ((%annotation;)?, (%restriction;|%extension;))>
+<!ATTLIST %complexContent;
+          mixed (true|false) #IMPLIED
+          id    ID           #IMPLIED
+          %complexContentAttrs;>
+
+<!-- restriction should use the branch defined above, not the simple
+     one from part2; extension should use the full model  -->
+
+<!ELEMENT %simpleContent; ((%annotation;)?, (%restriction;|%extension;))>
+<!ATTLIST %simpleContent;
+          id    ID           #IMPLIED
+          %simpleContentAttrs;>
+
+<!-- restriction should use the simple branch from part2, not the 
+     one defined above; extension should have no particle  -->
+
+<!ELEMENT %extension; ((%annotation;)?, (%particleAndAttrs;))>
+<!ATTLIST %extension;
+          base  %QName;      #REQUIRED
+          id    ID           #IMPLIED
+          %extensionAttrs;>
+
+<!-- an element is declared by either:
+ a name and a type (either nested or referenced via the type attribute)
+ or a ref to an existing element declaration -->
+
+<!ELEMENT %element; ((%annotation;)?, (%complexType;| %simpleType;)?,
+                     (%unique; | %key; | %keyref;)*)>
+<!-- simpleType or complexType only if no type|ref attribute -->
+<!-- ref not allowed at top level -->
+<!ATTLIST %element;
+            name               %NCName;               #IMPLIED
+            id                 ID                     #IMPLIED
+            ref                %QName;                #IMPLIED
+            type               %QName;                #IMPLIED
+            minOccurs          %nonNegativeInteger;   #IMPLIED
+            maxOccurs          CDATA                  #IMPLIED
+            nillable           %boolean;              #IMPLIED
+            substitutionGroup  %QName;                #IMPLIED
+            abstract           %boolean;              #IMPLIED
+            final              %complexDerivationSet; #IMPLIED
+            block              %blockSet;             #IMPLIED
+            default            CDATA                  #IMPLIED
+            fixed              CDATA                  #IMPLIED
+            form               %formValues;           #IMPLIED
+            %elementAttrs;>
+<!-- type and ref are mutually exclusive.
+     name and ref are mutually exclusive, one is required -->
+<!-- In the absence of type AND ref, type defaults to type of
+     substitutionGroup, if any, else the ur-type, i.e. unconstrained -->
+<!-- default and fixed are mutually exclusive -->
+
+<!ELEMENT %group; ((%annotation;)?,(%mgs;)?)>
+<!ATTLIST %group; 
+          name        %NCName;               #IMPLIED
+          ref         %QName;                #IMPLIED
+          minOccurs   %nonNegativeInteger;   #IMPLIED
+          maxOccurs   CDATA                  #IMPLIED
+          id          ID                     #IMPLIED
+          %groupAttrs;>
+
+<!ELEMENT %all; ((%annotation;)?, (%element;)*)>
+<!ATTLIST %all;
+          minOccurs   (1)                    #IMPLIED
+          maxOccurs   (1)                    #IMPLIED
+          id          ID                     #IMPLIED
+          %allAttrs;>
+
+<!ELEMENT %choice; ((%annotation;)?, (%element;| %group;| %cs; | %any;)*)>
+<!ATTLIST %choice;
+          minOccurs   %nonNegativeInteger;   #IMPLIED
+          maxOccurs   CDATA                  #IMPLIED
+          id          ID                     #IMPLIED
+          %choiceAttrs;>
+
+<!ELEMENT %sequence; ((%annotation;)?, (%element;| %group;| %cs; | %any;)*)>
+<!ATTLIST %sequence;
+          minOccurs   %nonNegativeInteger;   #IMPLIED
+          maxOccurs   CDATA                  #IMPLIED
+          id          ID                     #IMPLIED
+          %sequenceAttrs;>
+
+<!-- an anonymous grouping in a model, or
+     a top-level named group definition, or a reference to same -->
+
+<!-- Note that if order is 'all', group is not allowed inside.
+     If order is 'all' THIS group must be alone (or referenced alone) at
+     the top level of a content model -->
+<!-- If order is 'all', minOccurs==maxOccurs==1 on element/any inside -->
+<!-- Should allow minOccurs=0 inside order='all' . . . -->
+
+<!ELEMENT %any; (%annotation;)?>
+<!ATTLIST %any;
+            namespace       CDATA                  '##any'
+            processContents (skip|lax|strict)      'strict'
+            minOccurs       %nonNegativeInteger;   '1'
+            maxOccurs       CDATA                  '1'
+            id              ID                     #IMPLIED
+            %anyAttrs;>
+
+<!-- namespace is interpreted as follows:
+                  ##any      - - any non-conflicting WFXML at all
+
+                  ##other    - - any non-conflicting WFXML from namespace other
+                                  than targetNamespace
+
+                  ##local    - - any unqualified non-conflicting WFXML/attribute
+                  one or     - - any non-conflicting WFXML from
+                  more URI        the listed namespaces
+                  references
+
+                  ##targetNamespace ##local may appear in the above list,
+                    with the obvious meaning -->
+
+<!ELEMENT %anyAttribute; (%annotation;)?>
+<!ATTLIST %anyAttribute;
+            namespace       CDATA              '##any'
+            processContents (skip|lax|strict)  'strict'
+            id              ID                 #IMPLIED
+            %anyAttributeAttrs;>
+<!-- namespace is interpreted as for 'any' above -->
+
+<!-- simpleType only if no type|ref attribute -->
+<!-- ref not allowed at top level, name iff at top level -->
+<!ELEMENT %attribute; ((%annotation;)?, (%simpleType;)?)>
+<!ATTLIST %attribute;
+          name      %NCName;      #IMPLIED
+          id        ID            #IMPLIED
+          ref       %QName;       #IMPLIED
+          type      %QName;       #IMPLIED
+          use       (prohibited|optional|required) #IMPLIED
+          default   CDATA         #IMPLIED
+          fixed     CDATA         #IMPLIED
+          form      %formValues;  #IMPLIED
+          %attributeAttrs;>
+<!-- type and ref are mutually exclusive.
+     name and ref are mutually exclusive, one is required -->
+<!-- default for use is optional when nested, none otherwise -->
+<!-- default and fixed are mutually exclusive -->
+<!-- type attr and simpleType content are mutually exclusive -->
+
+<!-- an attributeGroup is a named collection of attribute decls, or a
+     reference thereto -->
+<!ELEMENT %attributeGroup; ((%annotation;)?,
+                       (%attribute; | %attributeGroup;)*,
+                       (%anyAttribute;)?) >
+<!ATTLIST %attributeGroup;
+                 name       %NCName;       #IMPLIED
+                 id         ID             #IMPLIED
+                 ref        %QName;        #IMPLIED
+                 %attributeGroupAttrs;>
+
+<!-- ref iff no content, no name.  ref iff not top level -->
+
+<!-- better reference mechanisms -->
+<!ELEMENT %unique; ((%annotation;)?, %selector;, (%field;)+)>
+<!ATTLIST %unique;
+          name     %NCName;       #REQUIRED
+	  id       ID             #IMPLIED
+	  %uniqueAttrs;>
+
+<!ELEMENT %key;    ((%annotation;)?, %selector;, (%field;)+)>
+<!ATTLIST %key;
+          name     %NCName;       #REQUIRED
+	  id       ID             #IMPLIED
+	  %keyAttrs;>
+
+<!ELEMENT %keyref; ((%annotation;)?, %selector;, (%field;)+)>
+<!ATTLIST %keyref;
+          name     %NCName;       #REQUIRED
+	  refer    %QName;        #REQUIRED
+	  id       ID             #IMPLIED
+	  %keyrefAttrs;>
+
+<!ELEMENT %selector; ((%annotation;)?)>
+<!ATTLIST %selector;
+          xpath %XPathExpr; #REQUIRED
+          id    ID          #IMPLIED
+          %selectorAttrs;>
+<!ELEMENT %field; ((%annotation;)?)>
+<!ATTLIST %field;
+          xpath %XPathExpr; #REQUIRED
+          id    ID          #IMPLIED
+          %fieldAttrs;>
+
+<!-- Schema combination mechanisms -->
+<!ELEMENT %include; (%annotation;)?>
+<!ATTLIST %include;
+          schemaLocation %URIref; #REQUIRED
+          id             ID       #IMPLIED
+          %includeAttrs;>
+
+<!ELEMENT %import; (%annotation;)?>
+<!ATTLIST %import;
+          namespace      %URIref; #IMPLIED
+          schemaLocation %URIref; #IMPLIED
+          id             ID       #IMPLIED
+          %importAttrs;>
+
+<!ELEMENT %redefine; (%annotation; | %simpleType; | %complexType; |
+                      %attributeGroup; | %group;)*>
+<!ATTLIST %redefine;
+          schemaLocation %URIref; #REQUIRED
+          id             ID       #IMPLIED
+          %redefineAttrs;>
+
+<!ELEMENT %notation; (%annotation;)?>
+<!ATTLIST %notation;
+	  name        %NCName;    #REQUIRED
+	  id          ID          #IMPLIED
+	  public      CDATA       #REQUIRED
+	  system      %URIref;    #IMPLIED
+	  %notationAttrs;>
+
+<!-- Annotation is either application information or documentation -->
+<!-- By having these here they are available for datatypes as well
+     as all the structures elements -->
+
+<!ELEMENT %annotation; (%appinfo; | %documentation;)*>
+<!ATTLIST %annotation; %annotationAttrs;>
+
+<!-- User must define annotation elements in internal subset for this
+     to work -->
+<!ELEMENT %appinfo; ANY>   <!-- too restrictive -->
+<!ATTLIST %appinfo;
+          source     %URIref;      #IMPLIED
+          id         ID         #IMPLIED
+          %appinfoAttrs;>
+<!ELEMENT %documentation; ANY>   <!-- too restrictive -->
+<!ATTLIST %documentation;
+          source     %URIref;   #IMPLIED
+          id         ID         #IMPLIED
+          xml:lang   CDATA      #IMPLIED
+          %documentationAttrs;>
+
+<!NOTATION XMLSchemaStructures PUBLIC
+           'structures' 'http://www.w3.org/2001/XMLSchema.xsd' >
+<!NOTATION XML PUBLIC
+           'REC-xml-1998-0210' 'http://www.w3.org/TR/1998/REC-xml-19980210' >
diff --git a/servletapi/jsr154/src/share/dtd/datatypes.dtd b/servletapi/jsr154/src/share/dtd/datatypes.dtd
new file mode 100644
index 0000000..2b7d185
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/datatypes.dtd
@@ -0,0 +1,218 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!--
+        DTD for XML Schemas: Part 2: Datatypes
+        $Id$
+        Note this DTD is NOT normative, or even definitive. - - the
+        prose copy in the datatypes REC is the definitive version
+        (which shouldn't differ from this one except for this comment
+        and entity expansions, but just in case)
+  -->
+
+<!--
+        This DTD cannot be used on its own, it is intended
+        only for incorporation in XMLSchema.dtd, q.v.
+  -->
+
+<!-- Define all the element names, with optional prefix -->
+<!ENTITY % simpleType "%p;simpleType">
+<!ENTITY % restriction "%p;restriction">
+<!ENTITY % list "%p;list">
+<!ENTITY % union "%p;union">
+<!ENTITY % maxExclusive "%p;maxExclusive">
+<!ENTITY % minExclusive "%p;minExclusive">
+<!ENTITY % maxInclusive "%p;maxInclusive">
+<!ENTITY % minInclusive "%p;minInclusive">
+<!ENTITY % totalDigits "%p;totalDigits">
+<!ENTITY % fractionDigits "%p;fractionDigits">
+<!ENTITY % length "%p;length">
+<!ENTITY % minLength "%p;minLength">
+<!ENTITY % maxLength "%p;maxLength">
+<!ENTITY % enumeration "%p;enumeration">
+<!ENTITY % whiteSpace "%p;whiteSpace">
+<!ENTITY % pattern "%p;pattern">
+
+<!--
+        Customisation entities for the ATTLIST of each element
+        type. Define one of these if your schema takes advantage
+        of the anyAttribute='##other' in the schema for schemas
+  -->
+
+<!ENTITY % simpleTypeAttrs "">
+<!ENTITY % restrictionAttrs "">
+<!ENTITY % listAttrs "">
+<!ENTITY % unionAttrs "">
+<!ENTITY % maxExclusiveAttrs "">
+<!ENTITY % minExclusiveAttrs "">
+<!ENTITY % maxInclusiveAttrs "">
+<!ENTITY % minInclusiveAttrs "">
+<!ENTITY % totalDigitsAttrs "">
+<!ENTITY % fractionDigitsAttrs "">
+<!ENTITY % lengthAttrs "">
+<!ENTITY % minLengthAttrs "">
+<!ENTITY % maxLengthAttrs "">
+<!ENTITY % enumerationAttrs "">
+<!ENTITY % whiteSpaceAttrs "">
+<!ENTITY % patternAttrs "">
+
+<!-- Define some entities for informative use as attribute
+        types -->
+<!ENTITY % URIref "CDATA">
+<!ENTITY % XPathExpr "CDATA">
+<!ENTITY % QName "NMTOKEN">
+<!ENTITY % QNames "NMTOKENS">
+<!ENTITY % NCName "NMTOKEN">
+<!ENTITY % nonNegativeInteger "NMTOKEN">
+<!ENTITY % boolean "(true|false)">
+<!ENTITY % simpleDerivationSet "CDATA">
+<!--
+        #all or space-separated list drawn from derivationChoice
+  -->
+
+<!--
+        Note that the use of 'facet' below is less restrictive
+        than is really intended:  There should in fact be no
+        more than one of each of minInclusive, minExclusive,
+        maxInclusive, maxExclusive, totalDigits, fractionDigits,
+        length, maxLength, minLength within datatype,
+        and the min- and max- variants of Inclusive and Exclusive
+        are mutually exclusive. On the other hand,  pattern and
+        enumeration may repeat.
+  -->
+<!ENTITY % minBound "(%minInclusive; | %minExclusive;)">
+<!ENTITY % maxBound "(%maxInclusive; | %maxExclusive;)">
+<!ENTITY % bounds "%minBound; | %maxBound;">
+<!ENTITY % numeric "%totalDigits; | %fractionDigits;">
+<!ENTITY % ordered "%bounds; | %numeric;">
+<!ENTITY % unordered
+   "%pattern; | %enumeration; | %whiteSpace; | %length; |
+   %maxLength; | %minLength;">
+<!ENTITY % facet "%ordered; | %unordered;">
+<!ENTITY % facetAttr 
+        "value CDATA #REQUIRED
+        id ID #IMPLIED">
+<!ENTITY % fixedAttr "fixed %boolean; #IMPLIED">
+<!ENTITY % facetModel "(%annotation;)?">
+<!ELEMENT %simpleType;
+        ((%annotation;)?, (%restriction; | %list; | %union;))>
+<!ATTLIST %simpleType;
+    name      %NCName; #IMPLIED
+    final     %simpleDerivationSet; #IMPLIED
+    id        ID       #IMPLIED
+    %simpleTypeAttrs;>
+<!-- name is required at top level -->
+<!ELEMENT %restriction; ((%annotation;)?,
+                         (%restriction1; |
+                          ((%simpleType;)?,(%facet;)*)),
+                         (%attrDecls;))>
+<!ATTLIST %restriction;
+    base      %QName;                  #IMPLIED
+    id        ID       #IMPLIED
+    %restrictionAttrs;>
+<!--
+        base and simpleType child are mutually exclusive,
+        one is required.
+
+        restriction is shared between simpleType and
+        simpleContent and complexContent (in XMLSchema.xsd).
+        restriction1 is for the latter cases, when this
+        is restricting a complex type, as is attrDecls.
+  -->
+<!ELEMENT %list; ((%annotation;)?,(%simpleType;)?)>
+<!ATTLIST %list;
+    itemType      %QName;             #IMPLIED
+    id        ID       #IMPLIED
+    %listAttrs;>
+<!--
+        itemType and simpleType child are mutually exclusive,
+        one is required
+  -->
+<!ELEMENT %union; ((%annotation;)?,(%simpleType;)*)>
+<!ATTLIST %union;
+    id            ID       #IMPLIED
+    memberTypes   %QNames;            #IMPLIED
+    %unionAttrs;>
+<!--
+        At least one item in memberTypes or one simpleType
+        child is required
+  -->
+
+<!ELEMENT %maxExclusive; %facetModel;>
+<!ATTLIST %maxExclusive;
+        %facetAttr;
+        %fixedAttr;
+        %maxExclusiveAttrs;>
+<!ELEMENT %minExclusive; %facetModel;>
+<!ATTLIST %minExclusive;
+        %facetAttr;
+        %fixedAttr;
+        %minExclusiveAttrs;>
+
+<!ELEMENT %maxInclusive; %facetModel;>
+<!ATTLIST %maxInclusive;
+        %facetAttr;
+        %fixedAttr;
+        %maxInclusiveAttrs;>
+<!ELEMENT %minInclusive; %facetModel;>
+<!ATTLIST %minInclusive;
+        %facetAttr;
+        %fixedAttr;
+        %minInclusiveAttrs;>
+
+<!ELEMENT %totalDigits; %facetModel;>
+<!ATTLIST %totalDigits;
+        %facetAttr;
+        %fixedAttr;
+        %totalDigitsAttrs;>
+<!ELEMENT %fractionDigits; %facetModel;>
+<!ATTLIST %fractionDigits;
+        %facetAttr;
+        %fixedAttr;
+        %fractionDigitsAttrs;>
+
+<!ELEMENT %length; %facetModel;>
+<!ATTLIST %length;
+        %facetAttr;
+        %fixedAttr;
+        %lengthAttrs;>
+<!ELEMENT %minLength; %facetModel;>
+<!ATTLIST %minLength;
+        %facetAttr;
+        %fixedAttr;
+        %minLengthAttrs;>
+<!ELEMENT %maxLength; %facetModel;>
+<!ATTLIST %maxLength;
+        %facetAttr;
+        %fixedAttr;
+        %maxLengthAttrs;>
+
+<!-- This one can be repeated -->
+<!ELEMENT %enumeration; %facetModel;>
+<!ATTLIST %enumeration;
+        %facetAttr;
+        %enumerationAttrs;>
+
+<!ELEMENT %whiteSpace; %facetModel;>
+<!ATTLIST %whiteSpace;
+        %facetAttr;
+        %fixedAttr;
+        %whiteSpaceAttrs;>
+
+<!-- This one can be repeated -->
+<!ELEMENT %pattern; %facetModel;>
+<!ATTLIST %pattern;
+        %facetAttr;
+        %patternAttrs;>
diff --git a/servletapi/jsr154/src/share/dtd/j2ee_1_4.xsd b/servletapi/jsr154/src/share/dtd/j2ee_1_4.xsd
new file mode 100644
index 0000000..0f9f2eb
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/j2ee_1_4.xsd
@@ -0,0 +1,1621 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<xsd:schema
+     targetNamespace="http://java.sun.com/xml/ns/j2ee"
+     xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
+     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+     elementFormDefault="qualified"
+     attributeFormDefault="unqualified"
+     version="1.4">
+  <xsd:annotation>
+    <xsd:documentation>
+      @(#)j2ee_1_4.xsds	1.43 03/09/16
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      Copyright 2002 Sun Microsystems, Inc., 901 San Antonio
+      Road, Palo Alto, California 94303, U.S.A. All rights
+      reserved.
+
+      Sun Microsystems, Inc. has intellectual property rights
+      relating to technology described in this document. In
+      particular, and without limitation, these intellectual
+      property rights may include one or more of the U.S. patents
+      listed at http://www.sun.com/patents and one or more
+      additional patents or pending patent applications in the
+      U.S. and other countries.
+
+      This document and the technology which it describes are
+      distributed under licenses restricting their use, copying,
+      distribution, and decompilation. No part of this document
+      may be reproduced in any form by any means without prior
+      written authorization of Sun and its licensors, if any.
+
+      Third-party software, including font technology, is
+      copyrighted and licensed from Sun suppliers.
+
+      Sun, Sun Microsystems, the Sun logo, Solaris, Java, J2EE,
+      JavaServer Pages, Enterprise JavaBeans and the Java Coffee
+      Cup logo are trademarks or registered trademarks of Sun
+      Microsystems, Inc. in the U.S. and other countries.
+
+      Federal Acquisitions: Commercial Software - Government Users
+      Subject to Standard License Terms and Conditions.
+
+    </xsd:documentation>
+  </xsd:annotation>
+<xsd:annotation>
+<xsd:documentation>
+
+The following definitions that appear in the common
+shareable schema(s) of J2EE deployment descriptors should be
+interpreted with respect to the context they are included:
+
+Deployment Component may indicate one of the following:
+    j2ee application;
+    application client;
+    web application;
+    enterprise bean;
+    resource adapter;
+
+Deployment File may indicate one of the following:
+    ear file;
+    war file;
+    jar file;
+    rar file;
+
+</xsd:documentation>
+</xsd:annotation>
+
+  <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
+	      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+
+  <xsd:include schemaLocation=
+	"http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd"/>
+
+
+<!-- **************************************************** -->
+
+  <xsd:group name="descriptionGroup">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This group keeps the usage of the contained description related
+	elements consistent across J2EE deployment descriptors.
+
+	All elements may occur multiple times with different languages,
+	to support localization of the content.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="display-name"
+		   type="j2ee:display-nameType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="icon"
+		   type="j2ee:iconType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+    </xsd:sequence>
+  </xsd:group>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="descriptionType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The description type is used by a description element to
+	provide text describing the parent element.  The elements
+	that use this type should include any information that the
+	Deployment Component's Deployment File file producer wants
+	to provide to the consumer of the Deployment Component's
+	Deployment File (i.e., to the Deployer). Typically, the
+	tools used by such a Deployment File consumer will display
+	the description when processing the parent element that
+	contains the description.
+
+	The lang attribute defines the language that the
+	description is provided in. The default value is "en" (English).
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="j2ee:xsdStringType">
+	<xsd:attribute ref="xml:lang"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:simpleType name="dewey-versionType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type defines a dewey decimal which is used
+	to describe versions of documents.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:restriction base="xsd:decimal">
+      <xsd:whiteSpace value="collapse"/>
+    </xsd:restriction>
+
+  </xsd:simpleType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="display-nameType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The display-name type contains a short name that is intended
+	  to be displayed by tools. It is used by display-name
+	  elements.  The display name need not be unique.
+
+	  Example:
+
+	  ...
+	     <display-name xml:lang="en">Employee Self Service</display-name>
+
+	  The value of the xml:lang attribute is "en" (English) by default.
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="j2ee:string">
+	<xsd:attribute ref="xml:lang"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="ejb-linkType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The ejb-linkType is used by ejb-link
+	  elements in the ejb-ref or ejb-local-ref elements to specify
+	  that an EJB reference is linked to enterprise bean.
+
+	  The value of the ejb-link element must be the ejb-name of an
+	  enterprise bean in the same ejb-jar file or in another ejb-jar
+	  file in the same J2EE application unit.
+
+	  Alternatively, the name in the ejb-link element may be
+	  composed of a path name specifying the ejb-jar containing the
+	  referenced enterprise bean with the ejb-name of the target
+	  bean appended and separated from the path name by "#".  The
+	  path name is relative to the Deployment File containing
+	  Deployment Component that is referencing the enterprise
+	  bean.  This allows multiple enterprise beans with the same
+	  ejb-name to be uniquely identified.
+
+	  Examples:
+
+	      <ejb-link>EmployeeRecord</ejb-link>
+
+	      <ejb-link>../products/product.jar#ProductEJB</ejb-link>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="ejb-local-refType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The ejb-local-refType is used by ejb-local-ref elements for
+	the declaration of a reference to an enterprise bean's local
+	home. The declaration consists of:
+
+	    - an optional description
+	    - the EJB reference name used in the code of the Deployment
+	      Component that's referencing the enterprise bean
+	    - the expected type of the referenced enterprise bean
+	    - the expected local home and local interfaces of the
+	      referenced enterprise bean
+	    - optional ejb-link information, used to specify the
+	      referenced enterprise bean
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="ejb-ref-name"
+		   type="j2ee:ejb-ref-nameType"/>
+      <xsd:element name="ejb-ref-type"
+		   type="j2ee:ejb-ref-typeType"/>
+      <xsd:element name="local-home"
+		   type="j2ee:local-homeType"/>
+      <xsd:element name="local"
+		   type="j2ee:localType"/>
+      <xsd:element name="ejb-link"
+		   type="j2ee:ejb-linkType"
+		   minOccurs="0"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="ejb-ref-nameType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The ejb-ref-name element contains the name of an EJB
+	  reference. The EJB reference is an entry in the
+	  Deployment Component's environment and is relative to the
+	  java:comp/env context.  The name must be unique within the
+	  Deployment Component.
+
+	  It is recommended that name is prefixed with "ejb/".
+
+	  Example:
+
+	  <ejb-ref-name>ejb/Payroll</ejb-ref-name>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:jndi-nameType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="ejb-ref-typeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The ejb-ref-typeType contains the expected type of the
+	referenced enterprise bean.
+
+	The ejb-ref-type designates a value
+	that must be one of the following:
+
+	    Entity
+	    Session
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="Entity"/>
+	<xsd:enumeration value="Session"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="ejb-refType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The ejb-refType is used by ejb-ref elements for the
+	declaration of a reference to an enterprise bean's home. The
+	declaration consists of:
+
+	    - an optional description
+	    - the EJB reference name used in the code of
+	      the Deployment Component that's referencing the enterprise
+	      bean
+	    - the expected type of the referenced enterprise bean
+	    - the expected home and remote interfaces of the referenced
+	      enterprise bean
+	    - optional ejb-link information, used to specify the
+	      referenced enterprise bean
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="ejb-ref-name"
+		   type="j2ee:ejb-ref-nameType"/>
+      <xsd:element name="ejb-ref-type"
+		   type="j2ee:ejb-ref-typeType"/>
+
+      <xsd:element name="home"
+		   type="j2ee:homeType"/>
+      <xsd:element name="remote"
+		   type="j2ee:remoteType"/>
+      <xsd:element name="ejb-link"
+		   type="j2ee:ejb-linkType"
+		   minOccurs="0"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="emptyType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type is used to designate an empty
+	element when used.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="env-entry-type-valuesType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  This type contains the fully-qualified Java type of the
+	  environment entry value that is expected by the
+	  application's code.
+
+	  The following are the legal values of env-entry-type-valuesType:
+
+	      java.lang.Boolean
+	      java.lang.Byte
+	      java.lang.Character
+	      java.lang.String
+	      java.lang.Short
+	      java.lang.Integer
+	      java.lang.Long
+	      java.lang.Float
+	      java.lang.Double
+
+	  Example:
+
+	  <env-entry-type>java.lang.Boolean</env-entry-type>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="java.lang.Boolean"/>
+	<xsd:enumeration value="java.lang.Byte"/>
+	<xsd:enumeration value="java.lang.Character"/>
+	<xsd:enumeration value="java.lang.String"/>
+	<xsd:enumeration value="java.lang.Short"/>
+	<xsd:enumeration value="java.lang.Integer"/>
+	<xsd:enumeration value="java.lang.Long"/>
+	<xsd:enumeration value="java.lang.Float"/>
+	<xsd:enumeration value="java.lang.Double"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="env-entryType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The env-entryType is used to declare an application's
+	environment entry. The declaration consists of an optional
+	description, the name of the environment entry, and an
+	optional value.  If a value is not specified, one must be
+	supplied during deployment.
+
+	It is used by env-entry elements.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="env-entry-name"
+		   type="j2ee:jndi-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+	    <![CDATA[
+
+	      The env-entry-name element contains the name of a
+	      Deployment Component's environment entry.  The name
+	      is a JNDI name relative to the java:comp/env
+	      context.  The name must be unique within a
+	      Deployment Component. The uniqueness
+	      constraints must be defined within the declared
+	      context.
+
+	      Example:
+
+	      <env-entry-name>minAmount</env-entry-name>
+
+	      ]]>
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="env-entry-type"
+		   type="j2ee:env-entry-type-valuesType"/>
+
+      <xsd:element name="env-entry-value"
+		   type="j2ee:xsdStringType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+	    <![CDATA[
+
+	      The env-entry-value designates the value of a
+	      Deployment Component's environment entry. The value
+	      must be a String that is valid for the
+	      constructor of the specified type that takes a
+	      single String parameter, or for java.lang.Character,
+	      a single character.
+
+	      Example:
+
+	      <env-entry-value>100.00</env-entry-value>
+
+	      ]]>
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="fully-qualified-classType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The elements that use this type designate the name of a
+	Java class or interface.  The name is in the form of a
+	"binary name", as defined in the JLS.  This is the form
+	of name used in Class.forName().  Tools that need the
+	canonical name (the name used in source code) will need
+	to convert this binary name to the canonical name.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="generic-booleanType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type defines four different values which can designate
+	boolean values. This includes values yes and no which are
+	not designated by xsd:boolean
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="true"/>
+	<xsd:enumeration value="false"/>
+	<xsd:enumeration value="yes"/>
+	<xsd:enumeration value="no"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="homeType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The homeType defines the fully-qualified name of
+	  an enterprise bean's home interface.
+
+	  Example:
+
+	      <home>com.aardvark.payroll.PayrollHome</home>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:fully-qualified-classType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="iconType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The icon type contains small-icon and large-icon elements
+	that specify the file names for small and large GIF or
+	JPEG icon images used to represent the parent element in a
+	GUI tool.
+
+	The xml:lang attribute defines the language that the
+	icon file names are provided in. Its value is "en" (English)
+	by default.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="small-icon" type="j2ee:pathType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+	    <![CDATA[
+
+	      The small-icon element contains the name of a file
+	      containing a small (16 x 16) icon image. The file
+	      name is a relative path within the Deployment
+	      Component's Deployment File.
+
+	      The image may be either in the JPEG or GIF format.
+	      The icon can be used by tools.
+
+	      Example:
+
+	      <small-icon>employee-service-icon16x16.jpg</small-icon>
+
+	      ]]>
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="large-icon" type="j2ee:pathType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+	    <![CDATA[
+
+	      The large-icon element contains the name of a file
+	      containing a large
+	      (32 x 32) icon image. The file name is a relative
+	      path within the Deployment Component's Deployment
+	      File.
+
+	      The image may be either in the JPEG or GIF format.
+	      The icon can be used by tools.
+
+	      Example:
+
+	      <large-icon>employee-service-icon32x32.jpg</large-icon>
+
+	      ]]>
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+    </xsd:sequence>
+
+    <xsd:attribute ref="xml:lang"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="java-identifierType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The java-identifierType defines a Java identifier.
+	The users of this type should further verify that
+	the content does not contain Java reserved keywords.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:pattern value="($|_|\p{L})(\p{L}|\p{Nd}|_|$)*"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="java-typeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This is a generic type that designates a Java primitive
+	type or a fully qualified name of a Java interface/type,
+	or an array of such types.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:pattern value="[^\p{Z}]*"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="jndi-nameType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The jndi-nameType type designates a JNDI name in the
+	Deployment Component's environment and is relative to the
+	java:comp/env context.  A JNDI name must be unique within the
+	Deployment Component.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:group name="jndiEnvironmentRefsGroup">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This group keeps the usage of the contained JNDI environment
+	reference elements consistent across J2EE deployment descriptors.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:element name="env-entry"
+		   type="j2ee:env-entryType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="ejb-ref"
+		   type="j2ee:ejb-refType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="ejb-local-ref"
+		   type="j2ee:ejb-local-refType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:group ref="j2ee:service-refGroup"/>
+      <xsd:element name="resource-ref"
+		   type="j2ee:resource-refType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="resource-env-ref"
+		   type="j2ee:resource-env-refType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="message-destination-ref"
+		   type="j2ee:message-destination-refType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+    </xsd:sequence>
+  </xsd:group>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="listenerType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The listenerType indicates the deployment properties for a web
+	application listener bean.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="listener-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The listener-class element declares a class in the
+	    application must be registered as a web
+	    application listener bean. The value is the fully
+	    qualified classname of the listener class.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="local-homeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The local-homeType defines the fully-qualified
+	name of an enterprise bean's local home interface.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:fully-qualified-classType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="localType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The localType defines the fully-qualified name of an
+	enterprise bean's local interface.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:fully-qualified-classType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="message-destination-linkType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The message-destination-linkType is used to link a message
+	destination reference or message-driven bean to a message
+	destination.
+
+	The Assembler sets the value to reflect the flow of messages
+	between producers and consumers in the application.
+
+	The value must be the message-destination-name of a message
+	destination in the same Deployment File or in another
+	Deployment File in the same J2EE application unit.
+
+	Alternatively, the value may be composed of a path name
+	specifying a Deployment File containing the referenced
+	message destination with the message-destination-name of the
+	destination appended and separated from the path name by
+	"#". The path name is relative to the Deployment File
+	containing Deployment Component that is referencing the
+	message destination.  This allows multiple message
+	destinations with the same name to be uniquely identified.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="message-destination-refType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The message-destination-ref element contains a declaration
+	  of Deployment Component's reference to a message destination
+	  associated with a resource in Deployment Component's
+	  environment. It consists of:
+
+		  - an optional description
+		  - the message destination reference name
+		  - the message destination type
+		  - a specification as to whether the
+		    destination is used for
+		    consuming or producing messages, or both
+		  - a link to the message destination
+
+	  Examples:
+
+	  <message-destination-ref>
+		  <message-destination-ref-name>jms/StockQueue
+		  </message-destination-ref-name>
+		  <message-destination-type>javax.jms.Queue
+		  </message-destination-type>
+		  <message-destination-usage>Consumes
+		  </message-destination-usage>
+		  <message-destination-link>CorporateStocks
+		  </message-destination-link>
+	  </message-destination-ref>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="message-destination-ref-name"
+		   type="j2ee:jndi-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The message-destination-ref-name element specifies
+	    the name of a message destination reference; its
+	    value is the environment entry name used in
+	    Deployment Component code.  The name is a JNDI name
+	    relative to the java:comp/env context and must be
+	    unique within an ejb-jar (for enterprise beans) or a
+	    Deployment File (for others).
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="message-destination-type"
+		   type="j2ee:message-destination-typeType"/>
+      <xsd:element name="message-destination-usage"
+		   type="j2ee:message-destination-usageType"/>
+      <xsd:element name="message-destination-link"
+		   type="j2ee:message-destination-linkType"
+		   minOccurs="0"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="message-destination-typeType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The message-destination-typeType specifies the type of
+	  the destination. The type is specified by the Java interface
+	  expected to be implemented by the destination.
+
+	  Example:
+
+	    <message-destination-type>javax.jms.Queue
+	    </message-destination-type>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:fully-qualified-classType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="message-destination-usageType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The message-destination-usageType specifies the use of the
+	message destination indicated by the reference.  The value
+	indicates whether messages are consumed from the message
+	destination, produced for the destination, or both.  The
+	Assembler makes use of this information in linking producers
+	of a destination with its consumers.
+
+	The value of the message-destination-usage element must be
+	one of the following:
+	    Consumes
+	    Produces
+	    ConsumesProduces
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="Consumes"/>
+	<xsd:enumeration value="Produces"/>
+	<xsd:enumeration value="ConsumesProduces"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="message-destinationType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The message-destinationType specifies a message
+	  destination. The logical destination described by this
+	  element is mapped to a physical destination by the Deployer.
+
+	  The message destination element contains:
+
+		  - an optional description
+		  - an optional display-name
+		  - an optional icon
+		  - a message destination name which must be unique
+		    among message destination names within the same
+		    Deployment File.
+
+	  Example:
+
+	  <message-destination>
+		  <message-destination-name>CorporateStocks
+		  </message-destination-name>
+	  </message-destination>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="message-destination-name"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The message-destination-name element specifies a
+	    name for a message destination.  This name must be
+	    unique among the names of message destinations
+	    within the Deployment File.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="param-valueType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type is a general type that can be used to declare
+	parameter/value lists.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="param-name"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The param-name element contains the name of a
+	    parameter.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="param-value"
+		   type="j2ee:xsdStringType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The param-value element contains the value of a
+	    parameter.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="pathType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The elements that use this type designate either a relative
+	path or an absolute path starting with a "/".
+
+	In elements that specify a pathname to a file within the
+	same Deployment File, relative filenames (i.e., those not
+	starting with "/") are considered relative to the root of
+	the Deployment File's namespace.  Absolute filenames (i.e.,
+	those starting with "/") also specify names in the root of
+	the Deployment File's namespace.  In general, relative names
+	are preferred.  The exception is .war files where absolute
+	names are preferred for consistency with the Servlet API.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="remoteType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The remote element contains the fully-qualified name
+	  of the enterprise bean's remote interface.
+
+	  Example:
+
+	      <remote>com.wombat.empl.EmployeeService</remote>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:fully-qualified-classType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="res-authType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The res-authType specifies whether the Deployment Component
+	code signs on programmatically to the resource manager, or
+	whether the Container will sign on to the resource manager
+	on behalf of the Deployment Component. In the latter case,
+	the Container uses information that is supplied by the
+	Deployer.
+
+	The value must be one of the two following:
+
+	    Application
+	    Container
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="Application"/>
+	<xsd:enumeration value="Container"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="res-sharing-scopeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The res-sharing-scope type specifies whether connections
+	obtained through the given resource manager connection
+	factory reference can be shared. The value, if specified,
+	must be one of the two following:
+
+	    Shareable
+	    Unshareable
+
+	The default value is Shareable.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="Shareable"/>
+	<xsd:enumeration value="Unshareable"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="resource-env-refType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The resource-env-refType is used to define
+	  resource-env-type elements.  It contains a declaration of a
+	  Deployment Component's reference to an administered object
+	  associated with a resource in the Deployment Component's
+	  environment.  It consists of an optional description, the
+	  resource environment reference name, and an indication of
+	  the resource environment reference type expected by the
+	  Deployment Component code.
+
+	  Example:
+
+	  <resource-env-ref>
+	      <resource-env-ref-name>jms/StockQueue
+	      </resource-env-ref-name>
+	      <resource-env-ref-type>javax.jms.Queue
+	      </resource-env-ref-type>
+	  </resource-env-ref>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="resource-env-ref-name"
+		   type="j2ee:jndi-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The resource-env-ref-name element specifies the name
+	    of a resource environment reference; its value is
+	    the environment entry name used in
+	    the Deployment Component code.  The name is a JNDI
+	    name relative to the java:comp/env context and must
+	    be unique within a Deployment Component.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="resource-env-ref-type"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The resource-env-ref-type element specifies the type
+	    of a resource environment reference.  It is the
+	    fully qualified name of a Java language class or
+	    interface.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="resource-refType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The resource-refType contains a declaration of a
+	  Deployment Component's reference to an external resource. It
+	  consists of an optional description, the resource manager
+	  connection factory reference name, the indication of the
+	  resource manager connection factory type expected by the
+	  Deployment Component code, the type of authentication
+	  (Application or Container), and an optional specification of
+	  the shareability of connections obtained from the resource
+	  (Shareable or Unshareable).
+
+	  Example:
+
+	  <resource-ref>
+	      <res-ref-name>jdbc/EmployeeAppDB</res-ref-name>
+	      <res-type>javax.sql.DataSource</res-type>
+	      <res-auth>Container</res-auth>
+	      <res-sharing-scope>Shareable</res-sharing-scope>
+	  </resource-ref>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="res-ref-name"
+		   type="j2ee:jndi-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The res-ref-name element specifies the name of a
+	    resource manager connection factory reference.
+	    The name is a JNDI name relative to the
+	    java:comp/env context.
+	    The name must be unique within a Deployment File.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="res-type"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The res-type element specifies the type of the data
+	    source. The type is specified by the fully qualified
+	    Java language class or interface
+	    expected to be implemented by the data source.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="res-auth"
+		   type="j2ee:res-authType"/>
+
+      <xsd:element name="res-sharing-scope"
+		   type="j2ee:res-sharing-scopeType"
+		   minOccurs="0"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="role-nameType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The role-nameType designates the name of a security role.
+
+	The name must conform to the lexical rules for a token.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="run-asType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The run-asType specifies the run-as identity to be
+	used for the execution of a component. It contains an
+	optional description, and the name of a security role.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="role-name"
+		   type="j2ee:role-nameType"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="security-role-refType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The security-role-refType contains the declaration of a
+	security role reference in a component's or a
+	Deployment Component's code. The declaration consists of an
+	optional description, the security role name used in the
+	code, and an optional link to a security role. If the
+	security role is not specified, the Deployer must choose an
+	appropriate security role.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="role-name"
+		   type="j2ee:role-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The value of the role-name element must be the String used
+	    as the parameter to the
+	    EJBContext.isCallerInRole(String roleName) method or the
+	    HttpServletRequest.isUserInRole(String role) method.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="role-link"
+		   type="j2ee:role-nameType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The role-link element is a reference to a defined
+	    security role. The role-link element must contain
+	    the name of one of the security roles defined in the
+	    security-role elements.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="security-roleType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The security-roleType contains the definition of a security
+	  role. The definition consists of an optional description of the
+	  security role, and the security role name.
+
+	  Example:
+
+	      <security-role>
+	      <description>
+		  This role includes all employees who are authorized
+		  to access the employee service application.
+	      </description>
+	      <role-name>employee</role-name>
+	      </security-role>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="role-name"
+		   type="j2ee:role-nameType"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="string">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This is a special string datatype that is defined by J2EE as
+	a base type for defining collapsed strings. When schemas
+	require trailing/leading space elimination as well as
+	collapsing the existing whitespace, this base type may be
+	used.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:token">
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="true-falseType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This simple type designates a boolean with only two
+	permissible values
+
+	- true
+	- false
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:xsdBooleanType">
+	<xsd:pattern value="(true|false)"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="url-patternType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The url-patternType contains the url pattern of the mapping.
+	It must follow the rules specified in Section 11.2 of the
+	Servlet API Specification. This pattern is assumed to be in
+	URL-decoded form and must not contain CR(#xD) or LF(#xA).
+	If it contains those characters, the container must inform
+	the developer with a descriptive error message.
+	The container must preserve all characters including whitespaces.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:string"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="xsdAnyURIType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type adds an "id" attribute to xsd:anyURI.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:anyURI">
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="xsdBooleanType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type adds an "id" attribute to xsd:boolean.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:boolean">
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="xsdIntegerType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type adds an "id" attribute to xsd:integer.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:integer">
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="xsdNMTOKENType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type adds an "id" attribute to xsd:NMTOKEN.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:NMTOKEN">
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="xsdNonNegativeIntegerType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type adds an "id" attribute to xsd:nonNegativeInteger.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:nonNegativeInteger">
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="xsdPositiveIntegerType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type adds an "id" attribute to xsd:positiveInteger.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:positiveInteger">
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="xsdQNameType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type adds an "id" attribute to xsd:QName.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:QName">
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="xsdStringType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type adds an "id" attribute to xsd:string.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:string">
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+</xsd:schema>
+
diff --git a/servletapi/jsr154/src/share/dtd/j2ee_web_services_1_1.xsd b/servletapi/jsr154/src/share/dtd/j2ee_web_services_1_1.xsd
new file mode 100644
index 0000000..dfb5bd2
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/j2ee_web_services_1_1.xsd
@@ -0,0 +1,504 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
+	    targetNamespace="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	    elementFormDefault="qualified"
+	    attributeFormDefault="unqualified"
+	    version="1.1">
+  <xsd:annotation>
+    <xsd:documentation>
+      @(#)j2ee_web_services_1_1.xsds	1.11 02/11/03
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      Copyright 2002 Sun Microsystems, Inc., 901 San Antonio
+      Road, Palo Alto, California 94303, U.S.A. All rights
+      reserved.
+
+      Sun Microsystems, Inc. has intellectual property rights
+      relating to technology described in this document. In
+      particular, and without limitation, these intellectual
+      property rights may include one or more of the U.S. patents
+      listed at http://www.sun.com/patents and one or more
+      additional patents or pending patent applications in the
+      U.S. and other countries.
+
+      This document and the technology which it describes are
+      distributed under licenses restricting their use, copying,
+      distribution, and decompilation. No part of this document
+      may be reproduced in any form by any means without prior
+      written authorization of Sun and its licensors, if any.
+
+      Third-party software, including font technology, is
+      copyrighted and licensed from Sun suppliers.
+
+      Sun, Sun Microsystems, the Sun logo, Solaris, Java, J2EE,
+      JavaServer Pages, Enterprise JavaBeans and the Java Coffee
+      Cup logo are trademarks or registered trademarks of Sun
+      Microsystems, Inc. in the U.S. and other countries.
+
+      Federal Acquisitions: Commercial Software - Government Users
+      Subject to Standard License Terms and Conditions.
+
+    </xsd:documentation>
+  </xsd:annotation>
+  <xsd:annotation>
+    <xsd:documentation>
+
+      (C) Copyright International Business Machines Corporation 2002
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+      <![CDATA[
+
+	The webservices element is the root element for the web services
+	deployment descriptor.  It specifies the set of web service
+	descriptions that are to be deployed into the J2EE Application
+	Server and the dependencies they have on container resources and
+	services.  The deployment descriptor must be named
+	"META-INF/webservices.xml" in the web services' jar file.
+
+	Used in: webservices.xml
+
+	All webservices deployment descriptors must indicate the
+	webservices schema by using the J2EE namespace:
+
+	http://java.sun.com/xml/ns/j2ee
+
+	and by indicating the version of the schema by using the version
+	element as shown below:
+
+	    <webservices xmlns="http://java.sun.com/xml/ns/j2ee"
+	      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
+		http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd"
+	      version="1.1">
+	      ...
+	    </webservices>
+
+	The instance documents may indicate the published version of the
+	schema using the xsi:schemaLocation attribute for the J2EE
+	namespace with the following location:
+
+	http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd
+
+	]]>
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      The following conventions apply to all J2EE
+      deployment descriptor elements unless indicated otherwise.
+
+      - In elements that specify a pathname to a file within the
+	same JAR file, relative filenames (i.e., those not
+	starting with "/") are considered relative to the root of
+	the JAR file's namespace.  Absolute filenames (i.e., those
+	starting with "/") also specify names in the root of the
+	JAR file's namespace.  In general, relative names are
+	preferred.  The exception is .war files where absolute
+	names are preferred for consistency with the Servlet API.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:include schemaLocation="j2ee_1_4.xsd"/>
+
+
+<!-- **************************************************** -->
+
+
+  <xsd:element name="webservices" type="j2ee:webservicesType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The webservices element is the root element for the web services
+	deployment descriptor.  It specifies the set of web service
+	descriptions that are to be deployed into the J2EE Application Server
+	and the dependencies they have on container resources and services.
+
+	Used in: webservices.xml
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:key name="webservice-description-name-key">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The webservice-description-name identifies the collection of
+	  port-components associated with a WSDL file and JAX-RPC mapping. The
+	  name must be unique within the deployment descriptor.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:webservice-description"/>
+      <xsd:field xpath="j2ee:webservice-description-name"/>
+    </xsd:key>
+  </xsd:element>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="port-componentType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The port-component element associates a WSDL port with a web service
+	interface and implementation.  It defines the name of the port as a
+	component, optional description, optional display name, optional iconic
+	representations, WSDL port QName, Service Endpoint Interface, Service
+	Implementation Bean.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0" maxOccurs="1"/>
+      <xsd:element name="display-name"
+		   type="j2ee:display-nameType"
+		   minOccurs="0" maxOccurs="1"/>
+      <xsd:element name="icon"
+		   type="j2ee:iconType"
+		   minOccurs="0" maxOccurs="1"/>
+      <xsd:element name="port-component-name"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+	    <![CDATA[
+
+	      The port-component-name element specifies a port component's
+	      name.  This name is assigned by the module producer to name
+	      the service implementation bean in the module's deployment
+	      descriptor. The name must be unique among the port component
+	      names defined in the same module.
+
+	      Used in: port-component
+
+	      Example:
+		      <port-component-name>EmployeeService
+		      </port-component-name>
+
+	      ]]>
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="wsdl-port"
+		   type="j2ee:xsdQNameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the name space and local name part of the WSDL port QName.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="service-endpoint-interface"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+	    <![CDATA[
+
+	      The service-endpoint-interface element contains the
+	      fully-qualified name of the port component's Service Endpoint
+	      Interface.
+
+	      Used in: port-component
+
+	      Example:
+		      <remote>com.wombat.empl.EmployeeService</remote>
+
+	      ]]>
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="service-impl-bean"
+		   type="j2ee:service-impl-beanType"/>
+
+      <xsd:element name="handler"
+		   type="j2ee:port-component_handlerType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="port-component_handlerType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	Declares the handler for a port-component. Handlers can access the
+	init-param name/value pairs using the HandlerInfo interface.
+
+	Used in: port-component
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="handler-name"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the name of the handler. The name must be unique within the
+	    module.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="handler-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines a fully qualified class name for the handler implementation.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="init-param"
+		   type="j2ee:param-valueType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+
+      <xsd:element name="soap-header"
+		   type="j2ee:xsdQNameType"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the QName of a SOAP header that will be processed by the
+	    handler.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="soap-role"
+		   type="j2ee:string"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The soap-role element contains a SOAP actor definition that the
+	    Handler will play as a role.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="service-impl-beanType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The service-impl-bean element defines the web service implementation.
+	A service implementation can be an EJB bean class or JAX-RPC web
+	component.  Existing EJB implementations are exposed as a web service
+	using an ejb-link.
+
+	Used in: port-component
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:choice>
+      <xsd:element name="ejb-link"
+		   type="j2ee:ejb-linkType"/>
+      <xsd:element name="servlet-link"
+		   type="j2ee:servlet-linkType"/>
+    </xsd:choice>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="servlet-linkType">
+    <xsd:annotation>
+      <xsd:documentation>
+	<![CDATA[
+
+	  The servlet-link element is used in the service-impl-bean element
+	  to specify that a Service Implementation Bean is defined as a
+	  JAX-RPC Service Endpoint.
+
+	  The value of the servlet-link element must be the servlet-name of
+	  a JAX-RPC Service Endpoint in the same WAR file.
+
+	  Used in: service-impl-bean
+
+	  Example:
+		  <servlet-link>StockQuoteService</servlet-link>
+
+	  ]]>
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="webservice-descriptionType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The webservice-description element defines a WSDL document file
+	and the set of Port components associated with the WSDL ports
+	defined in the WSDL document.  There may be multiple
+	webservice-descriptions defined within a module.
+
+	All WSDL file ports must have a corresponding port-component element
+	defined.
+
+	Used in: webservices
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0" maxOccurs="1"/>
+      <xsd:element name="display-name"
+		   type="j2ee:display-nameType"
+		   minOccurs="0" maxOccurs="1"/>
+      <xsd:element name="icon"
+		   type="j2ee:iconType"
+		   minOccurs="0" maxOccurs="1"/>
+      <xsd:element name="webservice-description-name"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The webservice-description-name identifies the collection of
+	    port-components associated with a WSDL file and JAX-RPC
+	    mapping. The name must be unique within the deployment descriptor.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="wsdl-file"
+		   type="j2ee:pathType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The wsdl-file element contains the name of a WSDL file in the
+	    module.  The file name is a relative path within the module.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="jaxrpc-mapping-file"
+		   type="j2ee:pathType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The jaxrpc-mapping-file element contains the name of a file that
+	    describes the JAX-RPC mapping between the Java interaces used by
+	    the application and the WSDL description in the wsdl-file.  The
+	    file name is a relative path within the module.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="port-component"
+		   type="j2ee:port-componentType"
+		   minOccurs="1" maxOccurs="unbounded">
+	<xsd:key name="port-component_handler-name-key">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      Defines the name of the handler. The name must be unique
+	      within the module.
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	  <xsd:selector xpath="j2ee:handler"/>
+	  <xsd:field xpath="j2ee:handler-name"/>
+	</xsd:key>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="webservicesType">
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="webservice-description"
+		   type="j2ee:webservice-descriptionType"
+		   minOccurs="1" maxOccurs="unbounded">
+	<xsd:key name="port-component-name-key">
+	  <xsd:annotation>
+	    <xsd:documentation>
+	      <![CDATA[
+
+		The port-component-name element specifies a port
+		component's name.  This name is assigned by the module
+		producer to name the service implementation bean in the
+		module's deployment descriptor. The name must be unique
+		among the port component names defined in the same module.
+
+		Used in: port-component
+
+		Example:
+			<port-component-name>EmployeeService
+			</port-component-name>
+
+		]]>
+	    </xsd:documentation>
+	  </xsd:annotation>
+	  <xsd:selector xpath="j2ee:port-component"/>
+	  <xsd:field xpath="j2ee:port-component-name"/>
+	</xsd:key>
+      </xsd:element>
+    </xsd:sequence>
+
+    <xsd:attribute name="version"
+		   type="j2ee:dewey-versionType"
+		   fixed="1.1"
+		   use="required">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The required value for the version is 1.1.
+
+	</xsd:documentation>
+      </xsd:annotation>
+    </xsd:attribute>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+</xsd:schema>
+
diff --git a/servletapi/jsr154/src/share/dtd/j2ee_web_services_client_1_1.xsd b/servletapi/jsr154/src/share/dtd/j2ee_web_services_client_1_1.xsd
new file mode 100644
index 0000000..59a4bae
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/j2ee_web_services_client_1_1.xsd
@@ -0,0 +1,358 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
+	    targetNamespace="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	    elementFormDefault="qualified"
+	    attributeFormDefault="unqualified"
+	    version="1.1">
+  <xsd:annotation>
+    <xsd:documentation>
+      @(#)j2ee_web_services_client_1_1.xsds	1.10 02/11/03
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      Copyright 2002 Sun Microsystems, Inc., 901 San Antonio
+      Road, Palo Alto, California 94303, U.S.A. All rights
+      reserved.
+
+      Sun Microsystems, Inc. has intellectual property rights
+      relating to technology described in this document. In
+      particular, and without limitation, these intellectual
+      property rights may include one or more of the U.S. patents
+      listed at http://www.sun.com/patents and one or more
+      additional patents or pending patent applications in the
+      U.S. and other countries.
+
+      This document and the technology which it describes are
+      distributed under licenses restricting their use, copying,
+      distribution, and decompilation. No part of this document
+      may be reproduced in any form by any means without prior
+      written authorization of Sun and its licensors, if any.
+
+      Third-party software, including font technology, is
+      copyrighted and licensed from Sun suppliers.
+
+      Sun, Sun Microsystems, the Sun logo, Solaris, Java, J2EE,
+      JavaServer Pages, Enterprise JavaBeans and the Java Coffee
+      Cup logo are trademarks or registered trademarks of Sun
+      Microsystems, Inc. in the U.S. and other countries.
+
+      Federal Acquisitions: Commercial Software - Government Users
+      Subject to Standard License Terms and Conditions.
+
+    </xsd:documentation>
+  </xsd:annotation>
+  <xsd:annotation>
+    <xsd:documentation>
+
+      (C) Copyright International Business Machines Corporation 2002
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="port-component-refType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The port-component-ref element declares a client dependency
+	on the container for resolving a Service Endpoint Interface
+	to a WSDL port. It optionally associates the Service Endpoint
+	Interface with a particular port-component. This is only used
+	by the container for a Service.getPort(Class) method call.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="service-endpoint-interface"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The service-endpoint-interface element defines a fully qualified
+	    Java class that represents the Service Endpoint Interface of a
+	    WSDL port.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="port-component-link"
+		   type="j2ee:string"
+		   minOccurs="0" maxOccurs="1">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The port-component-link element links a port-component-ref
+	    to a specific port-component required to be made available
+	    by a service reference.
+
+	    The value of a port-component-link must be the
+	    port-component-name of a port-component in the same module
+	    or another module in the same application unit. The syntax
+	    for specification follows the syntax defined for ejb-link
+	    in the EJB 2.0 specification.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:group name="service-refGroup">
+    <xsd:sequence>
+      <xsd:element name="service-ref"
+		   type="j2ee:service-refType"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:key name="service-ref_handler-name-key">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      Defines the name of the handler. The name must be unique
+	      within the module.
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	  <xsd:selector xpath="j2ee:handler"/>
+	  <xsd:field xpath="j2ee:handler-name"/>
+	</xsd:key>
+      </xsd:element>
+    </xsd:sequence>
+  </xsd:group>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="service-refType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The service-ref element declares a reference to a Web
+	service. It contains optional description, display name and
+	icons, a declaration of the required Service interface,
+	an optional WSDL document location, an optional set
+	of JAX-RPC mappings, an optional QName for the service element,
+	an optional set of Service Endpoint Interfaces to be resolved
+	by the container to a WSDL port, and an optional set of handlers.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="service-ref-name"
+		   type="j2ee:jndi-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The service-ref-name element declares logical name that the
+	    components in the module use to look up the Web service. It
+	    is recommended that all service reference names start with
+	    "service/".
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="service-interface"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The service-interface element declares the fully qualified class
+	    name of the JAX-RPC Service interface the client depends on.
+	    In most cases the value will be javax.xml.rpc.Service.  A JAX-RPC
+	    generated Service Interface class may also be specified.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="wsdl-file"
+		   type="j2ee:xsdAnyURIType"
+		   minOccurs="0" maxOccurs="1">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The wsdl-file element contains the URI location of a WSDL
+	    file. The location is relative to the root of the module.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="jaxrpc-mapping-file"
+		   type="j2ee:pathType"
+		   minOccurs="0" maxOccurs="1">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The jaxrpc-mapping-file element contains the name of a file that
+	    describes the JAX-RPC mapping between the Java interaces used by
+	    the application and the WSDL description in the wsdl-file.  The
+	    file name is a relative path within the module file.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="service-qname"
+		   type="j2ee:xsdQNameType"
+		   minOccurs="0" maxOccurs="1">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The service-qname element declares the specific WSDL service
+	    element that is being refered to.  It is not specified if no
+	    wsdl-file is declared.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="port-component-ref"
+		   type="j2ee:port-component-refType"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The port-component-ref element declares a client dependency
+	    on the container for resolving a Service Endpoint Interface
+	    to a WSDL port. It optionally associates the Service Endpoint
+	    Interface with a particular port-component. This is only used
+	    by the container for a Service.getPort(Class) method call.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="handler"
+		   type="j2ee:service-ref_handlerType"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Declares the handler for a port-component. Handlers can
+	    access the init-param name/value pairs using the
+	    HandlerInfo interface. If port-name is not specified, the
+	    handler is assumed to be associated with all ports of the
+	    service.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="service-ref_handlerType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	Declares the handler for a port-component. Handlers can access the
+	init-param name/value pairs using the HandlerInfo interface. If
+	port-name is not specified, the handler is assumed to be associated
+	with all ports of the service.
+
+	Used in: service-ref
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="handler-name"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the name of the handler. The name must be unique
+	    within the module.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="handler-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines a fully qualified class name for the handler
+	    implementation.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="init-param"
+		   type="j2ee:param-valueType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+
+      <xsd:element name="soap-header"
+		   type="j2ee:xsdQNameType"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the QName of a SOAP header that will be processed
+	    by the handler.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="soap-role"
+		   type="j2ee:string"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The soap-role element contains a SOAP actor definition that
+	    the Handler will play as a role.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="port-name"
+		   type="j2ee:string"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The port-name element defines the WSDL port-name that a
+	    handler should be associated with.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+</xsd:schema>
+
diff --git a/servletapi/jsr154/src/share/dtd/jsp_2_0.xsd b/servletapi/jsr154/src/share/dtd/jsp_2_0.xsd
new file mode 100644
index 0000000..799bbaa
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/jsp_2_0.xsd
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
+	    targetNamespace="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	    elementFormDefault="qualified"
+	    attributeFormDefault="unqualified"
+	    version="2.0">
+  <xsd:annotation>
+    <xsd:documentation>
+      @(#)jsp_2_0.xsds	1.17 03/18/03
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      Copyright 2002 Sun Microsystems, Inc., 901 San Antonio
+      Road, Palo Alto, California 94303, U.S.A. All rights
+      reserved.
+
+      Sun Microsystems, Inc. has intellectual property rights
+      relating to technology described in this document. In
+      particular, and without limitation, these intellectual
+      property rights may include one or more of the U.S. patents
+      listed at http://www.sun.com/patents and one or more
+      additional patents or pending patent applications in the
+      U.S. and other countries.
+
+      This document and the technology which it describes are
+      distributed under licenses restricting their use, copying,
+      distribution, and decompilation. No part of this document
+      may be reproduced in any form by any means without prior
+      written authorization of Sun and its licensors, if any.
+
+      Third-party software, including font technology, is
+      copyrighted and licensed from Sun suppliers.
+
+      Sun, Sun Microsystems, the Sun logo, Solaris, Java, J2EE,
+      JavaServer Pages, Enterprise JavaBeans and the Java Coffee
+      Cup logo are trademarks or registered trademarks of Sun
+      Microsystems, Inc. in the U.S. and other countries.
+
+      Federal Acquisitions: Commercial Software - Government Users
+      Subject to Standard License Terms and Conditions.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      This is the XML Schema for the JSP 2.0 deployment descriptor
+      types.  The JSP 2.0 schema contains all the special
+      structures and datatypes that are necessary to use JSP files
+      from a web application.
+
+      The contents of this schema is used by the web-app_2_4.xsd
+      file to define JSP specific content.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      The following conventions apply to all J2EE
+      deployment descriptor elements unless indicated otherwise.
+
+      - In elements that specify a pathname to a file within the
+	same JAR file, relative filenames (i.e., those not
+	starting with "/") are considered relative to the root of
+	the JAR file's namespace.  Absolute filenames (i.e., those
+	starting with "/") also specify names in the root of the
+	JAR file's namespace.  In general, relative names are
+	preferred.  The exception is .war files where absolute
+	names are preferred for consistency with the Servlet API.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:include schemaLocation="j2ee_1_4.xsd"/>
+
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="jsp-configType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The jsp-configType is used to provide global configuration
+	information for the JSP files in a web application. It has
+	two subelements, taglib and jsp-property-group.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="taglib"
+		   type="j2ee:taglibType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="jsp-property-group"
+		   type="j2ee:jsp-property-groupType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="jsp-fileType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The jsp-file element contains the full path to a JSP file
+	within the web application beginning with a `/'.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:pathType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="jsp-property-groupType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The jsp-property-groupType is used to group a number of
+	files so they can be given global property information.
+	All files so described are deemed to be JSP files.  The
+	following additional properties can be described:
+
+	    - Control whether EL is ignored
+	    - Control whether scripting elements are invalid
+	    - Indicate pageEncoding information.
+	    - Indicate that a resource is a JSP document (XML)
+	    - Prelude and Coda automatic includes.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="url-pattern"
+		   type="j2ee:url-patternType"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="el-ignored"
+		   type="j2ee:true-falseType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Can be used to easily set the isELIgnored
+	    property of a group of JSP pages.  By default, the
+	    EL evaluation is enabled for Web Applications using
+	    a Servlet 2.4 or greater web.xml, and disabled
+	    otherwise.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="page-encoding"
+		   type="j2ee:string"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The valid values of page-encoding are those of the
+	    pageEncoding page directive.  It is a
+	    translation-time error to name different encodings
+	    in the pageEncoding attribute of the page directive
+	    of a JSP page and in a JSP configuration element
+	    matching the page.  It is also a translation-time
+	    error to name different encodings in the prolog
+	    or text declaration of a document in XML syntax and
+	    in a JSP configuration element matching the document.
+	    It is legal to name the same encoding through
+	    mulitple mechanisms.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="scripting-invalid"
+		   type="j2ee:true-falseType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Can be used to easily disable scripting in a
+	    group of JSP pages.  By default, scripting is
+	    enabled.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="is-xml"
+		   type="j2ee:true-falseType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    If true, denotes that the group of resources
+	    that match the URL pattern are JSP documents,
+	    and thus must be interpreted as XML documents.
+	    If false, the resources are assumed to not
+	    be JSP documents, unless there is another
+	    property group that indicates otherwise.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="include-prelude"
+		   type="j2ee:pathType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The include-prelude element is a context-relative
+	    path that must correspond to an element in the
+	    Web Application.  When the element is present,
+	    the given path will be automatically included (as
+	    in an include directive) at the beginning of each
+	    JSP page in this jsp-property-group.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="include-coda"
+		   type="j2ee:pathType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The include-coda element is a context-relative
+	    path that must correspond to an element in the
+	    Web Application.  When the element is present,
+	    the given path will be automatically included (as
+	    in an include directive) at the end of each
+	    JSP page in this jsp-property-group.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="taglibType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The taglibType defines the syntax for declaring in
+	the deployment descriptor that a tag library is
+	available to the application.  This can be done
+	to override implicit map entries from TLD files and
+	from the container.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="taglib-uri"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    A taglib-uri element describes a URI identifying a
+	    tag library used in the web application.  The body
+	    of the taglib-uri element may be either an
+	    absolute URI specification, or a relative URI.
+	    There should be no entries in web.xml with the
+	    same taglib-uri value.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="taglib-location"
+		   type="j2ee:pathType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    the taglib-location element contains the location
+	    (as a resource relative to the root of the web
+	    application) where to find the Tag Library
+	    Description file for the tag library.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+</xsd:schema>
+
diff --git a/servletapi/jsr154/src/share/dtd/jspxml.dtd b/servletapi/jsr154/src/share/dtd/jspxml.dtd
new file mode 100644
index 0000000..dffe046
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/jspxml.dtd
@@ -0,0 +1,189 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- DTD for JSP 2.0
+     thanks to Bob Foster, WebGain
+-->
+
+<!-- 
+     This DTD is not conditional on any parameter entities in the internal
+     subset and does not export any general entities.
+-->
+
+<!--================== Constrained Names ====================================-->
+
+<!ENTITY % URI "CDATA">
+    <!-- a Uniform Resource Identifier, see [RFC2396] -->
+
+<!ENTITY % UriList "CDATA">
+    <!-- a space separated list of Uniform Resource Identifiers -->
+
+<!ENTITY % URL "CDATA">
+    <!-- a relative urlSpec is as in Section 2.10.2. -->
+
+<!ENTITY % BeanID "IDREF">
+    <!-- a previously declared bean ID in the current scope. -->
+
+<!ENTITY % Prefix "CDATA">
+    <!-- a Name that contains no : characters. -->
+
+<!ENTITY % ClassName "CDATA">
+    <!-- a fully qualified class name. -->
+
+<!ENTITY % TypeName "CDATA">
+    <!-- a fully qualified class or interface name. -->
+
+<!ENTITY % BeanName "CDATA">
+    <!-- a bean name as expected by java.beans.Beans instantiate(). -->
+
+<!ENTITY % Content "CDATA">
+    <!-- a MIME type followed by an IANA charset, as " type [; S? ['charset='] charset] " -->
+
+<!ENTITY % Length "CDATA">
+    <!-- nn for pixels or nn% for percentage length -->
+
+<!ENTITY % Pixels "CDATA">
+    <!-- integer representing length in pixels -->
+
+<!ENTITY % Bool "(true|false|yes|no)">
+    <!-- boolean -->
+
+<!-- used for object, applet, img, input and iframe -->
+<!ENTITY % ImgAlign "(top|middle|bottom|left|right)">
+
+<!--=================== Element Groups ====================================-->
+
+<!ENTITY % Directives "jsp:directive.page|jsp:directive.include">
+
+<!ENTITY % Scripts "jsp:scriptlet|jsp:declaration|jsp:expression">
+
+<!ENTITY % Actions
+    "jsp:useBean
+    |jsp:setProperty
+    |jsp:getProperty
+    |jsp:include
+    |jsp:forward
+    |jsp:plugin"
+>
+
+<!ENTITY % Body "(jsp:text|%Directives;|%Scripts;|%Actions;)*">
+
+
+<!-- ============================ Elements ============================ -->
+
+<!--    Root element of a JSP page.
+-->
+<!ELEMENT jsp:root %Body;>
+<!ATTLIST jsp:root
+    xmlns:jsp       CDATA           "http://java.sun.com/JSP/Page"
+    version         CDATA           #REQUIRED
+>
+
+<!ELEMENT jsp:directive.page EMPTY>
+<!ATTLIST jsp:directive.page
+    language        CDATA           "java"
+    extends         %ClassName;     #IMPLIED
+    contentType     %Content;       "text/html; ISO-8859-1"
+    import          CDATA           #IMPLIED
+    session         %Bool;          "true"
+    buffer          CDATA           "8kb"
+    autoFlush       %Bool;          "true"
+    isThreadSafe    %Bool;          "true"
+    info            CDATA           #IMPLIED
+    errorPage       %URL;           #IMPLIED
+    isErrorPage     %Bool;          "false"
+    pageEncoding    CDATA           #IMPLIED
+    isELIgnored     %Bool;          #IMPLIED
+>
+
+<!-- the jsp:directive.include only appears in JSP documents and does
+     not appear in the XML views of JSP pages.
+-->
+
+<!ELEMENT jsp:directive.include EMPTY>
+<!ATTLIST jsp:directive.include
+    file            %URI;           #REQUIRED
+>
+
+<!ELEMENT jsp:scriptlet (#PCDATA)>
+
+<!ELEMENT jsp:declaration (#PCDATA)>
+
+<!ELEMENT jsp:expression (#PCDATA)>
+
+<!ELEMENT jsp:useBean %Body;>
+<!ATTLIST jsp:useBean
+    id              ID              #REQUIRED
+    class           %ClassName;     #IMPLIED
+    type            %TypeName;      #IMPLIED
+    beanName        %BeanName;      #IMPLIED
+    scope           (page
+                    |session
+                    |request
+                    |application)   "page"
+>
+
+<!ELEMENT jsp:setProperty EMPTY>
+<!ATTLIST jsp:setProperty
+    name            %BeanID;        #REQUIRED
+    property        CDATA           #REQUIRED
+    value           CDATA           #IMPLIED
+    param           CDATA           #IMPLIED
+>
+
+<!ELEMENT jsp:getProperty EMPTY>
+<!ATTLIST jsp:getProperty
+    name            %BeanID;        #REQUIRED
+    property        CDATA           #REQUIRED
+>
+
+<!ELEMENT jsp:include (jsp:param*)>
+<!ATTLIST jsp:include
+    flush           %Bool;          "false"
+    page            %URL;           #REQUIRED
+>
+
+<!ELEMENT jsp:forward (jsp:param*)>
+<!ATTLIST jsp:forward
+    page            %URL;           #REQUIRED
+>
+
+<!ELEMENT jsp:plugin (jsp:params?, jsp:fallback?)>
+<!ATTLIST jsp:plugin
+    type            (bean|applet)   #REQUIRED
+    code            %URI;           #IMPLIED
+    codebase        %URI;           #IMPLIED
+    align           %ImgAlign;      #IMPLIED
+    archive         %UriList;       #IMPLIED
+    height          %Length;        #IMPLIED
+    hspace          %Pixels;        #IMPLIED
+    jreversion      CDATA           "1.2"
+    name            NMTOKEN         #IMPLIED
+    vspace          %Pixels;        #IMPLIED
+    width           %Length;        #IMPLIED
+    nspluginurl     %URI;           #IMPLIED
+    iepluginurl     %URI;           #IMPLIED
+>
+
+<!ELEMENT jsp:params (jsp:param+)>
+
+<!ELEMENT jsp:param EMPTY>
+<!ATTLIST jsp:param
+    name            CDATA           #REQUIRED
+    value           CDATA           #REQUIRED
+>
+
+<!ELEMENT jsp:text #PCDATA>
+
diff --git a/servletapi/jsr154/src/share/dtd/jspxml.xsd b/servletapi/jsr154/src/share/dtd/jspxml.xsd
new file mode 100644
index 0000000..886ee65
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/jspxml.xsd
@@ -0,0 +1,513 @@
+<?xml version ="1.0"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE schema [
+<!-- Patterns -->
+<!ENTITY Identifier   "(\p{L}|_|$)(\p{N}|\p{L}|_|$)*">
+<!ENTITY TypeName     "&Identifier;(\.&Identifier;)*">
+<!ENTITY WS       "\s*">
+<!ENTITY Import     "&TypeName;(\.\*)?">
+<!ENTITY ImportList   "&Import;(&WS;,&WS;&Import;)*">
+<!ENTITY SetProp    "(&Identifier;|\*)">
+<!ENTITY RelativeURL  "[^:#/\?]*(:{0,0}|[#/\?].*)">
+<!ENTITY Length     "[0-9]*&#x25;?">
+<!ENTITY AsciiName    "[A-Za-z0-9_-]*">
+<!ENTITY ValidContentType  "&AsciiName;/&AsciiName;(;&WS;(charset=)?&AsciiName;)?">
+<!ENTITY ValidPageEncoding  "&AsciiName;/&AsciiName;">
+<!ENTITY Buffer     "[0-9]+kb">
+<!ENTITY RTexpr     "&#x25;=.*&#x25;">
+]>
+
+
+<!--Conforms to w3c http://www.w3.org/2001/XMLSchema -->
+
+<xsd:schema
+    xmlns = "http://java.sun.com/JSP/Page"
+    xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
+    xmlns:jsp = "http://java.sun.com/JSP/Page"
+    targetNamespace = "http://java.sun.com/JSP/Page"
+    elementFormDefault = "qualified"
+    attributeFormDefault = "unqualified">
+
+  <xsd:annotation>
+    <xsd:documentation>
+      XML Schema for JSP 2.0.
+
+      This schema is based upon the recent (May 5th, 2001)
+      W3C recommendation for XML Schema.
+
+      A JSP translator should reject an XML-format file that is
+      not strictly valid according to this schema or does not observe
+      the constraints documented here. A translator is not required
+      to use this schema for validation or to use a validating parser.
+    </xsd:documentation>
+  </xsd:annotation>
+
+
+  <!-- Complex Types -->
+
+  <xsd:complexType name = "Body">
+    <xsd:annotation>
+      <xsd:documentation>
+        Body defines the "top-level" elements in root and beanInfo.
+        There are probably other elements that should use it.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:group ref = "Bodygroup" minOccurs = "0" maxOccurs = "unbounded"/>
+  </xsd:complexType>
+
+  <!-- groups -->
+
+  <xsd:group name = "Bodygroup">
+    <xsd:choice>
+      <xsd:element ref = "directive.page"/>
+      <xsd:element ref = "directive.include"/>
+      <xsd:element ref = "scriptlet"/>
+      <xsd:element ref = "declaration"/>
+      <xsd:element ref = "expression"/>
+      <xsd:element ref = "useBean"/>
+      <xsd:element ref = "setProperty"/>
+      <xsd:element ref = "getProperty"/>
+      <xsd:element ref = "include"/>
+      <xsd:element ref = "forward"/>
+      <xsd:element ref = "plugin"/>
+      <xsd:element ref = "text"/>
+      <xsd:any namespace="##other" processContents = "lax"/>
+    </xsd:choice>
+  </xsd:group>
+
+
+  <!-- Simple types are next -->
+
+  <xsd:simpleType name = "RTE">
+    <xsd:annotation>
+      <xsd:documentation>
+        A request-time expression value
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&RTexpr;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "Bool">
+    <xsd:annotation>
+      <xsd:documentation>
+        Bool would be boolean except it does not accept 1 and 0.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:NMTOKEN" >
+      <xsd:enumeration value = "true"/>
+      <xsd:enumeration value = "false"/>
+      <xsd:enumeration value = "yes"/>
+      <xsd:enumeration value = "no"/>
+    </xsd:restriction>     
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "Identifier">
+    <xsd:annotation>
+      <xsd:documentation>
+        Identifier is an unqualified Java identifier.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&Identifier;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "TypeName">
+    <xsd:annotation>
+      <xsd:documentation>
+        TypeName is one or more Java identifiers separated by dots
+        with no whitespace.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&TypeName;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "ImportList">
+    <xsd:annotation>
+      <xsd:documentation>
+        ImportList is one or more typeNames separated by commas.
+        Whitespace is allowed before and after the comma.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&ImportList;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "SetProp">
+    <xsd:annotation>
+      <xsd:documentation>
+        SetProp is an Identifier or *.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&SetProp;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "RelativeURL">
+    <xsd:annotation>
+      <xsd:documentation>
+        RelativeURL is a uriReference with no colon character
+        before the first /, ? or #, if any (RFC2396).
+      </xsd:documentation>
+    </xsd:annotation>
+	<xsd:restriction base = "xsd:anyURI">
+      <xsd:pattern value = "&RelativeURL;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "RTERelativeURL">
+    <xsd:union memberTypes = "RelativeURL RTE"/>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "Length">
+    <xsd:annotation>
+      <xsd:documentation>
+        Length is nn or nn%.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&Length;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  
+
+  <xsd:simpleType name = "ExplicitBufferSize">
+    <xsd:annotation>
+      <xsd:documentation>
+         Buffer Size with an explicit value
+      </xsd:documentation>
+    </xsd:annotation> 
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&Buffer;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "NoneBufferSize">
+    <xsd:annotation>
+      <xsd:documentation>
+         Buffer Size with a "none" value
+      </xsd:documentation>
+    </xsd:annotation> 
+       <xsd:restriction base = "xsd:string">
+         <xsd:enumeration value = "none"/>
+       </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "BufferSize">
+    <xsd:annotation>
+      <xsd:documentation>
+        Buffer size is xkb or none.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:union memberTypes = "ExplicitBufferSize NoneBufferSize"/>
+  </xsd:simpleType>
+  
+  <xsd:simpleType name = "ContentType">
+    <xsd:annotation>
+      <xsd:documentation>
+        Content type and character encoding for this page.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&ValidContentType;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "PageEncoding">
+    <xsd:annotation>
+      <xsd:documentation>
+        Page Encoding for this page.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:string">
+      <xsd:pattern value = "&ValidPageEncoding;"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "Scope">
+    <xsd:annotation>
+      <xsd:documentation>
+        valid scope values
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:NMTOKEN">
+      <xsd:enumeration value = "page"/>
+      <xsd:enumeration value = "session"/>
+      <xsd:enumeration value = "request"/>
+      <xsd:enumeration value = "application"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "PlugInType">
+    <xsd:annotation>
+      <xsd:documentation>
+        valid values for a plugin type
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:NMTOKEN">
+      <xsd:enumeration value = "bean"/>
+      <xsd:enumeration value = "applet"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <xsd:simpleType name = "AlignType">
+    <xsd:annotation>
+      <xsd:documentation>
+        Buffer size is xkb.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base = "xsd:NMTOKEN">
+      <xsd:enumeration value = "top"/>
+      <xsd:enumeration value = "middle"/>
+      <xsd:enumeration value = "bottom"/>
+      <xsd:enumeration value = "left"/>
+      <xsd:enumeration value = "right"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+  <!-- Elements follow -->
+
+  <xsd:element name = "root">
+    <xsd:annotation>
+      <xsd:documentation>
+        The root element of all JSP documents is named root.
+        
+        Authors may, if they wish, include schema location information.
+        If specified, the information may appear as attributes of
+        the root element as follows:
+
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/JSP/Page xsd-file-location"
+
+        Documents should not specify the system identifier of a DTD
+        in a DOCTYPE declaration.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:complexContent>
+        <xsd:extension base = "Body">
+          <xsd:attribute name = "version" fixed = "2.0" type = "xsd:string"/>
+        </xsd:extension>
+      </xsd:complexContent>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "directive.page">
+    <xsd:annotation>
+      <xsd:documentation>
+        directive.page is the "page directive".
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:attribute name = "language" default = "java" type = "xsd:string"/>
+      <xsd:attribute name = "extends" type = "TypeName"/>
+      <xsd:attribute name = "contentType" default = "text/html; ISO-8859-1" type = "ContentType"/>
+      <xsd:attribute name = "pageEncoding" use = "optional" type = "PageEncoding"/>
+      <xsd:attribute name = "import" type = "ImportList"/>
+      <xsd:attribute name = "session" default = "true" type = "Bool"/>
+      <xsd:attribute name = "buffer" default = "8kb" type = "BufferSize"/>
+      <xsd:attribute name = "autoFlush" default = "true" type = "Bool"/>
+      <xsd:attribute name = "isThreadSafe" default = "true" type = "Bool"/>
+      <xsd:attribute name = "info" type = "xsd:string"/>
+      <xsd:attribute name = "errorPage" type = "RelativeURL"/>
+      <xsd:attribute name = "isErrorPage" default = "false" type = "Bool"/>
+      <xsd:attribute name = "isELIgnored" type = "Bool"/>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "directive.include">
+    <xsd:annotation>
+      <xsd:documentation>
+        directive.include is the "include directive".
+	This element does not appear on XML views of JSP pages.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:attribute name = "file" use = "required" type = "RelativeURL"/>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name = "scriptlet" type = "xsd:string">
+    <xsd:annotation>
+      <xsd:documentation>
+        The representation of a scriplet.
+      </xsd:documentation>
+    </xsd:annotation>
+  </xsd:element>
+  
+  <xsd:element name = "declaration" type = "xsd:string">
+    <xsd:annotation>
+      <xsd:documentation>
+        The reprsentation of a declaration.
+      </xsd:documentation>
+    </xsd:annotation>
+  </xsd:element>
+  
+  <xsd:element name = "expression" type = "xsd:string">
+    <xsd:annotation>
+      <xsd:documentation>
+        The representation of an expression.
+      </xsd:documentation>
+    </xsd:annotation>
+  </xsd:element>
+ 
+  <xsd:element name = "text" type = "xsd:string">
+    <xsd:annotation>
+      <xsd:documentation>
+        Verbatim template text.
+      </xsd:documentation>
+    </xsd:annotation>
+  </xsd:element>
+
+  <xsd:element name = "useBean">
+    <xsd:annotation>
+      <xsd:documentation>
+        useBean instantiates or accesses a bean in the specified scope.
+        
+        Constraint: The allowed combinations of attributes are:
+        
+          class [type] | type [( class | beanName)]
+        
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:complexContent>
+        <xsd:extension base="Body">
+          <xsd:attribute name = "id" use = "required" type = "Identifier"/>
+          <xsd:attribute name = "class" type = "TypeName"/>
+          <xsd:attribute name = "type" type = "TypeName"/>
+          <xsd:attribute name = "beanName" type = "TypeName"/>
+          <xsd:attribute name = "scope" default = "page" type = "Scope"/>
+        </xsd:extension>
+      </xsd:complexContent>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "setProperty">
+    <xsd:annotation>
+      <xsd:documentation>
+        setProperty changes the value of an object property.
+        
+        Constraint: The object named by the name must have been
+        "introduced" to the JSP processor using either the
+        jsp:useBean action or a custom action with an associated
+        VariableInfo entry for this name.
+
+        Exact valid combinations are not expressable in XML Schema.
+        They are:
+
+        name="Identifier" property="*"
+        name="Identifier" property="Identfiier" param="string"
+        name="Identifier" property="Identifier" value="string"
+                
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:attribute name = "name" use = "required" type = "Identifier"/>
+      <xsd:attribute name = "property" use = "required" type = "SetProp"/>
+      <xsd:attribute name = "param" type = "xsd:string"/>
+      <xsd:attribute name = "value" type = "xsd:string"/>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name = "getProperty">
+    <xsd:annotation>
+      <xsd:documentation>
+        getProperty obtains the value of an object property.
+        
+        Constraint: The object named by the name must have been
+        "introduced" to the JSP processor using either the
+        jsp:useBean action or a custom action with an associated
+        VariableInfo entry for this name.
+        
+        ???The spec is interpreted as restricting the values of
+        property to Identifier.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:complexType>
+      <xsd:attribute name = "name" use = "required" type = "Identifier"/>
+      <xsd:attribute name = "property" use = "required" type = "Identifier"/>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "include">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element ref = "param" minOccurs = "0" maxOccurs = "unbounded"/>
+      </xsd:sequence>
+      <xsd:attribute name = "flush" default = "false" type = "Bool"/>
+      <xsd:attribute name = "page" use = "required" type = "RTERelativeURL"/>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name = "forward">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element ref = "param" minOccurs = "0" maxOccurs = "unbounded"/>
+      </xsd:sequence>
+      <xsd:attribute name = "page" use = "required" type = "RTERelativeURL"/>
+     </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "plugin">
+    <xsd:complexType> <!-- content only! -->
+      <xsd:sequence>
+        <xsd:element ref = "params" minOccurs = "0" maxOccurs = "1"/>
+        <xsd:element name = "fallback" minOccurs = "0" maxOccurs = "1" type = "Body"/>
+      </xsd:sequence>
+      <xsd:attribute name = "type" use = "required" type = "PlugInType"/>
+      <xsd:attribute name = "code" type = "xsd:anyURI"/>
+      <xsd:attribute name = "codebase" type = "xsd:anyURI"/>
+      <xsd:attribute name = "align" type = "AlignType"/>
+      <xsd:attribute name = "archive">
+        <xsd:simpleType>
+           <xsd:list itemType="xsd:anyURI"/>
+        </xsd:simpleType>
+      </xsd:attribute>
+      <xsd:attribute name = "height" type = "Length"/>
+      <xsd:attribute name = "hspace" type = "xsd:int"/>
+      <xsd:attribute name = "jreversion" default = "1.2" type = "xsd:string"/>
+      <xsd:attribute name = "name" type = "xsd:NMTOKEN"/>
+      <xsd:attribute name = "vspace" type = "xsd:int"/>
+      <xsd:attribute name = "width" type = "Length"/>
+      <xsd:attribute name = "nspluginurl" type = "xsd:anyURI"/>
+      <xsd:attribute name = "iepluginurl" type = "xsd:anyURI"/>
+    </xsd:complexType>
+  </xsd:element>
+  
+  <xsd:element name = "params">
+    <xsd:complexType>
+       <xsd:sequence>
+         <xsd:element ref = "param" minOccurs = "1" maxOccurs = "unbounded"/>
+       </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name = "param">
+    <xsd:complexType>
+      <xsd:attribute name = "name" use = "required" type = "xsd:NMTOKEN"/>
+      <xsd:attribute name = "value" use = "required" type = "xsd:string"/>
+    </xsd:complexType>
+  </xsd:element>
+  
+</xsd:schema>
diff --git a/servletapi/jsr154/src/share/dtd/web-app_2_2.dtd b/servletapi/jsr154/src/share/dtd/web-app_2_2.dtd
new file mode 100644
index 0000000..07eb17e
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/web-app_2_2.dtd
@@ -0,0 +1,580 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!--
+The web-app element is the root of the deployment descriptor for
+a web application
+-->
+
+<!ELEMENT web-app (icon?, display-name?, description?, distributable?,
+context-param*, servlet*, servlet-mapping*, session-config?,
+mime-mapping*, welcome-file-list?, error-page*, taglib*,
+resource-ref*, security-constraint*, login-config?, security-role*,
+env-entry*, ejb-ref*)>
+
+<!--
+The icon element contains a small-icon and a large-icon element
+which specify the location within the web application for a small and
+large image used to represent the web application in a GUI tool. At a
+minimum, tools must accept GIF and JPEG format images.
+-->
+
+<!ELEMENT icon (small-icon?, large-icon?)>
+
+<!--
+The small-icon element contains the location within the web
+application of a file containing a small (16x16 pixel) icon image.
+-->
+
+<!ELEMENT small-icon (#PCDATA)>
+
+<!--
+The large-icon element contains the location within the web
+application of a file containing a large (32x32 pixel) icon image.
+-->
+
+<!ELEMENT large-icon (#PCDATA)>
+
+<!--
+The display-name element contains a short name that is intended
+to be displayed by GUI tools
+-->
+
+<!ELEMENT display-name (#PCDATA)>
+
+<!--
+The description element is used to provide descriptive text about
+the parent element.
+-->
+
+<!ELEMENT description (#PCDATA)>
+
+<!--
+The distributable element, by its presence in a web application
+deployment descriptor, indicates that this web application is
+programmed appropriately to be deployed into a distributed servlet
+container
+-->
+
+<!ELEMENT distributable EMPTY>
+
+<!--
+The context-param element contains the declaration of a web
+application's servlet context initialization parameters.
+-->
+
+<!ELEMENT context-param (param-name, param-value, description?)>
+
+<!--
+The param-name element contains the name of a parameter.
+-->
+
+<!ELEMENT param-name (#PCDATA)>
+
+<!--
+The param-value element contains the value of a parameter.
+-->
+
+<!ELEMENT param-value (#PCDATA)>
+
+<!--
+The servlet element contains the declarative data of a
+servlet. If a jsp-file is specified and the load-on-startup element is
+present, then the JSP should be precompiled and loaded.
+-->
+
+<!ELEMENT servlet (icon?, servlet-name, display-name?, description?,
+(servlet-class|jsp-file), init-param*, load-on-startup?, security-role-ref*)>
+
+<!--
+The servlet-name element contains the canonical name of the
+servlet.
+-->
+
+<!ELEMENT servlet-name (#PCDATA)>
+
+<!--
+The servlet-class element contains the fully qualified class name
+of the servlet.
+-->
+
+<!ELEMENT servlet-class (#PCDATA)>
+
+<!--
+The jsp-file element contains the full path to a JSP file within
+the web application.
+-->
+
+<!ELEMENT jsp-file (#PCDATA)>
+
+<!--
+The init-param element contains a name/value pair as an
+initialization param of the servlet
+-->
+
+<!ELEMENT init-param (param-name, param-value, description?)>
+
+<!--
+The load-on-startup element indicates that this servlet should be
+loaded on the startup of the web application. The optional contents of
+these element must be a positive integer indicating the order in which
+the servlet should be loaded. Lower integers are loaded before higher
+integers. If no value is specified, or if the value specified is not a
+positive integer, the container is free to load it at any time in the
+startup sequence.
+-->
+
+<!ELEMENT load-on-startup (#PCDATA)>
+
+<!--
+The servlet-mapping element defines a mapping between a servlet
+and a url pattern
+-->
+
+<!ELEMENT servlet-mapping (servlet-name, url-pattern)>
+
+<!--
+The url-pattern element contains the url pattern of the
+mapping. Must follow the rules specified in Section 10 of the Servlet
+API Specification.
+-->
+
+<!ELEMENT url-pattern (#PCDATA)>
+
+<!--
+The session-config element defines the session parameters for
+this web application.
+-->
+
+<!ELEMENT session-config (session-timeout?)>
+
+<!--
+The session-timeout element defines the default session timeout
+interval for all sessions created in this web application. The
+specified timeout must be expressed in a whole number of minutes.
+-->
+
+<!ELEMENT session-timeout (#PCDATA)>
+
+<!--
+The mime-mapping element defines a mapping between an extension
+and a mime type.
+-->
+
+<!ELEMENT mime-mapping (extension, mime-type)>
+
+<!--
+The extension element contains a string describing an
+extension. example: "txt"
+-->
+
+<!ELEMENT extension (#PCDATA)>
+
+<!--
+The mime-type element contains a defined mime type. example:
+"text/plain"
+-->
+
+<!ELEMENT mime-type (#PCDATA)>
+
+<!--
+The welcome-file-list contains an ordered list of welcome files
+elements.
+-->
+
+<!ELEMENT welcome-file-list (welcome-file+)>
+
+<!--
+The welcome-file element contains file name to use as a default
+welcome file, such as index.html
+-->
+
+<!ELEMENT welcome-file (#PCDATA)>
+
+<!--
+The taglib element is used to describe a JSP tag library.
+-->
+
+<!ELEMENT taglib (taglib-uri, taglib-location)>
+
+<!--
+The taglib-uri element describes a URI, relative to the location
+of the web.xml document, identifying a Tag Library used in the Web
+Application.
+-->
+
+<!ELEMENT taglib-uri (#PCDATA)>
+
+<!--
+the taglib-location element contains the location (as a resource
+relative to the root of the web application) where to find the Tag
+Libary Description file for the tag library.
+-->
+
+<!ELEMENT taglib-location (#PCDATA)>
+
+<!--
+The error-page element contains a mapping between an error code
+or exception type to the path of a resource in the web application
+-->
+
+<!ELEMENT error-page ((error-code | exception-type), location)>
+
+<!--
+The error-code contains an HTTP error code, ex: 404
+-->
+
+<!ELEMENT error-code (#PCDATA)>
+
+<!--
+The exception type contains a fully qualified class name of a
+Java exception type.
+-->
+
+<!ELEMENT exception-type (#PCDATA)>
+
+<!--
+The location element contains the location of the resource in the
+web application
+-->
+
+<!ELEMENT location (#PCDATA)>
+
+<!--
+The resource-ref element contains a declaration of a Web
+Application's reference to an external resource.
+-->
+
+<!ELEMENT resource-ref (description?, res-ref-name, res-type, res-auth)>
+
+<!--
+The res-ref-name element specifies the name of the resource
+factory reference name.
+-->
+
+<!ELEMENT res-ref-name (#PCDATA)>
+
+<!--
+The res-type element specifies the (Java class) type of the data
+source.
+-->
+
+<!ELEMENT res-type (#PCDATA)>
+
+<!--
+The res-auth element indicates whether the application component
+code performs resource signon programmatically or whether the
+container signs onto the resource based on the principle mapping
+information supplied by the deployer. Must be CONTAINER or SERVLET
+-->
+
+<!ELEMENT res-auth (#PCDATA)>
+
+<!--
+The security-constraint element is used to associate security
+constraints with one or more web resource collections
+-->
+
+<!ELEMENT security-constraint (web-resource-collection+,
+auth-constraint?, user-data-constraint?)>
+
+<!--
+The web-resource-collection element is used to identify a subset
+of the resources and HTTP methods on those resources within a web
+application to which a security constraint applies. If no HTTP methods
+are specified, then the security constraint applies to all HTTP
+methods.
+-->
+
+<!ELEMENT web-resource-collection (web-resource-name, description?,
+url-pattern*, http-method*)>
+
+<!--
+The web-resource-name contains the name of this web resource
+collection
+-->
+
+<!ELEMENT web-resource-name (#PCDATA)>
+
+<!--
+The http-method contains an HTTP method (GET | POST |...)
+-->
+
+<!ELEMENT http-method (#PCDATA)>
+
+<!--
+The user-data-constraint element is used to indicate how data
+communicated between the client and container should be protected
+-->
+
+<!ELEMENT user-data-constraint (description?, transport-guarantee)>
+
+<!--
+The transport-guarantee element specifies that the communication
+between client and server should be NONE, INTEGRAL, or
+CONFIDENTIAL. NONE means that the application does not require any
+transport guarantees. A value of INTEGRAL means that the application
+requires that the data sent between the client and server be sent in
+such a way that it can't be changed in transit. CONFIDENTIAL means
+that the application requires that the data be transmitted in a
+fashion that prevents other entities from observing the contents of
+the transmission. In most cases, the presence of the INTEGRAL or
+CONFIDENTIAL flag will indicate that the use of SSL is required.
+-->
+
+<!ELEMENT transport-guarantee (#PCDATA)>
+
+<!--
+The auth-constraint element indicates the user roles that should
+be permitted access to this resource collection. The role used here
+must appear in a security-role-ref element.
+-->
+
+<!ELEMENT auth-constraint (description?, role-name*)>
+
+<!--
+The role-name element contains the name of a security role.
+-->
+
+<!ELEMENT role-name (#PCDATA)>
+
+<!--
+The login-config element is used to configure the authentication
+method that should be used, the realm name that should be used for
+this application, and the attributes that are needed by the form login
+mechanism.
+-->
+
+<!ELEMENT login-config (auth-method?, realm-name?, form-login-config?)>
+
+<!--
+The realm name element specifies the realm name to use in HTTP
+Basic authorization
+-->
+
+<!ELEMENT realm-name (#PCDATA)>
+
+<!--
+The form-login-config element specifies the login and error pages
+that should be used in form based login. If form based authentication
+is not used, these elements are ignored.
+-->
+
+<!ELEMENT form-login-config (form-login-page, form-error-page)>
+
+<!--
+The form-login-page element defines the location in the web app
+where the page that can be used for login can be found
+-->
+
+<!ELEMENT form-login-page (#PCDATA)>
+
+<!--
+The form-error-page element defines the location in the web app
+where the error page that is displayed when login is not successful
+can be found
+-->
+
+<!ELEMENT form-error-page (#PCDATA)>
+
+<!--
+The auth-method element is used to configure the authentication
+mechanism for the web application. As a prerequisite to gaining access
+to any web resources which are protected by an authorization
+constraint, a user must have authenticated using the configured
+mechanism. Legal values for this element are "BASIC", "DIGEST",
+"FORM", or "CLIENT-CERT".
+-->
+
+<!ELEMENT auth-method (#PCDATA)>
+
+<!--
+The security-role element contains the declaration of a security
+role which is used in the security-constraints placed on the web
+application.
+-->
+
+<!ELEMENT security-role (description?, role-name)>
+
+<!--
+The role-name element contains the name of a role. This element
+must contain a non-empty string.
+-->
+
+<!ELEMENT security-role-ref (description?, role-name, role-link)>
+
+<!--
+The role-link element is used to link a security role reference
+to a defined security role. The role-link element must contain the
+name of one of the security roles defined in the security-role
+elements.
+-->
+
+<!ELEMENT role-link (#PCDATA)>
+
+<!--
+The env-entry element contains the declaration of an
+application's environment entry. This element is required to be
+honored on in J2EE compliant servlet containers.
+-->
+
+<!ELEMENT env-entry (description?, env-entry-name, env-entry-value?,
+env-entry-type)>
+
+<!--
+The env-entry-name contains the name of an application's
+environment entry
+-->
+
+<!ELEMENT env-entry-name (#PCDATA)>
+
+<!--
+The env-entry-value element contains the value of an
+application's environment entry
+-->
+
+<!ELEMENT env-entry-value (#PCDATA)>
+
+<!--
+The env-entry-type element contains the fully qualified Java type
+of the environment entry value that is expected by the application
+code. The following are the legal values of env-entry-type:
+java.lang.Boolean, java.lang.String, java.lang.Integer,
+java.lang.Double, java.lang.Float.
+-->
+
+<!ELEMENT env-entry-type (#PCDATA)>
+
+<!--
+The ejb-ref element is used to declare a reference to an
+enterprise bean. 
+-->
+
+<!ELEMENT ejb-ref (description?, ejb-ref-name, ejb-ref-type, home, remote,
+ejb-link?)>
+
+<!--
+The ejb-ref-name element contains the name of an EJB
+reference. This is the JNDI name that the servlet code uses to get a
+reference to the enterprise bean.
+-->
+
+<!ELEMENT ejb-ref-name (#PCDATA)>
+
+<!--
+The ejb-ref-type element contains the expected java class type of
+the referenced EJB.
+-->
+
+<!ELEMENT ejb-ref-type (#PCDATA)>
+
+<!--
+The ejb-home element contains the fully qualified name of the
+EJB's home interface
+-->
+
+<!ELEMENT home (#PCDATA)>
+
+<!--
+The ejb-remote element contains the fully qualified name of the
+EJB's remote interface
+-->
+
+<!ELEMENT remote (#PCDATA)>
+
+<!--
+The ejb-link element is used in the ejb-ref element to specify
+that an EJB reference is linked to an EJB in an encompassing Java2
+Enterprise Edition (J2EE) application package. The value of the
+ejb-link element must be the ejb-name of and EJB in the J2EE
+application package.
+-->
+
+<!ELEMENT ejb-link (#PCDATA)>
+
+<!--
+The ID mechanism is to allow tools to easily make tool-specific
+references to the elements of the deployment descriptor. This allows
+tools that produce additional deployment information (i.e information
+beyond the standard deployment descriptor information) to store the
+non-standard information in a separate file, and easily refer from
+these tools-specific files to the information in the standard web-app
+deployment descriptor.
+-->
+
+<!ATTLIST web-app id ID #IMPLIED>
+<!ATTLIST icon id ID #IMPLIED>
+<!ATTLIST small-icon id ID #IMPLIED>
+<!ATTLIST large-icon id ID #IMPLIED>
+<!ATTLIST display-name id ID #IMPLIED>
+<!ATTLIST description id ID #IMPLIED>
+<!ATTLIST distributable id ID #IMPLIED>
+<!ATTLIST context-param id ID #IMPLIED>
+<!ATTLIST param-name id ID #IMPLIED>
+<!ATTLIST param-value id ID #IMPLIED>
+<!ATTLIST servlet id ID #IMPLIED>
+<!ATTLIST servlet-name id ID #IMPLIED>
+<!ATTLIST servlet-class id ID #IMPLIED>
+<!ATTLIST jsp-file id ID #IMPLIED>
+<!ATTLIST init-param id ID #IMPLIED>
+<!ATTLIST load-on-startup id ID #IMPLIED>
+<!ATTLIST servlet-mapping id ID #IMPLIED>
+<!ATTLIST url-pattern id ID #IMPLIED>
+<!ATTLIST session-config id ID #IMPLIED>
+<!ATTLIST session-timeout id ID #IMPLIED>
+<!ATTLIST mime-mapping id ID #IMPLIED>
+<!ATTLIST extension id ID #IMPLIED>
+<!ATTLIST mime-type id ID #IMPLIED>
+<!ATTLIST welcome-file-list id ID #IMPLIED>
+<!ATTLIST welcome-file id ID #IMPLIED>
+<!ATTLIST taglib id ID #IMPLIED>
+<!ATTLIST taglib-uri id ID #IMPLIED>
+<!ATTLIST taglib-location id ID #IMPLIED>
+<!ATTLIST error-page id ID #IMPLIED>
+<!ATTLIST error-code id ID #IMPLIED>
+<!ATTLIST exception-type id ID #IMPLIED>
+<!ATTLIST location id ID #IMPLIED>
+<!ATTLIST resource-ref id ID #IMPLIED>
+<!ATTLIST res-ref-name id ID #IMPLIED>
+<!ATTLIST res-type id ID #IMPLIED>
+<!ATTLIST res-auth id ID #IMPLIED>
+<!ATTLIST security-constraint id ID #IMPLIED>
+<!ATTLIST web-resource-collection id ID #IMPLIED>
+<!ATTLIST web-resource-name id ID #IMPLIED>
+<!ATTLIST http-method id ID #IMPLIED>
+<!ATTLIST user-data-constraint id ID #IMPLIED>
+<!ATTLIST transport-guarantee id ID #IMPLIED>
+<!ATTLIST auth-constraint id ID #IMPLIED>
+<!ATTLIST role-name id ID #IMPLIED>
+<!ATTLIST login-config id ID #IMPLIED>
+<!ATTLIST realm-name id ID #IMPLIED>
+<!ATTLIST form-login-config id ID #IMPLIED>
+<!ATTLIST form-login-page id ID #IMPLIED>
+<!ATTLIST form-error-page id ID #IMPLIED>
+<!ATTLIST auth-method id ID #IMPLIED>
+<!ATTLIST security-role id ID #IMPLIED>
+<!ATTLIST security-role-ref id ID #IMPLIED>
+<!ATTLIST role-link id ID #IMPLIED>
+<!ATTLIST env-entry id ID #IMPLIED>
+<!ATTLIST env-entry-name id ID #IMPLIED>
+<!ATTLIST env-entry-value id ID #IMPLIED>
+<!ATTLIST env-entry-type id ID #IMPLIED>
+<!ATTLIST ejb-ref id ID #IMPLIED>
+<!ATTLIST ejb-ref-name id ID #IMPLIED>
+<!ATTLIST ejb-ref-type id ID #IMPLIED>
+<!ATTLIST home id ID #IMPLIED>
+<!ATTLIST remote id ID #IMPLIED>
+<!ATTLIST ejb-link id ID #IMPLIED>
diff --git a/servletapi/jsr154/src/share/dtd/web-app_2_3.dtd b/servletapi/jsr154/src/share/dtd/web-app_2_3.dtd
new file mode 100644
index 0000000..67bed88
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/web-app_2_3.dtd
@@ -0,0 +1,1074 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!--
+Copyright 2000-2001 Sun Microsystems, Inc. 901 San Antonio Road,
+Palo Alto, CA  94303, U.S.A.  All rights reserved.
+
+This product or document is protected by copyright and distributed
+under licenses restricting its use, copying, distribution, and
+decompilation.  No part of this product or documentation may be
+reproduced in any form by any means without prior written authorization
+of Sun and its licensors, if any.
+
+Third party software, including font technology, is copyrighted and
+licensed from Sun suppliers.
+
+Sun, Sun Microsystems, the Sun Logo, Solaris, Java, JavaServer Pages, Java
+Naming and Directory Interface, JDBC, JDK, JavaMail and Enterprise JavaBeans,
+are trademarks or registered trademarks of Sun Microsystems, Inc in the U.S.
+and other countries.
+
+All SPARC trademarks are used under license and are trademarks
+or registered trademarks of SPARC International, Inc.
+in the U.S. and other countries. Products bearing SPARC
+trademarks are based upon an architecture developed by Sun Microsystems, Inc.
+
+PostScript is a registered trademark of Adobe Systems, Inc.
+
+Federal Acquisitions: Commercial Software - Government Users Subject to
+Standard License Terms and Conditions.
+
+DOCUMENTATION IS PROVIDED "AS IS" AND ALL EXPRESS OR IMPLIED
+CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT
+TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY
+INVALID.
+
+_________________________________________________________________________
+
+Copyright 2000-2001 Sun Microsystems, Inc.,
+901 San Antonio Road, Palo Alto, CA  94303, Etats-Unis.
+Tous droits re'serve's.
+
+
+Ce produit ou document est prote'ge' par un copyright et distribue' avec
+des licences qui en restreignent l'utilisation, la copie, la distribution,
+et la de'compilation.  Aucune partie de ce produit ou de sa documentation
+associe'e ne peut e^tre reproduite sous aucune forme, par quelque moyen
+que ce soit, sans l'autorisation pre'alable et e'crite de Sun et de ses
+bailleurs de licence, s'il y en a.
+
+Le logiciel de'tenu par des tiers, et qui comprend la technologie
+relative aux polices de caracte`res, est prote'ge' par un copyright
+et licencie' par des fournisseurs de Sun.
+
+Sun, Sun Microsystems, le logo Sun, Solaris, Java, JavaServer Pages, Java
+Naming and Directory Interface, JDBC, JDK, JavaMail, et Enterprise JavaBeans,
+sont des marques de fabrique ou des marques de'pose'es de Sun
+Microsystems, Inc. aux Etats-Unis et dans d'autres pays.
+
+Toutes les marques SPARC sont utilise'es sous licence et sont
+des marques de fabrique ou des marques de'pose'es de SPARC
+International, Inc. aux Etats-Unis et  dans
+d'autres pays. Les produits portant les marques SPARC sont
+base's sur une architecture de'veloppe'e par Sun Microsystems, Inc.
+
+Postcript est une marque enregistre'e d'Adobe Systems Inc.
+
+LA DOCUMENTATION EST FOURNIE "EN L'ETAT" ET TOUTES AUTRES CONDITIONS,
+DECLARATIONS ET GARANTIES EXPRESSES OU TACITES SONT FORMELLEMENT EXCLUES,
+DANS LA MESURE AUTORISEE PAR LA LOI APPLICABLE, Y COMPRIS NOTAMMENT
+TOUTE GARANTIE IMPLICITE RELATIVE A LA QUALITE MARCHANDE, A L'APTITUDE
+A UNE UTILISATION PARTICULIERE OU A L'ABSENCE DE CONTREFACON.
+-->
+
+<!--
+This is the XML DTD for the Servlet 2.3 deployment descriptor.
+All Servlet 2.3 deployment descriptors must include a DOCTYPE
+of the following form:
+
+  <!DOCTYPE web-app PUBLIC
+	"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+	"http://java.sun.com/dtd/web-app_2_3.dtd">
+
+-->
+
+<!--
+The following conventions apply to all J2EE deployment descriptor
+elements unless indicated otherwise.
+
+- In elements that contain PCDATA, leading and trailing whitespace
+  in the data may be ignored.
+
+- In elements whose value is an "enumerated type", the value is
+  case sensitive.
+
+- In elements that specify a pathname to a file within the same
+  JAR file, relative filenames (i.e., those not starting with "/")
+  are considered relative to the root of the JAR file's namespace.
+  Absolute filenames (i.e., those starting with "/") also specify
+  names in the root of the JAR file's namespace.  In general, relative
+  names are preferred.  The exception is .war files where absolute
+  names are preferred for consistency with the servlet API.
+-->
+
+
+<!--
+The web-app element is the root of the deployment descriptor for
+a web application.
+-->
+<!ELEMENT web-app (icon?, display-name?, description?, distributable?,
+context-param*, filter*, filter-mapping*, listener*, servlet*,
+servlet-mapping*, session-config?, mime-mapping*, welcome-file-list?,
+error-page*, taglib*, resource-env-ref*, resource-ref*, security-constraint*,
+login-config?, security-role*, env-entry*, ejb-ref*,  ejb-local-ref*)>
+
+<!--
+The auth-constraint element indicates the user roles that should
+be permitted access to this resource collection. The role-name
+used here must either correspond to the role-name of one of the
+security-role elements defined for this web application, or be
+the specially reserved role-name "*" that is a compact syntax for
+indicating all roles in the web application. If both "*" and
+rolenames appear, the container interprets this as all roles.
+If no roles are defined, no user is allowed access to the portion of
+the web application described by the containing security-constraint.
+The container matches role names case sensitively when determining
+access.
+
+
+Used in: security-constraint
+-->
+<!ELEMENT auth-constraint (description?, role-name*)>
+
+<!--
+The auth-method element is used to configure the authentication
+mechanism for the web application. As a prerequisite to gaining access to any web resources which are protected by an authorization
+constraint, a user must have authenticated using the configured
+mechanism. Legal values for this element are "BASIC", "DIGEST",
+"FORM", or "CLIENT-CERT".
+
+Used in: login-config
+-->
+<!ELEMENT auth-method (#PCDATA)>
+
+<!--
+The context-param element contains the declaration of a web
+application's servlet context initialization parameters.
+
+Used in: web-app
+-->
+<!ELEMENT context-param (param-name, param-value, description?)>
+
+<!--
+The description element is used to provide text describing the parent
+element.  The description element should include any information that
+the web application war file producer wants to provide to the consumer of
+the web application war file (i.e., to the Deployer). Typically, the tools
+used by the web application war file consumer will display the description
+when processing the parent element that contains the description.
+
+Used in: auth-constraint, context-param, ejb-local-ref, ejb-ref,
+env-entry, filter, init-param, resource-env-ref, resource-ref, run-as,
+security-role, security-role-ref, servlet, user-data-constraint,
+web-app, web-resource-collection
+-->
+<!ELEMENT description (#PCDATA)>
+
+<!--
+The display-name element contains a short name that is intended to be
+displayed by tools.  The display name need not be unique.
+
+Used in: filter, security-constraint, servlet, web-app
+
+Example:
+
+<display-name>Employee Self Service</display-name>
+-->
+<!ELEMENT display-name (#PCDATA)>
+
+<!--
+The distributable element, by its presence in a web application
+deployment descriptor, indicates that this web application is
+programmed appropriately to be deployed into a distributed servlet
+container
+
+Used in: web-app
+-->
+<!ELEMENT distributable EMPTY>
+
+<!--
+The ejb-link element is used in the ejb-ref or ejb-local-ref
+elements to specify that an EJB reference is linked to an
+enterprise bean.
+
+The name in the ejb-link element is composed of a
+path name specifying the ejb-jar containing the referenced enterprise
+bean with the ejb-name of the target bean appended and separated from
+the path name by "#".  The path name is relative to the war file
+containing the web application that is referencing the enterprise bean.
+This allows multiple enterprise beans with the same ejb-name to be
+uniquely identified.
+
+Used in: ejb-local-ref, ejb-ref
+
+Examples:
+
+	<ejb-link>EmployeeRecord</ejb-link>
+
+	<ejb-link>../products/product.jar#ProductEJB</ejb-link>
+
+-->
+<!ELEMENT ejb-link (#PCDATA)>
+
+<!--
+The ejb-local-ref element is used for the declaration of a reference to
+an enterprise bean's local home. The declaration consists of:
+
+	- an optional description
+	- the EJB reference name used in the code of the web application
+	  that's referencing the enterprise bean
+	- the expected type of the referenced enterprise bean
+	- the expected local home and local interfaces of the referenced
+	  enterprise bean
+	- optional ejb-link information, used to specify the referenced
+	  enterprise bean
+
+Used in: web-app
+-->
+<!ELEMENT ejb-local-ref (description?, ejb-ref-name, ejb-ref-type,
+		local-home, local, ejb-link?)>
+
+<!--
+The ejb-ref element is used for the declaration of a reference to
+an enterprise bean's home. The declaration consists of:
+
+	- an optional description
+	- the EJB reference name used in the code of
+	  the web application that's referencing the enterprise bean
+	- the expected type of the referenced enterprise bean
+	- the expected home and remote interfaces of the referenced
+	  enterprise bean
+	- optional ejb-link information, used to specify the referenced
+	  enterprise bean
+
+Used in: web-app
+-->
+<!ELEMENT ejb-ref (description?, ejb-ref-name, ejb-ref-type,
+		home, remote, ejb-link?)>
+
+<!--
+The ejb-ref-name element contains the name of an EJB reference. The
+EJB reference is an entry in the web application's environment and is
+relative to the java:comp/env context.  The name must be unique
+within the web application.
+
+It is recommended that name is prefixed with "ejb/".
+
+Used in: ejb-local-ref, ejb-ref
+
+Example:
+
+<ejb-ref-name>ejb/Payroll</ejb-ref-name>
+-->
+<!ELEMENT ejb-ref-name (#PCDATA)>
+
+<!--
+The ejb-ref-type element contains the expected type of the
+referenced enterprise bean.
+
+The ejb-ref-type element must be one of the following:
+
+	<ejb-ref-type>Entity</ejb-ref-type>
+	<ejb-ref-type>Session</ejb-ref-type>
+
+Used in: ejb-local-ref, ejb-ref
+-->
+<!ELEMENT ejb-ref-type (#PCDATA)>
+
+<!--
+The env-entry element contains the declaration of a web application's
+environment entry. The declaration consists of an optional
+description, the name of the environment entry, and an optional
+value.  If a value is not specified, one must be supplied
+during deployment.
+-->
+<!ELEMENT env-entry (description?, env-entry-name, env-entry-value?,
+env-entry-type)>
+
+<!--
+The env-entry-name element contains the name of a web applications's
+environment entry.  The name is a JNDI name relative to the
+java:comp/env context.  The name must be unique within a web application.
+
+Example:
+
+<env-entry-name>minAmount</env-entry-name>
+
+Used in: env-entry
+-->
+<!ELEMENT env-entry-name (#PCDATA)>
+
+<!--
+The env-entry-type element contains the fully-qualified Java type of
+the environment entry value that is expected by the web application's
+code.
+
+The following are the legal values of env-entry-type:
+
+	java.lang.Boolean
+	java.lang.Byte
+	java.lang.Character
+	java.lang.String
+	java.lang.Short
+	java.lang.Integer
+	java.lang.Long
+	java.lang.Float
+	java.lang.Double
+
+Used in: env-entry
+-->
+<!ELEMENT env-entry-type (#PCDATA)>
+
+<!--
+The env-entry-value element contains the value of a web application's
+environment entry. The value must be a String that is valid for the
+constructor of the specified type that takes a single String
+parameter, or for java.lang.Character, a single character.
+
+Example:
+
+<env-entry-value>100.00</env-entry-value>
+
+Used in: env-entry
+-->
+<!ELEMENT env-entry-value (#PCDATA)>
+
+<!--
+The error-code contains an HTTP error code, ex: 404
+
+Used in: error-page
+-->
+<!ELEMENT error-code (#PCDATA)>
+
+<!--
+The error-page element contains a mapping between an error code
+or exception type to the path of a resource in the web application
+
+Used in: web-app
+-->
+<!ELEMENT error-page ((error-code | exception-type), location)>
+
+<!--
+The exception type contains a fully qualified class name of a
+Java exception type.
+
+Used in: error-page
+-->
+<!ELEMENT exception-type (#PCDATA)>
+
+<!--
+The extension element contains a string describing an
+extension. example: "txt"
+
+Used in: mime-mapping
+-->
+<!ELEMENT extension (#PCDATA)>
+
+<!--
+Declares a filter in the web application. The filter is mapped to
+either a servlet or a URL pattern in the filter-mapping element, using
+the filter-name value to reference. Filters can access the
+initialization parameters declared in the deployment descriptor at
+runtime via the FilterConfig interface.
+
+Used in: web-app
+-->
+<!ELEMENT filter (icon?, filter-name, display-name?, description?,
+filter-class, init-param*)>
+
+<!--
+The fully qualified classname of the filter.
+
+Used in: filter
+-->
+<!ELEMENT filter-class (#PCDATA)>
+
+<!--
+Declaration of the filter mappings in this web application. The
+container uses the filter-mapping declarations to decide which filters
+to apply to a request, and in what order. The container matches the
+request URI to a Servlet in the normal way. To determine which filters
+to apply it matches filter-mapping declarations either on servlet-name,
+or on url-pattern for each filter-mapping element, depending on which
+style is used. The order in which filters are invoked is the order in
+which filter-mapping declarations that match a request URI for a
+servlet appear in the list of filter-mapping elements.The filter-name
+value must be the value of the <filter-name> sub-elements of one of the
+<filter> declarations in the deployment descriptor.
+
+Used in: web-app
+-->
+<!ELEMENT filter-mapping (filter-name, (url-pattern | servlet-name))>
+
+<!--
+The logical name of the filter. This name is used to map the filter.
+Each filter name is unique within the web application.
+
+Used in: filter, filter-mapping
+-->
+<!ELEMENT filter-name (#PCDATA)>
+
+<!--
+The form-error-page element defines the location in the web app
+where the error page that is displayed when login is not successful
+can be found. The path begins with a leading / and is interpreted
+relative to the root of the WAR.
+
+Used in: form-login-config
+-->
+<!ELEMENT form-error-page (#PCDATA)>
+
+<!--
+The form-login-config element specifies the login and error pages
+that should be used in form based login. If form based authentication
+is not used, these elements are ignored.
+
+Used in: login-config
+-->
+<!ELEMENT form-login-config (form-login-page, form-error-page)>
+
+<!--
+The form-login-page element defines the location in the web app
+where the page that can be used for login can be found. The path
+begins with a leading / and is interpreted relative to the root of the WAR.
+
+Used in: form-login-config
+-->
+<!ELEMENT form-login-page (#PCDATA)>
+
+<!--
+The home element contains the fully-qualified name of the enterprise
+bean's home interface.
+
+Used in: ejb-ref
+
+Example:
+
+<home>com.aardvark.payroll.PayrollHome</home>
+-->
+<!ELEMENT home (#PCDATA)>
+
+<!--
+The http-method contains an HTTP method (GET | POST |...).
+
+Used in: web-resource-collection
+-->
+<!ELEMENT http-method (#PCDATA)>
+
+<!--
+The icon element contains small-icon and large-icon elements that
+specify the file names for small and a large GIF or JPEG icon images
+used to represent the parent element in a GUI tool.
+
+Used in: filter, servlet, web-app
+-->
+<!ELEMENT icon (small-icon?, large-icon?)>
+
+<!--
+The init-param element contains a name/value pair as an
+initialization param of the servlet
+
+Used in: filter, servlet
+-->
+<!ELEMENT init-param (param-name, param-value, description?)>
+
+<!--
+The jsp-file element contains the full path to a JSP file within
+the web application beginning with a `/'.
+
+Used in: servlet
+-->
+<!ELEMENT jsp-file (#PCDATA)>
+
+<!--
+The large-icon element contains the name of a file
+containing a large (32 x 32) icon image. The file
+name is a relative path within the web application's
+war file.
+
+The image may be either in the JPEG or GIF format.
+The icon can be used by tools.
+
+Used in: icon
+
+Example:
+
+<large-icon>employee-service-icon32x32.jpg</large-icon>
+-->
+<!ELEMENT large-icon (#PCDATA)>
+
+<!--
+The listener element indicates the deployment properties for a web
+application listener bean.
+
+Used in: web-app
+-->
+<!ELEMENT listener (listener-class)>
+
+<!--
+The listener-class element declares a class in the application must be
+registered as a web application listener bean. The value is the fully qualified classname of the listener class.
+
+
+Used in: listener
+-->
+<!ELEMENT listener-class (#PCDATA)>
+
+<!--
+The load-on-startup element indicates that this servlet should be
+loaded (instantiated and have its init() called) on the startup
+of the web application. The optional contents of
+these element must be an integer indicating the order in which
+the servlet should be loaded. If the value is a negative integer,
+or the element is not present, the container is free to load the
+servlet whenever it chooses. If the value is a positive integer
+or 0, the container must load and initialize the servlet as the
+application is deployed. The container must guarantee that
+servlets marked with lower integers are loaded before servlets
+marked with higher integers. The container may choose the order
+of loading of servlets with the same load-on-start-up value.
+
+Used in: servlet
+-->
+<!ELEMENT load-on-startup (#PCDATA)>
+
+<!--
+
+The local element contains the fully-qualified name of the
+enterprise bean's local interface.
+
+Used in: ejb-local-ref
+
+-->
+<!ELEMENT local (#PCDATA)>
+
+<!--
+
+The local-home element contains the fully-qualified name of the
+enterprise bean's local home interface.
+
+Used in: ejb-local-ref
+-->
+<!ELEMENT local-home (#PCDATA)>
+
+<!--
+The location element contains the location of the resource in the web
+application relative to the root of the web application. The value of
+the location must have a leading `/'.
+
+Used in: error-page
+-->
+<!ELEMENT location (#PCDATA)>
+
+<!--
+The login-config element is used to configure the authentication
+method that should be used, the realm name that should be used for
+this application, and the attributes that are needed by the form login
+mechanism.
+
+Used in: web-app
+-->
+<!ELEMENT login-config (auth-method?, realm-name?, form-login-config?)>
+
+<!--
+The mime-mapping element defines a mapping between an extension
+and a mime type.
+
+Used in: web-app
+-->
+<!ELEMENT mime-mapping (extension, mime-type)>
+
+<!--
+The mime-type element contains a defined mime type. example:
+"text/plain"
+
+Used in: mime-mapping
+-->
+<!ELEMENT mime-type (#PCDATA)>
+
+<!--
+The param-name element contains the name of a parameter. Each parameter
+name must be unique in the web application.
+
+
+Used in: context-param, init-param
+-->
+<!ELEMENT param-name (#PCDATA)>
+
+<!--
+The param-value element contains the value of a parameter.
+
+Used in: context-param, init-param
+-->
+<!ELEMENT param-value (#PCDATA)>
+
+<!--
+The realm name element specifies the realm name to use in HTTP
+Basic authorization.
+
+Used in: login-config
+-->
+<!ELEMENT realm-name (#PCDATA)>
+
+<!--
+The remote element contains the fully-qualified name of the enterprise
+bean's remote interface.
+
+Used in: ejb-ref
+
+Example:
+
+<remote>com.wombat.empl.EmployeeService</remote>
+-->
+<!ELEMENT remote (#PCDATA)>
+
+<!--
+The res-auth element specifies whether the web application code signs
+on programmatically to the resource manager, or whether the Container
+will sign on to the resource manager on behalf of the web application. In the
+latter case, the Container uses information that is supplied by the
+Deployer.
+
+The value of this element must be one of the two following:
+
+	<res-auth>Application</res-auth>
+	<res-auth>Container</res-auth>
+
+Used in: resource-ref
+-->
+<!ELEMENT res-auth (#PCDATA)>
+
+<!--
+The res-ref-name element specifies the name of a resource manager
+connection factory reference.  The name is a JNDI name relative to the
+java:comp/env context.  The name must be unique within a web application.
+
+Used in: resource-ref
+-->
+<!ELEMENT res-ref-name (#PCDATA)>
+
+<!--
+The res-sharing-scope element specifies whether connections obtained
+through the given resource manager connection factory reference can be
+shared. The value of this element, if specified, must be one of the
+two following:
+
+	<res-sharing-scope>Shareable</res-sharing-scope>
+	<res-sharing-scope>Unshareable</res-sharing-scope>
+
+The default value is Shareable.
+
+Used in: resource-ref
+-->
+<!ELEMENT res-sharing-scope (#PCDATA)>
+
+<!--
+The res-type element specifies the type of the data source. The type
+is specified by the fully qualified Java language class or interface
+expected to be implemented by the data source.
+
+Used in: resource-ref
+-->
+<!ELEMENT res-type (#PCDATA)>
+
+<!--
+The resource-env-ref element contains a declaration of a web application's
+reference to an administered object associated with a resource
+in the web application's environment.  It consists of an optional
+description, the resource environment reference name, and an
+indication of the resource environment reference type expected by
+the web application code.
+
+Used in: web-app
+
+Example:
+
+<resource-env-ref>
+    <resource-env-ref-name>jms/StockQueue</resource-env-ref-name>
+    <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
+</resource-env-ref>
+-->
+<!ELEMENT resource-env-ref (description?, resource-env-ref-name,
+		resource-env-ref-type)>
+
+<!--
+The resource-env-ref-name element specifies the name of a resource
+environment reference; its value is the environment entry name used in
+the web application code.  The name is a JNDI name relative to the
+java:comp/env context and must be unique within a web application.
+
+Used in: resource-env-ref
+-->
+<!ELEMENT resource-env-ref-name (#PCDATA)>
+
+<!--
+The resource-env-ref-type element specifies the type of a resource
+environment reference.  It is the fully qualified name of a Java
+language class or interface.
+
+Used in: resource-env-ref
+-->
+<!ELEMENT resource-env-ref-type (#PCDATA)>
+
+<!--
+The resource-ref element contains a declaration of a web application's
+reference to an external resource. It consists of an optional
+description, the resource manager connection factory reference name,
+the indication of the resource manager connection factory type
+expected by the web application code, the type of authentication
+(Application or Container), and an optional specification of the
+shareability of connections obtained from the resource (Shareable or
+Unshareable).
+
+Used in: web-app
+
+Example:
+
+    <resource-ref>
+	<res-ref-name>jdbc/EmployeeAppDB</res-ref-name>
+	<res-type>javax.sql.DataSource</res-type>
+	<res-auth>Container</res-auth>
+	<res-sharing-scope>Shareable</res-sharing-scope>
+    </resource-ref>
+-->
+<!ELEMENT resource-ref (description?, res-ref-name, res-type, res-auth,
+		res-sharing-scope?)>
+
+<!--
+The role-link element is a reference to a defined security role. The
+role-link element must contain the name of one of the security roles
+defined in the security-role elements.
+
+Used in: security-role-ref
+-->
+<!ELEMENT role-link (#PCDATA)>
+
+<!--
+The role-name element contains the name of a security role.
+
+The name must conform to the lexical rules for an NMTOKEN.
+
+Used in: auth-constraint, run-as, security-role, security-role-ref
+-->
+<!ELEMENT role-name (#PCDATA)>
+
+<!--
+The run-as element specifies the run-as identity to be used for the
+execution of the web application. It contains an optional description, and
+the name of a security role.
+
+Used in: servlet
+-->
+<!ELEMENT run-as (description?, role-name)>
+
+<!--
+The security-constraint element is used to associate security
+constraints with one or more web resource collections
+
+Used in: web-app
+-->
+<!ELEMENT security-constraint (display-name?, web-resource-collection+,
+auth-constraint?, user-data-constraint?)>
+
+<!--
+The security-role element contains the definition of a security
+role. The definition consists of an optional description of the
+security role, and the security role name.
+
+Used in: web-app
+
+Example:
+
+    <security-role>
+	<description>
+	    This role includes all employees who are authorized
+	    to access the employee service application.
+	</description>
+	<role-name>employee</role-name>
+    </security-role>
+-->
+<!ELEMENT security-role (description?, role-name)>
+
+<!--
+The security-role-ref element contains the declaration of a security
+role reference in the web application's code. The declaration consists
+of an optional description, the security role name used in the code,
+and an optional link to a security role. If the security role is not
+specified, the Deployer must choose an appropriate security role.
+
+The value of the role-name element must be the String used as the
+parameter to the EJBContext.isCallerInRole(String roleName) method
+or the HttpServletRequest.isUserInRole(String role) method.
+
+Used in: servlet
+
+-->
+<!ELEMENT security-role-ref (description?, role-name, role-link?)>
+
+<!--
+The servlet element contains the declarative data of a
+servlet. If a jsp-file is specified and the load-on-startup element is
+present, then the JSP should be precompiled and loaded.
+
+Used in: web-app
+-->
+<!ELEMENT servlet (icon?, servlet-name, display-name?, description?,
+(servlet-class|jsp-file), init-param*, load-on-startup?, run-as?, security-role-ref*)>
+
+<!--
+The servlet-class element contains the fully qualified class name
+of the servlet.
+
+Used in: servlet
+-->
+<!ELEMENT servlet-class (#PCDATA)>
+
+<!--
+The servlet-mapping element defines a mapping between a servlet
+and a url pattern
+
+Used in: web-app
+-->
+<!ELEMENT servlet-mapping (servlet-name, url-pattern)>
+
+<!--
+The servlet-name element contains the canonical name of the
+servlet. Each servlet name is unique within the web application.
+
+Used in: filter-mapping, servlet, servlet-mapping
+-->
+<!ELEMENT servlet-name (#PCDATA)>
+
+<!--
+The session-config element defines the session parameters for
+this web application.
+
+Used in: web-app
+-->
+<!ELEMENT session-config (session-timeout?)>
+
+<!--
+The session-timeout element defines the default session timeout
+interval for all sessions created in this web application. The
+specified timeout must be expressed in a whole number of minutes.
+If the timeout is 0 or less, the container ensures the default
+behaviour of sessions is never to time out.
+
+Used in: session-config
+-->
+<!ELEMENT session-timeout (#PCDATA)>
+
+<!--
+The small-icon element contains the name of a file
+containing a small (16 x 16) icon image. The file
+name is a relative path within the web application's
+war file.
+
+The image may be either in the JPEG or GIF format.
+The icon can be used by tools.
+
+Used in: icon
+
+Example:
+
+<small-icon>employee-service-icon16x16.jpg</small-icon>
+-->
+<!ELEMENT small-icon (#PCDATA)>
+
+<!--
+The taglib element is used to describe a JSP tag library.
+
+Used in: web-app
+-->
+<!ELEMENT taglib (taglib-uri, taglib-location)>
+
+<!--
+the taglib-location element contains the location (as a resource
+relative to the root of the web application) where to find the Tag
+Libary Description file for the tag library.
+
+Used in: taglib
+-->
+<!ELEMENT taglib-location (#PCDATA)>
+
+<!--
+The taglib-uri element describes a URI, relative to the location
+of the web.xml document, identifying a Tag Library used in the Web
+Application.
+
+Used in: taglib
+-->
+<!ELEMENT taglib-uri (#PCDATA)>
+
+<!--
+The transport-guarantee element specifies that the communication
+between client and server should be NONE, INTEGRAL, or
+CONFIDENTIAL. NONE means that the application does not require any
+transport guarantees. A value of INTEGRAL means that the application
+requires that the data sent between the client and server be sent in
+such a way that it can't be changed in transit. CONFIDENTIAL means
+that the application requires that the data be transmitted in a
+fashion that prevents other entities from observing the contents of
+the transmission. In most cases, the presence of the INTEGRAL or
+CONFIDENTIAL flag will indicate that the use of SSL is required.
+
+Used in: user-data-constraint
+-->
+<!ELEMENT transport-guarantee (#PCDATA)>
+
+<!--
+The url-pattern element contains the url pattern of the mapping. Must
+follow the rules specified in Section 11.2 of the Servlet API
+Specification.
+
+Used in: filter-mapping, servlet-mapping, web-resource-collection
+-->
+<!ELEMENT url-pattern (#PCDATA)>
+
+<!--
+The user-data-constraint element is used to indicate how data
+communicated between the client and container should be protected.
+
+Used in: security-constraint
+-->
+<!ELEMENT user-data-constraint (description?, transport-guarantee)>
+
+<!--
+The web-resource-collection element is used to identify a subset
+of the resources and HTTP methods on those resources within a web
+application to which a security constraint applies. If no HTTP methods
+are specified, then the security constraint applies to all HTTP
+methods.
+
+Used in: security-constraint
+-->
+<!ELEMENT web-resource-collection (web-resource-name, description?,
+url-pattern*, http-method*)>
+
+<!--
+The web-resource-name contains the name of this web resource
+collection.
+
+Used in: web-resource-collection
+-->
+<!ELEMENT web-resource-name (#PCDATA)>
+
+<!--
+The welcome-file element contains file name to use as a default
+welcome file, such as index.html
+
+Used in: welcome-file-list
+-->
+<!ELEMENT welcome-file (#PCDATA)>
+
+<!--
+The welcome-file-list contains an ordered list of welcome files
+elements.
+
+Used in: web-app
+-->
+<!ELEMENT welcome-file-list (welcome-file+)>
+
+<!--
+The ID mechanism is to allow tools that produce additional deployment
+information (i.e., information beyond the standard deployment
+descriptor information) to store the non-standard information in a
+separate file, and easily refer from these tool-specific files to the
+information in the standard deployment descriptor.
+
+Tools are not allowed to add the non-standard information into the
+standard deployment descriptor.
+-->
+
+<!ATTLIST auth-constraint id ID #IMPLIED>
+<!ATTLIST auth-method id ID #IMPLIED>
+<!ATTLIST context-param id ID #IMPLIED>
+<!ATTLIST description id ID #IMPLIED>
+<!ATTLIST display-name id ID #IMPLIED>
+<!ATTLIST distributable id ID #IMPLIED>
+<!ATTLIST ejb-link id ID #IMPLIED>
+<!ATTLIST ejb-local-ref id ID #IMPLIED>
+<!ATTLIST ejb-ref id ID #IMPLIED>
+<!ATTLIST ejb-ref-name id ID #IMPLIED>
+<!ATTLIST ejb-ref-type id ID #IMPLIED>
+<!ATTLIST env-entry id ID #IMPLIED>
+<!ATTLIST env-entry-name id ID #IMPLIED>
+<!ATTLIST env-entry-type id ID #IMPLIED>
+<!ATTLIST env-entry-value id ID #IMPLIED>
+<!ATTLIST error-code id ID #IMPLIED>
+<!ATTLIST error-page id ID #IMPLIED>
+<!ATTLIST exception-type id ID #IMPLIED>
+<!ATTLIST extension id ID #IMPLIED>
+<!ATTLIST filter id ID #IMPLIED>
+<!ATTLIST filter-class id ID #IMPLIED>
+<!ATTLIST filter-mapping id ID #IMPLIED>
+<!ATTLIST filter-name id ID #IMPLIED>
+<!ATTLIST form-error-page id ID #IMPLIED>
+<!ATTLIST form-login-config id ID #IMPLIED>
+<!ATTLIST form-login-page id ID #IMPLIED>
+<!ATTLIST home id ID #IMPLIED>
+<!ATTLIST http-method id ID #IMPLIED>
+<!ATTLIST icon id ID #IMPLIED>
+<!ATTLIST init-param id ID #IMPLIED>
+<!ATTLIST jsp-file id ID #IMPLIED>
+<!ATTLIST large-icon id ID #IMPLIED>
+<!ATTLIST listener id ID #IMPLIED>
+<!ATTLIST listener-class id ID #IMPLIED>
+<!ATTLIST load-on-startup id ID #IMPLIED>
+<!ATTLIST local id ID #IMPLIED>
+<!ATTLIST local-home id ID #IMPLIED>
+<!ATTLIST location id ID #IMPLIED>
+<!ATTLIST login-config id ID #IMPLIED>
+<!ATTLIST mime-mapping id ID #IMPLIED>
+<!ATTLIST mime-type id ID #IMPLIED>
+<!ATTLIST param-name id ID #IMPLIED>
+<!ATTLIST param-value id ID #IMPLIED>
+<!ATTLIST realm-name id ID #IMPLIED>
+<!ATTLIST remote id ID #IMPLIED>
+<!ATTLIST res-auth id ID #IMPLIED>
+<!ATTLIST res-ref-name id ID #IMPLIED>
+<!ATTLIST res-sharing-scope id ID #IMPLIED>
+<!ATTLIST res-type id ID #IMPLIED>
+<!ATTLIST resource-env-ref id ID #IMPLIED>
+<!ATTLIST resource-env-ref-name id ID #IMPLIED>
+<!ATTLIST resource-env-ref-type id ID #IMPLIED>
+<!ATTLIST resource-ref id ID #IMPLIED>
+<!ATTLIST role-link id ID #IMPLIED>
+<!ATTLIST role-name id ID #IMPLIED>
+<!ATTLIST run-as id ID #IMPLIED>
+<!ATTLIST security-constraint id ID #IMPLIED>
+<!ATTLIST security-role id ID #IMPLIED>
+<!ATTLIST security-role-ref id ID #IMPLIED>
+<!ATTLIST servlet id ID #IMPLIED>
+<!ATTLIST servlet-class id ID #IMPLIED>
+<!ATTLIST servlet-mapping id ID #IMPLIED>
+<!ATTLIST servlet-name id ID #IMPLIED>
+<!ATTLIST session-config id ID #IMPLIED>
+<!ATTLIST session-timeout id ID #IMPLIED>
+<!ATTLIST small-icon id ID #IMPLIED>
+<!ATTLIST taglib id ID #IMPLIED>
+<!ATTLIST taglib-location id ID #IMPLIED>
+<!ATTLIST taglib-uri id ID #IMPLIED>
+<!ATTLIST transport-guarantee id ID #IMPLIED>
+<!ATTLIST url-pattern id ID #IMPLIED>
+<!ATTLIST user-data-constraint id ID #IMPLIED>
+<!ATTLIST web-app id ID #IMPLIED>
+<!ATTLIST web-resource-collection id ID #IMPLIED>
+<!ATTLIST web-resource-name id ID #IMPLIED>
+<!ATTLIST welcome-file id ID #IMPLIED>
+<!ATTLIST welcome-file-list id ID #IMPLIED>
diff --git a/servletapi/jsr154/src/share/dtd/web-app_2_4.xsd b/servletapi/jsr154/src/share/dtd/web-app_2_4.xsd
new file mode 100644
index 0000000..c5dbfa2
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/web-app_2_4.xsd
@@ -0,0 +1,1248 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
+	    targetNamespace="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
+	    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	    elementFormDefault="qualified"
+	    attributeFormDefault="unqualified"
+	    version="2.4">
+  <xsd:annotation>
+    <xsd:documentation>
+      %W% %E%
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      Copyright 2004 Sun Microsystems, Inc., 901 San Antonio
+      Road, Palo Alto, California 94303, U.S.A. All rights
+      reserved.
+
+      Sun Microsystems, Inc. has intellectual property rights
+      relating to technology described in this document. In
+      particular, and without limitation, these intellectual
+      property rights may include one or more of the U.S. patents
+      listed at http://www.sun.com/patents and one or more
+      additional patents or pending patent applications in the
+      U.S. and other countries.
+
+      This document and the technology which it describes are
+      distributed under licenses restricting their use, copying,
+      distribution, and decompilation. No part of this document
+      may be reproduced in any form by any means without prior
+      written authorization of Sun and its licensors, if any.
+
+      Third-party software, including font technology, is
+      copyrighted and licensed from Sun suppliers.
+
+      Sun, Sun Microsystems, the Sun logo, Solaris, Java, J2EE,
+      JavaServer Pages, Enterprise JavaBeans and the Java Coffee
+      Cup logo are trademarks or registered trademarks of Sun
+      Microsystems, Inc. in the U.S. and other countries.
+
+      Federal Acquisitions: Commercial Software - Government Users
+      Subject to Standard License Terms and Conditions.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+      <![CDATA[
+
+	This is the XML Schema for the Servlet 2.4 deployment descriptor.
+	The deployment descriptor must be named "WEB-INF/web.xml" in the
+	web application's war file.  All Servlet deployment descriptors
+	must indicate the web application schema by using the J2EE
+	namespace:
+
+	http://java.sun.com/xml/ns/j2ee
+
+	and by indicating the version of the schema by
+	using the version element as shown below:
+
+	    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+	      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	      xsi:schemaLocation="..."
+	      version="2.4">
+	      ...
+	    </web-app>
+
+	The instance documents may indicate the published version of
+	the schema using the xsi:schemaLocation attribute for J2EE
+	namespace with the following location:
+
+	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd
+
+	]]>
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+
+      The following conventions apply to all J2EE
+      deployment descriptor elements unless indicated otherwise.
+
+      - In elements that specify a pathname to a file within the
+	same JAR file, relative filenames (i.e., those not
+	starting with "/") are considered relative to the root of
+	the JAR file's namespace.  Absolute filenames (i.e., those
+	starting with "/") also specify names in the root of the
+	JAR file's namespace.  In general, relative names are
+	preferred.  The exception is .war files where absolute
+	names are preferred for consistency with the Servlet API.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:include schemaLocation="j2ee_1_4.xsd"/>
+  <xsd:include schemaLocation="jsp_2_0.xsd"/>
+
+
+<!-- **************************************************** -->
+
+
+  <xsd:element name="web-app" type="j2ee:web-appType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The web-app element is the root of the deployment
+	descriptor for a web application.  Note that the sub-elements
+	of this element can be in the arbitrary order. Because of
+	that, the multiplicity of the elements of distributable,
+	session-config, welcome-file-list, jsp-config, login-config,
+	and locale-encoding-mapping-list was changed from "?" to "*"
+	in this schema.  However, the deployment descriptor instance
+	file must not contain multiple elements of session-config,
+	jsp-config, and login-config. When there are multiple elements of
+	welcome-file-list or locale-encoding-mapping-list, the container
+	must concatinate the element contents.  The multiple occurance
+	of the element distributable is redundant and the container
+	treats that case exactly in the same way when there is only
+	one distributable.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:unique name="web-app-servlet-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The servlet element contains the name of a servlet.
+	  The name must be unique within the web application.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:servlet"/>
+      <xsd:field    xpath="j2ee:servlet-name"/>
+    </xsd:unique>
+
+    <xsd:unique name="web-app-filter-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The filter element contains the name of a filter.
+	  The name must be unique within the web application.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:filter"/>
+      <xsd:field    xpath="j2ee:filter-name"/>
+    </xsd:unique>
+
+    <xsd:unique name="web-app-ejb-local-ref-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The ejb-local-ref-name element contains the name of an EJB
+	  reference. The EJB reference is an entry in the web
+	  application's environment and is relative to the
+	  java:comp/env context.  The name must be unique within
+	  the web application.
+
+	  It is recommended that name is prefixed with "ejb/".
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:ejb-local-ref"/>
+      <xsd:field    xpath="j2ee:ejb-ref-name"/>
+    </xsd:unique>
+
+    <xsd:unique name="web-app-ejb-ref-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The ejb-ref-name element contains the name of an EJB
+	  reference. The EJB reference is an entry in the web
+	  application's environment and is relative to the
+	  java:comp/env context.  The name must be unique within
+	  the web application.
+
+	  It is recommended that name is prefixed with "ejb/".
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:ejb-ref"/>
+      <xsd:field    xpath="j2ee:ejb-ref-name"/>
+    </xsd:unique>
+
+    <xsd:unique name="web-app-resource-env-ref-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The resource-env-ref-name element specifies the name of
+	  a resource environment reference; its value is the
+	  environment entry name used in the web application code.
+	  The name is a JNDI name relative to the java:comp/env
+	  context and must be unique within a web application.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:resource-env-ref"/>
+      <xsd:field    xpath="j2ee:resource-env-ref-name"/>
+    </xsd:unique>
+
+    <xsd:unique name="web-app-message-destination-ref-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The message-destination-ref-name element specifies the name of
+	  a message destination reference; its value is the
+	  environment entry name used in the web application code.
+	  The name is a JNDI name relative to the java:comp/env
+	  context and must be unique within a web application.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:message-destination-ref"/>
+      <xsd:field    xpath="j2ee:message-destination-ref-name"/>
+    </xsd:unique>
+
+    <xsd:unique name="web-app-res-ref-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The res-ref-name element specifies the name of a
+	  resource manager connection factory reference.  The name
+	  is a JNDI name relative to the java:comp/env context.
+	  The name must be unique within a web application.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:resource-ref"/>
+      <xsd:field    xpath="j2ee:res-ref-name"/>
+    </xsd:unique>
+
+    <xsd:unique name="web-app-env-entry-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The env-entry-name element contains the name of a web
+	  application's environment entry.  The name is a JNDI
+	  name relative to the java:comp/env context.  The name
+	  must be unique within a web application.
+
+	</xsd:documentation>
+      </xsd:annotation>
+
+      <xsd:selector xpath="j2ee:env-entry"/>
+      <xsd:field    xpath="j2ee:env-entry-name"/>
+    </xsd:unique>
+
+    <xsd:key name="web-app-role-name-key">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  A role-name-key is specified to allow the references
+	  from the security-role-refs.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:security-role"/>
+      <xsd:field    xpath="j2ee:role-name"/>
+    </xsd:key>
+
+    <xsd:keyref name="web-app-role-name-references"
+		refer="j2ee:web-app-role-name-key">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The keyref indicates the references from
+	  security-role-ref to a specified role-name.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:servlet/j2ee:security-role-ref"/>
+      <xsd:field    xpath="j2ee:role-link"/>
+    </xsd:keyref>
+  </xsd:element>
+
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="auth-constraintType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The auth-constraintType indicates the user roles that
+	should be permitted access to this resource
+	collection. The role-name used here must either correspond
+	to the role-name of one of the security-role elements
+	defined for this web application, or be the specially
+	reserved role-name "*" that is a compact syntax for
+	indicating all roles in the web application. If both "*"
+	and rolenames appear, the container interprets this as all
+	roles.  If no roles are defined, no user is allowed access
+	to the portion of the web application described by the
+	containing security-constraint.  The container matches
+	role names case sensitively when determining access.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="role-name"
+		   type="j2ee:role-nameType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="auth-methodType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The auth-methodType is used to configure the authentication
+	mechanism for the web application. As a prerequisite to
+	gaining access to any web resources which are protected by
+	an authorization constraint, a user must have authenticated
+	using the configured mechanism. Legal values are "BASIC",
+	"DIGEST", "FORM", "CLIENT-CERT", or a vendor-specific
+	authentication scheme.
+
+	Used in: login-config
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="dispatcherType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The dispatcher has four legal values: FORWARD, REQUEST, INCLUDE,
+	and ERROR. A value of FORWARD means the Filter will be applied
+	under RequestDispatcher.forward() calls.  A value of REQUEST
+	means the Filter will be applied under ordinary client calls to
+	the path or servlet. A value of INCLUDE means the Filter will be
+	applied under RequestDispatcher.include() calls.  A value of
+	ERROR means the Filter will be applied under the error page
+	mechanism.  The absence of any dispatcher elements in a
+	filter-mapping indicates a default of applying filters only under
+	ordinary client calls to the path or servlet.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="FORWARD"/>
+	<xsd:enumeration value="INCLUDE"/>
+	<xsd:enumeration value="REQUEST"/>
+	<xsd:enumeration value="ERROR"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:simpleType name="encodingType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The encodingType defines IANA character sets.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:restriction base="xsd:string">
+      <xsd:pattern value="[^\s]+"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="error-codeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The error-code contains an HTTP error code, ex: 404
+
+	Used in: error-page
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:xsdPositiveIntegerType">
+	<xsd:pattern value="\d{3}"/>
+	<xsd:attribute name="id" type="xsd:ID"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="error-pageType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The error-pageType contains a mapping between an error code
+	or exception type to the path of a resource in the web
+	application.
+
+	Used in: web-app
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:choice>
+	<xsd:element name="error-code"
+		     type="j2ee:error-codeType"/>
+
+	<xsd:element name="exception-type"
+		     type="j2ee:fully-qualified-classType">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      The exception-type contains a fully qualified class
+	      name of a Java exception type.
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	</xsd:element>
+      </xsd:choice>
+
+      <xsd:element name="location"
+		   type="j2ee:war-pathType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The location element contains the location of the
+	    resource in the web application relative to the root of
+	    the web application. The value of the location must have
+	    a leading `/'.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="filter-mappingType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	Declaration of the filter mappings in this web
+	application is done by using filter-mappingType.
+	The container uses the filter-mapping
+	declarations to decide which filters to apply to a request,
+	and in what order. The container matches the request URI to
+	a Servlet in the normal way. To determine which filters to
+	apply it matches filter-mapping declarations either on
+	servlet-name, or on url-pattern for each filter-mapping
+	element, depending on which style is used. The order in
+	which filters are invoked is the order in which
+	filter-mapping declarations that match a request URI for a
+	servlet appear in the list of filter-mapping elements.The
+	filter-name value must be the value of the filter-name
+	sub-elements of one of the filter declarations in the
+	deployment descriptor.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="filter-name"
+		   type="j2ee:filter-nameType"/>
+      <xsd:choice>
+	<xsd:element name="url-pattern"
+		     type="j2ee:url-patternType"/>
+	<xsd:element name="servlet-name"
+		     type="j2ee:servlet-nameType"/>
+      </xsd:choice>
+      <xsd:element name="dispatcher"
+		   type="j2ee:dispatcherType"
+		   minOccurs="0" maxOccurs="4"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="filter-nameType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The logical name of the filter is declare
+	by using filter-nameType. This name is used to map the
+	filter.  Each filter name is unique within the web
+	application.
+
+	Used in: filter, filter-mapping
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:extension base="j2ee:nonEmptyStringType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="filterType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The filterType is used to declare a filter in the web
+	application. The filter is mapped to either a servlet or a
+	URL pattern in the filter-mapping element, using the
+	filter-name value to reference. Filters can access the
+	initialization parameters declared in the deployment
+	descriptor at runtime via the FilterConfig interface.
+
+	Used in: web-app
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="filter-name"
+		   type="j2ee:filter-nameType"/>
+      <xsd:element name="filter-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The fully qualified classname of the filter.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="init-param"
+		   type="j2ee:param-valueType"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The init-param element contains a name/value pair as
+	    an initialization param of a servlet filter
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="form-login-configType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The form-login-configType specifies the login and error
+	pages that should be used in form based login. If form based
+	authentication is not used, these elements are ignored.
+
+	Used in: login-config
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+
+      <xsd:element name="form-login-page"
+		   type="j2ee:war-pathType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The form-login-page element defines the location in the web
+	    app where the page that can be used for login can be
+	    found.  The path begins with a leading / and is interpreted
+	    relative to the root of the WAR.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="form-error-page"
+		   type="j2ee:war-pathType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The form-error-page element defines the location in
+	    the web app where the error page that is displayed
+	    when login is not successful can be found.
+	    The path begins with a leading / and is interpreted
+	    relative to the root of the WAR.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="http-methodType">
+    <xsd:annotation>
+
+      <xsd:documentation>
+
+	The http-method contains an HTTP method recognized by the
+	web-app, for example GET, POST, ...
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="GET"/>
+	<xsd:enumeration value="POST"/>
+	<xsd:enumeration value="PUT"/>
+	<xsd:enumeration value="DELETE"/>
+	<xsd:enumeration value="HEAD"/>
+	<xsd:enumeration value="OPTIONS"/>
+	<xsd:enumeration value="TRACE"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="locale-encoding-mapping-listType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The locale-encoding-mapping-list contains one or more
+	locale-encoding-mapping(s).
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="locale-encoding-mapping"
+		   type="j2ee:locale-encoding-mappingType"
+		   maxOccurs="unbounded"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="locale-encoding-mappingType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The locale-encoding-mapping contains locale name and
+	encoding name. The locale name must be either "Language-code",
+	such as "ja", defined by ISO-639 or "Language-code_Country-code",
+	such as "ja_JP".  "Country code" is defined by ISO-3166.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="locale"
+		   type="j2ee:localeType"/>
+      <xsd:element name="encoding"
+		   type="j2ee:encodingType"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:simpleType name="localeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The localeType defines valid locale defined by ISO-639-1
+	and ISO-3166.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:restriction base="xsd:string">
+      <xsd:pattern value="[a-z]{2}(_|-)?([\p{L}\-\p{Nd}]{2})?"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="login-configType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The login-configType is used to configure the authentication
+	method that should be used, the realm name that should be
+	used for this application, and the attributes that are
+	needed by the form login mechanism.
+
+	Used in: web-app
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="auth-method"
+		   type="j2ee:auth-methodType"
+		   minOccurs="0"/>
+      <xsd:element name="realm-name"
+		   type="j2ee:string" minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The realm name element specifies the realm name to
+	    use in HTTP Basic authorization.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="form-login-config"
+		   type="j2ee:form-login-configType"
+		   minOccurs="0"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="mime-mappingType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The mime-mappingType defines a mapping between an extension
+	and a mime type.
+
+	Used in: web-app
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The extension element contains a string describing an
+	  extension. example: "txt"
+
+	</xsd:documentation>
+      </xsd:annotation>
+
+      <xsd:element name="extension"
+		   type="j2ee:string"/>
+      <xsd:element name="mime-type"
+		   type="j2ee:mime-typeType"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="mime-typeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The mime-typeType is used to indicate a defined mime type.
+
+	Example:
+	"text/plain"
+
+	Used in: mime-mapping
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+          <xsd:pattern value="[^\p{Cc}^\s]+/[^\p{Cc}^\s]+"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="nonEmptyStringType">
+    <xsd:annotation>
+      <xsd:documentation>
+	This type defines a string which contains at least one
+	character.
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:minLength value="1"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="security-constraintType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The security-constraintType is used to associate
+	security constraints with one or more web resource
+	collections
+
+	Used in: web-app
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="display-name"
+		   type="j2ee:display-nameType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="web-resource-collection"
+		   type="j2ee:web-resource-collectionType"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="auth-constraint"
+		   type="j2ee:auth-constraintType"
+		   minOccurs="0"/>
+      <xsd:element name="user-data-constraint"
+		   type="j2ee:user-data-constraintType"
+		   minOccurs="0"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="servlet-mappingType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The servlet-mappingType defines a mapping between a
+	servlet and a url pattern.
+
+	Used in: web-app
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="servlet-name"
+		   type="j2ee:servlet-nameType"/>
+      <xsd:element name="url-pattern"
+		   type="j2ee:url-patternType"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="servlet-nameType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The servlet-name element contains the canonical name of the
+	servlet. Each servlet name is unique within the web
+	application.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:extension base="j2ee:nonEmptyStringType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="servletType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The servletType is used to declare a servlet.
+	It contains the declarative data of a
+	servlet. If a jsp-file is specified and the load-on-startup
+	element is present, then the JSP should be precompiled and
+	loaded.
+
+	Used in: web-app
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="servlet-name"
+		   type="j2ee:servlet-nameType"/>
+      <xsd:choice>
+	<xsd:element name="servlet-class"
+		     type="j2ee:fully-qualified-classType">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      The servlet-class element contains the fully
+	      qualified class name of the servlet.
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	</xsd:element>
+
+	<xsd:element name="jsp-file"
+		     type="j2ee:jsp-fileType"/>
+
+      </xsd:choice>
+
+      <xsd:element name="init-param"
+		   type="j2ee:param-valueType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="load-on-startup"
+		   type="j2ee:xsdIntegerType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The load-on-startup element indicates that this
+	    servlet should be loaded (instantiated and have
+	    its init() called) on the startup of the web
+	    application. The optional contents of these
+	    element must be an integer indicating the order in
+	    which the servlet should be loaded. If the value
+	    is a negative integer, or the element is not
+	    present, the container is free to load the servlet
+	    whenever it chooses. If the value is a positive
+	    integer or 0, the container must load and
+	    initialize the servlet as the application is
+	    deployed. The container must guarantee that
+	    servlets marked with lower integers are loaded
+	    before servlets marked with higher integers. The
+	    container may choose the order of loading of
+	    servlets with the same load-on-start-up value.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="run-as"
+		   type="j2ee:run-asType"
+		   minOccurs="0"/>
+      <xsd:element name="security-role-ref"
+		   type="j2ee:security-role-refType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="session-configType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The session-configType defines the session parameters
+	for this web application.
+
+	Used in: web-app
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="session-timeout"
+		   type="j2ee:xsdIntegerType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The session-timeout element defines the default
+	    session timeout interval for all sessions created
+	    in this web application. The specified timeout
+	    must be expressed in a whole number of minutes.
+	    If the timeout is 0 or less, the container ensures
+	    the default behaviour of sessions is never to time
+	    out. If this element is not specified, the container
+	    must set its default timeout period.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="transport-guaranteeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The transport-guaranteeType specifies that the communication
+	between client and server should be NONE, INTEGRAL, or
+	CONFIDENTIAL. NONE means that the application does not
+	require any transport guarantees. A value of INTEGRAL means
+	that the application requires that the data sent between the
+	client and server be sent in such a way that it can't be
+	changed in transit. CONFIDENTIAL means that the application
+	requires that the data be transmitted in a fashion that
+	prevents other entities from observing the contents of the
+	transmission. In most cases, the presence of the INTEGRAL or
+	CONFIDENTIAL flag will indicate that the use of SSL is
+	required.
+
+	Used in: user-data-constraint
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="NONE"/>
+	<xsd:enumeration value="INTEGRAL"/>
+	<xsd:enumeration value="CONFIDENTIAL"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="user-data-constraintType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The user-data-constraintType is used to indicate how
+	data communicated between the client and container should be
+	protected.
+
+	Used in: security-constraint
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="transport-guarantee"
+		   type="j2ee:transport-guaranteeType"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="war-pathType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The elements that use this type designate a path starting
+	with a "/" and interpreted relative to the root of a WAR
+	file.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:pattern value="/.*"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:simpleType name="web-app-versionType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type contains the recognized versions of
+	web-application supported. It is used to designate the
+	version of the web application.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:token">
+      <xsd:enumeration value="2.4"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="web-appType">
+
+    <xsd:choice minOccurs="0" maxOccurs="unbounded">
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="distributable"
+		   type="j2ee:emptyType"/>
+      <xsd:element name="context-param"
+		   type="j2ee:param-valueType">
+
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The context-param element contains the declaration
+	    of a web application's servlet context
+	    initialization parameters.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:element name="filter"
+		   type="j2ee:filterType"/>
+      <xsd:element name="filter-mapping"
+		   type="j2ee:filter-mappingType"/>
+      <xsd:element name="listener"
+		   type="j2ee:listenerType"/>
+      <xsd:element name="servlet"
+		   type="j2ee:servletType"/>
+      <xsd:element name="servlet-mapping"
+		   type="j2ee:servlet-mappingType"/>
+      <xsd:element name="session-config"
+		   type="j2ee:session-configType"/>
+      <xsd:element name="mime-mapping"
+		   type="j2ee:mime-mappingType"/>
+      <xsd:element name="welcome-file-list"
+		   type="j2ee:welcome-file-listType"/>
+      <xsd:element name="error-page"
+		   type="j2ee:error-pageType"/>
+      <xsd:element name="jsp-config"
+		   type="j2ee:jsp-configType"/>
+      <xsd:element name="security-constraint"
+		   type="j2ee:security-constraintType"/>
+      <xsd:element name="login-config"
+		   type="j2ee:login-configType"/>
+      <xsd:element name="security-role"
+		   type="j2ee:security-roleType"/>
+      <xsd:group ref="j2ee:jndiEnvironmentRefsGroup"/>
+      <xsd:element name="message-destination"
+		   type="j2ee:message-destinationType"/>
+      <xsd:element name="locale-encoding-mapping-list"
+		   type="j2ee:locale-encoding-mapping-listType"/>
+    </xsd:choice>
+
+    <xsd:attribute name="version"
+		   type="j2ee:web-app-versionType"
+		   use="required"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="web-resource-collectionType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The web-resource-collectionType is used to identify a subset
+	of the resources and HTTP methods on those resources within
+	a web application to which a security constraint applies. If
+	no HTTP methods are specified, then the security constraint
+	applies to all HTTP methods.
+
+	Used in: security-constraint
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="web-resource-name"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The web-resource-name contains the name of this web
+	    resource collection.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="url-pattern"
+		   type="j2ee:url-patternType"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="http-method"
+		   type="j2ee:http-methodType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="welcome-file-listType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The welcome-file-list contains an ordered list of welcome
+	files elements.
+
+	Used in: web-app
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="welcome-file"
+		   type="xsd:string"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The welcome-file element contains file name to use
+	    as a default welcome file, such as index.html
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+</xsd:schema>
+
diff --git a/servletapi/jsr154/src/share/dtd/web-jsptaglibrary_1_1.dtd b/servletapi/jsr154/src/share/dtd/web-jsptaglibrary_1_1.dtd
new file mode 100644
index 0000000..4a5fc7a
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/web-jsptaglibrary_1_1.dtd
@@ -0,0 +1,206 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!--
+
+   This is the DTD defining the JavaServer Pages 1.1 Tag Library
+   descriptor (.tld) (XML) file format/syntax.
+
+   A Tag Library is a JAR file containing a valid instance of a Tag Library
+   Descriptor (taglib.tld) file in the META-INF subdirectory, along with the
+   appropriate implementing classes, and other resources required to
+   implement the tags defined therein.
+
+   Use is subject to license terms.
+  -->
+
+<!--
+The taglib tag is the document root, it defines:
+
+tlibversion	the version of the tag library implementation
+jspversion	the version of JSP the tag library depends upon
+
+shortname	a simple default short name that could be used by
+		a JSP authoring tool to create names with a mnemonic
+		value; for example, the it may be used as the prefered
+		prefix value in taglib directives
+uri		a uri uniquely identifying this taglib
+info		a simple string describing the "use" of this taglib,
+		should be user discernable
+-->
+
+<!ELEMENT taglib (tlibversion, jspversion?, shortname, uri?, info?, tag+) >
+<!ATTLIST taglib id ID #IMPLIED
+	  xmlns CDATA #FIXED
+		"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"
+>
+
+<!--
+Describes this version (number) of the taglibrary (dewey decimal)
+
+#PCDATA ::= [0-9]*{ "."[0-9] }0..3
+-->
+
+<!ELEMENT tlibversion (#PCDATA) >
+
+<!--
+Describes the JSP version (number) this taglibrary requires in
+order to function (dewey decimal)
+
+The default is 1.1
+
+#PCDATA ::= [0-9]*{ "."[0-9] }0..3
+-->
+
+<!ELEMENT jspversion  (#PCDATA) >
+
+<!--
+Defines a short (default) shortname to be used for tags and
+variable names used/created by this tag library.  Do not use
+white space, and do not start with digits or underscore.
+
+#PCDATA ::= NMTOKEN
+-->
+
+<!ELEMENT shortname      (#PCDATA) >
+
+<!--
+Defines a public URI that uniquely identifies this version of
+the taglibrary Leave it empty if it does not apply.
+-->
+
+<!ELEMENT uri	 (#PCDATA) >
+
+<!--
+Defines an arbitrary text string descirbing the tag library
+-->
+
+<!ELEMENT info	(#PCDATA) >
+
+<!--
+The tag defines a unique tag in this tag library, defining:
+
+- the unique tag/element name
+- the subclass of javax.servlet.jsp.tagext.Tag implementation class
+- an optional subclass of javax.servlet.jsp.tagext.TagExtraInfo
+- the body content type (hint)
+- optional tag-specific information
+- any attributes
+-->
+
+<!ELEMENT tag (name, tagclass, teiclass?, bodycontent?, info?, attribute*) >
+
+<!--
+Defines the subclass of javax.serlvet.jsp.tagext.Tag that implements
+the request time semantics for this tag. (required)
+
+#PCDATA ::= fully qualified Java class name
+-->
+
+<!ELEMENT tagclass (#PCDATA) >
+
+<!--
+Defines the subclass of javax.servlet.jsp.tagext.TagExtraInfo for
+this tag. (optional)
+
+If this is not given, the class is not consulted at translation time.
+
+#PCDATA ::= fully qualified Java class name
+-->
+
+<!ELEMENT teiclass (#PCDATA) >
+
+<!--
+Provides a hint as to the content of the body of this tag. Primarily
+intended for use by page composition tools.
+
+There are currently three values specified:
+
+tagdependent	The body of the tag is interpreted by the tag
+		implementation itself, and is most likely in a
+		different "langage", e.g embedded SQL statements.
+
+JSP		The body of the tag contains nested JSP syntax
+
+empty		The body must be empty
+
+The default (if not defined) is JSP
+
+#PCDATA ::=  tagdependent | JSP | empty
+
+-->
+
+<!ELEMENT bodycontent (#PCDATA) >
+
+<!--
+The attribute tag defines an attribute for the nesting tag
+
+An attribute definition is composed of:
+
+- the attributes name (required)
+- if the attribute is required or optional (optional)
+- if the attributes value may be dynamically calculated at runtime
+  by a scriptlet expression (optional)
+
+-->
+
+<!ELEMENT attribute (name, required? , rtexprvalue?) >
+
+<!--
+Defines the canonical name of a tag or attribute being defined
+
+#PCDATA ::= NMTOKEN
+-->
+
+<!ELEMENT name	(#PCDATA) >
+
+<!--
+Defines if the nesting attribute is required or optional.
+
+#PCDATA ::= true | false | yes | no
+
+If not present then the default is "false", i.e the attribute
+is optional.
+-->
+
+<!ELEMENT required    (#PCDATA) >
+
+<!--
+Defines if the nesting attribute can have scriptlet expressions as
+a value, i.e the value of the attribute may be dynamically calculated
+at request time, as opposed to a static value determined at translation
+time.
+
+#PCDATA ::= true | false | yes | no
+
+If not present then the default is "false", i.e the attribute
+has a static value
+-->
+
+<!ELEMENT rtexprvalue (#PCDATA) >
+
+<!ATTLIST tlibversion id ID #IMPLIED>
+<!ATTLIST jspversion id ID #IMPLIED>
+<!ATTLIST shortname id ID #IMPLIED>
+<!ATTLIST uri id ID #IMPLIED>
+<!ATTLIST info id ID #IMPLIED>
+<!ATTLIST tag id ID #IMPLIED>
+<!ATTLIST tagclass id ID #IMPLIED>
+<!ATTLIST teiclass id ID #IMPLIED>
+<!ATTLIST bodycontent id ID #IMPLIED>
+<!ATTLIST attribute id ID #IMPLIED>
+<!ATTLIST name id ID #IMPLIED>
+<!ATTLIST required id ID #IMPLIED>
+<!ATTLIST rtexprvalue id ID #IMPLIED>
diff --git a/servletapi/jsr154/src/share/dtd/web-jsptaglibrary_1_2.dtd b/servletapi/jsr154/src/share/dtd/web-jsptaglibrary_1_2.dtd
new file mode 100644
index 0000000..41165b2
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/web-jsptaglibrary_1_2.dtd
@@ -0,0 +1,477 @@
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!--
+
+   This is the DTD defining the JavaServer Pages 1.2 Tag Library
+   descriptor (.tld) (XML) file format/syntax.
+
+   A Tag Library is a JAR file containing a valid instance of a Tag Library
+   Descriptor (taglib.tld) file in the META-INF subdirectory, along with the
+   appropriate implementing classes, and other resources required to
+   implement the tags defined therein.
+
+   Use is subject to license terms.
+  -->
+
+<!NOTATION WEB-JSPTAGLIB.1_2 PUBLIC
+          "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN">
+
+<!--
+This is the XML DTD for the JSP 1.2 Tag Library Descriptor.
+All JSP 1.2 tag library descriptors must include a DOCTYPE
+of the following form:
+
+  <!DOCTYPE taglib
+        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
+	"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
+
+-->
+
+<!--
+The taglib tag is the document root, it defines:
+
+tlib-version	the version of the tag library implementation
+
+jsp-version	the version of JSP the tag library depends upon
+
+short-name	a simple default short name that could be used by
+		a JSP authoring tool to create names with a mnemonic
+		value; for example, the it may be used as the prefered
+		prefix value in taglib directives
+
+uri		a uri uniquely identifying this taglib
+
+display-name    the display-name element contains a short name that
+                is intended to be displayed by tools
+small-icon      optional small-icon that can be used by tools
+
+large-icon      optional large-icon that can be used by tools
+
+description	a simple string describing the "use" of this taglib,
+		should be user discernable
+
+validator       optional TagLibraryValidator information
+
+listener        optional event listener specification
+
+
+-->
+
+<!ELEMENT taglib (tlib-version, jsp-version, short-name, uri?,
+                  display-name?, small-icon?, large-icon?, description?,
+                  validator?, listener*, tag+) >
+
+<!ATTLIST taglib id ID #IMPLIED
+	  xmlns CDATA #FIXED
+		"http://java.sun.com/JSP/TagLibraryDescriptor"
+>
+
+<!--
+Describes this version (number) of the taglibrary (dewey decimal)
+
+#PCDATA ::= [0-9]*{ "."[0-9] }0..3
+-->
+
+<!ELEMENT tlib-version (#PCDATA) >
+
+<!--
+Describes the JSP version (number) this taglibrary requires in
+order to function (dewey decimal)
+
+The default is 1.2
+
+#PCDATA ::= [0-9]*{ "."[0-9] }0..3
+-->
+
+<!ELEMENT jsp-version  (#PCDATA) >
+
+<!--
+Defines a short (default) short-name to be used for tags and
+variable names used/created by this tag library.  Do not use
+white space, and do not start with digits or underscore.
+
+#PCDATA ::= NMTOKEN
+-->
+
+<!ELEMENT short-name      (#PCDATA) >
+
+<!--
+Defines a public URI that uniquely identifies this version of
+the taglibrary.  Leave it empty if it does not apply.
+-->
+
+<!ELEMENT uri	 (#PCDATA) >
+
+<!--
+Defines an arbitrary text string descirbing the tag library
+-->
+
+<!ELEMENT description	(#PCDATA) >
+
+<!--
+
+Defines an optional validator that can be used to
+validate the conformance of a JSP page to using this tag library.
+-->
+
+<!ELEMENT validator (validator-class, init-param*, description?) >
+
+
+<!--
+
+Defines the TagLibraryValidator class that can be used to
+validate the conformance of a JSP page to using this tag library.
+-->
+
+<!ELEMENT validator-class (#PCDATA) >
+
+
+<!--
+
+The init-param element contains a name/value pair as an
+initialization param
+-->
+
+<!ELEMENT init-param (param-name, param-value, description?)>
+
+<!--
+
+The param-name element contains the name of a parameter.
+-->
+
+<!ELEMENT param-name (#PCDATA)>
+
+<!--
+
+The param-value element contains the value of a parameter.
+-->
+
+<!ELEMENT param-value (#PCDATA)>
+
+
+<!--
+
+Defines an optional event listener object to be instantiated and
+registered automatically.
+-->
+
+<!ELEMENT listener (listener-class) >
+
+<!--
+
+The listener-class element declares a class in the application that
+must be registered as a web application listener bean.  See the
+Servlet 2.3 specification for details.
+-->
+
+<!ELEMENT listener-class (#PCDATA) >
+
+
+<!--
+The tag defines a unique tag in this tag library.  It has one
+attribute, id.
+
+The tag element may have several subelements defining:
+
+name              The unique action name
+
+tag-class         The tag handler class implementing
+                  javax.servlet.jsp.tagext.Tag
+
+tei-class         An optional subclass of
+                  javax.servlet.jsp.tagext.TagExtraInfo
+
+body-content      The body content type
+
+display-name      A short name that is intended to be displayed
+                  by tools
+
+small-icon        Optional small-icon that can be used by tools
+
+large-icon        Optional large-icon that can be used by tools
+
+description       Optional tag-specific information
+
+variable          Optional scripting variable information
+
+attribute         All attributes of this action
+
+example           Optional informal description of an example of a
+                  use of this tag
+
+-->
+
+<!ELEMENT tag (name, tag-class, tei-class?, body-content?, display-name?,
+               small-icon?, large-icon?, description?, variable*, attribute*,
+               example?) >
+
+<!--
+Defines the subclass of javax.serlvet.jsp.tagext.Tag that implements
+the request time semantics for this tag. (required)
+
+#PCDATA ::= fully qualified Java class name
+-->
+
+<!ELEMENT tag-class (#PCDATA) >
+
+<!--
+Defines the subclass of javax.servlet.jsp.tagext.TagExtraInfo for
+this tag. (optional)
+
+If this is not given, the class is not consulted at translation time.
+
+#PCDATA ::= fully qualified Java class name
+-->
+
+<!ELEMENT tei-class (#PCDATA) >
+
+<!--
+Provides a hint as to the content of the body of this tag. Primarily
+intended for use by page composition tools.
+
+There are currently three values specified:
+
+tagdependent	The body of the tag is interpreted by the tag
+		implementation itself, and is most likely in a
+		different "langage", e.g embedded SQL statements.
+
+JSP		The body of the tag contains nested JSP syntax
+
+empty		The body must be empty
+
+The default (if not defined) is JSP
+
+#PCDATA ::=  tagdependent | JSP | empty
+
+-->
+
+<!ELEMENT body-content (#PCDATA) >
+
+<!--
+
+The display-name element contains a short name that is intended
+to be displayed by tools.
+-->
+
+<!ELEMENT display-name (#PCDATA) >
+
+
+<!--
+
+The large-icon element contains the name of a file containing a large
+(32 x 32) icon image.  The file name is a relative path within the
+tag library.  The image must be either in the JPEG or GIF format, and
+the file name must end with the suffix ".jpg" or ".gif" respectively.
+The icon can be used by tools.
+-->
+
+<!ELEMENT large-icon (#PCDATA) >
+
+<!--
+
+The small-icon element contains the name of a file containing a large
+(32 x 32) icon image.  The file name is a relative path within the
+tag library.  The image must be either in the JPEG or GIF format, and
+the file name must end with the suffix ".jpg" or ".gif" respectively.
+The icon can be used by tools.
+-->
+
+<!ELEMENT small-icon (#PCDATA) >
+
+<!--
+
+The example element contains an informal description of an example
+of the use of a tag.
+-->
+
+<!ELEMENT example (#PCDATA) >
+
+<!--
+
+The variable tag provides information on the scripting variables
+defined by this tag.  It is a (translation time) error for a tag
+that has one or more variable subelements to have a TagExtraInfo
+class that returns a non-null object.
+
+The subelements of variable are of the form:
+
+name-given               The variable name as a constant
+
+name-from-attribute      The name of an attribute whose (translation
+                         time) value will give the name of the
+                         variable.  One of name-given or
+                         name-from-attribute is required.
+
+variable-class           Name of the class of the variable.
+                         java.lang.String is default.
+
+declare                  Whether the variable is declared or not.
+                         True is the default.
+
+scope                    The scope of the scripting varaible
+                         defined.  NESTED is default.
+
+description              Optional description of this variable
+
+-->
+
+<!ELEMENT variable ( (name-given | name-from-attribute), variable-class?,
+                    declare?, scope?, description?) >
+
+<!--
+
+The name for the scripting variable.  One of name-given or
+name-from-attribute is required.
+-->
+
+<!ELEMENT name-given (#PCDATA) >
+
+<!--
+
+The name of an attribute whose (translation-time) value will give
+the name of the variable.  One of name-given or name-from-attribute
+is required.
+-->
+
+<!ELEMENT name-from-attribute (#PCDATA) >
+
+<!--
+
+The optional name of the class for the scripting variable.  The
+default is java.lang.String.
+-->
+
+<!ELEMENT variable-class (#PCDATA) >
+
+<!--
+
+Whether the scripting variable is to be defined or not.  See
+TagExtraInfo for details.  This element is optional and "true"
+is the default.
+-->
+
+<!ELEMENT declare (#PCDATA) >
+
+<!--
+
+The scope of the scripting variable.  See TagExtraInfo for details.
+The element is optional and "NESTED" is the default.  Other legal
+values are "AT_BEGIN" and "AT_END".
+-->
+
+<!ELEMENT scope (#PCDATA) >
+
+<!--
+
+The attribute tag defines an attribute for the nesting tag
+
+An attribute definition is composed of:
+	
+- the attributes name (required)
+- if the attribute is required or optional (optional)
+- if the attributes value may be dynamically calculated at runtime
+  by a scriptlet expression (optional)
+- the type of the attributes value (optional)
+- an informal description of the meaning of the attribute (optional)
+
+-->
+
+
+<!--
+The attribute tag defines an attribute for the nesting tag
+
+An attribute definition is composed of:
+
+- the attributes name (required)
+
+- if the attribute is required or optional (optional)
+
+- if the attributes value may be dynamically calculated at runtime
+  by a scriptlet expression (optional)
+
+- the type of the attributes value (optional)
+
+- an informal description of the meaning of the attribute (optional)
+-->
+
+<!ELEMENT attribute (name, required? , rtexprvalue?, type?, description?) >
+
+<!--
+Defines the canonical name of a tag or attribute being defined
+
+#PCDATA ::= NMTOKEN
+-->
+
+<!ELEMENT name	(#PCDATA) >
+
+<!--
+Defines if the nesting attribute is required or optional.
+
+#PCDATA ::= true | false | yes | no
+
+If not present then the default is "false", i.e the attribute
+is optional.
+-->
+
+<!ELEMENT required    (#PCDATA) >
+
+<!--
+Defines if the nesting attribute can have scriptlet expressions as
+a value, i.e the value of the attribute may be dynamically calculated
+at request time, as opposed to a static value determined at translation
+time.
+
+#PCDATA ::= true | false | yes | no
+
+If not present then the default is "false", i.e the attribute
+has a static value
+-->
+
+<!ELEMENT rtexprvalue (#PCDATA) >
+
+
+<!--
+
+Defines the Java type of the attributes value.  For static values
+(those determined at translation time) the type is always
+java.lang.String.
+-->
+
+<!ELEMENT type (#PCDATA) >
+
+
+<!-- ID attributes -->
+
+<!ATTLIST tlib-version id ID #IMPLIED>
+<!ATTLIST jsp-version id ID #IMPLIED>
+<!ATTLIST short-name id ID #IMPLIED>
+<!ATTLIST uri id ID #IMPLIED>
+<!ATTLIST description id ID #IMPLIED>
+<!ATTLIST example id ID #IMPLIED>
+<!ATTLIST tag id ID #IMPLIED>
+<!ATTLIST tag-class id ID #IMPLIED>
+<!ATTLIST tei-class id ID #IMPLIED>
+<!ATTLIST body-content id ID #IMPLIED>
+<!ATTLIST attribute id ID #IMPLIED>
+<!ATTLIST name id ID #IMPLIED>
+<!ATTLIST required id ID #IMPLIED>
+<!ATTLIST rtexprvalue id ID #IMPLIED>
+
+
+<!ATTLIST param-name id ID #IMPLIED>
+<!ATTLIST param-value id ID #IMPLIED>
+<!ATTLIST listener id ID #IMPLIED>
+<!ATTLIST listener-class id ID #IMPLIED>
diff --git a/servletapi/jsr154/src/share/dtd/web-jsptaglibrary_2_0.xsd b/servletapi/jsr154/src/share/dtd/web-jsptaglibrary_2_0.xsd
new file mode 100644
index 0000000..b4d46df
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/web-jsptaglibrary_2_0.xsd
@@ -0,0 +1,1023 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<xsd:schema
+     targetNamespace="http://java.sun.com/xml/ns/j2ee"
+     xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
+     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+     xmlns:xml="http://www.w3.org/XML/1998/namespace"
+     elementFormDefault="qualified"
+     attributeFormDefault="unqualified"
+     version="2.0">
+
+  <xsd:annotation>
+    <xsd:documentation>
+      %W% %G%
+    </xsd:documentation>
+  </xsd:annotation>
+  <xsd:annotation>
+    <xsd:documentation>
+
+      Copyright 2002 Sun Microsystems, Inc., 901 San Antonio
+      Road, Palo Alto, California 94303, U.S.A. All rights
+      reserved.
+
+      Sun Microsystems, Inc. has intellectual property rights
+      relating to technology described in this document. In
+      particular, and without limitation, these intellectual
+      property rights may include one or more of the U.S. patents
+      listed at http://www.sun.com/patents and one or more
+      additional patents or pending patent applications in the
+      U.S. and other countries.
+
+      This document and the technology which it describes are
+      distributed under licenses restricting their use, copying,
+      distribution, and decompilation. No part of this document
+      may be reproduced in any form by any means without prior
+      written authorization of Sun and its licensors, if any.
+
+      Third-party software, including font technology, is
+      copyrighted and licensed from Sun suppliers.
+
+      Sun, Sun Microsystems, the Sun logo, Solaris, Java, J2EE,
+      JavaServer Pages, Enterprise JavaBeans and the Java Coffee
+      Cup logo are trademarks or registered trademarks of Sun
+      Microsystems, Inc. in the U.S. and other countries.
+
+      Federal Acquisitions: Commercial Software - Government Users
+      Subject to Standard License Terms and Conditions.
+
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:annotation>
+    <xsd:documentation>
+      <![CDATA[
+
+	This is the XML Schema for the JSP Taglibrary
+	descriptor.  All Taglibrary descriptors must
+	indicate the tag library schema by using the Taglibrary
+	namespace:
+
+	http://java.sun.com/xml/ns/j2ee
+
+	and by indicating the version of the schema by
+	using the version element as shown below:
+
+	    <taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+	      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	      xsi:schemaLocation="..."
+	      version="2.0">
+	      ...
+	    </taglib>
+
+	The instance documents may indicate the published
+	version of the schema using xsi:schemaLocation attribute
+	for J2EE namespace with the following location:
+
+	http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd
+
+	]]>
+    </xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:include schemaLocation="j2ee_1_4.xsd"/>
+
+
+<!-- **************************************************** -->
+
+
+  <xsd:element name="taglib" type="j2ee:tldTaglibType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The taglib tag is the document root.
+	The definition of taglib is provided
+	by the tldTaglibType.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:unique name="tag-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The taglib element contains, among other things, tag and
+	  tag-file elements.
+	  The name subelements of these elements must each be unique.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:tag|j2ee:tag-file"/>
+      <xsd:field    xpath="j2ee:name"/>
+    </xsd:unique>
+
+    <xsd:unique name="function-name-uniqueness">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  The taglib element contains function elements.
+	  The name subelements of these elements must each be unique.
+
+	</xsd:documentation>
+      </xsd:annotation>
+      <xsd:selector xpath="j2ee:function"/>
+      <xsd:field    xpath="j2ee:name"/>
+    </xsd:unique>
+
+  </xsd:element>
+
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="body-contentType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+        Specifies the type of body that is valid for a tag.
+	This value is used by the JSP container to validate
+	that a tag invocation has the correct body syntax and
+	by page composition tools to assist the page author
+	in providing a valid tag body.
+
+	There are currently four values specified:
+
+	tagdependent    The body of the tag is interpreted by the tag
+			implementation itself, and is most likely
+			in a different "language", e.g embedded SQL
+			statements.
+
+	JSP             The body of the tag contains nested JSP
+			syntax.
+
+	empty           The body must be empty
+
+	scriptless      The body accepts only template text, EL
+			Expressions, and JSP action elements.  No
+			scripting elements are allowed.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="tagdependent"/>
+	<xsd:enumeration value="JSP"/>
+	<xsd:enumeration value="empty"/>
+	<xsd:enumeration value="scriptless"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="extensibleType" abstract="true">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The extensibleType is an abstract base type that is used to
+	define the type of extension-elements. Instance documents
+	must substitute a known type to define the extension by
+	using xsi:type attribute to define the actual type of
+	extension-elements.
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="functionType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The function element is used to provide information on each
+	function in the tag library that is to be exposed to the EL.
+
+	The function element may have several subelements defining:
+
+	description         Optional tag-specific information
+
+	display-name        A short name that is intended to be
+			    displayed by tools
+
+	icon                Optional icon element that can be used
+			    by tools
+
+	name                A unique name for this function
+
+	function-class      Provides the name of the Java class that
+			    implements the function
+
+	function-signature  Provides the signature, as in the Java
+			    Language Specification, of the Java
+			    method that is to be used to implement
+			    the function.
+
+	example             Optional informal description of an
+			    example of a use of this function
+
+	function-extension  Zero or more extensions that provide extra
+			    information about this function, for tool
+			    consumption
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="name"
+		   type="j2ee:tld-canonical-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    A unique name for this function.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="function-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Provides the fully-qualified class name of the Java
+	    class containing the static method that implements
+	    the function.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="function-signature"
+		   type="j2ee:string">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Provides the signature, of the static Java method that is
+	    to be used to implement the function.  The syntax of the
+	    function-signature element is as follows:
+
+		FunctionSignature ::= ReturnType S MethodName S?
+				      '(' S? Parameters? S? ')'
+
+                ReturnType        ::= Type
+
+		MethodName        ::= Identifier
+
+		Parameters        ::=   Parameter
+				      | ( Parameter S? ',' S? Parameters )
+
+                Parameter         ::= Type
+
+		Where:
+
+ 		    * Type is a basic type or a fully qualified Java class name
+		      (including package name), as per the 'Type' production
+		      in the Java Language Specification, Second Edition,
+		      Chapter 18.
+
+                    * Identifier is a Java identifier, as per the 'Identifier'
+		      production in the Java Language Specification, Second
+		      Edition, Chapter 18.
+
+	    Example:
+
+	    java.lang.String nickName( java.lang.String, int )
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="example"
+		   type="j2ee:xsdStringType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The example element contains an informal description
+	    of an example of the use of this function.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="function-extension"
+		   type="j2ee:tld-extensionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Function extensions are for tool use only and must not affect
+	    the behavior of a container.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tagFileType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	Defines an action in this tag library that is implemented
+	as a .tag file.
+
+	The tag-file element has two required subelements:
+
+	description       Optional tag-specific information
+
+	display-name      A short name that is intended to be
+			  displayed by tools
+
+	icon              Optional icon element that can be used
+			  by tools
+
+	name              The unique action name
+
+	path              Where to find the .tag file implementing this
+			  action, relative to the root of the web
+			  application or the root of the JAR file for a
+			  tag library packaged in a JAR.  This must
+			  begin with /WEB-INF/tags if the .tag file
+			  resides in the WAR, or /META-INF/tags if the
+			  .tag file resides in a JAR.
+
+	example           Optional informal description of an
+			  example of a use of this tag
+
+	tag-extension     Zero or more extensions that provide extra
+			  information about this tag, for tool
+			  consumption
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="name"
+		   type="j2ee:tld-canonical-nameType"/>
+      <xsd:element name="path"
+		   type="j2ee:pathType"/>
+      <xsd:element name="example"
+		   type="j2ee:xsdStringType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The example element contains an informal description
+	    of an example of the use of a tag.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="tag-extension"
+		   type="j2ee:tld-extensionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Tag extensions are for tool use only and must not affect
+	    the behavior of a container.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tagType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The tag defines a unique tag in this tag library.  It has one
+	attribute, id.
+
+	The tag element may have several subelements defining:
+
+	description       Optional tag-specific information
+
+	display-name      A short name that is intended to be
+			  displayed by tools
+
+	icon              Optional icon element that can be used
+			  by tools
+
+	name              The unique action name
+
+	tag-class         The tag handler class implementing
+			  javax.servlet.jsp.tagext.JspTag
+
+	tei-class         An optional subclass of
+			  javax.servlet.jsp.tagext.TagExtraInfo
+
+	body-content      The body content type
+
+	variable          Optional scripting variable information
+
+	attribute         All attributes of this action that are
+			  evaluated prior to invocation.
+
+	dynamic-attributes Whether this tag supports additional
+			   attributes with dynamic names.  If
+			   true, the tag-class must implement the
+			   javax.servlet.jsp.tagext.DynamicAttributes
+			   interface.  Defaults to false.
+
+	example           Optional informal description of an
+			  example of a use of this tag
+
+	tag-extension     Zero or more extensions that provide extra
+			  information about this tag, for tool
+			  consumption
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="name"
+		   type="j2ee:tld-canonical-nameType"/>
+      <xsd:element name="tag-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the subclass of javax.serlvet.jsp.tagext.JspTag
+	    that implements the request time semantics for
+	    this tag. (required)
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="tei-class"
+		   type="j2ee:fully-qualified-classType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the subclass of javax.servlet.jsp.tagext.TagExtraInfo
+	    for this tag. (optional)
+
+	    If this is not given, the class is not consulted at
+	    translation time.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="body-content"
+		   type="j2ee:body-contentType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Specifies the format for the body of this tag.
+	    The default in JSP 1.2 was "JSP" but because this
+	    is an invalid setting for simple tag handlers, there
+	    is no longer a default in JSP 2.0.  A reasonable
+	    default for simple tag handlers is "scriptless" if
+	    the tag can have a body.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="variable"
+		   type="j2ee:variableType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="attribute"
+		   type="j2ee:tld-attributeType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="dynamic-attributes"
+		   type="j2ee:generic-booleanType"
+		   minOccurs="0"/>
+      <xsd:element name="example"
+		   type="j2ee:xsdStringType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The example element contains an informal description
+	    of an example of the use of a tag.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="tag-extension"
+		   type="j2ee:tld-extensionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Tag extensions are for tool use only and must not affect
+	    the behavior of a container.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tld-attributeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The attribute element defines an attribute for the nesting
+	tag.  The attributre element may have several subelements
+	defining:
+
+	description     a description of the attribute
+
+	name            the name of the attribute
+
+	required        whether the attribute is required or
+			optional
+
+	rtexprvalue     whether the attribute is a runtime attribute
+
+	type            the type of the attributes
+
+	fragment        whether this attribute is a fragment
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="name"
+		   type="j2ee:java-identifierType"/>
+      <xsd:element name="required"
+		   type="j2ee:generic-booleanType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines if the nesting attribute is required or
+	    optional.
+
+	    If not present then the default is "false", i.e
+	    the attribute is optional.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+
+      <xsd:choice>
+	<xsd:sequence>
+	  <xsd:element name="rtexprvalue"
+		       type="j2ee:generic-booleanType"
+		       minOccurs="0">
+	    <xsd:annotation>
+	      <xsd:documentation>
+
+		Defines if the nesting attribute can have scriptlet
+		expressions as a value, i.e the value of the
+		attribute may be dynamically calculated at request
+		time, as opposed to a static value determined at
+		translation time.
+
+		If not present then the default is "false", i.e the
+		attribute has a static value
+
+	      </xsd:documentation>
+	    </xsd:annotation>
+
+	  </xsd:element>
+	  <xsd:element name="type"
+		       type="j2ee:fully-qualified-classType"
+		       minOccurs="0">
+	    <xsd:annotation>
+	      <xsd:documentation>
+
+		Defines the Java type of the attributes value.  For
+		static values (those determined at translation time)
+		the type is always java.lang.String.
+
+	      </xsd:documentation>
+	    </xsd:annotation>
+	  </xsd:element>
+	</xsd:sequence>
+	<xsd:element name="fragment"
+		     type="j2ee:generic-booleanType"
+		     minOccurs="0">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      "true" if this attribute is of type
+	      javax.jsp.tagext.JspFragment, representing dynamic
+	      content that can be re-evaluated as many times
+	      as needed by the tag handler.  If omitted or "false",
+	      the default is still type="java.lang.String"
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	</xsd:element>
+      </xsd:choice>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tld-canonical-nameType">
+
+    <xsd:annotation>
+      <xsd:documentation>
+
+	Defines the canonical name of a tag or attribute being
+	defined.
+
+	The name must conform to the lexical rules for an NMTOKEN.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:xsdNMTOKENType"/>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tld-extensionType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The tld-extensionType is used to indicate
+	extensions to a specific TLD element.
+
+	It is used by elements to designate an extension block
+	that is targeted to a specific extension designated by
+	a set of extension elements that are declared by a
+	namespace. The namespace identifies the extension to
+	the tool that processes the extension.
+
+	The type of the extension-element is abstract. Therefore,
+	a concrete type must be specified by the TLD using
+	xsi:type attribute for each extension-element.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="extension-element"
+		   type="j2ee:extensibleType"
+		   maxOccurs="unbounded"/>
+    </xsd:sequence>
+
+    <xsd:attribute name="namespace"
+		   use="required"
+		   type="xsd:anyURI"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="tldTaglibType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The taglib tag is the document root, it defines:
+
+	description     a simple string describing the "use" of this taglib,
+			should be user discernable
+
+	display-name    the display-name element contains a
+			short name that is intended to be displayed
+			by tools
+
+	icon            optional icon that can be used by tools
+
+	tlib-version    the version of the tag library implementation
+
+	short-name      a simple default short name that could be
+			used by a JSP authoring tool to create
+			names with a mnemonic value; for example,
+			the it may be used as the prefered prefix
+			value in taglib directives
+
+	uri             a uri uniquely identifying this taglib
+
+	validator       optional TagLibraryValidator information
+
+	listener        optional event listener specification
+
+	tag             tags in this tag library
+
+	tag-file        tag files in this tag library
+
+	function        zero or more EL functions defined in this
+			tag library
+
+	taglib-extension zero or more extensions that provide extra
+			information about this taglib, for tool
+			consumption
+
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:group ref="j2ee:descriptionGroup"/>
+      <xsd:element name="tlib-version"
+		   type="j2ee:dewey-versionType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Describes this version (number) of the taglibrary.
+	    It is described as a dewey decimal.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+
+      <xsd:element name="short-name"
+		   type="j2ee:tld-canonical-nameType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines a simple default name that could be used by
+	    a JSP authoring tool to create names with a
+	    mnemonicvalue; for example, it may be used as the
+	    preferred prefix value in taglib directives.  Do
+	    not use white space, and do not start with digits
+	    or underscore.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="uri"
+		   type="j2ee:xsdAnyURIType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines a public URI that uniquely identifies this
+	    version of the taglibrary.  Leave it empty if it
+	    does not apply.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+      <xsd:element name="validator"
+		   type="j2ee:validatorType"
+		   minOccurs="0">
+      </xsd:element>
+      <xsd:element name="listener"
+		   type="j2ee:listenerType"
+		   minOccurs="0" maxOccurs="unbounded">
+      </xsd:element>
+      <xsd:element name="tag"
+		   type="j2ee:tagType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="tag-file"
+		   type="j2ee:tagFileType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="function"
+		   type="j2ee:functionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="taglib-extension"
+		   type="j2ee:tld-extensionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Taglib extensions are for tool use only and must not affect
+	    the behavior of a container.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="version"
+		   type="j2ee:dewey-versionType"
+		   fixed="2.0"
+		   use="required">
+      <xsd:annotation>
+	<xsd:documentation>
+
+	  Describes the JSP version (number) this taglibrary
+	  requires in order to function (dewey decimal)
+
+	</xsd:documentation>
+      </xsd:annotation>
+
+    </xsd:attribute>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="validatorType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	A validator that can be used to validate
+	the conformance of a JSP page to using this tag library is
+	defined by a validatorType.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0"
+		   maxOccurs="unbounded"/>
+      <xsd:element name="validator-class"
+		   type="j2ee:fully-qualified-classType">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Defines the TagLibraryValidator class that can be used
+	    to validate the conformance of a JSP page to using this
+	    tag library.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="init-param"
+		   type="j2ee:param-valueType"
+		   minOccurs="0" maxOccurs="unbounded">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The init-param element contains a name/value pair as an
+	    initialization param.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="variable-scopeType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	This type defines scope of the scripting variable.  See
+	TagExtraInfo for details.  The allowed values are,
+	"NESTED", "AT_BEGIN" and "AT_END".
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:simpleContent>
+      <xsd:restriction base="j2ee:string">
+	<xsd:enumeration value="NESTED"/>
+	<xsd:enumeration value="AT_BEGIN"/>
+	<xsd:enumeration value="AT_END"/>
+      </xsd:restriction>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+<!-- **************************************************** -->
+
+  <xsd:complexType name="variableType">
+    <xsd:annotation>
+      <xsd:documentation>
+
+	The variableType provides information on the scripting
+	variables defined by using this tag.  It is a (translation
+	time) error for a tag that has one or more variable
+	subelements to have a TagExtraInfo class that returns a
+	non-null value from a call to getVariableInfo().
+
+	The subelements of variableType are of the form:
+
+	description              Optional description of this
+				 variable
+
+	name-given               The variable name as a constant
+
+	name-from-attribute      The name of an attribute whose
+				 (translation time) value will
+				 give the name of the
+				 variable.  One of name-given or
+				 name-from-attribute is required.
+
+	variable-class           Name of the class of the variable.
+				 java.lang.String is default.
+
+	declare                  Whether the variable is declared
+				 or not.  True is the default.
+
+	scope                    The scope of the scripting varaible
+				 defined.  NESTED is default.
+
+      </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:sequence>
+      <xsd:element name="description"
+		   type="j2ee:descriptionType"
+		   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:choice>
+	<xsd:element name="name-given"
+		     type="j2ee:java-identifierType">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      The name for the scripting variable.
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	</xsd:element>
+
+	<xsd:element name="name-from-attribute"
+		     type="j2ee:java-identifierType">
+	  <xsd:annotation>
+	    <xsd:documentation>
+
+	      The name of an attribute whose
+	      (translation-time) value will give the name of
+	      the variable.
+
+	    </xsd:documentation>
+	  </xsd:annotation>
+	</xsd:element>
+      </xsd:choice>
+      <xsd:element name="variable-class"
+		   type="j2ee:fully-qualified-classType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The optional name of the class for the scripting
+	    variable.  The default is java.lang.String.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+
+      </xsd:element>
+
+      <xsd:element name="declare"
+		   type="j2ee:generic-booleanType"
+		   minOccurs="0">
+
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    Whether the scripting variable is to be defined
+	    or not.  See TagExtraInfo for details.  This
+	    element is optional and "true" is the default.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+      <xsd:element name="scope"
+		   type="j2ee:variable-scopeType"
+		   minOccurs="0">
+	<xsd:annotation>
+	  <xsd:documentation>
+
+	    The element is optional and "NESTED" is the default.
+
+	  </xsd:documentation>
+	</xsd:annotation>
+      </xsd:element>
+    </xsd:sequence>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+
+</xsd:schema>
+
diff --git a/servletapi/jsr154/src/share/dtd/xml.xsd b/servletapi/jsr154/src/share/dtd/xml.xsd
new file mode 100644
index 0000000..0bea0fd
--- /dev/null
+++ b/servletapi/jsr154/src/share/dtd/xml.xsd
@@ -0,0 +1,96 @@
+<?xml version='1.0'?>
+<!--
+  Copyright 2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE xs:schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" >
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" xmlns:xs="http://www.w3.org/2001/XMLSchema" xml:lang="en">
+
+ <xs:annotation>
+  <xs:documentation>
+   See http://www.w3.org/XML/1998/namespace.html and
+   http://www.w3.org/TR/REC-xml for information about this namespace.
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>This schema defines attributes and an attribute group
+        suitable for use by
+        schemas wishing to allow xml:base, xml:lang or xml:space attributes
+        on elements they define.
+
+        To enable this, such a schema must import this schema
+        for the XML namespace, e.g. as follows:
+        &lt;schema . . .>
+         . . .
+         &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                    schemaLocation="http://www.w3.org/2001/03/xml.xsd"/>
+
+        Subsequently, qualified reference to any of the attributes
+        or the group defined below will have the desired effect, e.g.
+
+        &lt;type . . .>
+         . . .
+         &lt;attributeGroup ref="xml:specialAttrs"/>
+ 
+         will define a type which will schema-validate an instance
+         element with any of those attributes</xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>In keeping with the XML Schema WG's standard versioning
+   policy, this schema document will persist at
+   http://www.w3.org/2001/03/xml.xsd.
+   At the date of issue it can also be found at
+   http://www.w3.org/2001/xml.xsd.
+   The schema document at that URI may however change in the future,
+   in order to remain compatible with the latest version of XML Schema
+   itself.  In other words, if the XML Schema namespace changes, the version
+   of this document at
+   http://www.w3.org/2001/xml.xsd will change
+   accordingly; the version at
+   http://www.w3.org/2001/03/xml.xsd will not change.
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang" type="xs:language">
+  <xs:annotation>
+   <xs:documentation>In due course, we should install the relevant ISO 2- and 3-letter
+         codes as the enumerated possible values . . .</xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="space" default="preserve">
+  <xs:simpleType>
+   <xs:restriction base="xs:NCName">
+    <xs:enumeration value="default"/>
+    <xs:enumeration value="preserve"/>
+   </xs:restriction>
+  </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="base" type="xs:anyURI">
+  <xs:annotation>
+   <xs:documentation>See http://www.w3.org/TR/xmlbase/ for
+                     information about this attribute.</xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+  <xs:attribute ref="xml:base"/>
+  <xs:attribute ref="xml:lang"/>
+  <xs:attribute ref="xml:space"/>
+ </xs:attributeGroup>
+
+</xs:schema>
diff --git a/servletapi/jsr154/src/share/javax/servlet/Filter.java b/servletapi/jsr154/src/share/javax/servlet/Filter.java
new file mode 100644
index 0000000..db6d6b7
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/Filter.java
@@ -0,0 +1,91 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.IOException;
+
+	/** 
+	* A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.
+        * <br><br>
+	* Filters perform filtering in the <code>doFilter</code> method. Every Filter has access to 
+	** a FilterConfig object from which it can obtain its initialization parameters, a
+	** reference to the ServletContext which it can use, for example, to load resources
+	** needed for filtering tasks.
+	** <p>
+	** Filters are configured in the deployment descriptor of a web application
+	** <p>
+	** Examples that have been identified for this design are<br>
+	** 1) Authentication Filters <br>
+	** 2) Logging and Auditing Filters <br>
+	** 3) Image conversion Filters <br>
+    	** 4) Data compression Filters <br>
+	** 5) Encryption Filters <br>
+	** 6) Tokenizing Filters <br>
+	** 7) Filters that trigger resource access events <br>
+	** 8) XSL/T filters <br>
+	** 9) Mime-type chain Filter <br>
+	 * @since	Servlet 2.3
+	*/
+
+public interface Filter {
+
+	/** 
+	* Called by the web container to indicate to a filter that it is being placed into
+	* service. The servlet container calls the init method exactly once after instantiating the
+	* filter. The init method must complete successfully before the filter is asked to do any
+	* filtering work. <br><br>
+
+     	* The web container cannot place the filter into service if the init method either<br>
+        * 1.Throws a ServletException <br>
+        * 2.Does not return within a time period defined by the web container 
+	*/
+	public void init(FilterConfig filterConfig) throws ServletException;
+	
+	
+	/**
+	* The <code>doFilter</code> method of the Filter is called by the container
+	* each time a request/response pair is passed through the chain due
+	* to a client request for a resource at the end of the chain. The FilterChain passed in to this
+	* method allows the Filter to pass on the request and response to the next entity in the
+	* chain.<p>
+	* A typical implementation of this method would follow the following pattern:- <br>
+	* 1. Examine the request<br>
+	* 2. Optionally wrap the request object with a custom implementation to
+	* filter content or headers for input filtering <br>
+	* 3. Optionally wrap the response object with a custom implementation to
+	* filter content or headers for output filtering <br>
+	* 4. a) <strong>Either</strong> invoke the next entity in the chain using the FilterChain object (<code>chain.doFilter()</code>), <br>   
+	** 4. b) <strong>or</strong> not pass on the request/response pair to the next entity in the filter chain to block the request processing<br>
+	** 5. Directly set headers on the response after invocation of the next entity in the filter chain.
+	**/
+    public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;
+
+	/**
+	* Called by the web container to indicate to a filter that it is being taken out of service. This 
+	* method is only called once all threads within the filter's doFilter method have exited or after
+	* a timeout period has passed. After the web container calls this method, it will not call the
+	* doFilter method again on this instance of the filter. <br><br>
+	* 
+     	* This method gives the filter an opportunity to clean up any resources that are being held (for
+	* example, memory, file handles, threads) and make sure that any persistent state is synchronized
+	* with the filter's current state in memory.
+	*/
+
+	public void destroy();
+
+
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/FilterChain.java b/servletapi/jsr154/src/share/javax/servlet/FilterChain.java
new file mode 100644
index 0000000..d6210d5
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/FilterChain.java
@@ -0,0 +1,45 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.IOException;
+
+    /**
+    * A FilterChain is an object provided by the servlet container to the developer
+    * giving a view into the invocation chain of a filtered request for a resource. Filters
+    * use the FilterChain to invoke the next filter in the chain, or if the calling filter
+    * is the last filter in the chain, to invoke the resource at the end of the chain.
+    *
+    * @see Filter
+    * @since Servlet 2.3
+    **/
+
+public interface FilterChain {
+	
+	/**
+	* Causes the next filter in the chain to be invoked, or if the calling filter is the last filter
+	* in the chain, causes the resource at the end of the chain to be invoked.
+	*
+	* @param request the request to pass along the chain.
+	* @param response the response to pass along the chain.
+	*
+	* @since 2.3
+	*/
+	
+    public void doFilter ( ServletRequest request, ServletResponse response ) throws IOException, ServletException;
+
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/FilterConfig.java b/servletapi/jsr154/src/share/javax/servlet/FilterConfig.java
new file mode 100644
index 0000000..05c609d
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/FilterConfig.java
@@ -0,0 +1,91 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet;
+
+
+import java.util.Enumeration;
+
+	 /** 
+	 *
+	 * A filter configuration object used by a servlet container
+	 * to pass information to a filter during initialization.
+	 * @see Filter 
+	  * @since	Servlet 2.3
+	 *
+	 */
+
+
+public interface FilterConfig {
+
+	/** 
+	* Returns the filter-name of this filter as defined in the deployment descriptor. 
+	*/
+	
+	public String getFilterName();
+
+
+ /**
+     * Returns a reference to the {@link ServletContext} in which the caller
+     * is executing.
+     *
+     *
+     * @return		a {@link ServletContext} object, used
+     *			by the caller to interact with its servlet 
+     *                  container
+     * 
+     * @see		ServletContext
+     *
+     */
+
+    public ServletContext getServletContext();
+    
+    /**
+     * Returns a <code>String</code> containing the value of the 
+     * named initialization parameter, or <code>null</code> if 
+     * the parameter does not exist.
+     *
+     * @param name	a <code>String</code> specifying the name
+     *			of the initialization parameter
+     *
+     * @return		a <code>String</code> containing the value 
+     *			of the initialization parameter
+     *
+     */
+
+    public String getInitParameter(String name);
+
+
+    /**
+     * Returns the names of the filter's initialization parameters
+     * as an <code>Enumeration</code> of <code>String</code> objects, 
+     * or an empty <code>Enumeration</code> if the filter has
+     * no initialization parameters.
+     *
+     * @return		an <code>Enumeration</code> of <code>String</code> 
+     *			objects containing the names of the filter's 
+     *			initialization parameters
+     *
+     *
+     *
+     */
+
+    public Enumeration getInitParameterNames();
+
+
+
+
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/GenericServlet.java b/servletapi/jsr154/src/share/javax/servlet/GenericServlet.java
new file mode 100644
index 0000000..d4081d7
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/GenericServlet.java
@@ -0,0 +1,323 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ *
+ * Defines a generic, protocol-independent
+ * servlet. To write an HTTP servlet for use on the
+ * Web, extend {@link javax.servlet.http.HttpServlet} instead.
+ *
+ * <p><code>GenericServlet</code> implements the <code>Servlet</code>
+ * and <code>ServletConfig</code> interfaces. <code>GenericServlet</code>
+ * may be directly extended by a servlet, although it's more common to extend
+ * a protocol-specific subclass such as <code>HttpServlet</code>.
+ *
+ * <p><code>GenericServlet</code> makes writing servlets
+ * easier. It provides simple versions of the lifecycle methods 
+ * <code>init</code> and <code>destroy</code> and of the methods 
+ * in the <code>ServletConfig</code> interface. <code>GenericServlet</code>
+ * also implements the <code>log</code> method, declared in the
+ * <code>ServletContext</code> interface. 
+ *
+ * <p>To write a generic servlet, you need only
+ * override the abstract <code>service</code> method. 
+ *
+ *
+ * @author 	Various
+ * @version 	$Version$
+ *
+ *
+ *
+ */
+
+ 
+public abstract class GenericServlet 
+    implements Servlet, ServletConfig, java.io.Serializable
+{
+
+    private transient ServletConfig config;
+    
+
+    /**
+     *
+     * Does nothing. All of the servlet initialization
+     * is done by one of the <code>init</code> methods.
+     *
+     */
+
+    public GenericServlet() { }
+    
+    
+    
+   /**
+     * Called by the servlet container to indicate to a servlet that the
+     * servlet is being taken out of service.  See {@link Servlet#destroy}.
+     *
+     * 
+     */
+
+    public void destroy() {
+    }
+    
+    
+    
+    /**
+     * Returns a <code>String</code> containing the value of the named
+     * initialization parameter, or <code>null</code> if the parameter does
+     * not exist.  See {@link ServletConfig#getInitParameter}.
+     *
+     * <p>This method is supplied for convenience. It gets the 
+     * value of the named parameter from the servlet's 
+     * <code>ServletConfig</code> object.
+     *
+     * @param name 		a <code>String</code> specifying the name 
+     *				of the initialization parameter
+     *
+     * @return String 		a <code>String</code> containing the value
+     *				of the initialization parameter
+     *
+     */ 
+
+    public String getInitParameter(String name) {
+	return getServletConfig().getInitParameter(name);
+    }
+    
+    
+
+   /**
+    * Returns the names of the servlet's initialization parameters 
+    * as an <code>Enumeration</code> of <code>String</code> objects,
+    * or an empty <code>Enumeration</code> if the servlet has no
+    * initialization parameters.  See {@link
+    * ServletConfig#getInitParameterNames}.
+    *
+    * <p>This method is supplied for convenience. It gets the 
+    * parameter names from the servlet's <code>ServletConfig</code> object. 
+    *
+    *
+    * @return Enumeration 	an enumeration of <code>String</code>
+    *				objects containing the names of 
+    *				the servlet's initialization parameters
+    *
+    */
+
+    public Enumeration getInitParameterNames() {
+	return getServletConfig().getInitParameterNames();
+    }   
+    
+     
+ 
+     
+
+    /**
+     * Returns this servlet's {@link ServletConfig} object.
+     *
+     * @return ServletConfig 	the <code>ServletConfig</code> object
+     *				that initialized this servlet
+     *
+     */
+    
+    public ServletConfig getServletConfig() {
+	return config;
+    }
+    
+    
+ 
+    
+    /**
+     * Returns a reference to the {@link ServletContext} in which this servlet
+     * is running.  See {@link ServletConfig#getServletContext}.
+     *
+     * <p>This method is supplied for convenience. It gets the 
+     * context from the servlet's <code>ServletConfig</code> object.
+     *
+     *
+     * @return ServletContext 	the <code>ServletContext</code> object
+     *				passed to this servlet by the <code>init</code>
+     *				method
+     *
+     */
+
+    public ServletContext getServletContext() {
+	return getServletConfig().getServletContext();
+    }
+
+
+
+ 
+
+    /**
+     * Returns information about the servlet, such as 
+     * author, version, and copyright. 
+     * By default, this method returns an empty string.  Override this method
+     * to have it return a meaningful value.  See {@link
+     * Servlet#getServletInfo}.
+     *
+     *
+     * @return String 		information about this servlet, by default an
+     * 				empty string
+     *
+     */
+    
+    public String getServletInfo() {
+	return "";
+    }
+
+
+
+
+    /**
+     *
+     * Called by the servlet container to indicate to a servlet that the
+     * servlet is being placed into service.  See {@link Servlet#init}.
+     *
+     * <p>This implementation stores the {@link ServletConfig}
+     * object it receives from the servlet container for later use.
+     * When overriding this form of the method, call 
+     * <code>super.init(config)</code>.
+     *
+     * @param config 			the <code>ServletConfig</code> object
+     *					that contains configutation
+     *					information for this servlet
+     *
+     * @exception ServletException 	if an exception occurs that
+     *					interrupts the servlet's normal
+     *					operation
+     *
+     * 
+     * @see 				UnavailableException
+     *
+     */
+
+    public void init(ServletConfig config) throws ServletException {
+	this.config = config;
+	this.init();
+    }
+
+
+
+
+
+    /**
+     *
+     * A convenience method which can be overridden so that there's no need
+     * to call <code>super.init(config)</code>.
+     *
+     * <p>Instead of overriding {@link #init(ServletConfig)}, simply override
+     * this method and it will be called by
+     * <code>GenericServlet.init(ServletConfig config)</code>.
+     * The <code>ServletConfig</code> object can still be retrieved via {@link
+     * #getServletConfig}. 
+     *
+     * @exception ServletException 	if an exception occurs that
+     *					interrupts the servlet's
+     *					normal operation
+     *
+     */
+    
+    public void init() throws ServletException {
+
+    }
+    
+
+
+
+    /**
+     * 
+     * Writes the specified message to a servlet log file, prepended by the
+     * servlet's name.  See {@link ServletContext#log(String)}.
+     *
+     * @param msg 	a <code>String</code> specifying
+     *			the message to be written to the log file
+     *
+     */
+     
+    public void log(String msg) {
+	getServletContext().log(getServletName() + ": "+ msg);
+    }
+   
+   
+   
+   
+    /**
+     * Writes an explanatory message and a stack trace
+     * for a given <code>Throwable</code> exception
+     * to the servlet log file, prepended by the servlet's name.
+     * See {@link ServletContext#log(String, Throwable)}.
+     *
+     *
+     * @param message 		a <code>String</code> that describes
+     *				the error or exception
+     *
+     * @param t			the <code>java.lang.Throwable</code> error
+     * 				or exception
+     *
+     *
+     */
+   
+    public void log(String message, Throwable t) {
+	getServletContext().log(getServletName() + ": " + message, t);
+    }
+    
+    
+    
+    /**
+     * Called by the servlet container to allow the servlet to respond to
+     * a request.  See {@link Servlet#service}.
+     * 
+     * <p>This method is declared abstract so subclasses, such as 
+     * <code>HttpServlet</code>, must override it.
+     *
+     *
+     *
+     * @param req 	the <code>ServletRequest</code> object
+     *			that contains the client's request
+     *
+     * @param res 	the <code>ServletResponse</code> object
+     *			that will contain the servlet's response
+     *
+     * @exception ServletException 	if an exception occurs that
+     *					interferes with the servlet's
+     *					normal operation occurred
+     *
+     * @exception IOException 		if an input or output
+     *					exception occurs
+     *
+     */
+
+    public abstract void service(ServletRequest req, ServletResponse res)
+	throws ServletException, IOException;
+    
+
+
+    /**
+     * Returns the name of this servlet instance.
+     * See {@link ServletConfig#getServletName}.
+     *
+     * @return          the name of this servlet instance
+     *
+     *
+     *
+     */
+
+    public String getServletName() {
+        return config.getServletName();
+    }
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/LocalStrings.properties b/servletapi/jsr154/src/share/javax/servlet/LocalStrings.properties
new file mode 100644
index 0000000..c1dcf48
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/LocalStrings.properties
@@ -0,0 +1,20 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Default localized string information
+# Localized for Locale en_US
+
+err.not_iso8859_1=Not an ISO 8859-1 character: {0}
+value.true=true
+value.false=false
diff --git a/servletapi/jsr154/src/share/javax/servlet/LocalStrings_fr.properties b/servletapi/jsr154/src/share/javax/servlet/LocalStrings_fr.properties
new file mode 100644
index 0000000..987309b
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/LocalStrings_fr.properties
@@ -0,0 +1,22 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Default localized string information
+# Localized for Locale fr_FR
+
+err.not_iso8859_1={0} n''est pas un caractère ISO 8859-1
+value.true=true
+value.false=false 
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/LocalStrings_ja.properties b/servletapi/jsr154/src/share/javax/servlet/LocalStrings_ja.properties
new file mode 100644
index 0000000..4ed3709
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/LocalStrings_ja.properties
@@ -0,0 +1,20 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Default localized string information
+# Localized for Locale ja_JP
+
+err.not_iso8859_1=ISO 8859-1 \u306e\u6587\u5b57\u3067\u306f\u3042\u308a\u307e\u305b\u3093: {0}
+value.true=true
+value.false=false
diff --git a/servletapi/jsr154/src/share/javax/servlet/RequestDispatcher.java b/servletapi/jsr154/src/share/javax/servlet/RequestDispatcher.java
new file mode 100644
index 0000000..1993728
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/RequestDispatcher.java
@@ -0,0 +1,138 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet;
+
+import java.io.IOException;
+
+
+/**
+ * Defines an object that receives requests from the client
+ * and sends them to any resource (such as a servlet, 
+ * HTML file, or JSP file) on the server. The servlet
+ * container creates the <code>RequestDispatcher</code> object,
+ * which is used as a wrapper around a server resource located
+ * at a particular path or given by a particular name.
+ *
+ * <p>This interface is intended to wrap servlets,
+ * but a servlet container can create <code>RequestDispatcher</code>
+ * objects to wrap any type of resource.
+ *
+ * @author 	Various
+ * @version 	$Version$
+ *
+ * @see 	ServletContext#getRequestDispatcher(java.lang.String)
+ * @see 	ServletContext#getNamedDispatcher(java.lang.String)
+ * @see 	ServletRequest#getRequestDispatcher(java.lang.String)
+ *
+ */
+ 
+public interface RequestDispatcher {
+
+
+
+
+
+/**
+ * Forwards a request from
+ * a servlet to another resource (servlet, JSP file, or
+ * HTML file) on the server. This method allows
+ * one servlet to do preliminary processing of
+ * a request and another resource to generate
+ * the response.
+ *
+ * <p>For a <code>RequestDispatcher</code> obtained via 
+ * <code>getRequestDispatcher()</code>, the <code>ServletRequest</code> 
+ * object has its path elements and parameters adjusted to match
+ * the path of the target resource.
+ *
+ * <p><code>forward</code> should be called before the response has been 
+ * committed to the client (before response body output has been flushed).  
+ * If the response already has been committed, this method throws
+ * an <code>IllegalStateException</code>.
+ * Uncommitted output in the response buffer is automatically cleared 
+ * before the forward.
+ *
+ * <p>The request and response parameters must be either the same
+ * objects as were passed to the calling servlet's service method or be
+ * subclasses of the {@link ServletRequestWrapper} or {@link ServletResponseWrapper} classes
+ * that wrap them.
+ *
+ *
+ * @param request		a {@link ServletRequest} object
+ *				that represents the request the client
+ * 				makes of the servlet
+ *
+ * @param response		a {@link ServletResponse} object
+ *				that represents the response the servlet
+ *				returns to the client
+ *
+ * @exception ServletException	if the target resource throws this exception
+ *
+ * @exception IOException	if the target resource throws this exception
+ *
+ * @exception IllegalStateException	if the response was already committed
+ *
+ */
+
+    public void forward(ServletRequest request, ServletResponse response)
+	throws ServletException, IOException;
+
+
+
+
+    /**
+     *
+     * Includes the content of a resource (servlet, JSP page,
+     * HTML file) in the response. In essence, this method enables 
+     * programmatic server-side includes.
+     *
+     * <p>The {@link ServletResponse} object has its path elements
+     * and parameters remain unchanged from the caller's. The included
+     * servlet cannot change the response status code or set headers;
+     * any attempt to make a change is ignored.
+     *
+     * <p>The request and response parameters must be either the same
+     * objects as were passed to the calling servlet's service method or be
+     * subclasses of the {@link ServletRequestWrapper} or {@link ServletResponseWrapper} classes
+     * that wrap them.
+     * 
+     *
+     *
+     * @param request 			a {@link ServletRequest} object 
+     *					that contains the client's request
+     *
+     * @param response 			a {@link ServletResponse} object 
+     * 					that contains the servlet's response
+     *
+     * @exception ServletException 	if the included resource throws this exception
+     *
+     * @exception IOException 		if the included resource throws this exception
+     *
+     *
+     */
+     
+    public void include(ServletRequest request, ServletResponse response)
+	throws ServletException, IOException;
+}
+
+
+
+
+
+
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/Servlet.java b/servletapi/jsr154/src/share/javax/servlet/Servlet.java
new file mode 100644
index 0000000..3d568de
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/Servlet.java
@@ -0,0 +1,192 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ 
+package javax.servlet;
+
+import java.io.IOException;
+
+
+/**
+ * Defines methods that all servlets must implement.
+ *
+ * <p>A servlet is a small Java program that runs within a Web server.
+ * Servlets receive and respond to requests from Web clients,
+ * usually across HTTP, the HyperText Transfer Protocol. 
+ *
+ * <p>To implement this interface, you can write a generic servlet
+ * that extends
+ * <code>javax.servlet.GenericServlet</code> or an HTTP servlet that
+ * extends <code>javax.servlet.http.HttpServlet</code>.
+ *
+ * <p>This interface defines methods to initialize a servlet,
+ * to service requests, and to remove a servlet from the server.
+ * These are known as life-cycle methods and are called in the
+ * following sequence:
+ * <ol>
+ * <li>The servlet is constructed, then initialized with the <code>init</code> method.
+ * <li>Any calls from clients to the <code>service</code> method are handled.
+ * <li>The servlet is taken out of service, then destroyed with the 
+ * <code>destroy</code> method, then garbage collected and finalized.
+ * </ol>
+ *
+ * <p>In addition to the life-cycle methods, this interface
+ * provides the <code>getServletConfig</code> method, which the servlet 
+ * can use to get any startup information, and the <code>getServletInfo</code>
+ * method, which allows the servlet to return basic information about itself,
+ * such as author, version, and copyright.
+ *
+ * @author 	Various
+ * @version 	$Version$
+ *
+ * @see 	GenericServlet
+ * @see 	javax.servlet.http.HttpServlet
+ *
+ */
+
+
+public interface Servlet {
+
+    /**
+     * Called by the servlet container to indicate to a servlet that the 
+     * servlet is being placed into service.
+     *
+     * <p>The servlet container calls the <code>init</code>
+     * method exactly once after instantiating the servlet.
+     * The <code>init</code> method must complete successfully
+     * before the servlet can receive any requests.
+     *
+     * <p>The servlet container cannot place the servlet into service
+     * if the <code>init</code> method
+     * <ol>
+     * <li>Throws a <code>ServletException</code>
+     * <li>Does not return within a time period defined by the Web server
+     * </ol>
+     *
+     *
+     * @param config			a <code>ServletConfig</code> object 
+     *					containing the servlet's
+     * 					configuration and initialization parameters
+     *
+     * @exception ServletException 	if an exception has occurred that
+     *					interferes with the servlet's normal
+     *					operation
+     *
+     * @see 				UnavailableException
+     * @see 				#getServletConfig
+     *
+     */
+
+    public void init(ServletConfig config) throws ServletException;
+    
+    
+
+    /**
+     *
+     * Returns a {@link ServletConfig} object, which contains
+     * initialization and startup parameters for this servlet.
+     * The <code>ServletConfig</code> object returned is the one 
+     * passed to the <code>init</code> method. 
+     *
+     * <p>Implementations of this interface are responsible for storing the 
+     * <code>ServletConfig</code> object so that this 
+     * method can return it. The {@link GenericServlet}
+     * class, which implements this interface, already does this.
+     *
+     * @return		the <code>ServletConfig</code> object
+     *			that initializes this servlet
+     *
+     * @see 		#init
+     *
+     */
+
+    public ServletConfig getServletConfig();
+    
+    
+
+    /**
+     * Called by the servlet container to allow the servlet to respond to 
+     * a request.
+     *
+     * <p>This method is only called after the servlet's <code>init()</code>
+     * method has completed successfully.
+     * 
+     * <p>  The status code of the response always should be set for a servlet 
+     * that throws or sends an error.
+     *
+     * 
+     * <p>Servlets typically run inside multithreaded servlet containers
+     * that can handle multiple requests concurrently. Developers must 
+     * be aware to synchronize access to any shared resources such as files,
+     * network connections, and as well as the servlet's class and instance 
+     * variables. 
+     * More information on multithreaded programming in Java is available in 
+     * <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
+     * the Java tutorial on multi-threaded programming</a>.
+     *
+     *
+     * @param req 	the <code>ServletRequest</code> object that contains
+     *			the client's request
+     *
+     * @param res 	the <code>ServletResponse</code> object that contains
+     *			the servlet's response
+     *
+     * @exception ServletException 	if an exception occurs that interferes
+     *					with the servlet's normal operation 
+     *
+     * @exception IOException 		if an input or output exception occurs
+     *
+     */
+
+    public void service(ServletRequest req, ServletResponse res)
+	throws ServletException, IOException;
+	
+	
+
+    /**
+     * Returns information about the servlet, such
+     * as author, version, and copyright.
+     * 
+     * <p>The string that this method returns should
+     * be plain text and not markup of any kind (such as HTML, XML,
+     * etc.).
+     *
+     * @return 		a <code>String</code> containing servlet information
+     *
+     */
+
+    public String getServletInfo();
+    
+    
+
+    /**
+     *
+     * Called by the servlet container to indicate to a servlet that the
+     * servlet is being taken out of service.  This method is
+     * only called once all threads within the servlet's
+     * <code>service</code> method have exited or after a timeout
+     * period has passed. After the servlet container calls this 
+     * method, it will not call the <code>service</code> method again
+     * on this servlet.
+     *
+     * <p>This method gives the servlet an opportunity 
+     * to clean up any resources that are being held (for example, memory,
+     * file handles, threads) and make sure that any persistent state is
+     * synchronized with the servlet's current state in memory.
+     *
+     */
+
+    public void destroy();
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletConfig.java b/servletapi/jsr154/src/share/javax/servlet/ServletConfig.java
new file mode 100644
index 0000000..e5f9b1b
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletConfig.java
@@ -0,0 +1,94 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.util.Enumeration;
+
+
+
+/**
+ * 
+ * A servlet configuration object used by a servlet container
+ * to pass information to a servlet during initialization. 
+ *
+ */
+ 
+public interface ServletConfig {
+    
+
+    /**
+     * Returns the name of this servlet instance.
+     * The name may be provided via server administration, assigned in the 
+     * web application deployment descriptor, or for an unregistered (and thus
+     * unnamed) servlet instance it will be the servlet's class name.
+     *
+     * @return		the name of the servlet instance
+     *
+     *
+     *
+     */
+
+    public String getServletName();
+
+    /**
+     * Returns a reference to the {@link ServletContext} in which the caller
+     * is executing.
+     *
+     *
+     * @return		a {@link ServletContext} object, used
+     *			by the caller to interact with its servlet 
+     *                  container
+     * 
+     * @see		ServletContext
+     *
+     */
+
+    public ServletContext getServletContext();
+    
+    /**
+     * Returns a <code>String</code> containing the value of the 
+     * named initialization parameter, or <code>null</code> if 
+     * the parameter does not exist.
+     *
+     * @param name	a <code>String</code> specifying the name
+     *			of the initialization parameter
+     *
+     * @return		a <code>String</code> containing the value 
+     *			of the initialization parameter
+     *
+     */
+
+    public String getInitParameter(String name);
+
+
+    /**
+     * Returns the names of the servlet's initialization parameters
+     * as an <code>Enumeration</code> of <code>String</code> objects, 
+     * or an empty <code>Enumeration</code> if the servlet has
+     * no initialization parameters.
+     *
+     * @return		an <code>Enumeration</code> of <code>String</code> 
+     *			objects containing the names of the servlet's 
+     *			initialization parameters
+     *
+     *
+     *
+     */
+
+    public Enumeration getInitParameterNames();
+
+
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletContext.java b/servletapi/jsr154/src/share/javax/servlet/ServletContext.java
new file mode 100644
index 0000000..938f17d
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletContext.java
@@ -0,0 +1,643 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Set;
+
+
+/**
+ * 
+ * Defines a set of methods that a servlet uses to communicate with its
+ * servlet container, for example, to get the MIME type of a file, dispatch
+ * requests, or write to a log file.
+ *
+ * <p>There is one context per "web application" per Java Virtual Machine.  (A
+ * "web application" is a collection of servlets and content installed under a
+ * specific subset of the server's URL namespace such as <code>/catalog</code>
+ * and possibly installed via a <code>.war</code> file.) 
+ *
+ * <p>In the case of a web
+ * application marked "distributed" in its deployment descriptor, there will
+ * be one context instance for each virtual machine.  In this situation, the 
+ * context cannot be used as a location to share global information (because
+ * the information won't be truly global).  Use an external resource like 
+ * a database instead.
+ *
+ * <p>The <code>ServletContext</code> object is contained within 
+ * the {@link ServletConfig} object, which the Web server provides the
+ * servlet when the servlet is initialized.
+ *
+ * @author 	Various
+ * @version 	$Version$
+ *
+ * @see 	Servlet#getServletConfig
+ * @see 	ServletConfig#getServletContext
+ *
+ */
+
+public interface ServletContext {
+
+
+    /**
+     * Returns a <code>ServletContext</code> object that 
+     * corresponds to a specified URL on the server.
+     *
+     * <p>This method allows servlets to gain
+     * access to the context for various parts of the server, and as
+     * needed obtain {@link RequestDispatcher} objects from the context.
+     * The given path must be begin with "/", is interpreted relative 
+     * to the server's document root and is matched against the context roots of
+     * other web applications hosted on this container.
+     * 
+     * <p>In a security conscious environment, the servlet container may
+     * return <code>null</code> for a given URL.
+     *       
+     * @param uripath 	a <code>String</code> specifying the context path of
+     *			another web application in the container.
+     * @return		the <code>ServletContext</code> object that
+     *			corresponds to the named URL, or null if either
+			none exists or the container wishes to restrict 
+     * 			this access.
+     *
+     * @see 		RequestDispatcher
+     *
+     */
+
+    public ServletContext getContext(String uripath);
+    
+    
+
+    /**
+     * Returns the major version of the Java Servlet API that this
+     * servlet container supports. All implementations that comply
+     * with Version 2.4 must have this method
+     * return the integer 2.
+     *
+     * @return 		2
+     *
+     */
+    
+    public int getMajorVersion();
+    
+    
+
+    /**
+     * Returns the minor version of the Servlet API that this
+     * servlet container supports. All implementations that comply
+     * with Version 2.4 must have this method
+     * return the integer 4.
+     *
+     * @return 		4
+     *
+     */
+
+    public int getMinorVersion();
+    
+    
+
+    /**
+     * Returns the MIME type of the specified file, or <code>null</code> if 
+     * the MIME type is not known. The MIME type is determined
+     * by the configuration of the servlet container, and may be specified
+     * in a web application deployment descriptor. Common MIME
+     * types are <code>"text/html"</code> and <code>"image/gif"</code>.
+     *
+     *
+     * @param   file    a <code>String</code> specifying the name
+     *			of a file
+     *
+     * @return 		a <code>String</code> specifying the file's MIME type
+     *
+     */
+
+    public String getMimeType(String file);
+    
+    /**
+    * Returns a directory-like listing of all the paths to resources within the web application whose longest sub-path
+    * matches the supplied path argument. Paths indicating subdirectory paths end with a '/'. The returned paths are all 
+    * relative to the root of the web application and have a leading '/'. For example, for a web application 
+    * containing<br><br>
+
+    * /welcome.html<br>
+    * /catalog/index.html<br>
+    * /catalog/products.html<br>
+    * /catalog/offers/books.html<br>
+    * /catalog/offers/music.html<br>
+    * /customer/login.jsp<br>
+    * /WEB-INF/web.xml<br>
+    * /WEB-INF/classes/com.acme.OrderServlet.class,<br><br>
+    *
+    * getResourcePaths("/") returns {"/welcome.html", "/catalog/", "/customer/", "/WEB-INF/"}<br>
+    * getResourcePaths("/catalog/") returns {"/catalog/index.html", "/catalog/products.html", "/catalog/offers/"}.<br>
+	   
+
+
+    *@param path		the partial path used to match the resources,
+    *				which must start with a /
+    *@return a Set containing the directory listing, or null if there are no resources in the web application whose path
+	* begins with the supplied path.
+
+    * @since Servlet 2.3
+    */
+    
+    public Set getResourcePaths(String path);
+    
+    
+
+    /**
+     * Returns a URL to the resource that is mapped to a specified
+     * path. The path must begin with a "/" and is interpreted
+     * as relative to the current context root.
+     *
+     * <p>This method allows the servlet container to make a resource 
+     * available to servlets from any source. Resources 
+     * can be located on a local or remote
+     * file system, in a database, or in a <code>.war</code> file. 
+     *
+     * <p>The servlet container must implement the URL handlers
+     * and <code>URLConnection</code> objects that are necessary
+     * to access the resource.
+     *
+     * <p>This method returns <code>null</code>
+     * if no resource is mapped to the pathname.
+     *
+     * <p>Some containers may allow writing to the URL returned by
+     * this method using the methods of the URL class.
+     *
+     * <p>The resource content is returned directly, so be aware that 
+     * requesting a <code>.jsp</code> page returns the JSP source code.
+     * Use a <code>RequestDispatcher</code> instead to include results of 
+     * an execution.
+     *
+     * <p>This method has a different purpose than
+     * <code>java.lang.Class.getResource</code>,
+     * which looks up resources based on a class loader. This
+     * method does not use class loaders.
+     * 
+     * @param path 				a <code>String</code> specifying
+     *						the path to the resource
+     *
+     * @return 					the resource located at the named path,
+     * 						or <code>null</code> if there is no resource
+     *						at that path
+     *
+     * @exception MalformedURLException 	if the pathname is not given in 
+     * 						the correct form
+     *
+     */
+    
+    public URL getResource(String path) throws MalformedURLException;
+    
+    
+
+    /**
+     * Returns the resource located at the named path as
+     * an <code>InputStream</code> object.
+     *
+     * <p>The data in the <code>InputStream</code> can be 
+     * of any type or length. The path must be specified according
+     * to the rules given in <code>getResource</code>.
+     * This method returns <code>null</code> if no resource exists at
+     * the specified path. 
+     * 
+     * <p>Meta-information such as content length and content type
+     * that is available via <code>getResource</code>
+     * method is lost when using this method.
+     *
+     * <p>The servlet container must implement the URL handlers
+     * and <code>URLConnection</code> objects necessary to access
+     * the resource.
+     *
+     * <p>This method is different from 
+     * <code>java.lang.Class.getResourceAsStream</code>,
+     * which uses a class loader. This method allows servlet containers 
+     * to make a resource available
+     * to a servlet from any location, without using a class loader.
+     * 
+     *
+     * @param path 	a <code>String</code> specifying the path
+     *			to the resource
+     *
+     * @return 		the <code>InputStream</code> returned to the 
+     *			servlet, or <code>null</code> if no resource
+     *			exists at the specified path 
+     *
+     *
+     */
+
+    public InputStream getResourceAsStream(String path);
+    
+
+
+
+    /**
+     * 
+     * Returns a {@link RequestDispatcher} object that acts
+     * as a wrapper for the resource located at the given path.
+     * A <code>RequestDispatcher</code> object can be used to forward 
+     * a request to the resource or to include the resource in a response.
+     * The resource can be dynamic or static.
+     *
+     * <p>The pathname must begin with a "/" and is interpreted as relative
+     * to the current context root.  Use <code>getContext</code> to obtain
+     * a <code>RequestDispatcher</code> for resources in foreign contexts.
+     * This method returns <code>null</code> if the <code>ServletContext</code>
+     * cannot return a <code>RequestDispatcher</code>.
+     *
+     * @param path 	a <code>String</code> specifying the pathname
+     *			to the resource
+     *
+     * @return 		a <code>RequestDispatcher</code> object
+     *			that acts as a wrapper for the resource
+     *			at the specified path, or <code>null</code> if 
+     *			the <code>ServletContext</code> cannot return
+     *			a <code>RequestDispatcher</code>
+     *
+     * @see 		RequestDispatcher
+     * @see 		ServletContext#getContext
+     *
+     */
+
+    public RequestDispatcher getRequestDispatcher(String path);
+
+
+
+    /**
+     * Returns a {@link RequestDispatcher} object that acts
+     * as a wrapper for the named servlet.
+     *
+     * <p>Servlets (and JSP pages also) may be given names via server 
+     * administration or via a web application deployment descriptor.
+     * A servlet instance can determine its name using 
+     * {@link ServletConfig#getServletName}.
+     *
+     * <p>This method returns <code>null</code> if the 
+     * <code>ServletContext</code>
+     * cannot return a <code>RequestDispatcher</code> for any reason.
+     *
+     * @param name 	a <code>String</code> specifying the name
+     *			of a servlet to wrap
+     *
+     * @return 		a <code>RequestDispatcher</code> object
+     *			that acts as a wrapper for the named servlet,
+     *			or <code>null</code> if the <code>ServletContext</code>
+     *			cannot return a <code>RequestDispatcher</code>
+     *
+     * @see 		RequestDispatcher
+     * @see 		ServletContext#getContext
+     * @see 		ServletConfig#getServletName
+     *
+     */
+
+    public RequestDispatcher getNamedDispatcher(String name);
+    
+    
+    
+    
+    /**
+     *
+     * @deprecated	As of Java Servlet API 2.1, with no direct replacement.
+     *
+     * <p>This method was originally defined to retrieve a servlet
+     * from a <code>ServletContext</code>. In this version, this method 
+     * always returns <code>null</code> and remains only to preserve 
+     * binary compatibility. This method will be permanently removed 
+     * in a future version of the Java Servlet API.
+     *
+     * <p>In lieu of this method, servlets can share information using the 
+     * <code>ServletContext</code> class and can perform shared business logic
+     * by invoking methods on common non-servlet classes.
+     *
+     */
+
+    public Servlet getServlet(String name) throws ServletException;
+    
+  
+  
+  
+    
+
+    /**
+     *
+     * @deprecated	As of Java Servlet API 2.0, with no replacement.
+     *
+     * <p>This method was originally defined to return an <code>Enumeration</code>
+     * of all the servlets known to this servlet context. In this
+     * version, this method always returns an empty enumeration and
+     * remains only to preserve binary compatibility. This method
+     * will be permanently removed in a future version of the Java
+     * Servlet API.
+     *
+     */
+    
+    public Enumeration getServlets();
+    
+    
+    
+    
+    
+
+    /**
+     * @deprecated	As of Java Servlet API 2.1, with no replacement.
+     *
+     * <p>This method was originally defined to return an 
+     * <code>Enumeration</code>
+     * of all the servlet names known to this context. In this version,
+     * this method always returns an empty <code>Enumeration</code> and 
+     * remains only to preserve binary compatibility. This method will 
+     * be permanently removed in a future version of the Java Servlet API.
+     *
+     */
+ 
+    public Enumeration getServletNames();
+    
+  
+  
+    
+    
+    /**
+     *
+     * Writes the specified message to a servlet log file, usually
+     * an event log. The name and type of the servlet log file is 
+     * specific to the servlet container.
+     *
+     *
+     * @param msg 	a <code>String</code> specifying the 
+     *			message to be written to the log file
+     *
+     */
+     
+    public void log(String msg);
+    
+    
+    
+    
+
+    /**
+     * @deprecated	As of Java Servlet API 2.1, use
+     * 			{@link #log(String message, Throwable throwable)} 
+     *			instead.
+     *
+     * <p>This method was originally defined to write an 
+     * exception's stack trace and an explanatory error message
+     * to the servlet log file.
+     *
+     */
+
+    public void log(Exception exception, String msg);
+    
+    
+    
+    
+
+    /**
+     * Writes an explanatory message and a stack trace
+     * for a given <code>Throwable</code> exception
+     * to the servlet log file. The name and type of the servlet log 
+     * file is specific to the servlet container, usually an event log.
+     *
+     *
+     * @param message 		a <code>String</code> that 
+     *				describes the error or exception
+     *
+     * @param throwable 	the <code>Throwable</code> error 
+     *				or exception
+     *
+     */
+    
+    public void log(String message, Throwable throwable);
+    
+    
+    
+    
+    
+    /**
+     * Returns a <code>String</code> containing the real path 
+     * for a given virtual path. For example, the path "/index.html"
+     * returns the absolute file path on the server's filesystem would be
+     * served by a request for "http://host/contextPath/index.html",
+     * where contextPath is the context path of this ServletContext..
+     *
+     * <p>The real path returned will be in a form
+     * appropriate to the computer and operating system on
+     * which the servlet container is running, including the
+     * proper path separators. This method returns <code>null</code>
+     * if the servlet container cannot translate the virtual path
+     * to a real path for any reason (such as when the content is
+     * being made available from a <code>.war</code> archive).
+     *
+     *
+     * @param path 	a <code>String</code> specifying a virtual path
+     *
+     *
+     * @return 		a <code>String</code> specifying the real path,
+     *                  or null if the translation cannot be performed
+     *			
+     *
+     */
+
+    public String getRealPath(String path);
+    
+    
+
+
+    /**
+     * Returns the name and version of the servlet container on which
+     * the servlet is running. 
+     *
+     * <p>The form of the returned string is 
+     * <i>servername</i>/<i>versionnumber</i>.
+     * For example, the JavaServer Web Development Kit may return the string
+     * <code>JavaServer Web Dev Kit/1.0</code>.
+     *
+     * <p>The servlet container may return other optional information 
+     * after the primary string in parentheses, for example,
+     * <code>JavaServer Web Dev Kit/1.0 (JDK 1.1.6; Windows NT 4.0 x86)</code>.
+     *
+     *
+     * @return 		a <code>String</code> containing at least the 
+     *			servlet container name and version number
+     *
+     */
+
+    public String getServerInfo();
+    
+    
+
+
+    /**
+     * Returns a <code>String</code> containing the value of the named
+     * context-wide initialization parameter, or <code>null</code> if the 
+     * parameter does not exist.
+     *
+     * <p>This method can make available configuration information useful
+     * to an entire "web application".  For example, it can provide a 
+     * webmaster's email address or the name of a system that holds 
+     * critical data.
+     *
+     * @param	name	a <code>String</code> containing the name of the
+     *                  parameter whose value is requested
+     * 
+     * @return 		a <code>String</code> containing at least the 
+     *			servlet container name and version number
+     *
+     * @see ServletConfig#getInitParameter
+     */
+
+    public String getInitParameter(String name);
+    
+    
+
+
+    /**
+     * Returns the names of the context's initialization parameters as an
+     * <code>Enumeration</code> of <code>String</code> objects, or an
+     * empty <code>Enumeration</code> if the context has no initialization
+     * parameters.
+     *
+     * @return 		an <code>Enumeration</code> of <code>String</code> 
+     *                  objects containing the names of the context's
+     *                  initialization parameters
+     *
+     * @see ServletConfig#getInitParameter
+     */
+
+    public Enumeration getInitParameterNames();
+    
+    
+
+    /**
+     * Returns the servlet container attribute with the given name, 
+     * or <code>null</code> if there is no attribute by that name.
+     * An attribute allows a servlet container to give the
+     * servlet additional information not
+     * already provided by this interface. See your
+     * server documentation for information about its attributes.
+     * A list of supported attributes can be retrieved using
+     * <code>getAttributeNames</code>.
+     *
+     * <p>The attribute is returned as a <code>java.lang.Object</code>
+     * or some subclass.
+     * Attribute names should follow the same convention as package
+     * names. The Java Servlet API specification reserves names
+     * matching <code>java.*</code>, <code>javax.*</code>,
+     * and <code>sun.*</code>.
+     *
+     *
+     * @param name 	a <code>String</code> specifying the name 
+     *			of the attribute
+     *
+     * @return 		an <code>Object</code> containing the value 
+     *			of the attribute, or <code>null</code>
+     *			if no attribute exists matching the given
+     *			name
+     *
+     * @see 		ServletContext#getAttributeNames
+     *
+     */
+  
+    public Object getAttribute(String name);
+    
+    
+    
+
+    /**
+     * Returns an <code>Enumeration</code> containing the 
+     * attribute names available
+     * within this servlet context. Use the
+     * {@link #getAttribute} method with an attribute name
+     * to get the value of an attribute.
+     *
+     * @return 		an <code>Enumeration</code> of attribute 
+     *			names
+     *
+     * @see		#getAttribute
+     *
+     */
+
+    public Enumeration getAttributeNames();
+    
+    
+    
+    
+    /**
+     *
+     * Binds an object to a given attribute name in this servlet context. If
+     * the name specified is already used for an attribute, this
+     * method will replace the attribute with the new to the new attribute.
+     * <p>If listeners are configured on the <code>ServletContext</code> the  
+     * container notifies them accordingly.
+     * <p>
+     * If a null value is passed, the effect is the same as calling 
+     * <code>removeAttribute()</code>.
+     * 
+     * <p>Attribute names should follow the same convention as package
+     * names. The Java Servlet API specification reserves names
+     * matching <code>java.*</code>, <code>javax.*</code>, and
+     * <code>sun.*</code>.
+     *
+     *
+     * @param name 	a <code>String</code> specifying the name 
+     *			of the attribute
+     *
+     * @param object 	an <code>Object</code> representing the
+     *			attribute to be bound
+     *
+     *
+     *
+     */
+    
+    public void setAttribute(String name, Object object);
+    
+    
+
+
+
+    /**
+     * Removes the attribute with the given name from 
+     * the servlet context. After removal, subsequent calls to
+     * {@link #getAttribute} to retrieve the attribute's value
+     * will return <code>null</code>.
+
+     * <p>If listeners are configured on the <code>ServletContext</code> the 
+     * container notifies them accordingly.
+
+     *
+     *
+     * @param name	a <code>String</code> specifying the name 
+     * 			of the attribute to be removed
+     *
+     */
+
+    public void removeAttribute(String name);
+    
+    /**
+     * Returns the name of this web application corresponding to this ServletContext as specified in the deployment
+     * descriptor for this web application by the display-name element.
+     *
+     *
+     * @return	    The name of the web application or null if no name has been declared in the deployment descriptor.
+     * @since Servlet 2.3
+     */
+    
+    public String getServletContextName();
+}
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletContextAttributeEvent.java b/servletapi/jsr154/src/share/javax/servlet/ServletContextAttributeEvent.java
new file mode 100644
index 0000000..e7af9a9
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletContextAttributeEvent.java
@@ -0,0 +1,59 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+
+	/** 
+	* This is the event class for notifications about changes to the attributes of the
+	*  servlet context of a web application.
+	* @see ServletContextAttributeListener
+	 * @since	v 2.3
+	*/
+
+public class ServletContextAttributeEvent extends ServletContextEvent { 
+	private String name;
+	private Object value;
+
+	/** Construct a ServletContextAttributeEvent from the given context for the
+	** given attribute name and attribute value. 
+	*/
+	public ServletContextAttributeEvent(ServletContext source, String name, Object value) {
+	    super(source);
+	    this.name = name;
+	    this.value = value;
+	}
+	
+	/**
+	* Return the name of the attribute that changed on the ServletContext.
+	*
+	*/
+	public String getName() {
+		return this.name;
+	}
+	
+	/**
+	* Returns the value of the attribute that has been added, removed, or replaced.
+	* If the attribute was added, this is the value of the attribute. If the attribute was
+	* removed, this is the value of the removed attribute. If the attribute was replaced, this
+	* is the old value of the attribute.
+	*
+	*/
+	
+	public Object getValue() {
+	    return this.value;   
+	}
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletContextAttributeListener.java b/servletapi/jsr154/src/share/javax/servlet/ServletContextAttributeListener.java
new file mode 100644
index 0000000..3d8b938
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletContextAttributeListener.java
@@ -0,0 +1,36 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.util.EventListener;
+
+	/** Implementations of this interface receive notifications of
+	** changes to the attribute list on the servlet context of a web application. 
+	* To receive notification events, the implementation class
+	* must be configured in the deployment descriptor for the web application.
+	* @see ServletContextAttributeEvent
+	 * @since	v 2.3
+	*/
+
+public interface ServletContextAttributeListener extends EventListener {
+	/** Notification that a new attribute was added to the servlet context. Called after the attribute is added.*/
+public void attributeAdded(ServletContextAttributeEvent scab);
+	/** Notification that an existing attribute has been removed from the servlet context. Called after the attribute is removed.*/
+public void attributeRemoved(ServletContextAttributeEvent scab);
+	/** Notification that an attribute on the servlet context has been replaced. Called after the attribute is replaced. */
+public void attributeReplaced(ServletContextAttributeEvent scab);
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletContextEvent.java b/servletapi/jsr154/src/share/javax/servlet/ServletContextEvent.java
new file mode 100644
index 0000000..cb7b887
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletContextEvent.java
@@ -0,0 +1,46 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+
+	/** 
+	 * This is the event class for notifications about changes to
+	 * the servlet context of a web application.
+	 * @see ServletContextListener
+	 * @since	v 2.3
+	 */
+
+public class ServletContextEvent extends java.util.EventObject { 
+
+	/** Construct a ServletContextEvent from the given context.
+	 *
+	 * @param source - the ServletContext that is sending the event.
+	 */
+    public ServletContextEvent(ServletContext source) {
+	super(source);
+    }
+    
+	/**
+	 * Return the ServletContext that changed.
+	 *
+	 * @return the ServletContext that sent the event.
+	 */
+    public ServletContext getServletContext () { 
+	return (ServletContext) super.getSource();
+    }
+    
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletContextListener.java b/servletapi/jsr154/src/share/javax/servlet/ServletContextListener.java
new file mode 100644
index 0000000..693d2c1
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletContextListener.java
@@ -0,0 +1,50 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.util.EventListener;
+
+	/** 
+	 * Implementations of this interface receive notifications about
+	 * changes to the servlet context of the web application they are
+	 * part of.
+	 * To receive notification events, the implementation class
+	 * must be configured in the deployment descriptor for the web
+	 * application.
+	 * @see ServletContextEvent
+	 * @since	v 2.3
+	 */
+
+public interface ServletContextListener extends EventListener {
+	/**
+	 ** Notification that the web application initialization
+	 ** process is starting.
+	 ** All ServletContextListeners are notified of context
+	 ** initialization before any filter or servlet in the web
+	 ** application is initialized.
+	 */
+
+    public void contextInitialized ( ServletContextEvent sce );
+
+	/**
+	 ** Notification that the servlet context is about to be shut down.
+	 ** All servlets and filters have been destroy()ed before any
+	 ** ServletContextListeners are notified of context
+	 ** destruction.
+	 */
+    public void contextDestroyed ( ServletContextEvent sce );
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletException.java b/servletapi/jsr154/src/share/javax/servlet/ServletException.java
new file mode 100644
index 0000000..29533cb
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletException.java
@@ -0,0 +1,141 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+
+/**
+ * Defines a general exception a servlet can throw when it
+ * encounters difficulty.
+ *
+ * @author 	Various
+ * @version 	$Version$
+ *
+ */
+
+
+public class ServletException extends Exception {
+
+    private Throwable rootCause;
+
+
+
+
+
+    /**
+     * Constructs a new servlet exception.
+     *
+     */
+
+    public ServletException() {
+	super();
+    }
+    
+   
+
+    
+
+    /**
+     * Constructs a new servlet exception with the
+     * specified message. The message can be written 
+     * to the server log and/or displayed for the user. 
+     *
+     * @param message 		a <code>String</code> 
+     *				specifying the text of 
+     *				the exception message
+     *
+     */
+
+    public ServletException(String message) {
+	super(message);
+    }
+    
+   
+   
+    
+
+    /**
+     * Constructs a new servlet exception when the servlet 
+     * needs to throw an exception and include a message 
+     * about the "root cause" exception that interfered with its 
+     * normal operation, including a description message.
+     *
+     *
+     * @param message 		a <code>String</code> containing 
+     *				the text of the exception message
+     *
+     * @param rootCause		the <code>Throwable</code> exception 
+     *				that interfered with the servlet's
+     *				normal operation, making this servlet
+     *				exception necessary
+     *
+     */
+    
+    public ServletException(String message, Throwable rootCause) {
+	super(message);
+	this.rootCause = rootCause;
+    }
+
+
+
+
+
+    /**
+     * Constructs a new servlet exception when the servlet 
+     * needs to throw an exception and include a message
+     * about the "root cause" exception that interfered with its
+     * normal operation.  The exception's message is based on the localized
+     * message of the underlying exception.
+     *
+     * <p>This method calls the <code>getLocalizedMessage</code> method
+     * on the <code>Throwable</code> exception to get a localized exception
+     * message. When subclassing <code>ServletException</code>, 
+     * this method can be overridden to create an exception message 
+     * designed for a specific locale.
+     *
+     * @param rootCause 	the <code>Throwable</code> exception
+     * 				that interfered with the servlet's
+     *				normal operation, making the servlet exception
+     *				necessary
+     *
+     */
+
+    public ServletException(Throwable rootCause) {
+	super(rootCause.getLocalizedMessage());
+	this.rootCause = rootCause;
+    }
+  
+  
+ 
+ 
+    
+    /**
+     * Returns the exception that caused this servlet exception.
+     *
+     *
+     * @return			the <code>Throwable</code> 
+     *				that caused this servlet exception
+     *
+     */
+    
+    public Throwable getRootCause() {
+	return rootCause;
+    }
+}
+
+
+
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletInputStream.java b/servletapi/jsr154/src/share/javax/servlet/ServletInputStream.java
new file mode 100644
index 0000000..bcc3a45
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletInputStream.java
@@ -0,0 +1,105 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * 
+ * Provides an input stream for reading binary data from a client
+ * request, including an efficient <code>readLine</code> method
+ * for reading data one line at a time. With some protocols, such
+ * as HTTP POST and PUT, a <code>ServletInputStream</code>
+ * object can be used to read data sent from the client.
+ *
+ * <p>A <code>ServletInputStream</code> object is normally retrieved via
+ * the {@link ServletRequest#getInputStream} method.
+ *
+ *
+ * <p>This is an abstract class that a servlet container implements.
+ * Subclasses of this class
+ * must implement the <code>java.io.InputStream.read()</code> method.
+ *
+ *
+ * @author 	Various
+ * @version 	$Version$
+ *
+ * @see		ServletRequest 
+ *
+ */
+
+public abstract class ServletInputStream extends InputStream {
+
+
+
+    /**
+     * Does nothing, because this is an abstract class.
+     *
+     */
+
+    protected ServletInputStream() { }
+
+  
+  
+    
+    /**
+     *
+     * Reads the input stream, one line at a time. Starting at an
+     * offset, reads bytes into an array, until it reads a certain number
+     * of bytes or reaches a newline character, which it reads into the
+     * array as well.
+     *
+     * <p>This method returns -1 if it reaches the end of the input
+     * stream before reading the maximum number of bytes.
+     *
+     *
+     *
+     * @param b 		an array of bytes into which data is read
+     *
+     * @param off 		an integer specifying the character at which
+     *				this method begins reading
+     *
+     * @param len		an integer specifying the maximum number of 
+     *				bytes to read
+     *
+     * @return			an integer specifying the actual number of bytes 
+     *				read, or -1 if the end of the stream is reached
+     *
+     * @exception IOException	if an input or output exception has occurred
+     *
+     */
+     
+    public int readLine(byte[] b, int off, int len) throws IOException {
+
+	if (len <= 0) {
+	    return 0;
+	}
+	int count = 0, c;
+
+	while ((c = read()) != -1) {
+	    b[off++] = (byte)c;
+	    count++;
+	    if (c == '\n' || count == len) {
+		break;
+	    }
+	}
+	return count > 0 ? count : -1;
+    }
+}
+
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletOutputStream.java b/servletapi/jsr154/src/share/javax/servlet/ServletOutputStream.java
new file mode 100644
index 0000000..865b1d1
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletOutputStream.java
@@ -0,0 +1,363 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.CharConversionException;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+/**
+ * Provides an output stream for sending binary data to the
+ * client. A <code>ServletOutputStream</code> object is normally retrieved 
+ * via the {@link ServletResponse#getOutputStream} method.
+ *
+ * <p>This is an abstract class that the servlet container implements.
+ * Subclasses of this class
+ * must implement the <code>java.io.OutputStream.write(int)</code>
+ * method.
+ *
+ * 
+ * @author 	Various
+ * @version 	$Version$
+ *
+ * @see 	ServletResponse
+ *
+ */
+
+public abstract class ServletOutputStream extends OutputStream {
+
+    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
+    private static ResourceBundle lStrings =
+	ResourceBundle.getBundle(LSTRING_FILE);
+
+
+    
+    /**
+     *
+     * Does nothing, because this is an abstract class.
+     *
+     */
+
+    protected ServletOutputStream() { }
+
+
+    /**
+     * Writes a <code>String</code> to the client, 
+     * without a carriage return-line feed (CRLF) 
+     * character at the end.
+     *
+     *
+     * @param s			the <code>String</code> to send to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void print(String s) throws IOException {
+	if (s==null) s="null";
+	int len = s.length();
+	for (int i = 0; i < len; i++) {
+	    char c = s.charAt (i);
+
+	    //
+	    // XXX NOTE:  This is clearly incorrect for many strings,
+	    // but is the only consistent approach within the current
+	    // servlet framework.  It must suffice until servlet output
+	    // streams properly encode their output.
+	    //
+	    if ((c & 0xff00) != 0) {	// high order byte must be zero
+		String errMsg = lStrings.getString("err.not_iso8859_1");
+		Object[] errArgs = new Object[1];
+		errArgs[0] = new Character(c);
+		errMsg = MessageFormat.format(errMsg, errArgs);
+		throw new CharConversionException(errMsg);
+	    }
+	    write (c);
+	}
+    }
+
+
+
+    /**
+     * Writes a <code>boolean</code> value to the client,
+     * with no carriage return-line feed (CRLF) 
+     * character at the end.
+     *
+     * @param b			the <code>boolean</code> value 
+     *				to send to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void print(boolean b) throws IOException {
+	String msg;
+	if (b) {
+	    msg = lStrings.getString("value.true");
+	} else {
+	    msg = lStrings.getString("value.false");
+	}
+	print(msg);
+    }
+
+
+
+    /**
+     * Writes a character to the client,
+     * with no carriage return-line feed (CRLF) 
+     * at the end.
+     *
+     * @param c			the character to send to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void print(char c) throws IOException {
+	print(String.valueOf(c));
+    }
+
+
+
+
+    /**
+     *
+     * Writes an int to the client,
+     * with no carriage return-line feed (CRLF) 
+     * at the end.
+     *
+     * @param i			the int to send to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */  
+
+    public void print(int i) throws IOException {
+	print(String.valueOf(i));
+    }
+
+
+
+ 
+    /**
+     * 
+     * Writes a <code>long</code> value to the client,
+     * with no carriage return-line feed (CRLF) at the end.
+     *
+     * @param l			the <code>long</code> value 
+     *				to send to the client
+     *
+     * @exception IOException 	if an input or output exception 
+     *				occurred
+     * 
+     */
+
+    public void print(long l) throws IOException {
+	print(String.valueOf(l));
+    }
+
+
+
+    /**
+     *
+     * Writes a <code>float</code> value to the client,
+     * with no carriage return-line feed (CRLF) at the end.
+     *
+     * @param f			the <code>float</code> value
+     *				to send to the client
+     *
+     * @exception IOException	if an input or output exception occurred
+     *
+     *
+     */
+
+    public void print(float f) throws IOException {
+	print(String.valueOf(f));
+    }
+
+
+
+    /**
+     *
+     * Writes a <code>double</code> value to the client,
+     * with no carriage return-line feed (CRLF) at the end.
+     * 
+     * @param d			the <code>double</code> value
+     *				to send to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void print(double d) throws IOException {
+	print(String.valueOf(d));
+    }
+
+
+
+    /**
+     * Writes a carriage return-line feed (CRLF)
+     * to the client.
+     *
+     *
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void println() throws IOException {
+	print("\r\n");
+    }
+
+
+
+    /**
+     * Writes a <code>String</code> to the client, 
+     * followed by a carriage return-line feed (CRLF).
+     *
+     *
+     * @param s			the <code>String</code> to write to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void println(String s) throws IOException {
+	print(s);
+	println();
+    }
+
+
+
+
+    /**
+     *
+     * Writes a <code>boolean</code> value to the client, 
+     * followed by a 
+     * carriage return-line feed (CRLF).
+     *
+     *
+     * @param b			the <code>boolean</code> value 
+     *				to write to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void println(boolean b) throws IOException {
+	print(b);
+	println();
+    }
+
+
+
+    /**
+     *
+     * Writes a character to the client, followed by a carriage
+     * return-line feed (CRLF).
+     *
+     * @param c			the character to write to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void println(char c) throws IOException {
+	print(c);
+	println();
+    }
+
+
+
+    /**
+     *
+     * Writes an int to the client, followed by a 
+     * carriage return-line feed (CRLF) character.
+     *
+     *
+     * @param i			the int to write to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void println(int i) throws IOException {
+	print(i);
+	println();
+    }
+
+
+
+    /**  
+     *
+     * Writes a <code>long</code> value to the client, followed by a 
+     * carriage return-line feed (CRLF).
+     *
+     *
+     * @param l			the <code>long</code> value to write to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */  
+
+    public void println(long l) throws IOException {
+	print(l);
+	println();
+    }
+
+
+
+    /**
+     *
+     * Writes a <code>float</code> value to the client, 
+     * followed by a carriage return-line feed (CRLF).
+     *
+     * @param f			the <code>float</code> value 
+     *				to write to the client
+     *
+     *
+     * @exception IOException 	if an input or output exception 
+     *				occurred
+     *
+     */
+
+    public void println(float f) throws IOException {
+	print(f);
+	println();
+    }
+
+
+
+    /**
+     *
+     * Writes a <code>double</code> value to the client, 
+     * followed by a carriage return-line feed (CRLF).
+     *
+     *
+     * @param d			the <code>double</code> value
+     *				to write to the client
+     *
+     * @exception IOException 	if an input or output exception occurred
+     *
+     */
+
+    public void println(double d) throws IOException {
+	print(d);
+	println();
+    }
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletRequest.java b/servletapi/jsr154/src/share/javax/servlet/ServletRequest.java
new file mode 100644
index 0000000..a2cf60d
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletRequest.java
@@ -0,0 +1,597 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+
+
+/**
+ * Defines an object to provide client request information to a servlet.  The
+ * servlet container creates a <code>ServletRequest</code> object and passes
+ * it as an argument to the servlet's <code>service</code> method.
+ *
+ * <p>A <code>ServletRequest</code> object provides data including
+ * parameter name and values, attributes, and an input stream.
+ * Interfaces that extend <code>ServletRequest</code> can provide
+ * additional protocol-specific data (for example, HTTP data is
+ * provided by {@link javax.servlet.http.HttpServletRequest}.
+ * 
+ * @author 	Various
+ * @version 	$Version$
+ *
+ * @see 	javax.servlet.http.HttpServletRequest
+ *
+ */
+
+public interface ServletRequest {
+
+
+
+
+    /**
+     *
+     * Returns the value of the named attribute as an <code>Object</code>,
+     * or <code>null</code> if no attribute of the given name exists. 
+     *
+     * <p> Attributes can be set two ways.  The servlet container may set
+     * attributes to make available custom information about a request.
+     * For example, for requests made using HTTPS, the attribute
+     * <code>javax.servlet.request.X509Certificate</code> can be used to
+     * retrieve information on the certificate of the client.  Attributes
+     * can also be set programatically using 
+     * {@link ServletRequest#setAttribute}.  This allows information to be
+     * embedded into a request before a {@link RequestDispatcher} call.
+     *
+     * <p>Attribute names should follow the same conventions as package
+     * names. This specification reserves names matching <code>java.*</code>,
+     * <code>javax.*</code>, and <code>sun.*</code>. 
+     *
+     * @param name	a <code>String</code> specifying the name of 
+     *			the attribute
+     *
+     * @return		an <code>Object</code> containing the value 
+     *			of the attribute, or <code>null</code> if
+     *			the attribute does not exist
+     *
+     */
+
+    public Object getAttribute(String name);
+    
+    
+
+    /**
+     * Returns an <code>Enumeration</code> containing the
+     * names of the attributes available to this request. 
+     * This method returns an empty <code>Enumeration</code>
+     * if the request has no attributes available to it.
+     * 
+     *
+     * @return		an <code>Enumeration</code> of strings 
+     *			containing the names 
+     * 			of the request's attributes
+     *
+     */
+
+    public Enumeration getAttributeNames();
+    
+    
+    
+    
+    /**
+     * Returns the name of the character encoding used in the body of this
+     * request. This method returns <code>null</code> if the request
+     * does not specify a character encoding
+     * 
+     *
+     * @return		a <code>String</code> containing the name of 
+     *			the character encoding, or <code>null</code>
+     *			if the request does not specify a character encoding
+     *
+     */
+
+    public String getCharacterEncoding();
+
+ /**
+     * Overrides the name of the character encoding used in the body of this
+     * request. This method must be called prior to reading request parameters
+     * or reading input using getReader().
+     * 
+     *
+     * @param env	a <code>String</code> containing the name of 
+     *			the character encoding.
+     * @throws		java.io.UnsupportedEncodingException if this is not a valid encoding
+     */
+
+    public void setCharacterEncoding(String env) throws java.io.UnsupportedEncodingException;
+
+    
+    
+    
+    
+    /**
+     * Returns the length, in bytes, of the request body 
+     * and made available by the input stream, or -1 if the
+     * length is not known. For HTTP servlets, same as the value
+     * of the CGI variable CONTENT_LENGTH.
+     *
+     * @return		an integer containing the length of the 
+     * 			request body or -1 if the length is not known
+     *
+     */
+
+    public int getContentLength();
+    
+    
+    
+
+    /**
+     * Returns the MIME type of the body of the request, or 
+     * <code>null</code> if the type is not known. For HTTP servlets, 
+     * same as the value of the CGI variable CONTENT_TYPE.
+     *
+     * @return		a <code>String</code> containing the name 
+     *			of the MIME type of 
+     * 			the request, or null if the type is not known
+     *
+     */
+
+    public String getContentType();
+    
+    
+    
+
+    /**
+     * Retrieves the body of the request as binary data using
+     * a {@link ServletInputStream}.  Either this method or 
+     * {@link #getReader} may be called to read the body, not both.
+     *
+     * @return			a {@link ServletInputStream} object containing
+     * 				the body of the request
+     *
+     * @exception IllegalStateException  if the {@link #getReader} method
+     * 					 has already been called for this request
+     *
+     * @exception IOException    	if an input or output exception occurred
+     *
+     */
+
+    public ServletInputStream getInputStream() throws IOException; 
+     
+    
+    
+
+    /**
+     * Returns the value of a request parameter as a <code>String</code>,
+     * or <code>null</code> if the parameter does not exist. Request parameters
+     * are extra information sent with the request.  For HTTP servlets,
+     * parameters are contained in the query string or posted form data.
+     *
+     * <p>You should only use this method when you are sure the
+     * parameter has only one value. If the parameter might have
+     * more than one value, use {@link #getParameterValues}.
+     *
+     * <p>If you use this method with a multivalued
+     * parameter, the value returned is equal to the first value
+     * in the array returned by <code>getParameterValues</code>.
+     *
+     * <p>If the parameter data was sent in the request body, such as occurs
+     * with an HTTP POST request, then reading the body directly via {@link
+     * #getInputStream} or {@link #getReader} can interfere
+     * with the execution of this method.
+     *
+     * @param name 	a <code>String</code> specifying the 
+     *			name of the parameter
+     *
+     * @return		a <code>String</code> representing the 
+     *			single value of the parameter
+     *
+     * @see 		#getParameterValues
+     *
+     */
+
+    public String getParameter(String name);
+    
+    
+    
+
+    /**
+     *
+     * Returns an <code>Enumeration</code> of <code>String</code>
+     * objects containing the names of the parameters contained
+     * in this request. If the request has 
+     * no parameters, the method returns an 
+     * empty <code>Enumeration</code>. 
+     *
+     * @return		an <code>Enumeration</code> of <code>String</code>
+     *			objects, each <code>String</code> containing
+     * 			the name of a request parameter; or an 
+     *			empty <code>Enumeration</code> if the
+     *			request has no parameters
+     *
+     */
+     
+    public Enumeration getParameterNames();
+    
+    
+    
+
+    /**
+     * Returns an array of <code>String</code> objects containing 
+     * all of the values the given request parameter has, or 
+     * <code>null</code> if the parameter does not exist.
+     *
+     * <p>If the parameter has a single value, the array has a length
+     * of 1.
+     *
+     * @param name	a <code>String</code> containing the name of 
+     *			the parameter whose value is requested
+     *
+     * @return		an array of <code>String</code> objects 
+     *			containing the parameter's values
+     *
+     * @see		#getParameter
+     *
+     */
+
+    public String[] getParameterValues(String name);
+ 
+    /** Returns a java.util.Map of the parameters of this request.
+     * Request parameters
+     * are extra information sent with the request.  For HTTP servlets,
+     * parameters are contained in the query string or posted form data.
+     *
+     * @return an immutable java.util.Map containing parameter names as 
+     * keys and parameter values as map values. The keys in the parameter
+     * map are of type String. The values in the parameter map are of type
+     * String array.
+     *
+     */
+
+    public Map getParameterMap();
+    
+    
+
+    /**
+     * Returns the name and version of the protocol the request uses
+     * in the form <i>protocol/majorVersion.minorVersion</i>, for 
+     * example, HTTP/1.1. For HTTP servlets, the value
+     * returned is the same as the value of the CGI variable 
+     * <code>SERVER_PROTOCOL</code>.
+     *
+     * @return		a <code>String</code> containing the protocol 
+     *			name and version number
+     *
+     */
+    
+    public String getProtocol();
+    
+    
+    
+
+    /**
+     * Returns the name of the scheme used to make this request, 
+     * for example,
+     * <code>http</code>, <code>https</code>, or <code>ftp</code>.
+     * Different schemes have different rules for constructing URLs,
+     * as noted in RFC 1738.
+     *
+     * @return		a <code>String</code> containing the name 
+     *			of the scheme used to make this request
+     *
+     */
+
+    public String getScheme();
+    
+    
+    
+
+    /**
+     * Returns the host name of the server to which the request was sent.
+     * It is the value of the part before ":" in the <code>Host</code>
+     * header value, if any, or the resolved server name, or the server IP address.
+     *
+     * @return		a <code>String</code> containing the name 
+     *			of the server
+     */
+
+    public String getServerName();
+    
+    
+    
+
+    /**
+     * Returns the port number to which the request was sent.
+     * It is the value of the part after ":" in the <code>Host</code>
+     * header value, if any, or the server port where the client connection
+     * was accepted on.
+     *
+     * @return		an integer specifying the port number
+     *
+     */
+
+    public int getServerPort();
+    
+    
+    
+    /**
+     * Retrieves the body of the request as character data using
+     * a <code>BufferedReader</code>.  The reader translates the character
+     * data according to the character encoding used on the body.
+     * Either this method or {@link #getInputStream} may be called to read the
+     * body, not both.
+     * 
+     *
+     * @return					a <code>BufferedReader</code>
+     *						containing the body of the request	
+     *
+     * @exception UnsupportedEncodingException 	if the character set encoding
+     * 						used is not supported and the 
+     *						text cannot be decoded
+     *
+     * @exception IllegalStateException   	if {@link #getInputStream} method
+     * 						has been called on this request
+     *
+     * @exception IOException  			if an input or output exception occurred
+     *
+     * @see 					#getInputStream
+     *
+     */
+
+    public BufferedReader getReader() throws IOException;
+    
+    
+    
+
+    /**
+     * Returns the Internet Protocol (IP) address of the client 
+     * or last proxy that sent the request.
+     * For HTTP servlets, same as the value of the 
+     * CGI variable <code>REMOTE_ADDR</code>.
+     *
+     * @return		a <code>String</code> containing the 
+     *			IP address of the client that sent the request
+     *
+     */
+    
+    public String getRemoteAddr();
+    
+    
+    
+
+    /**
+     * Returns the fully qualified name of the client
+     * or the last proxy that sent the request.
+     * If the engine cannot or chooses not to resolve the hostname 
+     * (to improve performance), this method returns the dotted-string form of 
+     * the IP address. For HTTP servlets, same as the value of the CGI variable 
+     * <code>REMOTE_HOST</code>.
+     *
+     * @return		a <code>String</code> containing the fully 
+     *			qualified name of the client
+     *
+     */
+
+    public String getRemoteHost();
+    
+    
+    
+
+    /**
+     *
+     * Stores an attribute in this request.
+     * Attributes are reset between requests.  This method is most
+     * often used in conjunction with {@link RequestDispatcher}.
+     *
+     * <p>Attribute names should follow the same conventions as
+     * package names. Names beginning with <code>java.*</code>,
+     * <code>javax.*</code>, and <code>com.sun.*</code>, are
+     * reserved for use by Sun Microsystems.
+     *<br> If the object passed in is null, the effect is the same as
+     * calling {@link #removeAttribute}.
+     * <br> It is warned that when the request is dispatched from the
+     * servlet resides in a different web application by
+     * <code>RequestDispatcher</code>, the object set by this method
+     * may not be correctly retrieved in the caller servlet.
+     *
+     *
+     * @param name			a <code>String</code> specifying 
+     *					the name of the attribute
+     *
+     * @param o				the <code>Object</code> to be stored
+     *
+     */
+
+    public void setAttribute(String name, Object o);
+    
+    
+    
+
+    /**
+     *
+     * Removes an attribute from this request.  This method is not
+     * generally needed as attributes only persist as long as the request
+     * is being handled.
+     *
+     * <p>Attribute names should follow the same conventions as
+     * package names. Names beginning with <code>java.*</code>,
+     * <code>javax.*</code>, and <code>com.sun.*</code>, are
+     * reserved for use by Sun Microsystems.
+     *
+     *
+     * @param name			a <code>String</code> specifying 
+     *					the name of the attribute to remove
+     *
+     */
+
+    public void removeAttribute(String name);
+    
+    
+    
+
+    /**
+     *
+     * Returns the preferred <code>Locale</code> that the client will 
+     * accept content in, based on the Accept-Language header.
+     * If the client request doesn't provide an Accept-Language header,
+     * this method returns the default locale for the server.
+     *
+     *
+     * @return		the preferred <code>Locale</code> for the client
+     *
+     */
+
+    public Locale getLocale();
+    
+    
+    
+
+    /**
+     *
+     * Returns an <code>Enumeration</code> of <code>Locale</code> objects
+     * indicating, in decreasing order starting with the preferred locale, the
+     * locales that are acceptable to the client based on the Accept-Language
+     * header.
+     * If the client request doesn't provide an Accept-Language header,
+     * this method returns an <code>Enumeration</code> containing one 
+     * <code>Locale</code>, the default locale for the server.
+     *
+     *
+     * @return		an <code>Enumeration</code> of preferred 
+     *                  <code>Locale</code> objects for the client
+     *
+     */
+
+    public Enumeration getLocales();
+    
+    
+    
+
+    /**
+     *
+     * Returns a boolean indicating whether this request was made using a
+     * secure channel, such as HTTPS.
+     *
+     *
+     * @return		a boolean indicating if the request was made using a
+     *                  secure channel
+     *
+     */
+
+    public boolean isSecure();
+    
+    
+    
+
+    /**
+     *
+     * Returns a {@link RequestDispatcher} object that acts as a wrapper for
+     * the resource located at the given path.  
+     * A <code>RequestDispatcher</code> object can be used to forward
+     * a request to the resource or to include the resource in a response.
+     * The resource can be dynamic or static.
+     *
+     * <p>The pathname specified may be relative, although it cannot extend
+     * outside the current servlet context.  If the path begins with 
+     * a "/" it is interpreted as relative to the current context root.  
+     * This method returns <code>null</code> if the servlet container
+     * cannot return a <code>RequestDispatcher</code>.
+     *
+     * <p>The difference between this method and {@link
+     * ServletContext#getRequestDispatcher} is that this method can take a
+     * relative path.
+     *
+     * @param path      a <code>String</code> specifying the pathname
+     *                  to the resource. If it is relative, it must be
+     *                  relative against the current servlet.
+     *
+     * @return          a <code>RequestDispatcher</code> object
+     *                  that acts as a wrapper for the resource
+     *                  at the specified path, or <code>null</code>
+     *                  if the servlet container cannot return a
+     *                  <code>RequestDispatcher</code>
+     *
+     * @see             RequestDispatcher
+     * @see             ServletContext#getRequestDispatcher
+     *
+     */
+
+    public RequestDispatcher getRequestDispatcher(String path);
+    
+    
+    
+
+    /**
+     * 
+     * @deprecated 	As of Version 2.1 of the Java Servlet API,
+     * 			use {@link ServletContext#getRealPath} instead.
+     *
+     */
+
+    public String getRealPath(String path);
+    
+    
+    /**
+     * Returns the Internet Protocol (IP) source port of the client
+     * or last proxy that sent the request.
+     *
+     * @return	an integer specifying the port number
+     *
+     * @since 2.4
+     */    
+    public int getRemotePort();
+
+
+    /**
+     * Returns the host name of the Internet Protocol (IP) interface on
+     * which the request was received.
+     *
+     * @return	a <code>String</code> containing the host
+     *		name of the IP on which the request was received.
+     *
+     * @since 2.4
+     */
+    public String getLocalName();
+
+    /**
+     * Returns the Internet Protocol (IP) address of the interface on
+     * which the request  was received.
+     *
+     * @return	a <code>String</code> containing the
+     *		IP address on which the request was received. 
+     *
+     * @since 2.4
+     *
+     */       
+    public String getLocalAddr();
+
+
+    /**
+     * Returns the Internet Protocol (IP) port number of the interface
+     * on which the request was received.
+     *
+     * @return an integer specifying the port number
+     *
+     * @since 2.4
+     */
+    public int getLocalPort();
+
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletRequestAttributeEvent.java b/servletapi/jsr154/src/share/javax/servlet/ServletRequestAttributeEvent.java
new file mode 100644
index 0000000..8ca9eb4
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletRequestAttributeEvent.java
@@ -0,0 +1,66 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+
+    /** 
+      * This is the event class for notifications of changes to the 
+      * attributes of the servlet request in an application.
+      * @see ServletRequestAttributeListener
+      * @since	Servlet 2.4
+      */
+
+public class ServletRequestAttributeEvent extends ServletRequestEvent { 
+    private String name;
+    private Object value;
+
+     /** Construct a ServletRequestAttributeEvent giving the servlet context
+      * of this web application, the ServletRequest whose attributes are
+      * changing and the name and value of the attribute.
+      *
+      * @param sc		the ServletContext that is sending the event.
+      * @param request		the ServletRequest that is sending the event.
+      * @param name		the name of the request attribute.
+      * @param value		the value of the request attribute.
+      */
+    public ServletRequestAttributeEvent(ServletContext sc, ServletRequest request, String name, Object value) {
+        super(sc, request);
+        this.name = name;
+        this.value = value;
+    }
+
+    /**
+      * Return the name of the attribute that changed on the ServletRequest.
+      *
+      * @return		the name of the changed request attribute
+      */
+    public String getName() {
+        return this.name;
+    }
+
+    /**
+      * Returns the value of the attribute that has been added, removed or 
+      * replaced. If the attribute was added, this is the value of the 
+      * attribute. If the attribute was removed, this is the value of the 
+      * removed attribute. If the attribute was replaced, this is the old 
+      * value of the attribute.
+      *
+      * @return		the value of the changed request attribute
+      */
+    public Object getValue() {
+        return this.value;   
+    }
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletRequestAttributeListener.java b/servletapi/jsr154/src/share/javax/servlet/ServletRequestAttributeListener.java
new file mode 100644
index 0000000..b68bbc5
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletRequestAttributeListener.java
@@ -0,0 +1,49 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.util.EventListener;
+
+    /**
+     * A ServletRequestAttributeListener can be implemented by the
+     * developer interested in being notified of request attribute
+     * changes. Notifications will be generated while the request
+     * is within the scope of the web application in which the listener
+     * is registered. A request is defined as coming into scope when
+     * it is about to enter the first servlet or filter in each web
+     * application, as going out of scope when it exits the last servlet
+     * or the first filter in the chain.
+     *
+     * @since Servlet 2.4
+     */
+
+public interface ServletRequestAttributeListener extends EventListener {
+    /** Notification that a new attribute was added to the
+     ** servlet request. Called after the attribute is added.
+     */
+    public void attributeAdded(ServletRequestAttributeEvent srae);
+
+    /** Notification that an existing attribute has been removed from the
+     ** servlet request. Called after the attribute is removed.
+     */
+    public void attributeRemoved(ServletRequestAttributeEvent srae);
+
+    /** Notification that an attribute was replaced on the
+     ** servlet request. Called after the attribute is replaced.
+     */
+    public void attributeReplaced(ServletRequestAttributeEvent srae);
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletRequestEvent.java b/servletapi/jsr154/src/share/javax/servlet/ServletRequestEvent.java
new file mode 100644
index 0000000..a75b213
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletRequestEvent.java
@@ -0,0 +1,55 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+
+    /** 
+      * Events of this kind indicate lifecycle
+      * events for a ServletRequest.
+      * The source of the event
+      * is the ServletContext of this web application.
+      * @see ServletRequestListener
+      * @since	Servlet 2.4
+      */
+
+public class ServletRequestEvent extends java.util.EventObject { 
+    private ServletRequest request;
+
+    /** Construct a ServletRequestEvent for the given ServletContext
+      * and ServletRequest.
+      *
+      * @param sc		the ServletContext of the web application.
+      * @param request		the ServletRequest that is sending the event.
+      */
+    public ServletRequestEvent(ServletContext sc, ServletRequest request) {
+        super(sc);
+        this.request = request;
+    }
+    
+    /**
+      * Returns the ServletRequest that is changing.
+      */
+    public ServletRequest getServletRequest () { 
+        return this.request;
+    }
+
+    /**
+      * Returns the ServletContext of this web application.
+      */
+    public ServletContext getServletContext () { 
+        return (ServletContext) super.getSource();
+    }
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletRequestListener.java b/servletapi/jsr154/src/share/javax/servlet/ServletRequestListener.java
new file mode 100644
index 0000000..3518b19
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletRequestListener.java
@@ -0,0 +1,39 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.util.EventListener;
+
+    /**
+     * A ServletRequestListener can be implemented by the developer
+     * interested in being notified of requests coming in and out of
+     * scope in a web component. A request is defined as coming into
+     * scope when it is about to enter the first servlet or filter
+     * in each web application, as going out of scope when it exits
+     * the last servlet or the first filter in the chain.
+     *
+     * @since Servlet 2.4
+     */
+
+
+public interface ServletRequestListener extends EventListener {
+
+    /** The request is about to go out of scope of the web application. */
+    public void requestDestroyed ( ServletRequestEvent sre );
+
+    /** The request is about to come into scope of the web application. */
+    public void requestInitialized ( ServletRequestEvent sre );
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletRequestWrapper.java b/servletapi/jsr154/src/share/javax/servlet/ServletRequestWrapper.java
new file mode 100644
index 0000000..d5f232e
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletRequestWrapper.java
@@ -0,0 +1,400 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+
+
+/**
+ * 
+ * Provides a convenient implementation of the ServletRequest interface that
+ * can be subclassed by developers wishing to adapt the request to a Servlet.
+ * This class implements the Wrapper or Decorator pattern. Methods default to
+ * calling through to the wrapped request object.
+  * @since	v 2.3
+ * 
+ * 
+ *
+ * @see 	javax.servlet.ServletRequest
+ *
+ */
+
+public class ServletRequestWrapper implements ServletRequest {
+    private ServletRequest request;
+
+	/**
+	* Creates a ServletRequest adaptor wrapping the given request object. 
+	* @throws java.lang.IllegalArgumentException if the request is null
+	*/
+
+    public ServletRequestWrapper(ServletRequest request) {
+	if (request == null) {
+	    throw new IllegalArgumentException("Request cannot be null");   
+	}
+	this.request = request;
+    }
+
+	/**
+	* Return the wrapped request object.
+	*/
+	public ServletRequest getRequest() {
+		return this.request;
+	}
+	
+	/**
+	* Sets the request object being wrapped. 
+	* @throws java.lang.IllegalArgumentException if the request is null.
+	*/
+	
+	public void setRequest(ServletRequest request) {
+	    if (request == null) {
+		throw new IllegalArgumentException("Request cannot be null");
+	    }
+	    this.request = request;
+	}
+
+    /**
+     *
+     * The default behavior of this method is to call getAttribute(String name)
+     * on the wrapped request object.
+     */
+
+    public Object getAttribute(String name) {
+	return this.request.getAttribute(name);
+	}
+    
+    
+
+    /**
+     * The default behavior of this method is to return getAttributeNames()
+     * on the wrapped request object.
+     */
+
+    public Enumeration getAttributeNames() {
+	return this.request.getAttributeNames();
+	}    
+    
+    
+    
+    /**
+      * The default behavior of this method is to return getCharacterEncoding()
+     * on the wrapped request object.
+     */
+
+    public String getCharacterEncoding() {
+	return this.request.getCharacterEncoding();
+	}
+	
+    /**
+      * The default behavior of this method is to set the character encoding
+     * on the wrapped request object.
+     */
+
+    public void setCharacterEncoding(String enc) throws java.io.UnsupportedEncodingException {
+	this.request.setCharacterEncoding(enc);
+	}
+    
+    
+    /**
+      * The default behavior of this method is to return getContentLength()
+     * on the wrapped request object.
+     */
+
+    public int getContentLength() {
+	return this.request.getContentLength();
+    }
+    
+    
+    
+
+       /**
+      * The default behavior of this method is to return getContentType()
+     * on the wrapped request object.
+     */
+    public String getContentType() {
+	return this.request.getContentType();
+    }
+    
+    
+    
+
+     /**
+      * The default behavior of this method is to return getInputStream()
+     * on the wrapped request object.
+     */
+
+    public ServletInputStream getInputStream() throws IOException {
+	return this.request.getInputStream();
+	}
+     
+    
+    
+
+    /**
+      * The default behavior of this method is to return getParameter(String name)
+     * on the wrapped request object.
+     */
+
+    public String getParameter(String name) {
+	return this.request.getParameter(name);
+    }
+    
+    /**
+      * The default behavior of this method is to return getParameterMap()
+     * on the wrapped request object.
+     */
+    public Map getParameterMap() {
+	return this.request.getParameterMap();
+    }
+    
+    
+    
+
+    /**
+      * The default behavior of this method is to return getParameterNames()
+     * on the wrapped request object.
+     */
+     
+    public Enumeration getParameterNames() {
+	return this.request.getParameterNames();
+    }
+    
+    
+    
+
+       /**
+      * The default behavior of this method is to return getParameterValues(String name)
+     * on the wrapped request object.
+     */
+    public String[] getParameterValues(String name) {
+	return this.request.getParameterValues(name);
+	}
+    
+    
+    
+
+     /**
+      * The default behavior of this method is to return getProtocol()
+     * on the wrapped request object.
+     */
+    
+    public String getProtocol() {
+	return this.request.getProtocol();
+	}
+    
+    
+    
+
+    /**
+      * The default behavior of this method is to return getScheme()
+     * on the wrapped request object.
+     */
+    
+
+    public String getScheme() {
+	return this.request.getScheme();
+	}
+    
+    
+    
+
+    /**
+      * The default behavior of this method is to return getServerName()
+     * on the wrapped request object.
+     */
+    public String getServerName() {
+	return this.request.getServerName();
+	}
+    
+    
+    
+
+   /**
+      * The default behavior of this method is to return getServerPort()
+     * on the wrapped request object.
+     */
+
+    public int getServerPort() {
+	return this.request.getServerPort();
+	}
+    
+    
+    
+  /**
+      * The default behavior of this method is to return getReader()
+     * on the wrapped request object.
+     */
+
+    public BufferedReader getReader() throws IOException {
+	return this.request.getReader();
+	}
+    
+    
+    
+
+    /**
+      * The default behavior of this method is to return getRemoteAddr()
+     * on the wrapped request object.
+     */
+    
+    public String getRemoteAddr() {
+	return this.request.getRemoteAddr();
+    }
+    
+    
+    
+
+      /**
+      * The default behavior of this method is to return getRemoteHost()
+     * on the wrapped request object.
+     */
+
+    public String getRemoteHost() {
+	return this.request.getRemoteHost();
+    }
+    
+    
+    
+
+    /**
+      * The default behavior of this method is to return setAttribute(String name, Object o)
+     * on the wrapped request object.
+     */
+
+    public void setAttribute(String name, Object o) {
+	this.request.setAttribute(name, o);
+    }
+    
+    
+    
+
+    /**
+      * The default behavior of this method is to call removeAttribute(String name)
+     * on the wrapped request object.
+     */
+    public void removeAttribute(String name) {
+	this.request.removeAttribute(name);
+    }
+    
+    
+    
+
+   /**
+      * The default behavior of this method is to return getLocale()
+     * on the wrapped request object.
+     */
+
+    public Locale getLocale() {
+	return this.request.getLocale();
+    }
+    
+    
+    
+
+     /**
+      * The default behavior of this method is to return getLocales()
+     * on the wrapped request object.
+     */
+
+    public Enumeration getLocales() {
+	return this.request.getLocales();
+    }
+    
+    
+    
+
+    /**
+      * The default behavior of this method is to return isSecure()
+     * on the wrapped request object.
+     */
+
+    public boolean isSecure() {
+	return this.request.isSecure();
+    }
+    
+    
+    
+
+    /**
+      * The default behavior of this method is to return getRequestDispatcher(String path)
+     * on the wrapped request object.
+     */
+
+    public RequestDispatcher getRequestDispatcher(String path) {
+	return this.request.getRequestDispatcher(path);
+    }
+    
+    
+    
+
+    /**
+      * The default behavior of this method is to return getRealPath(String path)
+     * on the wrapped request object.
+     */
+
+    public String getRealPath(String path) {
+	return this.request.getRealPath(path);
+    }
+    
+    /**
+     * The default behavior of this method is to return
+     * getRemotePort() on the wrapped request object.
+     *
+     * @since 2.4
+     */    
+    public int getRemotePort(){
+        return this.request.getRemotePort();
+    }
+
+
+    /**
+     * The default behavior of this method is to return
+     * getLocalName() on the wrapped request object.
+     *
+     * @since 2.4
+     */
+    public String getLocalName(){
+        return this.request.getLocalName();
+    }
+
+    /**
+     * The default behavior of this method is to return
+     * getLocalAddr() on the wrapped request object.
+     *
+     * @since 2.4
+     */       
+    public String getLocalAddr(){
+        return this.request.getLocalAddr();
+    }
+
+
+    /**
+     * The default behavior of this method is to return
+     * getLocalPort() on the wrapped request object.
+     *
+     * @since 2.4
+     */
+    public int getLocalPort(){
+        return this.request.getLocalPort();
+    }
+    
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletResponse.java b/servletapi/jsr154/src/share/javax/servlet/ServletResponse.java
new file mode 100644
index 0000000..770d9a0
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletResponse.java
@@ -0,0 +1,452 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+
+/**
+ * Defines an object to assist a servlet in sending a response to the client.
+ * The servlet container creates a <code>ServletResponse</code> object and
+ * passes it as an argument to the servlet's <code>service</code> method.
+ *
+ * <p>To send binary data in a MIME body response, use
+ * the {@link ServletOutputStream} returned by {@link #getOutputStream}.
+ * To send character data, use the <code>PrintWriter</code> object 
+ * returned by {@link #getWriter}. To mix binary and text data,
+ * for example, to create a multipart response, use a
+ * <code>ServletOutputStream</code> and manage the character sections
+ * manually.
+ *
+ * <p>The charset for the MIME body response can be specified
+ * explicitly using the {@link #setCharacterEncoding} and
+ * {@link #setContentType} methods, or implicitly
+ * using the {@link #setLocale} method.
+ * Explicit specifications take precedence over
+ * implicit specifications. If no charset is specified, ISO-8859-1 will be
+ * used. The <code>setCharacterEncoding</code>,
+ * <code>setContentType</code>, or <code>setLocale</code> method must
+ * be called before <code>getWriter</code> and before committing
+ * the response for the character encoding to be used.
+ * 
+ * <p>See the Internet RFCs such as 
+ * <a href="http://www.ietf.org/rfc/rfc2045.txt">
+ * RFC 2045</a> for more information on MIME. Protocols such as SMTP
+ * and HTTP define profiles of MIME, and those standards
+ * are still evolving.
+ *
+ * @author 	Various
+ * @version 	$Version$
+ *
+ * @see		ServletOutputStream
+ *
+ */
+ 
+public interface ServletResponse {
+
+
+    
+    /**
+     * Returns the name of the character encoding (MIME charset)
+     * used for the body sent in this response.
+     * The character encoding may have been specified explicitly
+     * using the {@link #setCharacterEncoding} or
+     * {@link #setContentType} methods, or implicitly using the
+     * {@link #setLocale} method. Explicit specifications take
+     * precedence over implicit specifications. Calls made
+     * to these methods after <code>getWriter</code> has been
+     * called or after the response has been committed have no
+     * effect on the character encoding. If no character encoding
+     * has been specified, <code>ISO-8859-1</code> is returned.
+     * <p>See RFC 2047 (http://www.ietf.org/rfc/rfc2047.txt)
+     * for more information about character encoding and MIME.
+     *
+     * @return		a <code>String</code> specifying the
+     *			name of the character encoding, for
+     *			example, <code>UTF-8</code>
+     *
+     */
+  
+    public String getCharacterEncoding();
+    
+    
+
+    /**
+     * Returns the content type used for the MIME body
+     * sent in this response. The content type proper must
+     * have been specified using {@link #setContentType}
+     * before the response is committed. If no content type
+     * has been specified, this method returns null.
+     * If a content type has been specified and a
+     * character encoding has been explicitly or implicitly
+     * specified as described in {@link #getCharacterEncoding},
+     * the charset parameter is included in the string returned.
+     * If no character encoding has been specified, the
+     * charset parameter is omitted.
+     *
+     * @return		a <code>String</code> specifying the
+     *			content type, for example,
+     *			<code>text/html; charset=UTF-8</code>,
+     *			or null
+     *
+     * @since 2.4
+     */
+  
+    public String getContentType();
+    
+    
+
+    /**
+     * Returns a {@link ServletOutputStream} suitable for writing binary 
+     * data in the response. The servlet container does not encode the
+     * binary data.  
+     
+     * <p> Calling flush() on the ServletOutputStream commits the response.
+     
+     * Either this method or {@link #getWriter} may 
+     * be called to write the body, not both.
+     *
+     * @return				a {@link ServletOutputStream} for writing binary data	
+     *
+     * @exception IllegalStateException if the <code>getWriter</code> method
+     * 					has been called on this response
+     *
+     * @exception IOException 		if an input or output exception occurred
+     *
+     * @see 				#getWriter
+     *
+     */
+
+    public ServletOutputStream getOutputStream() throws IOException;
+    
+    
+
+    /**
+     * Returns a <code>PrintWriter</code> object that
+     * can send character text to the client.
+     * The <code>PrintWriter</code> uses the character
+     * encoding returned by {@link #getCharacterEncoding}.
+     * If the response's character encoding has not been
+     * specified as described in <code>getCharacterEncoding</code>
+     * (i.e., the method just returns the default value 
+     * <code>ISO-8859-1</code>), <code>getWriter</code>
+     * updates it to <code>ISO-8859-1</code>.
+     * <p>Calling flush() on the <code>PrintWriter</code>
+     * commits the response.
+     * <p>Either this method or {@link #getOutputStream} may be called
+     * to write the body, not both.
+     *
+     * 
+     * @return 		a <code>PrintWriter</code> object that 
+     *			can return character data to the client 
+     *
+     * @exception UnsupportedEncodingException
+     *			if the character encoding returned
+     *			by <code>getCharacterEncoding</code> cannot be used
+     *
+     * @exception IllegalStateException
+     *			if the <code>getOutputStream</code>
+     * 			method has already been called for this 
+     *			response object
+     *
+     * @exception IOException
+     *			if an input or output exception occurred
+     *
+     * @see 		#getOutputStream
+     * @see 		#setCharacterEncoding
+     *
+     */
+
+    public PrintWriter getWriter() throws IOException;
+    
+    
+    
+    
+    /**
+     * Sets the character encoding (MIME charset) of the response
+     * being sent to the client, for example, to UTF-8.
+     * If the character encoding has already been set by
+     * {@link #setContentType} or {@link #setLocale},
+     * this method overrides it.
+     * Calling {@link #setContentType} with the <code>String</code>
+     * of <code>text/html</code> and calling
+     * this method with the <code>String</code> of <code>UTF-8</code>
+     * is equivalent with calling
+     * <code>setContentType</code> with the <code>String</code> of
+     * <code>text/html; charset=UTF-8</code>.
+     * <p>This method can be called repeatedly to change the character
+     * encoding.
+     * This method has no effect if it is called after
+     * <code>getWriter</code> has been
+     * called or after the response has been committed.
+     * <p>Containers must communicate the character encoding used for
+     * the servlet response's writer to the client if the protocol
+     * provides a way for doing so. In the case of HTTP, the character
+     * encoding is communicated as part of the <code>Content-Type</code>
+     * header for text media types. Note that the character encoding
+     * cannot be communicated via HTTP headers if the servlet does not
+     * specify a content type; however, it is still used to encode text
+     * written via the servlet response's writer.
+     *
+     * @param charset 	a String specifying only the character set
+     * 			defined by IANA Character Sets
+     *			(http://www.iana.org/assignments/character-sets)
+     *
+     * @see		#setContentType
+     * 			#setLocale
+     *
+     * @since 2.4
+     *
+     */
+
+    public void setCharacterEncoding(String charset);
+    
+    
+
+
+    /**
+     * Sets the length of the content body in the response
+     * In HTTP servlets, this method sets the HTTP Content-Length header.
+     *
+     *
+     * @param len 	an integer specifying the length of the 
+     * 			content being returned to the client; sets
+     *			the Content-Length header
+     *
+     */
+
+    public void setContentLength(int len);
+    
+    
+
+    /**
+     * Sets the content type of the response being sent to
+     * the client, if the response has not been committed yet.
+     * The given content type may include a character encoding
+     * specification, for example, <code>text/html;charset=UTF-8</code>.
+     * The response's character encoding is only set from the given
+     * content type if this method is called before <code>getWriter</code>
+     * is called.
+     * <p>This method may be called repeatedly to change content type and
+     * character encoding.
+     * This method has no effect if called after the response
+     * has been committed. It does not set the response's character
+     * encoding if it is called after <code>getWriter</code>
+     * has been called or after the response has been committed.
+     * <p>Containers must communicate the content type and the character
+     * encoding used for the servlet response's writer to the client if
+     * the protocol provides a way for doing so. In the case of HTTP,
+     * the <code>Content-Type</code> header is used.
+     *
+     * @param type 	a <code>String</code> specifying the MIME 
+     *			type of the content
+     *
+     * @see 		#setLocale
+     * @see 		#setCharacterEncoding
+     * @see 		#getOutputStream
+     * @see 		#getWriter
+     *
+     */
+
+    public void setContentType(String type);
+    
+
+    /**
+     * Sets the preferred buffer size for the body of the response.  
+     * The servlet container will use a buffer at least as large as 
+     * the size requested.  The actual buffer size used can be found
+     * using <code>getBufferSize</code>.
+     *
+     * <p>A larger buffer allows more content to be written before anything is
+     * actually sent, thus providing the servlet with more time to set
+     * appropriate status codes and headers.  A smaller buffer decreases 
+     * server memory load and allows the client to start receiving data more
+     * quickly.
+     *
+     * <p>This method must be called before any response body content is
+     * written; if content has been written or the response object has
+     * been committed, this method throws an 
+     * <code>IllegalStateException</code>.
+     *
+     * @param size 	the preferred buffer size
+     *
+     * @exception  IllegalStateException  	if this method is called after
+     *						content has been written
+     *
+     * @see 		#getBufferSize
+     * @see 		#flushBuffer
+     * @see 		#isCommitted
+     * @see 		#reset
+     *
+     */
+
+    public void setBufferSize(int size);
+    
+    
+
+    /**
+     * Returns the actual buffer size used for the response.  If no buffering
+     * is used, this method returns 0.
+     *
+     * @return	 	the actual buffer size used
+     *
+     * @see 		#setBufferSize
+     * @see 		#flushBuffer
+     * @see 		#isCommitted
+     * @see 		#reset
+     *
+     */
+
+    public int getBufferSize();
+    
+    
+
+    /**
+     * Forces any content in the buffer to be written to the client.  A call
+     * to this method automatically commits the response, meaning the status 
+     * code and headers will be written.
+     *
+     * @see 		#setBufferSize
+     * @see 		#getBufferSize
+     * @see 		#isCommitted
+     * @see 		#reset
+     *
+     */
+
+    public void flushBuffer() throws IOException;
+    
+    
+    
+    /**
+     * Clears the content of the underlying buffer in the response without
+     * clearing headers or status code. If the 
+     * response has been committed, this method throws an 
+     * <code>IllegalStateException</code>.
+     *
+     * @see 		#setBufferSize
+     * @see 		#getBufferSize
+     * @see 		#isCommitted
+     * @see 		#reset
+     *
+     * @since 2.3
+     */
+
+    public void resetBuffer();
+    
+
+    /**
+     * Returns a boolean indicating if the response has been
+     * committed.  A committed response has already had its status 
+     * code and headers written.
+     *
+     * @return		a boolean indicating if the response has been
+     *  		committed
+     *
+     * @see 		#setBufferSize
+     * @see 		#getBufferSize
+     * @see 		#flushBuffer
+     * @see 		#reset
+     *
+     */
+
+    public boolean isCommitted();
+    
+    
+
+    /**
+     * Clears any data that exists in the buffer as well as the status code and
+     * headers.  If the response has been committed, this method throws an 
+     * <code>IllegalStateException</code>.
+     *
+     * @exception IllegalStateException  if the response has already been
+     *                                   committed
+     *
+     * @see 		#setBufferSize
+     * @see 		#getBufferSize
+     * @see 		#flushBuffer
+     * @see 		#isCommitted
+     *
+     */
+
+    public void reset();
+    
+    
+
+    /**
+     * Sets the locale of the response, if the response has not been
+     * committed yet. It also sets the response's character encoding
+     * appropriately for the locale, if the character encoding has not
+     * been explicitly set using {@link #setContentType} or
+     * {@link #setCharacterEncoding}, <code>getWriter</code> hasn't
+     * been called yet, and the response hasn't been committed yet.
+     * If the deployment descriptor contains a 
+     * <code>locale-encoding-mapping-list</code> element, and that
+     * element provides a mapping for the given locale, that mapping
+     * is used. Otherwise, the mapping from locale to character
+     * encoding is container dependent.
+     * <p>This method may be called repeatedly to change locale and
+     * character encoding. The method has no effect if called after the
+     * response has been committed. It does not set the response's
+     * character encoding if it is called after {@link #setContentType}
+     * has been called with a charset specification, after
+     * {@link #setCharacterEncoding} has been called, after
+     * <code>getWriter</code> has been called, or after the response
+     * has been committed.
+     * <p>Containers must communicate the locale and the character encoding
+     * used for the servlet response's writer to the client if the protocol
+     * provides a way for doing so. In the case of HTTP, the locale is
+     * communicated via the <code>Content-Language</code> header,
+     * the character encoding as part of the <code>Content-Type</code>
+     * header for text media types. Note that the character encoding
+     * cannot be communicated via HTTP headers if the servlet does not
+     * specify a content type; however, it is still used to encode text
+     * written via the servlet response's writer.
+     * 
+     * @param loc  the locale of the response
+     *
+     * @see 		#getLocale
+     * @see 		#setContentType
+     * @see 		#setCharacterEncoding
+     *
+     */
+
+    public void setLocale(Locale loc);
+    
+    
+
+    /**
+     * Returns the locale specified for this response
+     * using the {@link #setLocale} method. Calls made to
+     * <code>setLocale</code> after the response is committed
+     * have no effect. If no locale has been specified,
+     * the container's default locale is returned.
+     * 
+     * @see 		#setLocale
+     *
+     */
+
+    public Locale getLocale();
+
+
+
+}
+
+
+
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/ServletResponseWrapper.java b/servletapi/jsr154/src/share/javax/servlet/ServletResponseWrapper.java
new file mode 100644
index 0000000..7a95e28
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/ServletResponseWrapper.java
@@ -0,0 +1,217 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+/**
+ * 
+ * Provides a convenient implementation of the ServletResponse interface that
+ * can be subclassed by developers wishing to adapt the response from a Servlet.
+ * This class implements the Wrapper or Decorator pattern. Methods default to
+ * calling through to the wrapped response object.
+ * 
+ * @author 	Various
+ * @version 	$Version$
+ * @since	v 2.3
+ *
+ * @see 	javax.servlet.ServletResponse
+ *
+ */
+
+ 
+public class ServletResponseWrapper implements ServletResponse {
+	private ServletResponse response;
+	/**
+	* Creates a ServletResponse adaptor wrapping the given response object.
+	* @throws java.lang.IllegalArgumentException if the response is null.
+	*/
+
+
+	public ServletResponseWrapper(ServletResponse response) {
+	    if (response == null) {
+		throw new IllegalArgumentException("Response cannot be null");
+	    }
+	    this.response = response;
+	}
+
+	/**
+	* Return the wrapped ServletResponse object.
+	*/
+
+	public ServletResponse getResponse() {
+		return this.response;
+	}	
+	
+	
+	/**
+	* Sets the response being wrapped. 
+	* @throws java.lang.IllegalArgumentException if the response is null.
+	*/
+	
+	public void setResponse(ServletResponse response) {
+	    if (response == null) {
+		throw new IllegalArgumentException("Response cannot be null");
+	    }
+	    this.response = response;
+	}
+
+    /**
+     * The default behavior of this method is to call setCharacterEncoding(String charset)
+     * on the wrapped response object.
+     *
+     * @since 2.4
+     */
+
+    public void setCharacterEncoding(String charset) {
+	this.response.setCharacterEncoding(charset);
+    }
+
+    /**
+     * The default behavior of this method is to return getCharacterEncoding()
+     * on the wrapped response object.
+     */
+
+    public String getCharacterEncoding() {
+	return this.response.getCharacterEncoding();
+	}
+    
+    
+	  /**
+     * The default behavior of this method is to return getOutputStream()
+     * on the wrapped response object.
+     */
+
+    public ServletOutputStream getOutputStream() throws IOException {
+	return this.response.getOutputStream();
+    }  
+      
+     /**
+     * The default behavior of this method is to return getWriter()
+     * on the wrapped response object.
+     */
+
+
+    public PrintWriter getWriter() throws IOException {
+	return this.response.getWriter();
+	}
+    
+    /**
+     * The default behavior of this method is to call setContentLength(int len)
+     * on the wrapped response object.
+     */
+
+    public void setContentLength(int len) {
+	this.response.setContentLength(len);
+    }
+    
+    /**
+     * The default behavior of this method is to call setContentType(String type)
+     * on the wrapped response object.
+     */
+
+    public void setContentType(String type) {
+	this.response.setContentType(type);
+    }
+
+    /**
+     * The default behavior of this method is to return getContentType()
+     * on the wrapped response object.
+     *
+     * @since 2.4
+     */
+
+    public String getContentType() {
+	return this.response.getContentType();
+    }
+    
+    /**
+     * The default behavior of this method is to call setBufferSize(int size)
+     * on the wrapped response object.
+     */
+    public void setBufferSize(int size) {
+	this.response.setBufferSize(size);
+    }
+    
+    /**
+     * The default behavior of this method is to return getBufferSize()
+     * on the wrapped response object.
+     */
+    public int getBufferSize() {
+	return this.response.getBufferSize();
+    }
+
+    /**
+     * The default behavior of this method is to call flushBuffer()
+     * on the wrapped response object.
+     */
+
+    public void flushBuffer() throws IOException {
+	this.response.flushBuffer();
+    }
+    
+    /**
+     * The default behavior of this method is to return isCommitted()
+     * on the wrapped response object.
+     */
+    public boolean isCommitted() {
+	return this.response.isCommitted();
+    }
+
+    /**
+     * The default behavior of this method is to call reset()
+     * on the wrapped response object.
+     */
+
+    public void reset() {
+	this.response.reset();
+    }
+    
+    /**
+     * The default behavior of this method is to call resetBuffer()
+     * on the wrapped response object.
+     */
+     
+    public void resetBuffer() {
+	this.response.resetBuffer();
+    }
+    
+    /**
+     * The default behavior of this method is to call setLocale(Locale loc)
+     * on the wrapped response object.
+     */
+
+    public void setLocale(Locale loc) {
+	this.response.setLocale(loc);
+    }
+    
+    /**
+     * The default behavior of this method is to return getLocale()
+     * on the wrapped response object.
+     */
+    public Locale getLocale() {
+	return this.response.getLocale();
+    }
+
+
+}
+
+
+
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/SingleThreadModel.java b/servletapi/jsr154/src/share/javax/servlet/SingleThreadModel.java
new file mode 100644
index 0000000..646ff02
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/SingleThreadModel.java
@@ -0,0 +1,48 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet;
+
+/**
+ * Ensures that servlets handle
+ * only one request at a time. This interface has no methods.
+ *
+ * <p>If a servlet implements this interface, you are <i>guaranteed</i>
+ * that no two threads will execute concurrently in the
+ * servlet's <code>service</code> method. The servlet container
+ * can make this guarantee by synchronizing access to a single
+ * instance of the servlet, or by maintaining a pool of servlet
+ * instances and dispatching each new request to a free servlet.
+ *
+ * <p>Note that SingleThreadModel does not solve all thread safety
+ * issues.  For example, session attributes and static variables can
+ * still be accessed by multiple requests on multiple threads
+ * at the same time, even when SingleThreadModel servlets are used.
+ * It is recommended that a developer take other means to resolve
+ * those issues instead of implementing this interface, such as
+ * avoiding the usage of an instance variable or synchronizing
+ * the block of the code accessing those resources.
+ * This interface is deprecated in Servlet API version 2.4.
+ *
+ *
+ * @author	Various
+ * @version	$Version$
+ *
+ * @deprecated	As of Java Servlet API 2.4, with no direct
+ *	replacement.
+ */
+
+public interface SingleThreadModel {
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/UnavailableException.java b/servletapi/jsr154/src/share/javax/servlet/UnavailableException.java
new file mode 100644
index 0000000..cc0d7e5
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/UnavailableException.java
@@ -0,0 +1,205 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet;
+
+
+/**
+ * Defines an exception that a servlet or filter throws to indicate
+ * that it is permanently or temporarily unavailable. 
+ *
+ * <p>When a servlet or filter is permanently unavailable, something is wrong
+ * with it, and it cannot handle
+ * requests until some action is taken. For example, a servlet
+ * might be configured incorrectly, or a filter's state may be corrupted.
+ * The component should log both the error and the corrective action
+ * that is needed.
+ *
+ * <p>A servlet or filter is temporarily unavailable if it cannot handle
+ * requests momentarily due to some system-wide problem. For example,
+ * a third-tier server might not be accessible, or there may be 
+ * insufficient memory or disk storage to handle requests. A system
+ * administrator may need to take corrective action.
+ *
+ * <p>Servlet containers can safely treat both types of unavailable
+ * exceptions in the same way. However, treating temporary unavailability
+ * effectively makes the servlet container more robust. Specifically,
+ * the servlet container might block requests to the servlet or filter for a period
+ * of time suggested by the exception, rather than rejecting them until
+ * the servlet container restarts.
+ *
+ *
+ * @author 	Various
+ * @version 	$Version$
+ *
+ */
+
+public class UnavailableException
+extends ServletException {
+
+    private Servlet     servlet;           // what's unavailable
+    private boolean     permanent;         // needs admin action?
+    private int         seconds;           // unavailability estimate
+
+    /**
+     * 
+     * @deprecated	As of Java Servlet API 2.2, use {@link
+     * 			#UnavailableException(String)} instead.
+     *
+     * @param servlet 	the <code>Servlet</code> instance that is
+     *                  unavailable
+     *
+     * @param msg 	a <code>String</code> specifying the
+     *                  descriptive message
+     *
+     */
+
+    public UnavailableException(Servlet servlet, String msg) {
+	super(msg);
+	this.servlet = servlet;
+	permanent = true;
+    }
+ 
+    /**
+     * @deprecated	As of Java Servlet API 2.2, use {@link
+     *			#UnavailableException(String, int)} instead.
+     *
+     * @param seconds	an integer specifying the number of seconds
+     * 			the servlet expects to be unavailable; if
+     *			zero or negative, indicates that the servlet
+     *			can't make an estimate
+     *
+     * @param servlet	the <code>Servlet</code> that is unavailable
+     * 
+     * @param msg	a <code>String</code> specifying the descriptive 
+     *			message, which can be written to a log file or 
+     *			displayed for the user.
+     *
+     */
+    
+    public UnavailableException(int seconds, Servlet servlet, String msg) {
+	super(msg);
+	this.servlet = servlet;
+	if (seconds <= 0)
+	    this.seconds = -1;
+	else
+	    this.seconds = seconds;
+	permanent = false;
+    }
+
+    /**
+     * 
+     * Constructs a new exception with a descriptive
+     * message indicating that the servlet is permanently
+     * unavailable.
+     *
+     * @param msg 	a <code>String</code> specifying the
+     *                  descriptive message
+     *
+     */
+
+    public UnavailableException(String msg) {
+	super(msg);
+
+	permanent = true;
+    }
+
+    /**
+     * Constructs a new exception with a descriptive message
+     * indicating that the servlet is temporarily unavailable
+     * and giving an estimate of how long it will be unavailable.
+     * 
+     * <p>In some cases, the servlet cannot make an estimate. For
+     * example, the servlet might know that a server it needs is
+     * not running, but not be able to report how long it will take
+     * to be restored to functionality. This can be indicated with
+     * a negative or zero value for the <code>seconds</code> argument.
+     *
+     * @param msg	a <code>String</code> specifying the
+     *                  descriptive message, which can be written
+     *                  to a log file or displayed for the user.
+     *
+     * @param seconds	an integer specifying the number of seconds
+     * 			the servlet expects to be unavailable; if
+     *			zero or negative, indicates that the servlet
+     *			can't make an estimate
+     *
+     */
+    
+    public UnavailableException(String msg, int seconds) {
+	super(msg);
+
+	if (seconds <= 0)
+	    this.seconds = -1;
+	else
+	    this.seconds = seconds;
+
+	permanent = false;
+    }
+
+    /**
+     *
+     * Returns a <code>boolean</code> indicating
+     * whether the servlet is permanently unavailable.
+     * If so, something is wrong with the servlet, and the
+     * system administrator must take some corrective action.
+     *
+     * @return		<code>true</code> if the servlet is
+     *			permanently unavailable; <code>false</code>
+     *			if the servlet is available or temporarily
+     *			unavailable
+     *
+     */
+     
+    public boolean isPermanent() {
+	return permanent;
+    }
+  
+    /**
+     * @deprecated	As of Java Servlet API 2.2, with no replacement.
+     *
+     * Returns the servlet that is reporting its unavailability.
+     * 
+     * @return		the <code>Servlet</code> object that is 
+     *			throwing the <code>UnavailableException</code>
+     *
+     */
+     
+    public Servlet getServlet() {
+	return servlet;
+    }
+
+    /**
+     * Returns the number of seconds the servlet expects to 
+     * be temporarily unavailable.  
+     *
+     * <p>If this method returns a negative number, the servlet
+     * is permanently unavailable or cannot provide an estimate of
+     * how long it will be unavailable. No effort is
+     * made to correct for the time elapsed since the exception was
+     * first reported.
+     *
+     * @return		an integer specifying the number of seconds
+     *			the servlet will be temporarily unavailable,
+     *			or a negative number if the servlet is permanently
+     *			unavailable or cannot make an estimate
+     *
+     */
+     
+    public int getUnavailableSeconds() {
+	return permanent ? -1 : seconds;
+    }
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/Cookie.java b/servletapi/jsr154/src/share/javax/servlet/http/Cookie.java
new file mode 100644
index 0000000..7cbc6b4
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/Cookie.java
@@ -0,0 +1,536 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * Creates a cookie, a small amount of information sent by a servlet to 
+ * a Web browser, saved by the browser, and later sent back to the server.
+ * A cookie's value can uniquely 
+ * identify a client, so cookies are commonly used for session management.
+ * 
+ * <p>A cookie has a name, a single value, and optional attributes
+ * such as a comment, path and domain qualifiers, a maximum age, and a
+ * version number. Some Web browsers have bugs in how they handle the 
+ * optional attributes, so use them sparingly to improve the interoperability 
+ * of your servlets.
+ *
+ * <p>The servlet sends cookies to the browser by using the
+ * {@link HttpServletResponse#addCookie} method, which adds
+ * fields to HTTP response headers to send cookies to the 
+ * browser, one at a time. The browser is expected to 
+ * support 20 cookies for each Web server, 300 cookies total, and
+ * may limit cookie size to 4 KB each.
+ * 
+ * <p>The browser returns cookies to the servlet by adding 
+ * fields to HTTP request headers. Cookies can be retrieved
+ * from a request by using the {@link HttpServletRequest#getCookies} method.
+ * Several cookies might have the same name but different path attributes.
+ * 
+ * <p>Cookies affect the caching of the Web pages that use them. 
+ * HTTP 1.0 does not cache pages that use cookies created with
+ * this class. This class does not support the cache control
+ * defined with HTTP 1.1.
+ *
+ * <p>This class supports both the Version 0 (by Netscape) and Version 1 
+ * (by RFC 2109) cookie specifications. By default, cookies are
+ * created using Version 0 to ensure the best interoperability.
+ *
+ *
+ * @author	Various
+ * @version	$Version$
+ *
+ */
+
+// XXX would implement java.io.Serializable too, but can't do that
+// so long as sun.servlet.* must run on older JDK 1.02 JVMs which
+// don't include that support.
+
+public class Cookie implements Cloneable {
+
+    private static final String LSTRING_FILE =
+	"javax.servlet.http.LocalStrings";
+    private static ResourceBundle lStrings =
+	ResourceBundle.getBundle(LSTRING_FILE);
+    
+    //
+    // The value of the cookie itself.
+    //
+    
+    private String name;	// NAME= ... "$Name" style is reserved
+    private String value;	// value of NAME
+
+    //
+    // Attributes encoded in the header's cookie fields.
+    //
+    
+    private String comment;	// ;Comment=VALUE ... describes cookie's use
+				// ;Discard ... implied by maxAge < 0
+    private String domain;	// ;Domain=VALUE ... domain that sees cookie
+    private int maxAge = -1;	// ;Max-Age=VALUE ... cookies auto-expire
+    private String path;	// ;Path=VALUE ... URLs that see the cookie
+    private boolean secure;	// ;Secure ... e.g. use SSL
+    private int version = 0;	// ;Version=1 ... means RFC 2109++ style
+    
+    
+
+    /**
+     * Constructs a cookie with a specified name and value.
+     *
+     * <p>The name must conform to RFC 2109. That means it can contain 
+     * only ASCII alphanumeric characters and cannot contain commas, 
+     * semicolons, or white space or begin with a $ character. The cookie's
+     * name cannot be changed after creation.
+     *
+     * <p>The value can be anything the server chooses to send. Its
+     * value is probably of interest only to the server. The cookie's
+     * value can be changed after creation with the
+     * <code>setValue</code> method.
+     *
+     * <p>By default, cookies are created according to the Netscape
+     * cookie specification. The version can be changed with the 
+     * <code>setVersion</code> method.
+     *
+     *
+     * @param name 			a <code>String</code> specifying the name of the cookie
+     *
+     * @param value			a <code>String</code> specifying the value of the cookie
+     *
+     * @throws IllegalArgumentException	if the cookie name contains illegal characters
+     *					(for example, a comma, space, or semicolon)
+     *					or it is one of the tokens reserved for use
+     *					by the cookie protocol
+     * @see #setValue
+     * @see #setVersion
+     *
+     */
+
+    public Cookie(String name, String value) {
+	if (!isToken(name)
+		|| name.equalsIgnoreCase("Comment")	// rfc2019
+		|| name.equalsIgnoreCase("Discard")	// 2019++
+		|| name.equalsIgnoreCase("Domain")
+		|| name.equalsIgnoreCase("Expires")	// (old cookies)
+		|| name.equalsIgnoreCase("Max-Age")	// rfc2019
+		|| name.equalsIgnoreCase("Path")
+		|| name.equalsIgnoreCase("Secure")
+		|| name.equalsIgnoreCase("Version")
+		|| name.startsWith("$")
+	    ) {
+	    String errMsg = lStrings.getString("err.cookie_name_is_token");
+	    Object[] errArgs = new Object[1];
+	    errArgs[0] = name;
+	    errMsg = MessageFormat.format(errMsg, errArgs);
+	    throw new IllegalArgumentException(errMsg);
+	}
+
+	this.name = name;
+	this.value = value;
+    }
+
+
+
+
+
+    /**
+     *
+     * Specifies a comment that describes a cookie's purpose.
+     * The comment is useful if the browser presents the cookie 
+     * to the user. Comments
+     * are not supported by Netscape Version 0 cookies.
+     *
+     * @param purpose		a <code>String</code> specifying the comment 
+     *				to display to the user
+     *
+     * @see #getComment
+     *
+     */
+
+    public void setComment(String purpose) {
+	comment = purpose;
+    }
+    
+    
+    
+
+    /**
+     * Returns the comment describing the purpose of this cookie, or
+     * <code>null</code> if the cookie has no comment.
+     *
+     * @return			a <code>String</code> containing the comment,
+     *				or <code>null</code> if none
+     *
+     * @see #setComment
+     *
+     */ 
+
+    public String getComment() {
+	return comment;
+    }
+    
+    
+    
+
+
+    /**
+     *
+     * Specifies the domain within which this cookie should be presented.
+     *
+     * <p>The form of the domain name is specified by RFC 2109. A domain
+     * name begins with a dot (<code>.foo.com</code>) and means that
+     * the cookie is visible to servers in a specified Domain Name System
+     * (DNS) zone (for example, <code>www.foo.com</code>, but not 
+     * <code>a.b.foo.com</code>). By default, cookies are only returned
+     * to the server that sent them.
+     *
+     *
+     * @param pattern		a <code>String</code> containing the domain name
+     *				within which this cookie is visible;
+     *				form is according to RFC 2109
+     *
+     * @see #getDomain
+     *
+     */
+
+    public void setDomain(String pattern) {
+	domain = pattern.toLowerCase();	// IE allegedly needs this
+    }
+    
+    
+    
+    
+
+    /**
+     * Returns the domain name set for this cookie. The form of 
+     * the domain name is set by RFC 2109.
+     *
+     * @return			a <code>String</code> containing the domain name
+     *
+     * @see #setDomain
+     *
+     */ 
+
+    public String getDomain() {
+	return domain;
+    }
+
+
+
+
+    /**
+     * Sets the maximum age of the cookie in seconds.
+     *
+     * <p>A positive value indicates that the cookie will expire
+     * after that many seconds have passed. Note that the value is
+     * the <i>maximum</i> age when the cookie will expire, not the cookie's
+     * current age.
+     *
+     * <p>A negative value means
+     * that the cookie is not stored persistently and will be deleted
+     * when the Web browser exits. A zero value causes the cookie
+     * to be deleted.
+     *
+     * @param expiry		an integer specifying the maximum age of the
+     * 				cookie in seconds; if negative, means
+     *				the cookie is not stored; if zero, deletes
+     *				the cookie
+     *
+     *
+     * @see #getMaxAge
+     *
+     */
+
+    public void setMaxAge(int expiry) {
+	maxAge = expiry;
+    }
+
+
+
+
+    /**
+     * Returns the maximum age of the cookie, specified in seconds,
+     * By default, <code>-1</code> indicating the cookie will persist
+     * until browser shutdown.
+     *
+     *
+     * @return			an integer specifying the maximum age of the
+     *				cookie in seconds; if negative, means
+     *				the cookie persists until browser shutdown
+     *
+     *
+     * @see #setMaxAge
+     *
+     */
+
+    public int getMaxAge() {
+	return maxAge;
+    }
+    
+    
+    
+
+    /**
+     * Specifies a path for the cookie
+     * to which the client should return the cookie.
+     *
+     * <p>The cookie is visible to all the pages in the directory
+     * you specify, and all the pages in that directory's subdirectories. 
+     * A cookie's path must include the servlet that set the cookie,
+     * for example, <i>/catalog</i>, which makes the cookie
+     * visible to all directories on the server under <i>/catalog</i>.
+     *
+     * <p>Consult RFC 2109 (available on the Internet) for more
+     * information on setting path names for cookies.
+     *
+     *
+     * @param uri		a <code>String</code> specifying a path
+     *
+     *
+     * @see #getPath
+     *
+     */
+
+    public void setPath(String uri) {
+	path = uri;
+    }
+
+
+
+
+    /**
+     * Returns the path on the server 
+     * to which the browser returns this cookie. The
+     * cookie is visible to all subpaths on the server.
+     *
+     *
+     * @return		a <code>String</code> specifying a path that contains
+     *			a servlet name, for example, <i>/catalog</i>
+     *
+     * @see #setPath
+     *
+     */ 
+
+    public String getPath() {
+	return path;
+    }
+
+
+
+
+
+    /**
+     * Indicates to the browser whether the cookie should only be sent
+     * using a secure protocol, such as HTTPS or SSL.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @param flag	if <code>true</code>, sends the cookie from the browser
+     *			to the server only when using a secure protocol;
+     *			if <code>false</code>, sent on any protocol
+     *
+     * @see #getSecure
+     *
+     */
+ 
+    public void setSecure(boolean flag) {
+	secure = flag;
+    }
+
+
+
+
+    /**
+     * Returns <code>true</code> if the browser is sending cookies
+     * only over a secure protocol, or <code>false</code> if the
+     * browser can send cookies using any protocol.
+     *
+     * @return		<code>true</code> if the browser uses a secure protocol;
+     * 			 otherwise, <code>true</code>
+     *
+     * @see #setSecure
+     *
+     */
+
+    public boolean getSecure() {
+	return secure;
+    }
+
+
+
+
+
+    /**
+     * Returns the name of the cookie. The name cannot be changed after
+     * creation.
+     *
+     * @return		a <code>String</code> specifying the cookie's name
+     *
+     */
+
+    public String getName() {
+	return name;
+    }
+
+
+
+
+
+    /**
+     *
+     * Assigns a new value to a cookie after the cookie is created.
+     * If you use a binary value, you may want to use BASE64 encoding.
+     *
+     * <p>With Version 0 cookies, values should not contain white 
+     * space, brackets, parentheses, equals signs, commas,
+     * double quotes, slashes, question marks, at signs, colons,
+     * and semicolons. Empty values may not behave the same way
+     * on all browsers.
+     *
+     * @param newValue		a <code>String</code> specifying the new value 
+     *
+     *
+     * @see #getValue
+     * @see Cookie
+     *
+     */
+
+    public void setValue(String newValue) {
+	value = newValue;
+    }
+
+
+
+
+    /**
+     * Returns the value of the cookie.
+     *
+     * @return			a <code>String</code> containing the cookie's
+     *				present value
+     *
+     * @see #setValue
+     * @see Cookie
+     *
+     */
+
+    public String getValue() {
+	return value;
+    }
+
+
+
+
+    /**
+     * Returns the version of the protocol this cookie complies 
+     * with. Version 1 complies with RFC 2109, 
+     * and version 0 complies with the original
+     * cookie specification drafted by Netscape. Cookies provided
+     * by a browser use and identify the browser's cookie version.
+     * 
+     *
+     * @return			0 if the cookie complies with the
+     *				original Netscape specification; 1
+     *				if the cookie complies with RFC 2109
+     *
+     * @see #setVersion
+     *
+     */
+
+    public int getVersion() {
+	return version;
+    }
+
+
+
+
+    /**
+     * Sets the version of the cookie protocol this cookie complies
+     * with. Version 0 complies with the original Netscape cookie
+     * specification. Version 1 complies with RFC 2109.
+     *
+     * <p>Since RFC 2109 is still somewhat new, consider
+     * version 1 as experimental; do not use it yet on production sites.
+     *
+     *
+     * @param v			0 if the cookie should comply with 
+     *				the original Netscape specification;
+     *				1 if the cookie should comply with RFC 2109
+     *
+     * @see #getVersion
+     *
+     */
+
+    public void setVersion(int v) {
+	version = v;
+    }
+
+    // Note -- disabled for now to allow full Netscape compatibility
+    // from RFC 2068, token special case characters
+    // 
+    // private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
+
+    private static final String tspecials = ",; ";
+    
+    
+    
+
+    /*
+     * Tests a string and returns true if the string counts as a 
+     * reserved token in the Java language.
+     * 
+     * @param value		the <code>String</code> to be tested
+     *
+     * @return			<code>true</code> if the <code>String</code> is
+     *				a reserved token; <code>false</code>
+     *				if it is not			
+     */
+
+    private boolean isToken(String value) {
+	int len = value.length();
+
+	for (int i = 0; i < len; i++) {
+	    char c = value.charAt(i);
+
+	    if (c < 0x20 || c >= 0x7f || tspecials.indexOf(c) != -1)
+		return false;
+	}
+	return true;
+    }
+
+
+
+
+
+
+    /**
+     *
+     * Overrides the standard <code>java.lang.Object.clone</code> 
+     * method to return a copy of this cookie.
+     *		
+     *
+     */
+
+    public Object clone() {
+	try {
+	    return super.clone();
+	} catch (CloneNotSupportedException e) {
+	    throw new RuntimeException(e.getMessage());
+	}
+    }
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpServlet.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpServlet.java
new file mode 100644
index 0000000..08479b9
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpServlet.java
@@ -0,0 +1,1018 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Method;
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ *
+ * Provides an abstract class to be subclassed to create
+ * an HTTP servlet suitable for a Web site. A subclass of
+ * <code>HttpServlet</code> must override at least 
+ * one method, usually one of these:
+ *
+ * <ul>
+ * <li> <code>doGet</code>, if the servlet supports HTTP GET requests
+ * <li> <code>doPost</code>, for HTTP POST requests
+ * <li> <code>doPut</code>, for HTTP PUT requests
+ * <li> <code>doDelete</code>, for HTTP DELETE requests
+ * <li> <code>init</code> and <code>destroy</code>, 
+ * to manage resources that are held for the life of the servlet
+ * <li> <code>getServletInfo</code>, which the servlet uses to
+ * provide information about itself 
+ * </ul>
+ *
+ * <p>There's almost no reason to override the <code>service</code>
+ * method. <code>service</code> handles standard HTTP
+ * requests by dispatching them to the handler methods
+ * for each HTTP request type (the <code>do</code><i>XXX</i>
+ * methods listed above).
+ *
+ * <p>Likewise, there's almost no reason to override the 
+ * <code>doOptions</code> and <code>doTrace</code> methods.
+ * 
+ * <p>Servlets typically run on multithreaded servers,
+ * so be aware that a servlet must handle concurrent
+ * requests and be careful to synchronize access to shared resources.
+ * Shared resources include in-memory data such as
+ * instance or class variables and external objects
+ * such as files, database connections, and network 
+ * connections.
+ * See the
+ * <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
+ * Java Tutorial on Multithreaded Programming</a> for more
+ * information on handling multiple threads in a Java program.
+ *
+ * @author	Various
+ * @version	$Version$
+ *
+ */
+
+
+
+public abstract class HttpServlet extends GenericServlet
+    implements java.io.Serializable
+{
+    private static final String METHOD_DELETE = "DELETE";
+    private static final String METHOD_HEAD = "HEAD";
+    private static final String METHOD_GET = "GET";
+    private static final String METHOD_OPTIONS = "OPTIONS";
+    private static final String METHOD_POST = "POST";
+    private static final String METHOD_PUT = "PUT";
+    private static final String METHOD_TRACE = "TRACE";
+
+    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
+    private static final String HEADER_LASTMOD = "Last-Modified";
+    
+    private static final String LSTRING_FILE =
+	"javax.servlet.http.LocalStrings";
+    private static ResourceBundle lStrings =
+	ResourceBundle.getBundle(LSTRING_FILE);
+   
+   
+   
+    
+    /**
+     * Does nothing, because this is an abstract class.
+     * 
+     */
+
+    public HttpServlet() { }
+    
+    
+
+    /**
+     *
+     * Called by the server (via the <code>service</code> method) to
+     * allow a servlet to handle a GET request. 
+     *
+     * <p>Overriding this method to support a GET request also
+     * automatically supports an HTTP HEAD request. A HEAD
+     * request is a GET request that returns no body in the
+     * response, only the request header fields.
+     *
+     * <p>When overriding this method, read the request data,
+     * write the response headers, get the response's writer or 
+     * output stream object, and finally, write the response data.
+     * It's best to include content type and encoding. When using
+     * a <code>PrintWriter</code> object to return the response,
+     * set the content type before accessing the
+     * <code>PrintWriter</code> object.
+     *
+     * <p>The servlet container must write the headers before
+     * committing the response, because in HTTP the headers must be sent
+     * before the response body.
+     *
+     * <p>Where possible, set the Content-Length header (with the
+     * {@link javax.servlet.ServletResponse#setContentLength} method),
+     * to allow the servlet container to use a persistent connection 
+     * to return its response to the client, improving performance.
+     * The content length is automatically set if the entire response fits
+     * inside the response buffer.
+     *
+     * <p>When using HTTP 1.1 chunked encoding (which means that the response
+     * has a Transfer-Encoding header), do not set the Content-Length header.
+     *
+     * <p>The GET method should be safe, that is, without
+     * any side effects for which users are held responsible.
+     * For example, most form queries have no side effects.
+     * If a client request is intended to change stored data,
+     * the request should use some other HTTP method.
+     *
+     * <p>The GET method should also be idempotent, meaning
+     * that it can be safely repeated. Sometimes making a
+     * method safe also makes it idempotent. For example, 
+     * repeating queries is both safe and idempotent, but
+     * buying a product online or modifying data is neither
+     * safe nor idempotent. 
+     *
+     * <p>If the request is incorrectly formatted, <code>doGet</code>
+     * returns an HTTP "Bad Request" message.
+     * 
+     *
+     * @param req	an {@link HttpServletRequest} object that
+     *			contains the request the client has made
+     *			of the servlet
+     *
+     * @param resp	an {@link HttpServletResponse} object that
+     *			contains the response the servlet sends
+     *			to the client
+     * 
+     * @exception IOException	if an input or output error is 
+     *				detected when the servlet handles
+     *				the GET request
+     *
+     * @exception ServletException	if the request for the GET
+     *					could not be handled
+     *
+     * 
+     * @see javax.servlet.ServletResponse#setContentType
+     *
+     */
+
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+	throws ServletException, IOException
+    {
+	String protocol = req.getProtocol();
+	String msg = lStrings.getString("http.method_get_not_supported");
+	if (protocol.endsWith("1.1")) {
+	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
+	} else {
+	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
+	}
+    }
+
+
+
+
+
+    /**
+     *
+     * Returns the time the <code>HttpServletRequest</code>
+     * object was last modified,
+     * in milliseconds since midnight January 1, 1970 GMT.
+     * If the time is unknown, this method returns a negative
+     * number (the default).
+     *
+     * <p>Servlets that support HTTP GET requests and can quickly determine
+     * their last modification time should override this method.
+     * This makes browser and proxy caches work more effectively,
+     * reducing the load on server and network resources.
+     *
+     *
+     * @param req	the <code>HttpServletRequest</code> 
+     *			object that is sent to the servlet
+     *
+     * @return		a <code>long</code> integer specifying
+     *			the time the <code>HttpServletRequest</code>
+     *			object was last modified, in milliseconds
+     *			since midnight, January 1, 1970 GMT, or
+     *			-1 if the time is not known
+     *
+     */
+
+    protected long getLastModified(HttpServletRequest req) {
+	return -1;
+    }
+
+
+
+
+    /**
+     * 
+     *
+     * <p>Receives an HTTP HEAD request from the protected
+     * <code>service</code> method and handles the
+     * request.
+     * The client sends a HEAD request when it wants
+     * to see only the headers of a response, such as
+     * Content-Type or Content-Length. The HTTP HEAD
+     * method counts the output bytes in the response
+     * to set the Content-Length header accurately.
+     *
+     * <p>If you override this method, you can avoid computing
+     * the response body and just set the response headers
+     * directly to improve performance. Make sure that the
+     * <code>doHead</code> method you write is both safe
+     * and idempotent (that is, protects itself from being
+     * called multiple times for one HTTP HEAD request).
+     *
+     * <p>If the HTTP HEAD request is incorrectly formatted,
+     * <code>doHead</code> returns an HTTP "Bad Request"
+     * message.
+     *
+     *
+     * @param req	the request object that is passed
+     *			to the servlet
+     *			
+     * @param resp	the response object that the servlet
+     *			uses to return the headers to the clien
+     *
+     * @exception IOException		if an input or output error occurs
+     *
+     * @exception ServletException	if the request for the HEAD
+     *					could not be handled
+     */
+
+    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
+	throws ServletException, IOException
+    {
+	NoBodyResponse response = new NoBodyResponse(resp);
+	
+	doGet(req, response);
+	response.setContentLength();
+    }
+    
+
+
+
+
+    /**
+     *
+     * Called by the server (via the <code>service</code> method)
+     * to allow a servlet to handle a POST request.
+     *
+     * The HTTP POST method allows the client to send
+     * data of unlimited length to the Web server a single time
+     * and is useful when posting information such as
+     * credit card numbers.
+     *
+     * <p>When overriding this method, read the request data,
+     * write the response headers, get the response's writer or output
+     * stream object, and finally, write the response data. It's best 
+     * to include content type and encoding. When using a
+     * <code>PrintWriter</code> object to return the response, set the 
+     * content type before accessing the <code>PrintWriter</code> object. 
+     *
+     * <p>The servlet container must write the headers before committing the
+     * response, because in HTTP the headers must be sent before the 
+     * response body.
+     *
+     * <p>Where possible, set the Content-Length header (with the
+     * {@link javax.servlet.ServletResponse#setContentLength} method),
+     * to allow the servlet container to use a persistent connection 
+     * to return its response to the client, improving performance.
+     * The content length is automatically set if the entire response fits
+     * inside the response buffer.  
+     *
+     * <p>When using HTTP 1.1 chunked encoding (which means that the response
+     * has a Transfer-Encoding header), do not set the Content-Length header. 
+     *
+     * <p>This method does not need to be either safe or idempotent.
+     * Operations requested through POST can have side effects for
+     * which the user can be held accountable, for example, 
+     * updating stored data or buying items online.
+     *
+     * <p>If the HTTP POST request is incorrectly formatted,
+     * <code>doPost</code> returns an HTTP "Bad Request" message.
+     *
+     *
+     * @param req	an {@link HttpServletRequest} object that
+     *			contains the request the client has made
+     *			of the servlet
+     *
+     * @param resp	an {@link HttpServletResponse} object that
+     *			contains the response the servlet sends
+     *			to the client
+     * 
+     * @exception IOException	if an input or output error is 
+     *				detected when the servlet handles
+     *				the request
+     *
+     * @exception ServletException	if the request for the POST
+     *					could not be handled
+     *
+     *
+     * @see javax.servlet.ServletOutputStream
+     * @see javax.servlet.ServletResponse#setContentType
+     *
+     *
+     */
+
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+	throws ServletException, IOException
+    {
+	String protocol = req.getProtocol();
+	String msg = lStrings.getString("http.method_post_not_supported");
+	if (protocol.endsWith("1.1")) {
+	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
+	} else {
+	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
+	}
+    }
+
+
+
+
+    /**
+     * Called by the server (via the <code>service</code> method)
+     * to allow a servlet to handle a PUT request.
+     *
+     * The PUT operation allows a client to 
+     * place a file on the server and is similar to 
+     * sending a file by FTP.
+     *
+     * <p>When overriding this method, leave intact
+     * any content headers sent with the request (including
+     * Content-Length, Content-Type, Content-Transfer-Encoding,
+     * Content-Encoding, Content-Base, Content-Language, Content-Location,
+     * Content-MD5, and Content-Range). If your method cannot
+     * handle a content header, it must issue an error message
+     * (HTTP 501 - Not Implemented) and discard the request.
+     * For more information on HTTP 1.1, see RFC 2616
+     * <a href="http://www.ietf.org/rfc/rfc2616.txt"></a>.
+     *
+     * <p>This method does not need to be either safe or idempotent.
+     * Operations that <code>doPut</code> performs can have side
+     * effects for which the user can be held accountable. When using
+     * this method, it may be useful to save a copy of the
+     * affected URL in temporary storage.
+     *
+     * <p>If the HTTP PUT request is incorrectly formatted,
+     * <code>doPut</code> returns an HTTP "Bad Request" message.
+     *
+     *
+     * @param req	the {@link HttpServletRequest} object that
+     *			contains the request the client made of
+     *			the servlet
+     *
+     * @param resp	the {@link HttpServletResponse} object that
+     *			contains the response the servlet returns
+     *			to the client
+     *
+     * @exception IOException	if an input or output error occurs
+     *				while the servlet is handling the
+     *				PUT request
+     *
+     * @exception ServletException	if the request for the PUT
+     *					cannot be handled
+     *
+     */
+  
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
+	throws ServletException, IOException
+    {
+	String protocol = req.getProtocol();
+	String msg = lStrings.getString("http.method_put_not_supported");
+	if (protocol.endsWith("1.1")) {
+	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
+	} else {
+	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
+	}
+    }
+
+
+
+
+    /**
+     * 
+     * Called by the server (via the <code>service</code> method)
+     * to allow a servlet to handle a DELETE request.
+     *
+     * The DELETE operation allows a client to remove a document
+     * or Web page from the server.
+     * 
+     * <p>This method does not need to be either safe
+     * or idempotent. Operations requested through
+     * DELETE can have side effects for which users
+     * can be held accountable. When using
+     * this method, it may be useful to save a copy of the
+     * affected URL in temporary storage.
+     *
+     * <p>If the HTTP DELETE request is incorrectly formatted,
+     * <code>doDelete</code> returns an HTTP "Bad Request"
+     * message.
+     *
+     *
+     * @param req	the {@link HttpServletRequest} object that
+     *			contains the request the client made of
+     *			the servlet
+     *
+     *
+     * @param resp	the {@link HttpServletResponse} object that
+     *			contains the response the servlet returns
+     *			to the client				
+     *
+     *
+     * @exception IOException	if an input or output error occurs
+     *				while the servlet is handling the
+     *				DELETE request
+     *
+     * @exception ServletException	if the request for the
+     *					DELETE cannot be handled
+     *
+     */
+     
+    protected void doDelete(HttpServletRequest req,
+			    HttpServletResponse resp)
+	throws ServletException, IOException
+    {
+	String protocol = req.getProtocol();
+	String msg = lStrings.getString("http.method_delete_not_supported");
+	if (protocol.endsWith("1.1")) {
+	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
+	} else {
+	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
+	}
+    }
+    
+
+    private static Method[] getAllDeclaredMethods(Class c) {
+
+        if (c.equals(javax.servlet.http.HttpServlet.class)) {
+            return null;
+        }
+
+        Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
+        Method[] thisMethods = c.getDeclaredMethods();
+	
+        if ((parentMethods != null) && (parentMethods.length > 0)) {
+            Method[] allMethods =
+                new Method[parentMethods.length + thisMethods.length];
+	    System.arraycopy(parentMethods, 0, allMethods, 0,
+                             parentMethods.length);
+	    System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
+                             thisMethods.length);
+
+	    thisMethods = allMethods;
+	}
+
+	return thisMethods;
+    }
+
+
+    /**
+     * Called by the server (via the <code>service</code> method)
+     * to allow a servlet to handle a OPTIONS request.
+     *
+     * The OPTIONS request determines which HTTP methods 
+     * the server supports and
+     * returns an appropriate header. For example, if a servlet
+     * overrides <code>doGet</code>, this method returns the
+     * following header:
+     *
+     * <p><code>Allow: GET, HEAD, TRACE, OPTIONS</code>
+     *
+     * <p>There's no need to override this method unless the
+     * servlet implements new HTTP methods, beyond those 
+     * implemented by HTTP 1.1.
+     *
+     * @param req	the {@link HttpServletRequest} object that
+     *			contains the request the client made of
+     *			the servlet
+     *
+     *
+     * @param resp	the {@link HttpServletResponse} object that
+     *			contains the response the servlet returns
+     *			to the client				
+     *
+     *
+     * @exception IOException	if an input or output error occurs
+     *				while the servlet is handling the
+     *				OPTIONS request
+     *
+     * @exception ServletException	if the request for the
+     *					OPTIONS cannot be handled
+     *
+     */
+         
+    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
+	throws ServletException, IOException
+    {
+	Method[] methods = getAllDeclaredMethods(this.getClass());
+	
+	boolean ALLOW_GET = false;
+	boolean ALLOW_HEAD = false;
+	boolean ALLOW_POST = false;
+	boolean ALLOW_PUT = false;
+	boolean ALLOW_DELETE = false;
+	boolean ALLOW_TRACE = true;
+	boolean ALLOW_OPTIONS = true;
+	
+	for (int i=0; i<methods.length; i++) {
+	    Method m = methods[i];
+	    
+	    if (m.getName().equals("doGet")) {
+		ALLOW_GET = true;
+		ALLOW_HEAD = true;
+	    }
+	    if (m.getName().equals("doPost")) 
+		ALLOW_POST = true;
+	    if (m.getName().equals("doPut"))
+		ALLOW_PUT = true;
+	    if (m.getName().equals("doDelete"))
+		ALLOW_DELETE = true;
+	    
+	}
+	
+	String allow = null;
+	if (ALLOW_GET)
+	    if (allow==null) allow=METHOD_GET;
+	if (ALLOW_HEAD)
+	    if (allow==null) allow=METHOD_HEAD;
+	    else allow += ", " + METHOD_HEAD;
+	if (ALLOW_POST)
+	    if (allow==null) allow=METHOD_POST;
+	    else allow += ", " + METHOD_POST;
+	if (ALLOW_PUT)
+	    if (allow==null) allow=METHOD_PUT;
+	    else allow += ", " + METHOD_PUT;
+	if (ALLOW_DELETE)
+	    if (allow==null) allow=METHOD_DELETE;
+	    else allow += ", " + METHOD_DELETE;
+	if (ALLOW_TRACE)
+	    if (allow==null) allow=METHOD_TRACE;
+	    else allow += ", " + METHOD_TRACE;
+	if (ALLOW_OPTIONS)
+	    if (allow==null) allow=METHOD_OPTIONS;
+	    else allow += ", " + METHOD_OPTIONS;
+	
+	resp.setHeader("Allow", allow);
+    }
+    
+    
+    
+    
+    /**
+     * Called by the server (via the <code>service</code> method)
+     * to allow a servlet to handle a TRACE request.
+     *
+     * A TRACE returns the headers sent with the TRACE
+     * request to the client, so that they can be used in
+     * debugging. There's no need to override this method. 
+     *
+     *
+     *
+     * @param req	the {@link HttpServletRequest} object that
+     *			contains the request the client made of
+     *			the servlet
+     *
+     *
+     * @param resp	the {@link HttpServletResponse} object that
+     *			contains the response the servlet returns
+     *			to the client				
+     *
+     *
+     * @exception IOException	if an input or output error occurs
+     *				while the servlet is handling the
+     *				TRACE request
+     *
+     * @exception ServletException	if the request for the
+     *					TRACE cannot be handled
+     *
+     */
+
+    protected void doTrace(HttpServletRequest req, HttpServletResponse resp) 
+	throws ServletException, IOException
+    {
+	
+	int responseLength;
+	
+	String CRLF = "\r\n";
+	String responseString = "TRACE "+ req.getRequestURI()+
+	    " " + req.getProtocol();
+	
+	Enumeration reqHeaderEnum = req.getHeaderNames();
+	
+	while( reqHeaderEnum.hasMoreElements() ) {
+	    String headerName = (String)reqHeaderEnum.nextElement();
+	    responseString += CRLF + headerName + ": " +
+		req.getHeader(headerName); 
+	}
+	
+	responseString += CRLF;
+	
+	responseLength = responseString.length();
+	
+	resp.setContentType("message/http");
+	resp.setContentLength(responseLength);
+	ServletOutputStream out = resp.getOutputStream();
+	out.print(responseString);	
+	out.close();
+	return;
+    }		
+
+
+
+
+
+    /**
+     *
+     * Receives standard HTTP requests from the public
+     * <code>service</code> method and dispatches
+     * them to the <code>do</code><i>XXX</i> methods defined in 
+     * this class. This method is an HTTP-specific version of the 
+     * {@link javax.servlet.Servlet#service} method. There's no
+     * need to override this method.
+     *
+     *
+     *
+     * @param req	the {@link HttpServletRequest} object that
+     *			contains the request the client made of
+     *			the servlet
+     *
+     *
+     * @param resp	the {@link HttpServletResponse} object that
+     *			contains the response the servlet returns
+     *			to the client				
+     *
+     *
+     * @exception IOException	if an input or output error occurs
+     *				while the servlet is handling the
+     *				HTTP request
+     *
+     * @exception ServletException	if the HTTP request
+     *					cannot be handled
+     * 
+     * @see 				javax.servlet.Servlet#service
+     *
+     */
+
+    protected void service(HttpServletRequest req, HttpServletResponse resp)
+	throws ServletException, IOException
+    {
+	String method = req.getMethod();
+
+	if (method.equals(METHOD_GET)) {
+	    long lastModified = getLastModified(req);
+	    if (lastModified == -1) {
+		// servlet doesn't support if-modified-since, no reason
+		// to go through further expensive logic
+		doGet(req, resp);
+	    } else {
+		long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
+		if (ifModifiedSince < (lastModified / 1000 * 1000)) {
+		    // If the servlet mod time is later, call doGet()
+                    // Round down to the nearest second for a proper compare
+                    // A ifModifiedSince of -1 will always be less
+		    maybeSetLastModified(resp, lastModified);
+		    doGet(req, resp);
+		} else {
+		    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+		}
+	    }
+
+	} else if (method.equals(METHOD_HEAD)) {
+	    long lastModified = getLastModified(req);
+	    maybeSetLastModified(resp, lastModified);
+	    doHead(req, resp);
+
+	} else if (method.equals(METHOD_POST)) {
+	    doPost(req, resp);
+	    
+	} else if (method.equals(METHOD_PUT)) {
+	    doPut(req, resp);	
+	    
+	} else if (method.equals(METHOD_DELETE)) {
+	    doDelete(req, resp);
+	    
+	} else if (method.equals(METHOD_OPTIONS)) {
+	    doOptions(req,resp);
+	    
+	} else if (method.equals(METHOD_TRACE)) {
+	    doTrace(req,resp);
+	    
+	} else {
+	    //
+	    // Note that this means NO servlet supports whatever
+	    // method was requested, anywhere on this server.
+	    //
+
+	    String errMsg = lStrings.getString("http.method_not_implemented");
+	    Object[] errArgs = new Object[1];
+	    errArgs[0] = method;
+	    errMsg = MessageFormat.format(errMsg, errArgs);
+	    
+	    resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
+	}
+    }
+    
+
+
+
+
+    /*
+     * Sets the Last-Modified entity header field, if it has not
+     * already been set and if the value is meaningful.  Called before
+     * doGet, to ensure that headers are set before response data is
+     * written.  A subclass might have set this header already, so we
+     * check.
+     */
+
+    private void maybeSetLastModified(HttpServletResponse resp,
+				      long lastModified) {
+	if (resp.containsHeader(HEADER_LASTMOD))
+	    return;
+	if (lastModified >= 0)
+	    resp.setDateHeader(HEADER_LASTMOD, lastModified);
+    }
+   
+   
+   
+    
+    /**
+     *
+     * Dispatches client requests to the protected
+     * <code>service</code> method. There's no need to
+     * override this method.
+     *
+     * 
+     * @param req	the {@link HttpServletRequest} object that
+     *			contains the request the client made of
+     *			the servlet
+     *
+     *
+     * @param res	the {@link HttpServletResponse} object that
+     *			contains the response the servlet returns
+     *			to the client				
+     *
+     *
+     * @exception IOException	if an input or output error occurs
+     *				while the servlet is handling the
+     *				HTTP request
+     *
+     * @exception ServletException	if the HTTP request cannot
+     *					be handled
+     *
+     * 
+     * @see javax.servlet.Servlet#service
+     *
+     */
+
+    public void service(ServletRequest req, ServletResponse res)
+	throws ServletException, IOException
+    {
+	HttpServletRequest	request;
+	HttpServletResponse	response;
+	
+	try {
+	    request = (HttpServletRequest) req;
+	    response = (HttpServletResponse) res;
+	} catch (ClassCastException e) {
+	    throw new ServletException("non-HTTP request or response");
+	}
+	service(request, response);
+    }
+}
+
+
+
+
+/*
+ * A response that includes no body, for use in (dumb) "HEAD" support.
+ * This just swallows that body, counting the bytes in order to set
+ * the content length appropriately.  All other methods delegate directly
+ * to the HTTP Servlet Response object used to construct this one.
+ */
+// file private
+class NoBodyResponse implements HttpServletResponse {
+    private HttpServletResponse		resp;
+    private NoBodyOutputStream		noBody;
+    private PrintWriter			writer;
+    private boolean			didSetContentLength;
+
+    // file private
+    NoBodyResponse(HttpServletResponse r) {
+	resp = r;
+	noBody = new NoBodyOutputStream();
+    }
+
+    // file private
+    void setContentLength() {
+	if (!didSetContentLength)
+	  resp.setContentLength(noBody.getContentLength());
+    }
+
+
+    // SERVLET RESPONSE interface methods
+
+    public void setContentLength(int len) {
+	resp.setContentLength(len);
+	didSetContentLength = true;
+    }
+
+    public void setCharacterEncoding(String charset)
+      { resp.setCharacterEncoding(charset); }
+
+    public void setContentType(String type)
+      { resp.setContentType(type); }
+
+    public String getContentType()
+      { return resp.getContentType(); }
+
+    public ServletOutputStream getOutputStream() throws IOException
+      { return noBody; }
+
+    public String getCharacterEncoding()
+	{ return resp.getCharacterEncoding(); }
+
+    public PrintWriter getWriter() throws UnsupportedEncodingException
+    {
+	if (writer == null) {
+	    OutputStreamWriter	w;
+
+	    w = new OutputStreamWriter(noBody, getCharacterEncoding());
+	    writer = new PrintWriter(w);
+	}
+	return writer;
+    }
+
+    public void setBufferSize(int size) throws IllegalStateException
+      { resp.setBufferSize(size); }
+
+    public int getBufferSize()
+      { return resp.getBufferSize(); }
+
+    public void reset() throws IllegalStateException
+      { resp.reset(); }
+      
+      public void resetBuffer() throws IllegalStateException
+      { resp.resetBuffer(); }
+
+    public boolean isCommitted()
+      { return resp.isCommitted(); }
+
+    public void flushBuffer() throws IOException
+      { resp.flushBuffer(); }
+
+    public void setLocale(Locale loc)
+      { resp.setLocale(loc); }
+
+    public Locale getLocale()
+      { return resp.getLocale(); }
+
+
+    // HTTP SERVLET RESPONSE interface methods
+
+    public void addCookie(Cookie cookie)
+      { resp.addCookie(cookie); }
+
+    public boolean containsHeader(String name)
+      { return resp.containsHeader(name); }
+
+    /** @deprecated */
+    public void setStatus(int sc, String sm)
+      { resp.setStatus(sc, sm); }
+
+    public void setStatus(int sc)
+      { resp.setStatus(sc); }
+
+    public void setHeader(String name, String value)
+      { resp.setHeader(name, value); }
+
+    public void setIntHeader(String name, int value)
+      { resp.setIntHeader(name, value); }
+
+    public void setDateHeader(String name, long date)
+      { resp.setDateHeader(name, date); }
+
+    public void sendError(int sc, String msg) throws IOException
+      { resp.sendError(sc, msg); }
+
+    public void sendError(int sc) throws IOException
+      { resp.sendError(sc); }
+
+    public void sendRedirect(String location) throws IOException
+      { resp.sendRedirect(location); }
+    
+    public String encodeURL(String url) 
+      { return resp.encodeURL(url); }
+
+    public String encodeRedirectURL(String url)
+      { return resp.encodeRedirectURL(url); }
+      
+    public void addHeader(String name, String value)
+      { resp.addHeader(name, value); }
+      
+    public void addDateHeader(String name, long value)
+      { resp.addDateHeader(name, value); }
+      
+    public void addIntHeader(String name, int value)
+      { resp.addIntHeader(name, value); }
+      
+      
+      
+
+    /**
+     * @deprecated	As of Version 2.1, replaced by
+     * 			{@link HttpServletResponse#encodeURL}.
+     *
+     */
+     
+     
+    public String encodeUrl(String url) 
+      { return this.encodeURL(url); }
+      
+      
+      
+      
+      
+      
+      
+
+    /**
+     * @deprecated	As of Version 2.1, replaced by
+     *			{@link HttpServletResponse#encodeRedirectURL}.
+     *
+     */
+     
+     
+    public String encodeRedirectUrl(String url)
+      { return this.encodeRedirectURL(url); }
+
+}
+
+
+
+
+
+
+
+/*
+ * Servlet output stream that gobbles up all its data.
+ */
+ 
+// file private
+class NoBodyOutputStream extends ServletOutputStream {
+
+    private static final String LSTRING_FILE =
+	"javax.servlet.http.LocalStrings";
+    private static ResourceBundle lStrings =
+	ResourceBundle.getBundle(LSTRING_FILE);
+
+    private int		contentLength = 0;
+
+    // file private
+    NoBodyOutputStream() {}
+
+    // file private
+    int getContentLength() {
+	return contentLength;
+    }
+
+    public void write(int b) {
+	contentLength++;
+    }
+
+    public void write(byte buf[], int offset, int len)
+	throws IOException
+    {
+	if (len >= 0) {
+	    contentLength += len;
+	} else {
+	    // XXX
+	    // isn't this really an IllegalArgumentException?
+	    
+	    String msg = lStrings.getString("err.io.negativelength");
+	    throw new IOException("negative length");
+	}
+    }
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpServletRequest.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpServletRequest.java
new file mode 100644
index 0000000..c0a5d36
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpServletRequest.java
@@ -0,0 +1,660 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.http;
+
+import javax.servlet.ServletRequest;
+import java.util.Enumeration;
+
+/**
+ *
+ * Extends the {@link javax.servlet.ServletRequest} interface
+ * to provide request information for HTTP servlets. 
+ *
+ * <p>The servlet container creates an <code>HttpServletRequest</code> 
+ * object and passes it as an argument to the servlet's service
+ * methods (<code>doGet</code>, <code>doPost</code>, etc).
+ *
+ *
+ * @author 	Various
+ * @version	$Version$
+ *
+ *
+ */
+
+public interface HttpServletRequest extends ServletRequest {
+
+    /**
+    * String identifier for Basic authentication. Value "BASIC"
+    */
+    public static final String BASIC_AUTH = "BASIC";
+    /**
+    * String identifier for Form authentication. Value "FORM"
+    */
+    public static final String FORM_AUTH = "FORM";
+    /**
+    * String identifier for Client Certificate authentication. Value "CLIENT_CERT"
+    */
+    public static final String CLIENT_CERT_AUTH = "CLIENT_CERT";
+    /**
+    * String identifier for Digest authentication. Value "DIGEST"
+    */
+    public static final String DIGEST_AUTH = "DIGEST";
+
+    /**
+     * Returns the name of the authentication scheme used to protect
+     * the servlet. All servlet containers support basic, form and client 
+     * certificate authentication, and may additionally support digest 
+     * authentication.
+     * If the servlet is not authenticated <code>null</code> is returned. 
+     *
+     * <p>Same as the value of the CGI variable AUTH_TYPE.
+     *
+     *
+     * @return		one of the static members BASIC_AUTH, 
+     *			FORM_AUTH, CLIENT_CERT_AUTH, DIGEST_AUTH
+     *			(suitable for == comparison) or
+     *			the container-specific string indicating
+     *			the authentication scheme, or
+     *			<code>null</code> if the request was 
+     *			not authenticated.     
+     *
+     */
+   
+    public String getAuthType();
+    
+   
+    
+
+    /**
+     *
+     * Returns an array containing all of the <code>Cookie</code>
+     * objects the client sent with this request.
+     * This method returns <code>null</code> if no cookies were sent.
+     *
+     * @return		an array of all the <code>Cookies</code>
+     *			included with this request, or <code>null</code>
+     *			if the request has no cookies
+     *
+     *
+     */
+
+    public Cookie[] getCookies();
+    
+    
+    
+
+    /**
+     *
+     * Returns the value of the specified request header
+     * as a <code>long</code> value that represents a 
+     * <code>Date</code> object. Use this method with
+     * headers that contain dates, such as
+     * <code>If-Modified-Since</code>. 
+     *
+     * <p>The date is returned as
+     * the number of milliseconds since January 1, 1970 GMT.
+     * The header name is case insensitive.
+     *
+     * <p>If the request did not have a header of the
+     * specified name, this method returns -1. If the header
+     * can't be converted to a date, the method throws
+     * an <code>IllegalArgumentException</code>.
+     *
+     * @param name		a <code>String</code> specifying the
+     *				name of the header
+     *
+     * @return			a <code>long</code> value
+     *				representing the date specified
+     *				in the header expressed as
+     *				the number of milliseconds
+     *				since January 1, 1970 GMT,
+     *				or -1 if the named header
+     *				was not included with the
+     *				request
+     *
+     * @exception	IllegalArgumentException	If the header value
+     *							can't be converted
+     *							to a date
+     *
+     */
+
+    public long getDateHeader(String name);
+    
+    
+    
+
+    /**
+     *
+     * Returns the value of the specified request header
+     * as a <code>String</code>. If the request did not include a header
+     * of the specified name, this method returns <code>null</code>.
+     * If there are multiple headers with the same name, this method
+     * returns the first head in the request.
+     * The header name is case insensitive. You can use
+     * this method with any request header.
+     *
+     * @param name		a <code>String</code> specifying the
+     *				header name
+     *
+     * @return			a <code>String</code> containing the
+     *				value of the requested
+     *				header, or <code>null</code>
+     *				if the request does not
+     *				have a header of that name
+     *
+     */			
+
+    public String getHeader(String name); 
+
+
+
+
+    /**
+     *
+     * Returns all the values of the specified request header
+     * as an <code>Enumeration</code> of <code>String</code> objects.
+     *
+     * <p>Some headers, such as <code>Accept-Language</code> can be sent
+     * by clients as several headers each with a different value rather than
+     * sending the header as a comma separated list.
+     *
+     * <p>If the request did not include any headers
+     * of the specified name, this method returns an empty
+     * <code>Enumeration</code>.
+     * The header name is case insensitive. You can use
+     * this method with any request header.
+     *
+     * @param name		a <code>String</code> specifying the
+     *				header name
+     *
+     * @return			an <code>Enumeration</code> containing
+     *                  	the values of the requested header. If
+     *                  	the request does not have any headers of
+     *                  	that name return an empty
+     *                  	enumeration. If 
+     *                  	the container does not allow access to
+     *                  	header information, return null
+     *
+     */			
+
+    public Enumeration getHeaders(String name); 
+    
+    
+    
+    
+
+    /**
+     *
+     * Returns an enumeration of all the header names
+     * this request contains. If the request has no
+     * headers, this method returns an empty enumeration.
+     *
+     * <p>Some servlet containers do not allow
+     * servlets to access headers using this method, in
+     * which case this method returns <code>null</code>
+     *
+     * @return			an enumeration of all the
+     *				header names sent with this
+     *				request; if the request has
+     *				no headers, an empty enumeration;
+     *				if the servlet container does not
+     *				allow servlets to use this method,
+     *				<code>null</code>
+     *				
+     *
+     */
+
+    public Enumeration getHeaderNames();
+    
+    
+    
+
+    /**
+     *
+     * Returns the value of the specified request header
+     * as an <code>int</code>. If the request does not have a header
+     * of the specified name, this method returns -1. If the
+     * header cannot be converted to an integer, this method
+     * throws a <code>NumberFormatException</code>.
+     *
+     * <p>The header name is case insensitive.
+     *
+     * @param name		a <code>String</code> specifying the name
+     *				of a request header
+     *
+     * @return			an integer expressing the value 
+     * 				of the request header or -1
+     *				if the request doesn't have a
+     *				header of this name
+     *
+     * @exception	NumberFormatException		If the header value
+     *							can't be converted
+     *							to an <code>int</code>
+     */
+
+    public int getIntHeader(String name);
+    
+    
+    
+
+    /**
+     *
+     * Returns the name of the HTTP method with which this 
+     * request was made, for example, GET, POST, or PUT.
+     * Same as the value of the CGI variable REQUEST_METHOD.
+     *
+     * @return			a <code>String</code> 
+     *				specifying the name
+     *				of the method with which
+     *				this request was made
+     *
+     */
+ 
+    public String getMethod();
+    
+    
+    
+
+    /**
+     *
+     * Returns any extra path information associated with
+     * the URL the client sent when it made this request.
+     * The extra path information follows the servlet path
+     * but precedes the query string and will start with
+     * a "/" character.
+     *
+     * <p>This method returns <code>null</code> if there
+     * was no extra path information.
+     *
+     * <p>Same as the value of the CGI variable PATH_INFO.
+     *
+     *
+     * @return		a <code>String</code>, decoded by the
+     *			web container, specifying 
+     *			extra path information that comes
+     *			after the servlet path but before
+     *			the query string in the request URL;
+     *			or <code>null</code> if the URL does not have
+     *			any extra path information
+     *
+     */
+     
+    public String getPathInfo();
+    
+
+ 
+
+    /**
+     *
+     * Returns any extra path information after the servlet name
+     * but before the query string, and translates it to a real
+     * path. Same as the value of the CGI variable PATH_TRANSLATED.
+     *
+     * <p>If the URL does not have any extra path information,
+     * this method returns <code>null</code> or the servlet container
+     * cannot translate the virtual path to a real path for any reason
+     * (such as when the web application is executed from an archive).
+     *
+     * The web container does not decode this string.
+     *
+     *
+     * @return		a <code>String</code> specifying the
+     *			real path, or <code>null</code> if
+     *			the URL does not have any extra path
+     *			information
+     *
+     *
+     */
+
+    public String getPathTranslated();
+    
+
+ 
+
+    /**
+     *
+     * Returns the portion of the request URI that indicates the context
+     * of the request.  The context path always comes first in a request
+     * URI.  The path starts with a "/" character but does not end with a "/"
+     * character.  For servlets in the default (root) context, this method
+     * returns "". The container does not decode this string.
+     *
+     *
+     * @return		a <code>String</code> specifying the
+     *			portion of the request URI that indicates the context
+     *			of the request
+     *
+     *
+     */
+
+    public String getContextPath();
+    
+    
+    
+
+    /**
+     *
+     * Returns the query string that is contained in the request
+     * URL after the path. This method returns <code>null</code>
+     * if the URL does not have a query string. Same as the value
+     * of the CGI variable QUERY_STRING. 
+     *
+     * @return		a <code>String</code> containing the query
+     *			string or <code>null</code> if the URL 
+     *			contains no query string. The value is not
+     *			decoded by the container.
+     *
+     */
+
+    public String getQueryString();
+    
+    
+    
+
+    /**
+     *
+     * Returns the login of the user making this request, if the
+     * user has been authenticated, or <code>null</code> if the user 
+     * has not been authenticated.
+     * Whether the user name is sent with each subsequent request
+     * depends on the browser and type of authentication. Same as the 
+     * value of the CGI variable REMOTE_USER.
+     *
+     * @return		a <code>String</code> specifying the login
+     *			of the user making this request, or <code>null</code>
+     *			if the user login is not known
+     *
+     */
+
+    public String getRemoteUser();
+    
+    
+    
+
+    /**
+     *
+     * Returns a boolean indicating whether the authenticated user is included
+     * in the specified logical "role".  Roles and role membership can be
+     * defined using deployment descriptors.  If the user has not been
+     * authenticated, the method returns <code>false</code>.
+     *
+     * @param role		a <code>String</code> specifying the name
+     *				of the role
+     *
+     * @return		a <code>boolean</code> indicating whether
+     *			the user making this request belongs to a given role;
+     *			<code>false</code> if the user has not been 
+     *			authenticated
+     *
+     */
+
+    public boolean isUserInRole(String role);
+    
+    
+    
+
+    /**
+     *
+     * Returns a <code>java.security.Principal</code> object containing
+     * the name of the current authenticated user. If the user has not been
+     * authenticated, the method returns <code>null</code>.
+     *
+     * @return		a <code>java.security.Principal</code> containing
+     *			the name of the user making this request;
+     *			<code>null</code> if the user has not been 
+     *			authenticated
+     *
+     */
+
+    public java.security.Principal getUserPrincipal();
+    
+    
+    
+
+    /**
+     *
+     * Returns the session ID specified by the client. This may
+     * not be the same as the ID of the current valid session
+     * for this request.
+     * If the client did not specify a session ID, this method returns
+     * <code>null</code>.
+     *
+     *
+     * @return		a <code>String</code> specifying the session
+     *			ID, or <code>null</code> if the request did
+     *			not specify a session ID
+     *
+     * @see		#isRequestedSessionIdValid
+     *
+     */
+
+    public String getRequestedSessionId();
+    
+    
+    
+    
+    /**
+     *
+     * Returns the part of this request's URL from the protocol
+     * name up to the query string in the first line of the HTTP request.
+     * The web container does not decode this String.
+     * For example:
+     *
+     * 
+
+     * <table summary="Examples of Returned Values">
+     * <tr align=left><th>First line of HTTP request      </th>
+     * <th>     Returned Value</th>
+     * <tr><td>POST /some/path.html HTTP/1.1<td><td>/some/path.html
+     * <tr><td>GET http://foo.bar/a.html HTTP/1.0
+     * <td><td>/a.html
+     * <tr><td>HEAD /xyz?a=b HTTP/1.1<td><td>/xyz
+     * </table>
+     *
+     * <p>To reconstruct an URL with a scheme and host, use
+     * {@link HttpUtils#getRequestURL}.
+     *
+     * @return		a <code>String</code> containing
+     *			the part of the URL from the 
+     *			protocol name up to the query string
+     *
+     * @see		HttpUtils#getRequestURL
+     *
+     */
+
+    public String getRequestURI();
+    
+    /**
+     *
+     * Reconstructs the URL the client used to make the request.
+     * The returned URL contains a protocol, server name, port
+     * number, and server path, but it does not include query
+     * string parameters.
+     *
+     * <p>Because this method returns a <code>StringBuffer</code>,
+     * not a string, you can modify the URL easily, for example,
+     * to append query parameters.
+     *
+     * <p>This method is useful for creating redirect messages
+     * and for reporting errors.
+     *
+     * @return		a <code>StringBuffer</code> object containing
+     *			the reconstructed URL
+     *
+     */
+    public StringBuffer getRequestURL();
+    
+
+    /**
+     *
+     * Returns the part of this request's URL that calls
+     * the servlet. This path starts with a "/" character
+     * and includes either the servlet name or a path to
+     * the servlet, but does not include any extra path
+     * information or a query string. Same as the value of
+     * the CGI variable SCRIPT_NAME.
+     *
+     * <p>This method will return an empty string ("") if the
+     * servlet used to process this request was matched using
+     * the "/*" pattern.
+     *
+     * @return		a <code>String</code> containing
+     *			the name or path of the servlet being
+     *			called, as specified in the request URL,
+     *			decoded, or an empty string if the servlet
+     *			used to process the request is matched
+     *			using the "/*" pattern.
+     *
+     */
+
+    public String getServletPath();
+    
+    
+    
+
+    /**
+     *
+     * Returns the current <code>HttpSession</code>
+     * associated with this request or, if there is no
+     * current session and <code>create</code> is true, returns 
+     * a new session.
+     *
+     * <p>If <code>create</code> is <code>false</code>
+     * and the request has no valid <code>HttpSession</code>,
+     * this method returns <code>null</code>.
+     *
+     * <p>To make sure the session is properly maintained,
+     * you must call this method before 
+     * the response is committed. If the container is using cookies
+     * to maintain session integrity and is asked to create a new session
+     * when the response is committed, an IllegalStateException is thrown.
+     *
+     *
+     *
+     *
+     * @param create	<code>true</code> to create
+     *			a new session for this request if necessary; 
+     *			<code>false</code> to return <code>null</code>
+     *			if there's no current session
+     *			
+     *
+     * @return 		the <code>HttpSession</code> associated 
+     *			with this request or <code>null</code> if
+     * 			<code>create</code> is <code>false</code>
+     *			and the request has no valid session
+     *
+     * @see	#getSession()
+     *
+     *
+     */
+
+    public HttpSession getSession(boolean create);
+    
+    
+    
+   
+
+    /**
+     *
+     * Returns the current session associated with this request,
+     * or if the request does not have a session, creates one.
+     * 
+     * @return		the <code>HttpSession</code> associated
+     *			with this request
+     *
+     * @see	#getSession(boolean)
+     *
+     */
+
+    public HttpSession getSession();
+    
+    
+    
+    
+    
+
+    /**
+     *
+     * Checks whether the requested session ID is still valid.
+     *
+     * @return			<code>true</code> if this
+     *				request has an id for a valid session
+     *				in the current session context;
+     *				<code>false</code> otherwise
+     *
+     * @see			#getRequestedSessionId
+     * @see			#getSession
+     * @see			HttpSessionContext
+     *
+     */
+
+    public boolean isRequestedSessionIdValid();
+    
+    
+    
+
+    /**
+     *
+     * Checks whether the requested session ID came in as a cookie.
+     *
+     * @return			<code>true</code> if the session ID
+     *				came in as a
+     *				cookie; otherwise, <code>false</code>
+     *
+     *
+     * @see			#getSession
+     *
+     */ 
+
+    public boolean isRequestedSessionIdFromCookie();
+    
+    
+    
+
+    /**
+     *
+     * Checks whether the requested session ID came in as part of the 
+     * request URL.
+     *
+     * @return			<code>true</code> if the session ID
+     *				came in as part of a URL; otherwise,
+     *				<code>false</code>
+     *
+     *
+     * @see			#getSession
+     *
+     */
+    
+    public boolean isRequestedSessionIdFromURL();
+    
+    
+    
+    
+    
+    /**
+     *
+     * @deprecated		As of Version 2.1 of the Java Servlet
+     *				API, use {@link #isRequestedSessionIdFromURL}
+     *				instead.
+     *
+     */
+
+    public boolean isRequestedSessionIdFromUrl();
+
+
+    
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpServletRequestWrapper.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpServletRequestWrapper.java
new file mode 100644
index 0000000..361e38f
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpServletRequestWrapper.java
@@ -0,0 +1,262 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+import javax.servlet.ServletRequestWrapper;
+import java.util.Enumeration;
+
+/**
+ * 
+ * Provides a convenient implementation of the HttpServletRequest interface that
+ * can be subclassed by developers wishing to adapt the request to a Servlet.
+ * This class implements the Wrapper or Decorator pattern. Methods default to
+ * calling through to the wrapped request object.
+ * 
+ *
+ * @see 	javax.servlet.http.HttpServletRequest
+  * @since	v 2.3
+ *
+ */
+
+
+public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest {
+
+	/** 
+	* Constructs a request object wrapping the given request.
+	* @throws java.lang.IllegalArgumentException if the request is null
+	*/
+    public HttpServletRequestWrapper(HttpServletRequest request) {
+	    super(request);
+    }
+    
+    private HttpServletRequest _getHttpServletRequest() {
+	return (HttpServletRequest) super.getRequest();
+    }
+
+    /**
+     * The default behavior of this method is to return getAuthType()
+     * on the wrapped request object.
+     */
+
+    public String getAuthType() {
+	return this._getHttpServletRequest().getAuthType();
+    }
+   
+    /**
+     * The default behavior of this method is to return getCookies()
+     * on the wrapped request object.
+     */
+    public Cookie[] getCookies() {
+	return this._getHttpServletRequest().getCookies();
+    }
+
+    /**
+     * The default behavior of this method is to return getDateHeader(String name)
+     * on the wrapped request object.
+     */
+    public long getDateHeader(String name) {
+	return this._getHttpServletRequest().getDateHeader(name);
+    }
+        	
+    /**
+     * The default behavior of this method is to return getHeader(String name)
+     * on the wrapped request object.
+     */
+    public String getHeader(String name) {
+	return this._getHttpServletRequest().getHeader(name);
+    }
+    
+    /**
+     * The default behavior of this method is to return getHeaders(String name)
+     * on the wrapped request object.
+     */
+    public Enumeration getHeaders(String name) {
+	return this._getHttpServletRequest().getHeaders(name);
+    }  
+
+    /**
+     * The default behavior of this method is to return getHeaderNames()
+     * on the wrapped request object.
+     */
+  
+    public Enumeration getHeaderNames() {
+	return this._getHttpServletRequest().getHeaderNames();
+    }
+    
+    /**
+     * The default behavior of this method is to return getIntHeader(String name)
+     * on the wrapped request object.
+     */
+
+     public int getIntHeader(String name) {
+	return this._getHttpServletRequest().getIntHeader(name);
+    }
+    
+    /**
+     * The default behavior of this method is to return getMethod()
+     * on the wrapped request object.
+     */
+    public String getMethod() {
+	return this._getHttpServletRequest().getMethod();
+    }
+    
+    /**
+     * The default behavior of this method is to return getPathInfo()
+     * on the wrapped request object.
+     */
+    public String getPathInfo() {
+	return this._getHttpServletRequest().getPathInfo();
+    }
+
+    /**
+     * The default behavior of this method is to return getPathTranslated()
+     * on the wrapped request object.
+     */
+
+     public String getPathTranslated() {
+	return this._getHttpServletRequest().getPathTranslated();
+    }
+
+    /**
+     * The default behavior of this method is to return getContextPath()
+     * on the wrapped request object.
+     */
+    public String getContextPath() {
+	return this._getHttpServletRequest().getContextPath();
+    }
+    
+    /**
+     * The default behavior of this method is to return getQueryString()
+     * on the wrapped request object.
+     */
+    public String getQueryString() {
+	return this._getHttpServletRequest().getQueryString();
+    }
+    
+    /**
+     * The default behavior of this method is to return getRemoteUser()
+     * on the wrapped request object.
+     */
+    public String getRemoteUser() {
+	return this._getHttpServletRequest().getRemoteUser();
+    }
+    
+ 
+    /**
+     * The default behavior of this method is to return isUserInRole(String role)
+     * on the wrapped request object.
+     */
+    public boolean isUserInRole(String role) {
+	return this._getHttpServletRequest().isUserInRole(role);
+    }
+    
+    
+    
+    /**
+     * The default behavior of this method is to return getUserPrincipal()
+     * on the wrapped request object.
+     */
+    public java.security.Principal getUserPrincipal() {
+	return this._getHttpServletRequest().getUserPrincipal();
+    }
+    
+   
+    /**
+     * The default behavior of this method is to return getRequestedSessionId()
+     * on the wrapped request object.
+     */
+    public String getRequestedSessionId() {
+	return this._getHttpServletRequest().getRequestedSessionId();
+    }
+    
+    /**
+     * The default behavior of this method is to return getRequestURI()
+     * on the wrapped request object.
+     */
+    public String getRequestURI() {
+	return this._getHttpServletRequest().getRequestURI();
+    }
+	/**
+     * The default behavior of this method is to return getRequestURL()
+     * on the wrapped request object.
+     */
+    public StringBuffer getRequestURL() {
+	return this._getHttpServletRequest().getRequestURL();
+    }
+	
+    
+    /**
+     * The default behavior of this method is to return getServletPath()
+     * on the wrapped request object.
+     */
+    public String getServletPath() {
+	return this._getHttpServletRequest().getServletPath();
+    }
+    
+    
+    /**
+     * The default behavior of this method is to return getSession(boolean create)
+     * on the wrapped request object.
+     */
+    public HttpSession getSession(boolean create) {
+	return this._getHttpServletRequest().getSession(create);
+    }
+    
+    /**
+     * The default behavior of this method is to return getSession()
+     * on the wrapped request object.
+     */
+    public HttpSession getSession() {
+	return this._getHttpServletRequest().getSession();
+    }
+    
+    /**
+     * The default behavior of this method is to return isRequestedSessionIdValid()
+     * on the wrapped request object.
+     */ 
+
+    public boolean isRequestedSessionIdValid() {
+	return this._getHttpServletRequest().isRequestedSessionIdValid();
+    }
+     
+    
+    /**
+     * The default behavior of this method is to return isRequestedSessionIdFromCookie()
+     * on the wrapped request object.
+     */
+    public boolean isRequestedSessionIdFromCookie() {
+	return this._getHttpServletRequest().isRequestedSessionIdFromCookie();
+    }
+    
+    	  /**
+     * The default behavior of this method is to return isRequestedSessionIdFromURL()
+     * on the wrapped request object.
+     */ 
+    public boolean isRequestedSessionIdFromURL() {
+	return this._getHttpServletRequest().isRequestedSessionIdFromURL();
+    }
+    
+    /**
+     * The default behavior of this method is to return isRequestedSessionIdFromUrl()
+     * on the wrapped request object.
+     */
+    public boolean isRequestedSessionIdFromUrl() {
+	return this._getHttpServletRequest().isRequestedSessionIdFromUrl();
+    }
+
+
+    
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpServletResponse.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpServletResponse.java
new file mode 100644
index 0000000..6179050
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpServletResponse.java
@@ -0,0 +1,636 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+import java.io.IOException;
+
+import javax.servlet.ServletResponse;
+
+/**
+ *
+ * Extends the {@link ServletResponse} interface to provide HTTP-specific
+ * functionality in sending a response.  For example, it has methods
+ * to access HTTP headers and cookies.
+ *
+ * <p>The servlet container creates an <code>HttpServletResponse</code> object
+ * and passes it as an argument to the servlet's service methods
+ * (<code>doGet</code>, <code>doPost</code>, etc).
+ *
+ * 
+ * @author	Various
+ * @version	$Version$
+ *
+ * @see		javax.servlet.ServletResponse
+ *
+ */
+
+
+
+public interface HttpServletResponse extends ServletResponse {
+
+    /**
+     * Adds the specified cookie to the response.  This method can be called
+     * multiple times to set more than one cookie.
+     *
+     * @param cookie the Cookie to return to the client
+     *
+     */
+
+    public void addCookie(Cookie cookie);
+
+    /**
+     * Returns a boolean indicating whether the named response header 
+     * has already been set.
+     * 
+     * @param	name	the header name
+     * @return		<code>true</code> if the named response header 
+     *			has already been set; 
+     * 			<code>false</code> otherwise
+     */
+
+    public boolean containsHeader(String name);
+
+    /**
+     * Encodes the specified URL by including the session ID in it,
+     * or, if encoding is not needed, returns the URL unchanged.
+     * The implementation of this method includes the logic to
+     * determine whether the session ID needs to be encoded in the URL.
+     * For example, if the browser supports cookies, or session
+     * tracking is turned off, URL encoding is unnecessary.
+     * 
+     * <p>For robust session tracking, all URLs emitted by a servlet 
+     * should be run through this
+     * method.  Otherwise, URL rewriting cannot be used with browsers
+     * which do not support cookies.
+     *
+     * @param	url	the url to be encoded.
+     * @return		the encoded URL if encoding is needed;
+     * 			the unchanged URL otherwise.
+     */
+
+    public String encodeURL(String url);
+
+    /**
+     * Encodes the specified URL for use in the
+     * <code>sendRedirect</code> method or, if encoding is not needed,
+     * returns the URL unchanged.  The implementation of this method
+     * includes the logic to determine whether the session ID
+     * needs to be encoded in the URL.  Because the rules for making
+     * this determination can differ from those used to decide whether to
+     * encode a normal link, this method is separated from the
+     * <code>encodeURL</code> method.
+     * 
+     * <p>All URLs sent to the <code>HttpServletResponse.sendRedirect</code>
+     * method should be run through this method.  Otherwise, URL
+     * rewriting cannot be used with browsers which do not support
+     * cookies.
+     *
+     * @param	url	the url to be encoded.
+     * @return		the encoded URL if encoding is needed;
+     * 			the unchanged URL otherwise.
+     *
+     * @see #sendRedirect
+     * @see #encodeUrl
+     */
+
+    public String encodeRedirectURL(String url);
+
+    /**
+     * @deprecated	As of version 2.1, use encodeURL(String url) instead
+     *
+     * @param	url	the url to be encoded.
+     * @return		the encoded URL if encoding is needed; 
+     * 			the unchanged URL otherwise.
+     */
+
+    public String encodeUrl(String url);
+    
+    /**
+     * @deprecated	As of version 2.1, use 
+     *			encodeRedirectURL(String url) instead
+     *
+     * @param	url	the url to be encoded.
+     * @return		the encoded URL if encoding is needed; 
+     * 			the unchanged URL otherwise.
+     */
+
+    public String encodeRedirectUrl(String url);
+
+    /**
+     * Sends an error response to the client using the specified
+     * status.  The server defaults to creating the
+     * response to look like an HTML-formatted server error page
+     * containing the specified message, setting the content type
+     * to "text/html", leaving cookies and other headers unmodified.
+     *
+     * If an error-page declaration has been made for the web application
+     * corresponding to the status code passed in, it will be served back in 
+     * preference to the suggested msg parameter. 
+     *
+     * <p>If the response has already been committed, this method throws 
+     * an IllegalStateException.
+     * After using this method, the response should be considered
+     * to be committed and should not be written to.
+     *
+     * @param	sc	the error status code
+     * @param	msg	the descriptive message
+     * @exception	IOException	If an input or output exception occurs
+     * @exception	IllegalStateException	If the response was committed
+     */
+   
+    public void sendError(int sc, String msg) throws IOException;
+
+    /**
+     * Sends an error response to the client using the specified status
+     * code and clearing the buffer. 
+     * <p>If the response has already been committed, this method throws 
+     * an IllegalStateException.
+     * After using this method, the response should be considered
+     * to be committed and should not be written to.
+     *
+     * @param	sc	the error status code
+     * @exception	IOException	If an input or output exception occurs
+     * @exception	IllegalStateException	If the response was committed
+     *						before this method call
+     */
+
+    public void sendError(int sc) throws IOException;
+
+    /**
+     * Sends a temporary redirect response to the client using the
+     * specified redirect location URL.  This method can accept relative URLs;
+     * the servlet container must convert the relative URL to an absolute URL
+     * before sending the response to the client. If the location is relative 
+     * without a leading '/' the container interprets it as relative to
+     * the current request URI. If the location is relative with a leading
+     * '/' the container interprets it as relative to the servlet container root.
+     *
+     * <p>If the response has already been committed, this method throws 
+     * an IllegalStateException.
+     * After using this method, the response should be considered
+     * to be committed and should not be written to.
+     *
+     * @param		location	the redirect location URL
+     * @exception	IOException	If an input or output exception occurs
+     * @exception	IllegalStateException	If the response was committed or
+ if a partial URL is given and cannot be converted into a valid URL
+     */
+
+    public void sendRedirect(String location) throws IOException;
+    
+    /**
+     * 
+     * Sets a response header with the given name and
+     * date-value.  The date is specified in terms of
+     * milliseconds since the epoch.  If the header had already
+     * been set, the new value overwrites the previous one.  The
+     * <code>containsHeader</code> method can be used to test for the
+     * presence of a header before setting its value.
+     * 
+     * @param	name	the name of the header to set
+     * @param	date	the assigned date value
+     * 
+     * @see #containsHeader
+     * @see #addDateHeader
+     */
+
+    public void setDateHeader(String name, long date);
+    
+    /**
+     * 
+     * Adds a response header with the given name and
+     * date-value.  The date is specified in terms of
+     * milliseconds since the epoch.  This method allows response headers 
+     * to have multiple values.
+     * 
+     * @param	name	the name of the header to set
+     * @param	date	the additional date value
+     * 
+     * @see #setDateHeader
+     */
+
+    public void addDateHeader(String name, long date);
+    
+    /**
+     *
+     * Sets a response header with the given name and value.
+     * If the header had already been set, the new value overwrites the
+     * previous one.  The <code>containsHeader</code> method can be
+     * used to test for the presence of a header before setting its
+     * value.
+     * 
+     * @param	name	the name of the header
+     * @param	value	the header value  If it contains octet string,
+     *		it should be encoded according to RFC 2047
+     *		(http://www.ietf.org/rfc/rfc2047.txt)
+     *
+     * @see #containsHeader
+     * @see #addHeader
+     */
+
+    public void setHeader(String name, String value);
+    
+    /**
+     * Adds a response header with the given name and value.
+     * This method allows response headers to have multiple values.
+     * 
+     * @param	name	the name of the header
+     * @param	value	the additional header value   If it contains
+     *		octet string, it should be encoded
+     *		according to RFC 2047
+     *		(http://www.ietf.org/rfc/rfc2047.txt)
+     *
+     * @see #setHeader
+     */
+
+    public void addHeader(String name, String value);
+
+    /**
+     * Sets a response header with the given name and
+     * integer value.  If the header had already been set, the new value
+     * overwrites the previous one.  The <code>containsHeader</code>
+     * method can be used to test for the presence of a header before
+     * setting its value.
+     *
+     * @param	name	the name of the header
+     * @param	value	the assigned integer value
+     *
+     * @see #containsHeader
+     * @see #addIntHeader
+     */
+
+    public void setIntHeader(String name, int value);
+
+    /**
+     * Adds a response header with the given name and
+     * integer value.  This method allows response headers to have multiple
+     * values.
+     *
+     * @param	name	the name of the header
+     * @param	value	the assigned integer value
+     *
+     * @see #setIntHeader
+     */
+
+    public void addIntHeader(String name, int value);
+
+
+    
+    /**
+     * Sets the status code for this response.  This method is used to
+     * set the return status code when there is no error (for example,
+     * for the status codes SC_OK or SC_MOVED_TEMPORARILY).  If there
+     * is an error, and the caller wishes to invoke an error-page defined
+     * in the web application, the <code>sendError</code> method should be used
+     * instead.
+     * <p> The container clears the buffer and sets the Location header, preserving
+     * cookies and other headers.
+     *
+     * @param	sc	the status code
+     *
+     * @see #sendError
+     */
+
+    public void setStatus(int sc);
+  
+    /**
+     * @deprecated As of version 2.1, due to ambiguous meaning of the 
+     * message parameter. To set a status code 
+     * use <code>setStatus(int)</code>, to send an error with a description
+     * use <code>sendError(int, String)</code>.
+     *
+     * Sets the status code and message for this response.
+     * 
+     * @param	sc	the status code
+     * @param	sm	the status message
+     */
+
+    public void setStatus(int sc, String sm);
+
+    
+    /*
+     * Server status codes; see RFC 2068.
+     */
+
+    /**
+     * Status code (100) indicating the client can continue.
+     */
+
+    public static final int SC_CONTINUE = 100;
+
+    
+    /**
+     * Status code (101) indicating the server is switching protocols
+     * according to Upgrade header.
+     */
+
+    public static final int SC_SWITCHING_PROTOCOLS = 101;
+
+    /**
+     * Status code (200) indicating the request succeeded normally.
+     */
+
+    public static final int SC_OK = 200;
+
+    /**
+     * Status code (201) indicating the request succeeded and created
+     * a new resource on the server.
+     */
+
+    public static final int SC_CREATED = 201;
+
+    /**
+     * Status code (202) indicating that a request was accepted for
+     * processing, but was not completed.
+     */
+
+    public static final int SC_ACCEPTED = 202;
+
+    /**
+     * Status code (203) indicating that the meta information presented
+     * by the client did not originate from the server.
+     */
+
+    public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
+
+    /**
+     * Status code (204) indicating that the request succeeded but that
+     * there was no new information to return.
+     */
+
+    public static final int SC_NO_CONTENT = 204;
+
+    /**
+     * Status code (205) indicating that the agent <em>SHOULD</em> reset
+     * the document view which caused the request to be sent.
+     */
+
+    public static final int SC_RESET_CONTENT = 205;
+
+    /**
+     * Status code (206) indicating that the server has fulfilled
+     * the partial GET request for the resource.
+     */
+
+    public static final int SC_PARTIAL_CONTENT = 206;
+
+    /**
+     * Status code (300) indicating that the requested resource
+     * corresponds to any one of a set of representations, each with
+     * its own specific location.
+     */
+
+    public static final int SC_MULTIPLE_CHOICES = 300;
+
+    /**
+     * Status code (301) indicating that the resource has permanently
+     * moved to a new location, and that future references should use a
+     * new URI with their requests.
+     */
+
+    public static final int SC_MOVED_PERMANENTLY = 301;
+
+    /**
+     * Status code (302) indicating that the resource has temporarily
+     * moved to another location, but that future references should
+     * still use the original URI to access the resource.
+     *
+     * This definition is being retained for backwards compatibility.
+     * SC_FOUND is now the preferred definition.
+     */
+
+    public static final int SC_MOVED_TEMPORARILY = 302;
+
+    /**
+    * Status code (302) indicating that the resource reside
+    * temporarily under a different URI. Since the redirection might
+    * be altered on occasion, the client should continue to use the
+    * Request-URI for future requests.(HTTP/1.1) To represent the
+    * status code (302), it is recommended to use this variable.
+    */
+
+    public static final int SC_FOUND = 302;
+
+    /**
+     * Status code (303) indicating that the response to the request
+     * can be found under a different URI.
+     */
+
+    public static final int SC_SEE_OTHER = 303;
+
+    /**
+     * Status code (304) indicating that a conditional GET operation
+     * found that the resource was available and not modified.
+     */
+
+    public static final int SC_NOT_MODIFIED = 304;
+
+    /**
+     * Status code (305) indicating that the requested resource
+     * <em>MUST</em> be accessed through the proxy given by the
+     * <code><em>Location</em></code> field.
+     */
+
+    public static final int SC_USE_PROXY = 305;
+
+     /**
+     * Status code (307) indicating that the requested resource 
+     * resides temporarily under a different URI. The temporary URI
+     * <em>SHOULD</em> be given by the <code><em>Location</em></code> 
+     * field in the response.
+     */
+
+     public static final int SC_TEMPORARY_REDIRECT = 307;
+
+    /**
+     * Status code (400) indicating the request sent by the client was
+     * syntactically incorrect.
+     */
+
+    public static final int SC_BAD_REQUEST = 400;
+
+    /**
+     * Status code (401) indicating that the request requires HTTP
+     * authentication.
+     */
+
+    public static final int SC_UNAUTHORIZED = 401;
+
+    /**
+     * Status code (402) reserved for future use.
+     */
+
+    public static final int SC_PAYMENT_REQUIRED = 402;
+
+    /**
+     * Status code (403) indicating the server understood the request
+     * but refused to fulfill it.
+     */
+
+    public static final int SC_FORBIDDEN = 403;
+
+    /**
+     * Status code (404) indicating that the requested resource is not
+     * available.
+     */
+
+    public static final int SC_NOT_FOUND = 404;
+
+    /**
+     * Status code (405) indicating that the method specified in the
+     * <code><em>Request-Line</em></code> is not allowed for the resource
+     * identified by the <code><em>Request-URI</em></code>.
+     */
+
+    public static final int SC_METHOD_NOT_ALLOWED = 405;
+
+    /**
+     * Status code (406) indicating that the resource identified by the
+     * request is only capable of generating response entities which have
+     * content characteristics not acceptable according to the accept
+     * headers sent in the request.
+     */
+
+    public static final int SC_NOT_ACCEPTABLE = 406;
+
+    /**
+     * Status code (407) indicating that the client <em>MUST</em> first
+     * authenticate itself with the proxy.
+     */
+
+    public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+
+    /**
+     * Status code (408) indicating that the client did not produce a
+     * request within the time that the server was prepared to wait.
+     */
+
+    public static final int SC_REQUEST_TIMEOUT = 408;
+
+    /**
+     * Status code (409) indicating that the request could not be
+     * completed due to a conflict with the current state of the
+     * resource.
+     */
+
+    public static final int SC_CONFLICT = 409;
+
+    /**
+     * Status code (410) indicating that the resource is no longer
+     * available at the server and no forwarding address is known.
+     * This condition <em>SHOULD</em> be considered permanent.
+     */
+
+    public static final int SC_GONE = 410;
+
+    /**
+     * Status code (411) indicating that the request cannot be handled
+     * without a defined <code><em>Content-Length</em></code>.
+     */
+
+    public static final int SC_LENGTH_REQUIRED = 411;
+
+    /**
+     * Status code (412) indicating that the precondition given in one
+     * or more of the request-header fields evaluated to false when it
+     * was tested on the server.
+     */
+
+    public static final int SC_PRECONDITION_FAILED = 412;
+
+    /**
+     * Status code (413) indicating that the server is refusing to process
+     * the request because the request entity is larger than the server is
+     * willing or able to process.
+     */
+
+    public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
+
+    /**
+     * Status code (414) indicating that the server is refusing to service
+     * the request because the <code><em>Request-URI</em></code> is longer
+     * than the server is willing to interpret.
+     */
+
+    public static final int SC_REQUEST_URI_TOO_LONG = 414;
+
+    /**
+     * Status code (415) indicating that the server is refusing to service
+     * the request because the entity of the request is in a format not
+     * supported by the requested resource for the requested method.
+     */
+
+    public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+
+    /**
+     * Status code (416) indicating that the server cannot serve the
+     * requested byte range.
+     */
+
+    public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+
+    /**
+     * Status code (417) indicating that the server could not meet the
+     * expectation given in the Expect request header.
+     */
+
+    public static final int SC_EXPECTATION_FAILED = 417;
+
+    /**
+     * Status code (500) indicating an error inside the HTTP server
+     * which prevented it from fulfilling the request.
+     */
+
+    public static final int SC_INTERNAL_SERVER_ERROR = 500;
+
+    /**
+     * Status code (501) indicating the HTTP server does not support
+     * the functionality needed to fulfill the request.
+     */
+
+    public static final int SC_NOT_IMPLEMENTED = 501;
+
+    /**
+     * Status code (502) indicating that the HTTP server received an
+     * invalid response from a server it consulted when acting as a
+     * proxy or gateway.
+     */
+
+    public static final int SC_BAD_GATEWAY = 502;
+
+    /**
+     * Status code (503) indicating that the HTTP server is
+     * temporarily overloaded, and unable to handle the request.
+     */
+
+    public static final int SC_SERVICE_UNAVAILABLE = 503;
+
+    /**
+     * Status code (504) indicating that the server did not receive
+     * a timely response from the upstream server while acting as
+     * a gateway or proxy.
+     */
+
+    public static final int SC_GATEWAY_TIMEOUT = 504;
+
+    /**
+     * Status code (505) indicating that the server does not support
+     * or refuses to support the HTTP protocol version that was used
+     * in the request message.
+     */
+
+    public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpServletResponseWrapper.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpServletResponseWrapper.java
new file mode 100644
index 0000000..751e4bc
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpServletResponseWrapper.java
@@ -0,0 +1,195 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+import java.io.IOException;
+
+import javax.servlet.ServletResponseWrapper;
+
+/**
+ * 
+ * Provides a convenient implementation of the HttpServletResponse interface that
+ * can be subclassed by developers wishing to adapt the response from a Servlet.
+ * This class implements the Wrapper or Decorator pattern. Methods default to
+ * calling through to the wrapped response object.
+ * 
+ * @author 	Various
+ * @version 	$Version$
+  * @since	v 2.3
+ *
+ * @see 	javax.servlet.http.HttpServletResponse
+ *
+ */
+
+public class HttpServletResponseWrapper extends ServletResponseWrapper implements HttpServletResponse {
+
+
+    /** 
+    * Constructs a response adaptor wrapping the given response.
+    * @throws java.lang.IllegalArgumentException if the response is null
+    */
+    public HttpServletResponseWrapper(HttpServletResponse response) {
+	    super(response);
+    }
+    
+    private HttpServletResponse _getHttpServletResponse() {
+	return (HttpServletResponse) super.getResponse();
+    }
+    
+    /**
+     * The default behavior of this method is to call addCookie(Cookie cookie)
+     * on the wrapped response object.
+     */
+    public void addCookie(Cookie cookie) {
+	this._getHttpServletResponse().addCookie(cookie);
+    }
+
+    /**
+     * The default behavior of this method is to call containsHeader(String name)
+     * on the wrapped response object.
+     */
+
+ 
+    public boolean containsHeader(String name) {
+	return this._getHttpServletResponse().containsHeader(name);
+    }
+    
+    /**
+     * The default behavior of this method is to call encodeURL(String url)
+     * on the wrapped response object.
+     */
+    public String encodeURL(String url) {
+	return this._getHttpServletResponse().encodeURL(url);
+    }
+
+    /**
+     * The default behavior of this method is to return encodeRedirectURL(String url)
+     * on the wrapped response object.
+     */
+    public String encodeRedirectURL(String url) {
+	return this._getHttpServletResponse().encodeRedirectURL(url);
+    }
+
+    /**
+     * The default behavior of this method is to call encodeUrl(String url)
+     * on the wrapped response object.
+     */
+    public String encodeUrl(String url) {
+	return this._getHttpServletResponse().encodeUrl(url);
+    }
+    
+    /**
+     * The default behavior of this method is to return encodeRedirectUrl(String url)
+     * on the wrapped response object.
+     */
+    public String encodeRedirectUrl(String url) {
+	return this._getHttpServletResponse().encodeRedirectUrl(url);
+    }
+    
+    /**
+     * The default behavior of this method is to call sendError(int sc, String msg)
+     * on the wrapped response object.
+     */
+    public void sendError(int sc, String msg) throws IOException {
+	this._getHttpServletResponse().sendError(sc, msg);
+    }
+
+    /**
+     * The default behavior of this method is to call sendError(int sc)
+     * on the wrapped response object.
+     */
+
+
+    public void sendError(int sc) throws IOException {
+	this._getHttpServletResponse().sendError(sc);
+    }
+
+    /**
+     * The default behavior of this method is to return sendRedirect(String location)
+     * on the wrapped response object.
+     */
+    public void sendRedirect(String location) throws IOException {
+	this._getHttpServletResponse().sendRedirect(location);
+    }
+    
+    /**
+     * The default behavior of this method is to call setDateHeader(String name, long date)
+     * on the wrapped response object.
+     */
+    public void setDateHeader(String name, long date) {
+	this._getHttpServletResponse().setDateHeader(name, date);
+    }
+    
+    /**
+     * The default behavior of this method is to call addDateHeader(String name, long date)
+     * on the wrapped response object.
+     */
+   public void addDateHeader(String name, long date) {
+	this._getHttpServletResponse().addDateHeader(name, date);
+    }
+    
+    /**
+     * The default behavior of this method is to return setHeader(String name, String value)
+     * on the wrapped response object.
+     */
+    public void setHeader(String name, String value) {
+	this._getHttpServletResponse().setHeader(name, value);
+    }
+    
+    /**
+     * The default behavior of this method is to return addHeader(String name, String value)
+     * on the wrapped response object.
+     */
+     public void addHeader(String name, String value) {
+	this._getHttpServletResponse().addHeader(name, value);
+    }
+    
+    /**
+     * The default behavior of this method is to call setIntHeader(String name, int value)
+     * on the wrapped response object.
+     */
+    public void setIntHeader(String name, int value) {
+	this._getHttpServletResponse().setIntHeader(name, value);
+    }
+    
+    /**
+     * The default behavior of this method is to call addIntHeader(String name, int value)
+     * on the wrapped response object.
+     */
+    public void addIntHeader(String name, int value) {
+	this._getHttpServletResponse().addIntHeader(name, value);
+    }
+
+    /**
+     * The default behavior of this method is to call setStatus(int sc)
+     * on the wrapped response object.
+     */
+
+
+    public void setStatus(int sc) {
+	this._getHttpServletResponse().setStatus(sc);
+    }
+    
+    /**
+     * The default behavior of this method is to call setStatus(int sc, String sm)
+     * on the wrapped response object.
+     */
+     public void setStatus(int sc, String sm) {
+	this._getHttpServletResponse().setStatus(sc, sm);
+    }
+
+   
+}
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpSession.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpSession.java
new file mode 100644
index 0000000..2957975
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpSession.java
@@ -0,0 +1,423 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+import java.util.Enumeration;
+import javax.servlet.ServletContext;
+
+/**
+ *
+ * Provides a way to identify a user across more than one page
+ * request or visit to a Web site and to store information about that user.
+ *
+ * <p>The servlet container uses this interface to create a session
+ * between an HTTP client and an HTTP server. The session persists
+ * for a specified time period, across more than one connection or
+ * page request from the user. A session usually corresponds to one 
+ * user, who may visit a site many times. The server can maintain a 
+ * session in many ways such as using cookies or rewriting URLs.
+ *
+ * <p>This interface allows servlets to 
+ * <ul>
+ * <li>View and manipulate information about a session, such as
+ *     the session identifier, creation time, and last accessed time
+ * <li>Bind objects to sessions, allowing user information to persist 
+ *     across multiple user connections
+ * </ul>
+ *
+ * <p>When an application stores an object in or removes an object from a
+ * session, the session checks whether the object implements
+ * {@link HttpSessionBindingListener}. If it does, 
+ * the servlet notifies the object that it has been bound to or unbound 
+ * from the session. Notifications are sent after the binding methods complete. 
+ * For session that are invalidated or expire, notifications are sent after
+ * the session has been invalidated or expired.
+ *
+ * <p> When container migrates a session between VMs in a distributed container
+ * setting, all session attributes implementing the {@link HttpSessionActivationListener}
+ * interface are notified.
+ * 
+ * <p>A servlet should be able to handle cases in which
+ * the client does not choose to join a session, such as when cookies are
+ * intentionally turned off. Until the client joins the session,
+ * <code>isNew</code> returns <code>true</code>.  If the client chooses 
+ * not to join
+ * the session, <code>getSession</code> will return a different session
+ * on each request, and <code>isNew</code> will always return
+ * <code>true</code>.
+ *
+ * <p>Session information is scoped only to the current web application
+ * (<code>ServletContext</code>), so information stored in one context
+ * will not be directly visible in another.
+ *
+ * @author	Various
+ * @version	$Version$
+ *
+ *
+ * @see 	HttpSessionBindingListener
+ * @see 	HttpSessionContext
+ *
+ */
+
+public interface HttpSession {
+
+
+
+
+    /**
+     *
+     * Returns the time when this session was created, measured
+     * in milliseconds since midnight January 1, 1970 GMT.
+     *
+     * @return				a <code>long</code> specifying
+     * 					when this session was created,
+     *					expressed in 
+     *					milliseconds since 1/1/1970 GMT
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     *
+     */
+
+    public long getCreationTime();
+    
+    
+    
+    
+    /**
+     *
+     * Returns a string containing the unique identifier assigned 
+     * to this session. The identifier is assigned 
+     * by the servlet container and is implementation dependent.
+     * 
+     * @return				a string specifying the identifier
+     *					assigned to this session
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     *
+     */
+
+    public String getId();
+    
+    
+    
+
+    /**
+     *
+     * Returns the last time the client sent a request associated with
+     * this session, as the number of milliseconds since midnight
+     * January 1, 1970 GMT, and marked by the time the container received the request. 
+     *
+     * <p>Actions that your application takes, such as getting or setting
+     * a value associated with the session, do not affect the access
+     * time.
+     *
+     * @return				a <code>long</code>
+     *					representing the last time 
+     *					the client sent a request associated
+     *					with this session, expressed in 
+     *					milliseconds since 1/1/1970 GMT
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     *
+     */
+
+    public long getLastAccessedTime();
+    
+    
+    /**
+    * Returns the ServletContext to which this session belongs.
+    *    
+    * @return The ServletContext object for the web application
+    * @since 2.3
+    */
+
+    public ServletContext getServletContext();
+
+
+    /**
+     *
+     * Specifies the time, in seconds, between client requests before the 
+     * servlet container will invalidate this session.  A negative time
+     * indicates the session should never timeout.
+     *
+     * @param interval		An integer specifying the number
+     * 				of seconds 
+     *
+     */
+    
+    public void setMaxInactiveInterval(int interval);
+
+
+
+
+   /**
+    * Returns the maximum time interval, in seconds, that 
+    * the servlet container will keep this session open between 
+    * client accesses. After this interval, the servlet container
+    * will invalidate the session.  The maximum time interval can be set
+    * with the <code>setMaxInactiveInterval</code> method.
+    * A negative time indicates the session should never timeout.
+    *  
+    *
+    * @return		an integer specifying the number of
+    *			seconds this session remains open
+    *			between client requests
+    *
+    * @see		#setMaxInactiveInterval
+    *
+    *
+    */
+
+    public int getMaxInactiveInterval();
+    
+    
+
+
+   /**
+    *
+    * @deprecated 	As of Version 2.1, this method is
+    *			deprecated and has no replacement.
+    *			It will be removed in a future
+    *			version of the Java Servlet API.
+    *
+    */
+
+    public HttpSessionContext getSessionContext();
+    
+    
+    
+    
+    /**
+     *
+     * Returns the object bound with the specified name in this session, or
+     * <code>null</code> if no object is bound under the name.
+     *
+     * @param name		a string specifying the name of the object
+     *
+     * @return			the object with the specified name
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     *
+     */
+  
+    public Object getAttribute(String name);
+    
+    
+    
+    
+    /**
+     *
+     * @deprecated 	As of Version 2.2, this method is
+     * 			replaced by {@link #getAttribute}.
+     *
+     * @param name		a string specifying the name of the object
+     *
+     * @return			the object with the specified name
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     *
+     */
+  
+    public Object getValue(String name);
+    
+    
+    
+
+    /**
+     *
+     * Returns an <code>Enumeration</code> of <code>String</code> objects
+     * containing the names of all the objects bound to this session. 
+     *
+     * @return			an <code>Enumeration</code> of 
+     *				<code>String</code> objects specifying the
+     *				names of all the objects bound to
+     *				this session
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     *
+     */
+    
+    public Enumeration getAttributeNames();
+    
+    
+    
+
+    /**
+     *
+     * @deprecated 	As of Version 2.2, this method is
+     * 			replaced by {@link #getAttributeNames}
+     *
+     * @return				an array of <code>String</code>
+     *					objects specifying the
+     *					names of all the objects bound to
+     *					this session
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     *
+     */
+    
+    public String[] getValueNames();
+    
+    
+    
+
+    /**
+     * Binds an object to this session, using the name specified.
+     * If an object of the same name is already bound to the session,
+     * the object is replaced.
+     *
+     * <p>After this method executes, and if the new object
+     * implements <code>HttpSessionBindingListener</code>,
+     * the container calls 
+     * <code>HttpSessionBindingListener.valueBound</code>. The container then   
+     * notifies any <code>HttpSessionAttributeListener</code>s in the web 
+     * application.
+     
+     * <p>If an object was already bound to this session of this name
+     * that implements <code>HttpSessionBindingListener</code>, its 
+     * <code>HttpSessionBindingListener.valueUnbound</code> method is called.
+     *
+     * <p>If the value passed in is null, this has the same effect as calling 
+     * <code>removeAttribute()<code>.
+     *
+     *
+     * @param name			the name to which the object is bound;
+     *					cannot be null
+     *
+     * @param value			the object to be bound
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     *
+     */
+ 
+    public void setAttribute(String name, Object value);
+    
+
+
+
+    
+    /**
+     *
+     * @deprecated 	As of Version 2.2, this method is
+     * 			replaced by {@link #setAttribute}
+     *
+     * @param name			the name to which the object is bound;
+     *					cannot be null
+     *
+     * @param value			the object to be bound; cannot be null
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     *
+     */
+ 
+    public void putValue(String name, Object value);
+
+
+
+
+
+    /**
+     *
+     * Removes the object bound with the specified name from
+     * this session. If the session does not have an object
+     * bound with the specified name, this method does nothing.
+     *
+     * <p>After this method executes, and if the object
+     * implements <code>HttpSessionBindingListener</code>,
+     * the container calls 
+     * <code>HttpSessionBindingListener.valueUnbound</code>. The container
+     * then notifies any <code>HttpSessionAttributeListener</code>s in the web 
+     * application.
+     * 
+     * 
+     *
+     * @param name				the name of the object to
+     *						remove from this session
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     */
+
+    public void removeAttribute(String name);
+
+
+
+
+
+    /**
+     *
+     * @deprecated 	As of Version 2.2, this method is
+     * 			replaced by {@link #removeAttribute}
+     *
+     * @param name				the name of the object to
+     *						remove from this session
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					invalidated session
+     */
+
+    public void removeValue(String name);
+
+
+
+
+    /**
+     *
+     * Invalidates this session then unbinds any objects bound
+     * to it. 
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					already invalidated session
+     *
+     */
+
+    public void invalidate();
+    
+    
+    
+    
+    /**
+     *
+     * Returns <code>true</code> if the client does not yet know about the
+     * session or if the client chooses not to join the session.  For 
+     * example, if the server used only cookie-based sessions, and
+     * the client had disabled the use of cookies, then a session would
+     * be new on each request.
+     *
+     * @return 				<code>true</code> if the 
+     *					server has created a session, 
+     *					but the client has not yet joined
+     *
+     * @exception IllegalStateException	if this method is called on an
+     *					already invalidated session
+     *
+     */
+
+    public boolean isNew();
+
+
+
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionActivationListener.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionActivationListener.java
new file mode 100644
index 0000000..95963ae
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionActivationListener.java
@@ -0,0 +1,36 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+import java.util.EventListener;
+
+    /** Objects that are bound to a session may listen to container
+    ** events notifying them that sessions will be passivated and that
+    ** session will be activated. A container that migrates session between VMs
+    ** or persists sessions is required to notify all attributes bound to sessions
+    ** implementing HttpSessionActivationListener.
+    **
+    * @since 2.3
+    */
+    
+public interface HttpSessionActivationListener extends EventListener { 
+
+    /** Notification that the session is about to be passivated.*/
+    public void sessionWillPassivate(HttpSessionEvent se); 
+    /** Notification that the session has just been activated.*/
+    public void sessionDidActivate(HttpSessionEvent se);
+} 
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionAttributeListener.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionAttributeListener.java
new file mode 100644
index 0000000..b70e9f5
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionAttributeListener.java
@@ -0,0 +1,35 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+import java.util.EventListener;
+
+	/** This listener interface can be implemented in order to
+	* get notifications of changes to the attribute lists of sessions within
+	* this web application.
+	* @since	v 2.3
+*/
+
+public interface HttpSessionAttributeListener extends EventListener {
+	/** Notification that an attribute has been added to a session. Called after the attribute is added.*/
+    public void attributeAdded ( HttpSessionBindingEvent se );
+	/** Notification that an attribute has been removed from a session. Called after the attribute is removed. */
+    public void attributeRemoved ( HttpSessionBindingEvent se );
+	/** Notification that an attribute has been replaced in a session. Called after the attribute is replaced. */
+    public void attributeReplaced ( HttpSessionBindingEvent se );
+
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionBindingEvent.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionBindingEvent.java
new file mode 100644
index 0000000..5e84f32
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionBindingEvent.java
@@ -0,0 +1,151 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.http;
+
+
+
+/**
+ *
+ * Events of this type are either sent to an object that implements
+ * {@link HttpSessionBindingListener} when it is bound or 
+ * unbound from a session, or to a {@link HttpSessionAttributeListener} 
+ * that has been configured in the deployment descriptor when any attribute is
+ * bound, unbound or replaced in a session.
+ *
+ * <p>The session binds the object by a call to
+ * <code>HttpSession.setAttribute</code> and unbinds the object
+ * by a call to <code>HttpSession.removeAttribute</code>.
+ *
+ *
+ *
+ * @author		Various
+ * @version		$Version$
+ * 
+ * @see 		HttpSession
+ * @see 		HttpSessionBindingListener
+ * @see			HttpSessionAttributeListener
+ */
+
+public class HttpSessionBindingEvent extends HttpSessionEvent {
+
+
+
+
+    /* The name to which the object is being bound or unbound */
+
+    private String name;
+    
+    /* The object is being bound or unbound */
+
+    private Object value;
+    
+  
+
+    /**
+     *
+     * Constructs an event that notifies an object that it
+     * has been bound to or unbound from a session. 
+     * To receive the event, the object must implement
+     * {@link HttpSessionBindingListener}.
+     *
+     *
+     *
+     * @param session 	the session to which the object is bound or unbound
+     *
+     * @param name 	the name with which the object is bound or unbound
+     *
+     * @see			#getName
+     * @see			#getSession
+     *
+     */
+
+    public HttpSessionBindingEvent(HttpSession session, String name) {
+	super(session);
+	this.name = name;
+    }
+    
+    /**
+     *
+     * Constructs an event that notifies an object that it
+     * has been bound to or unbound from a session. 
+     * To receive the event, the object must implement
+     * {@link HttpSessionBindingListener}.
+     *
+     *
+     *
+     * @param session 	the session to which the object is bound or unbound
+     *
+     * @param name 	the name with which the object is bound or unbound
+     *
+     * @see			#getName
+     * @see			#getSession
+     *
+     */
+    
+    public HttpSessionBindingEvent(HttpSession session, String name, Object value) {
+	super(session);
+	this.name = name;
+	this.value = value;
+    }
+    
+    
+   	/** Return the session that changed. */
+    public HttpSession getSession () { 
+	return super.getSession();
+    }
+ 
+   
+  
+    
+    /**
+     *
+     * Returns the name with which the attribute is bound to or
+     * unbound from the session.
+     *
+     *
+     * @return		a string specifying the name with which
+     *			the object is bound to or unbound from
+     *			the session
+     *
+     *
+     */
+
+    public String getName() {
+	return name;
+    }
+    
+    /**
+	* Returns the value of the attribute that has been added, removed or replaced.
+	* If the attribute was added (or bound), this is the value of the attribute. If the attribute was
+	* removed (or unbound), this is the value of the removed attribute. If the attribute was replaced, this
+	* is the old value of the attribute.
+	*
+        * @since 2.3
+	*/
+	
+	public Object getValue() {
+	    return this.value;   
+	}
+    
+}
+
+
+
+
+
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionBindingListener.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionBindingListener.java
new file mode 100644
index 0000000..a076744
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionBindingListener.java
@@ -0,0 +1,77 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.http;
+
+import java.util.EventListener;
+
+
+ 
+ 
+
+/**
+ * Causes an object to be notified when it is bound to
+ * or unbound from a session. The object is notified
+ * by an {@link HttpSessionBindingEvent} object. This may be as a result
+ * of a servlet programmer explicitly unbinding an attribute from a session,
+ * due to a session being invalidated, or due to a session timing out.
+ *
+ *
+ * @author		Various
+ * @version		$Version$
+ *
+ * @see HttpSession
+ * @see HttpSessionBindingEvent
+ *
+ */
+
+public interface HttpSessionBindingListener extends EventListener {
+
+
+
+    /**
+     *
+     * Notifies the object that it is being bound to
+     * a session and identifies the session.
+     *
+     * @param event		the event that identifies the
+     *				session 
+     *
+     * @see #valueUnbound
+     *
+     */ 
+
+    public void valueBound(HttpSessionBindingEvent event);
+    
+    
+
+    /**
+     *
+     * Notifies the object that it is being unbound
+     * from a session and identifies the session.
+     *
+     * @param event		the event that identifies
+     *				the session 
+     *	
+     * @see #valueBound
+     *
+     */
+
+    public void valueUnbound(HttpSessionBindingEvent event);
+    
+    
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionContext.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionContext.java
new file mode 100644
index 0000000..dd7bfe6
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionContext.java
@@ -0,0 +1,70 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+package javax.servlet.http;
+
+import java.util.Enumeration;
+
+/**
+ *
+ * @author		Various
+ * @version		$Version$
+ *
+ * @deprecated		As of Java(tm) Servlet API 2.1
+ *			for security reasons, with no replacement.
+ *			This interface will be removed in a future
+ *			version of this API.
+ *
+ * @see			HttpSession
+ * @see			HttpSessionBindingEvent
+ * @see			HttpSessionBindingListener
+ *
+ */
+
+
+public interface HttpSessionContext {
+
+    /**
+     *
+     * @deprecated 	As of Java Servlet API 2.1 with
+     *			no replacement. This method must 
+     *			return null and will be removed in
+     *			a future version of this API.
+     *
+     */
+
+    public HttpSession getSession(String sessionId);
+    
+    
+    
+  
+    /**
+     *
+     * @deprecated	As of Java Servlet API 2.1 with
+     *			no replacement. This method must return 
+     *			an empty <code>Enumeration</code> and will be removed
+     *			in a future version of this API.
+     *
+     */
+
+    public Enumeration getIds();
+}
+
+
+
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionEvent.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionEvent.java
new file mode 100644
index 0000000..8aa85f9
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionEvent.java
@@ -0,0 +1,33 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+
+	/** This is the class representing event notifications for
+	* changes to sessions within a web application.
+	 * @since	v 2.3
+	*/
+public class HttpSessionEvent extends java.util.EventObject {
+	/** Construct a session event from the given source.*/
+	 public HttpSessionEvent(HttpSession source) {
+		super(source);
+}
+	/** Return the session that changed.*/
+    public HttpSession getSession () { 
+	return (HttpSession) super.getSource();
+    }
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionListener.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionListener.java
new file mode 100644
index 0000000..b1a9224
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpSessionListener.java
@@ -0,0 +1,44 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package javax.servlet.http;
+
+import java.util.EventListener;
+
+	/** 
+	* Implementations of this interface are notified of changes to the 
+	* list of active sessions in a web application.
+	* To receive notification events, the implementation class
+	* must be configured in the deployment descriptor for the web application.
+	* @see HttpSessionEvent
+	 * @since	v 2.3
+	*/
+
+public interface HttpSessionListener extends EventListener {
+    
+	/** 
+	* Notification that a session was created.
+	* @param se the notification event
+	*/
+    public void sessionCreated ( HttpSessionEvent se );
+    
+	/** 
+	* Notification that a session is about to be invalidated.
+	* @param se the notification event
+	*/
+    public void sessionDestroyed ( HttpSessionEvent se );
+    
+}
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/HttpUtils.java b/servletapi/jsr154/src/share/javax/servlet/http/HttpUtils.java
new file mode 100644
index 0000000..abc5d90
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/HttpUtils.java
@@ -0,0 +1,306 @@
+/*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package javax.servlet.http;
+
+import javax.servlet.ServletInputStream;
+import java.util.Hashtable;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+import java.io.IOException;
+
+/**
+ * @deprecated		As of Java(tm) Servlet API 2.3. 
+ *			These methods were only useful
+ *			with the default encoding and have been moved
+ *			to the request interfaces.
+ *
+*/
+
+
+public class HttpUtils {
+
+    private static final String LSTRING_FILE =
+	"javax.servlet.http.LocalStrings";
+    private static ResourceBundle lStrings =
+	ResourceBundle.getBundle(LSTRING_FILE);
+        
+    
+    
+    /**
+     * Constructs an empty <code>HttpUtils</code> object.
+     *
+     */
+
+    public HttpUtils() {}
+    
+    
+    
+    
+
+    /**
+     *
+     * Parses a query string passed from the client to the
+     * server and builds a <code>HashTable</code> object
+     * with key-value pairs. 
+     * The query string should be in the form of a string
+     * packaged by the GET or POST method, that is, it
+     * should have key-value pairs in the form <i>key=value</i>,
+     * with each pair separated from the next by a &amp; character.
+     *
+     * <p>A key can appear more than once in the query string
+     * with different values. However, the key appears only once in 
+     * the hashtable, with its value being
+     * an array of strings containing the multiple values sent
+     * by the query string.
+     * 
+     * <p>The keys and values in the hashtable are stored in their
+     * decoded form, so
+     * any + characters are converted to spaces, and characters
+     * sent in hexadecimal notation (like <i>%xx</i>) are
+     * converted to ASCII characters.
+     *
+     * @param s		a string containing the query to be parsed
+     *
+     * @return		a <code>HashTable</code> object built
+     * 			from the parsed key-value pairs
+     *
+     * @exception IllegalArgumentException	if the query string 
+     *						is invalid
+     *
+     */
+
+    static public Hashtable parseQueryString(String s) {
+
+	String valArray[] = null;
+	
+	if (s == null) {
+	    throw new IllegalArgumentException();
+	}
+	Hashtable ht = new Hashtable();
+	StringBuffer sb = new StringBuffer();
+	StringTokenizer st = new StringTokenizer(s, "&");
+	while (st.hasMoreTokens()) {
+	    String pair = (String)st.nextToken();
+	    int pos = pair.indexOf('=');
+	    if (pos == -1) {
+		// XXX
+		// should give more detail about the illegal argument
+		throw new IllegalArgumentException();
+	    }
+	    String key = parseName(pair.substring(0, pos), sb);
+	    String val = parseName(pair.substring(pos+1, pair.length()), sb);
+	    if (ht.containsKey(key)) {
+		String oldVals[] = (String []) ht.get(key);
+		valArray = new String[oldVals.length + 1];
+		for (int i = 0; i < oldVals.length; i++) 
+		    valArray[i] = oldVals[i];
+		valArray[oldVals.length] = val;
+	    } else {
+		valArray = new String[1];
+		valArray[0] = val;
+	    }
+	    ht.put(key, valArray);
+	}
+	return ht;
+    }
+
+
+
+
+    /**
+     *
+     * Parses data from an HTML form that the client sends to 
+     * the server using the HTTP POST method and the 
+     * <i>application/x-www-form-urlencoded</i> MIME type.
+     *
+     * <p>The data sent by the POST method contains key-value
+     * pairs. A key can appear more than once in the POST data
+     * with different values. However, the key appears only once in 
+     * the hashtable, with its value being
+     * an array of strings containing the multiple values sent
+     * by the POST method.
+     *
+     * <p>The keys and values in the hashtable are stored in their
+     * decoded form, so
+     * any + characters are converted to spaces, and characters
+     * sent in hexadecimal notation (like <i>%xx</i>) are
+     * converted to ASCII characters.
+     *
+     *
+     *
+     * @param len	an integer specifying the length,
+     *			in characters, of the 
+     *			<code>ServletInputStream</code>
+     *			object that is also passed to this
+     *			method
+     *
+     * @param in	the <code>ServletInputStream</code>
+     *			object that contains the data sent
+     *			from the client
+     * 
+     * @return		a <code>HashTable</code> object built
+     *			from the parsed key-value pairs
+     *
+     *
+     * @exception IllegalArgumentException	if the data
+     *			sent by the POST method is invalid
+     *
+     */
+     
+
+    static public Hashtable parsePostData(int len, 
+					  ServletInputStream in)
+    {
+	// XXX
+	// should a length of 0 be an IllegalArgumentException
+	
+	if (len <=0)
+	    return new Hashtable(); // cheap hack to return an empty hash
+
+	if (in == null) {
+	    throw new IllegalArgumentException();
+	}
+	
+	//
+	// Make sure we read the entire POSTed body.
+	//
+        byte[] postedBytes = new byte [len];
+        try {
+            int offset = 0;
+       
+	    do {
+		int inputLen = in.read (postedBytes, offset, len - offset);
+		if (inputLen <= 0) {
+		    String msg = lStrings.getString("err.io.short_read");
+		    throw new IllegalArgumentException (msg);
+		}
+		offset += inputLen;
+	    } while ((len - offset) > 0);
+
+	} catch (IOException e) {
+	    throw new IllegalArgumentException(e.getMessage());
+	}
+
+        // XXX we shouldn't assume that the only kind of POST body
+        // is FORM data encoded using ASCII or ISO Latin/1 ... or
+        // that the body should always be treated as FORM data.
+        //
+
+        try {
+            String postedBody = new String(postedBytes, 0, len, "8859_1");
+            return parseQueryString(postedBody);
+        } catch (java.io.UnsupportedEncodingException e) {
+            // XXX function should accept an encoding parameter & throw this
+            // exception.  Otherwise throw something expected.
+            throw new IllegalArgumentException(e.getMessage());
+        }
+    }
+
+
+
+
+    /*
+     * Parse a name in the query string.
+     */
+
+    static private String parseName(String s, StringBuffer sb) {
+	sb.setLength(0);
+	for (int i = 0; i < s.length(); i++) {
+	    char c = s.charAt(i); 
+	    switch (c) {
+	    case '+':
+		sb.append(' ');
+		break;
+	    case '%':
+		try {
+		    sb.append((char) Integer.parseInt(s.substring(i+1, i+3), 
+						      16));
+		    i += 2;
+		} catch (NumberFormatException e) {
+		    // XXX
+		    // need to be more specific about illegal arg
+		    throw new IllegalArgumentException();
+		} catch (StringIndexOutOfBoundsException e) {
+		    String rest  = s.substring(i);
+		    sb.append(rest);
+		    if (rest.length()==2)
+			i++;
+		}
+		
+		break;
+	    default:
+		sb.append(c);
+		break;
+	    }
+	}
+	return sb.toString();
+    }
+
+
+
+
+    /**
+     *
+     * Reconstructs the URL the client used to make the request,
+     * using information in the <code>HttpServletRequest</code> object.
+     * The returned URL contains a protocol, server name, port
+     * number, and server path, but it does not include query
+     * string parameters.
+     * 
+     * <p>Because this method returns a <code>StringBuffer</code>,
+     * not a string, you can modify the URL easily, for example,
+     * to append query parameters.
+     *
+     * <p>This method is useful for creating redirect messages
+     * and for reporting errors.
+     *
+     * @param req	a <code>HttpServletRequest</code> object
+     *			containing the client's request
+     * 
+     * @return		a <code>StringBuffer</code> object containing
+     *			the reconstructed URL
+     *
+     */
+
+    public static StringBuffer getRequestURL (HttpServletRequest req) {
+	StringBuffer url = new StringBuffer ();
+	String scheme = req.getScheme ();
+	int port = req.getServerPort ();
+	String urlPath = req.getRequestURI();
+	
+	//String		servletPath = req.getServletPath ();
+	//String		pathInfo = req.getPathInfo ();
+
+	url.append (scheme);		// http, https
+	url.append ("://");
+	url.append (req.getServerName ());
+	if ((scheme.equals ("http") && port != 80)
+		|| (scheme.equals ("https") && port != 443)) {
+	    url.append (':');
+	    url.append (req.getServerPort ());
+	}
+	//if (servletPath != null)
+	//    url.append (servletPath);
+	//if (pathInfo != null)
+	//    url.append (pathInfo);
+	url.append(urlPath);
+	return url;
+    }
+}
+
+
+
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings.properties b/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings.properties
new file mode 100644
index 0000000..bf4d4c8
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings.properties
@@ -0,0 +1,27 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Default localized string information
+# Localized for Locale en_US
+
+err.cookie_name_is_token=Cookie name \"{0}\" is a reserved token
+err.io.negativelength=Negative Length given in write method
+err.io.short_read=Short Read
+
+http.method_not_implemented=Method {0} is not defined in RFC 2068 and is not supported by the Servlet API 
+
+http.method_get_not_supported=HTTP method GET is not supported by this URL
+http.method_post_not_supported=HTTP method POST is not supported by this URL
+http.method_put_not_supported=HTTP method PUT is not supported by this URL
+http.method_delete_not_supported=Http method DELETE is not supported by this URL
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings_es.properties b/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings_es.properties
new file mode 100644
index 0000000..3630e6b
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings_es.properties
@@ -0,0 +1,29 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# $Id$
+#
+# Default localized string information
+# Localized para Locale es_ES
+
+err.cookie_name_is_token=El Nombre de Cookie {0} es una palabra reservada
+err.io.negativelength=Longitud Negativa en el metodo write
+err.io.short_read=Lectura Corta
+
+http.method_not_implemented=El Metodo {0} no esta definido en la especificacion RFC 2068 y no es soportado por la API Servlet 
+
+http.method_get_not_supported=El Metodo HTTP GET no es soportado por esta URL
+http.method_post_not_supported=El Metodo HTTP POST no es soportado por esta URL
+http.method_put_not_supported=El Metodo HTTP PUT no es soportado por esta URL
+http.method_delete_not_supported=El Metodo HTTP DELETE no es soportado por esta URL
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings_fr.properties b/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings_fr.properties
new file mode 100644
index 0000000..b2bc09c
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings_fr.properties
@@ -0,0 +1,27 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Default localized string information
+# Localized for Locale fr_FR
+
+err.cookie_name_is_token=Le nom de cookie \"{0}\" est un \"token\" réservé
+err.io.negativelength=Taille négative donnée dans la méthode \"write\"
+err.io.short_read=Lecture partielle
+
+http.method_not_implemented=Le méthode {0} n''est pas définie dans la RFC 2068 et n''est pas supportée par l''API Servlet
+
+http.method_get_not_supported=La méthode HTTP GET n''est pas supportée par cette URL
+http.method_post_not_supported=La méthode HTTP POST n''est pas supportée par cette URL
+http.method_put_not_supported=La méthode HTTP PUT n''est pas supportée par cette URL
+http.method_delete_not_supported=La méthode HTTP DELETE n''est pas supportée par cette URL 
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings_ja.properties b/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings_ja.properties
new file mode 100644
index 0000000..edfbdd5
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/LocalStrings_ja.properties
@@ -0,0 +1,27 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Default localized string information
+# Localized for Locale ja_JP
+
+err.cookie_name_is_token=\u30af\u30c3\u30ad\u30fc\u540d \"{0}\" \u306f\u4e88\u7d04\u6e08\u306e\u30c8\u30fc\u30af\u30f3\u3067\u3059\u3002
+err.io.negativelength=write\u30e1\u30bd\u30c3\u30c9\u306b\u8ca0\u306e\u9577\u3055\u304c\u6307\u5b9a\u3055\u308c\u307e\u3057\u305f\u3002
+err.io.short_read=\u8aad\u307f\u8fbc\u307f\u304c\u3059\u3050\u306b\u7d42\u308f\u308a\u307e\u3057\u305f\u3002
+
+http.method_not_implemented=\u30e1\u30bd\u30c3\u30c9 {0} \u306fRFC 2068\u306b\u306f\u5b9a\u7fa9\u3055\u308c\u3066\u304a\u3089\u305a\u3001\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8API\u3067\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u307e\u305b\u3093\u3002
+
+http.method_get_not_supported=HTTP\u306eGET\u30e1\u30bd\u30c3\u30c9\u306f\u3001\u3053\u306eURL\u3067\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002
+http.method_post_not_supported=HTTP\u306ePOST\u30e1\u30bd\u30c3\u30c9\u306f\u3001\u3053\u306eURL\u3067\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002
+http.method_put_not_supported=HTTP\u306ePUT\u30e1\u30bd\u30c3\u30c9\u306f\u3001\u3053\u306eURL\u3067\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002
+http.method_delete_not_supported=HTTP\u306eDELETE\u30e1\u30bd\u30c3\u30c9\u306f\u3001\u3053\u306eURL\u3067\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002
diff --git a/servletapi/jsr154/src/share/javax/servlet/http/package.html b/servletapi/jsr154/src/share/javax/servlet/http/package.html
new file mode 100644
index 0000000..efdbdf6
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/http/package.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<!--
+
+  Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
+
+  This software is the proprietary information of Sun Microsystems, Inc.  
+  Use is subject to license terms.
+
+-->
+
+</HEAD>
+<BODY BGCOLOR="white">
+
+The javax.servlet.http package contains a number of classes and interfaces
+that describe and define the contracts between a servlet class
+running under the HTTP protocol and the runtime environment provided
+for an instance of such a class by a conforming servlet container.
+
+
+</BODY>
+</HTML>
diff --git a/servletapi/jsr154/src/share/javax/servlet/package.html b/servletapi/jsr154/src/share/javax/servlet/package.html
new file mode 100644
index 0000000..9a720a8
--- /dev/null
+++ b/servletapi/jsr154/src/share/javax/servlet/package.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<!--
+
+  Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
+
+  This software is the proprietary information of Sun Microsystems, Inc.  
+  Use is subject to license terms.
+
+-->
+
+</HEAD>
+<BODY BGCOLOR="white">
+
+The javax.servlet package contains a number of classes and interfaces that
+describe and define the contracts between a servlet class and the
+runtime environment provided for an instance of such a class by a
+conforming servlet container.
+
+
+</BODY>
+</HTML>